Compare commits

...
1 Commits
Author SHA1 Message Date
Martin Kutter 25548e6296 import SOAP-WSDL 2.00_06 from CPAN
git-cpan-module:   SOAP-WSDL
git-cpan-version:  2.00_06
git-cpan-authorid: MKUTTER
git-cpan-file:     authors/id/M/MK/MKUTTER/SOAP-WSDL-2.00_06.tar.gz
2009-12-12 19:47:44 -08:00
45 changed files with 2227 additions and 844 deletions
+14 -7
View File
@@ -2,17 +2,21 @@ use Module::Build;
Module::Build->new( Module::Build->new(
dist_abstract => 'SOAP with WSDL support', dist_abstract => 'SOAP with WSDL support',
dist_name => 'SOAP-WSDL', dist_name => 'SOAP-WSDL',
dist_version => '2.00_05', dist_version => '2.00_06',
module_name => 'SOAP::WSDL', module_name => 'SOAP::WSDL',
license => 'artistic', license => 'artistic',
requires => { requires => {
'Class::Std' => q/v0.0.8/, 'Class::Std' => q/v0.0.8/,
'Class::Std::Storable' => 0, 'Class::Std::Storable' => 0,
'SOAP::Lite' => 0, 'SOAP::Lite' => 0,
'XML::XPath' => 0, 'List::Util' => 0,
'XML::LibXML' => 0, 'File::Basename' => 0,
'XML::SAX::Base' => 0, 'File::Path' => 0,
'XML::SAX::ParserFactory' => 0, 'XML::XPath' => 0,
'XML::LibXML' => 0,
'XML::SAX::Base' => 0,
'XML::SAX::ParserFactory' => 0,
'XML::Parser::Expat' => 0,
}, },
buildrequires => { buildrequires => {
'Benchmark' => 0, 'Benchmark' => 0,
@@ -21,14 +25,17 @@ Module::Build->new(
'SOAP::Lite' => 0, 'SOAP::Lite' => 0,
'Class::Std' => 0.0.8, 'Class::Std' => 0.0.8,
'Class::Std::Storable' => 0, 'Class::Std::Storable' => 0,
'List::Util' => 0,
'File::Basename' => 0,
'File::Path' => 0,
'XML::XPath' => 0, 'XML::XPath' => 0,
'XML::Simple' => 0, 'XML::Simple' => 0,
'XML::LibXML' => 0, 'XML::LibXML' => 0,
'XML::Parser::Expat' => 0,
'XML::SAX::Base' => 0, 'XML::SAX::Base' => 0,
'XML::SAX::ParserFactory' => 0, 'XML::SAX::ParserFactory' => 0,
'Pod::Simple::Text' => 0, 'Pod::Simple::Text' => 0,
'XML::SAX::ParserFactory' => 0, 'XML::SAX::ParserFactory' => 0,
}, },
recursive_test_files => 1, recursive_test_files => 1,
)->create_build_script; )->create_build_script;
+1 -57
View File
@@ -1,57 +1 @@
$Log: CHANGES,v $ See perldoc SOAP::WSDL
Revision 1.19 2004/07/27 13:00:03 lsc
- added missing test file
Revision 1.18 2004/07/16 07:43:05 lsc
fixed test scripts for windows
Revision 1.17 2004/07/05 08:19:49 lsc
- added wsdl_checkoccurs
Revision 1.16 2004/07/04 09:01:14 lsc
- change <definitions> element lookup from find('/definitions') and find('wsdl:definitions') to find('/*[1]') to process arbitrary default (wsdl) namespaces correctly
- fixed test output in test 06
Revision 1.15 2004/07/02 12:28:31 lsc
- documentation update
- cosmetics
Revision 1.14 2004/07/02 10:53:36 lsc
- API change:
- call now behaves (almost) like SOAP::Lite::call
- call() takes a list (hash) as second argument
- call does no longer support the "dispatch" option
- dispatching calls can be suppressed by passing
"no_dispatch => 1" to new()
- dispatching calls can be suppressed by calling
$soap->no_dispatch(1);
and re-enabled by calling
$soap->no_dispatch(0);
- Updated test skripts to reflect API change.
Revision 1.13 2004/06/30 12:08:40 lsc
- added IServiceInstance (ecmed) to acceptance tests
- refined documentation
Revision 1.12 2004/06/26 14:13:29 lsc
- refined file caching
- added descriptive output to test scripts
Revision 1.11 2004/06/26 07:55:40 lsc
- fixed "freeze" caching bug
- improved test scripts to test file system caching (and show the difference)
Revision 1.10 2004/06/26 06:30:33 lsc
- added filesystem caching using Cache::FileCache
Revision 1.9 2004/06/24 12:27:23 lsc
Cleanup
Revision 1.8 2004/06/11 19:49:15 lsc
- moved .t files to more self-describing names
- changed WSDL.pm to accept AXIS wsdl files
- implemented XPath query result caching on all absolute queries
Revision 1.7 2004/06/07 13:01:16 lsc
added changelog to pod
+26 -3
View File
@@ -1,7 +1,7 @@
Development of SOAP::WSDL takes place on sourceforge.net. Development of SOAP::WSDL takes place on sourceforge.net.
There's a svn repository available at There's a svn repository available at
https://svn.sourceforge.net/svnroot/soap-wsdl https://soap-wsdl.svn.sourceforge.net/svnroot/soap-wsdl
Engagement in the further development of this module is highly encouraged - Engagement in the further development of this module is highly encouraged -
many people have already contributed, and many more probably will. many people have already contributed, and many more probably will.
@@ -14,13 +14,36 @@ you as co-author.
The (my) current roadmap for SOAP::WSDL is: The (my) current roadmap for SOAP::WSDL is:
1.2*: Bugfixes and support for more XSD variants 1.2*: Bugfixes and support for more XSD variants
1.3: Bindings support 1.3: Bindings support
Development of the 1.* tree has stopped - I won't get past 1.2x anymore... Development of the 1.* tree has stopped - I won't get past 1.2x anymore...
2.*: WSDL -> Perl Class factory with offline WSDL processing 2.*: WSDL -> Perl Class factory with offline WSDL processing
May 2007, 2.01
- WSDL support for the most common type definitions
- Online-facility (SOAP::WSDL) using WSDL object tree directly
- usable code generator
- full namespace support when processing WSDL
- high performance when parsing WSDL messages - get nearly as fast as
XML::Simple...
2.02
- Support for Apache-SOAP datatypes
- support for embedded atomic simpleType/complexType definitions
- Caching of WSDL object tree + generated code (when using SOAP::WSDL).
- Online-facility (SOAP::WSDL) using code generator via cache directory
Somewhere on the TODO list (in no particular order):
- validation
- typemaps for use with the type="tns:MyComplexType" XML attribute
- external entities support when parsing WSDL
- support all these XML Schema variants
- support creating XML Schmema definitions via SOAP::WSDL::XSD::* ('minimal conformant')
- support other Schema definition languages than XML::Schema (maybe RelaxNG?)
- factor out SOAP::WSDL::XSD into it's own namespace (maybe just XSD ?)
July 2007,
Martin Kutter Martin Kutter
+5 -1
View File
@@ -8,9 +8,12 @@ lib/SOAP/WSDL/Client.pm
lib/SOAP/WSDL/Client/Base.pm lib/SOAP/WSDL/Client/Base.pm
lib/SOAP/WSDL/Definitions.pm lib/SOAP/WSDL/Definitions.pm
lib/SOAP/WSDL/Envelope.pm lib/SOAP/WSDL/Envelope.pm
lib/SOAP/WSDL/Expat/MessageParser.pm
lib/SOAP/WSDL/Expat/MessageStreamParser.pm
lib/SOAP/WSDL/Message.pm lib/SOAP/WSDL/Message.pm
lib/SOAP/WSDL/Operation.pm lib/SOAP/WSDL/Operation.pm
lib/SOAP/WSDL/OpMessage.pm lib/SOAP/WSDL/OpMessage.pm
lib/SOAP/WSDL/Parser.pod
lib/SOAP/WSDL/Part.pm lib/SOAP/WSDL/Part.pm
lib/SOAP/WSDL/Port.pm lib/SOAP/WSDL/Port.pm
lib/SOAP/WSDL/PortType.pm lib/SOAP/WSDL/PortType.pm
@@ -96,6 +99,7 @@ t/013_complexType.t
t/014_sax_typelib.t t/014_sax_typelib.t
t/015_to_typemap.t t/015_to_typemap.t
t/016_client_object.t t/016_client_object.t
t/017_generator.t
t/020_storable.t t/020_storable.t
t/098_pod.t t/098_pod.t
t/acceptance/results/03_complexType-all.xml t/acceptance/results/03_complexType-all.xml
@@ -123,6 +127,7 @@ t/acceptance/wsdl/contributed/ETest.wsdl
t/acceptance/wsdl/contributed/OITest.wsdl t/acceptance/wsdl/contributed/OITest.wsdl
t/acceptance/wsdl/contributed/tools.wsdl t/acceptance/wsdl/contributed/tools.wsdl
t/acceptance/wsdl/email_account.wsdl t/acceptance/wsdl/email_account.wsdl
t/Expat/01_expat.t
t/lib/MyComplexType.pm t/lib/MyComplexType.pm
t/lib/MyElement.pm t/lib/MyElement.pm
t/lib/MySimpleType.pm t/lib/MySimpleType.pm
@@ -147,5 +152,4 @@ t/SOAP/WSDL/05_simpleType-union.t
t/SOAP/WSDL/10_performance.t t/SOAP/WSDL/10_performance.t
t/SOAP/WSDL/11_helloworld.NET.t t/SOAP/WSDL/11_helloworld.NET.t
t/SOAP/WSDL/12_binding.pl t/SOAP/WSDL/12_binding.pl
t/SOAP/WSDL/97_pod.t
t/SOAP/WSDL/XSD/Typelib/Builtin/001_string.t t/SOAP/WSDL/XSD/Typelib/Builtin/001_string.t
+9 -3
View File
@@ -1,14 +1,18 @@
--- ---
name: SOAP-WSDL name: SOAP-WSDL
version: 2.00_05 version: 2.00_06
author: author:
abstract: SOAP with WSDL support abstract: SOAP with WSDL support
license: artistic license: artistic
requires: requires:
Class::Std: v0.0.8 Class::Std: v0.0.8
Class::Std::Storable: 0 Class::Std::Storable: 0
File::Basename: 0
File::Path: 0
List::Util: 0
SOAP::Lite: 0 SOAP::Lite: 0
XML::LibXML: 0 XML::LibXML: 0
XML::Parser::Expat: 0
XML::SAX::Base: 0 XML::SAX::Base: 0
XML::SAX::ParserFactory: 0 XML::SAX::ParserFactory: 0
XML::XPath: 0 XML::XPath: 0
@@ -33,6 +37,10 @@ provides:
file: lib/SOAP/WSDL/Definitions.pm file: lib/SOAP/WSDL/Definitions.pm
SOAP::WSDL::Envelope: SOAP::WSDL::Envelope:
file: lib/SOAP/WSDL/Envelope.pm file: lib/SOAP/WSDL/Envelope.pm
SOAP::WSDL::Expat::MessageParser:
file: lib/SOAP/WSDL/Expat/MessageParser.pm
SOAP::WSDL::Expat::MessageStreamParser:
file: lib/SOAP/WSDL/Expat/MessageStreamParser.pm
SOAP::WSDL::Message: SOAP::WSDL::Message:
file: lib/SOAP/WSDL/Message.pm file: lib/SOAP/WSDL/Message.pm
SOAP::WSDL::OpMessage: SOAP::WSDL::OpMessage:
@@ -47,8 +55,6 @@ provides:
file: lib/SOAP/WSDL/PortType.pm file: lib/SOAP/WSDL/PortType.pm
SOAP::WSDL::SAX::MessageHandler: SOAP::WSDL::SAX::MessageHandler:
file: lib/SOAP/WSDL/SAX/MessageHandler.pm file: lib/SOAP/WSDL/SAX/MessageHandler.pm
SOAP::WSDL::SAX::WSDLHandler:
file: lib/SOAP/WSDL/SAX/WSDLHandler.pm
SOAP::WSDL::SOAP::Typelib::Fault11: SOAP::WSDL::SOAP::Typelib::Fault11:
file: lib/SOAP/WSDL/SOAP/Typelib/Fault11.pm file: lib/SOAP/WSDL/SOAP/Typelib/Fault11.pm
SOAP::WSDL::Service: SOAP::WSDL::Service:
-2
View File
@@ -1,4 +1,2 @@
This is a developer release - everything may (and most things will) change. This is a developer release - everything may (and most things will) change.
You should not expect the SOAP::WSDL to survive - it will probably be replaced
by SOAP::WSDL::Client.
+89 -25
View File
@@ -3,11 +3,19 @@ use strict;
use warnings; use warnings;
use Carp; use Carp;
use Class::Std::Storable; use Class::Std::Storable;
use List::Util qw(first);
my %id_of :ATTR(:name<id> :default<()>); my %id_of :ATTR(:name<id> :default<()>);
my %name_of :ATTR(:name<name> :default<()>); my %name_of :ATTR(:name<name> :default<()>);
my %targetNamespace_of :ATTR(:name<targetNamespace> :default<()>); my %targetNamespace_of :ATTR(:name<targetNamespace> :default<()>);
my %xmlns_of :ATTR(:name<xmlns> :default<{}>); my %xmlns_of :ATTR(:name<xmlns> :default<{}>);
my %parent_of :ATTR(:name<parent> :default<()>);
sub DEMOLISH {
my $self = shift;
# delete upward references
delete $parent_of{ ident $self };
}
sub STORABLE_freeze_pre :CUMULATIVE {}; sub STORABLE_freeze_pre :CUMULATIVE {};
sub STORABLE_freeze_post :CUMULATIVE {}; sub STORABLE_freeze_post :CUMULATIVE {};
@@ -23,11 +31,15 @@ sub AUTOMETHOD {
# we're called as $self->push_something(@values); # we're called as $self->push_something(@values);
if ($subname =~s{^push_}{}xms) { if ($subname =~s{^push_}{}xms) {
# we're not paranoid - we could be checking get_subname, too
my $getter = "get_$subname"; my $getter = "get_$subname";
my $setter = "set_$subname"; my $setter = "set_$subname";
croak "no set accessor found for push_$subname" ## Checking here is paranoid - will fail fatally if
if not ($self->can( $setter )); ## there is no setter...
## And we would have to check getters, too.
## Maybe do it the Conway way via the Symbol table...
## ... can is way slow...
# croak "no set accessor found for push_$subname"
# if not ($self->can( $setter ));
return sub { return sub {
no strict qw(refs); no strict qw(refs);
my $old_value = $self->$getter(); my $old_value = $self->$getter();
@@ -42,12 +54,11 @@ sub AUTOMETHOD {
# we're called as $obj->find_something($ns, $key) # we're called as $obj->find_something($ns, $key)
elsif ($subname =~s {^find_}{get_}xms) { elsif ($subname =~s {^find_}{get_}xms) {
return sub { return sub {
my @found_at = grep { return first {
$_->get_targetNamespace() eq $values[0] && $_->get_targetNamespace() eq $values[0] &&
$_->get_name() eq $values[1] $_->get_name() eq $values[1]
} }
@{ $self->$subname() }; @{ $self->$subname() };
return $found_at[0];
} }
} }
elsif ($subname =~s {^first_}{get_}xms) { elsif ($subname =~s {^first_}{get_}xms) {
@@ -61,40 +72,36 @@ sub AUTOMETHOD {
croak "$subname not found in class " . (ref $self || $self); croak "$subname not found in class " . (ref $self || $self);
} }
#sub to_string :STRINGIFY {
# $_[0]->_DUMP();
#}
sub init { sub init {
my $self = shift; my $self = shift;
my @args = @_; my @args = @_;
foreach my $value (@args) foreach my $value (@args)
{ {
die $value if (not defined ($value->{ Name })); die $value if (not defined ($value->{ Name }));
if ($value->{ Name } =~m{^xmlns\:}xms) { if ($value->{ Name } =~m{^xmlns\:}xms) {
die $xmlns_of{ ident $self } die $xmlns_of{ ident $self }
if ref $xmlns_of{ ident $self } ne 'HASH'; if ref $xmlns_of{ ident $self } ne 'HASH';
$xmlns_of{ ident $self }->{ $value->{ Value } } = $xmlns_of{ ident $self }->{ $value->{ Value } } =
$value->{ LocalName }; $value->{ LocalName };
next; next;
} }
elsif ($value->{ Name } =~m{^xmlns$}xms) { elsif ($value->{ Name } =~m{^xmlns$}xms) {
# just ignore xmlns = for now # just ignore xmlns = for now
# TODO handle xmlns correctly - maybe via setting a prefix ? # TODO handle xmlns correctly - maybe via setting a prefix ?
next; next;
} }
my $name = $value->{ LocalName }; my $name = $value->{ LocalName };
my $method = "set_$name"; my $method = "set_$name";
$self->$method( $value->{ Value } ) if ( $method ); $self->$method( $value->{ Value } ) if ( $method );
} }
return $self; return $self;
} }
sub add_namespace { sub add_namespace {
my ($self, $uri, $prefix ) = @_; my ($self, $uri, $prefix ) = @_;
return unless $uri; return unless $uri;
$self->{ namespace } ||= {}; $self->{ namespace } ||= {};
$self->{ namespace }->{ $uri } = $prefix; $self->{ namespace }->{ $uri } = $prefix;
} }
sub to_typemap { sub to_typemap {
@@ -102,4 +109,61 @@ sub to_typemap {
return q{}; return q{};
} }
sub toClass {
my $self = shift;
warn 'toClass is deprecated and will be removed before reaching 2.01 - '
. 'use to_class instead (' . caller() . ')';
$self->to_class(@_);
}
sub to_class {
my $self = shift;
my $opt = shift;
my $template = shift;
$opt->{ base_path } ||= '.';
my $element_prefix = $opt->{ element_prefix } || $opt->{ prefix };
my $type_prefix = $opt->{ type_prefix } || $opt->{ prefix };
if (($type_prefix) && ($type_prefix !~m{ :: $ }xms ) ) {
warn 'type_prefix should end with "::"';
$type_prefix .= '::';
}
if (($element_prefix) && ($element_prefix !~m{ :: $ }xms) ) {
warn 'element_prefix should end with "::"';
$element_prefix .= '::';
}
# Be careful: a Element may be ComplexType, too
# (but not vice versa)
my $prefix = $self->isa('SOAP::WSDL::XSD::Element')
? $element_prefix
: $type_prefix;
die 'No prefix specified' if not $prefix;
my $filename = $prefix . $self->get_name() . '.pm';
$filename =~s{::}{/}xmsg;
my $output = $opt->{ output } || $filename;
require Template;
my $tt = Template->new(
RELATIVE => 1,
OUTPUT_PATH => $opt->{ base_path },
);
my $code = $tt->process( \$template, {
element_prefix => $element_prefix,
type_prefix => $type_prefix,
self => $self,
nsmap => { reverse %{ $opt->{ wsdl }->get_xmlns() } },
structure => $self->explain( { wsdl => $opt->{ wsdl } } ),
},
$output
)
or die $tt->error();
}
1; 1;
+41 -57
View File
@@ -9,43 +9,42 @@ my %type_of :ATTR(:name<type> :default<()>);
my %transport_of :ATTR(:name<transport> :default<()>); my %transport_of :ATTR(:name<transport> :default<()>);
my %style_of :ATTR(:name<style> :default<()>); my %style_of :ATTR(:name<style> :default<()>);
sub explain { sub explain {
my $self = shift; my $self = shift;
my $opt = shift; my $opt = shift;
my $name = $self->get_name(); my $name = $self->get_name();
my %ns_map = reverse %{ $opt->{ wsdl }->get_xmlns() }; die 'required atribute wsdl missing' if not $opt->{ wsdl };
my ($prefix, $localname) = split /:/ , $self->get_type();
my $portType = $opt->{ wsdl }->find_portType( my $portType = $opt->{ wsdl }->find_portType(
$ns_map{ $prefix }, $localname $opt->{ wsdl }->_expand( $self->get_type() )
) or die "portType $prefix:$localname not found !"; ) or die 'portType not found: ' . $self->get_type();
my $txt = <<"EOT";
Transport: $transport_of{ ident $self }
my $txt = <<"EOT"; =head2 METHODS
=head2 Binding name: $name B<Note:>
=over Input, output and fault messages are stated as perl hash refs.
=item * Style $style_of{ ident $self } These are only for informational purposes - the actual implementation
may be object trees, not hash refs, though the input messages may be passed
=item * Transport $transport_of{ ident $self } to the respective methods as hash refs and will be converted to object trees
automatically.
=back
=head3 Operations
EOT EOT
foreach my $operation (@{ $self->get_operation() }) foreach my $operation (@{ $self->get_operation() }) {
{
my $operation_name = $operation->get_name(); my $operation_name = $operation->get_name();
my $operation_style = $operation->get_style() || q{}; my $operation_style = $operation->get_style() || q{};
my $port_operation = $portType->find_operation( $ns_map{ $prefix }, my ($port_operation) = grep { $_->get_name eq $operation_name }
$operation->get_name() ) @{ $portType->get_operation() }
or die "operation not found:" . $operation->get_name(); or die "operation not found:" . $operation->get_name();
# TODO rename lexical $input to "message" # TODO rename lexical $input to "message"
my $input_message = do { my $input_message = do {
@@ -63,67 +62,52 @@ EOT
$txt .= <<"EOT"; $txt .= <<"EOT";
=over =head3 $operation_name
=item * $operation_name B<Input Message:>
=over 8
=item * Style: $operation_style
=item * Input Message:
$input_message $input_message
=item * Output Message: B<Output Message:>
$output_message $output_message
=item * Fault: B<Fault:>
$fault_message $fault_message
=back
=back
EOT EOT
} }
return $txt; return $txt;
} }
sub to_typemap { sub to_typemap {
my ($self, $opt) = @_; my ($self, $opt) = @_;
my $name = $self->get_name(); my $name = $self->get_name();
my %ns_map = reverse %{ $opt->{ wsdl }->get_xmlns() };
my ($prefix, $localname) = split /:/ , $self->get_type();
my $portType = $opt->{ wsdl }->find_portType( my $portType = $opt->{ wsdl }->find_portType(
$ns_map{ $prefix }, $localname $opt->{ wsdl }->_expand( $self->get_type )
) or die "portType $prefix:$localname not found !"; ) or die 'portType not found: ' . $self->get_type;
my $txt = q{}; my $txt = q{};
foreach my $operation (@{ $self->get_operation() }) foreach my $operation (@{ $self->get_operation() })
{ {
my $operation_name = $operation->get_name(); my $operation_name = $operation->get_name();
my $operation_style = $operation->get_style() || q{}; my $operation_style = $operation->get_style() || q{};
my $port_operation = $portType->find_operation( $ns_map{ $prefix }, my ($port_operation) = grep { $_->get_name eq $operation_name }
$operation->get_name() ) @{ $portType->get_operation() }
or die "operation not found:" . $operation->get_name(); or die "operation not found:" . $operation->get_name();
# TODO rename lexical $input to "message" no strict qw(refs);
$txt .= do { $txt .= join q{},
my $input = $port_operation->first_input(); map {
$input ? $input->to_typemap($opt) : q{}; my $message = $port_operation->$_;
}; $message
$txt .= do { ? $message->to_typemap($opt)
my $input = $port_operation->first_output(); : q{}
$input ? $input->to_typemap($opt) : q{}; } qw(first_input first_output first_fault);
};
$txt .= do {
my $input = $port_operation->first_fault();
$input ? $input->to_typemap($opt) : q{};
};
} }
return $txt; return $txt;
} }
+9 -25
View File
@@ -6,7 +6,7 @@ use Scalar::Util qw(blessed);
use SOAP::WSDL::Envelope; use SOAP::WSDL::Envelope;
use SOAP::Lite; use SOAP::Lite;
use Class::Std::Storable; use Class::Std::Storable;
use SOAP::WSDL::SAX::MessageHandler; use SOAP::WSDL::Expat::MessageParser;
use SOAP::WSDL::SOAP::Typelib::Fault11; use SOAP::WSDL::SOAP::Typelib::Fault11;
# Package globals for speed... # Package globals for speed...
@@ -14,9 +14,9 @@ my $PARSER;
my $MESSAGE_HANDLER; my $MESSAGE_HANDLER;
my %class_resolver_of :ATTR(:name<class_resolver> :default<()>); my %class_resolver_of :ATTR(:name<class_resolver> :default<()>);
my %no_dispatch_of :ATTR(:name<no_dispatch> :default<()>); my %no_dispatch_of :ATTR(:name<no_dispatch> :default<()>);
my %outputxml_of :ATTR(:name<outputxml> :default<()>); my %outputxml_of :ATTR(:name<outputxml> :default<()>);
my %proxy_of :ATTR(:name<proxy> :default<()>); my %proxy_of :ATTR(:name<proxy> :default<()>);
# TODO remove when preparing 2.01 # TODO remove when preparing 2.01
sub outputtree { warn 'outputtree is deprecated and' sub outputtree { warn 'outputtree is deprecated and'
@@ -38,19 +38,7 @@ SUBFACTORY: {
} }
BEGIN { BEGIN {
eval { $PARSER = SOAP::WSDL::Expat::MessageParser->new();
require XML::LibXML;
$PARSER = XML::LibXML->new();
$MESSAGE_HANDLER = SOAP::WSDL::SAX::MessageHandler->new();
$PARSER->set_handler( $MESSAGE_HANDLER );
};
if ($@) {
require XML::SAX::ParserFactory;
$MESSAGE_HANDLER = SOAP::WSDL::SAX::MessageHandler->new({
base => 'XML::SAX::Base' });
$PARSER = XML::SAX::ParserFactory->parser(
handler => $MESSAGE_HANDLER );
}
} }
sub call { sub call {
@@ -95,20 +83,16 @@ sub call {
); );
# warn 'Received ' . length($response) . ' bytes of content'; # warn 'Received ' . length($response) . ' bytes of content';
return $response if ($self->outputxml() ); return $response if ($self->outputxml() );
$MESSAGE_HANDLER->set_class_resolver( $self->get_class_resolver() ); $PARSER->class_resolver( $self->get_class_resolver() );
# if we had no success (Transport layer error status code) # if we had no success (Transport layer error status code)
# or if transport layer failed # or if transport layer failed
if (! $soap->transport->is_success() ) { if (! $soap->transport->is_success() ) {
# TODO Fix deserializing message - there's something wrong with Fault11
# Try deserializing response - there may be some # Try deserializing response - there may be some
if ($response) { if ($response) {
eval { $PARSER->parse( $response ); };
eval { $PARSER->parse_string( $response ); };
if ($@) { if ($@) {
warn "could not deserialize response: $@"; warn "could not deserialize response: $@";
} }
@@ -128,7 +112,7 @@ sub call {
. $soap->transport->message() . $soap->transport->message()
}); });
} }
eval { $PARSER->parse_string( $response ) }; eval { $PARSER->parse( $response ) };
# return fault if we cannot deserialize response # return fault if we cannot deserialize response
if ($@) { if ($@) {
@@ -140,7 +124,7 @@ sub call {
}); });
} }
return $MESSAGE_HANDLER->get_data(); return $PARSER->get_data();
} ## end sub call } ## end sub call
1; 1;
+195 -24
View File
@@ -1,6 +1,10 @@
package SOAP::WSDL::Definitions; package SOAP::WSDL::Definitions;
use strict; use strict;
use warnings; use warnings;
use Carp;
use File::Basename;
use File::Path;
use List::Util qw(first);
use Class::Std::Storable; use Class::Std::Storable;
use base qw(SOAP::WSDL::Base); use base qw(SOAP::WSDL::Base);
@@ -9,11 +13,10 @@ my %message_of :ATTR(:name<message> :default<()>);
my %portType_of :ATTR(:name<portType> :default<()>); my %portType_of :ATTR(:name<portType> :default<()>);
my %binding_of :ATTR(:name<binding> :default<()>); my %binding_of :ATTR(:name<binding> :default<()>);
my %service_of :ATTR(:name<service> :default<()>); my %service_of :ATTR(:name<service> :default<()>);
my %namespace_of :ATTR(:name<namespace> :default<()>);
my %namespace_of :ATTR(:name<namespace> :default<()>); # must be attr for Class::Std::Storable
my %attributes_of :ATTR(); my %attributes_of :ATTR();
%attributes_of = ( %attributes_of = (
binding => \%binding_of, binding => \%binding_of,
message => \%message_of, message => \%message_of,
@@ -23,31 +26,39 @@ my %attributes_of :ATTR();
# Function factory - we could be writing this method for all %attribute # Function factory - we could be writing this method for all %attribute
# keys, too, but that's just C&P (eehm, Copy & Paste...) # keys, too, but that's just C&P (eehm, Copy & Paste...)
foreach my $method(keys %attributes_of ) { BLOCK: {
no strict qw/refs/; no strict qw/refs/;
foreach my $method(keys %attributes_of ) {
# ... btw, we mean this method here... *{ "find_$method" } = sub {
*{ "find_$method" } = sub { my ($self, @args) = @_;
my ($self, @args) = @_; return first {
my @found_at = grep { $_->get_targetNamespace() eq $args[0]
$_->get_targetNamespace() eq $args[0] && && $_->get_name() eq $args[1]
$_->get_name() eq $args[1] }
} @{ $attributes_of{ $method }->{ ident $self } };
@{ $attributes_of{ $method }->{ ident $self } }; };
return $found_at[0]; }
};
} }
sub explain { sub explain {
my $self = shift; my $self = shift;
my $opt = shift; my $opt = shift;
my $txt = ''; $opt->{ wsdl } ||= $self;
foreach my $service (@{ $self->get_service() }) $opt->{ namespace } ||= $self->get_xmlns() || {};
{ my $txt = '';
$txt .= $service->explain( $opt );
$txt .= "\n"; for my $service (@{ $self->get_service() }) {
} $txt .= $service->explain( $opt );
return $txt; $txt .= "\n";
}
return $txt;
}
sub _expand {
my ($self, $prefix, $localname) = ($_[0], split /:/, $_[1]);
my %ns_map = reverse %{ $self->get_xmlns() };
return ($ns_map{ $prefix }, $localname);
} }
sub to_typemap { sub to_typemap {
@@ -61,6 +72,75 @@ sub to_typemap {
map { $_->to_typemap( $opt ) } @{ $service_of{ ident $self } }; map { $_->to_typemap( $opt ) } @{ $service_of{ ident $self } };
} }
sub create_interface {
my $self = shift;
my $opt = shift;
my $base_path = $opt->{ base_path }
or croak "missing or empty argument base_path";
$opt->{ prefix } ||= q{};
$opt->{ type_prefix } ||= $opt->{ prefix };
$opt->{ element_prefix } ||= $opt->{ prefix };
$opt->{ typemap_prefix } or die 'Required argument typemap_prefix missing';
mkpath $base_path;
for my $service (@{ $service_of{ ident $self } }) {
warn "creating typemap $opt->{ typemap_prefix }". $service->get_name() . "\n";
$self->_create_typemap({ %{ $opt }, service => $service });
}
my @schema = @{ $self->first_types()->get_schema() };
for my $type (map { @{ $_->get_type() } , @{ $_->get_element() } } @schema[1..$#schema] ) {
warn 'creating class for '. $type->get_name() . "\n";
$type->to_class( { %$opt, wsdl => $self } );
}
1;
}
sub _create_typemap {
my $self = shift;
my $opt = shift;
my $service_name = $opt->{ service }->get_name();
my $file_name = "$opt->{ base_path }/$opt->{ typemap_prefix }/$service_name.pm";
$file_name =~s{::}{/}gms;
my $path = dirname $file_name;
my $name = basename $file_name;
my $typemap = $opt->{ service }->to_typemap( { %{ $opt }, wsdl => $self } );
my $template = <<'EOT';
package [% typemap_prefix %][% service.get_name %];
use strict;
use warnings;
my %typemap = (
[% typemap %]
[% custom_types %]
);
sub get_class {
my $name = join '/', @{ $_[1] };
exists $typemap{ $name } or die "Cannot resolve $name via " . __PACKAGE__;
return $typemap{ $name };
}
1;
__END__
EOT
require Template;
my $tt = Template->new(
OUTPUT_PATH => $path,
);
$tt->process(\$template, { %{ $opt }, typemap => $typemap }, $name)
or die $tt->error();
}
1; 1;
=pod =pod
@@ -167,6 +247,97 @@ Try something like this for creating typemap classes:
1; 1;
"EOT" "EOT"
=head2 create_interface
Creates a typemap class, classes for all types and elements, and interface
classes for every service.
See L<CODE GENERATOR|CODE GENERATOR> below.
Options:
Name Description
----------------------------------------------------------------------------
prefix Prefix to use for types and elements. Should end with '::'.
element_prefix Prefix to use for element packages. Should end with '::'.
Must be specified if prefix is not given.
type_prefix Prefix to use for type packages. Should end with '::'.
Must be specified if prefix is not given.
typemap_prefix Prefix to use for type packages. Should end with '::'.
Mandatory.
custom_types A perl source code snippet defining custom types for the
class resolver (typemap).
Must look like this:
q{
'path/to/my/element' => 'My::Element',
'path/to/my/element/prop' => 'SOAP::WSDL::XSD::Typelib::Builtin::string',
'path/to/my/element/prop2' => 'SOAP::WSDL::XSD::Typelib::Builtin::string',
};
=head2 _expand
Expands a qualified name into a list consisting of namespace URI and
localname by using the definition's xmlns table.
Used internally by SOAP::WSDL::* classes.
=head1 CODE GENERATOR
TODO: move somewhere else - maybe SOAP::WSDL::Client ?
SOAP::WSDL::Definitions features a code generation facility for generating
perl classes (packages) from a WSDL definition.
The following classes are generated:
=over
=item * Typemaps
A typemap class is created for every service.
Typemaps are basically lookup classes. They allow the
SOAP::WSDL::SAX::MessageHandler to find out which class a XML element
in a SOAP message shoud be processed as.
Typemaps are passed to SOAP::WSDL::Client via the class_resolver
method.
=item * Interfaces
TODO: Implement Interface generation
Interface classes are just convenience shortcuts for accessing web
service methods. They define a method for every web service method,
dispatching the request to SOAP::WSDL::Client.
=item * Type and Element classes
For every top-level E<lt>elementE<gt>, E<lt>complexTypeE<gt> and
E<lt>simpleTypeE<gt> definition in the WSDL's schema, a perl class is
created.
Classes for E<lt>complexTypeE<gt> and E<lt>simpleTypeE<gt> definitions
are prefixed by the C<type_prefix> argument passed to
L<create_interface|create_interface>, classes for E<lt>elementE<gt>
definitions are prefixed by the C<element_prefix> passed to
L<create_interface|create_interface>. If the specific prefixes are not
specified, the C<prefix> argument is used instead.
If your web service is part of a bigger framework which defines types
globally, you probably do well always using the same C<type_prefix>:
This reduces the number of classes generated (provided types
are re-used by more than one service).
You probably should use different element prefixes, though -
E<lt>elementE<gt> definitions tend to be unique in the defining WSDL
only, especially when using document/literal style/encoding.
If not, you probably want to specify just C<prefix> (and use a
different one for every web service).
=back
=head1 LICENSE =head1 LICENSE
Copyright 2004-2007 Martin Kutter. Copyright 2004-2007 Martin Kutter.
+201
View File
@@ -0,0 +1,201 @@
#!/usr/bin/perl
package SOAP::WSDL::Expat::MessageParser;
use strict;
use warnings;
use SOAP::WSDL::XSD::Typelib::Builtin;
use XML::Parser::Expat;
=pod
=head2 new
=over
=item SYNOPSIS
my $obj = ->new();
=item DESCRIPTION
Constructor.
=back
=cut
sub new {
my $class = shift;
my $args = shift;
my $self = {
class_resolver => $args->{ class_resolver }
};
bless $self, $class;
return $self;
}
sub class_resolver {
my $self = shift;
$self->{ class_resolver } = shift;
}
sub parse {
my $self = shift;
my $xml = shift;
$self->{ data } = undef;
my $characters;
my $current = '__STOP__';
my $ignore = [ 'Envelope', 'Body' ];
my $list = [];
my $namespace = {};
my $path = [];
my $parser = XML::Parser::Expat->new();
no strict qw(refs);
$parser->setHandlers(
Start => sub {
my ($parser, $element, %attrs) = @_;
my ($prefix, $localname) = split m{:}xms , $element;
# for non-prefixed elements
if (not $localname) {
$localname = $element;
$prefix = q{};
}
# ignore top level elements
if (@{ $ignore } && $localname eq $ignore->[0]) {
shift @{ $ignore };
return;
}
# empty characters
$characters = q{};
push @{ $path }, $localname; # step down...
push @{ $list }, $current; # remember current
# resolve class of this element
my $class = $self->{ class_resolver }->get_class( $path )
or die "Cannot resolve class for "
. join('/', @{ $path }) . " via $self->{ class_resolver }";
# Check whether we have a primitive - we implement them as classes
# TODO replace with UNIVERSAL->isa()
# match is a bit faster if the string does not match, but WAY slower
# if $class matches...
# if (not $class=~m{^SOAP::WSDL::XSD::Typelib::Builtin}xms) {
if (index $class, 'SOAP::WSDL::XSD::Typelib::Builtin', 0 < 0) {
# check wheter there is a CODE reference for $class::new.
# If not, require it - all classes required here MUST
# define new()
# This is the same as $class->can('new'), but it's way faster
*{ "$class\::new" }{ CODE }
or eval "require $class" ## no critic qw(ProhibitStringyEval)
or die $@;
}
# create object
# set current object
$current = $class->new({ %attrs });
# remember top level element
defined $self->{ data }
or ($self->{ data } = $current);
},
Char => sub {
$characters .= $_[1];
},
End => sub {
my $element = $_[1];
my ($prefix, $localname) = split m{:}xms , $element;
# for non-prefixed elements
if (not $localname) {
$localname = $element;
$prefix = q{};
}
# This one easily handles ignores for us, too...
return if not ref $list->[-1];
if ( $current
->isa('SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType') ) {
$current->set_value( $characters );
}
# set appropriate attribute in last element
# multiple values must be implemented in base class
my $method = "add_$localname";
$list->[-1]->$method( $current );
# step up in path
pop @{ $path };
# step up in object hierarchy...
$current = pop @{ $list };
}
);
$parser->parse( $xml );
}
sub get_data {
my $self = shift;
return $self->{ data };
}
1;
=pod
=head1 NAME
SOAP::WSDL::Expat::MessageParser - Convert SOAP messages to custom object trees
=head1 SYNOPSIS
my $parser = SOAP::WSDL::Expat::MessageParser->new({
class_resolver => 'My::Resolver'
});
$parser->parse( $xml );
my $obj = $parser->get_data();
=head1 DESCRIPTION
Real fast expat based SOAP message parser.
See L<SOAP::WSDL::Parser> for details.
=head1 Bugs and Limitations
=over
=item * Ignores all namespaces
=item * Does not handle mixed content
=item * The SOAP header is ignored
=back
=head1 AUTHOR
Replace the whitespace by @ for E-Mail Address.
Martin Kutter E<lt>martin.kutter fen-net.deE<gt>
=head1 COPYING
This module may be used under the same terms as perl itself.
=head1 Repository information
$ID: $
$LastChangedDate: $
$LastChangedRevision: $
$LastChangedBy: $
$HeadURL: $
+206
View File
@@ -0,0 +1,206 @@
#!/usr/bin/perl
package SOAP::WSDL::Expat::MessageStreamParser;
use strict;
use warnings;
use SOAP::WSDL::XSD::Typelib::Builtin;
use XML::Parser::Expat;
=pod
=head2 new
=over
=item SYNOPSIS
my $obj = ->new();
=item DESCRIPTION
Constructor.
=back
=cut
sub new {
my $class = shift;
my $self = {
class_resolver => shift->{ class_resolver }
};
bless $self, $class;
return $self;
}
sub class_resolver {
my $self = shift;
$self->{ class_resolver } = shift;
}
sub init {
my $self = shift;
my $xml = shift;
$self->{ data } = undef;
my $characters;
my $current = '__STOP__';
my $ignore = [ 'Envelope', 'Body' ];
my $list = [];
my $namespace = {};
my $path = [];
my $parser = XML::Parser::ExpatNB->new();
no strict qw(refs);
$parser->setHandlers(
Start => sub {
my ($parser, $element, %attrs) = @_;
my ($prefix, $localname) = split m{:}xms , $element;
# for non-prefixed elements
if (not $localname) {
$localname = $element;
$prefix = q{};
}
# ignore top level elements
if (@{ $ignore } && $localname eq $ignore->[0]) {
shift @{ $ignore };
return;
}
# empty characters
$characters = q{};
push @{ $path }, $localname; # step down...
push @{ $list }, $current; # remember current
# resolve class of this element
my $class = $self->{ class_resolver }->get_class( $path )
or die "Cannot resolve class for "
. join('/', @{ $path }) . " via $self->{ class_resolver }";
# Check whether we have a primitive - we implement them as classes
# TODO replace with UNIVERSAL->isa()
# match is a bit faster if the string does not match, but WAY slower
# if $class matches...
# if (not $class=~m{^SOAP::WSDL::XSD::Typelib::Builtin}xms) {
if (index $class, 'SOAP::WSDL::XSD::Typelib::Builtin', 0 < 0) {
# check wheter there is a CODE reference for $class::new.
# If not, require it - all classes required here MUST
# define new()
# This is the same as $class->can('new'), but it's way faster
*{ "$class\::new" }{ CODE }
or eval "require $class" ## no critic qw(ProhibitStringyEval)
or die $@;
}
# create object
# set current object
$current = $class->new({ %attrs });
# remember top level element
defined $self->{ data }
or ($self->{ data } = $current);
},
Char => sub {
$characters .= $_[1];
},
End => sub {
my $element = $_[1];
my ($prefix, $localname) = split m{:}xms , $element;
# for non-prefixed elements
if (not $localname) {
$localname = $element;
$prefix = q{};
}
# This one easily handles ignores for us, too...
return if not ref $list->[-1];
if ( $current
->isa('SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType') ) {
$current->set_value( $characters );
}
# set appropriate attribute in last element
# multiple values must be implemented in base class
my $method = "add_$localname";
$list->[-1]->$method( $current );
# step up in path
pop @{ $path };
# step up in object hierarchy...
$current = pop @{ $list };
}
);
return $parser;
}
sub get_data {
my $self = shift;
return $self->{ data };
}
1;
=pod
=head1 NAME
SOAP::WSDL::Expat::MessageStreamParser - Convert SOAP messages to custom object trees
=head1 SYNOPSIS
my $lwp = LWP::UserAgent->new();
my $parser = SOAP::WSDL::Expat::MessageParser->new({
class_resolver => 'My::Resolver'
});
my $chunk_parser = $parser->init();
# process response while it comes in, trying to read 32k chunks.
$lwp->request( $request, sub { $chunk_parser->parse_more($_[0]) } , 32468 );
$chunk_parser->parse_done();
my $obj = $parser->get_data();
=head1 DESCRIPTION
ExpatNB based parser for parsing huge documents.
See L<SOAP::WSDL::Parser> for details.
=head1 Bugs and Limitations
=over
=item * Ignores all namespaces
=item * Does not handle mixed content
=item * The SOAP header is ignored
=back
=head1 AUTHOR
Replace the whitespace by @ for E-Mail Address.
Martin Kutter E<lt>martin.kutter fen-net.deE<gt>
=head1 COPYING
This module may be used under the same terms as perl itself.
=head1 Repository information
$ID: $
$LastChangedDate: $
$LastChangedRevision: $
$LastChangedBy: $
$HeadURL: $
+1 -4
View File
@@ -29,17 +29,14 @@ sub explain
); );
for my $part(@{ $message->[0]->get_part() }) { for my $part(@{ $message->[0]->get_part() }) {
$opt->{ indent } .= "\t";
$txt .= $part->explain($opt); $txt .= $part->explain($opt);
$opt->{ indent } =~s/\t//;
$txt .= $opt->{ indent } . "\n";
} }
} }
else else
{ {
if ($self->use()) if ($self->use())
{ {
$txt .= $opt->{ indent } . "$name use: " . $self->use(). "\n"; $txt .= " $name use: " . $self->use(). "\n";
} }
} }
return $txt; return $txt;
+213
View File
@@ -0,0 +1,213 @@
=pod
=head1 NAME
SOAP::WSDL::Parser - How SOAP::WSDL parses XML messages
=head1 Which XML message does SOAP::WSDL parse ?
Naturally, there are two kinds of XMLdocuments (or messages) SOAP::WSDL
has to parse:
=over
=item * WSDL definitions
=item * SOAP messages
=back
=head1 Parser implementations
There are different parser implementations available for SOAP messages -
currently there's only one for WSDL definitions.
=head2 WSDL definitions parser
=over
=item * SOAP::WSDL::SAX::WSDLHandler
This is a SAX handler for parsing WSDL files into object trees SOAP::WSDL
works with.
It's built as a native handler for XML::LibXML, but will also work with
XML::SAX::ParserFactory.
To parse a WSDL file, use one of the following variants:
my $parser = XML::LibXML->new();
my $handler = SOAP::WSDL::SAX::WSDLHandler->new();
$parser->set_handler( $handler );
$parser->parse( $xml );
my $data = $handler->get_data();
my $handler = SOAP::WSDL::SAX::WSDLHandler->new({
base => 'XML::SAX::Base'
});
my $parser = XML::SAX::ParserFactor->parser(
Handler => $handler
);
$parser->parse( $xml );
my $data = $handler->get_data();
=back
=head2 SOAP messages parser
All SOAP message handler use class resolvers for finding out which class
a particular XML element should be of and type libs containing these classes.
=head3 Writing a class resolver
The class resolver must returned a method "get_class", which is passed a list
ref of the current element's XPath (relative to Body), split by /.
This method must return a class name appropriate for a XML element.
A class resolver package might look like this:
package FakeResolver;
my %class_list = (
'EnqueueMessage' => 'Typelib::TEnqueueMessage',
'EnqueueMessage/MMessage' => 'Typelib::TMessage',
'EnqueueMessage/MMessage/MRecipientURI' => 'SOAP::WSDL::XSD::Builtin::anyURI',
'EnqueueMessage/MMessage/MMessageContent' => 'SOAP::WSDL::XSD::Builtin::string',
);
sub new { return bless {}, 'FakeResolver' };
sub get_class {
my $name = join('/', @{ $_[1] });
return ($class_list{ $name }) ? $class_list{ $name }
: warn "no class found for $name";
};
1;
=head3 Writing type library classes
Every element must have a correspondent one in the type library.
Type library classes must provide the following methods:
Builtin types should be resolved as SOAP::WSDL::XSD::Builtin::* classes
=over
=item * new
Constructor
=item * add_FOO
The add_FOO method is called for every child element of the XML node.
Characters are regarded as child element of the last XML node.
=back
A tyelib class implemented as Inside-Out object using Class::Std::Storable
as base class would look like this:
package Typelib::TEnqueueMessage;
use strict;
use Class::Std::Storable;
my %MMessage_of :ATTR(:name<MMessage> :default<()>);
sub add_MMessage {
my ($self, $value) = @_;
my $ident = ident $self;
# we're the first value
return $MMessage_of{ $ident } = $value
if not defined $MMessage_of{ $ident };
# we're the second value
return $MMessage_of{ $ident } = [
$MMessage_of{ $ident }, $value ]
if not ref $MMessage_of{ $ident } eq 'ARRAY';
# we're third or later
push @{ $MMessage_of{ $ident } }, $value;
return $MMessage_of{ $ident };
}
}
1;
Of course one could use a method factory for these add_FOO methods - see
t/lib/Typelib/Base.pm for an example.
=head3 Parser implementations
=over
=item * SOAP::WSDL::SAX::MessageHandler
This is a SAX handler for parsing WSDL files into object trees SOAP::WSDL
works with.
It's built as a native handler for XML::LibXML, but will also work with
XML::SAX::ParserFactory.
Can be used for parsing both streams (chunks) and documents.
See L<SOAP::WSDL::SAX::MessageHandler> for details.
=item * SOAP::WSDL::Expat::MessageParser
A L<XML::Parser::Expat|XML::Parser::Expat> based parser. This is the fastest
parser for most SOAP messages and the default for SOAP::WSDL::Client.
=item * SOAP::WSDL::Expat::MessageStreamParser
A XML::Parser::ExpatNB based parser. Useful for parsing huge HTTP responses,
as you don't need to keep everything in memory.
See L<SOAP::WSDL::Expat::MessageStreamParser|SOAP::WSDL::Expat::MessageStreamParser> for details.
=back
=head3 Performance
SOAP::WSDL::Expat::MessageParser is the fastest way of parsing SOAP messages
into object trees and only slightly slower than converting them into hash
data structures:
Parsing a SOAP message with a length of 5962 bytes:
SOAP::WSDL::Expat::MessageParser:
3 wallclock secs ( 3.28 usr + 0.05 sys = 3.33 CPU) @ 60.08/s (n=200)
SOAP::WSDL::SAX::MessageHandler (with raw XML::LibXML):
5 wallclock secs ( 4.95 usr + 0.00 sys = 4.95 CPU) @ 40.38/s (n=200)
XML::Simple (XML::Parser):
3 wallclock secs ( 2.36 usr + 0.03 sys = 2.39 CPU) @ 83.65/s (n=200)
XML::Simple (XML::SAX::Expat):
7 wallclock secs ( 6.50 usr + 0.03 sys = 6.53 CPU) @ 30.62/s (n=200)
As the benchmark shows, all SOAP::WSDL parser variants are faster than
XML::Simple with XML::SAX::Expat, and SOAP::WSDL::Expat::MessageParser almost
reaches the performance of XML::Simple with XML::Parser as backend.
Parsing SOAP responses in chunks does not increase speed - at least not up
to a response size of around 500k:
Benchmark: timing 5 iterations of SOAP::WSDL::SAX::MessageHandler,
SOAP::WSDL::Expat::MessageParser, SOAP::WSDL::Expat::MessageStreamParser...
SOAP::WSDL::Expat::MessageStreamParser:
13 wallclock secs ( 7.39 usr + 0.09 sys = 7.48 CPU) @ 0.67/s (n=5)
SOAP::WSDL::Expat::MessageParser:
10 wallclock secs ( 5.81 usr + 0.06 sys = 5.88 CPU) @ 0.85/s (n=5)
SOAP::WSDL::SAX::MessageHandler:
14 wallclock secs ( 8.78 usr + 0.03 sys = 8.81 CPU) @ 0.57/s (n=5)
Response size: 344330 bytes
=cut
+25 -34
View File
@@ -1,6 +1,7 @@
package SOAP::WSDL::Part; package SOAP::WSDL::Part;
use strict; use strict;
use warnings; use warnings;
use Carp qw(croak);
use Class::Std::Storable; use Class::Std::Storable;
use base qw(SOAP::WSDL::Base); use base qw(SOAP::WSDL::Base);
@@ -41,54 +42,44 @@ sub serialize
} }
sub explain { sub explain {
my ($self, $opt, $name ) = @_; my ($self, $opt, $name ) = @_;
my $typelib = $opt->{ wsdl }->first_types() my $typelib = $opt->{ wsdl }->first_types() || die "No typelib";
|| die "No typelib"; my $element = $self->get_type() || $self->get_element();
my %ns_map = reverse %{ $opt->{ namespace } }; # resolve type
my $element = $self->get_type() || $self->get_element(); my $type = $typelib->find_type( $opt->{ wsdl }->_expand( $element ) )
|| $typelib->find_element( $opt->{ wsdl }->_expand( $element ) );
# resolve type if (not $type)
my ($prefix, $localname) = split /:/ , $element; {
my $type = $typelib->find_type( warn "no type/element $element found for part " . $self->get_name();
$ns_map{ $prefix }, return q{};
$localname }
) return " {\n" . $type->explain( $opt, $self->get_name() ) . " }\n";
|| $typelib->find_element(
$ns_map{ $prefix },
$localname
);
if (not $type)
{
warn "no type/element $element ({ $ns_map{ $prefix } }$localname) found for part " . $self->get_name();
return q{};
}
return $type->explain( $opt, $self->get_name() );
} }
sub to_typemap { sub to_typemap {
my ($self, $opt, $name ) = @_; my ($self, $opt, $name ) = @_;
my $txt = q{}; my $txt = q{};
my $wsdl = $opt->{ wsdl };
my $typelib = $opt->{ wsdl }->first_types() my $typelib = $opt->{ wsdl }->first_types()
|| die "No typelib"; || die "No typelib";
my %ns_map = reverse %{ $opt->{ wsdl }->get_xmlns() };
my $element = $self->get_type() || $self->get_element();
# resolve type # resolve type
my ($prefix, $localname) = split /:/ , $element;
my $type; my $type;
if ($type = $typelib->find_type( $ns_map{ $prefix }, $localname ) ) { if (my $type_name = $self->get_type()) {
$txt .= "'/' => " . $type->get_name() . "\n"; $type = $typelib->find_type( $wsdl->_expand( $type_name ) )
|| croak "no type/element $type_name found for part " . $self->get_name();
$txt .= "q{} => " . $type->get_name() . "\n";
}
elsif ( my $element_name = $self->get_element() ) {
$type = $typelib->find_element( $wsdl->_expand( $element_name ) )
|| croak "no type/element $element_name found for part " . $self->get_name();
} }
else { else {
$type = $typelib->find_element( $ns_map{ $prefix }, $localname ); warn 'neither type nor element - do not know what to do for part '
} . $self->get_name();
return q{};
if (not $type) {
warn "no type/element $element ({ $ns_map{ $prefix } }$localname) found for part " . $self->get_name();
return q{};
} }
$opt->{ path } = []; $opt->{ path } = [];
$txt .= $type->to_typemap( $opt, $self->get_name() ); $txt .= $type->to_typemap( $opt, $self->get_name() );
+13 -16
View File
@@ -8,26 +8,23 @@ my %binding_of :ATTR(:name<binding> :default<()>);
my %location_of :ATTR(:name<location> :default<()>); my %location_of :ATTR(:name<location> :default<()>);
sub explain { sub explain {
my $self = shift;
my $opt = shift;
my $txt =
"=head2 Port name: " . $self->get_name() . "\n\n"
. "=over\n\n"
. "=item * Binding: " . $self->get_binding() ."\n\n"
. "=item * Location: " . $self->get_location() ."\n\n"
. "=back\n\n";
# if ( $self->location() );
my %ns_map = reverse %{ $opt->{ namespace } }; my $self = shift;
my $opt = shift;
$opt->{ wsdl } || die 'required attribute wsdl missing';
my ($prefix, $localname) = split /:/ , $self->get_binding(); my $binding = $opt->{ wsdl }->find_binding(
my $binding = $opt->{ wsdl }->find_binding( $opt->{ wsdl }->_expand( $self->get_binding() )
$ns_map{ $prefix }, $localname ) or die 'binding ' . $self->get_binding() . ' not found !';
) or die "binding $prefix:$localname not found !";
$txt .= $binding->explain($opt);
return $txt; my $txt = "=head2 Service information:\n\n"
. " Port name: " . $self->get_name() . "\n"
. " Binding: " . $self->get_binding() ."\n"
. " Location: " . $self->get_location() ."\n"
. $binding->explain($opt);
return $txt;
} }
sub to_typemap { sub to_typemap {
+19 -111
View File
@@ -76,6 +76,11 @@ my %data_of :ATTR(:default<()>);
} }
} }
sub class_resolver {
my $self = shift;
$class_resolver_of{ ident $self } = shift;
}
sub start_document { sub start_document {
my $ident = ident $_[0]; my $ident = ident $_[0];
$list_of{ $ident } = []; $list_of{ $ident } = [];
@@ -115,10 +120,16 @@ sub start_element {
# match is a bit faster if the string does not match, but WAY slower # match is a bit faster if the string does not match, but WAY slower
# if $class matches... # if $class matches...
# if (not $class=~m{^SOAP::WSDL::XSD::Typelib::Builtin}xms) { # if (not $class=~m{^SOAP::WSDL::XSD::Typelib::Builtin}xms) {
if (index $class, 'SOAP::WSDL::XSD::Typelib::Builtin', 0 < 0) { if (index $class, 'SOAP::WSDL::XSD::Typelib::Builtin', 0 < 0) {
eval "require $class" ## no critic qw(ProhibitStringyEval)
or die $@; # check wheter there is a CODE reference for $class::new.
# If not, require it - all classes required here MUST
# define new()
# This is the same as $class->can('new'), but it's way faster
no strict qw(refs);
*{ "$class\::new" }{ CODE }
or eval "require $class" ## no critic qw(ProhibitStringyEval)
or die $@;
} }
# create object # create object
# set current object # set current object
@@ -223,121 +234,18 @@ SOAP::WSDL::SAX::MessageHandler - Convert SOAP messages to custom object trees
class_resolver => FakeResolver->new(), class_resolver => FakeResolver->new(),
base => 'XML::SAX::Base', base => 'XML::SAX::Base',
), "Object creation"); ), "Object creation");
my $parser = XML::LibXML->new(); my $parser = XML::SAX::ParserFactor->parser(
$parser->set_handler( $filter ); Handler => $handler
);
$parser->parse_string( $soap_message ); $parser->parse_string( $soap_message );
my $object_tree = $filter->get_data(); my $object_tree = $filter->get_data();
=head1 DESCRIPTION =head1 DESCRIPTION
Parses a SOAP message into an object tree. SAX handler for parsing SOAP messages.
For every element in the SOAP message, an object is created. The class See L<SOAP::WSDL::Parser> for details.
of the object is determined via a Resolver object which has to be passed
to new via the class_resolver parameter.
=head1 Writing a class resolver
The class resolver must returned a method "get_class", which is passed a list
ref of the current element's XPath (relative to Body), split by /.
This method must return a class name appropriate for a XML element.
A class resolver package might look like this:
package FakeResolver;
my %class_list = (
'EnqueueMessage' => 'Typelib::TEnqueueMessage',
'EnqueueMessage/MMessage' => 'Typelib::TMessage',
'EnqueueMessage/MMessage/MRecipientURI' => 'SOAP::WSDL::XSD::Builtin::anyURI',
'EnqueueMessage/MMessage/MMessageContent' => 'SOAP::WSDL::XSD::Builtin::string',
);
sub new { return bless {}, 'FakeResolver' };
sub get_class {
my $name = join('/', @{ $_[1] });
return ($class_list{ $name }) ? $class_list{ $name }
: warn "no class found for $name";
};
1;
=head1 Writing type library classes
Every element must have a correspondent one in the type library.
Type library classes must provide the following methods:
Builtin types should be resolved as SOAP::WSDL::XSD::Builtin::* classes
=over
=item * new
Constructor
=item * add_FOO
The add_FOO method is called for every child element of the XML node.
Characters are regarded as child element of the last XML node.
=back
A tyelib class implemented as Inside-Out object using Class::Std::Storable
as base class would look like this:
package Typelib::TEnqueueMessage;
use strict;
use Class::Std::Storable;
my %MMessage_of :ATTR(:name<MMessage> :default<()>);
sub add_MMessage {
my ($self, $value) = @_;
my $ident = ident $self;
# we're the first value
return $MMessage_of{ $ident } = $value
if not defined $MMessage_of{ $ident };
# we're the second value
return $MMessage_of{ $ident } = [
$MMessage_of{ $ident }, $value ]
if not ref $MMessage_of{ $ident } eq 'ARRAY';
# we're third or later
push @{ $MMessage_of{ $ident } }, $value;
return $MMessage_of{ $ident };
}
}
1;
Of course one could use a method factory for these add_FOO methods - see
t/lib/Typelib/Base.pm for an example.
=head1 Performance
SOAP::WSDL::SAX::MessageHandler with a raw XML::LibXML parser almost reaches
the performance of XML::Simple with XML::Parser (and expat) as low-level
parser.
And SOAP::WSDL::SAX::MessageHandler builds up a object tree, while
XML::Simple just emits hash data structures:
SOAP::WSDL::SAX::MessageHandler:
1 wallclock secs ( 1.39 usr + 0.00 sys = 1.39 CPU) @ 719.42/s (n=1000)
XML::Simple:
2 wallclock secs ( 1.25 usr + 0.01 sys = 1.26 CPU) @ 790.51/s (n=1000)
If you know a faster way for parsing XML with a reasonable simple API than
XML::LibXML, please let me know...
=head1 Bugs and Limitations =head1 Bugs and Limitations
+79 -85
View File
@@ -1,4 +1,4 @@
package SOAP::WSDL::SAX::WSDLHandler; package SOAP::WSDL::SAX::WSDLHandler;
use strict; use strict;
use warnings; use warnings;
use Carp; use Carp;
@@ -34,34 +34,34 @@ my %current_of :ATTR(:name<current> :default<()>);
# ...we ignore em all... # ...we ignore em all...
no strict qw(refs); no strict qw(refs);
foreach my $method ( qw( foreach my $method ( qw(
characters characters
processing_instruction processing_instruction
ignorable_whitespace ignorable_whitespace
set_document_locator set_document_locator
start_prefix_mapping start_prefix_mapping
end_prefix_mapping end_prefix_mapping
skipped_entity skipped_entity
start_cdata start_cdata
end_cdata end_cdata
comment comment
entity_reference entity_reference
notation_decl notation_decl
unparsed_entity_decl unparsed_entity_decl
element_decl element_decl
attlist_decl attlist_decl
doctype_decl doctype_decl
xml_decl xml_decl
entity_decl entity_decl
attribute_decl attribute_decl
internal_entity_decl internal_entity_decl
external_entity_decl external_entity_decl
resolve_entity resolve_entity
start_dtd start_dtd
end_dtd end_dtd
start_entity start_entity
end_entity end_entity
warning warning
error error
) ) { ) ) {
*{ "$method" } = sub {}; *{ "$method" } = sub {};
} }
@@ -72,7 +72,7 @@ my %current_of :ATTR(:name<current> :default<()>);
}; };
sub start_document { sub start_document {
my ($self, $ident) = ($_[0], ident $_[0]); my $ident = ident $_[0];
$tree_of{ $ident } = {}; $tree_of{ $ident } = {};
$order_of{ $ident } = []; $order_of{ $ident } = [];
$targetNamespace_of{ $ident } = undef; $targetNamespace_of{ $ident } = undef;
@@ -80,69 +80,63 @@ sub start_document {
} }
sub start_element { sub start_element {
my ($self, $element) = @_; my ($self, $element) = @_;
my $ident = ident $self; my $ident = ident $self;
my $action = SOAP::WSDL::TypeLookup->lookup( my $action = SOAP::WSDL::TypeLookup->lookup(
$element->{ NamespaceURI }, $element->{ NamespaceURI },
$element->{ LocalName } $element->{ LocalName }
); );
if ($action) return if not $action;
{
if ($action->{ type } eq 'CLASS')
{
eval "require $action->{ class }";
croak $@, $tree_of{ $ident } if ($@);
my $class = $action->{ class }; if ($action->{ type } eq 'CLASS') {
my $obj = $class->new()->init( eval "require $action->{ class }";
values %{ $element->{ Attributes } } croak $@, $tree_of{ $ident } if ($@);
);
# set element in parent my $class = $action->{ class };
if ($current_of{ $ident }) my $obj = $class->new({ parent => $current_of{ $ident } })->init(
{ values %{ $element->{ Attributes } }
# inherit namespace, but don't override );
$obj->set_targetNamespace(
$current_of{ $ident }->get_targetNamespace() )
if not $obj->get_targetNamespace();
# push on name list # set element in parent
my $method = "push_$element->{ LocalName }"; if ($current_of{ $ident }) {
no strict qw(refs); # inherit namespace, but don't override
$current_of{ $ident }->$method( $obj ); $obj->set_targetNamespace(
$current_of{ $ident }->get_targetNamespace() )
if not $obj->get_targetNamespace();
# remember element for stepping back # push on name list
push @{ $order_of{ $ident } }, $current_of{ $ident }; my $method = "push_$element->{ LocalName }";
} no strict qw(refs);
else $current_of{ $ident }->$method( $obj );
{
$tree_of{ $ident } = $obj;
}
# set new element (step down)
$current_of{ $ident } = $obj;
}
elsif ($action->{ type } eq 'PARENT')
{
$current_of{ $ident }->init( values %{ $element->{ Attributes } } );
}
elsif ($action->{ type } eq 'METHOD')
{
my $method = $action->{ method } || $element->{ LocalName };
no strict qw(refs); # remember element for stepping back
# call method with push @{ $order_of{ $ident } }, $current_of{ $ident };
# - default value ($action->{ value } if defined, }
# dereferencing lists else {
# - the values of the elements Attributes hash $tree_of{ $ident } = $obj;
$current_of{ $ident }->$method( defined $action->{ value } }
? ref $action->{ value } # set new element (step down)
? @{ $action->{ value } } $current_of{ $ident } = $obj;
: ($action->{ value }) }
: values %{ $element->{ Attributes } } ); elsif ($action->{ type } eq 'PARENT') {
} $current_of{ $ident }->init( values %{ $element->{ Attributes } } );
} }
elsif ($action->{ type } eq 'METHOD') {
my $method = $action->{ method } || $element->{ LocalName };
no strict qw(refs);
# call method with
# - default value ($action->{ value } if defined,
# dereferencing lists
# - the values of the elements Attributes hash
$current_of{ $ident }->$method( defined $action->{ value }
? ref $action->{ value }
? @{ $action->{ value } }
: ($action->{ value })
: values %{ $element->{ Attributes } } );
}
} }
sub end_element { sub end_element {
+6 -1
View File
@@ -9,7 +9,7 @@ my %port_of :ATTR(:name<port> :default<()>);
sub explain { sub explain {
my $self = shift; my $self = shift;
my $opt = shift; my $opt = shift;
my $txt ="=head1 Service name\n\n" . $self->get_name() . "\n\n"; my $txt ="=head1 Service " . $self->get_name() . "\n\n";
foreach my $port (@{ $self->get_port() } ) foreach my $port (@{ $self->get_port() } )
{ {
$txt .= $port->explain( $opt ); $txt .= $port->explain( $opt );
@@ -24,4 +24,9 @@ sub to_typemap {
map { $_->to_typemap( $opt ) } @{ $port_of{ ident $self } }; map { $_->to_typemap( $opt ) } @{ $port_of{ ident $self } };
} }
# TODO implement to_class as class generator for a (complete) interface
sub to_class {
}
1; 1;
+119 -55
View File
@@ -57,8 +57,7 @@ sub init {
$self->SUPER::init( @args ); $self->SUPER::init( @args );
} }
sub serialize sub serialize {
{
my ($self, $name, $value, $opt) = @_; my ($self, $name, $value, $opt) = @_;
$opt->{ indent } ||= q{}; $opt->{ indent } ||= q{};
@@ -74,7 +73,7 @@ sub serialize
$xml .= join q{ } , "<$name" , @{ $opt->{ attributes } }; $xml .= join q{ } , "<$name" , @{ $opt->{ attributes } };
delete $opt->{ attributes }; # don't propagate... delete $opt->{ attributes }; # don't propagate...
if ( $opt->{ autotype }) { if ( $opt->{ autotype }) {
my $ns = $self->get_targetNamespace(); my $ns = $self->get_targetNamespace();
@@ -120,39 +119,38 @@ sub serialize
return $xml; return $xml;
} }
sub explain sub explain {
{ my ($self, $opt, $name ) = @_;
my ($self, $opt, $name ) = @_; my $flavor = $self->get_flavor();
my $flavor = $self->get_flavor();
my $xml = '';
$xml .= $opt->{ indent } if ($opt->{ readable }); # add indentation
$xml .= q{'} . $name . q{' => };
return q{} if not $flavor; # empty complexType $name ||= q{};
if ( ($flavor eq "sequence") or ($flavor eq "all") ) return q{} if not $flavor; # empty complexType
{ $opt->{ indent } ||= q{ };
$xml .= "{\n"; my $xml = q{};
$opt->{ indent } .= "\t"; $xml .= "$opt->{indent}\'$name'=> " if $name;
if ( ($flavor eq "sequence") or ($flavor eq "all") ) {
$xml .= "{\n";
$opt->{ indent } .= " ";
$xml .= join q{}, map { $_->explain( $opt ) } $xml .= join q{}, map { $_->explain( $opt ) }
@{ $self->get_element() }; @{ $self->get_element() };
$opt->{ indent } =~s/\t$//; # step back $opt->{ indent } =~s/\s{2}$//; # step back
$xml .= $opt->{ indent } . "},\n"; $xml .= "$opt->{ indent }},\n";
} }
elsif ($flavor eq "complexContent") elsif ($flavor eq "complexContent")
{ {
} }
elsif ($flavor eq "simpleContent") elsif ($flavor eq "simpleContent")
{ {
warn "found unsupported complexType definition $flavor";
} }
else else
{ {
warn "unknown complexType definition $flavor"; warn "found unsupported complexType definition $flavor";
} }
$xml .= "\n" if ($opt->{ readable } ); # add linebreak return $xml;
return $xml;
} }
sub to_typemap { sub to_typemap {
@@ -172,12 +170,12 @@ sub to_typemap {
return $txt; return $txt;
} }
sub toClass { sub to_class {
my $self = shift; my $self = shift;
my $opt = shift; my $opt = shift;
my $template = <<'EOT'; my $template = <<'EOT';
package [% class_prefix %]::[% self.get_name %]; package [% type_prefix %][% self.get_name %];
use strict; use strict;
use Class::Std::Storable; use Class::Std::Storable;
use SOAP::WSDL::XSD::Typelib::ComplexType; use SOAP::WSDL::XSD::Typelib::ComplexType;
@@ -194,19 +192,38 @@ __PACKAGE__->_factory(
[% element.get_name %] [% element.get_name %]
[% END %]) ], [% END %]) ],
{ {
[% FOREACH element=self.get_element %] [% element.get_name %] => \%[% element.get_name %]_of, [% FOREACH element=self.get_element %][% element.get_name %] => \%[% element.get_name %]_of,
[% END %] [% END %]
}, },
{ {
[%- FOREACH element=self.get_element; [%-
split_name = element.get_type.split(':'); FOREACH element=self.get_element;
prefix = split_name.0; IF (element.get_type);
localname = split_name.1; split_name = element.get_type.split(':');
IF nsmap.$prefix == 'http://www.w3.org/2001/XMLSchema' -%] prefix = split_name.0;
localname = split_name.1;
IF nsmap.$prefix == 'http://www.w3.org/2001/XMLSchema' -%]
[% element.get_name %] => 'SOAP::WSDL::XSD::Typelib::Builtin::[% localname %]', [% element.get_name %] => 'SOAP::WSDL::XSD::Typelib::Builtin::[% localname %]',
[% ELSE %] [% ELSE -%]
[% element.get_name %] => '[% class_prefix %]::[% localname %]', [% element.get_name %] => '[% type_prefix %][% localname %]',
[%- END; [%- END;
ELSIF (simpleType = element.first_simpleType);
base = simpleType.get_base();
%]
# basic simple type handling: we treat atomic simple types
# in complexType elements as their base types
# - and we only treat <restriction base="..."> yet.
# our base here is [% base %]
[%
split_name = base.split(':');
prefix = split_name.0;
localname = split_name.1;
IF nsmap.$prefix == 'http://www.w3.org/2001/XMLSchema' -%]
[% element.get_name %] => 'SOAP::WSDL::XSD::Typelib::Builtin::[% localname %]',
[% ELSE -%]
[% element.get_name %] => '[% type_prefix %][% localname %]',
[%- END;
END;
END %] END %]
} }
); );
@@ -214,21 +231,68 @@ __PACKAGE__->_factory(
sub get_xmlns { '[% self.get_targetNamespace %]' } sub get_xmlns { '[% self.get_targetNamespace %]' }
1; 1;
__END__
=pod
=head1 NAME [% type_prefix %][% self.get_name %]
=head1 SYNOPSIS
=head1 DESCRIPTION
Type class for the XML type [% self.get_name %].
=head1 PROPERTIES
The following properties may be accessed using get_PROPERTY / set_PROPERTY
methods:
[%- FOREACH element = self.get_element %]
[% element.get_name -%]
[% END %]
=head1 Object structure
[% FOREACH element=self.get_element;
IF (element.get_type);
split_name = element.get_type.split(':');
prefix = split_name.0;
localname = split_name.1;
IF nsmap.$prefix == 'http://www.w3.org/2001/XMLSchema' -%]
[% element.get_name %] => 'SOAP::WSDL::XSD::Typelib::Builtin::[% localname %]',
[% ELSE -%]
[% element.get_name %] => '[% type_prefix %][% localname %]',
[%- END;
ELSIF (simpleType = element.first_simpleType);
base = simpleType.get_base();
split_name = base.split(':');
prefix = split_name.0;
localname = split_name.1;
IF nsmap.$prefix == 'http://www.w3.org/2001/XMLSchema' -%]
[% element.get_name %] => 'SOAP::WSDL::XSD::Typelib::Builtin::[% localname %]',
[% ELSE -%]
[% element.get_name %] => '[% type_prefix %][% localname %]',
[%- END;
END;
END %]
Structure as perl hash:
The object structure is displayed as hash below though this is not correct.
Complex hash elements actually are objects of their corresponding classes
(look for classes of the same name in your typleib).
new() will accept a hash structure like this, but transform it to a object
tree.
[% structure %]
=cut
EOT EOT
$opt->{ base_path } ||= '.'; $self->SUPER::to_class($opt, $template);
require Template;
my $tt = Template->new(
RELATIVE => 1,
);
my $code = $tt->process( \$template, {
class_prefix => $opt->{ prefix },
self => $self,
nsmap => { reverse %{ $opt->{ wsdl }->get_xmlns() } },
},
$opt->{ output },
) or die $tt->error();
} }
sub _check_value { sub _check_value {
+86 -34
View File
@@ -43,8 +43,7 @@ sub first_complexType {
} }
# serialize type instead... # serialize type instead...
sub serialize sub serialize {
{
my ($self, $name, $value, $opt) = @_; my ($self, $name, $value, $opt) = @_;
my $type; my $type;
my $typelib = $opt->{ typelib }; my $typelib = $opt->{ typelib };
@@ -109,8 +108,7 @@ sub serialize
return $type->serialize( $name, $value, $opt ); return $type->serialize( $name, $value, $opt );
} }
sub explain sub explain {
{
my ($self, $opt, $name) = @_; my ($self, $opt, $name) = @_;
my $type; my $type;
my $text = q{}; my $text = q{};
@@ -119,10 +117,12 @@ sub explain
if ($type = $self->first_simpleType() ) if ($type = $self->first_simpleType() )
{ {
$text .= $type->explain( $opt, $self->get_name() ); $text .= $type->explain( $opt, $self->get_name() );
return $text;
} }
elsif ($type = $self->first_complexType() ) elsif ($type = $self->first_complexType() )
{ {
$text .= $type->explain( $opt, $self->get_name() ) $text .= $type->explain( $opt, $self->get_name() );
return $text;
} }
# return if it's not a derived type - we don't handle # return if it's not a derived type - we don't handle
@@ -131,14 +131,17 @@ sub explain
# if we have a derived type, fetch type and explain # if we have a derived type, fetch type and explain
my ($prefix, $localname) = split /:/ , $self->get_type(); my ($prefix, $localname) = split /:/ , $self->get_type();
my %ns_map = reverse %{ $opt->{ namespace } }; my %ns_map = reverse %{ $opt->{ wsdl }->get_xmlns };
my $ns = $ns_map{ $prefix };
$type = $opt->{ wsdl }->first_types()->find_type( $type = $opt->{ wsdl }->first_types()->find_type(
$ns, $localname $ns_map{ $prefix }, $localname
); );
die "no type for $prefix:$localname ($ns)" if (not $type); use Data::Dumper;
die "no type for $prefix:$localname ($ns_map{ $prefix })"
. Dumper $opt->{ wsdl }->first_types()->first_schema()->_DUMP
if (not $type);
return $text .= $type->explain( $opt, $self->get_name() ); return $text .= $type->explain( $opt, $self->get_name() );
return 'ERROR: '. $@; return 'ERROR: '. $@;
@@ -150,10 +153,10 @@ sub to_typemap {
my $txt = q{}; my $txt = q{};
my %nsmap = reverse %{ $opt->{ wsdl }->get_xmlns() }; my %nsmap = reverse %{ $opt->{ wsdl }->get_xmlns() };
my $type; my $type;
push @{ $opt->{path} }, $self->get_name(); push @{ $opt->{path} }, $self->get_name();
# referenced types need type_prefix
if ( my $typename = $self->get_type() ) { if ( my $typename = $self->get_type() ) {
my ($prefix, $localname) = split /:/, $self->get_type(); my ($prefix, $localname) = split /:/, $self->get_type();
my $ns = $nsmap{ $prefix }; my $ns = $nsmap{ $prefix };
@@ -166,18 +169,21 @@ sub to_typemap {
else else
{ {
$type = $opt->{ wsdl }->first_types()->find_type( $ns, $localname ); $type = $opt->{ wsdl }->first_types()->find_type( $ns, $localname );
# referenced types need type_prefix (may be globally unique) # referenced types need type_prefix (may be globally unique)
$typeclass = $opt->{ type_prefix } . $type->get_name(); $typeclass = $opt->{ type_prefix } . $type->get_name();
$txt .= $type->to_typemap($opt); $txt .= $type->to_typemap($opt);
} }
$txt .= q{'} . join( q{/}, @{ $opt->{path} } ) . "' => '$typeclass',\n"; $txt .= q{'} . join( q{/}, @{ $opt->{path} } ) . "' => '$typeclass',\n";
} }
# atomic types need element prefix # atomic types need element prefix
elsif ($type = $self->first_simpleType() ) { elsif ($type = $self->first_simpleType() ) {
# atomic types need element prefix (may be locally unique) # atomic types need element prefix (may be locally unique)
# TODO fix simpletype Typemap # TODO fix simpletype Typemap
my $typeclass = $opt->{ element_prefix } . $self->get_name(); my $typeclass = $opt->{ element_prefix } . $self->get_name();
$txt .= q{'} . join( q{/}, @{ $opt->{path} } ) . "' => '$typeclass',\n"; $txt .= q{'} . join( q{/}, @{ $opt->{path} } ) . "' => '$typeclass',\n";
my $flavor = $type->get_flavor(); my $flavor = $type->get_flavor();
if ( $flavor eq 'sequence' ) { if ( $flavor eq 'sequence' ) {
$txt .= "# atomic simple type (sequence)\n"; $txt .= "# atomic simple type (sequence)\n";
@@ -194,7 +200,8 @@ sub to_typemap {
elsif ($type = $self->first_complexType() ) { elsif ($type = $self->first_complexType() ) {
my $typeclass = $opt->{ element_prefix } . $self->get_name(); my $typeclass = $opt->{ element_prefix } . $self->get_name();
$txt .= q{'} . join( q{/}, @{ $opt->{path} } ) . "' => '$typeclass',\n"; $txt .= q{'} . join( q{/}, @{ $opt->{path} } ) . "' => '$typeclass',\n";
my $flavor = $type->get_flavor(); my $flavor = $type->get_flavor()
|| 'UNKNOWN';
if ( $flavor eq 'sequence' ) { if ( $flavor eq 'sequence' ) {
$txt .= "# atomic complex type (sequence)\n"; $txt .= "# atomic complex type (sequence)\n";
$txt .= $type->to_typemap($opt). "\n";; $txt .= $type->to_typemap($opt). "\n";;
@@ -205,18 +212,22 @@ sub to_typemap {
$txt .= $type->to_typemap($opt). "\n"; $txt .= $type->to_typemap($opt). "\n";
$txt .= "# end atomic complex type (all)\n"; $txt .= "# end atomic complex type (all)\n";
} }
else {
warn "flavor $flavor in element " . $self->get_name() . "\n";
}
} }
pop @{ $opt->{ path } }; pop @{ $opt->{ path } };
return $txt; return $txt;
} }
sub toClass {
sub to_class {
my $self = shift; my $self = shift;
my $opt = shift; my $opt = shift;
my $template = <<'EOT'; my $template = <<'EOT';
package [% class_prefix %]::[% self.get_name %]; package [% element_prefix %][% self.get_name %];
use strict; use strict;
use Class::Std::Storable; use Class::Std::Storable;
use SOAP::WSDL::XSD::Typelib::Element; use SOAP::WSDL::XSD::Typelib::Element;
@@ -227,7 +238,7 @@ use base qw(
SOAP::WSDL::XSD::Typelib::Element SOAP::WSDL::XSD::Typelib::Element
SOAP::WSDL::XSD::Typelib::SimpleType SOAP::WSDL::XSD::Typelib::SimpleType
[% type.flavor_class %] [% type.flavor_class %]
[% type.base_class($class_prefix) %] [% type.base_class($type_prefix) %]
); );
[% ELSIF (type = self.first_complexType) %] [% ELSIF (type = self.first_complexType) %]
# atomic complexType # atomic complexType
@@ -258,23 +269,24 @@ __PACKAGE__->_factory(
IF nsmap.$prefix == 'http://www.w3.org/2001/XMLSchema' %] IF nsmap.$prefix == 'http://www.w3.org/2001/XMLSchema' %]
[% element.get_name %] => 'SOAP::WSDL::XSD::Typelib::Builtin::[% localname %]', [% element.get_name %] => 'SOAP::WSDL::XSD::Typelib::Builtin::[% localname %]',
[% ELSE %] [% ELSE %]
[% element.get_name %] => '[% type_prefix %]::[% localname %]', [% element.get_name %] => '[% type_prefix %][% localname %]',
[% END %] [% END %]
[% END %] [% END %]
} }
); );
[%# END complexType %] [%# END complexType %]
[% ELSIF (type = self.get_type) %] [% ELSIF (type = self.get_type) %]
#
# <element name="[% self.get_name %]" type="[% self.get_type %]"/> definition # <element name="[% self.get_name %]" type="[% self.get_type %]"/> definition
[% (typename = self.get_type); #
split_name = element.type.split(':'); [% split_name = self.get_type.split(':');
prefix = split_name.0; prefix = split_name.0;
localname = split_name.1; localname = split_name.1;
-%]
[% IF (nsmap.$prefix == 'http://www.w3.org/2001/XMLSchema'); IF (nsmap.$prefix == 'http://www.w3.org/2001/XMLSchema');
base_class = 'SOAP::WSDL::XSD::Typelib::Builtin::' _ localname ; base_class = 'SOAP::WSDL::XSD::Typelib::Builtin::' _ localname ;
ELSE; ELSE;
base_class = type_prefix _ '::' _ localname; base_class = type_prefix _ localname;
-%] -%]
use [% base_class %]; use [% base_class %];
@@ -298,21 +310,61 @@ __PACKAGE__->__set_maxOccurs([% self.get_maxOccurs %]);
__PACKAGE__->__set_ref('[% self.get_ref %]'); __PACKAGE__->__set_ref('[% self.get_ref %]');
1; 1;
__END__
=pod
=head1 NAME [% element_prefix %][% self.get_name %]
=head1 SYNOPSIS
=head1 DESCRIPTION
Type class for the XML element [% self.get_name %].
=head1 PROPERTIES
The following properties may be accessed using get_PROPERTY / set_PROPERTY
methods:
[%- IF (type = self.first_complexType);
FOREACH element = type.get_element %]
[% element.get_name -%]
[% END;
END %]
=head1 Object structure
[%- IF (type = self.first_complexType);
FOREACH element = type.get_element;
split_name = element.get_type.split(':');
prefix = split_name.0;
localname = split_name.1;
IF nsmap.$prefix == 'http://www.w3.org/2001/XMLSchema' %]
[% element.get_name %] => 'SOAP::WSDL::XSD::Typelib::Builtin::[% localname %]',
[% ELSE %]
[% element.get_name %] => '[% type_prefix %]::[% localname %]',
[% END;
END;
END %]
Structure as perl hash:
The object structure is displayed as hash below though this is not correct.
Complex hash elements actually are objects of their corresponding classes
(look for classes of the same name in your typleib).
new() will accept a hash structure like this, but transform it to a object
tree.
[% structure %]
=cut
EOT EOT
require Template; $self->SUPER::to_class($opt, $template);
my $tt = Template->new(
RELATIVE => 1,
);
my $code = $tt->process( \$template, {
class_prefix => $opt->{ prefix },
type_prefix => $opt->{ type_prefix },
self => $self,
nsmap => { reverse %{ $opt->{ wsdl }->get_xmlns() } },
},
$opt->{ output },
)
or die $tt->error();
} }
1; 1;
+1 -6
View File
@@ -40,13 +40,8 @@ sub serialize
sub explain sub explain
{ {
my ($self, $opt, $name ) = @_; my ($self, $opt, $name ) = @_;
my $perl;
$opt->{ indent } ||= ""; $opt->{ indent } ||= "";
$perl .= $opt->{ indent } if ($opt->{ readable }); return "$opt->{ indent }'$name' => \$someValue,\n"
$perl .= q{'} . $name . q{' => $someValue };
$perl .= "\n" if ($opt->{ readable });
return $perl;
} }
sub toClass { sub toClass {
+31 -31
View File
@@ -5,43 +5,42 @@ use Class::Std::Storable;
use base qw(SOAP::WSDL::Base); use base qw(SOAP::WSDL::Base);
my %base_of :ATTR(:name<base> :default<()>); my %base_of :ATTR(:name<base> :default<()>);
my %flavor_of :ATTR(:name<flavor> :default<()>);
my %itemType_of :ATTR(:name<itemType> :default<()>); my %itemType_of :ATTR(:name<itemType> :default<()>);
my %enumeration_of :ATTR(:name<enumeration> :default<()>); my %enumeration_of :ATTR(:name<enumeration> :default<()>);
# is set to simpleContent/complexContent # is set to simpleContent/complexContent
my %content_Model_of :ATTR(:name<contentModel> :default<()>); my %content_Model_of :ATTR(:name<contentModel> :default<()>);
# set to restriction|list|union|enumeration
my %flavor_of :ATTR(:name<flavor> :default<()>);
sub set_restriction { sub set_restriction {
my $self = shift; my $self = shift;
my @attributes = @_; my @attributes = @_;
$self->set_flavor( 'restriction' ); $self->set_flavor( 'restriction' );
foreach my $attr (@attributes) for (@attributes) {
{ next if (not $_->{ LocalName } eq 'base');
next if (not $attr->{ LocalName } eq 'restriction'); $self->set_base( $_->{ Value } );
$self->base( $attr->{ Value } ); }
}
} }
sub set_list { sub set_list {
my $self = shift; my $self = shift;
my @attributes = @_; my @attributes = @_;
$self->set_flavor( 'list' ); $self->set_flavor( 'list' );
foreach my $attr (@attributes) for (@attributes) {
{ next if (not $_->{ LocalName } eq 'type');
next if (not $attr->{ LocalName } eq 'list'); $self->set_base( $_->{ Value } );
$self->set_base( $attr->{ Value } ); }
}
} }
sub set_union { sub set_union {
my $self = shift; my $self = shift;
my @attributes = @_; my @attributes = @_;
$self->set_flavor( 'union' ); $self->set_flavor( 'union' );
foreach my $attr (@attributes) for (@attributes) {
{ next if (not $_->{ LocalName } eq 'memberTypes');
next if (not $attr->{ LocalName } eq 'memberTypes'); $self->set_base( [ split /\s/, $_->{ Value } ] );
$self->set_base( [ split /\s/, $attr->{ Value } ] ); }
}
} }
sub push_enumeration sub push_enumeration
@@ -50,10 +49,9 @@ sub push_enumeration
my @attr = @_; my @attr = @_;
my @attributes = @_; my @attributes = @_;
$self->set_flavor( 'enumeration' ); $self->set_flavor( 'enumeration' );
foreach my $attr (@attributes) for (@attributes) {
{ next if (not $_->{ LocalName } eq 'value');
next if (not $attr->{ LocalName } eq 'value'); push @{ $enumeration_of{ ident $self } }, $_->{ 'Value' };
push @{ $enumeration_of{ ident $self } }, $attr->{ 'Value' };
} }
} }
@@ -116,10 +114,12 @@ sub _check_value {
my $self = shift; my $self = shift;
} }
sub toClass { # TODO: implement to_class based on template...
sub to_class {
my $self = shift; my $self = shift;
my $opt = shift; my $opt = shift;
my $class_prefix = $opt->{ prefix }; my $class_prefix = $opt->{ type_prefix };
my $name = $opt->{name} || $self->get_name(); my $name = $opt->{name} || $self->get_name();
my $flavor = $self->get_flavor() eq 'list' my $flavor = $self->get_flavor() eq 'list'
? 'SOAP::WSDL::XSD::Typelib::Builtin::list' ? 'SOAP::WSDL::XSD::Typelib::Builtin::list'
@@ -1,8 +1,11 @@
package SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType; package SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType;
use strict; use strict;
use warnings; use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anyType); BEGIN {
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anyType);
}
my %value_of :ATTR(:get<value> :init_arg<value> :default<()>); my %value_of :ATTR(:get<value> :init_arg<value> :default<()>);
@@ -28,6 +31,7 @@ sub as_bool :BOOLIFY {
Class::Std::initialize(); # make :BOOLIFY overloading serializable Class::Std::initialize(); # make :BOOLIFY overloading serializable
1; 1;
__END__ __END__
+26 -3
View File
@@ -1,13 +1,34 @@
package SOAP::WSDL::XSD::Typelib::Builtin::boolean; package SOAP::WSDL::XSD::Typelib::Builtin::boolean;
use strict; use strict;
use warnings; use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType); # Speed up. Class::Std::new is slow - and we don't need it's functionality...
BEGIN {
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
}
my %pattern_of :ATTR(:name<pattern> :default<()>); my %pattern_of :ATTR(:name<pattern> :default<()>);
my %whiteSpace_of :ATTR(:name<whiteSpace> :default<()>); my %whiteSpace_of :ATTR(:name<whiteSpace> :default<()>);
my %value_of :ATTR(:get<value> :init_attr<value> :default<()>);
{
no warnings qw(redefine);
no strict qw(refs);
# Yes, I know it's ugly - but this is the fastest constructor to write
# for Class::Std-Style inside out objects..
*{ __PACKAGE__ . '::new' } = sub {
my $self = bless \do { my $foo } , shift;
if (@_) {
$value_of{ ident $self } = $_[0]->{ value }
if exists $_[0]->{ value }
}
return $self;
};
}
my %value_of :ATTR(:get<value> :init_attr<value> :default<()>);
sub serialize { sub serialize {
my ($self, $opt) = @_; my ($self, $opt) = @_;
@@ -33,6 +54,8 @@ sub set_value {
: 0; : 0;
} }
Class::Std::initialize(); # make :BOOLIFY overloading serializable Class::Std::initialize(); # make :BOOLIFY overloading serializable
1; 1;
+20 -2
View File
@@ -1,9 +1,27 @@
package SOAP::WSDL::XSD::Typelib::Builtin::int; package SOAP::WSDL::XSD::Typelib::Builtin::int;
use strict; use strict;
use warnings; use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::long);
# Speed up. Class::Std::new is slow - and we don't need it's functionality...
BEGIN {
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::long);
no warnings qw(redefine);
no strict qw(refs);
# Yes, I know it's ugly - but this is the fastest constructor to write
# for Class::Std-Style inside out objects..
*{ __PACKAGE__ . '::new' } = sub {
my $self = bless \do { my $foo } , shift;
if (@_) {
$self->set_value( $_[0]->{ value } )
if exists $_[0]->{ value }
}
return $self;
};
};
1; 1;
+22 -2
View File
@@ -1,8 +1,28 @@
package SOAP::WSDL::XSD::Typelib::Builtin::integer; package SOAP::WSDL::XSD::Typelib::Builtin::integer;
use strict; use strict;
use warnings; use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::decimal); # Speed up. Class::Std::new is slow - and we don't need it's functionality...
BEGIN {
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::decimal);
no warnings qw(redefine);
no strict qw(refs);
# Yes, I know it's ugly - but this is the fastest constructor to write
# for Class::Std-Style inside out objects..
*{ __PACKAGE__ . '::new' } = sub {
my $self = bless \do { my $foo } , shift;
if (@_) {
$self->set_value( $_[0]->{ value } )
if exists $_[0]->{ value }
}
return $self;
};
};
sub as_num :NUMERIFY { sub as_num :NUMERIFY {
return $_[0]->get_value(); return $_[0]->get_value();
+21 -2
View File
@@ -1,8 +1,27 @@
package SOAP::WSDL::XSD::Typelib::Builtin::long; package SOAP::WSDL::XSD::Typelib::Builtin::long;
use strict; use strict;
use warnings; use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::integer); # Speed up. Class::Std::new is slow - and we don't need it's functionality...
BEGIN {
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::integer);
no warnings qw(redefine);
no strict qw(refs);
# Yes, I know it's ugly - but this is the fastest constructor to write
# for Class::Std-Style inside out objects..
*{ __PACKAGE__ . '::new' } = sub {
my $self = bless \do { my $foo } , shift;
if (@_) {
$self->set_value( $_[0]->{ value } )
if exists $_[0]->{ value }
}
return $self;
};
};
1; 1;
+24 -3
View File
@@ -1,9 +1,27 @@
package SOAP::WSDL::XSD::Typelib::Builtin::string; package SOAP::WSDL::XSD::Typelib::Builtin::string;
use strict; use strict;
use warnings; use warnings;
use Class::Std::Storable;
# use HTML::Entities qw(encode_entities); # Speed up. Class::Std::new is slow - and we don't need it's functionality...
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType); BEGIN {
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
no warnings qw(redefine);
no strict qw(refs);
# Yes, I know it's ugly - but this is the fastest constructor to write
# for Class::Std-Style inside out objects..
*{ __PACKAGE__ . '::new' } = sub {
my $self = bless \do { my $foo } , shift;
if (@_) {
$self->set_value( $_[0]->{ value } )
if exists $_[0]->{ value }
}
return $self;
};
}
my %length_of :ATTR(:name<length> :default<()>); my %length_of :ATTR(:name<length> :default<()>);
my %minLength_of :ATTR(:name<minLength> :default<()>); my %minLength_of :ATTR(:name<minLength> :default<()>);
@@ -12,6 +30,8 @@ my %pattern_of :ATTR(:name<pattern> :default<()>);
my %enumeration_of :ATTR(:name<enumeration> :default<()>); my %enumeration_of :ATTR(:name<enumeration> :default<()>);
my %whiteSpace_of :ATTR(:name<whiteSpace> :default<()>); my %whiteSpace_of :ATTR(:name<whiteSpace> :default<()>);
my %char2entity = ( my %char2entity = (
q{&} => q{&amp;}, q{&} => q{&amp;},
q{<} => q{&lt;}, q{<} => q{&lt;},
@@ -20,6 +40,7 @@ my %char2entity = (
q{'} => q{&apos;}, q{'} => q{&apos;},
); );
sub serialize { sub serialize {
my ($self, $opt) = @_; my ($self, $opt) = @_;
my $ident = ident $self; my $ident = ident $self;
+107 -81
View File
@@ -23,6 +23,7 @@ sub _factory {
$CLASSES_OF{ $class } = shift; $CLASSES_OF{ $class } = shift;
no strict qw(refs); no strict qw(refs);
no warnings qw(redefine);
while (my ($name, $attribute_ref) = each %{ $ATTRIBUTES_OF{ $class } } ) while (my ($name, $attribute_ref) = each %{ $ATTRIBUTES_OF{ $class } } )
{ {
my $type = $CLASSES_OF{ $class }->{ $name } my $type = $CLASSES_OF{ $class }->{ $name }
@@ -35,27 +36,40 @@ sub _factory {
*{ "$class\::set_$name" } = sub { *{ "$class\::set_$name" } = sub {
my ($self, $value) = @_; my ($self, $value) = @_;
# set to # we accept:
# a) objects
# b) scalars
# c) list refs
# d) hash refs
# e) mixed stuff of all of the above, so we have to
# set our element to
# a) value if it's an object # a) value if it's an object
# b) New object with value for simple values # b) New object with value for simple values
# c) New object with value for list values and list type # c 1) New object with value for list values and list type
# d) List ref of new objects with value for list values and non-list type # c 2) List ref of new objects with value for list values and non-list type
# e) New object with values passed to new for HASH references # c + e) List ref of objects for list values (list of objects) and non-list type
# d) New object with values passed to new for HASH references
# #
# Die on non-ARRAY/HASH references - if you can define semantics # Die on non-ARRAY/HASH references - if you can define semantics
# for GLOB references, feel free to add them. # for GLOB references, feel free to add them.
$attribute_ref->{ ident $self } = (blessed $value) $attribute_ref->{ ident $self } = blessed $value
? $value ? $value
: (ref $value ) ? : ref $value
(ref $value eq 'ARRAY') ? ref $value eq 'ARRAY'
? $type->isa('SOAP::WSDL::XSD::Typelib::Builtin::list') ? $type->isa('SOAP::WSDL::XSD::Typelib::Builtin::list')
? $type->new({ value => $value }) ? $type->new({ value => $value })
: [ map { $type->new({ value => $_ }) } @{ $value } ] : [ map {
: (ref $value eq 'HASH') blessed($_)
? $type->new( $value ) ? ($_->isa('SOAP::WSDL::XSD::Typelib::Builtin::anyType'))
: die "Cannot use non-ARRAY/HASH as data" ? $_
: croak 'cannot use non-XSD object as value'
: $type->new({ value => $_ })
} @{ $value }
]
: ref $value eq 'HASH'
? $type->new( $value )
: die 'Cannot use non-ARRAY/HASH as data'
: $type->new({ value => $value }); : $type->new({ value => $value });
}; };
*{ "$class\::add_$name" } = sub { *{ "$class\::add_$name" } = sub {
@@ -75,97 +89,109 @@ sub _factory {
# add to list # add to list
return push @{ $attribute_ref->{ $ident } }, $value; return push @{ $attribute_ref->{ $ident } }, $value;
}; };
}
}
sub START { *{ "$class\::START" } = sub {
my ($self, $ident, $args_of) = @_; my ($self, $ident, $args_of) = @_;
my $class = ref $self;
# iterate over keys of arguments # iterate over keys of arguments
# and call set appropriate field in clase # and call set appropriate field in clase
map { ($ATTRIBUTES_OF{ $class }->{ $_ }) map { ($ATTRIBUTES_OF{ $class }->{ $_ })
? do { ? do {
my $method = "set_$_"; my $method = "set_$_";
$self->$method( $args_of->{ $_ } ); $self->$method( $args_of->{ $_ } );
} }
: $_ =~ m{ \A # beginning of string : $_ =~ m{ \A # beginning of string
xmlns # xmlns xmlns # xmlns
}xms }xms
? do {} ? do {}
: croak "unknown field $_ in $class"; : do { use Data::Dumper;
# TODO maybe only warn for unknown fields ? croak "unknown field $_ in $class. Valid fields are "
. join(', ', @{ $ELEMENTS_FROM{ $class } }) . "\n"
. Dumper @_ };
# TODO maybe only warn for unknown fields ?
} keys %$args_of; } keys %$args_of;
}; };
sub _get_elements { # this serialize method works fine for <all> and <sequence>
my $self = shift; # complextypes, as well as for <restriction><all> or
my $class = ref $self; # <restriction><sequence>.
my $ident = ident $self; # But what about choice, group, extension ?
return map { $_->{ $ident } } @{ $ELEMENTS_FROM{ $class } }; #
} *{ "$class\::_serialize" } = sub {
my $ident = ident $_[0];
# my $class = ref $_[0];
# this serialize method works fine for <all> and <sequence> # return concatenated return value of serialize call of all
# complextypes, as well as for <restriction><all> or # elements retrieved from get_elements expanding list refs.
# <restriction><sequence>. # get_elements is inlined for performance.
# But what about choice, group, extension ? return join q{} , map {
# my $element = $ATTRIBUTES_OF{ $class }->{ $_ }->{ $ident };
sub _serialize {
my $ident = ident $_[0];
my $class = ref $_[0];
# return concatenated return value of serialize call of all if (defined $element) {
# elements retrieved from get_elements expanding list refs. $element = [ $element ]
# get_elements is inlined for performance. if not ref $element eq 'ARRAY';
return join q{} , map { my $name = $_;
my $element = $ATTRIBUTES_OF{ $class }->{ $_ }->{ $ident };
if (defined $element) { map {
$element = [ $element ] # serialize element elements with their own serializer
if not ref $element eq 'ARRAY'; # but name them like they're named here.
my $name = $_; if ( $_->isa( 'SOAP::WSDL::XSD::Typelib::Element' ) ) {
$_->serialize( { name => $name } );
map { }
# serialize element elements with their own serializer # serialize complextype elments (of other types) with their
# but name them like they're named here. # serializer, but add element tags around.
if ( $_->isa( 'SOAP::WSDL::XSD::Typelib::Element' ) ) { else {
$_->serialize( { name => $_ } ); join q{}, $_->start_tag({ name => $name })
, $_->serialize()
, $_->end_tag({ name => $name });
}
} @{ $element }
} }
# serialize complextype elments (of other types) with their
# serializer, but add element tags around.
else { else {
join q{}, $_->start_tag({ name => $name }) q{};
, $_->serialize()
, $_->end_tag({ name => $name });
} }
} @{ $element } } (@{ $ELEMENTS_FROM{ $class } });
} };
else {
q{};
}
} (@{ $ELEMENTS_FROM{ $class } });
}
sub serialize { *{ "$class\::serialize" } = sub {
my ($self, $opt) = @_; my ($self, $opt) = @_;
$opt ||= {}; $opt ||= {};
# do we have a empty element ? # do we have a empty element ?
return $self->start_tag({ %$opt, empty => 1 }) return $self->start_tag({ %$opt, empty => 1 })
if not @{ $ELEMENTS_FROM{ ref $self } }; if not defined $ELEMENTS_FROM{ $class } or not @{ $ELEMENTS_FROM{ $class } };
return join q{}, $self->start_tag($opt), return join q{}, $self->start_tag($opt),
$self->_serialize(), $self->end_tag(); $self->_serialize(), $self->end_tag();
}
}
} }
1; 1;
__END__
=pod =pod
=head1 NAME
SOAP::WSDL::XSD::Typelib::ComplexType - ComplexType XML Schema definitions
=head1 Bugs and limitations =head1 Bugs and limitations
=over =over
=item * Incomplete API
Not all variants of XML Schema ComplexType definitions are supported yet.
Variants known to work are:
sequence
all
complexContent containing sequence/all definitions
=item * Thread safety =item * Thread safety
SOAP::WSDL::XSD::Typelib::Builtin uses Class::Std::Storable which uses SOAP::WSDL::XSD::Typelib::Builtin uses Class::Std::Storable which uses
+5 -4
View File
@@ -40,7 +40,7 @@ sub start_tag {
my $class = ref $self; my $class = ref $self;
my $ending = '>'; my $ending = '>';
my @attr_from = (); my @attr_from = ();
my $name = $opt->{ name } || $NAME{$class};
$ending = '/>' if ($opt->{ empty }); $ending = '/>' if ($opt->{ empty });
if ($opt->{qualified}) { if ($opt->{qualified}) {
@@ -51,12 +51,13 @@ sub start_tag {
push @attr_from, 'xsi:nil="true"'; push @attr_from, 'xsi:nil="true"';
$ending = '/>'; $ending = '/>';
} }
return join q{ }, "<$NAME{$class}" , @attr_from , $ending; return join q{ }, "<$name" , @attr_from , $ending;
} }
sub end_tag { sub end_tag {
my ($self, $class) = ($_[0], ref $_[0]); my ($class, $opt) = (ref $_[0], $_[1]);
return "</$NAME{$class}>"; my $name = $opt->{ name } || $NAME{$class};
return "</$name>";
} }
1; 1;
+3 -4
View File
@@ -1,8 +1,7 @@
#!/usr/bin/perl -w #!/usr/bin/perl -w
use strict; use strict;
use warnings; use warnings;
use diagnostics; use Test::More tests => 18; # qw/no_plan/; # TODO: change to tests => N;
use Test::More tests => 17; # qw/no_plan/; # TODO: change to tests => N;
use lib '../lib'; use lib '../lib';
use XML::SAX::ParserFactory; use XML::SAX::ParserFactory;
@@ -28,10 +27,10 @@ $parser->parse_string( xml() );
my $wsdl; my $wsdl;
ok( $wsdl = $filter->get_data() , "get object tree"); ok( $wsdl = $filter->get_data() , "get object tree");
# print Dumper $wsdl;
my $types = $wsdl->first_types(); my $types = $wsdl->first_types();
is $types->get_parent(), $wsdl , 'types parent';
my $serializer_options = { my $serializer_options = {
readable => 1, readable => 1,
autotype => 1, autotype => 1,
+10 -5
View File
@@ -7,6 +7,7 @@ use lib 't/lib';
use_ok qw(SOAP::WSDL::XSD::Typelib::Element); use_ok qw(SOAP::WSDL::XSD::Typelib::Element);
use_ok qw( MyElement ); use_ok qw( MyElement );
# simple type derived from builtin via restriction # simple type derived from builtin via restriction
my $obj = MyElement->new({ value => 'test'}); my $obj = MyElement->new({ value => 'test'});
ok $obj->isa('SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType') ok $obj->isa('SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType')
@@ -20,8 +21,8 @@ ok $obj->isa('SOAP::WSDL::XSD::Typelib::Builtin::anyType')
ok $obj->get_test->isa('SOAP::WSDL::XSD::Typelib::Builtin::string') ok $obj->get_test->isa('SOAP::WSDL::XSD::Typelib::Builtin::string')
, 'element isa'; , 'element isa';
is $obj, '<MyAtomicComplexTypeElement xmlns="urn:Test" ><MyTestElement >Test</MyTestElement>' is $obj, '<MyAtomicComplexTypeElement xmlns="urn:Test" ><test >Test</test>'
. '<MyTestElement2 >Test2</MyTestElement2></MyAtomicComplexTypeElement>' . '<test2 >Test2</test2></MyAtomicComplexTypeElement>'
, 'stringification'; , 'stringification';
$obj = MyElement->new({ value => undef}); $obj = MyElement->new({ value => undef});
@@ -33,12 +34,16 @@ ok $obj->isa('SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType')
$obj = MyAtomicComplexTypeElement->new({ test=> 'Test', test2 => [ 'Test2', 'Test3' ]}); $obj = MyAtomicComplexTypeElement->new({ test=> 'Test', test2 => [ 'Test2', 'Test3' ]});
ok $obj->isa('SOAP::WSDL::XSD::Typelib::Builtin::anyType') ok $obj->isa('SOAP::WSDL::XSD::Typelib::Builtin::anyType')
, 'inherited class'; , 'inherited class';
is $obj, '<MyAtomicComplexTypeElement xmlns="urn:Test" ><MyTestElement >Test</MyTestElement>' is $obj, '<MyAtomicComplexTypeElement xmlns="urn:Test" ><test >Test</test>'
. '<MyTestElement2 >Test2</MyTestElement2>' . '<test2 >Test2</test2>'
. '<MyTestElement2 >Test3</MyTestElement2>' . '<test2 >Test3</test2>'
. '</MyAtomicComplexTypeElement>' . '</MyAtomicComplexTypeElement>'
, 'multi value stringification'; , 'multi value stringification';
use diagnostics;
ok $obj = MyComplexTypeElement->new({ MyTestName => 'test' });
is $obj, '<MyComplexTypeElement xmlns="urn:Test" ><MyTestName >test</MyTestName ></MyComplexTypeElement>';
__END__ __END__
+3 -3
View File
@@ -11,12 +11,12 @@ use_ok qw( MyComplexType );
my $obj = MyComplexType->new({ MyTestName => 'test' }); my $obj = MyComplexType->new({ MyTestName => 'test' });
ok $obj->isa('SOAP::WSDL::XSD::Typelib::Builtin::anyType') ok $obj->isa('SOAP::WSDL::XSD::Typelib::Builtin::anyType')
, 'inherited class'; , 'inherited class';
is $obj, '<MyElementName >test</MyElementName>', 'stringification'; is $obj, '<MyTestName >test</MyTestName >', 'stringification';
$obj = MyComplexType->new({ MyTestName => [ 'test', 'test2' ] }); $obj = MyComplexType->new({ MyTestName => [ 'test', 'test2' ] });
ok $obj->isa('SOAP::WSDL::XSD::Typelib::Builtin::anyType') ok $obj->isa('SOAP::WSDL::XSD::Typelib::Builtin::anyType')
, 'inherited class'; , 'inherited class';
is $obj, '<MyElementName >test</MyElementName><MyElementName >test2</MyElementName>', is $obj, '<MyTestName >test</MyTestName ><MyTestName >test2</MyTestName >',
'stringification'; 'stringification';
# try on the fly factory # try on the fly factory
@@ -35,7 +35,7 @@ is $obj, '<MyElementName >test</MyElementName><MyElementName >test2</MyElementNa
$obj = MyComplexType2->new({ MyTestName => [ 'test', 'test2' ] }); $obj = MyComplexType2->new({ MyTestName => [ 'test', 'test2' ] });
ok $obj->isa('SOAP::WSDL::XSD::Typelib::Builtin::anyType') ok $obj->isa('SOAP::WSDL::XSD::Typelib::Builtin::anyType')
, 'inherited class (on the fly-factory object)'; , 'inherited class (on the fly-factory object)';
is $obj, '<MyElementName >test</MyElementName><MyElementName >test2</MyElementName>', is $obj, '<MyTestName >test</MyTestName><MyTestName >test2</MyTestName>',
'stringification (on the fly-factory object)'; 'stringification (on the fly-factory object)';
# print Dumper $obj->get_MyTestName(); # print Dumper $obj->get_MyTestName();
+27 -14
View File
@@ -1,7 +1,7 @@
#!/usr/bin/perl -w #!/usr/bin/perl -w
use strict; use strict;
use warnings; use warnings;
use Test::More tests => 5; use Test::More tests => 16;
use lib '../lib'; use lib '../lib';
use XML::SAX::ParserFactory; use XML::SAX::ParserFactory;
@@ -33,27 +33,40 @@ else
pass("parsing XML"); pass("parsing XML");
} }
my $TMessage;
my $wsdl; my $wsdl;
ok( $wsdl = $filter->get_data() , "get object tree"); ok( $wsdl = $filter->get_data() , "get object tree");
my %content;
for my $element (@{ $wsdl->first_types()->get_schema()->[1]->get_type() } ) { for my $element (@{ $wsdl->first_types()->get_schema()->[1]->get_type() } ) {
# print Dumper $element; local $SIG{__WARN__} = sub {
like $_[0], qr{toClass \s is \s deprecated}xms, 'deprecated method warning';
};
my $output; my $output;
$element->toClass({ prefix => 'MessageGateway', wsdl => $wsdl, $element->to_class({ prefix => 'MessageGateway::', wsdl => $wsdl,
output => \$output output => \$output
}); });
eval "$output";
# skip eval'ing TMessage - it requires evalling other
# types first
if ($element->get_name() =~ m{\A (TMessage|TEnqueueMessage) \Z}xmsg ) {
$content{$1} = $output;
next;
}
my $name = 'MessageGateway::' . $element->get_name();
ok eval $output, $name;
ok $name->can('serialize'), "$name\->can('serialize')";
} }
if ($@) { while ( my ($name, $code) = (each %content) ) {
fail "evalling generated class"; ok eval "$code", $name;
}
else
{
pass "evalling generated class";
} }
exit; ok MessageGateway::TMessage->can('serialize'), 'MessageGateway::TMessage->can("serialize")';
ok MessageGateway::TEnqueueMessage->can('serialize'), "MessageGateway::TEnqueueMessage->can('serialize')";
sub xml { sub xml {
return q{<?xml version="1.0" encoding="UTF-8"?> return q{<?xml version="1.0" encoding="UTF-8"?>
@@ -302,7 +315,7 @@ sub xml {
<wsdl:portType name="MGWPortType"> <wsdl:portType name="MGWPortType">
<wsdl:documentation> <wsdl:documentation>
generic port type for all methods required for sending messages over the mosaic generic port type for all methods required for sending messages over the
message gatewa message gatewa
</wsdl:documentation> </wsdl:documentation>
<wsdl:operation name="EnqueueMessage"> <wsdl:operation name="EnqueueMessage">
@@ -350,11 +363,11 @@ sub xml {
</wsdl:binding> </wsdl:binding>
<wsdl:service name="MessageGateway"> <wsdl:service name="MessageGateway">
<wsdl:documentation> <wsdl:documentation>
Web Service for sending messages over the mosaic message gatewa Web Service for sending messages over the message gatewa
</wsdl:documentation> </wsdl:documentation>
<wsdl:port name="HTTPPort" binding="tns:MGWBinding"> <wsdl:port name="HTTPPort" binding="tns:MGWBinding">
<wsdl:documentation>HTTP(S) port for the mosaic message gatewa</wsdl:documentation> <wsdl:documentation>HTTP(S) port for the message gatewa</wsdl:documentation>
<soap:address location="https://www.example.org/MessageGateway/" /> <soap:address location="https://www.example.org/MessageGateway/" />
</wsdl:port> </wsdl:port>
</wsdl:service> </wsdl:service>
+4 -4
View File
@@ -20,8 +20,8 @@ ok $obj->isa('SOAP::WSDL::XSD::Typelib::Builtin::anyType')
ok $obj->get_test->isa('SOAP::WSDL::XSD::Typelib::Builtin::string') ok $obj->get_test->isa('SOAP::WSDL::XSD::Typelib::Builtin::string')
, 'element isa'; , 'element isa';
is $obj, '<MyAtomicComplexTypeElement xmlns="urn:Test" ><MyTestElement >Test</MyTestElement>' is $obj, '<MyAtomicComplexTypeElement xmlns="urn:Test" ><test >Test</test>'
. '<MyTestElement2 >Test2</MyTestElement2></MyAtomicComplexTypeElement>' . '<test2 >Test2</test2></MyAtomicComplexTypeElement>'
, 'stringification'; , 'stringification';
my $soap = SOAP::WSDL::Client->new( { my $soap = SOAP::WSDL::Client->new( {
@@ -34,8 +34,8 @@ is $soap->call('Test', $obj), q{<SOAP-ENV:Envelope }
. q{xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" } . q{xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" }
. q{xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" >} . q{xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" >}
. q{<SOAP-ENV:Body><MyAtomicComplexTypeElement xmlns="urn:Test" >} . q{<SOAP-ENV:Body><MyAtomicComplexTypeElement xmlns="urn:Test" >}
. q{<MyTestElement >Test</MyTestElement>} . q{<test >Test</test>}
. q{<MyTestElement2 >Test2</MyTestElement2>} . q{<test2 >Test2</test2>}
. q{</MyAtomicComplexTypeElement></SOAP-ENV:Body></SOAP-ENV:Envelope>} . q{</MyAtomicComplexTypeElement></SOAP-ENV:Body></SOAP-ENV:Envelope>}
, 'SOAP Envelope generation with objects'; , 'SOAP Envelope generation with objects';
+401
View File
@@ -0,0 +1,401 @@
#!/usr/bin/perl -w
use strict;
use warnings;
use Test::More tests => 18;
use lib '../lib';
use XML::LibXML;
use SOAP::WSDL::SAX::WSDLHandler;
use SOAP::WSDL::SAX::MessageHandler;
use File::Path;
use File::Basename;
my $path = dirname __FILE__;
my $filter = SOAP::WSDL::SAX::WSDLHandler->new();
my $parser = XML::LibXML->new();
$parser->set_handler( $filter );
$parser->parse_string( xml() );
my $wsdl;
ok( $wsdl = $filter->get_data() , "get object tree");
ok $wsdl->create_interface({
base_path => "$path/testlib",
typemap_prefix => "Test::Typemap::",
type_prefix => "Test::Type::",
element_prefix => "Test::Element::",
interface_prefix => "Test::Interface::",
});
eval "use lib '$path/testlib'";
use_ok qw(Test::Element::EnqueueMessage);
use_ok qw(Test::Type::TMessage);
use_ok qw(Test::Typemap::MessageGateway);
my $data = {
MMessage => {
MRecipientURI => 'anyURI',
MSenderAddress => 'a string',
MMessageContent => 'a string',
MSubject => 'a string',
MDeliveryReportRecipientURI => 'anyURI',
MKeepalive => {
MKeepaliveTimeout => 1234567,
MKeepaliveErrorPolicy => ' ( suppress | report ) ',
}
}
};
ok Test::Element::EnqueueMessage->new( $data ) , '(generated) object constructor';
my $handler = SOAP::WSDL::SAX::MessageHandler->new({
class_resolver => 'Test::Typemap::MessageGateway'
});
$parser->set_handler( $handler );
TODO: {
local $TODO = 'support embedded atomic simpleType/complexType definitions';
eval { $parser->parse_string( xml_message() ) };
ok ( !$@, 'parse XML message into object tree');
};
SKIP: {
eval "require Test::Pod";
skip 'Cannot test generated POD without Test::POD' , 6 if $@;
foreach my $module (Test::Pod::all_pod_files( "$path/testlib")) {
Test::Pod::pod_file_ok( $module )
}
}
# cleanup
rmtree "$path/testlib";
# print $wsdl->explain();
sub xml_message {
return
q{<EnqueueMessage xmlns="http://www.example.org/MessageGateway2/">
<MMessage>
<MRecipientURI>anyURI</MRecipientURI>
<MSenderAddress>a string</MSenderAddress>
<MMessageContent>a string</MMessageContent>
<MSubject>a string</MSubject>
<MDeliveryReportRecipientURI>anyURI</MDeliveryReportRecipientURI>
<MKeepalive>
<MKeepaliveTimeout>1234567</MKeepaliveTimeout>
<MKeepaliveErrorPolicy> ( suppress | report ) </MKeepaliveErrorPolicy>
</MKeepalive>
</MMessage>
</EnqueueMessage>
};
}
sub xml {
return q{<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="MessageGateway"
targetNamespace="http://www.example.org/MessageGateway2/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://www.example.org/MessageGateway2/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<wsdl:types>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.org/MessageGateway2/">
<xsd:element name="EnqueueMessage" type="tns:TEnqueueMessage"
xmlns:tns="http://www.example.org/MessageGateway2/">
<xsd:annotation>
<xsd:documentation>Enqueue message request element</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:complexType name="TMessage">
<xsd:annotation>
<xsd:documentation>
A type containing all elements of a message to enqueue.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="MRecipientURI" type="xsd:anyURI" minOccurs="1"
maxOccurs="1">
<xsd:annotation>
<xsd:documentation>
The recipient in URI notaitions. Valid URI schemas are: mailto:, sms:,
phone:. Not all URI schemas need to be implemented at the current
implementation stage.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="MSenderAddress" type="xsd:string" minOccurs="0"
maxOccurs="1">
<xsd:annotation>
<xsd:documentation>
E-Mail sender address. Ignored for all but mailto: recipient URIs.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="MMessageContent" type="xsd:string" minOccurs="1"
maxOccurs="1">
<xsd:annotation>
<xsd:documentation>Message Content.</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="MSubject" type="xsd:string" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation>
Message Subject. Ignored for all but mailto: URIs
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="MDeliveryReportRecipientURI" type="xsd:anyURI" minOccurs="0"
maxOccurs="1">
<xsd:annotation>
<xsd:documentation>
URI to send a delivery report to. May be of one of the following schemes:
mailto:, http:, https:. Reports to mailto: URIs are sent as plaintext,
reports to http(s) URIs are sent as SOAP requests following the
MessageGatewayClient service definition.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="MKeepalive" type="tns:TKeepalive" minOccurs="0"
maxOccurs="1">
<xsd:annotation>
<xsd:documentation>
Container for keepalive information. May be missing.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="TKeepalive">
<xsd:annotation>
<xsd:documentation>Type containing keeplive information.</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="MKeepaliveTimeout" type="xsd:double">
<xsd:annotation>
<xsd:documentation>
Keepalive timeout. The keepalive timeout spezifies how long the sending of
a message will be delayed waiting for keepalive updates. If a keepalive
update is received during this period, the timeout will be reset. If not,
the message will be sent after the timeout has expired.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="MKeepaliveErrorPolicy" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation>
Policy to comply to in case of system errors. Valid values are "suppress"
and "report". If the policy is set to "suppress", keepalive messages will
not be sent to their recipients in case of partial system failure, even if
the keepalive has expired. This may result in "false negatives", i.e.
messages may not be sent, even though their keepalive has expired. If the
value is "report", keepalive messages will be sent from any cluster node.
This may result in "false positive" alerts.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="suppress"></xsd:enumeration>
<xsd:enumeration value="report"></xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="TMessageID">
<xsd:annotation>
<xsd:documentation>Type containing a message ID.</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="MMessageID" type="xsd:string" minOccurs="1" maxOccurs="1"></xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="TKeepliveMessage">
<xsd:annotation>
<xsd:documentation>
Type containing all elements of a keppalive update / remove request.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="MMessageID" type="xsd:string" minOccurs="1" maxOccurs="1">
<xsd:annotation>
<xsd:documentation>
The ID for the message to update / remove
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="MAction" minOccurs="1" maxOccurs="1">
<xsd:annotation>
<xsd:documentation>
The action to perform. Valid values are: "remove", "update". On "remove",
the message with the ID specified will be removed from the queue, thus it
will never be sent, even if it's timeout expires. On "update" the
keepalive timeout of the corresponding message will be reset.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="remove"></xsd:enumeration>
<xsd:enumeration value="update"></xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="KeepaliveMessage" type="tns:TKeepaliveMessageRequest">
<xsd:annotation>
<xsd:documentation>Keepalive message request element</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="KeepaliveMessageResponse" type="tns:TMessageID">
<xsd:annotation>
<xsd:documentation>Response element for a keepalive request</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="EnqueueMessageResponse" type="tns:TMessageID">
<xsd:annotation>
<xsd:documentation>Enqueue message response element</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:complexType name="TEnqueueMessage">
<xsd:annotation>
<xsd:documentation>
A complex type containing one element: The message to enqueue.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="MMessage" type="tns:TMessage">
<xsd:annotation>
<xsd:documentation>
Element containing a message to enqueue.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="TKeepaliveMessageRequest">
<xsd:annotation>
<xsd:documentation>
A complex type containing one element: The keepalive message to process.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="MKeepaliveMessage" type="tns:TKeepliveMessage">
<xsd:annotation>
<xsd:documentation>
Element containing a keepalive message to process.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
</wsdl:types>
<wsdl:message name="EnqueueMessageRequest">
<wsdl:part name="parameters" element="tns:EnqueueMessage">
<wsdl:documentation>inputparameters for EnqueueMessag</wsdl:documentation>
</wsdl:part>
</wsdl:message>
<wsdl:message name="EnqueueMessageResponse">
<wsdl:part name="parameters" element="tns:EnqueueMessageResponse">
<wsdl:documentation>outputparameters for EnqueueMessag</wsdl:documentation>
</wsdl:part>
</wsdl:message>
<wsdl:message name="KeepaliveMessageRequest">
<wsdl:part name="parameters" element="tns:KeepaliveMessage">
<wsdl:documentation>input parameters for KeepaliveMessag</wsdl:documentation>
</wsdl:part>
</wsdl:message>
<wsdl:message name="KeepaliveMessageResponse">
<wsdl:part name="parameters" element="tns:KeepaliveMessageResponse">
<wsdl:documentation>output parameters for KeepaliveMessag</wsdl:documentation>
</wsdl:part>
</wsdl:message>
<wsdl:portType name="MGWPortType">
<wsdl:documentation>
generic port type for all methods required for sending messages over the mosaic
message gatewa
</wsdl:documentation>
<wsdl:operation name="EnqueueMessage">
<wsdl:documentation>
This method is used to enqueue a normal (immediate send) or a delayed message with
keepalive functionality.
</wsdl:documentation>
<wsdl:input message="tns:EnqueueMessageRequest"></wsdl:input>
<wsdl:output message="tns:EnqueueMessageResponse"></wsdl:output>
</wsdl:operation>
<wsdl:operation name="KeepaliveMessage">
<wsdl:documentation>
This method is used to update or remove a
keepalive message.
</wsdl:documentation>
<wsdl:input message="tns:KeepaliveMessageRequest"></wsdl:input>
<wsdl:output message="tns:KeepaliveMessageResponse"></wsdl:output>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="MGWBinding" type="tns:MGWPortType">
<wsdl:documentation>Generic binding for all (SOAP) port</wsdl:documentation>
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="EnqueueMessage">
<soap:operation soapAction="http://www.example.org/MessageGateway2/EnqueueMessage" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="KeepaliveMessage">
<soap:operation
soapAction="http://www.example.org/MessageGateway2/KeepaliveMessage" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="MessageGateway">
<wsdl:documentation>
Web Service for sending messages over the message gatewa
</wsdl:documentation>
<wsdl:port name="HTTPPort" binding="tns:MGWBinding">
<wsdl:documentation>HTTP(S) port for the message gateway</wsdl:documentation>
<soap:address location="https://www.example.org/MessageGateway/" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>};
}
+2 -1
View File
@@ -3,7 +3,8 @@ use lib '../lib';
eval "require SOAP::WSDL::XSD::Typelib::Builtin"; eval "require SOAP::WSDL::XSD::Typelib::Builtin";
use Storable; use Storable;
my $long = SOAP::WSDL::XSD::Typelib::Builtin::long->new( { value => 9 }); my $long = SOAP::WSDL::XSD::Typelib::Builtin::long->new();
$long->set_value( 9 );
my $clone = Storable::thaw( Storable::freeze( $long ) ); my $clone = Storable::thaw( Storable::freeze( $long ) );
is $clone->serialize, 9 , 'clone via freeze/thaw'; is $clone->serialize, 9 , 'clone via freeze/thaw';
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/perl -w
use strict;
use warnings;
use Test::More tests => 2;
use lib '../lib';
use lib 'lib';
use lib 't/lib';
use XML::LibXML;
use SOAP::WSDL::SAX::MessageHandler;
use_ok(qw/SOAP::WSDL::Expat::MessageParser/);
use MyComplexType;
use MyElement;
use MySimpleType;
use Benchmark;
my $xml = q{<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" >
<SOAP-ENV:Body><MyAtomicComplexTypeElement xmlns="urn:Test" >
<test>Test</test>
<test2 >Test2</test2>
</MyAtomicComplexTypeElement></SOAP-ENV:Body></SOAP-ENV:Envelope>};
my $parser = SOAP::WSDL::Expat::MessageParser->new({
class_resolver => 'FakeResolver'
});
my $libxml = XML::LibXML->new();
my $handler = SOAP::WSDL::SAX::MessageHandler->new({
class_resolver => 'FakeResolver',
});
$libxml->set_handler( $handler );
$parser->parse( $xml );
is $parser->get_data(), q{<MyAtomicComplexTypeElement xmlns="urn:Test" >}
. q{<test >Test</test><test2 >Test2</test2></MyAtomicComplexTypeElement>}
, 'Content comparison';
# data classes reside in t/lib/Typelib/
BEGIN {
package FakeResolver;
{
my %class_list = (
'MyAtomicComplexTypeElement' => 'MyAtomicComplexTypeElement',
'MyAtomicComplexTypeElement/test' => 'MyTestElement',
'MyAtomicComplexTypeElement/test2' => 'MyTestElement2',
);
sub new { return bless {}, 'FakeResolver' };
sub get_class {
my $name = join('/', @{ $_[1] });
return ($class_list{ $name }) ? $class_list{ $name }
: warn "no class found for $name";
};
};
};
-10
View File
@@ -1,15 +1,5 @@
use Test::More tests => 2; use Test::More tests => 2;
use lib '../lib'; use lib '../lib';
use lib 't/lib';
use lib 'lib';
use Cwd;
use File::Basename;
our $SKIP;
eval "use Test::SOAPMessage";
if ($@) {
$SKIP = "Test::Differences required for testing. $@";
}
use_ok qw/SOAP::WSDL/; use_ok qw/SOAP::WSDL/;
-2
View File
@@ -43,8 +43,6 @@ ok $soap = SOAP::WSDL->new(
no_dispatch => 1 no_dispatch => 1
), 'Create SOAP::WSDL object'; ), 'Create SOAP::WSDL object';
$soap->serializer()->namespace('SOAP-ENV');
$soap->serializer()->encodingspace('SOAP-ENC');
$soap->proxy('http://helloworld/helloworld.asmx'); $soap->proxy('http://helloworld/helloworld.asmx');
ok $soap->wsdlinit( ok $soap->wsdlinit(
-27
View File
@@ -1,27 +0,0 @@
use Test::More;
eval "use Test::Pod 1.00";
plan skip_all => "Test::Pod 1.00 required for testing POD" if $@;
use Cwd;
my $dir = cwd;
if ( $dir =~ /t$/ )
{
@directories = ('../lib/');
}
else
{
@directories = ();
}
my @files = all_pod_files(
@directories
);
plan tests => scalar(@files);
foreach my $module (@files)
{
pod_file_ok( $module )
}
+5 -6
View File
@@ -1,16 +1,15 @@
use Test::More tests => 4; use Test::More tests => 3;
use strict; use strict;
use warnings; use warnings;
use diagnostics;
use lib '../lib'; use lib '../lib';
use Benchmark;
use_ok('SOAP::WSDL::XSD::Typelib::Builtin::string'); use_ok('SOAP::WSDL::XSD::Typelib::Builtin::string');
my $obj; my $obj;
ok $obj = SOAP::WSDL::XSD::Typelib::Builtin::string->new( $obj = SOAP::WSDL::XSD::Typelib::Builtin::string->new();
{ value => '& "Aber" <test>'})
, "Object creation"; $obj->set_value( '& "Aber" <test>');
is $obj, '&amp; &qout;Aber&qout; &lt;test&gt;' is $obj, '&amp; &qout;Aber&qout; &lt;test&gt;'
, 'escape text on serialization'; , 'escape text on serialization';
+2 -2
View File
@@ -6,12 +6,12 @@ use lib '../../lib';
use SOAP::WSDL::XSD::Typelib::ComplexType; use SOAP::WSDL::XSD::Typelib::ComplexType;
use base ('SOAP::WSDL::XSD::Typelib::ComplexType'); use base ('SOAP::WSDL::XSD::Typelib::ComplexType');
my %MyTestName_of; # no :ATTR - _factory takes care of my %MyTestName_of :ATTR(:get<MyTestName>);
__PACKAGE__->_factory( __PACKAGE__->_factory(
[ qw(MyTestName) ], # order [ qw(MyTestName) ], # order
{ MyTestName => \%MyTestName_of }, # attribute lookup map { MyTestName => \%MyTestName_of }, # attribute lookup map
{ MyTestName => 'MyElement' } # class name lookup map { MyTestName => 'SOAP::WSDL::XSD::Typelib::Builtin::string' } # class name lookup map
); );
sub get_xmlns { 'urn:Test' }; sub get_xmlns { 'urn:Test' };
+16 -14
View File
@@ -9,11 +9,20 @@ use base (
'SOAP::WSDL::XSD::Typelib::Builtin::string', 'SOAP::WSDL::XSD::Typelib::Builtin::string',
); );
sub START { __PACKAGE__->__set_name('MyElementName');
my ($self, $ident, $args_of) =@_; sub get_xmlns { 'urn:Test' };
$self->__set_name('MyElementName');
}
package MyComplexTypeElement;
use strict;
use Class::Std::Storable;
use SOAP::WSDL::XSD::Typelib::Element;
use MyComplexType;
use base (
'SOAP::WSDL::XSD::Typelib::Element',
'MyComplexType',
);
__PACKAGE__->__set_name('MyComplexTypeElement');
sub get_xmlns { 'urn:Test' }; sub get_xmlns { 'urn:Test' };
package MyTestElement; package MyTestElement;
@@ -26,10 +35,6 @@ use base (
'SOAP::WSDL::XSD::Typelib::Builtin::string', 'SOAP::WSDL::XSD::Typelib::Builtin::string',
); );
sub START {
my ($self, $ident, $args_of) =@_;
}
__PACKAGE__->__set_name('MyTestElement'); __PACKAGE__->__set_name('MyTestElement');
sub get_xmlns { 'urn:Test' }; sub get_xmlns { 'urn:Test' };
@@ -44,12 +49,9 @@ use base (
'SOAP::WSDL::XSD::Typelib::Builtin::string', 'SOAP::WSDL::XSD::Typelib::Builtin::string',
); );
sub START { __PACKAGE__->__set_name('MyTestElement2');
my ($self, $ident, $args_of) =@_;
$self->__set_name('MyTestElement2');
}
sub get_xmlns { 'urn:Test' }; sub get_xmlns { 'urn:Test' };
;
package MyAtomicComplexTypeElement; package MyAtomicComplexTypeElement;
@@ -76,6 +78,7 @@ __PACKAGE__->_factory(
test2 => \%test2_of, test2 => \%test2_of,
}, },
{ {
# this is the <element ref="" variant....
test => 'MyTestElement', test => 'MyTestElement',
test2 => 'MyTestElement2', test2 => 'MyTestElement2',
}, },
@@ -83,5 +86,4 @@ __PACKAGE__->_factory(
__PACKAGE__->__set_name('MyAtomicComplexTypeElement'); __PACKAGE__->__set_name('MyAtomicComplexTypeElement');
1; 1;