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 same 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 46.1. Loading an object
<canvas
debug
="true
" width
="100%
">
<!-- 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.debug("----------");
Debug.debug("onerror: %w", err)
</handler
>
<handler
name
="onload
">
Debug.debug("----------");
Debug.debug("constructed with %w", this.createargs);
Debug.debug("%w", 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.debug("----------");
Debug.debug("method: %w", this.name)
Debug.debug("return type: %w", typeof(res))
Debug.debug("return value: %w", res);
</handler
>
</remotecall
>
</button
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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
Example 46.2. Unloading proxy from the RPC object
<canvas
debug
="true
" height
="300
" width
="100%
">
<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.debug("onerror: %w", err)
</handler
>
<handler
name
="onload
">
Debug.debug("loaded proxy: %w", this.proxy);
</handler
>
<handler
name
="onunload
">
Debug.debug("unloaded proxy: %w", 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.debug('proxy is %w', canvas.ce.proxy)
"/>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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 46.3. 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 |
lz.rpc.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 lz.rpc.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 lz.rpc.DoubleWrapper(1.0); myrpc.proxy.myMethod([ mydouble ], new LzDelegate(...));
lz.rpc can be found in $LPS_HOME/lps/components/rpc/library/rpc.js
.
Example 46.4. Passing different parameter types
<canvas
debug
="true
" height
="300
" width
="100%
">
<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.debug('(types ondata) response is: %w', res);
</handler
>
<handler
name
="onerror
" args
="errmsg
">
Debug.debug('(types onerror) error: %w', 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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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 46.5. Java to JavaScript
<canvas
debug
="true
" height
="450
" width
="100%
">
<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.debug('(return type ondata) value: %w, type: %w', res, typeof(res));
</handler
>
<handler
name
="onerror
" args
="errmsg
">
Debug.debug('(return type onerror) error: %w', 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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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-2010 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.