Prior to Mac OS X, Perl scripters were forced to telnet to a shell prompt on a remote Unix box to get full access to their favorite language. But now, with its BSD underpinnings and a terminal window, Mac OS X has the same version of Perl you’d find on any Unix system. Of course, Apple’s own scripting language, AppleScript, has been around for years. And a recent release of the language added the ability to use SOAP services, which makes it a nice complement to Perl. You can’t create a SOAP web service with AppleScript, but you can have it act as a SOAP web client and use Perl to create the service. So it becomes a simple matter of passing structured data between Perl and AppleScript, with the Perl service running either on the same machine or remotely. This is much nicer than firing off a new Perl process with the A Perl-built SOAP web service also has full access to the Mac OS X platform from the Unix perspective and can make use of the thousands of pre-written routines from the Comprehensive Perl Archive Network (CPAN). In fact, a SOAP web service in Perl can even use other SOAP web services to repackage, filter, or summarize data. (Beware that a SOAP web service runs with the full permissions of the user who started the process. Think twice before letting people connect to your web service remotely. Think ten times before starting it as root or an admin user.) Creating a Simple ApplicationTo show off the connection between an AppleScript and a Perl server, I created a simple task to keep the mechanics to a minimum: fetching headlines from a news site. Sites that provide news items frequently include a Rich Site Summary (RSS) file with headlines and links to the detailed stories. These RSS feeds can then be used by other sites to provide a sidebar of relevant stories. The AppleScript can drive the process, but it can’t fetch a URL directly or parse the resulting XML, so I delegated that to the Perl program. The AppleScript applet will give an RSS URL to the Perl program (running as a daemon) via the SOAP protocol, and the Perl script will take care of the fetching, RSS parsing, and repackaging of the essential information. Then the AppleScript will reformat the data as HTML and hand it to a browser to be viewed. There are probably better ways of doing this. For example, it would be trivial to have Perl simply return the repackaged HTML, or put the HTML into a file and return the name of that temp file or even launch the browser directly. But this approach allows me to return the structured data in a nice way. And by keeping the Perl side to a minimum, AppleScripters can use what they know to provide alternate views of the data, letting the Perl hackers concentrate on fetching the data. Creating the Perl SOAP ServerBefore 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 using Fink, assuming you already have it installed, open a Terminal window and type 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: =1= #!/usr/bin/perl -w =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 Line 5 pulls in the =5= use SOAP::Transport::HTTP; Lines 7 through 9 create a new =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 The port is declared with a The 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 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 =14= BEGIN { =15= package Server; =16= use base qw(SOAP::Server::Parameters); =17= =18= use XML::RSS; =19= use LWP::Simple; =20= =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 =26= =27= return [map { =28= my $item = $_; =29= +{ title => $item->{title}, link => $item->{link} }; =30= } @{$rss->{items}}]; =31= } =32= } Note that we’ve wrapped this in a Line 15 starts the Line 16 pulls in the Lines 18 and 19 pull in the Lines 21 to 31 define the Line 23 extracts the Line 24 fetches the RSS file at the given URI. The Line 25 takes the resulting RSS content and parses it into an 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. Creating the SOAP Client in AppleScirptNow, for the other side: the AppleScript client that will contact this server. You can see the entire script, along with line numbers, here. I must caution the reader here: while this code is workable, it is not as robust as it might be. It is only a simple example of using SOAP services with AppleScript. For more complete information, see the techpubs documentation. Lines 1 to 5 define the subroutine that lets you go from AppleScript to a Perl SOAP server. =1= on fetch_SOAP_lite(endpoint, method, p) =2= using terms from application "http://www.apple.com/placebo" =3= tell application endpoint to return call soap ¬ {method name:method, method namespace uri:endpoint, ¬ SOAPAction:(endpoint & "#" & method), parameters:p} =4= end using terms from =5= end fetch_SOAP_lite The The The The Line 3 performs the SOAP call, connects to the proper endpoint, selects the right method, and passes the right parameters. The response is returned, unless a SOAP fault or exception is thrown, in which case the appropriate exception propagates outward. Lines 7 through 9 define a =7= on fetch_headlines(uri) =8= return fetch_SOAP_lite("http://localhost:8001/Server", ¬ "fetch_headlines", {uri:uri}) =9= end fetch_headlines Given a URI parameter, I pass that as the named Line 11 invokes the =11= set response to fetch_headlines("http://www.perl.com/pace/perlnews.rdf") I’m using the URI for the RSS headlines of O’Reilly’s Perl.com web site, getting the latest information in the Perl world. Lines 12 to 16 format the output of the response as a very minimal HTML page: =12= set output to "<ul>" & return =13= repeat with i in response =14= set output to output & "<li><a href=\"" ¬ & (i's link) & "\">" & (i's title) ¬ & "</a></li>" & return =15= end repeat =16= set output to output & "</ul>" & return A bullet list is created in Now that I’ve got the HTML, I need to write it to a file so a browser can read it. I’ll select the name using the built-in =17= set output_file to (path to temporary items folder as string) ¬ & "fetch_headlines.html" Line 18 invokes the =18= write_to_file(output, output_file, false) Finally, Line 19 tells the Finder to open the file: =19= tell application "Finder" to open output_file As this file ends in Lines 21 to 38 were lifted from a script found created by the AppleScript team: =21= -- borrowed code =22= on write_to_file(this_data, target_file, append_data) =23= try =24= set the target_file to the target_file as text =25= set the open_target_file to å =26= open for access file target_file with write permission =27= if append_data is false then å =28= set eof of the open_target_file to 0 =29= write this_data to the open_target_file starting at eof =30= close access the open_target_file =31= return true =32= on error =33= try =34= close access file target_file =35= end try =36= return false =37= end try =38= end write_to_file This takes the data in the first parameter and writes it (or appends it, depending on the third parameter) to the file named in the second parameter. I’ll just treat it as a black box and give it to you in the same manner. One of the nice things about AppleScript is that it’s been around long enough to have lots of snippets like this available online. ConclusionAnd there you have it. Stick that into a compiled script using something like Script Editor, and then run it. The application will contact the Perl SOAP server to get the current headlines from Perl.com. The data then gets reformatted by AppleScript into a nice bulleted list, and your browser is automatically opened to that page. If you see a headline you like, click on it and get the whole story. In real life, I’d probably have the AppleScript pop up a dialog box asking me to choose from a list of sites instead of just hard wiring the one name into the script. In fact, you could build it as a full Cocoa application using AppleScript Studio and have the application itself show the headlines and make clickable links, which then get opened by a browser. And with an Now that you know how easy it is to create a SOAP web service with Perl, you can provide services for your local AppleScripts and access much more than AppleScript can do on its own. |