0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

sbtのGAEプロジェクトをMavenに移行する

Last updated at Posted at 2020-08-04

経緯

GAEのスタンドアロンなSDKが非推奨になり、Google Cloud SDKへの移行が必須になりました。

2019 年 7 月 30 日以降、スタンドアロンの App Engine SDK は非推奨になりました。2020 年 8 月 30 日以降は、ダウンロードできなくなります。

参考: https://cloud.google.com/appengine/docs/standard/java/sdk-gcloud-migration?hl=ja

sbtで使っていたGAE用のプラグインsbt-appengineがスタンドアロンSDKに依存しているのでGoogleさんが標準で用意しているMavenに移行してみた時の記録。

XMLからYAMLへのファイル形式の移行

Google Cloud SDKではqueue.xmlなどの設定系ファイルのXML形式が非サポートになり、queue.yamlなどYAML形式に変換しないとダメな様です。

gcloudコマンドで変換ができるのでこれを使います。
https://cloud.google.com/appengine/docs/standard/java/sdk-gcloud-migration?hl=ja#migrating_xml_to_yaml_file_formats

gcloud beta app migrate-config queue-xml-to-yaml src/main/webapp/WEB-INF/queue.xml 
gcloud beta app migrate-config datastore-indexes-xml-to-yaml src/main/webapp/WEB-INF/datastore-indexes.xml 
gcloud beta app migrate-config cron-xml-to-yaml src/main/webapp/WEB-INF/cron.xml 

など。

テストでqueue.xmlを読んでる場合はここもsetQueueXmlPathからsetQueueYamlPathに変更する必要があります。

(1.9.80以降で追加されたメソッドの様です。参考: https://stackoverflow.com/questions/59406695/appengine-unit-test-non-default-queues-in-queue-yaml

-      new LocalTaskQueueTestConfig().setQueueXmlPath("src/main/webapp/WEB-INF/queue.xml")
+      new LocalTaskQueueTestConfig().setQueueYamlPath("src/main/webapp/WEB-INF/queue.yaml")

build.sbtからpom.xmlへの移行

pom.xmlのテンプレを生成する

Mavenのarchetypeが用意されているので公式ドキュメントに従ってMavenプロジェクトを生成します。


mvn archetype:generate -Dappengine-version=1.9.81 -Dfilter=com.google.appengine.archetypes:

すると出てくる 2: remote -> com.google.appengine.archetypes:appengine-skeleton-archetype (A skeleton application with Google App Engine) を選びます。

※ドキュメント記載のappengine-versionが古かった(1.9.59)ので現時点での最新(1.9.81)を指定しました

artifactIdで指定したサブディレクトリにpom.xmlが生成されるのでプロジェクトのルート(build.sbtと同じところ)にコピーします。

Scala Maven Pluginを追加

https://docs.scala-lang.org/tutorials/scala-with-maven.html を参考に、
src/main/scala以下の*.scalaファイルをコンパイルする様に<plugins>以下に追加します。

      <plugin>
        <groupId>net.alchim31.maven</groupId>
        <artifactId>scala-maven-plugin</artifactId>
        <version>4.4.0</version>
        <configuration>
          <recompileMode>incremental</recompileMode>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>
              <goal>testCompile</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

<properties>以下に使用するScalaのバージョンを指定できる様にしておいて、

    <scala.version>2.10.4</scala.version>

該当バージョンのscala-libraryを<dependencies>以下に追加します。

    <dependency>
      <groupId>org.scala-lang</groupId>
      <artifactId>scala-library</artifactId>
      <version>${scala.version}</version>
    </dependency>

build.sbtからdependencyの移動

続けてbuild.sbtで設定していたライブラリ依存関係の定義をpom.xmlに移していきます。

sbt makePom

するとtargetディレクトリ以下にpom.xmlに変換してくれるので、そこから必要なdependencyをコピペするのが楽です。

例えば

libraryDependencies += "org.json4s" %% "json4s-jackson" % "3.1.0"

はこんな感じ↓になります。

    <dependency>
        <groupId>org.json4s</groupId>
        <artifactId>json4s-jackson_2.10</artifactId>
        <version>3.1.0</version>
    </dependency>

Twirlのコンパイルができる様にする

テンプレートエンジンとしてTwirlを使っている場合はTwirl Maven Pluginを追加する必要があります。

<plugins>以下に↓を追加します。

      <plugin>
        <groupId>com.jakewharton.twirl</groupId>
        <artifactId>twirl-maven-plugin</artifactId>
        <version>1.2.0</version>
        <executions>
          <execution>
            <phase>generate-sources</phase>
            <goals>
              <goal>compile</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

標準のコンパイラプラグインをスキップする

Scala Maven Pluginが*.javaファイルもコンパイルしてくれるので、標準のMaven Compiler Pluginの方は<configuration><skipMain>true</skipMain></configuration>を追加してコンパイルをスキップさせます。


      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.6.1</version>
        <configuration>
          <skipMain>true</skipMain>
        </configuration>
      </plugin>

Specsのテストが実行される様にする

※めっちゃ古いプロジェクトなので未だにSpecs(Specs2ではなく)のテストが残っていました

若干ソースコードの修正も必要です。とは言ってもextendするのをorg.specs.Specificationからorg.specs.SpecificationWithJUnitに変更するだけです。
(あと、objectではなくclassにする必要があります)

参考: https://code.google.com/archive/p/specs/wikis/RunningSpecs.wiki#Run_your_specifications_with_JUnit4_and_Maven


-import org.specs.Specification
+import org.specs.SpecificationWithJUnit

-object ExampleSpec extends Specification {
+class ExampleSpec extends SpecificationWithJUnit {

Surefire Pluginの<configuration>を↓の様に足してSpecsのテストが実行される様にします。

      <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.20</version>
        <configuration>
          <useSystemClassLoader>false</useSystemClassLoader>
          <argLine>-Xmx512m</argLine>
          <includes>
            <include>**/*Spec.java</include>
          </includes>
        </configuration>
      </plugin>

MavenでGAE開発サーバーを起動する

sbt appengineDevServer

に該当するのはMavenでは

mvn appengine:run

になります。

また、

sbt console

mvn scala:console

になります。

応用編

hot reloadingに対応する

sbt-appengineの場合は

sbt ~appengineDevServer

でソースコードの変更を検出して自動でGAEの開発サーバーを再起動してくれましたがMavenのPluginではやってくれません。
https://github.com/fizzed/maven-plugins に含まれるWatcher Pluginを使うとソースコードの変更に応じてMavenの任意のゴールを実行してくれるのでこれを使います。

変更を監視したいディレクトリを<watches>以下で設定し、実行したいゴールを<goals>以下で指定します。

ゴールは複数書けるので、Twirlで*.scalaを生成してからコンパイル、は↓みたいに設定できます。

      <plugin>
          <groupId>com.fizzed</groupId>
          <artifactId>fizzed-watcher-maven-plugin</artifactId>
          <version>1.0.6</version>
          <configuration>
              <touchFile>${project.build.directory}/${project.build.finalName}/WEB-INF/appengine-web.xml</touchFile>
              <watches>
                  <watch>
                      <!-- *.java混在してない場合はここ不要 -->
                      <directory>${project.basedir}/src/main/java</directory>
                  </watch>
                  <watch>
                      <directory>${project.basedir}/src/main/scala</directory>
                  </watch>
                  <watch>
                      <directory>${project.basedir}/src/main/twirl</directory>
                  </watch>
              </watches>
              <goals>
                  <goal>twirl:compile</goal>
                  <goal>scala:compile</goal>
              </goals>
          </configuration>
      </plugin>

また、GAEの開発サーバーは、appengine-web.xmlの変更を検出して再起動してくれるので<touchFile>appengine-web.xmlのタイムスタンプを書き換える様にします。

参考: https://cloud.google.com/appengine/docs/standard/java/maven-reference?hl=ja#appenginerun

サーバーの稼働中は、appengine-web.xml が変更されていないかどうか継続的に確認します。変更されている場合、サーバーがアプリケーションを再度読み込みます

以上の設定をして、別プロセスで

mvn fizzed-watcher:run

を起動しておくとhot reloadingぽいことが実現できます。

Dockerを使用する場合

例:

docker run --rm -ti \
  -v $HOME/.m2:/root/.m2 -v $HOME/.sbt:/root/.sbt \
  -v `pwd`:/app -p 8080:8080 $YOUR_IMAGE \
  mvn package appengine:run -DcloudSdkHome=/root/google-cloud-sdk

Mavenレポジトリのキャッシュ

-v $HOME/.m2:/root/.m2
dependencyのjarたちを落としてくるのが非常に遅いので、ホストのMavenレポジトリキャッシュのディレクトリをコンテナにマウントするとビルド時間を節約できます。

~/.sbtのキャッシュ

-v $HOME/.sbt:/root/.sbt
sbtは使っていませんが、Scala Maven Pluginで使われている https://github.com/sbt/zinc~/.sbt/以下のキャッシュを使う様なのでここもマウントしています。

インストール済みCloud SDKを使う

-DcloudSdkHome=/root/google-cloud-sdk
何も指定しないとGoogle Cloud SDKが既にインストール済みでもPluginが別途ダウンロードしてしまいます。

Dockerのイメージに入れておけば、cloudSdkHomeでインストール済みのCloud SDKのパスを指定してやるとそっちを使ってくれるのでだいぶ時間を短縮できます。

参考: https://github.com/GoogleCloudPlatform/app-maven-plugin/blob/master/USER_GUIDE.md#cloud-sdk-configuration

これはDockerに限った話では無いので、-DcloudSdkHome=$(dirname $(dirname $(which gcloud)))とかしておくとローカル環境でもインストール済みCloud SDKを使ってくれて便利です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?