経緯
現在携わっているPJでAWS SDKを使用してるのですが、処理実行時にjava.lang.NoSuchFieldError: CLIENT_ENDPOINT
が発生するようになり、調べたところ、サービスによってライブラリバージョンv1とv2が混在していることが原因だったため、全てv2に揃える対応を行いました。
(cf. Github issue)
前提
- ビルドツールはMavenを使用
- 利用サービスは以下
- Cognito
- S3
- SES
- SQS
概要・基本方針
v1とv2の基本的な違いは公式ドキュメント AWS SDK for Java 1.x と 2.x の相違点に記載されているのと、各サービスの主要な処理についてはコードサンプルが用意されています。
ただ、一度v1で実装済のものはいちいちコードサンプル確認せずとも、以下の要領でほとんどバージョンアップが可能でした。
pom.xmlのdependency修正
- groupId:
com.amazonaws
→software.amazon.awssdk
- artifactId:
aws-java-sdk-{サービス名}
→{サービス名}
(cf. つまずいたところ①)
※BOMを使用している場合、BOMにも上記ルールが適用できる(今回の対応の発端を踏まえると、複数サービス利用するならBOM使用した方が良さそう)
各種clientクラス名・初期化方法
- clientクラス名から"AWS"、"Amazon"がなくなり、単純に
{サービス名}Client
に変わっているのでクラス名を修正してインポート - 初期化方法は
ClientBuilder.defaultClient()
→Client.create()
に変更
(cf. つまずいたところ②)
各種インスタンスの生成方法
- コンストラクタがprivate化されているため、staticな
bulder().build()
でインスタンスを生成する - フィールドアクセスメソッドを
with属性名()
→属性名()
に修正
AwsResultクラスの名称変更
{サービス処理名}Result
→{サービス処理名}Response
に変更されているので、クラス名を修正した上でv2のクラスをインポートする
つまづいたところ
①Cognitoのライブラリ
ライブラリの変更ルールは前述の通りなんですが、cognitoidpだけ、artifactIdはcognitoidentityproviderに置き換える必要がありました
(cognitoidpのv2も存在はしていて、バージョンがpreviewのまま4年近く放置されていたので一瞬焦りました。 →Github issue)
②ClientBuilder.defaultClient()
ざっとClientクラスを見ただけでは旧ClientBuilder.defaultClient()に相当するものが分からなかったため、一旦サンプルコードの通り実装したら見事にデグレました…
正しくはClient.create()で、よく見たら相違点ドキュメントにちゃんと記載ありました。
③SdkInternalListの廃止
v1ではリストを扱うにあたり、AWS SDK独自のリストを生成する必要があったのですが、java.util.Collectionで渡せるようになりました
(一瞬修正方針が分からず戸惑いましたが、これはナイス変更!)
④AwsResponse
AwsResponse(旧AmasonWebserviceResult)クラスにステータスコードをセットする方法について、v1では
- デフォルトコンストラクタでHttpResponseインスタンス生成
- setterでHttpResponseにステータスコードをセット
- 上記をSdkHttpMetadata.from()に渡してSdkHttpMetadata生成
- デフォルトコンストラクタでAwsWebServiceResultインスタンス生成
- AwsWebServiceResultにSdkHttpMetadataをセット
とステップを踏む必要がありましたが、v2では
- SdkHttpResponseにステータスコードをintで渡してビルド
- AwsResponseに上記を渡してビルド
とかなり簡略化できるようになりました。
レスポンスコード毎の後続処理の挙動確認をMockを使用して行う場合にデータ準備がかなり楽になりました!
⑤S3関連
- v1ではS3のリクエスト用オブジェクトはコンストラクタで生成できていたのですが、v2では他と同様
builder().build()
で生成するようになりました - ファイルアップロード
putObject()
について、v1ではリクエスト用オブジェクトにFileオブジェクトも保持していましたが、v2ではputObject()の第2引数のRequestBodyに持たせるようになりました - ファイルの存在チェック
doesObjectExist()
に相当するメソッドがv2には見当たらなかったため、getObject()
呼出時にNoSuchKeyException
を捕捉してハンドリングするよう変更しました(これはイマイチ変更…)
⑥builder().build()の可読性問題
もともと値のセットをwith属性名()のチェーンでやっていたところに、builder()とbuild()の2つが追加になってさらにチェーンが長くなりました…
フィールドアクセスメソッドから"with"がなくなった分短くなっている&フォーマッタである程度ネストが分かるよう綺麗にしてもらえるとはいえ、セットする値もbuilderでビルドする必要ある場合は、変数に切り出した方が良いように思いました。
(特にSESはフィールド値をビルドする機会が多い)