Skip to content

Commit 92d0292

Browse files
authored
fix: add native image configurations for Spanner classes (#1858)
* fix: add native image configurations for Spanner classes
1 parent 67863d8 commit 92d0292

File tree

14 files changed

+242
-43
lines changed

14 files changed

+242
-43
lines changed

.github/workflows/ci.yaml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,22 @@ jobs:
8383
java: [8, 11, 17]
8484
steps:
8585
- uses: actions/checkout@v3
86+
# For Java 8 tests, use JDK 11 to compile
87+
- if: ${{matrix.java}} == '8'
88+
uses: actions/setup-java@v3
89+
with:
90+
java-version: 11
91+
distribution: zulu
92+
- if: ${{matrix.java}} == '8'
93+
run: echo "JAVA11_HOME=${JAVA_HOME}" >> $GITHUB_ENV
94+
shell: bash
8695
- uses: actions/setup-java@v3
8796
with:
8897
distribution: zulu
8998
java-version: ${{matrix.java}}
99+
- if: ${{matrix.java}} == '8'
100+
run: echo "JAVA8_HOME=${JAVA_HOME}" >> $GITHUB_ENV
101+
shell: bash
90102
- run: java -version
91103
- run: .kokoro/dependencies.sh
92104
lint:
@@ -108,7 +120,7 @@ jobs:
108120
- uses: actions/setup-java@v3
109121
with:
110122
distribution: zulu
111-
java-version: 8
123+
java-version: 11
112124
- run: java -version
113125
- run: .kokoro/build.sh
114126
env:

.github/workflows/integration-tests-against-emulator.yaml

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,27 @@ jobs:
2020
- uses: stCarolas/setup-maven@v4
2121
with:
2222
maven-version: 3.8.1
23-
- uses: actions/setup-java@v1
23+
# Build with JDK 11 and run tests with JDK 8
24+
- uses: actions/setup-java@v3
25+
with:
26+
java-version: 11
27+
distribution: zulu
28+
- run: echo "JAVA11_HOME=${JAVA_HOME}" >> $GITHUB_ENV
29+
shell: bash
30+
- uses: actions/setup-java@v3
2431
with:
2532
java-version: 8
33+
distribution: zulu
34+
- run: echo "JAVA8_HOME=${JAVA_HOME}" >> $GITHUB_ENV
35+
shell: bash
2636
- run: java -version
27-
- run: .kokoro/build.sh
28-
- run: mvn -B -Dspanner.testenv.instance="" -Penable-integration-tests -DtrimStackTrace=false -Dclirr.skip=true -Denforcer.skip=true -fae verify
37+
- name: Compiling main library
38+
run: .kokoro/build.sh
39+
- name: Running tests
40+
run: |
41+
mvn -B -Dspanner.testenv.instance="" -Penable-integration-tests \
42+
-DtrimStackTrace=false -Dclirr.skip=true -Denforcer.skip=true \
43+
-Dmaven.main.skip=true -fae verify
2944
env:
3045
JOB_TYPE: test
3146
SPANNER_EMULATOR_HOST: localhost:9010

.github/workflows/samples.yaml

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,39 @@ jobs:
1212
- name: Run checkstyle
1313
run: mvn -P lint --quiet --batch-mode checkstyle:check
1414
working-directory: samples/snippets
15+
compile-java8:
16+
name: "compile (8)"
17+
runs-on: ubuntu-latest
18+
steps:
19+
- uses: actions/checkout@v2
20+
- uses: actions/setup-java@v3
21+
with:
22+
# Java 11 to generate class file targeting Java 8
23+
java-version: 11
24+
distribution: zulu
25+
- name: Compile Spanner
26+
run: mvn clean install
27+
- uses: actions/setup-java@v3
28+
with:
29+
java-version: 8
30+
distribution: zulu
31+
- name: Compile samples
32+
run: mvn compile
33+
working-directory: samples
34+
1535
compile:
1636
runs-on: ubuntu-latest
1737
strategy:
1838
matrix:
19-
java: [8, 11]
39+
java: [11, 17]
2040
steps:
2141
- uses: actions/checkout@v2
22-
- uses: actions/setup-java@v1
42+
- uses: actions/setup-java@v3
2343
with:
2444
java-version: ${{matrix.java}}
45+
distribution: zulu
2546
- name: Compile Spanner
2647
run: mvn clean install
2748
- name: Compile samples
2849
run: mvn compile
29-
working-directory: samples
50+
working-directory: samples

.kokoro/build.sh

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@ cd ${scriptDir}/..
2323
# include common functions
2424
source ${scriptDir}/common.sh
2525

26-
function setJava() {
27-
export JAVA_HOME=$1
28-
export PATH=${JAVA_HOME}/bin:$PATH
29-
}
30-
3126
# units-java8 uses both JDK 11 and JDK 8. GraalVM dependencies require JDK 11 to
3227
# compile the classes touching GraalVM classes.
3328
if [ ! -z "${JAVA11_HOME}" ]; then
@@ -57,7 +52,6 @@ fi
5752
# are compatible with Java 8 when running tests.
5853
if [ ! -z "${JAVA8_HOME}" ]; then
5954
setJava "${JAVA8_HOME}"
60-
mvn -version
6155
fi
6256

6357
RETURN_CODE=0
@@ -87,10 +81,21 @@ integration)
8781
-DtrimStackTrace=false \
8882
-Dclirr.skip=true \
8983
-Denforcer.skip=true \
84+
-Dmaven.main.skip=true \
9085
-fae \
9186
verify
9287
RETURN_CODE=$?
9388
;;
89+
graalvm)
90+
# Run Unit and Integration Tests with Native Image
91+
mvn test -Pnative -Penable-integration-tests
92+
RETURN_CODE=$?
93+
;;
94+
graalvm17)
95+
# Run Unit and Integration Tests with Native Image
96+
mvn test -Pnative -Penable-integration-tests
97+
RETURN_CODE=$?
98+
;;
9499
slowtests)
95100
mvn -B ${INTEGRATION_TEST_ARGS} \
96101
-ntp \
@@ -100,6 +105,7 @@ slowtests)
100105
-DtrimStackTrace=false \
101106
-Dclirr.skip=true \
102107
-Denforcer.skip=true \
108+
-Dmaven.main.skip=true \
103109
-fae \
104110
verify
105111
RETURN_CODE=$?

.kokoro/common.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,9 @@ function retry_with_backoff {
5555
## Helper functionss
5656
function now() { date +"%Y-%m-%d %H:%M:%S" | tr -d '\n'; }
5757
function msg() { println "$*" >&2; }
58-
function println() { printf '%s\n' "$(now) $*"; }
58+
function println() { printf '%s\n' "$(now) $*"; }
59+
60+
function setJava() {
61+
export JAVA_HOME=$1
62+
export PATH=${JAVA_HOME}/bin:$PATH
63+
}

.kokoro/dependencies.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,21 @@ function determineMavenOpts() {
4949

5050
export MAVEN_OPTS=$(determineMavenOpts)
5151

52+
if [ ! -z "${JAVA11_HOME}" ]; then
53+
setJava "${JAVA11_HOME}"
54+
fi
55+
5256
# this should run maven enforcer
5357
retry_with_backoff 3 10 \
5458
mvn install -B -V -ntp \
5559
-DskipTests=true \
5660
-Dmaven.javadoc.skip=true \
5761
-Dclirr.skip=true
5862

63+
if [ ! -z "${JAVA8_HOME}" ]; then
64+
setJava "${JAVA8_HOME}"
65+
fi
66+
5967
mvn -B dependency:analyze -DfailOnWarning=true
6068

6169
echo "****************** DEPENDENCY LIST COMPLETENESS CHECK *******************"

google-cloud-spanner/pom.xml

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<properties>
1717
<site.installationModule>google-cloud-spanner</site.installationModule>
1818
<opencensus.version>0.31.1</opencensus.version>
19+
<graalvm.version>22.1.0</graalvm.version>
1920
<spanner.testenv.config.class>com.google.cloud.spanner.GceTestEnvConfig</spanner.testenv.config.class>
2021
<spanner.testenv.instance>projects/gcloud-devel/instances/spanner-testing-east1</spanner.testenv.instance>
2122
<spanner.gce.config.project_id>gcloud-devel</spanner.gce.config.project_id>
@@ -56,6 +57,16 @@
5657
<id>default-test</id>
5758
<configuration>
5859
<excludedGroups>com.google.cloud.spanner.TracerTest,com.google.cloud.spanner.IntegrationTest</excludedGroups>
60+
61+
<!-- These configurations are only needed for native tests. The native profile
62+
in shared-config uses maven-surefire to run both unit tests and integration tests -->
63+
<systemPropertyVariables>
64+
<spanner.testenv.config.class>${spanner.testenv.config.class}</spanner.testenv.config.class>
65+
<spanner.testenv.instance>${spanner.testenv.instance}</spanner.testenv.instance>
66+
<spanner.gce.config.project_id>${spanner.gce.config.project_id}</spanner.gce.config.project_id>
67+
<spanner.testenv.kms_key.name>${spanner.testenv.kms_key.name}</spanner.testenv.kms_key.name>
68+
</systemPropertyVariables>
69+
5970
</configuration>
6071
</execution>
6172
<execution>
@@ -105,20 +116,6 @@
105116
</executions>
106117
</plugin>
107118

108-
<!--Configurations for native image tests-->
109-
<plugin>
110-
<groupId>org.apache.maven.plugins</groupId>
111-
<artifactId>maven-surefire-plugin</artifactId>
112-
<configuration>
113-
<systemPropertyVariables>
114-
<spanner.testenv.config.class>${spanner.testenv.config.class}</spanner.testenv.config.class>
115-
<spanner.testenv.instance>${spanner.testenv.instance}</spanner.testenv.instance>
116-
<spanner.gce.config.project_id>${spanner.gce.config.project_id}</spanner.gce.config.project_id>
117-
<spanner.testenv.kms_key.name>${spanner.testenv.kms_key.name}</spanner.testenv.kms_key.name>
118-
</systemPropertyVariables>
119-
<forkedProcessTimeoutInSeconds>3000</forkedProcessTimeoutInSeconds>
120-
</configuration>
121-
</plugin>
122119
<plugin>
123120
<groupId>org.graalvm.buildtools</groupId>
124121
<artifactId>native-maven-plugin</artifactId>
@@ -142,7 +139,7 @@
142139
<groupId>org.apache.maven.plugins</groupId>
143140
<artifactId>maven-dependency-plugin</artifactId>
144141
<configuration>
145-
<ignoredDependencies>io.grpc:grpc-protobuf-lite,org.hamcrest:hamcrest,org.hamcrest:hamcrest-core,com.google.errorprone:error_prone_annotations,org.openjdk.jmh:jmh-generator-annprocess,com.google.api.grpc:grpc-google-cloud-spanner-v1,com.google.api.grpc:grpc-google-cloud-spanner-admin-instance-v1,com.google.api.grpc:grpc-google-cloud-spanner-admin-database-v1,javax.annotation:javax.annotation-api,io.opencensus:opencensus-impl</ignoredDependencies>
142+
<ignoredDependencies>io.grpc:grpc-protobuf-lite,org.hamcrest:hamcrest,org.hamcrest:hamcrest-core,com.google.errorprone:error_prone_annotations,org.openjdk.jmh:jmh-generator-annprocess,com.google.api.grpc:grpc-google-cloud-spanner-v1,com.google.api.grpc:grpc-google-cloud-spanner-admin-instance-v1,com.google.api.grpc:grpc-google-cloud-spanner-admin-database-v1,javax.annotation:javax.annotation-api,io.opencensus:opencensus-impl,org.graalvm.sdk:graal-sdk</ignoredDependencies>
146143
</configuration>
147144
</plugin>
148145
</plugins>
@@ -296,6 +293,13 @@
296293
<artifactId>grpc-alts</artifactId>
297294
</dependency>
298295

296+
<dependency>
297+
<groupId>org.graalvm.sdk</groupId>
298+
<artifactId>graal-sdk</artifactId>
299+
<version>${graalvm.version}</version>
300+
<scope>provided</scope>
301+
</dependency>
302+
299303
<!-- Test dependencies -->
300304
<dependency>
301305
<groupId>junit</groupId>
@@ -352,6 +356,7 @@
352356
<version>1.35</version>
353357
<scope>test</scope>
354358
</dependency>
359+
355360
</dependencies>
356361

357362
<profiles>
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright 2022 Google LLC
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+
* https://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+
package com.google.cloud.spanner.nativeimage;
18+
19+
import com.google.api.gax.nativeimage.NativeImageUtils;
20+
import org.graalvm.nativeimage.hosted.Feature;
21+
22+
/** Registers Spanner library classes for reflection. */
23+
final class SpannerFeature implements Feature {
24+
25+
private static final String SPANNER_CLASS = "com.google.spanner.v1.SpannerGrpc";
26+
private static final String SPANNER_TEST_CLASS = "com.google.cloud.spanner.GceTestEnvConfig";
27+
private static final String MOCK_CLASS = "com.google.cloud.spanner.MockDatabaseAdminServiceImpl";
28+
private static final String CLIENT_SIDE_IMPL_CLASS =
29+
"com.google.cloud.spanner.connection.ClientSideStatementImpl";
30+
private static final String CLIENT_SIDE_VALUE_CONVERTER =
31+
"com.google.cloud.spanner.connection.ClientSideStatementValueConverters";
32+
private static final String CONNECTION_IMPL =
33+
"com.google.cloud.spanner.connection.ConnectionImpl";
34+
private static final String CLIENT_SIDE_STATEMENTS =
35+
"com.google.cloud.spanner.connection.ClientSideStatements";
36+
private static final String CONNECTION_STATEMENT_EXECUTOR =
37+
"com.google.cloud.spanner.connection.ConnectionStatementExecutor";
38+
private static final String CLIENT_SIDE_STATEMENT_NO_PARAM_EXECUTOR =
39+
"com.google.cloud.spanner.connection.ClientSideStatementNoParamExecutor";
40+
private static final String CLIENT_SIDE_STATEMENT_SET_EXECUTOR =
41+
"com.google.cloud.spanner.connection.ClientSideStatementSetExecutor";
42+
private static final String CLIENT_SIDE_STATEMENT_PG_EXECUTOR =
43+
"com.google.cloud.spanner.connection.ClientSideStatementPgBeginExecutor";
44+
private static final String ABSTRACT_STATEMENT_PARSER =
45+
"com.google.cloud.spanner.connection.AbstractStatementParser";
46+
private static final String STATEMENT_PARSER =
47+
"com.google.cloud.spanner.connection.SpannerStatementParser";
48+
private static final String POSTGRESQL_STATEMENT_PARSER =
49+
"com.google.cloud.spanner.connection.PostgreSQLStatementParser";
50+
private static final String STATEMENT_RESULT =
51+
"com.google.cloud.spanner.connection.StatementResult$ResultType";
52+
53+
@Override
54+
public void beforeAnalysis(BeforeAnalysisAccess access) {
55+
registerSpannerTestClasses(access);
56+
if (access.findClassByName(CLIENT_SIDE_IMPL_CLASS) != null) {
57+
NativeImageUtils.registerClassHierarchyForReflection(access, CLIENT_SIDE_IMPL_CLASS);
58+
}
59+
if (access.findClassByName(CLIENT_SIDE_STATEMENT_NO_PARAM_EXECUTOR) != null) {
60+
NativeImageUtils.registerClassForReflection(access, CLIENT_SIDE_STATEMENT_NO_PARAM_EXECUTOR);
61+
}
62+
if (access.findClassByName(CLIENT_SIDE_STATEMENT_PG_EXECUTOR) != null) {
63+
NativeImageUtils.registerClassForReflection(access, CLIENT_SIDE_STATEMENT_PG_EXECUTOR);
64+
}
65+
if (access.findClassByName(CLIENT_SIDE_STATEMENT_SET_EXECUTOR) != null) {
66+
NativeImageUtils.registerClassForReflection(access, CLIENT_SIDE_STATEMENT_SET_EXECUTOR);
67+
}
68+
if (access.findClassByName(CLIENT_SIDE_VALUE_CONVERTER) != null) {
69+
NativeImageUtils.registerClassHierarchyForReflection(access, CLIENT_SIDE_VALUE_CONVERTER);
70+
}
71+
if (access.findClassByName(CLIENT_SIDE_STATEMENTS) != null) {
72+
NativeImageUtils.registerClassForReflection(access, CLIENT_SIDE_STATEMENTS);
73+
}
74+
if (access.findClassByName(CONNECTION_STATEMENT_EXECUTOR) != null) {
75+
NativeImageUtils.registerClassForReflection(access, CONNECTION_STATEMENT_EXECUTOR);
76+
}
77+
if (access.findClassByName(CONNECTION_IMPL) != null) {
78+
NativeImageUtils.registerClassForReflection(access, CONNECTION_IMPL);
79+
}
80+
if (access.findClassByName(ABSTRACT_STATEMENT_PARSER) != null) {
81+
NativeImageUtils.registerClassHierarchyForReflection(access, ABSTRACT_STATEMENT_PARSER);
82+
NativeImageUtils.registerClassForReflection(access, "com.google.cloud.spanner.Dialect");
83+
}
84+
if (access.findClassByName(STATEMENT_PARSER) != null) {
85+
NativeImageUtils.registerConstructorsForReflection(access, STATEMENT_PARSER);
86+
}
87+
if (access.findClassByName(POSTGRESQL_STATEMENT_PARSER) != null) {
88+
NativeImageUtils.registerConstructorsForReflection(access, POSTGRESQL_STATEMENT_PARSER);
89+
}
90+
if (access.findClassByName(STATEMENT_RESULT) != null) {
91+
NativeImageUtils.registerClassForReflection(access, STATEMENT_RESULT);
92+
}
93+
94+
Class<?> spannerClass = access.findClassByName(SPANNER_CLASS);
95+
if (spannerClass != null) {
96+
NativeImageUtils.registerClassHierarchyForReflection(
97+
access, "com.google.spanner.admin.database.v1.Database");
98+
NativeImageUtils.registerClassHierarchyForReflection(
99+
access, "com.google.spanner.admin.instance.v1.Instance");
100+
NativeImageUtils.registerClassForReflection(
101+
access, "com.google.spanner.admin.database.v1.RestoreInfo");
102+
}
103+
}
104+
105+
private void registerSpannerTestClasses(BeforeAnalysisAccess access) {
106+
Class<?> spannerTestClass = access.findClassByName(SPANNER_TEST_CLASS);
107+
if (spannerTestClass != null) {
108+
NativeImageUtils.registerConstructorsForReflection(access, SPANNER_TEST_CLASS);
109+
}
110+
Class<?> mockClass = access.findClassByName(MOCK_CLASS);
111+
if (mockClass != null) {
112+
NativeImageUtils.registerClassForReflection(
113+
access, "com.google.cloud.spanner.MockDatabaseAdminServiceImpl$MockBackup");
114+
}
115+
}
116+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Args = --initialize-at-build-time=com.google.cloud.spanner.IntegrationTestEnv,\
2+
org.junit.experimental.categories.CategoryValidator,\
3+
org.junit.validator.AnnotationValidator \
4+
--features=com.google.cloud.spanner.nativeimage.SpannerFeature
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"resources": [
3+
{"pattern": "\\Qcom/google/cloud/spanner/connection/ClientSideStatements.json\\E"},
4+
{"pattern": "\\Qcom/google/cloud/spanner/connection/PG_ClientSideStatements.json\\E"},
5+
{"pattern": "\\Qcom/google/cloud/spanner/spi/v1/grpc-gcp-apiconfig.json\\E"},
6+
{"pattern": "\\Qcom/google/cloud/spanner/connection/ITSqlScriptTest_TestQueryOptions.sql\\E"}
7+
]
8+
}

0 commit comments

Comments
 (0)