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