JSONRpcClientを作ってみた。

Tapestryとのコンボをやってみた。もっとうまい方法があるかも知れんけど。

使い方はこんな感じで。

<script jwcid="@brownie:JSONRpcClient" registerObjects="ognl: jsonObjects" />

registerObjectsは、クライアント側からアクセスするサーバー側のオブジェクトと名前のMapオブジェクトを指定します(名前がkeyでオブジェクトがvalueね)。

んで、例えば何かのボタンが押されたときにサーバー側にメッセージを送るとすると、

<script language="JavaScript" > 
function onclick_btnGetXXXXXX( btn ) {
	var key = document.Form0.field_key.value;
	var value = jsonrpc.serverObjeName.getXXXXXX( key );
	document.Form0.field_value.value = value
}
</script>

この場合、上のJSONRpcClientのregisterObjectsには、getXXXXXメソッドを持つオブジェクトをkeyを"serverObjeName"としてMapにputする必要があります。


で実装の方はどうなっているかってーと、component-specificationはこんな感じ。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE component-specification PUBLIC 
  "-//Apache Software Foundation//Tapestry Specification 3.0//EN" 
  "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">
  
<component-specification class="org.asyrinx.brownie.tapestry.components.jsonrpc.JSONRpcClient" allow-body="no" allow-informal-parameters="no">

  <description>
	JSON-RPC-Java client component  	
  </description>

  <parameter name="servletMappingUrl" type="java.lang.String" direction="in" default-value='"/JSON-RPC"'>
      <description>the mapping URL for JSONRPCServlet</description>
  </parameter>
  
  <parameter name="clientName" type="java.lang.String" direction="in" default-value='"jsonrpc"'>
      <description>variable name of JSONRpcClient object in JavaScript</description>
  </parameter>
  
  <parameter name="registerObjects" type="java.util.Map" direction="in" required="yes"/>

</component-specification>

登録したオブジェクトをJavaScript側から使うための名前は、Mapで指定するregisterObjectsのkeyが使われます。


んで、本体のJavaクラス。

package org.asyrinx.brownie.tapestry.components.jsonrpc;

import java.util.Iterator;
import java.util.Map;

import org.apache.tapestry.AbstractComponent;
import org.apache.tapestry.IMarkupWriter;
import org.apache.tapestry.IRequestCycle;
import org.asyrinx.brownie.tapestry.script.IScriptUser;
import org.asyrinx.brownie.tapestry.script.ScriptUsage;
import org.asyrinx.brownie.tapestry.script.ScriptWriter;
import org.asyrinx.brownie.tapestry.util.Scope;

import com.metaparadigm.jsonrpc.JSONRPCBridge;

/**
 * @author takeshi
 */
public abstract class JSONRpcClient extends AbstractComponent implements
        IScriptUser {

    private final ScriptWriter scriptWriter;

    public JSONRpcClient() {
        super();
        this.scriptWriter = new ScriptWriter(this, "JSONRpcClient.script");
        this.scriptWriter.setUsage(ScriptUsage.ONCE_BY_OBJECT);
    }

    public void prepareScriptSymbols(Map symbols, IRequestCycle cycle) {
        symbols.put("clientName", getClientName());
        symbols.put("contextPath", cycle.getRequestContext().getRequest()
                .getContextPath());
        symbols.put("servletMappingUrl", getServletMappingUrl());
    }

    protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle) {
        if (cycle.isRewinding())
            return;
        prepareBridge(cycle);
        scriptWriter.execute(cycle);
    }

    private static final String KEY_BRIDGE_ON_SESSION = "JSONRPCBridge";

    protected JSONRPCBridge prepareBridge(IRequestCycle cycle) {
        //final String key = this.getClass().getName() + "." + getClientName();
        final String key = KEY_BRIDGE_ON_SESSION;
        JSONRPCBridge result = (JSONRPCBridge) getScopeObject().getAttribute(
                cycle, key);
        if (result == null) {
            result = new JSONRPCBridge();
            getScopeObject().setAttribute(cycle, key, result);
        }
        for (Iterator i = getRegisterObjects().entrySet().iterator(); i
                .hasNext();) {
            final Map.Entry entry = (Map.Entry) i.next();
            result.registerObject(entry.getKey(), entry.getValue());
        }
        return result;
    }

    private Scope getScopeObject() {
        return Scope.SESSION;
    }

    public abstract String getServletMappingUrl();

    /**
     * request, session, application
     * 
     * @return
     */
    //public abstract String getScope();
    public abstract Map getRegisterObjects();

    public abstract String getClientName();

}

JSONRPCBridgeオブジェクトに登録するときには、sessionを使うしかないのね。それで問題はないんだけど、applicationで登録できてもいいなーと思ったり。

<?xml version="1.0"?>
<!-- $Id: DateValidator.script,v 1.3 2003/06/01 05:05:12 hlship Exp $ -->
<!DOCTYPE script PUBLIC
	"-//Apache Software Foundation//Tapestry Script Specification 3.0//EN"
	"http://jakarta.apache.org/tapestry/dtd/Script_3_0.dtd">
	
<script>
	
<include-script resource-path="/org/asyrinx/brownie/tapestry/components/jsonrpc/jsonrpc.js"/>

<input-symbol key="clientName" class="java.lang.String" required="yes"/>
<input-symbol key="contextPath" class="java.lang.String" required="yes"/>
<input-symbol key="servletMappingUrl" class="java.lang.String" required="yes"/>

<body>
var ${clientName} = null;
</body>

<initialization>
${clientName} = new JSONRpcClient("${contextPath}${servletMappingUrl}");
</initialization>

</script>

JavaScript側で使うJSONRpcClientオブジェクトを生成するだけです。
jsonrpc.jsもコピーしておく必要があります。


この実装はbrownie-tapestry-1.2.0でリリースする予定です。