thanks for your reply, I still feel there is a problem with ContainerServlets from 
arbitrary packages even when placed in $CATALINA_HOME/server/lib, see below...

> From: Craig R. McClanahan [mailto:[EMAIL PROTECTED]]
> I committed some Ant custom tasks yesterday that talk to 
> Manager ... they
> might be useful to you.

I'll check them out, thanks.


> So that one can write servlet-based services that need access 
> to Catalina
> internals.  Existing examples include:
> * Manager servlet (needs to install, remove, and reload webapps)
> * Invoker servlet (needs to create new Wrapper instances on the fly)
> * Administration servlet (will provide web-based user interface for
>   updating pretty much all of the things in server.xml).
> 
> The third thing is in the "admin" webapp in the HEAD branch.
> 

That also sounds really useful, I'm going to have to take a close look at the HEAD 
branch :) Hopefully it's capable of persisting changes back into server.xml...

> In order to execute a ContainerServlet that has access to the 
> internals of
> Tomcat 4, the following rules must all be followed:
> 
> * Your servlet must implement the org.apache.catalina.ContainerServlet
>   interface.
> 
> * Your servlet, and all the code it depends on, must be 
> loaded from the
>   "server" classloader.  That means it needs to be installed 
> as unpacked
>   classes under $CATALINA_HOME/server/classes or in JAR files in
>   $CATALINA_HOME/server/lib.  (Classes in common/classes and 
> common/lib
>   are also visible to these components.)
> 
> No changes to Tomcat code, or to standard server.xml settings, are
> required.
> 

IF a servlet which implements ContainerServlet is loaded by the Catalina class loader, 
everything will work fine, i.e. it will have its 'setWrapper()' command called by 
StandardWrapper.load().  The problem is in the way StandardWrapper decides which 
classloader to use to load the servlet.  It uses its private 
'isContainerServlet(string)' method, which only looks at the String representation of 
the className and checks if it starts with 'org.apache.catalina'.  So, if I have a 
servlet implementing ContainerServlet in some package, say 'com.foo.servlets', and put 
it in a jar $CATALINA_HOME/server/lib, and create some webapp with a web.xml with 
com.foo.servlets.SomeContainerServlet as a 'server-class' for a servlet, things will 
still fail.  The StandardWrapper.isContainerServlet(string classname) method will 
return false, so the StandardWrapper.load() method will try to load the class from the 
webapp classloader, which of course will not find the class since it is not in the 
webapp classpath.  You can put the com.foo.servlets.SomeContainerServlet in the 
common/lib folder and it will load correctly, but things will break because no 
catalina classes will be visible to it (an object instance cannot see other objects 
loaded in _child_ classloaders, only _parent_ classloaders).

The solution to this isn't trivial -- you want all ContainerServlets to be loaded in 
the catalina classloader, but how do you know whether a servlet is a ContainerServlet 
by its classname alone, before actually loading it and doing something like 
'instanceof' or 'isAssignableFrom()'?  I'm sure this was why the 
StandardWrapper.isContainerServlet() method was written but it just doesn't do the 
right thing.  I think the way to a nicer solution is through the 'privileged' 
attribute for a Context in servlet.xml (evaluated in ServletWrapper via the 
isServletAllowed() function).  I suggest that if a Context is designated as 
privileged, the context's wrapper should attempt to load servlets via the catalina 
classloader.  If the class isn't found, catch the exception and attempt to load it via 
the webapp classloader.  If the class is loaded, check if it implements 
ContainerServlet; if so, call setWrapper() on it and continue, if not destroy the 
class object and attempt to load it in the webapp classloader.  This is a bit more 
expensive way of loading classes than what is done now, but if only performed in 
Contexts marked 'privileged', most Context will be unaffected and privileged Contexts 
would actually work :)  I'm fairly sure I'm right about this, I've read the code 
pretty carefully and tested out the above behavior (i.e. I've previously tried doing 
the two things you outline as necessary above and things failed, it was a mystery 
until I delved into StandardWrapper source). I'd be happy to code up these proposed 
changes into StandardWrapper if you're interested.

-alan

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to