File Coverage

lib/XML/Compile/WSDL11.pm
Criterion Covered Total %
statement 215 290 74.1
branch 80 184 43.4
condition 14 50 28.0
subroutine 32 41 78.0
pod 15 17 88.2
total 356 582 61.1


line stmt bran cond sub pod time code
1             # Copyrights 2014-2021 by [Mark Overmeer ].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.02.
5             # This code is part of distribution XML-Compile-WSDL11. Meta-POD processed
6             # with OODoc into POD and HTML manual-pages. See README.md
7             # Copyright Mark Overmeer. Licensed under the same terms as Perl itself.
8              
9             package XML::Compile::WSDL11;
10 5     5   1461889 use vars '$VERSION';
  5         94  
  5         313  
11             $VERSION = '3.08';
12              
13 5     5   34 use base 'XML::Compile::Cache';
  5         6  
  5         3034  
14              
15 5     5   721840 use warnings;
  5         14  
  5         150  
16 5     5   28 use strict;
  5         9  
  5         119  
17              
18 5     5   26 use Log::Report 'xml-compile-soap';
  5         12  
  5         41  
19              
20 5     5   1517 use XML::Compile ();
  5         9  
  5         109  
21 5     5   30 use XML::Compile::Util qw/pack_type unpack_type/;
  5         11  
  5         275  
22 5     5   3077 use XML::Compile::SOAP ();
  5         49117  
  5         177  
23 5     5   46 use XML::Compile::SOAP::Util qw/:wsdl11/;
  5         17  
  5         658  
24 5     5   2800 use XML::Compile::SOAP::Extension;
  5         4688  
  5         176  
25              
26 5     5   2598 use XML::Compile::SOAP::Operation ();
  5         10983  
  5         128  
27 5     5   2553 use XML::Compile::Transport ();
  5         7566  
  5         120  
28              
29 5     5   34 use File::Spec ();
  5         10  
  5         108  
30 5     5   31 use List::Util qw/first/;
  5         9  
  5         310  
31 5     5   32 use Scalar::Util qw/blessed/;
  5         7  
  5         297  
32 5     5   30 use File::Basename qw/dirname/;
  5         9  
  5         21464  
33              
34              
35             sub init($)
36 5     5 0 8971 { my ($self, $args) = @_;
37 5 50       26 $args->{schemas} and panic "new(schemas) option removed in 0.78";
38 5         15 my $wsdl = delete $args->{top};
39              
40 5         17 local $args->{any_element} = 'ATTEMPT';
41 5         14 local $args->{any_attribute} = 'ATTEMPT'; # not implemented
42 5         15 local $args->{allow_undeclared} = 1;
43              
44 5         40 $self->SUPER::init($args);
45              
46 5         1871 $self->{index} = {};
47              
48 5         28 $self->addPrefixes(wsdl => WSDL11, soap => WSDL11SOAP, http => WSDL11HTTP);
49              
50             # next modules should change into an extension as well...
51             $_->can('_initWSDL11') && $_->_initWSDL11($self)
52 5   50     654 for XML::Compile::SOAP->registered;
53              
54 5         77 XML::Compile::SOAP::Extension->wsdl11Init($self, $args);
55              
56 5         61 $self->declare
57             ( READER => 'wsdl:definitions'
58             , key_rewrite => 'PREFIXED(wsdl,soap,http)'
59             , hook => {type => 'wsdl:tOperation', after => 'ELEMENT_ORDER'}
60             );
61              
62 5         22 $self->{XCW_dcopts} = {};
63 5         16 $self->{XCW_server} = $args->{server_type};
64              
65 5         670 my @xsds = map File::Spec->catdir(dirname(__FILE__), 'WSDL11', 'xsd', $_)
66             , qw(wsdl.xsd wsdl-mime.xsd wsdl-http.xsd);
67              
68 5         42 $self->importDefinitions(\@xsds, element_form_default => 'qualified');
69              
70 5 50       25086 $self->addWSDL($_) for ref $wsdl eq 'ARRAY' ? @$wsdl : $wsdl;
71 5         160 $self;
72             }
73              
74 0     0 0 0 sub schemas(@) { panic "schemas() removed in v2.00, not needed anymore" }
75              
76             #--------------------------
77              
78              
79             sub compileAll(;$$)
80 0     0 1 0 { my ($self, $need, $usens) = @_;
81 0 0 0     0 $self->SUPER::compileAll($need, $usens)
82             if !$need || $need ne 'CALLS';
83              
84 0 0 0     0 $self->compileCalls
85             if !$need || $need eq 'CALLS';
86 0         0 $self;
87             }
88              
89              
90             sub compileCalls(@)
91 0     0 1 0 { my ($self, %args) = @_;
92 0         0 my $long = $args{long_names};
93              
94             my @ops = $self->operations
95             ( service => delete $args{service}
96             , port => delete $args{port}
97             , binding => delete $args{binding}
98 0         0 );
99              
100 0         0 foreach my $op (@ops)
101 0 0       0 { my $alias = $long ? $op->longName : undef;
102 0         0 $self->compileCall($op, alias => $alias, %args);
103             }
104              
105 0         0 $self;
106             }
107              
108              
109             sub compileCall($@)
110 0     0 1 0 { my ($self, $oper, %opts) = @_;
111 0         0 my $alias = delete $opts{alias};
112 0 0       0 my $op = blessed $oper ? $oper : $self->operation($oper, %opts);
113              
114 0   0     0 my $name = $alias || $op->name;
115             error __x"a compiled call for {name} already exists", name => $name
116 0 0       0 if $self->{XCW_ccode}{$name};
117              
118 0   0     0 my $dopts = $self->{XCW_dcopts} || {};
119 0         0 my @opts = %opts;
120 0 0       0 push @opts, ref $dopts eq 'ARRAY' ? @$dopts : %$dopts;
121 0         0 trace "compiling call `$name'";
122 0         0 $self->{XCW_ccode}{$name} = $op->compileClient(@opts);
123             }
124              
125              
126             sub call($@)
127 0     0 1 0 { my ($self, $name) = (shift, shift);
128              
129             my $codes = $self->{XCW_ccode}
130 0 0       0 or error __x"you can only use call() after compileCalls()";
131              
132 0 0       0 my $call = $codes->{$name}
133             or error __x"operation `{name}' is not known", name => $name;
134            
135 0         0 $call->(@_);
136             }
137              
138             #--------------------------
139              
140              
141             sub addWSDL($%)
142 6     6 1 24 { my ($self, $data, %args) = @_;
143 6 50       25 defined $data or return ();
144              
145 6 50       24 if(ref $data eq 'ARRAY')
146 0         0 { $self->addWSDL($_) for @$data;
147 0         0 return $self;
148             }
149              
150 6         28 my ($node, %details) = $self->dataToXML($data);
151 6 50       1947 defined $node or return $self;
152              
153 6 50 33     83 $node->localName eq 'definitions' && $node->namespaceURI eq WSDL11
154             or error __x"root element for WSDL is not 'wsdl:definitions'";
155              
156 6         35 $self->importDefinitions($node, details => \%details);
157 6         9105 $self->learnPrefixes($node);
158              
159 6         260 my $spec = $self->reader('wsdl:definitions')->($node);
160             my $tns = $spec->{targetNamespace}
161 6 50       663312 or error __x"WSDL sets no targetNamespace";
162              
163             # WSDL 1.1 par 2.1.1 says: WSDL def types each in own name-space
164 6         23 my $index = $self->{index};
165              
166             # silly WSDL structure
167 6   50     29 my $toplevels = $spec->{gr_wsdl_anyTopLevelOptionalElement} || [];
168              
169 6         22 foreach my $toplevel (@$toplevels)
170 33         97 { my ($which, $def) = %$toplevel; # always only one
171 33 100       187 $which =~ s/^wsdl_(service|message|binding|portType)$/$1/
172             or next;
173              
174 27         109 $index->{$which}{pack_type $tns, $def->{name}} = $def;
175              
176 27 100       210 if($which eq 'service')
177 5 50       14 { foreach my $port ( @{$def->{wsdl_port} || []} )
  5         27  
178 9     9   85 { my $addr_label = first { /address$/ } keys %$port
179             or error __x"no address in port {port}"
180 5 50       53 , port => $port->{name};
181 5         28 my $first_addr = $port->{$addr_label};
182 5 50       29 $first_addr = $first_addr->[0] if ref $first_addr eq 'ARRAY';
183              
184             # Is XML::Compile::SOAP loaded?
185 5 50       22 ref $first_addr eq 'HASH'
186             or error __x"wsdl namespace {ns} not loaded"
187             , ns => $first_addr->namespaceURI;
188              
189 5         28 $index->{port}{pack_type $tns, $port->{name}} = $port;
190             }
191             }
192             }
193              
194             # no service block when only one port
195 6 50       69 unless($index->{service})
196             { # only from this WSDL, cannot use collective $index
197 0   0     0 my @portTypes = map $_->{wsdl_portType}||(), @$toplevels;
198 0 0       0 @portTypes==1
199             or error __x"no service definition so needs 1 portType, found {nr}"
200             , nr => scalar @portTypes;
201              
202 0   0     0 my @bindings = map $_->{wsdl_binding}||(), @$toplevels;
203 0 0       0 @bindings==1
204             or error __x"no service definition so needs 1 binding, found {nr}"
205             , nr => scalar @bindings;
206              
207 0         0 my $binding = pack_type $tns, $bindings[0]->{name};
208 0         0 my $portname = $portTypes[0]->{name};
209 0         0 my $servname = $portname;
210 0 0       0 $servname =~ s/Service$|(?:Service)?Port(?:Type)?$/Service/i
211             or $servname .= 'Service';
212              
213             my $addr
214             = $bindings[0]->{soap_binding} ? 'soap_address'
215 0 0       0 : $bindings[0]->{soap12_binding} ? 'soap12_address'
    0          
216             : error __x"unrecognized binding type for wsdl without service block";
217              
218 0         0 my %port = (name => $portname, binding => $binding
219             , $addr => {location => 'http://localhost'} );
220              
221 0         0 $index->{service}{pack_type $tns, $servname}
222             = { name => $servname, wsdl_port => [ \%port ] };
223 0         0 $index->{port}{pack_type $tns, $portname} = \%port;
224             }
225             #warn "INDEX: ",Dumper $index;
226 6         50 $self;
227             }
228              
229              
230             sub namesFor($)
231 0     0 1 0 { my ($self, $class) = @_;
232 0 0       0 keys %{shift->index($class) || {}};
  0         0  
233             }
234              
235              
236             # new options, then also add them to the list in compileClient()
237              
238             sub operation(@)
239 9     9 1 88968 { my $self = shift;
240 9 100       50 my $name = @_ % 2 ? shift : undef;
241 9         38 my %args = (name => $name, @_);
242              
243             #
244             ## Service structure
245             #
246              
247 9         43 my $service = $self->findDef(service => delete $args{service});
248              
249 9         24 my $port;
250 9 50       15 my @ports = @{$service->{wsdl_port} || []};
  9         41  
251 9 50   9   63 if(my $not = first {blessed $_} @ports)
  9         48  
252 0         0 { error __x"not all name-spaces loaded, {ns} not parsed in port"
253             , ns => $not->namespaceURI;
254             }
255              
256 9         63 my @portnames = map $_->{name}, @ports;
257 9 100       59 if(my $portname = delete $args{port})
    50          
258 2     2   9 { $port = first {$_->{name} eq $portname} @ports;
  2         6  
259 2 50       8 error __x"cannot find port `{portname}', pick from {ports}"
260             , portname => $portname, ports => join("\n ", '', @portnames)
261             unless $port;
262             }
263             elsif(@ports==1)
264 7         19 { $port = shift @ports;
265             }
266             else
267 0         0 { error __x"specify port explicitly, pick from {portnames}"
268             , portnames => join("\n ", '', @portnames);
269             }
270              
271             # get plugin for operation
272 14 100   14   150 my $address = first { /address$/ && $port->{$_}{location}} keys %$port
273             or error __x"no address provided in service {service} port {port}"
274 9 50       60 , service => $service->{name}, port => $port->{name};
275              
276 9 50       54 if($address =~ m/^{/) # }
277 0         0 { my ($ns) = unpack_type $address;
278              
279 0 0       0 warning __"Since v2.00 you have to require XML::Compile::SOAP11 explicitly"
280             if $ns eq WSDL11SOAP;
281              
282 0         0 error __x"ports of type {ns} not supported (not loaded?)", ns => $ns;
283             }
284              
285             #use Data::Dumper;
286             #warn Dumper $port, $self->prefixes;
287 9         68 my ($prefix) = $address =~ m/(\w+)_address$/;
288 9 50       31 $prefix
289             or error __x"port address not prefixed; probably need to add a plugin XML::Compile::SOAP12";
290              
291 9         90 my $opns = $self->findName("$prefix:");
292 9         299 my $protocol = XML::Compile::SOAP->plugin($opns);
293 9 50       70 unless($protocol)
294 0 0       0 { my $pkg = $opns eq WSDL11SOAP ? 'SOAP11'
    0          
295             : $opns eq WSDL11SOAP12 ? 'SOAP12'
296             : undef;
297              
298 0 0       0 if($pkg)
299 0         0 { error __x"add 'use XML::Compile::{pkg}' to your script", pkg=>$pkg;
300             }
301             else
302 0         0 { notice __x"ignoring unsupported namespace {ns}", ns => $opns;
303 0         0 return;
304             }
305             }
306              
307 9         28 my $opclass = $protocol.'::Operation';
308 9 50       194 $opclass->can('_fromWSDL11')
309             or error __x"WSDL11 not supported by {class}", class => $opclass;
310              
311             #
312             ## Binding
313             #
314              
315             my $bindtype = $port->{binding}
316             or error __x"no binding defined in port '{name}'"
317 9 50       41 , name => $port->{name};
318              
319 9         28 my $binding = $self->findDef(binding => $bindtype);
320              
321             my $type = $binding->{type} # get portTypeType
322 9 50       35 or error __x"no type defined with binding `{name}'"
323             , name => $bindtype;
324              
325 9         25 my $portType = $self->findDef(portType => $type);
326             my $types = $portType->{wsdl_operation}
327 9 50       34 or error __x"no operations defined for portType `{name}'"
328             , name => $type;
329              
330 9         97 my @port_ops = map $_->{name}, @$types;
331              
332 9   66     34 $name ||= delete $args{operation};
333 9         16 my $port_op;
334 9 50       40 if(defined $name)
    0          
335 9     10   70 { $port_op = first {$_->{name} eq $name} @$types;
  10         31  
336 9 100       55 error __x"no operation `{op}' for portType {pt}, pick from{ops}"
337             , op => $name, pt => $type, ops => join("\n ", '', @port_ops)
338             unless $port_op;
339             }
340             elsif(@port_ops==1)
341 0         0 { $port_op = shift @$types;
342 0         0 $name = $port_op->{name};
343             }
344             else
345 0         0 { error __x"multiple operations in portType `{pt}', pick from {ops}"
346             , pt => $type, ops => join("\n ", '', @port_ops)
347             }
348              
349 8 50       20 my @bindops = @{$binding->{wsdl_operation} || []};
  8         44  
350 8     9   42 my $bind_op = first {$_->{name} eq $name} @bindops;
  9         22  
351 8 50       59 $bind_op
352             or error __x"cannot find bind operation for {name}", name => $name;
353              
354             # This should be detected while parsing the WSDL because the order of
355             # input and output is significant (and lost), but WSDL 1.1 simplifies
356             # our life by saying that only 2 out-of 4 predefined types can actually
357             # be used at present.
358              
359 8         16 my @order = map +(unpack_type $_)[1], @{$port_op->{_ELEMENT_ORDER}};
  8         50  
360              
361 8         160 my ($first_in, $first_out);
362 8         53 for(my $i = 0; $i<@order; $i++)
363 16 100 66     75 { $first_in = $i if !defined $first_in && $order[$i] eq 'input';
364 16 100 100     94 $first_out = $i if !defined $first_out && $order[$i] eq 'output';
365             }
366              
367 8 50       52 my $kind
    100          
    50          
368             = !defined $first_in ? 'notification-operation'
369             : !defined $first_out ? 'one-way'
370             : $first_in < $first_out ? 'request-response'
371             : 'solicit-response';
372              
373             #
374             ### message components
375             #
376              
377             my $operation = $opclass->_fromWSDL11
378             ( name => $name,
379             , kind => $kind
380              
381             , service => $service
382             , serv_port => $port
383             , binding => $binding
384             , bind_op => $bind_op
385             , portType => $portType
386             , port_op => $port_op
387              
388             , wsdl => $self
389             , action => $args{action}
390              
391             , server_type => $args{server_type} || $self->{XCW_server}
392 8   33     115 );
393            
394 8         1538 $operation;
395             }
396              
397              
398             sub compileClient(@)
399 0     0 1 0 { my $self = shift;
400 0 0       0 unshift @_, 'operation' if @_ % 2;
401 0 0       0 my $op = $self->operation(@_) or return ();
402              
403 0   0     0 my $dopts = $self->{XCW_dcopts} || {};
404 0 0       0 $op->compileClient(@_, (ref $dopts eq 'ARRAY' ? @$dopts : %$dopts));
405             }
406              
407             #---------------------
408              
409              
410             sub declare($$@)
411 10     10 1 12242 { my ($self, $need, $names, @opts) = @_;
412 10 50       37 my $opts = @opts==1 ? shift @opts : \@opts;
413 10 50       36 $opts = [ %$opts ] if ref $opts eq 'HASH';
414              
415 10 50       73 $need eq 'OPERATION'
416             or $self->SUPER::declare($need, $names, @opts);
417              
418 10 100       1870 foreach my $name (ref $names eq 'ARRAY' ? @$names : $names)
419             { # checking existence of opname is expensive here
420             # and may be problematic with multiple bindings.
421 35         77 $self->{XCW_dcopts}{$name} = $opts;
422             }
423              
424 10         20 $self;
425             }
426              
427             #--------------------------
428              
429              
430             sub index(;$$)
431 53     53 1 77 { my $index = shift->{index};
432 53 50       121 @_ or return $index;
433              
434 53 50       131 my $class = $index->{ (shift) }
435             or return ();
436              
437 53 50       159 @_ ? $class->{ (shift) } : $class;
438             }
439              
440              
441             sub findDef($;$)
442 53     53 1 9268 { my ($self, $class, $name) = @_;
443 53 50       109 my $group = $self->index($class)
444             or error __x"no definitions for `{class}' found", class => $class;
445              
446 53 100       111 if(defined $name)
447 41 100       190 { return $group->{$name} if exists $group->{$name}; # QNAME
448              
449 3 50       17 if($name =~ m/\:/) # PREFIXED
450 0         0 { my $qname = $self->findName($name);
451 0 0       0 return $group->{$qname} if exists $group->{$qname};
452             }
453              
454 3 100   3   21 if(my $q = first { (unpack_type $_)[1] eq $name } keys %$group)
  3         14  
455 2         35 { return $group->{$q};
456             }
457              
458 1         25 error __x"no definition for `{name}' as {class}, pick from:{groups}"
459             , name => $name, class => $class
460             , groups => join("\n ", '', sort keys %$group);
461             }
462              
463 12 100       49 return values %$group
464             if wantarray;
465              
466 9 50       58 return (values %$group)[0]
467             if keys %$group==1;
468 0         0 my @alts = map $self->prefixed($_), sort keys %$group;
469 0         0 error __x"explicit selection required: pick one {class} from {alts}"
470             , class => $class, alts => join("\n ", '', @alts);
471             }
472              
473              
474             sub operations(@)
475 2     2 1 5336 { my ($self, %args) = @_;
476 2 50       9 $args{produce} and die "produce option removed in 0.81";
477              
478 2         2 my @ops;
479 2         5 my @services = $self->findDef('service');
480 2         5 foreach my $service (@services)
481 2         5 { my $sname = $service->{name};
482 2 50 33     7 next if $args{service} && $args{service} ne $sname;
483              
484 2 50       3 my @ports = @{$service->{wsdl_port} || []};
  2         8  
485 2         4 foreach my $port (@ports)
486             {
487 2 50 33     5 next if $args{port} && $args{port} ne $port->{name};
488             my $bindtype = $port->{binding}
489             or error __x"no binding defined in port '{name}'"
490 2 50       10 , name => $port->{name};
491 2         5 my $binding = $self->findDef(binding => $bindtype);
492              
493 2 50 33     6 next if $args{binding} && $args{binding} ne $binding->{name};
494              
495             my $type = $binding->{type}
496 2 50       7 or error __x"no type defined with binding `{name}'"
497             , name => $bindtype;
498              
499 2         2 my %all_ops;
500 2 50       3 foreach my $operation ( @{$binding->{wsdl_operation}||[]} )
  2         7  
501 2         3 { my $opname = $operation->{name};
502              
503 2 50       7 if(my $has = $all_ops{$opname})
504             { error __x"operation {name} found again; choose service {has} or {also}"
505             , name => $opname, has => $has->serviceName
506             , also => $sname
507 0 0 0     0 if @services > 1 && !$args{service};
508              
509             error __x"need one set of operations, pick port from {ports}"
510 0         0 , ports => [ map $_->{name}, @ports ], _join => ', ';
511             }
512            
513             my $op = $all_ops{$opname} = $self->operation
514             ( service => $sname
515             , port => $port->{name}
516             , binding => $bindtype
517             , operation => $opname
518             , portType => $type
519             , server_type => $args{server_type}
520 2         11 );
521              
522 2         8 push @ops, $op;
523             }
524             }
525             }
526              
527 2         7 @ops;
528             }
529              
530              
531             sub endPoint(@)
532 2     2 1 1561 { my ($self, %args) = @_;
533 2         6 my $service = $self->findDef(service => delete $args{service});
534              
535 2         3 my $port;
536 2 50       3 my @ports = @{$service->{wsdl_port} || []};
  2         9  
537 2         9 my @portnames = map $_->{name}, @ports;
538 2 50       10 if(my $portname = delete $args{port})
    50          
539 0     0   0 { $port = first {$_->{name} eq $portname} @ports;
  0         0  
540 0 0       0 error __x"cannot find port `{portname}', pick from {ports}"
541             , portname => $portname, ports => join("\n ", '', @portnames)
542             unless $port;
543             }
544             elsif(@ports==1)
545 2         3 { $port = shift @ports;
546             }
547             else
548 0         0 { error __x"specify port explicitly, pick from {portnames}"
549             , portnames => join("\n ", '', @portnames);
550             }
551              
552 2         8 foreach my $k (keys %$port)
553 2 50       29 { return $port->{$k}{location} if $k =~ m/address$/;
554             }
555              
556 0         0 ();
557             }
558              
559              
560             sub printIndex(@)
561 1     1 1 6829 { my $self = shift;
562 1 50       5 my $fh = @_ % 2 ? shift : select;
563 1         4 my @args = @_;
564              
565 1         2 my %tree;
566 1         4 foreach my $op ($self->operations(@args))
567 1         7 { my $port = $op->version.' port '.$op->portName;
568 1         7 my $bind = '(binding '.$op->bindingName.')';
569 1         8 $tree{'service '.$op->serviceName}{"$port $bind"}{$op->name} = $_;
570             }
571              
572 1         20 foreach my $service (sort keys %tree)
573 1         13 { $fh->print("$service\n");
574 1         11 foreach my $port (sort keys %{$tree{$service}})
  1         4  
575 1         5 { $fh->print(" $port\n");
576 1         7 foreach my $op (sort keys %{$tree{$service}{$port}})
  1         4  
577 1         4 { $fh->print(" $op\n");
578             }
579             }
580             }
581             }
582              
583              
584             sub explain($$$@)
585 0     0 1   { my ($self, $opname, $format, $direction, @opts) = @_;
586 0 0         my $op = $self->operation($opname, @opts)
587             or error __x"explain operation {name} not found", name => $opname;
588 0           $op->explain($self, $format, $direction, @opts);
589             }
590              
591             #--------------------------------
592              
593              
594             1;