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

NAME

Gantry::Plugins::SOAP::RPC - RPC style SOAP support

SYNOPSIS

In your GEN module:

    use Your::App::BaseModule qw( -PluginNamespace=YourApp SOAP::RPC );
    # this will export these into your package:
    #    soap_in
    #    soap_out
    #    do_main
    #    do_wsdl
    #    return_error

    sub get_soap_ops {
        my $self = shift;

        return {
            soap_name      => 'Kids',
            location       => $self->location,
            namespace_base => 'localhost',
            operations     => [
                {
                    name => 'get_count',
                    expects => [
                        { name => 'table_name', type => 'xsd:string' },
                    ],
                    returns => [
                        { name => 'count', type => 'xsd:int' },
                    ],
                },
            ],
        };

    }

Add as many operations as you need.

In your stub:

    use Your::GEN::Module;

    sub get_count {
        my $self = shift;
        my $data = shift;

        return { ... };
    }

Your data will have whatever was in your client's soap request. You are responsible for diagnosing all errors and for returning the correct structure (it should match the returns list). But feel free to just die when you spot an error, this module traps those and uses its return_error method which sends valid SOAP fault messages.

DESCRIPTION

This plugin is for rpc style SOAP requests only. If you need document style requests, you should use Gantry::Plugins::SOAP::Doc.

Bigtop can help a lot with the use of this plugin. Below is what you need to do manually, should you choose that route. But first, I'll explain what is happening from overhead.

For each SOAP handler in your app, there should be one controller (or a stub/GEN controller pair) placed on a location in your httpd conf. If you do the normal thing, the GEN module (or controller if you don't have a GEN) uses this module and accepts all the exports (the list is mainly for documentation, since all of them are exported by default).

Two of the exports are Gantry handlers: do_main and do_wsdl. This means that the caller will look for the service itself at the location from httpd.conf, while they will add /wsdl to get the WSDL file. For example, suppose you have this in your httpd.conf:

    <Location /appname/SOAP>
        SetHandler  perl-script
        PerlHandler YourApp::YourSoapStub
    </Location>

Then users will hit /appname/SOAP to get the service and /appname/SOAP/wsdl to get the WSDL file.

This module registers a pre_init callback which steals all of the body of the POST from the client. Then it lets Gantry do its normal work. So your stub methods will be called through a site object.

All SOAP requests are handled by do_main, which this module exports. It uses the internal soap_in method to parse the input. You must import soap_in, so it will be in the site object. It fishes the client's desired action out of the incoming SOAP request and calls the method of the same name in the stub module. That method receives the SOAP request as parsed by XML::Simple's XMLin function. It must return the structure which will be returned to the client.

The action method's structure is then fed to soap_out (which you must also import) and the result is returned as a plain text/xml SOAP message to the client. SOAP::Lite's SOAP::Data and SOAP::Serializer are used to the hard work of making output.

Here are the details of what your need to implement.

You need a namespace method which returns the same name as the -PluginNamespace.

You also need a get_soap_ops method which returns a hash describing your WSDL file. See the SYNOPSIS for an example. Here's what the keys do:

soap_name

This is used whenever SOAP requires a name. Prefixes and suffices are appended to it, as in NAME_Binding.

location

Normally, you should make this <$self-location>>, so that all requests come to the same SOAP controller which produced the WSDL file.

namespace_base

This should be everything after http:// and before <$self-app_rootp>> in the URL of the SOAP service. Usually that is just the domain.

operations

An array reference of hashes describing the services you offer. Each element is a hash with these keys:

name

The name of the action method in your stub controller, which will handle the request.

expects

An array reference of parameters the method expects. These have two keys: name and type. They type can be any valid xsd: type or any other type in your WSDL file. If you need to define types, see WSDL TYPES below.

returns

An array exactly like expects, except that it represents your promise to the client of what will be in the SOAP response.

get_callbacks

Called by Gantry's import method to register the callbacks for this module.

WSDL TYPES

Gantry ships with wsdl.tt which it uses by default to construct the WSDL file from the result of get_soap_ops. If you need to define types, simply copy that template into your own root path and add the types you need. If you want to supply your own data to the template, just implement your own get_soap_ops and return whatever your template expects.

METHODS

All of these are exported by default. You may supply your own, or accept the imports. Failure to do one of those two is fatal. Doing both will earn you a subroutine redefinition warning.

steal_post_body

Steals the body of the POST request before Gantry's engine can get to it. This method must be registered to work. Do that by using the plugin as you use your base module:

    use Your::App qw(
        -PluginNamespace=module_name
        SOAP::RPC
    );

Note that you don't have to do this in the same place as you load the engine. In fact, that probably isn't a great idea, since it could lead you down the primrose path to all of your modules using the plugin's steal_post_body, leaving you without form data.

Then, you must also implement a method called namespace which returns the same string as the -PluginNamespace.

This method delegates its work to consume_post_body, which is exported by each engine.

do_main

The SOAP service. Fishes the POST body with get_post_body parses it using soap_in, dispatches to your action method, makes the SOAP response with soap_out, and returns it.

do_wsdl

Uses wsdl.tt to return a WSDL file for your service to the client. It uses get_soap_ops to know what to put in the WSDL file. Its template is wsdl.tt. You must call yours that, but feel free to copy the standard one to an earlier element of the root template path and edit it.

soap_in

For internal use. Uses XML::Simple to parse the incoming SOAP request.

soap_out

For internal use. Forms a valid (though simple) SOAP response. Note that this module may not be able to handle your complex types. It uses SOAP::Lite's SOAP::Data and SOAP::Serializer modules to generate the XML response.

return_error

Returns a valid SOAP fault response. You must either accept this method in your imports or write one yourself. The standard error is not SOAP aware.

AUTHOR

Phil Crow, <crow.phil@gmail.com>

COPYRIGHT and LICENSE

Copyright (c) 2007, Phil Crow

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available.