Java 向け Windows Azure ストレージ クライアント ライブラリ v. 0.5.0
このポストは、12 月 19 日に Windows Azure Storage Team が投稿した Windows Azure Storage Client Library for Java v. 0.5.0 の翻訳です。
マイクロソフトは、Java 用 Windows Azure ストレージ クライアントの更新版のリリースを発表しました。今回のリリースには、ログ記録のサポート、新規 API オーバーロードの追加、2013-08-15 REST バージョンのストレージ サーバーの完全サポートなど、大きな機能変更が含まれています。詳細についてはこちらのページを参照してください。ソース コードは、これまでと同様にすべて Github (英語) から入手できます (場所が新しくなっていますのでご注意ください)。最新のバイナリ ファイルは Maven からダウンロードできます
<dependency>
<groupId>com.microsoft.windowsazure.storage</groupId>
<artifactId>microsoft-windowsazure-storage-sdk</artifactId>
<version>0.5.0</version>
</dependency>
エミュレーターについての案内
現在、2013-08-15 REST はストレージ エミュレーターのサポート対象外となっています。今後 2 ~ 3 か月以内にリリースが予定されている Windows Azure ストレージ エミュレーターの更新版では、これらの新機能が完全にサポートされます。現行バージョンのストレージ エミュレーターで開発を行う場合、暫定的に Bad Request エラーが返されます。更新版のエミュレーターがリリースされるまでは、2013-08-15 REST バージョンを活用するには Windows Azure ストレージ アカウントで開発、テストを行う必要があります。
サンプル
マイクロソフトは、サンプル (英語) のセットを Github に用意しました。このサンプルは、クライアントをセットアップして抽象化ストレージで実行する際に役立ちます。また、主要なシナリオをいくつかご紹介しています。このサンプルを Eclipse で実行する場合は、サンプル プロジェクトを読み込み、Utility.java の次の行を書き換えてストレージの認証情報を提供します。
public static final String storageConnectionString = "DefaultEndpointsProtocol=http;AccountName=[ACCOUNT_NAME];AccountKey=[ACCOUNT_KEY]";
サンプルの実行中に Fiddler でトラフィックを検査する場合は、Utility.java の次の 2 行のコメントを解除します。
// System.setProperty("http.proxyHost", "localhost");
// System.setProperty("http.proxyPort", "8888");
Utiltity.java を更新した後、実行するプロジェクトを右クリックして [Run As Java Application] を選択します。
パッケージ化およびバージョン管理に関する注意
今回のリリースで、ストレージのパッケージが Windows Azure SDK for Java から分離されました。現在既存の SDK を使用している開発者は、これに対応して依存関係を更新する必要があります。また、新しい構造が導入されたためパッケージ名が変更されています。
- com.microsoft.windowsazure.storage – RetryPolicies、LocationMode、StorageException、StorageCredentials など。サービス全体に共通のすべてのパブリック クラス。
- com.microsoft.windowsazure.storage.blob – BLOB 実装時の利便性を考慮し、Windows Azure BLOB を使用するアプリケーションではこの名前空間を import ステートメントに含むようにします。
- com.microsoft.windowsazure.storage.queue – キュー実装時の利便性を考慮し、Windows Azure キューを使用するアプリケーションではこの名前空間を import ステートメントに含むようにします。
- com.microsoft.windowsazure.storage.table – テーブル実装時の利便性を考慮し、Windows Azure テーブルを使用するアプリケーションではこの名前空間を import ステートメントに含むようにします。
今回のリリースで変更された点の詳細については、後述の「変更ログと大幅な変更点」のセクションを参照してください。
また、マイクロソフトが提供するストレージ クライアント SDK コンポーネントのすべてで、SemVer (英語) の仕様を採用します。これにより、SDK を使用する開発者にとって、一貫性のある予測可能なバージョン管理が実現されるようになります。
新機能
Java クライアント ライブラリ バージョン 0.5.0 では、2013-08-15 REST バージョンのサービスが完全にサポートされました (サポート対象の機能についての詳細はこちら)。また、クライアントのその他の主要な機能強化についても以下で説明します。
地理冗長ストレージへの読み取りアクセスのサポート
今回のリリースでは、セカンダリ拠点に存在するストレージ アカウント データへの読み取りアクセスが完全にサポートされました。この機能は、特定のストレージ アカウントに対して管理ポータルから有効にする必要があります。地理冗長ストレージの読み取りアクセスについての詳細は、こちらのページを参照してください。この記事で言及されているとおり、CloudBlobClient、CloudTableClient、CloudQueueClient の各クライアントに対して getServiceStats API が用意されており、この API を使用すると、レプリケーションのステータスと各サービスの LastSyncTime を簡単に取得できます。次に示す例では、クライアント オブジェクトで LocationMode を設定し、getServiceStats を呼び出しています。LocationMode は、RequestOptions オブジェクトで設定すると、要求ごとに構成することもできます。
CloudStorageAccount httpAcc = CloudStorageAccount.parse(connectionString);
CloudTableClient tClient = httpAcc.createCloudTableClient();
// getServiceStats はセカンダリのエンドポイントでのみサポートされているため、LocationMode は SECONDARY_ONLY に設定する
tClient.setLocationMode(LocationMode.SECONDARY_ONLY);
ServiceStats stats = tClient.getServiceStats();
Date lastSyncTime = stats.getGeoReplication().getLastSyncTime();
System.out.println(String.format("Replication status = %s and LastSyncTime = %s",stats.getGeoReplication().getStatus().toString(), lastSyncTime != null ? lastSyncTime.toString(): "empty"));
テーブルのサポート対象プロトコルの拡張 (JSON)
前回のリリースでは、テーブルのトラフィック送信時にはすべて AtomPub プロトコルが使用されていました。今回のリリースでは、既定のプロトコルが JSON になり、メタデータの量が最小化されました。このプロトコルの詳細およびペイロードのサンプルは、こちらのページ (英語) を参照してください。今回の機能強化により、クライアントでは、要求のペイロード サイズが大幅に削減されると共に、要求の処理による CPU 負荷も減少します。このため、クライアント アプリケーションをより大規模にスケーリングできるようになると共に、テーブル操作全体の待機時間 (レイテンシ) が短縮されます。次の例は、クライアント オブジェクトでの tablePayloadFormat の設定例を示したものです。tablePayloadFormat は、TableRequestOptions オブジェクトで設定すると、要求ごとに構成することもできます。
CloudStorageAccount httpAcc = CloudStorageAccount.parse(connectionString);
CloudTableClient tClient = httpAcc.createCloudTableClient();
// ペイロードの書式を JsonNoMetadata に設定する
tableClient.setTablePayloadFormat(TablePayloadFormat.JsonNoMetadata);
JsonNoMetadata を使用する場合、クライアント ライブラリは、クライアントから提供される POJO エンティティの種類に関する情報を検査し、この情報を基にプロパティの種類を「推察」します。さらに、DynamicTableEntity でクエリを実行する場合や、複数の種類のエンティティを混在して返す複雑なクエリを実行する場合など、シナリオによっては、クライアントがプロパティの種類の情報を実行時に提供することがあります。このようなときは、PropertyResolver を実装して、サービスから受け取ったデータに基づいて各プロパティに対応する EdmType 返せるようにします。次のサンプル コードは、PropertyResolver の実装例です。
public static class Class1 extends TableServiceEntity implements PropertyResolver {
private String A;
private byte[] B;
public String getA() {
return this.A;
}
public byte[] getB() {
return this.B;
}
public void setA(final String a) {
this.A = a;
}
public void setB(final byte[] b) {
this.B = b;
}
@Override
public EdmType propertyResolver(String pk, String rk, String key, String value) {
if (key.equals("A")) {
return EdmType.STRING;
}
else if (key.equals("B")) {
return EdmType.BINARY;
}
return null;
}
}
この例では、次に示すように PropertyResolver は TableRequestOptions に設定されています。
Class1 ref = new Class1();
ref.setA("myPropVal");
ref.setB(new byte[] { 0, 1, 2 });
ref.setPartitionKey("testKey");
ref.setRowKey(UUID.randomUUID().toString());
options.setPropertyResolver(ref);
テーブル挿入操作の最適化
前のバージョンの REST API では、挿入操作で Prefer ヘッダーがサポートされていなかったため、サービスは応答の本文でエンティティの内容を「エコー バック」するだけでした。今回のリリースでは、バッチ操作の一部として実行されるものも含めてテーブル挿入操作はすべて Prefer: return-no-content ヘッダーで送信されるため、コールバックは行われません。この最適化により、挿入操作による待機時間 (レイテンシ) が大きく短縮されます。ただし、挿入操作が正常に完了した場合に TableResult から返される HTTP ステータス コードは、201 (作成されました) ではなく 204 (コンテンツがありません) である点に注意してください。insert(TableEntity, ブール値) メソッドで true を指定すると、これまでと同様にエンティティの内容をエコー バックします。
テーブルのリフレクション操作の最適化
クライアントが POJO オブジェクトをテーブル サービスに保持している場合は、リフレクション呼び出しを繰り返し実行しないように、クライアントがその種類とプロパティ情報をキャッシュすることができるようになりました。この最適化により、クエリやその他のテーブル操作の実行による CPU 負荷が大きく低減されます。TableServiceEntity.setReflectedEntityCacheDisabled(true) と設定すると、クライアントはキャッシュを行いません。
新しい API とオーバーロード
お客様からのフィードバックにお応えし、下記の API サーフェイスを拡張して利便性の向上を図りました。
- CloudBlob.downloadRange
- CloudBlob.downloadToByteArray
- CloudBlob.downloadRangeToByteArray
- CloudBlob.uploadFromByteArray
- CloudBlob.uploadFromByteArray
- CloudBlob.downloadToFile
- CloudBlob.uploadFromFile
- CloudBlockBlob.uploadText
- CloudBlockBlob.downloadText
ログ記録
バージョン 0.5.0 では、SLF4J (英語) ログ ファサードを使用してログを記録することができます。これにより、ストレージ API と組み合わせてさまざまなログ フレームワークを使用し、要求実行時のログ情報を記録することができます。ログに記録される情報については、下の表を参照してください。アプリケーションのデバッグ作業でクライアントを支援するため、次期リリースではログに記録される情報をさらに拡充する予定です。
ログ出力データ
ログの各行には、次のデータが含まれます。
- クライアントの要求 ID: OperationContext でユーザーが指定した要求 ID
- イベント: 自由形式のテキスト
- ユーザーが選択した基盤ログ フレームワークによるその他の情報
たとえば、基盤となるログ フレームワークに Simple (英語) を選択した場合、ログ出力の行は次のようになります。
[main] INFO ROOT - {c88f2fe5-7150-467b-8571-7aa40a9a3e27} : {Starting operation.}
トレースのレベル
レベル |
イベント |
ERROR |
例外を内部的に処理できない、または処理しないでユーザーにスローする場合は、エラーとしてログに記録されます。 |
WARN |
例外をキャッチし内部的に処理する場合は、警告としてログに記録されます。この場合の主なユースケースとしては、処理の再試行が挙げられます。その際、ユーザーが再試行できるように、例外はユーザーにスロー バックされません。また、404 エラーを内部的に処理する CreateIfNotExists 操作などもこの場合に該当します。 |
INFO |
下記の情報がログに記録されます。
|
DEBUG |
現時点では、このレベルのログは記録されません。 |
TRACE |
現時点では、このレベルのログは記録されません。 |
*ログ記録が有効なときに SAS を使用する場合、SAS トークン自身がログに記録されるので注意が必要です。このような場合は、アプリケーションのログ出力を保護する必要があります。
ログの記録を有効にする
ここで重要となるコンセプトは、クライアントがトレース処理に提供するオプトイン/オプトアウト モデルです。通常のアプリケーションでは、指定したクラスで一定の冗長性を持たせながらトレースすることが一般的です。ほとんどのクライアント アプリケーションではこの方法で問題ありませんが、拡張性を備えたクラウド アプリケーションの場合、この方法ではユーザーが必要とする量よりもはるかに大量のデータが生成される可能性があります。このため、マイクロソフトが提供するオプトイン モデルのログ記録では、特定の冗長性を持つようにクライアントでリスナーを構成できると共に、選択された場合にのみ特定の要求のログだけを記録することが可能です。この設計により、必然的に、特定のクラスまたは層によるすべてのトラフィックを記録する「水平的」なログ記録ではなく、特定の要求で対象とされたスタックの層にまたがって「垂直的」なログ記録を実行できるようになりました。
もう 1 つの重要なコンセプトは、ファサード ログ記録モデルです。SLF4J (英語) はファサードであり、ログ フレームワークを提供するものではありません。つまり、ユーザーは、JDK のロガーで構築されたものや、その他のオープン ソースのロガーなど、基盤となるログ記録システムを使用可能です。SLF4J (英語) のサイトに、互換性のあるロガーのリストが記載されています。使用するロガーが決定し、それに対応する jar ファイルをクライアントに追加すると、選択したフレームワークにファサードをバインドすることができます。アプリケーションは、該当するフレームワーク専用の設定を使用してログを記録します。この結果、ユーザーが既にアプリケーション用のログ フレームワークを選択している場合、ストレージ SDK は個別のものを要求せずに、そのフレームワークを使用します。このファサード モデルにより、開発プロセス全体でフレームワークを簡単に変更できるため、ログ フレームワークを選ぶ際の柔軟性が高まります。
ログフレームワークの選択
ログを記録するためには、まず、ログ フレームワークを選択します。既にログ フレームワークを使用している場合は、SLF4J をバインドする jar ファイル (英語) のうち対応するものをクラスパスに追加するか、Maven に依存関係を追加します。ログ フレームワークを使用していない場合は、1 つ選択してクラスパスに追加するか、Maven に依存関係を追加します。さらに、SLF4J をネイティブで実装していない場合は、SLF4J をバインドする jar ファイル (英語) のうち対応するものを追加する必要があります。SLF4J は Simple (英語) と呼ばれる独自のロガー実装を採用しています。下に示すのは、その使用例です。SLF4J (英語) から slf4j-simple-1.7.5.jar をダウンロードし、クラスパスに追加するか、または次のコードを Maven の依存関係に追加します。
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.5</version>
</dependency>
これにより、コンソールに記録されるログのレベルが既定で INFO になります。ログ記録の設定変更に関する詳細は、Simple (英語) を参照してください。
1. SDK でログ記録を有効にする
既定では、Azure ストレージの Java SDK は、ログ フレームワークが存在しそれに対応する SLF4J がバインドされていても、ログを生成しません。このため、Azure ストレージのログが不要な場合は、ログ フレームワークの設定を編集する必要はありません。ログ記録が有効化されると、既定でルート ロガーの設定が使用されますが、要求ごとに他のロガーを指定することもできます。
例 1: すべての要求のログを記録する
OperationContext.setLoggingEnabledByDefault(true);
例 2: 単一の要求のログを記録する
OperationContext ctx = new OperationContext();
ctx.setLoggingEnabled(true);
blockBlobRef.upload(srcStream, -1, null /* accessCondition */, null /* requestOptions */, ctx);
例 3: 特定の要求に対し特定のロガーを使用する
OperationContext ctx = new OperationContext();
// OperationContext のログを記録する
ctx.setLoggingEnabled(true);
// SLF4J の LoggerFactory でロガーの名前を取得し、このロガーを使用する
// このロガー設定には、ログの場所とログを記録するレベルが記述される
ctx.setLogger(LoggerFactory.getLogger(“MyLogger”));
blockBlobRef.upload(srcStream, -1, null /* accessCondition */, null /* requestOptions */, ctx);
ストレージ サービスのログ記録機能とクライアント側のログ記録機能を併用すると、クライアント側とサーバー側の両方の視点から、アプリケーションを完全に監視することができます。
変更ログと大幅な変更点
既に述べたように、このリリースでは 2013-08-15 REST バージョンがサポートされています。このバージョンの機能および変更点の詳細については、MSDN やこちらのブログ記事をご確認ください。REST の変更の他に、今回のリリースではクライアント側でも機能の変更や追加が実施されています。主な変更点を以下にまとめました。また、変更ログ (英語) と大幅な変更点 (英語) の詳細は Github でご覧いただけます。
共通
- パッケージの構造が見直されました。
- RetryResult が RetryInfo に変更され、機能が追加されました。
- 要求の処理中に発生するイベント操作 (イベントの生成を含む) が同期されなくなりました (スレッドの安全性がイベント リスナーの CopyOnWriteArrayList で保証されるようになりました)。
- 接続が確立される前に OperationContext.sendingRequest イベントが生成されるようになり、ヘッダーを変更できるようになりました。
BLOB
- BLOB の downloadRange が Stream にダウンロードできるようになりました。これまでの downloadRange は、downloadRangeToByteArray に名前が変更されました。
- ページ BLOB のスパース化機能が廃止されました。
- CloudBlobContainer.createIfNotExist の名前が CloudBlobContainer.createIfNotExists に変更されました。
- CloudBlobClient.streamMinimumReadSizeInBytes が削除されました。この機能は、CloudBlob.streamMinimumReadSizeInBytes (クライアントごとではなく BLOB ごとに設定可能) で使用できます。
- CloudBlobClient.pageBlobStreamWriteSizeInBytes と CloudBlobClient.writeBlockSizeInBytes が削除されました。この機能は、CloudBlob.streamWriteSizeInBytes で使用できます。
テーブル
- Id フィールド (および getId、setId) が TableResult から削除されました。
- CloudTable.createIfNotExist の名前が CloudTable.createIfNotExists に変更されました。
- テーブルの挿入操作時に、内容をエコー バックしないようになりました。insert(TableEntity, ブール値) メソッドで true を指定すると、再び内容をエコー バックするようにできます。この変更により、挿入操作が正常に完了すると、TableResult の HTTP ステータス コードが 201 (作成されました) ではなく 204 (コンテンツがありません) になります。
- ペイロードの既定の形式が JsonMinimalMetadata になりました (AtomPub から変更)。ペイロードの形式は、CloudTableClient.setTablePayloadFormat を使用するとすべてのテーブルの要求に対して一括で指定でき、TableRequestOptions.setTablePayloadFormat を使用すると各要求に対して個別に指定できます。
キュー
- CloudQueue.createIfNotExist の名前が CloudQueue.createIfNotExists に変更されました。
まとめ
マイクロソフトは、今後も開発者のエクスペリエンスが向上するように Windows Azure ストレージを改良してまいります。ご意見やご要望がありましたら、お気軽にページ下部のコメント欄、フォーラム、または GitHub までお寄せください。また、何らかの問題が発生した場合は、GitHub にご報告いただけますと解決策が得られます。
Joe Giardino、Veena Udayabhanu、Emily Gerner、Adam Sorrin
関連情報
Windows Azure ストレージのリリース - CORS、JSON、分単位メトリックなど各種機能の導入
Windows Azure テーブル: JSON の紹介 (英語)