servlet forwarding by name WITHOUT modifying web.xml

H

Henry Townsend

I have a fairly complex question directed at the experts out there (but
then what question isn't :).

Briefly, my webapp implements a set of commands such that
http://myserver/myapp/foo corresponds to the "foo" command and will
be passed to a servlet called FooServlet. This naming convention
is strict; thus it's always possible to infer the name of the servlet
from the URL. I want to make this app user-extensible, meaning that any
user can write a BarServlet.java, compile it and install it, and voila -
a new "bar" command exists.

The above isn't hard. The problem is that adding a new servlet means
modifying the deployment descriptor. In normal usage this requires
adding <servlet-name> and <servlet-mapping> elements to web.xml for each
new servlet. This part is hard to document and explain for users, it
doesn't play well with upgrades, and it requires restarting Tomcat for
each web.xml mod. So I've set out to try to find a way around this; I
want a way for people to simply drop a BarServlet.class file into the
right directory and have a "bar" command be available from that moment
without any need to change web.xml.

The first thing I did was write a FrontDoorServlet and configure web.xml
to send all URLs matching /myapp/* to it. This can then parse the URL to
figure out what servlet to forward to. Unfortunately this doesn't help
because the getRequestDispatcher() method only works for servlets which
already have <servlet-name> and <servlet-mapping> in web.xml, so it's no
help. Meanwhile getNamedDispatcher() looks up the servlet by
<servlet-name>. So by using getNamedDispatcher() I can avoid the need
for <servlet-mapping> but not for <servlet-name>.

Next I figured I'd look into the reflection API since it will always be
true that for command "foo" I need to invoke FooServlet.doGet(). And in
fact I was able to get a reflection-based FrontDoorServlet working as
specified and I thought I was out of the woods ... unfortunately when
getServletContext() is called from the reflection-invoked servlet it
always returns null. Apparently the context is lost in the transition
from FrontDoorServlet to FooServlet.

So now I'm out of ideas. Has anyone else solved this problem? Or any
offhand thoughts pointing to a possible solution? Is there perhaps an
API for dynamically "installing" servlet names just as if they were read
from web.xml?

Thanks,
Henry Townsend
 
B

Ben_

Hello,

WebSphere Application Server has the ability to "serve servlet by classname"
(don't know for other Web Containers). Basically, if you activate this
feature in an extension deployment descriptor, a servlet of its own is
mapped to "/servlet*" and it will invoke "foo.bar" class for URLs like
"/servlet/foo.bar", so that any Servlet can be invoked by name (which is
then considered a security hole... :) So, you should be able to implement
something similar.

On the other hand, do you know Struts ? The way it does it is register a
FrontController servlet in web.xml (typically mapped to /*.do). Developers
add Actions configured in a Struts config xml file. So that
"/dosomething.do" will invoke the class specified (which has to extend the
Struts Action base class). To reduce configuration overhead, there are
convenience plug-ins for Eclipse to avoid editing the XML by hand. So you
could also do something similar and offer a page to administer it.

HTH.
 
B

Bryce

Briefly, my webapp implements a set of commands such that
http://myserver/myapp/foo corresponds to the "foo" command and will
be passed to a servlet called FooServlet. This naming convention
is strict; thus it's always possible to infer the name of the servlet
from the URL. I want to make this app user-extensible, meaning that any
user can write a BarServlet.java, compile it and install it, and voila -
a new "bar" command exists.

The above isn't hard. The problem is that adding a new servlet means
modifying the deployment descriptor. In normal usage this requires
adding <servlet-name> and <servlet-mapping> elements to web.xml for each
new servlet. This part is hard to document and explain for users, it
doesn't play well with upgrades, and it requires restarting Tomcat for
each web.xml mod. So I've set out to try to find a way around this; I
want a way for people to simply drop a BarServlet.class file into the
right directory and have a "bar" command be available from that moment
without any need to change web.xml.

The first thing I did was write a FrontDoorServlet and configure web.xml
to send all URLs matching /myapp/* to it. This can then parse the URL to
figure out what servlet to forward to. Unfortunately this doesn't help
because the getRequestDispatcher() method only works for servlets which
already have <servlet-name> and <servlet-mapping> in web.xml, so it's no
help. Meanwhile getNamedDispatcher() looks up the servlet by
<servlet-name>. So by using getNamedDispatcher() I can avoid the need
for <servlet-mapping> but not for <servlet-name>.

Instead of having the user write a servlet, why not create an
interface, one of the methods returns the output, and have the
controller servlet just delegate processing to the specified class,
and return its output?

Something like:

interface ServletProcessor {
void doProcess(PrintWriter pw);
}

user writes class;

public class MyServletProcessor implements ServletProcessor {
void doProcess(PrintWriter pw) {
// process and write output to pw
}
}

So, the URL incoming might look like:
/myapp/MyServletProcessor

your servlet gets the MyServletProcessor part, instantiates, and calls
the doProcess method.
 
H

Henry Townsend

Bryce said:
Instead of having the user write a servlet, why not create an
interface, one of the methods returns the output, and have the
controller servlet just delegate processing to the specified class,
and return its output?

Nice idea. It's complicated somewhat by the fact that servlets tend to
store object references in the context, in the session, etc. so I'd need
to provide an abstraction layer for all those. It would be easier to
just "let a servlet be a servlet", but your idea would likely produce a
more robust application.

HT
 
H

Henry Townsend

Ben_ said:
Hello,

WebSphere Application Server has the ability to "serve servlet by classname"
(don't know for other Web Containers). Basically, if you activate this
feature in an extension deployment descriptor, a servlet of its own is
mapped to "/servlet*" and it will invoke "foo.bar" class for URLs like
"/servlet/foo.bar", so that any Servlet can be invoked by name (which is
then considered a security hole... :) So, you should be able to implement
something similar.

Ah, that would be the "Invoker servlet" in Tomcat. And yes, I guess I
could use it. There's no security problem for me since (a) no JSP's,
only servlets and (b) it's an intranet app which happens to use a web
container as opposed to an internet-facing server. The only downside is
the loss of container-independence since the Invoker is Tomcat specific.
On the other hand, do you know Struts ? The way it does it is register a
FrontController servlet in web.xml (typically mapped to /*.do). Developers
add Actions configured in a Struts config xml file. So that
"/dosomething.do" will invoke the class specified (which has to extend the
Struts Action base class). To reduce configuration overhead, there are
convenience plug-ins for Eclipse to avoid editing the XML by hand. So you
could also do something similar and offer a page to administer it.

Good idea. I'll download Struts and have a look at how they handle it.

Thanks,
HT
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,821
Latest member
AleidaSchi

Latest Threads

Top