[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