### Complex SOAP::Lite requests - my rules for SOAP::Sanity!

Previously, I mentioned I'd come back to more complex request and response structures with SOAP::Lite.

Frankly, I haven't posted because I can't avoid the feeling that there's still a little more to unravel. Did I mention that good documentation is sparse? ;) Byrne and others have posted some good stuff (see for example the majordojo soaplite archive and this hands-on tour at builder.com), but mostly you'll find the experiences shared go along lines like "...after hacking around for a bit, I found that this worked...".

But is it possible to try and pin down at least a couple of guidelines for using SOAP::Data? Well, so far I can't claim to solving it all, but I am able to share a few anchors I've tried to plant for my own sanity!

## My Rules for SOAP::Sanity

In the following "rules", $soap is a pre-initialised SOAP::Lite object, as in: my$soap = SOAP::Lite->uri ( $serviceNs ) -> proxy ($serviceUrl );

### 1. The value of a SOAP::Data element becomes the content of the XML entity.

It may seem bleeding obvious. Nevertheless, get this idea fixed in you head and it will help for more complex structures.

So if we are calling a "getHoroscope" service with the following request structure:
<getHoroscope><sign>Aries</sign></getHoroscope>

"Aries" is the value, i.e. the content, of the XML entity called "sign". Thus our request will look like this:
$data = SOAP::Data->name("sign" => 'Aries');$som = $soap->getHoroscope($data );

### 2. To create a hiearchy of entities, use references to a SOAP::Data structure.

In (1), the content of the entity was a simple string ("Aries"). Here we consider the case where we need the content to encapsulate more XML elements rather than just a string. For example a request with this structure:
<getHoroscope><astrology><sign>Aries</sign></astrology></getHoroscope>

Here "astrology" has an XML child element rather than a string value.
To achieve this, we set the value of the "astrology" element as a reference to the "sign" SOAP::Data object:
$data = SOAP::Data->name("astrology" => \SOAP::Data->name("sign" => 'Aries') );$som = $soap->getHoroscope($data );

### 3. To handle multiple child entities, encapsuate as reference to a SOAP::Data collection.

In this case, we need our "astrology" element to have multiple children, for example:
<getHoroscope><astrology><sign>Aries</sign><sign>Pisces</sign></astrology></getHoroscope>

So a simple variation on (2). To achieve this, we collect the "Aries" and "Pisces" elements as a collection within an anonymous SOAP::Data object. We pass a reference to this object as the value of the "astrology" item.
$data = SOAP::Data->name("astrology" => \SOAP::Data->value( SOAP::Data->name("sign" => 'Aries'), SOAP::Data->name("sign" => 'Pisces') ));$som = $soap->getHoroscope($data );

### 4. Clearly distinguish method name structures from data.

This is perhaps just a style and clarity consideration. In the examples above, the method has been implicitly dispatched ("getHoroscope").
If you prefer (or need) to pass the method information to a SOAP::Lite call, I like to keep the method information distinct from the method data.

So for example, item (3) can be re-written (including some additional namespace handling) as:
$data = SOAP::Data->name("astrology" => \SOAP::Data->value( SOAP::Data->name("sign" => 'Aries'), SOAP::Data->name("sign" => 'Pisces') ));$som = $soap->call( SOAP::Data->name('x:getHoroscope')->attr({'xmlns:x' =>$serviceNs})   => $data ); I prefer to read this than have it all mangled together. That brings me to the end of my list of rules! I am by no means confident that there aren't more useful guidelines to be added, or that in fact the ones I have proposed above will even stand the test of time. Nevertheless, with these four ideas clearly in mind, I find I have a fair chance of sitting down to write a complex SOAP::Lite call correctly the first time, rather than the trial and error approach I used to be very familiar with! read more and comment.. ### Safe OCCI createStatelessConnectionPool usage I've been working a bit with Oracle's OCCI (the C++ API for Oracle Database), and stateless connection pools in particular. I've noticed a particular behaviour that's important to be aware of when creating the connection pool (using the oracle::occi::Environment method "createStatelessConnectionPool"). The problem is that this call will fail if you have some kind of conenction or TNS error, leaving you with an unusable and invalid pool. To give a concrete example, if you create a connection pool like this: scPool = env->createStatelessConnectionPool(... what I find is that if the database is down (for example), this call with throw a TNS error like ORA-12541: TNS:no listener, and the scPool object is invalid (but not a null reference). if you attempt to use the pool thereafter, e.g.: if (scPool) cout << "MaxConn=" << scPool->getMaxConnections() << endl; then firstly "if (scPool) .." wont protect you (because its not null), and the getMaxConnections method call will throw an uncatchable segmentation fault (this is Linux x86 I'm using) The workaround of course is to null your scPool if you catch the TNS error, and then if you want a robust application that must keep running even if the connection pool is not created, everytime you try and get a connection from the pool you should also first check to see if you have a pool object to use (and if not, try and create it again). Tortuous to say the least! I would have expected that the desired behaviour should be for createStatelessConnectionPool to return a valid connection pool even if connections are not possible at that point in time, and that for the TNS errors to be only thrown if and when you try and get a connection from the pool. Anyone have a view? ... bug, ER or expected? 12-Feb, an update: I've since discovered that this behaviour is true only if you set a "minumum connections" >0 for the pool. If you set "minumum connections"==0, then the behaviour is as I would expect - the pool is created without error, but you may hit an error when attempting to get a connection from the pool. read more and comment.. ### Handling range parameters in Bash I've come across a few occasions where I wanted to specify a "range parameter" for Bash scripts. Like "1..4" meaning do for 1,2,3 and 4. Here's a simple trick that uses the (relatively obscure) variable expansion and substring replacement capabilities of the shell. #!/bin/bashv_range="1..3" # or you could have taken it as a script parameterv_start=${v_range%%.*} # chomp everything from the left-most matching "."v_end=${v_range##*.} # chomp everything up to the right-most matching "." The repeated %% and ## basically mean you will get a "greedy" match, so you can say "1..4" or "1....5"; it doesn't matter how many repeats you have of the range delimiter. Of course, you can choose other delimiters such as a hyphen, as in "5-10", if you wish. Now that you have extracted the start and end indices, you can then loop or whatever to your hearts content: for ((a=v_start; a <= v_end ; a++))do echo "Looping with a=$a"done

Postscript 5-Feb-2008:
We live and learn! Hat tip to buford for alerting me to the seq utility, which simplifies the iteration over a range, as in:
for a in seq 1 10do  echo "Looping with a=$a"done  You still need to determine the start and end values of the range, which is the whole point of the variable expansions approach posted here. read more and comment.. ### Generating a temp filename in Bash Here's a function that simplifies the process of generating temporary filenames in shell scripts (I use bash). This function will generate a temporary filename given optional path and filename prefix parameters. If the file happens to already exist (a small chance), it recurses to try a new filename. It doesn't cleanup temp files or anything fancy like that, but it does have the behaviour that if you never write to the temp file, it is not created. If you don't like that idea, touch the file before returning the name. This procedure demonstrates the use of variable expansion modifiers to parse the parameters, and the$RANDOM builtin variable.

function gettmpfile(){  local tmppath=${1:-/tmp} local tmpfile=$2${2:+-} tmpfile=$tmppath/$tmpfile$RANDOM$RANDOM.tmp if [ -e$tmpfile ]  then    # if file already exists, recurse and try again    tmpfile=$(gettmpfile$1 $2) fi echo$tmpfile}

By default (with no parameters specified), this function will return a random filename in the /tmp directory:

[prompt]$echo$(gettmpfile)
/tmp/324003570.tmp

If you specify a path and filename prefix, these are used to generate the name:
[prompt]$echo$(gettmpfile /my_tmp_path app-tmpfile)
/my_tmp_path/app-tmpfile-276051579.tmp