Flashとの連携

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

セットアップ

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

ActionScriptJava
nullnull
undefinednull
booleanboolean
Numberint, long, double
Stringjava.math.BigDecimal
Stringjava.lang.String
Datejava.util.Date
配列java.util.ArrayList
Object.registerClass()
されているオブジェクト
対応するJavaのオブジェクト
Object.registerClass()
されていないオブジェクト
org.seasar.util.Struct
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