Flashとの連携

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

セットアップ

Flash側には、 Flash Remotingコンポーネントをインストールします。
Flash MXをインストールしたディレクトリを$FLASH_HOMEと呼ぶことにすると、
$SEASAR_HOME/actionscriptにあるseasar,examplesディレクトリを
$FLASH_HOME/Configuration/Includeにコピーします。

次に、NazunaAMFをセットアップします。
Seasarを終了させた後、
$SEASAR_HOME/src/org/seasarでant webappexamplesを実行し、
Seasarを起動しなおします。
ブラウザで、http://localhost:8080/seasar/gateway にアクセスし、AMFGateway is runningと表示されれば、
NazunaAMFのセットアップは完了です。

Flowletの呼び出し

それでは早速、足し算をするFlowletを作成して、Flashから呼び出してみましょう。
AddFlowlet.xmlを次のように記述し、seasar/WEB-INF/classes/examples/org/seasar/nazunaに置きます。
実際は、セットアップ済なので、この作業は不要です。
この場合のFlowletの名前は、seasar/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という名前を付けて保存します。
$SEASAR_HOME/flash/examples/seasar/nazuna/AddFlowletClient.flaに完成したバージョンがあります。
タイムラインパネルのレイヤー1の最初のフレームの上で右クリックして、アクションを選びます。
アクションパネルの右上の矢印のアイコンをクリックして、エキスパートモード、行番号の表示を
選択しておきます。今後、アクションスクリプトはエキスパートモードで記述していきます。
記述するアクションスクリプトは以下のようになります。

#include "NetServices.as"

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

function onResult(result) {
	trace(result);
}

function onStatus(result) {
	trace(result.details);
}

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

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

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

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

NetServiceProxyオブジェクトのexecute()を呼び出し、NazunaAMFにリクエストを送ります。
引数は、Flowletのargタグと対応させます。
ActionScriptの数値型は、NazunaAMFによって、argタグのclassName属性で指定したオブジェクトに変換されます。

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

Ruletの呼び出し

AddFlowletと同様に、足し算をするRuletを作成して、Flashから呼び出してみましょう。
AddRulet.javaを次のように記述し、seasar/WEB-INF/src/examples/org/seasar/nazunaに置きます。
実際は、セットアップ済なので、この作業は不要です。
この場合のRuletの名前は、seasar/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という名前を付けて保存します。
$SEASAR_HOME/flash/examples/seasar/nazuna/AddRuletClient.flaに完成したバージョンがあります。
記述するアクションスクリプトは以下のようになります。

#include "NetServices.as"

ruletCallback = new Object();
ruletCallback.onResult = function(result) {
	trace(result);
}
ruletCallback.onStatus = function(result) {
	trace(result.details);
}

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

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

Ruletの場合、NetServiceProxyオブジェクトのexecuteRulet()を呼び出し、
NazunaAMFにリクエストを送ります。
引数は、doExecute()の引数に対応させます。
Flashの数値型は、NazunaAMFによって、doExecute()の引数の型に合わせて変換されます。

Sqletの呼び出し

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

SqletのexecuteQuery()を呼び出した結果は、JavaBeansを要素にもつjava.util.ArrayListになります。
java.util.ArrayListは、ActionScriptの配列(Arrayオブジェクト)に変換されます。
JavaBeansは、ActionScriptのクラスのオブジェクトに変換されます。
ActionScriptのクラスの定義は次のようになります。
このファイルは、$FLASH_HOME/Configuration/Include/examples/seasar/nazuna/Employee.asに
保存します。$SEASAR_HOME/actionscript/examplesを$FLASH_HOME/Configuration/Includeに
コピー済ならこの作業は不要です。

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という名前を付けて保存します。
$SEASAR_HOME/flash/examples/seasar/nazuna/SelectSqletClient.flaに完成したバージョンがあります。
記述するアクションスクリプトは以下のようになります。

SelectSqletClient.fla

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

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

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

function onStatus(result) {
    trace(result.details);
}

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

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

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

departmentNo:20, commission:null, salary:800, hireDate:Wed Dec 17 00:00:00 GMT+0900 1980, job:CLERK, employeeName:SMITH, employeeNo:7369, manager:7902
departmentNo:30, commission:300, salary:1600, hireDate:Fri Feb 20 00:00:00 GMT+0900 1981, job:SALESMAN, employeeName:ALLEN, employeeNo:7499, manager:7698
departmentNo:30, commission:500, salary:1250, hireDate:Sun Feb 22 00:00:00 GMT+0900 1981, job:SALESMAN, employeeName:WARD, employeeNo:7521, manager:7698
departmentNo:20, commission:null, salary:2975, hireDate:Thu Apr 2 00:00:00 GMT+0900 1981, job:MANAGER, employeeName:JONES, employeeNo:7566, manager:7839
departmentNo:30, commission:1400, salary:1250, hireDate:Mon Sep 28 00:00:00 GMT+0900 1981, job:SALESMAN, employeeName:MARTIN, employeeNo:7654, manager:7698
departmentNo:30, commission:null, salary:2850, hireDate:Fri May 1 00:00:00 GMT+0900 1981, job:MANAGER, employeeName:BLAKE, employeeNo:7698, manager:7839
departmentNo:10, commission:null, salary:2450, hireDate:Tue Jun 9 00:00:00 GMT+0900 1981, job:MANAGER, employeeName:CLARK, employeeNo:7782, manager:7839
departmentNo:20, commission:null, salary:3000, hireDate:Thu Dec 9 00:00:00 GMT+0900 1982, job:ANALYST, employeeName:SCOTT, employeeNo:7788, manager:7566
departmentNo:10, commission:null, salary:5000, hireDate:Tue Nov 17 00:00:00 GMT+0900 1981, job:PRESIDENT, employeeName:KING, employeeNo:7839, manager:0
departmentNo:30, commission:0, salary:1500, hireDate:Tue Sep 8 00:00:00 GMT+0900 1981, job:SALESMAN, employeeName:TURNER, employeeNo:7844, manager:7698
departmentNo:20, commission:null, salary:1100, hireDate:Wed Jan 12 00:00:00 GMT+0900 1983, job:CLERK, employeeName:ADAMS, employeeNo:7876, manager:7788
departmentNo:30, commission:null, salary:950, hireDate:Thu Dec 3 00:00:00 GMT+0900 1981, job:CLERK, employeeName:JAMES, employeeNo:7900, manager:7698
departmentNo:20, commission:null, salary:3000, hireDate:Thu Dec 3 00:00:00 GMT+0900 1981, job:ANALYST, employeeName:FORD, employeeNo:7902, manager:7566
departmentNo:10, commission:null, salary:1300, hireDate:Sat Jan 23 00:00:00 GMT+0900 1982, job:CLERK, employeeName:MILLER, employeeNo:7934, manager:7782

RecordSetの取得

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

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

SelectSqletClient2.fla

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

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

function onResult(rs) {
    trace(rs.getColumnNames());
    var num = rs.getLength();
    for (var i = 0; i < num; ++i) {
        trace(rs.getItemAt(i));
    }
}

function onStatus(result) {
    trace(result.details);
}

executeRSQuery()を呼び出すために、seasar/nazuna/NzRecordSet.asをインクルードします。
後は、executeRSQuery()を呼び出すだけで、RecordSetオブジェクトを取得できます。
実行すると、以下のような内容がトレースウィンドウに表示されます。

employeeNo,employeeName,job,manager,hireDate,salary,commission,departmentNo
departmentNo:20, commission:null, salary:800, hireDate:Wed Dec 17 00:00:00 GMT+0900 1980, job:CLERK, employeeName:SMITH, employeeNo:7369, manager:7902
departmentNo:30, commission:300, salary:1600, hireDate:Fri Feb 20 00:00:00 GMT+0900 1981, job:SALESMAN, employeeName:ALLEN, employeeNo:7499, manager:7698
departmentNo:30, commission:500, salary:1250, hireDate:Sun Feb 22 00:00:00 GMT+0900 1981, job:SALESMAN, employeeName:WARD, employeeNo:7521, manager:7698
departmentNo:20, commission:null, salary:2975, hireDate:Thu Apr 2 00:00:00 GMT+0900 1981, job:MANAGER, employeeName:JONES, employeeNo:7566, manager:7839
departmentNo:30, commission:1400, salary:1250, hireDate:Mon Sep 28 00:00:00 GMT+0900 1981, job:SALESMAN, employeeName:MARTIN, employeeNo:7654, manager:7698
departmentNo:30, commission:null, salary:2850, hireDate:Fri May 1 00:00:00 GMT+0900 1981, job:MANAGER, employeeName:BLAKE, employeeNo:7698, manager:7839
departmentNo:10, commission:null, salary:2450, hireDate:Tue Jun 9 00:00:00 GMT+0900 1981, job:MANAGER, employeeName:CLARK, employeeNo:7782, manager:7839
departmentNo:20, commission:null, salary:3000, hireDate:Thu Dec 9 00:00:00 GMT+0900 1982, job:ANALYST, employeeName:SCOTT, employeeNo:7788, manager:7566
departmentNo:10, commission:null, salary:5000, hireDate:Tue Nov 17 00:00:00 GMT+0900 1981, job:PRESIDENT, employeeName:KING, employeeNo:7839, manager:0
departmentNo:30, commission:0, salary:1500, hireDate:Tue Sep 8 00:00:00 GMT+0900 1981, job:SALESMAN, employeeName:TURNER, employeeNo:7844, manager:7698
departmentNo:20, commission:null, salary:1100, hireDate:Wed Jan 12 00:00:00 GMT+0900 1983, job:CLERK, employeeName:ADAMS, employeeNo:7876, manager:7788
departmentNo:30, commission:null, salary:950, hireDate:Thu Dec 3 00:00:00 GMT+0900 1981, job:CLERK, employeeName:JAMES, employeeNo:7900, manager:7698
departmentNo:20, commission:null, salary:3000, hireDate:Thu Dec 3 00:00:00 GMT+0900 1981, job:ANALYST, employeeName:FORD, employeeNo:7902, manager:7566
departmentNo:10, commission:null, salary:1300, hireDate:Sat Jan 23 00:00:00 GMT+0900 1982, job:CLERK, employeeName:MILLER, employeeNo:7934, manager:7782

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

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

ActionScriptからJava

ActionScriptJava
nullnull
undefinednull
booleanboolean
Numberint, long, double
Stringjava.math.BigDecimal
Stringjava.lang.String
Datejava.util.Date
配列java.util.ArrayList
Object.registerClass()
されているオブジェクト
対応するJavaのオブジェクト
Object.registerClass()
されていないオブジェクト
java.util.HashMap
RecordSet×

JavaからActionScript

JavaActionScript
nullnull
booleanboolean
int, long, doubleNumber
java.math.BigDecimalString
java.lang.StringString
java.util.DateDate
配列, java.util.Collection配列
Object.registerClass()
されているオブジェクト
対応するActionScript
のオブジェクト
Object.registerClass()
されていないオブジェクト
オブジェクト
org.seasar.nazuna.NzRecordSetseasar.nazuna.NzRecordSet