1 #$Id: Service.pm,v 1.24 2006/01/31 22:19:02 fgibbons Exp $
5 MOBY::Client::Service - an object for communicating with MOBY Services
9 use MOBY::Client::Service;
11 my $Service = MOBY::Client::Service->new(service => $WSDL);
12 my $result = $Service->execute(@args);
16 Deals with all SOAPy rubbish required to communicate with a MOBY Service.
17 The object is created using the WSDL file returned from a
18 MOBY::Client::Central->retrieveService() call. The only useful method call
19 in this module is "execute", which executes the service.
23 Mark Wilkinson (markw@illuminae.com)
25 BioMOBY Project: http://www.biomoby.org
31 Usage : $Service = MOBY::Client::Service->new(@args)
32 Function : create a service connection
33 Returns : MOBY::Client::Service object, undef if no wsdl.
34 Args : service : string ; required
35 a WSDL file defining a MOBY service
36 uri : string ; optional ; default NULL
37 if the URI of the soap call needs to be personalized
38 this should almost never happen...
42 package MOBY
::Client
::Service
;
45 #use SOAP::Lite + 'trace';
50 use vars
qw($AUTOLOAD @ISA);
53 open( OUT, ">/tmp/ServiceCallLogOut.txt" ) || die "cant open logfile\n";
61 #Encapsulated class data
62 #___________________________________________________________
64 my %_attr_data = # DEFAULT ACCESSIBILITY
66 service => [ undef, 'read/write' ],
67 uri => [ undef, 'read/write' ],
68 serviceName => [ undef, 'read/write' ],
69 _soapService => [ undef, 'read/write' ],
70 smessageVersion => ['0.87', 'read' ],
73 #_____________________________________________________________
74 # METHODS, to operate on encapsulated class data
75 # Is a specified object attribute accessible in a given mode
77 my ( $self, $attr, $mode ) = @_;
78 $_attr_data{$attr}[1] =~ /$mode/;
81 # Classwide default value for a specified object attribute
83 my ( $self, $attr ) = @_;
84 $_attr_data{$attr}[0];
87 # List of names of all specified object attributes
99 my ( $caller, %args ) = @_;
100 my $caller_is_obj = ref( $caller );
101 my $class = $caller_is_obj || $caller;
102 my $self = bless {}, $class;
103 foreach my $attrname ( $self->_standard_keys ) {
104 if ( exists $args{$attrname} ) {
105 $self->{$attrname} = $args{$attrname};
106 } elsif ( $caller_is_obj ) {
107 $self->{$attrname} = $caller->{$attrname};
109 $self->{$attrname} = $self->_default_for( $attrname );
114 # seems to be a bug in SOAP::Lite that the WSDL document
115 # fails a parse if it is passed as a scalar rather than a file
116 # this section can be removed when this bug is fixed
117 #open (OUT, ">$dir/Service.wsdl") || die "cant open dump of wsdl file";
118 #print OUT $self->service;
120 # ________________________________________
122 URI::Escape::uri_escape( $self->service ); # this seems to fix the bug
123 return undef unless $wsdl;
124 my $soap = SOAP::Lite->service( "data:,$wsdl" );
125 if ( $self->uri ) { $soap->uri( $self->uri ) }
126 $self->serviceName( &_getServiceName( $soap ) );
127 $self->_soapService( $soap );
133 Usage : $result = $Service->execute(%args)
134 Function : execute the MOBY service
135 Returns : whatever the Service provides as output
136 Args : XMLinputlist => \@data
137 Comment : @data is a list of single invocation inputs; the XML goes between the
138 <queryInput> tags of a servce invocation XML.
139 Each element of @data is itself a listref of [articleName, $XML].
140 articleName may be undef if it isn't required.
141 $XML is the actual XML of the Input object
145 There are several ways in which you can execute a service. You may
146 wish to invoke the service on several objects, and get the response
147 back in a single message. You may wish to pass in a collection of
148 objects, which should be treated as a single entity. Or you may wish
149 to pass in parameters, along with data. In each case, you're passing in
151 XMLinputlist => [ ARGS ]
153 The structure of @ARGS helps MOBY to figure out what you want.
157 =item Iterate over multiple Simples
159 To have the service iterate over multiple equivalent objects, and
160 return all the results in a single message, use this syntax (ARGS =
163 $Service->execute(XMLinputlist => [
164 ['object1', '<Object namespace="blah" id="123"/>'],
165 ['object2', '<Object namespace="blah" id="234"/>']
168 This would invoke the service twice (in a single message) the first
169 time with an object "123" and the second time with object "234".
171 =item Process a Collection
173 To pass in a Collection, you need this syntax (ARGS = [ '', [..., ..., ...] ]):
175 $Service->execute(XMLinputlist => [
177 '<Object namespace="blah" id="123"/>',
178 '<Object namespace="blah" id="234"/>']
181 This would invoke the service once with a collection of inputs that
182 are not required to be named ('').
184 =item Process multiple Simple inputs
186 To pass in multiple inputs, to be considered neither a Collection nor sequentially evaluated, use this syntax (ARGS = [..., ..., ...])
188 $Service->execute(XMLinputlist => [
190 'input1', '<Object namespace="blah" id="123"/>',
191 'input2', '<Object namespace="blah" id="234"/>',
195 This would cause a single invocation of a service requiring two input
196 parameters named "input1" and "input2"
200 Finally, MOBY will recognize parameters by virtue of their having been
201 declared when the service was registered. You need to specify the name
204 $Service->execute(XMLinputlist => [
206 'input1', '<Object namespace="blah" id="123"/>',
207 'input2', '<Object namespace="blah" id="234"/>',
208 'param1', '<Value>0.001</Value>',
212 This would cause a single invocation of a service requiring two input
213 parameters named "input1" and "input2", and a parameter named 'param1'
214 with a value of 0.001
221 # The biggest unanswered question for this subroutine is how it should respond in the event
222 # that there is a problem with the service.
223 # It should probably die() rather than just return strings as error messages.
224 my ( $self, %args ) = @_;
225 die "ERROR: expected listref for XMLinputlist"
226 unless ( ref( $args{XMLinputlist} ) eq 'ARRAY' );
227 my @inputs = @{ $args{XMLinputlist} };
229 foreach ( @inputs ) {
230 die "ERROR: expected listref [articleName, XML] for data element"
231 unless ( ref( $_ ) eq 'ARRAY' );
232 my $qID = $self->_nextQueryID;
233 $data .= "<moby:mobyData queryID='$qID'>";
234 while ( my ( $articleName, $XML ) = splice( @{$_}, 0, 2 ) ) {
236 if ( ref( $XML ) ne 'ARRAY' ) {
238 if ( $XML =~ /\<(moby\:|)Value\>/ )
241 "<moby:Parameter moby:articleName='$articleName'>$XML</moby:Parameter>";
244 "<moby:Simple moby:articleName='$articleName'>\n$XML\n</moby:Simple>\n";
247 # need to do this for collections also!!!!!!
248 } elsif ( ref( $XML ) eq 'ARRAY' ) {
250 $data .= "<moby:Collection moby:articleName='$articleName'>\n";
252 $data .= "<moby:Simple>$_</moby:Simple>\n";
254 $data .= "</moby:Collection>\n";
257 $data .= "</moby:mobyData>\n";
260 # this was added on January 19th, 2005 and may not work!
263 my $version = $self->smessageVersion();
264 $data = "<?xml version='1.0' encoding='UTF-8'?>
265 <moby:MOBY xmlns:moby='http://www.biomoby.org/moby-s' moby:smessageVersion='$version'>
270 $data =~ s"&"&"g; # encode content in case it has CDATA
271 $data =~ s"\<"<"g;
272 $data =~ s"\]\]\>"\]\]>"g;
276 ### BEFORE IT WAS JUST THIS
278 #$data = "<![CDATA[<?xml version='1.0' encoding='UTF-8'?>
279 #<moby:MOBY xmlns:moby='http://www.biomoby.org/moby-s'>
282 # </moby:mobyContent>
284 my $METHOD = $self->serviceName;
285 &_LOG( %args, $METHOD );
287 eval { ( $response ) = $self->_soapService->$METHOD( $data ) };
288 if ($@) { die "Service execution failed: $@"}
289 else {return $response;} # the service execution failed then pass back ""
294 Usage : $name = $Service->serviceName()
295 Function : get the name of the service
301 =head2 _getServiceName
303 Usage : $name = $Service->_getServiceName()
304 Function : Internal method to retrieve the name of the service from the SOAP object
310 sub _getServiceName {
311 my ( $service ) = @_;
313 my ( $method ) = @{ join '::', ref $service, 'EXPORT_OK' };
319 my ( $self, $newval ) = @_;
320 $AUTOLOAD =~ /.*::(\w+)/;
322 if ( $self->_accessible( $attr, 'write' ) ) {
324 if ( defined $_[1] ) { $_[0]->{$attr} = $_[1] }
325 return $_[0]->{$attr};
326 }; ### end of created subroutine
327 ### this is called first time only
328 if ( defined $newval ) {
329 $self->{$attr} = $newval;
331 return $self->{$attr};
332 } elsif ( $self->_accessible( $attr, 'read' ) ) {
334 return $_[0]->{$attr};
335 }; ### end of created subroutine
336 return $self->{$attr};
339 # Must have been a mistake then...
340 croak "No such method: $AUTOLOAD";
344 sub SOAP::Transport::HTTP::Client::get_basic_credentials {
345 my ( $username, $password );
346 print "ENTER USERNAME: ";
349 print "ENTER PASSWORD: ";
352 return $username => $password;
356 return unless $debug;
357 open LOG, ">>/tmp/ServiceCallLogOut.txt" or die "can't open logfile $!\n";
358 print LOG join "\n", @_;
365 # --------------------------------------------------------------------------------------------------------