Apache HttpClient

Apache Software Foundation開発のJava向け成熟したHTTPクライアントライブラリ。企業環境で実績豊富なHTTP/1.1完全実装、認証、プロキシサポート、SSL/TLS、接続管理機能を提供。エンタープライズグレードの信頼性と安定性。

HTTPクライアントJavaApacheエンタープライズ高機能同期非同期

GitHub概要

apache/httpcomponents-client

Mirror of Apache HttpClient

スター1,514
ウォッチ103
フォーク982
作成日:2009年5月21日
言語:Java
ライセンス:Apache License 2.0

トピックス

httpcomponents

スター履歴

apache/httpcomponents-client Star History
データ取得日時: 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);
        }
    }
}