Vue.Draggable で並び替えたときのイベントハンドラの呼び出し順

最近、Vue.Draggable を使う機会があったのですが、ドラッグ&ドロップで要素を並び替えたときにどのような順序でイベントハンドラが呼ばれるのか気になったので調べてみました。とりあえず、以下に記載されているイベントハンドラを対象にします。ちなみに、move イベントは SortableJS の move プロパティを利用するようです。

github.com

サンプルコードはこちら。

gist.github.com

結果

呼び出しの順序は以下の通り。右端の数字は操作の番号と対応する。

パターン1

1) クリック ⇒ 2) ドラッグ ⇒ 3) 入れ替え ⇒ 4) ドロップ

  1. onchoose > (1)
  2. onclone > (2)
  3. onstart > (2)
  4. onmove > (3)
  5. onupdate > (4)
  6. onsort > (4)
  7. onend > (4)

パターン2

1) クリック ⇒ 2) ドロップ (※ドラッグなし)

  1. onchoose > (1)

パターン3

1) クリック ⇒ 2) ドラッグ ⇒ 3) ドロップ (※入れ替えなし)

  1. onchoose > (1)
  2. onclone > (2)
  3. onstart > (2)
  4. onend > (3)

パターン4

1) クリック ⇒ 2) ドラッグ ⇒ 3) 入れ替え ⇒ 4) 戻す ⇒ 5) ドロップ

  1. onchoose > (1)
  2. onclone > (2)
  3. onstart > (2)
  4. onmove > (3)
  5. onmove > (4)
  6. onend > (5)

パターン5

1) クリック ⇒ 2) ドラッグ ⇒ 3) 入れ替え ⇒ 4) 入れ替え ⇒ 5) ドロップ (※2行移動)

  1. onchoose > (1)
  2. onclone > (2)
  3. onstart > (2)
  4. onmove > (3)
  5. onmove > (4)
  6. onupdate > (5)
  7. onsort > (5)
  8. onend > (5)

まとめ

調べてはみたけど、Sortable の README.md などを読めばなんとなくイメージできるかもしれないですね...。

github.com

現場からは以上です。

GraphQL を Spring Boot で試してみる 3

GraphQL Advent Calendar 2018 の18日目です。

最近、GraphQL を試しに触り始めてみたという程度です。普段は Java や Spring を使っています。というわけで、今回は Spring Boot で試してみたときの備忘録シリーズの第3弾となります。第1弾と第2弾はこちら。Query と Mutation を試してみたときの備忘録です。が、本当に触ってみた程度の内容です...。

今回は Subscription を試してみます。相変わらず触ってみた程度の内容ですが、サンプルコードはこちら。

kntmr/playground/graphql-spring-examples - GitHub


以下、備忘録。

依存ライブラリ

Subscription の Resolver からは Publisher<T> を返す。取り急ぎ、Spring WebFlux を追加。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

schema / データクラス

スキーマに Subscription の IF を定義。

// ... (略)
type Subscription {
    added(id: ID!): ToDo
}

Resolver

Subscription の場合は、GraphQLSubscriptionResolver インタフェースを実装する。今回は EmitterProcessor<T>Publisher<T> を返すことに。また、引数で指定された ID で filter することで、Subscribe したユーザーごとに Subscription を返すようにする。

@Component
public class TodoSubscriptionResolver implements GraphQLSubscriptionResolver {
    private TodoEmitterProcessor processor;
    public TodoSubscriptionResolver(TodoEmitterProcessor processor) {
        this.processor = processor;
    }
    public Publisher<ToDo> added(int id) {
        return processor.getProcessor().filter(todo -> todo.getUserId() == id);
    }
}

EmitterProcessor<T> を持つクラス。

@Component
public class TodoEmitterProcessor {
    private EmitterProcessor<ToDo> processor;
    public TodoEmitterProcessor() {
        processor = EmitterProcessor.create();
    }
    public EmitterProcessor<ToDo> getProcessor() {
        return processor;
    }
    public void sendMessage(ToDo todo) {
        processor.onNext(todo);
    }
}

Mutation で更新するときに EmitterProcessor#onNext する。

// ... (略)
ToDo newTodo = new ToDo( ... );
todoDao.add(newTodo);
processor.sendMessage(newTodo); // 追加

リクエスト / Subscription

アプリを起動するとコンソールに以下のログが出力されている。

Registering ServerEndpointConfig: ServerEndpointRegistration for path '/subscriptions': class graphql.servlet.GraphQLWebsocketServlet

というわけで、今回は GraphiQL ではなく、WebSocket で /subscriptions にリクエストする。

const ws = new WebSocket('ws://localhost:8080/subscriptions')
ws.onopen = (e) => {
  console.log('ws opened.')
  ws.send(JSON.stringify({
    query: `subscription {
      added(id: 1) { // ★
        id,
        content,
        completed
      }
    }`
  }));
}
ws.onmessage = (e) => {
  console.log('ws message.')
  var resp = JSON.parse(e.data);
  // ... (略)
}
ws.onclose = (e) => {
  console.log('ws closed.')
  ws.close();
}
ws.onerror = (e) => {
  console.log('error!!')
  ws.close();
}

GraphiQL などから Mutation でデータを更新すると、ws.onmessage でイベントが通知される。Resolver 側で ID で filter しているので、他ユーザーのデータを Mutation に投げた場合はイベントは通知されない。

所感

まだまだあやしいところはありますが、ここまでで Query / Mutation / Subscription の3機能をざっくり試してみました。今後はもう少し実践的な内容を調べていきたいと思います。特に、自動生成などの開発ツール周りを調べたい。

とりあえず、以下のドキュメントをもっと読み込んでみようかと。

GraphQL Java

その他

先日の JJUG CCC 2018 Fall で GraphQL のセッションがありました。

www.slideshare.net

デモのリポジトリはこちら。

github.com

JJUG CCC 2018 Fall に行ってきた #jjug_ccc

JJUG CCC 2018 Fall に行ってきました。簡単に所感をまとめます。

www.java-users.jp

セッション資料は以下で公開されると思います。

GitHub - jjug-ccc/slides-articles-2018Fall: JJUG CCC 2018 Fall 登壇資料まとめ

今回は午後からの参加だったのですが、GraalVM とか午前のセッションでいくつか気になるものがあるのであとで見てみようと思います。

今回参加したセッションは以下の通り。メモから抜粋。

思考停止しないアーキテクチャ設計

www.slideshare.net

  • Classicalなアーキテクチャ設計
  • 非機能要求からアーキテクチャ設計☆
  • 品質特性のシナリオ
    • 特に環境の観点が抜けがち
  • タクティクス☆
  • 品質特性間のトレードオフ
  • アーキテクチャパターンはタクティクスのパッケージ
  • 代表的なアーキテクチャパターン
  • アーキテクチャパターン間のトレードオフ
    • 例) レイヤードはアプリケーションが巨大になりがちで、アジリティやデプロイ容易性は下がる
  • アーキテクチャに時間をかけると手戻りのリスクは減る
    • 時間をかけすぎてもいいものではない
    • コード規模によって Sweet Spot は変わる
  • 品質特性シナリオを使ってアーキテクチャ設計が十分が評価する
  • アーキテクチャの課題をいろいろな視点から見ることが大事☆
  • Architecture Decision Records☆
  • Architectural Significant Requirements (アーキテクチャ要求)
  • 非機能要求からのアプローチではなく、機能要求パターンからASRのアプローチ☆
  • デグラデーション
    • サービス全体のダウンを防ぐ
  • 柔軟さとテスト可能性のトレードオフ
  • 予期しないデータパターンによる不具合
  • 適度な柔軟さ (インタフェース)
  • 柔軟すぎる実装かどうかの判断☆
    • データパターンを網羅するテストケースが作れるか
  • 技術的負債バジェット
  • 技術的負債を金額換算する (時価)
    • 開発時間全体に対する負債返済の時間比を決める
    • 時価額の高いものから対処する
  • アーキテクチャ設計は終わりのない仕事

参加者が多くて人気のあるセッションでした。改めてアーキテクチャ設計の難しさを感じました。定期的に読み直したい資料です。冒頭で紹介されていた起業クエストはこちら。

エムスリーでのKotlinへの取り組み

  • サーバー/モバイルに関わらず多くのプロジェクトで Kotlin を採用している
  • Better Java
  • 技術選定の方針☆
    • 習熟度、周辺ライブラリ、既存システムとの連携
  • 移行パターン
  • 新規開発分に適用、あとから徐々に既存部分を変更する
  • フルリニューアル (アーキテクチャ再設計、機能の棚卸し)
  • 自動コンバート (IntelliJ)
    • Kotlin らしいコードにはならないので、手動リファクタリングが必要
    • コンバートと機能開発を混在させない (差分が分からなくなる)
    • リグレッションテストで品質担保
      • 自動コンバートをコードレビューしても仕方がない
      • Golden file testing でテストを量産する☆
  • Swagger, OpenAPI
  • GraphQL
  • Objectives and Key Results (OKR) ☆
    • 工数の20%程度を使うなどして Kotlin 化を進めた
  • Lombok ⇒ data class 移行
    • Kotlin では Builder パターンの代わりに名前付き引数を使う
  • コードレビューの指摘内容を中心にWikiに解説をまとめる

Kotlin というより、どのように技術選定をしているのか気になって参加しました。OKR でリプレースやリファクタリングを進めるのはよさそうです。が、Key Results を客観的に判断できるように定量化するところがなかなか難しそうでキモかもしれません。ところで、Builder パターンの代わりに名前付き引数を使うというのは、なんかメリットの捉え方が少しズレてるような気がしますが、どうなんでしょう...。

Migration Guide from Java 8 to Java 11

www.slideshare.net

  • 互換性
  • 非互換性
    • 非互換性ポリシー☆
    • 6バージョンごとのLTSのみ使っている場合はいきなり消えることがある
    • OpenJDK コミュニティでは非互換性は厳しくチェックされるらしい
  • Compatibility & Specification Review (CSR) ☆
    • Release Notes にだいたいは書いてある
    • CSR を読むとなおよし
  • マイグレーション
  • だいたいは Java 11 の時点でモジュール化する必要性はないかもしれない
  • 実行時例外☆
    • Options, API, JDK Internal APIs, Reflection
    • Java EEJava 11 以降はモジュール自体が削除されている
    • JDK モジュール化に伴い、リフレクションで JDK Internal API や private メンバへのアクセスが不可
  • 実行の動作不整合/パフォーマンス☆
  • コンパイル/テスト環境においてはほとんどのライブラリやプラグインを更新する必要がある
  • Java 11 では出力されるバイトコードに大きな変更が入ってる
    • JEP181 の影響が最も大きい
    • バイトコードを扱うツールは Java 11 対応を待つ必要がある
  • コンパイル時例外/警告
    • ソースレベルの非互換性
  • 動作の不整合のところは特に要注意

とても学びの多いセッションでした。ここまで資料がまとまってるというのは素晴らしい...。要復習。

GraphQL vs Traditional Rest API

www.slideshare.net

  • REST
    • クライアント/サーバー
    • ステートレス
    • キャッシュ
    • レイヤード
  • GraphQL
    • GraphQL is specification
  • Entity や Repository のコードは REST/GraphQL で同じ
  • REST
    • Schema Optional
    • Good practice, but still optional
  • GraphQL
    • schema is MANDATORY
  • REST
    • HATEOAS
  • Protect from abuse
    • Time out
    • Max Query Depth
    • Max Query Complexity
    • Throttling
  • GraphQL rich SDL

今回、最も聴きたかったセッション。なかなか graphql-java の話を聴くことがないので貴重でしたが、内容自体は割と基本的なところだったかもしれません。デモのリポジトリはこちら。要復習。

github.com

1番気になっている Subscription については少し触れていましたが、英語が聞き取れず...。悲しい。

マイクロソフト牛尾さん渡米直前記念」外資系企業で働くエンジニアの生産性向上物語

  • アメリカではお客さんでも技術力が高い (バリバリコーディングできる)
  • 学んだことを自分の言葉で書くことで理解を深める
  • 日本とUSの違い
    • とにかくラクをする
    • 無駄なものはなくす
    • 技術に明るい
    • 意思決定のスピードがとても早い
  • 英語の読み書きの速さが世界のエンジニアとの差
    • 目的の情報にアクセスするスピードが違う

その他

今回、海外からのスピーカーが多くて豪華でした。参加できなかったセッションの資料はあとで見てみようと思います。

あと、JJUG だけではないですか、最近、Kotlin がテーマのセッションが多くなった気がします。数年前はこのポジションに Scala がいたような。ある意味、Scala は枯れてきたと言えるのかもしれません。先日の Spring Fest 2018 でも Kotlin サポートの話がありましたね。

Spring Fest 2018 に行ってきた #jsug - kntmr-blog

最後のセッションで、「英語の読み書きの速さの差が日本と世界のエンジニアの差」という話がありましたが、まさに直前のセッションで英語が聞き取れなかったところなので、身に沁みました...。英語の勉強をしなければ。

Micronaut で Hello World on Windows

WindowsMicronaut を試してみたので備忘録。

インストール

一般的には SDKMAN を使うケースが多いかと思いますが、今回は Windows 環境ということでバイナリを使います。ダウンロードして Path を通す。

2.1.2 Install through Binary on Windows

初回の mn コマンド実行時に依存解決する模様。無念にもプロキシ環境の場合は事前に設定する。

$ set MN_OPTS=-Dhttps.proxyHost=proxy.com -Dhttps.proxyPort=8080

mn コマンドを叩いてみる。

$ mn --version
| Micronaut Version: 1.0.1
| JVM Version: 1.8.0_172

プロジェクト作成

無念にもプロキシ環境の場合は事前に設定する。

$ set JAVA_OPTS=-Dhttps.proxyHost=proxy.com -Dhttps.proxyPort=8080

プロジェクト作成。

$ mn create-app micronaut-examples
| Generating Java project...
| Application created at C:\Users\ ... \micronaut-examples

起動。

$ cd micronaut-examples
$ gradlew run
> Task :run
[main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 2238ms. Server Running: http://localhost:8080

環境起因なのか、起動は思っていたほど早くない気がする...?

Controller 作成

CLI から Controller を作成すると、Controller クラスとテストクラスが生成される。

$ mn create-controller Hello
| Rendered template Controller.java to destination src\main\java\micronaut\examples\HelloController.java
| Rendered template ControllerTest.java to destination src\test\java\micronaut\examples\HelloControllerTest.java

デフォルトでは 200 OK を返すコードが生成される。

@Controller("/hello")
public class HelloController {
    @Get("/")
    public HttpStatus index() {
        return HttpStatus.OK;
    }
}

gradlew run で起動して、http://localhost:8080/hello にアクセスするとレスポンスが返る。

テストクラスはこちら。gradlew test でテストを実行する。

public class HelloControllerTest {
    @Test
    public void testIndex() throws Exception {
        try(EmbeddedServer server = ApplicationContext.run(EmbeddedServer.class)) {
            try(RxHttpClient client = server.getApplicationContext().createBean(RxHttpClient.class, server.getURL())) {
                assertEquals(HttpStatus.OK, client.toBlocking().exchange("/hello").status());
            }
        }
    }
}

Controller のメソッドとテストを追加してみる。

@Get("hello2")
public HttpResponse<String> index2() {
    return HttpResponse.ok("Hello Micronaut");
}

テストメソッドはこんな感じ。

@Test
public void testIndex2() throws Exception {
    try(EmbeddedServer server = ApplicationContext.run(EmbeddedServer.class)) {
        try(RxHttpClient client = server.getApplicationContext().createBean(RxHttpClient.class, server.getURL())) {
            assertEquals("Hello Micronaut", client.toBlocking().exchange("/hello2", String.class).body());
        }
    }
}

最初、自動生成されたテストコードを参考に HttpResponse#exchange(String) で試してみたが、これだとどうしてもレスポンスボディが取得できない...。で、HttpResponse#exchange(String, Class<T>) でレスポンスの型を指定してみたら無事に取得できた。

client.toBlocking().exchange("/hello2").body() //=> null
client.toBlocking().exchange("/hello2", String.class).body() //=> "Hello Micronaut"

API ドキュメント にもそのような感じのことが書いてあるっぽいが、これは Reactive Streams の仕様なんだろうか...。

Returns: An Optional of the type or Optional.empty() if the body cannot be returned as the given type

ビルド

以下のコマンドを叩くと、build フォルダにいろいろ生成される。

$ gradlew assemble

その他

とりあえず、動かすことはできました。ドキュメントは割と充実しているようなので読んでみたいと思います。

User Guide - MICRONAUT DOCUMENTATION

Micronaut は、起動の早さや省メモリが特徴のようで、クラウド環境、特にサーバーレスな環境で Java アプリケーションを使うときに適しているようです。クラウド以外にも、起動が早いということからバッチアプリケーションとして使えるかもしれません。このあたり、Spring Batch と比較してみようかなー。

Effective Java 新旧比較

超小ネタですが、Effective Java第2版第3版 を見出しレベルで比較してみました。細かい言い回しの変更は含んでいません。というか、はじめに と付録の 第2版に対応する項目 を見ればだいたい新しい項目は把握できると思われます。が、そこをあえて項目同士を横に並べる形で一覧化してみました。ご参考まで。

第3版を購入したけど積読中の方、とりあえず新しく追加された項目を読んでみるのはいかがでしょう。

章レベルでは第3版から『第7章 ラムダとストリーム』が追加されています。項目レベルでは下記の通りです。左列に NEW と記載があるものが新しく追加されたか、他項目に移行されたものです。

また、第2版の『項目21 戦略を表現するために関数オブジェクトを使用する』『項目73 スレッドグループを避ける』は第3版では削除されています。(項目21については第3版では Lambda に移行された模様)

太字下線 の行は章見出しです。

- # 第2版 # 第3版
1 はじめに 1 はじめに
2 オブジェクトの生成と消滅 2 オブジェクトの生成と消滅
1 コンストラクタの代わりにstaticファクトリーメソッドを検討する 1 コンストラクタの代わりにstaticファクトリーメソッドを検討する
2 数多くのコンストラクタパラメータに直面した時にはビルダーを検討する 2 多くのコンストラクタパラメータに直面した時にはビルダーを検討する
3 privateのコンストラクタかenum型でシングルトン特性を強制する 3 privateのコンストラクタかenum型でシングルトン特性を強制する
4 privateのコンストラクタでインスタンス化不可能を強制する 4 privateのコンストラクタでインスタンス化不可能を強制する
NEW 5 資源を直接結び付けるよりも依存性注入を選ぶ
5 不必要なオブジェクトの生成を避ける 6 不必要なオブジェクトの生成を避ける
6 廃れたオブジェクト参照を取り除く 7 使われなくなったオブジェクト参照を取り除く
7 ファイナライザを避ける 8 ファイナライザとクリーナを避ける
NEW 9 try-finallyよりもtry-with-resourcesを選ぶ
3 すべてのオブジェクトに共通のメソッド 3 すべてのオブジェクトに共通のメソッド
8 equalsをオーバーライドする時は一般契約に従う 10 equalsをオーバーライドする時は一般契約に従う
9 equalsをオーバーライドする時は、常にhashCodeをオーバーライドする 11 equalsをオーバーライドする時は、常にhashCodeをオーバーライドする
10 toStringを常にオーバーライドする 12 toStringを常にオーバーライドする
11 cloneを注意してオーバーライドする 13 cloneを注意してオーバーライドする
12 Comparableの実装を検討する 14 Comparableの実装を検討する
4 クラスとインタフェース 4 クラスとインタフェース
13 クラスとメンバーへのアクセス可能性を最小限にする 15 クラスとメンバーへのアクセス可能性を最小限にする
14 publicのクラスでは、publicのフィールドではなく、アクセッサーメソッドを使う 16 publicのクラスでは、publicのフィールドではなく、アクセッサーメソッドを使う
15 可変性を最小限にする 17 可変性を最小限にする
16 継承よりコンポジションを選ぶ 18 継承よりコンポジションを選ぶ
17 継承のために設計および文書化する、でなければ継承を禁止する 19 継承のために設計および文書化する、でなければ継承を禁止する
18 抽象クラスよりインタフェースを選ぶ 20 抽象クラスよりインタフェースを選ぶ
NEW 21 将来のためにインタフェースを選ぶ
19 型を定義するためだけにインタフェースを使用する 22 型を定義するためだけにインタフェースを使う
20 タグ付クラスよりクラス階層を選ぶ 23 タグ付きクラスよりもクラス階層を選ぶ
DROP 21 戦略を表現するために関数オブジェクトを使用する
22 非staticのメンバークラスよりstaticのメンバークラスを選ぶ 24 非staticのメンバークラスよりもstaticのメンバークラスを選ぶ
NEW 25 ソースファイルを単一のトップレベルのクラスに限定する
5 ジェネリック 5 ジェネリック
23 新たなコードで原型を使用しない 26 原型を使わない
24 無検査警告を取り除く 27 無検査警告を取り除く
25 配列よりリストを選ぶ 28 配列よりもリストを選ぶ
26 ジェネリック型を使用する 29 ジェネリック型を使う
27 ジェネリックメソッドを使用する 30 ジェネリックメソッドを使う
28 APIの柔軟性向上のために境界ワイルドカードを使用する 31 APIの柔軟性向上のために境界ワイルドカードを使う
NEW 32 ジェネリックスと可変長引数を注意して組み合わせる
29 型安全な異種コンテナーを検討する 33 型安全な異種コンテナを検討する
6 enumアノテーション 6 enumアノテーション
30 int定数の代わりにenumを使用する 34 int定数の代わりにenumを使う
31 序数の代わりにインスタンスフィールドを使用する 35 序数の代わりにインスタンスフィールドを使う
32 ビットフィールドの代わりにEnumSetを使用する 36 ビットフィールドの代わりにEnumSetを使う
33 序数インデックスの代わりにEnumMapを使用する 37 序数インデックスの代わりにEnumMapを使う
34 拡張可能なenumをインタフェースで模倣する 38 拡張可能なenumをインタフェースで模倣する
35 命名パターンよりアノテーションを選ぶ 39 命名パターンよりもアノテーションを選ぶ
36 常にOverrideアノテーションを使用する 40 常にOverrideアノテーションを使う
37 型を定義するためにマーカーインタフェースを使用する 41 型を定義するためにマーカーインタフェースを使う
NEW 7 ラムダとストリーム
NEW 42 無名クラスよりもラムダを選ぶ
NEW 43 ラムダよりもメソッド参照を選ぶ
NEW 44 標準の関数型インタフェースを使う
NEW 45 ストリームを注意して使う
NEW 46 ストリームで副作用のない関数を選ぶ
NEW 47 戻り値型としてStreamよりもCollectionを選ぶ
NEW 48 ストリームを並列化するときは注意を払う
7 メソッド 8 メソッド
38 パラメータの正当性を検査する 49 パラメータの正当性を検査する
39 必要な場合には、防御的にコピーする 50 必要な場合、防御的にコピーする
40 メソッドのシグニチャを注意深く設計する 51 メソッドのシグニチャを注意深く設計する
41 オーバーロードを注意して使用する 52 オーバーロードを注意して使う
42 可変長引数を注意して使用する 53 可変長引数を注意して使う
43 nullではなく、空配列か空コレクションを返す 54 nullではなく、空コレクションか空配列を返す
NEW 55 オプショナルを注意して返す
44 すべての公開API要素に対してドキュメントコメントを書く 56 すべての公開API要素に対してドキュメントコメントを書く
8 プログラミング一般 9 プログラミング一般
45 ローカル変数のスコープを最小限にする 57 ローカル変数のスコープを最小限にする
46 従来のforループよりfor-eachループを選ぶ 58 従来のforループよりfor-eachループを選ぶ
47 ライブラリーを知り、ライブラリーを使う 59 ライブラリを知り、ライブラリを使う
48 正確な答えが必要ならば、floatとdoubleを避ける 60 正確な答えが必要ならば、floatとdoubleを避ける
49 ボクシングされた基本データより基本データ型を選ぶ 61 ボクシングされた基本データよりも基本データ型を選ぶ
50 他の型が適切な場所では、文字列を避ける 62 他の型が適切な場所では、文字列を避ける
51 文字列結合のパフォーマンスに用心する 63 文字列結合のパフォーマンスに用心する
52 インタフェースでオブジェクトを参照する 64 インタフェースでオブジェクトを参照する
53 リフレクションよりインタフェースを選ぶ 65 リフレクションよりもインタフェースを選ぶ
54 ネイティブメソッドを注意して使用する 66 ネイティブメソッドを注意して使う
55 注意して最適化する 67 注意して最適化する
56 一般的に受け入れられている命名規約を守る 68 一般的に受け入れられている命名規約を守る
9 例外 10 例外
57 例外的状態にだけ例外を使用する 69 例外的状態にだけ例外を使う
58 回復可能な状態にはチェックされる例外を、プログラミングエラーには実行時例外を使用する 70 回復可能な状態にはチェックされる例外を、プログラミングエラーには実行時例外を使う
59 チェックされる例外を不必要に使用するのを避ける 71 チェックされる例外を不必要に使うのを避ける
60 標準例外を使用する 72 標準的な例外を使う
61 抽象概念に適した例外をスローする 73 抽象概念に適した例外をスローする
62 各メソッドがスローするすべての例外を文書化する 74 各メソッドがスローするすべての例外を文書化する
63 詳細メッセージにエラー記録情報を含める 75 詳細メッセージにエラー記録情報を含める
64 エラーアトミック性に努める 76 エラーアトミック性に努める
65 例外を無視しない 77 例外を無視しない
10 並行性 11 並行性
66 共有された可変データへのアクセスを同期する 78 共有された可変データへのアクセスを同期する
67 過剰な同期は避ける 79 過剰な同期は避ける
68 スレッドよりエグゼキューターとタスクを選ぶ 80 スレッドよりもエグゼキュータ、タスク、ストリームを選ぶ
69 waitとnotifyよりコンカレンシーユーティリティを選ぶ 81 waitとnotifyよりも並行処理ユーティリティを選ぶ
70 スレッド安全性を文書化する 82 スレッド安全性を文書化する
71 遅延初期化を注意して使用する 83 遅延初期化を注意して使う
72 スレッドスケジューラに依存しない 84 スレッドスケジューラに依存しない
DROP 73 スレッドグループを避ける
11 シリアライズ 12 シリアライズ
NEW 85 Javaシリアライズよりも代替手段を選ぶ
74 Serializableを注意して実装する 86 Serializableを細心の注意を払って実装する
75 カスタムシリアライズ形式の使用を検討する 87 カスタムシリアライズ形式の使用を検討する
76 防御的にreadObjectを書く 88 防御的にreadObjectメソッドを書く
77 インスタンス制御に対しては、readResolveよりenum型を選ぶ 89 インスタンス制御に対しては、readResolveよりもenum型を選ぶ
78 シリアライズされたインスタンスの代わりに、シリアライズ・プロキシを検討する 90 シリアライズされたインスタンスの代わりに、シリアライズ・プロキシを検討する

その他

長年、本書の第2版は Java の必読書としてしばしば挙げられていますが、内容が古くなっていたり、新しい機能に追従できていないところはありました。以前よりも Java のアップデートサイクルは早くなっているため、特に入門者に書籍をおすすめするときには、古い内容が含まれていないか、移行された機能がないか、十分に注意したいところです。

ところで、全体的に Optional がカタカナで書かれているのはなぜだろう...。

GraphQL を Spring Boot で試してみる 2

こちらの続き。今回は Mutation を試してみます。

GraphQL を Spring Boot で試してみる 1 - kntmr-blog

サンプルコードはこちら。

kntmr/playground/graphql-spring-examples - GitHub


以下、備忘録。

schema / データクラス

スキーマに Mutation の IF を定義。引数に任意の型を指定する場合は input を定義する。(命名規則が雑なのは許してください)

// ... (略)
input _ToDo {
    content: String!
    completed: Boolean!
    userId: ID!
}
type Mutation {
    add(todo: _ToDo): [ToDo]
}

尚、内部で Jackson を利用しており、データクラスは Jackson のお作法に従う必要がある模様。最初、input に対応するデータクラスにデフォルトコンストラクタがなくて、API 呼び出し時に以下のエラーが発生。

Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.example.demo.type._ToDo` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
Resolver

Mutation の場合は GraphQLMutationResolver インタフェースを実装する。

@Component
public class TodoMutationResolver implements GraphQLMutationResolver {
    private TodoDao todoDao;   
    public TodoMutationResolver(TodoDao todoDao) {
        this.todoDao = todoDao;
    }   
    public List<ToDo> add(_ToDo _todo) {
        List<ToDo> current = todoDao.findAll(); // XXX
        return todoDao.add(new ToDo(current.size(), _todo.getContent(), _todo.isCompleted(), _todo.getUserId()));
    }
}
リクエス

アプリを起動して、http://localhost:8080/graphiql にアクセスする。以下の形式でリクエストする。更新するデータと併せて、レスポンスで返して欲しいフィールドを指定する。

mutation {
  add(todo: {
    content: "New content!"
    completed: false
    userId: 1
  }) {
    id
    content
    completed
  }
}

所感

今回は Mutation を試してみました。次回は Subscription を試してみようかと。

SpringOne Tour 2018 に行ってきた #s1t

先日、SpringOne Tour 2018 に行ってきました。簡単に所感をまとめます。

SpringOne Tour

メモから抜粋。スライドが公開されたらリンクを貼っておきます。逐次通訳はあったのですが、理解が誤ってる箇所があるかもしれません...。

Spring Boot 2.0によるリアクティブSpring

www.slideshare.net

  • Reactive Programming
    • non-blocking, event-driven
    • 省スレッド
    • backpressure
  • Java は拡張されやすいように進歩してきたが、効率は効率はよくなかった
  • 新しいリクエスト/接続があるたびにスレッドを生成する必要がある
    • 各スレッドでIOを待つ必要があったり
  • イベントループによって少ないリソースで処理する
    • 非同期で処理する
    • ただの非同期ではなくリアクティブ
    • 非同期とリアクティブの違いは Backpressure
    • Backpressure がないと Subscriber の処理が追い付かない
    • Subscriber は Publisher に Backpressure して大量データを制御する
  • スケジューラによって比較的少ない数のスレッドで処理できる
  • リアクティブの目的はパフォーマンス向上より拡張性の向上
  • Reactive Streams: 4 interfaces ★
    • Publisher
    • Subscriber
    • Subscription
    • Processor<T,R>
    • ↑の仕様を実装したのが Project Reactor
    • Reactor が Java/Spring で使いやすくていい
  • Publisher<T>
    • 通常の Java では単一の Object か Collection を返す
    • Publisher は 0..無限 の値を返せる
    • Spring WebFlux では、0 or 1 / 0..無限の値を返す
    • WebSocket はサーバーからのイベントに対応している
  • MongoDB はリアクティブサポートあり (Reactive MongoDB)
    • Embedded MongoDB は組み込みで手軽に開発できる
    • テスト/開発で使うときは build.gradle を testimplementationimplementation に変える
  • ReactiveCrudRepository
    • repo.deleteAll() が最初に実行される保証はない ⇒ repo.deleteAll().block()ブロッキングできる
    • ただし、.block() は書くべきではない ⇒ repo.deleteAll().thenMany(...) でアップストリームの完了を待つ
    • thenMany() はチェインできる
  • Subscriber より Publisher が早いケースをシミュレート
    • onBackpressureDrop : Subscriber が処理しきれないときは捨てる
    • onBackpressureBuffer : Subscriber が処理しきれないときはバッファする (サイズ指定可能)
  • @SpringBootTest
    • Spring 全体のコンテキストをロードする
  • @WebFluxTest
  • 無限の Flux をテストするときは考慮が必要
    • StepVerifier でテストする
    • StepVerifier.withVirtualTime で時間が経過したことをシミュレートしてテストする
  • (メモ) FSR: The Full Stack Reactive (meta) repository

クラウドネイティブSpring

  • Reactive MongoDB
  • Java 11
  • Lombok
  • R2DBC
    • R2DBC では Spring Data JDBC を使う
    • Reactive MongoDB は使わない?
    • Production ではまだ使わない (GAではないので)
    • R2DBC では deleteAll は使えない?
  • RouteLocator (Spring Cloud Gateway)
  • Spring Security
    • bcrypt を使う?
    • Reactive に対応しているが、古い AuthenticationManager は動作する (Problem?)
  • 429 Too Many Requests

Spring Cloud Gateway

  • マイクロサービスが増えるとサービス間の関連が増える
  • 通常は Gateway を配置する
  • 主な目的は Routing (リクエストを適切なインスタンスに流す)
  • すべてのアプリケーションに対してセキュリティを実施
  • Canary-ing
    • 新バージョンをリリースするときに Canary Release する
    • 少量のリクエストを流してテストする
  • Monolith なアプリケーションをマイクロサービスに移行する
  • ロードバランサ
  • Netflix Zuul 1
    • Servlet, Blocking IO
    • パフォーマンスに問題があった
    • API が使いにくい
  • Spring Cloud と Zuul 1 は統合済み
  • Netflix Zuul 2
    • Netty, Non-blocking IO
  • Spring Cloud が Zuul 2 と連携すると Zuul 1 と後方互換性がなくなる
  • Spring Cloud Gateway
    • Node.js のような event-loop のアーキテクチャ
    • Request --> Event Queue --(Schedule)--> Event Loop --(Schedule)--> Network Call
    • Network Call --(Callback)--> Event Loop --(Callback)--> Event Queue --> Response
  • Handler Mapping (Spring が提供する機能)
    • Predicates
    • Pre-Filters : ダウンストリームに送る前に実行される filter
    • Global Filters : ダウンストリームにリクエストする (WebSocket などのプロトコルをサポート)
    • Post-Filters : HTTP ステータスやヘッダを変えたり、Cookie をセットしたり
  • RouteLocator
    • route に ID を付けるとデバッグのときに便利
  • Hystrix
  • WebSocket
    • クライアント/サーバー間で Gateway を通して persistent な接続を維持する
  • Embedded
    • Spring アプリに Gateway を組み込む
    • レイテンシに厳しい場合など、Cross-Origin の問題も心配なし?
  • Facade
    • クライアントとアプリの間に配置する
    • アプリに直接接続する必要がない
    • 技術(?)が違う場合でも接続できる
    • アプリが切り替わってもクライアントに影響しない
  • Cross-Cutting + App-Specific
  • Gateway で Spring Boot の Auto Configuration が使える (Spring Boot ベースなので)
  • Micrometer Support
    • SLF4J メトリック
  • Zipkin で分散トレーシング
  • (メモ) httpbin.org
  • (メモ) HTTPie

Spring Cloud Stream 2.0によるクラウドイベント駆動型アーキテクチャ

サーバーレスSpring

www.slideshare.net

  • Serverless ★
    • sacle to N, scale to zero
  • Knative & riff
  • HTTP ★
    • spring-cloud-starter-function-web
    • Function インタフェースを実装して Bean 定義する
  • Message ★
    • spring-cloud-function-context + spring-cloud-stream-binder-rabbit
  • Supplier, Function, Consumer
  • Source, Processor, Sink
  • @EnableBinding(Processor.class)
  • Exchange, Topic
  • s.c.s.function.definition にパイプで function をチェインできる
  • Spring Cloud Function Adapter ★
    • PaaS プロバイダに依存せずデプロイ/実行できる
  • Knative & riff
  • Knative は k8s ベースのプロダクト
    • build, deploy, manage
  • riff は Knative 上で動く
    • Knative を簡単に使えるようにするもの
    • polyglot
    • CLI, Function Invokers, BuildTemplates
  • Pivotal Function Service
    • riff の商用サービスとして提供予定

Pivotal Application Serviceで稼働するSpring Boot/Spring Cloudアプリケーション

  • Pivotal Application Service
    • 開発した Spring Boot アプリをどのように運用するか
  • Full Cycle Developers (by Netflix) ★
  • Atlas
    • Netflix 製の監視ツール
    • アプリを監視できるように自分でツールに設定するのは面倒
    • アプリ側からツールに自身を設定するようにした
  • MOSH
  • Spring Cloud Pipeline
    • CredHub
    • 12 Factor App
  • アップロードされたパッケージからCF側でランタイムを判断して環境を構築してくれる?
    • Buildpacks の中でできるっぽい
  • Pivotal Cloud Cache

Using Spinnakerを使用したKubernetes上の開発ワークフローの作成

  • Container をまとめたものが Pod
  • Pod をまとめたものが ReplicaSet
    • 可用性
  • ReplicaSet を拡張したものが Deployment
  • LB側でグローバルIPを持つ
  • Ingress
    • 1つのURLで複数のアプリを持てる
  • Helm
  • Helm Chart
  • Kuberapps Hub

所感など

先週の Spring Fest 2018 に続いて、2週連続の Spring イベント。とにかく登壇者が豪華でした。みなさん、ユーモアが豊富でおもしろかったです。全体的にデモやライブコーディングがあってよかった。特に、Josh Long のセッションとライブコーディングは勢いが凄まじかったです。

とはいえ、PCF や k8s などは使ったことがなくてイマイチ理解が追い付かず。特に、k8s あたりは避けては通れないと思われるので勉強しなければ。

当日、JJUG x JSUG のナイトセミナー で Josh Long のイベントがあったのですが、そっちも参加すればよかった...。もったいない。