[Catalyst-commits] r7743 - in Catalyst-Controller-SOAP/1.0/tags: .
1.10/lib/Catalyst/Action/SOAP 1.10/lib/Catalyst/Controller
ruoso at dev.catalyst.perl.org
ruoso at dev.catalyst.perl.org
Mon May 12 20:38:30 BST 2008
Author: ruoso
Date: 2008-05-12 20:38:30 +0100 (Mon, 12 May 2008)
New Revision: 7743
Added:
Catalyst-Controller-SOAP/1.0/tags/1.10/
Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/DocumentLiteral.pm
Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/HTTPGet.pm
Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/RPCEndpoint.pm
Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/RPCLiteral.pm
Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Controller/SOAP.pm
Removed:
Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/DocumentLiteral.pm
Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/HTTPGet.pm
Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/RPCEndpoint.pm
Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/RPCLiteral.pm
Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Controller/SOAP.pm
Log:
fixing versioning schema
Copied: Catalyst-Controller-SOAP/1.0/tags/1.10 (from rev 7697, Catalyst-Controller-SOAP/1.0/trunk)
Deleted: Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/DocumentLiteral.pm
===================================================================
--- Catalyst-Controller-SOAP/1.0/trunk/lib/Catalyst/Action/SOAP/DocumentLiteral.pm 2008-05-06 19:57:10 UTC (rev 7697)
+++ Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/DocumentLiteral.pm 2008-05-12 19:38:30 UTC (rev 7743)
@@ -1,73 +0,0 @@
-{ package Catalyst::Action::SOAP::DocumentLiteral;
-
- use base qw/Catalyst::Action::SOAP/;
- use constant NS_SOAP_ENV => "http://schemas.xmlsoap.org/soap/envelope/";
-
- sub execute {
- my $self = shift;
- my ( $controller, $c ) = @_;
- $self->prepare_soap_helper($controller,$c);
- $self->prepare_soap_xml_post($controller,$c);
- unless ($c->stash->{soap}->fault) {
- my $envelope = $c->stash->{soap}->parsed_envelope;
- my $namespace = $c->stash->{soap}->namespace || NS_SOAP_ENV;
- my ($body) = $envelope->getElementsByTagNameNS($namespace, 'Body');
- my $operation = $self->name;
- $c->stash->{soap}->operation_name($operation);
- eval {
- if ($controller->wsdlobj) {
- $body = $c->stash->{soap}->arguments
- ($controller->decoders->{$operation}->($body));
- }
- };
- if ($@) {
- $c->stash->{soap}->fault
- ({ code => 'Client',
- reason => 'Bad Body', detail =>
- 'Schema validation on the body failed: '.$@});
- } else {
- $self->NEXT::execute($controller, $c, $body);
- }
- }
- }
-};
-
-1;
-
-__END__
-
-=head1 NAME
-
-Catalyst::Action::SOAP::DocumentLiteral - Document Literal service
-
-=head1 SYNOPSIS
-
- # not used directly.
-
-=head1 DESCRIPTION
-
-This action implements a simple parse of the envelope and passing the
-body to the service as a xml object.
-
-=head1 TODO
-
-Almost all the SOAP protocol is unsupported, only the method
-dispatching and, optionally, the soap-decoding of the arguments are
-made.
-
-=head1 AUTHORS
-
-Daniel Ruoso <daniel.ruoso at verticalone.pt>
-
-=head1 BUG REPORTS
-
-Please submit all bugs regarding C<Catalyst::Controller::SOAP> to
-C<bug-catalyst-controller-soap at rt.cpan.org>
-
-=head1 LICENSE
-
-This library is free software, you can redistribute it and/or modify
-it under the same terms as Perl itself.
-
-=cut
-
Copied: Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/DocumentLiteral.pm (from rev 7717, Catalyst-Controller-SOAP/1.0/trunk/lib/Catalyst/Action/SOAP/DocumentLiteral.pm)
===================================================================
--- Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/DocumentLiteral.pm (rev 0)
+++ Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/DocumentLiteral.pm 2008-05-12 19:38:30 UTC (rev 7743)
@@ -0,0 +1,73 @@
+{ package Catalyst::Action::SOAP::DocumentLiteral;
+
+ use base qw/Catalyst::Action::SOAP/;
+ use constant NS_SOAP_ENV => "http://schemas.xmlsoap.org/soap/envelope/";
+
+ sub execute {
+ my $self = shift;
+ my ( $controller, $c ) = @_;
+ $self->prepare_soap_helper($controller,$c);
+ $self->prepare_soap_xml_post($controller,$c);
+ unless ($c->stash->{soap}->fault) {
+ my $envelope = $c->stash->{soap}->parsed_envelope;
+ my $namespace = $c->stash->{soap}->namespace || NS_SOAP_ENV;
+ my ($body) = $envelope->getElementsByTagNameNS($namespace, 'Body');
+ my $operation = $self->name;
+ $c->stash->{soap}->operation_name($operation);
+ eval {
+ if ($controller->wsdlobj) {
+ $body = $c->stash->{soap}->arguments
+ ($controller->decoders->{$operation}->($body));
+ }
+ };
+ if ($@) {
+ $c->stash->{soap}->fault
+ ({ code => 'Client',
+ reason => 'Bad Body', detail =>
+ 'Schema validation on the body failed: '.$@});
+ } else {
+ $self->next::method($controller, $c, $body);
+ }
+ }
+ }
+};
+
+1;
+
+__END__
+
+=head1 NAME
+
+Catalyst::Action::SOAP::DocumentLiteral - Document Literal service
+
+=head1 SYNOPSIS
+
+ # not used directly.
+
+=head1 DESCRIPTION
+
+This action implements a simple parse of the envelope and passing the
+body to the service as a xml object.
+
+=head1 TODO
+
+Almost all the SOAP protocol is unsupported, only the method
+dispatching and, optionally, the soap-decoding of the arguments are
+made.
+
+=head1 AUTHORS
+
+Daniel Ruoso <daniel.ruoso at verticalone.pt>
+
+=head1 BUG REPORTS
+
+Please submit all bugs regarding C<Catalyst::Controller::SOAP> to
+C<bug-catalyst-controller-soap at rt.cpan.org>
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
Deleted: Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/HTTPGet.pm
===================================================================
--- Catalyst-Controller-SOAP/1.0/trunk/lib/Catalyst/Action/SOAP/HTTPGet.pm 2008-05-06 19:57:10 UTC (rev 7697)
+++ Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/HTTPGet.pm 2008-05-12 19:38:30 UTC (rev 7743)
@@ -1,51 +0,0 @@
-{ package Catalyst::Action::SOAP::HTTPGet;
-
- use base qw/Catalyst::Action::SOAP/;
-
- sub execute {
- my $self = shift;
- my ( $controller, $c ) = @_;
- $self->prepare_soap_helper($controller,$c);
- $self->NEXT::execute(@_);
- }
-};
-
-1;
-
-__END__
-
-=head1 NAME
-
-Catalyst::Action::SOAP::HTTPGet - HTTP Get service
-
-=head1 SYNOPSIS
-
- # not used directly.
-
-=head1 DESCRIPTION
-
-This actually is here just to help delivering services that are
-invoked by simple http get requests, as defined in the SOAP spec. It
-won't do much, except for preparing the $c->stash->{soap} variable, so
-the returns can be implemented.
-
-=head1 TODO
-
-There is not much to be done here.
-
-=head1 AUTHORS
-
-Daniel Ruoso <daniel.ruoso at verticalone.pt>
-
-=head1 BUG REPORTS
-
-Please submit all bugs regarding C<Catalyst::Controller::SOAP> to
-C<bug-catalyst-controller-soap at rt.cpan.org>
-
-=head1 LICENSE
-
-This library is free software, you can redistribute it and/or modify
-it under the same terms as Perl itself.
-
-=cut
-
Copied: Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/HTTPGet.pm (from rev 7717, Catalyst-Controller-SOAP/1.0/trunk/lib/Catalyst/Action/SOAP/HTTPGet.pm)
===================================================================
--- Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/HTTPGet.pm (rev 0)
+++ Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/HTTPGet.pm 2008-05-12 19:38:30 UTC (rev 7743)
@@ -0,0 +1,51 @@
+{ package Catalyst::Action::SOAP::HTTPGet;
+
+ use base qw/Catalyst::Action::SOAP/;
+
+ sub execute {
+ my $self = shift;
+ my ( $controller, $c ) = @_;
+ $self->prepare_soap_helper($controller,$c);
+ $self->next::method(@_);
+ }
+};
+
+1;
+
+__END__
+
+=head1 NAME
+
+Catalyst::Action::SOAP::HTTPGet - HTTP Get service
+
+=head1 SYNOPSIS
+
+ # not used directly.
+
+=head1 DESCRIPTION
+
+This actually is here just to help delivering services that are
+invoked by simple http get requests, as defined in the SOAP spec. It
+won't do much, except for preparing the $c->stash->{soap} variable, so
+the returns can be implemented.
+
+=head1 TODO
+
+There is not much to be done here.
+
+=head1 AUTHORS
+
+Daniel Ruoso <daniel.ruoso at verticalone.pt>
+
+=head1 BUG REPORTS
+
+Please submit all bugs regarding C<Catalyst::Controller::SOAP> to
+C<bug-catalyst-controller-soap at rt.cpan.org>
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
Deleted: Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/RPCEndpoint.pm
===================================================================
--- Catalyst-Controller-SOAP/1.0/trunk/lib/Catalyst/Action/SOAP/RPCEndpoint.pm 2008-05-06 19:57:10 UTC (rev 7697)
+++ Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/RPCEndpoint.pm 2008-05-12 19:38:30 UTC (rev 7743)
@@ -1,109 +0,0 @@
-{ package Catalyst::Action::SOAP::RPCEndpoint;
-
- use strict;
- use base qw/Catalyst::Action::SOAP/;
- use constant NS_SOAP_ENV => "http://schemas.xmlsoap.org/soap/envelope/";
- use UNIVERSAL;
-
- sub execute {
- my $self = shift;
- my ( $controller, $c ) = @_;
-
- $self->prepare_soap_helper($controller,$c);
- $self->prepare_soap_xml_post($controller,$c);
- unless ($c->stash->{soap}->fault) {
- my $envelope = $c->stash->{soap}->parsed_envelope;
- my $namespace = $c->stash->{soap}->namespace || NS_SOAP_ENV;
- my ($body) = $envelope->getElementsByTagNameNS($namespace,'Body',0);
- my @children = grep { UNIVERSAL::isa( $_, 'XML::LibXML::Element') } $body->getChildNodes();
- if (scalar @children != 1) {
- $c->stash->{soap}->fault
- ({ code => 'Client',
- reason => 'Bad Body', detail =>
- 'RPC messages should contain only one element inside body'})
- } else {
- my $rpc_element = $children[0];
- my ($smthing, $operation) = split /:/, $rpc_element->nodeName();
- $operation ||= $smthing; # if there's no ns prefix,
- # operation is the first
- # part.
- $c->stash->{soap}->operation_name($operation);
-
- eval {
- if ($controller->wsdlobj) {
- my $decoder = $controller->decoders->{$operation};
- my ($args) = $decoder->($rpc_element);
- $c->stash->{soap}->arguments($args);
- } else {
- my $arguments = $rpc_element->getChildNodes();
- $c->stash->{soap}->arguments($arguments);
- }
- };
- if ($@) {
- $c->stash->{soap}->fault
- ({ code => 'Client',
- reason => 'Bad Body', detail =>
- 'Malformed parts on the message body: '.$@});
- } else {
- my $action = $controller->action_for($operation);
-
- if (!$action ||
- !grep { /RPC(Encoded|Literal)/ } @{$action->attributes->{ActionClass}}) {
- $c->stash->{soap}->fault
- ({ code => 'Client',
- reason => 'Bad Operation', detail =>
- 'Invalid Operation'});
- } else {
- # this is our RPC action
- $c->forward($operation);
- }
- }
-
- }
- }
- }
-};
-
-1;
-
-__END__
-
-=head1 NAME
-
-Catalyst::Action::SOAP::RPCEndpoint - RPC Dispatcher
-
-=head1 SYNOPSIS
-
- # not used directly.
-
-=head1 DESCRIPTION
-
-This class is used by L<Catalyst::Controller::SOAP> to dispatch to the
-RPC operations inside a controller. These operations are quite
-different from the others, as they are seen by Catalyst as this single
-action. During the registering phase, the soap rpc operations are
-included in the hash that is sent to this object, so they can be
-invoked later.
-
-=head1 TODO
-
-Almost all the SOAP protocol is unsupported, only the method
-dispatching and, optionally, the soap-decoding of the arguments are
-made.
-
-=head1 AUTHORS
-
-Daniel Ruoso <daniel.ruoso at verticalone.pt>
-
-=head1 BUG REPORTS
-
-Please submit all bugs regarding C<Catalyst::Controller::SOAP> to
-C<bug-catalyst-controller-soap at rt.cpan.org>
-
-=head1 LICENSE
-
-This library is free software, you can redistribute it and/or modify
-it under the same terms as Perl itself.
-
-=cut
-
Copied: Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/RPCEndpoint.pm (from rev 7739, Catalyst-Controller-SOAP/1.0/trunk/lib/Catalyst/Action/SOAP/RPCEndpoint.pm)
===================================================================
--- Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/RPCEndpoint.pm (rev 0)
+++ Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/RPCEndpoint.pm 2008-05-12 19:38:30 UTC (rev 7743)
@@ -0,0 +1,110 @@
+{ package Catalyst::Action::SOAP::RPCEndpoint;
+
+ use strict;
+ use base qw/Catalyst::Action::SOAP/;
+ use constant NS_SOAP_ENV => "http://schemas.xmlsoap.org/soap/envelope/";
+ use UNIVERSAL;
+
+ sub execute {
+ my $self = shift;
+ my ( $controller, $c ) = @_;
+
+ $self->prepare_soap_helper($controller,$c);
+ $self->prepare_soap_xml_post($controller,$c);
+ unless ($c->stash->{soap}->fault) {
+ my $envelope = $c->stash->{soap}->parsed_envelope;
+ my $namespace = $c->stash->{soap}->namespace || NS_SOAP_ENV;
+ my ($body) = $envelope->getElementsByTagNameNS($namespace,'Body',0);
+ my @children = grep { UNIVERSAL::isa( $_, 'XML::LibXML::Element') } $body->getChildNodes();
+ if (scalar @children != 1) {
+ $c->stash->{soap}->fault
+ ({ code => 'Client',
+ reason => 'Bad Body', detail =>
+ 'RPC messages should contain only one element inside body'})
+ } else {
+ my $rpc_element = $children[0];
+ my ($smthing, $operation) = split /:/, $rpc_element->nodeName();
+ $operation ||= $smthing; # if there's no ns prefix,
+ # operation is the first
+ # part.
+ $c->stash->{soap}->operation_name($operation);
+
+ eval {
+ if ($controller->wsdlobj) {
+ my $decoder = $controller->decoders->{$operation};
+ my ($args) = $decoder->($rpc_element)
+ if UNIVERSAL::isa($decoder,'CODE');
+ $c->stash->{soap}->arguments($args);
+ } else {
+ my $arguments = $rpc_element->getChildNodes();
+ $c->stash->{soap}->arguments($arguments);
+ }
+ };
+ if ($@) {
+ $c->stash->{soap}->fault
+ ({ code => 'Client',
+ reason => 'Bad Body', detail =>
+ 'Malformed parts on the message body: '.$@});
+ } else {
+ my $action = $controller->action_for($operation);
+
+ if (!$action ||
+ !grep { /RPC(Encoded|Literal)/ } @{$action->attributes->{ActionClass}}) {
+ $c->stash->{soap}->fault
+ ({ code => 'Client',
+ reason => 'Bad Operation', detail =>
+ 'Invalid Operation'});
+ } else {
+ # this is our RPC action
+ $c->forward($operation);
+ }
+ }
+
+ }
+ }
+ }
+};
+
+1;
+
+__END__
+
+=head1 NAME
+
+Catalyst::Action::SOAP::RPCEndpoint - RPC Dispatcher
+
+=head1 SYNOPSIS
+
+ # not used directly.
+
+=head1 DESCRIPTION
+
+This class is used by L<Catalyst::Controller::SOAP> to dispatch to the
+RPC operations inside a controller. These operations are quite
+different from the others, as they are seen by Catalyst as this single
+action. During the registering phase, the soap rpc operations are
+included in the hash that is sent to this object, so they can be
+invoked later.
+
+=head1 TODO
+
+Almost all the SOAP protocol is unsupported, only the method
+dispatching and, optionally, the soap-decoding of the arguments are
+made.
+
+=head1 AUTHORS
+
+Daniel Ruoso <daniel.ruoso at verticalone.pt>
+
+=head1 BUG REPORTS
+
+Please submit all bugs regarding C<Catalyst::Controller::SOAP> to
+C<bug-catalyst-controller-soap at rt.cpan.org>
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
Deleted: Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/RPCLiteral.pm
===================================================================
--- Catalyst-Controller-SOAP/1.0/trunk/lib/Catalyst/Action/SOAP/RPCLiteral.pm 2008-05-06 19:57:10 UTC (rev 7697)
+++ Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/RPCLiteral.pm 2008-05-12 19:38:30 UTC (rev 7743)
@@ -1,51 +0,0 @@
-{ package Catalyst::Action::SOAP::RPCLiteral;
-
- use base qw/Catalyst::Action::SOAP/;
-
- sub execute {
- my $self = shift;
- my ( $controller, $c ) = @_;
- $self->NEXT::execute($controller, $c, $c->stash->{soap}->arguments);
- }
-};
-
-1;
-
-__END__
-
-=head1 NAME
-
-Catalyst::Action::SOAP::RPCLiteral - RPC style Literal encoding service
-
-=head1 SYNOPSIS
-
- # not used directly.
-
-=head1 DESCRIPTION
-
-This class implements the literal encoding dispatch on the service,
-which means that the arguments are passed to the service as a xml
-object in the parameters.
-
-=head1 TODO
-
-Almost all the SOAP protocol is unsupported, only the method
-dispatching and, optionally, the soap-decoding of the arguments are
-made.
-
-=head1 AUTHORS
-
-Daniel Ruoso <daniel.ruoso at verticalone.pt>
-
-=head1 BUG REPORTS
-
-Please submit all bugs regarding C<Catalyst::Controller::SOAP> to
-C<bug-catalyst-controller-soap at rt.cpan.org>
-
-=head1 LICENSE
-
-This library is free software, you can redistribute it and/or modify
-it under the same terms as Perl itself.
-
-=cut
-
Copied: Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/RPCLiteral.pm (from rev 7717, Catalyst-Controller-SOAP/1.0/trunk/lib/Catalyst/Action/SOAP/RPCLiteral.pm)
===================================================================
--- Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/RPCLiteral.pm (rev 0)
+++ Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Action/SOAP/RPCLiteral.pm 2008-05-12 19:38:30 UTC (rev 7743)
@@ -0,0 +1,52 @@
+{ package Catalyst::Action::SOAP::RPCLiteral;
+
+ use base qw/Catalyst::Action::SOAP/;
+ use Class::C3;
+
+ sub execute {
+ my $self = shift;
+ my ( $controller, $c ) = @_;
+ $self->next::method($controller, $c, $c->stash->{soap}->arguments);
+ }
+};
+
+1;
+
+__END__
+
+=head1 NAME
+
+Catalyst::Action::SOAP::RPCLiteral - RPC style Literal encoding service
+
+=head1 SYNOPSIS
+
+ # not used directly.
+
+=head1 DESCRIPTION
+
+This class implements the literal encoding dispatch on the service,
+which means that the arguments are passed to the service as a xml
+object in the parameters.
+
+=head1 TODO
+
+Almost all the SOAP protocol is unsupported, only the method
+dispatching and, optionally, the soap-decoding of the arguments are
+made.
+
+=head1 AUTHORS
+
+Daniel Ruoso <daniel.ruoso at verticalone.pt>
+
+=head1 BUG REPORTS
+
+Please submit all bugs regarding C<Catalyst::Controller::SOAP> to
+C<bug-catalyst-controller-soap at rt.cpan.org>
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
Deleted: Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Controller/SOAP.pm
===================================================================
--- Catalyst-Controller-SOAP/1.0/trunk/lib/Catalyst/Controller/SOAP.pm 2008-05-06 19:57:10 UTC (rev 7697)
+++ Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Controller/SOAP.pm 2008-05-12 19:38:30 UTC (rev 7743)
@@ -1,599 +0,0 @@
-{ package Catalyst::Controller::SOAP;
-
- use strict;
- use base qw/Catalyst::Controller/;
- use XML::LibXML;
- use XML::Compile::WSDL11;
- use UNIVERSAL qw(isa);
-
- use constant NS_SOAP_ENV => "http://schemas.xmlsoap.org/soap/envelope/";
- use constant NS_WSDLSOAP => "http://schemas.xmlsoap.org/wsdl/soap/";
-
- our $VERSION = '0.9';
-
- __PACKAGE__->mk_accessors qw(wsdl wsdlobj decoders encoders
- ports wsdlservice xml_compile soap_action_prefix rpc_endpoint_paths);
-
- # XXX - This is here as a temporary fix for a bug in _parse_attrs
- # that makes it impossible to return more than one
- # "final_attribute", a patch was already submitted and should make
- # into the next release.
- sub _parse_attrs {
- my ( $self, $c, $name, @attrs ) = @_;
-
- my @others = grep { $_ !~ /^WSDLPort/ } @attrs;
- my $final = $self->SUPER::_parse_attrs($c, $name, @others);
-
- my ($attr) = grep { $_ && $_ =~ /^WSDLPort/ } @attrs;
- return $final unless $attr;
-
- if ( my ( $key, $value ) = ( $attr =~ /^(.*?)(?:\(\s*(.+?)\s*\))?$/ ) )
- {
- if ( defined $value ) {
- ( $value =~ s/^'(.*)'$/$1/ ) || ( $value =~ s/^"(.*)"/$1/ );
- }
- my %ret = $self->_parse_WSDLPort_attr($c, $name, $value);
- push( @{ $final->{$_} }, $ret{$_} ) for
- keys %ret;
- }
-
-
- return $final;
- }
-
-
- sub __init_wsdlobj {
- my ($self, $c) = @_;
-
- my $wsdlfile = $self->wsdl;
-
- if ($wsdlfile) {
- if (!$self->wsdlobj) {
- my $schema;
- if (ref $wsdlfile eq 'HASH') {
- $schema = $wsdlfile->{schema};
- $wsdlfile = $wsdlfile->{wsdl};
- }
-
- if (ref $wsdlfile eq 'ARRAY') {
- my $main = shift @{$wsdlfile};
- $c->log->debug("WSDL: adding main WSDL $main");
- $self->wsdlobj(XML::Compile::WSDL11->new($main));
- foreach my $file (@{$wsdlfile}) {
- $c->log->debug("WSDL: adding additional WSDL $file");
- $self->wsdlobj->addWSDL($file);
- }
- } else {
- $c->log->debug("WSDL: adding WSDL $wsdlfile");
- $self->wsdlobj(XML::Compile::WSDL11->new($wsdlfile));
- }
-
- if (ref $schema eq 'ARRAY') {
- foreach my $file (@$schema) {
- $c->log->debug("WSDL: Import schema $file");
- $self->wsdlobj->importDefinitions($file);
- }
- } elsif ($schema) {
- $c->log->debug("WSDL: Import schema $schema");
- $self->wsdlobj->importDefinitions($schema)
- }
- }
- }
-
- return $self->wsdlobj ? 1 : 0;
- }
-
- sub _parse_WSDLPort_attr {
- my ($self, $c, $name, $value) = @_;
-
- die 'Cannot use WSDLPort without WSDL.'
- unless $self->__init_wsdlobj($c);
-
- $self->ports({}) unless $self->ports();
- $self->ports->{$name} = $value;
- my $operation = $self->wsdlobj->operation($name,
- port => $value,
- service => $self->wsdlservice)
- or die 'Every operation should be on the WSDL when using one.';
-
- # TODO: Use more intelligence when selecting the address.
- my ($path) = $operation->endPointAddresses();
-
- $path =~ s#^[^:]+://[^/]+##;
-
- # Finding out the style and input body use for this operation
- my $binding = $self->wsdlobj->find(binding => $operation->port->{binding});
- my $style = $binding->{'{'.NS_WSDLSOAP.'}binding'}[0]->getAttribute('style');
- my ($use) = map { $_->{input}{'{'.NS_WSDLSOAP.'}body'}[0]->getAttribute('use') }
- grep { $_->{name} eq $name } @{ $binding->{operation} || [] };
-
- $style = $style =~ /document/i ? 'Document' : 'RPC';
- $use = $use =~ /literal/i ? 'Literal' : 'Encoded';
- $c->log->debug("WSDLPort: [$name] [$value] [$path] [$style] [$use]");
-
- if ($style eq 'Document') {
- return
- (
- Path => $path,
- $self->_parse_SOAP_attr($c, $name, $style.$use)
- );
- } else {
- $self->rpc_endpoint_paths([]) unless $self->rpc_endpoint_paths;
- $path =~ s/\/$//;
- push @{$self->rpc_endpoint_paths}, $path
- unless grep { $_ eq $path }
- @{$self->rpc_endpoint_paths};
- return $self->_parse_SOAP_attr($c, $name, $style.$use)
- }
- }
-
- # Let's create the rpc_endpoint action.
- sub register_actions {
- my $self = shift;
- my ($c) = @_;
- $self->SUPER::register_actions(@_);
-
- if ($self->rpc_endpoint_paths) {
- my $namespace = $self->action_namespace($c);
- my $action = $self->create_action
- (
- name => '___base_rpc_endpoint',
- code => sub { },
- reverse => ($namespace ? $namespace.'/' : '') . '___base_rpc_endpoint',
- namespace => $namespace,
- class => (ref $self || $self),
- attributes => { ActionClass => [ 'Catalyst::Action::SOAP::RPCEndpoint' ],
- Path => $self->rpc_endpoint_paths }
- );
- $c->dispatcher->register($c, $action);
- }
- }
-
- sub _parse_SOAP_attr {
- my ($self, $c, $name, $value) = @_;
-
- my $wsdlfile = $self->wsdl;
- my $wsdlservice = $self->wsdlservice;
- my $compile_opts = $self->xml_compile || {};
- my $reader_opts = $compile_opts->{reader} || {};
- my $writer_opts = $compile_opts->{writer} || {};
-
- if ($wsdlfile) {
-
- die 'WSDL initialization failed.'
- unless $self->__init_wsdlobj($c);
-
- $self->ports({}) unless $self->ports();
- my $operation = $self->wsdlobj->operation($name,
- port => $self->ports->{$name},
- service => $wsdlservice)
- or die 'Every operation should be on the WSDL when using one.';
- my $portop = $operation->portOperation();
- $c->log->debug("SOAP: @{[$operation->name]} $portop->{input}{message} $portop->{output}{message}");
-
- my $input_parts = $self->wsdlobj->find(message => $portop->{input}{message})
- ->{part};
- for (@{$input_parts}) {
- my $type = $_->{type} ? $_->{type} : $_->{element};
- $c->log->debug("SOAP: @{[$operation->name]} input part $_->{name}, type $type");
- $_->{compiled_reader} = $self->wsdlobj->schemas->compile
- (READER => $type,
- %$reader_opts);
- };
-
- $self->decoders({}) unless $self->decoders();
- $self->decoders->{$name} = sub {
- my $body = shift;
- my @nodes = grep { UNIVERSAL::isa($_, 'XML::LibXML::Element') } $body->childNodes();
- return
- {
- map {
- my $data = $_->{compiled_reader}->(shift @nodes);
- $_->{name} => $data;
- } @{$input_parts}
- }, @nodes;
- };
-
- my $output_parts = $self->wsdlobj->find(message => $portop->{output}{message})
- ->{part};
- for (@{$output_parts}) {
- my $type = $_->{type} ? $_->{type} : $_->{element};
- $c->log->debug("SOAP: @{[$operation->name]} out part $_->{name}, type $type");
- $_->{compiled_writer} = $self->wsdlobj->schemas->compile
- (WRITER => $_->{type} ? $_->{type} : $_->{element},
- elements_qualified => 'ALL',
- %$writer_opts);
- }
-
- $self->encoders({}) unless $self->encoders();
- $self->encoders->{$name} = sub {
- my ($doc, $data) = @_;
- return
- [
- map {
- $_->{compiled_writer}->($doc, $data->{$_->{name}})
- } @{$output_parts}
- ];
- };
- }
-
- my $actionclass = ($value =~ /^\+/ ? $value :
- 'Catalyst::Action::SOAP::'.$value);
- (
- ActionClass => $actionclass,
- )
- }
-
- # this is implemented as to respond a SOAP message according to
- # what has been sent to $c->stash->{soap}
- sub end : Private {
- my ($self, $c) = (shift, shift);
- my $soap = $c->stash->{soap};
-
- return $self->NEXT::end($c, @_) unless $soap;
-
- if (scalar @{$c->error}) {
- $c->stash->{soap}->fault
- ({ code => 'Client',
- reason => 'Unexpected Error', detail =>
- 'Unexpected error in the application: '.(join "\n", @{$c->error} ).'!'});
- $c->error(0);
- }
-
- my $namespace = $soap->namespace || NS_SOAP_ENV;
- my $response = XML::LibXML->createDocument('1.0','UTF8');
-
- my $envelope = $response->createElementNS
- ($namespace,"Envelope");
-
- $response->setDocumentElement($envelope);
-
- # TODO: we don't support header generation in response yet.
-
- my $body = $response->createElementNS
- ($namespace,"Body");
-
- $envelope->appendChild($body);
-
- if ($soap->fault) {
- my $fault = $response->createElementNS
- ($namespace, "Fault");
- $body->appendChild($fault);
-
- my $code = $response->createElementNS
- ($namespace, "faultcode");
- $fault->appendChild($code);
- my $codestr = $soap->fault->{code};
- if (my ($ns, $val) = $codestr =~ m/^\{(.+)\}(.+)$/) {
- my $prefix = $code->lookupNamespacePrefix($ns);
- if ($prefix) {
- $code->appendText($prefix.':'.$val);
- } else {
- $code->appendText($val);
- }
- } else {
- $code->appendText($codestr);
- }
-
- my $faultstring = $response->createElementNS
- ($namespace, "faultstring");
- $fault->appendChild($faultstring);
- $faultstring->appendText($soap->fault->{reason});
-
- if (UNIVERSAL::isa($soap->fault->{detail}, 'XML::LibXML::Node')) {
- my $detail = $response->createElementNS
- ($namespace, "detail");
- $detail->appendChild($soap->fault->{detail});
- $fault->appendChild($detail);
- } elsif ($soap->fault->{detail}) {
- my $detail = $response->createElementNS
- ($namespace, "detail");
- $fault->appendChild($detail);
- # TODO: we don't support the xml:lang attribute yet.
- my $text = $response->createElementNS
- ('http://www.w3.org/2001/XMLSchema','xsd:documentation');
- $detail->appendChild($text);
- $text->appendText($soap->fault->{detail});
- }
- } else {
- if ($soap->string_return) {
- $body->appendText($soap->string_return);
- } elsif (my $lit = $soap->literal_return) {
- if (ref $lit eq 'XML::LibXML::NodeList') {
- for ($lit->get_nodelist) {
- $body->appendChild($_);
- }
- } else {
- $body->appendChild($lit);
- }
- } elsif (my $cmp = $soap->compile_return) {
- die 'Tried to use compile_return without WSDL'
- unless $self->wsdlobj;
-
- my $arr = $self->encoders->{$soap->operation_name}->($response, $cmp);
- $body->appendChild($_) for @$arr;
- }
- }
-
- $c->log->debug("Outgoing XML: ".$envelope->toString());
- $c->res->content_type('text/xml');
- $c->res->body($envelope->toString());
- }
-
-
-};
-
-{ package Catalyst::Controller::SOAP::Helper;
-
- use base qw(Class::Accessor::Fast);
-
- __PACKAGE__->mk_accessors(qw{envelope parsed_envelope arguments fault namespace
- encoded_return literal_return string_return
- compile_return operation_name});
-
-
-};
-
-
-1;
-
-__END__
-
-=head1 NAME
-
-Catalyst::Controller::SOAP - Catalyst SOAP Controller
-
-=head1 SYNOPSIS
-
- package MyApp::Controller::Example;
- use base 'Catalyst::Controller::SOAP';
-
- # When using a WSDL, you can just specify the Port name, and it
- # will infer the style and use. To do that, you just need to use
- # the WSDLPort attribute. This might be required if your service
- # has more than one port. This operation will be made available
- # using the path part of the location attribute of the port
- # definition.
- __PACKAGE__->config->{wsdl} = 'file.wsdl';
- sub servicefoo : WSDLPort('ServicePort') {}
-
- # available in "/example" as operation "ping". The arguments are
- # treated as a literal document and passed to the method as a
- # XML::LibXML object
- # Using XML::Compile here will help you reading the message.
- sub ping : SOAP('RPCLiteral') {
- my ( $self, $c, $xml) = @_;
- my $name = $xml->findValue('some xpath expression');
- }
-
- # avaiable as "/example/world" in document context. The entire body
- # is delivered to the method as a XML::LibXML object.
- # Using XML::Compile here will help you reading the message.
- sub world :Local SOAP('DocumentLiteral') {
- my ($self, $c, $xml) = @_;
- }
-
- # avaiable as "/example/get" in HTTP get context.
- # the get attributes will be available as any other
- # get operation in Catalyst.
- sub get :Local SOAP('HTTPGet') {
- my ($self, $c) = @_;
- }
-
- # this is the endpoint from where the RPC operations will be
- # dispatched. This code won't be executed at all.
- # See Catalyst::Controller::SOAP::RPC.
- sub index :Local SOAP('RPCEndpoint') {}
-
-
-=head1 ABSTACT
-
-Implements SOAP serving support in Catalyst.
-
-=head1 DESCRIPTION
-
-SOAP Controller for Catalyst which we tried to make compatible with
-the way Catalyst works with URLS.It is important to notice that this
-controller declares by default an index operation which will dispatch
-the RPC operations under this class.
-
-=head1 ATTRIBUTES
-
-This class implements the SOAP attribute wich is used to do the
-mapping of that operation to the apropriate action class. The name of
-the class used is formed as Catalyst::Action::SOAP::$value, unless the
-parameter of the attribute starts with a '+', which implies complete
-namespace.
-
-The implementation of SOAP Action classes helps delivering specific
-SOAP scenarios, like HTTP GET, RPC Encoded, RPC Literal or Document
-Literal, or even Document RDF or just about any required combination.
-
-See L<Catalyst::Action::SOAP::DocumentLiteral> for an example.
-
-=head1 ACCESSORS
-
-Once you tagged one of the methods, you'll have an $c->stash->{soap}
-accessor which will return an C<Catalyst::Controller::SOAP::Helper>
-object. It's important to notice that this is achieved by the fact
-that all the SOAP Action classes are subclasses of
-Catalyst::Action::SOAP, which implements most of that.
-
-You can query this object as follows:
-
-=over 4
-
-=item $c->stash->{soap}->envelope()
-
-The original SOAP envelope as string.
-
-=item $c->stash->{soap}->parsed_envelope()
-
-The parsed envelope as an XML::LibXML object.
-
-=item $c->stash->{soap}->arguments()
-
-The arguments of a RPC call.
-
-=item $c->stash->{soap}->fault({code => $code,reason => $reason, detail => $detail])
-
-Allows you to set fault code and message. Optionally, you may define
-the code itself as an arrayref where the first item will be this code
-and the second will be the subcode, which recursively may be another
-arrayref.
-
-=item $c->stash->{soap}->encoded_return(\@data)
-
-This method will prepare the return value to be a soap encoded data.
-
- # TODO: At this moment, only Literals are working...
-
-=item $c->stash->{soap}->literal_return($xml_node)
-
-This method will prepare the return value to be a literal XML
-document, in this case, you can pass just the node that will be the
-root in the return message or a nodelist.
-
-Using XML::Compile will help to elaborate schema based returns.
-
-=item $c->stash->{soap}->string_return($non_xml_text)
-
-In this case, the given text is encoded as CDATA inside the SOAP
-message.
-
-=back
-
-=head1 USING WSDL
-
-If you define the "wsdl" configuration key, Catalyst::Controller::SOAP
-will automatically map your operations into the WSDL operations, in
-which case you will receive the parsed Perl structure as returned by
-XML::Compile according to the type defined in the WSDL message.
-
-You can define additional wsdl files or even additional schema
-files. If $wsdl is an arrayref, the first element is the one passed to
-new, and the others will be the argument to subsequent addWsdl calls.
-If $wsdl is a hashref, the "wsdl" key will be handled like above and
-the "schema" key will be used to importDefinitions. If the content of
-the schema key is an arrayref, it will result in several calls to
-importDefinition.
-
-When using WSDL, you can use the WSDLPort attribute, that not only
-sets the port name but also infer which is the style of the binding,
-the use of the input body and also declares the Path for the operation
-according to the 'location' attribute in the WSDL file. For RPC
-operations, the endpoint action will be created dinamically also in
-the path defined by the WSDL file.
-
-This is the most convenient way of defining a SOAP service, which, in
-the end, will require you to have it as simple as:
-
- package SOAPApp::Controller::WithWSDL;
- use base 'Catalyst::Controller::SOAP';
- __PACKAGE__->config->{wsdl} = 't/hello4.wsdl';
-
- # in this case, the input has two parts, named 'who' and 'greeting'
- # and the output has a single 'greeting' part.
- sub Greet : WSDLPort('Greet') {
- my ( $self, $c, $args ) = @_;
- my $who = $args->{who};
- my $grt = $args->{greeting};
- $c->stash->{soap}->compile_return({ greeting => $grt.' '.$who.'!' });
- }
-
-When using WSDL with more than one port, the use of this attribute is
-mandatory.
-
-When the WSDL describes more than one service, the controller can only
-represent one of them, so you must define the 'wsdlservice' config key
-that will be used to select the service.
-
-Also, when using wsdl, you can define the response using:
-
- $c->stash->{soap}->compile_return($perl_structure)
-
-In this case, the given structure will be transformed by XML::Compile,
-according to what's described in the WSDL file.
-
-If you define "xml_compile" as a configuration key (which is a
-hashref with keys 'reader' and 'writer', which both have a hashref
-as their value), those key / value pairs will be passed as options
-to the XML::Compile::Schema::compile() method.
-
- __PACKAGE__->config->{xml_compile} = {
- reader => {sloppy_integers => 1}, writer => {sloppy_integers => 1},
- };
-
-=head1 USING WSDL AND Catalyst::Test
-
-If you'd like to use the built-in server from Catalyst::Test with your
-WSDL file (which likely defines an <address location="..."> that differs
-from the standard test server) you'll need to use the transport_hook
-option available with $wsdl->compileClient() in your test file.
-
-
- # t/soap_message.t
- use XML::Compile::WSDL11;
- use XML::Compile::Transport::SOAPHTTP;
- use Test::More qw(no_plan);
-
- BEGIN {
- use_ok 'Catalyst::Test', 'MyServer';
- }
-
- sub proxy_to_test_app
- {
- my ($request, $trace) = @_;
- # request() is a function inserted by Catalyst::Test which
- # sends HTTP requests to the just-started test server.
- return request($request);
- }
-
- my $xml = '/path/to/wsdl/file';
- my $message = 'YourMessage';
- my $port_name = 'YourPort';
- my $wsdl = XML::Compile::WSDL11->new($xml);
- my $client = $wsdl->compileClient($message,
- port => $port_name, transport_hook => \&proxy_to_test_app,
- );
- $client->(...);
-
-
-=head1 TODO
-
-No header features are implemented yet.
-
-The SOAP Encoding support is also missing, when that is ready you'll
-be able to do something like the code below:
-
- # available in "/example" as operation "echo"
- # parsing the arguments as soap-encoded.
- sub echo : SOAP('RPCEncoded') {
- my ( $self, $c, @args ) = @_;
- }
-
-=head1 SEE ALSO
-
-L<Catalyst::Action::SOAP>, L<XML::LibXML>, L<XML::Compile>
-L<Catalyst::Action::SOAP::DocumentLiteral>,
-L<Catalyst::Action::SOAP::RPCLiteral>,
-L<Catalyst::Action::SOAP::HTTPGet>, L<XML::Compile::WSDL11>,
-L<XML::Compile::Schema>
-
-=head1 AUTHORS
-
-Daniel Ruoso C<daniel.ruoso at verticalone.pt>
-
-Drew Taylor C<drew at drewtaylor.com>
-
-=head1 BUG REPORTS
-
-Please submit all bugs regarding C<Catalyst::Controller::SOAP> to
-C<bug-catalyst-controller-soap at rt.cpan.org>
-
-=head1 LICENSE
-
-This library is free software, you can redistribute it and/or modify
-it under the same terms as Perl itself.
-
-=cut
Copied: Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Controller/SOAP.pm (from rev 7742, Catalyst-Controller-SOAP/1.0/trunk/lib/Catalyst/Controller/SOAP.pm)
===================================================================
--- Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Controller/SOAP.pm (rev 0)
+++ Catalyst-Controller-SOAP/1.0/tags/1.10/lib/Catalyst/Controller/SOAP.pm 2008-05-12 19:38:30 UTC (rev 7743)
@@ -0,0 +1,636 @@
+{ package Catalyst::Controller::SOAP;
+
+ use strict;
+ use base qw/Catalyst::Controller/;
+ use XML::LibXML;
+ use XML::Compile::WSDL11;
+ use UNIVERSAL qw(isa);
+ use Class::C3;
+
+ use constant NS_SOAP_ENV => "http://schemas.xmlsoap.org/soap/envelope/";
+ use constant NS_WSDLSOAP => "http://schemas.xmlsoap.org/wsdl/soap/";
+
+ our $VERSION = '1.10';
+
+ __PACKAGE__->mk_accessors qw(wsdl wsdlobj decoders encoders
+ ports wsdlservice xml_compile soap_action_prefix rpc_endpoint_paths);
+
+ # XXX - This is here as a temporary fix for a bug in _parse_attrs
+ # that makes it impossible to return more than one
+ # "final_attribute", a patch was already submitted and should make
+ # into the next release.
+ sub _parse_attrs {
+ my ( $self, $c, $name, @attrs ) = @_;
+
+ my %raw_attributes;
+
+ foreach my $attr (@attrs) {
+
+ # Parse out :Foo(bar) into Foo => bar etc (and arrayify)
+
+ if ( my ( $key, $value ) = ( $attr =~ /^(.*?)(?:\(\s*(.+?)\s*\))?$/ ) ) {
+
+ if ( defined $value ) {
+ ( $value =~ s/^'(.*)'$/$1/ ) || ( $value =~ s/^"(.*)"/$1/ );
+ }
+ push( @{ $raw_attributes{$key} }, $value );
+ }
+ }
+
+ my $hash = (ref $self ? $self : $self->config); # hate app-is-class
+
+ if (exists $hash->{actions} || exists $hash->{action}) {
+ my $a = $hash->{actions} || $hash->{action};
+ %raw_attributes = ((exists $a->{'*'} ? %{$a->{'*'}} : ()),
+ %raw_attributes,
+ (exists $a->{$name} ? %{$a->{$name}} : ()));
+ }
+
+ my %final_attributes;
+
+ foreach my $key (keys %raw_attributes) {
+
+ my $raw = $raw_attributes{$key};
+
+ foreach my $value (ref($raw) eq 'ARRAY' ? @$raw : $raw) {
+
+ my $meth = "_parse_${key}_attr";
+ my %new_attributes;
+ if ( $self->can($meth) ) {
+ %new_attributes = $self->$meth( $c, $name, $value );
+ }
+ push( @{ $final_attributes{$_} }, $new_attributes{$_} )
+ for keys %new_attributes;
+ }
+ }
+
+ return \%final_attributes;
+ }
+
+
+ sub __init_wsdlobj {
+ my ($self, $c) = @_;
+
+ my $wsdlfile = $self->wsdl;
+
+ if ($wsdlfile) {
+ if (!$self->wsdlobj) {
+ my $schema;
+ if (ref $wsdlfile eq 'HASH') {
+ $schema = $wsdlfile->{schema};
+ $wsdlfile = $wsdlfile->{wsdl};
+ }
+
+ if (ref $wsdlfile eq 'ARRAY') {
+ my $main = shift @{$wsdlfile};
+ $c->log->debug("WSDL: adding main WSDL $main");
+ $self->wsdlobj(XML::Compile::WSDL11->new($main));
+ foreach my $file (@{$wsdlfile}) {
+ $c->log->debug("WSDL: adding additional WSDL $file");
+ $self->wsdlobj->addWSDL($file);
+ }
+ } else {
+ $c->log->debug("WSDL: adding WSDL $wsdlfile");
+ $self->wsdlobj(XML::Compile::WSDL11->new($wsdlfile));
+ }
+
+ if (ref $schema eq 'ARRAY') {
+ foreach my $file (@$schema) {
+ $c->log->debug("WSDL: Import schema $file");
+ $self->wsdlobj->importDefinitions($file);
+ }
+ } elsif ($schema) {
+ $c->log->debug("WSDL: Import schema $schema");
+ $self->wsdlobj->importDefinitions($schema)
+ }
+ }
+ }
+
+ return $self->wsdlobj ? 1 : 0;
+ }
+
+ sub _parse_WSDLPort_attr {
+ my ($self, $c, $name, $value) = @_;
+
+ die 'Cannot use WSDLPort without WSDL.'
+ unless $self->__init_wsdlobj($c);
+
+ $self->ports({}) unless $self->ports();
+ $self->ports->{$name} = $value;
+ my $operation = $self->wsdlobj->operation($name,
+ port => $value,
+ service => $self->wsdlservice)
+ or die 'Every operation should be on the WSDL when using one.';
+
+ # TODO: Use more intelligence when selecting the address.
+ my ($path) = $operation->endPointAddresses();
+
+ $path =~ s#^[^:]+://[^/]+##;
+
+ # Finding out the style and input body use for this operation
+ my $binding = $self->wsdlobj->find(binding => $operation->port->{binding});
+ my $style = $binding->{'{'.NS_WSDLSOAP.'}binding'}[0]->getAttribute('style');
+ my ($use) = map { $_->{input}{'{'.NS_WSDLSOAP.'}body'}[0]->getAttribute('use') }
+ grep { $_->{name} eq $name } @{ $binding->{operation} || [] };
+
+ $style = $style =~ /document/i ? 'Document' : 'RPC';
+ $use = $use =~ /literal/i ? 'Literal' : 'Encoded';
+ $c->log->debug("WSDLPort: [$name] [$value] [$path] [$style] [$use]");
+
+ if ($style eq 'Document') {
+ return
+ (
+ Path => $path,
+ $self->_parse_SOAP_attr($c, $name, $style.$use)
+ );
+ } else {
+ $self->rpc_endpoint_paths([]) unless $self->rpc_endpoint_paths;
+ $path =~ s/\/$//;
+ push @{$self->rpc_endpoint_paths}, $path
+ unless grep { $_ eq $path }
+ @{$self->rpc_endpoint_paths};
+ return
+ (
+ $self->_parse_SOAP_attr($c, $name, $style.$use),
+ );
+ }
+ }
+
+ # Let's create the rpc_endpoint action.
+ sub register_actions {
+ my $self = shift;
+ my ($c) = @_;
+ $self->SUPER::register_actions(@_);
+
+ if ($self->rpc_endpoint_paths) {
+ my $namespace = $self->action_namespace($c);
+ my $action = $self->create_action
+ (
+ name => '___base_rpc_endpoint',
+ code => sub { },
+ reverse => ($namespace ? $namespace.'/' : '') . '___base_rpc_endpoint',
+ namespace => $namespace,
+ class => (ref $self || $self),
+ attributes => { ActionClass => [ 'Catalyst::Action::SOAP::RPCEndpoint' ],
+ Path => $self->rpc_endpoint_paths }
+ );
+ $c->dispatcher->register($c, $action);
+ }
+ }
+
+ sub _parse_SOAP_attr {
+ my ($self, $c, $name, $value) = @_;
+
+ my $wsdlfile = $self->wsdl;
+ my $wsdlservice = $self->wsdlservice;
+ my $compile_opts = $self->xml_compile || {};
+ my $reader_opts = $compile_opts->{reader} || {};
+ my $writer_opts = $compile_opts->{writer} || {};
+
+ if ($wsdlfile) {
+
+ die 'WSDL initialization failed.'
+ unless $self->__init_wsdlobj($c);
+
+ $self->ports({}) unless $self->ports();
+ my $operation = $self->wsdlobj->operation($name,
+ port => $self->ports->{$name},
+ service => $wsdlservice)
+ or die 'Every operation should be on the WSDL when using one.';
+ my $portop = $operation->portOperation();
+ $c->log->debug("SOAP: @{[$operation->name]} $portop->{input}{message} $portop->{output}{message}");
+
+ if ($portop->{input}{message}) {
+
+ my $input_parts = $self->wsdlobj->find(message => $portop->{input}{message})
+ ->{part};
+ for (@{$input_parts}) {
+ my $type = $_->{type} ? $_->{type} : $_->{element};
+ $c->log->debug("SOAP: @{[$operation->name]} input part $_->{name}, type $type");
+ $_->{compiled_reader} = $self->wsdlobj->schemas->compile
+ (READER => $type,
+ %$reader_opts);
+ };
+
+ $self->decoders({}) unless $self->decoders();
+ $self->decoders->{$name} = sub {
+ my $body = shift;
+ my @nodes = grep { UNIVERSAL::isa($_, 'XML::LibXML::Element') } $body->childNodes();
+ return
+ {
+ map {
+ my $data = $_->{compiled_reader}->(shift @nodes);
+ $_->{name} => $data;
+ } @{$input_parts}
+ }, @nodes;
+ };
+ }
+
+ if ($portop->{output}{message}) {
+
+ my $output_parts = $self->wsdlobj->find(message => $portop->{output}{message})
+ ->{part};
+ for (@{$output_parts}) {
+ my $type = $_->{type} ? $_->{type} : $_->{element};
+ $c->log->debug("SOAP: @{[$operation->name]} out part $_->{name}, type $type");
+ $_->{compiled_writer} = $self->wsdlobj->schemas->compile
+ (WRITER => $_->{type} ? $_->{type} : $_->{element},
+ elements_qualified => 'ALL',
+ %$writer_opts);
+ }
+
+ $self->encoders({}) unless $self->encoders();
+ $self->encoders->{$name} = sub {
+ my ($doc, $data) = @_;
+ return
+ [
+ map {
+ $_->{compiled_writer}->($doc, $data->{$_->{name}})
+ } @{$output_parts}
+ ];
+ };
+
+ }
+ }
+
+ my $actionclass = ($value =~ /^\+/ ? $value :
+ 'Catalyst::Action::SOAP::'.$value);
+ (
+ ActionClass => $actionclass,
+ )
+ }
+
+ # this is implemented as to respond a SOAP message according to
+ # what has been sent to $c->stash->{soap}
+ sub end : Private {
+ my ($self, $c) = (shift, shift);
+ my $soap = $c->stash->{soap};
+
+ return $self->maybe::next::method($c, @_) unless $soap;
+
+ if (scalar @{$c->error}) {
+ $c->stash->{soap}->fault
+ ({ code => 'Client',
+ reason => 'Unexpected Error', detail =>
+ 'Unexpected error in the application: '.(join "\n", @{$c->error} ).'!'})
+ unless $c->stash->{soap}->fault;
+ $c->error(0);
+ }
+
+ my $namespace = $soap->namespace || NS_SOAP_ENV;
+ my $response = XML::LibXML->createDocument('1.0','UTF8');
+
+ my $envelope = $response->createElementNS
+ ($namespace,"Envelope");
+
+ $response->setDocumentElement($envelope);
+
+ # TODO: we don't support header generation in response yet.
+
+ my $body = $response->createElementNS
+ ($namespace,"Body");
+
+ $envelope->appendChild($body);
+
+ if ($soap->fault) {
+ my $fault = $response->createElementNS
+ ($namespace, "Fault");
+ $body->appendChild($fault);
+
+ my $code = $response->createElementNS
+ ($namespace, "faultcode");
+ $fault->appendChild($code);
+ my $codestr = $soap->fault->{code};
+ if (my ($ns, $val) = $codestr =~ m/^\{(.+)\}(.+)$/) {
+ my $prefix = $code->lookupNamespacePrefix($ns);
+ if ($prefix) {
+ $code->appendText($prefix.':'.$val);
+ } else {
+ $code->appendText($val);
+ }
+ } else {
+ $code->appendText($codestr);
+ }
+
+ my $faultstring = $response->createElementNS
+ ($namespace, "faultstring");
+ $fault->appendChild($faultstring);
+ $faultstring->appendText($soap->fault->{reason});
+
+ if (UNIVERSAL::isa($soap->fault->{detail}, 'XML::LibXML::Node')) {
+ my $detail = $response->createElementNS
+ ($namespace, "detail");
+ $detail->appendChild($soap->fault->{detail});
+ $fault->appendChild($detail);
+ } elsif ($soap->fault->{detail}) {
+ my $detail = $response->createElementNS
+ ($namespace, "detail");
+ $fault->appendChild($detail);
+ # TODO: we don't support the xml:lang attribute yet.
+ my $text = $response->createElementNS
+ ('http://www.w3.org/2001/XMLSchema','xsd:documentation');
+ $detail->appendChild($text);
+ $text->appendText($soap->fault->{detail});
+ }
+ } else {
+ if ($soap->string_return) {
+ $body->appendText($soap->string_return);
+ } elsif (my $lit = $soap->literal_return) {
+ if (ref $lit eq 'XML::LibXML::NodeList') {
+ for ($lit->get_nodelist) {
+ $body->appendChild($_);
+ }
+ } else {
+ $body->appendChild($lit);
+ }
+ } elsif (my $cmp = $soap->compile_return) {
+ die 'Tried to use compile_return without WSDL'
+ unless $self->wsdlobj;
+
+ my $arr = $self->encoders->{$soap->operation_name}->($response, $cmp);
+ $body->appendChild($_) for @$arr;
+ }
+ }
+
+ $c->log->debug("Outgoing XML: ".$envelope->toString());
+ $c->res->content_type('text/xml');
+ $c->res->body($envelope->toString());
+ }
+
+
+};
+
+{ package Catalyst::Controller::SOAP::Helper;
+
+ use base qw(Class::Accessor::Fast);
+
+ __PACKAGE__->mk_accessors(qw{envelope parsed_envelope arguments fault namespace
+ encoded_return literal_return string_return
+ compile_return operation_name});
+
+
+};
+
+
+1;
+
+__END__
+
+=head1 NAME
+
+Catalyst::Controller::SOAP - Catalyst SOAP Controller
+
+=head1 SYNOPSIS
+
+ package MyApp::Controller::Example;
+ use base 'Catalyst::Controller::SOAP';
+
+ # When using a WSDL, you can just specify the Port name, and it
+ # will infer the style and use. To do that, you just need to use
+ # the WSDLPort attribute. This might be required if your service
+ # has more than one port. This operation will be made available
+ # using the path part of the location attribute of the port
+ # definition.
+ __PACKAGE__->config->{wsdl} = 'file.wsdl';
+ sub servicefoo : WSDLPort('ServicePort') {}
+
+ # available in "/example" as operation "ping". The arguments are
+ # treated as a literal document and passed to the method as a
+ # XML::LibXML object
+ # Using XML::Compile here will help you reading the message.
+ sub ping : SOAP('RPCLiteral') {
+ my ( $self, $c, $xml) = @_;
+ my $name = $xml->findValue('some xpath expression');
+ }
+
+ # avaiable as "/example/world" in document context. The entire body
+ # is delivered to the method as a XML::LibXML object.
+ # Using XML::Compile here will help you reading the message.
+ sub world :Local SOAP('DocumentLiteral') {
+ my ($self, $c, $xml) = @_;
+ }
+
+ # avaiable as "/example/get" in HTTP get context.
+ # the get attributes will be available as any other
+ # get operation in Catalyst.
+ sub get :Local SOAP('HTTPGet') {
+ my ($self, $c) = @_;
+ }
+
+ # this is the endpoint from where the RPC operations will be
+ # dispatched. This code won't be executed at all.
+ # See Catalyst::Controller::SOAP::RPC.
+ sub index :Local SOAP('RPCEndpoint') {}
+
+
+=head1 ABSTACT
+
+Implements SOAP serving support in Catalyst.
+
+=head1 DESCRIPTION
+
+SOAP Controller for Catalyst which we tried to make compatible with
+the way Catalyst works with URLS.It is important to notice that this
+controller declares by default an index operation which will dispatch
+the RPC operations under this class.
+
+=head1 ATTRIBUTES
+
+This class implements the SOAP attribute wich is used to do the
+mapping of that operation to the apropriate action class. The name of
+the class used is formed as Catalyst::Action::SOAP::$value, unless the
+parameter of the attribute starts with a '+', which implies complete
+namespace.
+
+The implementation of SOAP Action classes helps delivering specific
+SOAP scenarios, like HTTP GET, RPC Encoded, RPC Literal or Document
+Literal, or even Document RDF or just about any required combination.
+
+See L<Catalyst::Action::SOAP::DocumentLiteral> for an example.
+
+=head1 ACCESSORS
+
+Once you tagged one of the methods, you'll have an $c->stash->{soap}
+accessor which will return an C<Catalyst::Controller::SOAP::Helper>
+object. It's important to notice that this is achieved by the fact
+that all the SOAP Action classes are subclasses of
+Catalyst::Action::SOAP, which implements most of that.
+
+You can query this object as follows:
+
+=over 4
+
+=item $c->stash->{soap}->envelope()
+
+The original SOAP envelope as string.
+
+=item $c->stash->{soap}->parsed_envelope()
+
+The parsed envelope as an XML::LibXML object.
+
+=item $c->stash->{soap}->arguments()
+
+The arguments of a RPC call.
+
+=item $c->stash->{soap}->fault({code => $code,reason => $reason, detail => $detail])
+
+Allows you to set fault code and message. Optionally, you may define
+the code itself as an arrayref where the first item will be this code
+and the second will be the subcode, which recursively may be another
+arrayref.
+
+=item $c->stash->{soap}->encoded_return(\@data)
+
+This method will prepare the return value to be a soap encoded data.
+
+ # TODO: At this moment, only Literals are working...
+
+=item $c->stash->{soap}->literal_return($xml_node)
+
+This method will prepare the return value to be a literal XML
+document, in this case, you can pass just the node that will be the
+root in the return message or a nodelist.
+
+Using XML::Compile will help to elaborate schema based returns.
+
+=item $c->stash->{soap}->string_return($non_xml_text)
+
+In this case, the given text is encoded as CDATA inside the SOAP
+message.
+
+=back
+
+=head1 USING WSDL
+
+If you define the "wsdl" configuration key, Catalyst::Controller::SOAP
+will automatically map your operations into the WSDL operations, in
+which case you will receive the parsed Perl structure as returned by
+XML::Compile according to the type defined in the WSDL message.
+
+You can define additional wsdl files or even additional schema
+files. If $wsdl is an arrayref, the first element is the one passed to
+new, and the others will be the argument to subsequent addWsdl calls.
+If $wsdl is a hashref, the "wsdl" key will be handled like above and
+the "schema" key will be used to importDefinitions. If the content of
+the schema key is an arrayref, it will result in several calls to
+importDefinition.
+
+When using WSDL, you can use the WSDLPort attribute, that not only
+sets the port name but also infer which is the style of the binding,
+the use of the input body and also declares the Path for the operation
+according to the 'location' attribute in the WSDL file. For RPC
+operations, the endpoint action will be created dinamically also in
+the path defined by the WSDL file.
+
+This is the most convenient way of defining a SOAP service, which, in
+the end, will require you to have it as simple as:
+
+ package SOAPApp::Controller::WithWSDL;
+ use base 'Catalyst::Controller::SOAP';
+ __PACKAGE__->config->{wsdl} = 't/hello4.wsdl';
+
+ # in this case, the input has two parts, named 'who' and 'greeting'
+ # and the output has a single 'greeting' part.
+ sub Greet : WSDLPort('Greet') {
+ my ( $self, $c, $args ) = @_;
+ my $who = $args->{who};
+ my $grt = $args->{greeting};
+ $c->stash->{soap}->compile_return({ greeting => $grt.' '.$who.'!' });
+ }
+
+When using WSDL with more than one port, the use of this attribute is
+mandatory.
+
+When the WSDL describes more than one service, the controller can only
+represent one of them, so you must define the 'wsdlservice' config key
+that will be used to select the service.
+
+Also, when using wsdl, you can define the response using:
+
+ $c->stash->{soap}->compile_return($perl_structure)
+
+In this case, the given structure will be transformed by XML::Compile,
+according to what's described in the WSDL file.
+
+If you define "xml_compile" as a configuration key (which is a
+hashref with keys 'reader' and 'writer', which both have a hashref
+as their value), those key / value pairs will be passed as options
+to the XML::Compile::Schema::compile() method.
+
+ __PACKAGE__->config->{xml_compile} = {
+ reader => {sloppy_integers => 1}, writer => {sloppy_integers => 1},
+ };
+
+=head1 USING WSDL AND Catalyst::Test
+
+If you'd like to use the built-in server from Catalyst::Test with your
+WSDL file (which likely defines an <address location="..."> that differs
+from the standard test server) you'll need to use the transport_hook
+option available with $wsdl->compileClient() in your test file.
+
+
+ # t/soap_message.t
+ use XML::Compile::WSDL11;
+ use XML::Compile::Transport::SOAPHTTP;
+ use Test::More qw(no_plan);
+
+ BEGIN {
+ use_ok 'Catalyst::Test', 'MyServer';
+ }
+
+ sub proxy_to_test_app
+ {
+ my ($request, $trace) = @_;
+ # request() is a function inserted by Catalyst::Test which
+ # sends HTTP requests to the just-started test server.
+ return request($request);
+ }
+
+ my $xml = '/path/to/wsdl/file';
+ my $message = 'YourMessage';
+ my $port_name = 'YourPort';
+ my $wsdl = XML::Compile::WSDL11->new($xml);
+ my $client = $wsdl->compileClient($message,
+ port => $port_name, transport_hook => \&proxy_to_test_app,
+ );
+ $client->(...);
+
+
+=head1 TODO
+
+No header features are implemented yet.
+
+The SOAP Encoding support is also missing, when that is ready you'll
+be able to do something like the code below:
+
+ # available in "/example" as operation "echo"
+ # parsing the arguments as soap-encoded.
+ sub echo : SOAP('RPCEncoded') {
+ my ( $self, $c, @args ) = @_;
+ }
+
+=head1 SEE ALSO
+
+L<Catalyst::Action::SOAP>, L<XML::LibXML>, L<XML::Compile>
+L<Catalyst::Action::SOAP::DocumentLiteral>,
+L<Catalyst::Action::SOAP::RPCLiteral>,
+L<Catalyst::Action::SOAP::HTTPGet>, L<XML::Compile::WSDL11>,
+L<XML::Compile::Schema>
+
+=head1 AUTHORS
+
+Daniel Ruoso C<daniel.ruoso at verticalone.pt>
+
+Drew Taylor C<drew at drewtaylor.com>
+
+=head1 BUG REPORTS
+
+Please submit all bugs regarding C<Catalyst::Controller::SOAP> to
+C<bug-catalyst-controller-soap at rt.cpan.org>
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
More information about the Catalyst-commits
mailing list