File Coverage

blib/lib/SOAP/WSDL.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package SOAP::WSDL;
2 18     18   44033 use strict;
  18         29  
  18         539  
3 18     18   62 use warnings;
  18         16  
  18         418  
4              
5 18     18   302 use 5.008; # require at least perl 5.8
  18         33  
  18         495  
6              
7 18     18   60 use vars qw($AUTOLOAD);
  18         21  
  18         707  
8              
9 18     18   62 use Carp;
  18         22  
  18         1008  
10 18     18   68 use Scalar::Util qw(blessed);
  18         22  
  18         1007  
11 18     18   5697 use SOAP::WSDL::Client;
  0            
  0            
12             use SOAP::WSDL::Expat::WSDLParser;
13             use Class::Std::Fast constructor => 'none';
14             use SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType;
15             use LWP::UserAgent;
16              
17             # perl -p -i -e 's{our \$VERSION = 3\.\d*;}{our \$VERSION = 3.003;}' `ack -l 'our \\$VERSION = '` # in the lib/ directory, to change version numbers
18             our $VERSION = 3.003;
19              
20             my %no_dispatch_of :ATTR(:name);
21             my %wsdl_of :ATTR(:name);
22             my %autotype_of :ATTR(:name);
23             my %outputxml_of :ATTR(:name :default<0>);
24             my %outputtree_of :ATTR(:name);
25             my %outputhash_of :ATTR(:name);
26             my %servicename_of :ATTR(:name);
27             my %portname_of :ATTR(:name);
28             my %class_resolver_of :ATTR(:name);
29              
30             my %method_info_of :ATTR(:default<()>);
31             my %port_of :ATTR(:default<()>);
32             my %porttype_of :ATTR(:default<()>);
33             my %binding_of :ATTR(:default<()>);
34             my %service_of :ATTR(:default<()>);
35             my %definitions_of :ATTR(:get :default<()>);
36             my %serialize_options_of :ATTR(:default<()>);
37              
38             my %client_of :ATTR(:name :default<()>);
39             my %keep_alive_of :ATTR(:name :default<0> );
40              
41             my %LOOKUP = (
42             no_dispatch => \%no_dispatch_of,
43             class_resolver => \%class_resolver_of,
44             wsdl => \%wsdl_of,
45             autotype => \%autotype_of,
46             outputxml => \%outputxml_of,
47             outputtree => \%outputtree_of,
48             outputhash => \%outputhash_of,
49             portname => \%portname_of,
50             servicename => \%servicename_of,
51             keep_alive => \%keep_alive_of,
52             );
53              
54             sub readable { carp <<'EOT';
55             'readable' has no effect any more. If you want formatted XML,
56             copy the debug output to your favorite XML editor and run the
57             source format command.
58             EOT
59             return;
60             }
61              
62             sub set_readable; *set_readable = \&readable;
63              
64             for my $method (keys %LOOKUP ) {
65             no strict qw(refs); ## no critic (ProhibitNoStrict)
66             *{ $method } = sub {
67             my $self = shift;
68             my $ident = ident $self;
69             if (@_) {
70             $LOOKUP{ $method }->{ $ident } = shift;
71             return $self;
72             }
73             return $LOOKUP{ $method }->{ $ident };
74             };
75             }
76              
77             { # just a BLOCK for scoping warnings.
78              
79             # we need to roll our own for supporting
80             # SOAP::WSDL->new( key => value ) syntax,
81             # like SOAP::Lite does. Class::Std enforces a single hash ref as
82             # parameters to new()
83             no warnings qw(redefine); ## no critic ProhibitNoWarnings;
84              
85             sub new {
86             my ($class, %args_from) = @_;
87             my $self = \do { my $foo = Class::Std::Fast::ID() };
88             bless $self, $class;
89             for (keys %args_from) {
90             my $method = $self->can("set_$_")
91             or croak "unknown parameter $_ passed to new";
92             $method->($self, $args_from{$_});
93             }
94              
95             my $ident = ident $self;
96             $client_of{ $ident } = SOAP::WSDL::Client->new();
97             $self->wsdlinit() if ($wsdl_of{ $ident });
98             return $self;
99             }
100             }
101              
102             sub set_proxy {
103             my $self = shift;
104             return $self->get_client()->set_proxy(@_);
105             }
106              
107             sub get_proxy {
108             my $self = shift;
109             return $self->get_client()->get_proxy();
110             }
111              
112             sub proxy {
113             my $self = shift;
114             if (@_) {
115             return $self->set_proxy(@_);
116             }
117             return $self->get_proxy();
118             }
119              
120             sub wsdlinit {
121             my ($self, %opt) = @_;
122             my $ident = ident $self;
123              
124             my $lwp = LWP::UserAgent->new(
125             $keep_alive_of{ $ident }
126             ? (keep_alive => 1)
127             : ()
128             );
129             $lwp->agent(qq[SOAP::WSDL $VERSION]);
130             my $response = $lwp->get( $wsdl_of{ $ident } );
131             croak $response->message() if ($response->code != 200);
132              
133             my $parser = SOAP::WSDL::Expat::WSDLParser->new();
134             $parser->parse_string( $response->content() );
135              
136             my $wsdl_definitions = $parser->get_data();
137              
138             # sanity checks
139             my $types = $wsdl_definitions->first_types()
140             or croak "unable to extract schema from WSDL";
141             my $ns = $wsdl_definitions->get_xmlns();
142              
143             # setup lookup variables
144             $definitions_of{ $ident } = $wsdl_definitions;
145             $serialize_options_of{ $ident } = {
146             autotype => 0,
147             typelib => $types,
148             namespace => $ns,
149             };
150              
151             $servicename_of{ $ident } = $opt{servicename} if $opt{servicename};
152             $portname_of{ $ident } = $opt{portname} if $opt{portname};
153              
154             $self->_wsdl_init_methods();
155              
156             # pass-through keep_alive if we need it...
157             $self->get_client()->set_proxy(
158             $port_of{ $ident }->first_address()->get_location(),
159             $keep_alive_of{ $ident } ? (keep_alive => 1) : (),
160             );
161              
162             return $self;
163             } ## end sub wsdlinit
164              
165             sub _wsdl_get_service :PRIVATE {
166             my $ident = ident shift;
167             my $wsdl = $definitions_of{ $ident };
168             return $service_of{ $ident } = $servicename_of{ $ident }
169             ? $wsdl->find_service( $wsdl->get_targetNamespace() , $servicename_of{ $ident } )
170             : ( $service_of{ $ident } = $wsdl->get_service()->[ 0 ] );
171             } ## end sub _wsdl_get_service
172              
173             sub _wsdl_get_port :PRIVATE {
174             my $ident = ident shift;
175             my $wsdl = $definitions_of{ $ident };
176             my $ns = $wsdl->get_targetNamespace();
177             return $port_of{ $ident } = $portname_of{ $ident }
178             ? $service_of{ $ident }->get_port( $ns, $portname_of{ $ident } )->[ 0 ]
179             : ( $port_of{ $ident } = $service_of{ $ident }->get_port()->[ 0 ] );
180             }
181              
182             sub _wsdl_get_binding :PRIVATE {
183             my $self = shift;
184             my $ident = ident $self;
185             my $wsdl = $definitions_of{ $ident };
186             my $port = $self->_wsdl_get_port();
187             $binding_of{ $ident } = $wsdl->find_binding( $port->expand( $port->get_binding() ) )
188             or croak "no binding found for ", $port->get_binding();
189             return $binding_of{ $ident };
190             }
191              
192             sub _wsdl_get_portType :PRIVATE {
193             my $self = shift;
194             my $ident = ident $self;
195             my $wsdl = $definitions_of{ $ident };
196             my $binding = $self->_wsdl_get_binding();
197             $porttype_of{ $ident } = $wsdl->find_portType( $binding->expand( $binding->get_type() ) )
198             or croak "cannot find portType for " . $binding->get_type();
199             return $porttype_of{ $ident };
200             }
201              
202             sub _wsdl_init_methods :PRIVATE {
203             my $self = shift;
204             my $ident = ident $self;
205             my $wsdl = $definitions_of{ $ident };
206             my $ns = $wsdl->get_targetNamespace();
207              
208             # get bindings, portType, message, part(s) - use private methods for clear separation...
209             $self->_wsdl_get_service();
210             $self->_wsdl_get_portType();
211              
212             $method_info_of{ $ident } = {};
213              
214             foreach my $binding_operation (@{ $binding_of{ $ident }->get_operation() })
215             {
216             my $method = {};
217              
218             # get SOAP Action
219             # SOAP-Action is a required HTTP Header, so we need to look it up...
220             # There must be a soapAction uri - or the WSDL is invalid (and
221             # it's not us to prove that...)
222             my $soap_binding_operation = $binding_operation->get_operation()->[0];
223             $method->{ soap_action } = $soap_binding_operation->get_soapAction();
224              
225             # get parts
226             # 1. get operation from port
227             my $operation = $porttype_of{ $ident }->find_operation( $ns,
228             $binding_operation->get_name() );
229              
230             # 2. get input message name
231             my ( $prefix, $localname ) = split /:/xm,
232             $operation->first_input()->get_message();
233              
234             # 3. get input message
235             my $message = $wsdl->find_message( $ns, $localname )
236             or croak "Message {$ns}$localname not found in WSDL definition";
237              
238             # Is body not required? So there must be one? Do we need the "if"?
239             # if (
240             my $body=$binding_operation->first_input()->first_body();
241             # {
242             if ($body->get_parts()) {
243             $method->{ parts } = []; # make sure it's empty
244             my $message_part_ref = $message->get_part();
245             for my $name ( split m{\s}xm , $body->get_parts() ) {
246             $name =~s{ \A [^:]+: }{}xm; # throw away ns prefix
247             # could probably made more efficient, but our lists are
248             # usually quite short
249             push @{ $method->{ parts } },
250             grep { $_->get_name() eq $name } @{ $message_part_ref };
251             }
252             }
253             # }
254             # A body does not need to specify the parts of a messages.
255             # Use all of the message's parts if it does not.
256             $method->{ parts } ||= $message->get_part();
257              
258             # rpc / encoded methods may have a namespace specified.
259             # look it up and set it...
260             $method->{ namespace } = $binding_operation
261             ? do {
262             my $input = $binding_operation->first_input();
263             $input ? $input->first_body()->get_namespace() : undef;
264             }
265             : undef;
266              
267             $method_info_of{ $ident }->{ $binding_operation->get_name() } = $method;
268             }
269              
270             return $method_info_of{ $ident };
271             }
272              
273             # on_action is a no-op and just here for compatibility reasons.
274             # It returns the first parameter to allow method chaining.
275             sub on_action { return shift }
276              
277             sub call {
278             my ($self, $method, @data_from) = @_;
279             my $ident = ${ $self };
280              
281             my ($data, $header) = ref $data_from[0]
282             ? ($data_from[0], $data_from[1] )
283             : (@data_from>1)
284             ? ( { @data_from }, undef )
285             : ( $data_from[0], undef );
286              
287             $self->wsdlinit() if not ($definitions_of{ $ident });
288             $self->_wsdl_init_methods() if not ($method_info_of{ $ident });
289              
290             my $client = $client_of{ $ident };
291              
292             $client->set_no_dispatch( $no_dispatch_of{ $ident } );
293             $client->set_outputxml( $outputxml_of{ $ident } ? 1 : 0 );
294              
295             # only load ::Deserializer::SOM if we really need to deserialize to SOM.
296             # maybe we should introduce something like $output{ $ident } with a fixed
297             # set of values - m{^(TREE|HASH|XML|SOM)$}xms ?
298             if ( ( ! $outputtree_of{ $ident } )
299             && ( ! $outputhash_of{ $ident } )
300             && ( ! $outputxml_of{ $ident } )
301             && ( ! $no_dispatch_of{ $ident } ) ) {
302             require SOAP::WSDL::Deserializer::SOM;
303             $client->set_deserializer( SOAP::WSDL::Deserializer::SOM->new() );
304             }
305              
306             my $method_info = $method_info_of{ $ident }->{ $method };
307              
308             # TODO serialize both header and body, not only header
309             my (@response) = (blessed $data)
310             ? $client->call( {
311             operation => $method,
312             soap_action => $method_info->{ soap_action },
313             }, $data )
314             : do {
315             my $content = q{};
316             # TODO support RPC-encoding: Top-Level element + namespace...
317             foreach my $part ( @{ $method_info->{ parts } } ) {
318              
319             $content .= $part->serialize( $method, $data,
320             {
321             %{ $serialize_options_of{ $ident } }
322             } );
323             }
324             $client->call(
325             {
326             operation => $method,
327             soap_action => $method_info->{ soap_action }
328             },
329             # absolutely stupid, but we need a reference which
330             # serializes to XML on stringification...
331             SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType->new({
332             value => $content
333             }),
334             SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType->new({
335             value => $header
336             })
337             );
338             };
339              
340             return if not @response; # nothing to do for one-ways
341             return wantarray ? @response : $response[0];
342             }
343             1;
344              
345             __END__