## Flashとの連携

Nazunaは、NazunaAMFという機能で、Flashと連携することもできます。
Flash側は、Flash Remotingを使って、NazunaAMFと会話します。

- セットアップ
- Flowletの呼び出し
- Ruletの呼び出し
- Sqletの呼び出し
- RecordSetの取得
- ActionScriptとJavaのデータマッピング

## セットアップ

Flash側には、 Flash Remotingコンポーネントをインストールします。
Flash MXをインストールしたディレクトリを$FLASH_HOMEと呼ぶことにすると、
$SEASAR_HOME/actionscriptにあるseasarディレクトリを
$FLASH_HOME/Configuration/Includeにコピーします。
EclipseからFlashを立ち上げることができるように設定します。
メニューのウィンドウ->設定->ワークベンチのツリーを展開し、
ファイルの関連付けを選びます。
ファイルタイプの追加をクリックし、*.flaと入力します。
関連付けられたエディターを追加し、外部プログラムから、Flash Docを選びます。
これで、パッケージエクスポローらーから、*.flaをダブルクリックするとFlashが立ち上がります。

## Flowletの呼び出し

それでは早速、足し算をするFlowletを作成して、Flashから呼び出してみましょう。
AddFlowlet.xmlを次のように記述し、WEB-INF/classes/examples/org/seasar/nazunaに置きます。
実際は、セットアップ済なので、この作業は不要です。
この場合のFlowletの名前は、WEB-INF/classes/以下のディレクトリの区切りを.に変換し、
拡張子の.xmlを除いたexamples.org.seasar.nazuna.AddFlowletになります。

#### examples.org.seasar.nazuna.AddFlowlet

    <flowlet>
        <input>
            <arg name="a" className="java.lang.Integer"/>
            <arg name="b" className="java.lang.Integer"/>
        </input>
        <output className="java.lang.Integer"/>
        <return>a + b</return>
    </flowlet>

Flash MXを起動します。新しいファイルにAddFlowletClient.flaという名前を付けて保存します。
実際は、examplesプロジェクト/flash/AddFlowletClient.flaに完成したバージョンがあります。
タイムラインパネルのレイヤー1の最初のフレームの上で右クリックして、アクションを選びます。
アクションパネルの右上の矢印のアイコンをクリックして、エキスパートモード、行番号の表示を
選択しておきます。今後、アクションスクリプトはエキスパートモードで記述していきます。
記述するアクションスクリプトは以下のようになります。

    #include "NetServices.as"

    NetServices.setDefaultGatewayURL("http://localhost:8080/examples/gateway");
    conn = NetServices.createGatewayConnection();
    flowlet = conn.getService("examples.org.seasar.nazuna.AddFlowlet", this);

    function onResult(result) {
    	c_txt.text = result;
    }

    function onStatus(result) {
        error_mb._x = (Stage.width - error_mb.width) / 2;
        error_mb._y = (Stage.height - error_mb.height) / 2;
        error_mb.setMessage(result.type + "\n" + result.description);
    	trace(result.details);
    }

    function calculate() {
        flowlet.execute(Number(a_txt.text), Number(b_txt.text));
    }

最初に、NetService.asをインクルード(#include)します。これで、Flash Remotingが使えるようになります。
NetDebug.asには、まだ対応していないので、インクルードしないようにしてください。

NetServices.setDefaultGatewayURL()で、NazunaAMFが稼動しているURLを指定します。
localhost:8080の部分は、自分の環境に応じて書き換えます。

NetServices.createGatewayConnection()で、NetConnectionオブジェクトを取得します。

NetConnectionオブジェクトのgetService()を呼び出し、NetServiceProxyオブジェクトを取得します。
最初の引数は、Flowlet名です。2番目の引数は、コールバックファンクションを
定義しているオブジェクトを指定します。この場合は、メインのタイムライン(this)になります。

Flowletを実行した結果は、非同期にコールバックファンクションで取得します。
正常に実行された場合、onResult()の引数で結果を取得します。
今回のケースでは、c_txt.textに結果を代入します。
例外が発生した場合、onStatus()の引数で例外を取得します。
例外オブジェクトのdetailsプロパティでJavaの例外のスタックトレースを取得できます。
その他に、typeプロパティで例外クラス名、descriptionプロパティで例外のメッセージが取得できます。

calculateファンクションでNetServiceProxyオブジェクトのexecute()を呼び出し、
NazunaAMFにリクエストを送ります。引数は、Flowletのargタグと対応させます。
計算ボタンをクリックするとcalculateファンクションが呼び出されます。

実行するには、 http://localhost:8080/examples/flash/AddFlowletClient.htmlをクリックします。

## Ruletの呼び出し

AddFlowletと同様に、足し算をするRuletを作成して、Flashから呼び出してみましょう。
AddRulet.javaを次のように記述し、WEB-INF/src/examples/org/seasar/nazunaに置きます。
実際は、セットアップ済なので、この作業は不要です。
この場合のRuletの名前は、WEB-INF/src/以下のディレクトリの区切りを.に変換し、
拡張子の.javaを除いたexamples.org.seasar.nazuna.AddRuletになります。

#### examples.org.seasar.nazuna.AddRulet

    package examples.org.seasar.nazuna;

    import org.seasar.nazuna.Rulet;

    public class AddRulet extends Rulet {

        public int doExecute(int a, int b) {
            return a + b;
        }
    }

Flash MXを起動します。新しいファイルにAddRuletClient.flaという名前を付けて保存します。
実際は、examplesプロジェクト/flash/AddRuletClient.flaに完成したバージョンがあります。
記述するアクションスクリプトは以下のようになります。

    #include "NetServices.as"

    ruletCallback = new Object();
    ruletCallback.onResult = function(result) {
        _root.c_txt.text = result;
    }
    ruletCallback.onStatus = function(result) {
        error_mb._x = (Stage.width - error_mb.width) / 2;
        error_mb._y = (Stage.height - error_mb.height) / 2;
        error_mb.setMessage(result.type + "\n" + result.description);
        trace(result.details);
    }

    NetServices.setDefaultGatewayURL("http://localhost:8080/examples/gateway");
    conn = NetServices.createGatewayConnection();
    rulet = conn.getService("examples.org.seasar.nazuna.AddRulet", ruletCallback);

    function calculate() {
        rulet.executeRulet(Number(a_txt.text), Number(b_txt.text));
    }

onResult()やonStatus()のコールバックファンクションは、
メインのタイムラインに直接記述するのではなく、Flowlet,Rulet,Sqletごとに
コールバックオブジェクトを作成して、そこに記述したほうが、
コードの見通しが良くなります。
作成したコールバックオブジェクトは、NetConnection.getService()の
2番目の引数に指定します。

Ruletの場合、NetServiceProxyオブジェクトのexecuteRulet()を呼び出し、
NazunaAMFにリクエストを送ります。
引数は、doExecute()の引数に対応させます。

実行するには、 http://localhost:8080/examples/flash/AddRuletClient.htmlをクリックします。

## Sqletの呼び出し

今度は、SelectSqletを、Flashから呼び出してみましょう。
SelectSqlet.xmlをWEB-INF/classes/examples/org/seasar/nazunaに置きます。
実際は、セットアップ済なので、この作業は不要です。
この場合のSqletの名前は、WEB-INF/classes/以下のディレクトリの区切りを.に変換し、
拡張子の.xmlを除いたexamples.org.seasar.nazuna.SelectSqletになります。

SqletのexecuteQuery()を呼び出した結果は、JavaBeansを要素にもつjava.util.ArrayListになります。
java.util.ArrayListは、ActionScriptの配列(Arrayオブジェクト)に変換されます。
JavaBeansは、ActionScriptのクラスのオブジェクトに変換されます。
ActionScriptのクラスの定義は次のようになります。
このファイルは、examplesプロジェクト/falsh/examples/seasar/nazuna/Employee.asに
保存します。実際は、セットアップ済なので、この作業は不要です。

#### examples/seasar/nazuna/Employee.as

    if (examples.seasar.nazuna.Employee === undefined) {
        #include "seasar/lang/SObject.as"

        seasar.lang.SObject.defineClass("examples.seasar.nazuna.Employee", null,
            ["employeeNo", "employeeName", "job", "manager", "hireDate", "salary", "commission", "departmentNo"]);
        Object.registerClass("examples.org.seasar.nazuna.Employee", examples.seasar.nazuna.Employee);
    }

クラスが、まだ未定義の場合にだけ、クラスを定義するようにするため
if (examples.seasar.nazuna.Employee === undefined)でチェックします。
例えば、A.asをB.asとC.asで使うためにインクルードするとき、A.asが

    if (A === undefined) {
        Aの定義
    }

のようになっていれば、B.asとC.asは、A.asが他のクラスで使われているかどうか気にせずに
インクルードすることができます。

クラスを定義するために、seasar/lang/SObject.asを先ずインクルードします。
これで、seasar.lang.SObjectが使えるようになります。
クラスの定義は、seasar.lang.SObject.defineClass()を呼び出して行います。
最初の引数はクラス名です。$FLASH_HOME/Configuration/Includeからのパスを
/を.に変換し、拡張子を取ったものがクラス名になります。
2番目の引数は、スーバークラスです。何も指定されていない場合、seasar.lang.SObjectを
継承することになります。
3番目の引数は、プロパティ名の配列です。SObjectを継承している場合、
ここで指定されたプロパティ以外を参照すると、トレースウィンドウで定義されていない
プロパティを参照したことを知ることができるので、スペルミスなどの発見が早くなります。

最後に、Object.registerClass()でJavaのクラスとActionScriptのクラスを関連付けます。
最初の引数は、Javaのクラス名です。2番目の引数は、ActionScriptのクラスになります。

Flash MXを起動します。新しいファイルにSelectSqletClient.flaという名前を付けて保存します。
実際は、examplesプロジェクト/flash/SelectSqletClient.flaに完成したバージョンがあります。
記述するアクションスクリプトは以下のようになります。

#### SelectSqletClient.fla

    #include "NetServices.as"
    #include "examples/seasar/nazuna/Employee.as"

    NetServices.setDefaultGatewayURL("http://localhost:8080/examples/gateway");
    conn = NetServices.createGatewayConnection();
    sqlet = conn.getService("examples.org.seasar.nazuna.SelectSqlet", this);

    function onResult(result) {
        result_lb.removeAll();
        var num = result.length;
        for (var i = 0; i < num; ++i) {
            result_lb.addItem(result[i].employeeName, result[i]);
            trace(result[i]);
    	}
    }

    function onStatus(result) {
        error_mb._x = (Stage.width - error_mb.width) / 2;
        error_mb._y = (Stage.height - error_mb.height) / 2;
        error_mb.setMessage(result.type + "\n" + result.description);
        trace(result.details);
    }

    function search() {
        sqlet.executeQuery();
    }

    function clear() {
        result_lb.removeAll();
    }

先ほど作成したEmployee.asをインクルードします。

Sqletの場合、NetServiceProxyオブジェクトのexecuteQuery()を呼び出し、NazunaAMFにリクエストを送ります。
引数がある場合、Sqletのargタグと対応させます。今回は引数はありません。

executeQuery()を実行した結果は、onResulet()の引数で、配列として取得できます。
配列の個々の要素は、先ほど説明したように、Employeeオブジェクトにマッピングされています。
EmployeeはSObjectを継承しているため、トレースによってプロパティとその値を知ることができます。

実行するには、 http://localhost:8080/examples/flash/SelectSqletClient.htmlをクリックします。

## RecordSetの取得

Flash Remotingでは、UIコンポーネントと簡単に連動できるRecordSetクラスが
提供されています。
executeQuery()のかわりにexecuteRSQuery()を呼び出すことで、RecordSetを取得することができます。

Flash MXを起動します。新しいファイルにSelectSqletClient2.flaという名前を付けて保存します。
実際は、examplesプロジェクト/flash/SelectSqletClient2.flaに完成したバージョンがあります。
記述するアクションスクリプトは以下のようになります。

#### SelectSqletClient2.fla

    #include "NetServices.as"
    #include "seasar/nazuna/NzRecordSet.as"
    #include "examples/seasar/nazuna/Employee.as"

    NetServices.setDefaultGatewayURL("http://localhost:8080/examples/gateway");
    conn = NetServices.createGatewayConnection();
    sqlet = conn.getService("examples.org.seasar.nazuna.SelectSqlet", this);

    function onResult(result) {
        result_lb.removeAll();
        var num = result.getLength();
        var emp = null;
        for (var i = 0; i < num; ++i) {
            emp = result.getItemAt(i);
            result_lb.addItem(emp.employeeName, emp);
            trace(emp);
        }
    }

    function onStatus(result) {
        error_mb._x = (Stage.width - error_mb.width) / 2;
        error_mb._y = (Stage.height - error_mb.height) / 2;
        error_mb.setMessage(result.type + "\n" + result.description);
        trace(result.details);
    }

    function search() {
        sqlet.executeRSQuery();
    }

    function clear() {
        result_lb.removeAll();
    }

executeRSQuery()を呼び出すために、seasar/nazuna/NzRecordSet.asをインクルードします。
後は、executeRSQuery()を呼び出すだけで、RecordSetオブジェクトを取得できます。

実行するには、 http://localhost:8080/examples/flash/SelectSqletClient2.htmlをクリックします。

## ActionScriptとJavaのデータマッピング

ActionScriptとJavaのデータマッピングは、次のようになります。

#### ActionScriptからJava

ActionScript | Java
--- | ---
null | null
undefined | null
boolean | boolean
Number | int, long, double
String | java.math.BigDecimal
String | java.lang.String
Date | java.util.Date
配列 | java.util.ArrayList
Object.registerClass() されているオブジェクト | 対応するJavaのオブジェクト
Object.registerClass() されていないオブジェクト | org.seasar.util.Struct
RecordSet | ×

#### JavaからActionScript

Java | ActionScript
--- | ---
null | null
boolean | boolean
int, long, double | Number
java.math.BigDecimal | String
java.lang.String | String
java.util.Date | Date
配列, java.util.Collection | 配列
Object.registerClass() されているオブジェクト | 対応するActionScript のオブジェクト
Object.registerClass() されていないオブジェクト | オブジェクト
org.seasar.nazuna.NzRecordSet | seasar.nazuna.NzRecordSet