m a j o r d o j o About majordojo | Mailing Lists | Bugs | Contact Us

April 30, 2003

Override the root element of the response

Last week I showed how to override the root element of a request, but did not show a way to do the same thing for the root element of a response. So, read on if you would like to know how to modify the response's namespace, namespace prefix, encoding style, etc. It is not very intuitive, or pretty for that matter, but at least it is relatively simple.

The key in overriding properties of the root element of the response is to write a customer serializer. What is a serializer? A serializer converts abstract data into a format that can more easily be transmitted between endpoints. In this case, a serializer converts scalar and complex data into XML. By writing our own serializer we can tell SOAP::Lite how to construct the XML representing the response.

To create a custom serializer, you need to know a little bit about Perl OOP (Object Oriented Programing) and inheritence. The following code block shows a custom serializer which inherits all properties and behavior from the default SOAP::Lite Serializer, but overrides one of its methods/subroutines with its own:

package MySerializer;
@MySerializer::ISA = 'SOAP::Serializer';
sub envelope {
    $_[2] = SOAP::Data->name($_[2])->prefix('temp')->uri('urn:TemperatureService')
        if $_[1] =~ /^(?:method|response)$/;
    shift->SUPER::envelope(@_);
}

This is just a simple serializer, one that changes the root element only, but a serializer could be used to serialize any part of a message (in this case a response).

You can save your serializer to its own file (in the form of a perl module), and then 'use' it. Or alternatively, you can encapsulate the package in a BEGIN { } block anywhere in your code. Either way, to tell SOAP::Lite to use your serializer as opposed to its own, you will need to do the following within your Web service instance:

SOAP::Transport::HTTP::CGI
  ->serializer(MySerializer->new)
  ->dispatch_to('MyService')
  ->handle;

You can find an example of a trivial temperature conversion service which utilizes this custom serializer: here.

Posted by byrnereese at 08:44 AM | Comments (0)

April 29, 2003

How to easily turn on/off debugging information

One thing I find myself constantly doing with my SOAP::Lite clients is commenting in and out the "+trace => 'debug'" from the "use SOAP::Lite" call so that I can turn on and off debugging information. This became so annoying to me that I decided to programitize it. Now I just turn debugging on and off with a command line flag.

The following code fragment shows how you can easily turn on and off SOAP::Lite debugging information from the command line. Obviating your need to go into the code everytime you want to toggle debugging:

use Getopt::Long;
my $result = GetOptions ("debug" => \$DEBUG);

if ($DEBUG) {
    eval "use SOAP::Lite +trace => 'debug';";
} else {
    eval "use SOAP::Lite;";
}

Note: The Getopt::Long package typically comes standard with Perl, but if for some reason your Perl doesn't have it, just download it from CPAN.

Posted by byrnereese at 12:52 PM | Comments (1)

How To Specify Multiple Namespaces

Someone asked on the SOAP::Lite message board about how to specify multiple namespaces in a request. The following little code snippet answers that question. The trick is using the SOAP::Data->attr() subroutine...

The following code snippet shows how to specify an additional namespace. It is easy - in fact anytime you need to specify additional attributes for an XML element in SOAP::Lite, just use the SOAP::Data->attr() subroutine like so:

my $method = SOAP::Data->name('methodName')
                ->attr({'xmlns:ns2' => 'urn:SecondaryNamespace'})
                ->prefix('ns1')
                ->uri('urn:PrimaryNamespace');
my $params = SOAP::Data->name('param' => '123');
my $results = $search->call($method => $params);
Posted by byrnereese at 09:53 AM | Comments (0)

April 28, 2003

Read it before they take legal action

Penny Arcade is an online comic strip in my daily surf routine - a niche strip aimed at gamers. Last week they posted a strip that was later retracted by a court order. They just posted a new strip today in response. Ouch - harsh. Me likee.
Posted by byrnereese at 04:53 PM | Comments (0)

April 24, 2003

How do you modify the root element of a request?

SOAP::Lite's API allows you to call remote Web service operations easily within your code. Simply instantiate an instance of a service, and execute (or call) the remote method directly from your service instance. For example, a call might look like "$soap->myMethod(arguments)." However, while this convention makes for a nice API, by calling the method in this way you give up control over the attributes of the root SOAP Body element. So how do you change the namespace, the prefix, or attributes of the root element?

The secret in changing the root element is to make use of the call subroutine. This allows you to specify a SOAP::Data element (or string) as the method name, and then a set of SOAP::Data elements as parameters. For example:

my $method = SOAP::Data
       ->name("myMethodName")
       ->prefix("ns")
       ->uri("urn:MerchantAccountService")
       ->type("ns:Merchant");
my $service->call($method => SOAP::Data->name("param" => "value");

Will produce a SOAP Body that looks like this:

<soap:Body>
  <ns:myMethodName xmlns:ns="urn:MerchantAccountService" xsi:type="ns:Merchant">
    <param>value</param>
  </ns:myMethodName>
</soap:Body>
Posted by byrnereese at 07:20 AM | Comments (0)

April 22, 2003

How to nest XML elements using SOAP::Lite

Someone posted a message to the SOAP::Lite newsgroup asking a common question: How do you nest XML elements using SOAP::Lite's SOAP::Data constructs. It is a simple problem, but enough people have problems with it that I figured I would detail the answer here.

Nesting elements is actually a bit tricky. This article I wrote a while ago for builder.com.com (why on earth is their domain dotcom-dotcom? how innane) discusses this topic in much more detail. But here is the short answer:

SOAP::Data->name('foo' => 
     \SOAP::Data->value(SOAP::Data->name('bar' => '123'))

In a nutshell, the above code will produce the following XML:

<foo><bar>123</bar></foo>

The trick is dereferencing the nested SOAP::Data element (shown in bold above). Doing so is very unintuitive, but it works.

Posted by byrnereese at 06:22 PM | Comments (0)

Working with multirefs, Axis, and SOAP::Lite

A posting (which is not archived in the group for some reason) in the SOAP::Lite message board raised yet again the shortcomings of SOAP::Lite and how it parses and interprets WSDL. It also brought to light a horrible SOAP Fault returned by Axis which led the poster and I in a completely mistaken direction when trying to solve the problem...

The original problem reported that the Apache Axis Web service was claiming that it could not find the operation called 'getAirFareQuote.' However, upon inspecting the WSDL, one can see that it is indeed defined.

So I spent the next couple of minutes trying to figure out why it wouldn't recognize the operation, especially when I could call other operations documented in the same WSDL file. Weird.

In the end, it had to do with the way Axis was expecting its input parameters... I was misinterpreting an XML multiref that looked like this:

#  <ns1:getAirFareQuote
#     soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
#     xmlns:ns1="urn:SBGAirFareQuotes.sbg.travel.ws.dsdata.co.uk">
#   <in0 href="#id0"/>
#  </ns1:getAirFareQuote>
#  <multiRef id="id0" soapenc:root="0"
#     soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
#     xsi:type="ns2:AirFareQuoteRequest"
#     xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
#     xmlns:ns2="urn:SBGAirFareQuotes.sbg.travel.ws.dsdata.co.uk">
#   <outwardDate
#     xsi:type="xsd:dateTime">2003-04-28T08:00:00.000Z</outwardDate>
#   <returnDate
#     xsi:type="xsd:dateTime">2003-05-05T08:00:00.000Z</returnDate>
#   <originAirport xsi:type="xsd:string">LAX</originAirport>
#   <destinationAirport xsi:type="xsd:string">JFK</destinationAirport>
#  </multiRef>

Into something that looked like this:

#  <ns1:getAirFareQuote
#     soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
#     xmlns:ns1="urn:SBGAirFareQuotes.sbg.travel.ws.dsdata.co.uk">
#   <outwardDate
#     xsi:type="xsd:dateTime">2003-04-28T08:00:00.000Z</outwardDate>
#   <returnDate
#     xsi:type="xsd:dateTime">2003-05-05T08:00:00.000Z</returnDate>
#   <originAirport xsi:type="xsd:string">LAX</originAirport>
#   <destinationAirport xsi:type="xsd:string">JFK</destinationAirport>
#  </ns1:getAirFareQuote>

When it should have looked like this:

#  <ns1:getAirFareQuote
#     soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
#     xmlns:ns1="urn:SBGAirFareQuotes.sbg.travel.ws.dsdata.co.uk">
#   <in0>
#   <outwardDate
#     xsi:type="xsd:dateTime">2003-04-28T08:00:00.000Z</outwardDate>
#   <returnDate
#     xsi:type="xsd:dateTime">2003-05-05T08:00:00.000Z</returnDate>
#   <originAirport xsi:type="xsd:string">LAX</originAirport>
#   <destinationAirport xsi:type="xsd:string">JFK</destinationAirport>
#   </in0>
#  </ns1:getAirFareQuote>

Ok, ok, ok, that is a lot of XML... in the end, the following client was hacked together for SOAP::Lite to fetch airfares for a given set of times, destinations and origins:

#!/usr/bin/perl
#
# airfare.pl
# Author: breese at grandcentral.com

# Location of the WSDL file
my $WSDL
="http://wavendon.dsdata.co.uk:8080/axis/services/SBGGetAirFareQuote?wsdl";
my $NS = "urn:SBGAirFareQuotes.sbg.travel.ws.dsdata.co.uk";
my $HOST = "http://wavendon.dsdata.co.uk:8080/axis/services/SBGGetAirFareQuote";

use strict;
use SOAP::Lite +trace => qw (debug);

my $search = SOAP::Lite
->readable(1)
->xmlschema('http://www.w3.org/2001/XMLSchema')
->on_action( sub { return '""';} )
->proxy($HOST)
->uri($NS);

# CCYY-MM-DDThh:mm:ss
my $outwardDate = "2003-04-28T08:00:00.000Z";
my $returnDate = "2003-05-05T08:00:00.000Z";
my $origin = "LAX";
my $destination = "JFK";

my $method = SOAP::Data->name('getAirFareQuote')
->prefix('air')
->uri($NS);

my $params =
SOAP::Data
->type('air:AirFareQuoteRequest')
->name('in0' =>
\SOAP::Data->value(
SOAP::Data->name('outwardDate' => $outwardDate)->type('xsd:dateTime'),
SOAP::Data->name('returnDate' => $returnDate)->type('xsd:dateTime'),
SOAP::Data->name('originAirport' => $origin),
SOAP::Data->name('destinationAirport' => $destination)
));

my $results = $search->call($method => $params);

# Loop through the results
foreach my $result (@{$results->{'result'}}) {
print $result->{airlineName} . ": " . $result->{fare} . "\n";
}

1;
Posted by byrnereese at 02:10 PM | Comments (0)

April 21, 2003

Inside Saddam's Regime

Putting aside for a moment whether we should be there or not, and the means we took to get there, now that we are there I must admit, I am somewhat morbidly curious about how Saddam and his regime lived. This article on MSNBC gives one of the first glances into his intelligence agency, his lifestyle, and his son Uday. Creepy.
Posted by byrnereese at 12:41 PM | Comments (3)

April 20, 2003

SOAP-MIME-0.55-7 Released

SOAP-MIME-0.55-7, a Perl module extending the attachment capabilities of SOAP::Lite was released earlier this week. Read on for a complete change log.

SOAP-MIME-0.55-7 (Released 16-April-2003)

Relaxed parsing of the start parameter located inside the Content-type header
There could certainly be some debate about this change, especially for those of you who are standards or RFC junkies, but I chose to mirror the functionality of Apache Axis in this regard. Here is the background, the specification IMHO is clear that the start parameter requires angle brackets, but others believe the RFC detailing how content ids should be used (2111) is unclear in this regard which has led some SOAP implementations especially more forgiving low-level SOAP APIs to break more strict toolkits like SOAP::Lite. The set of cases where it would break are rare, but sufficient enough to warrant the change.

Fixed bug around an overlooked unqualified package name
Embarrasingly, this is something I should have caught initially considering I had to make a very similar change to the code on the line just prior to the one this bug was on. But alas, I didn't. But it's fixed now.

Added CHANGE_LOG and INSTALL files to distro
This is really just to be more conventional in regards to Perl modules.

Posted by byrnereese at 04:18 PM | Comments (0)

April 19, 2003

The Hypocrisy of American Democracy

The framers of our constitution realized the importance of protecting America from itself by establishing a system of checks and balances by which no one part of our government could abuse its power. I think what amazes me is that in the noble process of spreading democracy and freedom throughout the world we are completely willing to ignore the very principles of democracy established by our own country: primarily that no one should be allowed to weild too much power, or the very least should be allowed to exercise that power without any other authority to answer to.
Posted by byrnereese at 07:35 AM | Comments (1)


© 2001-2003 - majordojo. All Rights Reserved.
This weblog is licensed under a Creative Commons License.
Powered by Movable Type 2.63.