S2Laszloもどき 実装

LaszloJapanのichanさんと同じでJavaの受け口は一箇所になっています。また、S2Flexのソースを見ながら作ったので、ほとんど同じコードになっちゃいました。S2Flexと同じにしておくとJava側を作成する人あまり悩まなくてもすむしね。

まず、Java側から・・・。

package u1h.seasar.laszlomodoki;

import java.util.List;

import org.seasar.framework.beans.BeanDesc;
import org.seasar.framework.beans.factory.BeanDescFactory;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.SingletonS2ContainerFactory;

/**
 * @author u1hoshino
 */
public class S2LaszloModokiService {
  
  private S2LaszloModokiService() {
  }

  public static ReturnObject service(String compname, String methodName) {
    return service(compname, methodName, null);
  }

  public static ReturnObject service(String compname, String methodName,
      List argList) {
    ReturnObject returnObject = new ReturnObject();

    try {
      S2Container container = SingletonS2ContainerFactory.getContainer();
      Object component = container.getComponent(compname);
      BeanDesc beanDesc = BeanDescFactory.getBeanDesc(component
          .getClass());
      Object obj = beanDesc.invoke(component, methodName,
          convertArgList(argList));

      returnObject.setObject(obj);
      returnObject.setSuccess(true);
    } catch (Throwable t) {
      // TODO エラー処理
      // t.printStackTrace();
      returnObject.setSuccess(false);
      returnObject.setErrMesg(t.getMessage());
    }
    return returnObject;
  }

  private static Object[] convertArgList(List argList) {
    if (argList == null) {
      return null;
    }

    Object[] args = new Object[argList.size()];
    for (int i = 0; i < argList.size(); i++) {
      args[i] = argList.get(i);
    }
    return args;
  }
}

次に転送用クラス

package u1h.seasar.laszlomodoki;

/**
 * @author u1hoshino
 */
public class ReturnObject {
  private boolean success;

  private String errMesg;

  private Object object;

  public String getErrMesg() {
    return errMesg;
  }

  void setErrMesg(String errMesg) {
    this.errMesg = errMesg;
  }

  public Object getObject() {
    return object;
  }

  void setObject(Object object) {
    this.object = object;
  }

  public boolean isSuccess() {
    return success;
  }

  void setSuccess(boolean success) {
    this.success = success;
  }

}

次はLaszlo側のクラス

<?xml version="1.0" encoding="UTF-8"?>
<!-- u1h/s2laszlomodoki.lzx -->
<library>
  <security>
    <allow>
      <pattern>^u1h.seasar.laszlomodoki.S2LaszloModokiService</pattern>
    </allow>
  </security>
  
  <!-- javarpcの宣言:ここでないと上手く動きません? -->
  <javarpc name="_rpc" scope="none" classname="u1h.seasar.laszlomodoki.S2LaszloModokiService"
    objectreturntype="javabean">
  </javarpc>

  <class name="s2laszlomodoki">
  
    <attribute name="_timestamp" value="0"/>
    <attribute name="_inittime" type="number" value="0"/>
    <attribute name="_invoketime" type="number" value="0"/>
    <attribute name="_endtime" type="number" value="0"/>
    <attribute name="_invokeflag" type="boolean" value="false"/>

    <attribute name="compname" value="null"/>
    
    <!-- メソッド名 -->
    <attribute name="method" value="null"/>
    
    <!-- 取得データ格納場所 -->
    <attribute name="resultObject" value="null"/>
    <attribute name="resultAttribute" value="null"/>
    
    <!-- データ取得後に処理させたいメソッド:未実装 -->
    <attribute name="afterProcObject" value="null"/>
    <attribute name="afterProcMethod" value="null"/>
    
    <!-- データ取得エラー時に処理させたいメソッド:未実装 -->
    <attribute name="afterErrObject" value="null"/>
    <attribute name="afterErrMethod" value="null"/>
    
    <!-- イベント -->
    <attribute name="ondata" value="null"/>
    <attribute name="onerror" value="null"/>
    
    <!-- remotecall:このオブジェクトのライフサイクルに依存 -->
    <remotecall name="service" funcname="service" remotecontext="$once{_rpc}">
    </remotecall>
    
    <method event="oninit">
      // 初期化:イベントのリスニングを行います。
      // あと、一度使用されたオブジェクトを使用されないように_timestampで制御します
      this._inittime=(new Date()).getTime();
      this._invokeflag = true;
      
      var onloadDelegate = new LzDelegate(this, '_onload');
      onloadDelegate.register( _rpc, "onload" );
      
      var onerrorDelegate = new LzDelegate(this, '_onerror');
      onerrorDelegate.register( _rpc, "onerror" );
      
      var ondataDalegate = new LzDelegate(this, '_ondata');
      ondataDalegate.register( this.service, 'ondata' );
    </method>
    
    <method name="_onload">
      // 処理なし
    </method>
    
    <method name="_onerror" args="errmsg">
      // TODO: もうちょっとエラー処理が必要
      Debug.write("s2laszlomodoki error: " + errmsg );
    </method>
    
    <method name="_ondata" args="remoteObject"><![CDATA[
      var success = remoteObject['success'];
      if( !success ){
        var errMesg = remoteObject['errMesg'];
        this.onerror.sendEvent(errMesg);
        return;
      }
      
      var obj = remoteObject['object'];
      
      if( resultObject != null && resultAttribute != null ) {
        resultObject.setAttribute(resultAttribute, obj );
      }
    
    ]]>  
    </method>
    
    <method name="invoke" args="argList"><![CDATA[
      if( !this._invokeflag ) {
        this.onerror.sendEvent("オブジェクトが使いまわされとるがや" );
        return;
      }
      this._invokeflag = false;
      this._exectime = ( new Date() ).getTime();
      
      if( target == null ) {
        this.onerror.sendEvent("targetがねーがや" );
        return;
      }
      
      if( method == null ) {
        this.onerror.sendEvent("methodがねーでかんわー" );
        return;
      }
      
      var params = null;
      if( argList == null ){
        // こうしないとargListがnullという文字列で渡されてしまう・・・
        params = new Array(target, method);
      } else {
        params = new Array(target, method, argList);
      }
      this.service.invoke(params, null);
    ]]>
    </method>
  </class>
</library>

こいつは、まだまだ改良の余地がありですな・・・テストほとんどしてないし。

あとは、web.xml、app.dicon、modoki.dicon(何でもいいんだけど)を書いてあげれば出来上がるのかな?

そうそう、よく考えれば、これってサーバ側はSpringでもいいし、リフレクションでそのまま起動できるようにしちゃってもいいんだよね。でも、まずはSeasar2ですよね。

あー、つかれた・・・。ほんとは昨日出来てたんだけどね、書くと大変だから次の日にと思ってたんだけど・・・一日ちがいとは・・・。

ま、何はともあれ、ご意見募集中!