The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

JRPC - Create JSON-RPC Services focusing on app logic, not worrying about the details of JSON-RPC Processing.

SYNOPSIS

   use JRPC;
   # Load one of the Service modules JRPC::CGI, JRPC::Apache2 or JRPC::Nginx
   # See particular submodule documentation for the details.
   use JRPC::CGI;

DESCRIPTION

JRPC Module bundle consists of Server and Client pieces for creating JSON-RPC services. For the server piece it takes a slightly different approach than many other "API Heavy" CPAN modules. Instead of assembing your service out of API calls, JRPC forms a framework on top of your implementation and allows you to write a (single) callback:

  • receiving parameters (JSON-RPC "params") of the of JSON-RPC call pre-parsed, ready-to be worked with by your app code

  • returning the "result" data (to framework taking care of JSON-RPC)

The callback should be wrapped into a class package. One package can host multiple service methods.

When any exception is thrown (by die()) during the processing by callback, the framework takes care of turning this to an appropriate JSON-RPC fault. The framework will also take care of dealing with JSON-RPC "envelope" (term borrowed from SOAP lingo) of both request and response, "unwrapping" it on request and wrapping the result with it on response.

   package MyEchoService;
   our $VERSION = '0.01';
   
   # Respond with whatever was sent
   sub echo {
      my ($param) = @_;
      # Pass-through - Just send the "params" as "result"
      return($param);
   }

DISPATCHING OF SERVICE REQUEST

Dispatching of service request can use 2 methods:

  • URL based dispatching, where relative URL (after server name and port) defines the package and method name ("method" in JSON-RPC envelope) defines the runtime method

  • URL independent dispatching where method name with dot-notation defines the method name

Currently the dispatching method is automatically chosen based on what is found in "method" member of JSON-RPC envelope. Examples highlighting the (automatically) chosen dispatching method:

  • "method": "echo", URL "/MyEchoService" - Choose URL based dispatching, map relative URL to package and echo() method ( MyEchoService::echo() )

  • "method": "MyEchoService.echo" - Derive both Class and method from dot-notation ( MyEchoService::echo() )

I'd recommend the latter as a more modern way of dispatching. Additionally (because of URL independence and need to "map" URL:s) it is less likely to require config changes in your web server.

METHODS

createfault($req, $msg, $errcode)

Internal method to create a JSON-RPC Fault message. As these parameters are coming from the server side code, they are trusted (i.e. not validated) here. Parameters:

  • $msg - Message (typically originating from exceptions). Placed to member "message" of "error" branch of fault (See JSON-RPC 2.0 spec for details).

  • $errcode - Numeric error code (must be given)

Notice that the service methods should not be using this directly, but only be throwing exceptions. As a current shortcoming, the service methods cannot set $errcode (Only basic string based exceptions are currently allowed / accepted).

This should not be called explicitly by service developer. Throw execptions in your service handler to have them automatically converted to valid JSON-RPC faults by createfault().

parse($jsontext)

Parse JSON-RPC Message and validate the essential parts of it. What is validated (per JSON-RPC spec):

  • method - must be non-empty

  • params - presence (of key) - even null (value) is okay.

  • id - JSON-RPC ID of message - must be present (format not strictly constrained)

  • jsonrpc - JSON-RPC protocol version (must be '2.0')

The particular format of "params" (Object/Array/scalar) or individual parameter validation in case of most common case "Object" is not in the scope here.

respond_async($client, $url, $meth, $p, %opts);

After async processing, acknowledge the original client tier (or any URL) of the completion of the asynchronous part. This method is experimental and the whole concept of using asynchronous processing at service is an unofficial extension to standard JSON-RPC 2.0 protocol spec.

Parameters:

  • $client - Instance of JRPC::Client. If not provided, a new client will be instantiated here.

  • $url - URL of the async callback - Must be provided

  • $meth - JSON-RPC Method to callback to on the server (default: "oncomplete")

  • $p - JSON-RPC "params" to send in completion acknowledgement (must be supplied, likely to be Object/Hash)

If optional KW params in have param 'cb' set, it is used to process the response from callback service. The "result" of JSON-RPC response is passed to this callback.

Return "result" of response (likely to be Object/Hash).

LIMITATIONS

JSON-RPC batch requests are not supported.

NOTES ON SERVICE PACKAGES

Service packages that implement the service are and should be completely independent of the JRPC framework and its package namespaces.

FAQ

Should I inline my service package into my .pl file calling JRPC::CGI::handle_cgi($cgi) ?

Possibly, during the first prototyping minutes of the project. CGI is great for developing and prototyping, but it has a low efficiency runtime for high-volume service. After getting your package running as a proof of concept, place your package into external file named by package.

Does my service package with its service methods run in all JRPC::*(Apache2|Nginx|CGI) service runtimes ?

Assuming you paid attention to basic requirements of re-entrancy and long-lived persistent runtime - it will. This means you can migrate a service package developed in plain old CGI runtime to run memory persistently in Nginx. Anything running in long-lived runtime is prone to memory/variable corruption when sloppily designed, so approach these solutions with the respect and care they deserve.

Using the "dot-notation" dispatching - can I have deep package paths ?

Yes. JSON-RPC methods, like "Shop.Product.Cart.add", will be translated to respective perl package "Shop::Product::Cart" and a method add() within there. The need for particular dispathing method is autp-detected and things should "just work". Choose your method notation strategy (read or review the section "DISPATCHING OF SERVICE REQUEST" for more info).

Is auto-loading of service packages supported ?

Not at the moment. The security reasons and potential for exploits are against this. The service packages thus have to be loaded explicitly into the service runtime (by use ... or require(...)). In future there could be an option to do auto-loading for development and quick prototyping reasons.

Using JRPC::Apache2 - how do I load the service packages ?

Use either Apache PerlModule directive in httpd.conf:

   PerlModule Matrix

... or do the loading in a mod_perl conventional startup.pl file by including a loading directive (Use the following to load startp.pl PerlPostConfigRequire /path/to/startup.pl):

   # startup.pl
   use Matrix;

The general hassle here is you typically have to have admin rights to author httpd.conf and startup.pl. Per-directory ".htaccess" config is usually not allowed to do these config steps.

My Service package to use with JRPC::Apache2 is in an odd location and perl cannot find it in @INC. How do I enable Perl to find it?

Again you can add to @INC in httpd.conf or startup.pl. In httpd.conf, add:

   PerlSwitches "-I/odd/path/with/packages"

Or in startup.pl:

   use lib('/odd/path/with/packages')

A sustainable solution is to package your service package to a Perl standard (CPAN style) module package that is easy to install to standard perl (@INC) library path locations (thus avoiding the PerlSwitches -I... or use lib(...)). However during development you will be likely using the above additions to perl library path (@INC).