Enterprise Software

An introduction to SOAP

Simple Object Access Protocol (SOAP) exchanges application data over HTTP in XML encoding. Find out why SOAP and Web services have huge implications for distributed computing.


By James Scheinblum

Dynamically generated HTML content works fine in Web browsers, but it presents a nightmare for anyone trying to utilize that data with other programs. For example, you can easily view an auction site in a browser, but an application would require a complex HTML parser to read your bid's status from the same site. Worse, you would need a different parser to track a different auction site, and the simplest redesign of either site could throw off your program.

Web services solve this problem with a consistent and easy method for accessing online information. As more online services are offered, new applications can be built to interact directly with them. For example, that Web-based auction site could let you write software that automatically updates your bids based on the status of a bid on a different auction site. Or you could edit your Web log with your favorite word processor once the site and application were both speaking the same language. Web services could potentially create a whole new type of Web.

A protocol created by Microsoft, DevelopMentor, and Userland Software and backed by companies that include IBM, Lotus, and Compaq gives a big push toward that vision. Simple Object Access Protocol (SOAP) exchanges application data over HTTP in XML encoding. Because HTTP is ubiquitous and XML parsers are widely available, SOAP can be easily adopted and quickly developed. The trade-off is speed; SOAP won't replace lower-level technologies, but it works where interoperability is paramount.

SOAP toolkits are already available for most popular development environments, including Python, Java, Visual Basic, and, of course, Perl. Programmers experienced with remote procedure call APIs such as Java's RMI or Microsoft's COM+ will find the SOAP toolkits familiar.

In this article, we'll look at how to use Perl to both provide Web services and to build applications on top of SOAP servers. Because Perl is available on many platforms, Perl-based SOAP services are an easy way to tie diverse computing environments together by using a common high-level format. Soon you'll be exploiting this powerful new technology in ways that you never imagined.

Get started with SOAP
First you'll want to download the SOAP::Lite toolkit, a Perl module by Paul Kulchenko. Once you've installed it and any requisite libraries, you're ready to create a SOAP service.

Before we create a SOAP listener, we need to understand how SOAP handles requests. On the server side, SOAP matches the incoming request to a Perl object by reading the class name and function name out of a SOAP envelope, the XML file sent from the client. For the following examples, we'll use a class World that has two functions, HelloWorld and GoodByeWorld.
package World;
 
sub new {
   bless {}, shift;
};
 
sub HelloWorld {
   my ($self) = @_;
 
   return "Hello World\n";
};
 
sub GoodByeWorld {
   my ($self,$adjective) = @_;
   return "Goodbye $adjective World\n";
}
1;

Though you may never have to actually see a SOAP request, it can help to know what's going on behind the curtain, especially when it comes time to debug your requests. Here is a very simple SOAP request to the World class calling the HelloWorld function.
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<namesp1:HelloWorld xmlns:namesp1="World"/>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

You can see where the envelope and its body begin in the XML; the HelloWorld function is called inside the body. The namespace is the name of the function to be called on the server, and the XML namespace is the name of the class from which the function will be called. In this case, we're calling the function HelloWorld in the class World. When GoodByeWorld is called, everything pretty much looks the same, except that the SOAP body passes a parameter:
<SOAP-ENV:Body>
<namesp2:GoodByeWorld xmlns:namesp2="World">
<c-gensym9 xsi:type="xsd:string">cruel</c-gensym9>
</namesp2:GoodByeWorld>
</SOAP-ENV:Body>

If this is confusing, don't worry: The toolkit will hide the actual SOAP transaction from you.

Serve up SOAP
Because SOAP runs over HTTP, we can take advantage of Perl's existing relationship with Web servers. First, we create a simple CGI script to pass the request to our class and return the SOAP response to the client. Make sure that your server supports CGI scripts and that you make the script executable. Here's a CGI script that receives a SOAP request for the World class from the previous page:
#!/usr/bin/perl
 
use SOAP::Transport::HTTP;
use World;
 
SOAP::Transport::HTTP::CGI
  -> dispatch_to('World')
  -> handle;

That's all; the SOAP module takes care of the rest. Create a CGI script like the one above for each SOAP class that you wish to call. You only need to make sure that the first dispatch_to argument is the name of the class you wish to bind. The class must behave like any other, with a new method $self as the first argument to all method calls. Create the module with the package name at the top and save it with the .pm suffix.

SOAP::Lite can also dynamically load modules based on a requested name, a capability that lets you create one CGI script that loads any module from a specified directory. It's less secure and gives you less control over which modules are loaded, but a directory of SOAP modules can be more convenient to manage. To dynamically load modules, remove the use statement and make the SOAP module directory the first dispatch_to argument:
#!/usr/bin/perl
 
use SOAP::Transport::HTTP;
 
SOAP::Transport::HTTP::CGI
  -> dispatch_to('/home/httpd/soap_modules/')
  -> handle;

Create a SOAP client
A SOAP client is just as easy to create as a SOAP server. You just have to load the SOAP::Lite module and know some information about the remote service (or endpoint). In fact, gathering that information is the most difficult part. You'll need to know the endpoint URL, the method namespace URI, and the remote method's names and parameters. Once you have this information you can create a SOAP client. Let's make one for the server we just created.
use SOAP::Lite;
 
my $s = SOAP::Lite
   ->uri('World')
   ->proxy('http://soapserver.mycompany.com/soap/soapserver.cgi')
   ->HelloWorld();
print $s->result();
 
my $s = SOAP::Lite
   ->uri('World')
   ->proxy('http://soapserver.mycompany.com/soap/soapserver.cgi')
   ->GoodByeWorld("cruel");
print $s->result();

In this example, uri accepts the name of the class we're accessing on the remote server. Not every SOAP implementation uses this convention, however, so you'll have to find out what the URI should be. The proxy method accepts the URL of the endpoint SOAP handler. You must provide URI and proxy for every connection in order for your SOAP request to be completed. The last function is the method that you wish to call; here we call HelloWorld() and GoodByeWorld("cruel") and retrieve the result via the result function. Note that even though both methods are from World, we've instantiated the class twice over two separate transactions. This means that the state of one instance is unknown by the other, so you can't (for example) set a class value with one method and retrieve it with another.

A SOAP service may require a specific SOAPAction field, a parameter sent in the HTTP header. You can set it with an on_action method that overrides the default handler. Here's an example:
my $s = SOAP::Lite
   ->uri('World')
   ->on_action(sub { return "/Action#Action" })
   ->proxy('http://soapserver.mycompany.com/soap/soapserver.cgi')
   ->GoodByeWorld("cruel");
print $s->result();

Advanced SOAP options
If you're curious or need debugging information, SOAP::Lite gives you the option of viewing the client-server transactions in great detail. This verbose output includes how the request is processed and is very handy for tracking down errors. Most errors result from a mismatched URI or proxy address and will show up in the text of the SOAP packet. Add +trace => all after use SOAP::Lite to send all debugging output to the STDERR output stream.
use SOAP::Lite +trace => all;
 
my $s = SOAP::Lite
   ->uri('World')
   ->proxy('http://soapserver.mycompany.com/soap/soapserver.cgi')
   ->GoodByeWorld("cruel");
print $s->result();

SOAP::Lite also supports a feature called Autodispatch that lets you bind remote methods into the execution of your program. When Autodispatch is enabled, methods that aren't locally available are automatically sent to a remote server for execution there. Autodispatch makes using SOAP a transparent process; once you establish the remote server, you can write your script without further SOAP references. Enable Autodispatch with the +autodispatch flag:
use SOAP::Lite +autodispatch =>
   uri=>"World",
   proxy=>'http://soapserver.mycompany.com/soap/soapserver.cgi';
 
print HelloWorld();
print GoodByeWorld("sweet");

Because they aren't locally defined, the HelloWorld and GoodByeWorld functions are automatically sent to the remote server named in proxy for execution. Using Autodispatch is like inheriting from another class, so be sure to define a function with the same name as the one you're trying to call on the remote server.

Conclusion
As you can see, using SOAP with Perl is easy. It's simple to set up services and make requests. Now you can offer new Web services or take advantage of other SOAP servers to bring more value to your site.

Editor's Picks