8.3 Transports with Server and Client


Not all the transport layers examined in this chapter provide both a server and client class for application use. This section focuses on the transports that do provide both.

8.3.1 Using SOAP::Transport::TCP

Even with the proliferation of lightweight HTTP implementations , there are cases in which an application may need to allow for simple, direct TCP/IP communication instead of (or in addition to) HTTP. Such cases might include embedded devices with simple TCP/IP stacks, or cases in which the server just doesn't want to provide (or support) full HTTP support.

8.3.1.1 SOAP::Transport::TCP::Server

Using the SOAP::Transport::TCP::Server class is as easy as using the HTTP classes. The constructor method uses the IO::Socket class family to create and manage the sockets the object will ultimately use. Binding to a hostname and port is done in the same fashion as for the HTTP classes. Additionally, the handle method of the class acts as an accept loop, waiting infinitely for new connections, until interrupted .

Example 8-3 is the daemon application from the previous chapter except it includes this class in place of an HTTP variant.

Example 8-3. WishListCustomer::SOAP daemon using TCP
 #!/usr/bin/perl     use strict;     use SOAP::Transport::TCP; use WishListCustomer::SOAP; use WishListCustomer::Transport         'SOAP::Transport::TCP::Server';     my $port = pop(@ARGV)  9000; my $host = shift(@ARGV)  'localhost';     WishListCustomer::Transport     ->new(LocalAddr => $host, LocalPort => $port,           Listen => 5, Reuse => 1)     ->dispatch_with({ 'urn:/WishListCustomer' =>                       'WishListCustomer::SOAP' })     ->objects_by_reference('WishListCustomer::SOAP')     ->handle;     exit; 

The flexibility of the SOAP::Lite transport layer means that a change of only a few lines switched that same basic application from one protocol to another.

8.3.1.2 SOAP::Transport::TCP::Client

Of course, the client that communicates with this server doesn't explicitly load the corresponding client class. That is handled by the SOAP::Lite module for the client.

The transport classes that provide clients also provide bindings for nonstandard protocol identifiers in the URI strings that the clients use. For the TCP transport, these strings are expected to start with the sequence, tcp:// . When SOAP::Lite sees this in an endpoint, it knows to load the TCP-oriented module, just as http:// (and https :// ) load the HTTP code.

The client shown in Example 8-4 defaults the endpoint to a TCP binding but can just as easily use one of the other transports without changing any of the code itself.

Example 8-4. A generic, non-HTTP client application
 #!/usr/bin/perl     use strict;     use WishListCustomer::Client;     my ($user, $passwd) = (shift, shift); die "USAGE: 
 #!/usr/bin/perl use strict; use WishListCustomer::Client; my ($user, $passwd) = (shift, shift); die "USAGE: $0 username passwd [ endpoint ]\n" unless ($user and $passwd); my $endpoint = shift  'tcp://localhost:9000'; my $soap = WishListCustomer::Client ->uri('urn:/WishListCustomer') ->proxy($endpoint); # Set the authentication credentials $soap->setAuth($ user , $passwd); my $result = $soap->Wishlist; if ($result->fault) { die "$0: Operation failed: " . $result->faultstring; } my $books = $result->result; format = @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>> $result->{title}, $result->{us_price} @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>>>>>>>>>>>> $result->{authors}, $result->{isbn} . for ( sort { $a->{title} cmp $b->{title} } @$books) { $result = $soap->GetBook($_->{isbn}); # Quietly skip books that cause faults next if ($result->fault); $result = $result->result; write; } exit; 
username passwd [ endpoint ]\n" unless ($user and $passwd); my $endpoint = shift 'tcp://localhost:9000'; my $soap = WishListCustomer::Client ->uri('urn:/WishListCustomer') ->proxy($endpoint); # Set the authentication credentials $soap->setAuth($user, $passwd); my $result = $soap->Wishlist; if ($result->fault) { die "
 #!/usr/bin/perl use strict; use WishListCustomer::Client; my ($user, $passwd) = (shift, shift); die "USAGE: $0 username passwd [ endpoint ]\n" unless ($user and $passwd); my $endpoint = shift  'tcp://localhost:9000'; my $soap = WishListCustomer::Client ->uri('urn:/WishListCustomer') ->proxy($endpoint); # Set the authentication credentials $soap->setAuth($ user , $passwd); my $result = $soap->Wishlist; if ($result->fault) { die "$0: Operation failed: " . $result->faultstring; } my $books = $result->result; format = @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>> $result->{title}, $result->{us_price} @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>>>>>>>>>>>> $result->{authors}, $result->{isbn} . for ( sort { $a->{title} cmp $b->{title} } @$books) { $result = $soap->GetBook($_->{isbn}); # Quietly skip books that cause faults next if ($result->fault); $result = $result->result; write; } exit; 
: Operation failed: " . $result->faultstring; } my $books = $result->result; format = @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>> $result->{title}, $result->{us_price} @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>>>>>>>>>>>> $result->{authors}, $result->{isbn} . for (sort { $a->{title} cmp $b->{title} } @$books) { $result = $soap->GetBook($_->{isbn}); # Quietly skip books that cause faults next if ($result->fault); $result = $result->result; write; } exit;

The code is recognizable as the same client from the previous chapter. Gone is the code for the HTTP::Cookies class, and in its place is the SOAP::Lite subclass and a single call to a method called setAuth . The default value for $endpoint is the only indication of what protocol the client is presuming to eventually use.

8.3.2 Using SOAP::Transport::JABBER

The Jabber messaging protocol is a distributed messaging system that uses XML as the core means of data communication and exchange. As a topic, it is complex enough to require its own book. [1] Fortunately, using Jabber as a transport mechanism doesn't require an indepth understanding of the protocol itself. The SOAP::Transport::JABBER module handles the majority of the work. Jabber services can be designed to connect otherwise unrelated components (services that aren't always related to instant messaging).

[1] See Programming Jabber , by D.J. Adams (O'Reilly).

Jabber can function as a bridge between disparate protocols, such as AOL Instant Messaging and Internet Relay Chat. Jabber offers store-and-forward, with servers that retain messages until an endpoint reconnects. SOAP services can be introduced into an environment like this quite easily, for tasks ranging from personal information management (calendar services, reminders) to information retrieval. As Jabber clients grow in number and proliferate, the application of SOAP as a basis for providing services as content is likely to grow as well.

To use Jabber with SOAP::Lite , you must have the CPAN module Net::Jabber installed. Because of the changes in Net::Jabber interface, the current version of SOAP::Lite (0.55) doesn't work with the latest version of Net::Jabber , so users may need to downgrade to Version 1.0021 of Net::Jabber . JABBER transport will be synchronized in a future version of SOAP::Lite . There is no need to be running a Jabber daemon on the same host providing the SOAP service via Jabber, but that host will have to be able to contact a Jabber server. Starting up a server process also requires that a Jabber profile be available for use.

8.3.2.1 Creating a Jabber-based server

The structure of the Jabber-based server isn't different from the TCP server shown earlier. The information that the server class needs to establish a connection with the Jabber server and create the proper interfaces is provided by instantiating the new object with a URI-like string that begins with the sequence, jabber:// . Later, the client will use the same string to connect to the server. The server also differs in that the handle method isn't itself an endless loop like the TCP or HTTP classes provide. In Example 8-5, the daemon polls the Jabber server on a 10-second interval.

Example 8-5. The Jabber-based daemon
 #!/usr/bin/perl     use strict;     use SOAP::Transport::JABBER; use WishListCustomer::SOAP; use WishListCustomer::Transport         'SOAP::Transport::JABBER::Server';     my ($user, $passwd, $host, $port) = @ARGV; $host = 'jabber.org' unless $host; $port = 5222         unless $port; my $jabber_url = "jabber://$user:$passwd\@$host:$port";     $server = WishListCustomer::Transport     ->new($jabber_url)     ->dispatch_with({ 'urn:/WishListCustomer' =>                       'WishListCustomer::SOAP' })     ->objects_by_reference('WishListCustomer::SOAP');     while (1) {     $server->handle;     sleep 10; }     exit; 

In the example, the command line is expected to provide the user ID, password, Jabber server, and port. Port 5222 is the current standard port for the Jabber service, and the host jabber.org is the most-used server, making it a logical default.

8.3.2.2 The Jabber-based client

Connecting to the Jabber-based SOAP server can be done with the same client used earlier for the TCP server, simply by providing a different endpoint:

 % perl client-general rjray bad_password \        jabber://rjray:xxx@jabber.org:5222 

The SOAP::Transport::JABBER module hooks into the URI class, which is one of the supporting elements of LWP itself. The TCP module does this, as well. This not only provides the basis for treating jabber:// (and tcp:// ) with the same consideration as http:// or ftp:// , it also lets the module use the methods of the URI class to extract data from the string, without having to explicitly provide the regular expressions to do so.

The architecture of the SOAP::Transport::JABBER is interesting in itself. Both client and server classes are built on the client class of the Net::Jabber package, Net::Jabber::Client . This is because even though the server class represents a SOAP server instance, it is still a client from the Jabber perspective. The client and server on the SOAP level are essentially using the Jabber server as an intermediary for their conversation. This fits with the Jabber concept of agents , the basic structures on which Jabber is designed. Agents handle the messages between different connections and interfaces, usually between chat clients and chat servers. But Jabber isn't limited to that, which is what SOAP takes advantage of.

8.3.3 Using SOAP::Transport::MQ

The MQSeries software is a commercial package from IBM that forms the communications basis for a range of applications. The software can be obtained for evaluation or purchase through IBM's web site, http://www.ibm.com/software/ts/mqseries/.

As with Jabber and TCP, the module registers a new type of URI scheme that can be recognized by a client request for an endpoint whose string starts with the sequence mq:// . The form and content of the URI string has more in common with the Jabber form than the TCP or HTTP forms. The strings that specify the MQ connections must provide information on the queue manager, and the channels for sending and receiving messages (see Example 8-6).

Example 8-6. The MQ server daemon
 #!/usr/bin/perl     use strict;     use SOAP::Transport::MQ; use WishListCustomer::SOAP; use WishListCustomer::Transport         'SOAP::Transport::MQ::Server';     my ($chan, $mgr, $reqest, $reply, $host, $port) = @ARGV; $host = 'localhost' unless $host; $port = 9000        unless $port; die "USAGE: 
 #!/usr/bin/perl use strict; use SOAP::Transport::MQ; use WishListCustomer::SOAP; use WishListCustomer::Transport 'SOAP::Transport::MQ::Server'; my ($chan, $mgr, $reqest, $reply, $host, $port) = @ARGV; $host = 'localhost' unless $host; $port = 9000 unless $port; die "USAGE: $0 channel manager request_queue reply_queue " . '[ host port ]' unless ($chan and $mgr and $request and $reply); my $mq_url = "mq://$host:$port?Channel=$chan;" . "QueueManager=$mgr;RequestQueue=$request;" . "ReplyQueue=$reply"; my $server = WishListCustomer::Transport ->new($mq_url) ->dispatch_with({ 'urn:/WishListCustomer' => 'WishListCustomer::SOAP' }) ->objects_by_reference('WishListCustomer::SOAP'); do { $server->handle } while sleep 1; exit; 
channel manager request_queue reply_queue " . '[ host port ]' unless ($chan and $mgr and $request and $reply); my $mq_url = "mq://$host:$port?Channel=$chan;" . "QueueManager=$mgr;RequestQueue=$request;" . "ReplyQueue=$reply"; my $server = WishListCustomer::Transport ->new($mq_url) ->dispatch_with({ 'urn:/WishListCustomer' => 'WishListCustomer::SOAP' }) ->objects_by_reference('WishListCustomer::SOAP'); do { $server->handle } while sleep 1; exit;

A client would be expected to use the same URI-style string when connecting to the server. The client from earlier in this chapter would be sufficient, if given the connection string on the command line.

8.3.4 Using POP3 and MAILTO

These are actually two different modules that aren't directly related or even intentionally designed to work together. However, they share a common underlying element: electronic mail. If a developer wanted to, she could develop a complete client/server model around Simple Mail Transport Protocol (SMTP), one in which communications are wrapped within ordinary email messages. A client connects to a server by sending an email to the proper address, and the client's own email address is used by the server to send back the response. Such a system can even be built using the server and client components from this section.

The examples presented in this section demonstrate one-way communications. The client class built upon the SOAP::Transport::MAILTO module sends requests without expecting a reply. Its concept of success is instead based on whether the message was accepted by the target SMTP server. Likewise, the server built upon SOAP::Transport::POP3 reads requests from a POP3 server and processes them, without attempting to send the results of the operations to any client.

At first glance, the usefulness of a model such as this may not be obvious. There are many types of applications, however, in which a client may not need a reply, or in which a server would not be expected to provide one. Some examples of these applications include inventory updates or position reporting from devices attached to global positioning system (GPS) gear. Operations that are generally one-sided in nature, such as periodic updates, can be written more efficiently if there is no need to wait for or receive a reply that is going to be discarded.

Some of this can be said to overlap with other, newer technologies like Jabber, which also provides a kind of "store and forward" management. Of course, the design drives the choice, and SMTP is often more readily available than Jabber.

8.3.4.1 SOAP::Transport::POP3::Server

The server shown in Example 8-7 is best associated with the Jabber server presented earlier, just as the MQ server is also a close derivative. The application requires that a host be provided, as well as a username and password for connecting to the POP3 server at the specified host.

Example 8-7. A POP3-based server
 #!/usr/bin/perl     use strict;     use SOAP::Transport::TCP; use WishListCustomer::SOAP; use WishListCustomer::Transport         'SOAP::Transport::POP3::Server';     my ($user, $passwd, $host) = @ARGV; $host = 'localhost'; my $pop3_url = "pop://$user:$passwd\@$host";     WishListCustomer::Transport     ->new($pop3_url)     ->dispatch_with({ 'urn:/WishListCustomer' =>                       'WishListCustomer::SOAP' })     ->objects_by_reference('WishListCustomer::SOAP');     do { $server->handle ) while sleep 10;     exit; 

As the Jabber and MQ modules do, the POP3 module defines pop:// as a type of URI specification, allowing the server code to use the URI module to extract the data it needs from the string passed to the constructor. Note also that this server class must provide its own looping mechanism around the handle method, as Jabber and MQ do.

8.3.4.2 SOAP::Transport::MAILTO::Client

This client class is by no means the only way to send messages to the POP3-based server. Any email client can be used, or a mail-transfer agent (MTA) such as sendmail or postfix , could be used directly. As long as the client can verify a successful delivery and craft a valid SOAP message the server understands, the client can be used.

To demonstrate the SOAP::Transport::MAILTO module, we will create such a client. It can, in fact, be used through the same generic client presented earlier in this chapter, by specifying a suitable endpoint:

 mailto:soap.server@soaplite.com?From=you@home.com 

An endpoint such as this defaults to using the system's sendmail MTA. Additionally, it's important to point out that the default client expects to format the output from the remote method it calls, and in this case there would be no such output.

Instead, we'll make a different client. This client triggers the purchase of one or more books on the user's wish list. The script expects the user's ID and password for the SOAP server as the first two arguments, followed by the email address for the SOAP POP3 listener, and one or more ISBNs of books to purchase. The code is shown in Example 8-8.

Example 8-8. An email client using SOAP::Transport::MAILTO
 #!/usr/bin/perl     use strict;     use WishListCustomer::Client; use Sys::Hostname 'hostname';     my ($user, $passwd, $mailto) = (shift, shift, shift); die "USAGE: 
 #!/usr/bin/perl use strict; use WishListCustomer::Client; use Sys::Hostname 'hostname'; my ($user, $passwd, $mailto) = (shift, shift, shift); die "USAGE: $0 username passwd endpoint ISBN [ ISBN... ]\n" unless ($user and $passwd and $mailto and @ARGV); my $hostname = eval { hostname }; $hostname = 'localhost' if $@; my $endpoint = sprintf("maito:%s?From=%s&Subject=SOAP", $mailto, "$user\@$hostname"); my $soap = WishListCustomer::Client ->uri('urn:/WishListCustomer') ->proxy($endpoint); $soap->setAuth($user, $passwd); my $result = $soap->PurchaseBooks(\@ARGV); if ($result->fault) { die "$0: Operation failed: " . $result->faultstring; } else { print "Request sent\n"; } exit; 
username passwd endpoint ISBN [ ISBN... ]\n" unless ($user and $passwd and $mailto and @ARGV); my $hostname = eval { hostname }; $hostname = 'localhost' if $@; my $endpoint = sprintf("maito:%s?From=%s&Subject=SOAP", $mailto, "$user\@$hostname"); my $soap = WishListCustomer::Client ->uri('urn:/WishListCustomer') ->proxy($endpoint); $soap->setAuth($user, $passwd); my $result = $soap->PurchaseBooks(\@ARGV); if ($result->fault) { die "
 #!/usr/bin/perl use strict; use WishListCustomer::Client; use Sys::Hostname 'hostname'; my ($user, $passwd, $mailto) = (shift, shift, shift); die "USAGE: $0 username passwd endpoint ISBN [ ISBN... ]\n" unless ($user and $passwd and $mailto and @ARGV); my $hostname = eval { hostname }; $hostname = 'localhost' if $@; my $endpoint = sprintf("maito:%s?From=%s&Subject=SOAP", $mailto, "$user\@$hostname"); my $soap = WishListCustomer::Client ->uri('urn:/WishListCustomer') ->proxy($endpoint); $soap->setAuth($user, $passwd); my $result = $soap->PurchaseBooks(\@ARGV); if ($result->fault) { die "$0: Operation failed: " . $result->faultstring; } else { print "Request sent\n"; } exit; 
: Operation failed: " . $result->faultstring; } else { print "Request sent\n"; } exit;

As it happens, the PurchaseBooks method in the sample code doesn't actually do anything except remove the books from the list and update the user record. An actual enterprise-level application interface would have defined behaviors for cases such as keys being passed for books that aren't in the wish list, server errors in order completion, and so forth. (Not to mention the obvious: not enough room on the credit card for all the books!)

As you may have noticed, the SOAP::Transport::MAILTO module isn't explicitly loaded because the SOAP::Lite client object handles it automatically. The WishListCustomer::Client class is used, though, because the request still needs SOAP headers for authentication.



Programming Web Services with Perl
Programming Web Services with Perl
ISBN: 0596002068
EAN: 2147483647
Year: 2000
Pages: 123

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net