Anypoint コネクタ DevKit チュートリアル

チュートリアルの目標

Anypoint コネクタ DevKit を使用して Web サービスへのコネクタを作成する方法について学習します。

DevKit は Anypoint Connectors (for Mule 3)の作成に役立ちます。このチュートリアルでは、認証、接続、サービス操作などをサポートするようにコネクタをコーディングする方法について説明します。これは、コネクタ開発者がコネクタのエンドユーザ (Mule アプリケーション開発者) に公開する機能です。

Anypoint コネクタ DevKit とは?

Anypoint コネクタ DevKit は、Anypoint コネクタのコーディングを容易にする、Anypoint Platform の不可欠な開発ツールです。DevKit は、Mule アプリケーションの一部として実行できる再利用可能なコンポーネントを作成するために使用でき、​Anypoint Studio から容易に設定できる Maven ベースのツールです。

DevKit は、Mule Runtime や Anypoint Studio とやり取りするためのコードとファイルを生成する Java アノテーションをコネクタ開発者に公開します。生成されたコードでは、コネクタと Mule 間のインターフェースが提供されます。これがない場合、各コネクタ開発者は広範な定型コードのほか、Anypoint Studio でコネクタとやり取りするために必要なコードやファイルを含める必要があります。

コネクタとは?

Anypoint コネクタは、Mule Runtime および Anypoint Studio とやり取りする再利用可能なコンポーネントです。コネクタは対象リソースと通信し、リソースと Mule 間で情報を伝送するほか、データを Mule メッセージに変換します。

Anypoint コネクタ DevKit は、Mule と外部サービスまたは API 間で発生する通信を抽象化します。また、最終的にアプリケーションでコネクタを使用する開発者がコネクタを簡単に使用できるようにするためのユーザインターフェースを生成します。

適切に開発されたコネクタを使用すると、ページネーション、セッションの有効期限、入出力メタデータなどのタスクを処理する場合に、ユーザの Mule アプリケーションの開発が大幅に容易になります。このチュートリアルでは、適切にデザインされたコネクタを作成する方法について説明します。

DevKit を使用してコネクタを作成する理由は?

次のような場合に独自のコネクタを作成することが望まれます。

  • Mule アプリケーションから API をコンシュームする必要があり、同じコネクタを使用することで開発者が確実に一貫性を維持できるようにしたい。

  • API があり、Web サービスへのインターフェースを提供するコネクタを開発者に提供することでビジネスに戦略的価値を加えたい。

  • SaaS やオンプレミスの Web サービス、アプリケーション、およびデータソースとのインテグレーションを促進したい。

  • コンシュームする API がページネーションやバッチをサポートするか、SQL 機能を持つ。

  • コンシュームする API にさまざまなエンティティ種別があるか、その構造が API/サービス操作によって異なる。

  • Mule コアを拡張したい。

前提条件

コネクタを開発する前に、Mule、Anypoint Studio、および Java 開発全般に関する実用的な知識、特に Java アノテーションの使用に関する知識が必要です。

ソフトウェアのセットアップおよび DevKit についての詳細は、「開発環境のセットアップ」および「Anypoint コネクタ DevKit」を参照してください。

コネクタは Windows、Mac、または Linux マシンで開発できます。

Cookbook を取得する

GitHub に保存されている Cookbook チュートリアルを取得します。

  1. https://github.com/mulesoft/mule-cookbook リポジトリをローカルマシンに複製します。たとえば、目的の開発フォルダに複製するには、次のコマンドを実行します。

    cd ~/dev
    git clone https://github.com/mulesoft/mule-cookbook.git
  2. 新しく作成した mule-cookbook ディレクトリに入り、次の Maven コマンドを実行します。

    mvn install eclipse:eclipse
  3. 完了したら、Anypoint Studio を開き、mule-cookbook ディレクトリを既存のプロジェクトとしてインポートします。[File (ファイル)] > [Import (インポート)] > [Existing Projects into Workspace (既存のプロジェクトをワークスペースへ)] を選択します。

    既存のインポート - mule-cookbook

これで、上の画面に表示されているいくつかのフォルダが Studio のワークスペースに作成されます。ワークスペースは Package Explorer で確認できます。

Cookbook についての説明

先に進む前に Cookbook サービスについて簡単に説明します。この Web サービスは、ユーザが Web からアクセス、保存、または更新したい材料やレシピを整理するのに役立ちます。

Cookbook API の説明

この API を使用すると、ユーザは 1 つまたは複数のレシピと材料に対して CRUD (create、read、update、delete) 操作を使用できます。また、この API を使用すると、最近追加されたレシピを参照することもできます。

この API は次のように公開されます。

  • WSDL ファイルを使用する SOAP サービスとして

  • RAML ファイルを使用する REST API として、および

  • Java SDK として

認証

このコネクタは次の認証メカニズムをサポートします。

  • 要求の一部として各要求と共に送信するトークンを提供する、ユーザ名とパスワードを使用するカスタム認証。

  • OAuthV2

ログイン情報

カスタム認証:
Username (ユーザ名) Password (パスワード)

admin

admin

OAuth:
Client ID (クライアント ID) Client Secret (クライアントシークレット)

ePU9CxyEVFIXF9nMoGH16s1lUGe5JYPPzClnnVBG

YeKAsQCdr264tNmDdvTdJUAh9TQraJqZpwbEoTuz

サービスへの接続

この Web サービスはオンラインで使用できます。

  • SOAP バージョンをコンシュームするには、SDK は次のアドレスへの要求を実行します: http://devkit-cookbook.cloudhub.io/soap

  • REST のベース URL は http://devkit-cookbook.cloudhub.io/rest です。

    • OAuth 認証 URL は /oauth/authorize です。

    • アクセストークン URL は /oauth/accessToken です。

また、開始準備がすでに整っているサーバ用のソースコードが用意されているため、これをローカルで実行することもできます。

com.cookbook.tutorial.service.MuleStoreServer クラスを実行するだけで、ローカルの SOAP サーバをソープサーバプロジェクトから実行できます。

デフォルトでは、サーバはアドレス http://localhost:9090/cook-book で開始されます。

com.cookbook.tutorial.Main クラスを実行するだけで、ローカルの REST サーバを REST サーバプロジェクトから実行できます。

デフォルトでは、サーバはアドレス http://localhost/9091 で開始されます。

Cookbook コネクタの作成手順

Cookbook サービス用の基本コネクタを作成するには、次の手順を実行する必要があります。

  1. コネクタプロジェクトを作成します。

  2. サービスへの接続で使用するクライアントが含まれる連動関係を追加します。

  3. サービスをホストする URL をユーザが指定できるように設定可能な URL を追加します。

  4. Anypoint Studio でユーザがコンシュームできる操作を追加します。

Cookbook コネクタの作成

上述の手順に従って mule-cookbook ディレクトリを既存のプロジェクトとしてワークスペースにインポートしている場合、コネクタプロジェクトの作成に着手して、コーディングを開始できます。

  1. Anypoint Studio で [File (ファイル)] > [New (新規)] > [Anypoint Connector Project (Anypoint コネクタプロジェクト)] をクリックするか、Package Explorer でプロジェクト名を右クリックして [New (新規)] > [Anypoint Connector Project (Anypoint コネクタプロジェクト)] をクリックします。

    new connector 1
  2. 作成するプロジェクト種別を選択します。この場合は、[SDK Based (SDK ベース)] を選択します。

    new0
  3. コネクタの名前を指定し、デフォルトの生成を必ずオフにしてから [Finish (完了)] をクリックします。

    new1

    これにより、構造とすべての必須要素 (スケルトンコネクタ、アイコン、サンプルドキュメントファイルを含む。ただし、コネクタの基本テストは含まない) を含むプロジェクトが生成されます。

    new connector 3
DevKit ビューを有効にするには、上部バーから [Window (ウィンドウ)] > [Show View (ビューを表示)] > [Other (その他)] をクリックし、[MuleSoft] ドロップダウンで DevKit を探します。
enable view
Figure 1. コネクタのコンパクトビューが表示される DevKit ビュー。

接続設定

サービスをコンシュームする場合、さまざまな値を設定して接続を確立する必要があります。

接続設定戦略の開発を容易にするため、DevKit には 1 組のアノテーションが用意されており、これらを使用して、以下の定義を複数のクラス内でモジュール化できます。

  • ユーザに公開する動作。@Connector アノテーションを使用します。

  • 接続設定に関連するコードが、@Config アノテーションを追加した場所に挿入されます。使用できるいくつかの設定種別があり、それらをこのチュートリアルで見ていきます。

@Config でクラスをマークした場合、Mule アプリケーションの実行時に初期化されたオブジェクトセットが存在し、対象のコネクタに対して要求が実行されることが保証されます。

pom.xml での連動関係の追加

では、コーディングを開始しましょう。最近追加した項目を Cookbook サービスから取得できるコネクタを作成します。

最近追加されたレシピをコンシュームするのにどのような種類の認証も必要とされません。そのため、この操作はコネクタの作成方法の学習を開始するには最適な操作になります。

  1. コネクタで使用できるようにクライアントの連動関係を追加します。これにより、Java API を使用して Cookbook に接続できます。

    pom.xml ファイルに次のコードを追加します。

    <dependencies>
      <dependency>
        <groupId>org.mule.modules</groupId>
        <artifactId>sdk-client</artifactId>
        <version>1.0.0-SNAPSHOT</version>
      </dependency>
    </dependencies>

設定可能項目をサポートするためのコードの追加

ConnectorConfig.java ファイルで、Cookbook サービスをホストするアドレスに対する @Configurable アノテーションを付加します。

  1. コネクタ設定用のエディタで​「conf」​と入力し、control + space を使用してテンプレートを表示します。

    config field
  2. クライアントの接続先のエンドポイントのデフォルト値 (@Default("http://devkit-cookbook.cloudhub.io/soap") など) を使用して、@Configuration アノテーション付きクラスの設定可能項目を定義します。

  3. @Configurable アノテーションでマークされた項目には、その項目用の getter と setter が必要です。

    設定 の完全なソースコードを参照してください。

クライアントの初期化

Connector.java ファイル内の @Connector アノテーション付きクラス内で、クライアントを初期化するためのメソッドに対して @Start アノテーションを使用します。

/**
 * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
 * a copy of which has been included with this distribution in the LICENSE.md file.
 */

package org.mule.modules.cookbook;

import java.util.List;

import org.mule.api.annotations.Config;
import org.mule.api.annotations.Connector;
import org.mule.api.annotations.Processor;
import org.mule.api.annotations.lifecycle.Start;
import org.mule.modules.cookbook.config.ConnectorConfig;

import com.cookbook.tutorial.client.MuleCookBookClient;
import com.cookbook.tutorial.service.Recipe;

/**
 * Anypoint Connector
 *
 * @author MuleSoft, Inc.
 */
@Connector(name = "cookbook", friendlyName = "Cookbook")
public class CookbookConnector {

	@Config
	ConnectorConfig config;

	private MuleCookBookClient client;

	@Start
	public void initialize() {
		client = new MuleCookBookClient(config.getAddress());
	}

	/**
	 * Returns the list of recently added recipes
	 *
	 * {@sample.xml ../../../doc/cookbook-connector.xml.sample
	 * cook-book:getRecentlyAdded}
	 *
	 * @return A list of the recently added recipes
	 */
	@Processor
	public List<Recipe> getRecentlyAdded() {
		return client.getRecentlyAdded();
	}

	public ConnectorConfig getConfig() {
		return config;
	}

	public void setConfig(ConnectorConfig config) {
		this.config = config;
	}

}

Mule 固有のインターフェースを実装しなくても、Anypoint コネクタは Mule Runtime のライフサイクルを完全に認識するように作成されています。

ライフサイクルの 4 つの各フェーズにアノテーションメソッドがあります。

メソッドにこのいずれかのアノテーションが付加されている場合、DevKit は、そのアノテーションが表すライフサイクルフェーズ中にメソッドを呼び出します。

@Processor メソッドの追加

  1. Connector.java ファイルからダミーの @Processor 操作を削除します。

  2. プロセッサを追加するには、Studio コードエディタに proc と入力して、ctrl + space を使用してテンプレートを表示し、simple processor (シンプルプロセッサ) を選択します。

    processor1
  3. getRecentlyAdded メソッドの署名を反映するようにこれを変更します (実装例については、​こちらを参照)。この時点で、最初のコネクタを作成するためのコードが用意されます。このコネクタをすぐにテストできます。

  4. [Generate Sources (ソースを生成)] アクションを実行します。

    コネクタの完全なソースコードを参照してください。

パッケージのインポートエラーを解決するには、Studio の Package Explorer で対象のコネクタプロジェクトを右クリックします。[Build Path (ビルドパス)] > [Add External Archives (外部アーカイブを追加)] を選択します。適用可能な .jar ファイルなどを追加し、プロジェクトで Cookbook の sdk-client を参照できるようにします。これにより、import com.cookbook.tutorial.client.MuleCookBookClient; import com.cookbook.tutorial.service.Recipe; のインポートエラーが阻止されます。

プロジェクトのプロパティで参照を確認できます。

Cookbook コネクタプロジェクトのプロパティ

この時点で、このコネクタをインストールし、必要に応じて Studio でコネクタを試すことができます。

コネクタを変更すると、生成されたフォルダにエラーマーカーが表示される場合があります。

これは無視してください。

ソースを再生成すると、生成されたコードが更新されるためエラーは表示されなくなります。

コネクタプロセッサのサンプルコードの定義

DevKit 3.9 で作成されたコネクタでは、異なる形式のサンプルの仕様がサポートされます。新しい仕様と生成形式については、コネクタリファレンスドキュメントを参照してください。

手順に従って、操作 (@Processor のアノテーション付き) の使用サンプルを追加します。

Connector.java ファイルの {@sample …​} の後のコードブロックで参照されているファイルを作成します (ファイルがまだ存在しない場合)。

/**
 * Returns the list of recently added recipes
 *
 * {@sample.xml ../../../doc/cookbook-connector.xml.sample
 * cook-book:getRecentlyAdded}
 *
 * @return A list of the recently added recipes
 */
サンプルを追加する場合、ファイル内のサンプルを示す名前と同じ名前を使用してください。この内側に @Processor のサンプルを配置する必要があります。

「Javadoc check (Javadoc チェック)」を有効にしている場合、DevKit プラグインは欠落しているサンプルをエラーとしてマークし、サンプルを簡単に追加できるようにクイック修正を提供します。

有効にしていない場合、エディタでファイルを開き、​「<」​と入力し、control + space を使用してテンプレートを表示し、操作に最も適したサンプルを選択します。

sample1

この場合のサンプルは次のようになります。

<!-- BEGIN_INCLUDE(cook-book:getRecentlyAdded) -->
	<cook-book:get-recently-added/>
<!-- END_INCLUDE(cook-book:getRecentlyAdded) -->

Mule アプリケーションでのコネクタの使用

HTTP エンドポイントでリスンする簡単なアプリケーションを作成します。エンドポイントへのアクセスがあると、アプリケーションはコネクタを使用して、最近追加された項目のリストを Cookbook サービスから取得します。

  1. Studio で Mule アプリケーションを作成し、HTTP リスナを追加して、パスに /get-recently を指定します。これが初めての Mule アプリケーションの場合は、Hello World アプリケーションを参照してください。

  2. コネクタをキャンバスにドロップし、adminadmin をログイン情報として使用するように設定します (これは OAuth 以外の設定用です)。

    devkit tutorial 4cb84
  3. コネクタの後に [Object to JSON] トランスフォーマをドラッグアンドドロップし、アプリケーションを実行します。([Play (プレイ)] アイコンまたは [Run As (別のユーザとして実行)] > [Mule Application (Mule アプリケーション)])

    devkit tutorial 1c0f8
    <?xml version="1.0" encoding="UTF-8"?>
    <mule xmlns:cookbook="http://www.mulesoft.org/schema/mule/cookbook" xmlns:json="http://www.mulesoft.org/schema/mule/json" xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
          xmlns:spring="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
    http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
    http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
    http://www.mulesoft.org/schema/mule/json http://www.mulesoft.org/schema/mule/json/current/mule-json.xsd
    http://www.mulesoft.org/schema/mule/cookbook http://www.mulesoft.org/schema/mule/cookbook/current/mule-cookbook.xsd">
        <http:listener-config name="HTTP_Listener_Configuration" host="0.0.0.0" port="8081" doc:name="HTTP Listener Configuration"/>
        <cookbook:config name="Cookbook__Configuration" doc:name="Cookbook: Configuration"/>
        <flow name="mule-appFlow">
            <http:listener config-ref="HTTP_Listener_Configuration" path="/get-recently" doc:name="HTTP"/>
            <cookbook:get-recently-added config-ref="Cookbook__Configuration" doc:name="Cookbook"/>
            <json:object-to-json-transformer doc:name="Object to JSON"/>
        </flow>
    </mule>

    URL http://localhost:8081/get-recently にアクセスすると、次のような応答が表示されます。

    [
       {
          "created":1428678371866,
          "id":2,
          "lastModified":null,
          "name":"Baked Apples",
          "cookTime":20.0,
          "directions":[
             "Cut the Apples",
             "Put them in the oven",
             "Remove from the oven after 20.0 minutes"
          ],
          "ingredients":[
             {
                "created":1428678371866,
                "id":1,
                "lastModified":null,
                "name":"Apple",
                "quantity":0.0,
                "unit":"UNIT"
             }
          ],
          "prepTime":30.0
       }
    ]

コネクタのテストの作成方法については、以下を参照してください。

接続管理の追加

getRecentlyAdded コールでは、認証は不要です。他のすべての Cookbook 操作では、各要求でトークンを設定する必要があります。

使用しているクライアントは、トークンを初期化するログインコールを提供し、後続の要求でそのトークンを使用します。

セッションが期限切れになる可能性があり、それによりコネクタがログイン要求を再度実行することを考慮してください。

DevKit は、コードをクリーンの状態に維持し、接続を処理するための一連のアノテーションを提供します。

  1. Config.java ファイル内の @Configuration@ConnectionManagement に変更します。

    /**
     * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
     * a copy of which has been included with this distribution in the LICENSE.md file.
     */
    
    package org.mule.modules.cookbook.config;
    
    import org.mule.api.ConnectionException;
    import org.mule.api.ConnectionExceptionCode;
    import org.mule.api.annotations.Configurable;
    import org.mule.api.annotations.Connect;
    import org.mule.api.annotations.ConnectionIdentifier;
    import org.mule.api.annotations.Disconnect;
    import org.mule.api.annotations.TestConnectivity;
    import org.mule.api.annotations.ValidateConnection;
    import org.mule.api.annotations.components.ConnectionManagement;
    import org.mule.api.annotations.display.Password;
    import org.mule.api.annotations.param.ConnectionKey;
    import org.mule.api.annotations.param.Default;
    
    import com.cookbook.tutorial.client.MuleCookBookClient;
    import com.cookbook.tutorial.service.InvalidCredentialsException;
    
    /**
     * Configuration type Config
     *
     * @author MuleSoft, Inc.
     */
    @ConnectionManagement(friendlyName = "Configuration")
    public class ConnectorConfig {
    
        /**
         * Connect
         *
         * @param username
         *            A username
         * @param password
         *            A password
         * @throws ConnectionException
         */
        @Connect
        @TestConnectivity
        public void connect(@ConnectionKey String username, @Password String password) throws ConnectionException {
            setClient(new MuleCookBookClient(getAddress()));
            try {
                getClient().login(username, password);
            } catch (InvalidCredentialsException e) {
                throw new ConnectionException(ConnectionExceptionCode.INCORRECT_CREDENTIALS, e.getMessage(), "Invalid credentials");
            }
        }
    
        /**
         * Disconnect
         */
        @Disconnect
        public void disconnect() {
            setClient(null);
        }
    
        /**
         * Are we connected
         */
        @ValidateConnection
        public boolean isConnected() {
            return getClient() != null;
        }
    
        /**
         * Id used only when debuging.
         */
        @ConnectionIdentifier
        public String connectionId() {
            return "001";
        }
    
        /**
         * Description for address
         */
        @Configurable
        @Default("http://devkit-cookbook.cloudhub.io/soap")
        private String address;
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        private MuleCookBookClient client;
    
        public MuleCookBookClient getClient() {
            return client;
        }
    
        public void setClient(MuleCookBookClient client) {
            this.client = client;
        }
    
    }
  2. ConnectorConfig.javaMuleCookbookClient に getter および setter が備わっていることを確認します。

    /**
     * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
     * a copy of which has been included with this distribution in the LICENSE.md file.
     */
    
    package org.mule.modules.cookbook.config;
    
    import org.mule.api.ConnectionException;
    import org.mule.api.ConnectionExceptionCode;
    import org.mule.api.annotations.Configurable;
    import org.mule.api.annotations.Connect;
    import org.mule.api.annotations.ConnectionIdentifier;
    import org.mule.api.annotations.Disconnect;
    import org.mule.api.annotations.TestConnectivity;
    import org.mule.api.annotations.ValidateConnection;
    import org.mule.api.annotations.components.ConnectionManagement;
    import org.mule.api.annotations.display.Password;
    import org.mule.api.annotations.param.ConnectionKey;
    import org.mule.api.annotations.param.Default;
    
    import com.cookbook.tutorial.client.MuleCookBookClient;
    import com.cookbook.tutorial.service.InvalidCredentialsException;
    
    /**
     * Configuration type Config
     *
     * @author MuleSoft, Inc.
     */
    @ConnectionManagement(friendlyName = "Configuration")
    public class ConnectorConfig {
    
        /**
         * Connect
         *
         * @param username
         *            A username
         * @param password
         *            A password
         * @throws ConnectionException
         */
        @Connect
        @TestConnectivity
        public void connect(@ConnectionKey String username, @Password String password) throws ConnectionException {
            setClient(new MuleCookBookClient(getAddress()));
            try {
                getClient().login(username, password);
            } catch (InvalidCredentialsException e) {
                throw new ConnectionException(ConnectionExceptionCode.INCORRECT_CREDENTIALS, e.getMessage(), "Invalid credentials");
            }
        }
    
        /**
         * Disconnect
         */
        @Disconnect
        public void disconnect() {
            setClient(null);
        }
    
        /**
         * Are we connected
         */
        @ValidateConnection
        public boolean isConnected() {
            return getClient() != null;
        }
    
        /**
         * Id used only when debuging.
         */
        @ConnectionIdentifier
        public String connectionId() {
            return "001";
        }
    
        /**
         * Description for address
         */
        @Configurable
        @Default("http://devkit-cookbook.cloudhub.io/soap")
        private String address;
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        private MuleCookBookClient client;
    
        public MuleCookBookClient getClient() {
            return client;
        }
    
        public void setClient(MuleCookBookClient client) {
            this.client = client;
        }
    
    }
  3. 以下の 4 つのメソッドを示されているとおりに実装します。

    • @Connect - クライアントを初期化し、ログインが成功しない場合に例外をスローします。

      /**
       * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
       * a copy of which has been included with this distribution in the LICENSE.md file.
       */
      
      package org.mule.modules.cookbook.config;
      
      import org.mule.api.ConnectionException;
      import org.mule.api.ConnectionExceptionCode;
      import org.mule.api.annotations.Configurable;
      import org.mule.api.annotations.Connect;
      import org.mule.api.annotations.ConnectionIdentifier;
      import org.mule.api.annotations.Disconnect;
      import org.mule.api.annotations.TestConnectivity;
      import org.mule.api.annotations.ValidateConnection;
      import org.mule.api.annotations.components.ConnectionManagement;
      import org.mule.api.annotations.display.Password;
      import org.mule.api.annotations.param.ConnectionKey;
      import org.mule.api.annotations.param.Default;
      
      import com.cookbook.tutorial.client.MuleCookBookClient;
      import com.cookbook.tutorial.service.InvalidCredentialsException;
      
      /**
       * Configuration type Config
       *
       * @author MuleSoft, Inc.
       */
      @ConnectionManagement(friendlyName = "Configuration")
      public class ConnectorConfig {
      
          /**
           * Connect
           *
           * @param username
           *            A username
           * @param password
           *            A password
           * @throws ConnectionException
           */
          @Connect
          @TestConnectivity
          public void connect(@ConnectionKey String username, @Password String password) throws ConnectionException {
              setClient(new MuleCookBookClient(getAddress()));
              try {
                  getClient().login(username, password);
              } catch (InvalidCredentialsException e) {
                  throw new ConnectionException(ConnectionExceptionCode.INCORRECT_CREDENTIALS, e.getMessage(), "Invalid credentials");
              }
          }
      
          /**
           * Disconnect
           */
          @Disconnect
          public void disconnect() {
              setClient(null);
          }
      
          /**
           * Are we connected
           */
          @ValidateConnection
          public boolean isConnected() {
              return getClient() != null;
          }
      
          /**
           * Id used only when debuging.
           */
          @ConnectionIdentifier
          public String connectionId() {
              return "001";
          }
      
          /**
           * Description for address
           */
          @Configurable
          @Default("http://devkit-cookbook.cloudhub.io/soap")
          private String address;
      
          public String getAddress() {
              return address;
          }
      
          public void setAddress(String address) {
              this.address = address;
          }
      
          private MuleCookBookClient client;
      
          public MuleCookBookClient getClient() {
              return client;
          }
      
          public void setClient(MuleCookBookClient client) {
              this.client = client;
          }
      
      }
    • @Disconnect - 接続をリリースします。

      /**
       * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
       * a copy of which has been included with this distribution in the LICENSE.md file.
       */
      
      package org.mule.modules.cookbook.config;
      
      import org.mule.api.ConnectionException;
      import org.mule.api.ConnectionExceptionCode;
      import org.mule.api.annotations.Configurable;
      import org.mule.api.annotations.Connect;
      import org.mule.api.annotations.ConnectionIdentifier;
      import org.mule.api.annotations.Disconnect;
      import org.mule.api.annotations.TestConnectivity;
      import org.mule.api.annotations.ValidateConnection;
      import org.mule.api.annotations.components.ConnectionManagement;
      import org.mule.api.annotations.display.Password;
      import org.mule.api.annotations.param.ConnectionKey;
      import org.mule.api.annotations.param.Default;
      
      import com.cookbook.tutorial.client.MuleCookBookClient;
      import com.cookbook.tutorial.service.InvalidCredentialsException;
      
      /**
       * Configuration type Config
       *
       * @author MuleSoft, Inc.
       */
      @ConnectionManagement(friendlyName = "Configuration")
      public class ConnectorConfig {
      
          /**
           * Connect
           *
           * @param username
           *            A username
           * @param password
           *            A password
           * @throws ConnectionException
           */
          @Connect
          @TestConnectivity
          public void connect(@ConnectionKey String username, @Password String password) throws ConnectionException {
              setClient(new MuleCookBookClient(getAddress()));
              try {
                  getClient().login(username, password);
              } catch (InvalidCredentialsException e) {
                  throw new ConnectionException(ConnectionExceptionCode.INCORRECT_CREDENTIALS, e.getMessage(), "Invalid credentials");
              }
          }
      
          /**
           * Disconnect
           */
          @Disconnect
          public void disconnect() {
              setClient(null);
          }
      
          /**
           * Are we connected
           */
          @ValidateConnection
          public boolean isConnected() {
              return getClient() != null;
          }
      
          /**
           * Id used only when debuging.
           */
          @ConnectionIdentifier
          public String connectionId() {
              return "001";
          }
      
          /**
           * Description for address
           */
          @Configurable
          @Default("http://devkit-cookbook.cloudhub.io/soap")
          private String address;
      
          public String getAddress() {
              return address;
          }
      
          public void setAddress(String address) {
              this.address = address;
          }
      
          private MuleCookBookClient client;
      
          public MuleCookBookClient getClient() {
              return client;
          }
      
          public void setClient(MuleCookBookClient client) {
              this.client = client;
          }
      
      }
    • @ValidateConnection - 接続が動作していることを確認します。

      /**
       * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
       * a copy of which has been included with this distribution in the LICENSE.md file.
       */
      
      package org.mule.modules.cookbook.config;
      
      import org.mule.api.ConnectionException;
      import org.mule.api.ConnectionExceptionCode;
      import org.mule.api.annotations.Configurable;
      import org.mule.api.annotations.Connect;
      import org.mule.api.annotations.ConnectionIdentifier;
      import org.mule.api.annotations.Disconnect;
      import org.mule.api.annotations.TestConnectivity;
      import org.mule.api.annotations.ValidateConnection;
      import org.mule.api.annotations.components.ConnectionManagement;
      import org.mule.api.annotations.display.Password;
      import org.mule.api.annotations.param.ConnectionKey;
      import org.mule.api.annotations.param.Default;
      
      import com.cookbook.tutorial.client.MuleCookBookClient;
      import com.cookbook.tutorial.service.InvalidCredentialsException;
      
      /**
       * Configuration type Config
       *
       * @author MuleSoft, Inc.
       */
      @ConnectionManagement(friendlyName = "Configuration")
      public class ConnectorConfig {
      
          /**
           * Connect
           *
           * @param username
           *            A username
           * @param password
           *            A password
           * @throws ConnectionException
           */
          @Connect
          @TestConnectivity
          public void connect(@ConnectionKey String username, @Password String password) throws ConnectionException {
              setClient(new MuleCookBookClient(getAddress()));
              try {
                  getClient().login(username, password);
              } catch (InvalidCredentialsException e) {
                  throw new ConnectionException(ConnectionExceptionCode.INCORRECT_CREDENTIALS, e.getMessage(), "Invalid credentials");
              }
          }
      
          /**
           * Disconnect
           */
          @Disconnect
          public void disconnect() {
              setClient(null);
          }
      
          /**
           * Are we connected
           */
          @ValidateConnection
          public boolean isConnected() {
              return getClient() != null;
          }
      
          /**
           * Id used only when debuging.
           */
          @ConnectionIdentifier
          public String connectionId() {
              return "001";
          }
      
          /**
           * Description for address
           */
          @Configurable
          @Default("http://devkit-cookbook.cloudhub.io/soap")
          private String address;
      
          public String getAddress() {
              return address;
          }
      
          public void setAddress(String address) {
              this.address = address;
          }
      
          private MuleCookBookClient client;
      
          public MuleCookBookClient getClient() {
              return client;
          }
      
          public void setClient(MuleCookBookClient client) {
              this.client = client;
          }
      
      }
    • @ConnectionIdentifier - 文字列値を返します。これは、デバッグ時のみ使用します。

      /**
       * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
       * a copy of which has been included with this distribution in the LICENSE.md file.
       */
      
      package org.mule.modules.cookbook.config;
      
      import org.mule.api.ConnectionException;
      import org.mule.api.ConnectionExceptionCode;
      import org.mule.api.annotations.Configurable;
      import org.mule.api.annotations.Connect;
      import org.mule.api.annotations.ConnectionIdentifier;
      import org.mule.api.annotations.Disconnect;
      import org.mule.api.annotations.TestConnectivity;
      import org.mule.api.annotations.ValidateConnection;
      import org.mule.api.annotations.components.ConnectionManagement;
      import org.mule.api.annotations.display.Password;
      import org.mule.api.annotations.param.ConnectionKey;
      import org.mule.api.annotations.param.Default;
      
      import com.cookbook.tutorial.client.MuleCookBookClient;
      import com.cookbook.tutorial.service.InvalidCredentialsException;
      
      /**
       * Configuration type Config
       *
       * @author MuleSoft, Inc.
       */
      @ConnectionManagement(friendlyName = "Configuration")
      public class ConnectorConfig {
      
          /**
           * Connect
           *
           * @param username
           *            A username
           * @param password
           *            A password
           * @throws ConnectionException
           */
          @Connect
          @TestConnectivity
          public void connect(@ConnectionKey String username, @Password String password) throws ConnectionException {
              setClient(new MuleCookBookClient(getAddress()));
              try {
                  getClient().login(username, password);
              } catch (InvalidCredentialsException e) {
                  throw new ConnectionException(ConnectionExceptionCode.INCORRECT_CREDENTIALS, e.getMessage(), "Invalid credentials");
              }
          }
      
          /**
           * Disconnect
           */
          @Disconnect
          public void disconnect() {
              setClient(null);
          }
      
          /**
           * Are we connected
           */
          @ValidateConnection
          public boolean isConnected() {
              return getClient() != null;
          }
      
          /**
           * Id used only when debuging.
           */
          @ConnectionIdentifier
          public String connectionId() {
              return "001";
          }
      
          /**
           * Description for address
           */
          @Configurable
          @Default("http://devkit-cookbook.cloudhub.io/soap")
          private String address;
      
          public String getAddress() {
              return address;
          }
      
          public void setAddress(String address) {
              this.address = address;
          }
      
          private MuleCookBookClient client;
      
          public MuleCookBookClient getClient() {
              return client;
          }
      
          public void setClient(MuleCookBookClient client) {
              this.client = client;
          }
      
      }
  4. MuleCookBookClient の宣言を CookbookConnector.java から削除し、新しい MuleCookBookClient クライアントがインスタンス化されているメソッドを削除します。

    	private MuleCookBookClient client;
    
    	@Start
    	public void initialize() {
    		client = new MuleCookBookClient(config.getAddress());
    	}
  5. getRecentlyAdded メソッド (@Processor のアノテーション付き) で、getclient メソッドをコールします。このメソッドは ConnectorConfig.java で定義されている必要があります。

    /**
     * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
     * a copy of which has been included with this distribution in the LICENSE.md file.
     */
    
    package org.mule.modules.cookbook;
    
    import java.util.List;
    
    import org.mule.api.annotations.Config;
    import org.mule.api.annotations.Connector;
    import org.mule.api.annotations.Processor;
    import org.mule.modules.cookbook.config.ConnectorConfig;
    
    import com.cookbook.tutorial.service.Recipe;
    
    /**
     * Anypoint Connector
     *
     * @author MuleSoft, Inc.
     */
    @Connector(name = "cookbook", friendlyName = "Cookbook")
    public class CookbookConnector {
    
        @Config
        ConnectorConfig config;
    
        /**
         * Returns the list of recently added recipes
         *
         * {@sample.xml ../../../doc/cookbook-connector.xml.sample cook-book:getRecentlyAdded}
         *
         * @return A list of the recently added recipes
         */
        @Processor
        public List<Recipe> getRecentlyAdded() {
            return config.getClient().getRecentlyAdded();
        }
    
        public ConnectorConfig getConfig() {
            return config;
        }
    
        public void setConfig(ConnectorConfig config) {
            this.config = config;
        }
    
    }

情報:

このバージョンをインストールし、前に作成した Mule アプリケーションを実行すると、「SAXParseException」で失敗します。設定にユーザ名とパスワードを追加する必要があるためです。

コネクタのグローバル設定を開き、[Username (ユーザ名)] と [Password (パスワード)] の 2 つの新しい項目があることを確認します。これらを設定し、アプリケーションを再度実行します。

connection management

以下に、更新された Mule アプリケーションの XML を示します。

<?xml version="1.0" encoding="UTF-8"?>

<mule xmlns:cookbook="http://www.mulesoft.org/schema/mule/cookbook" xmlns:json="http://www.mulesoft.org/schema/mule/json" xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
      xmlns:spring="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/json http://www.mulesoft.org/schema/mule/json/current/mule-json.xsd
http://www.mulesoft.org/schema/mule/cookbook http://www.mulesoft.org/schema/mule/cookbook/current/mule-cookbook.xsd">
    <http:listener-config name="HTTP_Listener_Configuration" host="0.0.0.0" port="8081" doc:name="HTTP Listener Configuration"/>
    <cookbook:config name="Cookbook__Configuration" doc:name="Cookbook: Configuration type config" password="admin" username="admin"/>
    <flow name="mule-appFlow">
        <http:listener config-ref="HTTP_Listener_Configuration" path="/get-recently" doc:name="HTTP"/>
        <cookbook:get-recently-added config-ref="Cookbook__Configuration" doc:name="Cookbook"/>
        <json:object-to-json-transformer doc:name="Object to JSON"/>
    </flow>
</mule>
  • 接続のテストについて理解してください。

  • DevKit を使用した認証のセットアップに関するドキュメントを参照してください。

例外およびエラー処理の向上

このセクションでは、例外処理と再接続を向上する方法について説明します。

@Handler を使用した例外処理

@Handler 機能は、例外処理のコードの重複を回避するのに役立ち、コードを読みやすくします。

API から取得したメッセージを処理するときに、そのメッセージが役に立たないことがわかり、それを改善する方法がわかっている場合は、@Handler メカニズムを使用して、充実したエラーメッセージをユーザに提供します。

このしくみを確認するため、Cookbook SDK create() コールによりスローされる InvalidEntityException のコネクタコードにハンドラを作成しましょう。

MuleCookBookClient.java の次の create() コールは InvalidEntityException をスローします。
@Override
   public CookBookEntity create(CookBookEntity entity) throws InvalidEntityException,
           SessionExpiredException {
       Create request = factory.createCreate();
       request.setEntity(entity);
       try {
           return port.create(request, token).getReturn();
       } catch (InvalidTokenException e) {
           logger.warn("Should never happen.", e);
           throw new RuntimeException(e);
       }
     }
  1. メニュー内を右クリックし、Anypoint コネクタコンポーネントの項目に移動するか、[New (新規)] > [Other (その他)] を選択し、ウィザードで該当のオプションを選択して、新しい Anypoint コネクタコンポーネント​を作成します。

    new component
  2. パッケージ、コンポーネントの種類、クラス名を選択し、[Finish (完了)] をクリックします。

    new component 2
  3. InvalidEntityException がスローされた場合のエラーメッセージを改善します。

    package org.mule.modules.cookbook.handler;
    
    import org.mule.api.annotations.Handle;
    import org.mule.api.annotations.components.Handler;
    
    import com.cookbook.tutorial.service.InvalidEntityException;
    
    @Handler
    public class CookbookHandler {
    
        @Handle
        public void handleException(Exception ex) throws Exception {
            if (ex instanceof InvalidEntityException) {
                throw new RuntimeException("You cannot provide an Id when creating an Ingredient");
            } else {
                // Just let it go
                throw ex;
            }
        }
    
    }

    完全な​ソースコードを確認してください。

  4. Ingredient (材料) を作成するための新しいプロセッサを追加し、含めた例外ハンドラがどのように参照されるかを確認します。

    /**
     * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
     * a copy of which has been included with this distribution in the LICENSE.md file.
     */
    
    package org.mule.modules.cookbook;
    
    import java.util.List;
    
    import org.mule.api.annotations.Config;
    import org.mule.api.annotations.Connector;
    import org.mule.api.annotations.Processor;
    import org.mule.api.annotations.lifecycle.OnException;
    import org.mule.api.annotations.param.Default;
    import org.mule.modules.cookbook.config.ConnectorConfig;
    import org.mule.modules.cookbook.handler.CookbookHandler;
    
    import com.cookbook.tutorial.service.Ingredient;
    import com.cookbook.tutorial.service.InvalidEntityException;
    import com.cookbook.tutorial.service.Recipe;
    import com.cookbook.tutorial.service.SessionExpiredException;
    
    /**
     * Anypoint Connector
     *
     * @author MuleSoft, Inc.
     */
    @Connector(name = "cookbook", friendlyName = "Cookbook")
    public class CookbookConnector {
    
        @Config
        ConnectorConfig config;
    
        /**
         * Returns the list of recently added recipes
         *
         * {@sample.xml ../../../doc/cookbook-connector.xml.sample cook-book:getRecentlyAdded}
         *
         * @return A list of the recently added recipes
         */
        @Processor
        public List<Recipe> getRecentlyAdded() {
            return config.getClient().getRecentlyAdded();
        }
    
        /**
         * Description for createIngredient
         *
         * {@sample.xml ../../../doc/cookbook-connector.xml.sample cook-book:createIngredient}
         *
         * @param Ingredient
         *            Ingredient to be created
         * @return return Ingredient with Id from the system.
         *
         * @throws InvalidTokenException
         * @throws SessionExpiredException
         * @throws InvalidEntityException
         */
        @Processor
        @OnException(handler = CookbookHandler.class)
        public Ingredient createIngredient(@Default("#[payload]") Ingredient Ingredient) throws InvalidEntityException, SessionExpiredException {
            return (Ingredient) config.getClient().create(Ingredient);
        }
    
        public ConnectorConfig getConfig() {
            return config;
        }
    
        public void setConfig(ConnectorConfig config) {
            this.config = config;
        }
    
    }

    完全な​ソースコードを確認してください。

セッションの有効期限の処理

ユーザはセッションの有効期限を処理するためのカスタムコードを Mule アプリケーションに追加する必要はありません。DevKit はこれを手際よく行うメカニズムを提供します。

@Processor メソッドに @ReconnectOn 例外のアノテーションを付加します。

/**
 * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
 * a copy of which has been included with this distribution in the LICENSE.md file.
 */

package org.mule.modules.cookbook;

import java.util.List;

import org.mule.api.annotations.Config;
import org.mule.api.annotations.Connector;
import org.mule.api.annotations.Processor;
import org.mule.api.annotations.ReconnectOn;
import org.mule.api.annotations.lifecycle.OnException;
import org.mule.api.annotations.param.Default;
import org.mule.modules.cookbook.config.ConnectorConfig;
import org.mule.modules.cookbook.handler.CookbookHandler;

import com.cookbook.tutorial.service.Ingredient;
import com.cookbook.tutorial.service.InvalidEntityException;
import com.cookbook.tutorial.service.Recipe;
import com.cookbook.tutorial.service.SessionExpiredException;

/**
 * Anypoint Connector
 *
 * @author MuleSoft, Inc.
 */
@Connector(name = "cookbook", friendlyName = "Cookbook")
public class CookbookConnector {

    @Config
    ConnectorConfig config;

    /**
     * Returns the list of recently added recipes
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:getRecentlyAdded}
     *
     * @return A list of the recently added recipes
     */
    @Processor
    public List<Recipe> getRecentlyAdded() {
        return config.getClient().getRecentlyAdded();
    }

    /**
     * Description for createIngredient
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:createIngredient}
     *
     * @param Ingredient
     *            Ingredient to be created
     * @return return Ingredient with Id from the system.
     *
     * @throws InvalidTokenException
     * @throws SessionExpiredException
     * @throws InvalidEntityException
     */
    @Processor
    @OnException(handler = CookbookHandler.class)
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    public Ingredient createIngredient(@Default("#[payload]") Ingredient Ingredient) throws InvalidEntityException, SessionExpiredException {
        return (Ingredient) config.getClient().create(Ingredient);
    }

    public ConnectorConfig getConfig() {
        return config;
    }

    public void setConfig(ConnectorConfig config) {
        this.config = config;
    }

}

完全な​ソースコードを確認してください。

Mule アプリケーションでは、Mule アプリケーションでセッションの有効期限の処理ができるように再接続戦略を設定できます。

Cookbook コネクタのグローバル設定ダイアログウィンドウ

reconnect

生成された、Cookbook コネクタ設定要素の XML は次のようになります。

<cookbook:config-type doc:name="config" name="config" password="admin" username="admin">
    <reconnect/>
</cookbook:config-type>

DataSense の追加

概要

DataSense では、コネクタが対象リソースの API を判別できるため、Mule アプリケーションを作成する場合に、デザイン時のユーザエクスペリエンスが向上します。

DataSense は省略可能ですが、これを使用すると、コネクタユーザはサービスのエンティティのメタデータを取得できます。

情報:

このチュートリアルでは、静的 DataSense モデルを使用します。つまり、エンティティは固定され、事前に認識されており、変更されません。このモデルでサポートされる項目も固定されています。

DataSense を使用する

Cookbook で DataSense を使用する手順は、次のとおりです。

  1. {mule} サービスのエンティティを分析します。エンティティは、CookBookEntity から拡張されたシンプルな Recipe (レシピ) と Ingredient (材料) の 2 つのみです。

  2. Anypoint Studio 内で createIngredient 操作がどのように表示されるか、また、createIngredient 操作が他のコンポーネントとどのようにやり取りするかを確認します。

    /**
     * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
     * a copy of which has been included with this distribution in the LICENSE.md file.
     */
    
    package org.mule.modules.cookbook;
    
    import java.util.List;
    
    import org.mule.api.annotations.Config;
    import org.mule.api.annotations.Connector;
    import org.mule.api.annotations.Processor;
    import org.mule.api.annotations.ReconnectOn;
    import org.mule.api.annotations.lifecycle.OnException;
    import org.mule.api.annotations.param.Default;
    import org.mule.modules.cookbook.config.ConnectorConfig;
    import org.mule.modules.cookbook.handler.CookbookHandler;
    
    import com.cookbook.tutorial.service.Ingredient;
    import com.cookbook.tutorial.service.InvalidEntityException;
    import com.cookbook.tutorial.service.Recipe;
    import com.cookbook.tutorial.service.SessionExpiredException;
    
    /**
     * Anypoint Connector
     *
     * @author MuleSoft, Inc.
     */
    @Connector(name = "cookbook", friendlyName = "Cookbook")
    public class CookbookConnector {
    
        @Config
        ConnectorConfig config;
    
        /**
         * Returns the list of recently added recipes
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:getRecentlyAdded}
         *
         * @return A list of the recently added recipes
         */
        @Processor
        public List<Recipe> getRecentlyAdded() {
            return config.getClient().getRecentlyAdded();
        }
    
        /**
         * Description for createIngredient
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:createIngredient}
         *
         * @param Ingredient
         *            Ingredient to be created
         * @return return Ingredient with Id from the system.
         *
         * @throws InvalidTokenException
         * @throws SessionExpiredException
         * @throws InvalidEntityException
         */
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public Ingredient createIngredient(@Default("#[payload]") Ingredient Ingredient) throws InvalidEntityException, SessionExpiredException {
            return (Ingredient) config.getClient().create(Ingredient);
        }
    
        public ConnectorConfig getConfig() {
            return config;
        }
    
        public void setConfig(ConnectorConfig config) {
            this.config = config;
        }
    
    }

材料の処理

Ingredient (材料) を受け取って、サーバから入力された追加の項目と共に Ingredient (材料) を返すように操作を定義しました。

では、材料を処理しましょう。

  1. 入力メタデータを調べて、想定される出力が POJO であることと、想定される Ingredient (材料) の項目を確認します。

    datasense expected ingredients
  2. 出力メタデータが予期されていることを確認します。

    datasense ingredients
  3. [Transform Message] 要素をコネクタの背後または後にドラッグアンドドロップします。入力/出力構造が自動的に収集されます。

    datasense input
    Figure 2. コネクタの出力を受け取る Transform Message

    DevKit は静的メタデータを自動生成するため、コネクタでメタデータ情報の伝播方法が認識されることが自動的に保証されます。

レシピの処理

エンティティは Ingredient (材料) だけではありません。Recipe (レシピ) もあります。そのため、モデルに含まれるエンティティごとに 1 つのメソッドを持つことは望まれません。

CookBookEntity クラスのみと連携するようにコネクタを変更します。

  1. 操作を作成します。

    /**
     * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
     * a copy of which has been included with this distribution in the LICENSE.md file.
     */
    
    package org.mule.modules.cookbook;
    
    import java.util.List;
    
    import org.mule.api.annotations.Config;
    import org.mule.api.annotations.Connector;
    import org.mule.api.annotations.Processor;
    import org.mule.api.annotations.ReconnectOn;
    import org.mule.api.annotations.lifecycle.OnException;
    import org.mule.api.annotations.param.Default;
    import org.mule.api.annotations.param.RefOnly;
    import org.mule.modules.cookbook.handler.CookbookHandler;
    import org.mule.modules.cookbook.config.ConnectorConfig;
    
    import com.cookbook.tutorial.service.CookBookEntity;
    import com.cookbook.tutorial.service.InvalidEntityException;
    import com.cookbook.tutorial.service.InvalidTokenException;
    import com.cookbook.tutorial.service.NoSuchEntityException;
    import com.cookbook.tutorial.service.Recipe;
    import com.cookbook.tutorial.service.SessionExpiredException;
    
    /**
     * Anypoint Connector
     *
     * @author MuleSoft, Inc.
     */
    @Connector(name = "cookbook", friendlyName = "Cookbook")
    public class CookbookConnector {
    
    	@Config
    	private
        ConnectorConfig config;
    
        /**
         * Get a list of the most recently added Ingredients
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:getRecentlyAdded}
         *
         * @return List of recently added Ingredients.
         *
         */
        @Processor
        public List<Recipe> getRecentlyAdded() {
            return config.getClient().getRecentlyAdded();
        }
    
    	/**
    	 * Description for create
    	 *
    	 * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:create}
    	 *
    	 * @param entity Ingredient to be created
    	 * @return return Ingredient with Id from the system.
    	 *
    	 * @throws InvalidTokenException
    	 * @throws SessionExpiredException
    	 * @throws InvalidEntityException
    	 */
    	@Processor
    	@OnException (handler=CookbookHandler.class)
    	@ReconnectOn(exceptions = { SessionExpiredException.class })
    	public CookBookEntity create(@Default("#[payload]") @RefOnly CookBookEntity entity) throws InvalidEntityException, SessionExpiredException {
    		return config.getClient().create(entity);
    	}
    
    	/**
    	 * Description for update
    	 *
    	 * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:update}
    	 *
    	 * @param entity Ingredient to be updated
    	 * @return return Ingredient with Id from the system.
    	 *
    	 * @throws SessionExpiredException
    	 * @throws InvalidEntityException
    	 * @throws NoSuchEntityException
    	 */
    	@Processor
    	@OnException (handler=CookbookHandler.class)
    	@ReconnectOn(exceptions = { SessionExpiredException.class })
    	public CookBookEntity update(@Default("#[payload]") @RefOnly CookBookEntity entity) throws InvalidEntityException, SessionExpiredException, NoSuchEntityException {
    		return config.getClient().update(entity);
    	}
    
    	/**
    	 * Description for get
    	 *
    	 * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:get}
    	 *
    	 * @param id Id of the entity to retrieve
    	 * @return return Ingredient with Id from the system.
    	 *
    	 * @throws SessionExpiredException
    	 * @throws InvalidEntityException
    	 * @throws NoSuchEntityException
    	 */
    	@Processor
    	@OnException (handler=CookbookHandler.class)
    	@ReconnectOn(exceptions = { SessionExpiredException.class })
    	public CookBookEntity get(@Default("1") Integer id) throws InvalidEntityException, SessionExpiredException, NoSuchEntityException {
    		return config.getClient().get(id);
    	}
    
    	/**
    	 * Description for get
    	 *
    	 * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:get}
    	 *
    	 * @param id Id of the entity to retrieve
    	 * @return return Ingredient with Id from the system.
    	 *
    	 * @throws SessionExpiredException
    	 * @throws NoSuchEntityException
    	 */
    	@Processor
    	@OnException (handler=CookbookHandler.class)
    	@ReconnectOn(exceptions = { SessionExpiredException.class })
    	public void delete(@Default("1") Integer id) throws NoSuchEntityException, SessionExpiredException {
    		config.getClient().delete(id);
    	}
    
    	public ConnectorConfig getConfig() {
    		return config;
    	}
    
    	public void setConfig(ConnectorConfig config) {
    		this.config = config;
    	}
    
    }
  2. @RefOnly アノテーションを使用して、入力は参照としてのみ指定できること (抽象クラスの処理に関する DevKit の制限のため) を DevKit に指示します。

    これが UI とユーザエクスペリエンスにどのように影響するかを確認しましょう。

    Studio は入力または出力の種別を判別できなくなりました。

    ref only input
    Figure 3. @RefOnly の場合のコネクタの入力
    ref only output
    Figure 4. 抽象クラスの場合のコネクタの出力

次のセクションでは、DataSense 対応のユーザエクスペリエンスを戻します。

create、update、get、および delete 操作を使用するコネクタの完全なソースコードは、​こちらを参照してください。

MetaDataCategory の実装

@MetaDataCategory を使用して DataSense を実装するには、2 つのステップに実装を分割する必要があります。キーを取得するステップとキーについて説明するステップです。

  1. Anypoint の [DevKit Component (DevKit コンポーネント)] ウィザードを使用して新しい MetaDataCategory を作成します。

    new metadatacategory
  2. Recipe (レシピ) 用と Ingredient (材料) 用の 2 つのキーを取得するように、@MetaDataKeyRetriever のアノテーションが付加されたメソッドを変更します。

    package org.mule.modules.cookbook.datasense;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.inject.Inject;
    
    import org.mule.api.annotations.MetaDataKeyRetriever;
    import org.mule.api.annotations.MetaDataRetriever;
    import org.mule.api.annotations.components.MetaDataCategory;
    import org.mule.common.metadata.DefaultMetaData;
    import org.mule.common.metadata.DefaultMetaDataKey;
    import org.mule.common.metadata.MetaData;
    import org.mule.common.metadata.MetaDataKey;
    import org.mule.common.metadata.MetaDataModel;
    import org.mule.common.metadata.builder.DefaultMetaDataBuilder;
    import org.mule.common.metadata.builder.PojoMetaDataBuilder;
    
    import com.cookbook.tutorial.service.Ingredient;
    import com.cookbook.tutorial.service.Recipe;
    
    @MetaDataCategory
    public class DataSenseResolver {
    
        /**
         * Retrieves the list of keys
         */
        @MetaDataKeyRetriever
        public List<MetaDataKey> getMetaDataKeys() throws Exception {
            List<MetaDataKey> keys = new ArrayList<MetaDataKey>();
    
            // Generate the keys
            keys.add(new DefaultMetaDataKey("id1", "Ingredient"));
            keys.add(new DefaultMetaDataKey("id2", "Recipe"));
    
            return keys;
        }
    
        /**
         * Get MetaData given the Key the user selects
         *
         * @param key
         *            The key selected from the list of valid keys
         * @return The MetaData model of that corresponds to the key
         * @throws Exception
         *             If anything fails
         */
        @MetaDataRetriever
        public MetaData getMetaData(MetaDataKey key) throws Exception {
            DefaultMetaDataBuilder builder = new DefaultMetaDataBuilder();
            // Since our model is static and we can simply create the pojo model.
            PojoMetaDataBuilder<?> pojoObject = null;
            if ("id1".equals(key.getId())) {
                pojoObject = builder.createPojo(Ingredient.class);
            } else if ("id2".equals(key.getId())) {
                pojoObject = builder.createPojo(Recipe.class);
            } else {
                throw new RuntimeException("Invalid key:" + key.getId());
            }
            MetaDataModel model = pojoObject.build();
            MetaData metaData = new DefaultMetaData(model);
    
            return metaData;
        }
    
    
        @Inject
        private CookbookConnector connector;
    
    
        public CookbookConnector getConnector() {
            return connector;
        }
    
        public void setConnector(CookbookConnector connector) {
            this.connector = connector;
        }
    }
  3. 説明を取得するように、@MetaDataRetriever のアノテーションが付加されたメソッドを変更します。静的モデルを使用しているため、POJO モデルのみを作成できます。

    package org.mule.modules.cookbook.datasense;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.inject.Inject;
    
    import org.mule.api.annotations.MetaDataKeyRetriever;
    import org.mule.api.annotations.MetaDataRetriever;
    import org.mule.api.annotations.components.MetaDataCategory;
    import org.mule.common.metadata.DefaultMetaData;
    import org.mule.common.metadata.DefaultMetaDataKey;
    import org.mule.common.metadata.MetaData;
    import org.mule.common.metadata.MetaDataKey;
    import org.mule.common.metadata.MetaDataModel;
    import org.mule.common.metadata.builder.DefaultMetaDataBuilder;
    import org.mule.common.metadata.builder.PojoMetaDataBuilder;
    
    import com.cookbook.tutorial.service.Ingredient;
    import com.cookbook.tutorial.service.Recipe;
    
    @MetaDataCategory
    public class DataSenseResolver {
    
        /**
         * Retrieves the list of keys
         */
        @MetaDataKeyRetriever
        public List<MetaDataKey> getMetaDataKeys() throws Exception {
            List<MetaDataKey> keys = new ArrayList<MetaDataKey>();
    
            // Generate the keys
            keys.add(new DefaultMetaDataKey("id1", "Ingredient"));
            keys.add(new DefaultMetaDataKey("id2", "Recipe"));
    
            return keys;
        }
    
        /**
         * Get MetaData given the Key the user selects
         *
         * @param key
         *            The key selected from the list of valid keys
         * @return The MetaData model of that corresponds to the key
         * @throws Exception
         *             If anything fails
         */
        @MetaDataRetriever
        public MetaData getMetaData(MetaDataKey key) throws Exception {
            DefaultMetaDataBuilder builder = new DefaultMetaDataBuilder();
            // Since our model is static and we can simply create the pojo model.
            PojoMetaDataBuilder<?> pojoObject = null;
            if ("id1".equals(key.getId())) {
                pojoObject = builder.createPojo(Ingredient.class);
            } else if ("id2".equals(key.getId())) {
                pojoObject = builder.createPojo(Recipe.class);
            } else {
                throw new RuntimeException("Invalid key:" + key.getId());
            }
            MetaDataModel model = pojoObject.build();
            MetaData metaData = new DefaultMetaData(model);
    
            return metaData;
        }
    
    
        @Inject
        private CookbookConnector connector;
    
    
        public CookbookConnector getConnector() {
            return connector;
        }
    
        public void setConnector(CookbookConnector connector) {
            this.connector = connector;
        }
    }

    完全な​ソースコードを確認してください。

これをコネクタで使用するには、ユーザがエンティティを選択できるように @Processor を変更します。

  1. @Connector クラスに @MetaDataScope アノテーションを付加します。これにより、デフォルトの MetaDataCategory が設定され、ユーザが @MetaDataKeyParam を含む @Processor を選択するたびにそのデフォルトが使用されます。

    /**
     * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
     * a copy of which has been included with this distribution in the LICENSE.md file.
     */
    
    package org.mule.modules.cookbook;
    
    import java.util.List;
    
    import org.mule.api.annotations.Config;
    import org.mule.api.annotations.Connector;
    import org.mule.api.annotations.MetaDataScope;
    import org.mule.api.annotations.Processor;
    import org.mule.api.annotations.ReconnectOn;
    import org.mule.api.annotations.lifecycle.OnException;
    import org.mule.api.annotations.param.Default;
    import org.mule.api.annotations.param.MetaDataKeyParam;
    import org.mule.api.annotations.param.MetaDataKeyParamAffectsType;
    import org.mule.api.annotations.param.RefOnly;
    import org.mule.modules.cookbook.config.ConnectorConfig;
    import org.mule.modules.cookbook.datasense.DataSenseResolver;
    import org.mule.modules.cookbook.handler.CookbookHandler;
    
    import com.cookbook.tutorial.service.CookBookEntity;
    import com.cookbook.tutorial.service.InvalidEntityException;
    import com.cookbook.tutorial.service.NoSuchEntityException;
    import com.cookbook.tutorial.service.Recipe;
    import com.cookbook.tutorial.service.SessionExpiredException;
    
    /**
     * Anypoint Connector
     *
     * @author MuleSoft, Inc.
     */
    @Connector(name = "cookbook", friendlyName = "Cookbook")
    @MetaDataScope(DataSenseResolver.class)
    public class CookbookConnector {
    
        @Config
        ConnectorConfig config;
    
        /**
         * Returns the list of recently added recipes
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:getRecentlyAdded}
         *
         * @return A list of the recently added recipes
         */
        @Processor
        public List<Recipe> getRecentlyAdded() {
            return config.getClient().getRecentlyAdded();
        }
    
        /**
         * Description for create
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:create}
         *
         * @param type
         *            Object Type
         * @param entity
         *            Ingredient to be created
         * @return return Ingredient with Id from the system.
         *
         * @throws InvalidTokenException
         * @throws SessionExpiredException
         * @throws InvalidEntityException
         */
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public CookBookEntity create(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly CookBookEntity entity)
                throws InvalidEntityException, SessionExpiredException {
            return config.getClient().create(entity);
        }
    
        /**
         * Description for update
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:update}
         *
         * @param type
         *            Object Type
         * @param entity
         *            Ingredient to be updated
         * @return return Ingredient with Id from the system.
         *
         * @throws SessionExpiredException
         * @throws InvalidEntityException
         * @throws NoSuchEntityException
         */
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public CookBookEntity update(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly CookBookEntity entity)
                throws InvalidEntityException, SessionExpiredException, NoSuchEntityException {
            return config.getClient().update(entity);
        }
    
        /**
         * Description for get
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:get}
         *
         * @param type
         *            Object Type
         * @param id
         *            Id of the entity to retrieve
         * @return return Ingredient with Id from the system.
         *
         * @throws SessionExpiredException
         * @throws InvalidEntityException
         * @throws NoSuchEntityException
         */
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public CookBookEntity get(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.OUTPUT) String type, @Default("1") Integer id) throws InvalidEntityException,
                SessionExpiredException, NoSuchEntityException {
            return config.getClient().get(id);
        }
    
        /**
         * Description for get
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:get}
         *
         * @param id
         *            Id of the entity to retrieve
         * @return return Ingredient with Id from the system.
         *
         * @throws SessionExpiredException
         * @throws NoSuchEntityException
         */
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public void delete(@Default("1") Integer id) throws NoSuchEntityException, SessionExpiredException {
            config.getClient().delete(id);
        }
    
        public ConnectorConfig getConfig() {
            return config;
        }
    
        public void setConfig(ConnectorConfig config) {
            this.config = config;
        }
    
    }
  2. 入力と出力について説明するには、@MetaDataKeyParam のアノテーションが付加された文字列を追加し、affects=MetaDataKeyParamAffectsType.BOTH を追加して、文字列が入力と出力に影響することを指定します。

    /**
     * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
     * a copy of which has been included with this distribution in the LICENSE.md file.
     */
    
    package org.mule.modules.cookbook;
    
    import java.util.List;
    
    import org.mule.api.annotations.Config;
    import org.mule.api.annotations.Connector;
    import org.mule.api.annotations.MetaDataScope;
    import org.mule.api.annotations.Processor;
    import org.mule.api.annotations.ReconnectOn;
    import org.mule.api.annotations.lifecycle.OnException;
    import org.mule.api.annotations.param.Default;
    import org.mule.api.annotations.param.MetaDataKeyParam;
    import org.mule.api.annotations.param.MetaDataKeyParamAffectsType;
    import org.mule.api.annotations.param.RefOnly;
    import org.mule.modules.cookbook.config.ConnectorConfig;
    import org.mule.modules.cookbook.datasense.DataSenseResolver;
    import org.mule.modules.cookbook.handler.CookbookHandler;
    
    import com.cookbook.tutorial.service.CookBookEntity;
    import com.cookbook.tutorial.service.InvalidEntityException;
    import com.cookbook.tutorial.service.NoSuchEntityException;
    import com.cookbook.tutorial.service.Recipe;
    import com.cookbook.tutorial.service.SessionExpiredException;
    
    /**
     * Anypoint Connector
     *
     * @author MuleSoft, Inc.
     */
    @Connector(name = "cookbook", friendlyName = "Cookbook")
    @MetaDataScope(DataSenseResolver.class)
    public class CookbookConnector {
    
        @Config
        ConnectorConfig config;
    
        /**
         * Returns the list of recently added recipes
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:getRecentlyAdded}
         *
         * @return A list of the recently added recipes
         */
        @Processor
        public List<Recipe> getRecentlyAdded() {
            return config.getClient().getRecentlyAdded();
        }
    
        /**
         * Description for create
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:create}
         *
         * @param type
         *            Object Type
         * @param entity
         *            Ingredient to be created
         * @return return Ingredient with Id from the system.
         *
         * @throws InvalidTokenException
         * @throws SessionExpiredException
         * @throws InvalidEntityException
         */
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public CookBookEntity create(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly CookBookEntity entity)
                throws InvalidEntityException, SessionExpiredException {
            return config.getClient().create(entity);
        }
    
        /**
         * Description for update
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:update}
         *
         * @param type
         *            Object Type
         * @param entity
         *            Ingredient to be updated
         * @return return Ingredient with Id from the system.
         *
         * @throws SessionExpiredException
         * @throws InvalidEntityException
         * @throws NoSuchEntityException
         */
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public CookBookEntity update(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly CookBookEntity entity)
                throws InvalidEntityException, SessionExpiredException, NoSuchEntityException {
            return config.getClient().update(entity);
        }
    
        /**
         * Description for get
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:get}
         *
         * @param type
         *            Object Type
         * @param id
         *            Id of the entity to retrieve
         * @return return Ingredient with Id from the system.
         *
         * @throws SessionExpiredException
         * @throws InvalidEntityException
         * @throws NoSuchEntityException
         */
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public CookBookEntity get(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.OUTPUT) String type, @Default("1") Integer id) throws InvalidEntityException,
                SessionExpiredException, NoSuchEntityException {
            return config.getClient().get(id);
        }
    
        /**
         * Description for get
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:get}
         *
         * @param id
         *            Id of the entity to retrieve
         * @return return Ingredient with Id from the system.
         *
         * @throws SessionExpiredException
         * @throws NoSuchEntityException
         */
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public void delete(@Default("1") Integer id) throws NoSuchEntityException, SessionExpiredException {
            config.getClient().delete(id);
        }
    
        public ConnectorConfig getConfig() {
            return config;
        }
    
        public void setConfig(ConnectorConfig config) {
            this.config = config;
        }
    
    }
  3. get 操作で、影響が出力にのみ適用されることを指定する必要があるため、若干の変更を加えます。

    /**
     * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
     * a copy of which has been included with this distribution in the LICENSE.md file.
     */
    
    package org.mule.modules.cookbook;
    
    import java.util.List;
    
    import org.mule.api.annotations.Config;
    import org.mule.api.annotations.Connector;
    import org.mule.api.annotations.MetaDataScope;
    import org.mule.api.annotations.Processor;
    import org.mule.api.annotations.ReconnectOn;
    import org.mule.api.annotations.lifecycle.OnException;
    import org.mule.api.annotations.param.Default;
    import org.mule.api.annotations.param.MetaDataKeyParam;
    import org.mule.api.annotations.param.MetaDataKeyParamAffectsType;
    import org.mule.api.annotations.param.RefOnly;
    import org.mule.modules.cookbook.config.ConnectorConfig;
    import org.mule.modules.cookbook.datasense.DataSenseResolver;
    import org.mule.modules.cookbook.handler.CookbookHandler;
    
    import com.cookbook.tutorial.service.CookBookEntity;
    import com.cookbook.tutorial.service.InvalidEntityException;
    import com.cookbook.tutorial.service.NoSuchEntityException;
    import com.cookbook.tutorial.service.Recipe;
    import com.cookbook.tutorial.service.SessionExpiredException;
    
    /**
     * Anypoint Connector
     *
     * @author MuleSoft, Inc.
     */
    @Connector(name = "cookbook", friendlyName = "Cookbook")
    @MetaDataScope(DataSenseResolver.class)
    public class CookbookConnector {
    
        @Config
        ConnectorConfig config;
    
        /**
         * Returns the list of recently added recipes
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:getRecentlyAdded}
         *
         * @return A list of the recently added recipes
         */
        @Processor
        public List<Recipe> getRecentlyAdded() {
            return config.getClient().getRecentlyAdded();
        }
    
        /**
         * Description for create
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:create}
         *
         * @param type
         *            Object Type
         * @param entity
         *            Ingredient to be created
         * @return return Ingredient with Id from the system.
         *
         * @throws InvalidTokenException
         * @throws SessionExpiredException
         * @throws InvalidEntityException
         */
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public CookBookEntity create(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly CookBookEntity entity)
                throws InvalidEntityException, SessionExpiredException {
            return config.getClient().create(entity);
        }
    
        /**
         * Description for update
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:update}
         *
         * @param type
         *            Object Type
         * @param entity
         *            Ingredient to be updated
         * @return return Ingredient with Id from the system.
         *
         * @throws SessionExpiredException
         * @throws InvalidEntityException
         * @throws NoSuchEntityException
         */
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public CookBookEntity update(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly CookBookEntity entity)
                throws InvalidEntityException, SessionExpiredException, NoSuchEntityException {
            return config.getClient().update(entity);
        }
    
        /**
         * Description for get
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:get}
         *
         * @param type
         *            Object Type
         * @param id
         *            Id of the entity to retrieve
         * @return return Ingredient with Id from the system.
         *
         * @throws SessionExpiredException
         * @throws InvalidEntityException
         * @throws NoSuchEntityException
         */
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public CookBookEntity get(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.OUTPUT) String type, @Default("1") Integer id) throws InvalidEntityException,
                SessionExpiredException, NoSuchEntityException {
            return config.getClient().get(id);
        }
    
        /**
         * Description for get
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:get}
         *
         * @param id
         *            Id of the entity to retrieve
         * @return return Ingredient with Id from the system.
         *
         * @throws SessionExpiredException
         * @throws NoSuchEntityException
         */
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public void delete(@Default("1") Integer id) throws NoSuchEntityException, SessionExpiredException {
            config.getClient().delete(id);
        }
    
        public ConnectorConfig getConfig() {
            return config;
        }
    
        public void setConfig(ConnectorConfig config) {
            this.config = config;
        }
    
    }
  4. 新しいコネクタが Studio にどのように表示されるかを確認します。エンティティ種別を選択して保存すると、コンボができます。これにより、メタデータが自動的に更新されます。

    datasense static

    これで、{dataWeave} でも、@Connector とやり取りする方法が認識されるようになります。

    datasense static2

コネクタの完全なソースコードは、​こちらを参照してください。

動的 DataSense

前のセクションでは、モデルが静的な場合のシナリオについて説明しました。もっと複雑なシナリオを見てみましょう。

エンティティ定義を動的に取得する方法を提供する API があります。Salesforce、NetSuite はその一例です。

ここでは、エンティティについて説明する操作が Cookbook で提供されているため、代わりにその操作を使用してエンティティと構造を取得しましょう。

  1. サポートされるエンティティを取得し、後で使用できるキーを生成します。

    package org.mule.modules.cookbook.datasense;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.inject.Inject;
    
    import org.mule.api.annotations.MetaDataKeyRetriever;
    import org.mule.api.annotations.MetaDataRetriever;
    import org.mule.api.annotations.components.MetaDataCategory;
    import org.mule.common.metadata.DefaultMetaData;
    import org.mule.common.metadata.DefaultMetaDataKey;
    import org.mule.common.metadata.MetaData;
    import org.mule.common.metadata.MetaDataKey;
    import org.mule.common.metadata.MetaDataModel;
    import org.mule.common.metadata.builder.DefaultMetaDataBuilder;
    import org.mule.common.metadata.builder.DynamicObjectBuilder;
    import org.mule.common.metadata.datatype.DataType;
    import org.mule.modules.cookbook.CookbookConnector;
    
    import com.cookbook.tutorial.service.CookBookEntity;
    import com.cookbook.tutorial.service.Description;
    import com.cookbook.tutorial.service.Ingredient;
    import com.cookbook.tutorial.service.InvalidEntityException;
    import com.cookbook.tutorial.service.InvalidTokenException;
    import com.cookbook.tutorial.service.NoSuchEntityException;
    import com.cookbook.tutorial.service.SessionExpiredException;
    import com.cookbook.tutorial.service.UnitType;
    
    @MetaDataCategory
    public class DataSenseResolver {
    
        @Inject
        private CookbookConnector connector;
    
        /**
         * Retrieves the list of keys
         */
        @MetaDataKeyRetriever
        public List<MetaDataKey> getMetaDataKeys() throws Exception {
            List<MetaDataKey> keys = new ArrayList<MetaDataKey>();
            List<CookBookEntity> entities = getConnector().getConfig()
                    .getClient().getEntities();
            // Generate the keys
            for (CookBookEntity entity : entities) {
                keys.add(new DefaultMetaDataKey(entity.getClass().getName() + "#"
                        + entity.getId(), entity.getName()));
            }
    
            return keys;
        }
    
        /**
         * Get MetaData given the Key the user selects
         *
         * @param key The key selected from the list of valid keys
         * @return The MetaData model of that corresponds to the key
         * @throws Exception If anything fails
         */
        @MetaDataRetriever
        public MetaData getMetaData(MetaDataKey key) throws Exception {
            DefaultMetaDataBuilder builder = new DefaultMetaDataBuilder();
            // Since our model is static and we can simply create the pojo model.
            String[] keyParts = key.getId().split("#");
            if (keyParts.length != 2) {
                throw new RuntimeException(
                        "Invalid key. Format should be 'entityType#id'");
            }
            Integer id = Integer.valueOf(keyParts[1]);
            CookBookEntity entity = (CookBookEntity) Class.forName(keyParts[0])
                    .newInstance();
            entity.setId(id);
            Description description = getConnector().getConfig().getClient()
                    .describeEntity(entity);
    
            DynamicObjectBuilder<?> dynamicObject = builder.createDynamicObject(key
                    .getId());
    
            for (Description fields : description.getInnerFields()) {
                addFields(fields, dynamicObject);
            }
    
            MetaDataModel model = builder.build();
            MetaData metaData = new DefaultMetaData(model);
    
            return metaData;
        }
    
        private void addFields(Description description,
                DynamicObjectBuilder<?> dynamicObject) {
            switch (description.getDataType()) {
            case DATE:
                dynamicObject.addSimpleField(description.getName(),
                        DataType.DATE_TIME);
                break;
            case DOUBLE:
                dynamicObject
                        .addSimpleField(description.getName(), DataType.DOUBLE);
                break;
            case INTEGER:
                dynamicObject.addSimpleField(description.getName(),
                        DataType.INTEGER);
                break;
            case LIST:
                if (description.getInnerType().equals("String")) {
                    dynamicObject.addList(description.getName()).ofSimpleField(
                            DataType.STRING);
                } else if (description.getInnerType().equals("Ingredient")) {
                    DynamicObjectBuilder<?> innerObject = dynamicObject.addList(
                            description.getName()).ofDynamicObject("ingredients");
                    try {
                        Description ingredientDescription = getConnector()
                                .getConfig().getClient()
                                .describeEntity(new Ingredient());
                        for (Description desc : ingredientDescription.getInnerFields()) {
                            addFields(desc, innerObject);
                        }
                    } catch (InvalidTokenException e) {
                        e.printStackTrace();
                    } catch (InvalidEntityException e) {
                        e.printStackTrace();
                    } catch (NoSuchEntityException e) {
                        e.printStackTrace();
                    } catch (SessionExpiredException e) {
                        e.printStackTrace();
                    }
                }
                break;
            case OBJECT:
                DynamicObjectBuilder<?> innerObject = dynamicObject
                        .addDynamicObjectField(description.getName());
                for (Description field : description.getInnerFields()) {
                    addFields(field, innerObject);
                }
                break;
            case STRING:
                dynamicObject
                        .addSimpleField(description.getName(), DataType.STRING);
                break;
            case UNIT_TYPE:
                dynamicObject.addEnumField(description.getName(),
                        UnitType.class.getName());
                break;
            default:
                break;
            }
    
        }
    
        public CookbookConnector getConnector() {
            return connector;
        }
    
        public void setConnector(CookbookConnector connector) {
            this.connector = connector;
        }
    
    }
  2. 動的に変更できる構造を使用します。これを Mule で行うには、Map<String,Object> をコネクタのパラメータまたは戻り値のデータ型として使用します。

    Mule には、エンティティの MetaData の生成に役立つビルダーが用意されています。

    package org.mule.modules.cookbook.datasense;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.inject.Inject;
    
    import org.mule.api.annotations.MetaDataKeyRetriever;
    import org.mule.api.annotations.MetaDataRetriever;
    import org.mule.api.annotations.components.MetaDataCategory;
    import org.mule.common.metadata.DefaultMetaData;
    import org.mule.common.metadata.DefaultMetaDataKey;
    import org.mule.common.metadata.MetaData;
    import org.mule.common.metadata.MetaDataKey;
    import org.mule.common.metadata.MetaDataModel;
    import org.mule.common.metadata.builder.DefaultMetaDataBuilder;
    import org.mule.common.metadata.builder.DynamicObjectBuilder;
    import org.mule.common.metadata.datatype.DataType;
    import org.mule.modules.cookbook.CookbookConnector;
    
    import com.cookbook.tutorial.service.CookBookEntity;
    import com.cookbook.tutorial.service.Description;
    import com.cookbook.tutorial.service.Ingredient;
    import com.cookbook.tutorial.service.InvalidEntityException;
    import com.cookbook.tutorial.service.InvalidTokenException;
    import com.cookbook.tutorial.service.NoSuchEntityException;
    import com.cookbook.tutorial.service.SessionExpiredException;
    import com.cookbook.tutorial.service.UnitType;
    
    @MetaDataCategory
    public class DataSenseResolver {
    
        @Inject
        private CookbookConnector connector;
    
        /**
         * Retrieves the list of keys
         */
        @MetaDataKeyRetriever
        public List<MetaDataKey> getMetaDataKeys() throws Exception {
            List<MetaDataKey> keys = new ArrayList<MetaDataKey>();
            List<CookBookEntity> entities = getConnector().getConfig()
                    .getClient().getEntities();
            // Generate the keys
            for (CookBookEntity entity : entities) {
                keys.add(new DefaultMetaDataKey(entity.getClass().getName() + "#"
                        + entity.getId(), entity.getName()));
            }
    
            return keys;
        }
    
        /**
         * Get MetaData given the Key the user selects
         *
         * @param key The key selected from the list of valid keys
         * @return The MetaData model of that corresponds to the key
         * @throws Exception If anything fails
         */
        @MetaDataRetriever
        public MetaData getMetaData(MetaDataKey key) throws Exception {
            DefaultMetaDataBuilder builder = new DefaultMetaDataBuilder();
            // Since our model is static and we can simply create the pojo model.
            String[] keyParts = key.getId().split("#");
            if (keyParts.length != 2) {
                throw new RuntimeException(
                        "Invalid key. Format should be 'entityType#id'");
            }
            Integer id = Integer.valueOf(keyParts[1]);
            CookBookEntity entity = (CookBookEntity) Class.forName(keyParts[0])
                    .newInstance();
            entity.setId(id);
            Description description = getConnector().getConfig().getClient()
                    .describeEntity(entity);
    
            DynamicObjectBuilder<?> dynamicObject = builder.createDynamicObject(key
                    .getId());
    
            for (Description fields : description.getInnerFields()) {
                addFields(fields, dynamicObject);
            }
    
            MetaDataModel model = builder.build();
            MetaData metaData = new DefaultMetaData(model);
    
            return metaData;
        }
    
        private void addFields(Description description,
                DynamicObjectBuilder<?> dynamicObject) {
            switch (description.getDataType()) {
            case DATE:
                dynamicObject.addSimpleField(description.getName(),
                        DataType.DATE_TIME);
                break;
            case DOUBLE:
                dynamicObject
                        .addSimpleField(description.getName(), DataType.DOUBLE);
                break;
            case INTEGER:
                dynamicObject.addSimpleField(description.getName(),
                        DataType.INTEGER);
                break;
            case LIST:
                if (description.getInnerType().equals("String")) {
                    dynamicObject.addList(description.getName()).ofSimpleField(
                            DataType.STRING);
                } else if (description.getInnerType().equals("Ingredient")) {
                    DynamicObjectBuilder<?> innerObject = dynamicObject.addList(
                            description.getName()).ofDynamicObject("ingredients");
                    try {
                        Description ingredientDescription = getConnector()
                                .getConfig().getClient()
                                .describeEntity(new Ingredient());
                        for (Description desc : ingredientDescription.getInnerFields()) {
                            addFields(desc, innerObject);
                        }
                    } catch (InvalidTokenException e) {
                        e.printStackTrace();
                    } catch (InvalidEntityException e) {
                        e.printStackTrace();
                    } catch (NoSuchEntityException e) {
                        e.printStackTrace();
                    } catch (SessionExpiredException e) {
                        e.printStackTrace();
                    }
                }
                break;
            case OBJECT:
                DynamicObjectBuilder<?> innerObject = dynamicObject
                        .addDynamicObjectField(description.getName());
                for (Description field : description.getInnerFields()) {
                    addFields(field, innerObject);
                }
                break;
            case STRING:
                dynamicObject
                        .addSimpleField(description.getName(), DataType.STRING);
                break;
            case UNIT_TYPE:
                dynamicObject.addEnumField(description.getName(),
                        UnitType.class.getName());
                break;
            default:
                break;
            }
    
        }
    
        public CookbookConnector getConnector() {
            return connector;
        }
    
        public void setConnector(CookbookConnector connector) {
            this.connector = connector;
        }
    
    }
  3. 完全な ソースコード を確認してください。

  4. @Connector では、Map からエンティティを生成するためのコードを追加し、すべての操作で Map を返すことが必要になりました。なぜこれが重要なのでしょうか? API での一貫性を維持するためです。

    新しい Create は次のようになります。

    /**
     * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
     * a copy of which has been included with this distribution in the LICENSE.md file.
     */
    
    package org.mule.modules.cookbook;
    
    import java.util.List;
    import java.util.Map;
    
    import org.mule.api.annotations.Config;
    import org.mule.api.annotations.Connector;
    import org.mule.api.annotations.MetaDataScope;
    import org.mule.api.annotations.Processor;
    import org.mule.api.annotations.ReconnectOn;
    import org.mule.api.annotations.Transformer;
    import org.mule.api.annotations.lifecycle.OnException;
    import org.mule.api.annotations.param.Default;
    import org.mule.api.annotations.param.MetaDataKeyParam;
    import org.mule.api.annotations.param.MetaDataKeyParamAffectsType;
    import org.mule.api.annotations.param.RefOnly;
    import org.mule.modules.cookbook.config.ConnectorConfig;
    import org.mule.modules.cookbook.datasense.DataSenseResolver;
    import org.mule.modules.cookbook.handler.CookbookHandler;
    
    import com.cookbook.tutorial.service.CookBookEntity;
    import com.cookbook.tutorial.service.Ingredient;
    import com.cookbook.tutorial.service.InvalidEntityException;
    import com.cookbook.tutorial.service.NoSuchEntityException;
    import com.cookbook.tutorial.service.Recipe;
    import com.cookbook.tutorial.service.SessionExpiredException;
    import com.fasterxml.jackson.core.type.TypeReference;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    /**
     * Anypoint Connector
     *
     * @author MuleSoft, Inc.
     */
    @Connector(name = "cookbook", friendlyName = "Cookbook")
    @MetaDataScope(DataSenseResolver.class)
    public class CookbookConnector {
    
        @Config
        ConnectorConfig config;
    
        /**
         * Returns the list of recently added recipes
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:getRecentlyAdded}
         *
         * @return A list of the recently added recipes
         */
        @Processor
        public List<Recipe> getRecentlyAdded() {
            return config.getClient().getRecentlyAdded();
        }
    
        /**
         * Description for create
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:create}
         *
         * @param entity
         *            Ingredient to be created
         * @return return Ingredient with Id from the system.
         *
         * @throws InvalidTokenException
         * @throws SessionExpiredException
         * @throws InvalidEntityException
         */
        @SuppressWarnings("unchecked")
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public Map<String, Object> create(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly Map<String, Object> entity)
                throws InvalidEntityException, SessionExpiredException {
            ObjectMapper m = new ObjectMapper();
            CookBookEntity input = null;
            if (type.contains("com.cookbook.tutorial.service.Recipe")) {
                input = m.convertValue(entity, Recipe.class);
            } else if (type.contains("com.cookbook.tutorial.service.Ingredient")) {
                input = m.convertValue(entity, Ingredient.class);
            } else {
                throw new InvalidEntityException("Don't know how to handle type:" + type);
            }
            return m.convertValue(this.getConfig().getClient().create(input), Map.class);
        }
    
        /**
         * Description for update
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:update}
         *
         * @param entity
         *            Ingredient to be updated
         * @return return Ingredient with Id from the system.
         *
         * @throws SessionExpiredException
         * @throws InvalidEntityException
         * @throws NoSuchEntityException
         */
        @SuppressWarnings("unchecked")
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public Map<String, Object> update(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly Map<String, Object> entity)
                throws InvalidEntityException, SessionExpiredException, NoSuchEntityException {
            ObjectMapper m = new ObjectMapper();
            CookBookEntity input = null;
            if (type.contains("com.cookbook.tutorial.service.Recipe")) {
                input = m.convertValue(entity, Recipe.class);
            } else if (type.contains("com.cookbook.tutorial.service.Ingredient")) {
                input = m.convertValue(entity, Ingredient.class);
            } else {
                throw new InvalidEntityException("Don't know how to handle type:" + type);
            }
            return m.convertValue(this.getConfig().getClient().update(input), Map.class);
        }
    
        /**
         * Description for get
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:get}
         *
         * @param id
         *            Id of the entity to retrieve
         * @return return Ingredient with Id from the system.
         *
         * @throws SessionExpiredException
         * @throws InvalidEntityException
         * @throws NoSuchEntityException
         */
        @SuppressWarnings("unchecked")
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public Map<String, Object> get(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.OUTPUT) String type, @Default("1") Integer id) throws InvalidEntityException,
                SessionExpiredException, NoSuchEntityException {
            ObjectMapper m = new ObjectMapper();
            return m.convertValue(this.getConfig().getClient().get(id), Map.class);
        }
    
        /**
         * Description for get
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:get}
         *
         * @param id
         *            Id of the entity to retrieve
         * @return return Ingredient with Id from the system.
         *
         * @throws SessionExpiredException
         * @throws NoSuchEntityException
         */
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public void delete(@Default("1") Integer id) throws NoSuchEntityException, SessionExpiredException {
            this.getConfig().getClient().delete(id);
        }
    
        @Transformer(sourceTypes = { List.class })
        public static List<Map<String, Object>> transformJsonToComments(List<Recipe> list) {
            ObjectMapper mapper = new ObjectMapper();
            List<Map<String, Object>> result = mapper.convertValue(list, new TypeReference<List<Map<String, Object>>>() {
            });
            return result;
        }
    
        public ConnectorConfig getConfig() {
            return config;
        }
    
        public void setConfig(ConnectorConfig config) {
            this.config = config;
        }
    
    }

    入力がマップになったため、UI のフォームが前とは異なっています。

    datasense ui
  5. Mule アプリケーションでは、メタデータを更新できず、コネクタを使用できません。また、ご覧のとおり、POJO ではなくマップ構造があることに注意してください。

    datasense map

DataSense のテスト方法は、​DataSense のテストガイドを参照してください。

ページネーションの追加

ページネーションを実装する

この機能を表示するには、SDK の searchWithQuery 操作をコールするプロセッサを追加します。

ページ分割操作を使用するには、3 つのことを行う必要があります。

/**
 * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
 * a copy of which has been included with this distribution in the LICENSE.md file.
 */

package org.mule.modules.cookbook;

import java.util.List;
import java.util.Map;

import org.mule.api.annotations.Config;
import org.mule.api.annotations.Connector;
import org.mule.api.annotations.MetaDataScope;
import org.mule.api.annotations.Paged;
import org.mule.api.annotations.Processor;
import org.mule.api.annotations.ReconnectOn;
import org.mule.api.annotations.Source;
import org.mule.api.annotations.SourceStrategy;
import org.mule.api.annotations.Transformer;
import org.mule.api.annotations.lifecycle.OnException;
import org.mule.api.annotations.param.Default;
import org.mule.api.annotations.param.MetaDataKeyParam;
import org.mule.api.annotations.param.MetaDataKeyParamAffectsType;
import org.mule.api.annotations.param.RefOnly;
import org.mule.api.callback.SourceCallback;
import org.mule.modules.cookbook.config.ConnectorConfig;
import org.mule.modules.cookbook.datasense.DataSenseResolver;
import org.mule.modules.cookbook.handler.CookbookHandler;
import org.mule.modules.cookbook.pagination.CookbookPagingDelegate;
import org.mule.streaming.PagingConfiguration;
import org.mule.streaming.ProviderAwarePagingDelegate;

import com.cookbook.tutorial.client.ICookbookCallback;
import com.cookbook.tutorial.service.CookBookEntity;
import com.cookbook.tutorial.service.Ingredient;
import com.cookbook.tutorial.service.InvalidEntityException;
import com.cookbook.tutorial.service.NoSuchEntityException;
import com.cookbook.tutorial.service.Recipe;
import com.cookbook.tutorial.service.SessionExpiredException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * Anypoint Connector
 *
 * @author MuleSoft, Inc.
 */
@Connector(name = "cookbook", friendlyName = "Cookbook")
@MetaDataScope(DataSenseResolver.class)
public class CookbookConnector {

    @Config
    ConnectorConfig config;

    /**
     * Returns the list of recently added recipes
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:getRecentlyAdded}
     *
     * @return A list of the recently added recipes
     */
    @Processor
    public List<Recipe> getRecentlyAdded() {
        return config.getClient().getRecentlyAdded();
    }

    /**
     * Description for getRecentlyAddedSource
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:getRecentlyAddedSource}
     *
     * @param callback
     *            The callback that will hook the result into mule event.
     * @throws Exception
     *             When the source fails.
     */
    @Source(sourceStrategy = SourceStrategy.POLLING, pollingPeriod = 10000)
    public void getRecentlyAddedSource(final SourceCallback callback) throws Exception {

        if (this.getConfig().getClient() != null) {
            // Every 5 seconds our callback will be executed
            this.getConfig().getClient().getRecentlyAdded(new ICookbookCallback() {

                @Override
                public void execute(List<Recipe> recipes) throws Exception {
                    callback.process(recipes);
                }
            });

            if (Thread.interrupted()) {
                throw new InterruptedException();
            }
        }
    }

    /**
     * Description for create
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:create}
     *
     * @param entity
     *            Ingredient to be created
     * @return return Ingredient with Id from the system.
     *
     * @throws InvalidTokenException
     * @throws SessionExpiredException
     * @throws InvalidEntityException
     */
    @SuppressWarnings("unchecked")
    @Processor
    @OnException(handler = CookbookHandler.class)
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    public Map<String, Object> create(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly Map<String, Object> entity)
            throws InvalidEntityException, SessionExpiredException {
        ObjectMapper m = new ObjectMapper();
        CookBookEntity input = null;
        if (type.contains("com.cookbook.tutorial.service.Recipe")) {
            input = m.convertValue(entity, Recipe.class);
        } else if (type.contains("com.cookbook.tutorial.service.Ingredient")) {
            input = m.convertValue(entity, Ingredient.class);
        } else {
            throw new InvalidEntityException("Don't know how to handle type:" + type);
        }
        return m.convertValue(this.getConfig().getClient().create(input), Map.class);
    }

    /**
     * Description for update
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:update}
     *
     * @param entity
     *            Ingredient to be updated
     * @return return Ingredient with Id from the system.
     *
     * @throws SessionExpiredException
     * @throws InvalidEntityException
     * @throws NoSuchEntityException
     */
    @SuppressWarnings("unchecked")
    @Processor
    @OnException(handler = CookbookHandler.class)
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    public Map<String, Object> update(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly Map<String, Object> entity)
            throws InvalidEntityException, SessionExpiredException, NoSuchEntityException {
        ObjectMapper m = new ObjectMapper();
        CookBookEntity input = null;
        if (type.contains("com.cookbook.tutorial.service.Recipe")) {
            input = m.convertValue(entity, Recipe.class);
        } else if (type.contains("com.cookbook.tutorial.service.Ingredient")) {
            input = m.convertValue(entity, Ingredient.class);
        } else {
            throw new InvalidEntityException("Don't know how to handle type:" + type);
        }
        return m.convertValue(this.getConfig().getClient().update(input), Map.class);
    }

    /**
     * Description for get
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:get}
     *
     * @param id
     *            Id of the entity to retrieve
     * @return return Ingredient with Id from the system.
     *
     * @throws SessionExpiredException
     * @throws InvalidEntityException
     * @throws NoSuchEntityException
     */
    @SuppressWarnings("unchecked")
    @Processor
    @OnException(handler = CookbookHandler.class)
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    public Map<String, Object> get(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.OUTPUT) String type, @Default("1") Integer id) throws InvalidEntityException,
            SessionExpiredException, NoSuchEntityException {
        ObjectMapper m = new ObjectMapper();
        return m.convertValue(this.getConfig().getClient().get(id), Map.class);
    }

    /**
     * Description for get
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:get}
     *
     * @param id
     *            Id of the entity to retrieve
     * @return return Ingredient with Id from the system.
     *
     * @throws SessionExpiredException
     * @throws NoSuchEntityException
     */
    @Processor
    @OnException(handler = CookbookHandler.class)
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    public void delete(@Default("1") Integer id) throws NoSuchEntityException, SessionExpiredException {
        this.getConfig().getClient().delete(id);
    }

    /**
     * Description for queryPaginated
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:query-paginated}
     *
     *  @param query The query
     *  @param pagingConfiguration the paging configuration
     *  @return return comment
     */
    @Processor
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    @Paged (1)
    public ProviderAwarePagingDelegate<Map<String, Object>, CookbookConnector> queryPaginated( (2)
            final String query, final PagingConfiguration pagingConfiguration) (3)
            throws SessionExpiredException {
        return new CookbookPagingDelegate(query, pagingConfiguration.getFetchSize());
    }

    @Transformer(sourceTypes = { List.class })
    public static List<Map<String, Object>> recipesToMaps(List<Recipe> list) {
        ObjectMapper mapper = new ObjectMapper();
        List<Map<String, Object>> result = mapper.convertValue(list, new TypeReference<List<Map<String, Object>>>() {
        });
        return result;
    }

    @Transformer(sourceTypes = { Recipe.class })
    public static Map<String, Object> recipeToMap(Recipe recipe) {
        ObjectMapper mapper = new ObjectMapper();
        Map<String, Object> result = mapper.convertValue(recipe, new TypeReference<Map<String, Object>>() {
        });
        return result;
    }

    public ConnectorConfig getConfig() {
        return config;
    }

    public void setConfig(ConnectorConfig config) {
        this.config = config;
    }

}
1 プロセッサに @Paged アノテーションを付加します。
2 ProviderAwarePagingDelegate を返します。
3 パラメータの 1 つとして PagingConfiguration を受け取ります。

ProviderAwarePagingDelegate を実装する場合、2 つの要素を指定する必要があります。

  1. 各ページに返すリストの種別。この場合は Map<String,Object>。

  2. コネクタの種別。

これを作成するには、Anypoint の [DevKit Component (DevKit コンポーネント)] ウィザードを使用して、次の手順を実行します。

  1. 作成するパッケージを指定します。この例では、org.mule.cookbook.pagination

  2. ProviderAwarePagingDelegate を作成することを指定します。

  3. クラス名を「CookbookPagingDelegate」として設定します。

    pagination component

後は、新しいページ要求を処理するために必要なメソッドを実装するだけです。

完全なソースコードは、​CookbookPagingDelegate を参照してください。

再接続戦略がある場合、DevKit は自動的に再接続し、ページの取得を再試行します。取得されなかった最後のページを確実に再試行するには、PagingDelegate の状態を処理することが重要です。

情報:

Mule アプリケーションでのページネーションの使用

たとえば、paged アノテーション付きのプロセッサの前で foreach を使用できます。

Mule アプリケーション

pagination example

XML

<?xml version="1.0" encoding="UTF-8"?>

<mule xmlns:json="http://www.mulesoft.org/schema/mule/json" xmlns:cookbook="http://www.mulesoft.org/schema/mule/cookbook" xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
      xmlns:spring="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/cookbook http://www.mulesoft.org/schema/mule/cookbook/current/mule-cookbook.xsd
http://www.mulesoft.org/schema/mule/json http://www.mulesoft.org/schema/mule/json/current/mule-json.xsd">
    <cookbook:config name="CookBook__Connection_Management_Config" username="admin" password="admin" doc:name="CookBook: Connection Management Config">
        <reconnect/>
    </cookbook:config>
    <http:listener-config name="HTTP_Listener_Configuration" host="0.0.0.0" port="8081" doc:name="HTTP Listener Configuration"/>
    <flow name="mule-appFlow1">
        <http:listener config-ref="HTTP_Listener_Configuration" path="/page" doc:name="HTTP"/>
        <cookbook:query-paginated config-ref="CookBook__Connection_Management_Config" query="GET ALL FROM INGREDIENT" fetchSize="5" doc:name="CookBook"/>
        <foreach doc:name="For Each">
            <logger message="#[payload]" level="INFO" doc:name="Logger"/>
        </foreach>
        <set-payload value="Finished Processing" doc:name="Set Payload"/>
    </flow>
</mule>

コネクタのインストール

Anypoint DevKit プラグインの使用

基本的にコネクタのインストールは Eclipse プラグインのインストールと同じです。

DevKit プラグインからコネクタをインストールする手順は、次のとおりです。

  1. Package Explorer でプロジェクト名を右クリックし、[Anypoint Connector (Anypoint コネクタ)] > [Install Or Update (インストールまたは更新)] をクリックします。

    install1

    これにより、DevKit Maven 構築ツールが起動され、​更新サイトフォルダが生成されます。

    ファイルは、AnypointStudio 実行可能ファイルと同じディレクトリに配置されている dropins フォルダの下にコピーされ、AnypointStudio を再起動することなくインストールされます。

  2. Mule アプリケーションでコネクタを使用できるようになりました。

    install2

コネクタをアンインストールするには、UI からショートカットを使用するか、dropins ディレクトリからフォルダを削除して、AnypointStudio を再起動します。

uninstall

UpdateSite.zip からのインストール

コネクタは、DevKit により生成された更新サイトを選択することで、手動でインストールできます。

  1. コマンドプロンプトまたはターミナルを開き、プロジェクトの pom.xml ファイルが存在する場所 (Eclipse ワークスペース内) にディレクトリを変更します。

  2. mvn clean package を実行します。これにより、コネクタが作成されます。作成が成功すると、コネクタの「UpdateSite.zip」が生成されたディレクトリが表示されます。通常、これは Studio を指すものです。

  3. [Help (ヘルプ)] > [Install New Software…​ (新規ソフトウェアをインストール…​)] をクリックします。

    install updatesite
  4. [Add (追加)] をクリックし、新しいダイアログでフォルダを探します。

  5. プロジェクトの生成先フォルダの下に生成されている UpdateSite ファイルをクリックします。

    install updatesite2

    UpdateSite.zip という名前の zip ファイルまたはフォルダ update-site を選択できます。

    install updatesite3

    ポップアップが開き、実行されたタスクが表示されます。

    install12
  6. インストールと更新の項目を確認し、ライセンス契約に同意します。

  7. [Finish (完了)] をクリックし、Studio を再起動すると、変更が認識され、パレットが更新されます。

    この実行中に JAR ファイルが署名されないため、ポップアップが表示されます。
    security warning
  8. Mule アプリケーションでコネクタを使用できるようになりました。

    install2

コネクタの更新

コネクタを更新するには、コネクタのインストールで実行した手順を繰り返します。

AnypointStudio は、更新であることを検出し、対応するアクションを実行します。

コネクタのデバッグ

コネクタが正常にインストールされたら、Mule アプリケーションでコネクタの使用を開始できます。 コネクタのソースコードにブレークポイントを追加して、デバッグすることができます。 コネクタが Community コネクタの場合、インストールしたコネクタにソースコードが自動的に付属します。 コネクタが Enterprise コネクタの場合、JAR のソースコードを手動で添付する必要があります。

コードを正しくデバッグするには、実行している Mule アプリケーションで最新のインストール済みバージョンが使用されていることを考慮に入れてください。このため、変更を加えて Mule アプリケーションをデバッグする必要がある場合は、コネクタを再インストールする必要があります。

テストの実行時にコネクタをデバッグする方法は、Java クラスをデバッグする方法と同様に簡単です。

コネクタのコードにブレークポイントを設定するだけです。

コネクタの共有

コネクタは Anypoint Studio に「更新サイト」としてインストールできます。

更新サイトを使用して機能を整理し、エクスポートすることで、機能をコンポーネント/パッケージとして Eclipse アプリケーションにインストールできます。

更新サイトを生成することは、それに含まれる機能が (それらの機能のプラグイン部分と共に) インストール可能なフォームにエクスポートされることを意味します。エクスポートされたプラグインと機能は「plug-ins」と「features」の 2 つのフォルダに入れられます。他の 2 つのファイル (「content.xml」と「artifacts.xml」) も生成され、これらには、インストールを容易にする、エクスポートされたファイルのメタデータが含まれます。これらのファイルと「site.xml」がまとめられ、Eclipse 更新サイトが形成されます。他のユーザが更新サイトを使用できるようにするには、このすべてのファイルを共有ディレクトリまたは Web サイトで使用できるようにする必要があります。

コネクタを作成すると、必要なリソースを DevKit が自動的に生成するため、それらを自分で生成する必要はありません。

Anypoint DevKit プラグインを使用して、コネクタを現在の Studio にインストールしたり、コネクタを他のユーザが使用できるように更新サイトとしてエクスポートしたりできます。

コネクタ構造

すべてのコンポーネントがどのように関連しているかを確認するため、Hello World コネクタを作成しましょう。

  1. Anypoint Studio で [File (ファイル)] > [New (新規)] > [Anypoint Connector Project (Anypoint コネクタプロジェクト)] をクリックするか、Package Explorer でプロジェクト名を右クリックして [New (新規)] > [Anypoint Connector Project (Anypoint コネクタプロジェクト)] をクリックします。

    new connector 1
  2. 作成するコネクタ種別を選択します。この場合は、[SDK Based (SDK ベース)] を選択します。

    new0
  3. コネクタの名前を指定して、[Finish (完了)] をクリックします。

    new connector 2

    これにより、構造とすべての必須要素 (スケルトンコネクタ、画像、サンプルドキュメントファイル、コネクタの基本テストなど) を含むプロジェクトが生成されます。

    new connector 3
  4. 上部バーから [Window (ウィンドウ)] > [Show View (ビューを表示)] > [Other (その他)] をクリックし、リストで DevKit を探して、DevKit ビューを有効にします。

    enable view

最初にコネクタはメッセージプロセッサとユーザインターフェース要素で構成されます。ユーザは Anypoint Studio で UI 要素を設定できます。

DevKit を使用すると、コネクタを Studio に容易にインストールできます。Studio にコネクタをインストールしたら、ユーザはそのコネクタを検索して Mule フローにドラッグできます。

インストールするには、Studio の Package Explorer でコネクタの名前を右クリックし、[Anypoint Connector (Anypoint コネクタ)] > [Install or Update (インストールまたは更新)] をクリックして、指示に従って Studio を再起動します (インストールのセクションを参照)。インストールはコーディング中にいつでも実行できます。開始スケルトンコネクタをインストールすることもできます。

スケルトンコネクタの構造を確認してみましょう。

この画像では、ほとんどのコードが UI 要素にマップしていることを確認できます。

Image1
Figure 1: 構造と UI の表示

次の例では、コードが XML および他の UI 要素とどのように一致するかを確認できます。

Image2
Figure 2: 設定と XML の表示

@ConnectionStrategy アノテーションは非推奨になります。ユーザは代わりに @Config を使用する必要があります。

@Configurable およびパラメータ修飾子

省略可能なパラメータまたは設定可能なパラメータは、必須ではない要素です。そのため、ユーザはその値を指定する必要はありません。

設定可能な項目は、[Connector Configuration (コネクタ設定)] クラス内で (@Configurable を使用して) 指定するか、@Connector クラス内で @Processor メソッドのパラメータとして指定できます。

import org.mule.api.annotations.Configurable;
import org.mule.api.annotations.Processor;
import org.mule.api.annotations.param.Optional;

//Inside the Configuration class

/**
 * Optional Field documentation
 */
@Configurable
@Optional
private String  optionalField;
//Getter and Setter of the field are required

//Inside your @Connector

/**
 * Optional parameter
 */
@Processor
public String sayHi(String firstName,@Optional String lastName ) {
    return "Hi "+firstName+" "+((lastName==null) ? "":lastName);
}

@Default

省略可能なパラメータまたは設定可能なパラメータが必要な場合、@Optional を使用せずに @Default アノテーションのみを使用できます。

import org.mule.api.annotations.Configurable;
import org.mule.api.annotations.Processor;
import org.mule.api.annotations.param.Default;

//Inside the Configuration class

/**
 *  Field with Default
 */
@Configurable
@Default("Hi")
private String  greeting;
//Getter and Setter of the field are required

//Inside your @Connector

/**
 * Default parameter
 */
@Processor
public String sayHi(String firstName,@Default("Unknown") String lastName ) {
    return greeting+" "+firstName+" "+lastName;
}

@Default アノテーションのもう 1 つの非常に重要な使用方法は、DataSense を含むコネクタを作成する場合です。

OAuthV2 の追加

サーバはログイン要求を実行する代わりに OAuth 2.0 を使用してトークンを提供することもできます。

戦略に @OAuth2 アノテーションを付加するだけで、コネクタで OAuth を使用できます。

OAuth2 設定のみがある場合、Config.java は次のようになります。

package org.mule.modules.cookbook.config;

import org.mule.api.annotations.Configurable;
import org.mule.api.annotations.oauth.*;
import org.mule.api.annotations.param.Default;

import com.cookbook.tutorial.client.MuleCookBookClient;

@OAuth2(configElementName = "oauth2", friendlyName = "OAuth 2.0", authorizationUrl = "http://devkit-cookbook.cloudhub.io/rest/oauth/authorize", accessTokenUrl = "http://devkit-cookbook.cloudhub.io/rest/oauth/accessToken")
public class OAuthConfig {

    private MuleCookBookClient client;

    @OAuthAccessToken
    private String accessToken;

    @Configurable
    @OAuthConsumerKey
    private String consumerKey;

    @Configurable
    @OAuthConsumerSecret
    private String consumerSecret;

    /**
     * URL used to connect to the service
     */
    @Configurable
    @Default("http://devkit-cookbook.cloudhub.io/soap")
    private String address;

    @OAuthPostAuthorization
    public void postAuthorize() {
        setClient(new MuleCookBookClient(getAddress()));
        getClient().setToken(getAccessToken());
    }

    public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
    }

    public String getAccessToken() {
        return this.accessToken;
    }

    public void setConsumerKey(String consumerKey) {
        this.consumerKey = consumerKey;
    }

    public String getConsumerKey() {
        return this.consumerKey;
    }

    public void setConsumerSecret(String consumerSecret) {
        this.consumerSecret = consumerSecret;
    }

    public String getConsumerSecret() {
        return this.consumerSecret;
    }

    public MuleCookBookClient getClient() {
        return client;
    }

    public void setClient(MuleCookBookClient client) {
        this.client = client;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

}

@Source でも、すべての操作を @OAuthProtected でマークすることが必要になりました。

たとえば、コネクタのコードは次のようになります。

/**
 * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
 * a copy of which has been included with this distribution in the LICENSE.md file.
 */

package org.mule.modules.cookbook;

import java.util.List;
import java.util.Map;

import org.mule.api.annotations.Config;
import org.mule.api.annotations.Connector;
import org.mule.api.annotations.MetaDataScope;
import org.mule.api.annotations.Paged;
import org.mule.api.annotations.Processor;
import org.mule.api.annotations.ReconnectOn;
import org.mule.api.annotations.Source;
import org.mule.api.annotations.SourceStrategy;
import org.mule.api.annotations.Transformer;
import org.mule.api.annotations.lifecycle.OnException;
import org.mule.api.annotations.oauth.OAuthProtected;
import org.mule.api.annotations.param.Default;
import org.mule.api.annotations.param.MetaDataKeyParam;
import org.mule.api.annotations.param.MetaDataKeyParamAffectsType;
import org.mule.api.annotations.param.RefOnly;
import org.mule.api.callback.SourceCallback;
import org.mule.modules.cookbook.config.ConnectorConfig;
import org.mule.modules.cookbook.datasense.DataSenseResolver;
import org.mule.modules.cookbook.handler.CookbookHandler;
import org.mule.modules.cookbook.pagination.CookbookPagingDelegate;
import org.mule.streaming.PagingConfiguration;
import org.mule.streaming.ProviderAwarePagingDelegate;

import com.cookbook.tutorial.client.ICookbookCallback;
import com.cookbook.tutorial.service.CookBookEntity;
import com.cookbook.tutorial.service.Ingredient;
import com.cookbook.tutorial.service.InvalidEntityException;
import com.cookbook.tutorial.service.NoSuchEntityException;
import com.cookbook.tutorial.service.Recipe;
import com.cookbook.tutorial.service.SessionExpiredException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * Anypoint Connector
 *
 * @author MuleSoft, Inc.
 */
@Connector(name = "cookbook", friendlyName = "Cookbook")
@MetaDataScope(DataSenseResolver.class)
public class CookbookConnector {

    @Config
    ConnectorConfig config;

    /**
     * Returns the list of recently added recipes
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:getRecentlyAdded}
     *
     * @return A list of the recently added recipes
     */
    @OAuthProtected
    @Processor
    public List<Recipe> getRecentlyAdded() {
        return config.getClient().getRecentlyAdded();
    }

    /**
     * Description for getRecentlyAddedSource
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:getRecentlyAddedSource}
     *
     * @param callback
     *            The callback that will hook the result into mule event.
     * @throws Exception
     *             When the source fails.
     */
    @OAuthProtected
    @Source(sourceStrategy = SourceStrategy.POLLING, pollingPeriod = 10000)
    public void getRecentlyAddedSource(final SourceCallback callback) throws Exception {

        if (this.getConfig().getClient() != null) {
            // Every 5 seconds our callback will be executed
            this.getConfig().getClient().getRecentlyAdded(new ICookbookCallback() {

                @Override
                public void execute(List<Recipe> recipes) throws Exception {
                    callback.process(recipes);
                }
            });

            if (Thread.interrupted()) {
                throw new InterruptedException();
            }
        }
    }

    /**
     * Description for create
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:create}
     *
     * @param entity
     *            Ingredient to be created
     * @return return Ingredient with Id from the system.
     *
     * @throws InvalidTokenException
     * @throws SessionExpiredException
     * @throws InvalidEntityException
     */
    @SuppressWarnings("unchecked")
    @OAuthProtected
    @Processor
    @OnException(handler = CookbookHandler.class)
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    public Map<String, Object> create(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly Map<String, Object> entity)
            throws InvalidEntityException, SessionExpiredException {
        ObjectMapper m = new ObjectMapper();
        CookBookEntity input = null;
        if (type.contains("com.cookbook.tutorial.service.Recipe")) {
            input = m.convertValue(entity, Recipe.class);
        } else if (type.contains("com.cookbook.tutorial.service.Ingredient")) {
            input = m.convertValue(entity, Ingredient.class);
        } else {
            throw new InvalidEntityException("Don't know how to handle type:" + type);
        }
        return m.convertValue(this.getConfig().getClient().create(input), Map.class);
    }

    /**
     * Description for update
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:update}
     *
     * @param entity
     *            Ingredient to be updated
     * @return return Ingredient with Id from the system.
     *
     * @throws SessionExpiredException
     * @throws InvalidEntityException
     * @throws NoSuchEntityException
     */
    @SuppressWarnings("unchecked")
    @OAuthProtected
    @Processor
    @OnException(handler = CookbookHandler.class)
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    public Map<String, Object> update(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly Map<String, Object> entity)
            throws InvalidEntityException, SessionExpiredException, NoSuchEntityException {
        ObjectMapper m = new ObjectMapper();
        CookBookEntity input = null;
        if (type.contains("com.cookbook.tutorial.service.Recipe")) {
            input = m.convertValue(entity, Recipe.class);
        } else if (type.contains("com.cookbook.tutorial.service.Ingredient")) {
            input = m.convertValue(entity, Ingredient.class);
        } else {
            throw new InvalidEntityException("Don't know how to handle type:" + type);
        }
        return m.convertValue(this.getConfig().getClient().update(input), Map.class);
    }

    /**
     * Description for get
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:get}
     *
     * @param id
     *            Id of the entity to retrieve
     * @return return Ingredient with Id from the system.
     *
     * @throws SessionExpiredException
     * @throws InvalidEntityException
     * @throws NoSuchEntityException
     */
    @SuppressWarnings("unchecked")
    @OAuthProtected
    @Processor
    @OnException(handler = CookbookHandler.class)
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    public Map<String, Object> get(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.OUTPUT) String type, @Default("1") Integer id) throws InvalidEntityException,
            SessionExpiredException, NoSuchEntityException {
        ObjectMapper m = new ObjectMapper();
        return m.convertValue(this.getConfig().getClient().get(id), Map.class);
    }

    /**
     * Description for get
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:get}
     *
     * @param id
     *            Id of the entity to retrieve
     * @return return Ingredient with Id from the system.
     *
     * @throws SessionExpiredException
     * @throws NoSuchEntityException
     */
    @OAuthProtected
    @Processor
    @OnException(handler = CookbookHandler.class)
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    public void delete(@Default("1") Integer id) throws NoSuchEntityException, SessionExpiredException {
        this.getConfig().getClient().delete(id);
    }

    /**
     * Description for queryPaginated
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:query-paginated}
     *
     *  @param query The query
     *  @param pagingConfiguration the paging configuration
     *  @return return comment
     */
    @OAuthProtected
    @Processor
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    @Paged                                                                                       (1)
    public ProviderAwarePagingDelegate<Map<String, Object>, CookbookConnector> queryPaginated(   (2)
            final String query, final PagingConfiguration pagingConfiguration)                   (3)
            throws SessionExpiredException {
        return new CookbookPagingDelegate(query, pagingConfiguration.getFetchSize());
    }

    @Transformer(sourceTypes = { List.class })
    public static List<Map<String, Object>> recipesToMaps(List<Recipe> list) {
        ObjectMapper mapper = new ObjectMapper();
        List<Map<String, Object>> result = mapper.convertValue(list, new TypeReference<List<Map<String, Object>>>() {
        });
        return result;
    }

    @Transformer(sourceTypes = { Recipe.class })
    public static Map<String, Object> recipeToMap(Recipe recipe) {
        ObjectMapper mapper = new ObjectMapper();
        Map<String, Object> result = mapper.convertValue(recipe, new TypeReference<Map<String, Object>>() {
        });
        return result;
    }

    public ConnectorConfig getConfig() {
        return config;
    }

    public void setConfig(ConnectorConfig config) {
        this.config = config;
    }

}

完全なソースコードを確認してください。

情報:

複数の設定

複数の接続設定が必要な場合、次の 2 つの考慮事項があります。

  • すべての設定で同じインターフェースを実装するか、共通の親クラスを使用する必要がある。

  • コネクタの [Config (設定)] 項目をインターフェースとして宣言するか共通の親クラスとして宣言する必要がある。

設定に共通の項目がある場合、それらを親クラスで定義できます。

コネクタでは、リファクタリングの後、クラスは次のようになります。

uml-model

情報:

@Source の追加

ソースとは?

場合によって、メッセージプロセッサではなくメッセージソースの作成が必要になることがあります。

基本的に、メッセージソースでは、Mule により処理される新しいメッセージを受信または生成します。 メッセージソースのユースケースの 1 つとして、ストリーミング API の実装があります。@Source アノテーションでは、@Connector アノテーション付きクラス内のメソッドが、Mule フローからのコールが可能、および Mule イベントの生成が可能としてマークされます。マークされた各メソッドでメッセージソースが生成されます。メソッドは、その引数の 1 つとして、チェーン内の次のメッセージプロセッサを表す SourceCallback を受け取る必要があります。このパラメータがメソッドの署名に存在する限り、このパラメータの順序は重要ではありません。

メッセージソースの実装

例として、GetRecentlyAdded メソッドを使用します。

  1. @Connector の内側で「source」と入力し、Ctrl + Space バーを使用してテンプレートを表示します。

    source template
  2. コールバックを使用してコネクタでの最近の更新の取得をコンシュームする @Source を作成します。

    /**
     * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
     * a copy of which has been included with this distribution in the LICENSE.md file.
     */
    
    package org.mule.modules.cookbook;
    
    import java.util.List;
    import java.util.Map;
    
    import org.mule.api.annotations.Config;
    import org.mule.api.annotations.Connector;
    import org.mule.api.annotations.MetaDataScope;
    import org.mule.api.annotations.Processor;
    import org.mule.api.annotations.ReconnectOn;
    import org.mule.api.annotations.Source;
    import org.mule.api.annotations.SourceStrategy;
    import org.mule.api.annotations.Transformer;
    import org.mule.api.annotations.lifecycle.OnException;
    import org.mule.api.annotations.param.Default;
    import org.mule.api.annotations.param.MetaDataKeyParam;
    import org.mule.api.annotations.param.MetaDataKeyParamAffectsType;
    import org.mule.api.annotations.param.RefOnly;
    import org.mule.api.callback.SourceCallback;
    import org.mule.modules.cookbook.config.ConnectorConfig;
    import org.mule.modules.cookbook.datasense.DataSenseResolver;
    import org.mule.modules.cookbook.handler.CookbookHandler;
    
    import com.cookbook.tutorial.client.ICookbookCallback;
    import com.cookbook.tutorial.service.CookBookEntity;
    import com.cookbook.tutorial.service.Ingredient;
    import com.cookbook.tutorial.service.InvalidEntityException;
    import com.cookbook.tutorial.service.NoSuchEntityException;
    import com.cookbook.tutorial.service.Recipe;
    import com.cookbook.tutorial.service.SessionExpiredException;
    import com.fasterxml.jackson.core.type.TypeReference;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    /**
     * Anypoint Connector
     *
     * @author MuleSoft, Inc.
     */
    @Connector(name = "cookbook", friendlyName = "Cookbook")
    @MetaDataScope(DataSenseResolver.class)
    public class CookbookConnector {
    
        @Config
        ConnectorConfig config;
    
        /**
         * Returns the list of recently added recipes
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:getRecentlyAdded}
         *
         * @return A list of the recently added recipes
         */
        @Processor
        public List<Recipe> getRecentlyAdded() {
            return config.getClient().getRecentlyAdded();
        }
    
        /**
         * Description for getRecentlyAddedSource
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:getRecentlyAddedSource}
         *
         * @param callback
         *            The callback that will hook the result into mule event.
         * @throws Exception
         *             When the source fails.
         */
        @Source(sourceStrategy = SourceStrategy.POLLING, pollingPeriod = 10000)
        public void getRecentlyAddedSource(final SourceCallback callback) throws Exception {
    
            if (this.getConfig().getClient() != null) {
                // Every 10 seconds our callback will be executed
                this.getConfig().getClient().getRecentlyAdded(new ICookbookCallback() {
    
                    @Override
                    public void execute(List<Recipe> recipes) throws Exception {
                        callback.process(recipes);
                    }
                });
    
                if (Thread.interrupted()) {
                    throw new InterruptedException();
                }
            }
        }
    
        /**
         * Description for create
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:create}
         *
         * @param entity
         *            Ingredient to be created
         * @return return Ingredient with Id from the system.
         *
         * @throws InvalidTokenException
         * @throws SessionExpiredException
         * @throws InvalidEntityException
         */
        @SuppressWarnings("unchecked")
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public Map<String, Object> create(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly Map<String, Object> entity)
                throws InvalidEntityException, SessionExpiredException {
            ObjectMapper m = new ObjectMapper();
            CookBookEntity input = null;
            if (type.contains("com.cookbook.tutorial.service.Recipe")) {
                input = m.convertValue(entity, Recipe.class);
            } else if (type.contains("com.cookbook.tutorial.service.Ingredient")) {
                input = m.convertValue(entity, Ingredient.class);
            } else {
                throw new InvalidEntityException("Don't know how to handle type:" + type);
            }
            return m.convertValue(this.getConfig().getClient().create(input), Map.class);
        }
    
        /**
         * Description for update
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:update}
         *
         * @param entity
         *            Ingredient to be updated
         * @return return Ingredient with Id from the system.
         *
         * @throws SessionExpiredException
         * @throws InvalidEntityException
         * @throws NoSuchEntityException
         */
        @SuppressWarnings("unchecked")
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public Map<String, Object> update(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly Map<String, Object> entity)
                throws InvalidEntityException, SessionExpiredException, NoSuchEntityException {
            ObjectMapper m = new ObjectMapper();
            CookBookEntity input = null;
            if (type.contains("com.cookbook.tutorial.service.Recipe")) {
                input = m.convertValue(entity, Recipe.class);
            } else if (type.contains("com.cookbook.tutorial.service.Ingredient")) {
                input = m.convertValue(entity, Ingredient.class);
            } else {
                throw new InvalidEntityException("Don't know how to handle type:" + type);
            }
            return m.convertValue(this.getConfig().getClient().update(input), Map.class);
        }
    
        /**
         * Description for get
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:get}
         *
         * @param id
         *            Id of the entity to retrieve
         * @return return Ingredient with Id from the system.
         *
         * @throws SessionExpiredException
         * @throws InvalidEntityException
         * @throws NoSuchEntityException
         */
        @SuppressWarnings("unchecked")
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public Map<String, Object> get(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.OUTPUT) String type, @Default("1") Integer id) throws InvalidEntityException,
                SessionExpiredException, NoSuchEntityException {
            ObjectMapper m = new ObjectMapper();
            return m.convertValue(this.getConfig().getClient().get(id), Map.class);
        }
    
        /**
         * Description for get
         *
         * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:get}
         *
         * @param id
         *            Id of the entity to retrieve
         * @return return Ingredient with Id from the system.
         *
         * @throws SessionExpiredException
         * @throws NoSuchEntityException
         */
        @Processor
        @OnException(handler = CookbookHandler.class)
        @ReconnectOn(exceptions = { SessionExpiredException.class })
        public void delete(@Default("1") Integer id) throws NoSuchEntityException, SessionExpiredException {
            this.getConfig().getClient().delete(id);
        }
    
        @Transformer(sourceTypes = { List.class })
        public static List<Map<String, Object>> transformJsonToRecipeList(List<Recipe> list) {
            ObjectMapper mapper = new ObjectMapper();
            List<Map<String, Object>> result = mapper.convertValue(list, new TypeReference<List<Map<String, Object>>>() {
            });
            return result;
        }
    
        public ConnectorConfig getConfig() {
            return config;
        }
    
        public void setConfig(ConnectorConfig config) {
            this.config = config;
        }
    
    }
  3. この新しいバージョンをインストールします。

  4. フローで [Cookbook] コネクタをドラッグアンドドロップするだけで、フローの生成中に自動的に表示されるようになります。

    source example
  5. ロガーを追加し、デバッグして、ペイロードにレシピが含まれるようになったことを確認します。

    Mule アプリケーション

    source debug

    XML

    <?xml version="1.0" encoding="UTF-8"?>
    
    <mule xmlns:tracking="http://www.mulesoft.org/schema/mule/ee/tracking" xmlns:cookbook="http://www.mulesoft.org/schema/mule/cookbook" xmlns:json="http://www.mulesoft.org/schema/mule/json" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
          xmlns:spring="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
    http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
    http://www.mulesoft.org/schema/mule/cookbook http://www.mulesoft.org/schema/mule/cookbook/current/mule-cookbook.xsd
    http://www.mulesoft.org/schema/mule/json http://www.mulesoft.org/schema/mule/json/current/mule-json.xsd
    http://www.mulesoft.org/schema/mule/ee/tracking http://www.mulesoft.org/schema/mule/ee/tracking/current/mule-tracking-ee.xsd">
        <cookbook:config name="CookBook__Connection_Management_Config" username="admin" password="admin" doc:name="CookBook: Connection Management Config"/>
        <flow name="source-get-recently-added">
            <cookbook:get-recently-added-source config-ref="CookBook__Connection_Management_Config" doc:name="CookBook (Streaming)"/>
            <logger message="#[payload]" level="INFO" doc:name="Logger"/>
        </flow>
    </mule>

トランスフォーマの追加について

トランスフォーマは、メッセージペイロードを、宛先で期待される形式に変換します。Mule ESB は多くの標準トランスフォーマを提供します。ユーザは Mule XML 設定ファイル内で要素と属性を使用してそのトランスフォーマを設定できます。

独自のカスタムトランスフォーマを作成すると便利な場合があります。

メソッドに @Transformer のアノテーションを付加すると、メソッドの機能をトランスフォーマとしてエクスポートすることが DevKit に通知されます。@Module または @Connector のアノテーションが付加されたクラスでトランスフォーマを宣言する必要があり、1 つのクラスで複数のトランスフォーマを宣言できます。トランスフォーマ、メッセージプロセッサ、メッセージソースのすべてを同じクラスで宣言できます。

@Transformer アノテーション付きメソッドの要件を以下に示します。

  • 静的である

  • 公開である

  • void を返さない

  • java.lang.Object を返さない

  • 1 つの引数のみを受け取る

  • @Connector のアノテーションが付加されたクラス内にある

トランスフォーマの作成

ここでは、List<Recipe> を List<Map<String,Object>> に変換するトランスフォーマを作成します。この方法では、既存の操作を変更する必要はなく、その出力を、List<Map<String,Object>> を受け取る操作で引き続き使用できます。

/**
 * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
 * a copy of which has been included with this distribution in the LICENSE.md file.
 */

package org.mule.modules.cookbook;

import java.util.List;
import java.util.Map;

import org.mule.api.annotations.Config;
import org.mule.api.annotations.Connector;
import org.mule.api.annotations.MetaDataScope;
import org.mule.api.annotations.Processor;
import org.mule.api.annotations.ReconnectOn;
import org.mule.api.annotations.Transformer;
import org.mule.api.annotations.lifecycle.OnException;
import org.mule.api.annotations.param.Default;
import org.mule.api.annotations.param.MetaDataKeyParam;
import org.mule.api.annotations.param.MetaDataKeyParamAffectsType;
import org.mule.api.annotations.param.RefOnly;
import org.mule.modules.cookbook.config.ConnectorConfig;
import org.mule.modules.cookbook.datasense.DataSenseResolver;
import org.mule.modules.cookbook.handler.CookbookHandler;

import com.cookbook.tutorial.service.CookBookEntity;
import com.cookbook.tutorial.service.Ingredient;
import com.cookbook.tutorial.service.InvalidEntityException;
import com.cookbook.tutorial.service.NoSuchEntityException;
import com.cookbook.tutorial.service.Recipe;
import com.cookbook.tutorial.service.SessionExpiredException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * Anypoint Connector
 *
 * @author MuleSoft, Inc.
 */
@Connector(name = "cookbook", friendlyName = "Cookbook")
@MetaDataScope(DataSenseResolver.class)
public class CookbookConnector {

    @Config
    ConnectorConfig config;

    /**
     * Returns the list of recently added recipes
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:getRecentlyAdded}
     *
     * @return A list of the recently added recipes
     */
    @Processor
    public List<Recipe> getRecentlyAdded() {
        return config.getClient().getRecentlyAdded();
    }

    /**
     * Description for create
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:create}
     *
     * @param entity
     *            Ingredient to be created
     * @return return Ingredient with Id from the system.
     *
     * @throws InvalidTokenException
     * @throws SessionExpiredException
     * @throws InvalidEntityException
     */
    @SuppressWarnings("unchecked")
    @Processor
    @OnException(handler = CookbookHandler.class)
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    public Map<String, Object> create(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly Map<String, Object> entity)
            throws InvalidEntityException, SessionExpiredException {
        ObjectMapper m = new ObjectMapper();
        CookBookEntity input = null;
        if (type.contains("com.cookbook.tutorial.service.Recipe")) {
            input = m.convertValue(entity, Recipe.class);
        } else if (type.contains("com.cookbook.tutorial.service.Ingredient")) {
            input = m.convertValue(entity, Ingredient.class);
        } else {
            throw new InvalidEntityException("Don't know how to handle type:" + type);
        }
        return m.convertValue(this.getConfig().getClient().create(input), Map.class);
    }

    /**
     * Description for update
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:update}
     *
     * @param entity
     *            Ingredient to be updated
     * @return return Ingredient with Id from the system.
     *
     * @throws SessionExpiredException
     * @throws InvalidEntityException
     * @throws NoSuchEntityException
     */
    @SuppressWarnings("unchecked")
    @Processor
    @OnException(handler = CookbookHandler.class)
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    public Map<String, Object> update(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly Map<String, Object> entity)
            throws InvalidEntityException, SessionExpiredException, NoSuchEntityException {
        ObjectMapper m = new ObjectMapper();
        CookBookEntity input = null;
        if (type.contains("com.cookbook.tutorial.service.Recipe")) {
            input = m.convertValue(entity, Recipe.class);
        } else if (type.contains("com.cookbook.tutorial.service.Ingredient")) {
            input = m.convertValue(entity, Ingredient.class);
        } else {
            throw new InvalidEntityException("Don't know how to handle type:" + type);
        }
        return m.convertValue(this.getConfig().getClient().update(input), Map.class);
    }

    /**
     * Description for get
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:get}
     *
     * @param id
     *            Id of the entity to retrieve
     * @return return Ingredient with Id from the system.
     *
     * @throws SessionExpiredException
     * @throws InvalidEntityException
     * @throws NoSuchEntityException
     */
    @SuppressWarnings("unchecked")
    @Processor
    @OnException(handler = CookbookHandler.class)
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    public Map<String, Object> get(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.OUTPUT) String type, @Default("1") Integer id) throws InvalidEntityException,
            SessionExpiredException, NoSuchEntityException {
        ObjectMapper m = new ObjectMapper();
        return m.convertValue(this.getConfig().getClient().get(id), Map.class);
    }

    /**
     * Description for get
     *
     * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:get}
     *
     * @param id
     *            Id of the entity to retrieve
     * @return return Ingredient with Id from the system.
     *
     * @throws SessionExpiredException
     * @throws NoSuchEntityException
     */
    @Processor
    @OnException(handler = CookbookHandler.class)
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    public void delete(@Default("1") Integer id) throws NoSuchEntityException, SessionExpiredException {
        this.getConfig().getClient().delete(id);
    }

    @Transformer(sourceTypes = { List.class })
    public static List<Map<String, Object>> transformJsonToComments(List<Recipe> list) {
        ObjectMapper mapper = new ObjectMapper();
        List<Map<String, Object>> result = mapper.convertValue(list, new TypeReference<List<Map<String, Object>>>() {
        });
        return result;
    }

    public ConnectorConfig getConfig() {
        return config;
    }

    public void setConfig(ConnectorConfig config) {
        this.config = config;
    }

}

Mule アプリケーションでのトランスフォーマの使用

トランスフォーマを使用するには、パレットからトランスフォーマをドラッグアンドドロップし、フローを作成します。

  • ここでは、トランスフォーマを明示的に使用して、レシピをマップに変換しています。

    Mule アプリケーション

    transformer explicit

    XML

    <?xml version="1.0" encoding="UTF-8"?>
    
    <mule xmlns:json="http://www.mulesoft.org/schema/mule/json" xmlns:cookbook="http://www.mulesoft.org/schema/mule/cookbook" xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
          xmlns:spring="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
    http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
    http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
    http://www.mulesoft.org/schema/mule/cookbook http://www.mulesoft.org/schema/mule/cookbook/current/mule-cookbook.xsd
    http://www.mulesoft.org/schema/mule/json http://www.mulesoft.org/schema/mule/json/current/mule-json.xsd">
        <cookbook:config name="CookBook__Connection_Management_Config" username="admin" password="admin" doc:name="CookBook: Connection Management Config">
            <reconnect/>
        </cookbook:config>
        <http:listener-config name="HTTP_Listener_Configuration" host="0.0.0.0" port="8081" doc:name="HTTP Listener Configuration"/>
    
        <flow name="mule-appFlow">
            <http:listener config-ref="HTTP_Listener_Configuration" path="/transform" doc:name="HTTP"/>
            <cookbook:get-recently-added config-ref="CookBook__Connection_Management_Config" doc:name="CookBook"/>
            <cookbook:recipes-to-maps doc:name="CookBook"/>
            <json:object-to-json-transformer doc:name="Object to JSON"/>
        </flow>
    </mule>
  • 種別を解決するためのトランスフォーマが 1 つのみの場合は、トランスフォーマを暗黙的に使用することもできます。

    1. Recipe (レシピ) オブジェクトを Map<String,Object> に変換できる別のトランスフォーマを定義します。

      /**
       * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
       * a copy of which has been included with this distribution in the LICENSE.md file.
       */
      
      package org.mule.modules.cookbook;
      
      import java.util.List;
      import java.util.Map;
      
      import org.mule.api.annotations.Config;
      import org.mule.api.annotations.Connector;
      import org.mule.api.annotations.MetaDataScope;
      import org.mule.api.annotations.Paged;
      import org.mule.api.annotations.Processor;
      import org.mule.api.annotations.ReconnectOn;
      import org.mule.api.annotations.Source;
      import org.mule.api.annotations.SourceStrategy;
      import org.mule.api.annotations.Transformer;
      import org.mule.api.annotations.lifecycle.OnException;
      import org.mule.api.annotations.param.Default;
      import org.mule.api.annotations.param.MetaDataKeyParam;
      import org.mule.api.annotations.param.MetaDataKeyParamAffectsType;
      import org.mule.api.annotations.param.RefOnly;
      import org.mule.api.callback.SourceCallback;
      import org.mule.modules.cookbook.config.ConnectorConfig;
      import org.mule.modules.cookbook.datasense.DataSenseResolver;
      import org.mule.modules.cookbook.handler.CookbookHandler;
      import org.mule.modules.cookbook.pagination.CookbookPagingDelegate;
      import org.mule.streaming.PagingConfiguration;
      import org.mule.streaming.ProviderAwarePagingDelegate;
      
      import com.cookbook.tutorial.client.ICookbookCallback;
      import com.cookbook.tutorial.service.CookBookEntity;
      import com.cookbook.tutorial.service.Ingredient;
      import com.cookbook.tutorial.service.InvalidEntityException;
      import com.cookbook.tutorial.service.NoSuchEntityException;
      import com.cookbook.tutorial.service.Recipe;
      import com.cookbook.tutorial.service.SessionExpiredException;
      import com.fasterxml.jackson.core.type.TypeReference;
      import com.fasterxml.jackson.databind.ObjectMapper;
      
      /**
       * Anypoint Connector
       *
       * @author MuleSoft, Inc.
       */
      @Connector(name = "cookbook", friendlyName = "Cookbook")
      @MetaDataScope(DataSenseResolver.class)
      public class CookbookConnector {
      
          @Config
          ConnectorConfig config;
      
          /**
           * Returns the list of recently added recipes
           *
           * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:getRecentlyAdded}
           *
           * @return A list of the recently added recipes
           */
          @Processor
          public List<Recipe> getRecentlyAdded() {
              return config.getClient().getRecentlyAdded();
          }
      
          /**
           * Description for getRecentlyAddedSource
           *
           * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:getRecentlyAddedSource}
           *
           * @param callback
           *            The callback that will hook the result into mule event.
           * @throws Exception
           *             When the source fails.
           */
          @Source(sourceStrategy = SourceStrategy.POLLING, pollingPeriod = 10000)
          public void getRecentlyAddedSource(final SourceCallback callback) throws Exception {
      
              if (this.getConfig().getClient() != null) {
                  // Every 5 seconds our callback will be executed
                  this.getConfig().getClient().getRecentlyAdded(new ICookbookCallback() {
      
                      @Override
                      public void execute(List<Recipe> recipes) throws Exception {
                          callback.process(recipes);
                      }
                  });
      
                  if (Thread.interrupted()) {
                      throw new InterruptedException();
                  }
              }
          }
      
          /**
           * Description for create
           *
           * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:create}
           *
           * @param entity
           *            Ingredient to be created
           * @return return Ingredient with Id from the system.
           *
           * @throws InvalidTokenException
           * @throws SessionExpiredException
           * @throws InvalidEntityException
           */
          @SuppressWarnings("unchecked")
          @Processor
          @OnException(handler = CookbookHandler.class)
          @ReconnectOn(exceptions = { SessionExpiredException.class })
          public Map<String, Object> create(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly Map<String, Object> entity)
                  throws InvalidEntityException, SessionExpiredException {
              ObjectMapper m = new ObjectMapper();
              CookBookEntity input = null;
              if (type.contains("com.cookbook.tutorial.service.Recipe")) {
                  input = m.convertValue(entity, Recipe.class);
              } else if (type.contains("com.cookbook.tutorial.service.Ingredient")) {
                  input = m.convertValue(entity, Ingredient.class);
              } else {
                  throw new InvalidEntityException("Don't know how to handle type:" + type);
              }
              return m.convertValue(this.getConfig().getClient().create(input), Map.class);
          }
      
          /**
           * Description for update
           *
           * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:update}
           *
           * @param entity
           *            Ingredient to be updated
           * @return return Ingredient with Id from the system.
           *
           * @throws SessionExpiredException
           * @throws InvalidEntityException
           * @throws NoSuchEntityException
           */
          @SuppressWarnings("unchecked")
          @Processor
          @OnException(handler = CookbookHandler.class)
          @ReconnectOn(exceptions = { SessionExpiredException.class })
          public Map<String, Object> update(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly Map<String, Object> entity)
                  throws InvalidEntityException, SessionExpiredException, NoSuchEntityException {
              ObjectMapper m = new ObjectMapper();
              CookBookEntity input = null;
              if (type.contains("com.cookbook.tutorial.service.Recipe")) {
                  input = m.convertValue(entity, Recipe.class);
              } else if (type.contains("com.cookbook.tutorial.service.Ingredient")) {
                  input = m.convertValue(entity, Ingredient.class);
              } else {
                  throw new InvalidEntityException("Don't know how to handle type:" + type);
              }
              return m.convertValue(this.getConfig().getClient().update(input), Map.class);
          }
      
          /**
           * Description for get
           *
           * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:get}
           *
           * @param id
           *            Id of the entity to retrieve
           * @return return Ingredient with Id from the system.
           *
           * @throws SessionExpiredException
           * @throws InvalidEntityException
           * @throws NoSuchEntityException
           */
          @SuppressWarnings("unchecked")
          @Processor
          @OnException(handler = CookbookHandler.class)
          @ReconnectOn(exceptions = { SessionExpiredException.class })
          public Map<String, Object> get(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.OUTPUT) String type, @Default("1") Integer id) throws InvalidEntityException,
                  SessionExpiredException, NoSuchEntityException {
              ObjectMapper m = new ObjectMapper();
              return m.convertValue(this.getConfig().getClient().get(id), Map.class);
          }
      
          /**
           * Description for get
           *
           * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:get}
           *
           * @param id
           *            Id of the entity to retrieve
           * @return return Ingredient with Id from the system.
           *
           * @throws SessionExpiredException
           * @throws NoSuchEntityException
           */
          @Processor
          @OnException(handler = CookbookHandler.class)
          @ReconnectOn(exceptions = { SessionExpiredException.class })
          public void delete(@Default("1") Integer id) throws NoSuchEntityException, SessionExpiredException {
              this.getConfig().getClient().delete(id);
          }
      
          /**
           * Description for queryPaginated
           *
           * {@sample.xml ../../../doc/cook-book-connector.xml.sample cook-book:query-paginated}
           *
           *  @param query The query
           *  @param pagingConfiguration the paging configuration
           *  @return return comment
           */
          @Processor
          @ReconnectOn(exceptions = { SessionExpiredException.class })
          @Paged (1)
          public ProviderAwarePagingDelegate<Map<String, Object>, CookbookConnector> queryPaginated( (2)
                  final String query, final PagingConfiguration pagingConfiguration) (3)
                  throws SessionExpiredException {
              return new CookbookPagingDelegate(query, pagingConfiguration.getFetchSize());
          }
      
          @Transformer(sourceTypes = { List.class })
          public static List<Map<String, Object>> recipesToMaps(List<Recipe> list) {
              ObjectMapper mapper = new ObjectMapper();
              List<Map<String, Object>> result = mapper.convertValue(list, new TypeReference<List<Map<String, Object>>>() {
              });
              return result;
          }
      
          @Transformer(sourceTypes = { Recipe.class })
          public static Map<String, Object> recipeToMap(Recipe recipe) {
              ObjectMapper mapper = new ObjectMapper();
              Map<String, Object> result = mapper.convertValue(recipe, new TypeReference<Map<String, Object>>() {
              });
              return result;
          }
      
          public ConnectorConfig getConfig() {
              return config;
          }
      
          public void setConfig(ConnectorConfig config) {
              this.config = config;
          }
      
      }
    2. コネクタをインストールします。

    3. Mule アプリケーションで、リストの最初の項目を取得し、その後に update 操作を追加するフローを作成します。トランスフォーマを使用しないでください。

      Mule アプリケーション

      transformer implicitly

      XML

      <?xml version="1.0" encoding="UTF-8"?>
      
      <mule xmlns:json="http://www.mulesoft.org/schema/mule/json" xmlns:cookbook="http://www.mulesoft.org/schema/mule/cookbook" xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
            xmlns:spring="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
      http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
      http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
      http://www.mulesoft.org/schema/mule/cookbook http://www.mulesoft.org/schema/mule/cookbook/current/mule-cookbook.xsd
      http://www.mulesoft.org/schema/mule/json http://www.mulesoft.org/schema/mule/json/current/mule-json.xsd">
          <cookbook:config name="CookBook__Connection_Management_Config" username="admin" password="admin" doc:name="CookBook: Connection Management Config">
              <reconnect/>
          </cookbook:config>
          <http:listener-config name="HTTP_Listener_Configuration" host="0.0.0.0" port="8081" doc:name="HTTP Listener Configuration"/>
      
          <flow name="mule-appFlow">
              <http:listener config-ref="HTTP_Listener_Configuration" path="/transform" doc:name="HTTP"/>
              <cookbook:get-recently-added config-ref="CookBook__Connection_Management_Config" doc:name="CookBook"/>
              <!-- Get the first item of the list -->
              <set-payload value="#[payload.get(0)]" doc:name="Set Payload"/>
              <!-- This operation is expecting a Map. Not a Recipe, but we are not using the transformer in the flow. The transformer will be called automatically -->
              <cookbook:update config-ref="CookBook__Connection_Management_Config" type="com.cookbook.tutorial.service.Recipe#0" doc:name="CookBook">
                  <!-- Take the payload as input for the connector -->
                  <cookbook:entity ref="#[payload]"/>
              </cookbook:update>
              <json:object-to-json-transformer doc:name="Object to JSON"/>
          </flow>
      </mule>
    4. 例を実行し、フローが正常に実行することを確認します。

@Password の使用

@@Password パラメータで Connect を使用すると、マスクされた入力項目が UI に生成されます。

/**
 * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
 * a copy of which has been included with this distribution in the LICENSE.md file.
 */

package org.mule.modules.cookbook.config;

import org.mule.api.ConnectionException;
import org.mule.api.ConnectionExceptionCode;
import org.mule.api.annotations.Configurable;
import org.mule.api.annotations.Connect;
import org.mule.api.annotations.ConnectionIdentifier;
import org.mule.api.annotations.Disconnect;
import org.mule.api.annotations.TestConnectivity;
import org.mule.api.annotations.ValidateConnection;
import org.mule.api.annotations.components.ConnectionManagement;
import org.mule.api.annotations.display.Password;
import org.mule.api.annotations.param.ConnectionKey;
import org.mule.api.annotations.param.Default;

import com.cookbook.tutorial.client.MuleCookBookClient;
import com.cookbook.tutorial.service.InvalidCredentialsException;

/**
 * Configuration type Config
 *
 * @author MuleSoft, Inc.
 */
@ConnectionManagement(friendlyName = "Configuration")
public class ConnectorConfig {

    /**
     * Connect
     *
     * @param username
     *            A username
     * @param password
     *            A password
     * @throws ConnectionException
     */
    @Connect
    @TestConnectivity
    public void connect(@ConnectionKey String username, @Password String password) throws ConnectionException {
        setClient(new MuleCookBookClient(getAddress()));
        try {
            getClient().login(username, password);
        } catch (InvalidCredentialsException e) {
            throw new ConnectionException(ConnectionExceptionCode.INCORRECT_CREDENTIALS, e.getMessage(), "Invalid credentials");
        }
    }

    /**
     * Disconnect
     */
    @Disconnect
    public void disconnect() {
        setClient(null);
    }

    /**
     * Are we connected
     */
    @ValidateConnection
    public boolean isConnected() {
        return getClient() != null;
    }

    /**
     * Id used only when debuging.
     */
    @ConnectionIdentifier
    public String connectionId() {
        return "001";
    }

    /**
     * Description for address
     */
    @Configurable
    @Default("http://devkit-cookbook.cloudhub.io/soap")
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    private MuleCookBookClient client;

    public MuleCookBookClient getClient() {
        return client;
    }

    public void setClient(MuleCookBookClient client) {
        this.client = client;
    }

}

Studio では、これは次のような設定になります。

password

API 用のコネクタの作成

API はいくつかの方法で公開されます。API の使用を開始するには、コネクタ内で API を使用する前にいくつかの項目をセットアップする必要があります。

SDK クライアント

SDK がある場合、jar 用の Maven 連動関係を pom.xml に含めるだけで済みます。

たとえば、Cookbook 用の SDK をコンシュームするには、その連動関係を追加することができます。

<dependencies>
  <dependency>
    <groupId>foo.sdk.group.id</groupId>
    <artifactId>foo.sdk.artifact.id</artifactId>
    <version>${sdk.version}</version>
  </dependency>
</dependencies>

SOAP API

wsdl がある場合、コネクタを作成するための最も簡単な方法は、CXF wsdl2java を使用してクライアントを作成することです。

CXF の目標を pom.xml ファイルで非常に簡単に設定できます。詳細なドキュメントは Apache の CXF サイトを参照してください。

たとえば、pom.xml ファイルに次のコードとすべての必須の連動関係を追加できます。

<build>
  <plugins>
    <!-- CXF Code generation -->
    <plugin>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-codegen-plugin</artifactId>
      <version>${cxf.version}</version>
      <executions>
        <execution>
          <phase>clean</phase> <!-- This is required for it to work with DevKit -->
          <goals>
            <goal>wsdl2java</goal>
          </goals>
          <configuration>
            <wsdlOptions>
              <wsdlOption>
                <wsdl>${basedir}/src/main/resources/wsdl/IMuleCookBookService.wsdl</wsdl>
                <autoNameResolution>true</autoNameResolution>
                <extendedSoapHeaders>false</extendedSoapHeaders>
                <extraargs>
                  <extraarg>-xjc-Xbg</extraarg>
                   <extraarg>-xjc-Xcollection-setter-injector</extraarg>
                  <extraarg>-p</extraarg>
                  <extraarg>org.mule.modules.wsdl.api</extraarg>
                </extraargs>
              </wsdlOption>
            </wsdlOptions>
          </configuration>
         </execution>
      </executions>
      <dependencies>
        <!-- Boolean Getters -->
        <dependency>
          <groupId>org.apache.cxf.xjcplugins</groupId>
          <artifactId>cxf-xjc-boolean</artifactId>
          <version>${cxf.version.boolean}</version>
        </dependency>
        <!-- Collection Setters -->
        <dependency>
          <groupId>net.java.dev.vcc.thirdparty</groupId>
          <artifactId>collection-setter-injector</artifactId>
          <version>0.5.0-1</version>
        </dependency>
      </dependencies>
    </plugin>
  </plugins>
</build>

DevKit プラグインを使用すると、開始するために必要なすべてのものが自動的に生成されます。必要なことは、コンピュータ上の WSDL の場所を指定することのみです。

REST API

HTTP 要求を実行するのに役立つ任意のライブラリを使用して、要求を作成します。

Mule バージョン 3.6.0 以降で提供される Jersey 2.11 を使用することをお勧めします。

常に適切なバージョンを使用するには、次の連動関係をコネクタの pom.xml に追加します。

<dependencies>
    <dependency>
        <groupId>org.mule.modules</groupId>
        <artifactId>mule-module-jersey</artifactId>
        <version>${mule.version}</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

GET 要求の例:

ClientConfig clientConfig = new ClientConfig();
Client client = ClientBuilder.newClient(clientConfig);
WebTarget webTarget = client.target("http://example.com/rest"); // (1)
WebTarget resourceWebTarget = webTarget.path("resource");
WebTarget helloworldWebTarget = resourceWebTarget.path("helloworld"); // (2)
WebTarget helloworldWebTargetWithQueryParam =
        helloworldWebTarget.queryParam("greeting", "Hi World!"); // (3)

Invocation.Builder invocationBuilder =
        helloworldWebTargetWithQueryParam.request(MediaType.APPLICATION_JSON_TYPE); // (4)

Response response = invocationBuilder.get(); // (5)
System.out.println(response.getStatus());
System.out.println(response.readEntity(String.class));
1 クライアントで URL http://example.com/rest に対して要求を実行する準備ができました。
2 http://example.com/rest/resource/helloworld のパスを追加します。
3 クエリパラメータを設定します。クエリパラメータは http://example.com/rest/resource/helloworld?greeting=Hi+World%21 のようになります。
4 JSON 形式の応答が必要であることを指定します。
5 GET 要求を実行します。

Cookbook 用の REST サーバ

Cookbook REST サーバに含まれる 3 つのリソースをテストする場合、SDK に基づくようにコネクタをセットアップし、認証メカニズムとして OAuth v2 を選択します。

詳細は、​Jersey クライアントに関するドキュメントを参照してください。

Was this article helpful?

💙 Thanks for your feedback!

Edit on GitHub