[Cloud, Java] Controlling Your Cloud - A Look At The Oracle Cloud Infrastructure Java SDK

原文はこちら。
https://blogs.oracle.com/developers/controlling-your-cloud-a-look-at-the-oracle-cloud-infrastructure-java-sdk

数週間前、クラウドエバンジェリズムチームは、3日間のクラウドhackfestを行い、カリフォルニア州サンタクララの弊社のお客様企業から3名のすばらしい開発者が参加されました。このイベントの間、開発者の一人が、自身のチームが直面している課題は、非常に大きいサイズになる可能性のあるファイルのアップロード処理であると述べました。私も以前、開発者としてこの問題に直面しており、確かに困難ではあります。(後で説明するように、ここ数年で状況は大幅に改善されましたが)Webは大きなサイズのファイルを転送するために作られたものではありません。hackfest期間中にこの問題に完全に対処するまでには至りませんでしたが、帰宅後Oracle Cloud Infrastructure APIを深く掘り下げ、引き続き解決策を探すことを開発者に約束しました。それで昨日、私はプロセスを掘り下げることにし、その開発者のためにOCI Object Storageに大きなファイルをアップロードする方法についてかなりしっかりとしたデモを設計しました。しかしそのソリューションを説明する前に、内容が高度になっても追随できるよう、入手可能なSDKを使ったOracle Cloudの操作の基本を紹介いたします。

Oracleは他にもいくつかの言語(Python、Ruby、Go)のSDKを提供していますが、通常Groovyを使ってコードを書くので、Java SDKを使いました。
Software Development Kits and Command Line Interface
https://docs.cloud.oracle.com/iaas/Content/API/Concepts/sdks.htm
Java SDK
https://docs.cloud.oracle.com/iaas/Content/API/SDKDocs/javasdk.htm
Oracleはクラウド操作のための完全なREST APIを提供していますが、SDKでは素敵なネイティブソリューションを提供し、リクエストを署名し、HTTP呼び出しをアプリケーション内にバンドルできる素敵なパッケージにするという面倒な部分を抽象化してくれます。 Java SDKは以下のOCIサービスをサポートします。
  • Audit
  • Container Engine for Kubernetes
  • Core Services (Networking, Compute, Block Volume)
  • Database
  • DNS
  • Email Delivery
  • File Storage
  • IAM
  • Load Balancing
  • Object Storage
  • Search
  • Key Management
実際にJava SDK、特にObject Storageサービスとやり取りするためのJava SDKの使用方法を見てみましょう。SDKはオープンソースでGitHubから利用できます。
Java SDK for Oracle Cloud Infrastructure
https://github.com/oracle/oci-java-sdk
このデモ用に非常にシンプルなWebアプリを作成しましたが、残念ながら、まだMavenからSDKを入手できませんので、まず第一にSDKをダウンロードして、それをアプリケーションの依存関係として含める必要がありました。
Maven release
https://github.com/oracle/oci-java-sdk/issues/25
oci-java-sdk library
https://github.com/oracle/oci-java-sdk/releases
Gradleを使っているので、アプリケーションのルート直下にあるlibsディレクトリにJARを配置し、以下の依存関係ブロックを宣言してGradleが確実にローカルJARを拾うようにしました(重要なのは、8行目の "implementation"メソッドです) .

dependencies {
    localGroovyConf localGroovy()
    compile 'org.codehaus.groovy:groovy-all:2.5.4'
    compile 'com.sparkjava:spark-core:2.7.2'
    compile 'org.slf4j:slf4j-simple:1.7.21'
    compile group: 'org.apache.tika', name: 'tika-core', version: '1.19.1'
    
    implementation fileTree(dir: 'libs', include: ['*.jar'])
}
次は、認証およびサービス呼び出しに必要なシステムプロパティの作成です。このために、ローカルに構成ファイルを設定し、キーペアを生成する必要があります。これは最初は面倒ではありますが、一度設定すれば将来設定する必要がありません。後で使いたい場合、OCI CLI用の設定もすでに済んでいるというメリットもあります。
SDK and CLI Configuration File
https://docs.cloud.oracle.com/iaas/Content/API/Concepts/sdkconfig.htm
構成ファイルとキーを生成したら、プロパティをアプリケーションのルートにあるgradle.propertiesに書き出します。このプロパティファイルと以下に示すキーの命名規則を使用すれば、Gradleで変数をシステムスクリプトとしてビルドスクリプト内で使用できます。
ビルドスクリプト内でシステムプロパティとして変数を使用しても、それらをアプリケーション内で使用できるわけではありません。利用できるようにするため、'run'タスクを介してシンプルに変数を渡すことができます。
task runServer(dependsOn: 'classes', type: JavaExec) {
    System.setProperty('environment', 'prod')
    dependsOn 'classes'
    classpath = sourceSets.main.runtimeClasspath
    main = 'codes.recursive.Bootstrap'
    systemProperties = System.getProperties()
}
続いて、プロバイダーとサービスクライアントを管理するためのクラスを作成しました。このクラスには現在1個のクライアントしかありませんが、将来簡単に他のサービス用にクライアントを追加できます。
package codes.recursive.service

import com.oracle.bmc.auth.AuthenticationDetailsProvider
import com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider
import com.oracle.bmc.objectstorage.ObjectStorage
import com.oracle.bmc.objectstorage.ObjectStorageClient

import java.security.Security

class OciClientManager {

    AuthenticationDetailsProvider provider

    OciClientManager(configFilePath=System.getProperty("ociConfigPath"), profile=System.getProperty("ociProfile")) {
        this.provider =  new ConfigFileAuthenticationDetailsProvider(configFilePath, profile)
        // per https://docs.cloud.oracle.com/iaas/Content/API/SDKDocs/javasdkconfig.htm#JavaVirtualMachineTTLforDNSNameLookups
        Security.setProperty("networkaddress.cache.ttl" , "60")
    }

    ObjectStorage getObjectClient(region=System.getProperty("ociObjectStorageRegion")) {
        ObjectStorage client = new ObjectStorageClient(this.provider)
        client.setRegion(region)
        return client
    }
}
続いて、Object Storage APIを操作するための'ObjectService'を作成しました。コンストラクタが上記のOciClientManagerインスタンスを受け入れ、コンストラクタは上で見たOciClientManagerのインスタンスを受け取り、多くのSDKメソッドに共通するもの(名前空間、バケット名、コンパートメントIDなど)用のクラス変数を設定します。
OciClientManager clientManager
ObjectStorage objectClient
String bucketName = "doggos"
String namespaceName

ObjectService(OciClientManager clientManager) {
    this.clientManager = clientManager
    this.objectClient = clientManager.getObjectClient()
    GetNamespaceResponse namespaceResponse = objectClient.getNamespace(
            GetNamespaceRequest.builder().build()
    )
    this.namespaceName = namespaceResponse.getValue()
}
これでSDKと対話する準備が整いました。 開発者の立場としては、それは間違いなく直感的なAPIといってよいでしょうし、他のクラウドプロバイダが自身のクラウドAPIでも使用する標準的なリクエスト/レスポンスモデルに従っています。何が次のメソッドやプロパティを呼ぶのかを単純に推測して、それが正しい(もしくはIntelliSenseが適切な場所をガイドしてくれる)ことが多かったわけですが、これは優れたAPIのためのベンチマークといってよいでしょう。ただし、直感的で肥大化した認証方式で邪魔をしないのであれば、ですが。誤解しないで頂きたいのは、強力な認証とセキュリティは確かに重要ですが、SDKの目的は複雑さを隠し、APIを簡単な方法で使用するためのメソッドを公開することです。それはさておき、Object Storageクライアントの使い方を見てみましょう。

では早速、クライアントを使って以下のアクションを実行する方法を示します(各コードブロックの後に結果の例を付けています)。
  1. List Buckets
  2. Get A Bucket
  3. List Objects In A Bucket
  4. Get An Object
List Buckets:
def listBuckets() {
    ListBucketsRequest listBucketsRequest = ListBucketsRequest.builder()
            .namespaceName(this.namespaceName)
            .compartmentId(System.getProperty("ociCompartmentId"))
            .build()
    ListBucketsResponse listBucketsResponse = objectClient.listBuckets(listBucketsRequest)
    return listBucketsResponse
}


Get Bucket:
def getBucket() {
    def listBucketsResponse = listBuckets()
    GetBucketRequest getBucketRequest = GetBucketRequest.builder()
            .namespaceName(this.namespaceName)
            .bucketName( listBucketsResponse.items.find { it.name == this.bucketName }.name )
            .fields([GetBucketRequest.Fields.ApproximateCount])
            .build()
    GetBucketResponse getBucketResponse = objectClient.getBucket(getBucketRequest)
    return getBucketResponse
}


List Objects:
def listObjects() {
    ListObjectsRequest listObjectsRequest = ListObjectsRequest.builder()
            .namespaceName(this.namespaceName)
            .bucketName(this.bucketName)
            .build()
    ListObjectsResponse listObjectsResponse = objectClient.listObjects(listObjectsRequest)
    return listObjectsResponse
}


Get Object:
def getObject() {
    def listObjectsResponse = listObjects()
    GetObjectRequest getObjectRequest = GetObjectRequest.builder()
            .namespaceName(namespaceName)
            .bucketName(bucketName)
            .objectName(listObjectsResponse.listObjects.objects.first().name)
            .build()
    GetObjectResponse getObjectResponse = objectClient.getObject(getObjectRequest)
    def object = Util.writeInputStream(getObjectResponse.inputStream, getObjectResponse.contentType)
    return [object: object, response: getObjectResponse]
}


'Get Object'の例には、ファイルへ書き込み可能なオブジェクトを含むInputStreamも含まれています。

ご覧のとおり、Object Storage APIは予測可能で一貫性があります。別のエントリでは、SDKを使った大規模ファイルのアップロード処理に関する、より複雑な問題に取り組みます。

0 件のコメント:

コメントを投稿