こちらのエントリの、続編的なやつです。
JavaでのHttpClientサンプル
http://d.hatena.ne.jp/Kazuhira/20131026/1382796711
これを書いた後に、いくつか気になるHTTPクライアントライブラリを見てはいたのですが、ずっと触らないままだったのでこれを機にと思いまして。
こちらのエントリに、触発されたというのもあります。
google-http-java-client 入門
http://vividcode.hatenablog.com/entry/java/google-http-java-client
せっかくなので、google-http-java-client含めて試していってみます。
なお、サンプルとしては前回同様
サンプルとしては、
- 簡単なGET/POSTを行う
- HTTPステータスコードが200かどうか判定する
- レスポンスをコンソールに出力する
として書いていきます。
また、通信相手のHTTPサーバも同様にGroovyで書いたものを使用しています。これからJavaコードで記述するアクセス先に対して、curlでリクエストを投げるとこういう結果になります。
## GET $ curl http://localhost:8080/get?param=value Accessed URL = /get?param=value Accessed Method = GET Accessed Date = Sat Nov 15 20:41:08 JST 2014 ## POST $ curl -X POST http://localhost:8080/post -d '<<EOF POST BODY Hello Http Server!! EOF ' Accessed URL = /post Accessed Method = POST Accessed Date = Sat Nov 15 20:45:22 JST 2014 Request Body<< <<EOF POST BODY Hello Http Server!! EOF >>
コード例にはテストコードを使用しますので、JUnitとAssertJを使います。
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>1.7.0</version> <scope>test</scope> </dependency>
書くテストコードでは、以下のimport文が入っているものとします。
import static org.assertj.core.api.Assertions.*; import org.junit.Test;
では、いってみます。
なお、今回ご紹介するHTTPクライアントは、いずれも何らかのHTTP通信ライブラリの上に構築されたものになります。
Async Http Client
今回、1番試してみたかったHTTPクライアントです。他のJVM言語で使われているHTTPクライアントの内部でも使われたりしている、非同期HTTP通信を行うライブラリです。
Async Http Client
https://github.com/AsyncHttpClient/async-http-client
https://asynchttpclient.github.io/async-http-client/
Webサイトのドキュメントは、古い点があるので注意してください。
Maven依存関係。
<dependency> <groupId>com.ning</groupId> <artifactId>async-http-client</artifactId> <version>1.8.14</version> </dependency>
import文。
import java.io.IOException; import java.util.concurrent.ExecutionException; import com.ning.http.client.AsyncCompletionHandler; import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.Response;
GETの例。
@Test public void testAsynHttpClientGet() { try (AsyncHttpClient client = new AsyncHttpClient()) { Response response = client .prepareGet("http://localhost:8080/get?param=value") .execute() .get(); assertThat(response.getStatusCode()) .isEqualTo(200); assertThat(response.getResponseBody("UTF-8")) .contains("Accessed URL = /get?param=value", "Accessed Method = GET"); } catch (IOException | InterruptedException | ExecutionException e) { e.printStackTrace(); } }
POSTの例。
@Test public void testAsynHttpClientPost() { try (AsyncHttpClient client = new AsyncHttpClient()) { StringBuilder builder = new StringBuilder(); builder.append("POST Body"); builder.append("\r\n"); builder.append("Hello Http Server!!"); builder.append("\r\n"); Response response = client .preparePost("http://localhost:8080/post") .setBody(builder.toString()) .execute() .get(); assertThat(response.getStatusCode()) .isEqualTo(200); assertThat(response.getResponseBody("UTF-8")) .contains("Accessed URL = /post", "Accessed Method = POST", "Request Body<<", "POST Body", "Hello Http Server!!", ">>"); } catch (IOException | InterruptedException | ExecutionException e) { e.printStackTrace(); } }
いずれも、executeメソッドを呼び出した後にgetメソッドの呼び出しが入っていますが、これはFuture#getになります。なので、いったんFutureで受けて後で結果を取り出すことも可能ということになります。
AsyncHttpClientは、Closeableです。
AsyncHandlerインターフェースというものを使って、コールバックを受けるスタイルで書くこともできます。以下のコードでは、AsyncHandlerインターフェースの拡張である、AsyncCompleteHandlerを使用しています。
@Test public void testAsynHttpClientGetWithHandler() { try (AsyncHttpClient client = new AsyncHttpClient()) { String responseData = client .prepareGet("http://localhost:8080/get?param=value") .execute(new AsyncCompletionHandler<String>() { @Override public String onCompleted(Response response) throws Exception { return response.getResponseBody("UTF-8"); } }) .get(); assertThat(responseData) .contains("Accessed URL = /get?param=value", "Accessed Method = GET"); } catch (IOException | InterruptedException | ExecutionException e) { e.printStackTrace(); } }
また、Async Http Clientは、以下のHTTP通信ライブラリを基盤として使用することができます。
- Netty(デフォルト)
- Grizzly
- Commons HttpClient
- java.net.URL
デフォルトはNettyで、Commons HttpClientなどのライブラリを使用する場合は、オプションとなっている依存関係の追加が必要です。
例えば、Commons HttpClientを使う場合は、以下の依存関係と
<!-- Async Http Client, using Commons Http Client--> <dependency> <groupId>commons-httpclient</groupId> <artifactId>commons-httpclient</artifactId> <version>3.1</version> </dependency>
import文を追加して
// for Async Http Client, using Apache Http Client 3.1 import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.AsyncHttpProvider; import com.ning.http.client.providers.apache.ApacheAsyncHttpProvider;
このようなコードになります。
@Test public void testAsynHttpClientGetUsingCommonsHttpClient() { AsyncHttpClientConfig.Builder builder = new AsyncHttpClientConfig.Builder(); AsyncHttpProvider provider = new ApacheAsyncHttpProvider(builder.build()); try (AsyncHttpClient client = new AsyncHttpClient(provider)) { Response response = client .prepareGet("http://localhost:8080/get?param=value") .execute() .get(); assertThat(response.getStatusCode()) .isEqualTo(200); assertThat(response.getResponseBody("UTF-8")) .contains("Accessed URL = /get?param=value", "Accessed Method = GET"); } catch (IOException | InterruptedException | ExecutionException e) { e.printStackTrace(); } }
この部分が、少し長くなりましたね。
AsyncHttpClientConfig.Builder builder = new AsyncHttpClientConfig.Builder(); AsyncHttpProvider provider = new ApacheAsyncHttpProvider(builder.build()); try (AsyncHttpClient client = new AsyncHttpClient(provider)) {
API的には、なかなか使いやすそうなHTTPクライアントライブラリだと思います。
依存ライブラリが古い(Nettyは3系、Commons HttpClient使用)なのですが、GitHubのmasterはすでにパッケージ名から変わった2系が進んでいるようです。ただ、Providerの部分はNettyとGrizzlyしかなさそうでしたが。
まあ、通信基盤はNettyで良いと思うので、2系の登場についても待っていようと思います。
google-http-java-client
こちらは、前述のブログを見るまで存在を知らなかった、GoogleによるHTTPクライアントライブラリです。参照先の説明が詳しいので、詳細はそちらへ…。
google-http-java-client
https://code.google.com/p/google-http-java-client/
Apache HttpComponents、java.net.URL、そしてGoogle App Engine向けの実装を含んだHTTPクライアントのようです。
Apache HttpComponentsは、少し古くて4.0.1に依存しています(google-http-java-clientの1.19.0時点)。
Maven依存関係。
<dependency> <groupId>com.google.http-client</groupId> <artifactId>google-http-client</artifactId> <version>1.19.0</version> </dependency>
import文。
import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpRequest; import com.google.api.client.http.HttpRequestFactory; import com.google.api.client.http.HttpResponse; import com.google.api.client.http.HttpTransport; import com.google.api.client.http.InputStreamContent; import com.google.api.client.http.apache.ApacheHttpTransport;
ここでは、Apache HttpComponentsを使うことにします。
GETの例。
@Test public void testGoogleHttpJavaClientGet() { HttpTransport transport = new ApacheHttpTransport(); HttpRequestFactory factory = transport.createRequestFactory(); GenericUrl url = new GenericUrl("http://localhost:8080/get?param=value"); try { HttpRequest request = factory.buildGetRequest(url); HttpResponse response = request.execute(); try { assertThat(response.getStatusCode()) .isEqualTo(200); assertThat(response.parseAsString()) .contains("Accessed URL = /get?param=value", "Accessed Method = GET"); } finally { response.disconnect(); } } catch (IOException e) { e.printStackTrace(); } finally { try { transport.shutdown(); } catch (IOException e ) { e.printStackTrace(); } } }
POSTの例。
@Test public void testGoogleHttpJavaClientPost() { HttpTransport transport = new ApacheHttpTransport(); HttpRequestFactory factory = transport.createRequestFactory(); GenericUrl url = new GenericUrl("http://localhost:8080/post"); try { StringBuilder builder = new StringBuilder(); builder.append("POST Body"); builder.append("\r\n"); builder.append("Hello Http Server!!"); builder.append("\r\n"); HttpRequest request = factory.buildPostRequest(url, new InputStreamContent("text/plain", new ByteArrayInputStream(builder.toString().getBytes(StandardCharsets.UTF_8)))); HttpResponse response = request.execute(); try { assertThat(response.getStatusCode()) .isEqualTo(200); assertThat(response.parseAsString()) .contains("Accessed URL = /post", "Accessed Method = POST", "Request Body<<", "POST Body", "Hello Http Server!!", ">>"); } finally { response.disconnect(); } } catch (IOException e) { e.printStackTrace(); } finally { try { transport.shutdown(); } catch (IOException e ) { e.printStackTrace(); } } }
HttpTransport#shutdownは、お作法的に呼び出した方がよさそうですね。特にApache HttpComponentsを使った場合は、これの呼び出しがHttpClient#getConnectionManager#shutdownになっていますので。
jcabi-http
最後は、ちょっと前に見かけたもので、jcabi-httpです。こちらも、それほど情報を見たことはないですが…。
Fluent HTTP Client
http://http.jcabi.com/
通信基盤はApache HttpComponentsとjava.net.URLで、HTTP通信をFluentに書けるライブラリです。
Maven依存関係。
<dependency>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-http</artifactId>
<version>1.9.2</version>
</dependency>
引っ張られてくる依存関係が、上記2つのライブラリに比べて多めなのがちょっと気になるところだったりします…。
import文。
import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import com.jcabi.http.Request; import com.jcabi.http.Response; import com.jcabi.http.request.JdkRequest;
まずは、JdkRequest(java.net.URL)を使ってみます。
GETの例。
@Test public void testJcabiHttpGet() { try { Response response = new JdkRequest("http://localhost:8080/get?param=value") .method(Request.GET) .fetch(); assertThat(response.status()) .isEqualTo(200); assertThat(response.body()) .contains("Accessed URL = /get?param=value", "Accessed Method = GET"); } catch (IOException e) { e.printStackTrace(); } }
POSTの例。
@Test public void testJcabiHttpPost() { try { StringBuilder builder = new StringBuilder(); builder.append("POST Body"); builder.append("\r\n"); builder.append("Hello Http Server!!"); builder.append("\r\n"); Response response = new JdkRequest("http://localhost:8080/post") .method(Request.POST) .fetch(new ByteArrayInputStream(builder.toString().getBytes(StandardCharsets.UTF_8))); assertThat(response.status()) .isEqualTo(200); assertThat(response.body()) .contains("Accessed URL = /post", "Accessed Method = POST", "Request Body<<", "POST Body", "Hello Http Server!!", ">>"); } catch (IOException e) { e.printStackTrace(); } }
特にクローズ関係の呼び出しがないですが、Request#fetchの呼び出し時に、クローズ処理が入っているようです。
続いて、通信ライブラリをApache HttpComponentsに切り替えてみます。Maven依存関係に以下を追加。
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> <version>4.3.3</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.3.3</version> </dependency>
import文に以下を追加。
import com.jcabi.http.request.ApacheRequest;
GETの例だと、このようになります。
@Test public void testJcabiHttpGetUsingApacheHttpComponents() { try { Response response = new ApacheRequest("http://localhost:8080/get?param=value") .method(Request.GET) .fetch(); assertThat(response.status()) .isEqualTo(200); assertThat(response.body()) .contains("Accessed URL = /get?param=value", "Accessed Method = GET"); } catch (IOException e) { e.printStackTrace(); } }
まあ、ここが変わっただけですが。
new ApacheRequest("http://localhost:8080/get?param=value")