Skip to content

Commit 58a8bdc

Browse files
committed
[GR-46872] Create a guide for configuring Reachability Metadata with Maven.
PullRequest: graal/16147
2 parents d29c5d2 + 3ed1e82 commit 58a8bdc

File tree

5 files changed

+694
-292
lines changed

5 files changed

+694
-292
lines changed

docs/reference-manual/native-image/guides/build-with-reflection.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ To build a native executable for a Java application that uses Java reflection, d
1414
You can create configuration file(s) by hand, but a more convenient approach is to generate the configuration using the Tracing agent (from now on, the agent).
1515
This guide demonstrates how to configure `native-image` with the agent. The agent generates the configuration for you automatically when you run an application on a JVM.
1616

17-
To learn how to build a native executable with the metadata pre-computed in the code, [follow this guide](use-reachability-metadata-repository-gradle.md).
17+
To learn how to build a native executable with the metadata pre-computed in the code, [see the documentation](../ReachabilityMetadata.md).
1818

1919
The example application in this guide uses Java reflection. The `native-image` tool only partially detects application elements that are accessed using the Java Reflection API. So, you need to provide it with details about reflectively accessed classes, methods, and fields.
2020
## Example with No Configuration

docs/reference-manual/native-image/guides/guides.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@ Here you will learn how to:
2525
- [Containerise a Native Executable and Run in a Docker Container](containerise-native-executable-with-docker.md)
2626
- [Create a Heap Dump from a Native Executable](create-heap-dump-from-native-executable.md)
2727
- [Debug Native Executables with GDB](debug-native-executables-with-gdb.md)
28+
- [Include Reachability Metadata Using the Native Image Gradle Plugin](include-reachability-metadata-gradle.md)
29+
- [Include Reachability Metadata Using the Native Image Maven Plugin](include-reachability-metadata-maven.md)
2830
- [Include Resources in a Native Executable](include-resources.md)
2931
- [Optimize a Native Executable with Profile-Guided Optimizations](optimize-native-executable-with-pgo.md)
3032
- [Use GraalVM Dashboard to Optimize the Size of a Native Executable](use-graalvm-dashboard.md)
3133
- [Use Gradle to Build a Native Executable from a Java Application](https://graalvm.github.io/native-build-tools/latest/gradle-plugin-quickstart.html)
3234
- [Use Maven to Build a Native Executable from a Java Application](https://graalvm.github.io/native-build-tools/latest/maven-plugin-quickstart.html)
33-
- [Use Shared Reachability Metadata with Native Image Gradle Plugin](use-reachability-metadata-repository-gradle.md)
3435
- [Use System Properties in a Native Executable](use-system-properties.md)
3536

3637
## Microservices Frameworks
Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
---
2+
layout: ni-docs
3+
toc_group: how-to-guides
4+
link_title: Include Reachability Metadata Using the Native Image Gradle Plugin
5+
permalink: /reference-manual/native-image/guides/use-reachability-metadata-repository-gradle/
6+
---
7+
8+
# Include Reachability Metadata Using the Native Image Gradle Plugin
9+
10+
You can build a native executable from a Java application with **Gradle**.
11+
For that, use the GraalVM Native Image Gradle plugin provided as part of the [Native Build Tools project](https://graalvm.github.io/native-build-tools/latest/gradle-plugin.html).
12+
13+
A "real-world" Java application likely requires some Java reflection objects, or it calls some native code, or accesses resources on the class path - dynamic features that the `native-image` tool must be aware of at build time, and provided in the form of [metadata](../ReachabilityMetadata.md).
14+
(Native Image loads classes dynamically at build time, and not at run time.)
15+
16+
Depending on your application dependencies, there are three ways to provide the metadata with the Native Image Gradle Plugin:
17+
18+
1. [Using the GraalVM Reachability Metadata Repository](#build-a-native-executable-using-the-graalvm-reachability-metadata-repository)
19+
2. [Using the Tracing Agent](#build-a-native-executable-with-the-tracing-agent)
20+
3. [Autodetecting](https://graalvm.github.io/native-build-tools/latest/gradle-plugin-quickstart.html#build-a-native-executable-with-resources-autodetection) (if the required resources are directly available on the classpath, in the _src/main/resources_ directory)
21+
22+
This guide demonstrates how to build a native executable using the [GraalVM Reachability Metadata Repository](https://github.com/oracle/graalvm-reachability-metadata), and with the [Tracing agent](https://graalvm.github.io/native-build-tools/latest/gradle-plugin.html#agent-support).
23+
The goal of this guide is to illustrate the difference between the two approaches, and demonstrate how the use of reachability metadata can simplify your development tasks.
24+
25+
We recommend that you follow the instructions and create the application step-by-step.
26+
Alternatively, you can go right to the [completed example](https://github.com/graalvm/native-build-tools/tree/master/samples/metadata-repo-integration).
27+
28+
## Prepare a Demo Application
29+
30+
> Note: A Java version between 17 and 20 is required to execute Gradle (see the [Gradle Compatibility Matrix](https://docs.gradle.org/current/userguide/compatibility.html)). However, if you want to run your application with Java 21 (or higher), there is a workaround: set `JAVA_HOME` to a Java version between 17 and 20, and `GRAALVM_HOME` to GraalVM for JDK 21. See the [Native Image Gradle Plugin documentation](https://graalvm.github.io/native-build-tools/latest/gradle-plugin.html#_installing_graalvm_native_image_tool) for more details.
31+
32+
1. Make sure you have installed GraalVM.
33+
The easiest way to get started is with [SDKMAN!](https://sdkman.io/jdks#graal).
34+
For other installation options, visit the [Downloads section](https://www.graalvm.org/downloads/).
35+
36+
2. Create a new Java project with **Gradle** in your favorite IDE, called "H2Example", in the `org.graalvm.example` package.
37+
38+
3. Rename the default _app_ directory to _H2Example_, then rename the default filename _App.java_ to _H2Example.java_ and replace its contents with the following:
39+
40+
```java
41+
package org.graalvm.example;
42+
43+
import java.sql.Connection;
44+
import java.sql.DriverManager;
45+
import java.sql.PreparedStatement;
46+
import java.sql.ResultSet;
47+
import java.sql.SQLException;
48+
import java.util.ArrayList;
49+
import java.util.Comparator;
50+
import java.util.HashSet;
51+
import java.util.List;
52+
import java.util.Set;
53+
54+
public class H2Example {
55+
56+
public static final String JDBC_CONNECTION_URL = "jdbc:h2:./data/test";
57+
58+
public static void main(String[] args) throws Exception {
59+
// Cleanup
60+
withConnection(JDBC_CONNECTION_URL, connection -> {
61+
connection.prepareStatement("DROP TABLE IF EXISTS customers").execute();
62+
connection.commit();
63+
});
64+
65+
Set<String> customers = Set.of("Lord Archimonde", "Arthur", "Gilbert", "Grug");
66+
67+
System.out.println("=== Inserting the following customers in the database: ");
68+
printCustomers(customers);
69+
70+
// Insert data
71+
withConnection(JDBC_CONNECTION_URL, connection -> {
72+
connection.prepareStatement("CREATE TABLE customers(id INTEGER AUTO_INCREMENT, name VARCHAR)").execute();
73+
PreparedStatement statement = connection.prepareStatement("INSERT INTO customers(name) VALUES (?)");
74+
for (String customer : customers) {
75+
statement.setString(1, customer);
76+
statement.executeUpdate();
77+
}
78+
connection.commit();
79+
});
80+
81+
System.out.println("");
82+
System.out.println("=== Reading customers from the database.");
83+
System.out.println("");
84+
85+
Set<String> savedCustomers = new HashSet<>();
86+
// Read data
87+
withConnection(JDBC_CONNECTION_URL, connection -> {
88+
try (ResultSet resultSet = connection.prepareStatement("SELECT * FROM customers").executeQuery()) {
89+
while (resultSet.next()) {
90+
savedCustomers.add(resultSet.getObject(2, String.class));
91+
}
92+
}
93+
});
94+
95+
System.out.println("=== Customers in the database: ");
96+
printCustomers(savedCustomers);
97+
}
98+
99+
private static void printCustomers(Set<String> customers) {
100+
List<String> customerList = new ArrayList<>(customers);
101+
customerList.sort(Comparator.naturalOrder());
102+
int i = 0;
103+
for (String customer : customerList) {
104+
System.out.println((i + 1) + ". " + customer);
105+
i++;
106+
}
107+
}
108+
109+
private static void withConnection(String url, ConnectionCallback callback) throws SQLException {
110+
try (Connection connection = DriverManager.getConnection(url)) {
111+
connection.setAutoCommit(false);
112+
callback.run(connection);
113+
}
114+
}
115+
116+
private interface ConnectionCallback {
117+
void run(Connection connection) throws SQLException;
118+
}
119+
}
120+
```
121+
122+
4. Delete the _H2Example/src/test/java_ directory (if it exists).
123+
124+
5. Open the Gradle configuration file _build.gradle_, and replace its contents with the following:
125+
126+
```groovy
127+
plugins {
128+
id 'application'
129+
// 1. Native Image Gradle plugin
130+
id 'org.graalvm.buildtools.native' version '0.9.28'
131+
}
132+
133+
repositories {
134+
mavenCentral()
135+
}
136+
137+
// 2. Application main class
138+
application {
139+
mainClass.set('org.graalvm.example.H2Example')
140+
}
141+
142+
dependencies {
143+
// 3. H2 Database dependency
144+
implementation("com.h2database:h2:2.2.220")
145+
}
146+
147+
// 4. Native Image build configuration
148+
graalvmNative {
149+
agent {
150+
defaultMode = "standard"
151+
}
152+
binaries {
153+
main {
154+
imageName.set('h2example')
155+
buildArgs.add("-Ob")
156+
}
157+
}
158+
}
159+
```
160+
161+
**1** Enable the [Native Image Gradle plugin](https://graalvm.github.io/native-build-tools/latest/gradle-plugin.html).
162+
The plugin discovers which JAR files it needs to pass to `native-image` and what the executable main class should be.
163+
164+
**2** Specify explicitly the application main class.
165+
166+
**3** Add a dependency on the [H2 Database](https://www.h2database.com/html/main.html), an open source SQL database for Java. The application interacts with this database through the JDBC driver.
167+
168+
**4** You can pass parameters to the `native-image` tool in the `graalvmNative` plugin configuration. In individual `buildArgs` you can pass parameters exactly the same way as you do from a command line. The `-Ob` option to enable quick build mode (recommended during development only) is used as an example. `imageName.set()` is used to specify the name for the resulting binary. Learn about other configuration options from the [plugin's documentation](https://graalvm.github.io/native-build-tools/latest/gradle-plugin.html#configuration).
169+
170+
6. The plugin is not yet available on the Gradle Plugin Portal, so declare an additional plugin repository. Open the _settings.gradle_ file and replace the default content with this:
171+
172+
```groovy
173+
pluginManagement {
174+
repositories {
175+
mavenCentral()
176+
gradlePluginPortal()
177+
}
178+
}
179+
180+
rootProject.name = 'H2Example'
181+
include('H2Example')
182+
```
183+
Note that the `pluginManagement {}` block must appear before any other statements in the file.
184+
185+
7. (Optional) Build the application. From the root directory of the repository, run the following command:
186+
```bash
187+
./gradlew run
188+
```
189+
This generates an "executable" JAR file, one that contains all of the application's dependencies and also a correctly configured _MANIFEST_ file.
190+
191+
## Build a Native Executable Using the GraalVM Reachability Metadata Repository
192+
193+
The Native Image Gradle plugin provides support for the [GraalVM Reachability Metadata repository](https://github.com/oracle/graalvm-reachability-metadata).
194+
This repository provides GraalVM configuration for libraries which do not support GraalVM Native Image by default.
195+
One of these is the [H2 Database](https://www.h2database.com/html/main.html) this application depends on.
196+
The support needs to be enabled explicitly.
197+
198+
1. Open the _build.gradle_ file, and enable the GraalVM Reachability Metadata Repository in the `graalvmNative` plugin configuration:
199+
200+
```groovy
201+
metadataRepository {
202+
enabled = true
203+
}
204+
```
205+
The whole configuration block should look like:
206+
```groovy
207+
graalvmNative {
208+
agent {
209+
defaultMode = "standard"
210+
}
211+
binaries {
212+
main {
213+
imageName.set('h2example')
214+
buildArgs.add("-Ob")
215+
}
216+
}
217+
metadataRepository {
218+
enabled = true
219+
}
220+
}
221+
```
222+
The plugin automatically downloads the metadata from the repository.
223+
224+
2. Now build a native executable using the metadata:
225+
```shell
226+
./gradlew nativeRun
227+
```
228+
This generates a native executable for the platform in the _build/native/nativeCompile/_ directory, called `h2example`.
229+
The command also runs the application from that native executable.
230+
231+
Using the GraalVM Reachability Metadata Repository enhances the usability of Native Image for Java applications depending on 3rd party libraries.
232+
233+
## Build a Native Executable with the Tracing Agent
234+
235+
The second way to provide the medatata configuration for `native-image` is by injecting the [Tracing agent](https://graalvm.github.io/native-build-tools/latest/gradle-plugin.html#agent-support) (later *the agent*) at compile time.
236+
237+
The agent can run in three modes:
238+
- **Standard**: Collects metadata without conditions. This is recommended if you are building a native executable.
239+
- **Conditional**: Collects metadata with conditions. This is recommended if you are creating conditional metadata for a native shared library intended for further use.
240+
- **Direct**: For advanced users only. This mode allows directly controlling the command line passed to the agent.
241+
242+
You can configure the agent by either passing the options on the command line, or in the _build.gradle_ file.
243+
See below how to collect metadata with the tracing agent, and build a native executable applying the provided configuration.
244+
245+
1. Open the _build.gradle_ file and see the agent mode specified in the `graalvmNative` plugin configuration:
246+
```groovy
247+
graalvmNative {
248+
agent {
249+
defaultMode = "standard"
250+
}
251+
...
252+
}
253+
```
254+
If you prefer the command-lime option, it is `-Pagent=standard`.
255+
256+
2. Now run your application with the agent, on the JVM. To enable the agent with the Native Image Gradle plugin, pass the `-Pagent` option to any Gradle tasks that extends `JavaForkOptions` (for example, `test` or `run`):
257+
```shell
258+
./gradlew -Pagent run
259+
```
260+
The agent captures and records calls to the H2 Database and all the dynamic features encountered during a test run into multiple _*-config.json_ files.
261+
262+
3. Once the metadata is collected, copy it into the project's _/META-INF/native-image/_ directory using the `metadataCopy` task:
263+
264+
```shell
265+
./gradlew metadataCopy --task run --dir src/main/resources/META-INF/native-image
266+
```
267+
268+
It is not required but recommended that the output directory is _/resources/META-INF/native-image/_. The `native-image` tool picks up metadata from that location automatically. For more information about how to collect metadata for your application automatically, see [Collecting Metadata Automatically](../AutomaticMetadataCollection.md).
269+
Here is the expected files tree after this step:
270+
271+
![Configuration Files Generated by the Agent](img/H2Example-json-configs.png)
272+
273+
4. Build a native executable using configuration collected by the agent:
274+
275+
```shell
276+
./gradlew nativeCompile
277+
```
278+
The native executable, named _h2example_, is created in the _build/native/nativeCompile_ directory.
279+
280+
5. Run the application from the native executable:
281+
282+
```shell
283+
./build/native/nativeCompile/h2example
284+
```
285+
286+
6. (Optional) To clean up the project, run `./gradlew clean`, and delete the directory _META-INF_ with its contents.
287+
288+
### Summary
289+
290+
This guide demonstrated how to build a native executable using the [GraalVM Reachability Metadata Repository](https://github.com/oracle/graalvm-reachability-metadata) and with the Tracing agent. The goal was to show the difference, and prove how using the reachability metadata can simplify the work.
291+
292+
Note that if your application does not call any dynamic features at run time, enabling the GraalVM Reachability Metadata Repository is needless.
293+
Your workflow in that case would just be:
294+
```shell
295+
./gradlew nativeRun
296+
```
297+
298+
### Related Documentation
299+
300+
- [Reachability Metadata](../ReachabilityMetadata.md)
301+
- [Native Image Build Tools](https://graalvm.github.io/native-build-tools/latest/index.html)
302+
- [Collect Metadata with the Tracing Agent](../AutomaticMetadataCollection.md#tracing-agent)

0 commit comments

Comments
 (0)