Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(sample): Add sample for native image support in Firestore #872

Merged
merged 2 commits into from
Feb 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions samples/native-image-sample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Firestore Sample Application with Native Image

This application uses the [Google Cloud Firestore client libraries](https://cloud.google.com/firestore/docs/quickstart-servers#java) and is compatible with Native Image compilation.

This sample runs through basic operations of creating a new document, running queries, and then deleting the created resources.

## Setup Instructions

You will need to follow these prerequisite steps in order to run the 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/#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.7" 2020-04-14
OpenJDK Runtime Environment GraalVM CE 20.1.0 (build 11.0.7+10-jvmci-20.1-b02)
OpenJDK 64-Bit Server VM GraalVM CE 20.1.0 (build 11.0.7+10-jvmci-20.1-b02, mixed mode, sharing)
```

## Sample

1. If you wish to run the application against the [Firestore emulator](https://cloud.google.com/sdk/gcloud/reference/beta/emulators/firestore), 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 firestore start --host-port=localhost:9010
```

Leave the emulator running in this terminal for now.
In the next section, we will run the sample application against the Firestore emulator instance.


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 `FIRESTORE_EMULATOR_HOST` as an environment variable in your terminal.

```
export FIRESTORE_EMULATOR_HOST=localhost:9010
```

The Firestore Client Libraries will detect this environment variable and automatically connect to the emulator instance if this variable is set.

4. Run the application.

```
./target/native-image-sample
```

5. The application will run through some basic Firestore operations and log some output statements.

```
Created user alovelace. Timestamp: 2020-12-15T20:19:28.444070000Z
The following users were saved:
Document: alovelace | Ada Lovelace born 1815
Number of users born before 1900: 1
Number of users born earlier after 1900: 0
Number of users whose first name is 'Ada': 0
```

## Sample Integration test with Native Image Support
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might wish to include some metrics they might be interested in if they were to do this. (ie. how much faster are things?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, that's a nice idea! Modified the code to also print the duration at the end.


In order to run the sample integration test as a native image, call the following command:

```
mvn test -Pnative
```
150 changes: 150 additions & 0 deletions samples/native-image-sample/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<?xml version='1.0' encoding='UTF-8'?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.firestore</groupId>
<artifactId>native-image-sample</artifactId>
<name>Native Image Sample</name>
<url>https://github.com/googleapis/java-firestore</url>

<!--
The parent pom defines common style checks and testing strategies for our samples.
Removing or replacing it should not affect the execution of the samples in anyway.
-->
<parent>
<groupId>com.google.cloud.samples</groupId>
<artifactId>shared-configuration</artifactId>
<version>1.2.0</version>
</parent>

<properties>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>libraries-bom</artifactId>
<version>24.2.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-firestore</artifactId>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.truth</groupId>
<artifactId>truth</artifactId>
<version>1.1.3</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.example.firestore.NativeImageFirestoreSample
</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>

<!-- Native Profile-->
<profiles>
<profile>
<id>native</id>

<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>native-image-support</artifactId>
<version>0.10.0</version>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.8.2</version>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect these version number declaration for junit-vintage-engine and junit-platform-native would create multiple RenovateBot pull requests for different repositories in future. Is there a better place to declare the pair?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And native-maven-plugin version.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's right. That's a good point! We can consider putting these settings in com.google.cloud.samples:shared-configuration? https://github.com/GoogleCloudPlatform/java-repo-tools/blob/main/pom.xml

<scope>test</scope>
</dependency>
<dependency>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>junit-platform-native</artifactId>
<version>0.9.9</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin
</artifactId> <!-- Must use older version of surefire plugin for native-image testing. -->
<version>2.22.2</version>
<configuration>
<includes>
<include>**/IT*</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.9.9</version>
<extensions>true</extensions>
<configuration>
<mainClass>com.example.firestore.NativeImageFirestoreSample
</mainClass>
<buildArgs>
<buildArg>--no-fallback</buildArg>
<buildArg>--no-server</buildArg>
</buildArgs>
</configuration>
<executions>
<execution>
<id>build-native</id>
<goals>
<goal>build</goal>
<goal>test</goal>
</goals>
<phase>package</phase>
</execution>
<execution>
<id>test-native</id>
<goals>
<goal>test</goal>
</goals>
<phase>test</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright 2020-2021 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
*
* https://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.firestore;

import com.google.api.core.ApiFuture;
import com.google.cloud.firestore.CollectionReference;
import com.google.cloud.firestore.DocumentReference;
import com.google.cloud.firestore.FieldPath;
import com.google.cloud.firestore.Firestore;
import com.google.cloud.firestore.FirestoreOptions;
import com.google.cloud.firestore.Query;
import com.google.cloud.firestore.QueryDocumentSnapshot;
import com.google.cloud.firestore.QuerySnapshot;
import com.google.cloud.firestore.WriteResult;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Sample Firestore application demonstrating basic operations.
*/
public class NativeImageFirestoreSample {

private static final String USERS_COLLECTION = "nativeimage_test_users";

/**
* Entrypoint to the Firestore sample application.
*/
public static void main(String[] args) throws Exception {
Instant startTime = Instant.now();
FirestoreOptions firestoreOptions = FirestoreOptions.getDefaultInstance();
Firestore db = firestoreOptions.getService();

deleteCollection(db);
createUserDocument(db);
createUserDocumentPojo(db);
readDocuments(db);
runSampleQueries(db);
Instant endTime = Instant.now();
Duration duration = Duration.between(startTime, endTime);
System.out.println("Duration: " + duration.toString());
}

static void deleteCollection(Firestore db) throws Exception {
Iterable<DocumentReference> documents = db.collection(USERS_COLLECTION).listDocuments();
for (DocumentReference doc : documents) {
doc.delete().get();
}
}

static void createUserDocument(Firestore db) throws Exception {
DocumentReference docRef = db.collection(USERS_COLLECTION).document("alovelace");
Map<String, Object> data = new HashMap<>();
data.put("id", "10");
data.put("first", "Ada");
data.put("last", "Lovelace");
data.put("born", 1815);

WriteResult result = docRef.set(data).get();
System.out.println("Created user " + docRef.getId() + ". Timestamp: " + result.getUpdateTime());
}

static void createUserDocumentPojo(Firestore db) throws Exception {
CollectionReference collectionReference = db.collection(USERS_COLLECTION);
WriteResult result =
collectionReference.document()
.set(new Person("Alan", "Turing", 1912))
.get();

System.out.println("Created user by POJO. Timestamp: " + result.getUpdateTime());
}

static void readDocuments(Firestore db) throws Exception {
ApiFuture<QuerySnapshot> query = db.collection(USERS_COLLECTION).get();
QuerySnapshot querySnapshot = query.get();
List<QueryDocumentSnapshot> documents = querySnapshot.getDocuments();
System.out.println("The following users were saved:");
printUsers(documents);
}

static void runSampleQueries(Firestore db) throws Exception {
List<QueryDocumentSnapshot> results =
runQuery(db.collection(USERS_COLLECTION).whereLessThan("born", 1900));
System.out.println("Number of users born before 1900: " + results.size());

results = runQuery(
db.collection(USERS_COLLECTION).whereGreaterThan(FieldPath.of("born"), 1900));
System.out.println("Number of users born earlier after 1900: " + results.size());

results = runQuery(
db.collection(USERS_COLLECTION).whereEqualTo("name", "Ada"));
System.out.println("Number of users whose first name is 'Ada': " + results.size());
}

private static List<QueryDocumentSnapshot> runQuery(Query query) throws Exception {
QuerySnapshot querySnapshot = query.get().get();
List<QueryDocumentSnapshot> documents = querySnapshot.getDocuments();
return documents;
}

private static void printUsers(Iterable<QueryDocumentSnapshot> documents) {
for (QueryDocumentSnapshot document : documents) {
System.out.printf(
"Document: %s | %s %s born %d\n",
document.getId(),
document.getString("first"),
document.getString("last"),
document.getLong("born"));
}
}
}
Loading