OpenLaszloアドバンスド・テクニック
連載:Flexのクライアントサイドをオープンソースで制覇する
(最終回)

Flexのフレームワーク、Cairngormで
検索アプリ完成



ダイヤモンドコンピューターサービス
SI技術部 技術推進グループ
吉田 靖宏
2006/10/13
最終回となる今回は、サンプルアプリケーションの残りの部分(サーバサイド連携部分)を作成し、サンプルアプリケーションを完成させる

 前回(Flexのフレームワーク、Cairngormでサンプルアプリ)では、実際にCairngorm Frameworkを使ってアプリケーションサーバの検索・登録・更新・削除を行う簡単なサンプルアプリケーションを途中まで作成しました。最終回となる今回は、サンプルアプリケーションの残りの部分(サーバサイド連携部分)を作成し、サンプルアプリケーションを完成させます。

BusinessDelegateの作成

 それでは、サーバサイド連携部分を作成していきましょう。まず、BusinessDelegateを作成します。下のリストは、サンプルアプリケーションのBusinessDelegateであるSampleappBusinessDelegate.asです。これをcairngorm-sample/WebContent/sample/businessに作成してください。

リスト1 SampleappBusinessDelegate.as

import org.nevis.cairngorm.business.Responder;
import org.nevis.cairngorm.business.ServiceLocator;
import sample.vo.APServerVO;
import mx.utils.Delegate;

class sample.business.SampleappBusinessDelegate {

    private var service : Object;

    private var responder : Responder;

    public function SampleappBusinessDelegate
(responder : Responder)
 {(本来は1行)
        this.service = ServiceLocator.getInstance().
getService("sampleAppService")
;(本来は1行)
        this.responder = responder;
    }

    public function searchAPServer(searchCond : String) : Void {
        var call = service.search(searchCond);

        call.resultHandler = Delegate.create(responder, responder.onResult);
        call.faultHandler = Delegate.create(responder, responder.onFault);
    }

    public function registAPServer(apServerVO : APServerVO) : Void {
        var call = service.regist(apServerVO);

        call.resultHandler = Delegate.create(responder, responder.onResult);
        call.faultHandler = Delegate.create(responder, responder.onFault);
    }

    public function updateAPServer(apServerVO : APServerVO) : Void {
        var call = service.update(apServerVO);

        call.resultHandler = Delegate.create(responder, responder.onResult);
        call.faultHandler = Delegate.create(responder, responder.onFault);
    }

    public function removeAPServer(selectedNo : String) : Void {
        var call = service.remove(selectedNo);

        call.resultHandler = Delegate.create(responder, responder.onResult);
        call.faultHandler = Delegate.create(responder, responder.onFault);
    }
}

 BusinessDelegateは、特に継承すべき親クラスや実装すべきインターフェイスはありません。BusinessDelegateでは、サーバサイドのサービスにアクセスする処理を記述します。

 ここでは、各Commandに対応するsearch(検索)、regist(登録)、update(更新)、remove(削除)の4つのメソッドを実装しています。それぞれのメソッドの中では、ServiceLocatorから取得した「sampleAppService」という名前のサービス(このサンプルではRemote Object)のメソッドを呼び出しています。サービスの取得は、コンストラクタの中で行っており、取得したサービスはインスタンス変数に保持しています。

 また、Flexのクラスライブラリで提供されているDelegateクラスのcreateメソッドを使用して、サーバサイドのサービス呼び出しが成功したときと失敗したときのハンドリングをCommandに委譲させています(Delegateクラスについては、Flex1.5のAPIリファレンスを参照してください)。

 Commandのインスタンスは、コンストラクタの引数に渡されてくるので、サービスと同じようにインスタンス変数に保持しています。Commandのインスタンスは、Responderインターフェイスで受けていることに注意してください。

Servicesの作成

 次にServicesを作成します。下のリストは、サンプルアプリケーションのServicesであるSampleappServices.mxmlです。これをcairngorm-sample/WebContent/sample/businessに作成してください。

リスト2 SampleappServices.mxml

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

<cairngorm:ServiceLocator xmlns:mx="http://www.macromedia.com
/2003/mxml"(本来は1行) 
                          xmlns:cairngorm="http://www.
iterationtwo.com/cairngorm">(本来は1行)

  <mx:RemoteObject id="sampleAppService" named="
sampleAppService" 
(本来は1行)
                   protocol="http"
                   showBusyCursor="true"
                   result="event.call.resultHandler(event)"
                   fault="event.call.faultHandler(event)">
  </mx:RemoteObject>


</cairngorm:ServiceLocator>

 今回は、サーバサイドのサービスにRemote Objectを使用するので、ログインサンプルと同じように、<mx:RemoteObject>タグを使ってサービスを定義しています。idプロパティは、ServiceLocatorのgetServiceメソッドでサービスを取得する際に指定する文字列です。namedプロパティは、WEB-INF/flex/flex-config.xmlに定義するサービス名です。ここでは、idプロパティとnamedプロパティの値を同じにしていますが、ここは別々の値でも構いません。そのほかのプロパティについては、Flex1.5のAPIリファレンスまたは、Flexアプリケーション開発ガイドを参照してください。

 なお、対応するflex-config.xmlは以下のようになります。<object>タグ内の<source>タグには、Remote Objectとして作成するJavaクラスの完全修飾名を、<type>タグにはstateless-classかstateful-classのいずれかを指定します。

リスト3 flex-config.xml

&<?xml version="1.0" encoding="UTF-8"?>
<flex-config xmlns="http://www.macromedia.com/2003/flex-config">

  (省略)

  <remote-objects>
    <whitelist>

  (省略)

      <named>
        <object name=" sampleAppService ">
          <source>sample.service.SampleappService</source>
          <type>stateful-class</type>
        </object>

      </named>

    </whitelist>
  </remote-objects>

  (省略)

</flex-config>

StatelessとStateful

Remote Objectには、クラインアントからリクエストが来るたびにインスタンスを生成・破棄するStatelessなものと、一度インスタンスを生成するとそれを保持しておいて、以後、同一クライアントからのリクエストには、そのインスタンスが応えるStatefulなものの2種類があります。

今回のサンプルでは、サービスであるJavaクラスのインスタンス変数に保持しているMapオブジェクトに対して、アプリケーションサーバの追加や更新、削除などを行うため、毎回インスタンスを生成してしまうとインスタンス変数も初期化されてしまい、登録や更新、削除などが反映されなくなってしまいます。そのため、<type>にはstateful-classを指定しています。

サービス(Remote Object)の作成

 続いて、サーバサイドのサービス(Remote Object)を作成します。下のリストは、サンプルアプリケーションのRemote ObjectであるSampleappService.javaです。これをcairngorm-sample/src/sample/serviceに作成してください。

リスト4 SampleappService.java

package sample.service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import sample.vo.APServerVO;

public class SampleappService {

    /** アプリケーションサーバを保持するMap(DBの代わり) */
    private Map _apServerMap;

    /**
     * コンストラクタ
     */
    public SampleappService() {
        // 初期データの準備
        _apServerMap = new LinkedHashMap();

        Map apServer = new HashMap();
        apServer.put("productCode", "101");
        apServer.put("productName", "Tomcat");
        apServer.put("vendor", "Apache Software Foundation");
        apServer.put("price", "0");
        apServer.put("note", "EJBコンテナなし、オープンソース");
        _apServerMap.put("101", apServer);

        apServer = new HashMap();
        apServer.put("productCode", "102");
        apServer.put("productName", "WebLogic Server");
        apServer.put("vendor", "BEA");
        apServer.put("price", "2000000");
        apServer.put("note", "CPUがデュアルコアの場合は1.5倍の金額");
        _apServerMap.put("102", apServer);

        apServer = new HashMap();
        apServer.put("productCode", "103");
        apServer.put("productName", "WebSphere Application Server");
        apServer.put("vendor", "IBM");
        apServer.put("price", "2500000");
        apServer.put("note", "1年間サポート付");
        _apServerMap.put("103", apServer);
    }

    /**
     * アプリケーションサーバの検索を行います。
     */
    public Collection search(String searchCond) {

        // 検索条件(製品コード)がない場合、全件を返す
        if (searchCond == null || searchCond.trim().length() == 0) {
            return _apServerMap.values();
        }

        // 検索条件(製品コード)に該当するAPサーバをMapから取得
        Map targetObj = (Map) _apServerMap.get(searchCond);
        if (targetObj != null) {
            List list = new ArrayList();
            list.add(targetObj);
            return list;
        }

        return null;
    }

    /**
     * アプリケーションサーバの登録を行います。
     */
    public void regist(APServerVO apServerVO) {
        Map apServer = new HashMap();
        apServer.put("productCode", apServerVO.getProductCode());
        apServer.put("productName", apServerVO.getProductName());
        apServer.put("vendor", apServerVO.getVendor());
        apServer.put("price", apServerVO.getPrice());
        apServer.put("note", apServerVO.getNote());
        _apServerMap.put(apServerVO.getProductCode(),apServer);
    }

    /**
     * アプリケーションサーバの更新を行います。
     */
    public void update(APServerVO apServerVO) {
        regist(apServerVO);
    }

    /**
     * アプリケーションサーバの削除を行います。
     */
    public void remove(String productCode) {
        _apServerMap.remove(productCode);
    }
}

  Remote Objectは、特に継承すべき親クラスや実装すべきインターフェイスはなく、POJOとして作成できます。ここでは、BusinessDelegateから呼ばれるsearch(検索)、regist(登録)、update(更新)、remove(削除)の4つのメソッドを実装しています。今回は、サンプルということで実装を簡略化するため、実際にデータベースへのアクセスは行わず、インスタンス変数のMapに対して検索・登録・更新・削除を行っています。

 インスタンス変数のMapは、コンストラクタの中で初期化され、“Tomcat”“WebLogic Server”“WebSphere Application Server”の3つのアプリケーションサーバを初期データとして保持しています。登録した順番を考慮してデータグリッドに表示させるため、Mapの実装クラスにはLinkedHashMapを使用しています。

 1つのアプリケーションサーバは、製品コード、製品名、ベンダ、価格、備考の5つのキーを持った1つのMapで表しています。このMapがインスタンス変数のMapに製品コードをキーにして保持されています。こちらのMapは、キーの順番を考慮する必要がないので、Mapの実装クラスにはHashMapを使用しています(価格、備考に関しては、実際の製品のものではなく、サンプル用の適当な値です)。

 Mapの中にMapが入る入れ子の構造になっているので、「そんなことしないでListの中にMapを入れていけばいいんじゃないの? そうすれば、LinkedHashMapなんて使わなくても登録された順番が考慮されるし」と思った方がいるかもしれません。確かに、Listの中にMapを入れていく方が分かりやすいのですが、これだと検索するときにListの中のMapたちを全件、走査していかなければなりません。Listの要素が少ない場合は、これでもいいかもしれませんがListの要素が多くなるにつれて、検索に掛かる時間はどんどん増えてしまいます。更新や削除を行う際も、まず更新・削除対象を特定するために検索を行うので、更新や削除にも時間が掛かってしまいます。

 しかし、ListではなくMapを使うと、キーでアクセスできるため、検索が速くなります。これは、データベースにおけるインデックスを使った検索に当たります。データベースでもインデックスを使わない検索は、遅くなりますがListを使うのもそれと同じことです。そのような理由から、アプリケーションサーバを保持するオブジェクトは、ListではなくMapにしています。

  1/2

 INDEX

連載:Flexのクライアントサイドをオープンソースで制覇する(5)
 Flexのフレームワーク、Cairngormでサンプルアプリ
Page1 BusinessDelegateの作成
Servicesの作成/サービス(Remote Object)の作成
  Page2 サーバサイドとの連携
サンプルアプリケーションの動作確認/まとめ




HTML5 + UX フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

HTML5+UX 記事ランキング

本日 月間