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 Native Image sample for Storage #1283

Merged
merged 10 commits into from
Mar 10, 2022
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ Samples are in the [`samples/`](https://github.com/googleapis/java-storage/tree/

| Sample | Source Code | Try it |
| --------------------------- | --------------------------------- | ------ |
| Native Image Storage Sample | [source code](https://github.com/googleapis/java-storage/blob/main/samples/native-image-sample/src/main/java/com/example/storage/NativeImageStorageSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-storage&page=editor&open_in_editor=samples/native-image-sample/src/main/java/com/example/storage/NativeImageStorageSample.java) |
| Configure Retries | [source code](https://github.com/googleapis/java-storage/blob/main/samples/snippets/src/main/java/com/example/storage/ConfigureRetries.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-storage&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/storage/ConfigureRetries.java) |
| Quickstart Sample | [source code](https://github.com/googleapis/java-storage/blob/main/samples/snippets/src/main/java/com/example/storage/QuickstartSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-storage&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/storage/QuickstartSample.java) |
| Add Bucket Default Owner | [source code](https://github.com/googleapis/java-storage/blob/main/samples/snippets/src/main/java/com/example/storage/bucket/AddBucketDefaultOwner.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-storage&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/storage/bucket/AddBucketDefaultOwner.java) |
Expand Down
80 changes: 80 additions & 0 deletions samples/native-image-sample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Storage Sample Application with Native Image

The Storage sample application demonstrates some common operations with Google Cloud Storage and is compatible with Native Image compilation.

## 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/#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)
```

4. [Enable the Cloud Storage APIs](https://console.cloud.google.com/apis/api/storage.googleapis.com).

### Run with Native Image Compilation

Navigate to this directory in a new terminal.

1. Compile the application using the Native Image Compiler. This step may take a few minutes.

```
mvn package -P native -DskipTests
```

2. Run the application:

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

3. The application will run through basic Cloud Storage operations of creating, reading, and deleting Cloud Storage resources.

You can manually manage Cloud Storage resources through [Google Cloud Console](https://console.cloud.google.com/storage) to verify that the resources are cleaned up.

```
Creating bucket nativeimage-sample-bucket-7221f161-688c-4a7a-9120-8900d20f0802
Write file to bucket.
Reading the file that was written...
Successfully wrote to file: Hello World!
Cleaning up resources...
Deleted file nativeimage-sample-file-5d927aaf-cb03-41de-8383-696733893db5
Deleted bucket nativeimage-sample-bucket-7221f161-688c-4a7a-9120-8900d20f0802
```

## 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
```
173 changes: 173 additions & 0 deletions samples/native-image-sample/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
<?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.google.cloud</groupId>
<artifactId>native-image-sample</artifactId>
<name>Native Image Sample</name>
<url>https://github.com/googleapis/java-storage</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>
<!-- Java 8 because the Kokoro Sample test uses that Java version -->
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</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.3.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-core</artifactId>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-storage</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>
<!-- This plugin enables building the application to a JAR *not* using Native Image -->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>dependency-jars/</classpathPrefix>
<mainClass>com.example.storage.NativeImageStorageSample</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.directory}/dependency-jars/
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

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

<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>native-image-support</artifactId>
<version>0.12.0</version>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.8.2</version>
<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.storage.NativeImageStorageSample</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,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.storage;

import com.google.cloud.BatchResult.Callback;
import com.google.cloud.storage.Blob;
import com.google.cloud.storage.BlobId;
import com.google.cloud.storage.BlobInfo;
import com.google.cloud.storage.BucketInfo;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.StorageBatch;
import com.google.cloud.storage.StorageException;
import com.google.cloud.storage.StorageOptions;
import java.nio.charset.StandardCharsets;
import java.util.UUID;

/** Sample Storage application compiled with Native Image. */
public class NativeImageStorageSample {

static String BUCKET_NAME = "nativeimage-sample-bucket-" + UUID.randomUUID();
static String FILENAME = "nativeimage-sample-file-" + UUID.randomUUID();

/** Runs the storage sample application. */
public static void main(String[] args) {

Storage storageClient = StorageOptions.getDefaultInstance().getService();

try {
createBucket(storageClient, BUCKET_NAME);
createFile(storageClient, BUCKET_NAME, FILENAME);
runBatchOperations(storageClient, BUCKET_NAME, FILENAME);
} finally {
System.out.println("Deleting resources.");
storageClient.delete(BUCKET_NAME, FILENAME);
storageClient.delete(BUCKET_NAME);
}
}

private static void runBatchOperations(
Storage storageClient, String bucketName, String fileName) {
BlobId blobId = BlobId.of(bucketName, fileName);

StorageBatch batch = storageClient.batch();
batch
.update(BlobInfo.newBuilder(blobId).build())
.notify(
new Callback<Blob, StorageException>() {
@Override
public void success(Blob blob) {
System.out.println("Batch update succeeded on " + fileName);
}

@Override
public void error(StorageException e) {
System.out.println("Batch update failed with cause: " + e);
}
});

batch.submit();
}

private static void createBucket(Storage storageClient, String bucketName) {
BucketInfo bucketInfo = BucketInfo.newBuilder(bucketName).setLocation("us-east1").build();
storageClient.create(bucketInfo);
System.out.println("Created bucket " + bucketName);
}

private static void createFile(Storage storageClient, String bucketName, String fileName) {
BlobInfo blobInfo =
BlobInfo.newBuilder(bucketName, fileName).setContentType("text/plain").build();
storageClient.create(blobInfo, "Hello World!".getBytes(StandardCharsets.UTF_8));
System.out.println("Created file " + blobInfo.getName());

Blob blob = storageClient.get(bucketName, fileName);
String content = new String(blob.getContent(), StandardCharsets.UTF_8);
System.out.println("Successfully wrote to file: " + content);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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.storage;

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 NativeImageStorageSampleIT {

@Rule public StdOutCaptureRule stdOut = new StdOutCaptureRule();

@Test
public void createAndReadStorageResources() {
NativeImageStorageSample.main(new String[] {});
assertThat(stdOut.getCapturedOutputAsUtf8String())
.contains("Created bucket " + NativeImageStorageSample.BUCKET_NAME);
assertThat(stdOut.getCapturedOutputAsUtf8String())
.contains("Created file " + NativeImageStorageSample.FILENAME);
assertThat(stdOut.getCapturedOutputAsUtf8String())
.contains("Successfully wrote to file: Hello World!");
}
}