Apache HttpClient
Apache Software Foundation開発のJava向け成熟したHTTPクライアントライブラリ。企業環境で実績豊富なHTTP/1.1完全実装、認証、プロキシサポート、SSL/TLS、接続管理機能を提供。エンタープライズグレードの信頼性と安定性。
GitHub概要
apache/httpcomponents-client
Mirror of Apache HttpClient
スター1,514
ウォッチ103
フォーク982
作成日:2009年5月21日
言語:Java
ライセンス:Apache License 2.0
トピックス
httpcomponents
スター履歴
データ取得日時: 2025/10/22 04:10
ライブラリ
Apache HttpClient
概要
Apache HttpClientは「Java向けの包括的HTTPクライアントライブラリ」として開発された、Apache Software Foundationの長い歴史を持つオープンソースプロジェクトです。エンタープライズ環境での堅牢なHTTP通信を目的として設計され、豊富な認証メカニズム、プロキシサポート、SSL/TLS処理、接続管理、キャッシング機能を提供。JavaのHTTPクライアントライブラリとして20年以上の実績を誇り、多くの企業システムや大規模アプリケーションで採用されています。
詳細
Apache HttpClient 2025年版は企業システムのHTTP通信基盤として更なる成熟を達成し、Java生態系において重要な地位を維持しています。HTTP/1.1完全対応、高度なSSL/TLS処理、多様な認証プロトコル(Basic、Digest、NTLM、Kerberos)、詳細な接続プール管理により、ミッションクリティカルなシステムでの安定性を確保。スレッドセーフ設計、豊富な設定オプション、拡張可能なアーキテクチャにより、複雑な企業要件に対応可能な高機能HTTPクライアントとして発展を続けています。
主な特徴
- エンタープライズ級機能: 企業システムに必要な豊富な認証・セキュリティ機能
- 接続管理: 高度な接続プーリングとライフサイクル管理
- 多様な認証: Basic、Digest、NTLM、Kerberos等の幅広い認証方式
- SSL/TLS対応: 詳細なSSL設定とクライアント証明書サポート
- プロキシサポート: HTTP、HTTPS、SOCKSプロキシの完全対応
- スレッドセーフ: 並行処理環境での安全な利用
メリット・デメリット
メリット
- 20年以上の実績による高い信頼性と安定性
- エンタープライズ環境で必要な豊富な認証・セキュリティ機能
- 詳細な設定オプションによる柔軟なカスタマイズ性
- スレッドセーフ設計による並行処理での安全性
- Apache Foundationによる継続的なメンテナンスとサポート
- 大量の同時接続を効率的に管理する接続プール機能
デメリット
- 学習コストが高く初心者には複雑
- モダンなJavaライブラリと比較して冗長なAPI設計
- HTTP/2対応が限定的で最新仕様への対応が遅れ気味
- パフォーマンスがOkHttpなど最新ライブラリより劣る場合あり
- 非同期処理の機能が他ライブラリと比較して制限的
- 設定項目が多すぎて適切な設定が困難
参考ページ
書き方の例
インストールと基本セットアップ
<!-- Maven (pom.xml) -->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3</version>
</dependency>
<!-- ログ機能追加(推奨) -->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5-fluent</artifactId>
<version>5.3</version>
</dependency>
<!-- キャッシュ機能(オプション) -->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5-cache</artifactId>
<version>5.3</version>
</dependency>
// Gradle (build.gradle)
dependencies {
implementation 'org.apache.httpcomponents.client5:httpclient5:5.3'
implementation 'org.apache.httpcomponents.client5:httpclient5-fluent:5.3'
implementation 'org.apache.httpcomponents.client5:httpclient5-cache:5.3' // オプション
}
基本的なリクエスト(GET/POST/PUT/DELETE)
import org.apache.hc.client5.http.classic.methods.*;
import org.apache.hc.client5.http.impl.classic.*;
import org.apache.hc.core5.http.*;
import org.apache.hc.core5.http.io.entity.*;
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
public class ApacheHttpClientExample {
// 基本的なGETリクエスト
public void basicGetRequest() throws Exception {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpGet request = new HttpGet("https://api.example.com/users");
// ヘッダーの設定
request.setHeader("Accept", "application/json");
request.setHeader("User-Agent", "MyApp/1.0 (Apache HttpClient)");
try (CloseableHttpResponse response = httpClient.execute(request)) {
System.out.println("ステータス: " + response.getCode());
System.out.println("Content-Type: " + response.getFirstHeader("Content-Type"));
// レスポンスボディの取得
HttpEntity entity = response.getEntity();
if (entity != null) {
String responseBody = EntityUtils.toString(entity);
System.out.println("レスポンス: " + responseBody);
}
}
}
}
// POSTリクエスト(JSON送信)
public void postJsonRequest() throws Exception {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost request = new HttpPost("https://api.example.com/users");
// JSONデータの設定
String jsonData = "{\"name\": \"田中太郎\", \"email\": \"[email protected]\"}";
StringEntity entity = new StringEntity(jsonData, ContentType.APPLICATION_JSON);
request.setEntity(entity);
// ヘッダーの設定
request.setHeader("Authorization", "Bearer your-jwt-token");
request.setHeader("Accept", "application/json");
try (CloseableHttpResponse response = httpClient.execute(request)) {
if (response.getCode() >= 200 && response.getCode() < 300) {
String responseBody = EntityUtils.toString(response.getEntity());
System.out.println("作成されたユーザー: " + responseBody);
} else {
System.out.println("POSTリクエスト失敗: " + response.getCode());
}
}
}
}
// PUTリクエスト(更新)
public void putRequest() throws Exception {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPut request = new HttpPut("https://api.example.com/users/123");
String jsonData = "{\"name\": \"田中次郎\", \"email\": \"[email protected]\"}";
StringEntity entity = new StringEntity(jsonData, ContentType.APPLICATION_JSON);
request.setEntity(entity);
request.setHeader("Authorization", "Bearer your-jwt-token");
try (CloseableHttpResponse response = httpClient.execute(request)) {
if (response.getCode() == 200) {
System.out.println("ユーザー更新成功");
}
}
}
}
// DELETEリクエスト
public void deleteRequest() throws Exception {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpDelete request = new HttpDelete("https://api.example.com/users/123");
request.setHeader("Authorization", "Bearer your-jwt-token");
try (CloseableHttpResponse response = httpClient.execute(request)) {
if (response.getCode() == 204) {
System.out.println("ユーザー削除成功");
}
}
}
}
// フォームデータの送信
public void submitFormData() throws Exception {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost request = new HttpPost("https://api.example.com/login");
// フォームパラメータの設定
List<NameValuePair> parameters = new ArrayList<>();
parameters.add(new BasicNameValuePair("username", "testuser"));
parameters.add(new BasicNameValuePair("password", "secret123"));
parameters.add(new BasicNameValuePair("remember", "true"));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parameters);
request.setEntity(entity);
try (CloseableHttpResponse response = httpClient.execute(request)) {
String responseBody = EntityUtils.toString(response.getEntity());
System.out.println("ログインレスポンス: " + responseBody);
}
}
}
// Fluent APIの使用(簡潔な記述)
public void fluentApiExample() throws Exception {
import org.apache.hc.client5.http.fluent.Request;
// 簡潔なGETリクエスト
String response = Request.get("https://api.example.com/users")
.addHeader("Accept", "application/json")
.execute()
.returnContent()
.asString();
System.out.println("Fluent APIレスポンス: " + response);
// 簡潔なPOSTリクエスト
String postResponse = Request.post("https://api.example.com/users")
.bodyString("{\"name\": \"田中太郎\"}", ContentType.APPLICATION_JSON)
.addHeader("Authorization", "Bearer token")
.execute()
.returnContent()
.asString();
System.out.println("Fluent POST: " + postResponse);
}
}
高度な設定とカスタマイズ(ヘッダー、認証、タイムアウト等)
import org.apache.hc.client5.http.auth.*;
import org.apache.hc.client5.http.config.*;
import org.apache.hc.client5.http.impl.auth.*;
import org.apache.hc.client5.http.impl.classic.*;
import org.apache.hc.client5.http.impl.io.*;
import org.apache.hc.client5.http.socket.*;
import org.apache.hc.client5.http.ssl.*;
import org.apache.hc.core5.http.config.*;
import org.apache.hc.core5.pool.*;
import org.apache.hc.core5.util.Timeout;
public class AdvancedHttpClientExample {
// カスタム設定のHttpClientインスタンス作成
public CloseableHttpClient createCustomHttpClient() {
// 接続プールの設定
PoolingHttpClientConnectionManager connectionManager =
PoolingHttpClientConnectionManagerBuilder.create()
.setMaxConnTotal(100) // 最大接続数
.setMaxConnPerRoute(20) // ルート毎の最大接続数
.setConnectionTimeToLive(TimeValue.ofMinutes(10)) // 接続生存時間
.build();
// タイムアウト設定
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(Timeout.ofSeconds(5)) // 接続要求タイムアウト
.setConnectTimeout(Timeout.ofSeconds(10)) // 接続タイムアウト
.setResponseTimeout(Timeout.ofSeconds(30)) // レスポンスタイムアウト
.setCookieSpec(StandardCookieSpec.STRICT) // Cookie仕様
.setRedirectsEnabled(true) // リダイレクト許可
.setMaxRedirects(5) // 最大リダイレクト数
.build();
// HTTPクライアントの構築
return HttpClients.custom()
.setConnectionManager(connectionManager)
.setDefaultRequestConfig(requestConfig)
.setRetryStrategy(DefaultHttpRequestRetryStrategy.INSTANCE)
.setRedirectStrategy(DefaultRedirectStrategy.INSTANCE)
.setUserAgent("MyApp/1.0 (Apache HttpClient 5.3)")
.build();
}
// Basic認証の設定
public void basicAuthenticationExample() throws Exception {
// 認証情報を格納
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(
new AuthScope("api.example.com", 443),
new UsernamePasswordCredentials("username", "password".toCharArray())
);
try (CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultCredentialsProvider(credentialsProvider)
.build()) {
HttpGet request = new HttpGet("https://api.example.com/protected");
try (CloseableHttpResponse response = httpClient.execute(request)) {
String responseBody = EntityUtils.toString(response.getEntity());
System.out.println("認証済みレスポンス: " + responseBody);
}
}
}
// Digest認証の設定
public void digestAuthenticationExample() throws Exception {
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(
AuthScope.ANY,
new UsernamePasswordCredentials("username", "password".toCharArray())
);
// Digest認証設定
AuthSchemeProvider digestSchemeProvider = new DigestSchemeProvider();
Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
.register(StandardAuthScheme.DIGEST, digestSchemeProvider)
.build();
try (CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultCredentialsProvider(credentialsProvider)
.setDefaultAuthSchemeRegistry(authSchemeRegistry)
.build()) {
HttpGet request = new HttpGet("https://api.example.com/digest-auth");
try (CloseableHttpResponse response = httpClient.execute(request)) {
System.out.println("Digest認証レスポンス: " + response.getCode());
}
}
}
// プロキシ設定
public void proxyExample() throws Exception {
// HTTPプロキシの設定
HttpHost proxy = new HttpHost("proxy.example.com", 8080, "http");
// プロキシ認証(必要な場合)
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(
new AuthScope(proxy),
new UsernamePasswordCredentials("proxy-user", "proxy-pass".toCharArray())
);
RequestConfig requestConfig = RequestConfig.custom()
.setProxy(proxy)
.build();
try (CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultCredentialsProvider(credentialsProvider)
.setDefaultRequestConfig(requestConfig)
.build()) {
HttpGet request = new HttpGet("https://api.example.com/data");
try (CloseableHttpResponse response = httpClient.execute(request)) {
String responseBody = EntityUtils.toString(response.getEntity());
System.out.println("プロキシ経由レスポンス: " + responseBody);
}
}
}
// SSL/TLS設定
public void sslConfiguration() throws Exception {
// カスタムSSLコンテキスト
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(null, (certificate, authType) -> true) // 全証明書を信頼(開発用のみ)
.build();
// SSL設定
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
sslContext,
new String[]{"TLSv1.2", "TLSv1.3"},
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier()
);
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", sslSocketFactory)
.register("http", new PlainConnectionSocketFactory())
.build();
PoolingHttpClientConnectionManager connectionManager =
PoolingHttpClientConnectionManagerBuilder.create()
.setConnectionSocketFactoryRegistry(socketFactoryRegistry)
.build();
try (CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(connectionManager)
.build()) {
HttpGet request = new HttpGet("https://secure-api.example.com/data");
try (CloseableHttpResponse response = httpClient.execute(request)) {
System.out.println("SSL通信成功: " + response.getCode());
}
}
}
// Cookie処理
public void cookieHandling() throws Exception {
// Cookieストアの作成
CookieStore cookieStore = new BasicCookieStore();
// カスタムCookieの追加
BasicClientCookie cookie = new BasicClientCookie("session_id", "abc123");
cookie.setDomain("api.example.com");
cookie.setPath("/");
cookieStore.addCookie(cookie);
try (CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultCookieStore(cookieStore)
.build()) {
// Cookieが自動的に送信される
HttpGet request = new HttpGet("https://api.example.com/session-data");
try (CloseableHttpResponse response = httpClient.execute(request)) {
String responseBody = EntityUtils.toString(response.getEntity());
System.out.println("Cookieありレスポンス: " + responseBody);
// 新しいCookieの確認
for (Cookie receivedCookie : cookieStore.getCookies()) {
System.out.println("受信Cookie: " + receivedCookie.getName() + "=" + receivedCookie.getValue());
}
}
}
}
}
エラーハンドリングとリトライ機能
import org.apache.hc.client5.http.HttpRequestRetryStrategy;
import org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.util.TimeValue;
public class ErrorHandlingExample {
// 包括的なエラーハンドリング
public String safeHttpRequest(String url) {
try (CloseableHttpClient httpClient = createRobustHttpClient()) {
HttpGet request = new HttpGet(url);
try (CloseableHttpResponse response = httpClient.execute(request)) {
int statusCode = response.getCode();
if (statusCode >= 200 && statusCode < 300) {
return EntityUtils.toString(response.getEntity());
} else {
handleHttpError(response);
return null;
}
} catch (ConnectTimeoutException e) {
System.err.println("接続タイムアウト: " + e.getMessage());
System.err.println("ネットワーク接続を確認してください");
} catch (SocketTimeoutException e) {
System.err.println("読み込みタイムアウト: " + e.getMessage());
System.err.println("サーバーからの応答が遅れています");
} catch (UnknownHostException e) {
System.err.println("ホストが見つかりません: " + e.getMessage());
System.err.println("URLまたはネットワーク接続を確認してください");
} catch (IOException e) {
System.err.println("ネットワークエラー: " + e.getMessage());
}
} catch (Exception e) {
System.err.println("予期しないエラー: " + e.getMessage());
e.printStackTrace();
}
return null;
}
private void handleHttpError(CloseableHttpResponse response) throws IOException {
int statusCode = response.getCode();
String reasonPhrase = response.getReasonPhrase();
String errorBody = EntityUtils.toString(response.getEntity());
System.err.println("HTTPエラー: " + statusCode + " " + reasonPhrase);
switch (statusCode) {
case 400:
System.err.println("不正なリクエスト: パラメータを確認してください");
break;
case 401:
System.err.println("認証が必要です: 認証情報を確認してください");
break;
case 403:
System.err.println("アクセス拒否: 権限を確認してください");
break;
case 404:
System.err.println("リソースが見つかりません");
break;
case 429:
String retryAfter = response.getFirstHeader("Retry-After").getValue();
System.err.println("レート制限: " + (retryAfter != null ? retryAfter + "秒後に再試行" : "時間をおいて再試行"));
break;
case 500:
System.err.println("サーバー内部エラー");
break;
case 502:
System.err.println("不正なゲートウェイ: サーバーが一時的に利用不可");
break;
case 503:
System.err.println("サービス利用不可: サーバーが過負荷状態");
break;
default:
System.err.println("HTTPエラー詳細: " + errorBody);
}
}
// カスタムリトライ戦略
public static class CustomRetryStrategy implements HttpRequestRetryStrategy {
private final int maxRetries;
private final TimeValue retryInterval;
public CustomRetryStrategy(int maxRetries, TimeValue retryInterval) {
this.maxRetries = maxRetries;
this.retryInterval = retryInterval;
}
@Override
public boolean retryRequest(HttpRequest request, IOException exception, int execCount, HttpContext context) {
if (execCount > maxRetries) {
return false;
}
// 特定の例外でリトライ
if (exception instanceof ConnectTimeoutException ||
exception instanceof SocketTimeoutException ||
exception instanceof NoHttpResponseException) {
System.out.println("リトライ実行 (" + execCount + "/" + maxRetries + "): " + exception.getMessage());
return true;
}
return false;
}
@Override
public boolean retryRequest(HttpResponse response, int execCount, HttpContext context) {
if (execCount > maxRetries) {
return false;
}
int statusCode = response.getCode();
// 特定のステータスコードでリトライ
if (statusCode == 429 || // Too Many Requests
statusCode == 500 || // Internal Server Error
statusCode == 502 || // Bad Gateway
statusCode == 503 || // Service Unavailable
statusCode == 504) { // Gateway Timeout
System.out.println("HTTPステータスによるリトライ (" + execCount + "/" + maxRetries + "): " + statusCode);
return true;
}
return false;
}
@Override
public TimeValue getRetryInterval(HttpResponse response, int execCount, HttpContext context) {
// Retry-Afterヘッダーを確認
Header retryAfterHeader = response.getFirstHeader("Retry-After");
if (retryAfterHeader != null) {
try {
int retryAfterSeconds = Integer.parseInt(retryAfterHeader.getValue());
return TimeValue.ofSeconds(retryAfterSeconds);
} catch (NumberFormatException e) {
// パースできない場合はデフォルト値を使用
}
}
// 指数バックオフ
long delayMillis = retryInterval.toMilliseconds() * (long) Math.pow(2, execCount - 1);
return TimeValue.ofMilliseconds(Math.min(delayMillis, 30000)); // 最大30秒
}
}
// 堅牢なHttpClientの作成
private CloseableHttpClient createRobustHttpClient() {
return HttpClients.custom()
.setRetryStrategy(new CustomRetryStrategy(3, TimeValue.ofSeconds(1)))
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectTimeout(Timeout.ofSeconds(10))
.setResponseTimeout(Timeout.ofSeconds(30))
.build())
.build();
}
// 非同期エラーハンドリング
public void asyncErrorHandling() throws Exception {
CloseableHttpAsyncClient asyncClient = HttpAsyncClients.createDefault();
asyncClient.start();
try {
HttpGet request = new HttpGet("https://api.example.com/data");
Future<SimpleHttpResponse> future = asyncClient.execute(
SimpleRequestBuilder.copy(request).build(),
new FutureCallback<SimpleHttpResponse>() {
@Override
public void completed(SimpleHttpResponse response) {
System.out.println("非同期リクエスト完了: " + response.getCode());
}
@Override
public void failed(Exception ex) {
System.err.println("非同期リクエスト失敗: " + ex.getMessage());
}
@Override
public void cancelled() {
System.out.println("非同期リクエストがキャンセルされました");
}
}
);
// 結果を待機(タイムアウト付き)
SimpleHttpResponse response = future.get(30, TimeUnit.SECONDS);
System.out.println("非同期レスポンス: " + response.getBodyText());
} finally {
asyncClient.close();
}
}
}
並行処理と非同期リクエスト
import org.apache.hc.client5.http.async.methods.*;
import org.apache.hc.client5.http.impl.async.*;
import org.apache.hc.core5.concurrent.FutureCallback;
import java.util.concurrent.*;
import java.util.stream.Collectors;
public class ConcurrentRequestsExample {
// 並列同期リクエスト
public List<String> fetchMultipleUrlsSync(List<String> urls) {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
// ExecutorServiceを使用した並列処理
ExecutorService executor = Executors.newFixedThreadPool(10);
try {
List<Future<String>> futures = urls.stream()
.map(url -> executor.submit(() -> {
try {
HttpGet request = new HttpGet(url);
try (CloseableHttpResponse response = httpClient.execute(request)) {
if (response.getCode() == 200) {
return EntityUtils.toString(response.getEntity());
} else {
return "Error: " + response.getCode();
}
}
} catch (Exception e) {
return "Exception: " + e.getMessage();
}
}))
.collect(Collectors.toList());
// 結果の収集
return futures.stream()
.map(future -> {
try {
return future.get(30, TimeUnit.SECONDS);
} catch (Exception e) {
return "Timeout or error: " + e.getMessage();
}
})
.collect(Collectors.toList());
} finally {
executor.shutdown();
}
} catch (IOException e) {
throw new RuntimeException("HTTPクライアント作成エラー", e);
}
}
// 非同期並列リクエスト
public CompletableFuture<List<String>> fetchMultipleUrlsAsync(List<String> urls) {
CloseableHttpAsyncClient asyncClient = HttpAsyncClients.createDefault();
asyncClient.start();
List<CompletableFuture<String>> futures = urls.stream()
.map(url -> {
CompletableFuture<String> future = new CompletableFuture<>();
SimpleHttpRequest request = SimpleRequestBuilder.get(url).build();
asyncClient.execute(request, new FutureCallback<SimpleHttpResponse>() {
@Override
public void completed(SimpleHttpResponse response) {
try {
if (response.getCode() == 200) {
future.complete(response.getBodyText());
} else {
future.complete("Error: " + response.getCode());
}
} catch (Exception e) {
future.complete("Response error: " + e.getMessage());
}
}
@Override
public void failed(Exception ex) {
future.complete("Failed: " + ex.getMessage());
}
@Override
public void cancelled() {
future.complete("Cancelled");
}
});
return future;
})
.collect(Collectors.toList());
// 全ての非同期処理の完了を待機
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenApply(v -> futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList()))
.whenComplete((result, throwable) -> {
try {
asyncClient.close();
} catch (IOException e) {
System.err.println("非同期クライアント終了エラー: " + e.getMessage());
}
});
}
// ページネーション対応
public List<String> fetchAllPages(String baseUrl, int maxPages) throws Exception {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
ExecutorService executor = Executors.newFixedThreadPool(5);
List<Future<String>> futures = new ArrayList<>();
for (int page = 1; page <= maxPages; page++) {
final int currentPage = page;
Future<String> future = executor.submit(() -> {
try {
String url = baseUrl + "?page=" + currentPage + "&limit=20";
HttpGet request = new HttpGet(url);
try (CloseableHttpResponse response = httpClient.execute(request)) {
if (response.getCode() == 200) {
String responseBody = EntityUtils.toString(response.getEntity());
System.out.println("ページ " + currentPage + " 取得完了");
return responseBody;
} else {
System.err.println("ページ " + currentPage + " エラー: " + response.getCode());
return null;
}
}
} catch (Exception e) {
System.err.println("ページ " + currentPage + " 例外: " + e.getMessage());
return null;
}
});
futures.add(future);
}
List<String> results = new ArrayList<>();
for (Future<String> future : futures) {
try {
String result = future.get(60, TimeUnit.SECONDS);
if (result != null) {
results.add(result);
}
} catch (TimeoutException e) {
System.err.println("ページ取得タイムアウト");
}
}
executor.shutdown();
return results;
}
}
// ファイルのストリーミングダウンロード
public void downloadLargeFile(String url, String filename) throws Exception {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpGet request = new HttpGet(url);
try (CloseableHttpResponse response = httpClient.execute(request)) {
if (response.getCode() != 200) {
throw new IOException("ダウンロード失敗: " + response.getCode());
}
HttpEntity entity = response.getEntity();
long contentLength = entity.getContentLength();
System.out.println("ファイルサイズ: " + contentLength + " bytes");
try (InputStream inputStream = entity.getContent();
FileOutputStream outputStream = new FileOutputStream(filename);
BufferedOutputStream bufferedOut = new BufferedOutputStream(outputStream)) {
byte[] buffer = new byte[8192];
long downloaded = 0;
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
bufferedOut.write(buffer, 0, bytesRead);
downloaded += bytesRead;
if (contentLength > 0) {
int progress = (int) ((downloaded * 100) / contentLength);
System.out.print("\rダウンロード進捗: " + progress + "%");
}
}
System.out.println("\nダウンロード完了: " + filename);
}
}
}
}
// レート制限付き並列処理
public void processWithRateLimit(List<String> urls, int requestsPerSecond) throws Exception {
Semaphore rateLimiter = new Semaphore(requestsPerSecond);
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
// 1秒毎にセマフォを補充
scheduler.scheduleAtFixedRate(() -> {
rateLimiter.release(requestsPerSecond - rateLimiter.availablePermits());
}, 1, 1, TimeUnit.SECONDS);
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
CountDownLatch latch = new CountDownLatch(urls.size());
for (String url : urls) {
new Thread(() -> {
try {
rateLimiter.acquire(); // レート制限待機
HttpGet request = new HttpGet(url);
try (CloseableHttpResponse response = httpClient.execute(request)) {
System.out.println("処理完了: " + url + " (" + response.getCode() + ")");
}
} catch (Exception e) {
System.err.println("処理エラー: " + url + " - " + e.getMessage());
} finally {
latch.countDown();
}
}).start();
}
// 全処理完了を待機
latch.await(300, TimeUnit.SECONDS);
} finally {
scheduler.shutdown();
}
}
}
フレームワーク統合と実用例
// Spring Framework統合
@Service
public class HttpClientService {
private final CloseableHttpClient httpClient;
@Autowired
public HttpClientService() {
this.httpClient = HttpClients.custom()
.setConnectionManager(createConnectionManager())
.setDefaultRequestConfig(createRequestConfig())
.build();
}
private PoolingHttpClientConnectionManager createConnectionManager() {
return PoolingHttpClientConnectionManagerBuilder.create()
.setMaxConnTotal(200)
.setMaxConnPerRoute(50)
.build();
}
private RequestConfig createRequestConfig() {
return RequestConfig.custom()
.setConnectTimeout(Timeout.ofSeconds(10))
.setResponseTimeout(Timeout.ofSeconds(30))
.build();
}
public String callExternalApi(String url, Map<String, String> headers) {
try {
HttpGet request = new HttpGet(url);
// ヘッダーの設定
headers.forEach(request::setHeader);
try (CloseableHttpResponse response = httpClient.execute(request)) {
if (response.getCode() == 200) {
return EntityUtils.toString(response.getEntity());
} else {
throw new RuntimeException("API呼び出しエラー: " + response.getCode());
}
}
} catch (IOException e) {
throw new RuntimeException("HTTP通信エラー", e);
}
}
@PreDestroy
public void cleanup() {
try {
httpClient.close();
} catch (IOException e) {
System.err.println("HTTPクライアント終了エラー: " + e.getMessage());
}
}
}
// キャッシュ機能付きHTTPクライアント
public class CachingHttpClientExample {
private final CloseableHttpClient cachingClient;
public CachingHttpClientExample() {
// キャッシュ設定
CacheConfig cacheConfig = CacheConfig.custom()
.setMaxCacheEntries(1000)
.setMaxObjectSize(8192)
.setHeuristicCachingEnabled(true)
.setHeuristicDefaultLifetime(TimeValue.ofMinutes(10))
.build();
this.cachingClient = CachingHttpClients.custom()
.setCacheConfig(cacheConfig)
.setHttpCacheStorage(new BasicHttpCacheStorage(cacheConfig))
.build();
}
public String getCachedData(String url) throws Exception {
HttpGet request = new HttpGet(url);
request.setHeader("Cache-Control", "max-age=300"); // 5分キャッシュ
try (CloseableHttpResponse response = cachingClient.execute(request)) {
Header cacheStatus = response.getFirstHeader("X-Cache-Status");
if (cacheStatus != null) {
System.out.println("キャッシュステータス: " + cacheStatus.getValue());
}
return EntityUtils.toString(response.getEntity());
}
}
}
// マルチパートファイルアップロード
public class FileUploadExample {
public String uploadFile(File file, Map<String, String> metadata) throws Exception {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost uploadRequest = new HttpPost("https://api.example.com/upload");
// マルチパートエンティティの構築
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
// ファイルの追加
builder.addBinaryBody("file", file, ContentType.DEFAULT_BINARY, file.getName());
// メタデータの追加
metadata.forEach((key, value) ->
builder.addTextBody(key, value, ContentType.TEXT_PLAIN));
HttpEntity multipartEntity = builder.build();
uploadRequest.setEntity(multipartEntity);
// 認証ヘッダー
uploadRequest.setHeader("Authorization", "Bearer your-token");
try (CloseableHttpResponse response = httpClient.execute(uploadRequest)) {
if (response.getCode() == 200) {
return EntityUtils.toString(response.getEntity());
} else {
throw new IOException("アップロード失敗: " + response.getCode());
}
}
}
}
// 進捗付きアップロード
public void uploadWithProgress(File file, ProgressCallback callback) throws Exception {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost request = new HttpPost("https://api.example.com/upload");
// 進捗追跡機能付きエンティティ
FileEntity fileEntity = new FileEntity(file, ContentType.DEFAULT_BINARY) {
@Override
public void writeTo(OutputStream outStream) throws IOException {
try (FileInputStream inStream = new FileInputStream(file);
ProgressOutputStream progressOut = new ProgressOutputStream(outStream, file.length(), callback)) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = inStream.read(buffer)) != -1) {
progressOut.write(buffer, 0, bytesRead);
}
}
}
};
request.setEntity(fileEntity);
try (CloseableHttpResponse response = httpClient.execute(request)) {
if (response.getCode() == 200) {
callback.onComplete(EntityUtils.toString(response.getEntity()));
} else {
callback.onError(new IOException("Upload failed: " + response.getCode()));
}
}
}
}
public interface ProgressCallback {
void onProgress(long bytesTransferred, long totalBytes);
void onComplete(String response);
void onError(Exception error);
}
private static class ProgressOutputStream extends OutputStream {
private final OutputStream target;
private final long totalBytes;
private final ProgressCallback callback;
private long bytesTransferred = 0;
public ProgressOutputStream(OutputStream target, long totalBytes, ProgressCallback callback) {
this.target = target;
this.totalBytes = totalBytes;
this.callback = callback;
}
@Override
public void write(int b) throws IOException {
target.write(b);
bytesTransferred++;
callback.onProgress(bytesTransferred, totalBytes);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
target.write(b, off, len);
bytesTransferred += len;
callback.onProgress(bytesTransferred, totalBytes);
}
}
}