Table of Contents
Warning | |
---|---|
Remote procedure calls work in proxied applications only. They do not work in SOLO applications. |
OpenLaszlo RPC, or just RPC, is the general term used to
define the implementation and APIs that invoke remote procedures calls or
services over the network. Current RPC implementations are <javarpc>
, <xmlrpc>
, and
<soap>
.
A related class, XMLHTTPRequest
(also called "AJAX API"), is described in Chapter 36, Data, XML, and XPath.
<javarpc>
allows server-side Java methods to be invoked from the
application. Java objects can be stored in the OpenLaszlo Server using an HTTP session (HttpSession)
or the web application object (SevletContext).
See the Java Servlet
APIs for more information.
<xmlrpc>
implements XML-RPC, a simple spec that
allows applications to make remote procedure calls on different systems. Its
transport is HTTP and it uses an XML encoded message to invoke remote functions.
<soap>
implements the W3C SOAP
specification. Like <xmlrpc>
SOAO also uses XML to send messages over the network. Though HTTP
is commonly used as its transport, it isn't required. The specification is more
complex than XML-RPC and supports different styles of operations (rpc or
document), overloaded methods, passing header information, and so forth.
The <rpc>
tag is the abstract base class for allRPC classes.
Instances of RPC classes are referred to as RPC objects. RPC classes
include <soap>, <javarpc>, <xmlrpc>, <sessionrpc>, and
<webapprpc>. These classes are essentially LZX wrappers of ORL LzRPC
services. LzRPC
is the ORL abstract class for ORL RPC services. This service
provides the basic framework to invoke remote functions over the network.
LzJavaRPCService, LzXMLRPCService, and LzSOAPService are subclasses of LzRPC,
and implement JavaRPC, XML-RPC, and SOAP, respectively.
The <rpc> tag is the abstract base class for RPC classes. Subclasses
must implement the load()
method, which is responsible for creating the proxy
object. The proxy object contains a set of stub functions 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 you're calling a JavaRPC function, for example, you will need to look at the associated Java API. If you're calling a SOAP function, you will need to look at the corresponding operation in a WSDL file.
<rpc autoload="[true|false]" secure="[true|false]" secureport="...">
The implementation of this class can be seen in
lps/components/rpc/rpc.lzx
.
autoload: (Boolean) if true, calls to load client proxy during init stage. If false, the proxy must be loaded using the load() method. Default is true.
secure: (Boolean) if true, creates a secure HTTPS connection between the client and the OpenLaszlo Server. Also see secureport below. Default is false.
secureport: (Number) valid only when secure attribute is set to true. The secure port to use. There is no client-side default. Most servers use port 443 as the default HTTPS port.
proxy: (Object) this is the object containing function stubs. It
is created by calling load()
(which happens during init if autoload is
true).
proxy
is equivalent to:
a class in JavaRPC
a service in SOAP
proxy
function stubs are equivalent to:
class methods in JavaRPC
operations in SOAP
methods in XML-RPC.
Note that proxy is not defined until the onload
event is
sent, thus, function stubs cannot be invoked until onload. Each function stub
requires two arguments: an array of parameters and delegate. You can unload it
(i.e., set to null) by calling the unload()
method. Go to the proxy section for
details.
Note: event handler methods must be declared in the body of <rpc>. Attribute event handlers will not work.
onload: this event is triggered when the proxy is returned to the client.
onunload: this event is triggered when the proxy is unloaded from the client.
ondata: this event is triggered when a declared <remotecall> doesn't handle its ondata events. See the <remotecall> section for details.
onerror: this event is triggered if there was a problem loading or unloading the stub, or if a declared <remotecall> didn't handle its onerror event. See the <remotecall> section for details.
The load()
method is abstract in this class. Each subclass must define this
method. load()
is responsible for setting up the proxy
load() implementation in javarpc.lzx
<method name="load"> /* other code here */ var opts = { loadoption: this.loadoption, params: this.createargs, classname: this.classname, oname: this.attributename, scope: this.scope } LzJavaRPCService.loadObject(this._loadDel, opts, this.secure, this.secureport); </method>
load() implementation in soap.lzx
<method name="load"> LzSOAPService.loadObject(this._loadDel, { wsdl: this.wsdl, service: this.service, port: this.port }, this.secure, this.secureport); </method>
load() implementation in xmlrpc.lzx
<method name="load"> // Since there aren't any prototypes to load for XML-RPC services, we just // create proxy using declared calls. for (var cn in this.subnodes) { /* code to set up this.proxy */ } this._loadDel.execute( { status: 'ok', message: 'ok', stub: this.proxy, stubinfo: null } ); </method>
There are several private, undocumented properties in <rpc>. But one
that implementors of subclasses should be made aware of is the delegate property
called _loadDel (note: an underscore prefix in a component variable indicates that it is
private). This delegate must be passed in to any lower-level ORL APIs or must be
called at the end of load()
, as is done in the xmlrpc.lzx implementation of
load()
(shown above). In turn, _loadDel
calls the _load()
method (a private
method in <rpc>) that sets up the proxy, registers declared
<remotecall> nodes, and finally sends the onload event.
This method unloads the proxy from the RPC object. Just like load(), this method has a corresponding _unloadDel delegate and _unload handler method. By default, unload() sets the proxy property to null and then an unload event is sent. However, you can override this function in the cases where server-side clean up is required, as is done with JavaRPC objects.
unload() implementation in javarpc.lzx
<method name="unload"> /* some other code here */ // clean up server-side code LzJavaRPCService.unloadObject(this._unloadDel, { classname: this.classname, oname: this.attributename, scope: this.scope }, this.secure, this.secureport); </method>
See lps/components/rpc/javarpc.lzx
for more details.
Since rpc is an abstract class, SOAP, JavaRPC, and XML-RPC declarations will be used for demonstration.
<!-- SOAP --> <soap name="mySOAP" wsdl="..."> <handler name="onload"> Debug.write('soap proxy loaded:', this.proxy); </handler> <handler name="onunload"> Debug.write('soap proxy unloaded:', this.proxy); </handler> </soap> <!-- JavaRPC --> <javarpc name="myJavaRPC" classname="..." scope="..."> <handler name="onload"> Debug.write('javarpc proxy loaded:', this.proxy); </handler> <handler name="onunload"> Debug.write('javarpc proxy unloaded:', this.proxy); </handler> </javarpc> <!-- XML-RPC --> <xmlrpc name="myXMLRPC" service="..."> <handler name="onload"> Debug.write('xmlrpc proxy loaded:', this.proxy); </handler> <handler name="onunload"> Debug.write('xmlrpc proxy unloaded:', this.proxy); </handler> </xmlrpc>
The proxy property is an object that contains function stubs. It's set when the load call returns. Each function represents a client-side stub to a remote function or operation. Each function requires two arguments: an array of parameters and a delegate. The order of parameters in the array should match the parameters the backend operation expects. The delegate is required because RPC calls are asynchronous, that is, there is no way to know when the function call will return from the backend. When the response is received, the delegate calls the appropriate method.
<!-- Assume mySOAP.proxy contains a function stub called someFunction() --> <soap name="mySOAP" wsdl="..."> <handler name="onload"> Debug.write('soap proxy loaded:'); Debug.inspect(this.proxy); </handler> <handler name="onunload"> Debug.write('soap proxy unloaded:', this.proxy); </handler> </soap> <button text="clickme"> <attribute name="mydel" type="expression" value="$once{new LzDelegate(this, 'handler')}" <handler name="onclick"> mySOAP.proxy.someFunction([p1, p2, ..., pN], this.myDel); </handler> <method name="handler" args="retObj"> Debug.write('RPC call returned', retObj); </method> </button>
When a remote call returns, an object is returned into the callback handler that is specified in the delegate passed in the function stub call. The object contains information relevant to the call and will be referred to as the return object. Successful return objects contain return values, which is the actual value returned from the RPC call. The return value can be a simple type, array, or object. A successful return object looks like:
{ status: 'ok', message: 'ok', data: DATA, opinfo: OPINFO }
Data is the return value from the RPC call. Opinfo is information specific to the operation. For SOAP, this will contain the operation name, operation style, and SOAP response headers. For JavaRPC, will contain the remote class name, method name, attribute name of where this object was saved, and other values specific to this call. For XML-RPC, opinfo will contain service URL and method name.
A successful call can also return a void return type:
{ status: 'ok', message: 'void', data: LzRPC.t_void, opinfo: OPINFO }
LzRPC.t_void is an object that represent a void return type.
A bad call returns an error object:
{ status: 'error', errortype: ERROR_TYPE, message: ERROR_MESSAGE, error: ERROR_OBJECT, opinfo: OPINFO }
Message is a one sentence description of the error. The error property exists only for exception and fault error types. There are four types of errors:
fault
exception
timeout
servererror
A fault error type indicates that there was a problem handling the remote function call. An exception error type is only thrown by SOAP calls and indicates the OpenLaszlo Server had a problem handling the remote function call. Both fault and exception return an error object that can be inspected by looking at the error property. A servererror is returned for general OpenLaszlo Server errors like forbidden requests. Timeout is returned when the application hasn't heard from back from the OpenLaszlo Server in a certain amount of time. This currently defaults to 30 seconds and can't be changed.
Example 44.1. Return object
<canvas debug="true" width="600"> <debug x="150" y="20" width="400" height="360" /> <soap name="temperature" wsdl="http://developerdays.com/cgi-bin/tempconverter.exe/wsdl/ITempConverter"> <attribute name="myDel" value="$once{new LzDelegate(this, 'myhandler')}" /> <method name="init"> Debug.write('soap service loading...'); super.init(); </method> <handler name="onload"> Debug.write('temperature service loaded!'); Debug.inspect(this.proxy); </handler> <method name="myhandler" args="returnObject"> Debug.write('got returned object:'); Debug.inspect(returnObject); </method> </soap> <view layout="spacing: 5" x="20" y="20"> <button text="ok conversion"> <handler name="onclick"> Debug.write('requesting good conversion...'); temperature.proxy.FtoC([ 100 ], temperature.myDel); </handler> </button> <button text="bad conversion"> <handler name="onclick"> Debug.write('requesting bad conversion...'); temperature.proxy.FtoC([ 'string' ], temperature.myDel); </handler> </button> </view> </canvas>
Warning | |
---|---|
Remote procedure calls return native objects, not XML, and cannot be used with XPath. |
Return values can be mapped to datasets or LzDataElements. This creates a
convenient way to bind return values from remote calls zto elements in the OpenLaszlo canvas. Data mapped
return values can be generated using the LzDataElement.valueToElement()
method.
Example 44.2. LzDataElement.valueToElement()
<canvas debug="true" width="600"> <debug x="150" y="15" width="400" height="360" /> <simplelayout spacing="5" /> <method name="v2e" args="v"> Debug.write('Got', v); var de = LzDataElement.valueToElement(v); Debug.write(de.serialize()); </method> <button text="number"> <handler name="onclick"> var num = 5; canvas.v2e(num); </handler> </button> <button text="string"> <handler name="onclick"> var str = "a string"; canvas.v2e(str); </handler> </button> <button text="array"> <handler name="onclick"> var arr = [1, 2, 3]; canvas.v2e(arr); </handler> </button> <button text="object"> <handler name="onclick"> var obj = { p1: "a string", p2: 5 } canvas.v2e(obj); </handler> </button> <button text="complex array"> <handler name="onclick"> var arr = [ 1, { p1: "a string", p2: 5 }, [ 1, 2, 3] ]; canvas.v2e(arr); </handler> </button> <button text="complex object"> <handler name="onclick"> var obj = { p1: [1, 2, 3], p2: 5 }; canvas.v2e(obj); </handler> </button> </canvas>
The root node of LzDataElement.valueToElement()
is named
<element>. Simple type values are placed as text nodes of the root node.
Array items are placed as <item> nodes under the root. Object properties
are placed under the root with the name of the property representing the
element that wraps its value.
In the example above, the number
5
maps to
<element>5</element>
The string
"a string"
maps to
<element>a string</element>
the array
[1, 2, 3]
maps to
<element> <item>1</item> <item>2</item> <item>3</item> </element>
the object
{ p1: "a string", p2: 5 }
maps to
<element> <p1>a string</p1> <p2>5</p2> </element>
Note that returned arrays from SOAP calls are treated a little
differently. Each item in a SOAP array has a wrapper element, which is remapped
back when turned into an LzDataElement
. That is, instead of wrapping array items
in <item>, they're wrapped with the original element name specified in the
SOAP message.
For example, assume the original SOAP message for an array return value looks like:
<soap:Envelope soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" ...> <soap:Body> <n:getTypesResponse xmlns:n="http://arcweb.esri.com/v2"> <Result href="#id0" /> </n:getTypesResponse> <id0 id="id0" soapenc:root="0" xsi:type="soapenc:Array" soapenc:arrayType="ns5:KeyValue[19]"> <i href="#id1" /> <i href="#id2" /> <i href="#id3" /> <i href="#id4" /> <!-- ... --> </id0> <id1 id="id1" soapenc:root="0" xsi:type="ns5:KeyValue"> <key xsi:type="xsd:string">A</key> <value xsi:type="xsd:string">Countries</value> </id1> <id2 id="id2" soapenc:root="0" xsi:type="ns5:KeyValue"> <key xsi:type="xsd:string">B</key> <value xsi:type="xsd:string">Large Non-U.S. Cities</value> </id2> <id3 id="id3" soapenc:root="0" xsi:type="ns5:KeyValue"> <key xsi:type="xsd:string">C</key> <value xsi:type="xsd:string">Large U.S. Cities</value> </id3> <!-- ... --> </soap:Body> </soap:Envelope>
This example shows that <item> is not used as the wrapper element for array items.
Example 44.3. SOAP array
<canvas debug="true" height="300"> <debug width="470" height="285" /> <soap name="geography" wsdl="http://arcweb.esri.com/services/v2/PlaceFinderSample.wsdl"> <attribute name="myDel" value="$once{new LzDelegate(this, 'handler')}" /> <method name="handler" args="retval"> if (retval.status == 'error') { Debug.write('error:', retval.message); return; } Debug.write('Got', retval.data); var el = LzDataElement.valueToElement(retval.data); Debug.write(el.serialize()); </method> <handler name="onload"> Debug.write('geography soap service stub loaded'); this.proxy.getTypes([], this.myDel); </handler> </soap> </canvas>
The data-mapped return value can then be used with another LzDataElement
for
databinding purposes.
Example 44.4. Setting generated return value LzDataElement in dataset
<canvas debug="true" height="150"> <debug x="80" y="10" width="400" height="120" /> <dataset name="myDataset" /> <simplelayout spacing="5" /> <method name="v2e" args="v"> Debug.write('setting', v, 'to mydataset'); var de = LzDataElement.valueToElement(v); myDataset.setChildNodes( de.childNodes ) Debug.write(myDataset.serialize()); </method> <view datapath="myDataset:/" x="10" layout="inset: 10; spacing: 2"> <button text="array"> <handler name="onclick"> var arr = [1, 2, 3]; canvas.v2e(arr); </handler> </button> <text datapath="item/text()" /> </view> </canvas>
There is more convenient way to generate data mapped return values. Instead
of calling LzDataElement.valueToElement()()
after each call, a dataobject
property
can be set in the delegate. Dataobject must be a dataset or an
LzDataElement and tells the RPC function to create a data mapped return value
and set it on the dataobject. If the dataobject is a dataset, the child nodes of
the data mapped return value are set as the child nodes of the dataset. If the
dataobject is an LzDataElement, the root node of data mapped return value is
appended as a child of the LzDataElement dataobject.
Example 44.5. Setting dataobject to a dataset
<canvas debug="true" width="700"> <debug x="220" width="470" height="285" /> <dataset name="myDataset" /> <soap name="geography" wsdl="http://arcweb.esri.com/services/v2/PlaceFinderSample.wsdl"> <attribute name="myDel" value="$once{new LzDelegate(this, 'handler')}" /> <method name="handler" args="ret"> Debug.write('ret:', ret); if (ret.status == 'ok') { Debug.write('myDataset childNodes:', myDataset.childNodes); } </method> <handler name="onload"> Debug.write('geography soap service stub loaded'); // Here we set data this.myDel.dataobject = myDataset; this.proxy.getTypes([], this.myDel); </handler> </soap> <view datapath="myDataset:/" x="10" layout="inset: 10; spacing: 2"> <view datapath="i"> <text datapath="key/text()" resize="true"/> <text x="20" datapath="value/text()" resize="true"/> </view> </view> </canvas>
Note how the passed in LzDataElement child nodes are the child nodes of the dataset. Here's another example using an LzDataElement.
Example 44.6. Setting dataobject to an LzDataElement
<canvas debug="true" height="500" width="700"> <debug x="220" width="470" height="285" /> <dataset name="myDataset"> <region name="Region-A" /> <region name="Region-B"/> <region name="Region-C"/> </dataset> <soap name="placefinder" wsdl="http://arcweb.esri.com/services/v2/PlaceFinderSample.wsdl"> <attribute name="myDel" value="$once{new LzDelegate(this, 'handler')}" /> <method name="handler" args="ret"> Debug.write('ret:', ret); if (ret.status == 'ok') { Debug.write('myDataset childNodes:', myDataset.childNodes); } </method> <handler name="onload"> Debug.write('placefinder soap service stub loaded'); myView.setAttribute('visible', true); this.proxy.getTypes([], this.myDel); </handler> </soap> <view name="myView" datapath="myDataset:/" x="10" visible="false"> <simplelayout spacing="2" inset="10" /> <view datapath="region" layout="spacing: 5"> <attribute name="text" value="$path{'@name'}" type="string" /> <text name="t" text="${parent.text + ' (click me)'}" resize="true" /> <method name="gotData"> this.setAttribute('clickable', false); this.t.setText(this.text); </method> <handler name="onclick"> if (text == "Region-A") { this.regions.setDatapath("element/i[1-6]"); } else if (text == "Region-B") { this.regions.setDatapath("element/i[7-12]"); } else { this.regions.setDatapath("element/i[12-]"); } var myDel = new LzDelegate(this, "gotData"); myDel.dataobject = this.datapath.p; placefinder.proxy.getTypes([], myDel); </handler> <view name="regions" x="10"> <text datapath="key/text()" resize="true"/> <text x="20" datapath="value/text()" resize="true"/> </view> </view> </view> </canvas>
Unlike a dataset, the generated LzDataElement from the return value is appended as a child of the LzDataElement dataobject.
In the previous section we discussed how to make RPC calls using function
stubs defined in the proxy. Though not discouraged, calling function stubs
directly will generally result in your code looking very "scripty". The
<remotecall>
tag allows for a more declarative style approach to using RPC
functions.
<remotecall funcname="..." name="..." dataobject="..." remotecontext="...">
The <remotecall>
tag has the following attributes:
funcname: (String) the name of the function stub remotecall represents. This is required.
name: (String) the name of the remotecall. Multiple remotecalls can refer to the same function stub, but names must be unique. Default is value of funcname.
dataobject: (Dataset|LzDataElement) if set, the return value will also be represented as a dataset or as a child of the LzDataElement. Default is null.
remotecontext: (Object) this attribute is used when the remotecall isn't declared inside of an RPC object. This attribute should be set to the RPC context the remotecall should run from. Default is null.
ondata: this event is triggered when the remotecall successfully returns data. If ondata is not handled in the remotecall, ondata cascades up to its parent. If the parent doesn't handle it, it then cascades up to the remotecontext, if defined. If none of these objects handle the ondata event, the returned data is ignored. The ondata event sends two arguments to its event handler. The first is the actual return value, the second is information on the operation that returned the value.
onerror: this event is triggered when the remotecall returns an error. If an onerror is not handled in the remotecall, onerror cascades up to its parent. If the parent doesn't handle it, it then cascades up to the remotecontext, if defined. If none of these objects handle the onerror event, the error is displayed in the debugger. The onerror event sends three arguments to its event handler: the error message, an error object (which can be null or undefined depending on the error type), and information on the operation that originated the failed return.
params: (Array) an array of parameters to pass to the remotecall. If null, the remotecall will use <param> tag values declared inside of it. Default is null.
delegate: (LzDelegate) a delegate to handle the callback. If null, the remotecall will use the default delegate which calls the default handler, whose job is to receive data and raise ondata/onerror events. Default is null.
Call this method to invoke remotecall.
Use the invoke() method to use remotecall. If the parameter array (the first
argument) is null, declared <param> are used as arguments. A <param>
can set either a value attribute or define a getValue() method in its body. The
getValue() method is expected to return a value. If both value and getValue()
are defined, getValue() always wins. Note that the value attribute is an
expression type so strings must be quoted like value="'my
string'"
. To set null, use the value="$once{null}"
syntax.
Function stubs can be referenced by two different remotecalls with different parameters. The funcname attribute doesn't have to be unique, but if referenced more than once, remotecalls must explicitly define their name attributes.
Example 44.7. Remotecall
<canvas debug="true" height="300" width="600"> <debug x="145" width="450" height="280" /> <soap name="google" wsdl="http://api.google.com/GoogleSearch.wsdl"> <handler name="onload"> Debug.write('google soap service stub loaded'); </handler> <handler name="onerror" args="error"> Debug.write('error:', error); </handler> <remotecall name="search" funcname="doGoogleSearch"> <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.write('result is:') Debug.inspect(value); </handler> </remotecall> <remotecall name="togglesearch" funcname="doGoogleSearch"> <param value="'2TKUw4ZQFHJ84ByemZK0EXV0Lj+7xGOx'" /> <param> <attribute name="toggle" value="0" type="number" /> <method name="getValue"> var searchTerm; if (toggle % 2 == 0) { searchTerm = 'democrat'; toggle = 1; } else { searchTerm = 'republican'; toggle = 0; } Debug.write('search term is', searchTerm); return searchTerm; </method> </param> <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.write('result is:') Debug.inspect(value); </handler> </remotecall> </soap> <simplelayout spacing="10" /> <button x="10" y="10" text="search"> <handler name="onclick"> Debug.write('invoking search...'); canvas.google.search.invoke(); </handler> </button> <button x="10" y="10" text="toggle search"> <handler name="onclick"> Debug.write('invoking togglesearch...'); canvas.google.togglesearch.invoke(); </handler> </button> </canvas>
Just like passing in a delegate with a dataobject property as discussed previously with function stubs, a dataobject can be used in a remotecall by setting the dataobject attribute.
Example 44.8. Setting dataobject in remotecall
<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.write('google soap service stub loaded'); </handler> <handler name="onerror" args="error"> Debug.write('error:', 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.write('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>
In previous examples, a button was used to invoke a remotecall that was declared in an RPC object. The code for a remotecall might have read better if it was placed in the location where it's actually being used. Remotecall can actually be declared anywhere in the node hierarchy, but when doing so, not only must it know the function stub that it will trigger (i.e., the funcname), it also must know which RPC context it will run from. To do so, the remotecontext attribute must be set when declaring a remotecall outside of an RPC object.
Example 44.9. Remotecontext
<canvas debug="true" height="300" width="680"> <debug x="100" width="450" height="280" /> <soap name="google" wsdl="http://api.google.com/GoogleSearch.wsdl"> <handler name="onload"> Debug.write('google soap service stub loaded'); </handler> </soap> <button text="search" onclick="this.doGoogleSearch.invoke()"> <!-- if name isn't set, name defaults to value of funcname. --> <remotecall funcname="doGoogleSearch" remotecontext="$once{canvas.google}"> <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.write('got result'); Debug.inspect(value); </handler> <handler name="onerror" args="error"> Debug.write('error:', error); </handler> </remotecall> </button> </canvas>
Copyright © 2002-2007 Laszlo Systems, Inc. All Rights Reserved. Unauthorized use, duplication or distribution is strictly prohibited. This is the proprietary information of Laszlo Systems, Inc. Use is subject to license terms.