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