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.061";
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
60sub new {
61	my ($pkg, %data) = @_;
62	my $nsnum = 0;
63
64	croak "I need a location, died" unless defined $data{location};
65	croak "I need a file or module name or a filehandle, died" unless defined $data{source};
66
67	$data{use} = $LITERAL_USE if $data{style} and $data{style} eq $DOCUMENT_STYLE and !defined $data{use};
68	$data{use} = $LITERAL_USE and $data{style} = $DOCUMENT_STYLE if $data{wrapped} and !defined $data{use} and !defined $data{style};
69
70	my $me = bless {
71		_source              => $data{source},
72		_baseName            => undef,
73		_methods             => [],
74		_location            => $data{location},
75		_namespaces          => {},
76		_targetNS            => undef,
77		_generateNS          => sub {return $DEFAULT_NS_DECL . $nsnum++},
78		_types               => {},
79		_writer              => new Pod::WSDL::Writer(withDocumentation => $data{withDocumentation}, pretty => $data{pretty}),
80		_standardTypeArrays  => {},
81		_emptymessagewritten => 0,
82		_use                 => $data{use} || $ENCODED_USE,
83		_style               => $data{style} || $RPC_STYLE,
84		_wrapped             => $data{wrapped} || 0,
85	}, $pkg;
86
87	croak "'use' argument may only be one of $ENCODED_USE or $LITERAL_USE, died" if $me->use ne $ENCODED_USE and $me->use ne $LITERAL_USE;
88	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;
89	croak "The combination of use=$ENCODED_USE and style=$DOCUMENT_STYLE is not valid, died" if ($me->style eq $DOCUMENT_STYLE and $me->use eq $ENCODED_USE);
90
91	## AHICOX 10/12/2006
92	## this is a quick and dirty hack to set the baseName
93	## the baseName should probably be set from the POD
94	## source (which is why it's set in _getModuleCode)
95	## this quick hack takes the 'name' parameter when
96	## we create the object, and
97
98	$me->_initSource($data{'source'});
99	$me->_initNS;
100	$me->_initTypes;
101
102	return $me;
103}
104
105sub WSDL {
106	my $me = shift;
107	my %args = @_;
108
109	my $wr = $me->writer;
110	$wr->prepare;
111
112	if (%args) {
113		$wr->pretty($args{pretty}) if defined $args{pretty};
114		$wr->withDocumentation($args{withDocumentation}) if defined $args{withDocumentation};
115	}
116
117	$me->writer->comment("WSDL for " . $me->{_location} . " created by " . ref ($me) . " version: $VERSION on " . scalar localtime);
118	$me->writer->startTag('wsdl:definitions', targetNamespace => $me->targetNS, %{$me->{_namespaces}});
119	$me->writer->wrNewLine(2);
120
121	$me->_writeTypes;
122
123	$_->writeMessages($me->types, $me->style, $me->wrapped) for @{$me->methods};
124
125	$me->_writePortType;
126	$me->_writeBinding;
127	$me->_writeService;
128
129	$me->writer->endTag('wsdl:definitions');
130	$me->writer->end;
131	return $me->writer->output;
132}
133
134sub addNamespace {
135	my $me   = shift;
136	my $uri  = shift;
137	my $decl = shift;
138
139	croak "I need a namespace, died" unless defined $uri;
140
141	defined $decl or $decl = $me->{_generateNS};
142
143	$decl = 'xmlns:' . $decl unless $decl =~ /xmlns:/;
144
145	$me->{_namespaces}->{$decl} = $uri;
146}
147
148# -------------------------------------------------------------------------- #
149# ---------------- > INIT METHODS < ---------------------------------------- #
150# -------------------------------------------------------------------------- #
151
152sub _initNS {
153	my $me         = shift;
154	my $namespaces = shift;
155
156	$namespaces ||= {};
157
158	$me->addNamespace($namespaces->{$_}, $_) for keys %$namespaces;
159	$me->addNamespace($BASIC_NAMESPACES{$_}, $_) for keys %BASIC_NAMESPACES;
160	$me->addNamespace($me->targetNS, $IMPL_NS_DECL);
161	$me->addNamespace($me->targetNS, $TARGET_NS_DECL);
162}
163
164sub _initSource {
165	my $me  = shift;
166	my $src = shift;
167
168	my ($baseName, $contents) = $me->_getModuleCode($src, 1);
169
170	#set the baseName in the object
171	$me->baseName($baseName);
172
173	# find =begin wsdl ... =end
174	while ($contents =~ /$WSDL_METHOD_REGEXP_BEG/g) {
175		$me->_parseMethodPod($2, $1);
176	}
177
178	# find =for wsdl
179	while ($contents =~ /$WSDL_METHOD_REGEXP_FOR/g) {
180		$me->_parseMethodPod($2, $1);
181	}
182}
183
184sub _initTypes {
185	my $me = shift;
186
187
188	for my $method (@{$me->{_methods}}) {
189    for my $param (@{$method->params},$method->return) {
190      next unless $param;
191			unless (exists $XSD_STANDARD_TYPE_MAP{$param->type}) {
192				$me->_addType($param->type, $param->array);
193			} elsif ($param->array) {
194
195				#AHICOX: 10/10/2006
196				#changed to _standardTypeArrays (was singular)
197				$me->{_standardTypeArrays}->{$param->type} = 1;
198			}
199		}
200
201		for my $fault (@{$method->faults}) {
202			unless (exists $XSD_STANDARD_TYPE_MAP{$fault->type}) {
203				$me->_addType($fault->type, 0);
204			}
205		}
206	}
207
208}
209
210sub _addType {
211	my $me    = shift;
212	my $name  = shift;
213	my $array = shift;
214
215	if (exists $me->types->{$name}) {
216		$me->types->{$name}->array($array) if $array;
217		return;
218	}
219
220	my $code = $me->_getModuleCode($name);
221	my $pod = '';
222	my $in = $code;
223	my $out = '';
224
225	# collect =begin wsdl ... =end
226	while ($code =~ /$WSDL_TYPE_REGEXP_BEG/g) {
227		$pod .= "$1\n";
228	}
229
230	# collect =for wsdl
231	while ($code =~ /$WSDL_TYPE_REGEXP_FOR/g) {
232		$pod .= "$1\n";
233	}
234
235	warn "No pod wsdl found for type '$name'.\n" unless $pod;
236
237	my $IN  = new IO::Scalar \$in;
238	my $OUT = new IO::Scalar \$out;
239
240	new Pod::Text()->parse_from_filehandle($IN, $OUT);
241
242	$me->types->{$name} = new Pod::WSDL::Type(name => $name, array => $array, pod => $pod, descr => $out, writer => $me->writer);
243
244	for my $attr (@{$me->types->{$name}->attrs}) {
245		unless (exists $XSD_STANDARD_TYPE_MAP{$attr->type}) {
246			$me->_addType($attr->type, $attr->array);
247		} elsif ($attr->array) {
248
249			#AHICOX: 10/10/2006
250			#changed to _standardTypeArrays (was singular)
251			$me->{_standardTypeArrays}->{$attr->type} = 1;
252		}
253	}
254}
255
256sub _parseMethodPod {
257	my $me         = shift;
258	my $methodName = shift;
259	my $podData    = shift;
260
261	my $method = new Pod::WSDL::Method(name => $methodName, writer => $me->writer);
262
263	my @data = split "\n", $podData;
264
265	# Preprocess wsdl pod: trim all lines and concatenate lines not
266	# beginning with wsdl type tokens to previous line.
267	# Ignore first element, if it does not begin with wsdl type token.
268	for (my $i = $#data; $i >= 0; $i--) {
269
270		if ($data[$i] !~ /^\s*(_INOUT|_IN|_OUT|_RETURN|_DOC|_FAULT|_ONEWAY)/i) {
271			if ($i > 0) {
272				$data[$i - 1] .= " $data[$i]";
273				$data[$i] = '';
274			}
275		}
276	}
277
278	for (@data) {
279		s/\s+/ /g;
280		s/^ //;
281		s/ $//;
282
283		if (/^_(INOUT|IN|OUT)\s+/i) {
284			my $param = new Pod::WSDL::Param($_);
285			$method->addParam($param);
286			$me->standardTypeArrays->{$param->type} = 1 if $param->array and $XSD_STANDARD_TYPE_MAP{$param->type};
287		} elsif (/^_RETURN\s+/i) {
288			my $return = new Pod::WSDL::Return($_);
289			$method->return($return);
290			$me->standardTypeArrays->{$return->type} = 1 if $return->array and $XSD_STANDARD_TYPE_MAP{$return->type};
291		} elsif (/^_DOC\s+/i) {
292			$method->doc(new Pod::WSDL::Doc($_));
293		} elsif (/^_FAULT\s+/i) {
294			$method->addFault(new Pod::WSDL::Fault($_));
295		} elsif (/^_ONEWAY\s*$/i) {
296			$method->oneway(1);
297		}
298	}
299
300	push @{$me->{_methods}}, $method;
301}
302
303sub _getModuleCode {
304	my $me     = shift;
305	my $src    = shift;
306	my $findNS = shift;
307
308	if (ref $src and ($src->isa('IO::Handle') or $src->isa('GLOB'))) {
309		local $/ = undef;
310		my $contents = <$src>;
311		$me->_setTargetNS($contents) if $findNS;
312
313		##AHICOX: 10/12/2006
314		##attempt to construct a base name based on the package
315		my $baseName = $DEFAULT_BASE_NAME;
316		$src =~ /package\s+(.*?)\s*;/s;
317		if ($1){
318			$baseName = $1;
319			$baseName =~ s/::(.)/uc $1/eg;
320		}
321
322		return ($baseName, $contents);
323	} else {
324
325		my $moduleFile;
326
327		if (-e $src) {
328			$moduleFile = $src;
329		} else {
330			my $subDir = $src;
331			$subDir =~ s!::!/!g;
332
333			my @files = map {"$_/$subDir.pm"} @INC;
334
335			my $foundPkg = 0;
336
337			for my $file (@files) {
338				if (-e $file) {
339					$moduleFile = $file;
340					last;
341				}
342			}
343		}
344
345		if ($moduleFile) {
346			open IN, $moduleFile or die "Could not open $moduleFile, died";
347			local $/ = undef;
348			my $contents = <IN>;
349			close IN;
350			$me->_setTargetNS($contents) if $findNS;
351
352			##AHICOX: 10/12/2006
353			##attempt to construct a base name based on the package
354			my $baseName = $DEFAULT_BASE_NAME;
355			$contents =~ /package\s+(.*?)\s*;/s;
356			if ($1){
357				$baseName = $1;
358				$baseName =~ s/::(.)/uc $1/eg;
359			}
360
361			return ($baseName, $contents);
362		} else {
363			die "Can't find any file '$src' and can't locate it as a module in \@INC either (\@INC contains " . join (" ", @INC) . "), died";
364		}
365	}
366}
367
368sub _setTargetNS {
369	my $me = shift;
370	my $contents = shift;
371
372	$contents =~ /package\s+(.*?)\s*;/s;
373
374	if ($1) {
375		my $tmp = $1;
376		$tmp =~ s!::!/!g;
377		my $serverURL = $me->location;
378		$serverURL =~ s!(http(s)??://[^/]*).*!$1!;
379		$me->targetNS("$serverURL/$tmp");
380	} else {
381		$me->targetNS($me->location);
382	}
383}
384
385# -------------------------------------------------------------------------- #
386# -------------- > OUTPUT UTILITIES < -------------------------------------- #
387# -------------------------------------------------------------------------- #
388
389sub _writeTypes {
390	my $me = shift;
391
392	return if keys %{$me->standardTypeArrays} == 0 and keys %{$me->types} == 0;
393
394	$me->writer->wrElem($START_PREFIX_NAME, 'wsdl:types');
395	$me->writer->wrElem($START_PREFIX_NAME, 'schema', targetNamespace => $me->namespaces->{'xmlns:' . $TARGET_NS_DECL}, xmlns => "http://www.w3.org/2001/XMLSchema");
396	$me->writer->wrElem($EMPTY_PREFIX_NAME, "import",  namespace => "http://schemas.xmlsoap.org/soap/encoding/");
397
398	for my $type (sort keys %{$me->standardTypeArrays}) {
399		$me->writer->wrElem($START_PREFIX_NAME, "complexType",  name => $ARRAY_PREFIX_NAME . ucfirst $type);
400		$me->writer->wrElem($START_PREFIX_NAME, "complexContent");
401		$me->writer->wrElem($START_PREFIX_NAME, "restriction",  base => "soapenc:Array");
402		$me->writer->wrElem($EMPTY_PREFIX_NAME, "attribute",  ref => "soapenc:arrayType", "wsdl:arrayType" => 'soapenc:' . $type . '[]');
403		$me->writer->wrElem($END_PREFIX_NAME, "restriction");
404		$me->writer->wrElem($END_PREFIX_NAME, "complexContent");
405		$me->writer->wrElem($END_PREFIX_NAME, "complexType");
406	}
407
408	for my $type (values %{$me->types}) {
409		$type->writeComplexType($me->types);
410	}
411
412	if ($me->style eq $DOCUMENT_STYLE) {
413		for my $method (@{$me->methods}) {
414			$method->writeDocumentStyleSchemaElements($me->types);
415		}
416	}
417
418	$me->writer->wrElem($END_PREFIX_NAME, 'schema');
419	$me->writer->wrElem($END_PREFIX_NAME, 'wsdl:types');
420	$me->writer->wrNewLine;
421}
422
423sub _writePortType {
424	my $me = shift;
425
426	$me->writer->wrElem($START_PREFIX_NAME, 'wsdl:portType', name => $me->baseName . $PORT_TYPE_SUFFIX_NAME);
427
428	for my $method (@{$me->{_methods}}) {
429		$method->writePortTypeOperation;
430		$me->writer->wrNewLine;
431	}
432
433	$me->writer->wrElem($END_PREFIX_NAME, 'wsdl:portType');
434	$me->writer->wrNewLine(1);
435}
436
437sub _writeBinding {
438	my $me = shift;
439
440	$me->writer->wrElem($START_PREFIX_NAME, 'wsdl:binding', name => $me->baseName . $BINDING_SUFFIX_NAME, type => $IMPL_NS_DECL . ':' . $me->baseName . $PORT_TYPE_SUFFIX_NAME);
441	$me->writer->wrElem($EMPTY_PREFIX_NAME, "wsdlsoap:binding", style => $me->style, transport => "http://schemas.xmlsoap.org/soap/http");
442	$me->writer->wrNewLine;
443
444	for my $method (@{$me->methods}) {
445		$method->writeBindingOperation($me->targetNS, $me->use);
446		$me->writer->wrNewLine;
447	}
448
449	$me->writer->wrElem($END_PREFIX_NAME, 'wsdl:binding');
450	$me->writer->wrNewLine;
451}
452
453sub _writeService {
454	my $me = shift;
455
456	$me->writer->wrElem($START_PREFIX_NAME, 'wsdl:service', name => $me->baseName . $PORT_TYPE_SUFFIX_NAME . $SERVICE_SUFFIX_NAME);
457	$me->writer->wrElem($START_PREFIX_NAME, 'wsdl:port', binding => $IMPL_NS_DECL . ':' . $me->baseName . $BINDING_SUFFIX_NAME, name => $me->baseName);
458	$me->writer->wrElem($EMPTY_PREFIX_NAME, "wsdlsoap:address", location => $me->location);
459	$me->writer->wrElem($END_PREFIX_NAME, 'wsdl:port');
460	$me->writer->wrElem($END_PREFIX_NAME, 'wsdl:service');
461
462	$me->writer->wrNewLine;
463}
464
4651;
466__END__
467
468=head1 NAME
469
470Pod::WSDL - Creates WSDL documents from (extended) pod
471
472=head1 SYNOPSIS
473
474  use Pod::WSDL;
475
476  my $pod = new Pod::WSDL(source => 'My::Server',
477    location => 'http://localhost/My/Server',
478    pretty => 1,
479    withDocumentation => 1);
480
481  print $pod->WSDL;
482
483=head1 DESCRIPTION - How to use Pod::WSDL
484
485=head2 Parsing the pod
486
487How 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
488
489  my $pwsdl = new Pod::WSDL(source => 'My::Module',
490	location => 'http://my.services.location/on/the/web');
491
492Pod::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
493
494  $pwsdl->WSDL;
495
496Pod::WSDL will output the WSDL document. That's it.
497
498When using Pod::WSDL, the parser expects you to do the following:
499
500=over 2
501
502=item *
503
504Put 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.
505
506=item *
507
508Use 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.
509
510=back
511
512Any subroutine not preceeded 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).
513
514The 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.
515
516=head2 Describing Methods
517
518How 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.
519
520=head3 Parameters
521
522WSDL 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.
523
524The pod string describing a parameter has the structure
525
526  (_IN|_OUT|_INOUT) NAME ($|@)TYPE DESCRIPTION
527
528like
529
530  _IN foo $string This is a foo
531
532or
533
534  _INOUT bar @bar An array of bars
535
536You 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.
537
538=head3 Return Values
539
540Return 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
541
542  _RETURN ($|@)TYPE DESCRIPTION
543
544as in
545
546  _RETURN $string Returns a string
547
548The 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.
549
550If 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.
551
552=head3 Faults
553
554SOAP 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:
555
556  _FAULT TYPE DESCRIPTION
557
558An example could be the following:
559
560  _FAULT My::Fault If anything goes wrong
561
562Since 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.
563
564As 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.
565
566=head3 Method Documentation
567
568Method documentation is easily explained. Its structure is
569
570  _DOC Here comes my documentation ...
571
572That'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.
573
574=head2 Describing Modules - Using Own Complex Types
575
576Quite 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
577
578  package My::Type;
579
580  sub new {
581    bless {
582      foo => 'foo',
583      bar => -1
584    }, $_[0];
585  }
586
587  1;
588
589simply describe the keys of your blessed hash like this.
590
591  =begin WSDL
592
593    _ATTR foo $string A foo
594    _ATTR bar $integer And a bar
595
596  =end WSDL
597
598Put 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).
599
600If 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.
601
602=head1 METHODS
603
604=head2 new
605
606Instantiates a new Pod::WSDL.
607
608=head3 Parameters
609
610=over 4
611
612=item
613
614source - 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?
615
616=item
617
618location - Target namespace for the WSDL, usually the full URL of your webservice's proxy.
619
620=item
621
622pretty - 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!
623
624=item
625
626withDocumentation - 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!
627
628=back
629
630=head2 WSDL
631
632Returns WSDL as string.
633
634=head3 Parameters
635
636=over 4
637
638=item
639
640pretty - 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!
641
642=item
643
644withDocumentation - 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!
645
646=back
647
648=head2 addNamespace
649
650Adds a namespace. Will be taken up in WSDL's definitions element.
651
652=head3 Parameters
653
654=over 4
655
656=item 1
657
658URI of the namespace
659
660=item 2
661
662Declarator of the namespace
663
664=back
665
666=head1 EXTERNAL DEPENDENCIES
667
668  Carp
669  XML::Writer
670  IO::Scalar
671  Pod::Text
672
673The test scripts use
674
675  XML::XPath
676
677=head1 EXAMPLES
678
679see the *.t files in the distribution
680
681=head1 BUGS
682
683Please send me any bug reports, I will fix them or mention the bugs here :-)
684
685=head1 TODO
686
687=head2 Describe Several Signatures for one Method
688
689Of 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.
690
691=head2 Implement a Better Parsing of the pod
692
693Right 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.
694
695=head2 Handle Several Package Declarations in One File
696
697So 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.
698
699=head2 Handle Array based blessed References
700
701Array based blessed references used for complex types are something of a problem.
702
703=head2 Get Information on Complex Types from Somewhere Else
704
705If 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?
706
707=head2 Integrate Pod::WSDL with SOAP::Lite
708
709With 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.
710
711=head2 Implement Non RPC Style Messages
712
713Pod::WSDL writes WSDL documents in encoded RPC style. It should be able to generate literal RPC and document styles, too.
714
715=head1 REFERENCES
716
717[1] L<http://ws.apache.org/axis/>
718
719[2] L<http://search.cpan.org/~kbrown/SOAP-0.28/>
720
721[3] L<http://search.cpan.org/~byrne/SOAP-Lite-0.65_5/>
722
723[4] L<http://www.w3.org/TR/wsdl.html>
724
725[5] L<http://www.w3.org/TR/xmlschema-2/>
726
727=head1 SEE ALSO
728
729  http://ws.apache.org/axis/
730  http://search.cpan.org/~kbrown/SOAP-0.28/
731  http://search.cpan.org/~byrne/SOAP-Lite-0.65_5/
732  http://www.w3.org/TR/wsdl
733
734  WSDL::Generator (a different way to do it)
735  SOAP::WSDL (the client side)
736  SOAP::Clean::WSDL (I have not tried this)
737
738=head1 AUTHOR
739
740Tarek Ahmed, E<lt>bloerch -the character every email address contains- oelbsk.orgE<gt>
741
742=head1 COPYRIGHT AND LICENSE
743
744Copyright (C) 2006 by Tarek Ahmed
745
746This library is alpha software and comes with no warranty whatsoever.
747It is free software; you can redistribute it and/or modify
748it under the same terms as Perl itself, either Perl version 5.8.5 or,
749at your option, any later version of Perl 5 you may have available.
750
751=cut
752