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