Wicket + Maven なアプリを Google App Engine で動かす その2

 昨日のエントリがあまりにも「動かすだけ」だったので、もうちょっとまともなアプローチを模索してみました。ポイントは

  • Maven2 に GAE/J SDK 関連の依存性を追加
  • ロガーは java.util.logger を使いましょう
  • ローカルで動かそう
  • デプロイしよう

 Wicket のプログラム自体は昨日と同じです。将来的に GAE/J で JDO を使用することを睨んでいます。

Maven2 に GAE/J SDK 関連の依存性を追加

 Wicket 使いの方の中には Maven を使っている方が多いんじゃないかという思い込みのある私です。私も Maven 愛好者です。ということで、GAE/J に関するパッケージも Maven 管理したいわけです。セントラルリポジトリに該当のパッケージは登録されていないのですが Maven Archetype for GAE/J project で参照している mvnsearch リポジトリから持ってくることができます。ただし、このリポジトリはすごく遅い上にチェックサムが登録されていないのでたまにパッケージが壊れます。

 また、JDO のエンハンスも Maven でやってみたいと考えましたので、404 shin1のつぶやき ないわー Not Found: mvnsearch上のGAE/J関連の1.2.0から1.2.1への移行での変更 の記事をパクって参考にして、Wicket on GAE/J 用の pom.xml を作りました。グループ ID やアーティファクト ID は適宜修正してください。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example.hoge</groupId>
    <artifactId>hoge</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <!-- TODO project name  -->
    <name>quickstart</name>
    <description></description>
    <!--
        TODO <organization> <name>company name</name> <url>company url</url>
        </organization>
    -->
    <licenses>
        <license>
            <name>The Apache Software License, Version 2.0</name>
            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
            <distribution>repo</distribution>
        </license>
    </licenses>
    <dependencies>
        <!--  WICKET DEPENDENCIES -->
        <dependency>
            <groupId>org.apache.wicket</groupId>
            <artifactId>wicket</artifactId>
            <version>${wicket.version}</version>
        </dependency>
        <!--
            OPTIONAL <dependency> <groupId>org.apache.wicket</groupId>
            <artifactId>wicket-extensions</artifactId>
            <version>${wicket.version}</version> </dependency>
        -->

        <!-- LOGGING DEPENDENCIES -JDK1.4 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-jdk14</artifactId>
            <version>1.4.2</version>
        </dependency>

        <!--  JUNIT DEPENDENCY FOR TESTING -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.2</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
        
        <!-- GAE for java(compile) -->
        <dependency>
            <groupId>com.google.appengine</groupId>
            <artifactId>appengine-api-1.0-sdk</artifactId>
            <version>1.2.1</version>
        </dependency>
        <!-- GAE for java(test) -->
        <dependency>
            <groupId>com.google.appengine</groupId>
            <artifactId>appengine-api-1.0-stubs</artifactId>
            <version>1.2.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.google.appengine</groupId>
            <artifactId>appengine-api-1.0-runtime</artifactId>
            <version>1.2.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.google.appengine</groupId>
            <artifactId>appengine-tools-sdk</artifactId>
            <version>1.2.1</version>
            <scope>test</scope>
        </dependency>
        <!-- GAE for java(runtime) -->
        <dependency>
            <groupId>com.google.appengine.orm</groupId>
            <artifactId>datanucleus-appengine</artifactId>
            <version>1.0.1</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.datanucleus</groupId>
            <artifactId>datanucleus-core</artifactId>
            <version>1.1.0</version>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
    <build>
        <finalName>${project.artifactId}</finalName>

        <resources>
            <resource>
                <filtering>false</filtering>
                <directory>src/main/resources</directory>
            </resource>
            <resource>
                <filtering>false</filtering>
                <directory>src/main/java</directory>
                <includes>
                    <include>**</include>
                </includes>
                <excludes>
                    <exclude>**/*.java</exclude>
                </excludes>
            </resource>
        </resources>
        <testResources>
            <testResource>
                <filtering>false</filtering>
                <directory>src/test/java</directory>
                <includes>
                    <include>**</include>
                </includes>
                <excludes>
                    <exclude>**/*.java</exclude>
                </excludes>
            </testResource>
        </testResources>
        <plugins>
            <plugin>
                <inherited>true</inherited>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                    <optimize>true</optimize>
                    <debug>true</debug>
                </configuration>
            </plugin>
            <plugin>
                <!--
                    http://www.datanucleus.org/products/accessplatform/enhancer.html#maven2
                -->
                <groupId>org.datanucleus</groupId>
                <artifactId>maven-datanucleus-plugin</artifactId>
                <version>1.1.0</version>
                <configuration>
                    <verbose>true</verbose>
                    <mappingIncludes>**/entity/*.class</mappingIncludes>
                    <fork>false</fork>
                    <enhancerName>ASM</enhancerName>
                    <api>JDO</api>
                </configuration>
                <executions>
                    <execution>
                        <phase>compile</phase>
                        <goals>
                            <goal>enhance</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-eclipse-plugin</artifactId>
                <configuration>
                    <downloadSources>true</downloadSources>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <properties>
        <wicket.version>1.4-rc4</wicket.version>
    </properties>
    <repositories>
        <repository>
            <id>DataNucleus</id>
            <url>http://www.datanucleus.org/downloads/maven</url>
        </repository>
        <repository>
            <id>DataNucleus2</id>
            <url>http://www.datanucleus.org/downloads/maven2</url>
        </repository>
        <repository>
            <id>mvnsearch</id>
            <url>http://www.mvnsearch.org/maven2</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>DataNucleus</id>
            <url>http://www.datanucleus.org/downloads/maven2</url>
        </pluginRepository>
    </pluginRepositories>
</project>

ロガーは java.util.logger を使いましょう

 GAE/J では java.util.logger(jul)を推奨しているようなので、前述の pom.xml でも、Wicket のクィックスタートでは SLF4J - Log4J が指定されるところを SLF4J - JDK14 を指定しています。

 この jul のための設定ファイルを src/main/resoucrces/logging.properties として作成しましょう。

# ルート
.level = WARNING

# Wicket
org.apache.wicket.level=WARNING

# O/R マッパ
DataNucleus.JDO.level=WARNING
DataNucleus.Persistence.level=WARNING
DataNucleus.Cache.level=WARNING
DataNucleus.MetaData.level=WARNING
DataNucleus.General.level=WARNING
DataNucleus.Utility.level=WARNING
DataNucleus.Transaction.level=WARNING
DataNucleus.Datastore.level=WARNING
DataNucleus.ClassLoading.level=WARNING
DataNucleus.Plugin.level=WARNING
DataNucleus.ValueGeneration.level=WARNING
DataNucleus.Enhancer.level=WARNING
DataNucleus.SchemaTool.level=WARNING

 さらに jul にこのファイルを読ませるように appengine-web.xml で指定します。

<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
    <!-- アプリケーション ID -->
    <application>hoge</application>
    <!-- バージョン -->
    <version>1</version>
	
    <!-- システムプロパティの設定 -->
    <system-properties>
        <!-- Wicket を deployment モードで動かす -->
        <property name="wicket.configuration" value="deployment"/>
        <!-- JDK 1.4 ロガーの設定ファイルのありか -->
        <property name="java.util.logging.config.file" value="WEB-INF/classes/logging.properties"/>
    </system-properties>

    <!-- セッションを使用する -->
    <sessions-enabled>true</sessions-enabled>
</appengine-web-app>

ローカルで動かそう

 Maven Archetype for GAE/J project の ant タスクを利用します。プロジェクトルートに build.xml として以下のファイルを作成します。2行目の location には GAE/J SDK のありかを指定します。また、4行目の valueアーティファクト ID によって変わりますので適宜修正してください。

<project name="hoge" default="datanucleusenhance">
    <property name="appengine.sdk.dir" location="/path/to/appengine-java-sdk" />
    <import file="${appengine.sdk.dir}/config/user/ant-macros.xml" />
    <property name="war.dir" value="target/hoge"/>

    <target name="datanucleusenhance" description="Performs JDO enhancement on compiled data classes.">
        <enhance_war war="${war.dir}"/>
    </target>

    <target name="runserver" depends="datanucleusenhance" description="Starts the development server.">
        <!--many developer's workstation using port 8080, change the default port -->
        <dev_appserver war="${war.dir}" port="9000"/>
    </target>

    <target name="update" depends="datanucleusenhance" description="Uploads the application to App Engine.">
        <appcfg action="update" war="${war.dir}"/>
    </target>

    <target name="update_indexes" depends="datanucleusenhance"
            description="Uploads just the datastore index configuration to App Engine.">
        <appcfg action="update_indexes" war="${war.dir}"/>
    </target>

    <target name="rollback" depends="datanucleusenhance"
            description="Rolls back an interrupted application update.">
        <appcfg action="rollback" war="${war.dir}"/>
    </target>

    <target name="request_logs"
            description="Downloads log data from App Engine for the application.">
        <appcfg action="request_logs" war="${war.dir}">
            <options>
                <arg value="--num_days=5"/>
            </options>
            <args>
                <arg value="logs.txt"/>
            </args>
        </appcfg>
    </target>
</project>

 するとコマンドライン

mvn package

コンパイル、パッケージ化し、

ant runserver

でテスト用サーバが起動します。 http://localhost:9000/ で動作を確認してください。

デプロイしよう

 上記の ant タスクを作っているのならばデプロイも簡単です。プロジェクトルートで以下のようにコマンドを実行します。

ant update