uokadaの見逃し三振は嫌いです

ここで述べられていることは私の個人的な意見に基づくものであり、私が所属する組織には一切の関係はありません。

SONY Linkbuds Sを買った

SONY WI-SP510が壊れたので同じSONYのLinkBuds sに買い替えた。

www.sony.jp

www.sony.jp

WI-SP510を2020年に買って主にジムでトレーニング用に使ってきたんだけど、先週突然右側だけ聞こえなくなってしまいました。4年近く使って来たのでこれは寿命かなと思い、週末ヨドバシカメラで色々と比較していました。 少し前から、WI-SP510の後継機が無いことに気づいていたので次をどうしようか悩んでいたんですが目的から考えてジムで使ってる時にズレにくいものに重点を置いて見ていった結果、最終的にLinkBuds Sに行き着きました。

LinkBuds Sは2022年6月発売で発売から2年の時が経過しているのにヨドバシカメラの価格で26,000円がちょっと高いなと感じて色々と見て廻って、最終的に秋葉原のeイヤホンで中古品を1万そこらで買いました。 ヤフオクなどのオークションサイトなら8千円前後でもう少し安く買えるみたいですがすぐ使いたかったので今回初めてeイヤホンにお世話になりました。 kakaku.com

これを今週ジムで使ってみて運動中にも問題なく使えることが確認できたら、LinkBudsの後継機が出た時に乗り換えれたらなと考えてます。

[9/21 追記] ジムでランニングする際に使ってみて、全然耳からずり落ちないことを確認できた。 Google Pixel Budsで試したときは直ぐに外れるって印象だったからLinkBuds Sの方が全然エクササイズ向きって印象です。(もしかしたら、サイズが合ってないだけかもしれないけど)

Pixel Budsも併用して使っていたんだけど1年ぐらい前から右側だけ直ぐに充電が切れるのでずっと買い替え時を探っていたので今回良いイヤホンに出会えて良かった。 やっぱり、自分の中ではSONYのオーディオ・デバイスは安心感がある。

[追記ここまで]


他に、買わなかったけどAnkerのSoundcore Sport X20の装着感が良かったのでLinkBuds Sに出会ってなかったら予算的にもこれを買ってたと思います。 イヤホン上部についているフックが伸び縮みや回転して調節が出来るのでしっかりと自分の耳にフィットする感がありランニング中でも外れなさそうだなという印象でした。

Soundcore Sport X20www.ankerjapan.com https://www.yodobashi.com/product-detail/100000001008561488/www.yodobashi.com

quarkus-smallrye-openapi とInstancioを使って手軽にOpenAPI.yamlとスタブサーバーを作る

ja.quarkus.io

www.instancio.org

quarkus-smallrye-openapi はJavaのクラスからOpenAPI のYAML/JSONを生成出来るQuarkusの拡張で、InstancioはJavaのクラスから手早くダミーのオブジェクトを生成するライブラリです。

自分はこれらを2つの目的で使用しています。

1つ目はOpenAPI YAMLの生成・提供です。OpenAPI YAMLを直接書いても良いのかもしれませんが、修正作業が入ったときに手作業だと修正漏れなどのミスが発生する可能性が高いです。Javaファイルの修正の場合IDEに修正を任せらることやCompilerでエラーの検出が可能となります。

2つ目はスタブサーバーの提供です。ダミーデータの生成をするライブラリはdatafakerなどいくつかありますが、自分はInstancioが一番手軽に利用できるライブラリだと思います。 よりリアルなスタブサーバーを作成したい場合には、datafakerを使ってリアルなデータを生成して使っています。

次からは、どうやって今回の目的のquarkusアプリケーションを生成するか手順を紹介します。

まず、quarkusコマンド*1を使ってアプリケーションを生成します。

# quarkus create app --java <java version> --maven -P <quarkus version> --wrapper --extension <Quarkus extensionのリスト(カンマ区切り)> -o <出力先ディレクトリ> <Javaアプリケーション>
$ quarkus create app --java 17 --maven -P 3.11.3 --wrapper --extension io.quarkus:quarkus-smallrye-openapi,rest-jackson -o . io.github.yuokada.sample:quarkus-openapi-app
-----------
selected extensions:
- io.quarkus:quarkus-rest-jackson
- io.quarkus:quarkus-smallrye-openapi


applying codestarts...
📚 java
🔨 maven
📦 quarkus
📝 config-properties
🔧 tooling-dockerfiles
🔧 tooling-maven-wrapper
🚀 rest-codestart

-----------
[SUCCESS] ✅  quarkus project has been successfully generated in:
--> /path/to/quarkus-openapi-app
-----------
Navigate into this directory and get started: quarkus dev

次に、ダミーデータを生成するためにpom.xmlに次のコードを追加してinstancio-coreを導入します。

    <dependency>
      <groupId>org.instancio</groupId>
      <artifactId>instancio-core</artifactId>
      <version>4.8.0</version>
    </dependency>

そして、APIの実装をInterfaceを使って定義していきます。Interfaceを作成せずClassに直接アノテーションを付与してもOpenAPI yamlの生成は出来ますが、OpenAPIの複数のアノテーションを付与すると見通しが悪くなるので自分はInterfaceとClassに分けるスタイルを採用しています。

メソッドに@GET, @Path, @APIResponses などのAPIエンドポイントの定義をアノテーションで付与していきます。*2

必須なアノテーションとしては @(GET|POST|PUT|DELETE), @Path をぐらいを定義していればスタブサーバーの生成には十分です。あとは、必要に応じてクエリパラメータ・パスパラメータなどを定義します。

@Produces(MediaType.APPLICATION_JSON)
public interface SampleApiIf {

    @GET
    @Path("/")
    @Operation(
        summary = "Return a list of players",
        description = "Return a list of players"
    )
    @APIResponses({
        @APIResponse(
            responseCode = "200",
            description = "Returns a list of players",
            content = @Content(
                mediaType = "application/json",
                schema = @Schema(type = SchemaType.ARRAY, implementation = Player.class)
            )
        ),
    })
    Response getPlayers();

    @GET
    @Path("/{id}")
    @Operation(
        summary = "Return the detail of a player",
        description = "Return the detail of a player"
    )
    @APIResponses({
        @APIResponse(
            responseCode = "200",
            description = "Returns the detail of a player",
            content = @Content(
                mediaType = "application/json",
                schema = @Schema(type = SchemaType.OBJECT, implementation = Player.class)
            )
        ),
    })
    Response getPlayer(@PathParam("id") Long userId);
}

Interfaceの定義が完了したら次はその実装を作ります。今回はInstancioを使ってダミーデータを生成しそれをAPIのレスポンスとします。

Instancioでは完全にランダムな値を生成する以外にもset()で値を指定したり、generate()で範囲を決めたランダムな値を付与することが出来ます。

@Path("/v1/api/stub/players")
@Tag(name = "Demo App stub API", description = "Demo App Common APIs for development")
@Deprecated
public class StubApiImpl implements SampleApiIf {

    @Override
    public Response getPlayers() {
        List<Player> players = Instancio.ofList(Player.class).size(10)
            .generate(field(Player.class, "age"), gen -> gen.ints().range(15, 85))
            .create();
        return Response.ok(players).build();
    }

    @Override
    public Response getPlayer(Long userId) {
        Player player = Instancio.of(Player.class)
            .set(field("userId"), userId)
            .generate(field("age"), gen -> gen.ints().range(15, 85))
            .create();
        return Response.ok(player).build();
    }
}

ここまで実装が出来たらmavenからquarkus サーバーを立ち上げます。デフォルトだと http://localhost:8080 でサーバーが立ち上がります。 また、http://localhost:8080/q/swagger-ui/#/ でSwagger UIが利用できるので実装したAPIに手軽にリクエストを投げて検証出来ます。

$ ./mvnw quarkus:dev

openapi.yamlの出力先はquarkus.smallrye-openapi.store-schema-directoryで変更することが出来ます。次の例だとプロジェクト直下にopenapi-definitionが作成されてそこに出力されます。

# OpenApi
quarkus.smallrye-openapi.enable=true
quarkus.smallrye-openapi.store-schema-directory=openapi-definition

実装に迷った場合はこちらのリポジトリが参考になります。 github.com

まとめです。

quarkus-smallrye-openapi を使えばOpenAPIの仕様をあまり知らなくてもOpenAPI yamlの生成が簡単に出来ることが伝わったかと思います。 自分も昔OpenAPI yamlを手書きしていましたが、仕様をある程度知らないと書くのも難しいし修正も大変だった経験があるのでJavaのコードから自動的に生成出来るというのは作成・修正するハードルが下がるので良い手法だなと思いました。

今回のコードのリポジトリはこちらです。 github.com

*1: インストールはこちらから。ja.quarkus.io

*2:詳しい仕様はこちらを参照してください。 download.eclipse.org

blackをruffで置き換えてる - Replacement black with ruff

最近、プライベートなpythonプロジェクトでblackをruffで置き換えたのでそのメモ。

ここ数ヶ月の間にblackとruffを導入したんですが、ruff v0.1.2でformatterが導入されたのでblackへの置き換えを小さめなプロジェクトで実施しました。

astral.sh

blackとruff formatのdiff check/formatのコマンド体系はほぼ同じで機械的に置換しただけで動かすことが可能でした。

# 差分チェック
$ poetry run black           --check --diff  **/*.py

$ poetry run ruff format --check --diff **/*.py

----
# ファイルのフォーマット
$ poetry run black **/*.py

$ poetry run ruff format  **/*.py

ただ、設定でまだ移行出来ないものがあります。

自プロジェクトのblackの設定はあまりカスタマイズせずで設定で使っていました。

[tool.black]
target-version = ["py39"]
line-length = 120
skip-string-normalization = true

ですが、移行を検討開始した当初skip-string-normalization = true に相当する設定がまだ存在しませんでした。 issueは上がっていたのですが、議論が続いていて解決するまでruffへの移行のブロッカーとなっていました。 v0.1.18で quote-style = "preserve" が設定できるようになりました。これが入った事で、自分のプロジェクトではpyproject.tomlとCIの設定変更だけでblackを置き換えることが出来ました。

github.com

docs.astral.sh

ruffに置き換えたことで良かったこととしては、依存関係が減ったのが一つ。それと、パフォーマンスに関してはあんまり改善された実感はありません。自分は poetry run ruff で呼び出しているのでpoetryの処理のオーバーヘッドが体感できない一因かと思います。

ruffは開発のスピードが早いので自分がこのエントリーを数ヶ月放置しているあいだにバージョン0.3までリリースされていました。 2024年はrust製のツールがこれまで以上に流行っていくのでは無いかなと思っております。

java-fakerからdatafakerに移行した

TL;DR

java-fakerのメンテナンスがしばらくされていないのでjava-fakerをforkしたdatafakerに移行した。

What's java-faker?

github.com

Java製のfake data generatorの実装。 rubyのfakerのJavaバージョンという位置づけのライブラリ。

github.com

最終リリースの1.0.2が2020年と1年以上新しいバージョンがリリースされていないことから有志がforkして新たにdatafakerというリポジトリが作成されそこでメンテナンスがされています。

github.com

datafakerの使い方としてはほぼjava-fakerと同じです。

ここにjavafakerを使ったサンプルコードがあります。

import com.github.javafaker.Faker;
import com.github.javafaker.Internet;
import java.util.Locale;

public class FakerApp {
    public static void main(String[] args) {
        Faker faker = new Faker(Locale.US);
        Internet internet = faker.internet();
        for (int i = 0; i < 10; i++) {
            System.out.println(internet.emailAddress());
        }
    }
}

ここからdatafakerへの移行は簡単で、パッケージ名をcom.github.javafaker から net.datafakerに変えるだけです。

import java.util.Locale;
import net.datafaker.Faker;
import net.datafaker.Internet;

public class FakerApp2 {
    public static void main(String[] args) {
        Faker faker = new Faker(Locale.US);
        Internet internet = faker.internet();
        for (int i = 0; i < 10; i++) {
            System.out.println(internet.emailAddress());
        }
    }
}

datafakerは積極的にメンテナンスされていて機能も順調に増えているので手が空いていたら移行をオススメします。

AWS samでQuarkus アプリケーションをデプロイする

What is quarkus?

quarkus.io quarkus.io

Quarkus はJava製の幅広い分散アプリケーションを構築するのに適したフレームワークです。RHELで有名なRedhat 社が中心となって開発を進めています。

Why I wrote this blog post

quarkus.io

上記の通り、Quarkus自身でAWS lambdaを構築する拡張機能とその使い方を提供しているがここにはいくつか課題があります。まず、mvn packageでlambda関数をデプロイするためのAWS samテンプレートを出力してくれるんですがこれをカスタムすることが出来ません。そして、上の方法だと1つのQuarkusアプリケーション=lambda関数しか管理することが出来ないので複数のlambda関数を管理・デプロイすることになると関数の数だけsamのテンプレートを管理するコストが発生することです。

そこで、AWS samもテンプレートをベースに各言語のアプリケーションをビルドすることが出来るのですが、Java 11用に作られたテンプレートがビルドとデプロイは出来るがそのままでは実行出来ないという問題があります。

aws.amazon.com

Lambda関数をQuarkusで作る理由としては、ロギングなどの拡張機能のサポートが充実しているのも1つの理由です。設定を数行追加するだけでAWS Lamda上で実行する際のロギングフォーマットをJSONに変換することが出来ます。

quarkus.io

Create a lambda function using Quarkus with AWS sam

まず、sam initコマンドでlambda関数とsam テンプレートを作成します。

$ sam --version
SAM CLI, version 1.46.0

$ sam init -r java11 -d maven --app-template hello-world  -o . -n sam-hello-lambda

Cloning from https://github.com/aws/aws-sam-cli-app-templates (process may take a moment)

SAM CLI update available (1.48.0); (1.46.0 installed)
To download: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html

次に、quarkusでアプリケーションを作成してHellowWorldFunction以下のファイルを置き換えます。 quarkusコマンドのインストールはこちらを参照してください。

quarkus.io

$ cd sam-hello-lambda 

$ rm -rf HelloWorldFunction

$ quarkus create app --java 11 --maven --extension quarkus-amazon-lambda io.github.yuokada:HelloWorldFunction
-----------
selected extensions:
- io.quarkus:quarkus-amazon-lambda


applying codestarts...
📚  java
🔨  maven
📦  quarkus
📝  config-properties
🔧  dockerfiles
🔧  maven-wrapper
🚀  amazon-lambda-example

-----------
[SUCCESS] ✅  quarkus project has been successfully generated in:
--> /path/to/sam-hello-lambda/HelloWorldFunction
-----------
Navigate into this directory and get started: quarkus dev

そして、次にHelloWorldFunctionの下にMakefileを作成します。これはsam buildコマンドを実行時にbuild-Helloworldfunctionが呼ばれてs3にアップロードされるファイルの中身を生成するのに利用します。 実際のところ、sam packageでここで作成されたLambda関数ごとのディレクトリをzipファイルに固めたものがs3にデプロイされるので、ここではfunction.zipを一度展開しているだけです。

clean:
  ./mvnw clean
build:
  ./mvnw package

build-Helloworldfunction: clean build
  unzip target/function.zip -d $(ARTIFACTS_DIR)/

そして、上で作成したMakefileを利用するようにtemplate.yamlも修正します。重要なポイントは以下の通りです。

  1. Matadataセクションの追加。 see: Building custom runtimes - AWS Serverless Application Model
  2. Handlerの値をio.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequestに書き換え。これは全てのQuarkusアプリケーションでこの値になります。
  3. Eventsセクションは今回使ってないので削除。
diff --git a/template.yaml b/template.yaml
index 71c9004..a405959 100644
--- a/template.yaml
+++ b/template.yaml
@@ -12,32 +12,24 @@ Globals:

 Resources:
   HelloWorldFunction:
-    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
+    Type: AWS::Serverless::Function
     Properties:
      CodeUri: HelloWorldFunction
-      Handler: helloworld.App::handleRequest
+      Handler: io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest
       Runtime: java11
       Architectures:
         - x86_64
      MemorySize: 256
+      Policies: AWSLambdaBasicExecutionRole
       Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object
-        Variables:
-          PARAM1: VALUE
-          JAVA_TOOL_OPTIONS: -XX:+TieredCompilation -XX:TieredStopAtLevel=1 # More info about tiered compilation https://aws.amazon.com/blogs/compute/optimizing-aws-lambda-function-performance-for-java/
+         Variables:
+           JAVA_TOOL_OPTIONS: -XX:+TieredCompilation -XX:TieredStopAtLevel=1 # More info about tiered compilation https://aws.amazon.com/blogs/compute/optimizing-aws-lambda-function-performance-for-java/
-      Events:
-        HelloWorld:
-          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
-          Properties:
-            Path: /hello
-            Method: get
+    Metadata:
+      BuildMethod: makefile

ここまで変更を加えれば、後は他のsam製アプリケーションと同様にsam package && sam deployをしてやればアプリケーションを構築出来ます。 (今は、sam deployを呼ぶと暗黙的にsam packageが実行されるみたいですが自分はデプロイ用のテンプレートを出力してそれを使ってsam deployを呼び出したいので明示的にsam packageを呼んでいます。)

最終的に、出来上がったlambda関数のリポジトリがこちらです。 github.com

これで、Quarkusuアプリケーションとそれに付随するリソースを1つのAWS samテンプレートで管理出来るようになりました。

関連リンク

Why I migrated my domain from star domain to Google domains

スタードメイン(旧ネットオウル)を使っていたのだが、 AWS Amplifyで独自ドメインSSL化する過程で""が入ったドメインを入力する必要があって、その際にスタードメインだと""が入ったドメインを入力できないという事が分かりました。

DNS CNAMEレコードに _(アンダーバー)を入力できない https://secure.netowl.jp/bbs/detail.cgi?td=1091

まぁ、"_"を含んだドメインがRFC952に違反するとのことで入力できないのは仕方がないかもしれません。

ホスト名 - Wikipedia https://ja.wikipedia.org/wiki/%E3%83%9B%E3%82%B9%E3%83%88%E5%90%8D

AWS側もそういったドメインレジストラが存在することを想定して"_"を抜いても検証が進められるという仕様になっているようです。

docs.aws.amazon.com

ただ、"_"抜きで設定しても検証がうまく出来なかったのと2013年の状況から9年経って、世の中の変化などに対応する気が無さそうだったので今回移管することとしました。

それに、5月にドメインの登録料の支払いがあったのでその後に移管することになると無駄な支払いになるなと思ってさっさと移行を決めました。

現在、絶賛移管手続き中でGoogle domainsの使い勝手はまだ分からないんですがGoogle domainsでやりたいこと出来なかったら潔く諦めようかなという気持ちです。

新年だしNintendo Switch 有機ELモデルを買った

久しぶりに任天堂のハードウェアを買った。
前回買ったのはNintendo DS (3DSじゃないよ)なので10年以上振りなのかな?

過去2年間、コロナの影響で思うように外出することも出来ずにちょっと運動不足気味だなと感じていて 実際に体重も結構増えてしまい健康診断で色々と言われてしまったので2022年はダイエットをしようかなと思っている。

なんのサポートもなしにダイエットをして1年間続く気がしなかったので続けられるためには何が必要か考えた結果、 ゲーミフィケーション要素があるものが必要だと思いNintendo Switch + リングフィットアドベンチャーという結論に至った。

思い立ったが吉日ということで、年明け早速Nintendo Switch有機ELモデル)を探してヨドバシ.comで購入した。購入前は転売ヤーのせいで直ぐには入手出来ないのかな?と考えてたんだがそんなことはなくて一切待つことはなく購入できてしまった。

調べていくと、Nintendo Switch有機ELモデル)が十二分に供給されているらしく転売ヤーの影響力は全く無いみたいだ。

ここ十数年はPS3,PS4 Pro, PSP, PS Vitaとソニーのハードウェアしか買って来てなかったので、 任天堂ハードのUXがどうなのか未知数の部分が多かったんだけど、switchを起動したら期待以上だった。

まだセットアップとリングフィットアドベンチャーを始めて1日だけswitchが凄く使いやすい。まず、Nintendo Switchの箱に説明書入ってないのにビックリした。説明書なくても問題なく5分程度でセットアップ完了したしこれが最新のUXかーって感心した。リングフィットアドベンチャー用のコントローラーも同じぐらい簡単にセットアップ出来たし全くつまずかなかった。

とりあえず、リングフィットアドベンチャー使って期待通りの結果が得られたらこの約5万円の投資は成功だったと言えるので1年ぐらい継続して結果を残せるように頑張りたい。