|
| 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 | +  |
| 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