Java9 の HttpClient を試す

Java9 HttpClient インキュベーターなので、この先どうなるか判らないが、
Apatch HTTPClient を使わなくて済むようになるのか?
とりあえず試してみる。
モジュール使用宣言を用意する必要があり、module-info..java を次のように用意する。

module sample{
    requires jdk.incubator.httpclient;
}

パッケージは、jdk.incubator.http だが、requires で書くのは、jdk.incubator.httpclient
import するもの

import java.net.URI;
import java.nio.charset.StandardCharsets;
import jdk.incubator.http.HttpClient;
import jdk.incubator.http.HttpRequest;
import jdk.incubator.http.HttpResponse;

まずは、同期型

try{
   HttpRequest request = HttpRequest.newBuilder(URI.create("http://www.google.com"))
   .GET()
   .build();
   HttpResponse<String> res = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1)
   .followRedirects(HttpClient.Redirect.SAME_PROTOCOL).build()
   .send(request, HttpResponse.BodyHandler.asString(StandardCharsets.UTF_8));
   // HttpResponse を参照
   res.headers().map().entrySet().forEach(e->{
      System.out.println(e.getKey() + "→" + e.getValue() );
   });
   System.out.println(res.body());
}catch(Exception e){
   e.printStackTrace();
} 

非同期型は、sendAsync を使う、HttpResponse.BodyHandlerを処理することになる。

HttpRequest request = HttpRequest.newBuilder(URI.create("http://www.google.com"))
.GET()
.build();

CompletableFuture<HttpResponse<String>> future
= HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1)
.followRedirects(HttpClient.Redirect.SAME_PROTOCOL).build()
.sendAsync(request, HttpResponse.BodyHandler.asString(StandardCharsets.UTF_8));

// CompletableFuture<HttpResponse<String>> を join する。
future.handle((r, t)->{
   System.out.println(Thread.currentThread().getName());
   r.headers().map().entrySet().forEach(e->{
      System.out.println(e + "→" + e.getValue() );
   });
   System.out.println(r.body());
   return r;
}).join();

ExecutorService  handleAsync で、、、

final ExecutorService service = Executors.newFixedThreadPool(4);
HttpRequest request = HttpRequest.newBuilder(URI.create("http://www.google.com"))
.GET()
.build();

HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1)
.followRedirects(HttpClient.Redirect.SAME_PROTOCOL)
.executor(service)
.build()
.sendAsync(request, HttpResponse.BodyHandler.asString(StandardCharsets.UTF_8))
.handleAsync((r, t)->{
   System.out.println(Thread.currentThread().getName());
   r.headers().map().entrySet().forEach(e->{
      System.out.println(e + "→" + e.getValue() );
   });
   System.out.println(r.body());
   return r;
}).join();
service.shutdown();

同じリクエスト、ExecutorService  whenCompleteAsync で、例外発生時も捕捉する場合

final ExecutorService service = Executors.newFixedThreadPool(4);
HttpRequest request = HttpRequest.newBuilder(URI.create("http://www.google.com"))
.GET()
.build();

HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1)
.followRedirects(HttpClient.Redirect.SAME_PROTOCOL)
.executor(service)
.build()
.sendAsync(request, HttpResponse.BodyHandler.asString(StandardCharsets.UTF_8))
.whenCompleteAsync((r, x)->{
   r.headers().map().entrySet().forEach(e->{
      System.out.println(e + "→" + e.getValue() );
   });
   System.out.println(r.body());
   Optional.ofNullable(x).ifPresent(t->t.printStackTrace());
}).join();

これは、java.util.concurrent.CompletionException でラップされた例外を捕捉することになる。
URL が誤ってれば、java.nio.channels.UnresolvedAddressExceptionなどがラップされて捕捉される。

サンプルとしてもっと簡単に、返されるレスポンス BODYだけを標準出力するなら、、、

HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1)
.followRedirects(HttpClient.Redirect.SAME_PROTOCOL)
.executor(service)
.build()
.sendAsync(request, HttpResponse.BodyHandler.asString(StandardCharsets.UTF_8))
.thenApply(HttpResponse::body)
.thenAccept(System.out::println)
.join();

結果 body をファイル出力するなら。 HttpResponse.BodyHandler.asFile を使う。

HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1)
.followRedirects(HttpClient.Redirect.SAME_PROTOCOL)
.executor(service)
.build()
.sendAsync(request, HttpResponse.BodyHandler.asFile(FileSystems.getDefault().getPath("c:/work/test")))
.thenApply(HttpResponse::body)
//.thenAccept(System.out::println) /* ファイル出力した時の path を標準出力 */ 
.join();

↑ //.thenAccept(System.out::println)  は、ファイル出力もするけど、標準出力もするようにもできる。