Java SE 9 の javac で過去バージョンをターゲットにするときには --release オプションを使用すると良い
JDK の javac
コマンドを使って過去バージョンの Java 言語で書かれたソースコードをコンパイルする際には、-source
オプションや -target
オプションを使用してきました。 (JDK 8 までの話。) このとき、適切なブートストラップクラスパスを設定しなければ、対象バージョンに存在しない Java API を使用していてもビルドが通ってしまう、という問題がありました。
JDK 9 の javac
コマンドには --release
オプションが追加されました。 今後は (基本的には) このオプションを使用するようにすると良さそうです。
- ドキュメント : javac
Java SE 9 (Oracle JDK 9) のマイグレーションガイドより
マイグレーションガイドには以下のように書かれています。
If you use the
-source
and-target
options withjavac
, then check the values that you use. In JDK 9,javac
uses a "one plus three back" policy of supporting-source
and-target
options.The supported
-source
/-target
values are 9 (the default), 8, 7, and 6 (6 is deprecated, and a warning is displayed when this value is used).In JDK 8,
-source
and-target
values of 1.5/5 and earlier were deprecated and caused a warning to be generated. In JDK 9, those values cause an error.>javac -source 5 -target 5 Sample.java warning: [options] bootstrap class path not set in conjunction with -source 1.5 error: Source option 1.5 is no longer supported. Use 1.6 or later. error: Target option 1.5 is no longer supported. Use 1.6 or later.If possible, use the new
--release
flag instead of the-source
and-target
options. The--release N
flag is conceptually a macro for:-source N -target N -bootclasspath $PATH_TO_rt.jar_FOR_RELEASE_NThe valid arguments for the
--release
flag follow the same policy as for-source
and-target
, one plus three back.javac can recognize and process class files of all previous JDKs, going all the way back to JDK 1.0.2 class files.
See JEP 182: Policy for Retiring
Java Platform, Standard Edition Oracle JDK 9 Migration Guide, Release 9javac
-source
and-target
Options.
つまり、--release
フラグを指定すると、-source
オプションと -target
オプションを指定し、さらに適切なブートクラスパスの指定も行った状態になります。 これまで、過去バージョンのブートクラスパスを設定するには、過去バージョンの Java 実行環境を取得してその中の rt.jar を指定してやる必要があったので、格段に便利になりました。
試してみた
実際に、以下のようなコードを含む Java のソースファイルをコンパイルしてみました。
public class Main { public static void main(String[] args) { String test = String.join("", ""); // Java SE 8 で Java API に導入されたメソッド。 } }
--release 8
を指定した場合は、問題なくコンパイルされます。
~\java-project> javac --release 8 .\Main.java
--release 7
を指定した場合は、下記のようにエラーになります。
~\java-project> javac --release 7 .\Main.java .\Main.java:3: エラー: シンボルを見つけられません String test = String.join("", ""); ^ シンボル: メソッド join(String,String) 場所: クラス String エラー1個
Java 7 向けにビルドしたけど Java 7 の実行環境で動かすと (Java 7 にない API を使っていて) 例外が発生する、というようなミスが減りそうで最高ですね!
Gradle での使い方
Gradle 4.2.1 現在、JavaCompile
タスクで javac
の --release
オプションを指定する方法として特別なメソッドは提供されていません。 通常のコンパイラオプションを指定する方法は使用できるので、通常のコンパイラオプションの指定と同じ方法で --release
オプションを指定します。
具体的には CompileOptions#compilerArgs
プロパティを使用します。 このドキュメントを読むと、『For example, it is possible to pass the --release
option of JDK 9』 とか 『Note that if --release
is added then -target
and -source
are ignored.』 とか書かれています。 --release
オプションにもばっちり対応されていますね。
超単純な build.gradle の例を書いておきます。
apply plugin: 'java' tasks.withType(JavaCompile) { options.compilerArgs.addAll(['--release', '8']) }
Java 9 のモジュールシステム (JPMS; JSR 376) の概要
Java SE 9 がリリースされましたね! めでたい!
さてさて、Java SE 9 の目玉といえばやはり The Java Platform Module System (JPMS; JSR 376) ですよね! Project Jigsaw の心臓部です。
この記事では JSR 376 をさらっと読んで、JPMS の全体像をまとめておきます。 (実際の使い方などはこの記事の範囲外です。) モジュールシステムの理解への取っ掛かりとして皆さんの一助となれば幸いです。
仕様 (JSR 376) 概要
The Java Platform Module System (JPMS) の目的とその手段
JSR 376 に書かれているとおりの内容を日本語にしてます。
目標
JPMS の目標は、親しみやすく、それでいてスケーラブルなモジュールシステムを定義すること。
JPMS の全体像
JPMS の詳細は Java 言語仕様や JVM 仕様などの中に含まれています。 JSR 376 は、それらの仕様のどこに JPMS が影響しているのかを分かりやすく表現しています。
- Java 言語仕様と JVM 仕様
- まとめ : JPMS: Modules in the Java Language and JVM
- Java 言語仕様 (JPMS による差分が強調されている) : The Java Language Specification Java SE 9 Edition (JPMS に関する注記版)
- JVM 仕様 (JPMS による差分が強調されている) : The Java Virtual Machine Specification Java SE 9 Edition (JPMS に関する注記版)
- Java API
- JAR ファイル仕様への変更 : JPMS: Changes to the JAR File Specification
- JNI ファイル仕様への変更 : JPMS: Changes to the Java Native Interface
- JVM TI 仕様への変更 : JPMS: Changes to the JVM Tool Interface
- JDWT 仕様への変更 : JPMS: Changes to the Java Debug Wire Protocol
多くのアプリケーションエンジニアが気にするのは、特に Java 言語仕様や Java API 仕様、JAR ファイル仕様といったところだと思います。 ここら辺の内容は後で少し触れます。
JSR 376 の一部ではありませんが、次のような文書も紹介されています。
また、今回のリリースに含めなかった機能等についての説明や、変更履歴についても JSR 376 には書かれています。
Java 言語仕様における JPMS
Java 言語仕様の中の JPMS に関わる部分をさらっと読んだのでメモ程度にまとめておきます。
- 7.7 節 Module Declarations より
- モジュール宣言は、新しい名前付きモジュール (named module) を記述する。
- 名前付きモジュールは、他のモジュールへの依存や、他のモジュールに公開するものを記述する。
- モジュール宣言により、モジュール名が導入される。 モジュール名は、他のモジュールとの関係を記述するのに使用される。
- モジュール名は、1 個以上の Java 識別子をドットで連結したもの。
- モジュールにはノーマルモジュール (normal module) とオープンモジュール (open module) の 2 種類がある。
- モジュール宣言では、
java.util.ServiceLoader
によるサービスの提供あるいは利用の宣言も可能。 - 名前付きモジュールに関連付けられていないクラスなどは、無名モジュール (unnamed module) に関連付けられる。
- 6.1 節 より
- モジュール名は、モジュールが export する本質的なパッケージ名に合わせるのが良い。 それが難しい場合は、著者が持つドメイン名を逆さに並べたもので始めると良い。
JAR ファイル仕様における JPMS
こちらもメモ程度に。
- クラスパス上ではなくモジュールパス上に配置された JAR ファイルはモジュールである。
- トップレベルに module-info.class ファイルを持つ JAR ファイルは、モジュール式 JAR ファイルである。
- そうでない JAR ファイルは、自然発生的なモジュール (automatic module) とみなされる。
- その場合のモジュール名は JAR ファイル名から決められ、export されるパッケージは .class ファイルから決められる。
さあ、始めましょう
- IntelliJ IDEA : Java 9 and IntelliJ IDEA | IntelliJ IDEA Blog
- Gradle : Building Java 9 Modules
- Gradle で Java 9 のモジュールを利用するガイド。 私は試してません。
- ヌーラボのアカウント基盤を Java 9 にマイグレーションして起きた問題と解決法 | ヌーラボ
- モジュール化以外も含め、Java 9 対応についてわかりやすくまとまっています。
- モジュールシステムのスタートガイド : Project Jigsaw: Quick Start Guide
読んだ : RESTful Web Services with Dropwizard / Alexandros Dallas 著
Dropwizard に関わる仕事をしているので読んでみました。
RESTful Web Services with Dropwizard
- 作者: Alexandros Dallas
- 出版社/メーカー: Packt Publishing
- 発売日: 2014/02/19
- メディア: Kindle版
- この商品を含むブログを見る
Dropwizard について
Dropwizard は Java の web アプリケーションフレームワークです。 基本的には既存ライブラリの組み合わせで web アプリケーションを構築するというもので、Dropwizard 固有の仕組みはさほど多くありません。 (例えば Web リクエストを受け取るのは Jersey で、DB アクセスには Hibernate か jDBI が使われる。)
特に Java EE 系の知識を持っている人であれば、とっつきやすい感じです。
本書について
本書は、Dropwizard を使って web アプリケーションを構築するための方法を説明するものです。 プロジェクトの準備や、HTTP リクエストを受けるエンドポイントの記述、DB アクセス、ユーザー認証、HTML を返す View テンプレートについてなど、基本的な要素について、サンプルコードを交えながら仕組みが解説されます。
Java EE などについてある程度わかっている人が読むと Dropwizard の公式ドキュメントを読むのと大差ないと思いますが、初心者の人が読むと結構わかりやすいんじゃないかなと思います。 (ある程度わかってる人にとっても、Dropwizard についてざっと知ることができて良いかもしれませんが、そういう使い方だとちょっと値段は高めに感じる気がします。)
本書での学び
参考のリンクとしては現時点での最新バージョンのドキュメントへのリンクです。 閲覧時の最新バージョンのドキュメントは各自探してください。
- Dropwizard では、maven-shade プラグイン を使って、単体で web アプリケーションとして実行可能な JAR ファイル (uber-jar) を作る。
- Hibernate Validator によるアプリケーション設定のバリデーションが可能。
- jDBI で DB から取得した結果をオブジェクトにマップする方法として、
@MapResultAsBean
アノテーションを使うという方法もある。- 参考 : MapResultAsBean (jDBI 2.48.2 API)
- とはいえ公式的なドキュメントは何もなく、ドキュメント化されていない挙動に依存することになるので不安。 (JDBI の機能の多くがドキュメント化されてないのでまあこれに限った話ではないのだけど。)
- HTTP リクエストパラメータのバリデーション周り。
- JAX-RS のリソースメソッドのパラメータのバリデーションを明示的に実行することも可能。 (
@Valid
アノテーションでのバリデーション実行しか知らなかった。) - 複数フィールドにまたがるバリデーション。
- 参考 : Dropwizard Validation | Dropwizard
- JAX-RS のリソースメソッドのパラメータのバリデーションを明示的に実行することも可能。 (
- Dropwizard には HTTP クライアント用モジュールも含まれている。
- 認証周り。
- Basic 認証用のクラスが用意されている。
- オプションの認証も可能。 (認証されたユーザーの場合はそのユーザー専用のコンテンツを表示し、さもなければ一般ユーザー向けのコンテンツを表示する、みたいな。)
CachingAuthenticator
によるキャッシング。- 参考 : Dropwizard Authentication | Dropwizard
- Fixtures for Easy Software Testing (FEST) というプロジェクトがある。 TestNG や JUnit と一緒に使える。 ソフトウェアテストを書きやすくするライブラリ。
感想
Dropwizard をそこそこ使ってて公式ドキュメントも (全部ではないけど) 読んでいたので、本書での学びはあんまりなかったです。 とはいえ上に書いたように新たに知れたこともいくつかあったので読んでよかったです。 (そんなに時間もかけずにざっと読めましたし。)
とはいえ紙の本だと 30 ドル以上するので、ちょっと高い感じはしますね。
Dropwizard + JDBI で SQL オブジェクトの返り値に Optional を使うときには SingleValueResult アノテーションが必要
Dropwizard で SQL ライブラリ JDBI を使うときのおはなし。
OptionalContainerFactory
dropwizard-jdbi ライブラリは OptionalContainerFactory
クラスを提供してくれていて、JDBI の SQL オブジェクトで返り値に Java 8 で導入された Optional
指定することができます。
ちなみに普通に dropwizard-jdbi の DBIFactory#build
メソッドを使うと自動的に OptionalContainerFactory
を DBI
に登録してくれるので、自分で登録する必要はありません。 (Dropwizard 1.0.5 で確認。)
SingleValueResult
アノテーションが必要 (単に Optional
を指定するだけでは動かない)
SQL オブジェクトのクラス定義で返り値に Optional
を指定すればそれだけで動くのかと思いきや、実はそんなことはありません。 Optional
を返すメソッドに SingleValueResult
アノテーションを付ける必要があります。
import info.vividcode.app.web.example.dropwizard.domain.Person; import org.skife.jdbi.v2.sqlobject.SqlQuery; import org.skife.jdbi.v2.sqlobject.customizers.SingleValueResult; import java.util.Optional; public interface PersonDao { @SqlQuery("SELECT * FROM person LIMIT 1") @SingleValueResult(Person.class) Optional<Person> findOne(); }
ここら辺のドキュメントがないので、ソースコードを読んで確認しました。
- 返り値に応じた
ResultReturnThing
を生成する箇所 : jdbi/ResultReturnThing.java at 3fe1480fcc3c42be94d355f8c7335bd784dbbc13 · jdbi/jdbi · GitHub SingleValueResult
アノテーションがあるかどうかを検査している箇所 : jdbi/ResultReturnThing.java at 3fe1480fcc3c42be94d355f8c7335bd784dbbc13 · jdbi/jdbi · GitHub
Optional
を使う場合に限らず、Iterable
じゃなくてコンテナを使いたい場合は SingleValueResult
アノテーションを付ける必要がありそうですね。
関連ページ
MySQL Connector/J 5.1 系では useLegacyDatetimeCode=false にしよう
JDBC で MySQL に接続するときに使用する MySQL Connector/J (mysql:mysql-connector-java) の話。 サーバー・クライアントのタイムゾーン設定が違っている場合にどう対応するのがいいか。
結論
- MySQL Connector/J 6 (まだ開発版だけど) 以降は自動でやってくれるので気にする必要はない。
- MySQL Connector/J 5.1 では URL に
useLegacyDatetimeCode=false
を入れて、時刻周りの新しい処理が動くようにしろ。- 新しい処理では、タイムゾーンの変換を一貫性をもってやってくれるようになる。
- 『Use code for DATE/TIME/DATETIME/TIMESTAMP handling in result sets and statements that consistently handles time zone conversions from client to server and back again』
- 参考 : 5.1 Driver/Datasource Class Names, URL Syntax and Configuration Properties for Connector/J
- 5.1 系ではデフォルトでは互換のために新しい処理は動かないようになっているので、明示的に新しい処理を使うように URL で指定する必要がある。
- バージョン 5.1.6 で導入された機能なので、それより古いものでは使えない。
問題
そもそもどういう問題に遭遇したのか。
- Java のアプリケーションサーバーのタイムゾーンが JST。
- MySQL サーバーのタイムゾーンが UTC。
- Java アプリケーションから MySQL サーバーには MySQL Connector/J 5.1 系で接続。
- タイムゾーン周りのオプションは何も指定せず。
- SQL 文の
NOW()
関数やDEFAULT CURRENT_TIMESTAMP
で設定された時刻を Java アプリケーション側で取得すると、現在時刻から 9 時間前の時刻が返ってきた。 - → MySQL Connector/J がサーバー・クライアント間のタイムゾーン差を扱ってくれてない。
MySQL Connector/J とタイムゾーン
- もともとは
useTimezone
プロパティやserverTimezone
プロパティを使って対応する必要があった。 - MySQL Connector/J 5.1.6 で時刻周りの処理が書き直されて、
useLegacyDatetimeCode=false
することでタイムゾーン変換などを自動で扱ってくれるようになった。 - MySQL Connector/J 6 では
useLegacyDatetimeCode
プロパティを含め、古いタイムゾーン周りのプロパティは全部削除される。
おわり
タイムゾーンはライブラリ側がちゃんと面倒見てくれるだろう、と思って気にしなかったら、環境を変えて Java アプリケーションと MySQL サーバーのタイムゾーン設定がずれたときにいきなり想定しない動作になったりするので気を付けましょう。