HTTP ベースのコネクタ

HTTP 要求を発行するか、または HTTP エンドポイントを公開することによってリモートシステムにアクセスするコネクタでは、特別に考慮すべき点がいくつかあります。

Mule HTTP クライアントの使用

HTTP 要求の実行には Mule HTTP クライアントを使用する​必要があります​。他の HTTP クライアント (Jersey クライアント、Apache HTTP クライアント、Jetty、Netty など) を Mule HTTP クライアントの代わりに使用す​べきではありません​。

このルールの唯一の例外は、Mule HTTP クライアントで再現するのが非常に困難な付加価値を持つクライアントライブラリが、すでにリモートシステムベンダーから提供されている場合です。

たとえば、Amazon AWS Connector は、カスタムメッセージ署名を必要とする HTTP API を消費することで機能します。Amazon では、この署名を行うためのロジックをカプセル化したクライアントライブラリを提供しています。

一方、利便性を高めるために HTTP クライアントをラップする以外は何もしないクライアントライブラリ (Google クライアントなど) は、このルールの例外とはなりません。

  • HTTP クライアントライフサイクルの処理

    HTTP クライアントのライフサイクルは、HttpClient クラスを作成する ConnectionProvider クラスの責任です。

  • キャッシュされた接続プロバイダーの使用

    HttpClient クラスを作成するすべての ConnectionProvider クラスは、CachedConnectionProvider インターフェースを実装する​必要があります​。

    このルールは、接続プロバイダーが生成する接続オブジェクトにも影響します。以前に説明した「接続オブジェクトは内部クライアントを公開しては (内部クライアントであっては) ​ならない​」というルールにより、提供される接続オブジェクトは、その状態の一部として HTTP クライアントを含む必要があり、スレッドセーフである​必要があります​。

  • Startable および Stoppable 接続プロバイダーの使用

    接続プロバイダーは、Startable インターフェースと Stoppable インターフェースも提供する​必要があります​。HttpClient は、start() フェーズで作成および開始される​必要があり​、stop() フェーズで終了される​必要があります​。

    ライフサイクルの例:

    public class HttpConnectionProvider implements CachedConnectionProvider<HttpConnection>, Startable, Stoppable {
    
            @Inject
            private HttpService httpService;
    
            @Parameter
            private TlsContextFactory tlsContext;
    
            @Override
       public void start() throws MuleException {
           initialiseIfNeeded(tlsContext);
           httpClient = httpService.getClientFactory().create(getHttpClientConfiguration());
           httpClient.start();
       }
    
       @Override
       public void stop() throws MuleException {
               if (httpClient != null) {
                  httpClient.stop();
               }
       }
    
       // rest of the implementation removed for example simplicity
    
    }
  • HTTP クライアント名の設定

    各 ConnectionProvider で作成された HttpClient には、その ConnectionProvider を所有する設定の名前に基づいた (あるいは単純に一致する) 名前を付ける​必要があります​。

    これは、パフォーマンスのトラブルシューティングにおいて非常に重要です。各 HttpClient は、I/O に使用するセレクタースレッドを作成します。これらのスレッドには、クライアント名にちなんだ名前が付けられます。これらのスレッドに意味のある名前を付けることで、パフォーマンスの問題を診断しやすくなります。

    所有する設定の名前は @RefName アノテーションを使用して取得できます。

    そのやり方の例を次に示します。

    public class HttpConnectionProvider implements
    	CachedConnectionProvider<HttpConnection>, Startable, Stoppable {
    
            @Inject
            private HttpService httpService;
    
            @RefName
            private String configName
    
            @Override
       public void start() throws MuleException {
           httpClient = httpService.getClientFactory().
           	create(new HttpClientConfiguration.Builder()
                           .setName(configName)
                           .build());
           httpClient.start();
       }
    
       // rest of implementation removed for example simplicity
    
    }
  • 操作での非ブロック I/O の活用

    HttpClient は、非ブロック I/O を使用して要求を実行できます。操作は、この機能を活用し、さらに非ブロックとして定義される​必要があります​。

  • <http:requester-config> 要素を参照しない

    HttpService では、個別の <http:requester-config> 要素に関連付けられている HttpClient を取得できます。この機能は、コネクタで使用するために用意されているわけではありません。コネクタでは、この機能を使用しては​なりません​。

    HttpClient インスタンスを必要とするすべてのコネクタは、自分で作成して管理する​必要があります​。

  • HTTP プロキシ設定の提供

    HTTP クライアントインスタンスを使用するコネクタは、HTTP Connector と同様にプロキシを設定する機能を提供する必要があります。プロキシ設定を使用する一般的な理由には、セキュリティ、メンテナンス、プロキシキャッシュなどがあります。多くの組織では、ファイアウォールの背後で作業する場合にもプロキシ設定が必要です。

    次のコードスニペットは、HTTP 要求コネクタの OAuth エンドポイントなどでプロキシを設定する方法を示しています。

    このグローバルプロキシ設定を参照として使用します。

    <http:proxy name="proxyConfig" host="localhost" port="3128" username="yourProxyUsername" password="yourProxyPassword" />

    クライアントログイン情報許可種別を設定する場合は、次のスニペットを使用します。

    <http:request-config name="HTTP_Request_Configuration" host="api.github.com" port="443" doc:name="HTTP Request Configuration">
            <oauth2:client-credentials-grant-type clientId="111" clientSecret="222" >
            <oauth2:token-request tokenUrl="https://github.com/login/oauth/authorize"" />
                        <spring:property name="proxyConfig" ref="proxyConfig"/>
            </oauth2:client-credentials-grant-type>
    </http:request-config>

    認証コード許可種別を設定する場合は、次のスニペットを使用します。

    <http:request-config name="HTTP_Request_Configuration" host="api.github.com" protocol="HTTPS" port="443" usePersistentConnections="false" doc:name="HTTP Request Configuration">
            <oauth2:authorization-code-grant-type clientId="00a3d08a823faf378568" clientSecret="19c09306adbb84c2d4bc99c585df51d49fe858cf" redirectionUrl="http://localhost:8082/callback">;
                <oauth2:authorization-request authorizationUrl="https://github.com/login/oauth/authorize"" localAuthorizationUrl="http://localhost:8082/login"/>;
                <oauth2:token-request tokenUrl="https://github.com/login/oauth/access_token">;
                    <oauth2:token-response accessToken="#[payload.'access_token']" refreshToken="#[payload['access_token']]"/>
                </oauth2:token-request>
                <spring:property name="proxyConfig" ref="proxyConfig"/>
            </oauth2:authorization-code-grant-type>
    </http:request-config>

    proxyConfig​ 種別を使用する場合、次のように、接続プロバイダーで接続がプロキシを参照するように ​@Parameter​ アノテーションを含める必要があります。

    @Parameter
    @Optional
    @Placement(tab = "Proxy", order = 1)
    private MyConnectorProxyConfiguration proxyConfig;

    HTTP クライアントでプロキシ設定も定義する必要があります。

    .setProxyConfig(connectionParams.getProxyConfig())

    詳細は、HTTP Connector がプロキシを処理する方法に関する Java コード​を参照してください。

HTTP インバウンドエンドポイントの公開

一部のコネクタでは、自身の HTTP インバウンドエンドポイントを公開する必要があります。

そのためには、コネクタで外部 <http:listener-config> 要素を参照して HttpServer インスタンスを取得する​必要があります​。コネクタ自身でサービスを作成しては​なりません​。

これは次のようにして実現する​必要があります​。

  • リスナー設定を正しく参照する

    接続プロバイダーは、listenerConfig という文字列パラメーターを公開する​必要があります​。このパラメーターは、文字列型で、式を受け入れず、@ConfigReference アノテーションを使用して自身が <http:listener-config> 要素を参照することを示す​必要があります​。

    次に例を示します。

    @Parameter
    @Expression(NOT_SUPPORTED)
    @ConfigReference(name = "LISTENER_CONFIG", namespace = "HTTP")
    private String listenerConfig;
  • RequestHandler の追加

    上の listenerConfig パラメーターを使用して、一致する HttpServer インスタンスを次のように取得できます。

    HttpServer httpServer;
    try {
            httpServer = httpService.getServerFactory().lookup(listenerConfig);
            } catch (ServerNotFoundException e) {
            throw new IllegalArgumentException(
               format("Connector configuration '%s' refers to an <http:listener-config> with name '%s', "
                   + "but such element doesn't exist", configName, listenerConfigName),
               e);
    }

    取得した HttpServer で org.mule.runtime.http.api.server.HttpServer#addRequestHandler(java.lang.String, org.mule.runtime.http.api.server.RequestHandler) メソッドを使用して、新しい RequestHandler を追加できます。

    コネクタは、このメソッドの Javadocs で定義されているコントラクトに慎重に準拠する​必要があります​。最も重要な点として、このメソッドを呼び出すと RequestHandlerManager インスタンスが返されます。これらのインスタンスは、start() と stop() という 2 つの重要なメソッドを公開するため、コネクタ側で保持する​必要があります​。

    インバウンドエンドポイントは、RequestHandlerManager で start() メソッドを呼び出すまでは実際には機能しません。また、RequestHandlerManager で stop() メソッドを呼び出すまで、このエンドポイントは機能し続けます。

    そのため、カスタムエンドポイントを所有するコンポーネントが停止した場合は、RequestHandlerManager も停止する​必要があります​。そうしないと、コネクタでメモリリークが発生します。

タイムアウトを設定可能にする

コネクタは、HTTP 要求のタイムアウトを制御するパラメーターを公開する​必要があります​。

これは、期間パラメーターの処理方法と、タイムアウトパラメーターを設定で上書きできるようにする方法に関する前述のルールと一貫した方法で行う​必要があります​。

HTTPS セキュリティ

コネクタは、可能な限り HTTPS スキームをサポートしている​必要があり​、推奨されるオプションとして考慮する​必要があります​。

  • リモートシステムで HTTPS しかサポートしていない場合は、コネクタ側で必要な TlsContextFactory パラメーターを用意する​必要があります​。

  • リモートシステムで HTTP と HTTPS の両方のスキームをサポートしている場合は、コネクタで両方のオプションをサポートする必要がありますが、HTTP が使用されたときには警告をログに記録する​必要があります​。