聞こえないJavaエンジニアが適当に書き連ねていく

つらつらとメモしたり日頃の溜まっている想いを吐き出す場所です。

保守運用からの成長

これは何

システム開発に関わる人は以下の流れでシステム開発に慣れていくのがよいのではと思ったのでメモする。

悠長なことを書いていると思われるかもしれないが、ある程度は個々のセンス次第でもある。

※タイトルはAIに考えてもらった

社歴ごとのフェーズ

未経験~2年目くらい

このフェーズでは、ひたすら実装経験を積んでコーディング、単体テストに慣れる。また、わからないところは先輩に聞いたり、自分で検索して解決する力を身につける。

新規開発プロジェクトなのか、保守プロジェクトなのか、はあまり問わない。保守プロジェクトといっても、動いているシステムに対して新規機能を追加したり、既存機能を直すプロジェクトであればよい。ひたすら実装経験を積むことが大事と考える。

3年目~5年目くらい

実装に慣れて、問題の解決もできるようになってくる頃。この段階では一度、実際に動いているシステムの保守運用に関わってシステムがどんなふうに動くのか、とか、システムトラブルが起きた時に先輩がどう動くのか、などを体で覚えるのが大事と考える。

また、動いているシステムに対して機能追加を行うとき、また、機能修正を行うときに気を付けないといけないこと、意識しないといけないことを覚えることで、次のステップに進んだ時に動きやすいのではないかと思う。

気を付けないといけないことはいろいろあると思うが、例えば

  • とある機能を直したときに、ほかの機能に影響が出ないかどうか
    • ほかの機能に影響が出る場合、どのように根回しが必要か
  • 既存の処理の流れに新しい機能を追加するとき、どのような対処が必要か
  • 新しく追加したい処理はいつ、どのタイミングで動くのか
  • いつまでに対応が必要か、また、リリースするときに何か臨時でやらないといけないことはあるか
  • 過去の修正履歴をどのように資料として保管するか

などがあると思う。この辺を意識できているかどうかで新規開発プロジェクトに参画した時にだいぶ違うのでは。

6年目~10年目くらい

このあたりになると設計もバリバリできることを期待されるはず。必要な機能をどのようにして実装、テストしやすい形に設計するか、を考えたり、どのようにして運用に載せるか、というリリース後も想定した設計を学ぶ段階と考える。

この時に、動いているシステムを保守運用したことがあるかどうか、で機能設計時の粒度が変わってきそうだな、と最近ちょっと思っているところがある。 あまり保守を経験したことがないと、一つ一つの機能がとてもでっかくて、長くなってしまって開発もテストも修正も全部が大変になってしまう設計をするような気がしている。

10年目以降

区切りよく10年目以降としているが、ある程度の経験を積んだ人は開発を引っ張っていく立場を求められるようになる。この時、できるだけ自分の持ち作業を減らしていくのが大事だと思う。自分が全部見る、というのは結果的に自分がパンクして精神的につらくなる。

システム開発に必要なものとは

個人的な見解だが、システム開発に必要なのは

  • 実装、テストの経験(エラーを自分で解決する経験)
  • 動いているシステムを保守運用して、どんなふうに動くのかを自分の目で覚える経験
  • ユーザーとの会話から必要な機能を幅広く考える経験
  • 設計書をわかりやすく書く能力(文章をだらだらと書かない)

ではなかろうかと思う。

特に、動いているシステムを保守運用して、どんなふうに動くのかを自分の目で覚える経験が希薄な状態で大規模開発に参画してしまうと、この機能はどんなふうに動くのか、とか、データ量はこのくらいか、などひらめくのが難しいように思う。

まとめ

大規模開発に設計担当として参画する(参画させる)人は半年から1年は保守運用の経験を積むべき!

Eclipse + checkStyle の設定メモ

前提

ベースにするのはGooglecheckStyle設定。

いつかリンクが切れる可能性はあるけど直リンクも貼っておく。 checkstyle/google_checks.xml at master · checkstyle/checkstyle

困ること

日本人なので、JavaDocのコメントは「。」終わりにしたいのに、「.」で終わらないとダメだよと怒られる。

エラーメッセージ:Javadocの最初の文に末尾のピリオドがありません。

対処方法

注意:checkStyle バージョン8.43の時の対処法なので、バージョンアップで設定方法が変わる可能性はある。

SummaryJavadocのプロパティ、periodを定義する。

<module name="SummaryJavadoc">
  <property name="forbiddenSummaryFragments"
           value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
  <property name="period" value="。"/>   <!-- この行を追加 -->      
</module>

参考

SummaryJavadocCheck (checkstyle 8.43 API)

少し在宅勤務を経験した思い出

これは何

ちょっとだけ在宅勤務を経験したのであとから振り返れるように。

前提

  • ブログ主は聴覚障害があるので音声コミュニケーションが不可能。
  • ブログ主は関東圏に住んでいる。
  • 期間限定だろうなと察していたので在宅勤務用に新たに何か買うということはしなかった。(家に元々あった環境で対応した)

在宅勤務の期間について

緊急事態宣言が発令されたことに伴い、 2021/1/12~2021/3/2までの基本、月曜日と火曜日が在宅勤務になった。 水曜日~金曜日は内部/外部打ち合わせがあったので会社に出勤していた。

出勤時も時差勤務は認められていたので、9:00~17:30が通常勤務時間帯のところを、8:30~17:00勤務にした。

緊急事態宣言が明けることで、在宅勤務も終了というお達しが出た。 (これを書いているのが2/26なのでまだ明けていないが、今の状況だと2021/3/7で終了でしょ?)

在宅勤務をやってみて思ったこと

よかった点

  • 始業時間ギリギリまで家のことが出来るのは楽だった。
  • 犬を飼っているので、朝の散歩に行けたのはちょっとした運動になってよかった。
    • 暗くなるのが早いので、2回目の散歩は日中に妻に行ってもらった。
  • 子どもが学校から帰ってきたときに、「おかえり」と言えたのは嬉しかった。
  • 終業時間になったらすぐにOFFモードになれるのは楽。

これは厳しいかなという点

対面で相談したいときに困る

今の業務ではSlackを使っているのでテキストコミュニケーションのため、さほど困らなかったが、 もし、プログラミングしていて、プログラムについて相談したいときに対面でないと相談し辛いことがあるかもしれない。

聞こえる人同士なら、画面を共有して、ボイスチャットとかできるのかもしれないが、その辺はよくわからない。

運動不足になる

歩数を確認したところ、通勤って意外とエネルギーを使っているのだなということがはっきりした。 ちょっと寒い時期だったので仕事後にウォーキングやランニングをする気力がわかなかったが、暖かい時期なら 仕事後に少し運動すべきかもしれない。

通勤時間帯のスイッチの切り替えが出来ない

片道1時間半以上かけて通勤しているので、通勤時間帯にスマホゲームをしたりTwitterを読んだりYahooニュースを見たりして なんとなくスイッチを切り替えていた。

在宅だと、この時間が無くなるのでスイッチの切り替え時間がなくなる、というのはある。 仕事に行き詰まる時期ではなかったのでさほど支障はなかったが、仕事に行き詰まっている時はこの気分転換の時間は必要だろうなと思う。

まとめると

在宅勤務のいいところ、会社に出勤のいいところ、両方それぞれ存在する。

社員が自分で選択できる勤務体制になるのがいいのだろうな、と思う。

単純なSpring Applicationで、コマンドラインから動かしたい処理を指定する方法

これは何

単純なSpring Applicationで、コマンドラインから動かしたい処理を指定する方法のメモ。

やりたいこと

  • Spring Batchではない、通常のコマンドラインアプリケーション。
  • Spring資源を使いたい。
  • 起動する処理は引数で指定したい。

方法

  • SpringApplication.runの戻り値でApplicationContextが取得できる。
  • ApplicationContext.getBean(bean名称)で動かしたいBeanが取得できる。

ソース

Gist多田さんに教えてもらった記述をほぼそのままコピペしている。

Componentの名前を指定しない時

package ta.su.simple;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(DemoApplication.class, args);

    //context.containsBean(bean名称) でそのBeanが存在するか事前にチェックが出来る。
        if(!context.containsBean("cBean")) {
            throw new IllegalArgumentException("不正なBean名を指定した");
        }

    //Bean名称はクラス名称と同一になる。
        Bean bean = context.getBean("ABean", Bean.class);
        bean.run();
    }

}

interface Bean {
    public void run();
}

@Component
class ABean implements Bean {

    @Override
    public void run() {
        System.out.println("aBeanが呼ばれた");
    }
}

@Component
class BBean implements Bean {

    @Override
    public void run() {
        System.out.println("bBeanが呼ばれた");
    }
}

Componentの名前を指定する時

package ta.su.simple;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
        
        if(!context.containsBean("cBean")) {
            throw new IllegalArgumentException("不正なBean名を指定した");
        }
        
    //Bean名称はComponentアノテーションで指定した名称になる。
        Bean bean = context.getBean("aBean", Bean.class);
        bean.run();
    }

}

interface Bean {
    public void run();
}

@Component("aBean")
class ABean implements Bean {

    @Override
    public void run() {
        System.out.println("aBeanが呼ばれた");
    }
}

@Component("bBean")
class BBean implements Bean {

    @Override
    public void run() {
        System.out.println("bBeanが呼ばれた");
    }
}

COVID-19のワクチン接種でマイナンバーはどう使われる想定なのか?

ニュースや河野大臣のtweetを見て、こういう感じなのかなとざっくりフローを作ってみた。

処理順は省いてしまった。書いたほうがよかったか…?

f:id:su_zu_ki_1010:20210127083422p:plain
ワクチン接種フロー

接種実績登録や接種実績照会のキーとしてマイナンバーを使うことを想定されているのだろうと思う。 マイナンバーは重複して割り振られることは無いので、登録漏れが無ければ確実に結果が分かるはず。

plantumlのソースコードは以下。

@startuml

actor 自治体A
actor 自治体B
actor 住民
actor 接種会場
database 専用システム
database マイナンバー

自治体A --> (クーポン券発行)
(クーポン券発行) <-- (専用システム)

(専用システム) <- (マイナンバー)

自治体A --> (クーポン券支給)
(クーポン券支給) --> (住民)

(住民) --> (ワクチン接種):クーポンを\n提示
(ワクチン接種) --> (接種会場)
(接種会場) -> (接種報告):クーポンを\n提出?
(接種報告) -> (自治体A)

(自治体A) --> (接種実績登録):この人は\n接種済と登録
(接種実績登録) --> (専用システム)

(自治体A) --> (接種実績照会)
(接種実績照会) --> (専用システム)

(住民) -> (転居)
(転居) -> (自治体B)

(自治体B)-> (接種実績照会)

note bottom of (自治体B)
転居してきた住民が
接種済か確認する。
end note

@enduml

「ユーザのための要件定義ガイド 第2版 要件定義を成功に導く128の勘どころ」を読んだ

これは何

要件定義フェーズに関わりそうなので、要件定義について調べようと検索したらヒットした ユーザのための要件定義ガイド 第2版 要件定義を成功に導く128の勘どころ:IPA 独立行政法人 情報処理推進機構を読んだので感想をつらつらと書いておく。

独立行政法人情報処理推進機構IPA)社会基盤センターが2019年12月20日にに発行したもので、 書籍版とPDF版があるが、内容は同じ。書籍版は2500円するが、PDF版は無料で読める。

全部で498ページあり、PDF版は14.6MBある。

ユーザーのための、と書いてあるが、ITベンダも読んで理解すべき内容が多く載っていた。

構成

章立ては以下の通り。

  • はじめに
  • 第1章 背景
  • 第2章 要件定義の問題認識
  • 第3章 要件定義の全体像
  • 第4章 ビジネス要求定義(BR)における問題と解決の勘どころ
  • 第5章 システム化要求定義(SR)における問題と解決の勘どころ
  • 第6章 要件定義マネジメント(RM)における問題と解決の勘どころ
  • 第7章 要件定義の主要ドキュメント作成(DD)の勘どころ

はじめに

最後の方に、章ごとに想定している読者層の表が載っている。自分がどの層に当てはまるのか確認すると 自分が特に重視して読むべき章はどれかが分かる。

自分はITベンダという立場で読んだ。

第1章 背景

現状のシステム開発の背景が載っている章。 特に関心が無ければ、流し読みでいいかな、と思う。

第2章 要件定義の問題認識

要件定義を行うにあたって、それぞれの立場で意識しないといけないことが書かれている。 この章はある程度しっかり読みこむことで、要件定義を行う際に自分が意識しないといけないことを改めて認識出来る。

一度でも要件定義をしたことがある人は、前回の要件定義でここがダメだったな、という振り返りにもなる。

第3章 要件定義の全体像

要件定義の全体像がまとめられている。自分がこれからやること、作るものについてざっくり把握するのに役立つ。

第4章 ビジネス要求定義(BR)における問題と解決の勘どころ

ITベンダの立場で読むと、お客様に何をやっていただきたいか、何をお願いしたほうがいいのか、を整理できる。 お客様の話を聞いていて、なんかはっきりしないな?と思った時に確認すべき事項が載っている、ともいえる。

自分はこの章は真剣に読んだ。

第5章 システム化要求定義(SR)における問題と解決の勘どころ

お客様の要望をシステム化するにあたって何を考える必要があるのか、を整理できる。

自分はこの章は真剣に読んだ。

特に5.1 システム化要求の仕様化 の章に書いてある内容は読んでいて 自分が今までに書いたドキュメントについてとても反省した。

第6章 要件定義マネジメント(RM)における問題と解決の勘どころ

この章はITベンダ側のプロジェクトマネージャという立場で読むときは重要だと思う。 でも、ITベンダの一介の設計担当として読む分には、あまり重要ではないかなと感じた。 (意識しすぎると却って疲れてしまうので、この章の部分はマネジメント担当にお願いしたほうが楽)

第7章 要件定義の主要ドキュメント作成(DD)の勘どころ

要件定義を行い、最終的にどのようなシステムにするのかを決めるにはドキュメント作成は避けて通れない。 後々困らないためのドキュメントとはどういうものなのか、がこの章にまとまっている。

作る必要があるドキュメントの量が多くて気が滅入りそうになる。 でも、各ドキュメントのサンプルも載っているのでイメージはしやすい。

自分がこれからどんなドキュメントを作るのか、を改めて意識することが出来た。

まとめ

要件定義という一連の作業についてざっくり把握する入門のドキュメントとしては非常にお勧め出来る。 ただし、一気に読むのは疲れてしまうので、他の技術文書同様に段階を踏んで読むのがいい。

  1. 全体をざっと流し読みして自分に特に関係ありそうな章をまず把握する。
  2. 自分に関係ありそうな章を読み、わからないキーワードをメモする。
  3. わからないキーワードを調べて意味を理解して、再度関係ありそうな章を読む。

2と3を繰り返すことで理解が深まると思うし、その上で詳細をもっと勉強したくなったら 詳細が知りたいキーワードで検索して出てきたページや本を読む、という流れがよさそう。

LocalStack + Spring Boot でAWS SQSの開発環境を構築する際のメモ

これは何

動くようになるまでにドはまりしたのであとあと確認出来るようにメモ

前提

関係ありそうなバージョン情報は以下の通り

  • Java:11
  • Spring Boot:2.3.7
  • Spring Cloud:Hoxton.SR9
  • aws-java-sdk-core:1.11.792

AWS SDKはVersion2が出ているようだが、Spring Boot側がそれに対応したバージョンがまだリリースされていない模様。

build.gradleの定義

spring initializr でweb、aws core、aws simple queue serviceを選択している。

plugins {
    id 'org.springframework.boot' version '2.3.7.RELEASE'
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
    mavenCentral()
}

ext {
    set('springCloudVersion', "Hoxton.SR9")
}

dependencies {

    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.cloud:spring-cloud-starter-aws'
    implementation 'org.springframework.cloud:spring-cloud-starter-aws-messaging'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

test {
    useJUnitPlatform()
}

依存関係

compileClasspathのみ記載。

compileClasspath - Compile classpath for source set 'main'.
+--- org.springframework.boot:spring-boot-starter-web -> 2.3.7.RELEASE
|    +--- org.springframework.boot:spring-boot-starter:2.3.7.RELEASE
|    |    +--- org.springframework.boot:spring-boot:2.3.7.RELEASE
|    |    |    +--- org.springframework:spring-core:5.2.12.RELEASE
|    |    |    |    \--- org.springframework:spring-jcl:5.2.12.RELEASE
|    |    |    \--- org.springframework:spring-context:5.2.12.RELEASE
|    |    |         +--- org.springframework:spring-aop:5.2.12.RELEASE
|    |    |         |    +--- org.springframework:spring-beans:5.2.12.RELEASE
|    |    |         |    |    \--- org.springframework:spring-core:5.2.12.RELEASE (*)
|    |    |         |    \--- org.springframework:spring-core:5.2.12.RELEASE (*)
|    |    |         +--- org.springframework:spring-beans:5.2.12.RELEASE (*)
|    |    |         +--- org.springframework:spring-core:5.2.12.RELEASE (*)
|    |    |         \--- org.springframework:spring-expression:5.2.12.RELEASE
|    |    |              \--- org.springframework:spring-core:5.2.12.RELEASE (*)
|    |    +--- org.springframework.boot:spring-boot-autoconfigure:2.3.7.RELEASE
|    |    |    \--- org.springframework.boot:spring-boot:2.3.7.RELEASE (*)
|    |    +--- org.springframework.boot:spring-boot-starter-logging:2.3.7.RELEASE
|    |    |    +--- ch.qos.logback:logback-classic:1.2.3
|    |    |    |    +--- ch.qos.logback:logback-core:1.2.3
|    |    |    |    \--- org.slf4j:slf4j-api:1.7.25 -> 1.7.30
|    |    |    +--- org.apache.logging.log4j:log4j-to-slf4j:2.13.3
|    |    |    |    +--- org.slf4j:slf4j-api:1.7.25 -> 1.7.30
|    |    |    |    \--- org.apache.logging.log4j:log4j-api:2.13.3
|    |    |    \--- org.slf4j:jul-to-slf4j:1.7.30
|    |    |         \--- org.slf4j:slf4j-api:1.7.30
|    |    +--- jakarta.annotation:jakarta.annotation-api:1.3.5
|    |    +--- org.springframework:spring-core:5.2.12.RELEASE (*)
|    |    \--- org.yaml:snakeyaml:1.26
|    +--- org.springframework.boot:spring-boot-starter-json:2.3.7.RELEASE
|    |    +--- org.springframework.boot:spring-boot-starter:2.3.7.RELEASE (*)
|    |    +--- org.springframework:spring-web:5.2.12.RELEASE
|    |    |    +--- org.springframework:spring-beans:5.2.12.RELEASE (*)
|    |    |    \--- org.springframework:spring-core:5.2.12.RELEASE (*)
|    |    +--- com.fasterxml.jackson.core:jackson-databind:2.11.3
|    |    |    +--- com.fasterxml.jackson.core:jackson-annotations:2.11.3
|    |    |    \--- com.fasterxml.jackson.core:jackson-core:2.11.3
|    |    +--- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.3
|    |    |    +--- com.fasterxml.jackson.core:jackson-core:2.11.3
|    |    |    \--- com.fasterxml.jackson.core:jackson-databind:2.11.3 (*)
|    |    +--- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.3
|    |    |    +--- com.fasterxml.jackson.core:jackson-annotations:2.11.3
|    |    |    +--- com.fasterxml.jackson.core:jackson-core:2.11.3
|    |    |    \--- com.fasterxml.jackson.core:jackson-databind:2.11.3 (*)
|    |    \--- com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.3
|    |         +--- com.fasterxml.jackson.core:jackson-core:2.11.3
|    |         \--- com.fasterxml.jackson.core:jackson-databind:2.11.3 (*)
|    +--- org.springframework.boot:spring-boot-starter-tomcat:2.3.7.RELEASE
|    |    +--- jakarta.annotation:jakarta.annotation-api:1.3.5
|    |    +--- org.apache.tomcat.embed:tomcat-embed-core:9.0.41
|    |    +--- org.glassfish:jakarta.el:3.0.3
|    |    \--- org.apache.tomcat.embed:tomcat-embed-websocket:9.0.41
|    |         \--- org.apache.tomcat.embed:tomcat-embed-core:9.0.41
|    +--- org.springframework:spring-web:5.2.12.RELEASE (*)
|    \--- org.springframework:spring-webmvc:5.2.12.RELEASE
|         +--- org.springframework:spring-aop:5.2.12.RELEASE (*)
|         +--- org.springframework:spring-beans:5.2.12.RELEASE (*)
|         +--- org.springframework:spring-context:5.2.12.RELEASE (*)
|         +--- org.springframework:spring-core:5.2.12.RELEASE (*)
|         +--- org.springframework:spring-expression:5.2.12.RELEASE (*)
|         \--- org.springframework:spring-web:5.2.12.RELEASE (*)
+--- org.springframework.cloud:spring-cloud-starter-aws -> 2.2.5.RELEASE
|    +--- org.springframework.cloud:spring-cloud-aws-context:2.2.5.RELEASE
|    |    +--- org.springframework.cloud:spring-cloud-aws-core:2.2.5.RELEASE
|    |    |    +--- org.springframework:spring-beans:5.2.8.RELEASE -> 5.2.12.RELEASE (*)
|    |    |    +--- org.springframework:spring-aop:5.2.8.RELEASE -> 5.2.12.RELEASE (*)
|    |    |    +--- com.amazonaws:aws-java-sdk-core:1.11.792
|    |    |    |    +--- org.apache.httpcomponents:httpclient:4.5.9 -> 4.5.13
|    |    |    |    |    +--- org.apache.httpcomponents:httpcore:4.4.13 -> 4.4.14
|    |    |    |    |    \--- commons-codec:commons-codec:1.11 -> 1.14
|    |    |    |    +--- software.amazon.ion:ion-java:1.0.2
|    |    |    |    +--- com.fasterxml.jackson.core:jackson-databind:2.6.7.3 -> 2.11.3 (*)
|    |    |    |    +--- com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.6.7 -> 2.11.3
|    |    |    |    |    +--- com.fasterxml.jackson.core:jackson-databind:2.11.3 (*)
|    |    |    |    |    \--- com.fasterxml.jackson.core:jackson-core:2.11.3
|    |    |    |    \--- joda-time:joda-time:2.8.1
|    |    |    +--- com.amazonaws:aws-java-sdk-s3:1.11.792
|    |    |    |    +--- com.amazonaws:aws-java-sdk-kms:1.11.792
|    |    |    |    |    +--- com.amazonaws:aws-java-sdk-core:1.11.792 (*)
|    |    |    |    |    \--- com.amazonaws:jmespath-java:1.11.792
|    |    |    |    |         \--- com.fasterxml.jackson.core:jackson-databind:2.6.7.3 -> 2.11.3 (*)
|    |    |    |    +--- com.amazonaws:aws-java-sdk-core:1.11.792 (*)
|    |    |    |    \--- com.amazonaws:jmespath-java:1.11.792 (*)
|    |    |    +--- com.amazonaws:aws-java-sdk-ec2:1.11.792
|    |    |    |    +--- com.amazonaws:aws-java-sdk-core:1.11.792 (*)
|    |    |    |    \--- com.amazonaws:jmespath-java:1.11.792 (*)
|    |    |    +--- com.amazonaws:aws-java-sdk-cloudformation:1.11.792
|    |    |    |    +--- com.amazonaws:aws-java-sdk-core:1.11.792 (*)
|    |    |    |    \--- com.amazonaws:jmespath-java:1.11.792 (*)
|    |    |    \--- org.slf4j:slf4j-api:1.7.30
|    |    +--- org.springframework:spring-context:5.2.8.RELEASE -> 5.2.12.RELEASE (*)
|    |    \--- org.slf4j:slf4j-api:1.7.30
|    +--- org.springframework.cloud:spring-cloud-aws-autoconfigure:2.2.5.RELEASE
|    |    +--- org.springframework.cloud:spring-cloud-aws-context:2.2.5.RELEASE (*)
|    |    +--- org.springframework.boot:spring-boot-autoconfigure:2.3.2.RELEASE -> 2.3.7.RELEASE (*)
|    |    \--- org.slf4j:slf4j-api:1.7.30
|    \--- org.slf4j:slf4j-api:1.7.30
\--- org.springframework.cloud:spring-cloud-starter-aws-messaging -> 2.2.5.RELEASE
     +--- org.springframework.cloud:spring-cloud-starter-aws:2.2.5.RELEASE (*)
     +--- org.springframework.cloud:spring-cloud-aws-messaging:2.2.5.RELEASE
     |    +--- org.springframework.cloud:spring-cloud-aws-context:2.2.5.RELEASE (*)
     |    +--- com.amazonaws:aws-java-sdk-sns:1.11.792
     |    |    +--- com.amazonaws:aws-java-sdk-sqs:1.11.792
     |    |    |    +--- com.amazonaws:aws-java-sdk-core:1.11.792 (*)
     |    |    |    \--- com.amazonaws:jmespath-java:1.11.792 (*)
     |    |    +--- com.amazonaws:aws-java-sdk-core:1.11.792 (*)
     |    |    \--- com.amazonaws:jmespath-java:1.11.792 (*)
     |    +--- com.amazonaws:aws-java-sdk-sqs:1.11.792 (*)
     |    +--- org.springframework:spring-messaging:5.2.8.RELEASE -> 5.2.12.RELEASE
     |    |    +--- org.springframework:spring-beans:5.2.12.RELEASE (*)
     |    |    \--- org.springframework:spring-core:5.2.12.RELEASE (*)
     |    \--- org.slf4j:slf4j-api:1.7.30
     \--- org.slf4j:slf4j-api:1.7.30

application.propertiesの定義

AWSにアクセスするのではなく、開発端末でLocalStackを使って疑似サーバーを立ててテストするための設定になっている。

cloud.aws.stack.auto=false
cloud.aws.region.auto=false
cloud.aws.region.static=ap-northeast-1
cloud.aws.credentials.access-key=dummy
cloud.aws.credentials.secret-key=dummy

logging.level.com.amazonaws.util.EC2MetadataUtils=error

aws.endpoint=http://localhost:4566

LocalStackについて

localstack/localstack: 💻 A fully functional local AWS cloud stack. Develop and test your cloud & Serverless apps offline!

  • AWSだと課金が怖いので、まずは開発環境で色々試すためのツール。
  • Dockerで動かす。
  • 今回はWindows 10 Pro の端末上にDockerをインストールして、そのうえで動かした。

docker-compose.yml の内容

初期値のままでも大丈夫だったかもしれないけど、とりあえず動いたときのものを貼り付けておく。

version: '2.1'

services:
  localstack:
    container_name: "${LOCALSTACK_DOCKER_NAME-localstack_main}"
    image: localstack/localstack
    network_mode: bridge
    ports:
      - "4566:4566"
      - "4571:4571"
      - "18080:18080"
    environment:
      - SERVICES=${SERVICES- }
      - DEBUG=${DEBUG- }
      - DATA_DIR=${DATA_DIR- }
      - PORT_WEB_UI=${PORT_WEB_UI- }
      - LAMBDA_EXECUTOR=${LAMBDA_EXECUTOR- }
      - KINESIS_ERROR_PROBABILITY=${KINESIS_ERROR_PROBABILITY- }
      - DOCKER_HOST=unix:///var/run/docker.sock
      - HOST_TMP_FOLDER=${TMPDIR}
      - LAMBDA_DOCKER_NETWORK=host
      - AWS_CBOR_DISABLE=true
      - USE_SSL=false
      - AWS_ACCESS_KEY_ID=dummy
      - AWS_SECRET_ACCESS_KEY=dummy
    volumes:
      - "${TMPDIR:-/tmp/localstack}:/tmp/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"

コマンド集

キューを作成

aws --region ap-northeast-1 --endpoint-url http://localhost:4566 sqs create-queue --queue-name 'foo-queue'

メッセージ送信

aws --region ap-northeast-1 --endpoint-url http://localhost:4566 sqs send-message --queue-url 'http://localhost:4566/000000000000/foo-queue' --message-body 'hogehoge'

動くようになるまでに悩まされたものその1

アプリ起動時にcom.amazonaws.SdkClientException: Failed to connect to service endpoint:エラーが出る

2020-12-15 15:08:00.739  WARN 18360 --- [           main] i.InstanceMetadataServiceResourceFetcher : Fail to retrieve token 

com.amazonaws.SdkClientException: Failed to connect to service endpoint: 
    at com.amazonaws.internal.EC2ResourceFetcher.doReadResource(EC2ResourceFetcher.java:100) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.internal.InstanceMetadataServiceResourceFetcher.getToken(InstanceMetadataServiceResourceFetcher.java:91) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.internal.InstanceMetadataServiceResourceFetcher.readResource(InstanceMetadataServiceResourceFetcher.java:69) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.internal.EC2ResourceFetcher.readResource(EC2ResourceFetcher.java:66) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.util.EC2MetadataUtils.getItems(EC2MetadataUtils.java:402) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.util.EC2MetadataUtils.getData(EC2MetadataUtils.java:371) ~[aws-java-sdk-core-1.11.792.jar:na]
    at org.springframework.cloud.aws.context.support.env.AwsCloudEnvironmentCheckUtils.isRunningOnCloudEnvironment(AwsCloudEnvironmentCheckUtils.java:38) ~[spring-cloud-aws-context-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.cloud.aws.context.annotation.OnAwsCloudEnvironmentCondition.matches(OnAwsCloudEnvironmentCondition.java:38) ~[spring-cloud-aws-context-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.context.annotation.ConditionEvaluator.shouldSkip(ConditionEvaluator.java:108) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader$TrackedConditionEvaluator.shouldSkip(ConfigurationClassBeanDefinitionReader.java:477) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:131) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:120) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:331) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:236) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:280) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:96) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:707) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:533) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:405) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at com.example.demo.AaaaApplication.main(AaaaApplication.java:21) ~[main/:na]
Caused by: java.net.SocketException: Network is unreachable: connect
    at java.base/java.net.PlainSocketImpl.waitForConnect(Native Method) ~[na:na]
    at java.base/java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:107) ~[na:na]
    at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:399) ~[na:na]
    at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:242) ~[na:na]
    at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:224) ~[na:na]
    at java.base/java.net.Socket.connect(Socket.java:609) ~[na:na]
    at java.base/sun.net.NetworkClient.doConnect(NetworkClient.java:177) ~[na:na]
    at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:474) ~[na:na]
    at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:569) ~[na:na]
    at java.base/sun.net.www.http.HttpClient.<init>(HttpClient.java:242) ~[na:na]
    at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:341) ~[na:na]
    at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:362) ~[na:na]
    at java.base/sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1253) ~[na:na]
    at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1232) ~[na:na]
    at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1081) ~[na:na]
    at java.base/sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:1015) ~[na:na]
    at com.amazonaws.internal.ConnectionUtils.connectToEndpoint(ConnectionUtils.java:52) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.internal.EC2ResourceFetcher.doReadResource(EC2ResourceFetcher.java:80) ~[aws-java-sdk-core-1.11.792.jar:na]
    ... 25 common frames omitted

検索しまくったところ、EC2 metadata resolution related exception thrown when running application locally · Issue #556 · spring-cloud/spring-cloud-aws を見つけた。

EC2 metadata resolution related exception thrown when running application locally · Issue #556 · spring-cloud/spring-cloud-aws によると、AWS SDK v1 (checked with 1.11.791)からこのエラーが出るようになったらしい。

application.propertiosに以下の記述を追加して回避する。

logging.level.com.amazonaws.util.EC2MetadataUtils=error

動くようになるまでに悩まされたものその2

トークンの認証なんたらとエラーが出る。

2020-12-15 15:23:48.243 ERROR 11644 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'simpleMessageListenerContainer' defined in class path resource [org/springframework/cloud/aws/messaging/config/annotation/SqsConfiguration.class]: Invocation of init method failed; nested exception is com.amazonaws.services.sqs.model.AmazonSQSException: The security token included in the request is invalid. (Service: AmazonSQS; Status Code: 403; Error Code: InvalidClientTokenId; Request ID: 806c2625-65f0-56f0-95a0-7e773d023d56; Proxy: null)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1794) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:897) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:879) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:551) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:405) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at com.example.demo.AaaaApplication.main(AaaaApplication.java:21) ~[main/:na]
Caused by: com.amazonaws.services.sqs.model.AmazonSQSException: The security token included in the request is invalid. (Service: AmazonSQS; Status Code: 403; Error Code: InvalidClientTokenId; Request ID: 806c2625-65f0-56f0-95a0-7e773d023d56; Proxy: null)
    at com.amazonaws.http.AmazonHttpClient$RequestExecutor.handleErrorResponse(AmazonHttpClient.java:1811) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.http.AmazonHttpClient$RequestExecutor.handleServiceErrorResponse(AmazonHttpClient.java:1395) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeOneRequest(AmazonHttpClient.java:1371) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeHelper(AmazonHttpClient.java:1145) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.http.AmazonHttpClient$RequestExecutor.doExecute(AmazonHttpClient.java:802) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeWithTimer(AmazonHttpClient.java:770) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.http.AmazonHttpClient$RequestExecutor.execute(AmazonHttpClient.java:744) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.http.AmazonHttpClient$RequestExecutor.access$500(AmazonHttpClient.java:704) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.http.AmazonHttpClient$RequestExecutionBuilderImpl.execute(AmazonHttpClient.java:686) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:550) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:530) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.services.sqs.AmazonSQSClient.doInvoke(AmazonSQSClient.java:2207) ~[aws-java-sdk-sqs-1.11.792.jar:na]
    at com.amazonaws.services.sqs.AmazonSQSClient.invoke(AmazonSQSClient.java:2174) ~[aws-java-sdk-sqs-1.11.792.jar:na]
    at com.amazonaws.services.sqs.AmazonSQSClient.invoke(AmazonSQSClient.java:2163) ~[aws-java-sdk-sqs-1.11.792.jar:na]
    at com.amazonaws.services.sqs.AmazonSQSClient.executeGetQueueUrl(AmazonSQSClient.java:1201) ~[aws-java-sdk-sqs-1.11.792.jar:na]
    at com.amazonaws.services.sqs.AmazonSQSClient.getQueueUrl(AmazonSQSClient.java:1173) ~[aws-java-sdk-sqs-1.11.792.jar:na]
    at org.springframework.cloud.aws.messaging.support.destination.DynamicQueueUrlDestinationResolver.resolveDestination(DynamicQueueUrlDestinationResolver.java:94) ~[spring-cloud-aws-messaging-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.cloud.aws.messaging.support.destination.DynamicQueueUrlDestinationResolver.resolveDestination(DynamicQueueUrlDestinationResolver.java:38) ~[spring-cloud-aws-messaging-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.messaging.core.CachingDestinationResolverProxy.resolveDestination(CachingDestinationResolverProxy.java:92) ~[spring-messaging-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.cloud.aws.messaging.listener.AbstractMessageListenerContainer.queueAttributes(AbstractMessageListenerContainer.java:321) ~[spring-cloud-aws-messaging-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.cloud.aws.messaging.listener.AbstractMessageListenerContainer.initialize(AbstractMessageListenerContainer.java:293) ~[spring-cloud-aws-messaging-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.cloud.aws.messaging.listener.SimpleMessageListenerContainer.initialize(SimpleMessageListenerContainer.java:112) ~[spring-cloud-aws-messaging-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.cloud.aws.messaging.listener.AbstractMessageListenerContainer.afterPropertiesSet(AbstractMessageListenerContainer.java:268) ~[spring-cloud-aws-messaging-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.cloud.aws.messaging.listener.SimpleMessageListenerContainer.afterPropertiesSet(SimpleMessageListenerContainer.java:46) ~[spring-cloud-aws-messaging-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1853) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1790) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    ... 17 common frames omitted

このエラーが出たときのコードは以下の通り。

package com.example.demo.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.aws.messaging.core.QueueMessagingTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.sqs.AmazonSQSAsync;
import com.amazonaws.services.sqs.AmazonSQSAsyncClientBuilder;

@Configuration
public class AwsSQSConfig {

    Logger logger = LoggerFactory.getLogger(AwsSQSConfig.class);

    @Value("${cloud.aws.region.static}")
    private String region;

    @Value("${cloud.aws.credentials.access-key}")
    private String awsAccessKey;

    @Value("${cloud.aws.credentials.secret-key}")
    private String awsSecretKey;

    @Value("${aws.endpoint}")
    private String endPoint;

    @Bean
    public QueueMessagingTemplate queueMessagingTemplate() {
        return new QueueMessagingTemplate(amazonSQSAsync());
    }

    @Primary
    @Bean
    public AmazonSQSAsync amazonSQSAsync() {

        logger.info("region:{}, awsAccessKey:{}, awsSecretKey:{}", region, awsAccessKey, awsSecretKey);

        AmazonSQSAsync async = AmazonSQSAsyncClientBuilder.standard()
                .withRegion(Regions.AP_NORTHEAST_1)
                .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(awsAccessKey, awsSecretKey)))
                //.withEndpointConfiguration(new EndpointConfiguration(endPoint, region))
                .build();
        
        return async;
    }

}

デバッグで追いかけたところ、エンドポイントのURLがlocalhostになっていなかった。(指定していないから当たり前) 以下のように修正してみる。

   @Primary
    @Bean
    public AmazonSQSAsync amazonSQSAsync() {

        logger.info("region:{}, awsAccessKey:{}, awsSecretKey:{}", region, awsAccessKey, awsSecretKey);

        AmazonSQSAsync async = AmazonSQSAsyncClientBuilder.standard()
                .withRegion(Regions.AP_NORTHEAST_1)
                .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(awsAccessKey, awsSecretKey)))
                .withEndpointConfiguration(new EndpointConfiguration(endPoint, region))
                .build();
        
        return async;
    }

今度は以下のエラーが出る。

Caused by: java.lang.IllegalStateException: Only one of Region or EndpointConfiguration may be set.
    at com.amazonaws.client.builder.AwsClientBuilder.setRegion(AwsClientBuilder.java:450) ~[aws-java-sdk-core-1.11.792.jar:na]

リージョンは複数セットできないよ、ということらしい。(それはそう)

‘withRegion()`の部分を削除することで正常に立ち上がった。

   @Primary
    @Bean
    public AmazonSQSAsync amazonSQSAsync() {

        logger.info("region:{}, awsAccessKey:{}, awsSecretKey:{}", region, awsAccessKey, awsSecretKey);

        AmazonSQSAsync async = AmazonSQSAsyncClientBuilder.standard()
                //.withRegion(Regions.AP_NORTHEAST_1)
                .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(awsAccessKey, awsSecretKey)))
                .withEndpointConfiguration(new EndpointConfiguration(endPoint, region))
                .build();
        
        return async;
    }

SQSにプットされたら自動で受信処理が動くようにする

org.springframework.cloud.aws.messaging.listener.annotation.SqsListener を使う。

package com.example.demo.listener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.aws.messaging.listener.annotation.SqsListener;
import org.springframework.stereotype.Component;

@Component
public class AwsSQSListener {

    Logger logger= LoggerFactory.getLogger(AwsSQSListener.class);

    @SqsListener("foo-queue")
    public void loadMessageFromSQS(String message)  {
        logger.info("message from SQS Queue {}",message);
    }
}

aws lambdaを使うより、常駐のSpringアプリケーションを一つ用意して、このアプリ内でリスナーを立ち上げたほうが色々やりやすそうな感じを受けた。