Google App Engine for Java: 第 1 回 開発をスピードアップ!

App Engine for Java を使って、スケーラブルな Java ベースのキラー・アプリケーションを構築する

Google App Engine が Python 使いのためだけのものだった頃を覚えていますか?当時は Java™ 開発者にとって不遇の時代でした。しかし 2009年4月になってついに、Google Inc. はそのクラウド・コンピューティング・プラットフォームを Java 開発者に開放しました。この 3 回の連載記事では、Java 技術に関する書籍の著者であり、Java 技術の指導者でもある Rick Hightower が、この信頼性に優れた堅牢かつ面白みのある Java ベースの開発プラットフォームを使い始めるお手伝いをします。今回の記事では、Google App Engine for Java が非常にスケーラブルなキラー・アプリケーションのデプロイメント・プラットフォームとなる理由を概説した後、Google Plugin for Eclipse を使って 2 つのサンプル・アプリケーションを作成します。1 つは GWT (Google Web Toolkit) ベースのアプリケーション、もう 1 つは Java Servlet API ベースのアプリケーションです。これらのサンプル・アプリケーションの作成手順をとおして、アプリケーションを一から構築するという点、そしてそのアプリケーションを最大 500万件のアクセスに対応できるように調整してデプロイするという点で、Google App Engine for Java がもたらす違いを学んでください (しかも、Google App Engine for Java は無料です)。

Richard Hightower, CTO, Mammatus Inc.

Rick Hightower は、クラウド・コンピューティング、GWT、Java EE、Spring、そしてHibernate 開発のトレーニングを専門とする会社、Mammatus Inc. の最高技術責任者です。人気の高い『Java Tools for Extreme Programming』の著者であり、長年 TheServerSide.com でのダウンロード件数第 1 位となっている『Struts Live』初版の共著者でもあります。また、IBM developerWorks にも記事、チュートリアルを投稿している他、Java Developer's Journal の編集委員を務め、DZone では Java および Groovy に関するトピックのコントリビューターとしてお馴染みです。



2009年 8月 11日

アイデアとは、痒みに似ています。つまり、どこかに痒いところがあって、そこを掻くと気持ちがいいといったようなものです。ソフトウェア開発者として、私たちは多種多様なアプリケーションのアイデアを考え出すことに多くの時間を費やしています。ここがソフトウェア開発の面白い部分だとは思いますが、何が難しいかと言えば、ソフトウェア製品のアイデアを実現する方法を考え出すことです。想像したことを実際に形にできれば満足感を得られます。そうでなければ (痒いところを掻かなければ) 歯がゆいだけです。

多くのアプリケーションが決して実現されることのない理由の 1 つは、インフラストラクチャーが必要になることにあります。管理の行き届いたインフラストラクチャーには、システム管理者、DBA、ネットワーク・エンジニアからなるチームが関わっているのが通常です。このようなチームを編成できるのは、最近までは主に金持ち相手の企業でした。サード・パーティーにアプリケーションのホスト費を支払うとしても、それは愚かさの証明ではありません。アプリケーションの人気が急上昇し、突然アクセス件数が増えたとしたらどうなるか、考えてもみてください。負荷の急増を予測しにくいというだけの理由で、いわゆるスラッシュドット効果によって名案が完全に失敗に変わる可能性があります。

しかし周知のとおり、この事態は変わりつつあります。Web サービスの前提は進化し、今ではクラウド・コンピューティングとそのたくましい親戚である PaaS (Platform-as-a-Service) によって、以前より簡単にアプリケーションを構築し、デプロイし、広く使われるようにする、という手段を使えるようになっています。今や、Twitter のようなアプリケーションを作成してクラウド・プラットフォームにデプロイすれば、そのアプリケーションはとどまることなく広がっていきます。何と爽快な話でしょう!

この 3 回の連載記事では、クラウド・コンピューティングや PaaS がソフトウェア開発の進化にとっての重要な転換点となる理由、そして現在プレビュー・リリースとして入手可能な非常に興味深い Java 開発用の新しいプラットフォーム、Google App Engine for Java の使い方を実践的に学びます。この記事ではまず、App Engine for Java が提供するアプリケーション・サービスのタイプを含め、その概要を説明します。概要を説明したら、早速、App Engine for Java の Google Plugin for Eclipse を使って 2 つのサンプル・アプリケーションの 1 つ目に取り掛かります。最初に手掛けるのは、App Engine for Java が Java Servlet API をサポートしていることを利用したアプリケーションです。そして 2 番目のアプリケーションでは、App Engine for Java が GWT サポートしていることを利用します。連載第 2 回では、App Engine for Java がサーブレットと GWT をサポートしていることを利用した小さな連絡先管理アプリケーションを作成します。そして第 3 回では、この独自に構築したカスタム・アプリケーションを使って、JDO (Java Data Objects) と JPA (Java Persistence API) をベースとする App Engine for Java の Java 永続化サポートについて探ります。

背景の説明はこれくらいにして、早速本題に入りましょう!

Google App Engine for Java の概要

Google では検索エンジンの作成も行っていますが、一方で 2008年4月には最初の Google App Engine をリリースしました。多くの Java 開発者にとって残念なことに、初期リリースは全くの Python プログラマーのドメインでした (Python プログラマーは、ブロックには空白スペースを使うべきだと考えている人々です (私は Python に関する本を書いたことがあるので、知っているのです))。しかし Google は多くの要望に応え、2009年4月に Google App Engine for Java をリリースしました。

Google App Engine for Java は、使いやすいブラウザー・べースの Ajax GUI、Eclipse ツールのサポート、そしてバックエンドに Google App Engine を配備し、エンタープライズ Java 開発のためにすべてをカバーしたソリューションを提供します。この使いやすさと、提供されるツールは、他のクラウド・コンピューティング・ソリューションに優る Google App Engine for Java の利点です。

App Engine for Java でアプリケーションを開発するということは、Googleのリソースを使って Java オブジェクトを保存および取得することを意味します。データ・ストレージは BigTable をベースとしていますが、JDO および JPA インターフェースを利用することで、BigTable に直接アクセスしないコードを作成することができます。事実、Google は多数の API に標準ベースのサポートを提供しているため、App Engine for Java プラットフォームにまったく縛られないコードを作成することができます。

App Engine for Java は、以下の標準 Java API に依存しています。

  • java.net.URL によってサービスを取得 (HTTP および HTTPS プロトコルを使って他のホストと通信)
  • JavaMail によってメール・メッセージを送信
  • Memcache との JCache (JSR 107) インターフェースによって、クエリーと計算をキャッシュする高速な一時分散ストレージを提供

WebSphere および DB2 での App Engine for Java のデプロイメント

App Engine for Java の発表に際して、Google と IBM® の代表者は同じサンプル・アプリケーションを DB2® と WebSphere® にデプロイしました。IBM は現在、App Engine for Java 用に作成されたアプリケーションを IBM WebSphere および DB2 でも実行できるようにするため、Tivoli® LDAP および DB2 に対する下位レベルでの Google API サポートの提供に取り組んでいます。

さらに、App Engine for Java は以下のアプリケーション・サービスのサポートを提供します。

  • ユーザー認証および許可
  • CRON
  • データのインポート/エクスポート
  • ファイアウォール・データへのアクセス

データのインポート/エクスポートは、App Engine for Java アプリケーションに他のソースからのデータを取り込んだり、その逆の操作を行ったりする上で重要です。またこれは、App Engine for Java に縛られないようにするためのもう 1 つの手段でもあります。Google の CRON サポートは、特定のスケジュールでアクセスする内部 URL をベースとしているため、App Engine for Java にあまり結合することのない優れたサービスとなっています。ユーザーの認証および許可に関しては、App Engine for Java に固有のメカニズムになっていますが、ServletFilter、アスペクト、または Spring Security プラグインを作成して、この密結合を最小限にすることもできます。

最初の App Engine for Java アプリケーションの作成

ここまで読めば、最初の App Engine for Java アプリケーションの構築に取り掛かる準備は万端です。最初のステップとして、App Engine for Java 対応 Google Plugin for Eclipse をインストールしてください。インストールが完了したら、アプリケーションの作成を開始することができます。

Eclipse IDE を立ち上げると、Eclipse IDE の「Printer (印刷)」ボタンの隣に 3 つの新しいボタンが追加されていることがわかります。図 1 に示されているように、その 3 つのボタンとは、g の文字が付いた青いボール、G の文字が付いた赤いツールボックス、そして App Engine for Java の小型ジェット機です。

図 1. Eclipse IDE に表示されたピカピカの新しいボタン
Eclipse IDE に表示されたピカピカの新しいボタン

この新しい 3 つのボタンには、それぞれ以下の機能があります。

  • 青いボールをクリックすると、App Engine for Java プロジェクト作成ウィザードにアクセスすることができます。
  • 赤いツールボックスをクリックすると、GWT プロジェクトをコンパイルすることができます。
  • 小型ジェット機は、App Engine プロジェクトをデプロイするための鍵となります。

これから、プロジェクト作成ウィザードを使って 2 つの新規プロジェクトを作成します。1 つはサーブレット・ベースのプロジェクト、もう 1 つは GWT を使用して作成するプロジェクトです。GWT プロジェクトをコンパイルするには、ツールボックスの機能を使います。App Engine プロジェクトをデプロイする準備が整ったら、小型ジェット機を起動して活躍させることになります。

まずは、App Engine for Java プロジェクトを作成するところから始めます。青いボールをクリックして、プロジェクト作成ウィザードを呼び出してください。このウィザードで、gaej.example というパッケージを使用する、SimpleServletApp という名前のアプリケーションを作成します (図 2 を参照)。

図 2. 新規プロジェクトの開始
新規プロジェクトの開始

この最初の単純なサンプル・アプリケーションでは、GWT サポートを選択しないので注意してください。このステップを完了すると、プロジェクト作成ウィザードによって、Hello World タイプのサーブレットを備えた単純なサーブレット・ベースのアプリケーションが作成されます。図 3 は、作成されたプロジェクトのスクリーンショットです。

図 3. SimpleServletApp プロジェクト
SimpleServletApp プロジェクト

このサーブレット・ベースの新規プロジェクトには、以下の JAR ファイルが自動的に組み込まれます。

  • datanucleus-*.jar: 標準 JDO または下位レベルの BigTable API を使用して App Engine for Java データ・ストアにアクセスするための JAR
  • appengine-api-sdk.1.2.0.jar: App Engine for Java Security などの標準外の App Engine for Java アプリケーション・サービスを使用するための JAR
  • geronimo-*.jar: JTA (Java Transaction Management API) や JPA などの標準 Java API を使用するための JAR
  • jdo2-api-2.3-SNAPSHOT.jar: JDO API を使用するための JAR

App Engine for Java と一部の App Engine for Java アプリケーション・サービスの永続化 API を使用する方法については、第 2 回の記事から説明します。

Google App Engine のランタイム・コンテナーを構成する appengine.xml という名前のファイルにも注目してください。ここではこの appengine.xml を使って、App Engine for Java でロギングを行う logging.properties ファイルを構成します。

まずは App Engine for Java サーブレット・アプリケーションについて調べる

プロジェクト作成ウィザードでの構成が完了すると、App Engine for Java が Hello World スタイルのサーブレット・アプリケーションを構成する必要最小限のコードを提供します。このコードを調べてから、App Engine for Java の Eclipse ツールを使ってアプリケーションを実行する方法を調べてください。このアプリケーションへのメイン・エントリー・ポイントは、リスト 1 に記載する SimpleServletAppServlet です。

リスト 1. SimpleServletAppServlet
package gaej.example;

import java.io.IOException;
import javax.servlet.http.*;

@SuppressWarnings("serial")
public class SimpleServletAppServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        resp.setContentType("text/plain");
        resp.getWriter().println("Hello, world");
    }
}

web.xml では、このサーブレットは /simpleservletapp という URI でマッピングされます (リスト 2 を参照)。

リスト 2. web.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
    <servlet>
        <servlet-name>simpleservletapp</servlet-name>
        <servlet-class>gaej.example.SimpleServletAppServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>simpleservletapp</servlet-name>
        <url-pattern>/simpleservletapp</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
</web-app>

プロジェクト作成ウィザードによって、新規サーブレットへのリンクが設定された index.html ファイルも提供されています (リスト 3 を参照)。

リスト 3. プロジェクト作成ウィザードによって生成された index.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<!-- The HTML 4.01 Transitional DOCTYPE declaration-->
<!-- above set at the top of the file will set     -->
<!-- the browser's rendering engine into           -->
<!-- "Quirks Mode". Replacing this declaration     -->
<!-- with a "Standards Mode" doctype is supported, -->
<!-- but may lead to some differences in layout.   -->

<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    
    <!--                                           -->
    <!-- Any title is fine                         -->
    <!--                                           -->
    <title>Hello App Engine</title>
  </head>

  <!--                                           -->
  <!-- The body can have arbitrary html, or      -->
  <!-- you can leave the body empty if you want  -->
  <!-- to create a completely dynamic UI.        -->
  <!--                                           -->
  <body>
    <h1>Hello App Engine!</h1>
    
    <table>
      <tr>
        <td colspan="2" style="font-weight:bold;">Available Servlets:</td>        
      </tr>
      <tr>
        <td><a href="simpleservletapp"/>SimpleServletAppServlet</td>
      </tr>
    </table>
  </body>
</html>

App Engine for Java で他に使えるものは?

Google では、App Engine for Java との相性が良いツールおよびフレームワークのリストを維持管理しています (「参考文献」を参照)。例えば、App Engine for Java は BeanShell、Groovy、Scala、JRuby、Jython、および Rhino をはじめ、数多くの JVM 言語をサポートします。App Engine for Java がサポートする Java SE と Java EE API はかなりの数にのぼるため (Servlets、JSP、JPA、JavaMail、JAXP (Java API for XML Processing) など)、既存のフレームワークの多くは App Engine for Java でそのまま使えます。その一例は、Spring フレームワークです (ただし、Spring ORM に対する多少の次善策が必要になります)。Tapestry、Wicket、DWR、Tiles、SiteMesh、Grails も機能します。Struts 2 も小さなパッチを加えれば機能します。App Engine for Java で機能しないフレームワークには、Hibernate と JDBC (リレーショナル・データベースのサポートがないため)、JMX、Java WebServices、JAX-RPC と JAX-WS、JCA、JNDI、JMS、EJB、および Java RMI があります。

ほんの少数の Java API を使って、単純なサーブレット・アプリケーションが作成できました。ここがまさに、ポイントです。つまり、App Engine for Java は標準 API を使用して App Engine の機能をラップし、Java プラットフォームに用意されている豊富なフレームワークを App Engine がサポートできるようにしています。

アプリケーションをデプロイする

App Engine for Java Eclipse ツールでサーブレット・ベースのアプリケーションを実行するには、プロジェクトを右クリックし、「Run As (実行)」メニューを選択した後、次に青いボールが頭に付いた「Web Application (Web アプリケーション)」を選択します (図 4 を参照)。

図 4. App Engine for Java 開発サーバーの実行
App Engine for Java 開発サーバーの実行

これで、ブラウザーで http://localhost:8080/simpleservletapp にナビゲートすると、Hello World メッセージを表示するアプリケーションを確認できるはずです。

GWT と従来の Java Web アプリケーションとの違い

GWT で作業するときには、Java コードを JavaScript にコンパイルしてから、ブラウザーで Web アプリケーションの GUI を実行します。したがって、従来の Java Web アプリケーションよりもずっと、従来の GUI アプリケーションに近いものになります。GWT は、ブラウザーでクライアント・コンポーネントを実行します。このクライアント・コンポーネントが、開発者によって作成された一連の RMI スタイルの Java クラスを介して Java サーバー・サイドのコードと対話します。

App Engine for Java/GWT アプリケーションの作成

単純な App Engine for Java サーブレット・アプリケーションがどのように機能するかがわかったところで、次は GWT アプリケーション用の App Engine for Java Eclipse ツールについて検討していきます。まずは Eclipse IDE ツールバーにある青いボールをクリックして、Google プロジェクト作成ウィザードを起動してください。今回は、GWT のサポートを選択します (図 5 を参照)。

図 5. App Engine for Java プロジェクト作成ウィザードでの単純な GWT アプリケーションの作成
App Engine for Java プロジェクト作成ウィザードでの単純な GWT アプリケーションの作成

図 6 を見るとわかるように、App Engine for Java は、単純なサーブレット・ベースのアプリケーションの場合に比べて遙かに多くのコード成果物を GWT アプリケーションに提供します。このサンプル・アプリケーションは、グリーティング・サービス・アプリケーションと対話するための GUI であり、GWT を使用したアプリケーションです。

図 6. GWT アプリケーションに提供されたコード成果物
GWT アプリケーションに提供されたコード成果物

GWT アプリケーションには、サーブレット・ベースのアプリケーションには必要なかった gwt-servlet.jar という JAR が追加されます。

その他の成果物は以下のとおりです。

  • src/gaej/example: SimpleGWTApp.gwt.xml: GWT モジュール記述子
  • src/gaej.example.server: GreetingServiceImpl.java: グリーティング・サービスの実装
  • src/gaej.example.client: GreetingService.java: グリーティング・サービスの同期 API
  • src/gaej.example.client: GreetingServiceAsync.java: グリーティング・サービスの非同期 API
  • src/gaej.example.client: SimpleGWTApp.java: スターター GUI も作成するメイン・エントリー・ポイント
  • war/WEB-INF: web.xml: GreetingServiceImpl を構成するデプロイメント記述子
  • war: SimpleGWTApp.html: GWT GUI を表示する HTML ページ
  • war: SimpleGWTApp.css: GWT GUI のスタイルシート

このアプリケーションのアーキテクチャーとソース・コードの詳細を探っていく前に、アプリケーションを実行すると何が起こるかを確認してください。アプリケーションを実行するには、ツールバーの赤いツールボックスをクリックし、次に「Compile (コンパイル)」ボタンをクリックします。その上でプロジェクトを右クリックし、前と同じように「Run As (実行)」、「Web Application (Web アプリケーション)」の順にメニュー項目を選択します。今回の対象は GWT アプリケーションであることから、この操作によって GWT Hosted Mode (ホスト・モード) のコンソールとブラウザーが表示されます。この Web アプリケーションで自分の名前を入力して、どんな応答が返ってくるかを確かめてみてください。図 7 に、私が受け取った応答を示します。

図 7. サンプル GWT アプリケーションの実行
サンプル GWT アプリケーションの実行

次のセクションで、このサンプル GWT アプリケーションの内容を説明します。GWT について詳しく調べたい場合 (または GWT チュートリアルを受けたい場合) は、「参考文献」を参照してください。

GWT アプリケーションの内部

指定された構成に応じて、Eclipse の GWT ツールはスターター・アプリケーションを作成します。このアプリケーションに備わった HTML フロントエンド (リスト 10 に記載する SimpleGWTApp.html) は、simplegwtapp.js と simplegwtapp.nocache.js をロードします。これらの JavaScript コードは、GWT によって開発者の Java コードから生成されます。つまり、gaej.example.client パッケージの src ディレクトリーに置かれた Java コードから生成されます (リスト 67、および 8 を参照)。

GUI を作成するメイン・エントリー・ポイントは gaej.example.client.SimpleGWTApp です (リスト 8 を参照)。このクラスは GWT GUI 要素を作成し、これらの要素を SimpleGWTApp.html にある HTML DOM 要素と関連付けます (リスト 10 を参照)。SimpleGWTApp.html は、nameFieldContainersendButtonContainer という 2 つの DOM 要素 (表内の列) を定義します。SimpleGWTApp クラスは RootPanel.get("nameFieldContainer") を使用してこの 2 つの DOM 要素と関連付けられたパネルにアクセスし、DOM 要素を GUI 要素で置き換えます。続いて SimpleGWTApp クラスはテキスト・ボックスとボタンを定義します。ユーザーはこのテキスト・ボックスに誰かの名前を入力した後、このボタンを使ってグリーティングを送信します (リスト 10 を参照)。

GWT は SimpleGWTApp クラスをアプリケーションのメイン・エントリー・ポイントとして認識しますが、それは、SimpleGWTApp.gwt.xml が entry-point 要素を使って、このクラスをメイン・エントリー・ポイントとして指定しているからです。

SimpleGWTAppsendButton というボタンを接続し、このボタンがクリックされると、SimpleGWTAppGreetingServicegreetServer メソッドを呼び出すようにしています。GreetingService インタ-フェースは、src/gaej.example.client.GreetingService.java に定義されます (リスト 6)。

Ajax は本質的に非同期であることから、GWT はリモート・サービスにアクセスするための非同期インターフェースを定義します。SimpleGWTApp が使用する非同期インターフェースは、src/gaej.example.client.GreetingServiceAsync.java に定義されます (リスト 7)。GreetingServiceImpl (src/gaej.example.server.GreetingServiceImpl.java) は、GreetingService (リスト 5) に定義された greetServer メソッドを実装します。GreetingServiceImpl.greetServer メソッドが返すグリーディング・メッセージの String は、SimpleGWTApp が作成したダイアログ・ボックスにグリーティング・メッセージを表示するために使用します。

GWT モジュール記述子は、GUI アプリケーションのメイン・エントリー・ポイント、gaej.example.client.SimpleGWTApp をリスト 4 のように宣言します。

リスト 4. GWT モジュール記述子 (src/gaej/example/SimpleGWTApp.gwt.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.6.4//EN"

"http://google-web-toolkit.googlecode.com/svn/tags/1.6.4/
  distro-source/core/src/gwt-module.dtd">


<module rename-to='simplegwtapp'>
  <!-- Inherit the core Web Toolkit stuff.                        -->
  <inherits name='com.google.gwt.user.User'/>

  <!-- Inherit the default GWT style sheet.  You can change       -->
  <!-- the theme of your GWT application by uncommenting          -->
  <!-- any one of the following lines.                            -->
  <inherits name='com.google.gwt.user.theme.standard.Standard'/>
  <!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->
  <!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/>     -->

  <!-- Other module inherits                                      -->

  <!-- Specify the app entry point class.                         -->
  <entry-point class='gaej.example.client.SimpleGWTApp'/>
</module>

リスト 5 に、グリーティング・サービス・アプリケーションの実際の実装である GreetingServiceImpl を記載します。クライアント・コードは、サーバー・サイドで実行されるこの実装を、リモート・プロシージャー・コールを使って呼び出します。

リスト 5. グリーティング・サービス・アプリケーションの実装 (src/gaej.example.server.GreetingServiceImpl.java)
package gaej.example.server;

import gaej.example.client.GreetingService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**
 * The server side implementation of the RPC service.
 */
@SuppressWarnings("serial")
public class GreetingServiceImpl extends RemoteServiceServlet implements
        GreetingService {

    public String greetServer(String input) {
        String serverInfo = getServletContext().getServerInfo();
        String userAgent = getThreadLocalRequest().getHeader("User-Agent");
        return "Hello, " + input + "!<br><br>I am running " + serverInfo
                + ".<br><br>It looks like you are using:<br>" + userAgent;
    }
}

リスト 6 に記載する GreetingService は、クライアント・コードが使用するリモート・プロシージャー・コールのインターフェースです。

リスト 6. 同期 API (src/gaej.example.client.GreetingService.java)
package gaej.example.client;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;


/**
 * The client side stub for the RPC service.
 */
@RemoteServiceRelativePath("greet")
public interface GreetingService extends RemoteService {
    String greetServer(String name);
}

リスト 7 に記載する GreetingServiceAsync は、クライアント・コードが使用する実際のインターフェースです。それぞれのメソッドがコールバック・オブジェクトを提供するため、リモート・プロシージャー・コールが完了すると、ユーザーが非同期に完了の通知を受けることになります。GWT は内部で Ajax を使用します。Ajax をクライアントで使用するときには、クライアントをブロックしないで、非同期呼び出しにするのが最善です。ブロックすると、Ajax を使用する目的が台無しになってしまいます。

リスト 7. 非同期 API (src/gaej.example.client.GreetingServiceAsync.java)
package gaej.example.client;

import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * The async counterpart of <code>GreetingService</code>.
 */
public interface GreetingServiceAsync {
    void greetServer(String input, AsyncCallback<String> callback);
}

SimpleGWTApp ではアクションの大部分が行われます。SimpleGWTApp は、GUI イベントを登録し、それから Ajax リクエストを GreetingService に送信します。

リスト 8. スターター GUI の作成も行うアプリケーションのメイン・エントリー・ポイント (src/gaej.example.client.SimpleGWTApp.java)
package gaej.example.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class SimpleGWTApp implements EntryPoint {
    /**
     * The message displayed to the user when the server cannot be reached or
     * returns an error.
     */
    private static final String SERVER_ERROR = "An error occurred while "
            + "attempting to contact the server. Please check your network "
            + "connection and try again.";

    /**
     * Create a remote service proxy to talk to the server-side Greeting service.
     */
    private final GreetingServiceAsync greetingService = GWT
            .create(GreetingService.class);

    /**
     * This is the entry point method.
     */
    public void onModuleLoad() {
        final Button sendButton = new Button("Send");
        final TextBox nameField = new TextBox();
        nameField.setText("GWT User");

        // You can add style names to widgets
        sendButton.addStyleName("sendButton");

        // Add the nameField and sendButton to the RootPanel
        // Use RootPanel.get() to get the entire body element
        RootPanel.get("nameFieldContainer").add(nameField);
        RootPanel.get("sendButtonContainer").add(sendButton);

        // Focus the cursor on the name field when the app loads
        nameField.setFocus(true);
        nameField.selectAll();

        // Create the popup dialog box
        final DialogBox dialogBox = new DialogBox();
        dialogBox.setText("Remote Procedure Call");
        dialogBox.setAnimationEnabled(true);
        final Button closeButton = new Button("Close");
        // You can set the id of a widget by accessing its Element
        closeButton.getElement().setId("closeButton");
        final Label textToServerLabel = new Label();
        final HTML serverResponseLabel = new HTML();
        VerticalPanel dialogVPanel = new VerticalPanel();
        dialogVPanel.addStyleName("dialogVPanel");
        dialogVPanel.add(new HTML("<b>Sending name to the server:</b>"));
        dialogVPanel.add(textToServerLabel);
        dialogVPanel.add(new HTML("<br><b>Server replies:</b>"));
        dialogVPanel.add(serverResponseLabel);
        dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
        dialogVPanel.add(closeButton);
        dialogBox.setWidget(dialogVPanel);

        // Add a handler to close the DialogBox
        closeButton.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                dialogBox.hide();
                sendButton.setEnabled(true);
                sendButton.setFocus(true);
            }
        });

        // Create a handler for the sendButton and nameField
        class MyHandler implements ClickHandler, KeyUpHandler {
            /**
             * Fired when the user clicks on the sendButton.
             */
            public void onClick(ClickEvent event) {
                sendNameToServer();
            }

            /**
             * Fired when the user types in the nameField.
             */
            public void onKeyUp(KeyUpEvent event) {
                if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
                    sendNameToServer();
                }
            }

            /**
             * Send the name from the nameField to the server and wait for a response.
             */
            private void sendNameToServer() {
                sendButton.setEnabled(false);
                String textToServer = nameField.getText();
                textToServerLabel.setText(textToServer);
                serverResponseLabel.setText("");
                greetingService.greetServer(textToServer,
                        new AsyncCallback<String>() {
                            public void onFailure(Throwable caught) {
                                // Show the RPC error message to the user
                                dialogBox
                                        .setText("Remote Procedure Call - Failure");
                                serverResponseLabel
                                        .addStyleName("serverResponseLabelError");
                                serverResponseLabel.setHTML(SERVER_ERROR);
                                dialogBox.center();
                                closeButton.setFocus(true);
                            }

                            public void onSuccess(String result) {
                                dialogBox.setText("Remote Procedure Call");
                                serverResponseLabel
                                        .removeStyleName("serverResponseLabelError");
                                serverResponseLabel.setHTML(result);
                                dialogBox.center();
                                closeButton.setFocus(true);
                            }
                        });
            }
        }

        // Add a handler to send the name to the server
        MyHandler handler = new MyHandler();
        sendButton.addClickHandler(handler);
        nameField.addKeyUpHandler(handler);
    }
}

リスト 9 に記載する Web デプロイメント記述子 web.xml は、GreetingService をサーブレット・ベースの Web リソースとしてマッピングします。GreetingService サーブレットは /simplegwtapp/greet という名前でマッピングされるため、SimpleGWTApp がこのサーブレットをロードし、このサーブレットに対して呼び出しを行うことが可能になります。この Web デプロイメント記述子は SimpleGWTApp.html がアプリケーションのウェルカム・ページであることも示しているため、常にこのファイルをロードします。

リスト 9. GreetingServiceImpl を構成するデプロイメント記述子 (war/WEB-INF/web.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

  <!-- Default page to serve -->
  <welcome-file-list>
    <welcome-file>SimpleGWTApp.html</welcome-file>
  </welcome-file-list>
 
  <!-- Servlets -->
  <servlet>
    <servlet-name>greetServlet</servlet-name>
    <servlet-class>gaej.example.server.GreetingServiceImpl</servlet-class>
  </servlet>
 
  <servlet-mapping>
    <servlet-name>greetServlet</servlet-name>
    <url-pattern>/simplegwtapp/greet</url-pattern>
  </servlet-mapping>

</web-app>

リスト 10 に、HTMLフロントエンドとなる SimpleGWTApp.html を記載します。このページでは、GWT が開発者の Java コードから生成する JavaScript コード (simplegwtapp.js と simplegwtapp.nocache.js) をロードします。前述のとおり、JavaScript コードの生成に使用される Java コードは、src ディレクトリーの gaej.example.client パッケージの下に置かれています (リスト 678 を参照)。

リスト 10. GWT GUI を表示する HTML ページ (war/SimpleGWTApp.html)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<!-- The HTML 4.01 Transitional DOCTYPE declaration-->
<!-- above set at the top of the file will set     -->
<!-- the browser's rendering engine into           -->
<!-- "Quirks Mode". Replacing this declaration     -->
<!-- with a "Standards Mode" doctype is supported, -->
<!-- but may lead to some differences in layout.   -->

<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">

    <!--                                                               -->
    <!-- Consider inlining CSS to reduce the number of requested files -->
    <!--                                                               -->
    <link type="text/css" rel="stylesheet" href="SimpleGWTApp.css">

    <!--                                           -->
    <!-- Any title is fine                         -->
    <!--                                           -->
    <title>Web Application Starter Project</title>
    
    <!--                                           -->
    <!-- This script loads your compiled module.   -->
    <!-- If you add any GWT meta tags, they must   -->
    <!-- be added before this line.                -->
    <!--                                           -->
    <script type="text/javascript" language="javascript" 
  src="simplegwtapp/simplegwtapp.nocache.js"></script>
  </head>

  <!--                                           -->
  <!-- The body can have arbitrary html, or      -->
  <!-- you can leave the body empty if you want  -->
  <!-- to create a completely dynamic UI.        -->
  <!--                                           -->
  <body>

    <!-- OPTIONAL: include this if you want history support -->
    <iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' 
  style="position:absolute;width:0;height:0;border:0"></iframe>

    <h1>Web Application Starter Project</h1>

    <table align="center">
      <tr>
        <td colspan="2" style="font-weight:bold;">Please enter your name:</td> 
      </tr>
      <tr>
        <td id="nameFieldContainer"></td>
        <td id="sendButtonContainer"></td>
      </tr>
    </table>
  </body>
</html>

リスト 11 に示すように、GWT では CSS を使ってアプリケーションのルック・アンド・フィールを制御します。

リスト 11. GWT GUI のスタイルシート (war/SimpleGWTApp.css)
/** Add css rules here for your application. */


/** Example rules used by the template application (remove for your app) */
h1 {
  font-size: 2em;
  font-weight: bold;
  color: #777777;
  margin: 40px 0px 70px;
  text-align: center;
}

.sendButton {
  display: block;
  font-size: 16pt;
}

/** Most GWT widgets already have a style name defined */
.gwt-DialogBox {
  width: 400px;
}

.dialogVPanel {
  margin: 5px;
}

.serverResponseLabelError {
  color: red;
}

/** Set ids using widget.getElement().setId("idOfElement") */
#closeButton {
  margin: 15px 6px 6px;
}

Google App Engine へのデプロイメント

次なる世界的キラー・アプリケーションを作成し終えると、(私たちはユーザー・フレンドリーなグリーティング・アプリケーションを切望しているため) 今度はこのアプリケーションをデプロイしたいと思うはずです。Google App Engine を使用するそもそもの意味は、Google の堅実なインフラストラクチャーへのデプロイメントを可能にして、アプリケーションをスケーラブルにすることに他なりません。Google App Engine は、(App Engine ホーム・ページの言葉を引用すると)「インフラストラクチャーに頭を悩ませることなく、1 人から 100 万人のユーザーにまで拡張する」スケーラブルなアプリケーションを構築するためのプラットフォームになります。このインフラストラクチャーを使用するには、Google App Engine for Java アカウントが必要です。

日常でもよくあるように、使い始めは無料です。無料版の App Engine for Java では、デプロイしたアプリケーションに十分な CPU、帯域幅、そして 500 万のページ・ビューに対応するストレージを使うことができます。この使用枠を超えた場合には、使った分だけ課金されることになります (この記事を執筆している時点で入手できるのは、App Engine for Java プラットフォームのレビュー版リリースであることに注意してください)。

アカウントを手に入れると、App Engine for Java サイトに空のアプリケーション・リストが表示されます。「Create New Application (新規アプリケーションの作成)」ボタンをクリックすると、図 8 のようなフォームが表示されるので、ここに固有のアプリケーション名と説明を入力します。この作業が終わると、アプリケーションの ID を示した確認メッセージが表示されます。

この ID は、アプリケーションの app.yaml ファイルに固有のものでもあります。ID は変更できないことに注意してください。アプリケーションに Google 認証を適用する場合、アプリケーションにアクセスための 「Sign In (サインイン)」ページに、「GAEj Article For Rick Part 1」と表示されます。つまり、App Engine for Java の Eclipse プラグインでアプリケーションを Google App Engine にデプロイするときには、gaejarticleforrick を使用することになります。

図 8. 新規 App Engine for Java アプリケーションの作成
新規 App Engine for Java アプリケーションの作成

アプリケーション ID を設定すると、Eclipse からアプリケーションをデプロイできるようになります。デプロイするには最初に、Google App Engine のロゴ (翼と尾翼が付いたジェット機) のようなツールバー・ボタンをクリックします (図 9 を参照)。

図 9. App Engine for Java の Eclipse プラグイン
App Engine for Java の Eclipse プラグイン

図 10 に示すダイアログで、該当する App Engine for Java プロジェクトが選択されていることを確認してから、「Deploy (デプロイ)」をクリックします。すると、Google 用のクレデンシャルを入力するように求めるプロンプトが表示されます。これは、開発者本人の E メール・アドレスとユーザー名です。

図 10. プロジェクトのデプロイメント
プロジェクトのデプロイメント

図 10 のダイアログには、「App Engine project settings… (App Engine のプロジェクト設定…)」というリンクがあります。このリンクをクリックして (プロジェクト設定ファイルからもアクセス可能)、アプリケーション ID (この例では gaejarticleforrick) を入力してください (図 11 を参照)。アプリケーション ID を入力したら、「OK」をクリックし、次に「Deploy (デプロイ)」をクリックします。

図 11. Google App Engine 用のプロジェクト設定
Google App Engine 用のプロジェクト設定

アプリケーションのデプロイメントが完了した後、このアプリケーションは http://<アプリケーション ID>.appspot.com/ で利用できるようになります。また、http://gaejarticleforrick.appspot.com/ にアクセスすれば、このアプリケーションの動作を確認することもできます。

まとめ

Google App Engine for Java を紹介する連載の第 1 回は以上です。今回は、App Engine for Java とは一体何であるかを概説し、App Engine for Java の Google Plugin for Eclipse を使用する第一歩として、2 つの小さな初歩的アプリケーション (サーブレット・ベースのアプリケーションと、GWT ベースのアプリケーション) を作成しました。GWT アプリケーションについては、Google App Engine プラットフォームにデプロイしました。

これまで記載したサンプルでは、スケーラブルな (YouTube や Facebook の規模にまで拡張するかもしれない) Java ベースのアプリケーションの作成、そしてデプロイメントを容易にするツールと機能について示しました。第 2 回では引き続き、App Engine for Java を使用することによって Java 開発者に与えられる可能性を探っていきます。この記事で紹介したサンプル・アプリケーションとは別に、次回はカスタムの連絡先管理アプリケーションを作成します。このアプリケーションは、App Engine for Java のデータ・ストアとその GUI フロントエンドを詳しく探る、第 3 回の演習でも中心的な役割を果たします。

参考文献

学ぶために

製品や技術を入手するために

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology, Open source, Cloud computing
ArticleID=426786
ArticleTitle=Google App Engine for Java: 第 1 回 開発をスピードアップ!
publish-date=08112009