Module rfc4627_jsonrpc

Provides a registry of running JSON-RPC objects, and a transport-neutral means of invoking methods defined on such objects.

Copyright © 2007-2010, 2011, 2012 Tony Garnock-Jones and 2007-2010 LShift Ltd.

Introduced in: 1.2.0

Authors: Tony Garnock-Jones (tonygarnockjones@gmail.com), LShift Ltd. (query@lshift.net).

References

Description

Provides a registry of running JSON-RPC objects, and a transport-neutral means of invoking methods defined on such objects.

Other modules provide interfaces to specific transports and transport implementations. See rfc4627_jsonrpc_http and rfc4627_jsonrpc_inets, for example.

JSON-RPC Objects are Erlang Processes

In the normal case, each JSON-RPC object in a running system corresponds to one Erlang process. This makes object lifecycle control very natural.

Basic usage

Ensure the registry process is running, using start/0 or start_link/0. Once it's up and running, use register_service/2 to expose a process as a JSON-RPC service.

To register a service, you will need to describe the methods available on it. Use service/4 to do so.

Implementing a service

Your service should be implemented by a gen_server process. JSON-RPC requests will be sent to it as gen_server:call/2 messages:

{jsonrpc, ProcedureNameBin, RequestInfo, Args}

Your module's handle_call function should respond to these messages with a reply of type jsonrpc_response().

Here's the implementation of the "test_proc" example:

  handle_call({jsonrpc, <<"test_proc">>, _RequestInfo, [Value]}, _From, State) ->
      {reply, {result, <<"ErlangServer: ", Value/binary>>}, State}.

See also the complete example Erlang module included with the source code, test_jsonrpc_inets.erl.

Registering a service with the Service Registry

You will need to -include("rfc4627_jsonrpc.hrl"). (Or, if you've installed the compiled rfc4627_jsonrpc code in your Erlang lib directory, -include_lib("rfc4627/include/rfc4627_jsonrpc.hrl").)

The service registry must be started before any registrations can be performed: simply call start/0 or start_link/0. This will start the registry if it wasn't running, or if it was, it will inform you of the existing registry's Pid.

Registering a service is as simple as starting a process to receive service requests, and passing its pid to rfc4627_jsonrpc along with a service descriptor object built from Erlang records defined in mod_jsonrpc.hrl:

  {ok, Pid} = gen_server:start(?MODULE, [], []),
  rfc4627_jsonrpc:register_service
        (Pid,
         rfc4627_jsonrpc:service(<<"test">>,
                                 <<"urn:uuid:afe1b4b5-23b0-4964-a74a-9168535c96b2">>,
                                 <<"1.0">>,
                                 [#service_proc{name = <<"test_proc">>,
                                                idempotent = true,
                                                params = [#service_proc_param{name = <<"value">>,
                                                                              type = <<"str">>}]}])).

This code registers a service called "test":

Note that almost all of the string values are expressed as binaries: this is because rfc4627 uses binaries to represent JSON strings.

To register a service with multiple procedures, add additional #service_proc records to the procedure list in the call to service/4. Similarly, additional parameters for each procedure can be defined by the addition of extra #service_proc_param records in the appropriate place.

The available types for parameters are the strings defined in this part of the JSON-RPC specification, namely "bit", "num", "str", "arr", "obj", "any" or "nil". See also rfc4627_jsonrpc:proc_param_type/1.

Invoking methods on local services

Usually, JSON-RPC services are invoked via HTTP using rfc4627_jsonrpc_inets or similar. However, the interface used by specific network transports to call methods on services is also available to ordinary programs. (And, of course, programs that implement new kinds of network transport for JSON-RPC.)

To invoke a local service method, first retrieve its service descriptor using lookup_service/1. Then use jsonrpc_post/3 or invoke_service_method/8 to call a method on the service.

The service record as retrieved from the registry contains the pid of the process responsible for handling service requests.

Experimental extension: 'Stateless' services

Instead of registering a pid with the rfc4627_jsonrpc registry, an alternative is to use a service record with a function object instead of a pid. This allows more control over how a service is implemented: if using a gen_server service is too heavy, a function object that sends a simple message could be used; or if the service didn't need an implementing process at all, the function object could process the request without sending any messages at all.

To build a service descriptor object with a function handler instead of a pid, call rfc4627_jsonrpc:service/5 instead of rfc4627_jsonrpc:service/4:

  rfc4627_jsonrpc:service({function, fun my_handler/3}, Name, Id, Version, Procs)
      -> service descriptor object
 
  my_handler(ProcedureNameBin, RequestInfo, Args) -> jsonrpc_response()
The resulting service descriptor can be registered with register_service/1 as well as used directly with invoke_service_method/8.

Data Types

json()

json() = rfc4627:json()

A JSON value.

jsonarray()

jsonarray() = rfc4627:jsonarray()

A JSON array.

jsonobj()

jsonobj() = rfc4627:jsonobj()

A JSON "object" or "struct".

jsonrpc_response()

jsonrpc_response() = {result | error, json()} | {result | error, json(), jsonobj()}

The type that JSON-RPC service implementations are required to return.

The first value should be result for a normal return value, or error for an error response. Use error_response/2 or error_response/3 to construct approprate error response values.

The second value is the main response body: for normal returns, this is the ordinary return value of the procedure, and for error responses, it is the response structure defined in the JSON-RPC specification.

The third, optional, value is called ResponseInfo. It can be used by the service implementation to supply transport-specific information back to its caller. For instance, if invoked via HTTP, extra headers to send back to the HTTP client can be passed in the ResponseInfo object. If ResponseInfo is omitted, {obj, []} is assumed.

service()

service() = #service{}

A service description record, as defined in rfc4627_jsonrpc.hrl. Can be constructed using service/4, or retrieved from the registry using lookup_service/1.

Function Index

error_response/2Constructs an error response as per the JSON-RPC specification.
error_response/3Constructs an error response as per the JSON-RPC specification.
expand_jsonrpc_reply/2
gen_object_name/0Generates a unique name that can be used for otherwise unnamed JSON-RPC services.
invoke_service_method/8Calls a method defined on a JSON-RPC service.
jsonrpc_post/3Calls jsonrpc_post/4 with a Timeout of default.
jsonrpc_post/4Performs a POST-style invocation of a JSON-RPC service method.
lookup_service/1Calls the registry to look up a service by name.
proc/2Constructs a service procedure description record.
register_service/1Registers a JSON-RPC service.
register_service/2Registers a JSON-RPC service.
service/4Constructs a service description record.
service/5As for service/4, but supplying a handler for use with an experimental "stateless" service implementation.
start/0Starts the registry process.
start_link/0Starts the registry process, linking it to the calling process.
system_describe/2Builds a JSON-RPC service description JSON object.

Function Details

error_response/2

error_response(CodeOrMessage::integer() | string() | binary(), ErrorValue::json()) -> {error, jsonobj()}

Constructs an error response as per the JSON-RPC specification.

Either a code or a message can be supplied as the first argument.

See also: error_response/3.

error_response/3

error_response(Code::integer(), Message::string() | binary(), ErrorValue::json()) -> {error, jsonobj()}

Constructs an error response as per the JSON-RPC specification.

The first argument should hold the error code. Error codes are defined in the JSON-RPC specification.

The second argument can be either a string() or a binary() describing the error as text.

The third argument is a general JSON value providing arbitrary further detail on the error.

expand_jsonrpc_reply/2

expand_jsonrpc_reply(RequestId, X2) -> any()

gen_object_name/0

gen_object_name() -> string()

Generates a unique name that can be used for otherwise unnamed JSON-RPC services.

invoke_service_method/8

invoke_service_method(ServiceRec, RequestId, PostOrGet, RequestInfo, EndpointAddress, Method, Args, Timeout) -> jsonrpc_response()

Calls a method defined on a JSON-RPC service.

Use lookup_service/1 or service/5 to get a usable #service record for use with this function.

The request ID should be the ID from the JSON-RPC request, as it was encoded for the transport the request arrived on. It will be used by this function in constructing the JSON-RPC reply object. Since the request ID is optional, it is acceptable to supply null instead of an integer.

The PostOrGet parameter is used to check the idempotency setting for the chosen service procedure. If the parameter is passed as post, no check is performed, as it is assumed that a stateful method call is permitted; if it is passed as get, then the idempotency flag is checked, and an error object may be returned in the case that the invoked method is non-idempotent.

The RequestInfo structure contains transport-specific details about the request. For HTTP, for example, this will include the HTTP headers and HTTP method. For AMQP, it will include the exchange and routing-key.

The EndpointAddress is only used in the case that the method being invoked is system.describe, in which case it is incorporated into the returned description as detailed in the documentation for system_describe/2.

Method is the name of the service method to invoke, and Args is either a JSON "object" or a JSON array, to serve as the parameters for the call.

The Timeout parameter is used to control how long the system will wait for a reply from the backing gen_server. If default is specified, the default gen_server:call timeout is used; otherwise, infinity or a number of milliseconds is passed in to the gen_server:call.

jsonrpc_post/3

jsonrpc_post(ServiceRec::service(), RequestInfo::jsonobj(), RequestObj::jsonobj()) -> jsonrpc_response()

Calls jsonrpc_post/4 with a Timeout of default.

jsonrpc_post/4

jsonrpc_post(ServiceRec::service(), RequestInfo::jsonobj(), RequestObj::jsonobj(), Timeout) -> jsonrpc_response()

Performs a POST-style invocation of a JSON-RPC service method.

RequestObj is to be a JSON "object" containing at minimum fields named "id", "method" and "params", with meanings as defined by the JSON-RPC specification.

See invoke_service_method/8 for descriptions of the other parameters.

lookup_service/1

lookup_service(Service::binary()) -> not_found | service()

Calls the registry to look up a service by name.

proc/2

proc(Name, Params::[Parameter]) -> #service_proc{}

Constructs a service procedure description record.

register_service/1

register_service(ServiceDescription::service()) -> ok

Registers a JSON-RPC service.

The name of the service is contained within its service record.

register_service/2

register_service(Pid::pid(), ServiceDescription::service()) -> ok

Registers a JSON-RPC service.

The name of the service is contained within its service record.

service/4

service(Name, Id, Version, Procs) -> service()

Constructs a service description record.

The Procs parameter should be a list of procedure-descriptions, which can be either constructed manually or using proc/2.

service/5

service(Handler, Name, Id, Version, Procs) -> service()

As for service/4, but supplying a handler for use with an experimental "stateless" service implementation.

start/0

start() -> {ok, pid()} | {error, {already_started, pid()}}

Starts the registry process.

start_link/0

start_link() -> {ok, pid()} | {error, {already_started, pid()}}

Starts the registry process, linking it to the calling process.

system_describe/2

system_describe(EndpointAddress::binary(), Service::service()) -> jsonobj()

Builds a JSON-RPC service description JSON object.

This is used in the implementation of the system.describe JSON-RPC method that all JSON-RPC services are required by the specification to support.

If EndpointAddress is undefined, no address field is returned in the resulting description. Otherwise, it is included verbatim. The other fields in the description are constructed using the information in the Service record.


Generated by EDoc, Nov 21 2012, 14:49:54.