お気に入り | 日本語 | ログイン

Apache Ant の使用

Eclipse および Google Plugin for Eclipse を使用していない場合は、App Engine アプリケーションのビルドやテストのプロセスを何らかの方法で管理する必要があります。Apache Ant を使用すると、コマンド ラインや他の Ant 対応総合開発環境を使用してプロジェクトを簡単に管理できます。Java SDK には、一般的な App Engine 開発タスクを実行するさまざまな Ant マクロが含まれており、これらを使用して開発サーバーを起動したり、アプリケーションを App Engine にアップロードしたりできます。

この記事では、ほとんどのプロジェクトで使用できる Ant ビルド ファイルを作成しながら詳しく説明します。完成したファイルを今すぐコピーして使用したい場合は、完成したビルド ファイルをご覧ください。

Ant のインストール

お使いのシステムに Ant がインストールされていない場合は、Apache Ant Web サイトからダウンロードしてインストールしてください。

Ant がインストールされており、コマンド パスに ant コマンドが含まれている場合は、次のコマンドを実行することで、Ant が機能すること、およびインストールされている Ant のバージョンを確認できます:

ant -version

プロジェクト ディレクトリ

スタート ガイドでも説明しましたが、App Engine プロジェクトを作成する際には、Java Web アプリケーションの WAR 標準レイアウトに従ってディレクトリ構造を構成する必要があります(WAR アーカイブ ファイルは、App Engine SDK ではまだサポートされていません)。

ここでは、プロジェクト関連のすべてのファイルを、Guestbook/ という 1 つのディレクトリ(スタート ガイドで説明したゲストブック プロジェクトのディレクトリ)に格納します。また、Java ソース コードはそのサブディレクトリ src/ に、完成したアプリケーション ファイルはサブディレクトリ war/ に格納します。ビルド プロセスでは、ソース コードをコンパイルし、コンパイル済みの Java バイト コードを war/ 内の適切な場所に格納します。WAR のその他のファイルは、war/ ディレクトリに直接作成します。

完成後のプロジェクト ディレクトリは次のようになります:

Guestbook/
  src/
    ...Java source code...
    META-INF/
      ...other configuration...
  war/
    ...JSPs, images, data files...
    WEB-INF/
      ...app configuration...
      classes/
        ...compiled classes...
      lib/
        ...JARs for libraries...

このディレクトリ構造をコマンド プロンプトから作成するには、次のコマンドを実行します。Windows の場合は、パス内のスラッシュ / をバックスラッシュ \ に変更してください(Ant ファイル内で使用されているスラッシュ / は、すべてのオペレーティングシステムで機能します)。

mkdir Guestbook
cd Guestbook
mkdir -p src/META-INF
mkdir -p war/WEB-INF/classes
mkdir -p war/WEB-INF/lib

ビルド ファイルの作成

Guestbook/ ディレクトリ内に build.xml という名前のファイルを作成し、そのコンテンツを次のようにします:

<project>
  <property name="sdk.dir" location="../appengine-java-sdk" />

  <import file="${sdk.dir}/config/user/ant-macros.xml" />

</project>

このビルド ファイルにはタスク実行の指示を記述した「ターゲット」が含まれていないため、実行してもまだ何も行われません。このファイルには、後でターゲットやパスの定義に使用する 2 つの Ant プロパティが定義されています。

sdk.dir プロパティには、App Engine Java SDK へのパス(SDK の zip ファイルを解凍したときに作成された appengine-java-sdk というディレクトリ)を指定します。"../appengine-java-sdk" という記述から、このディレクトリがプロジェクト ディレクトリの親ディレクトリを想定していることがわかります。この値は、必要に応じて変更してください(build.xml ファイルからの相対パスで指定します)。

<import> 要素により、App Engine Java SDK に含まれている App Engine 開発用の一連の Ant マクロが読み込まれます。これらのマクロは後ほど使用します。

クラス パスの定義

作成したクラスをコンパイルするためには、App Engine API の JAR や、プロジェクトの war/WEB-INF/lib/ ディレクトリに追加したその他の JAR(たとえば DataNucleus JDO/JPA JAR)が、Java クラス パスに含まれている必要があります。アプリケーションは、このディレクトリに追加されている JAR からクラスにアクセスできます。また、SDK の lib/shared/ ディレクトリ内の JAR も Java クラス パスに含めます。

これらの JAR を含むクラス パスを定義するには、build.xml の終了タグ </project> の上に次のコード行を挿入します:

  <path id="project.classpath">
    <pathelement path="war/WEB-INF/classes" />
    <fileset dir="war/WEB-INF/lib">
      <include name="**/*.jar" />
    </fileset>
    <fileset dir="${sdk.dir}/lib">
      <include name="shared/**/*.jar" />
    </fileset>
  </path>

JAR のコピー

SDK に含まれている JAR の 1 つ appengine-api-*.jar* は API および SDK のバージョン番号)を、アプリケーションの war/WEB-INF/lib/ ディレクトリに含める必要があります。App Engine では、この JAR のクラスをチェックして、アプリケーションでどのバージョンの API が使用されているかを識別します。アプリケーションにおいて、App Engine データストアへのインターフェースとして JDO または JPA を使用している場合は、そのアプリケーションの war/WEB-INF/lib/ に JDO/JPA 実装を含める必要があります。これらの JAR はすべて、SDK の lib/user/ ディレクトリに格納されています。

これらの JAR は、手動で WAR にコピーすることもできますが、ビルド プロセスの一部としてコピーした方が便利です。この方法なら、後日 SDK をアップグレードした場合でも、SDK と同じバージョンのインターフェースを確実に使用できます。

すべての JAR は、サブディレクトリではなく war/WEB-INF/lib/ ディレクトリ自体に格納する必要があります。下に示す Ant ターゲットでは JAR を ${sdk.dir}/lib/user/ から繰り返しコピーしていますが、copy タスクの flatten="true" 属性を使用しているため、すべての JAR は同じディレクトリに格納されます。

build.xml で、次のコード行を終了タグ </project> の上に追加します:

  <target name="copyjars"
      description="Copies the App Engine JARs to the WAR.">
    <copy
        todir="war/WEB-INF/lib"
        flatten="true">
      <fileset dir="${sdk.dir}/lib/user">
        <include name="**/*.jar" />
      </fileset>
    </copy>
  </target>

これにより、JAR を適切な場所にコピーする "copyjars" という名前のビルド ターゲットが作成されます。このターゲットをテストするには、現在の作業ディレクトリがプロジェクト ディレクトリ(Guestbook/)であることを確認し、次のコマンドを実行します:

ant copyjars

war/WEB-INF/lib/ に必要な JAR がすべて含まれていることを確認します。

ソース ファイルのコンパイル

build.xml で、次のコード行を終了タグ </project> の上に追加します:

  <target name="compile" depends="copyjars"
      description="Compiles Java source and copies other source files to the WAR.">
    <mkdir dir="war/WEB-INF/classes" />
    <copy todir="war/WEB-INF/classes">
      <fileset dir="src">
        <exclude name="**/*.java" />
      </fileset>
    </copy>
    <javac
        srcdir="src"
        destdir="war/WEB-INF/classes"
        classpathref="project.classpath"
        debug="on" />
  </target>

このコードでは、"compile" という名前のビルド ターゲットを定義しています。このビルド ターゲットは、src/ ディレクトリ内で見つかったすべての Java ソース ファイルをコンパイルし、コンパイル済みのクラスを war/WEB-INF/classes/ に格納します。src/ 内で見つかったその他のファイル(たとえば META-INF/ ディレクトリ)は、順次 war/WEB-INF/classes/ にコピーされます。このターゲットは "copyjars" に依存しているため、必要に応じて両方のターゲットがビルドされます。

プロジェクトのソース ファイルをコンパイルするには、次のコマンドを実行します:

ant compile

JDO クラスの拡張

このセクションでは、データストアへのインターフェースとして JDO(Java Data Objects)をサポートするプロジェクトを、Ant ファイルを使用してビルドする方法について説明します。プロジェクトで JDO インターフェースを使用しない場合は、次のセクションに進んでください。

スタート ガイドおよびデータストアに関するドキュメントでも説明したように、JDO インターフェースでは、作成したクラスのインスタンスをデータストアに格納しておき、後でオブジェクトとして取得できます。インスタンスを格納する方法と取得する方法は、クラス定義内で Java アノテーションを使用して記述します。

JDO でそれらのアノテーションを認識するためには、DataNucleus Access Platform(App Engine に付属の JDO 実装)に含まれているツールを使用してアノテーションをコンパイルした後で、データ クラスのバイトコードを処理する必要があります。DataNucleus では、このプロセスを「拡張」と呼んでいます。

App Engine SDK には、JDO データ クラスを拡張するための Ant マクロが用意されています。<enhance_war> マクロは、プロジェクト内のすべての JDO データ クラスを、そのプロジェクトの適切なクラス パスを使用して拡張します。よりきめ細かく拡張したい場合は、<enhance> マクロを使用します。

build.xml で、次のコード行を終了タグ </project> の上に追加します:

  <target name="datanucleusenhance" depends="compile"
      description="Performs JDO enhancement on compiled data classes.">
    <enhance_war war="war" />
  </target>

このターゲットは "compile" に依存しています。したがって、このターゲットをビルドすると、必ずクラスをコンパイルして最新の状態にしてから拡張タスクが実行されます。

開発サーバーの実行

開発用の Web サーバーも、Ant マクロを使用して実行できます。このターゲットを "datanucleusenhance" ターゲット(DataNucleus を使用していない場合は "compile" ターゲット)に依存させることにより、1 つの簡単なコマンドでプロジェクトをビルドしてサーバーを起動できます。

build.xml で、次のコード行を終了タグ </project> の上に追加します:

  <target name="runserver" depends="datanucleusenhance"
      description="Starts the development server.">
    <dev_appserver war="war" />
  </target>

開発サーバーの引数は、属性と <options> 要素を使用して渡します。たとえば次のターゲットでは、ポート 8888 でサーバーを起動し、ポート 9999 でリモート Java デバッグを有効にしています:

  <target name="runserver" depends="datanucleusenhance"
      description="Starts the development server.">
    <dev_appserver war="war" port="8888" >
      <options>
        <arg value="--jvm_flag=-Xdebug"/>
        <arg value="--jvm_flag=-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=9999"/>
      </options>
    </dev_appserver>
  </target>

プロジェクトをビルドしてサーバーを実行するには、次のコマンドを使用します:

ant runserver

サーバーを停止するには、Ctrl+C を押します。

アップロードとその他の AppCfg タスク

アプリケーションを App Engine にアップロードする Ant タスクや、AppCfg によって提供されるその他の操作を実行する Ant タスクも定義できます。

<appcfg> マクロは、action 属性として操作の名前、war 属性としてプロジェクトの WAR までのパスを取ります。<options> 要素と <args> 要素は省略可能です。

build.xml に次のコード行を追加します:

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

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

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

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

完成したビルド ファイル

次に、完成した build.xml ファイルを示します:

<project>
  <property name="sdk.dir" location="../appengine-java-sdk" />

  <import file="${sdk.dir}/config/user/ant-macros.xml" />

  <path id="project.classpath">
    <pathelement path="war/WEB-INF/classes" />
    <fileset dir="war/WEB-INF/lib">
      <include name="**/*.jar" />
    </fileset>
    <fileset dir="${sdk.dir}/lib">
      <include name="shared/**/*.jar" />
    </fileset>
  </path>

  <target name="copyjars"
      description="Copies the App Engine JARs to the WAR.">
    <copy
        todir="war/WEB-INF/lib"
        flatten="true">
      <fileset dir="${sdk.dir}/lib/user">
        <include name="**/*.jar" />
      </fileset>
    </copy>
  </target>

  <target name="compile" depends="copyjars"
      description="Compiles Java source and copies other source files to the WAR.">
    <mkdir dir="war/WEB-INF/classes" />
    <copy todir="war/WEB-INF/classes">
      <fileset dir="src">
        <exclude name="**/*.java" />
      </fileset>
    </copy>
    <javac
        srcdir="src"
        destdir="war/WEB-INF/classes"
        classpathref="project.classpath"
        debug="on" />
  </target>

  <target name="datanucleusenhance" depends="compile"
      description="Performs JDO enhancement on compiled data classes.">
    <enhance_war war="war" />
  </target>

  <target name="runserver" depends="datanucleusenhance"
      description="Starts the development server.">
    <dev_appserver war="war" />
  </target>

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

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

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

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

</project>