2012年8月29日水曜日

GCM(Google Cloud Messaging for Android) のサーバサイド(Java)

GCM、Google Cloud Messaging for Androidをご存知でしょうか?
Androidをサーバから呼び出す処理です。
以前はC2DMという仕組みを使ったのですが、C2DMはすでに無効になっていますので、サーバからAndroidを呼び出したい時はC2DMを使います。

GCMの使い方は、
1.Google APIsでコードの取得
2.Androidアプリの作成
3.サーバサイドアプリの作成
です。
1.2.はGoogleで「Google Cloud Messaging for Android」で検索するとたくさん出てきます。
しかしながら、3.サーバサイドのJava実装は見当たらなかったので、ここに乗せておきます。

package com.blogspot.ukiuni;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;

public class GCMPush {
 private static final String GCM_ENDPOINT_URL = "https://android.googleapis.com/gcm/send";
 private static final String API_KEY = "{API-KEY(Google APIsのコンソールで得られる文字列)}";
 private static final String CHARSET = "UTF-8";
 private static final int BUFFER_SIZE = 1024;

 public static void main(String[] args) throws IOException {
  String registationId = "{REGISTATION_ID(Androidでregistした際に得られる、スゴク長い文字列)}";
  String message = "Androidへの送信メッセージ";
  GCMPush.push(registationId, message);
 }
 public static String push(String registationId, String message) throws IOException {
  URL url = new URL(GCM_ENDPOINT_URL);
  HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  connection.setDoOutput(true);
  connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + CHARSET);
  connection.setRequestProperty("Authorization", "key=" + API_KEY);
  String postParameter = "registration_id=" + URLEncoder.encode(registationId, CHARSET) + "&collapse_key=update&data.message=" + URLEncoder.encode(message, CHARSET);
  PrintStream ps = new PrintStream(connection.getOutputStream());
  ps.print(postParameter);
  int responseCode = connection.getResponseCode();
  if (200 != responseCode) {
   String errorMessage = streamToString(connection.getErrorStream());
   throw new IOException("status = " + responseCode + ", message = " + errorMessage);
  }
  return streamToString(connection.getInputStream());
 }

 private static String streamToString(InputStream in) throws IOException {
  byte[] buffer = new byte[BUFFER_SIZE];
  int readed = in.read(buffer);
  ByteArrayOutputStream bout = new ByteArrayOutputStream();
  while (readed > 0) {
   bout.write(buffer, 0, readed);
   readed = in.read(buffer);
  }
  return new String(bout.toByteArray(), CHARSET);
 }
}

2012年8月16日木曜日

Androidでjarを使った時にNoClassDefFoundExceptionが出てしまう時の対応

Androidで外部jarファイルを使うときに、Eclipseではjarファイルを取り込めていて、コンパイルもちゃんとできているのに、エミュレータや実機で動かした時に、

java.lang.NoClassDefFoundError
が出てしまうことがあります。

その対応策ですが、
Eclipseのプロジェクト→Build Path→Configure Build PathのOrder and Exportタブで、使いたいjarファイルにチェックを入れてください。

その後、Projectツールバー→Cleanで、プロジェクトをクリーンビルドすれば、問題は解決します。

2012年8月9日木曜日

GlassFishで非同期処理(AsyncContext)

J2EE6(Servlet3.0)で、Servletで非同期処理ができるようになりました。
ここでは、GlassFish3.1でServletの非同期処理の設定方法についてご紹介します。
■非同期処理って何?

Servletは通常、ブラウザからリクエストを受け付けたThreadでServletが処理をしてレスポンスをブラウザに返します。
コレが同期処理です。
同期処理で困るのが、Servletでの処理時間が長いと、ブラウザが待たされることです。

コレに対応するのが、非同期処理です。
非同期処理では、ブラウザがらリクエストを受け付けたThreadとは別のThreadで処理をすることができます。

非同期処理は、時間のかかる処理を行うのに使ったり、
Chatなど、レスポンスのタイミングをコントロールしたい時に使用します。

では、非同期処理の実装の仕方。
やることは2つです。
1. HttpServletRequest#startAsync()を使う。
2. web.xmlをServlet3.0対応にし、非同期処理を扱うすべてのルーティーン(FilterとServlet)で非同期処理を有効にする。

では、詳細を。

1. HttpServletRequest#startAsync()を使う。
filterやservletのリクエストを受け取るメソッドの中で、
AsyncContext asyncContext = request.startAsync();
を呼び出します。
それから、
asyncContext.start(Runnable);
で、非同期で処理したい内容を起動します。
例えば、↓みたいな感じです。
final AsyncContext asyncContext = request.startAsync();
asyncContext.start(new Runnable() {
        @Override
        publicvoid run() {
            try {
            //処理時間時間のかかる処理や待ち合わせ処理
            //例えば、チャットの相手の文字入力を待つとか。
            //・・・・
            OutputStream out = asyncContext.getResponse().getOutputStream();
            out.write("complete".getBytes());
            asyncContext.complete();
        } catch (Exception e) {
            log.fatal("exception on wait", e);
        }
    }
 });


2. web.xmlをServlet3.0対応にし、非同期処理を扱うすべてのルーティーン(FilterとServlet)で非同期処理を有効にする。

web.xmlをServlet3.0設定にします。
web.xmlのルートノードを、
<web-appversion="3.0"
 xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
というように設定します。
さらに、filterタグやservletタグの中で、非同期処理をすることを宣言します。

例えば
<servlet>
    <servlet-name>encodingfilter</servlet-name>
    <servlet-class>net.ukiuni.EncodingFilter</servlet-class>
    <async-supported>true</async-supported>
</servlet>
というようにasync-supportedタグを入れます。

この時に重要なのが、リクエストを受け付けるすべての処理(FilterやServlet)でasync-supportedタグを入れる必要がある、ということです。
Servletで処理をする前に、Filterでエンコーディングを設定したり、セキュリティーをかけてることがありますが、そのFilterにもasync-supportedタグを入れる必要があります。

これで、非同期処理ができるようになります。

■追記
あんまりGlassFishっぽくない、汎用的な説明になったので、GlassFishの情報を一つ。
GlassFishには、管理コンソールから「Cometを有効にする」設定ができますが、この設定をOnにしなくても、AsyncContextは使うことができます。

XperiaをPCが認識しない時の対応


XperiaをPCと繋ぎたい時があります。
・Xperiaに音楽を入れたい時
・Xperiaから写真を抜き出したい時
・Xperiaをアップデートしたい時

そのために使うソフトはコチラ。
・Windows PC Companion(Xperiaの設定→Xperia→接続設定→PC Companionのインストールにチェックを入れて、XperiaをPCにつなぐとインストールされます)
・Mac Sony Bridge for Mac http://www.sonymobile.com/gb/tools/bridge-for-mac/

で、以下が本題なんですが、
私がやったときは、Windowsで試しても、Macで試しても、Xperiaを認識しない。認識しても、処理途中でアプリが止まっちゃったり、Xperiaが落ちる。
対応として、Xperiaの設定→アプリケーション→開発→USBデバッグのチェックを外したら、無事認識しました。

同様の事象に陥っている方、ぜひお試しあれ。