Diligence is still under development and incomplete, and some this documentation is wrong. For a more comprehensive but experimental version download the Savory Framework, which was the preview release of Diligence.
RPC Service
The RPC (Remote Procedure Call) Service provides robust, elegant support for various versions of the JSON-RPC and XML-RPC specifications, including support for batch processing for JSON-RPC 2.0. It's powerful enough that it may be in itself the primary reason why you wish to use Diligence.
In most cases, all you need to do is hookup your JavaScript functions to a URI, and let the RPC Service do the rest. All error codes, system APIs and type conversions will be properly handled.
As a bonus, the RPC Service also includes a nice client utility for calling JSON-RPC and XML-RPC.
Is RPC a good idea? We're inclined to say: no. REST is a much more scalable and robust pattern, all things considered. REST uses all the power of HTTP to provide client-cacheable representations. RPC, on the other hand, supports only HTTP POST, the only non-idempotent HTTP operation, which can never be cached. However, RPC may be necessary for communication with other services and clients, so you might not have a choice. And, sometimes, it's just the most straightforward, quick-and-dirty solution to a problem. Especially with the Diligence RPC Service, it's so easy to just allow clients to call functions on the server, that sometimes you might prefer it to designing a RESTful URI-space. So be it! Just make sure you understand the pros and cons of you choice.
Setup
Make sure to check out the API documentation for Diligence.RPC.
First, let's configure the URI-space in your application's "routing.js". Add the following to app.routes and app.dispatchers:
app.routes = { ... '/calc/': '@calc' } app.dispatchers = { ... javascript: '/manual-resources/' }
We can now configure our resources in "/libraries/manual-resources.js":
document.executeOnce('/diligence/service/rpc/') var Calc = { multiply: function(x, y) { return x y } } resources = { ... calc: new Diligence.RPC.Resource({namespaces: {Calc: Calc}}) }
And… that's pretty much it! You can now call your methods using JSON-RPC or XML-RPC.
Namespaces
The key of the namespace is prefixed with a period before all method identifiers. So, our method above would be identified as "Calc.multiply".
However, if you do not want this prefix, you can use the special "." key, which here means the root namespace:
resources = { ... calc: new Diligence.RPC.Resource({'.': Calc}) }
The method would now be identified simply as "multiply".
If you don't need the namespaces feature at all, you can use the following shortcut (note the "namespace" key, singular):
resources = { ... calc: new Diligence.RPC.Resource({namespace: Calc}) }
Long Form
You have some more control over the exported functions, should you need it. The long form of creating namespaces is like so:
var Calc = { multiply: { fn: function(x, y) { return x y }, arity: 2 } }
The "artity" key counts how many arguments the function requires. If it's not there, the RPC Service will count them from the function spec. However, this won't work if you access JavaScript "arguments" directly, hence this long form exists.
System Namespace
The "system" namespace is reserved for parts of the RPC protocols. The RPC Service implements these for you:
- system.getCapabilities
- system.listMethods
- system.methodSignature
- system.methodHelp: By default, this will just show the method name, but in the long form definition you can add a "help" key to set this as you need.
Scope
When your function is called, the "this" will be automatically populated with the following keys:
- definition: Your long-form function definition (short-form function definitions will be expanded into the long form)
- namespace: The original namespace object you supplied
- resource: The Diligence.RPC.Resource instance
- conversation: The Prudence conversation of the call
- call: The RPC call object, as sent from the client
The "method" key is useful in that you can add anything you want to the method object. For a rather silly example:
var Calc = { multiply: { fn: function(x, y) { return x y this.definition.multiplyAll }, multiplyAll: 100 } }
One special key is reserved: "scope". Use it to override "this" to be any value you desire:
var Calc = { multiply: { fn: function(x, y) { return x y this }, scope: 100 } }
If you are using JavaScript object oriented programming, you might want "this" to always just be the namespace object itself. In that case, you can use the "objects" key instead of the "namespaces" key when creating your Diligence.RPC.Resource constructor. It works the same way as a namespace except that the scope will be the object itself for all method calls:
// This is a class var Calc = function(multiplyAll) { this.multiplyAll = multiplyAll this.multiply = function(x, y) { return x y this.multiplyAll } } resources = { ... calc: new Diligence.RPC.Resource({objects: {Calc: new Calc(100)}}) }
You can mix "namespaces" and "objects" in the same constructor. Also note that you can also use "object" (singular) in the same way as "namespace" (singular).
Fault Codes
If your function throws an exception, the RPC Service will return a ServerError fault code with the exception string as the message.
However, you can also return specific XML-RPC fault codes (the same code numbers are used by JSON-RPC):
-
By throwing a number (all fault codes are negative numbers). You can use the convenient constants in "Diligence.Fault". For example:
throw Diligence.Fault.InvalidParams
-
By throwing a dict with both the fault code and the message. For example:
throw {code: Diligence.Fault.InvalidParams, message: 'Cannot divide by 0!'}
Usage
URI Query Parameters
- type: The resource will automatically determine whether it should work in JSON-RPC or XML-RPC according to the media type of the incoming payload, or if that's not available, the preferred media type for the returned representation. Unfortunately, some clients don't or can't set either. In that case, you can set the type explicitly in the URI, with either "json" or "xml" as values.
- human: Set this to "true" to generate multiline, indented human-readable results (both for JSON and XML). Great for debugging.
Calling RPC with the API
The RPC Service includes a useful RPC client function, "Diligence.RPC.request". It's essentially a wrapper over the Prudence.Resources API that builds the payload for you and nicely unpacks the results. The results will always be in JSON-RPC's format, even if you are using XML-RPC. This allows for uniform processing on your end.
Here's an example of an internal call using JSON-RPC:
document.executeOnce('/diligence/service/rpc/') var result = Diligence.RPC.request({ uri: '/calc/', internal: true, name: 'Calc.multiply', params: [5, 6], id: 'abc', protocol: 'json' }) if (result.error) { print('Error: ' + result.error.message) } else { print(result.result) }
For XML-RPC, simply set "protocol" to "xml". If not provided, it defaults to "json". Note that the result will also include that "protocol" key you provided, in case you need to know which protocol was used.
Generally, if you have the option to use JSON-RPC, you should prefer it. XML serialization incurs an extra overhead in JavaScript.
Calling RPC with cURL
cURL is an HTTP command line tool based on the cURL library, available for a great many Unix-like operating systems as well as Windows. It's especially useful for testing RESTful APIs. Here's a quick tutorial to get you started with using cURL with the RPC Service.
First, let's create our payload. With a text editor, create a file named "rpc.json" and paste this:
{ "jsonrpc": "2.0", "method": "Calc.multiply", "params": [2, 3], "id": "abc" }
You can send a payload using the "-d" switch, which also sets the HTTP verb to POST. When using "-d", you can also start your payload with "@" to signify that you want to send the contents of a file:
curl -d @rpc.json "http://localhost:8080/myapp/calc/?type=json&human=true"
You should get this result:
{ "id": "abc", "result": "6", "error": null, "jsonrpc": "2.0" }
Calling RPC from Web Browsers
Many client-side JavaScript frameworks include support for RPC, but if all you need is a straightforward, self-contained library, we recommend jsonrpcjs.
The Diligence Manual is provided for you under the terms of the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. The complete manual is available for download as a PDF.