要約
Java の native-image で PostgreSQL の JDBC ドライバに含まれる DataSource を使うには META-INF/native-image/serialization-config.json
が必要です。
org.postgresql.ds.common.BaseDataSource
を継承している全ての DataSource が対象になる可能性があります。
問題
以下のような PostgreSQL へ接続するアプリを作って native-image 化しました。
public class NativeImageTest { public static void main(String[] args) throws Exception { PGPoolingDataSource ds = new PGPoolingDataSource(); ds.setUrl("jdbc:postgresql://localhost:5432/postgres"); ds.setUser("postgres"); ds.setPassword("postgres"); try(Connection con = ds.getConnection(); PreparedStatement stmt = con.prepareStatement("SELECT datname FROM pg_database"); ResultSet rs = stmt.executeQuery()) { rs.next(); System.out.println(rs.getString("datname")); } } }
JVM上では問題なく動いたにもかかわらず、native-image では動きません。
Exception in thread "main" org.postgresql.util.PSQLException: Failed to setup DataSource. at org.postgresql.ds.PGPoolingDataSource.initialize(PGPoolingDataSource.java:275) at org.postgresql.ds.PGPoolingDataSource.getConnection(PGPoolingDataSource.java:331) at dev.chiroito.NativeImageTest.main(NativeImageTest.java:16) Caused by: java.io.InvalidClassException: java.util.Properties; no valid constructor at java.base@11.0.19/java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:158) at java.base@11.0.19/java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:746) at java.base@11.0.19/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2202) at java.base@11.0.19/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1687) at java.base@11.0.19/java.io.ObjectInputStream.readObject(ObjectInputStream.java:489) at java.base@11.0.19/java.io.ObjectInputStream.readObject(ObjectInputStream.java:447) at org.postgresql.ds.common.BaseDataSource.readBaseObject(BaseDataSource.java:1582) at org.postgresql.ds.common.BaseDataSource.initializeFrom(BaseDataSource.java:1592) at org.postgresql.ds.PGPoolingDataSource.initialize(PGPoolingDataSource.java:273) ... 3 more
java.util.Properties
クラスのデシリアライズができないことが原因です。
解決方法
シリアライズの設定を追加しましょう。シリアライズの設定は META-INF/native-image/serialization-config.json
ファイルに記載します。
場所は以下の位置に置きましょう。
META-INF/native-image/serialization-config.json
のファイルの中身は以下の通りです。java.util.Properties
が継承している java.util.Hashtable
と java.util.Dictionary
も記載します。
[ { "name":"java.util.Properties" }, { "name":"java.util.Hashtable" }, { "name":"java.util.Dictionary" } ]
クラスが足りないと以下のメッセージが出力されます。
Exception in thread "main" com.oracle.svm.core.jdk.UnsupportedFeatureError: SerializationConstructorAccessor class not found for declaringClass: java.util.Hashtable (targetConstructorClass: java.util.Dictionary). Usually adding java.util.Hashtable to serialization-config.json fixes the problem.
記載が終わったら再び native-image 化すればOKです。