Wicket on GAE/j #01
会社の人と話をして色々思うところがあったので、本格的に個人で開発してみて、GAE/jに載せてみようと思います。で、取り敢えず以下のような構成を考えています。
- Wicket+Slim3 Datastore
単体のSlim3の方がspin-upも早くて最適解だとは思いますが、個人的にJSPはもう飽きたのでHTMLベースのWicketを使ってみようと思ってます。
以前環境構築をどこかに書いた気がしますが、ちょっと見つからなかったので備忘録を兼ねて。
Eclipseのインストール
GooglePlugin for Eclipseは3.3/3.4しかサポートしていないので、ここから「Eclipse IDE for Java Developers」をダウンロードしました。
GooglePlugin for Eclipseのインストール
Eclipseを起動して[Help]->[Software Updates...]->[Available Software]->[Add site]で表示されたダイアログに以下のURLを入力しました。GooglePlugin for EclipseのUpdate siteです
http://dl.google.com/eclipse/plugin/3.4
これで最低限の環境構築が完了しましたので、次に実際にプロジェクトを作成します。
プロジェクトの作成
Package Explorerのコンテキストメニューから[New]->[Web Application Project]をクリックしました。表示されたダイアログで、「Project name:gaej-tutorial」、「Package:net.masa.tutorial」と入力します。
また、最下部の「Generate GWT project sample code」のチェックを外して「Finish」ボタンをクリックしました。
プロジェクトが作成されたので、次にWicketが動作するようにします。
Wicketの準備
Wicket用のjarを${project}/war/WEB-INF/lib配下にコピーしました。取り敢えずwicket-quickstartで指定されているjarのみです。
- wicket-1.4.7.jar
- wicket-extensions-1.4.7.jar
- slf4j-api-1.5.8.jar
- slf4j-log4j12-1.5.8.jar
- log4j-1.2.14.jar
追加したjarはコンテキストメニューの[Build Path]->[Add to Build Path]をクリックして、ビルドパスに追加しておきます。
Wicketの動作設定
appengine-web.xmlの設定
Wicketではセッションを利用する必要があるので、appengine-web.xmlでセッションを利用可能にします。以下の記述を追加しました。またapplicationにはgaej-tutorialと追加しました。
<application>gaej-tutorial</application> <!-- 中略 --> <sessions-enabled>true</sessions-enabled>
web.xmlの設定
web.xmlにWicketのフィルター設定を追加しました。内容は以下の通りです。
<?xml version="1.0" encoding="utf-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <filter> <filter-name>wicket.tutorial</filter-name> <filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class> <init-param> <param-name>applicationClassName</param-name> <param-value>net.masa.tutorial.application.TutorialApplication</param-value> </init-param> </filter> <filter-mapping> <filter-name>wicket.tutorial</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
Applicationクラスの作成
web.xmlで指定したWebApplicationクラスを作成します。GAE/JでWicketを動作させる場合、以下のポイントがあるので、その設定を行います。
WicketApplication#newSessionStore()でHttpSessionStoreを返すようにさせる
WicketをDEPLOYMENTモードで動作させる
リソースの更新チェックを停止させる
Wicketのデフォルト設定では、セッションオブジェクトをファイルに保存しますが、GAE/Jではファイルの作成が制限されているのでHttpSessionに保存するように設定します。
次にWicketのdevelopmentモードではThreadを生成してリソースの更新チェックを行いますが、GAE/JではThreadの生成を制限しているのでdeploymentモードで動作させる必要があります。これにより、Threadの生成を停止できます。
上記のポイントを踏まえ、実際のソースは以下の通りとなります。
/** * アプリケーション設定クラスです。 * * @author m-namiki * */ public class TutorialApplication extends WebApplication { // ------------------------------------------------------------- [Constants] /** リクエスト・レスポンスおよびHTMLテンプレートファイルのエンコードです。 */ private static final String CHAR_ENCODE = "UTF-8"; /** HTMLテンプレートファイルのベースディレクトリです。 */ private static final String PAGES_BASE_DIR = "WEB-INF"; /** HTMLテンプレートファイルの本来の階層から除去するパスです。 */ private static final String PAGES_PATH = "net/masa/tutorial"; // -------------------------------------------------------- [Public methods] /** * コンストラクタです。 */ public TutorialApplication() { } /** * 動作モードを設定します。<br> * WicketをGAE/Jで動作させる場合、{@link org.apache.wicket.Application.DEPLOYMENT} * モードで動作させなければなりません。 * * @see org.apache.wicket.protocol.http.WebApplication#getConfigurationType() */ @Override public String getConfigurationType() { return Application.DEPLOYMENT; } /** * 初期画面のページクラスを返却します。 * * @see org.apache.wicket.Application#getHomePage() */ @Override public Class<? extends Page> getHomePage() { return HelloWorldPage.class; } // ----------------------------------------------------- [Protected methods] /** * アプリケーションの初期化処理です。<br> * 文字コードの設定、テンプレートファイルの場所の指定を行います。また、WicketをGAE/Jで動作させる場合、 * リソースの更新チェックは停止させる必要があります。 * * @see org.apache.wicket.protocol.http.WebApplication#init() */ @Override protected void init() { super.init(); // リクエスト・レスポンスの文字コード設定 getRequestCycleSettings().setResponseRequestEncoding(CHAR_ENCODE); // HTMLテンプレートの文字コード設定 getMarkupSettings().setDefaultMarkupEncoding(CHAR_ENCODE); // リソースの更新チェックを停止 getResourceSettings().setResourcePollFrequency(null); // テンプレートファイルの場所を指定 getResourceSettings().addResourceFolder(PAGES_BASE_DIR); getResourceSettings().setResourceStreamLocator( new ResourceStreamLocator() { @SuppressWarnings("unchecked") @Override public IResourceStream locate(Class clazz, String path) { if (path.indexOf(PAGES_PATH, 0) != -1) { IResourceStream located = super.locate(clazz, path .substring(PAGES_PATH.length() + 1)); if (located != null) { return located; } } return super.locate(clazz, path); } }); } /** * セッションストアを作成します。 * * WicketをGAE/Jで動作させる場合、セッション情報を{@link javax.servlet.http.HttpSession} * に格納する必要があります。 */ @Override protected ISessionStore newSessionStore() { return new HttpSessionStore(this); } }
上記init()内でHTMLテンプレートファイルの場所を指定してしました。Wicketは通常の場合ページクラスと同じ階層からHTMLテンプレートファイルを探しますが、PAGES_BASE_DIR(=WEB-INF)から探すようにして、また、PAGES_PATH(=net/masa/tutorial)は、パッケージ名から除去すべきパスを定義しています。上記の設定からWicketはHTMLテンプレートファイルをWEB-INF/pagesから探すようになります。
HelloWorldPageの作成
アプリケーションが最初に呼び出すページはApplication#getHomePage()で指定しますが、TutorialApplicationではHelloWorldPage.classを指定しているので、そのPageクラスを作成します。パッケージはnet.masa.tutorial.pagesです。
/** * HelloWorld画面クラスです。 * * @author m-namiki * */ public class HelloWorldPage extends WebPage { public HelloWorldPage() { add(new Label("message", "Hello, World.")); } }
ここではHTMLテンプレートファイルに表示するメッセージを指定しました。次にこのメッセージを表示するためのHTMLテンプレートファイルを作成します。パスはgaej-tutorial/war/WEB-INF/pages/HelloWorldPage.htmlです。
<html> <head> <title>Hello World</title> </head> <body> <span wicket:id="message">Dummy text.</span> </body> </html>
動作確認
これですべての準備が整ったので、実際に動作させてみます。プロジェクトのコンテキストメニューから[Run As]->[Web Application]をクリックするとサーバが立ち上がります。
…はずですが、以下のようなエラーが表示されて起動に失敗しました。
Missing required argument 'module[s]' Google Web Toolkit 2.3.0 DevMode [-noserver] [-port port-number | "auto"] [-whitelist whitelist-string] [-blacklist blacklist-string] [-logdir directory] [-logLevel level] [-gen dir] [-bindAddress host-name-or-address] [-codeServerPort port-number | "auto"] [-server servletContainerLauncher[:args]] [-startupUrl url] [-war dir] [-deploy dir] [-extra dir] [-workDir dir] module[s]
調べてみたところ、GWTのモジュールが存在しないとダメらしいので、プロジェクトのコンテキストメニューから[New]->[Module]をクリックし、ダイアログでは「Package:net.masa.tutorial.module」、「Module name:GwtModule」と入力して「Finish」ボタンをクリックしました。次に再度プロジェクトのコンテキストメニューから[Run As]->[Run Configurations...]をクリックし、右ペインで[GWT]タブを選択して作成したモジュールを[Add]してから「Run」ボタンをクリックすると今度は無事にサーバが起動しましたので、Webブラウザでhttp://localhost:8888/にアクセスしてみるとHelloWorldPage.javaで指定したメッセージが画面に表示されました。
一応GAE/J上でWicketの動作を確認したので、次回はSlim3との連携を試してみようと思います。
デプロイエラーで参考にしたのはこちらのページです。
http://d.hatena.ne.jp/reppets/20110403/1301788939