You are on page 1of 45

第二回 京都 GWT 勉強会

株式会社 SOBA プロジェクト 山下 大介


http://blog.daisukeyamashita.com
はじめに
• 本日は、弊社までお越し下さいましてあ
りがとうございます。
私は誰?
• 本日の会場である、株式会社 SOBA プロジェクトの取締

• 会社では自社開発の P2P 通信による、 VoIP 技術の普及


に努めており、ここ数年は主にサーバサイド( J2EE )
周りの技術検証 / 実装をしています( SOBA CITY や SOBA mieruka を
開発しました)

• それ以前は、自社開発の P2P 通信ネットワークに携帯


電話 /PDA を接続するためのフレームワークを研究 / 開
発していました

• 基本的にネットワーク系のプログラマーです
おさらい
GWT(Google Web Toolkit) とは?
• 非同期通信 (Ajax) を Java でコーディング
するための Web プログラミングツールで

• JavaScript のライブラリではなく、 Java


で書いたプログラムを、 HTML と Java
Script のコードに変換するコンパイラです

• 各ブラウザ向けに最適化された Java
Script が送られるようになっています
アドバンテージ
• どのような Java IDE でも利用できます。
もちろんエディタでも OK!
• Unit テスト
• 素早い edit/test/debug/refactor サイクル
• 既存の Java 資産を利用できる
• オブジェクト指向デザインパターン
• Javadoc
• タイプミスなどをコンパイル時に発見で
きる
デバッグとテスト
開発シェル
• GWT では、デバックコンソールとしての
開発シェルが付属しています。
開発シェル
• 開発シェルは、ログ出力のコンソールと
して利用します

• ロギング用のメソッド「 GWT.log() 」を
利用して挿入されたログは、この開発シ
ェルに出力されます

• また、実行時に発行された Exception もす
べてフックされ、開発シェルに出力され
ます
デモ
Hosted モード
• applicationCreator で生成されるスクリプ
トから開発シェルを起動すると、 Java
IDE と連動することができる Hosted モー
ドで起動します

• Hosted モードは、 JVM で Java バイトコ


ードを実行します

• また、デバッグオプションとともに起動
されるので、手持ちのデバッガーでアタ
ッチすることができます
Web モード
• Web モードで実行するには、まず Java プログ
ラムを JavaScript にコンパイルする必要があり
ます

– com.google.gwt.dev.GWTCompiler を利用して、コマンドライ
ンでプログラムをコンパイルする

– applicationCretor で生成される、 <project-name>-compile スク


リプトを実行する

– 開発シェルのメニューから [Compile/Browse] を実行する


Web モード
• 開発シェルは -noserver オプションをつけ
る事によって、 Web サーバと連動させる
事ができる Web モードになります

• GWT.log() メソッドで書いたログは、プ
ログラムをサーバサイドで実行しても、
ローカルの開発シェルに出力することが
できます
デモ
自動テスト
• 自動テストは JUnit を利用した事があれば、簡
単です。必要なスクリプトは junitCreator で生成
できます

• junit.framework.TestCase を拡張した
com.google.gwt.junit.client.GWTTestCase を拡
張したテストケースクラスを作成

• GWTTestCase.getModuleName() は、テスト対
象のクラス名(パッケージ名込み)を返すよう
に実装
自動テスト
• GWT の自動テストでは、バックグラウンドで見
えないブラウザを実行しています

• 目に見えないブラウザを実行することによって
、テストに GUI ライブラリを使用することがで
きます

• Linux 上では見えないブラウザを起動し、テスト
を実行する前に、ターゲット環境として、 X 仮
想フレームバッファ (Xvfb) を起動することがで
きます
注意点
• Hosted モードでテストを実行している場
合、プログラムは Java バイトコードで実
行されているという点を理解しておく必
要があります

• つまり、 Hosted モードでテストされたプ


ログラムは、 JavaScript にコンパイルさ
れた後のコードが完璧である事を保障し
ていないという事を意味します
junitCreator
• -module では、 client を含めない
• 引き数では、 client を含めるが、クラス名
が衝突しないようにする (xxxxTest がオス
スメ )
%EclipseWorkspace%>junitCreator -junit "c:\Program Files\eclipse\plugins\org.junit_
3.8.2.v20080602-1318\junit.jar" -module com.daisukeyamashita.test.gwt.Test -eclipse
KyotoGwt-1-DEMO -out KyotoGwt-1-DEMO com.daisukeyamashita.test.gwt.client.
TestTest
非同期テスト
• GWT の拡張 JUnit では、 RPC 通信とそのレスポンスの
チェックなどの特別なテスト関数を提供しています

• JUnit では、テストを実行しているスレッドが停止する
とすぐにテストメソッドの呼び出し元に制御が移るよう
になっています
非同期テスト
• GWT の拡張 JUnit では、この問題を解決
する(&非同期のテストをする)ために
、2つのメソッドを用意しています

– GWTTestCase.delayTestFinish(int)
• 非同期モードへの切り替え
– GWTTestCase.finishTest()
• 非同期モードの終了
ただし、

• 一般的なブラウザではマルチスレッドが実装さ
れていないので、 GWT の拡張 JUnit でも、マル
チスレッドやスレッドを停止するテストが許可
されていません
非同期テスト
• 非同期モード中に発生する可能性がある処理

– 非同期モードの終了時間が経過する前で、さらに
finishTest() メソッドが呼び出される前に、テストが
正常終了する

– テストのどこかで例外をスローする

– finishTest() が呼び出されるか、テストが終了する前
に、非同期モードの終了時間が経過する
( TimeoutException がスローされる)
デモ
TestSuite でテストをまとめる
public class MapsTestSuite extends GWTTestSuite {
public static Test suite() {
TestSuite suite = new TestSuite("Test for a Maps Application");
suite.addTestSuite(MapTest.class);
suite.addTestSuite(EventTest.class);
suite.addTestSuite(CopyTest.class);
return suite;
}
}
Setup と Teardown (GWT1.5 ~ )
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;

private static native String getNodeName(Element elem) /*-{


return (elem.nodeName || "").toLowerCase();
}-*/;

/**
* Removes all elements in the body, except scripts and iframes.
*/
public void gwtSetUp () {
Element bodyElem = RootPanel.getBodyElement();

List<Element> toRemove = new ArrayList<Element>();


for (int i = 0, n = DOM.getChildCount(bodyElem); i < n; ++i) {
Element elem = DOM.getChild(bodyElem, i);
String nodeName = getNodeName(elem);
if (!"script".equals(nodeName) && !"iframe".equals(nodeName)) {
toRemove.add(elem);
}
}

for (int i = 0, n = toRemove.size(); i < n; ++i) {


DOM.removeChild(bodyElem, toRemove.get(i));
}
}
自動テスト
• テストの実行に必要なクラスパス
– src ディレクトリ
– bin ディレクトリ
– gwt-user.jar ライブラリ
– gwt-dev-<OS-name>.jar
– junit.jar ライブラリ
いくつかのテストモード
• Hosted モード
– junitCreator でスクリプトを生成した場合、デフォル
トで Hosted モードでのテスト実行になります

• Web モード
– junitCreator でスクリプトを生成して、 -
Dgwt.args="-web" オプションを指定して実行します

• マニュアルモード (GWT1.5 ~ )
– マニュアルモードはかなり特殊なモードで、自分の
好きなブラウザで動作させながらテストすることが
できます。出力された URL をブラウザに貼り付けて
実行します
GWT で JSON を使う
HTTP と JSON
• GWT では、 Ajax 通信をするときに、ネ
ットワークプロトコルを意識する必要は
ありませんが、 GWT で既存の WebAPI
を利用したい事があります

– 既存の WebAPI
– 他社が開発した WebAPI
JSON
• Ajax 開発者の間で、 JSON というフォー
マットがますます一般的になっています
[
{
"symbol": "BA",
"price": 87.86,
"change": -0.41
},
{
"symbol": "KO",
"price": 62.79,
"change": 0.49
},
{
"symbol": "JNJ",
"price": 67.64,
"change": 0.05
}
]
JSON
JavaScript だと以下のように書きます
var point = { "x": 3, "y": 4 };

最近だと、ビルトイン関数の eval を使って、


var jsonObject = eval('(' + jsonText + ')');

こんなに簡単!
JSON
• 以前、作ったあの WebAPI を利用したい
んだけど、 JSON で結果を返すようにな
ってるんだよなぁ。。。

• GWT で、 JSON って簡単に使えるの?


GWT と JSON
• GWT では JSON type のフルセットを持ってい
ます。( com.google.gwt.json.client )

– クラス
• JSONArray
• JSONBoolean
• JSONNull
• JSONNumber
• JSONObject
• JSONString
– 例外
• JSONException
GWT と JSON
• 文字列を JSON 型にマッピングするには

– JSONParser.parse(String)

• JSON 型を文字列に戻すには、
– 単に toString()
JSON で SOP を超える!
public class MyJSONUtility
{
interface JSONHandler {
public void handleJSON(JavaScriptObject obj);
}

public static native void makeJSONRequest(String url, JSONHandler handler) /*-{


$wnd.jsonCallback = function(jsonObj) {

@mypackage.MyJSONUtility::dispatchJSON(Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/sample/
client/JsonTestApp$JSONHandler;)(jsonObj, handler);
}

// create SCRIPT tag, and set SRC attribute equal to JSON feed URL + callback function name
var script = $wnd.document.createElement("script");
script.setAttribute("src", url+"jsonCallback");
script.setAttribute("type", "text/javascript");

$wnd.document.getElementsByTagName("head")[0].appendChild(script);

}-*/;

public static void dispatchJSON(JavaScriptObject jsonObj, JSONHandler handler) {


handler.handleJSON(jsonObj);
}
}
非同期 HTTP クライアント
• GWT には、非同期 HTTP クライアント
(com.google.gwt.http.client) が用意されています

GET メソッド
public class GetExample implements EntryPoint {
public static final int STATUS_CODE_OK = 200;

public static void doGet(String url) {


RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url);

try {
Request response = builder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable exception) {
// Code omitted for clarity
}

public void onResponseReceived(Request request, Response response) {


// Code omitted for clarity
}
});
} catch (RequestException e) {
// Code omitted for clarity
}
}

public void onModuleLoad() {


doGet("/");
}
}
POST メソッド
public class PostExample {
public static void doPost(String url, String postData) {
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, url);

try {
builder.setHeader("Content-Type", "application/x-www-form-urlencoded");
Request response = builder.sendRequest(postData, new RequestCallback() {

public void onError(Request request, Throwable exception) {


// code omitted for clarity
}

public void onResponseReceived(Request request, Response response) {


// code omitted for clarity
}
});
} catch (RequestException e) {
Window.alert("Failed to send the request: " + e.getMessage());
}
}

public void onModuleLoad() {


doPost("/", "Hello World!");
}
}
タイムアウトの設定
public static void doGetWithTimeout(String url) {
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url);

try {
//2000 ミリ秒でタイムアウト
builder.setTimeoutMillis(2000);

Request response = builder.sendRequest(null, new RequestCallback() {


public void onError(Request request, Throwable exception) {
if (exception instanceof RequestTimeoutException) {
// handle a request timeout
} else {
// handle other request errors
}
}

public void onResponseReceived(Request request, Response response) {


// code omitted for clarity
}
});
} catch (RequestException e) {
Window.alert("Failed to send the request: " + e.getMessage());
}
}
POST メソッドのリクエスト
public static interface Entry {
String getName();
String getValue();
}

public static String buildQueryString(Entry[] queryEntries) {


StringBuffer sb = new StringBuffer();

for (int i = 0, n = queryEntries.length; i < n; ++i) {


Entry queryEntry = queryEntries[i];

if (i > 0) {
sb.append("&");
}

String encodedName = URL.encodeComponent(queryEntry.getName());


sb.append(encodedName);

sb.append("=");

String encodedValue = URL.encodeComponent(queryEntry.getValue());


sb.append(encodedValue);
}

return sb.toString();
}
GET/POST 以外のメソッド
• RequestBuilder(String httpMethod, String
url) を利用し、 httpMethod に好きなメソ
ッドを書きます

– 注意!古い WebKit には XMLHttpRequest に


バグがありました
(https://bugs.webkit.org/show_bug.cgi?id=3812)Safari に対応する場合

には、ご注意ください
デモ
おわり
お知らせ
• つい先日、 8 月 28 日に GWT Ver1.5 が正
式リリースされました!

ついでにドキュメントも充実しました!!

ただし、英語だけ  orz

• 英語が苦手な人は Google JAPAN にお願


いしましょう!
お知らせ
• みなさん、『京都 GWT 勉強会』の和を広
げるために、ぜひ友達を誘ってください

• また、講演者や議題も募集します!

• 定期的に、『京都 GWT 勉強会』を開催し


ていきたいと思いますので、ぜひご協力
をお願い致します。
Photo by (c)Tomo.Yun(http://www.yunphoto.net

You might also like