Maven から Gradle への移行

既存の Maven プロジェクトを Gradle に移行してみる。

対象のプロジェクトは shibainu。SpringMVC と spring-data、QueryDSL を使った Web アプリケーションのテンプレートプロジェクト。このような pom.xml を使っている。

Maven でやっているのは主に下記の処理。

  • ビルド (依存ライブラリの解決、コンパイル、war の作成)
  • Maven プラグイン
    • querydsl-maven-plugin (QueryDSL のメタデータエクスポート)
    • flyway-maven-plugin (Flyway の DB マイグレーション)
    • jetty-maven-plugin (Jetty の起動)

これらを Gradle に移行する。

ビルド (依存ライブラリの解決、コンパイル、war の作成) の移行

War プラグインを apply したら後は Maven とだいたい同じ。ずっと簡潔に書ける。

apply plugin: 'java'
apply plugin: 'war'

group = 'net.physalis'
version = '0.1-SNAPSHOT'

sourceCompatibility = 1.7
targetCompatibility = 1.7

repositories {
  mavenCentral()
  maven {
    url 'http://repo.springsource.org/release'
  }
}

dependencies {
  ['core', 'context', 'web', 'webmvc', 'context-support', 'jdbc', 'tx'].each { name ->
    compile "org.springframework:spring-$name:3.2.2.RELEASE"
  }

  compile 'org.springframework.data:spring-data-jdbc-core:1.0.0.RELEASE'
  compile 'org.springframework.data:spring-data-redis:1.0.3.RELEASE'

  // 以下略
}

maven プラグインの移行

Maven プラグインを移行するには、Gradle プラグインがあればそれ、なければ Ant タスクを使うのが簡単。

querydsl-maven-plugin の移行

このプラグインは DB に接続して、クエリ用クラスを生成する。残念ながら Gradle プラグインがないので Ant タスクを使う。

直接 Ant を使うときと同様に taskdef してからタスクを実行する。Ant タスクのマニュアルを元に build.xml での書き方から Gradle のそれに変換するのは比較的容易。XML を書かなくて良い分気持ち良く書ける。

task generateQuerydslSource {
  ext.destDir = new File('src/main/generated/querydsl')

  doLast {
    destDir.mkdirs()
    ant.taskdef(
        name: 'antMetaDataExporter',
        classname: 'com.mysema.query.sql.ant.AntMetaDataExporter',
        classpath: configurations.querydsl.asPath,
    )
    ant.antMetaDataExporter(
        dbUrl: 'jdbc:postgresql://localhost/shibainu',
        dbUserName: 'shibainu',
        jdbcDriverClass: 'org.postgresql.Driver',
        schemaPattern: 'shibainu',
        targetPackage: 'net.physalis.shibainu.domain.model',
        targetSourceFolder: destDir,
    )
  }
}

// 生成した Java ファイルを compileJava の対象にする
compileJava {
  source generateQuerydslSource.destDir
}

flyway-maven-plugin の移行

こちらも Gradle プラグインがないので Ant タスクを使う。

task flywayMigrate {
  doLast {
    ant.taskdef(
      resource: 'com/googlecode/flyway/ant/antlib.xml',
      classpath: configurations.flyway.asPath + ':build/resources/main'
    )
    ant.migrate(
      url: dbUrl,
      user: dbUser,
    )
  }
}

jetty-maven-plugin の移行

Gradle の Jetty プラグインは Jetty6 を使う。このプロジェクトでは Servlet3.0 に対応している Jetty8 以上が必要であるため、他の方法を試した。(jetty-maven-plugin は Jetty8 以上に対応)

まずはじめに JettyRunAnt タスクへの移行を試みた。通常は GRADLE-1956 に書かれている簡単な方法でうまくいくようだ。ただこのプロジェクトでは DataSource を Jetty 側で作成して JNDI に登録するところで InitialContextFactory が ClassNotFoundException になってしまう問題が発生してうまくいかなかった。

次に最終手段として (はやっ) pom.xml を生成して jetty-maven-plugin を使うことにした。つまり、結局 Maven を使う。

このような pom タスクを用意する。

task pom << {
  pom {
    project {
      build {
        plugins {
          plugin {
            artifactId 'maven-resources-plugin'
            version '2.5'
            configuration {
              encoding 'UTF-8'
            }
            executions {
              ['default-resources', 'default-testResources',
               'process-resources', 'process-test-resources'].each { x ->
                execution {
                  id x
                  phase x
                }
              }
            }
          }
          plugin {
            artifactId 'maven-compiler-plugin'
            version '3.1'
            configuration {
              source '1.7'
              target '1.7'
            }
            executions {
              ['compile', 'test-compile', 'default-testCompile',
               'default-compile', 'process-resources'].each { x ->
                execution {
                  id x
                  phase x
                }
              }
            }
          }
          plugin {
            groupId 'org.mortbay.jetty'
            artifactId 'jetty-maven-plugin'
            version '8.1.9.v20130131'
            configuration {
              webApp {
                webInfIncludeJarPattern '^spring-web.*\\.jar$'
                extraClasspath 'build/resources/main'
                tempDirectory 'build/tmp'
              }
              classesDirectory 'build/classes/main'
              jettyXml 'src/main/jetty/jetty.xml'
              stopKey 'stop'
              stopPort '8091'
              connectors {
                connector(implementation: 'org.eclipse.jetty.server.bio.SocketConnector') {
                  port '8080'
                }
              }
            }
            dependencies {
              dependency {
                groupId 'postgresql'
                artifactId 'postgresql'
                version postgresqlJdbcDriverVersion
              }
            }
          }
        }
      }
    }
  }.writeTo("pom.xml")
}

ここでは

  • Jetty が Gradle のビルドディレクトリを見るようにした
  • Gradle でコンパイルするので、maven-compiler-plugin と maven-resources-plugin が動作しないようにした

Jetty を起動するには初回だけ gradle pom して、あとは次のコマンドを実行する。

% gradle build && mvn jetty:run

まとめ

  • 既存の Maven プロジェクトを Gradle に移行することができた
  • Gradle の方が簡潔に書けてわかりやすい
  • Meven プラグインを移行する場合は対応する Gradle プラグイン、Ant プラグインを検討し、他に方法がなければ pom.xml を生成して Maven で実行することもできる
  • pom.xml を生成するのは無理矢理感あるけど、それでも Gradle を使えるメリットがあると思う
  • それなりに時間がかかるので既存プロジェクトを移行するかは微妙だが新規プロジェクトは Gradle にしたい

こんな build.gradle になりました。