kagamihogeの日記

kagamihogeの日記です。

Web on Reactive Stack(version 5.0.3.RELEASE) 1. Spring WebFluxをテキトーに訳した(1.1 - 1.3)

Spring WebFluxってなんだ? って感じだったんで https://docs.spring.io/spring/docs/5.0.3.RELEASE/spring-framework-reference/web-reactive.html を読んでテキトーに訳した。なおマッタクといっていいほど推敲してないです。

Web on Reactive Stack

このパートでは、Netty, Undertow, Servlet 3.1+コンテナなどノンブロッキングサーバ上で動作するReactive Streams APIでwebアプリケーションを構築する、リアクティブスタック(reactive stack)について解説します。Spring WebFluxフレームワーク、リアクティブWebClientTesting、についてのチャプターがあります。ServletスタックについてはWeb on Servlet Stackを参照してください。

1. Spring WebFlux

1.1. Introduction

Spring Frameworkに含まれるSpring Web MVCServlet APIServletコンテナ向けに作られました。リアクティブスタックのwebフレームワークWebFluxはversion 5.0で後から追加されました。完全にノンブロッキングで、Reactive Streamsのバックプレッシャをサポートし、Netty, Undertow, Servlet 3.1+コンテナなどのサーバ上で動作します。

spring-webmvcspring-webfluxとソースモジュール名は似たものなっており、Spring Frameworkで共存しています。各モジュールはオプショナルです。アプリケーションはどちらか一方を使ってもよいし、両方使っても構いません。たとえば、Spring MVCのcontrollerをリアクティブなWebClientにするなど。

1.1.1. Why a new web framework?

理由の一つは、少数のスレッドで並行処理し、少ないハードウェアリソースでスケールする、ノンブロッキングなwebスタックに対するニーズです。Servlet 3.1はノンブロッキングI/OのAPIを提供しました。しかし、これを利用すると、同期(Fileter, Servlet)やブロッキングgetParameter, getPart)を契約とするServlet APIの部分から離れることになります。これが何らかのノンブロッキングランタイムの基盤として振る舞う新しい共通APIが求められた背景です。Nettyなどのサーバは非同期・ノンブロッキング空間で確立するのでこのことは重要です。

もう一つの理由は関数型プログラミングです。Java 5で追加されたアノテーションが、アノテーションベースのRESTコントローラーやユニットテストなど、新たな可能性を切り開いたように、Java 8のラムダ式追加は関数型APIの可能性を開きました。これらは、CompletableFutureReactiveXで一般的となった、ノンブロッキングアプリケーションとcontinuation style APIに恩恵を与えました。これらでは非同期ロジックの宣言的なコンポジションが可能です。プログラミングモデルのレベルでは、Java 8によって、Spring WebFluxはコントローラと共にファンクショナルなwebエンドポイントを提供できるようになりました。

1.1.2. Reactive: what and why?

ノンブロッキングとファンクショナルについては上記で触れましたが、リアクティブの理由とその意味については以下で説明します。

リアクティブ""reactive")という用語は、I/Oイベントに反応するネットワークコンポーネント、マウスイベントに反応するUIコントローラーなど、変更に対する反応で構築するプログラミングモデルを指します。そういう意味でノンブロッキングはリアクティブで、操作完了やデータ到着など、ブロッキングではなく通知に反応するモードを使用します。 また、Springチームが"リアクティブ"と関連付けたもう一つの重要なメカニズムにノンブロッキングのバックプレッシャがあります。非同期・命令型(imperative code)・ブロッキングの呼び出しでは、バックプレッシャは呼び出し元に待機を自然と強制する形になります。ノンブロッキングでは、イベントのレート制御が重要で、これは高速なプロデューサーがその宛先(destination)を溢れさせないようにするためです。

Reactive Streamsの仕様は小さく(small spec)Java 9で採用*1され、非同期コンポーネントとバックプレッシャ間の相互作用を定義しています。たとえばデータリポジトリ(data repository)は、Publisherとしての振る舞いはHTTPサーバのデータをプロデュースし、それからSubscriberとしての振る舞いはレスポンスを書き込みます。パプリッシャのデータプロデュースを、サブスクライバでどのくらい高速or低速に制御させるか、がReactive Streamsの主目的となります。

Common question: パブリッシャがスローダウン出来ない場合は?
eactive Streamsの目的は、あくまでも、メカニズムと境界を確立するものです。パブリッシャがスローダウン出来ない場合、バッファリング・廃棄・失敗、するかどうかを決定します。

1.1.3. Reactive API

Reactive Streamは相互運用性(interoperability)で重要な役割を担います。ライブラリと基盤コンポーネントに関心事が存在し、低レベルなのでアプリケーションAPIとしてはあまり有用ではありません。アプリケーションは、Java 8のStream APIのようだがコレクションに限定しない、非同期ロジックをまとめる関数型のAPIという高レベルでリッチなAPIを必要とします。リアクティブのライブラリの役割はこれです。

Spring WebFluxはリアクティブのライブラリにRactorを使用します。

ReactiveXのvocabulary of operatorsで定められた豊富な演算子で0..1および0..Nのデータシーケンスを動作させるMonoFlux APIがあります。ReactorはReactive Streamsのライブラリなので演算子はすべてノンブロッキングのバックプレッシャをサポートします。ReactorはサーバーサイドJavaを強く意識しており、Springと密に意見交換しながら開発されています。

WebFluxはコア依存性としてReactorを要求しますが、Reactive Streams経由で別のリアクティブライブラリとの相互運用性が存在します。通常、WebFlux APIは入力にプレーンなPublisherを取り、内部的にReactor型に変換して使用し、出力にFluxMonoのどちらかを返します。入力に何らかのPublisherを渡して出力で操作を適用できますが、それとは別のリアクティブライブラリでこれを使うには出力を変換する必要があります。変換可能な場合であれば、たとえばアノテーションを付与したコントローラーでは、WebFluxはRxJavaもしくはその他のリアクティブライブラリに透過的に変換します。

1.1.4. Programming models

spring-webモジュールにはリアクティブの基礎部分が含まれ、これにはHTTPの抽象化を含むSpring WebFlux、サポートするサーバ向けのReactive StreamsアダプタコーデックServlet API相当(ただしノンブロッキング)のコアWebHandler API、があります。

これら基礎部分の上にSpring WebFluxは二つのプログラミングモデルを提供しています。

1.1.5. Choosing a web framework

Spring MVCとWebFluxのどちらを選べば良いか。いくつかの異なる視点から見ていきます。

既に稼働中のSpring MVCアプリケーションがある場合、何も変える必要はありません。命令型プログラミングは、書きやすく、理解しやすくて、デバッグもしやすいです。歴史的に大半のライブラリはブロッキングなので過去の資産を最大限生かせます。

既にノンブロッキングのwebスタックの採用経験がある場合、 Spring WebFluxからそこのwbスタックと同一の実行モデルによる利点が得られます。また、サーバの選択肢、Netty, Tomcat, Jetty, Undertow, Servlet 3.1+コンテナ、プログラミングモデルの選択肢、アノテーション付与のコントローラとファンクショナルエンドポイント、リアクティブライブラリの選択肢、Reactor, RxJava, など、があります。

Java 8のラムダやKotlinで使うための軽量な関数型webフレームワークに関心がある場合はSpring WebFluxのファンクショナルエンドポイントを使います。小規模アプリケーションか、透過性と制御性で要求の複雑さを抑えるマイクロサービスでの選択肢の一つになります。

マイクロサービスアーキテクチャでは、 Spring MVCもしくはSpring WebFluxのコントローラを使うアプリケーションを混在するか、Spring WebFluxのファンクショナルエンドポイントを用います。両方のフレームワークとも同じアノテーションベースのプログラミングモデルをサポートしているので知識の再利用はしやすいです。ただし妙な使い方をしない場合に限る*3

アプリケーションを評価する方法の一つは依存性を調べることです。ブロッキングの永続化API(JPA, JDBC)や、ネットワークAPIがある場合、少なくともSpring MVCは良くあるアーキテクチャ向けに最適です。技術的には、ReactorもRxJavaも異なるスレッドでブロッキングAPIを実行することで適応可能ですが、ノンブロッキングwebスタックを最大限活用しているとは言えません。

Spring MVCアプリケーションでリモートサービス呼び出しがある場合、リアクティブのWebClientを検討してください。Spring MVCのコントローラメソッドで直接リアクティブの型(Reactor, RxJava, その他)を返せます。呼び出しごとのレイテンシや、呼び出し間の相互依存性が大きいほど、大きなメリットを得られます。Spring MVCのコントローラは同様にその他のリアクティブコンポーネントも呼び出せます。

大規模チームの場合、ノンブロッキング・関数型・宣言的プログラミングに移行する際の急激な学習曲線に気を付けてください。完全には移行しない現実的な方法としてリアクティブのWebClientから始めるものがあります。スモールスタートの後に効果を測定してください。我々の想定では、アプリケーションの大部分を移行する必要は無い、と考えています。

今一つメリットが分からない場合、ノンブロッキングI/Oの動作と効果を知る事から始めてください(例:シングルスレッドNode.jsの並行処理、は矛盾してるわけではありません)。"scale with less hardware"はキャッチフレーズで何らかの効果を保証するものではないし、スローダウンしたり予測不能なネットワークI/Oが無いわけでは無いです。Netflixblog postが好例です。

1.1.6. Choosing a server

Spring WebFluxはNetty, Undertow, Tomcat, Jetty, Servlet 3.1+コンテナでサポートしています。サーバは共通のReactive Streams APIに対応しています。Spring WebFluxのプログラミングモデルはその共通API上に作られています。

Common question: 両方のスタックでTomcatとJettyを使うには
TomcatとJettyのコアはノンブロッキングです。ブロッキングファサードを追加するServlet APIがそれに当たります。3.1からServlet APIはノンブロッキングI/Oを追加しています。ただし、同期やブロッキングを避ける必要があります。そのため、SpringのリアクティブwebスタックにはReactive Streamsとブリッジする低レベルなServletアダプタがありますが、Servlet APIを直接使う方法は公開していません。

Spring Boot 2はデフォルトではWebFluxを使用し、これはNettyが非同期・ノンブロッキングで広く使われており、また、クライアントとサーバの両方でリソースを共有できます。Nettyと比較するとServlet 3.1のノンブロッキングI/Oは、敷居が高いため、あまり使われていません。Spring WebFluxは導入の糸口となります。

Spring Bootのデフォルトサーバはすぐに使い始めるための意味合いが強いです。アプリケーションでは、パフォーマンス最適化・完全ノンブロッキング・Reactive Streamsバックプレッシャに変換、を行うその他のサーバを選択可能です。Spring Bootで別のサーバにスイッチするのは簡単です。

1.1.7. Performance vs scale

Performanceは様々な特徴と意味合いを持っています。Reactiveとノンブロッキングは基本的にはアプリケーションを高速にはしません。例えば、WebClientでパラレルにリモート呼び出しを実行すると、高速になる場合があります。一般論として、ノンブロッキング化には別途の開発が必要で、処理時間が少々上昇する可能性があります。

リアクティブとノンブロッキングに期待されるメリットは、小規模・固定スレッド数・省メモリでスケールする能力です。これにより、アプリケーションは予測可能な方法でスケールするので負荷に弾力性を持つようになります。ただし、これらの利点が活きるには、低速で予測不能なネットワークI/Oの混在するレイテンシの場合です。そういう場所ではリアクティブスタックは強靭さを発揮し、劇的な違いを見せるでしょう。

1.2. Reactive Spring Web

spring-webモジュールには、リアクティブwebアプリケーションを構築するための、クライアントとサーバ双方の、低レベル基盤とHTTP抽象化があります。実装にReactorを使用するReactive Streams上にすべてのpubli APIは構築されています。

サーバは二つのレイヤーに分けられます。

  • HttpHandlerとサーバアダプタ。Reactive StreamsバックプレッシャでHTTPリクエストを処理する、最も基本的な共通API
  • WebHandler API - やや高レベルなフィルターチェーンで処理をする汎用サーバweb API

1.2.1. HttpHandler

HTTPサーバはHTTPリクエストを処理するための何らかのAPIを備えています。HttpHandlerは、リクエストとレスポンスを処理する一つのメソッドからなる、シンプルな契約(simple contract)です。このAPIは意図的に小さくしています。その主な用途は、異なるサーバでHTTPリクエストを処理するReactive StreamsベースのAPIという、汎用部品の提供です。

spring-webモジュールにはサポート対象サーバ別のアダプタが含まれます。以下の表は使用されるサーバAPIとReactive Streamsのサポート方法です。

Server name Server API used Reactive Streams support
Netty Netty API Reactor Netty
Undertow Undertow API spring-web: Undertow to Reactive Streams bridge
Tomcat Servlet 3.1 non-blocking I/O; Tomcat API to read and write ByteBuffers vs byte spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge
Jetty Servlet 3.1 non-blocking I/O; Jetty API to write ByteBuffers vs byte spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge
Servlet 3.1 container Servlet 3.1 non-blocking I/O spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge

以下は必要となる依存性、サポートバージョン、各サーバ別のコード例です。

Server name Group id Artifact name
Reactor Netty io.projectreactor.ipc reactor-netty
Undertow io.undertow undertow-core
Tomcat org.apache.tomcat.embed tomcat-embed-core
Jetty org.eclipse.jetty jetty-server, jetty-servlet

Reactor Netty:

HttpHandler handler = ...
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer.create(host, port).newHandler(adapter).block();

Undertow:

HttpHandler handler = ...
UndertowHttpHandlerAdapter adapter = new UndertowHttpHandlerAdapter(handler);
Undertow server = Undertow.builder().addHttpListener(port, host).setHandler(adapter).build();
server.start();

Tomcat:

HttpHandler handler = ...
Servlet servlet = new TomcatHttpHandlerAdapter(handler);

Tomcat server = new Tomcat();
File base = new File(System.getProperty("java.io.tmpdir"));
Context rootContext = server.addContext("", base.getAbsolutePath());
Tomcat.addServlet(rootContext, "main", servlet);
rootContext.addServletMappingDecoded("/", "main");
server.setHost(host);
server.setPort(port);
server.start();

Jetty:

HttpHandler handler = ...
Servlet servlet = new JettyHttpHandlerAdapter(handler);

Server server = new Server();
ServletContextHandler contextHandler = new ServletContextHandler(server, "");
contextHandler.addServlet(new ServletHolder(servlet), "/");
contextHandler.start();

ServerConnector connector = new ServerConnector(server);
connector.setHost(host);
connector.setPort(port);
server.addConnector(connector);
server.start();

Servlet 3.1+コンテナにWARとしてデプロイするには、ServletHttpHandlerAdapterHttpHandlerをラップして、Servletとして登録します。AbstractReactiveWebInitializerを使用する場合は自動的に行われます。

1.2.2. WebHandler API

HttpHandlerは異なるHTTPサーバ上で動かすための最も低レベルな契約です。これの上の、WebHandler APIはそれより少し上のレベルですが、WebExceptionHandler's, WebFilter's, WebHandlerのチェーンを形成する汎用用途のコンポーネント群になります。

WebHandler APIのすべてのコンポーネントは入力にServerWebExchangeを取り、このクラスの裏では、リクエスト変数・セッション変数・パースしたフォームデータへのアクセス・マルチパートなど、webアプリケーションで使用する構成要素を提供するためのServerHttpRequestServerHttpResponseが存在します。

WebHttpHandlerBuilderはリクエスト処理チェーンをアセンブルするのに使われます。コンポーネントを手動で追加するにはこのビルダーを使うか、たいていはSpringのApplicationContextから取得することになるHttpHandlerは、サーバアダプタ経由の実行準備が出来た状態で取得できます。

ApplicationContext context = ...
HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context).build()

以下の表はWebHttpHandlerBuilderが検出するコンポーネントのリストです。

Bean name Bean type Count Description
WebExceptionHandler 0..N すべてのWebFilterとそのターゲットWebHandlerの後に適用する例外ハンドラ
WebFilter 0..N ターゲットWebHandlerの前後で実行するフィルタ
"webHandler" WebHandler 1 リクエストハンドラ
"webSessionManager" WebSessionManager 0..1 ServerWebExchangeのメソッド経由で公開されるWebSession用のマネージャ。デフォルトはDefaultWebSessionManager
"serverCodecConfigurer" ServerCodecConfigurer 0..1 フォームとマルチパートをパースするHttpMessageReaderにアクセスする時に使う。ServerWebExchangeのメソッド経由で公開される。デフォルトはServerCodecConfigurer.create()
"localeContextResolver" LocaleContextResolver 0..1 ServerWebExchangeのメソッド経由で公開されるLocaleContext用のリゾルバ。デフォルトはAcceptHeaderLocaleContextResolver

Form Reader

ServerWebExchangeはフォームデータにアクセスする以下の公開メソッドがあります。

Mono<MultiValueMap<String, String>> getFormData();

DefaultServerWebExchangeは設定されているHttpMessageReaderでフォームデータ("application/x-www-form-urlencoded")をMultiValueMapにパースします。デフォルトではFormHttpMessageReaderServerCodecConfigurerのbeanを使用して設定されます。(Web Handler API参照)

Multipart Reader

Spring MVCと同様

ServerWebExchangeはマルチパートにアクセルする以下の公開メソッドがあります。

Mono<MultiValueMap<String, Part>> getMultipartData();

DefaultServerWebExchangeは設定されているHttpMessageReader<MultiValueMap<String, Part>>で"multipart/form-data"をMultiValueMapにパースします。現状、サポートされているサードパーティライブラリはSynchronoss NIO Multipartだけで、我々の知る限りではこのライブラリはマルチパートリクエストのノンブロッキングなパースが出来ます。ServerCodecConfigurerのbeanを通して有効化します。(Web Handler API

ストリーミングのマルチパートをパースするには、HttpMessageReader<Part>から返されるFlux<Part>を代わりに使います。たとえば、@RequestPartを使うコントローラーはnameとパートが対応するMapライクのアクセスを意味し、そのため、完全なマルチパートのパースが必要になります。対照的に、Flux<Part>のコンテンツをデコードするのに@RequestBodyを使用可能で、その際MultiValueMapを生成しません。

1.2.3. HTTP Message Codecs

Spring MVCと同等

spring-webモジュールはRective StreamsのPublisherを介してHTTPのリクエストとレスポンスボディのエンコードとデコードをするためのHttpMessageReaderHttpMessageWriterを定義しています。これらはクライアント側で使うものでは例えばWebClient、サーバ側ではアノテーションを付与するコントローラとファンクショナルエンドポイントで使います。

spring-coreモジュールはEncoderDecoderを定義しており、これらはHTTPに非依存で、NettyのByteBufjava.nio.ByteBufferなど異なるバイトバッファ表現を抽象化するDataBufferを使用します(Buffers and Codecsを参照)。EncoderHttpMessageWriterとして使うためにEncoderHttpMessageWriterでラップ可能で、DecoderHttpMessageReaderとして使うためにDecoderHttpMessageReaderでラップ可能です。

spring-coreモジュールには、byte[], ByteBuffer, DataBuffer, Resource, String、で使う基本的なEncoderDecoderの実装があります。spring-webモジュールにはJackson JSON, Jackson Smile, JAXB2で使うEncoderDecoderを追加しています。また、spring-webモジュールには、server-sentイベント、フォームデータ、マルチパートリクエストで使うweb固有のreaderとwriterがあります。

アプリケーションで使うためにreaderとwriterを設定したりカスタマイズするには、基本的にはClientCodecConfigurerServerCodecConfigurerを使います。

Jackson JSON

decoderはバイトのチャンクストリームをTokenBufferストリームへパースするのにJacksonのノンブロッキングなバイト配列パーサーを用います。これはJacksonのObjectMapperに変換できます。

encoderは以下のようにPublisher<?>を処理します。

  • PublisherMono(つまり単一の値)の場合、値はJSONにエンコードされる。
  • メディアタイプがapplication/stream+jsonの場合、Publisherが生成するそれぞれの値はJSONに改行つきでエンコードされます。
  • 上記以外の場合、Publisherのすべての中身はFlux#collectToList()に集約され、このコレクションがJSON配列にエンコードされます。

上記ルールの特殊ケースに、ServerSentEventHttpMessageWriterは入力のPublisherの内容をそれぞれMono<?>としてJackson2JsonEncoderに送ります。

注意点として、Jackson JSON encoderとdecoderはString型のレンダリング要素を明示的に返します。Instead String's are treated as low level content, (i.e. serialized JSON) and are rendered as-is by the CharSequenceEncoder.*4 JSON配列としてレンダリングされるFlux<String>にしたい場合、Flux#collectToList()を使用してMono<List<String>>にします。

1.3. DispatcherHandler

Spring MVCと同等

Spring WebFluxやSpring MVCはfront controller patternで設計されており、その中心にあるWebHandlerDispatcherHandlerは、リクエスト処理に共通なアルゴリズムがあり、実際の動作は設定可能でコンポーネントに処理を委譲します。このモデルは柔軟性があり多用な処理の流れに対応できます。

DispatcherHandlerはコンポーネントに処理を委譲し、委譲先はSpringのconfigurationから取得します。また、それ自身もSpringのbeanで、このbeanを実行するcontextにアクセスするためにApplicationContextAwareを実装しています。DispatcherHandlerはbean名"webHandler"で宣言されているので、これによってWebHttpHandlerBuilderでwebHandlerが参照可能となり、WebHandler APIで解説したリクエスト処理チェーンとの結びつけを行います。

WebFluxアプリケーションのSpring configurationは基本的には以下があります。

処理のチェーンを構築するためにWebHttpHandlerBuilderにconfigurationが渡されます。

ApplicationContext context = ...
HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context);

戻り値HttpHandlererver adapterで使える状態になっています。

1.3.1. Special bean types

Spring MVCと同等

DispatcherHandlerはリクエスト処理と適切なレスポンスレンダリングに別のbeanへ委譲します。Springマネージドオブジェクトインスタンスは以下表のフレームワークのクラスの一つを実装します。SpringのWebFluxはそうしたクラスのビルトイン実装を提供しており、カスタマイズ・拡張・置換も可能です。

Bean type Explanation
HandlerMapping リクエストとハンドラのマッピング。マッピングは何らかの基準をベースにするもので、HandlerMappingの実装に依ります。アノテーションを付与するコントローラー、シンプルなURLパターンマッピング、など。
メインとなるHandlerMapping実装は、@RequestMappingを付与するメソッドに基づくRequestMappingHandlerMapping、ファンクショナルエンドポイントのルーティングに基づくRouterFunctionMapping、ハンドラにURLパスパターンを明示的に登録するSimpleUrlHandlerMapping、があります。
HandlerAdapter リクエストにマッピングされているハンドラを呼び出すDispatcherHandlerのヘルパーです。ハンドラ呼び出しの実装に無関係の処理が担当です。例えば、コントローラ呼び出し時に必要となるアノテーション解決などです。HandlerAdapterの主な用途はそういた詳細をDispatcherHandlerから分離することです。
HandlerResultHandler ハンドラ呼び出し結果を処理してレスポンスを確定します。
ビルトインのHandlerResultHandler実装は、戻り値ResponseEntityResponseEntityResultHandler@ResponseBodyメソッドのResponseBodyResultHandler、ファンクショナルエンドポイントが返すServerResponseServerResponseResultHandler、viewとmodelでレンダリングするViewResolutionResultHandler、があります。

1.3.2. Framework Config

Spring MVCと同等

DispatcherHandlerApplicationContext内で自身が必要とするbeanを検出します。アプリケーションで必要に応じてそうしたbeanを宣言します。ただし、WebFluxのJava configは高レベルのAPIを設定済みで、ここに必要なbean宣言がしてあるので、これを起点に出来ます。詳細はWebFlux Configを参照してください。

1.3.3. Processing

Spring MVCと同等

DispatcherHandlerは以下のようにリクエストを処理します。

  • HandlerMappingそれぞれにハンドラマッチングを問い合わせ、最初にマッチしたものが使われる。
  • マッチするハンドラがある場合、適当なHandlerAdapterを介してハンドラを実行し、HandlerResultで実行結果を返す。
  • HandlerResultHandlerHandlerResultが与えられます。HandlerResultHandlerは処理を完了させるためのもので、レスポンスに直接書き込んだり、レンダリングするビューを使用したりします。

*1:リンク切れてるけどflow apiとかでぐぐる

*2:Functionalをファンクショナルとも関数型とも訳しちゃっててブレまくりだけど許して

*3:while also selecting the right tool for the right jobが原文。向いてる案件に向いてるツールを使うこと、って感じですかね?

*4:よくわからん

JEP 323: Local-Variable Syntax for Lambda Parametersをテキトーに訳した

http://openjdk.java.net/jeps/323 を読んだ。

JEP 323: Local-Variable Syntax for Lambda Parameters

Author   Brian Goetz
Owner   Vicente Arturo Romero Zaldivar
Created 2017/12/08 15:15
Updated 2018/02/12 17:25
Type    Feature
Status  Targeted
Component   specification/language
Scope   SE
Discussion  amber dash dev at openjdk dot java dot net
Effort  XS
Duration    XS
Priority    3
Reviewed by Alex Buckley
Release 11
Issue   8193259
Relates to  JEP 286: Local-Variable Type Inference

Summary

暗黙的に型指定されるラムダ式(implicitly typed lambda expressions)の仮引数宣言でvarを使用可能にします。

Goals

ローカル変数宣言と暗黙的に型指定されるラムダ式の仮引数宣言でのシンタックスを揃える。

Non-goals

メソッドの仮引数などその他の変数宣言は対象外。

Motivation

ラムダ式は暗黙的な型指定が可能で、この場合はすべての仮引数は型推論されます。

(x, y) -> x.process(y)    // 暗黙的に型指定されるラムダ式

Java SE 10ではローカル変数で暗黙的な型指定が使用可能になります。

var x = new Foo();
for (var x : xs) { ... }
try (var x = ...) { ... } catch ...

ローカル変数の統一感の観点から、暗黙的に型指定されるラムダ式の仮引数でも'var'を使えるようにしたい、と我々は考えました。

(var x, var y) -> x.process(y)   // 暗黙的に型指定されるラムダ式

統一感によるメリットには、修飾子やnotably annotationsをローカル変数とラムダ式の仮引数に、簡潔さを失うことなく、適用できる点です。

@Nonnull var x = new Foo();
(@Nonnull var x, @Nullable var y) -> x.process(y)

Description

暗黙的に型指定されるラムダ式の仮引数で、予約済みの型名varを使用可能にします。

(var x, var y) -> x.process(y)

上記は以下と同等です。

(x, y) -> x.process(y)

暗黙的に型指定されるラムダ式は、すべての仮引数でvarを使うか、全く使わないか、のどちらかでなければなりません。また、varは暗黙的に型指定されるラムダ式の仮引数でだけ使用可能で、明示的に型指定されるラムダ式では引き続きすべての仮引数でマニフェスト型(manifest types)を指定します。よって、マニフェスト型の仮引数を使いつつvarも使うことは出来ません。以下の使い方は出来ません。

(var x, y) -> x.process(y)         // 暗黙的に型指定されるラムダ式で'var'と'非var'を一緒には使えない
(var x, int y) -> x.process(y)     // 暗黙的に型指定されるラムダ式で'var'とマニフェスト型を一緒には使えない

理屈の上では、上記例の下側、半明示的な型指定(semi-explicitly typed )(もしくは半暗黙的な型指定(semi-implicitly typed)、どちらに重きを置くかの見方の違いに依る)のようなラムダ式は可能に見えます。しかし、このJEPではそれはスコープ外とし、これは型推論オーバーロード解決に深く影響するためです。これが、ラムダ式ですべてマニフェスト引数型にするか全くしないか、という制限を設ける主な理由です。同様に、暗黙的に型指定されるラムダ式の引数での型推論varを使うか全く使わないか、という制限を設ける考えです。将来のJEPでこの部分推論(partial inference)の課題に取り組むかもしれません。また、省略のシンタックスの簡潔さを損ないたくないので、以下のような表現が出来るようにはしない予定です。*1

var x -> x.foo()

Alternatives

従来通りJava SE 8 の暗黙的に型指定されるラムダ式の宣言を使用し続ける。

Risks and Assumptions

このJEPの、暗黙的に型指定されるラムダ式の引数名の前にvarを追加するのは、ソース互換性に関してはノーリスクです。これはvarなし引数の型推論var型推論は同一なためです。

*1:Also, we do not wish to compromise the brevity of the shorthand syntax, so we won't allow expressions like:が原文。compromise がよくわからん

JEP 321: HTTP Client (Standard)をテキトーに訳した

http://openjdk.java.net/jeps/321 を読んだ。

JEP 321: HTTP Client (Standard)

Owner    Chris Hegarty
Created 2017/06/08 11:46
Updated 2018/01/31 16:29
Type    Feature
Status  Candidate
Component   core-libs/java.net
Scope   SE
Discussion  net dash dev at openjdk dot java dot net
Effort  M
Duration    M
Priority    2
Reviewed by Alan Bateman, Brian Goetz
Endorsed by Brian Goetz
Issue   8181784

Summary

JDK 9とJDK 10のupdatedで導入されたincubatedのHTTP Client APIを標準化します。これはJEP 110由来のものです。

Goals

JEP 110のGoalsに加え、このJEPでは以下を行います。

  • incubated APIに寄せられたフォードバックの取り込み
  • incubated APIをベースにjava.net.httpパッケージで標準APIを提供
  • incubated APIの削除

Motivation

このJEPのMotivationはJEP 110のMotivationから変更はありません。

Description

このJEPの提案内容は、JDK 9とJDK 10のupdatedでincubating APIとして導入されたHTTP Clientの標準化です。このincubating APIには重要な改善に繋がった多数のフィードバックが寄せられましたが、大筋はそのままです。このAPIにはノンブロッキングリクエストとCompletableFuturesに基づくレスポンスセマンティクスが含まれ、依存アクションをトリガするチェーン処理ができます。リクエストとレスポンスボディのBack-pressureとflow-controlはjava.util.concurrent.Flow APIreactive-streamsを介して提供されます。

JDK 9とJDK 10ではincubatingなので、その実装をほぼ完全に書き直します。現在の実装は完全に非同期です(以前のHTTP/1.1実装はブロッキング)。RX Flowの概念を実装にプッシュダウンしており、HTTP/2をサポートするのに必要だった元々の概念の多くを削除しました。データフローのトレース、ユーザレベルのリクエストパブリッシャとレスポンスサブスクライバから基底のソケットまで、が簡単になります。これらにより、各種の概念とコードの複雑さを大きく削減し、HTTP/1.1とHTTP/2の再利用の可能性(possibility of reuse)を最大化しました。

提案した標準のモジュール名はjava.net.httpで、標準パッケージ名はjava.net.httpです。

APIの詳細についてはJEP 110を参照してください。

Testing

incubated APIに対する既存のテストを修正して新しい標準APIで使えるようにする必要があります。すべてのシナリオをカバーするテストを追加する必要があり、特にHTTP/1.1とHTTP/2間のアップグレード・ダウングレードが挙げられます。

Risks and Assumptions

incubated HTTP Client APIに依存するコードは、パッケージインポートを少々、修正の必要があります。その他のincubatedの機能に差異はありません。incubatingモジュールに依存するコードは既にコンパイル・実行時に適切な警告を出しています。