Perl SOAP::Lite Example


Recently I was tasked with integrating systemA with systemB. Could I be more vague? Yes, probably so. Anyway, systemB is a vendor’s system which is implemented with both a SOAP and a REST API. REST is inherently easier to implement given its use of URLs. However, I chose to use SOAP, because I figured that in my field I was far more likely to encounter a vendor who only implemented SOAP than I was a vendor who only implemented REST. That decided I set off down a long and SOAPy road…

So, a quick primer. SOAP stands for Simple Object Access Protocol it utilizes XML messages to exchange data between a client and a server. So lets jump into the code.

#!/usr/bin/perl -w
use strict;
use warnings;
use SOAP::Lite;

# Set SOAP username
my $USER = "user";

# Set SOAP password
my $PASSWORD = "password";

# Set SOAP API URL
my $SERVICE_LOC = 'https://some.soapy-service.somedomain';

# XML namespace
my $SERVICE_NS = 'http://name.space.domain/widget';

# we use this lat
# The username and password are set by overriding the
# SOAP::Transport::HTTP::Client::get_basic_credentials method
#### Authentication

sub SOAP::Transport::HTTP::Client::get_basic_credentials {
   return $USER => $PASSWORD;
}

I had quite a bit of trouble working out the authentication piece despite how simple it looks enjoy the fruits of my search.

#### prototypes

sub method1;
sub method2;

#### CONNECT TO SERVICE

my $Service = SOAP::Lite
       -> proxy ($SERVICE_LOC);

$Service->outputxml($OUTPUT_XML);

Connecting to the service. Pretty simple here. We also apply the option for XML output which can be set for debugging purposes.

#### Invoking Calls

print "\nHey does SOAP work?\n";

#### method1 Test

print "==> Invoking method1"
my $result1 = method1($Service);
if($OUTPUT_XML = 'true'){
   print $result1;
} else {
   if($result){
       for my $t ($result->valueof('//tag/subtag')) {
         print $t->{value1} . " - " . $t->{value2} . "\n";
       }
   } else {
       print "no SOAP for you";
   }
}

#### method2 Test
my %DataStructure1 = (
   'data1'         => 'John Doe',
   'data2'         => '1234',
   );
my %DataStructure2 = (
   'data1'         => 'Jane Doe',
   'data2'         => '4321',
   );
my $result2 = method2($Service, \%DataStructure1, \%DataStructure2);
if($OUTPUT_XML = 'true'){
   print $result2;
} else {
   if($result2){
       for my $t ($result2->valueof('//tag/subtag')) {
         print " " . $t->{value1} . " - " . $t->{value2} . " - " .
$t->{subsubtag}{value3} . "\n";
       }
   } else {
       print "no SOAP for you";
   }
}

Big lesson here. Can’t do much with the result if you don’t know how to access it. As it turns out the result from a SOAP call is a SOM object which is a hash of hashes. Once you know this, accessing the data is trivial. See the following XML for an example of how the returned XML looks.

<tag>
   <subtag>
       <value1>
           something
       </value1>
       <value2>
           something else
       </value2>
   </subtag>
</tag>

<tag>
   <subtag>
       <value1>
           something
       </value1>
       <value2>
           something else
       </value2>
       <subsubtag>
           <value3>
               something
           </value3>
       </subsubtag>
   </subtag>
</tag>
#### Accessing Functions

sub method1{
   my $SOAP = shift;
   print "\n==> Invoking call method1\n";

   my $URIs;
   my $SOM = $SOAP->method1('');

   if($SOM){
       if($OUTPUT_XML eq 'true'){
           return $SOM;
       }elsif($SOM->fault) {
               die $SOM->faultstring;
       }else{
               return $SOM;
       }
   }

 return 0;
}

sub method2{
   my $Service            = $_[0];
   my $DataStructure1 = $_[1];
   my $DataStructure2 = $_[2];
   print "\n==> Invoking call method2\n";

   my $Structure1 = SOAP::Data->name('structure1')->value([
                           SOAP::Data->name('data1')->value($DataStructure1->{'data1'}),
                           SOAP::Data->name('data2')->value($DataStructure1->{'data2'}),
                       ]);

   my $Structure2 = SOAP::Data->name('structure2')->value([
                           SOAP::Data->name('data1')->value($DataStructure2->{'data1'}),
                           SOAP::Data->name('data2')->value($DataStructure2->{'data2'}),
                       ]);

   my $Meth        = SOAP::Data->name('method2')->uri($SERVICE_NS);

   my $SOM         = $Service->call($Meth, $URI, $Location);

   if($SOM){
       if($OUTPUT_XML eq 'true'){
           return $SOM;
       }elsif($SOM->fault) {
               die $SOM->faultstring;
       }else{
               return $SOM;
       }
   }

   return 0;
}

In this last section we find what probably took me the longest time to figure out. In method1 you can see that you can call methods directly. This works for methods that require no arguments, but I was unable to make it work with arguments at least the implementation I tested. In method2 you can see an alternate method that while more verbose is a little more organized. Of particular note here is that when using call() you may need to specify the uri().

Whew! Glad that’s over. Really though your mileage may vary since SOAP implementations are about as varied as every other vague(read extensible) standard. I hope this guide gets you a little bit closer to implementing your SOAP client.

Resources that you will probably find useful.

SOAP::Lite

SOAP::SOM

SOAP::Data

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s