Posts for: #Java

jvm の脆弱性対応

23時に寝て3時に起きて5時に起きて7時に起きた。なんか調子が微妙。

jvm の脆弱性対応

少し前だけど、jvm のセキュリティアナウンスがあった。

java 11 向けの docker イメージのビルドはあまり緊急度が高くなかったせいか、17よりは優先度が低かったようにみえる。docker イメージビルドの作業状況は次の issue で管理されている。

これまで adoptopenjdk/openjdk11:alpine-jre という docker イメージを使っていた。Transition to Eclipse - An Update によると、Eclipse Temurin という組織が管理する Adoptium というプロジェクトに移管されたらしい。この機会に docker イメージも eclipse-temurin:11-jre-alpine に移行した。

jjug の cfp に応募した

0時に寝て5時に起きた。

ストレッチ

今日の開脚幅は開始前161cmで、ストレッチ後164cmだった。先週とあまり変わらずといったところ。今日は腰の張りが強かった。トレーナーさんは左の張りが強いと言ってたんだけど、私は右の方が体感的に張りが強いように感じた。もう1年以上担当してくれたトレーナーさんが4月いっぱいで転勤になるらしい。社内制度で広島に最低でも半年間は転勤になるという。広島が終わっても神戸に戻ってくるとは限らず、また別の地域へ転勤になる可能性もあるという。きっと優秀な社員だから転勤するんだろうなと思えた。20代後半ぐらいの若もので個人でも筋トレが好きでボディービルダーの大会などにも参加していると聞いた。趣味と業務が近いのでトレーナーとしてのパフォーマンスも高いのだろうと推測する。若い人はなんでも挑戦して経験した方がよいと私も応援した。5月から後任で別のトレーナーに変わる。新しいトレーナーさんに変わることで前のトレーナーさんとの相対評価もできる。これはこれで楽しみでもある。

JJUG CCC 2022 Spring の cfp 応募

本当は締め切りが先週末だったんだけど、募集期間が1週間伸びたのでちょうど作ったばかりのツールで応募してみた。今回はオンラインイベントなので事前にビデオを撮って送るらしい。当日は質疑応答の時間 (10分間) だけオンラインで参加すればよいという感じ。地方在住で物理的に東京に行けないという開発者にも発表しやすいと言えるかもしれない。内容的には初心者向けなので GitHub Actions というネタがいまどきどのぐらい参加者の関心を集めるか、他のプロポーザルとの競争がどのぐらいか次第かな。

生田川公園のお花見予定

cfp を投稿して14時頃に気分転換がてら生田川公園に再訪した。今日は絶好のお花見日和なのでどのぐらい人がいるかを調べるために行ってきた。昨日の夜よりはたくさん人がいて、そこそこ賑わっていた。だいたいの桜の木の下は集団にスペースをとられていた。とはいえ、お花見をするためのスペースが全くないというわけでもないのでよい場所を気にしないなら普通に10人ぐらいの集団でお花見はできそう。個人的には川に入って川遊びしているのが楽しそうにみえた。公園管理者に怒られないならちょっとやってみたい。

帰ってきてから 三宮.dev のすみよしさんと連絡をとって4月10日(日) 11:00 - 16:00 で開催することに決めた。来週中には大半が散ってしまうかも?だけど、実際にやってみてイベント開催の経験値を積むリハーサルの意図もある。bizpy はもはや全く神戸のコミュニティではなくなってしまったけれど、またいつか盛り返す可能性もあるかもしれない。その日のために素振りはしておきたい。

backlog-github-integration-action を作った

0時に寝て7時に起きた。丸一日開発していた。構想1ヶ月、実装2日といったところか。

backlog と github のインテグレーション action

お手伝い先が backlog を課題管理システムとして使っている。backlog は git 連携 の機能をもっているが、これは nulab 社のクラウド上に git リポジトリを構築したものと連携する機能であって、github と連携する機能ではない。そこで github と backlog と連携するためのカスタム github action を作った。

カスタム github action を java で開発するのは普通にはやらないと思うが、いくつか理由があってお手伝い先が java しかできないというのと、nulab 社が提供している公式クライアント nulab/backlog4j が java しかないから。最初は go で実装しようと思って go のクライアントを試したんだけど、サンプルコードをかいたら一部の処理でエラーになって、そのエラーがよくわからなくてやる気がなくなってしまった。最新の rest api の仕様にそってメンテナンスされていないのかな?と思って、やっぱり公式クライアントしかないなと。他にも次のライブラリを使っている。

これまでは commons-cli を使ってきたけど、サブコマンドの機能を提供していない。もうメンテされてないかも?サブコマンドの機能をもつ argument parser がほしくて picocli を選択した。初めて使っていて、実装してみたらわりと私の好みでよく出来ていると思う。今後は cli ライブラリとして picocli を使っていこうと思う。

イベント登壇のススメ

1時に寝て7時に起きた。今日も雨。雨降りの日が増えると春が来たなって感じがしてきた。

cfp のススメ

先日、過去に私が jjug ccc に登壇した資料を紹介していて、そう言えば jjug ccc とかいまぐらいの時期かな?と思って調べたら、ちょうど3月27日が cfp の締め切りになる。「ぼくのかんがえたさいきょうのでぷろい」は java アプリケーション開発の基本には沿っていないやり方なので発表したらおもしろいかもしれないと、slack に軽く書き込んだらわりといいねが付いたので社員さんに cfp 送ったら?と勧めた。その社員さんは島根県在住なのでリモートで登壇できるならいいかも?という話しになってイベントの要項を確認したらオンライン開催なので大丈夫そう。

今日がスクラムのプランニングだったのでチームに共有して業務として cfp を送るための工数も確保した。私が発表してもよいのだけど、なるべく若い人がイベントに登壇すべきだし、業務でやったことはその会社の人が発表すべきだろうというのもあって、私はバックアップにまわって発表は社員さんに任せようと思う。今月末に事例紹介させてほしいという交渉をする予定なので、それがうまくいったら、技術協力として当社のクレジットだけスライドに入れてもらえればみたいところが私の狙い。いずれにしても cfp が採択されないとその展望もないので cfp 作りにも協力していきたい。

継続的デリバリーへの第一歩

0時に寝て6時に起きた。

docker object labels と git リビジョン

継続的デリバリーのために snapshot jar のマニフェストから取得した git のリビジョンを docker イメージの labels に追加するサンプルコードを jib-sample に実装してみた。jib の gradle プラグインを作って簡略化すれば便利かもしれない。

java アプリケーションの継続的デリバリー

0時に寝て3時に起きて6時ぐらいまでだらだらして寝て7時に起きた。

snapshot jar と継続的デリバリー

昨日 jar に git のリビジョン番号を含める ことについて書いた。jar から git のリビジョン番号を取得できれば、docker イメージを生成するときに labels に jar の artifact id と git のリビジョン番号のラベルを追加して、docker イメージからもソースコードの追跡ができるようになる。いまデプロイは docker イメージのみで運用しているため、maven のバージョン管理ができなくても docker イメージの追跡可能性さえあれば現実の運用で問題にならないのではないかと考えた。つまり、snapshot jar で開発したものをそのまま本番環境にデプロイするということを意味する。こうすれば特定のバージョン番号を付けるだけのビルドとデプロイが不要になって、テスト環境にデプロイされた docker イメージのテストを完了すれば、そのイメージをいつでも本番環境にデプロイできるようになる。デプロイするタイミングでビルドする必要がなくなるので継続的デリバリーに近づくのではないかと考えた。

今日、開発の偉い人やインフラ担当者も含めて、みんなでわいわい打ち合わせして、現状の開発では、インターフェースや互換性の変更にあわせてバージョン番号を付けていないし、古いバージョンに戻すことも現実にはなく、保守は常に最新のリビジョンを更新していくから maven でバージョン管理できなくなっても snapshot jar の運用でがんがん開発していけばいいんちゃうという合意を得られた。

実際にこのやり方がうまくいくかどうか、私も初めての試みなのでやってみないとわからないが、この運用によるワークフローの効率化のメリットも大きいので、引き続き、イニシアティブをもって取り組んでいきたい。

jar のマニフェストファイル

0時に寝て3時に起きて5時に起きて6時半に起きた。

jar ファイルと git のリビジョン

java パッケージのフォーマットとして jar ファイルがある。開発中の jar ファイルは snapshot という特別なバージョンで管理したりするが、この snapshot と git のリビジョンが対応していないので snapshot jar だけではどのリビジョンのソースからビルドされたかがわからない。jar には JAR File Specification で定義された META-INF/MANIFEST.MF に任意のメタデータを保持できる。maven なら maven git commit id pluginApache Maven JAR Plugin を組み合わせれば、ビルド設定だけで git のリポジトリ情報を任意のメタデータとして jar に含めることができる。試しにプラグインの検証も兼ねてやってみた。例えば、次のようなマニフェストを作れる。

Manifest-Version: 1.0
Created-By: Apache Maven 3.6.3
Built-By: t2y
Build-Jdk: 11.0.13
Specification-Title: My Nice Product
Specification-Version: 1.0
Artifact-Id: my-product
Build-Time: 2022-02-21T11:39:07Z
Git-Branch: main
Git-Commit-Id: 81a4642
Git-Commit-Time: 2022-02-21T19:39:30+0900
Git-Commit-User: Tetsuya Morimoto

java のコードからマニフェストを取得するサンプルコードはこんな感じ。ググるといくつかやり方があるようなので他の実装もある。但し、このコードだと複数の jar のマニフェストを取得してしまうので、あとで自分がみたい jar のマニフェストをフィルターする処理が必要になる。

private static final String MANIFEST_PATH = "META-INF/MANIFEST.MF";

public static Map<String, Manifest> getManifests() throws IOException, URISyntaxException {
    var map = new HashMap<String, Manifest>();
    var resources = MyUtil.class.getClassLoader().getResources(MANIFEST_PATH);
    while (resources.hasMoreElements()) {
        var elem = resources.nextElement();
        var part = elem.toURI().getSchemeSpecificPart();
        if (part != null) {
            try (var stream = elem.openStream()) {
                map.put(part, new Manifest(stream));
            }
        }
    }
    return map;
}

Mockito を触ってみた

0時に寝て4時に起きて6時に起きた。6時過ぎに slack でインフラ担当者から作業の報告があってその対応してた。

Mockito のモック作成

Spring 5 WebClient のテストコードを書いてみた。Mockito というモックライブラリを使っているのをみかけたのでそれを使うことにした。当初は WebClient そのもののモックを用意して、どんなメソッドを呼び出しても Null オブジェクトのように無視すればいいんじゃないかと思ってたんだけど、Mockito はそういう用途に使うものではなく、それぞれのメソッドごとにモックを返すような設定ができる。次のような WebClient のメソッドチェーンでリクエストするようなモックを考える。

var response = this.client
        .get()
        .uri(uriBuilder -> uriBuilder
                .path(path)
                .queryParam("param", param)
                .build())
        .retrieve()
        .bodyToMono(MyResponse.class)
        .block();

他にもっとよいやり方があるかもしれないけど、私がよくわかってなくてこんなやり方しかできなかった。最終的には block() を呼び出したときに任意のレスポンスを取得できればよいのだけど、メソッド単位にモックを呼び出していかないと型チェックやら実行時エラーやらで意図したようにテストできなかった。これだけをみたらメソッドチェーンのモック作りは面倒にみえる。Mockito がどうやってこれを実現しているのかわからないけど、すごい仕組みだなとは思った。

    @MockBean
    WebClient client;
    @Mock
    WebClient.RequestHeadersUriSpec requestHeadersUriSpec;
    @Mock
    WebClient.RequestHeadersSpec requestHeadersSpec;
    @Mock
    WebClient.ResponseSpec responseSpec;
    @Mock
    Mono<MyResponse> mono;

    private void mockWebClientMethodChain(MyResponse response) {
        Mockito.when(client.get()).thenReturn(requestHeadersUriSpec);
        Mockito.when(requestHeadersUriSpec.uri((Function<UriBuilder, URI>) Mockito.any())).thenReturn(requestHeadersSpec);
        Mockito.when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
        Mockito.when(responseSpec.bodyToMono(MyResponse.class)).thenReturn(mono);
        Mockito.when(mono.block()).thenReturn(response);
    }

java のコードレビューサービス

22時に寝て0時に起きて5時に起きて7時に起きた。なんか体調が微妙。

リファクタリングのチケット作成

これまでは業務に関係しないところの機能拡張をしていたので新規にコードを書くことが多かった。いま業務の開発にも着手し始め、その過程で既存のコードを読むことも多くなった。私からみたらコードの品質が著しく低くて、ただ動いているだけで堅牢でもないし、設計の意図も伝わらないコードが多い。そういうのを自分が変更するときに少しずつ出来る範囲でリファクタリングしたりしている。今日もコードを読んでいて enum の扱いについてチケットを作成した。

前に手伝っていた会社の契約を終了するときに java のコードレビューだけなんらかの契約で依頼できないかという話しはあった。そのときは別件のお仕事が火の車だったので断ってしまった。いまお手伝いしている java のコードをみても、java に慣れていない開発者の書く java のコードはたいていひどい。なぜひどくなるかというと、java は言語仕様が複雑で難しいからだと思う。java の経験が少ないと Effective Java を読んでも理解できないぐらいには java の設計は難しい。その結果として java で開発しているのに設計を練っておらず「動けばいい」コードが散見される。java で開発するなら「これ以外に動かない」コードを書いた方がよいと私は考えている。その意図が伝わるからドキュメントなんか読まなくても「動かすにはこう書けばいい」とわかる。さらにインターフェースが明確であれば、責務や拡張方法も明示的になる。

前から考えてはいたけど、sier や内製始めたばかりの事業会社向けに java のコードレビューのサービスを提供することも考えている。私1人だとたくさん受けられないし、コードレビューサービスのようなものは会社の信頼関係の方が重要なのでビジネスにするのはちょっと難しいかもなぁ。

見積もりのまとめ

先日 見積もりについて考察した 。もともとは社内向けに書こうと書き始めたが、内容の大半は社内に閉じたものではなかったので一般論の記事として書き直した。最終的には1万文字ぐらい書いた。

テストコードのリファクタリング

0時に寝て6時に起きた。今日は7時半から23時過ぎまで集中してコードを書いてた。最近は19-20時には帰って、晩ご飯食べて、ドラクエタクトやったり漫画読んだりだらだらしている。そんな暇あったら積ん読の本読めって感じだ。

テストコードのリファクタリング

業務機能の開発をするにあたって、既存のテストコードをみていて、@BeforeEach というテストメソッド単位に呼ばれるメソッドでテストデータの削除と postgresql の sequence のリセット処理をしていた。こんなの共通処理ですべてのテーブルの truncate と sequence のリセット処理をすればいいやんとか思って、いろいろ調べて2つのリファクタリングの PR を作成した。先日 JUnit5 の拡張 を調べたばかりだから、テストの共通化のノウハウが溜まっている。Testcontainers Postgres Module と連携して、postgresql コンテナに接続して sequence のリセット処理を汎用のテスト拡張として実装した。テストを実装する開発者は、次のように @ExtendWith(DatabaseInitializer.class) をアノテーションに付与すれば、自分で sequence のリセット処理を @BeforeEach のメソッドに実装する必要がなくなる。

@SpringBootTest
@Transactional
@ExtendWith(SetupDatabaseContainer.class)
@ExtendWith(DatabaseInitializer.class)
class MyTest {
    ...
}

この作業の過程で spring boot の @Transactional はデフォルトでテストメソッドの実行後にロールバックする機能が提供されていて、いままで @BeforeEach のメソッドで明示的にテーブルのデータを削除する必要はなかったんやと気付いた。じゃあ、なぜ削除するコードを書いてたかと言うと、テストの外部で初期データを作成する仕組みがあるから、初期データを削除する目的でそうしていたことが判明した。そして、一部のコードはそこで作った外部の初期データに依存して実装されていた。テストコードの一部が外部のデータに依存しつつ、テストメソッドでは外部のデータに依存しないように削除のコードが書いてある。書いていて何を言っているのかわからないと思うけど、私も調べてて訳がわからんくて、PR に「いまの状況はかなりややこしい」と前置きしつつ、無駄なコードや仕組みを取り除くための修正を行った。本当は機能開発やらないといけないのにテストコードのリファクタリングするのに大きな時間をかけるわけにはいかないだろうという意図で、半日掛けてリファクタリングして23時過ぎまで作業して、既存のテストコードも含めて全部直した。このリファクタリングで数十のテストケースの約300行ぐらいの初期化コードをなくせた。

spring boot の xml 変換の仕組み

0時に寝て吐き気がして3時に起きて、断続的に仮眠をとってみたけど、それでも気分悪くて5時から起きてた。昨日の晩ご飯食べて寝てから吐き気が出てきた。なにかの食べ合わせなのだろうか。コロッケとその後にチョコレート食べたのが悪かったのか。普通にオフィスへ行ってお仕事してたら直った。

spring boot の xml 変換

いまお手伝いしているお仕事で spring boot で SOAP の xml 通信しているサービスがある。任意の文字列を受け取って任意の文字列を返すような仕組みで設計されていて、xml の変換処理を jackson を使ってアプリケーションコードで書いていた。

これをやるならミドルウェアでやるべきだなと思って spring boot のドキュメントを調べてみた。Error Handling のように例外が発生したときの処理をフックする ResponseEntityExceptionHandler のようなミドルウェアに近い仕組みはあるが、通常のレスポンスに対して行う処理はなかった。代わりに HttpMessageConverters という、レスポンスを変換する仕組み自体は操作できないが、変換する変換器は置き換えたり拡張したりできるようになっている。レスポンスのデータフォーマットのカスタマイズをしたい場合は HttpMessageConverters で行うというのが spring boot 的なやり方にみえる。

さらに調べていると Write an XML REST Servicejackson-dataformat-xml がクラスパスにあれば jackson の ObjectMapper を使って xml に変換するよと書いてあって、試しにレスポンスのオブジェクトを返したら自動的に xml に変換されるという振る舞いを確認できた。つまり、アプリケーションコードで xml の変換処理を自前で実装しなくてもほぼ同じことを spring boot のデフォルトの仕組みでやってくれるというわけだ。jackson の ObjectMapper のカスタマイズがしたいときもいくつかやり方がある。例えば、 @Configuration をもつ Config オブジェクトで次のような bean を生成すれば任意の設定にカスタマイズした ObjectMapper が使われるようになる。

@Bean
public Jackson2ObjectMapperBuilderCustomizer configureObjectMapper() {
    return builder -> {
        builder.serializationInclusion(JsonInclude.Include.NON_EMPTY);
    };
}