Re: [soaplite] My own dispatching sub - is it possible?
- On Sat, Mar 05, 2005 at 11:03:47AM -0000, ?????? ????? scratched on the wall:
> My task is to write a SOAP server, that would dispatch requests toI am faced with a similar problem. In addition to not being able to
> some modules using its own "sophisticated" naming scheme. Imagine
> that I have several modules somehow located in directory tree, and
> I've got a mapping of module names to their paths. Due to some
> requirements I cannot place modules in SOAP::Lite's default naming
> scheme (like My::Example), but I can dynamically map URI to module and
> call the needed methods myself. Is there any possibility to make SOAP
> ::Transport::HTTP::CGI (or whatever) call some specified sub as a
> handler for ANY request, giving method URI to it as a parameter and
> taking its result as a respone for SOAP call, while this sub parses
> URI and calls the needed modules itself, forming a return value? I'm
> not afraid of some additional coding :), but I don't want to patch
> original SOAP::Lite distribution, that's why I'm asking you for some
> gentle solution.
use a structured namespace, I need to support an environment where
security is taken very seriously, but where modules to our SOAP
server can be written by people without a lot of knowledge or
understanding of SOAP (in other words, security is my problem, but
I'm not writing most of the code).
In addition, all calls are authenticated using SOAP Headers, and
I wanted to handle that auth/auth centrally, rather than have each
exported function start with the same four lines of code (assuming
the developer remembers to put it there!). I also export both the
auth/auth info and the full request SOM object to the function as
"localized package globals" (only in Perl could there be such a
thing!) so that the average developer can just use the normal
function style, but those that require more specific information
(like parameter type info or names) can access that if required.
In order to do all this, I'm using the "dispatch_to" method to
dispatch into a directory of modules. All of those modules have
exactly one line:
-<dir to dispatch_to>/wsModule/Pkg.pm---------------------------------
package wsModule::Pkg; our( @ISA ) = ( 'Project::WebsvcsBase'); 1;
Substituting the project, project module, and package name as required
(this project is a very big deployment).
The WebsvcsBase class is pre-loaded by mod_perl, so there is no need for
a "use" statement and the security hassles of that under a "dispatch_to".
The WebsvcsBase package has an AUTOLOAD function that catches all unknown
calls, which in this case is all of them. My
Project::WebsvcsBase::AUTOLOAD function extracts the SOAP Headers
and does the auth/auth on that, then converts the call to
"wsModule::Pkg::Function" into something like
"Project::Module::Pkg::Websvcs::Function". After checking to be sure
the function doesn't start with "_" (used to indicate a package
internal helper function that should not be exposed to SOAP), "-> can()"
is called on the package to see if the package/function exists, and
get a CODE ref if it does. Finally, the localized package globals are
set and the call is dispatched to the altered package path as a class
This is pretty complex and carries some baggage (like the need for
the "wsModule/Pkg.pm" files), but it gives me total control over the
altering and dispatch of all calls. If you make sure the package
Project::WebsvcsBase ONLY has the AUTOLOAD function, and you make
sure the $AUTOLOAD var is not undef (indicating an explicit call to
AUTOLOAD( )), then you've got only one spot that everything must go
through in a controlled way.
If all you need is to take a request (with SOAP-Action and URI path)
and convert it to a new module path, it would be much simpler to
write an "on_dispatch( )" hook into SOAP::Server. The SOAP::Server
module still dispatches the call with one of the existing systems
(dispatch_with, dispatch_to), but you can define (and limit) the
conversion from URI to Perl module path. In my case I might be able
to the auth/auth verification there, but I couldn't store the
localized symbols. If you do choose to do that, it might not be
obvious, but errors can result in a "die SOAP::Fault -> new(...)"
call; there is no need (nor ability) to return error codes or the
Talking with other SOAP::Lite developers, this desire to have better
control over the dispatch seems like a fairly common thing. I have
considered writing a patch to the core sources to implement a
"dispatch_thru" system, by which the user could designate a
superclass such as Project::WebsvcsBase. The SOAP::Server module
would then create a "@Package....::ISA = ( $dispatch_thru_superclass )"
entry in the symbol table for each call that came through and
dispatch the call to that class (perhaps even local() to keep things
tidy and neat).
Why didn't I do it? In short, if you think the SOAP::Lite examples
are opaque and difficult to follow, try looking at the core sources.
I'll grant Paul a lot of credit in the fact that it works, but the
source seems to value "compact and clever" over "easy to read and
understand." It is very difficult to follow and understand, and even
more difficult to modify "in the spirit" of the original style (at
least for me). I may still write such a patch, but for now it was
considered easier to auto-generate the stub class files.
Jay A. Kreibich | CommTech, Emrg Net Tech Svcs
jak@... | Campus IT & Edu Svcs
<http://www.uiuc.edu/~jak> | University of Illinois at U/C