Web DevCenter
oreilly.comSafari Books Online.Conferences.

Sponsored Developer Resources

Web Columns
Adobe GoLive
Essential JavaScript

Web Topics
All Articles
Scripting Languages

Atom 1.0 Feed RSS 1.0 Feed RSS 2.0 Feed

Learning Lab

Web Services with AppleScript and Perl
Pages: 1, 2, 3

Creating the Perl SOAP Server

Before you try to run the Perl SOAP server you will need a few modules: XML::Parser, which requires the Expat parser, and SOAP::Lite, XML::RSS, and LWP::Simple. If you don't have Expat, start by installing that with the instructions found here. To install the other modules, just open a Terminal window and type sudo perl -MCPAN -eshell (authenticate if necessary), then install XML::Parser, install SOAP::Lite, install XML::RSS, and install LWP::Simple. Answer any necessary questions and be sure to say "yes" when asked if you want to install SOAP::Transport::HTTP. The CPAN installer may ask you for some configuration information if this is your first time running it. You can get details on the whole process by entering perldoc CPAN at a prompt.

Take a look at the Perl SOAP server. You can see the entire script here. I've included line numbers to make it easier to read. Here's how I begin every Perl program I write:

=2= use strict;
=3= $|++;

Line 1 turns on Perl warnings and identifies this as a Perl script by providing the path to the Perl compiler/interpreter on this system. Line 2 enables three very useful compiler restrictions, demanding that I declare my variables (so a typo doesn't ruin a good day), use only hard references (as opposed to accidentally de-referencing a text string), and forgo bare-words-as-text-strings. Line 3 causes every print to be flushed, effectively giving me output as soon as I ask for it, not buffered for efficiency until later.

Line 5 pulls in the SOAP::Transport::HTTP module, part of the SOAP::Lite distribution, found in the CPAN:

=5=	use SOAP::Transport::HTTP;

Lines 7 through 9 create a new SOAP::Transport::HTTP::Daemon object, with enough information to become a SOAP endpoint:

=7= my $daemon = SOAP::Transport::HTTP::Daemon
=8=   -> new (LocalAddr => 'localhost', LocalPort => 8001, Reuse => 1)
=9=   -> dispatch_to ('Server');

I defined the host for binding the server socket as localhost which keeps non-local processes from connecting to the socket. The port number is arbitrarily chosen as 8001. Anything between 7000 and 65535 is usually OK as long as no other process is using it.

The port is declared with a Reuse of true (1), so I can stop the server and immediately restart it. Normally, a server port is quarantined for a period of time after exit to ensure that stray TCP packets don't end up in the inbox of a new server, but we're in control here, so that protection isn't necessary.

The dispatch_to method in line 9 directs incoming methods to be found within the Server class, defined later in this same file.

Line 11 tells us that the server is up, and reminds us of the settings for the server:

=11=	print "Contact SOAP server at ", $daemon->url, "\n";

If you leave out the LocalPort parameter (see Line 8), the server can fire up on a system-selected available port, and the message would tell us what to edit in to the client software to contact this server.

Line 12 puts this server into an event-based loop, handling each incoming request until the program is aborted. The program never returns from this method invocation:

=12=	$daemon->handle;

As each SOAP request comes in on the HTTP server port, it is parsed into the method name and parameters. The methods are expected to be found in the Server class, and this is defined in lines 14 to 32:

=14= BEGIN {
=15=   package Server;
=16=   use base qw(SOAP::Server::Parameters);
=18=   use XML::RSS;
=19=   use LWP::Simple;
=21=   sub fetch_headlines {
=22=     my $p = pop->method;
=23=     my $uri = $p->{uri} or die "missing uri parameter";
=24=     my $rdf = get $uri or die "Can't fetch rdf";
=25=     (my $rss = XML::RSS->new)->parse($rdf); # might die, we don't care
=27=     return [map {
=28=       my $item = $_;
=29=       +{ title => $item->Web Services with AppleScript and Perl, 
           link => $item->{link} };
=30=     } @{$rss->{items}}];
=31=   }
=32= }

Note that we've wrapped this in a BEGIN block to emulate a use Server compilation directive.

Line 15 starts the Server package, effective until the end of the block in which it was declared (which happens to be the end of the BEGIN block).

Line 16 pulls in the SOAP::Server::Parameter class, and also declares the Server class to be inherited from SOAP::Server::Parameter. The effect is that each method is handed an additional parameter at the end of the argument list of type SOAP::SOM, which contains methods to access named parameters instead of just positional parameters.

Lines 18 and 19 pull in the XML::RSS and LWP::Simple modules.

Related Reading

Perl & LWP
By Sean M. Burke

Lines 21 to 31 define the fetch_methods SOAP method. In Line 22, the parameters' hashref is obtained by popping the argument list and calling method on the resulting object, lifted directly from the SOAP::Lite manpage. (Kudos to the many SOAP::Lite examples in the documentation and distribution.)

Line 23 extracts the uri parameter from the hashref. If we die here, the SOAP server will capture this abnormal exit and automatically return a SOAP fault back to the requester. AppleScript's default response was to pop up a dialog box with the error text, so this seemed perfectly acceptable for a simple example, but I imagine I'd want to wrap that into a AppleScript try block for more robust handling. Or perhaps trap this on the Perl server side and return a default response for an invalid input.

Line 24 fetches the RSS file at the given URI. The get subroutine is imported from LWP::Simple, taking a single URI and returning the contents of the page, or undef if it fails. Again, a death here will simply generate a SOAP fault, triggering an error on the AppleScript side.

Line 25 takes the resulting RSS content and parses it into an XML::RSS object, and then Lines 27 to 31 return the response. Basically, we're constructing an arrayref to a list of elements, and each element will be a hashref to a hash containing a headline and its URL. We'll get that by iterating over the list of items (Line 30), extracting each title and link and creating a hashref from that (Line 29), and then wrapping the whole thing into an anonymous array constructor (the brackets in Lines 27 and 30). The beauty of this is that the SOAP::Lite methods figure out how to bundle this appropriately into a list and records for AppleScript to process properly.

And that's it on the server side. Launch this script, and you're greeted with:

Contact SOAP server at http://localhost:8001/

...and your server is up and running at the indicated address.

Pages: 1, 2, 3

Next Pagearrow