1package Pod::WSDL;
2
3# TODO: make array based objects work as own complex types
4# TODO: non RPC style bindings
5# TODO: read type information alternatively from own file
6# TODO: write soapAction attribute in operations?
7
8use strict;
9use warnings;
10use Carp;
11use IO::Scalar;
12use Pod::Text;
13use Pod::WSDL::Method;
14use Pod::WSDL::Return;
15use Pod::WSDL::Param;
16use Pod::WSDL::Fault;
17use Pod::WSDL::Doc;
18use Pod::WSDL::Type;
19use Pod::WSDL::Writer;
20use Pod::WSDL::Utils qw(:writexml :namespaces :messages :types);
21use Pod::WSDL::AUTOLOAD;
22
23# -------------------------------------------------------------------------- #
24# ------------------ > "CONSTANTS" ----------------------------------------- #
25# -------------------------------------------------------------------------- #
26
27our $VERSION                = "0.063";
28our @ISA                    = qw/Pod::WSDL::AUTOLOAD/;
29
30our $WSDL_METHOD_REGEXP_BEG = qr/^=(?:begin)\s+wsdl\s*\n(.*?)^=(?:cut|end\s+wsdl).*?^\s*sub\s+(\w+)/ims;
31our $WSDL_METHOD_REGEXP_FOR = qr/^=(?:for)\s+wsdl\s*\n(.*?)\n\n^\s*sub\s+(\w+)/ims;
32our $WSDL_TYPE_REGEXP_BEG   = qr/^=(?:begin)\s+wsdl\s*\n(.*?_ATTR.*?)^=(?:cut|end\s+wsdl)/ims;
33our $WSDL_TYPE_REGEXP_FOR   = qr/^=(?:for)\s+wsdl\s*\n(.*?_ATTR.*?)\n\n/ims;
34
35our $DEFAULT_BASE_NAME      = 'myService';
36our $PORT_TYPE_SUFFIX_NAME  = 'Handler';
37our $BINDING_SUFFIX_NAME    = 'SoapBinding';
38our $SERVICE_SUFFIX_NAME    = 'Service';
39
40# Pod::WSDL::AUTOLOAD uses this
41our %FORBIDDEN_METHODS = (
42	source              => {get => 0, set =>  0},
43	source              => {get => 0, set =>  0},
44	baseName            => {get => 0, set =>  0},
45	methods             => {get => 0, set =>  0},
46	location            => {get => 1, set =>  1},
47	namespaces          => {get => 0, set =>  0},
48	generateNS          => {get => 0, set =>  0},
49	types               => {get => 0, set =>  0},
50	writer              => {get => 0, set =>  0},
51	standardTypeArrays  => {get => 0, set =>  0},
52	emptymessagewritten => {get => 0, set =>  0},
53	targetNS            => {get => 1, set =>  1},
54);
55
56# -------------------------------------------------------------------------- #
57# --------------- > PUBLIC METHODS  ---------------------------------------- #
58# -------------------------------------------------------------------------- #
59
60# avoid using reserved words with our autoload methods
61#   use => pw_use; no not break backward compatibility
62sub new {
63	my ($pkg, %data) = @_;
64	my $nsnum = 0;
65
66	croak "I need a location, died" unless defined $data{location};
67	croak "I need a file or module name or a filehandle, died" unless defined $data{source};
68
69    if ( $data{use} ) {
70        $data{pw_use} = delete $data{use} ;
71    }
72	$data{pw_use} = $LITERAL_USE if $data{style} and $data{style} eq $DOCUMENT_STYLE and !defined $data{pw_use};
73	$data{pw_use} = $LITERAL_USE and $data{style} = $DOCUMENT_STYLE if $data{wrapped} and !defined $data{pw_use} and !defined $data{style};
74
75	my $me = bless {
76		_source              => $data{source},
77		_baseName            => undef,
78		_methods             => [],
79		_location            => $data{location},
80		_namespaces          => {},
81		_targetNS            => undef,
82		_generateNS          => sub {return $DEFAULT_NS_DECL . $nsnum++},
83		_types               => {},
84		_writer              => new Pod::WSDL::Writer(withDocumentation => $data{withDocumentation}, pretty => $data{pretty}),
85		_standardTypeArrays  => {},
86		_emptymessagewritten => 0,
87		_pw_use              => $data{pw_use} || $ENCODED_USE,
88		_style               => $data{style} || $RPC_STYLE,
89		_wrapped             => $data{wrapped} || 0,
90	}, $pkg;
91
92	croak "'use' argument may only be one of $ENCODED_USE or $LITERAL_USE, died" if $me->pw_use ne $ENCODED_USE and $me->pw_use ne $LITERAL_USE;
93	croak "'style' argument may only be one of $RPC_STYLE or $DOCUMENT_STYLE, died" if $me->style ne $RPC_STYLE and $me->style ne $DOCUMENT_STYLE;
94	croak "The combination of use=$ENCODED_USE and style=$DOCUMENT_STYLE is not valid, died" if ($me->style eq $DOCUMENT_STYLE and $me->pw_use eq $ENCODED_USE);
95
96	## AHICOX 10/12/2006
97	## this is a quick and dirty hack to set the baseName
98	## the baseName should probably be set from the POD
99	## source (which is why it's set in _getModuleCode)
100	## this quick hack takes the 'name' parameter when
101	## we create the object, and
102
103	$me->_initSource($data{'source'});
104	$me->_initNS;
105	$me->_initTypes;
106
107	return $me;
108}
109
110sub WSDL {
111	my $me = shift;
112	my %args = @_;
113
114	my $wr = $me->writer;
115	$wr->prepare;
116
117	if (%args) {
118		$wr->pretty($args{pretty}) if defined $args{pretty};
119		$wr->withDocumentation($args{withDocumentation}) if defined $args{withDocumentation};
120	}
121
122	$me->writer->comment("WSDL for " . $me->{_location} . " created by " . ref ($me) . " version: $VERSION on " . scalar localtime);
123	$me->writer->startTag('wsdl:definitions', targetNamespace => $me->targetNS, %{$me->{_namespaces}});
124	$me->writer->wrNewLine(2);
125
126	$me->_writeTypes;
127
128	$_->writeMessages($me->types, $me->style, $me->wrapped) for @{$me->methods};
129
130	$me->_writePortType;
131	$me->_writeBinding;
132	$me->_writeService;
133
134	$me->writer->endTag('wsdl:definitions');
135	$me->writer->end;
136	return $me->writer->output;
137}
138
139sub addNamespace {
140	my $me   = shift;
141	my $uri  = shift;
142	my $decl = shift;
143
144	croak "I need a namespace, died" unless defined $uri;
145
146	defined $decl or $decl = $me->{_generateNS};
147
148	$decl = 'xmlns:' . $decl unless $decl =~ /xmlns:/;
149
150	$me->{_namespaces}->{$decl} = $uri;
151}
152
153# -------------------------------------------------------------------------- #
154# ---------------- > INIT METHODS < ---------------------------------------- #
155# -------------------------------------------------------------------------- #
156
157sub _initNS {
158	my $me         = shift;
159	my $namespaces = shift;
160
161	$namespaces ||= {};
162
163	$me->addNamespace($namespaces->{$_}, $_) for keys %$namespaces;
164	$me->addNamespace($BASIC_NAMESPACES{$_}, $_) for keys %BASIC_NAMESPACES;
165	$me->addNamespace($me->targetNS, $IMPL_NS_DECL);
166	$me->addNamespace($me->targetNS, $TARGET_NS_DECL);
167}
168
169sub _initSource {
170	my $me  = shift;
171	my $src = shift;
172
173	my ($baseName, $contents) = $me->_getModuleCode($src, 1);
174
175	#set the baseName in the object
176	$me->baseName($baseName);
177
178	# find =begin wsdl ... =end
179	while ($contents =~ /$WSDL_METHOD_REGEXP_BEG/g) {
180		$me->_parseMethodPod($2, $1);
181	}
182
183	# find =for wsdl
184	while ($contents =~ /$WSDL_METHOD_REGEXP_FOR/g) {
185		$me->_parseMethodPod($2, $1);
186	}
187}
188
189sub _initTypes {
190	my $me = shift;
191
192
193	for my $method (@{$me->{_methods}}) {
194    for my $param (@{$method->params},$method->return) {
195      next unless $param;
196			unless (exists $XSD_STANDARD_TYPE_MAP{$param->type}) {
197				$me->_addType($param->type, $param->array);
198			} elsif ($param->array) {
199
200				#AHICOX: 10/10/2006
201				#changed to _standardTypeArrays (was singular)
202				$me->{_standardTypeArrays}->{$param->type} = 1;
203			}
204		}
205
206		for my $fault (@{$method->faults}) {
207			unless (exists $XSD_STANDARD_TYPE_MAP{$fault->type}) {
208				$me->_addType($fault->type, 0);
209			}
210		}
211	}
212
213}
214
215sub _addType {
216	my $me    = shift;
217	my $name  = shift;
218	my $array = shift;
219
220	if (exists $me->types->{$name}) {
221		$me->types->{$name}->array($array) if $array;
222		return;
223	}
224
225	my $code = $me->_getModuleCode($name);
226	my $pod = '';
227	my $in = $code;
228	my $out = '';
229
230	# collect =begin wsdl ... =end
231	while ($code =~ /$WSDL_TYPE_REGEXP_BEG/g) {
232		$pod .= "$1\n";
233	}
234
235	# collect =for wsdl
236	while ($code =~ /$WSDL_TYPE_REGEXP_FOR/g) {
237		$pod .= "$1\n";
238	}
239
240	warn "No pod wsdl found for type '$name'.\n" unless $pod;
241
242	my $IN  = new IO::Scalar \$in;
243	my $OUT = new IO::Scalar \$out;
244
245	new Pod::Text()->parse_from_filehandle($IN, $OUT);
246
247	$me->types->{$name} = new Pod::WSDL::Type(name => $name, array => $array, pod => $pod, descr => $out, writer => $me->writer);
248
249	for my $attr (@{$me->types->{$name}->attrs}) {
250		unless (exists $XSD_STANDARD_TYPE_MAP{$attr->type}) {
251			$me->_addType($attr->type, $attr->array);
252		} elsif ($attr->array) {
253
254			#AHICOX: 10/10/2006
255			#changed to _standardTypeArrays (was singular)
256			$me->{_standardTypeArrays}->{$attr->type} = 1;
257		}
258	}
259}
260
261sub _parseMethodPod {
262	my $me         = shift;
263	my $methodName = shift;
264	my $podData    = shift;
265
266	my $method = new Pod::WSDL::Method(name => $methodName, writer => $me->writer);
267
268	my @data = split "\n", $podData;
269
270	# Preprocess wsdl pod: trim all lines and concatenate lines not
271	# beginning with wsdl type tokens to previous line.
272	# Ignore first element, if it does not begin with wsdl type token.
273	for (my $i = $#data; $i >= 0; $i--) {
274
275		if ($data[$i] !~ /^\s*(_INOUT|_IN|_OUT|_RETURN|_DOC|_FAULT|_ONEWAY)/i) {
276			if ($i > 0) {
277				$data[$i - 1] .= " $data[$i]";
278				$data[$i] = '';
279			}
280		}
281	}
282
283	for (@data) {
284		s/\s+/ /g;
285		s/^ //;
286		s/ $//;
287
288		if (/^_(INOUT|IN|OUT)\s+/i) {
289			my $param = new Pod::WSDL::Param($_);
290			$method->addParam($param);
291			$me->standardTypeArrays->{$param->type} = 1 if $param->array and $XSD_STANDARD_TYPE_MAP{$param->type};
292		} elsif (/^_RETURN\s+/i) {
293			my $return = new Pod::WSDL::Return($_);
294			$method->return($return);
295			$me->standardTypeArrays->{$return->type} = 1 if $return->array and $XSD_STANDARD_TYPE_MAP{$return->type};
296		} elsif (/^_DOC\s+/i) {
297			$method->doc(new Pod::WSDL::Doc($_));
298		} elsif (/^_FAULT\s+/i) {
299			$method->addFault(new Pod::WSDL::Fault($_));
300		} elsif (/^_ONEWAY\s*$/i) {
301			$method->oneway(1);
302		}
303	}
304
305	push @{$me->{_methods}}, $method;
306}
307
308sub _getModuleCode {
309	my $me     = shift;
310	my $src    = shift;
311	my $findNS = shift;
312
313	if (ref $src and ($src->isa('IO::Handle') or $src->isa('GLOB'))) {
314		local $/ = undef;
315		my $contents = <$src>;
316		$me->_setTargetNS($contents) if $findNS;
317
318		##AHICOX: 10/12/2006
319		##attempt to construct a base name based on the package
320		my $baseName = $DEFAULT_BASE_NAME;
321		$src =~ /package\s+(.*?)\s*;/s;
322		if ($1){
323			$baseName = $1;
324			$baseName =~ s/::(.)/uc $1/eg;
325		}
326
327		return ($baseName, $contents);
328	} else {
329
330		my $moduleFile;
331
332		if (-e $src) {
333			$moduleFile = $src;
334		} else {
335			my $subDir = $src;
336			$subDir =~ s!::!/!g;
337
338			my @files = map {"$_/$subDir.pm"} @INC;
339
340			my $foundPkg = 0;
341
342			for my $file (@files) {
343				if (-e $file) {
344					$moduleFile = $file;
345					last;
346				}
347			}
348		}
349
350		if ($moduleFile) {
351			open IN, $moduleFile or die "Could not open $moduleFile, died";
352			local $/ = undef;
353			my $contents = <IN>;
354			close IN;
355			$me->_setTargetNS($contents) if $findNS;
356
357			##AHICOX: 10/12/2006
358			##attempt to construct a base name based on the package
359			my $baseName = $DEFAULT_BASE_NAME;
360			$contents =~ /package\s+(.*?)\s*;/s;
361			if ($1){
362				$baseName = $1;
363				$baseName =~ s/::(.)/uc $1/eg;
364			}
365
366			return ($baseName, $contents);
367		} else {
368			die "Can't find any file '$src' and can't locate it as a module in \@INC either (\@INC contains " . join (" ", @INC) . "), died";
369		}
370	}
371}
372
373sub _setTargetNS {
374	my $me = shift;
375	my $contents = shift;
376
377	$contents =~ /package\s+(.*?)\s*;/s;
378
379	if ($1) {
380		my $tmp = $1;
381		$tmp =~ s!::!/!g;
382		my $serverURL = $me->location;
383		$serverURL =~ s!(http(s)??://[^/]*).*!$1!;
384		$me->targetNS("$serverURL/$tmp");
385	} else {
386		$me->targetNS($me->location);
387	}
388}
389
390# -------------------------------------------------------------------------- #
391# -------------- > OUTPUT UTILITIES < -------------------------------------- #
392# -------------------------------------------------------------------------- #
393
394sub _writeTypes {
395	my $me = shift;
396
397	return if keys %{$me->standardTypeArrays} == 0 and keys %{$me->types} == 0;
398
399	$me->writer->wrElem($START_PREFIX_NAME, 'wsdl:types');
400	$me->writer->wrElem($START_PREFIX_NAME, 'schema', targetNamespace => $me->namespaces->{'xmlns:' . $TARGET_NS_DECL}, xmlns => "http://www.w3.org/2001/XMLSchema");
401	$me->writer->wrElem($EMPTY_PREFIX_NAME, "import",  namespace => "http://schemas.xmlsoap.org/soap/encoding/");
402
403	for my $type (sort keys %{$me->standardTypeArrays}) {
404		$me->writer->wrElem($START_PREFIX_NAME, "complexType",  name => $ARRAY_PREFIX_NAME . ucfirst $type);
405		$me->writer->wrElem($START_PREFIX_NAME, "complexContent");
406		$me->writer->wrElem($START_PREFIX_NAME, "restriction",  base => "soapenc:Array");
407		$me->writer->wrElem($EMPTY_PREFIX_NAME, "attribute",  ref => "soapenc:arrayType", "wsdl:arrayType" => 'soapenc:' . $type . '[]');
408		$me->writer->wrElem($END_PREFIX_NAME, "restriction");
409		$me->writer->wrElem($END_PREFIX_NAME, "complexContent");
410		$me->writer->wrElem($END_PREFIX_NAME, "complexType");
411	}
412
413	for my $type (values %{$me->types}) {
414		$type->writeComplexType($me->types);
415	}
416
417	if ($me->style eq $DOCUMENT_STYLE) {
418		for my $method (@{$me->methods}) {
419			$method->writeDocumentStyleSchemaElements($me->types);
420		}
421	}
422
423	$me->writer->wrElem($END_PREFIX_NAME, 'schema');
424	$me->writer->wrElem($END_PREFIX_NAME, 'wsdl:types');
425	$me->writer->wrNewLine;
426}
427
428sub _writePortType {
429	my $me = shift;
430
431	$me->writer->wrElem($START_PREFIX_NAME, 'wsdl:portType', name => $me->baseName . $PORT_TYPE_SUFFIX_NAME);
432
433	for my $method (@{$me->{_methods}}) {
434		$method->writePortTypeOperation;
435		$me->writer->wrNewLine;
436	}
437
438	$me->writer->wrElem($END_PREFIX_NAME, 'wsdl:portType');
439	$me->writer->wrNewLine(1);
440}
441
442sub _writeBinding {
443	my $me = shift;
444
445	$me->writer->wrElem($START_PREFIX_NAME, 'wsdl:binding', name => $me->baseName . $BINDING_SUFFIX_NAME, type => $IMPL_NS_DECL . ':' . $me->baseName . $PORT_TYPE_SUFFIX_NAME);
446	$me->writer->wrElem($EMPTY_PREFIX_NAME, "wsdlsoap:binding", style => $me->style, transport => "http://schemas.xmlsoap.org/soap/http");
447	$me->writer->wrNewLine;
448
449	for my $method (@{$me->methods}) {
450		$method->writeBindingOperation($me->targetNS, $me->pw_use);
451		$me->writer->wrNewLine;
452	}
453
454	$me->writer->wrElem($END_PREFIX_NAME, 'wsdl:binding');
455	$me->writer->wrNewLine;
456}
457
458sub _writeService {
459	my $me = shift;
460
461	$me->writer->wrElem($START_PREFIX_NAME, 'wsdl:service', name => $me->baseName . $PORT_TYPE_SUFFIX_NAME . $SERVICE_SUFFIX_NAME);
462	$me->writer->wrElem($START_PREFIX_NAME, 'wsdl:port', binding => $IMPL_NS_DECL . ':' . $me->baseName . $BINDING_SUFFIX_NAME, name => $me->baseName);
463	$me->writer->wrElem($EMPTY_PREFIX_NAME, "wsdlsoap:address", location => $me->location);
464	$me->writer->wrElem($END_PREFIX_NAME, 'wsdl:port');
465	$me->writer->wrElem($END_PREFIX_NAME, 'wsdl:service');
466
467	$me->writer->wrNewLine;
468}
469
4701;
471__END__
472
473=head1 NAME
474
475Pod::WSDL - Creates WSDL documents from (extended) pod
476
477=head1 SYNOPSIS
478
479  use Pod::WSDL;
480
481  my $pod = new Pod::WSDL(source => 'My::Server',
482    location => 'http://localhost/My/Server',
483    pretty => 1,
484    withDocumentation => 1);
485
486  print $pod->WSDL;
487
488=head1 DESCRIPTION - How to use Pod::WSDL
489
490=head2 Parsing the pod
491
492How does Pod::WSDL work? If you instantiate a Pod::WSDL object with the name of the module (or the path of the file, or an open filehandle) providing the web service like this
493
494  my $pwsdl = new Pod::WSDL(source => 'My::Module',
495	location => 'http://my.services.location/on/the/web');
496
497Pod::WSDL will try to find C<My::Module> in C<@INC>, open the file, parse it for WSDL directives and prepare the information for WSDL output. By calling
498
499  $pwsdl->WSDL;
500
501Pod::WSDL will output the WSDL document. That's it.
502
503When using Pod::WSDL, the parser expects you to do the following:
504
505=over 2
506
507=item *
508
509Put the pod directly above the subroutines which the web service's client is going to call. There may be whitespace between the pod and the sub declaration but nothing else.
510
511=item *
512
513Use the C<=begin>/C<=end> respectively the C<=for> directives according to standard pod: anything between C<=begin WSDL> and C<=end> will be treated as pod. Anything composing a paragraph together with C<=for WSDL> will be treated as pod.
514
515=back
516
517Any subroutine not preceded by WSDL pod will be left unmentioned. Any standard pod will be ignored (though, for an exception to this, see the section on own complex types below).
518
519The individual instructions for Pod::WSDL always begin with a keyword, like C<_RETURN> or C<_DOC> or C<_FAULT>. After this different things may follow, according to the specific type of instruction. The instruction may take one or more lines - everything up to the next line beginning with a keyword or the end of the pod is belonging to the current instruction.
520
521=head2 Describing Methods
522
523How do we use Pod::WSDL? In describing a web service's method we have to say something about parameters, return values and faults. In addition you might want to add some documentation to these items and to the method itself.
524
525=head3 Parameters
526
527WSDL differentiates between in-, out- and inout-parameters, so we do that, too. A different matter is the question, if the client can do this too, but now we are talking about possibilities, not actualities.
528
529The pod string describing a parameter has the structure
530
531  (_IN|_OUT|_INOUT) NAME ($|@)TYPE DESCRIPTION
532
533like
534
535  _IN foo $string This is a foo
536
537or
538
539  _INOUT bar @bar An array of bars
540
541You will easily guess what C<_IN>, C<_OUT> and C<_INOUT> stand for so we can move on. C<NAME> is the name of your parameter. It does not have any real function (the order of the parameters being the only important thing) but it is nice to have it since in a WSDL document the parameters need to have names. So instead of having Pod::WSDL automatically generate cryptic names (it cannot do that right now) be nice to the client and use some sensible name. The C<TYPE> of the parameters can be any of the xsd (schema) standard types (see [5]) or a type of your own creation. The C<$> resp. C<@> symbols tell Pod::WSDL and your client if it is a scalar or array parameter. Everything following the type up to the next instruction is treated as the parameter's documentation. If you call the constructor of Pod::WSDL with the argument C<withDocumentation =E<gt> 1>, it will be added to the WSDL.
542
543=head3 Return Values
544
545Return values work like parameters but since in WSDL there is provision for only one return value (you have (in)out parameters, or can return arrays if that isn't enough), you do not need to give them a name. Pod::WSDL will automatically call them 'Return' in the WSDL document. So, the structure of C<_RETURN> instructions is
546
547  _RETURN ($|@)TYPE DESCRIPTION
548
549as in
550
551  _RETURN $string Returns a string
552
553The pod for one method may only have one C<_RETURN> instruction. If you don't specify a C<_RETURN> instruction, Pod::WSDL will assume that you return void. Of course the perl subroutine still will return something, but your web service won't. To make this clear Pod::WSDL generates an empty response message for this.
554
555If you want some method to be a one way operation (see [4], ch. 2.4.1), say so by using the instruction C<_ONEWAY> in the pod. In this case no response message will be generated and a C<_RETURN> instruction will be ignored.
556
557=head3 Faults
558
559SOAP faults are usually translated into exceptions in languages like Java. If you set up a web service using SOAP::Lite, SOAP will trap your dying program and generate a generic fault using the message of C<die>. It is also possible to access SOAP::Lite's SOAP::Fault directly if you want more control - but this is not our issue. If you want to use custom-made fault messages of your own, define them in C<_FAULT> instructions, which look like this:
560
561  _FAULT TYPE DESCRIPTION
562
563An example could be the following:
564
565  _FAULT My::Fault If anything goes wrong
566
567Since you probably won't return an array of fault objects, you do not need to use the C<($|@)> tokens. Just say that you return a fault, declare its type and add an optional description.
568
569As with parameters (but in contrary to C<_RETURN> instructions) you can declare as many C<_FAULT> instructions as you like, providing for different exception types your method might throw.
570
571=head3 Method Documentation
572
573Method documentation is easily explained. Its structure is
574
575  _DOC Here comes my documentation ...
576
577That's it. Use several lines of documentation if you like. If you instantiate the Pod::WSDL object with the parameter C<withDocumentation =E<gt> 1>, it will be written into the WSDL document.
578
579=head2 Describing Modules - Using Own Complex Types
580
581Quite often it will be the case that you have to use complex types as parameters or return values. One example of this we saw when talking about faults: you might want to create custom fault types (exceptions) of your own to fullfill special needs in the communication between web service and client. But of course you also might simply want to pass a complex parameter like a address object containing customer data to your application. WSDL provides the means to describe complex types borrowing the xsd schema syntax. Pod::WSDL makes use of this by allowing you to add WSDL pod to your own types. Assuming you have some own type like
582
583  package My::Type;
584
585  sub new {
586    bless {
587      foo => 'foo',
588      bar => -1
589    }, $_[0];
590  }
591
592  1;
593
594simply describe the keys of your blessed hash like this.
595
596  =begin WSDL
597
598    _ATTR foo $string A foo
599    _ATTR bar $integer And a bar
600
601  =end WSDL
602
603Put this pod anywhere within the package My::Type. Pod::WSDL will find it (if it is in @INC), parse it and integrate it into the WSDL document. The C<_ATTR> instruction works exactly as the C<_IN>, C<_OUT> and C<_INOUT> instructions for methods (see above).
604
605If you initialize the Pod::WSDL object using C<withDocumentation =E<gt> 1>, Pod::WSDL will look for standard pod in the module, parse it using Pod::Text and put it into the WSDL document.
606
607=head1 METHODS
608
609=head2 new
610
611Instantiates a new Pod::WSDL.
612
613=head3 Parameters
614
615=over 4
616
617=item
618
619source - Name of the source file, package of the source module or file handle on source file for which the WSDL shall be generated. This source must contain specialized Pod tags. So, if your source is '/some/directory/modules/Foo/Bar.pm' with package declaration 'Foo::Bar', source may be '/some/directory/modules/Foo/Bar.pm' or 'Foo::Bar' (in which case '/some/directory/modules' has to be in @INC) or an open file handle on the file. Right?
620
621=item
622
623location - Target namespace for the WSDL, usually the full URL of your webservice's proxy.
624
625=item
626
627pretty - Pretty print WSDL, if true. Otherwise the WSDL will come out in one line. The software generating the client stubs might not mind, but a person reading the WSDL will!
628
629=item
630
631withDocumentation - If true, put available documentation in the WSDL (see "Pod Syntax" above). For used own complex types ('modules') this will be the output of Pod::Text on these modules. The software generating the client stubs might give a damn, but a person reading the WSDL won't!
632
633=back
634
635=head2 WSDL
636
637Returns WSDL as string.
638
639=head3 Parameters
640
641=over 4
642
643=item
644
645pretty - Pretty print WSDL, if true. Otherwise the WSDL will come out in one line. The software generating the client stubs might not mind, but a person reading the WSDL will!
646
647=item
648
649withDocumentation - If true, put available documentation in the WSDL (see "Pod Syntax" above). For used own complex types ('modules') this will be the output of Pod::Text on these modules. The software generating the client stubs might give a damn, but a person reading the WSDL won't!
650
651=back
652
653=head2 addNamespace
654
655Adds a namespace. Will be taken up in WSDL's definitions element.
656
657=head3 Parameters
658
659=over 4
660
661=item 1
662
663URI of the namespace
664
665=item 2
666
667Declarator of the namespace
668
669=back
670
671=head1 EXTERNAL DEPENDENCIES
672
673  Carp
674  XML::Writer
675  IO::Scalar
676  Pod::Text
677
678The test scripts use
679
680  XML::XPath
681
682=head1 EXAMPLES
683
684see the *.t files in the distribution
685
686=head1 BUGS
687
688Please send me any bug reports, I will fix them or mention the bugs here :-)
689
690=head1 TODO
691
692=head2 Describe Several Signatures for one Method
693
694Of course, one subroutine declaration might take a lot of different sets of parameters. In Java or C++ you would have to have several methods with different signatures. In perl you fix this within the method. So why not put several WSDL pod blocks above the method so the web service's client can handle that.
695
696=head2 Implement a Better Parsing of the pod
697
698Right know, the pod is found using some rather complex regular expressions. This is evil and will certainly fail in some situations. So, an issue on top of the fixme list is to switch to regular parsing. I'm not sure if I can use Pod::Parser since I need the sub declaration outside the pod, too.
699
700=head2 Handle Several Package Declarations in One File
701
702So far, Pod::WSDL assumes a one to one relation between packages and files. If it meets several package declarations in one file, it will fail some way or the other. For most uses, one package in one file will presumably suffice, but it would be nice to be able to handle the other cases, too.
703
704=head2 Handle Array based blessed References
705
706Array based blessed references used for complex types are something of a problem.
707
708=head2 Get Information on Complex Types from Somewhere Else
709
710If you use complex types for parameters that are not your own (we assume, that the module containing the web service always is your own), you might not be able to put the WSDL pod into the module files. So why not fetch it from somewhere else like a configuration file?
711
712=head2 Integrate Pod::WSDL with SOAP::Lite
713
714With Axis, you simply call the web service's URL with the parameter '?wsdl' and you get the WSDL document. It would be nice to be able to do this with SOAP::Lite, too.
715
716=head2 Implement Non RPC Style Messages
717
718Pod::WSDL writes WSDL documents in encoded RPC style. It should be able to generate literal RPC and document styles, too.
719
720=head1 REFERENCES
721
722[1] L<http://ws.apache.org/axis/>
723
724[2] L<http://search.cpan.org/~kbrown/SOAP-0.28/>
725
726[3] L<http://search.cpan.org/~byrne/SOAP-Lite-0.65_5/>
727
728[4] L<http://www.w3.org/TR/wsdl.html>
729
730[5] L<http://www.w3.org/TR/xmlschema-2/>
731
732=head1 SEE ALSO
733
734  http://ws.apache.org/axis/
735  http://search.cpan.org/~kbrown/SOAP-0.28/
736  http://search.cpan.org/~byrne/SOAP-Lite-0.65_5/
737  http://www.w3.org/TR/wsdl
738
739  WSDL::Generator (a different way to do it)
740  SOAP::WSDL (the client side)
741  SOAP::Clean::WSDL (I have not tried this)
742
743=head1 AUTHOR
744
745Tarek Ahmed, E<lt>bloerch -the character every email address contains- oelbsk.orgE<gt>
746
747=head1 COPYRIGHT AND LICENSE
748
749Copyright (C) 2006 by Tarek Ahmed
750
751This library is alpha software and comes with no warranty whatsoever.
752It is free software; you can redistribute it and/or modify
753it under the same terms as Perl itself, either Perl version 5.8.5 or,
754at your option, any later version of Perl 5 you may have available.
755
756=cut
757