You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by "Craig R. McClanahan" <Cr...@eng.sun.com> on 2000/07/12 00:31:51 UTC

Context Reloading Bug in Tomcat 3.2

BACKGROUND:

Jon Stevens <jo...@latchkey.com> and Vincent Deconinck <vi...@cplus.be> have
been corresponding about an apparent problem with context reloading in Tomcat 3.2.  Jon
asked me to look at the issue, and I confirmed that the problem exists, and I think I
understand the cause.  What's not clear yet is how to fix it.

THE PROBLEM:

When Tomcat detects the need for reloading, it destroys and re-inits the servlet that
discovered this need (in ServletWrapper.handleReload()).  Along the way, the reload()
method of all defined ContextInterceptors is called, which (among other things) causes
the current session data to be serialized before reloading, and deserialized afterwards.
So far, so good.

However, servlets *other* than the one that discovered the need for reloading do not
appear to be re-initialized.  This results in ClassCastException problems when they try
to reference session attributes that were saved and then restored (and are thus now
loaded by a new class loader).  The attached test case from Vincent illustrates this
problem when you follow the script in the included message below.

THE SOLUTION:

It's not clear what the best course towards solving this problem is.  Among the options:

* De-support auto-reload for apps that have
  more than one servlet (just kidding :-).

* Leave auto-reload detection inside the
  ServletWrapper.handleReload, and add a
  ContextInterceptor that listens to the reload()
  event and re-initializes every servlet wrapper
  *except* the one that detected the need.

* Move the auto-reload detection out of
  ServletWrapper and into a RequestInterceptor
  that performs this function (as suggested by
  a comment inside this code).

* Move reloading detection to a background thread
  instead of checking on every request, and then make
  sure you perform the reload "in between" requests
  (this should also reduce the performance impact
  of the current approach, which checks for needing
  a reload on every single request) -- as Catalina does.

Given the fact that we're in beta for 3.2 right now, we probably want to take the
lowest-risk approach to fixing this.  Costin, that's probably your call since you know
this part of the code the best.

Craig



Jon Stevens wrote:

> craig, i'm getting the same errors. his code looks fine and is a very simple
> test. i'm not having any problems with turbine which is doing some pretty
> complicated stuff. i don't know what is going on. any ideas?
>
> -jon
>
> ----------
> From: "Vincent.Deconinck" <vi...@cplus.be>
> Date: Mon, 10 Jul 2000 13:09:30 +0200
> To: Jon Stevens <jo...@latchkey.com>
> Subject: Re: Tomcat and reloading - may I be of any help ?
>
>  First of all, thanks a lot for your reply...
>
> Jon Stevens wrote:
> > > we used to have JServ run them, but I was quite desappointed to see
> > > session data was lost each time I recompiled a class.
> >
> > Uh. that isn't true.
>
>  Correct. It didn't lose all session data - only the instances of
> classes loaded from that zone's classloader, which was the way I used
> sessions.
>
> > > and came across the evil ClassCastExceptions (I'm using the session to
> > > store the currently logged user's profile and it's part of the classes I
> > > develop => must be reloadable).
> >
> > Works fine for me.
>
>  I coded a simple test case where that behaviour occurs and attached it
> to this mail. Unzip it to the classes directory of one of your web-apps
> and try it. It's perfectly reproductible (at least with Tomcat 3.2beta 1
> + Apache 1.3.12 on RedHat 6.2). I also attached a README file with
> directions of use.
>
> > > Is **class** reloading stable in 3.1 and/or 3.2beta ? I mean, was it
> > > tested that each time a single loaded classfile's timestamp changes, it
> > > triggers a classLoader
> > > kill/start cycle ?
> >
> > It is totally stable in both JServ and Tomcat 3.2b1.
>
>  OK. That's what I thought too, so the problem clearly is with session
> reloading.
>
> > The problem is with your configuration or something.
>
>  I'd be really happy if it were so :-)
>
>  Anyway, I think it'll only take you one minute or two to test the the 2
> attached servlets and see if they behave correctly on your platform...
>
>  Thanks again for considering this issue.
>
>   Vince
>
>
> --
>
> Vincent Deconinck.
> CANAL+ Belgique   -  Informatique
> mailto:vincent.deconinck@cplus.be
> (Assuming cookies are allowed on your browser)
>
> Here is a sequence of actions that invariably causes an erroneous behaviour.
>
> Key:
>  *   = action
>  ->  = Result in Browser window.
>  ### = my comments
>
> --------
>
> *   Start Tomcat
>
> *   Point your browser to the first Servlet (Test1)
>
> ->  Welcome.
> ->  This is your first access to this session.
> ->  I just created new random number for you with value : <n>
>
> *   Point your browser to the second Servlet (Test2)
>
> ->  Hi, I recognize you.
> ->  I could recover your random number : <n>
>
> ### Normal behaviour that made me happy at first
>
> *   touch Test2.class
>
> *   Press the browser's Reload button (to reload Test2)
>
> ->  Hi, I recognize you.
> ->  I could recover your random number : <n>
>
> ### Great : the value has been remembered although the class has been
> reloaded, but...
>
> *   Press the browser's Back button (Test1), then 'Reload' to force a
> re-request
> ->  Error: 500
> ->  Location:<path>/Test1
> ->  Internal Servlet Error:
> ->  java.lang.ClassCastException: MyRandom
> ->          at Test1.service(Test1.java, Compiled Code)
> ->          at javax.servlet.http.HttpServlet.service(HttpServlet.java,
> Compiled Code)
> ->          at
> org.apache.tomcat.core.ServletWrapper.doService(ServletWrapper.java,
> Compiled Code)
> ->          at org.apache.tomcat.core.Handler.service(Handler.java, Compiled
> Code)
> ->          at
> org.apache.tomcat.core.ServletWrapper.service(ServletWrapper.java, Compiled
> Code)
> ->          at
> org.apache.tomcat.core.ContextManager.internalService(ContextManager.java,
> Compiled Code)
> ->          at
> org.apache.tomcat.core.ContextManager.service(ContextManager.java, Compiled
> Code)
> ->          at
> org.apache.tomcat.service.connector.Ajp12ConnectionHandler.processConnection
> (Ajp12ConnectionHandler.java, Compiled Code)
> ->          at
> org.apache.tomcat.service.TcpWorkerThread.runIt(PoolTcpEndpoint.java,
> Compiled Code)
> ->          at
> org.apache.tomcat.util.ThreadPool$ControlRunnable.run(ThreadPool.java,
> Compiled Code)
> ->          at java.lang.Thread.run(Thread.java, Compiled Code)
>
> ### Aouch !
>
>   ------------------------------------------------------------------------
>                              Name: ClassCastException.zip
>    ClassCastException.zip    Type: Zip Compressed Data (application/x-zip-compressed)
>                          Encoding: base64

Re: Context Reloading Bug in Tomcat 3.2

Posted by "Craig R. McClanahan" <Cr...@eng.sun.com>.
"Vincent.Deconinck" wrote:

> "Craig R. McClanahan" wrote:
> > THE PROBLEM:
> > However, servlets *other* than the one that discovered the need for reloading do not
> > appear to be re-initialized.
>
>         I confirm that sentence perfectly reflects the situation. Indeed, after
> recompiling any of the loaded servlets, only the first one you'll reload
> in your browser will work (that's the one that 'discovers the need for
> reloading', no matter if it was the just-recompiled one or not). All the
> other ones trying to access saved-and-restored session data will cause a
> ClassCastException.
>         Moreover, there's no 'soft way' to get back to a working state.
> Removing the offending object from the session or even invalidating the
> whole session won't help, because from now on, the different servlets
> are not talking about the same Class anymore, and you'll fall into
> ClassCastExceptions again. The only way out is to stop/start Tomcat.
>

Yep.  :-(

>
> > THE SOLUTION:
>
>         Well, I'm not a Tomcat developer, so I won't go into details here, but
> from a user point of view, I think the JServ behaviour was better,
> though far from perfect. My colleagues and I used to have a single
> server with one zone per developer. This of course is impossible now...
>         It's a pity the great serialize/deserialize trick only works for
> single-servlet apps in practice for the moment... :-(
>
> > Given the fact that we're in beta for 3.2 right now, we probably want to take the
> > lowest-risk approach to fixing this.
>
>         I understand. This is the right behaviour...
>
>         In my understanding of Costin's answer, the code should (? :-)) work in
> Catalina... Is this correct ? Can you then point me to a URL or tell me
> the current status of this project... Is it a codename for Tomcat 3.3 or
> just a very long-term project ?
>

The example that illustrates this problem does in fact work correctly on Catalina (thank
you for a very useful test case!), because Catalina uses the "background thread that
checks for modified classes" approach to triggering a reload.  Once the need is detected,
it shuts down and restarts the entire context (and therefore all the servlets in that
context), in a manner that is "in between" requests, so you don't have the wierd state
problems of "what do I do with my half-completed request?"

Catalina is code that exists, and currently lives in the "proposals/catalina" directory of
the Tomcat source code repository.  It is nearly feature complete (the biggest missing
functionality is the web server connectors to run behind Apache/iPlanet/IIS -- you can
only run Catalina stand alone right now), fully functional for servlets and JSP pages, and
passes all of the Watchdog tests for compliance to the servlet 2.2 and JSP 1.1
specifications (just as Tomcat 3.2 does).  Nightly builds of Catalina are available at:

    http://jakarta.apache.org/builds/catalina/nightly

and adventerous folks are welcome to download it and try it out.  What Catalina has had
very little of (yet) is real world testing.

When it's feature complete, Catalina is going to be proposed as the code base for Tomcat
4.0 (in conjunction with supporting the new features of Servlet 2.3 and JSP 1.2).

>
>         Anymay, as soon as you think it's stable enough to test the session
> reloading behaviour, I'd be glad to do so and give you feedback. Don't
> hesitate to contact me directly then...
>
>                 Vince.
>
> --
>
> Vincent Deconinck.
> CANAL+ Belgique   -  Informatique
> mailto:vincent.deconinck@cplus.be
>

Craig McClanahan



Re: Context Reloading Bug in Tomcat 3.2

Posted by "Vincent.Deconinck" <vi...@cplus.be>.
"Craig R. McClanahan" wrote:
> THE PROBLEM:
> However, servlets *other* than the one that discovered the need for reloading do not
> appear to be re-initialized.

	I confirm that sentence perfectly reflects the situation. Indeed, after
recompiling any of the loaded servlets, only the first one you'll reload
in your browser will work (that's the one that 'discovers the need for
reloading', no matter if it was the just-recompiled one or not). All the
other ones trying to access saved-and-restored session data will cause a
ClassCastException.
	Moreover, there's no 'soft way' to get back to a working state.
Removing the offending object from the session or even invalidating the
whole session won't help, because from now on, the different servlets
are not talking about the same Class anymore, and you'll fall into
ClassCastExceptions again. The only way out is to stop/start Tomcat.

> THE SOLUTION:

	Well, I'm not a Tomcat developer, so I won't go into details here, but
from a user point of view, I think the JServ behaviour was better,
though far from perfect. My colleagues and I used to have a single
server with one zone per developer. This of course is impossible now...
	It's a pity the great serialize/deserialize trick only works for
single-servlet apps in practice for the moment... :-(

> Given the fact that we're in beta for 3.2 right now, we probably want to take the
> lowest-risk approach to fixing this.  

	I understand. This is the right behaviour...

	In my understanding of Costin's answer, the code should (? :-)) work in
Catalina... Is this correct ? Can you then point me to a URL or tell me
the current status of this project... Is it a codename for Tomcat 3.3 or
just a very long-term project ?

	Anymay, as soon as you think it's stable enough to test the session
reloading behaviour, I'd be glad to do so and give you feedback. Don't
hesitate to contact me directly then...


		Vince.

-- 

Vincent Deconinck.
CANAL+ Belgique   -  Informatique
mailto:vincent.deconinck@cplus.be

Re: Context Reloading Bug in Tomcat 3.2

Posted by cm...@yahoo.com.
> * Move the auto-reload detection out of
>   ServletWrapper and into a RequestInterceptor
>   that performs this function (as suggested by
>   a comment inside this code).
> 
> * Move reloading detection to a background thread
>   instead of checking on every request, and then make
>   sure you perform the reload "in between" requests
>   (this should also reduce the performance impact
>   of the current approach, which checks for needing
>   a reload on every single request) -- as Catalina does.

One thing is clear - the auto-reload should go out of
ServletWrapper as a long-term solution. 

Of course, the second solution is the best - assuming 
you can port the code from Catalina to tomcat. I already
have 3 features to port back and I don't seem to advance
too well ( and I have very little time). 

In any case, I don't think that affects in any way 3.2 
release - but of course it's Sam's decision.

 We can  implement the background reload check as a standalone 
interceptor, and then configure tomcat to use the  new interceptor and not
use normal reloading ( i.e. add the interceptor in server.xml, and add
"reload=false" to all context ) - that can be  either the default or the
solution for those needing reload.

Costin