Table of Contents
JavaRPC is a feature that allows server-side Java objects and methods to be accessed from a client application. The <javarpc> element is used to declare a JavaRPC object in LZX. JavaRPC is part of the OpenLaszlo RPC family and shares similar APIs with SOAP and XML-RPC. See the RPC chapter for details.
Proxied only: The features described in this section only work in applications compiled to Proxied. They do not work in applications compiled to other runtimes.
Declare a javarpc element to create a JavaRPC object in LZX. The remoteclassname attribute specifies what class javarpc represents. To use a class, place it in WEB-INF/classes or, if it exists in a jar, in WEB-INF lib. This will ensure that the class is accessible to the OpenLaszlo Server.
<javarpc remoteclassname="..." attributename="..." scope="[session|webapp|none]" loadoption="[loadcreate|loadonly|create]" createargs="..." objectreturntype="[pojo|javabean]" autoload="[true|false]" secure="[true|false]" secureport="...">
remoteclassname: (String) the remote java class to instantiate in the server, or if scope is 'none', the static stub to return to the client associated to the remoteclassname. This is a required attribute.
attributename: (String) the key to use for the server-side object (see scope attribute). Attributename (or name) is required if scope is 'session' or 'webapp'. Defaults to the name attribute of this object.
scope: (String) one of 'session', 'webapp', or 'none'. Session
scope means that the server object will be saved in a session attribute (see
javax.servlet.http.HttpSession). If scope is webapp, the server object is saved
in a web application context (see javax.servlet.ServletContext). For session and
webapp scopes, client-side remote methods will always invoke the samve saved
server objects. The objects are saved in a java.util.Map that is placed in an
attribute called "__lzobj". The map key for the object is the
attributename
of the client-side javarpc object. If scope is none, no
object is saved in the server and only public static methods are defined in the
client-side javarpc object. This is a required attribute.
loadoption: (String) one of 'loadcreate', 'loadonly', or 'create'. Loadcreate tries to load javarpc object if it exists in the server, else it creates it before loading. 'Loadonly' will only load object if it exists, else an error is returned. 'Create' will always create the object in the server. Default is 'loadcreate'.
createargs: (Array) valid only when loadoption='loadcreate' or loadoption='create'. The array consists of parameters to construct server-side object, for example [1, 'mystring', 1.45] would instantiate an object using a constructor that takes an integer, a string, and a double. Default is null.
objectreturntype: (String) one of 'pojo' or 'javabean'. If an object is returned from the server, 'pojo' will return only the public member values of that object and 'javabean' will return member values for members that have corresponding getters. See examples/javarpc/returnpojo.lzx and examples/javarpc/returnjavabean.lzx for usage example.
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 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). If scope is session or webapp, proxy contains all public methods described in the server class (see remoteclassname attribute). If scope is none, proxy contains all public static methods described in the server class. 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 the proxy (i.e., set to null) by calling the unload() method. Calling unload will also remove the server-side object if scope is session or webapp. See the proxy section in the RPC chapter for details.
Note: event handler methods must be declared in the body of <javarpc>. 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 in the RPC chapter for details.
Load() is responsible for setting up the proxy property. When loading a session scoped or webapp scoped object, a tuple of arguments may be passed in to instantiate the server-side java object using the createargs attribute. Createargs is an array of values and must match the server-side parameter signature of the constructor. When the call returns, an onload event is sent and the proxy will contain function stubs that mirror the public methods in the Java class specified.
Example 45.1. Loading an object
<canvas debug="true"> <debug x="100" y="10" height="275" /> <!-- The security describes what classes are accessible or are allowed to be instantiated in the server. See the security section in this chapter for more information. --> <security> <allow> <pattern>^examples\.ConstructExample</pattern> </allow> </security> <!-- See $LPS_HOME/WEB-INF/classes/ConstructExample.java for java source. --> <javarpc name="ce" scope="session" autoload="false" remoteclassname="examples.ConstructExample" createargs="[1, 'a string', 1.45]"> <handler name="onerror" args="err"> Debug.write("----------"); Debug.write("onerror:", err) </handler> <handler name="onload"> Debug.write("----------"); Debug.write("constructed with", this.createargs); Debug.write(this.proxy); </handler> </javarpc> <simplelayout inset="10" spacing="5" /> <button x="10" text="construct"> <handler name="onclick"> canvas.ce.load(); </handler> </button> <button x="10" text="getinfo"> <handler name="onclick"> this.getInfo.invoke(); </handler> <remotecall funcname="getInfo" remotecontext="$once{canvas.ce}"> <handler name="ondata" args="res"> Debug.write("----------"); Debug.write("method:", this.name) Debug.write("return type:", typeof(res)) Debug.write("return value:", res); </handler> </remotecall> </button> </canvas>
The Java source for the previous example can be found in the
$LPS_HOME/WEB-INF/classes/examples
directory. Here's what it looks
like:
package examples; public class ConstructExample { int mInt = 0; String mString = ""; double mDouble = 0.0; public ConstructExample(int i) { mInt = i; } public ConstructExample(int i, String s, double d) { mInt = i; mString = s; mDouble = d; } public String getInfo() { return "int: " + mInt + "\n" + "string: " + mString + "\n" + "double: " + mDouble + "\n"; } }
See RPC chapter for information on remotecall and other details about the load() method.
This method unloads the proxy from the RPC object and sets it to null. Also, the associated java object is removed from the server-side map. When the call returns, an onunload event is sent.
unload() implementation in javarpc.lzx
<canvas debug="true" height="300"> <debug x="100" y="10" height="275" /> <security> <allow> <pattern>^examples\.ConstructExample</pattern> </allow> </security> <!-- See $LPS_HOME/WEB-INF/classes/ConstructExample.java for java source. --> <javarpc name="ce" scope="session" remoteclassname="examples.ConstructExample" createargs="[1, 'a string', 1.45]"> <handler name="onerror" args="err"> Debug.write("onerror:", err) </handler> <handler name="onload"> Debug.write("loaded proxy:", this.proxy); </handler> <handler name="onunload"> Debug.write("unloaded proxy:", this.proxy); </handler> </javarpc> <simplelayout spacing="10" /> <button text="load" onclick="canvas.ce.load()" /> <button text="unload" onclick="canvas.ce.unload()" /> <button text="proxy" onclick="Debug.write('proxy is', canvas.ce.proxy)" /> </canvas>
Java classes used in an application must be declared in a security element. Classes not defined in a security element are not allowed to be accessed or instantiated. The format of the security element looks like:
<security> <allow> <pattern>CLASS1</pattern> <pattern>CLASS2</pattern> ... <pattern>CLASSN</pattern> </allow> </security>
Each <pattern> is a regular expression.
Example 45.2. Allow classes that start with org.openlaszlo
<security> <allow> <pattern>^org\.openlaszlo</pattern> </allow> </security>
A javarpc object who's class is not declared in a security tag will result in a load error.
This section describes how types are mapped from JavaScript function stub parameters to Java method parameter and from Java return type to JavaScript return type.
JavaScript data type | Parameter types expected by java method |
---|---|
Number (int) | int |
Number (double)* | double |
LzRPC.DoubleWrapper | double |
Boolean | boolean |
Array | Vector |
Object | Hashtable |
* Any floating point number with a zero decimal value is considered to be an integer, i.e., 1.0 is really 1. Use LzRPC.DoubleWrapper to ensure a number is considered a double. For example:
// assume myrpc is a javarpc object and myrpc.proxy.myMethod is a function // that expects a single double as a parameter var mydouble = new LzRPC.DoubleWrapper(1.0); myrpc.proxy.myMethod([ mydouble ], new LzDelegate(...));
LzRPC can be found in $LPS_HOME/lps/components/rpc/library/LzRPC.js
.
Example 45.3. Passing different parameter types
<canvas debug="true" height="300" width="800"> <debug x="250" y="10" width="500" height="275" /> <security> <allow> <pattern>^examples\.TypesExample</pattern> </allow> </security> <!-- See WEB-INF/classes/TypesExample.java for java source. --> <javarpc name="types_example_rpc" scope="none" remoteclassname="examples.TypesExample"> <handler name="onload"> // Set buttons visible only after JavaRPC object loads canvas.buttons.setAttribute('visible', true); </handler> <handler name="ondata" args="res"> Debug.write('(types ondata) response is:', res); </handler> <handler name="onerror" args="errmsg"> Debug.write('(types onerror) error:', errmsg); </handler> <!-- Declaratively pass an integer. --> <remotecall funcname="passInteger"> <param value="42" /> </remotecall> <!-- Declaratively pass a double. Note that we name this function pd1 because we have multiple remotecall declarations that call passDouble but with different parameters. --> <remotecall name="pd1" funcname="passDouble"> <param value="42.1" /> </remotecall> <!-- Declaratively pass a double with 0 decimal. The 0 decimal will truncate and the number will become an integer type when it reaches the server. This call will fail. --> <remotecall name="pd2" funcname="passDouble"> <param value="42.0" /> </remotecall> <!-- Declaratively pass a double with 0 decimal. Wrapping the double in DoubleWrapper will ensure the value will remain a double when reaching the server. --> <remotecall name="pd3" funcname="passDouble"> <param> <method name="getValue"> return new LzRPC.DoubleWrapper(42.0); </method> </param> </remotecall> </javarpc> <view name="buttons" visible="false" layout="spacing: 10" > <button text="pass integer" onclick="types_example_rpc.passInteger.invoke()" /> <button text="pass double" onclick="types_example_rpc.pd1.invoke()" /> <button text="pass double (will fail)" onclick="types_example_rpc.pd2.invoke()" /> <button text="pass double w/LzRPC.DoubleWrapper" onclick="types_example_rpc.pd3.invoke()" /> <button text="pass boolean" onclick="this.passBoolean.invoke()"> <!-- This is a way to declare a remotecall closer to where it's being used. The remotecontext must be set. --> <remotecall funcname="passBoolean" remotecontext="$once{ types_example_rpc }"> <param value="true" /> </remotecall> </button> <button text="pass array" onclick="this.passArray.invoke()"> <remotecall name="passArray" funcname="passClientArray" remotecontext="$once{ types_example_rpc }"> <param value="[1, 'a string', 4.5, false]" /> </remotecall> </button> <button text="pass hash" onclick="this.passObject.invoke()"> <remotecall name="passObject" funcname="passClientObject" remotecontext="$once{ types_example_rpc }"> <param value="{ a: 1, b: 3.14159, c: 'a string value', d: true}"> </param> </remotecall> </button> </view> </canvas>
The java source code used by the previous example can be found in
$LPS_HOME/WEB-INF/classes/examples
and looks like:
package examples; import java.util.Vector; import java.util.Hashtable; public class TypesExample { public static String passInteger(int i) { return "got integer parameter: " + i; } public static String passDouble(double d) { return "got double parameter: " + d; } public static String passBoolean(boolean b) { return "got boolean parameter: " + b; } public static String passClientArray(Vector v) { return "got vector parameter: " + v; } public static String passClientObject(Hashtable t) { return "got hashtable parameter: " + t; } }
Server-side Java method return type | Client-side JavaScript function stub return type |
---|---|
int/java.lang.Integer | Number+ |
short/java.lang.Short | Number+ |
long/java.lang.Long | Number+ |
float/java.lang.Float | Number+ |
double/java.lang.Double | Number+ |
byte/java.lang.Byte | Number+ |
boolean/java.lang.Boolean | Boolean |
char/java.lang.Character | String |
java.lang.String | String |
"Array"/java.util.List* | Array |
java.util.Map/java.lang.Object* | Object |
+ From "JavaScript The Definitive Guide" - O'Reilly. In JavaScript all numbers are floating-point numbers. JavaScript uses the standard 8 byte IEEE floating-point numeric format, which means the range is from: +/- 1.7976931348623157x10^308 - very large, and +/- 5x10^-324 - very small. As JavaScript uses floating-point numbers the accuracy is only assured for integers between: -9,007,199,254,740,992 (-2^53) and 9,007,199,254,740,992 (2^53)
* User-defined objects returned only contain public members. Also, the object isn't saved anywhere in the server.
Example 45.4. Java to Javascript return
<canvas debug="true" height="450" width="800"> <debug x="10" y="130" width="740" height="275" /> <security> <allow> <pattern>^examples\.ReturnTypeExample</pattern> </allow> </security> <!-- See WEB-INF/classes/ReturnTypeExample.java for java source. --> <javarpc name="return_type_example_rpc" scope="none" remoteclassname="examples.ReturnTypeExample"> <handler name="onload"> // Set buttons visible only after JavaRPC object loads canvas.buttons.setAttribute('visible', true); </handler> <handler name="ondata" args="res"> Debug.write('(return type ondata) value:', res, ', type:', typeof(res)); </handler> <handler name="onerror" args="errmsg"> Debug.write('(return type onerror) error:', errmsg); </handler> </javarpc> <view x="10" y="10" name="buttons" visible="false" layout="spacing: 5" > <view layout="axis: x; spacing: 2" > <button text="integer" onclick="this.returnInteger.invoke()"> <remotecall funcname="returnInteger" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="integer object " onclick="this.returnIntegerObject.invoke()"> <remotecall funcname="returnIntegerObject" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="short" onclick="this.returnShort.invoke()"> <remotecall funcname="returnShort" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="short object " onclick="this.returnShortObject.invoke()"> <remotecall funcname="returnShortObject" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="long" onclick="this.returnLong.invoke()"> <remotecall funcname="returnLong" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="long object" onclick="this.returnLongObject.invoke()"> <remotecall funcname="returnLongObject" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="float" onclick="this.returnFloat.invoke()"> <remotecall funcname="returnFloat" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="float object" onclick="this.returnFloatObject.invoke()"> <remotecall funcname="returnFloatObject" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="double" onclick="this.returnDouble.invoke()"> <remotecall funcname="returnDouble" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="double object" onclick="this.returnDoubleObject.invoke()"> <remotecall funcname="returnDoubleObject" remotecontext="$once{ return_type_example_rpc }" /> </button> </view> <view layout="axis: x; spacing: 2" > <button text="byte" onclick="this.returnByte.invoke()"> <remotecall funcname="returnByte" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="byte object" onclick="this.returnByteObject.invoke()"> <remotecall funcname="returnByteObject" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="boolean" onclick="this.returnBoolean.invoke()"> <remotecall funcname="returnBoolean" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="boolean object" onclick="this.returnBooleanObject.invoke()"> <remotecall funcname="returnBooleanObject" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="character" onclick="this.returnCharacter.invoke()"> <remotecall funcname="returnCharacter" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="character object" onclick="this.returnCharacterObject.invoke()"> <remotecall funcname="returnCharacterObject" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="string" onclick="this.returnString.invoke()"> <remotecall funcname="returnString" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="coordinate object" onclick="this.rco.invoke()"> <remotecall name="rco" funcname="returnCoordinateObject" remotecontext="$once{ return_type_example_rpc }" /> </button> </view> <view layout="axis: x; spacing: 2" > <button text="integer array" onclick="this.returnIntegerArray.invoke()"> <remotecall funcname="returnIntegerArray" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="string array" onclick="this.returnStringArray.invoke()"> <remotecall funcname="returnStringArray" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="coordinate object array " onclick="this.returnCoordinateObjectArray.invoke()"> <remotecall funcname="returnCoordinateObjectArray" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="integer list" onclick="this.returnIntegerList.invoke()"> <remotecall funcname="returnIntegerList" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="integer map" onclick="this.rim.invoke()"> <remotecall name="rim" funcname="returnIntegerMap" remotecontext="$once{ return_type_example_rpc }" /> </button> </view> <view layout="axis: x; spacing: 2" > <button text="coordinate object list " onclick="this.returnCoordinateObjectList.invoke()"> <remotecall funcname="returnCoordinateObjectList" remotecontext="$once{ return_type_example_rpc }" /> </button> <button text="coordinate object map" onclick="this.rcom.invoke()"> <remotecall name="rcom" funcname="returnCoordinateObjectMap" remotecontext="$once{ return_type_example_rpc }" /> </button> </view> </view> </canvas>
The java source code used by the previous example looks like:
package examples; import java.util.List; import java.util.Vector; import java.util.Map; import java.util.HashMap; public class ReturnTypeExample { public static int returnInteger() { return 1; } public static Integer returnIntegerObject() { return new Integer(2); } public static short returnShort() { return 3; } public static Short returnShortObject() { return new Short((short)4); } public static long returnLong() { return 5; } public static Long returnLongObject() { return new Long(6); } public static float returnFloat() { return 7; } public static Float returnFloatObject() { return new Float(8); } public static double returnDouble() { return 3.14159; } public static Double returnDoubleObject() { return new Double(3.14159); } public static byte returnByte() { return (byte)11; } public static Byte returnByteObject() { return new Byte((byte)12); } public static boolean returnBoolean() { return true; } public static Boolean returnBooleanObject() { return new Boolean(false); } public static char returnCharacter() { return 'a'; } public static Character returnCharacterObject() { return new Character('b'); } public static String returnString() { return "returing a string"; } public static Coordinate returnCoordinateObject() { return new Coordinate(4,2); } public static int[] returnIntegerArray() { int[] intarr = { 1, 2, 3, 4, 5 }; return intarr; } public static String[] returnStringArray() { String[] strarr = { "one", "two", "three", "four", "five" }; return strarr; } public static Coordinate[] returnCoordinateObjectArray() { Coordinate[] coarr = { new Coordinate(1,1), new Coordinate(2,2), new Coordinate(3,3), new Coordinate(4,4), new Coordinate(5,5) }; return coarr; } public static List returnIntegerList() { List list = new Vector(); list.add(new Integer(1)); list.add(new Integer(2)); list.add(new Integer(3)); list.add(new Integer(4)); list.add(new Integer(5)); return list; } public static Map returnIntegerMap() { Map map = new HashMap(); map.put("one", new Integer(1)); map.put("two", new Integer(2)); map.put("three", new Integer(3)); map.put("four", new Integer(4)); map.put("five", new Integer(5)); return map; } public static List returnCoordinateObjectList() { List list = new Vector(); list.add(new Coordinate(1,1)); list.add(new Coordinate(2,2)); list.add(new Coordinate(3,3)); list.add(new Coordinate(4,4)); list.add(new Coordinate(5,5)); return list; } public static Map returnCoordinateObjectMap() { Map map = new HashMap(); map.put("one", new Coordinate(1,1)); map.put("two", new Coordinate(2,3)); map.put("three", new Coordinate(5,8)); map.put("four", new Coordinate(13,21)); map.put("five", new Coordinate(34,55)); return map; } static public class Coordinate { public int x; public int y; public Coordinate(int x, int y) { this.x = x; this.y = y; } public String toString() { return "x: " + this.x + ", y: " + this.y; } } }
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.