Skip to content

Commit cf9acbe

Browse files
committed
Test against GraalVM native image
Added an example of a native application that uses the driver and is built using GraalVM. The application runs the existing tour applications for the sync, legacy and reactive driver APIs. JAVA-3580
1 parent ddd5c5b commit cf9acbe

24 files changed

+1135
-38
lines changed

.evergreen/.evg.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,15 @@ functions:
882882
params:
883883
file: src/results.json
884884

885+
"run graalvm native image app":
886+
- command: shell.exec
887+
type: test
888+
params:
889+
working_dir: "src"
890+
script: |
891+
${PREPARE_SHELL}
892+
JAVA_VERSION="${JAVA_VERSION}" .evergreen/run-graalvm-native-image-app.sh
893+
885894
# Anchors
886895

887896
pre:
@@ -1641,6 +1650,10 @@ tasks:
16411650
VERSION: latest
16421651
TOPOLOGY: replica_set
16431652
- func: run socks5 tests
1653+
- name: "graalvm-native-image-app"
1654+
commands:
1655+
- func: "bootstrap mongo-orchestration"
1656+
- func: "run graalvm native image app"
16441657
axes:
16451658
- id: version
16461659
display_name: MongoDB Version
@@ -2286,3 +2299,10 @@ buildvariants:
22862299
tasks:
22872300
- name: testazurekms_task_group
22882301
batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README
2302+
2303+
- matrix_name: "graalvm-native-image-app"
2304+
matrix_spec: { version: [ "5.0", "7.0" ], topology: [ "replicaset" ], auth: [ "noauth" ], ssl: [ "nossl" ],
2305+
jdk: [ "jdk21" ], os: [ "linux" ] }
2306+
display_name: "GraalVM native image app: ${version} ${topology} ${auth} ${ssl} ${jdk} ${os}"
2307+
tasks:
2308+
- name: "graalvm-native-image-app"

.evergreen/javaConfig.bash

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ export JDK8="/opt/java/jdk8"
44
export JDK11="/opt/java/jdk11"
55
export JDK17="/opt/java/jdk17"
66
export JDK21="/opt/java/jdk21"
7+
# note that `JDK21_GRAALVM` is used in `run-graalvm-native-image-app.sh`
8+
# by dynamically constructing the variable name
9+
export JDK21_GRAALVM="/opt/java/jdk21-graalce"
710

811
if [ -d "$JDK17" ]; then
912
export JAVA_HOME=$JDK17
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/bin/bash
2+
3+
# Supported/used environment variables:
4+
# MONGODB_URI The connection string to use, including credentials and topology info.
5+
# JAVA_VERSION The Java SE version for Gradle toolchain.
6+
7+
set -o errexit
8+
9+
echo "MONGODB_URI=${MONGODB_URI}"
10+
echo "JAVA_HOME=${JAVA_HOME}"
11+
12+
RELATIVE_DIR_PATH="$(dirname "${BASH_SOURCE[0]:-$0}")"
13+
source "${RELATIVE_DIR_PATH}/javaConfig.bash"
14+
15+
readonly JDK_GRAALVM_VAR_NAME="JDK${JAVA_VERSION}_GRAALVM"
16+
readonly JDK_GRAALVM="${!JDK_GRAALVM_VAR_NAME}"
17+
echo "The JDK distribution for running Gradle is"
18+
echo "$("${JAVA_HOME}"/bin/java --version)"
19+
echo "The Java SE version for the Gradle toolchain is ${JAVA_VERSION}"
20+
echo "The GraalVM JDK distribution expected to be found at \`${JDK_GRAALVM}\` by the Gradle toolchain functionality is"
21+
echo "$("${JDK_GRAALVM}"/bin/java --version)"
22+
echo "The Gradle version is"
23+
./gradlew --version
24+
25+
echo "Building and running the GraalVM native image app"
26+
./gradlew -PjavaVersion=${JAVA_VERSION} -Dorg.mongodb.test.uri=${MONGODB_URI} :graalvm-native-image-app:nativeRun

driver-reactive-streams/src/examples/reactivestreams/tour/ClientSideEncryptionAutoEncryptionSettingsTour.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,14 @@ public static void main(final String[] args) {
6464
}});
6565
}};
6666

67+
MongoClientSettings commonClientSettings = (
68+
args.length == 0
69+
? MongoClientSettings.builder()
70+
: MongoClientSettings.builder().applyConnectionString(new ConnectionString(args[0])))
71+
.build();
6772
String keyVaultNamespace = "admin.datakeys";
6873
ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder()
69-
.keyVaultMongoClientSettings(MongoClientSettings.builder()
70-
.applyConnectionString(new ConnectionString("mongodb://localhost"))
71-
.build())
74+
.keyVaultMongoClientSettings(commonClientSettings)
7275
.keyVaultNamespace(keyVaultNamespace)
7376
.kmsProviders(kmsProviders)
7477
.build();
@@ -107,7 +110,7 @@ public static void main(final String[] args) {
107110
+ "}"));
108111
}}).build();
109112

110-
MongoClientSettings clientSettings = MongoClientSettings.builder()
113+
MongoClientSettings clientSettings = MongoClientSettings.builder(commonClientSettings)
111114
.autoEncryptionSettings(autoEncryptionSettings)
112115
.build();
113116

driver-reactive-streams/src/examples/reactivestreams/tour/ClientSideEncryptionExplicitEncryptionAndDecryptionTour.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,12 @@ public static void main(final String[] args) {
6969
}};
7070

7171
MongoNamespace keyVaultNamespace = new MongoNamespace("encryption.testKeyVault");
72-
73-
MongoClientSettings clientSettings = MongoClientSettings.builder().build();
74-
MongoClient mongoClient = MongoClients.create(clientSettings);
72+
MongoClientSettings commonClientSettings = (
73+
args.length == 0
74+
? MongoClientSettings.builder()
75+
: MongoClientSettings.builder().applyConnectionString(new ConnectionString(args[0])))
76+
.build();
77+
MongoClient mongoClient = MongoClients.create(commonClientSettings);
7578

7679
// Set up the key vault for this example
7780
MongoCollection<Document> keyVaultCollection = mongoClient.getDatabase(keyVaultNamespace.getDatabaseName())
@@ -96,9 +99,7 @@ public static void main(final String[] args) {
9699

97100
// Create the ClientEncryption instance
98101
ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder()
99-
.keyVaultMongoClientSettings(MongoClientSettings.builder()
100-
.applyConnectionString(new ConnectionString("mongodb://localhost"))
101-
.build())
102+
.keyVaultMongoClientSettings(commonClientSettings)
102103
.keyVaultNamespace(keyVaultNamespace.getFullName())
103104
.kmsProviders(kmsProviders)
104105
.build();

driver-reactive-streams/src/examples/reactivestreams/tour/ClientSideEncryptionExplicitEncryptionOnlyTour.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,12 @@ public static void main(final String[] args) {
6969
}};
7070

7171
MongoNamespace keyVaultNamespace = new MongoNamespace("encryption.testKeyVault");
72-
73-
MongoClientSettings clientSettings = MongoClientSettings.builder()
72+
MongoClientSettings commonClientSettings = (
73+
args.length == 0
74+
? MongoClientSettings.builder()
75+
: MongoClientSettings.builder().applyConnectionString(new ConnectionString(args[0])))
76+
.build();
77+
MongoClientSettings clientSettings = MongoClientSettings.builder(commonClientSettings)
7478
.autoEncryptionSettings(AutoEncryptionSettings.builder()
7579
.keyVaultNamespace(keyVaultNamespace.getFullName())
7680
.kmsProviders(kmsProviders)
@@ -102,9 +106,7 @@ public static void main(final String[] args) {
102106

103107
// Create the ClientEncryption instance
104108
ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder()
105-
.keyVaultMongoClientSettings(MongoClientSettings.builder()
106-
.applyConnectionString(new ConnectionString("mongodb://localhost"))
107-
.build())
109+
.keyVaultMongoClientSettings(commonClientSettings)
108110
.keyVaultNamespace(keyVaultNamespace.getFullName())
109111
.kmsProviders(kmsProviders)
110112
.build();

driver-reactive-streams/src/examples/reactivestreams/tour/ClientSideEncryptionSimpleTour.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package reactivestreams.tour;
1919

2020
import com.mongodb.AutoEncryptionSettings;
21+
import com.mongodb.ConnectionString;
2122
import com.mongodb.MongoClientSettings;
2223
import com.mongodb.client.result.InsertOneResult;
2324
import com.mongodb.reactivestreams.client.MongoClient;
@@ -64,7 +65,10 @@ public static void main(final String[] args) {
6465
.kmsProviders(kmsProviders)
6566
.build();
6667

67-
MongoClientSettings clientSettings = MongoClientSettings.builder()
68+
MongoClientSettings clientSettings = (
69+
args.length == 0
70+
? MongoClientSettings.builder()
71+
: MongoClientSettings.builder().applyConnectionString(new ConnectionString(args[0])))
6872
.autoEncryptionSettings(autoEncryptionSettings)
6973
.build();
7074

driver-sync/src/examples/documentation/CausalConsistencyExamples.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package documentation;
1818

1919
import com.mongodb.ClientSessionOptions;
20+
import com.mongodb.ConnectionString;
21+
import com.mongodb.MongoClientSettings;
2022
import com.mongodb.ReadConcern;
2123
import com.mongodb.ReadPreference;
2224
import com.mongodb.WriteConcern;
@@ -42,8 +44,13 @@ public final class CausalConsistencyExamples {
4244
* @param args takes an optional single argument for the connection string
4345
*/
4446
public static void main(final String[] args) {
45-
setupDatabase();
46-
MongoClient client = MongoClients.create();
47+
MongoClientSettings clientSettings = (
48+
args.length == 0
49+
? MongoClientSettings.builder()
50+
: MongoClientSettings.builder().applyConnectionString(new ConnectionString(args[0])))
51+
.build();
52+
setupDatabase(clientSettings);
53+
MongoClient client = MongoClients.create(clientSettings);
4754

4855
// Start Causal Consistency Example 1
4956
// Example 1: Use a causally consistent session to ensure that the update occurs before the insert.
@@ -81,8 +88,8 @@ public static void main(final String[] args) {
8188
// End Causal Consistency Example 2
8289
}
8390

84-
private static void setupDatabase() {
85-
MongoClient client = MongoClients.create();
91+
private static void setupDatabase(final MongoClientSettings clientSettings) {
92+
MongoClient client = MongoClients.create(clientSettings);
8693
client.getDatabase("test").drop();
8794

8895
MongoDatabase database = client.getDatabase("test");

driver-sync/src/examples/tour/ClientSideEncryptionAutoEncryptionSettingsTour.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,14 @@ public static void main(final String[] args) {
5858
put("key", localMasterKey);
5959
}});
6060
}};
61-
61+
MongoClientSettings commonClientSettings = (
62+
args.length == 0
63+
? MongoClientSettings.builder()
64+
: MongoClientSettings.builder().applyConnectionString(new ConnectionString(args[0])))
65+
.build();
6266
String keyVaultNamespace = "encryption.__keyVault";
6367
ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder()
64-
.keyVaultMongoClientSettings(MongoClientSettings.builder()
65-
.applyConnectionString(new ConnectionString("mongodb://localhost"))
66-
.build())
68+
.keyVaultMongoClientSettings(commonClientSettings)
6769
.keyVaultNamespace(keyVaultNamespace)
6870
.kmsProviders(kmsProviders)
6971
.build();
@@ -99,7 +101,7 @@ public static void main(final String[] args) {
99101
+ "}"));
100102
}}).build();
101103

102-
MongoClientSettings clientSettings = MongoClientSettings.builder()
104+
MongoClientSettings clientSettings = MongoClientSettings.builder(commonClientSettings)
103105
.autoEncryptionSettings(autoEncryptionSettings)
104106
.build();
105107

driver-sync/src/examples/tour/ClientSideEncryptionExplicitEncryptionAndDecryptionTour.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,12 @@ public static void main(final String[] args) {
6060
put("key", localMasterKey);
6161
}});
6262
}};
63-
64-
MongoClientSettings clientSettings = MongoClientSettings.builder().build();
65-
MongoClient mongoClient = MongoClients.create(clientSettings);
63+
MongoClientSettings commonClientSettings = (
64+
args.length == 0
65+
? MongoClientSettings.builder()
66+
: MongoClientSettings.builder().applyConnectionString(new ConnectionString(args[0])))
67+
.build();
68+
MongoClient mongoClient = MongoClients.create(commonClientSettings);
6669

6770
// Set up the key vault for this example
6871
MongoNamespace keyVaultNamespace = new MongoNamespace("encryption.testKeyVault");
@@ -81,9 +84,7 @@ public static void main(final String[] args) {
8184

8285
// Create the ClientEncryption instance
8386
ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder()
84-
.keyVaultMongoClientSettings(MongoClientSettings.builder()
85-
.applyConnectionString(new ConnectionString("mongodb://localhost"))
86-
.build())
87+
.keyVaultMongoClientSettings(commonClientSettings)
8788
.keyVaultNamespace(keyVaultNamespace.getFullName())
8889
.kmsProviders(kmsProviders)
8990
.build();

driver-sync/src/examples/tour/ClientSideEncryptionExplicitEncryptionOnlyTour.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,12 @@ public static void main(final String[] args) {
6262
}};
6363

6464
MongoNamespace keyVaultNamespace = new MongoNamespace("encryption.testKeyVault");
65-
66-
MongoClientSettings clientSettings = MongoClientSettings.builder()
65+
MongoClientSettings commonClientSettings = (
66+
args.length == 0
67+
? MongoClientSettings.builder()
68+
: MongoClientSettings.builder().applyConnectionString(new ConnectionString(args[0])))
69+
.build();
70+
MongoClientSettings clientSettings = MongoClientSettings.builder(commonClientSettings)
6771
.autoEncryptionSettings(AutoEncryptionSettings.builder()
6872
.keyVaultNamespace(keyVaultNamespace.getFullName())
6973
.kmsProviders(kmsProviders)
@@ -87,9 +91,7 @@ public static void main(final String[] args) {
8791

8892
// Create the ClientEncryption instance
8993
ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder()
90-
.keyVaultMongoClientSettings(MongoClientSettings.builder()
91-
.applyConnectionString(new ConnectionString("mongodb://localhost"))
92-
.build())
94+
.keyVaultMongoClientSettings(commonClientSettings)
9395
.keyVaultNamespace(keyVaultNamespace.getFullName())
9496
.kmsProviders(kmsProviders)
9597
.build();

driver-sync/src/examples/tour/ClientSideEncryptionSimpleTour.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package tour;
1818

1919
import com.mongodb.AutoEncryptionSettings;
20+
import com.mongodb.ConnectionString;
2021
import com.mongodb.MongoClientSettings;
2122
import com.mongodb.client.MongoClient;
2223
import com.mongodb.client.MongoClients;
@@ -59,7 +60,10 @@ public static void main(final String[] args) {
5960
.kmsProviders(kmsProviders)
6061
.build();
6162

62-
MongoClientSettings clientSettings = MongoClientSettings.builder()
63+
MongoClientSettings clientSettings = (
64+
args.length == 0
65+
? MongoClientSettings.builder()
66+
: MongoClientSettings.builder().applyConnectionString(new ConnectionString(args[0])))
6367
.autoEncryptionSettings(autoEncryptionSettings)
6468
.build();
6569

graalvm-native-image-app/build.gradle

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
plugins {
18+
id 'application'
19+
id 'org.graalvm.buildtools.native' version '0.9.23'
20+
}
21+
22+
application {
23+
mainClass = 'com.mongodb.internal.graalvm.NativeImageApp'
24+
}
25+
26+
// see https://graalvm.github.io/native-build-tools/latest/gradle-plugin.html
27+
graalvmNative {
28+
metadataRepository {
29+
enabled = true
30+
version = '0.3.6'
31+
}
32+
agent {
33+
// Executing the `run` Gradle task with the tracing agent
34+
// https://www.graalvm.org/latest/reference-manual/native-image/metadata/AutomaticMetadataCollection/
35+
// requires running Gradle with GraalVM despite the toolchain for the task already being GraalVM.
36+
// The same is true about executing the `metadataCopy` Gradle task.
37+
// This may be a manifestation of an issue with the `org.graalvm.buildtools.native` plugin.
38+
enabled = false
39+
defaultMode = 'standard'
40+
metadataCopy {
41+
inputTaskNames.add('run')
42+
outputDirectories.add('src/main/resources/META-INF/native-image')
43+
mergeWithExisting = false
44+
}
45+
}
46+
binaries {
47+
configureEach {
48+
buildArgs.add('--strict-image-heap')
49+
// see class initialization report is generated at `graalvm/build/native/nativeCompile/reports`,
50+
// informing us on the kind of initialization for each Java class
51+
buildArgs.add('-H:+PrintClassInitialization')
52+
// see the "registerResource" entries in the `native-image` built-time output,
53+
// informing us on the resources included in the native image being built
54+
buildArgs.add('-H:Log=registerResource:5')
55+
}
56+
main {
57+
sharedLibrary = false
58+
def mainClassName = application.mainClass.get()
59+
imageName = mainClassName.substring(mainClassName.lastIndexOf('.') + 1, mainClassName.length())
60+
quickBuild = true
61+
// See the "Apply" entries in the `native-image` built-time output, informing us on
62+
// the build configuration files (https://www.graalvm.org/latest/reference-manual/native-image/overview/BuildConfiguration/)
63+
// and the reachability metadata files (https://www.graalvm.org/latest/reference-manual/native-image/metadata/)
64+
// which are applied at build time.
65+
verbose = true
66+
}
67+
}
68+
}
69+
70+
dependencies {
71+
// we intentionally depend here on the driver artifacts instead of depending on compiled classes
72+
implementation project(path:':bson', configuration:'archives')
73+
implementation project(path:':driver-core', configuration:'archives')
74+
implementation project(path:':driver-sync', configuration:'archives')
75+
implementation project(path:':driver-reactive-streams', configuration:'archives')
76+
implementation project(path:':driver-legacy', configuration:'archives')
77+
// note that as a result of these `sourceSets` dependencies, `driver-sync/src/test/resources/logback-test.xml` is used
78+
implementation project(':driver-core').sourceSets.test.output
79+
implementation project(':driver-sync').sourceSets.test.output
80+
implementation project(':driver-legacy').sourceSets.test.output
81+
implementation project(':driver-reactive-streams').sourceSets.test.output
82+
implementation "org.mongodb:mongodb-crypt:$mongoCryptVersion"
83+
implementation 'org.slf4j:slf4j-api:2.0.12'
84+
implementation 'ch.qos.logback:logback-classic:1.5.3'
85+
implementation platform("io.projectreactor:reactor-bom:$projectReactorVersion")
86+
implementation 'io.projectreactor:reactor-core'
87+
}

0 commit comments

Comments
 (0)