From ef187f4fccaf1c5550e9f6795228e6c7361030db Mon Sep 17 00:00:00 2001 From: Mridula <66699525+mpeddada1@users.noreply.github.com> Date: Thu, 2 Jun 2022 10:44:58 -0400 Subject: [PATCH] docs(sample): relocate native image sample from old repo (#1758) * docs(sample): relocate native image sample from old repo --- README.md | 7 +- samples/native-image/README.md | 92 ++++++++++ samples/native-image/pom.xml | 164 ++++++++++++++++++ .../example/spanner/DatabaseOperations.java | 92 ++++++++++ .../example/spanner/InstanceOperations.java | 57 ++++++ .../spanner/NativeImageSpannerSample.java | 70 ++++++++ .../spanner/NativeImageSpannerSampleIT.java | 36 ++++ samples/pom.xml | 1 + 8 files changed, 517 insertions(+), 2 deletions(-) create mode 100644 samples/native-image/README.md create mode 100644 samples/native-image/pom.xml create mode 100644 samples/native-image/src/main/java/com/example/spanner/DatabaseOperations.java create mode 100644 samples/native-image/src/main/java/com/example/spanner/InstanceOperations.java create mode 100644 samples/native-image/src/main/java/com/example/spanner/NativeImageSpannerSample.java create mode 100644 samples/native-image/src/test/java/com/example/spanner/NativeImageSpannerSampleIT.java diff --git a/README.md b/README.md index b970d67849..7468cf8598 100644 --- a/README.md +++ b/README.md @@ -56,13 +56,13 @@ implementation 'com.google.cloud:google-cloud-spanner' If you are using Gradle without BOM, add this to your dependencies ```Groovy -implementation 'com.google.cloud:google-cloud-spanner:6.25.4' +implementation 'com.google.cloud:google-cloud-spanner:6.25.5' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.25.4" +libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.25.5" ``` ## Authentication @@ -240,6 +240,9 @@ Samples are in the [`samples/`](https://github.com/googleapis/java-spanner/tree/ | Sample | Source Code | Try it | | --------------------------- | --------------------------------- | ------ | +| Database Operations | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/native-image/src/main/java/com/example/spanner/DatabaseOperations.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/native-image/src/main/java/com/example/spanner/DatabaseOperations.java) | +| Instance Operations | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/native-image/src/main/java/com/example/spanner/InstanceOperations.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/native-image/src/main/java/com/example/spanner/InstanceOperations.java) | +| Native Image Spanner Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/native-image/src/main/java/com/example/spanner/NativeImageSpannerSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/native-image/src/main/java/com/example/spanner/NativeImageSpannerSample.java) | | Add Json Column Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/AddJsonColumnSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/AddJsonColumnSample.java) | | Add Numeric Column Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/AddNumericColumnSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/AddNumericColumnSample.java) | | Async Dml Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/AsyncDmlExample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/AsyncDmlExample.java) | diff --git a/samples/native-image/README.md b/samples/native-image/README.md new file mode 100644 index 0000000000..7e02ac77c9 --- /dev/null +++ b/samples/native-image/README.md @@ -0,0 +1,92 @@ +# Spanner Sample Application with Native Image + +This is a sample application which uses the Cloud Spanner client libraries and demonstrates compatibility with Native Image compilation. + +The application creates a new Spanner instance and database, and it runs basic operations including queries and Spanner mutations. + +## Setup Instructions + +You will need to follow these prerequisite steps in order to run these samples: + +1. If you have not already, [create a Google Cloud Platform Project](https://cloud.google.com/resource-manager/docs/creating-managing-projects#creating_a_project). + +2. Install the [Google Cloud SDK](https://cloud.google.com/sdk/) which will allow you to run the sample with your project's credentials. + + Once installed, log in with Application Default Credentials using the following command: + + ``` + gcloud auth application-default login + ``` + + **Note:** Authenticating with Application Default Credentials is convenient to use during development, but we recommend [alternate methods of authentication](https://cloud.google.com/docs/authentication/production) during production use. + +3. Install the GraalVM compiler. + + You can follow the [official installation instructions](https://www.graalvm.org/docs/getting-started-with-graalvm/#install-graalvm) from the GraalVM website. + After following the instructions, ensure that you install the Native Image extension installed by running: + + ``` + gu install native-image + ``` + + Once you finish following the instructions, verify that the default version of Java is set to the GraalVM version by running `java -version` in a terminal. + + You will see something similar to the below output: + + ``` + $ java -version + + openjdk version "11.0.15" 2022-04-19 + OpenJDK Runtime Environment GraalVM CE 22.1.0 (build 11.0.15+10-jvmci-22.1-b06) + OpenJDK 64-Bit Server VM GraalVM CE 22.1.0 (build 11.0.15+10-jvmci-22.1-b06, mixed mode, sharing) + + ``` +## Run with Native Image Compilation + +1. **(Optional)** If you wish to run the application against the [Spanner emulator](https://cloud.google.com/spanner/docs/emulator), make sure that you have the [Google Cloud SDK](https://cloud.google.com/sdk) installed. + + In a new terminal window, start the emulator via `gcloud`: + + ``` + gcloud beta emulators spanner start + ``` + + You may leave the emulator running for now. + In the next section, we will run the sample application against the Spanner emulator instsance. + +2. Navigate to this directory and compile the application with the Native Image compiler. + + ``` + mvn package -P native -DskipTests + ``` + +3. **(Optional)** If you're using the emulator, export the `SPANNER_EMULATOR_HOST` as an environment variable in your terminal. + + ``` + export SPANNER_EMULATOR_HOST=localhost:9010 + ``` + + The Spanner Client Libraries will detect this environment variable and will automatically connect to the emulator instance if this variable is set. + +4. Run the application. + + ``` + ./target/native-image + ``` + +5. The application will run through some basic Spanner operations and log some output statements. + + ``` + Running the Spanner Sample. + Singers Registered in Spanner: + Bob Loblaw + Virginia Watson + ``` + +## Sample Integration test with Native Image Support + +In order to run the sample integration test as a native image, call the following command: + + ``` + mvn test -Pnative + ``` diff --git a/samples/native-image/pom.xml b/samples/native-image/pom.xml new file mode 100644 index 0000000000..50e8308d02 --- /dev/null +++ b/samples/native-image/pom.xml @@ -0,0 +1,164 @@ + + + 4.0.0 + com.google.cloud + native-image + Native Image Sample + https://github.com/googleapis/java-spanner + + + + com.google.cloud.samples + shared-configuration + 1.2.0 + + + + + 1.8 + 1.8 + UTF-8 + + + + + + com.google.cloud + libraries-bom + 25.2.0 + pom + import + + + + + + + com.google.cloud + google-cloud-spanner + + + + junit + junit + 4.13.2 + test + + + com.google.truth + truth + 1.1.3 + test + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.2.2 + + + + true + dependency-jars/ + com.example.spanner.NativeImageSpannerSample + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.3.0 + + + copy-dependencies + package + + copy-dependencies + + + + ${project.build.directory}/dependency-jars/ + + + + + + + + + + + + native + + + + org.junit.vintage + junit-vintage-engine + 5.8.2 + test + + + org.graalvm.buildtools + junit-platform-native + 0.9.10 + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + 2.22.2 + + + **/*IT + + + + + org.graalvm.buildtools + native-maven-plugin + 0.9.9 + true + + com.example.spanner.NativeImageSpannerSample + + --no-fallback + --no-server + + + + + build-native + + build + test + + package + + + test-native + + test + + test + + + + + + + + \ No newline at end of file diff --git a/samples/native-image/src/main/java/com/example/spanner/DatabaseOperations.java b/samples/native-image/src/main/java/com/example/spanner/DatabaseOperations.java new file mode 100644 index 0000000000..829556c674 --- /dev/null +++ b/samples/native-image/src/main/java/com/example/spanner/DatabaseOperations.java @@ -0,0 +1,92 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.spanner; + +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.KeySet; +import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.ResultSet; +import com.google.cloud.spanner.Statement; +import com.google.common.collect.ImmutableList; +import java.util.Collections; +import java.util.List; + +/** Helper methods to manage Spanner Databases. */ +public class DatabaseOperations { + + private static final List DDL_STATEMENTS = + ImmutableList.of( + "CREATE TABLE Singers (SingerId INT64 NOT NULL, FirstName " + + "STRING(1024), LastName STRING(1024)) PRIMARY KEY (SingerId)"); + + static void createDatabase( + DatabaseAdminClient databaseAdminClient, String instanceId, String databaseId) { + + if (databaseExists(databaseAdminClient, instanceId, databaseId)) { + databaseAdminClient.dropDatabase(instanceId, databaseId); + } + databaseAdminClient.createDatabase(instanceId, databaseId, DDL_STATEMENTS); + } + + static boolean databaseExists( + DatabaseAdminClient databaseAdminClient, String instanceId, String databaseId) { + + for (Database database : databaseAdminClient.listDatabases(instanceId).iterateAll()) { + if (databaseId.equals(database.getId().getDatabase())) { + return true; + } + } + return false; + } + + static void insertUsingDml(DatabaseClient dbClient) { + dbClient + .readWriteTransaction() + .run( + transaction -> { + String sql = + "INSERT INTO Singers (SingerId, FirstName, LastName) " + + " VALUES (10, 'Virginia', 'Watson')"; + transaction.executeUpdate(Statement.of(sql)); + return null; + }); + } + + static void insertUsingMutation(DatabaseClient dbClient) { + Mutation mutation = + Mutation.newInsertBuilder("Singers") + .set("SingerId") + .to(12) + .set("FirstName") + .to("Bob") + .set("LastName") + .to("Loblaw") + .build(); + dbClient.write(Collections.singletonList(mutation)); + } + + static ResultSet performRead(DatabaseClient dbClient) { + return dbClient.singleUse().executeQuery(Statement.of("SELECT * FROM Singers")); + } + + static void deleteDatabase(DatabaseClient dbClient) { + dbClient.write(Collections.singletonList(Mutation.delete("Singers", KeySet.all()))); + System.out.println("Records deleted."); + } +} diff --git a/samples/native-image/src/main/java/com/example/spanner/InstanceOperations.java b/samples/native-image/src/main/java/com/example/spanner/InstanceOperations.java new file mode 100644 index 0000000000..75efd6b45b --- /dev/null +++ b/samples/native-image/src/main/java/com/example/spanner/InstanceOperations.java @@ -0,0 +1,57 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.spanner; + +import com.google.cloud.spanner.Instance; +import com.google.cloud.spanner.InstanceAdminClient; +import com.google.cloud.spanner.InstanceConfigId; +import com.google.cloud.spanner.InstanceId; +import com.google.cloud.spanner.InstanceInfo; + +/** Helper methods to manage Spanner instances. */ +public class InstanceOperations { + + static void createTestInstance( + InstanceAdminClient instanceAdminClient, String projectId, String instanceId) + throws Exception { + + if (instanceExists(instanceAdminClient, instanceId)) { + instanceAdminClient.deleteInstance(instanceId); + } + + InstanceInfo instanceInfo = + InstanceInfo.newBuilder(InstanceId.of(projectId, instanceId)) + .setInstanceConfigId(InstanceConfigId.of(projectId, "regional-us-central1")) + .setNodeCount(1) + .setDisplayName(instanceId) + .build(); + try { + instanceAdminClient.createInstance(instanceInfo).get(); + } catch (Exception e) { + throw new Exception("Failed to create Spanner instance.", e); + } + } + + static boolean instanceExists(InstanceAdminClient instanceAdminClient, String instanceName) { + for (Instance instance : instanceAdminClient.listInstances().iterateAll()) { + if (instanceName.equals(instance.getId().getInstance())) { + return true; + } + } + return false; + } +} diff --git a/samples/native-image/src/main/java/com/example/spanner/NativeImageSpannerSample.java b/samples/native-image/src/main/java/com/example/spanner/NativeImageSpannerSample.java new file mode 100644 index 0000000000..b71404daf0 --- /dev/null +++ b/samples/native-image/src/main/java/com/example/spanner/NativeImageSpannerSample.java @@ -0,0 +1,70 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.spanner; + +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.InstanceAdminClient; +import com.google.cloud.spanner.ResultSet; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; + +/** Sample Spanner application compiled with Native Image. */ +public class NativeImageSpannerSample { + + private static final String TEST_INSTANCE_ID = "test-instance"; + private static final String TEST_DATABASE_ID = "test-database"; + + /** + * Runs the Spanner sample application. + * + *

This application should be run with the Spanner emulator for testing purposes. + */ + public static void main(String[] args) throws Exception { + System.out.println("Running the Spanner Sample."); + + SpannerOptions options = SpannerOptions.newBuilder().build(); + Spanner spanner = options.getService(); + + // Setup the Spanner environment. + InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); + DatabaseAdminClient databaseAdminClient = spanner.getDatabaseAdminClient(); + + InstanceOperations.createTestInstance( + instanceAdminClient, options.getProjectId(), TEST_INSTANCE_ID); + DatabaseOperations.createDatabase(databaseAdminClient, TEST_INSTANCE_ID, TEST_DATABASE_ID); + + // Insert data + DatabaseClient dbClient = + spanner.getDatabaseClient( + DatabaseId.of(options.getProjectId(), TEST_INSTANCE_ID, TEST_DATABASE_ID)); + DatabaseOperations.insertUsingDml(dbClient); + DatabaseOperations.insertUsingMutation(dbClient); + + // Run some queries. + ResultSet resultSet = DatabaseOperations.performRead(dbClient); + System.out.println("Singers Registered in Spanner:"); + while (resultSet.next()) { + System.out.println(resultSet.getString("FirstName") + " " + resultSet.getString("LastName")); + } + if (DatabaseOperations.databaseExists( + databaseAdminClient, TEST_INSTANCE_ID, TEST_DATABASE_ID)) { + DatabaseOperations.deleteDatabase(dbClient); + } + } +} diff --git a/samples/native-image/src/test/java/com/example/spanner/NativeImageSpannerSampleIT.java b/samples/native-image/src/test/java/com/example/spanner/NativeImageSpannerSampleIT.java new file mode 100644 index 0000000000..ce47de9273 --- /dev/null +++ b/samples/native-image/src/test/java/com/example/spanner/NativeImageSpannerSampleIT.java @@ -0,0 +1,36 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.spanner; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.cloud.testing.junit4.StdOutCaptureRule; +import org.junit.Rule; +import org.junit.Test; + +public class NativeImageSpannerSampleIT { + + @Rule public StdOutCaptureRule stdOut = new StdOutCaptureRule(); + + @Test + public void testStoreAndRead() throws Exception { + NativeImageSpannerSample.main(new String[] {}); + assertThat(stdOut.getCapturedOutputAsUtf8String()).contains("Singers Registered in Spanner:"); + assertThat(stdOut.getCapturedOutputAsUtf8String()).contains("Virginia Watson"); + assertThat(stdOut.getCapturedOutputAsUtf8String()).contains("Bob Loblaw"); + } +} diff --git a/samples/pom.xml b/samples/pom.xml index b46876310d..2556f678e8 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -31,6 +31,7 @@ install-without-bom snapshot snippets + native-image