JSON logoJSON-RPC-Java

JavaScript to Java AJAX communications library.

JSON-RPC-Java Manual

This manual covers the architecture and implementation details of JSON-RPC-Java and contains a reference guide to the various components and interfaces. Please start with the tutorial if want to get started quickly.

Table of Contents

Architecture

JSON-RPC-Java consists of two main user visible components, the JSONRPCBridge and the JSONRPCServlet.

JSONRPCBridge

The JSONRPCBridge holds references to classes and objects that are exported to allow remote invocation by a JSON-RPC client. It is passed JSON-RPC requests from the transport (JSONRPCServlet) and it then performs the unmarshalling of JSON objects to Java objects, the method invocation and finally marshalls the method's Java object result back to JSON. Serializer objects perform the actual type conversion between Java and JavaScript objects.

The JSONRPCServlet (which implements the JSON-RPC HTTP transport) will use either a global singleton JSONRPCBridge instance or optionally one in the user's HttpSession (if it exists). You can place a JSONRPCBridge into a user's HttpSession to allow the JSONRPCServlet to make calls on objects that are exported only to that specific user session (these objects may for instance contain stateful data related to the specific user). A session specific bridge will delegate requests for objects it does not know about to the global singleton JSONRPCBridge instance.

Session specific bridges are useful for a number of reasons:

  • to improve the security of the application
  • export object methods to specific users
  • hold state specific to a user in an exported object
  • hold references to objects returned to a specific client

The JSONRPCServlet looks for the session specific bridge object under the attribute "JSONRPCBridge" in the HttpSession associated with the request (without creating a session if one does not already exist). If it can't find a session specific bridge instance, it will default to invoking against the global bridge.

An example or creating a session specific bridge in JSP is as follows:

<jsp:useBean id="JSONRPCBridge" scope="session"
           class="com.metaparadigm.jsonrpc.JSONRPCBridge"/>

An example in Java (i.e. in a Servlet):

HttpSession session = request.getSession();
JSONRPCBridge bridge = (JSONRPCBridge) session.getAttribute("JSONRPCBridge");
if(bridge == null) {
    bridge = new JSONRPCBridge();
    session.setAttribute("JSONRPCBridge", bridge);
}

To export all instance methods of an object to a client:

bridge.registerObject("myObject", myObject);

To export all static methods of a class to a client:

bridge.registerClass("MyClass", com.example.MyClass.class);

If registerObject and registerClass are called multiple times with the same key, then the object is replaced with the new one.

Global bridge

The global bridge singleton object allows exporting objects to all HTTP clients. This can be used for registering factory classes although care must be taken with authentication and security issues as these objects will be accessible to all clients. It can be fetched with JSONRPCBridge.getGlobalBridge().

To export all instance methods of an object to all clients:

JSONRPCBridge.getGlobalBridge().registerObject("myObject", myObject);

To export all static methods of a class to all clients:

JSONRPCBridge.getGlobalBridge().registerClass("MyClass", com.example.MyClass.class);

See the JSONRPCBridge Javadocs for more info.

JSONRPCServlet

This servlet, the transport part of JSON-RPC-Java, handles JSON-RPC requests over HTTP and dispatches them to a JSONRPCBridge instance registered in the HttpSession if one exists (without creating a session if there isn't one already), or otherwise the global bridge.

The following would be used in your web.xml to export the servlet under the URI "/JSON-RPC" (this is the standard location):

<servlet>
  <servlet-name>com.metaparadigm.jsonrpc.JSONRPCServlet</servlet-name>
  <servlet-class>com.metaparadigm.jsonrpc.JSONRPCServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>com.metaparadigm.jsonrpc.JSONRPCServlet</servlet-name>
  <url-pattern>/JSON-RPC</url-pattern>
pre>

Please note: due to relative mapping of URIs in your web container. You may need to set the URL in your client to: "/<web-app-name>/JSON-RPC".

See the JSONRPCServlet Javadocs for more info.

Type mapping

To allow JSON-RPC-Java to transparently unmarshall complex nested objects and with that, the usage of Java's container classes, JSON-RPC needs a mechanism to preserve type information.

This comes from the combination of the JavaScript's typeless nature and the method that Java container classes gain their genericity (through the usage of a single base class 'Object', rather than using parameterized types such as C++ templates. Note: Java 5.0 however supports these parameterized collection types but support is not yet included in JSON-RPC-Java for this).<

In the case of unmarshalling a JavaScript object into a Java method argument with a generic container interface (such as List, Map, Set, or their concrete counterparts), the need for additional type information is apparant. We have no type information on either side to work out the mapping from the contained type of JavaScript array to the contained type of the Java container class.

With normal array method arguments ie. a Java method argument of class Foo[], we know the items in the JavaScript array must (or should) be all be class Foo.

With a Java method argument of class ArrayList, we only have Object as the contained type and no type information with a regular JavaScript object.

This leads to the method that JSON-RPC-Java maintains it's transparent mapping - 'class hinting'.

Class Hinting

In the case of regular Java Beans an extra field javaClass is added that maps the typeless JavaScript object back to the Java class. The is used on the Java side during unmarshalling to ease the transparent mapping of the object back to it's Java Class when it is sent back to the server (although JSON-RPC-Java can in some cases map the objects without the additional type information if the mapping is unambiguos ie. if the object is not inside of a generic container class, then the method class signature can be used).

For Java container classes such as the List, we can't map them to a JavaScript native array as we would have nowhere to store the type hint. So Java container classes have a special type mapping from Java to JavaScript described here:

Bean

Java beans (objects conforming to get getProperty, setProperty, etc. syntax) map directy to a JavaScript Object with the additional of the string member javaClass containing the Java class name. The property names have the get or set prefix removed and the first letter lowercased. eg.

{
  "javaClass": "com.example.MyBean",
  "someStringProperty": "foo",
  "someBooleanProperty": true,
  "someIntegerProperty": 10
}

List

List (ArrayList, LinkedList, Vector) maps to a JavaScript object with a string member javaClass containing the Java class name and a nativeJavaScript array member list containing the data. eg.

{
  "javaClass": "java.util.ArrayList",
  "list": [0, 1, 2, 3, 4]
}

Map

Map (HashMap, TreeMap, LinkedHaspMap) maps to a JavaScript object with a string member javaClass containing the Java class name and a nativeJavaScript object containing the key value pairs. eg.

{
  "javaClass": "java.util.HashMap",
  "map": {"foo key": "foo value"}
}

The Java string representation of the key object is used as the native JavaScript object type only supports strings as keys.

Set

Set (HashSet, TreeSet, LinkedHashSet) maps to a JavaScript object with a string member javaClass containing the Java class name and a native JavaScript object containing the set item string values as keys and set objects as values. eg.

{
  "javaClass": "java.util.HashSet",
  "set": {"foo key": "foo key"}
}

The Java string representation of the key object is used as the native JavaScript object type only supports strings as keys.

JavaScript Client

The JavaScript client JSONRpcClient constructs a transparent proxy providing method access to all methods on the JSON-RPC server.

It is constructued like this:

var jsonrpc = new JSONRpcClient("/webapp/JSON-RPC/")

HTTP authentication can also be used

var jsonrpc = new JSONRpcClient("/webapp/JSON-RPC/", user, pass)

The consutrctor of the JSONRpcClient object queries the server using the internal method system.listMethods which returns an array with the list of object methods available on the server. Proxy delegating functions are then added to the new JSONRpcClient object with each of the method names on the server.

Synchronous calls

A synchronous call can be made on one of the server methods by calling the associated object method on the JSONRpcClient object. eg. to call the method echo on the object exported with the name test, we would use:

jsonrpc.test.echo("hello");

Asynchronous calls

Asynchronous calls are simply made by inserting a JavaScript callback function as the first argument.

jsonrpc.test.echo(cb, "hello");

Anonymous functions can also be used:

jsonrpc.test.echo(function (msg) { print(msg); }, "hello");

The callback function is passed two arguments:

function cb(result, exception) {
  if(exception) { alert(exception.message); }
  // do stuff here ...
}

The second argument to callback functions is required to capture exception information. You must be aware of the following when using async callback functions:

  • result == null && exception != null when an exception occured.
  • result != null && exception == null when the call completed successfully.

Exceptions

The JSONRpcClient constructor proxy methods can throw an exception of type JSONRpcClient.Exception (ie. e.constructor == JSONRpcClient.Exception). The JSONRpcClient.Exception object is derived from the JavaScript Error object and thus inherits its general properties such as message.

Two types of exceptions are thrown from proxy methods on the JSONRpcClient object:

  • Client exceptions - exceptions that occured during the remote communication with the JSON-RPC server.
  • Java native exceptions - exceptions throw by the remote code.

Client Exceptions

Thrown if a communication error occurs, a method cannot be found. It has the following properties:

  • e.name == "JSONRpcClientException"
  • e.code an integer error code containing either an HTTP status code or one of the following codes:
    • JSONRpcClient.Exception.CODE_ERR_PARSE = 590
    • JSONRpcClient.Exception.CODE_ERR_NOMETHOD = 591
    • JSONRpcClient.Exception.CODE_ERR_UNMARSHALL = 592
    • JSONRpcClient.Exception.CODE_ERR_MARSHALL = 593
  • e.message a string containing descriptive text of the exception.

Java native exceptions

Thrown if the remote Java method raises an exception. It has the following properties:

  • e.name == "<class name of remote exception>"
  • e.code == JSONRpcClient.Exception.CODE_REMOTE_EXCEPTION
  • e.message a string containing descriptive text of the exception.
  • e.javaStack a string containing the Java stack trace.

References

JSON-RPC-Java has some basic ORB (Object Request Broker) functionality with the ability to pass objects by reference and keep these references in the user's session.

Two types of references are handled: opaque references and callable references.

Opaque References

Objects of classes registered as References will be returned as opaque reference objects to JavaScript instead of passed by value which is the default behaviour. When these opaque reference objects are passed to succussive Java method calls will then be reassociated back to the original Java object (great for security sensitive objects).

A class can be registered as an opaque reference on the JSONRPCBridge as follows:

bridge.registerReference(com.metaparadigm.test.Foo.class)

A reference in JSON format looks like this:

{ "javaClass":"com.metaparadigm.test.Foo",
  "objectID":5535614,
  "JSONRPCType":"Reference" }

References could should be used for privileged objects that contain information that needs to be kept secure or complex types that are not required in the Javascript client but need to be passed as a reference in methods of exported objects.

Callable References

Objects of classes registered as Callable References will return dynamic proxies to allow invocation on the particular object instance in the server-side Java. There are extensions to the JSON-RPC protocol in the provided JSON-RPC JavaScript client for dynamic proxy creation support.

A class can be registered as a callable reference on the JSONRPCBridge as follows:

bridge.registerCallableReference(com.metaparadigm.test.Bar)

A callable reference in JSON format looks list this:

{ "javaClass":"com.metaparadigm.test.Bar",
  "objectID":4827452,
  "JSONRPCType":"CallableReference" }

CallableReferences can be registered for classes that for instance are returned from factory classes as a convenient way to avoid having to manually export these objects returned from these factory methods.

Note: A limitation exists in the JSON-RPC client where only the top most object returned from a method can be made into a proxy.

Local Argument Resolvers

LocalArgResolvers are classes that can resolve an argument from the exported method signatures on the server-side. The exported signature of methods that contain a class registered as a LocalArgResolver have that class removed. It does not need to be provided in call in the JSON-RPC client.

The LocalArgResolver provides a mechanism to get access to the HttpServletRequest or HttpSession object associated with a request/method invocation. There are 3 LocalArgResolvers that are enabled by default:

  • JSONRPCBridgeServletArgResolver
  • HttpSessionArgResolver
  • HttpServletRequestArgResolver

Additional LocalArgResolvers can be created by implementing the LocalArgResolver interface and calling JSONRPCBridge.registerLocalArgResolver()

An example method that has JSONRPCBridge in the method signature:

public void setDebug(JSONRPCBridge bridge, boolean flag)
{
    bridge.setDebug(flag);
}

This can be called from JavaScript like this:

jsonserver.test.setDebug(true);

The JSONRPCBridge object associated with the users session will be resolved on the server-side and passed in to the remote method. Likewise you can add HttpSession or HttpServletRequest to your methods and have them filled in automatically.

Custom Serializers

...