Travis CIでOracle JDK 9 EAを使う方法

今回は、2017/07/27にGA予定のJDK 9をTravis CI上で使う方法を簡単に紹介します。

# Unstable環境でのお試し版

JDK 9(Oracle JDK 9 Early Access)のサポートについては、[travis-ci#gh-5220](https://github.com/travis-ci/travis-ci/issues/5520)で対応が行われており、去年の1/28にはUnstable環境上での利用が可能になっていたようです。（今日まで知らなかった・・・:sweat_smile:）

# どうすれば使えるの？

`.travis.yml`の設定を以下のようにするだけです。

```yaml:.travis.yml
language: java

sudo: required
dist: trusty

jdk:
  - oraclejdk8
  - oraclejdk9

before_install: export MAVEN_SKIP_RC=true

install: mvn install
```

ポイントは・・・・

* `sudo: required`
* `dist: trusty`
* `jdk`に`oraclejdk9`を追加
* `before_install: export MAVEN_SKIP_RC=true` (Mavenを使う場合のみ)

の4つです。
`sudo`は`false`でも動くみたいですが`required`が無難っぽいです。
あと・・・Mavenを使う場合は、`MAVEN_SKIP_RC=true`を指定しないと・・・・以下のようなエラーが出てしまいます。

```
...
$ mvn install
Unrecognized VM option 'MaxPermSize=192m'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
...
```

これは、Java 8で廃止された `-XX:MaxPermSize` がVMの起動オプションに指定されているためです。Java 8では警告ログを出力した上で実行時は無視するようになっていましたが、Java 9からはエラーになるようになっています。（詳しくは、[Issue](https://github.com/travis-ci/travis-ci/issues/5520#issuecomment-176088947)を参照してください）


# 実際にTravis CIでJDK 9を試してみよう

「Hallo World !」を標準出力する簡易アプリケーションを作り、Travis CI上でMavenを使ってビルドしてみましょう。

> **Note:**
>
> 実際にためした簡易アプリケーションは、以下のGitHubリポジトリに格納してあります。（Mavenビルド時にMaven wapperを使っている点の除けば、本投稿と内容は同じです）
>
> * https://github.com/kazuki43zoo/jdk9-demo-with-maven


```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.example</groupId>
	<artifactId>jdk9-demo-with-maven</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>jdk9-demo-with-maven</name>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<maven.compiler.target>1.8</maven.compiler.target>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.testTarget>1.8</maven.compiler.testTarget>
		<maven.compiler.testSource>1.8</maven.compiler.testSource>
	</properties>

	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<profiles>
		<profile>
			<id>jdk9</id>
			<activation>
				<jdk>9</jdk>
			</activation>
			<properties>
				<maven.compiler.target>1.9</maven.compiler.target>
				<maven.compiler.testTarget>1.9</maven.compiler.testTarget>
			</properties>
		</profile>

	</profiles>

</project>
```

```java:簡易アプリ
package com.example;

public class Jdk9DemoWithMavenApplication {

	public static void main(String[] args) {
		Message message = new Message("Hello World !");
		System.out.println(message.getText());
		System.out.println(System.getProperties());
	}

	static class Message {
		private final String text;

		Message(String text) {
			this.text = text;
		}

		String getText() {
			return text;
		}
	}
}
```

```java:せっかくなんで簡易テストケース
package com.example;

import org.hamcrest.core.Is;
import org.junit.Assert;
import org.junit.Test;

public class Jdk9DemoWithMavenApplicationTest {

	@Test
	public void contextLoads() {
		System.out.println(System.getProperties());
		Jdk9DemoWithMavenApplication.Message message = new Jdk9DemoWithMavenApplication.Message("Hello World !");
		Assert.assertThat(message.getText(), Is.is("Hello World !"));
	}

}
```

```yaml:.travis.yml
language: java

sudo: required
dist: trusty

jdk:
  - oraclejdk8
  - oraclejdk9

before_install: export MAVEN_SKIP_RC=true

install: mvn install

# 簡易アプリを起動するためのスクリプト
script: mvn exec:java -Dexec.mainClass=com.example.Jdk9DemoWithMavenApplication
```

# これをGitHubにPushすれば・・・

Travis CI上でビルドが開始し、以下のような実行ログが出力されます。

```
...
$ jdk_switcher use oraclejdk9
Switching to Oracle JDK9 (java-9-oracle), JAVA_HOME will be set to /usr/lib/jvm/java-9-oracle
$ java -Xmx32m -version
java version "9-ea"
Java(TM) SE Runtime Environment (build 9-ea+140)
Java HotSpot(TM) 64-Bit Server VM (build 9-ea+140, mixed mode)
$ javac -J-Xmx32m -version
javac 9-ea
$ export MAVEN_SKIP_RC=true
$ mvn install

...

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.example.Jdk9DemoWithMavenApplicationTest
{awt.toolkit=sun.awt.X11.XToolkit, java.specification.version=9, file.encoding.pkg=sun.io, sun.cpu.isalist=, sun.jnu.encoding=UTF-8, java.class.path=/home/travis/build/kazuki43zoo/jdk9-demo-with-maven/target/test-classes:/home/travis/build/kazuki43zoo/jdk9-demo-with-maven/target/classes:/home/travis/.m2/repository/junit/junit/4.12/junit-4.12.jar:/home/travis/.m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar:, java.vm.vendor=Oracle Corporation, sun.arch.data.model=64, java.vendor.url=http://java.oracle.com/, user.timezone=, os.name=Linux, java.vm.specification.version=9, sun.java.launcher=SUN_STANDARD, user.country=US, sun.boot.library.path=/usr/lib/jvm/java-9-oracle/lib/amd64, sun.java.command=/home/travis/build/kazuki43zoo/jdk9-demo-with-maven/target/surefire/surefirebooter8688134916500123866.jar /home/travis/build/kazuki43zoo/jdk9-demo-with-maven/target/surefire/surefire965005284627576688tmp /home/travis/build/kazuki43zoo/jdk9-demo-with-maven/target/surefire/surefire_05891703120810359159tmp, jdk.debug=release, surefire.test.class.path=/home/travis/build/kazuki43zoo/jdk9-demo-with-maven/target/test-classes:/home/travis/build/kazuki43zoo/jdk9-demo-with-maven/target/classes:/home/travis/.m2/repository/junit/junit/4.12/junit-4.12.jar:/home/travis/.m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar:, sun.cpu.endian=little, user.home=/home/travis, user.language=en, java.specification.vendor=Oracle Corporation, java.home=/usr/lib/jvm/java-9-oracle, file.separator=/, basedir=/home/travis/build/kazuki43zoo/jdk9-demo-with-maven, java.vm.compressedOopsMode=32-bit, line.separator=
, java.vm.specification.vendor=Oracle Corporation, java.specification.name=Java Platform API Specification, java.awt.graphicsenv=sun.awt.X11GraphicsEnvironment, surefire.real.class.path=/home/travis/build/kazuki43zoo/jdk9-demo-with-maven/target/surefire/surefirebooter8688134916500123866.jar, sun.management.compiler=HotSpot 64-Bit Tiered Compilers, java.runtime.version=9-ea+140, user.name=travis, path.separator=:, os.version=4.4.0-51-generic, java.runtime.name=Java(TM) SE Runtime Environment, file.encoding=UTF-8, java.vm.name=Java HotSpot(TM) 64-Bit Server VM, localRepository=/home/travis/.m2/repository, java.vendor.url.bug=http://bugreport.java.com/bugreport/, java.io.tmpdir=/tmp, java.version=9-ea, user.dir=/home/travis/build/kazuki43zoo/jdk9-demo-with-maven, os.arch=amd64, java.vm.specification.name=Java Virtual Machine Specification, java.awt.printerjob=sun.print.PSPrinterJob, sun.os.patch.level=unknown, java.library.path=/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib, java.vm.info=mixed mode, java.vendor=Oracle Corporation, java.vm.version=9-ea+140, sun.io.unicode.encoding=UnicodeLittle, java.class.version=53.0}
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.11 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

...

$ mvn exec:java -Dexec.mainClass=com.example.Jdk9DemoWithMavenApplication

...

Hello World !
{awt.toolkit=sun.awt.X11.XToolkit, exec.mainClass=com.example.Jdk9DemoWithMavenApplication, java.specification.version=9, file.encoding.pkg=sun.io, sun.cpu.isalist=, sun.jnu.encoding=UTF-8, java.class.path=/home/travis/build/kazuki43zoo/jdk9-demo-with-maven/.mvn/wrapper/maven-wrapper.jar, java.vm.vendor=Oracle Corporation, sun.arch.data.model=64, java.vendor.url=http://java.oracle.com/, user.timezone=UTC, os.name=Linux, java.vm.specification.version=9, sun.java.launcher=SUN_STANDARD, user.country=US, sun.boot.library.path=/usr/lib/jvm/java-9-oracle/lib/amd64, sun.java.command=org.apache.maven.wrapper.MavenWrapperMain exec:java -Dexec.mainClass=com.example.Jdk9DemoWithMavenApplication, jdk.debug=release, maven.home=/home/travis/.m2/wrapper/dists/apache-maven-3.3.9-bin/2609u9g41na2l7ogackmif6fj2/apache-maven-3.3.9, sun.cpu.endian=little, user.home=/home/travis, user.language=en, java.specification.vendor=Oracle Corporation, java.home=/usr/lib/jvm/java-9-oracle, file.separator=/, java.vm.compressedOopsMode=32-bit, line.separator=
, java.vm.specification.vendor=Oracle Corporation, java.specification.name=Java Platform API Specification, java.awt.graphicsenv=sun.awt.X11GraphicsEnvironment, sun.management.compiler=HotSpot 64-Bit Tiered Compilers, java.runtime.version=9-ea+140, user.name=travis, path.separator=:, securerandom.source=file:/dev/./urandom, os.version=4.4.0-51-generic, java.runtime.name=Java(TM) SE Runtime Environment, file.encoding=UTF-8, guice.disable.misplaced.annotation.check=true, java.vm.name=Java HotSpot(TM) 64-Bit Server VM, java.vendor.url.bug=http://bugreport.java.com/bugreport/, java.io.tmpdir=/tmp, java.version=9-ea, user.dir=/home/travis/build/kazuki43zoo/jdk9-demo-with-maven, os.arch=amd64, maven.multiModuleProjectDirectory=/home/travis/build/kazuki43zoo/jdk9-demo-with-maven, java.vm.specification.name=Java Virtual Machine Specification, java.awt.printerjob=sun.print.PSPrinterJob, sun.os.patch.level=unknown, java.library.path=/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib, java.vm.info=mixed mode, java.vendor=Oracle Corporation, java.vm.version=9-ea+140, classworlds.conf=/home/travis/.m2/wrapper/dists/apache-maven-3.3.9-bin/2609u9g41na2l7ogackmif6fj2/apache-maven-3.3.9/bin/m2.conf, sun.io.unicode.encoding=UnicodeLittle, java.class.version=53.0}
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.089 s
[INFO] Finished at: 2017-01-06T15:49:53+00:00
[INFO] Final Memory: 12M/43M
[INFO] ------------------------------------------------------------------------

The command "mvn exec:java -Dexec.mainClass=com.example.Jdk9DemoWithMavenApplication" exited with 0.

Done. Your build exited with 0.
```

# まとめ

Travis CI上でJDK 9を使うのは簡単ですね。ただ・・・自分が作っているライブラリやアプリケーションがJDK 9でビルド＆実行できるかは別の話です・・・。使っているライブラリがJava ９のJVMで動かない・・・ということは普通にありそうですし、ビルドツールのプラグインがJDK 9をサポートできていないケースなどもあるようです・・・ :cry:
とりあえず現状を把握するために、Travis CIを使ってJDK 9でビルドしてみるとよいでしょう。

> Note:
>
> JavassistやCGLIBなどのバイトコードをいじるライブラリを使っている場合は、実行時にエラーになる可能性たかそう・・・。

# 参考サイト

* https://github.com/travis-ci/travis-ci/issues/5520