JSON-RPC Protocol

From KitwarePublic
Jump to navigationJump to search

JSON RPC

Refer to http://json-rpc.org/wiki/specification for details on how json-rpc calls are made and how responses/errors are returned.

In summary: A JSONRPC call is of the following structure: <source lang="javascript"> {

 "id": <number>,
 "method": <string identifying the method to call>,
 "params": [list of parameters]

} </source>

A response is of the following form: <source lang="javascript"> {

 "id" : <number>,
 "result" : <reply>,
 "error" : null or error message/object or missing if no error.

} </source>

Communicating with the WebServer

Clients can only communicate with the web server. When communicating with the ParaViewAdaptor, they still need to route the requests through the web-server. <context-root>/PWService/json is the URL for the servlet that accepts JSON requests.

Obtaining list of available RMIs

Call "system.listMethods" with no parameters to obtain a list available RMIs.

Request: <source lang="javascript"> {

 "id": <num>,
 "method" : "system.listMethods",
 "params" : []

} </source>

Response: <source lang="javascript"> {

 "id": <num>,
 "result" : ["methodname1", "methodname2", ...]

} </source>

The methods exposed are defined in: <ParaViewWebSource>/WebServer/PWService/src/java/org/paraview/ParaViewVisualizationService.java

To invoke any of the methods returned, one simply creates a JSONRPC message with the "method" string referring to the method to call, and params matching the params expected on the servlet (defined in ParaViewVisualizationService.java)

Next we will see each of these methods and how and when to use them.

Creating a new visualization session i.e. start a new ParaViewAdaptor

Visualization is done by ParaViewAdaptor which is ParaView-based application. For every visualization session, we need a new ParaViewAdaptor instance. This is referred to as a visualization session. To create a visualization session or launch an instance of ParaViewAdaptor, use the following request:

Request: <source lang="javascript"> {

 "id": <num>,
 "method": "VisualizationsManager.createVisualization",
 "params": ["session-name", "descriptive comments/text", "configuration"]

} </source>

Where the parameters are: "session-name" : is an arbitrary string used to humanly identify the session. It's merely used by the admin page when listing all sessions. "descriptive comments": arbitrary comments again used for listing on admin page. "configuration" : identifies the configuration in pw-config.properties file. Confuguration is essential to identify how to start ParaViewAdaptor as well as locations of plugin directories etc. e.g. "default"

Response: <source lang="javascript"> {

 "id": <num>,
 "result": "session-id"

} </source>

"session-id" is a unique identifier for the session. Use this for subsequent communication affecting this session.

Terminating a session

To gracefully close (or abort if close fails) a ParaViewAdaptor instance use the following request:

Request: <source lang="javascript"> {

 "id": <num>,
 "method": "VisualizationsManager.stopVisualization",
 "params": ["session-id"]

} </source>

Response: <source lang="javascript"> {

 "id": <num>,
 "result": "SUCCESS" or "ERROR <message>"

} </source>

Note that this call never returns a jsonrpc error.

Communicating with a ParaViewAdaptor instance

For all other interesting communications, one needs to talk with ParaViewAdaptor. These include requests to create visualization pipeline objects, update their parameters, get information about them etc. All these communications are made using the "invoke" request as follows:

Request: <source lang="javascript"> {

 "id": <num>,
 "method": "VisualizationsManager.invoke",
 "params":["session-id", "string message to forward to ParaViewAdaptor"]

} </source>

Response: <source lang="javascript"> {

 "id": <num>,
 "result": "string of response from ParaViewAdaptor"

} </source>

All messages sent to ParaViewAdaptor using this route themselves are JSONRPC messages (as are the responses).

The next sections explains the JSON messages that can be sent to ParaViewAdaptor. All the JSON-RPC objects created must be converted to a string and then set as the params for the invoke message e.g. <source lang="javascript"> {

 "id": <num>,
 "method" : "VisualizationsManager.invoke",
 "params" : ["session-id", "{\"id\": <num>, \"method\":\"get_module\", \"params\":[]}"]

} </source>

Communicating with ParaViewAdaptor

ParaViewAdaptor handles all JSON messages sent to it by forwarding them to the Python interpreter where they are processed. For WebServer, the ParaViewVisualizationService.java defines the exposed API similarly for ParaViewAdaptor, <ParaViewWebSource>/ParaViewAdapter/pwservice.py defines the RMI calls available.

Note that all these calls are typically routed through the WebServer in which case the requests as well as responses themselves are wrapped in another JSONRPC message as explained in the previous section.

Plugins

Getting the list of plugins available

Plugins are python scripts that are placed in a directory specified by the pw-config.properties file. To get a list of available plugins call get_plugins().

Request: <source lang="javascript"> {

 "id": <num>,
 "method": "get_plugins",
 "params": []

} </source>

Response: <source lang="javascript"> {

 "id": <num>,
 "result": ["plugin_name_1", "plugin_name_2"],
 "error" : null

} </source>

Getting list of functions available in a plugin

Once one knows the list of plugins, to know what functions are available in a particular plugin, use:

Request: <source lang="javascript"> {

 "id": <num>,
 "method": "get_plugin",
 "params": ["<plugin_name>"]

} </source>

The result is a JSON object as follows: Response: <source lang="javascript"> {

 "id": <num>,
 "result": {
           "__jsonclass__" : "Plugin",
           "__selfid__"    : "<plugin_name>",
           "__methods__"   : ["func1", "func2", func3"]
           },
 "error" : null

} </source>

Calling a function in a plugin

To call a function in a plugin, use the following form.

Request: <source lang="javascript"> {

 "id": <num1>,
 "method": "execute_command_on_plugin",
 "params": {
           "id" : <num2>,
           "method" : "<plugin-name>",
           "params" : {
                      "id": <num3>,
                      "method": "<func_name>",
                      "params": [ <params to the function> ]
                      }
           }

} </source>

Response: <source lang="javascript"> {

 "id": <num1>,
 "result": {
           "id": <num2>,
           "result": <return value from <plugin_name>.<func_name> or null>,
           "error": null
           },
 "error" : null

} </source>

Using pwsimple.py

pwservice.py defines the RMI calls available, while pwsimple.py defines all visualization specific API. pwsimple.py is a extension of ParaView's simple.py and hence has all functions defined in simple.py plus a few more. One can think of pwsimple as a special plugin available by default to all session and having a slightly different API to access it.

Getting the list of functions available

Use get_module() to obtain the list of functions.

Request: <source lang="javascript"> {

 "id": <num>,
 "method": "get_module",
 "params": []

} </source>

Response: <source lang="javascript"> {

 "id": <num>,
 "result": [ "func1", "func2" ...],
 "error" : null

} </source>

Invoke a function in pwsimple

We use execute_command for that. The return value can be object representing a proxy. We will see how proxies are defined in the following section.

Request: <source lang="javascript"> {

 "id": <num1>,
 "method": "execute_command",
 "params": {
           "id": <num2>,
           "method": "<function_name>",
           "params": [<params to function>]
           }

} </source>

Response: <source lang="javascript"> {

 "id": <num1>,
 "result": {
           "id": <num2>,
           "result": <return value from function>,
           "error": null
           },
 "error" : null

} </source>

Dealing with Proxies

When it comes to creating/updating visualization pipelines, one cannot but run into proxies. Proxies are objects that represent various components in the visualization pipelines including views, representations, sources, filters, readers. pwsimple as well as plugins can export functions that return proxies. The JSON api gracefully handles returning of the proxies to the client so that the client can use them when constructing future requests.

A JSON object for a proxy is of the following structure:

<source lang="javascript"> {

 "__jsonclass__": "Proxy",
 "__selfid__": "85",
 "__methods__": ["ListProperties"],
 "__properties__": ["GUISize", .., "ViewSize"],

} </source>

The __jsonclass__ helps us identify that the object is a proxy. When such a object is passed in as an parameter to pwsimple or a plugin as described earlier, then it automatically gets replaced by the actual Proxy object before evaluation.

__selfid__ is a unique identifier for the proxy. Every proxy gets a __selfid__ once it's created. One should never change this. This is critical to ensure that the right Proxy is identified.

__methods__ is the list of methods available on the Proxy object itself. One can use execute_command_on in pwservice to call such methods.

__properties__ is the list of properties or parameters on the proxy object. One can update these by using the handle_property function in pwservice.

Calling a Proxy method

To call one of the methods defined on the proxy, use the following message:

Request: <source lang="javascript"> {

 "id": <num1>,
 "method": "execute_command_on",
 "params": {
           "id": <num2>,
           "method": "<proxy.__selfid__>",
           "params": {
                     "id": <num3>,
                     "method": "method-name",
                     "params": [<arguments>]
                     }
           }

} </source>

Response: <source lang="javascript"> {

 "id": num1,
 "result": {
           "id": <num2>,
           "result": <return value>,
           "error": null
           },
 "error": null

} </source>

Setting a Property value on a Proxy

Request: <source lang="javascript"> {

 "id": <num1>,
 "method": "handle_property",
 "params": {
           "id":<num2>,
           "method": "proxy.__selfid__",
           "params": {
                     "id": <num3>,
                     "method" : "set<PropertyName>",
                     "params" : <arguments>
                     }
           }

} </source>

Response: <source lang="javascript"> {

 "id": <num1>,
 "result": {
           "id": <num2>,
           "result": <return value>,
           "error": null
           },
 "error": null

} </source>

Getting a Property value on a Proxy

Request: <source lang="javascript"> {

 "id": <num1>,
 "method": "handle_property",
 "params": {
           "id":<num2>,
           "method": "proxy.__selfid__",
           "params": {
                     "id": <num3>,
                     "method" : "get<PropertyName>",
                     "params" : []
                     }
           }

} </source>

Response: <source lang="javascript"> {

 "id": <num1>,
 "result": {
           "id": <num2>,
           "result": <return value>,
           "error": null
           },
 "error": null

} </source>