- OCI Object Storage
The Helidon SE OCI Object Storage integration provides a reactive API to files stored in Oracle cloud.
Deprecated
The custom Helidon SE OCI clients documented here are deprecated. It is recommended that you use the OCI Java SDK directly, in particular the Async clients. For more information see:
Experimental
Helidon integration with Oracle Cloud Infrastructure is still experimental and not intended for production use. APIs and features have not yet been fully tested and are subject to change.
Maven Coordinates
To enable OCI Object Storage add the following dependency to your project’s pom.xml (see Managing Dependencies).
<dependency>
<groupId>io.helidon.integrations.oci</groupId>
<artifactId>helidon-integrations-oci-objectstorage</artifactId>
</dependency>Setting up the Object Storage
In order to use the OCI Object Storage integration, the following setup should be made:
Config ociConfig = config.get("oci");
OciObjectStorageRx ociObjectStorage = OciObjectStorageRx.create(ociConfig);Current configuration requires ~/.oci/config to be available in the home folder. This configuration file can be downloaded from OCI.
Routing should be added to the WebServer, in our case pointing to /file:
String bucketName = ociConfig.get("objectstorage").get("bucket").asString().get();
WebServer.builder()
.config(config.get("server"))
.routing(Routing.builder()
.register("/files", new ObjectStorageService(ociObjectStorage, bucketName)))
.build()
.start()
.await()Additionally, in application.yaml OCI properties should be specified:
oci:
properties:
compartment-ocid: "ocid<1>tenancy.oc<1>.<..>"
objectstorage-namespace: "<...>"
objectstorage-bucket: "<...>"The exact values are available in OCI object storage and bucket properties.

Using the Object Storage
In the Service we must specify the mapping for CRUD operations with the files and their handlers:
@Override
public void update(Routing.Rules rules) {
rules.get("/file/{file-name}", this::download)
.post("/file/{file-name}", this::upload)
.delete("/file/{file-name}", this::delete)
.get("/rename/{old-name}/{new-name}", this::rename);
}Upload file
To upload a file to OCI Object Storage using the PUT method:
private void upload(ServerRequest req, ServerResponse res) {
OptionalLong contentLength = req.headers().contentLength();
if (contentLength.isEmpty()) {
req.content().forEach(DataChunk::release);
res.status(Http.Status.BAD_REQUEST_400).send("Content length must be defined");
return;
}
String objectName = req.path().param("file-name");
PutObject.Request request = PutObject.Request.builder()
.objectName(objectName)
.bucket(bucketName)
.contentLength(contentLength.getAsLong());
req.headers().contentType().ifPresent(request::requestMediaType);
objectStorage.putObject(request,
req.content())
.forSingle(response -> res.send(response.requestId()))
.exceptionally(res::send);
}- Create the
RequestusingPutObject.Request.builder() - Define
MediaType - Execute the request to OCI in asynchronous way and put the result in
responseobject
Download file
To download a file from OCI Object Storage using the GET method:
private void download(ServerRequest req, ServerResponse res) {
String objectName = req.path().param("file-name");
objectStorage.getObject(GetObject.Request.builder()
.bucket(bucketName)
.objectName(objectName))
.forSingle(apiResponse -> {
Optional<GetObjectRx.Response> entity = apiResponse.entity();
if (entity.isEmpty()) {
res.status(Http.Status.NOT_FOUND_404).send();
} else {
GetObjectRx.Response response = entity.get();
// copy the content length header to response
apiResponse.headers()
.first(Http.Header.CONTENT_LENGTH)
.ifPresent(res.headers()::add);
res.send(response.publisher());
}
})
.exceptionally(res::send);
}- Use
getObjectfunction to make asynchronous request to OCI Object Storage - The result is of type
Optional - Whenever the result is empty, return status
404 - Get the response, set headers and return the result as a
Publisher
Rename file
To rename an existing file in the OCI bucket, submit a GET method with two parameters:
private void rename(ServerRequest req, ServerResponse res) {
String oldName = req.path().param("old-name");
String newName = req.path().param("new-name");
objectStorage.renameObject(RenameObject.Request.builder()
.bucket(bucketName)
.objectName(oldName)
.newObjectName(newName))
.forSingle(it -> res.send("Renamed to " + newName))
.exceptionally(res::send);
}- Use
renameObjectfunction and configure aRenameObject.Request.builder()to submit the rename request - The request is made in asynchronous way; a
Singleis returned
Delete file
Finally, to delete a file, DELETE request should be used:
private void delete(ServerRequest req, ServerResponse res) {
String objectName = req.path().param("file-name");
objectStorage.deleteObject(DeleteObject.Request.builder()
.bucket(bucketName)
.objectName(objectName))
.forSingle(response -> res.status(response.status()).send())
.exceptionally(res::send);
}- Use
deleteObjectfunction and configure aDeleteObject.Request.builder()to submit the delete request - The request is made in asynchronous way; a
Singleis returned
Object Storage Health Check
If your Helidon application depends on Object Storage accessibility, you may consider setting up a health check to verify connectivity with an OCI bucket. To do so, first add the following dependency in your pom file:
<dependency>
<groupId>io.helidon.integrations.oci</groupId>
<artifactId>helidon-integrations-oci-objectstorage-health</artifactId>
</dependency>In order to register the new health check in Helidon SE, create an instance of HealthSupport and configure it as shown next:
HealthSupport health = HealthSupport.builder()
.addLiveness(OciObjectStorageHealthCheck.builder()
.ociObjectStorage(ociObjectStorage)
.bucket(bucketName)
.namespace(namespace)
.build())
.build();where ociObjectStorage, bucketName and namespace are as required for any other Object Storage access. Finally, include your newly created HealthSupport object as part of your application’s routing:
Routing routing = Routing.builder()
.register(health)
// other routes here
.build();When executed, this health check will ping the bucket to make sure it is accessible in your environment. For more information about health checks see Health Checks.