[Xml-compile] Generating WSDL

Drew Taylor drew at drewtaylor.com
Thu Nov 13 10:13:54 GMT 2008


On 13 Nov 2008, at 09:46, Zbigniew Lukasiak wrote:

> We have tried to reuse the WSDL files from our legacy Java server in
> the new Perl implementation - but that turned to be difficult (and
> impossible automatically - because it uses RPC style calls).  Now the
> plan is to generate the WSDL file for our services.  I've tried to
> google for solutions to this problem and I found:
>
> 1) http://search.cpan.org/~pdenis/WSDL-Generator-0.02/lib/WSDL/Generator.pm
> - development of this one stopped in 2002 and does not work in 5.8 -
> rather scary
>
> 2) http://search.cpan.org/~tareka/Pod-WSDL-0.05/lib/Pod/WSDL.pm -
> looks nice but generates RPC style calls - which are problematic with
> XML-Compile as I understand.

At $work we're using document literal style, and luckily the schemas  
we're using turned out to be easy enough to generate from a simple  
little script which builds up the various pieces of the WSDL together.  
I know I've shared this script somewhere before, but can't remember if  
it was here or on the Catalyst list.  It's nothing special, and makes  
assumptions based on the schema file names. That may or may not work  
for you, but perhaps it will give you a jump start.

In my case, the schema filenames are like: TTI_PkgCostRQ.xsd,  
TTI_PkgCostRS.xsd, TTI_PkgBookRQ.xsd, TTI_PkgBookRS.xsd, etc which  
makes for easy autogeneration. :-) And yes, I know that names are  
independent within each type, but it helps my poor brain keep them all  
together if I include the type in the name.

Drew

The relevant portions of my WSDL generator are below:

----------8<---------------------8<----------------------
use FindBin qw($Bin);
use Path::Class qw(dir file);
use Getopt::Long;
use File::Copy;
use File::Path;
use Pod::Usage;
my @operations = qw(OTA_Ping TTI_ProductSearch TTI_PkgAvail  
TTI_PkgCost TTI_PkgBook);

# find schemas to <include>
# RQ/RS pair for each operation
my $schema_dir = dir($opt{schema_dir});
my @schemas;
foreach my $op (@operations)
{
     push @schemas,
         file($schema_dir, $op.'RQ.xsd')->basename,
         file($schema_dir, $op.'RS.xsd')->basename;
}

$schema_dir = dir($schema_dir);
print "Schema dir: $schema_dir\n" if $verbose;
foreach my $file ($schema_dir->children)
{
     next if $file->is_dir;                       # CVS, etc
     my $basename = $file->basename();
     next if $basename !~ /.xsd$/;                # schema files only
     next if $basename =~ /(RQ|RS).xsd$/;         # already included
     push @schemas, $basename;
}
$opt{schemas} = \@schemas;

print "Writing $opt{filename} with schemas:\n  " . join("\n  ",  
@schemas) . "\n";
print "and operations:" . join("\n  ", @operations) . "\n\n\n";

write_static_files();

sub write_static_files
{
     my $wsdl = make_wsdl();
     # print $wsdl if $verbose;

     my $wsdl_dir = "$Bin/../webservices/root/static/wsdl/v 
$opt{version}";
     mkpath($wsdl_dir) unless -d $wsdl_dir;
     my $wsdl_file = "$wsdl_dir/$opt{filename}";
     print "Writing WSDL to $wsdl_file\n" if $verbose;
     open(my $FH, ">", $wsdl_file) || die "Error opening file  
($wsdl_file): $!";
     print $FH $wsdl;
     close $FH;

     foreach my $schema (@schemas)
     {
         print "Copying schema $schema to $wsdl_dir\n" if $verbose;
         copy(file($opt{schema_dir}, $schema), file($wsdl_dir,  
$schema));
     }
}

sub make_wsdl
{
     my $wsdl = make_header();
     $wsdl .= make_types(@{ $opt{schemas} });
     foreach my $op (@operations)
     {
         $wsdl .= make_message_pair($op);
         $wsdl .= make_port_type($op);
     }
     # bindings & services at bottom so we can split out easily
     foreach my $op (@operations)
     {
         $wsdl .= make_binding($op);
     }
     $wsdl .= make_service();
     $wsdl .= make_footer();

     return $wsdl;
}

sub make_header
{
     return <<"END_XML";
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
                   xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/ 
soap/"
                   xmlns:schema="$opt{namespace}"
                   xmlns:tns="$opt{namespace}"
                   name="$opt{name}"
                   targetNamespace="$opt{namespace}">
END_XML
}

sub make_footer
{
     return <<"END_XML";
</wsdl:definitions>
END_XML
}

sub make_types
{
     my @schemas = @_;
     my $base_url = $opt{host}.'/static/wsdl/v'.$opt{version};
     my $includes;
     $includes .= qq{        <xsd:include schemaLocation="${base_url}/ 
$_"/>\n} foreach @schemas;
     chomp $includes;

     return <<"END_XML";
     <wsdl:types>
$includes
     </wsdl:types>
END_XML
}

sub make_message_pair
{
     my $name = shift;

     return <<"END_XML";
     <wsdl:message name="${name}RQMessage">
        <wsdl:part name="body" element="schema:${name}RQ"/>
     </wsdl:message>
     <wsdl:message name="${name}RSMessage">
        <wsdl:part name="body" element="schema:${name}RS"/>
     </wsdl:message>
END_XML
}

sub make_port_type
{
     my $name = shift;

     return <<"END_XML";
     <wsdl:portType name="${name}PortType">
        <wsdl:operation name="${name}">
           <wsdl:input name="${name}" message="tns:${name}RQMessage"/>
           <wsdl:output name="${name}Response" message="tns:$ 
{name}RSMessage"/>
        </wsdl:operation>
     </wsdl:portType>
END_XML
}

sub make_binding
{
     my $name = shift;

     return <<"END_XML";
     <wsdl:binding name="${name}Binding" type="tns:${name}PortType">
        <wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http 
"/>
        <wsdl:operation name="${name}">
           <wsdlsoap:operation soapAction="${name}"/>
           <wsdl:input name="${name}">
              <wsdlsoap:body use="literal"/>
           </wsdl:input>
           <wsdl:output name="${name}Response">
              <wsdlsoap:body use="literal"/>
           </wsdl:output>
        </wsdl:operation>
     </wsdl:binding>
END_XML
}

sub make_service
{
     my $port_list;
     foreach my $op (@operations)
     {
         $port_list .= <<"END_XML";
         <wsdl:port name="${op}Port" binding="tns:${op}Binding">
            <wsdlsoap:address location="$opt{host}$opt{path}/v 
$opt{version}/${op}"/>
         </wsdl:port>
END_XML
     }
     chomp $port_list;

     return <<"END_XML";
     <wsdl:service name="$opt{name}Service">
$port_list
     </wsdl:service>
END_XML
}



More information about the Xml-compile mailing list