NAME

Catalyst::Plugin::Server::XMLRPC::Tutorial -- Using Catalyst as XMLRPC Server

Setting up your server

Please consult the Catalyst::Tutorial on how to set up your basic application, in which you can use this plugin.

Configuring your server

Next, you can configure the application to do your bidding. First of all, every XMLRPC server needs to have an entrypoint. That is the url that every client needs to post to, in order to post methods to your server.

By default, this is http://your.host.tld/rpc

The full configuration options are described in Catalyst::Plugin::Server::XMLRPC.

Server types

You can set up the server in 2 ways; One is to merely dispatch to external code, the other is to integrate it in your Catalyst application.

Note that in both setups, the usual Catalyst dispatch logic is used, meaning that every method will have it's corresponding begin, auto and end code called.

Dispatching XMLRPC Server

This is the easiest configuration, that uses your catalyst app as just an XMLRPC server. This means that the XMLRPC server will just be used to dispatch to library code somewhere else.

Seting up the server

You could set this up as follows:

package MyApp;
use Catalyst    qw/Server Server::XMLRPC/;

sub dispatcher : XMLRPCRegex('.') {
    ...
}

Now, every xmlrpc call that gets posted to your application, will be handled by the sub dispatcher, which will be shown in the server startup as follows:

[catalyst] [debug] Loaded XMLRPCRegex actions:
.---------------------------+----------------------------------.
| XMLRPCRegex               | Private                          |
+---------------------------+----------------------------------+
| .                         | /dispatcher                      |
'---------------------------+----------------------------------'

IMPORTANT: Newer versions of Catalyst add a forward to a default view when no response body has been defined yet, which interferes with this plugin. To fix this, look for the following line in your Root controller:

sub end : ActionClass('RenderView') {}

And simply comment it out:

#sub end : ActionClass('RenderView') {}

Handling incoming method calls

A user could now post to your XMLRPC server as follows, with the bundled rpc_client script in this distribution

$ rpc_client -u http://your.host.tld/rpc -m foo

Your dispatcher now has to deal with the incoming request. The data returned, is whatever is present on the stash.

The below, contrived, example simply returns the method name that was called.

sub dispatcher : XMLRPCRegex('.') {
    my ($self, $c, @args) = @_;
    $c->stash->{ method } = $c->request->xmlrpc->method;
}

Application XMLRPC Server

This uses your catalyst application as an XMLRPC server, dispatching method calls to your catalyst app, rather than external code. This also allows you to use the XMLRPC plugin transparently, meaning you can post to the same method in your class both via the web, and via XMLRPC.

Setting up the server

You could set this up as follows:

package MyApp;
use Catalyst    qw/Server Server::XMLRPC/;

package MyApp::Controller::RPC;

### available as rpc.path
sub goto_path : XMLRPCPath('/rpc/path') {
    my ($self, $c, @args) = @_;
    ...
}

### available as any method containing 'foo'
sub goto_regex : XMLRPCRegex('foo') {
    my ($self, $c, @args) = @_;
    ...
}

### available as rpc.goto_local
sub goto_local : XMLRPCLocal {
    my ($self, $c, @args) = @_;
    ...
}

### avaiable as goto_global
sub goto_global : XMLRPCGlobal {
    my ($self, $c, @args) = @_;
    ...
}

This gives you several method calls, available as rpc calls, as also shown by the catalyst server startup messages:

[catalyst] [debug] Loaded XMLRPCPath Method actions:
.---------------------------+---------------------------------------.
| XMLRPCPath Method         | Private                               |
+---------------------------+---------------------------------------+
| goto_global               | /rpc/goto_global                      |
| rpc.goto_local            | /rpc/goto_local                       |
| rpc.path                  | /rpc/goto_path                        |
'---------------------------+---------------------------------------'

[catalyst] [debug] Loaded XMLRPCRegex actions:
.---------------------------+---------------------------------------.
| XMLRPCRegex               | Private                               |
+---------------------------+---------------------------------------+
| foo                       | /rpc/goto_regex                       |
'---------------------------+---------------------------------------'

A user could now post to your XMLRPC server as follows, with the bundled rpc_client script in this distribution, to get to the goto_regex method;

$ rpc_client -u http://your.host.tld/rpc -m foo.bar 

Arguments

Any XMLRPC call will have arguments available to it, if the client provided them.

To use our dispatcher exaple again:

package MyApp;
use Catalyst    qw/Server Server::XMLRPC/;

sub dispatcher : XMLRPCRegex('.') {
    my($self, $c, @args) = @_;

    ### the XMLRPC arguments, as a list
    ### same as the ones provides in @args
    $aref = $c->req->xmlrpc->args;

    ### if the arguments provided were a list with 1 single item
    ### and that item was a hashref, they are added as ->params,
    ### just like in regular catalyst
    $href = $c->req->xmlrpc->params

    ...
}

Return values

By default, this plugin returns all values that are on the stash. If you wish to have XMLRPC specific return values, put them in $c->stash->{xmlrpc} and that will be the only thing returned to the client.

For example, returning the whole stash:

sub some_rpc_call : XMLRPCLocal {
    ...
    $c->stash->{foo} = 1;
    $c->stash->{bar} = 2;
}        

### will return:
{ foo => 1, bar => 2 }

For exmpale, returning xmlrpc specific values:

sub some_rpc_call : XMLRPCLocal {
    ...
    $c->stash->{foo}    = 1;
    $c->stash->{xmlrpc} = { bar => 2 };
}        

### will return:
{ bar => 2 }

Authentication

If you wish to add authentication to your XMLRPC server, you can do this as follows:

package MyApp;
use Catalyst    qw/Server Server::XMLRPC/;

### Make sure we show errors to the outside world
__PACKAGE__->config({
        'xmlrpc' => {
                'show_errors' => 1,
            }
        );

### always called on any method dispatch
sub auto {
    my( $self, $c ) = @_;
    
    ### someone from an illegal ip posting to us!
    unless( in_allowed_ips( $c->request->address ) ) {

        $c->error( "You are not allowed to use this server" );
        return 0;
    }
    
    return 1;
}

You can use this same mechanism to do user/password based authentication by checking for arguments provided to the xmlrpc server. See the Arguments section above.

Transparent XMLRPC

There is actually nothing stopping you from making your methods available both via the web, as traditionally done by Catalyst, and via XMLRPC as well.

You could set this up as follows:

package MyApp;
use Catalyst    qw/Server Server::XMLRPC/;

package MyApp::Controller::RPC;

### available as rpc.path as well as /web/path
sub goto_path : Path ('/web/path') : XMLRPCPath('/rpc/path') {
    my ($self, $c, @args) = @_;
    
    if( $c->request->xmlrpc->is_xmlrpc_request ) {
        ### got called via xmlrpc
        ...
    } else {
        ...
    }     
}

This makes goto_path available as http://your.host.tld/web/path and http://your.host.tld/rpc with method name rpc.path

Examples

Standard remote call

Here's a bit of code that uses the default configuration of this package to dispatch an rpc method to a subroutine in our Catalyst application.

package MyApp::Controller::Demo;

sub echo : XMLRPC {
    my ($self, $c) = @_
    @args = @{ $c->req->xmlrpc->args };

    $c->stash->{xmlrpc} = \@args;
}

### meanwhile, in an rpc client far away
$ rpc_client -u http://your-host.tld/rpc -m demo.echo test
Output: test

Let's break it down line by line;

package MyApp::Controller::Demo;

This is the package you are currently working in. The XMLRPC server will dispatch, by default, to something under your MyApp::Controller namespace.

sub echo : XMLRPC {

You now have a method that's available via XMLRPC, thanks to the XMLRPC attribute.

@args = @{ $c->req->xmlrpc->args };

$c->stash->{xmlrpc} = \@args;

First, we will retrieve our arguments from the XMLRPC::Server object.

We then set $c->stash->{xmlrpc} to @args, in good echo server tradition.

### meanwhile, in an rpc client far away
$ rpc_client -u http://your-host.tld/rpc -m demo.echo test
-----------------Output-----------
$VAR1 = 'test';

The rpc_client uses 2 configurable parts: the /rpc appended to the http hostname, which is called the entry point of the server. /rpc is the default (you can configure this to anything you want). The XMLRPC server will only listen to requests that are posted to the entry point.

The second part is the method the client posts to. To reach the method Echo in your package, the client will have to use the method demo.echo; demo as that is the name of your package, and the echo bit as it is the name of your method.

The fictive rpc_client program then just prints out what was returned to it.

More code examples

Browse through the test directory of this distribution, to see some examples of all of the above usage. The relevant modules can be found under t/lib of this distribution.

AUTHORS

Jos Boumans (kane@cpan.org)

Michiel Ootjers (michiel@cpan.org)

BUG REPORTS

Please submit all bugs regarding Catalyst::Plugin::Server to bug-catalyst-plugin-server@rt.cpan.org

LICENSE

This library is free software, you can redistribute it and/or modify it under the same terms as Perl itself.