Tuscany SCA で独自の Implementation Types を使用

Tuscany SCA では、比較的容易に SCA アセンブリモデルを拡張できるアーキテクチャが採用されており、モジュールを追加する事によって以下のような型を拡張する事ができる。

  • Implementation types
  • Binding types
  • Data binding types
  • Interface types

実際、Tuscany SCA ではコンポーネントの実装に Java・Spring・スクリプト言語BPEL等が使えるようになっているが、これらも個々の実装毎にモジュールとして定義されている。

そこで今回は、Tuscany Java SCA 1.0 を使って独自の Implementation type を追加する方法を調べてみる事にする。

独自 Implementation type の追加(ヘルパークラス使用)

Tuscany SCA 1.0 ソースコードの modules ディレクトリ内の implementation-xxxx ディレクトリ内のファイル構成やソースの内容をざっと見た限りでは、独自 Implementation type を作成するには、正式に定義する方法とヘルパークラスを使って簡易的に定義する方法があるようだ。

とりあえず、今回は implementation-script のソースを参考にして、ヘルパークラスを使った独自 Implementation type の作成を実施してみた。

作成手順は以下の通りで、Tuscany に追加するには JAR ファイル化して Tuscany Runtime 実行時のクラスパスに追加するだけでよい模様。

  1. .composite ファイルの定義値を格納するための Implementation クラス
  2. 処理を実行する Invoker クラス
  3. Invoker のファクトリクラス
  4. Implementation と Invoker のファクトリクラスをシステム側に提供する Activator クラス
  5. Activator の定義ファイル
.composite ファイルの定義値を格納するための Implementation クラス作成

.composite ファイルで定義する implementation 要素とその属性に基づいた Implementation クラスを以下のようなルールに則って作成する。

  • 作成方法
    • implementation.xxxx 要素の xxxx の先頭を大文字にして Implementation を付けたクラス名を定義(XxxxImplementation)
    • implementation.xxxx 要素の属性をアクセサメソッドで定義

例えば、.composite ファイルで のような記述をしたい場合、 AaaaImplementation クラスを作成し、bbbb のアクセサメソッドを定義する。

(例) SimpleImplementation.java ファイル

package simple;

public class SimpleImplementation {

    private String name;

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

なお、上記 SimpleImplementation クラスで処理可能な .composite ファイルの記述例は以下のようになる。

compisite ファイル記述例

<composite xmlns="http://www.osoa.org/xmlns/sca/1.0" 
        xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.0"
        targetNamespace="http://aaa" name="aaa">

    <component name="SimpleServiceComponent">
        <!-- 属性の値は . を含んでいないと実行時にエラー発生 -->
        <tuscany:implementation.simple name="test.bbb" />
    </component>
</composite>
処理を実行する Invoker クラス作成

実装処理を実行するクラスを org.apache.tuscany.sca.invocation.Invoker インターフェースを実装する事で作成する。

実行すべきメソッドの名称や入力値(引数)が Message オブジェクトとして渡されるため、この情報を使って処理を実施する。(implementation.script ではスクリプトの処理を呼び出している)

  • 作成方法
    • Invoker インターフェースを実装
    • Message オブジェクトの getBody で引数を取得
    • Message オブジェクトの getOperation でメソッド情報を取得
    • Message オブジェクトの setBody で戻り値を設定

(例) SimpleInvoker.java ファイル

package simple;

import org.apache.tuscany.sca.invocation.Invoker;
import org.apache.tuscany.sca.invocation.Message;

public class SimpleInvoker implements Invoker {

    private SimpleImplementation impl;

    public SimpleInvoker(SimpleImplementation impl) {
        this.impl = impl;
    }

    public Message invoke(Message msg) {
        //メソッド呼び出し時の引数を取得
        Object[] params = (Object[])msg.getBody();

        //メソッドの実行結果を設定
        msg.setBody(params[0] + "_simple");

        return msg;
    }
}
Invoker のファクトリクラス作成

Invoker のファクトリクラスを org.apache.tuscany.sca.extension.helper.InvokerFactory インターフェースを実装する事で作成する。

  • 作成方法
    • InvokerFactory インターフェースを実装
    • createInvokder メソッドで Invoker のインスタンスを返す

(例) SimpleInvokerFactory.java ファイル

package simple;

import org.apache.tuscany.sca.extension.helper.InvokerFactory;
import org.apache.tuscany.sca.interfacedef.Operation;
import org.apache.tuscany.sca.invocation.Invoker;

public class SimpleInvokerFactory implements InvokerFactory {

    private SimpleImplementation impl;

    public SimpleInvokerFactory(SimpleImplementation impl) {
        this.impl = impl;
    }

    public Invoker createInvoker(Operation operation) {
        return new SimpleInvoker(impl);
    }
}
Implementation と Invoker のファクトリクラスをシステム側に提供する Activator クラス作成
  • 作成方法
    • ImplementationActivator を Implementation のクラスを指定して実装
    • getImplementationClass メソッドで Implementation のクラスを返す
    • createInvokerFactory メソッドで InvokerFactory のインスタンスを返す

(例) SimpleImplementationActivator.java ファイル

package simple;

import org.apache.tuscany.sca.assembly.ComponentType;
import org.apache.tuscany.sca.extension.helper.ImplementationActivator;
import org.apache.tuscany.sca.extension.helper.InvokerFactory;

import org.apache.tuscany.sca.runtime.RuntimeComponent;

public class SimpleImplementationActivator implements ImplementationActivator<SimpleImplementation> {

    public Class<SimpleImplementation> getImplementationClass() {
        return SimpleImplementation.class;
    }

    public InvokerFactory createInvokerFactory(RuntimeComponent rc, ComponentType ct, SimpleImplementation implementation) {
        return new SimpleInvokerFactory(implementation);
    }
}
Activator の定義ファイル作成
  • 作成方法
    • org.apache.tuscany.sca.extension.helper.ImplementationActivator ファイルに Activator のクラス名を記述
    • JAR ファイルの META-INF/services にファイルを配置

(例) org.apache.tuscany.sca.extension.helper.ImplementationActivator ファイル

simple.SimpleImplementationActivator
JAR ファイル化

これまでに作成した 5つのファイルを含めた JAR ファイルを作成する。ただし、org.apache.tuscany.sca.extension.helper.ImplementationActivator ファイルは META-INF/services 内に配置する必要がある。

(例) JAR ファイルの構成例

  • simple
    • SimpleImplementation.class ファイル
    • SimpleImplementationActivator.class ファイル
    • SimpleInvoker.class ファイル
    • SimpleInvokerFactory.class ファイル
  • META-INF
    • services
      • org.apache.tuscany.sca.extension.helper.ImplementationActivator ファイル

作成した Implementation Type を試す

上記で作成した JAR ファイルをクラスパスの設定に加え、下記のようなファイルで構成されるコンポジットを作成して実行する。

コンポジット定義ファイルの作成

作成した Implementation Type を使ったサービスコンポーネントを定義する。
SimpleImplementation クラスを使用するために tuscany:implementation.simple 要素を記述し、SimpleImplementation クラスの各プロパティの設定値を "プロパティ名=値" のように属性として記述する。

なお、理由はわからないが、tuscany:implementation.simple 要素の属性値は "." を含んでいる必要があり、 "." を含めないと実行時に StringIndexOutOfBoundsException が発生してしまう。

(例)simple.composite ファイル

<?xml version="1.0" encoding="UTF-8"?>
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0" 
        xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.0"
        targetNamespace="http://simple" name="simple">

    <component name="SimpleServiceComponent">
        <!-- 属性の値は . を含む必要あり -->
        <!-- SimpleImplementation の name プロパティに "test.aaa" を設定 -->
        <tuscany:implementation.simple name="test.aaa" />
    </component>

</composite>
インターフェースの作成

実行クラスでサービスコンポーネントをキャストするためのインターフェースを作成する。メソッドのシグネチャ(名称・引数・戻り値)は何でもよい。

(例) Simple.java ファイル

package simple;

public interface Simple {
    String getGreetings(String name);
}
実行クラスの作成

コンポジット定義ファイルからサービスコンポーネントを取得し、メソッドを実行するクラスを作成する。
サービスコンポーネントのメソッドを実行すれば、Invoker インターフェース実装オブジェクトの invoke メソッドが呼び出される。

(例) Simple.java ファイル

package simple;

import org.apache.tuscany.sca.host.embedded.SCADomain;

public class SimpleClient {

    public static void main(String[] args) {

        SCADomain scad = SCADomain.newInstance("simple.composite");

        Simple service = scad.getService(Simple.class, "SimpleServiceComponent");

        //SimpleInvoker の invoke メソッドが実行される
        System.out.printf("result: %s\n", service.getGreetings("a1234"));
        //実行結果 "result: a1234_simple"

        scad.close();
    }
}