package SOAP::WSDL::Server; use strict; use warnings; use Class::Std::Fast::Storable; use Scalar::Util qw(blessed); use SOAP::WSDL::Factory::Deserializer; use SOAP::WSDL::Factory::Serializer; use version; our $VERSION = qv('2.00.01'); my %dispatch_to_of :ATTR(:name :default<()>); my %action_map_ref_of :ATTR(:name :default<{}>); my %class_resolver_of :ATTR(:name :default<()>); my %deserializer_of :ATTR(:name :default<()>); my %serializer_of :ATTR(:name :default<()>); sub handle { my $self = shift; my $ident = ident $self; # this involves copying the request... my $request = shift; # once # we only support 1.1 now... $deserializer_of{ $ident } ||= SOAP::WSDL::Factory::Deserializer->get_deserializer({ soap_version => '1.1' }); $serializer_of{ $ident } ||= SOAP::WSDL::Factory::Serializer->get_serializer({ soap_version => '1.1' }); # TODO: factor out dispatcher logic into dispatcher factory + dispatcher # classes # $dispatcher_of{ $ident } ||= SOAP::WSDL::Factory::Dispatcher->get_dispatcher({}); # set class resolver if deserializer supports it $deserializer_of{ $ident }->set_class_resolver( $class_resolver_of{ $ident } ) if ( $deserializer_of{ $ident }->can('set_class_resolver') ); # Try deserializing response my ($body, $header) = eval { $deserializer_of{ $ident }->deserialize( $request->content() ); }; if ($@) { die $deserializer_of{ $ident }->generate_fault({ code => 'soap:Server', role => 'urn:localhost', message => "Error deserializing message: $@. \n" }); }; if (blessed($body) && $body->isa('SOAP::WSDL::SOAP::Typelib::Fault11')) { die $body; } # lookup method name by SOAPAction my $soap_action = $request->header('SOAPAction'); $soap_action = '' if not defined $soap_action; $soap_action =~s{ \A(?:"|')(.+)(?:"|') \z }{$1}xms; my $method_name = $action_map_ref_of{ $ident }->{ $soap_action }; # $dispatcher_of{ $ident }->dispatch({ # soap_action => $soap_action, # request_body => $body, # request_header => $header, # }); if (!$dispatch_to_of{ $ident }) { die $deserializer_of{ $ident }->generate_fault({ code => 'soap:Server', role => 'urn:localhost', message => "No handler registered", }); }; if (! defined $request->header('SOAPAction') ) { die $deserializer_of{ $ident }->generate_fault({ code => 'soap:Server', role => 'urn:localhost', message => "Not found: No SOAPAction given", }); }; if (! defined $method_name) { die $deserializer_of{ $ident }->generate_fault({ code => 'soap:Server', role => 'urn:localhost', message => "Not found: No method found for the SOAPAction '$soap_action'", }); }; # find method in handling class/object my $method_ref = $dispatch_to_of{ $ident }->can($method_name); if (!$method_ref) { die $deserializer_of{ $ident }->generate_fault({ code => 'soap:Server', role => 'urn:localhost', message => "Not implemented: The handler does not implement the method $method_name", }); }; my ($response_body, $response_header) = $method_ref->($dispatch_to_of{ $ident }, $body, $header ); return $serializer_of{ $ident }->serialize({ body => $response_body, header => $response_header, }); } 1; =pod =head1 NAME SOAP::WSDL::Server - WSDL based SOAP server base class =head1 SYNOPSIS Don't use directly, use the SOAP::WSDL::Server::* subclasses instead. =head1 DESCRIPTION SOAP::WSDL::Server basically follows the architecture sketched below (though dispatcher classes are not implemented yet) SOAP Request SOAP Response | ^ V | ------------------------------------------ | SOAP::WSDL::Server | | -------------------------------------- | | | Transport Class | | | |--------------------------------------| | | | Deserializer | Serializer | | | |--------------------------------------| | | | Dispatcher | | | -------------------------------------- | ------------------------------------------ | calls ^ v | returns ------------------------------------- | Handler | ------------------------------------- All of the components (Transport class, deserializer, dispatcher and serializer) are implemented as plugins. The architecture is not implemented as planned yet, but the dispatcher is currently part of SOAP::WSDL::Server, which aggregates serializer and deserializer, and is subclassed by transport classes (of which SOAP::WSDL::Server::CGI is the only implemented one yet). The dispatcher is currently based on the SOAPAction header. This does not comply to the WS-I basic profile, which declares the SOAPAction as optional. The final dispatcher will be based on wire signatures (i.e. the classes of the deserialized messages). A hash-based dispatcher could be implemented by examining the top level hash keys. =head1 EXCEPTION HANDLING =head2 Builtin exceptions SOAP::WSDL::Server handles the following errors itself: In case of errors, a SOAP Fault containing an appropriate error message is returned. =over =item * XML parsing errors =item * Configuration errors =back =head2 Throwing exceptions The proper way to throw a exception is just to die - SOAP::WSDL::Server::CGI catches the exception and sends a SOAP Fault back to the client. If you want more control over the SOAP Fault sent to the client, you can die with a SOAP::WSDL::SOAP::Fault11 object - or just let the SOAP::Server's deserializer create one for you: my $soap = MyServer::SomeService->new(); die $soap->get_deserializer()->generate_fault({ code => 'soap:Server', role => 'urn:localhost', message => "The error message to pas back", detail => "Some details on the error", }); You may use any other object as exception, provided it has a serialize() method which returns the object's XML representation. =head1 LICENSE AND COPYRIGHT Copyright 2004-2008 Martin Kutter. This file is part of SOAP-WSDL. You may distribute/modify it under the same terms as perl itself =head1 AUTHOR Martin Kutter Emartin.kutter fen-net.deE =head1 REPOSITORY INFORMATION $Rev: 391 $ $LastChangedBy: kutterma $ $Id: Client.pm 391 2007-11-17 21:56:13Z kutterma $ $HeadURL: https://soap-wsdl.svn.sourceforge.net/svnroot/soap-wsdl/SOAP-WSDL/trunk/lib/SOAP/WSDL/Client.pm $ =cut