rpc.lzx
<library>
<class name="rpc" extends="node">
<attribute name="autoload" value="true
" type="boolean"/>
<attribute name="proxy" value="null
" type="expression"/>
<attribute name="proxyinfo" value="null
" type="expression"/>
<attribute name="secure" value="false
" type="boolean"/>
<attribute name="secureport" value="null
" type="number"/>
<event name="onload"/>
<event name="onunload"/>
<event name="ondata"/>
<event name="onerror"/>
<attribute name="_loadDel" value="null
" type="expression"/>
<attribute name="_unloadDel" value="null
" type="expression"/>
<attribute name="_registered" value="${[]}" type="expression"/>
<attribute name="_verifywithproxy" value="true
" type="boolean"/>
<method name="load">
Debug.error(this.name, "load method not defined");
</method>
<method name="unload">
this._unloadDel.execute( { status: 'ok' } );
</method>
<method name="init">
super.init();
this._loadDel = new LzDelegate(this , "_load");
this._unloadDel = new LzDelegate(this , "_unload");
if (this.autoload) this.load();
</method>
<method name="_load" args="o">
if (o.status == "error") {
if (this.onerror != LzDeclaredEvent) {
this.onerror.sendEvent(o.message + ' ' + o['error']);
} else {
Debug.error(this.name, "load error:", o.message);
}
return false;
}
if (this._setup(o) && this.onload) {
this.onload.sendEvent();
}
</method>
<method name="_unload" args="o">
if (o.status == "error") {
if (this.onerror != LzDeclaredEvent) {
this.onerror.sendEvent(o.message, o['error']);
} else {
Debug.error(this.name, "load error:", o.message);
}
return false;
}
this.proxy = null;
this.proxyinfo = null;
if (this.onunload) this.onunload.sendEvent();
</method>
<method name="_register" args="callcontext,funcname">
if ( this.proxy == null ) {
this._registered.push( { c: callcontext, f: funcname } );
return;
}
//If the remotecall is registered dynamically AFTER the remotecontext is already loaded
//This part becomes active and the STUB is created swagner 12.03.2009
if (!this.proxy[funcname]) {
this.proxy[funcname] = this.makeProxyStubFunction(funcname);
}
if (this._verifywithproxy && typeof(this.proxy[funcname]) != 'function') {
Debug.write(this.name, "warning: function", funcname, "not defined");
//continue;
}
for (var k in this.proxy) {
var tpka = this.proxy[k]['args'];
if (tpka == null) {
tpka = this.proxy[k].args = {};
}
tpka.secure = this.secure;
tpka.secureport = this.secureport;
tpka.scope = this.scope;
tpka.attributename = this.attributename;
tpka.objectreturntype = this.objectreturntype;
}
//c._funcref = this.proxy[funcname];
callcontext._funcref = this.proxy[funcname];
</method>
<method name="_setup" args="o">
this.proxyinfo = o.stub;
if (this.proxy == null) {
this.proxy = {};
}
// register proxy functions for remotecall objects using
// remotecontext reference.
for (var i=0; i < this._registered.length; i++) {
var item = this._registered[i];
var fname = item.f;
this.proxy[fname] = this.makeProxyStubFunction(fname);
item.c['_funcref'] = this.proxy[fname];
}
if (this.subnodes) {
for (var k=0, len=this.subnodes.length; k<len; k++) {
var c = this.subnodes[k];
if ( ! (c instanceof lz.remotecall) ) continue;
// funcname is required
var fn = c['funcname'];
if (fn == null) continue;
if (c['name'] == null) c['name'] = fn;
if (!this.proxy[fn]) {
this.proxy[fn] = this.makeProxyStubFunction(fn);
}
if (this._verifywithproxy && typeof(this.proxy[fn]) != 'function') {
Debug.write(this.name, "warning: function", fn, "not defined");
continue;
}
c._funcref = this.proxy[fn];
}
}
return true;
</method>
<method name="makeProxyStubFunction" args="fn">
return function(...ignore) { Debug.write('base rpc does not automatically define proxy stubs'); };
</method>
<doc>
<tag name="shortdesc"><text>remote procedure call</text></tag>
<text>
<p>The <tag><rpc></tag> tag is the abstract base class for RPC classes. Subclasses
must implement the <method>load()</method> method, which is responsible for creating
the proxy object. The proxy object contains a set of function stubs
that invoke a remote function (or procedure) over the network. It's
up to the caller of the stub function to know what parameters need to
be passed in by looking at what the backend remote function expects.
If calling a JavaRemoting (<sgmltag class="element" role="lz.javarpc"><javarpc></sgmltag>) function, looking at the associated
Java API will be necessary. If calling a SOAP (<sgmltag class="element" role="lz.soap"><soap></sgmltag>) function, the
developer will need to look at the corresponding operation in a WSDL file.</p>
<p>The implementation of this class can be found in lps/components/rpc/rpc.lzx. For details, see the
<a href="../developers/rpc.html">RPC</a> chapter of the Developer's Guide.</p>
<p>Objects derived from rpc use function stubs in the proxy object to invoke a remote function.
Each function stub expects an array of arguments and a delegate to deal with the return value,
respectively. Both the array and the delegate are required. If a function has no arguments,
rpc passes in an empty array.</p>
<example title="Invoking remote function using proxy function stub">
<canvas debug="true" height="280">
<debug x="10" y="40" width="470" height="230" />
<soap name="temperature" autoload="false"
wsdl="http://developerdays.com/cgi-bin/tempconverter.exe/wsdl/ITempConverter">
<method name="init">
super.init();
Debug.debug('soap service loading...');
this.load();
</method>
<handler name="onload">
Debug.debug('temperature service loaded!');
Debug.debug('---');
// don't allow others to call RPC object until proxy is loaded.
canvas.convert.setAttribute('visible', true);
</handler>
</soap>
<button name="convert" text="convert" x="10" y="10" visible="false">
<attribute name="myDel" value="$once{new LzDelegate(this, 'myhandler')}" />
<handler name="onclick">
var f = 100;
canvas.temperature.proxy.FtoC([ f ], this.myDel)
Debug.debug('Invoking FtoC...');
</handler>
<method name="myhandler" args="data">
Debug.debug('got data: %w', data);
</method>
</button>
</canvas>
</example>
<p>Alternatively, you can use remotecall for a more declarative approach to invoking functions.
See the <sgmltag class="element" role="lz.remotecall"><remotecall></sgmltag> reference or the <link linkend="rpc">RPC chapter</link> of the Developer's Guide for more information.</p>
<example title="Invoking remote function using remotecall">
<canvas debug="true" height="280">
<debug x="10" y="40" width="470" height="230" />
<soap name="temperature" autoload="false"
wsdl="http://developerdays.com/cgi-bin/tempconverter.exe/wsdl/ITempConverter">
<method name="init">
super.init();
Debug.debug('soap service loading...');
this.load();
</method>
<handler name="onload">
Debug.debug('temperature service loaded!');
Debug.debug('---');
// don't allow others to call RPC object until proxy is loaded.
canvas.convert.setAttribute('visible', true);
</handler>
<remotecall funcname="FtoC">
<param value="100" />
<handler name="ondata" args="value">
Debug.debug('got data: %w', value);
</handler>
<handler name="onerror" args="errmsg">
Debug.debug('error: %w', errmsg);
</handler>
</remotecall>
</soap>
<button name="convert" text="convert" x="10" y="10" visible="false">
<handler name="onclick">
canvas.temperature.FtoC.invoke();
Debug.debug('Invoking FtoC...');
</handler>
</button>
</canvas>
</example>
<p><b>See Also:</b></p>
<ul>
<li>
<sgmltag class="element" role="lz.javarpc"><javarpc></sgmltag>
</li>
<li>
<sgmltag class="element" role="lz.soap"><soap></sgmltag>
</li>
<li>
<sgmltag class="element" role="lz.xmlrpc"><xmlrpc></sgmltag>
</li>
<li>
<sgmltag class="element" role="lz.remotecall"><remotecall></sgmltag>
</li>
<li>
<link linkend="rpc">Developer's Guide: RPC chapter</link>
</li>
<li>
<link linkend="rpc-javarpc">Developer's Guide: JavaRPC chapter</link>
</li>
<li>
<link linkend="rpc-soap">Developer's Guide: SOAP chapter</link>
</li>
<li>
<link linkend="rpc-xmlrpc">Developer's Guide: XML-RPC chapter</link>
</li>
</ul>
</text>
</doc>
</class>
<class name="remotecall" extends="node">
<attribute name="name" value="$once{null}"/>
<attribute name="funcname" value="$once{null}" type="string"/>
<attribute name="dataobject" value="null
" type="expression"/>
<attribute name="remotecontext" value="null
" type="expression"/>
<attribute name="inforeturnvalue" value="false
" type="boolean"/>
<event name="ondata"/>
<event name="onerror"/>
<attribute name="_funcref" value="null
" type="expression"/>
<method name="invoke" args="params = null, delegate = null">
if (typeof(this._funcref) != 'function') {
Debug.error(this.name, "could not invoke; is remote service loaded?");
return -1;
}
if (params == null) {
params = [];
if (this.subnodes != null) {
for (var i=0; i < this.subnodes.length; i++) {
// if getValue method is declared in param, call that
// instead to get value.
var tsi = this.subnodes[i];
params[i] = tsi.getValue();
}
}
} else if (! (params instanceof Array)) {
Debug.error(this.name, "first argument (params) is not an array");
return -1;
}
if (delegate == null) {
delegate = this._delegate
} else if ( ! ( delegate instanceof LzDelegate ) ) {
Debug.error(this.name, "second argument (delegate) is not a delegate");
return -1;
}
return this._funcref(params, delegate);
</method>
<method name="init">
if (this.funcname == null) {
Debug.error(this.name, "required funcname is not set");
return;
}
super.init();
if ( this.name == null ) {
this.setAttribute('name', this.funcname);
}
if (this.remotecontext != null) {
this.remotecontext._register(this, this.funcname);
}
this._delegate = new LzDelegate(this, '_handler');
this._delegate.dataobject = this.dataobject;
</method>
<method name="_handler" args="o">
// responseheaders are SOAP specific
if (o.opinfo['responseheaders'] != null) {
var r = new LzDataElement("root", { }, o.opinfo.responseheaders);
if ( this.remotecontext != null && this.remotecontext.responseheaders != null) {
this.remotecontext.responseheaders.setAttribute("data", r);
} else if ( parent.responseheaders != null ) {
parent.responseheaders.setAttribute("data", r);
}
}
var retval;
if (o.status == 'error') {
retval =
inforeturnvalue
? { message: o.message, error: o['error'],
info: o.opinfo, seqnum: o.seqnum }
: o.message;
if (this.onerror != LzDeclaredEvent) {
this.onerror.sendEvent(retval);
} else if (parent.onerror != LzDeclaredEvent) {
parent.onerror.sendEvent(retval);
} else if (this.remotecontext != null &&
this.remotecontext['onerror'] &&
this.remotecontext.onerror != LzDeclaredEvent
) {
this.remotecontext.onerror.sendEvent(retval);
} else {
Debug.error(this.name, "remotecall onerror:", o.error);
}
return;
}
retval =
inforeturnvalue
? { data: o.data, info: o.opinfo, seqnum: o.seqnum }
: o.data;
if (this['ondata'] && this.ondata != LzDeclaredEvent) {
this.ondata.sendEvent(retval);
} else if (parent['ondata'] && parent.ondata != LzDeclaredEvent) {
parent.ondata.sendEvent(retval);
} else if (this.remotecontext != null &&
this.remotecontext['ondata']) {
this.remotecontext.ondata.sendEvent(retval);
}
</method>
<doc>
<tag name="shortdesc"><text>A class to use rpc methods declaratively</text></tag><text>
<p>The <remotecall> element allows for a more declarative style approach to using RPC functions. Calling
function stubs will generally result in your code looking very scripty.</p>
<p>Remotecall requires the funcname attribute to be set. Funcname refers to the remote function name. By default,
the name of the remotecall is the same as funcname unless explicitly set to something else. Multiple remotecalls
can refer to the same funcname, but remotecall names must be unique within the same element context.</p>
<p><param> elements declared in the remotecall body are used when invoking the remote function. The declaration order of
<param>s should match what the remote function expects.</p>
<example title="Using RPC methods">
<canvas debug="true" height="280">
<debug x="10" y="40" width="470" height="230" />
<soap name="temperature" autoload="false"
wsdl="http://developerdays.com/cgi-bin/tempconverter.exe/wsdl/ITempConverter">
<method name="init">
super.init();
Debug.debug('soap service loading...');
this.load();
</method>
<handler name="onload">
Debug.debug('temperature service loaded!');
Debug.debug('---');
// don't allow others to call RPC object until proxy is loaded.
canvas.convert.setAttribute('visible', true);
</handler>
<handler name="ondata" args="value">
Debug.debug('soap object parent ondata: %w', value);
</handler>
<remotecall <em>name="ftoc1" funcname="FtoC"</em>>
<param value="100" />
</remotecall>
<remotecall <em>name="ftoc2" funcname="FtoC"</em>>
<param value="200" />
<handler name="ondata" args="value">
Debug.debug('ftoc ondata: %w', value);
</handler>
</remotecall>
</soap>
<view name="convert" x="10" y="10" visible="false"
layout="axis: x; spacing: 5">
<button text="convert 100F to C">
<handler name="onclick">
canvas.temperature.ftoc1.invoke()
Debug.debug('Invoking FtoC 1...');
</handler>
</button>
<button text="convert 200F to C">
<handler name="onclick">
canvas.temperature.ftoc2.invoke()
Debug.debug('Invoking FtoC 2...');
</handler>
</button>
</view>
</canvas>
</example>
<p>Any events not handled by remotecall can be handled by a remotecall's parent. A remotecall element can be
declared anywhere in the view hierarchy. However, if it's not within an RPC object
(like <soap>, <javarpc, <xmlrpc>), the remotecontext attribute must be set. The remotecontext tells the
remotemethod which RPC object to refer to when invoking the remote method.</p>
<example>
<canvas debug="true" height="280">
<debug x="10" y="40" width="470" height="230" />
<soap name="temperature" autoload="false"
wsdl="http://developerdays.com/cgi-bin/tempconverter.exe/wsdl/ITempConverter">
<method name="init">
super.init();
Debug.debug('soap service loading...');
this.load();
</method>
<handler name="onload">
Debug.debug('temperature service loaded!');
Debug.debug('---');
// don't allow others to call RPC object until proxy is loaded.
canvas.convert.setAttribute('visible', true);
</handler>
<handler name="ondata" args="value">
Debug.debug('soap object ondata: %w', value);
</handler>
<handler name="onerror" args="errmsg">
Debug.debug('error: %w', errmsg);
</handler>
</soap>
<view name="convert" x="10" y="10" visible="false"
layout="axis: x; spacing: 5">
<button text="convert 100F to C">
<handler name="onclick">
this.FtoC.invoke()
Debug.debug('Converting 100F to Celsius...');
</handler>
<remotecall funcname="FtoC"
remotecontext="$once{canvas.temperature}">
<param value="100" />
</remotecall>
</button>
<button text="convert 200F to C">
<handler name="onclick">
this.FtoC.invoke()
Debug.debug('Converting 200F to Celsius...');
</handler>
<remotecall funcname="FtoC"
remotecontext="$once{canvas.temperature}">
<param value="200" />
<handler name="ondata" args="value">
Debug.debug('200F in Celsius is %w', value);
</handler>
</remotecall>
</button>
</view>
</canvas>
</example>
<p>The order in which events (ondata, onerror) are handled are as follow:</p>
<ol>
<li>Use event handler defined in remotecall.</li>
<li>Else use event handler defined in parent.</li>
<li>Else if remotecall is defined outside of RPC object (i.e. remotecontext
attribute is set) use event handler defined in RPC object (i.e. remotecontext
value).</li>
<li>Otherwise event is not handled.</li>
</ol>
<p>To databind to a successful return value, you can set the dataobject
attribute to a dataset. For more information on databinding with RPC, see the
<link linkend="rpc">RPC chapter</link></p>
<example>
<canvas debug="true" height="300" width="680">
<debug x="225" width="450" height="280" />
<dataset name="googleDset" />
<soap name="google" wsdl="http://api.google.com/GoogleSearch.wsdl">
<handler name="onload">
Debug.debug('google soap service stub loaded');
</handler>
<handler name="onerror" args="error">
Debug.debug('error: %w', error);
</handler>
<remotecall name="search" funcname="doGoogleSearch"
dataobject="googleDset">
<param value="'2TKUw4ZQFHJ84ByemZK0EXV0Lj+7xGOx'" />
<param value="'sweet'" />
<param value="1" />
<param value="10" />
<param value="true" />
<param value="''" />
<param value="true" />
<param value="''" />
<param value="''" />
<param value="''" />
<handler name="ondata" args="value">
Debug.debug('got result');
Debug.inspect(value);
</handler>
</remotecall>
</soap>
<view layout="spacing: 5" >
<button text="search" onclick="google.search.invoke()" />
<view bgcolor="yellow" layout="axis: y" >
<view>
<datapath xpath="googleDset:/resultElements/item" pooling="true" />
<text datapath="URL/text()" resize="true"/>
</view>
</view>
</view>
</canvas>
</example>
<p><b>See Also:</b></p>
<ul>
<li>
<sgmltag class="element" role="lz.param"><param></sgmltag>
</li>
<li>
<sgmltag class="element" role="lz.rpc"><rpc></sgmltag>
</li>
<li>
<sgmltag class="element" role="lz.javarpc"><javarpc></sgmltag>
</li>
<li>
<sgmltag class="element" role="lz.soap"><soap></sgmltag>
</li>
<li>
<sgmltag class="element" role="lz.xmlrpc"><xmlrpc></sgmltag>
</li>
<li>
<link linkend="rpc">Developer's Guide: RPC chapter</link>
</li>
</ul>
</text>
</doc>
</class>
<class name="param" extends="node">
<attribute name="value" value="null
" type="expression"/>
<method name="getValue">
return this.value;
</method>
<doc>
<tag name="shortdesc"><text>param tag for remotecall</text></tag>
<text>
<p>Tag to declaratively set parameter values for <sgmltag class="element" role="lz.remotecall"><remotecall></sgmltag>. If a
<code>getValue()</code> method is declared in the body of <code>remotecall</code>, use its return as
value for parameter. If both <code>getValue()</code> method and <code>value</code> attribute are set in
param, <code>getValue()</code> wins.</p>
<programlisting title="Usage example">
<remotecall name="func1" funcname="someFunc">
<param value="'some string value'" />
</remotecall>
<remotecall name="func2" funcname="someFunc">
<param>
<method name="getValue">
return 'some string value';
</method>
</param>
</remotecall>
<remotecall name="func2" funcname="someFunc">
<!-- param will use getValue's return value. -->
<param value="'a value'">
<method name="getValue">
return 'some string value';
</method>
</param>
</remotecall>
</programlisting>
<p> <b>See Also:</b></p>
<ul>
<li>
<a href="../reference/remotecall.html"><code class="classname">remotecall</code></a>
</li>
</ul>
</text>
</doc>
</class>
</library>
Cross References
Classes
- <class name="rpc" extends="node">
- <class name="remotecall" extends="node">
- <class name="param" extends="node">