Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First class support of native compilation with GraalVM #2475

Open
Eng-Fouad opened this issue Aug 22, 2023 · 6 comments
Open

First class support of native compilation with GraalVM #2475

Eng-Fouad opened this issue Aug 22, 2023 · 6 comments

Comments

@Eng-Fouad
Copy link
Contributor

According to GraalVM docs, native image configuration can be embedded within the jar file. For JDBI, it would be at this path:

META-INF/native-image/org.jdbi/jdbi3-core/native-image.properties
META-INF/native-image/org.jdbi/jdbi3-sqlobject/native-image.properties
META-INF/native-image/org.jdbi/jdbi3-postgres/native-image.properties
// include it for all modules

To register classes of jdbi3-core for reflection, we can add refelct-config.json at this path:

META-INF/native-image/org.jdbi/jdbi3-core/refelct-config.json

The content of the file would be something like:

[
  {"name":"org.jdbi.v3.core.statement.SqlStatements","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.core.statement.StatementExceptions","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.core.argument.Arguments","methods":[{"name":"<init>","parameterTypes":["org.jdbi.v3.core.config.ConfigRegistry"]}]},
  {"name":"org.jdbi.v3.core.mapper.RowMappers","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.core.mapper.ColumnMappers","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.core.mapper.Mappers","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.core.mapper.MapMappers","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.core.mapper.MapEntryMappers","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.core.mapper.reflect.ReflectionMappers","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.core.mapper.reflect.internal.PojoTypes","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.core.collector.JdbiCollectors","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.core.qualifier.Qualifiers","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.core.result.ResultProducers","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.core.array.SqlArrayTypes","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.core.transaction.SerializableTransactionRunner$Configuration","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.core.extension.Extensions","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.core.internal.OnDemandExtensions","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.core.internal.EnumStrategies","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.core.config.internal.ConfigCaches","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.core.enums.Enums","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.core.Handles","methods":[{"name":"<init>"}]}
]

then update native-image.properties to include it:

Args=-H:ReflectionConfigurationResources=${.}/refelct-config.json

For jdbi3-sqlobject, content of refelct-config.json would be:

[
  {"name":"org.jdbi.v3.sqlobject.Handlers","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.sqlobject.HandlerDecorators","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.sqlobject.SqlObjects","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.sqlobject.SqlObject","methods":[{"name":"getHandle"}]},
  {"name":"org.jdbi.v3.sqlobject.config.internal.RegisterRowMapperImpl","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.sqlobject.transaction.internal.TransactionDecorator","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.sqlobject.customizer.TimestampedConfig","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.sqlobject.customizer.internal.BindFactory","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.sqlobject.customizer.internal.BindListFactory","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.sqlobject.statement.internal.SqlObjectStatementConfiguration","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.sqlobject.statement.internal.SqlQueryHandler","methods":[{"name":"<init>"}]},
  {"name":"org.jdbi.v3.sqlobject.statement.internal.SqlUpdateHandler","methods":[{"name":"<init>"}]}
]

For jdbi3-postgres, content of refelct-config.json would be:

[
  {"name":"org.jdbi.v3.postgres.PostgresTypes","methods":[{"name":"<init>"}]}
]

For more on this, see this gist.

@stevenschlansker
Copy link
Member

Thanks for this. I think to integrate this, we will need to also include test cases, that check that the reflect-config.json is correct and does not get out of date.

@Eng-Fouad
Copy link
Contributor Author

Thanks for this. I think to integrate this, we will need to also include test cases, that check that the reflect-config.json is correct and does not get out of date.

How about running the existing test cases twice? Once in JVM mode and once in native mode. That ensures it covers all cases.

@stevenschlansker
Copy link
Member

It looks like it should be possible to setup graalvm in ci: https://github.com/graalvm/setup-graalvm

@stevenschlansker
Copy link
Member

It looks like the graal native maven plugin does a lot for us. I started #2476 to add graal native-image to ci.

I ran into:

 Produced artifacts:
 /home/runner/work/jdbi/jdbi/core/target/libawt.so (jdk_library)
 /home/runner/work/jdbi/jdbi/core/target/libawt_headless.so (jdk_library)
 /home/runner/work/jdbi/jdbi/core/target/libawt_xawt.so (jdk_library)
 /home/runner/work/jdbi/jdbi/core/target/libfontmanager.so (jdk_library)
 /home/runner/work/jdbi/jdbi/core/target/libjava.so (jdk_library_shim)
 /home/runner/work/jdbi/jdbi/core/target/libjavajpeg.so (jdk_library)
 /home/runner/work/jdbi/jdbi/core/target/libjvm.so (jdk_library_shim)
 /home/runner/work/jdbi/jdbi/core/target/liblcms.so (jdk_library)
 /home/runner/work/jdbi/jdbi/core/target/native-tests (executable)
========================================================================================================================
Finished generating 'native-tests' in 12m 24s.
[INFO] Executing: /home/runner/work/jdbi/jdbi/core/target/native-tests --xml-output-dir /home/runner/work/jdbi/jdbi/core/target/native-test-reports -Djava.awt.headless=true -Djava.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$s %5$s%6$s%n -Djunit.platform.listeners.uid.tracking.output.dir=/home/runner/work/jdbi/jdbi/core/target/test-ids -Dpg-embedded.postgres-version=15 -Dsun.jnu.encoding=UTF-8 -Duser.timezone=UTC
JUnit Platform on Native Image - report
----------------------------------------


Failures (1):
  JUnit Jupiter
    => java.lang.NoSuchMethodError: java.lang.invoke.MethodHandle.close()
       org.junit.jupiter.engine.descriptor.AbstractExtensionContext.<clinit>(AbstractExtensionContext.java:43)
       java.base@17.0.8/java.lang.Class.ensureInitialized(DynamicHub.java:579)
       org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor.prepare(JupiterEngineDescriptor.java:57)
       org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor.prepare(JupiterEngineDescriptor.java:31)
       org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$2(NodeTestTask.java:123)
       org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
       org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:123)
       org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:90)

and don't know how to proceed...

@Eng-Fouad
Copy link
Contributor Author

Eng-Fouad commented Aug 25, 2023

It looks like the graal native maven plugin does a lot for us. I started #2476 to add graal native-image to ci.

I ran into:

 Produced artifacts:
 /home/runner/work/jdbi/jdbi/core/target/libawt.so (jdk_library)
 /home/runner/work/jdbi/jdbi/core/target/libawt_headless.so (jdk_library)
 /home/runner/work/jdbi/jdbi/core/target/libawt_xawt.so (jdk_library)
 /home/runner/work/jdbi/jdbi/core/target/libfontmanager.so (jdk_library)
 /home/runner/work/jdbi/jdbi/core/target/libjava.so (jdk_library_shim)
 /home/runner/work/jdbi/jdbi/core/target/libjavajpeg.so (jdk_library)
 /home/runner/work/jdbi/jdbi/core/target/libjvm.so (jdk_library_shim)
 /home/runner/work/jdbi/jdbi/core/target/liblcms.so (jdk_library)
 /home/runner/work/jdbi/jdbi/core/target/native-tests (executable)
========================================================================================================================
Finished generating 'native-tests' in 12m 24s.
[INFO] Executing: /home/runner/work/jdbi/jdbi/core/target/native-tests --xml-output-dir /home/runner/work/jdbi/jdbi/core/target/native-test-reports -Djava.awt.headless=true -Djava.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$s %5$s%6$s%n -Djunit.platform.listeners.uid.tracking.output.dir=/home/runner/work/jdbi/jdbi/core/target/test-ids -Dpg-embedded.postgres-version=15 -Dsun.jnu.encoding=UTF-8 -Duser.timezone=UTC
JUnit Platform on Native Image - report
----------------------------------------


Failures (1):
  JUnit Jupiter
    => java.lang.NoSuchMethodError: java.lang.invoke.MethodHandle.close()
       org.junit.jupiter.engine.descriptor.AbstractExtensionContext.<clinit>(AbstractExtensionContext.java:43)
       java.base@17.0.8/java.lang.Class.ensureInitialized(DynamicHub.java:579)
       org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor.prepare(JupiterEngineDescriptor.java:57)
       org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor.prepare(JupiterEngineDescriptor.java:31)
       org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$2(NodeTestTask.java:123)
       org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
       org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:123)
       org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:90)

and don't know how to proceed...

Try to register the field AbstractExtensionContext.CLOSE_RESOURCES for reflection, something like:

[
    {"name":"org.junit.jupiter.engine.descriptor.AbstractExtensionContext","fields":[{"name":"CLOSE_RESOURCES"}]}
]
<plugin>
    <groupId>org.graalvm.buildtools</groupId>
    <artifactId>native-maven-plugin</artifactId>
    <version>${dep.plugin.native.version}</version>
    <extensions>true</extensions>
    <configuration>
        <buildArgs>
            <buildArg>-H:ReflectionConfigurationFiles=/path/to/reflect-config.json</buildArg>
        </buildArgs>
    </configuration>

@Eng-Fouad
Copy link
Contributor Author

Also, according to the release notes of JUnit 5.10.0:

Building native images with GraalVM now requires configuring the build arg --initialize-at-build-time=org.junit.platform.launcher.core.LauncherConfig and --initialize-at-build-time=org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

3 participants