You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ja...@almery.com on 1999/11/18 03:03:02 UTC

ContextPath, ServletPath and backwards compatibility

Hey all,

I seem to recall that issues surrounding this topic have recently been
discussed, but I am unaware of any archives for the Jakarta lists, nor
did I save any of the discussions.  Apologies if this is redundant.

Recently, I got Turbine to run on Tomcat.  The main problem (so far)
resulted from the use in Turbine of the
HttpServletRequest.getServletPath().  I lazily placed Turbine inside
the /examples context.  Turbine uses the getServletPath() method to
figure out how it was called, and builds its response from that
information.

As a result, under Tomcat, Turbine kept dropping the "/examples" part
of the URL (since getServletPath() doesn't return the context part in
Tomcat).  Naturally, this caused Turbine to break.

However, according to the spec, Tomcat seems to be behaving correctly.

The "obvious fix" is heinous, and goes something like this:

	    // Allow Turbine to work with both 2.2 and 2.0 Servlet API
	    Class clazz = req.getClass();
	    try {
		java.lang.reflect.Method meth = 
                    clazz.getDeclaredMethod("getContextPath", null);
		data.scriptName = (String)meth.invoke(req, null);
	    } catch (Exception ex) {}
	    data.scriptName += req.getServletPath();

This is very unpleasant and inefficient.

I suppose I would have argued that the getServletPath() method should
have been backwards compatible, behaving like it did in the 2.0 JSDK,
and returning the "full" servlet path, context included.  One could
then trivially obtain the non-context part by getting a substring.  As
it is, this new behaviour seems likely to break many applications, and
force ugly, inefficient hacks to make them compatible with the
different API versions.

I suppose, however, that it may be possible to change Turbine in such
a way that the above hack is not necessary.  I currently don't know
how to do that, though.

Comments?

Jay
-- 
Jay Doane | doane@acm.org

Re: ContextPath, ServletPath and backwards compatibility

Posted by Brett McLaughlin <ma...@hotmail.com>.
>
>  > 2) This (in Turbine) is done once, at the initial servlet call, then
cached.
>  > The whole reason for caching is, quite frankly, to hide the code of
heinous
>  > and time-consuming behaviour, so this isn't too bad either.
>
> I'm not sure what you mean by this.  It appears to me that every page
> request through Turbine will execute this code.  It even says as much
> in the comments:
>
>         // Create a new RunData object. This object caches all
>         // the information that is needed for the execution lifetime
>         // of a single request. A new RunData object is created for
>         // each and every request and is passed to each and every
>         // module. Since each thread has its own RunData object,
>         // it is not necessary to perform syncronization for the
>         // data within this object.
>
> Jay

Uggh... I should have stayed in bed instead of working last night :)  You
are right, and I totally disagree with that being done the way it is.  That
is information that doesn't change, and should be held on to.  I am (being
bad) and cross posting this to the Turbine list.  There isn't a good reason
to reload _all_ of that - there is for some, but I think things like this
should be pulled back a layer, and only changed if needed.... comments?  or
am I asleep at the wheel again?

-Brett

Re: ContextPath, ServletPath and backwards compatibility

Posted by jon * <jo...@clearink.com>.
on 11/18/99 12:51 PM, ja@almery.com <ja...@almery.com> wrote:

> I'm not sure what you mean by this.  It appears to me that every page
> request through Turbine will execute this code.  It even says as much
> in the comments:
> 
> // Create a new RunData object. This object caches all
> // the information that is needed for the execution lifetime
> // of a single request. A new RunData object is created for
> // each and every request and is passed to each and every
> // module. Since each thread has its own RunData object,
> // it is not necessary to perform syncronization for the
> // data within this object.

This is the wrong discussion for this topic. You should move follow ups to
it to the Turbine list.

RunData is not where the stuff that you are talking about gets initialized.
The stuff that you are talking about gets initialized in Turbine.init() and
is only initialized once.

The purpose of RunData is to encapsulate data for a single execution of the
Turbine Servlet. Because servlets are multithreaded, you have to do this.
Passing around a RunData object is a lot easier than passing around only the
values that you need. Essentially, RunData is HTTPServletRequest/Response +
a bunch of other junk.

-jon


Re: ContextPath, ServletPath and backwards compatibility

Posted by ja...@almery.com.
Brett McLaughlin writes:

 > > The "obvious fix" is heinous, and goes something like this:
 > >
 > >     // Allow Turbine to work with both 2.2 and 2.0 Servlet API
 > >     Class clazz = req.getClass();
 > >     try {
 > > java.lang.reflect.Method meth =
 > >                     clazz.getDeclaredMethod("getContextPath", null);
 > > data.scriptName = (String)meth.invoke(req, null);
 > >     } catch (Exception ex) {}
 > >     data.scriptName += req.getServletPath();
 > >
 > > This is very unpleasant and inefficient.
 > 
 > This is not as inefficient as you think.

 > 2) This (in Turbine) is done once, at the initial servlet call, then cached.
 > The whole reason for caching is, quite frankly, to hide the code of heinous
 > and time-consuming behaviour, so this isn't too bad either.

I'm not sure what you mean by this.  It appears to me that every page
request through Turbine will execute this code.  It even says as much
in the comments:

        // Create a new RunData object. This object caches all 
        // the information that is needed for the execution lifetime
        // of a single request. A new RunData object is created for 
        // each and every request and is passed to each and every 
        // module. Since each thread has its own RunData object, 
        // it is not necessary to perform syncronization for the 
        // data within this object.

Jay
-- 
Jay Doane | doane@acm.org

Re: ContextPath, ServletPath and backwards compatibility

Posted by James Duncan Davidson <ja...@eng.sun.com>.
> The problem as I see it is that this isn't "legacy code". It is that a
> method now returns a different value without giving any real hint that it is
> doing that other than the documentation in the spec.

The real problem is that the spec is designed to be as "backwards
compatible" as possible (with deprecations for warnings) -- but not
forwards compatible. That is an application written for 2.1 can't really
run well on 2.0. 

Yes, this gets tricky when you are going general purpose servlets that
need to go both ways, but it's a general design problem. Newer versions
always have more functionality. I don't know how else the api can evolve
without having some of these sorts of issues. :(

-- 
James Davidson                                     duncan@eng.sun.com 
Java + XML / Portable Code + Portable Data                 !try; do()



Re: ContextPath, ServletPath and backwards compatibility

Posted by jon * <jo...@clearink.com>.
on 11/17/99 6:26 PM, Brett McLaughlin <bm...@algx.net> wrote:

> We can change Turbine much easier than changing the spec.  I know that this
> is a bigger issue than that, but I don't think servlets are old enough to
> have lots of "legacy" code laying around that can't suffer a few tweaks for
> the sake of a better API.  Just my thoughts.
> 
> -Brett

The problem as I see it is that this isn't "legacy code". It is that a
method now returns a different value without giving any real hint that it is
doing that other than the documentation in the spec.

;-(

-jon


Re: ContextPath, ServletPath and backwards compatibility

Posted by Brett McLaughlin <bm...@algx.net>.
> Hey all,
>
> I seem to recall that issues surrounding this topic have recently been
> discussed, but I am unaware of any archives for the Jakarta lists, nor
> did I save any of the discussions.  Apologies if this is redundant.
>
> Recently, I got Turbine to run on Tomcat.  The main problem (so far)
> resulted from the use in Turbine of the
> HttpServletRequest.getServletPath().  I lazily placed Turbine inside
> the /examples context.  Turbine uses the getServletPath() method to
> figure out how it was called, and builds its response from that
> information.

This is fixed in Turbine, with the hack in your mail.  Yes it's ugly, but
major changes in a spec often result in ugly hacks for a while, until
everybody gets caught up.

>
> As a result, under Tomcat, Turbine kept dropping the "/examples" part
> of the URL (since getServletPath() doesn't return the context part in
> Tomcat).  Naturally, this caused Turbine to break.
>
> However, according to the spec, Tomcat seems to be behaving correctly.

Yeah, it is actually doing exactly what the spec says it should.

>
> The "obvious fix" is heinous, and goes something like this:
>
>     // Allow Turbine to work with both 2.2 and 2.0 Servlet API
>     Class clazz = req.getClass();
>     try {
> java.lang.reflect.Method meth =
>                     clazz.getDeclaredMethod("getContextPath", null);
> data.scriptName = (String)meth.invoke(req, null);
>     } catch (Exception ex) {}
>     data.scriptName += req.getServletPath();
>
> This is very unpleasant and inefficient.

This is not as inefficient as you think.

1) Because the servlet engine will already have the jsdk classes loaded,
there is no hit for getting a handle to the class.  A class is only loaded
once; so you pay minimally here.
2) This (in Turbine) is done once, at the initial servlet call, then cached.
The whole reason for caching is, quite frankly, to hide the code of heinous
and time-consuming behaviour, so this isn't too bad either.
3) It still beats the pants off EJB :-)  EJB is great, but there's a lot
more than just one reflection call going on in a container!

>
> I suppose I would have argued that the getServletPath() method should
> have been backwards compatible, behaving like it did in the 2.0 JSDK,
> and returning the "full" servlet path, context included.  One could
> then trivially obtain the non-context part by getting a substring.  As
> it is, this new behaviour seems likely to break many applications, and
> force ugly, inefficient hacks to make them compatible with the
> different API versions.

I don't know - with the introduction of context, it seems Sun is moving more
towards a uniform way of handling namespaces - EJB has been using enContexts
for a while now, and this is a good step in soldifying things into a
cohesive unit.  There is also a substantial difference between a servlet
path and the servlet context, as well as what the class loader/servlet
engine needs to discover about a servlet at load time.  I think 2.2 spec is
a mjor change; as great as that is, major changes cause problems for a
while.  That's sort of just the way it goes; unfortunate, but the whole J2EE
bundle changes the playing field quite a bit... we're working on EJBoss and
I work with some guys at Persistence, and there are major changes there
because of spec changes too... that's progress for ya! :-)

>
> I suppose, however, that it may be possible to change Turbine in such
> a way that the above hack is not necessary.  I currently don't know
> how to do that, though.

Well, I will work on a better solution (or at least think about if there is
one) later this week; I'm using Tomcat at work, and seeing as I'm one of the
guilty parties who came up with Turbine :-) I suppose I should fix it
better...

>
> Comments?
>

We can change Turbine much easier than changing the spec.  I know that this
is a bigger issue than that, but I don't think servlets are old enough to
have lots of "legacy" code laying around that can't suffer a few tweaks for
the sake of a better API.  Just my thoughts.

-Brett

Re: ContextPath, ServletPath and backwards compatibility

Posted by James Duncan Davidson <ja...@eng.sun.com>.

> I suppose I would have argued that the getServletPath() method should
> have been backwards compatible, behaving like it did in the 2.0 JSDK,
> and returning the "full" servlet path, context included.

It is fully backwards compatible. In the Servlet 2.0 api, there was no
concept of a context path. The context path / web application is a
recent development that allows the splitting up of the server's
namespace -- something that wasn't defined before.

.duncan

-- 
James Davidson                                     duncan@eng.sun.com 
Java + XML / Portable Code + Portable Data                 !try; do()



Re: ContextPath, ServletPath and backwards compatibility

Posted by "Craig R. McClanahan" <cm...@mytownnet.com>.
jon * wrote:

> on 11/17/99 6:03 PM, ja@almery.com <ja...@almery.com> wrote:
>
> > I suppose I would have argued that the getServletPath() method should
> > have been backwards compatible, behaving like it did in the 2.0 JSDK,
> > and returning the "full" servlet path, context included.  One could
> > then trivially obtain the non-context part by getting a substring.  As
> > it is, this new behaviour seems likely to break many applications, and
> > force ugly, inefficient hacks to make them compatible with the
> > different API versions.
> >
> > I suppose, however, that it may be possible to change Turbine in such
> > a way that the above hack is not necessary.  I currently don't know
> > how to do that, though.
> >
> > Comments?
>
> Hey Jay, thanks for bringing this up. ;-) For now, I think that Brett is
> going to add your ugly hack (well, it is well done) to Turbine so that you
> can use it. But, I think that this list should discuss this issue further.
> This change is really bad for backwards compatibility. The method should
> have been deprecated or something imho so that it would be more easily
> possible to know if it was broken or not.
>
> -jon
>

First thought -- it's kinda late now to respecify the behavior of
getServletPath() in 2.2 even if everyone wanted to, so discussion on this
point is somewhat academic.

Second thought -- looking to the future rather than the past, compatibility
with the other calls that parse components of the request URI
(getContextPath(), getServletPath(), getPathInfo(), and getQueryString()) is
very important.  Renaming it might have been nice, but "getServletPath" nicely
connotes what it really does -- grabs the part of the URI that selected which
servlet to execute.

Third thought -- a large number of servlet applications based on the 2.0 API
are going to need to be rearchitected anyway because they relied on
getServlet() or HttpSessionContext.  Dealing with the getServletPath() issue
is trivial compared to dealing with those impacts.

Fourth though -- I can be somewhat sympathetic to the "backward compatible"
concept, until I remember that the 2.0 API is *ancient* in terms of Internet
time, and is essentially two major revs back.  Even the 2.1 based engines need
to be using the 2.2 interpretation to be compatible (see the JSP 1.0 spec,
Appendix B - "Java Servlet 2.1 clarifications").

Fifth thought -- even without the "hack", Turbine should have worked OK in the
default context (i.e. no context path prefix).  It only fails when you map to
a non-default context path, which is a feature not even contemplated in the
2.0 API.  If there's any incompatibility, it's actually in the opposite
direction :-).

Craig McClanahan



Re: ContextPath, ServletPath and backwards compatibility

Posted by jon * <jo...@clearink.com>.
on 11/17/99 6:03 PM, ja@almery.com <ja...@almery.com> wrote:

> I suppose I would have argued that the getServletPath() method should
> have been backwards compatible, behaving like it did in the 2.0 JSDK,
> and returning the "full" servlet path, context included.  One could
> then trivially obtain the non-context part by getting a substring.  As
> it is, this new behaviour seems likely to break many applications, and
> force ugly, inefficient hacks to make them compatible with the
> different API versions.
> 
> I suppose, however, that it may be possible to change Turbine in such
> a way that the above hack is not necessary.  I currently don't know
> how to do that, though.
> 
> Comments?

Hey Jay, thanks for bringing this up. ;-) For now, I think that Brett is
going to add your ugly hack (well, it is well done) to Turbine so that you
can use it. But, I think that this list should discuss this issue further.
This change is really bad for backwards compatibility. The method should
have been deprecated or something imho so that it would be more easily
possible to know if it was broken or not.

-jon