tilly has asked for the wisdom of the Perl Monks concerning the following question:

My problem is that I need to interact with a vendor that provides a SOAP API. And I've never done SOAP. So I've run into some trouble.

What I'm supposed to do is call the login method, then go on to call methods that do work, then logout when I'm done. I'm able to call the login method. I can tell that login worked. But then I'm unable to do anything else.

Here is some sample code:

#! /usr/bin/perl -wl use strict; use SOAP::Lite; use Data::Dumper; $Data::Dumper::Indent = 1; my $soap = SOAP::Lite->uri( 'https://ws1.responsys.net/webservices/ResponsysWS?wsdl' )->proxy( 'https://ws1.responsys.net/webservices/ResponsysWS?wsdl' ); my $return = $soap->login('USER', 'PASSWORD'); #print Dumper($return); if ($return->fault) { die $return->faultstring; } $return = $soap->listFolders(); if ($return->fault) { die $return->faultstring; }
The response I get is:
java.rmi.RemoteException: RIAccount object is not available in session + at test.pl line 24.
which is coming from the attempt to call listFolders. (If you run this you'll get a different error since I haven't provided you with the right login information - login really did something.) So obviously there is an RIAccount object that I need to find and do something. But I can't find anything appropriate when I look through the datastructures I have available in $soap or $response.

When I compare with the Java code example in the documentation I see an interesting call to:

((ResponsysWSSoapBindingStub) service).setMaintainSession(true);
that looks like it could be relevant to keeping a login around. But I can't figure out what the Perl equivalent might be. (If there is one.)

Of course the interface is defined with WSDL. And if I look at WSDL examples in Perl, I see that might be better off calling the session method. However that produces an immediate error. Based on http://support.microsoft.com/kb/308438 I tried downloading the WSDL and deleting the wsdl:portType section. Now I don't get an error, but I get nothing back from:

#! /usr/bin/perl -wl use strict; use SOAP::Lite; use Data::Dumper; $Data::Dumper::Indent = 1; my $soap = SOAP::Lite->service( 'file://users/btilly/responsys/ResponsysWS.wsdl' )->proxy( 'https://ws1.responsys.net/webservices/ResponsysWS?wsdl' ); $soap->login('USER', 'PASSWORD'); print $soap->listFolders;
which isn't very useful. Possibly related is that the datastructure of $soap includes a fault:
'org.xml.sax.SAXParseException: The value of the attribute "p refix="xmlns",localpart="namesp1",rawname="xmlns:namesp1"" is invalid. + Prefixed namespace bindings may not be empty.'
and I don't know where this comes from.

Does anybody have ideas for what else I can try to be able to login to the service then start calling useful methods? (If it involves ignoring the WSDL and hand-coding stuff, I'm fine with that as long as I have an example to work from to let me know what I should be hand-coding.)

Replies are listed 'Best First'.
Re: SOAP::Lite and sessions?
by ForgotPasswordAgain (Vicar) on Jul 19, 2007 at 15:54 UTC

    What's probably happening is that the service is coded crappily and is not handling this error gracefully. Did you try using a cookie_jar? I had a lot of problems getting that to work when I tried it, so I'll put this example (_handle_fault is from bric_soap in Bricolage, in case Sam Tregar reads this :) ):

    #!/usr/bin/perl use strict; use warnings; use SOAP::Lite trace => [qw(debug)], on_fault => \&_handle_fault; import SOAP::Data 'name'; use HTTP::Cookies; my $COOKIE_FILE = '/tmp/bs-wsdl-cookies.txt'; my $WSDL_FILE = 'file:///tmp/bricolage-simpletest.wsdl'; main(); exit; sub main { my $client = SOAP::Lite->service($WSDL_FILE); # xxx: I have no idea what magic is done # to initialize things (transport), # so I end up calling login a 2nd time just to add the cookie_jar! # $client->login(name(username => 'admin'), # name(password => 'change me now!')); $client->login('admin', 'change me now!'); my $cookie_jar = HTTP::Cookies->new(ignore_discard => 1, file => $COOKIE_FILE, autosave=>1); $client->transport->cookie_jar($cookie_jar); # xxx: 2nd login # $client->login(name(username => 'admin'), # name(password => 'change me now!')), $/; $client->login('admin', 'change me now!'); # .... } # handle faults from SOAP::Lite's on_fault event sub _handle_fault { my ($soap, $r) = @_; # print out the error as appropriate if (ref $r) { if ($r->faultstring eq 'Application error' and ref $r->faultdetail and ref $r->faultdetail eq 'HASH' ) + { # this is a bric exception, the interesting stuff is in de +tail print STDERR "Call failed:\n\n", join("\n", values %{$r->faultdetail}), $/, $/; } else { print STDERR "Call failed:\n\n", $r->faultstring, $/, $/; } print STDERR "Check the Apache error log for more detail.\n"; } else { print STDERR "TRANSPORT ERROR: ", $soap->transport->status, "\ +n"; print STDERR "Check the Apache error log for more information. +\n"; } return SOAP::SOM->new(); }

    I think that worked for me at least. Also, if you figure out how to only have to login once, I'd appreciate knowing how!

    Maybe you should write it in Java.... I was complaining about WSDL support in Perl (generally Perl in the "enterprise") just the other day.

      I had not tried the cookie jar idea.

      I'd already settled on writing it in Java. At this point I know I can hit my deadline writing it in Java, and Perl is a question mark, so I'll stick with Java. When I'm done I'll have other things to do, so I doubt I'll revisit this project.

      Just a tip. If you do this, you won't have to call login 2x:
      use SOAP::Lite; use HTTP:: Cookies; my $COOKIE = './coooooooooookie.txt'; my $SET_COOKIE = HTTP::Cookies->new(ignore_discard => 1, file=> $COOKIE, autosave=>1); my $soapLite = SOAP::Lite->new(); $soapLite->proxy($end_point, cookie_jar => $SET_COOKIE); $soapLite->uri($uri_slash_method); ...
      I've had success putting that (plus other pertinent info) into a _call method, then constructing separate methods that are called through _call. Hope that helps.
        Sorry, wasn't logged in. If anybody has any questions, that was my post above...
      Tried SOAP::WSDL ?

      It handles complex types, seems to properly map to client side objects if you supply appropriately named classes, handles caching, and is a sub-class of SOAP::Lite for clients.

      -David

        I didn't try it yet, but I did ask the author to update the CHANGES file a couple weeks ago after noticing that it was being updated recently. :) He replied that 2.0 is a rewrite, though should have mostly the same API.
        I tried it, and it was worse for my needs. I couldn't even generate evidence that the login method did anything!