Return-Path: Mailing-List: contact tomcat-dev-help@jakarta.apache.org; run by ezmlm Delivered-To: mailing list tomcat-dev@jakarta.apache.org Received: (qmail 31488 invoked by uid 500); 12 May 2000 21:59:35 -0000 Delivered-To: apmail-jakarta-tomcat-cvs@apache.org Received: (qmail 31485 invoked by uid 1059); 12 May 2000 21:59:35 -0000 Date: 12 May 2000 21:59:35 -0000 Message-ID: <20000512215935.31484.qmail@locus.apache.org> From: craigmcc@locus.apache.org To: jakarta-tomcat-cvs@apache.org Subject: cvs commit: jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/session StandardManager.java craigmcc 00/05/12 14:59:34 Modified: proposals/catalina/src/conf server.xml proposals/catalina/src/share/org/apache/tomcat Context.java Loader.java proposals/catalina/src/share/org/apache/tomcat/core LocalStrings.properties StandardContext.java proposals/catalina/src/share/org/apache/tomcat/loader FileClassLoader.java LocalStrings.properties StandardLoader.java proposals/catalina/src/share/org/apache/tomcat/session StandardManager.java Log: Initial implementation of automatic reloading for Catalina. Compared to Tomcat 3.x, the following differences will be observed: - Checking for modified classes (to trigger an automatic reload) is done by a background thread at a user-specified interval (default is 15 seconds), rather than on the request processing path. - All servlets, and subordinate classes referenced by those servlets, are loaded by a single class loader. (Classes that are initially referenced from a JSP page appear to be loaded by a different class loader -- more research is necessary in this area). - When the need for reloading is detected, the entire web application is shut down and restarted (including serialization of session data if your session manager is configured to do so). This is important to avoid potential class cast problems (class a.b.c.Foo loaded by one class loader is *not* the same as class a.b.c.Foo loaded by a different class loader, even if they both came from the same class file on disk). - During a reload, request processing for this application is gracefully paused, so there should be no race conditions caused by requests being executed in the middle of a reload. - The default class loader follows a different policy than the AdaptiveClassLoader/AdaptiveServletLoader set used by Tomcat. The new class loader looks *first* in the WEB-INF/classes and WEB-INF/lib/*.jar repositories, and *second* to the system class path, while the current Tomcat code does the opposite. IMHO the new approach is better because it allows webapps to override JAR files etc. that are listed on the system classpath with different versions specific to that application. - The Context interface exposes a "reload()" method that can be used by an administrative application to reload the app unconditionally. The following similarities to the current Tomcat implementation exist: - Automatic reloading is configured on a per-context basis with an attribute in the "conf/server.xml" file (in the case of Catalina, it is just "reloadable" instead of "isReloadable"). - Automatic reloading only checks for updated classes that came from WEB-INF/classes or WEB-INF/lib/*.jar -- updates to classes loaded from the system class path are not checked. - Automatic reloading is very useful during development, but is not recommended for production applications. Therefore, reloading is *not* enabled by default. At present, at least the following issues exist: - Java classes (such as beans) that are initially loaded from a JSP page do not trigger reloading. Apparently, the JSP servlet uses its own class loader for the pages. Further investigation is needed here. - Although servlets and sessions are unloaded and reloaded when a reload occurs, servlet context attributes are not. Class cast problems can still exist if these objects were created by a servlet (which is quite typical) using classes loaded from WEB-INF/classes or WEB-INF/lib/*.jar. Revision Changes Path 1.23 +2 -1 jakarta-tomcat/proposals/catalina/src/conf/server.xml Index: server.xml =================================================================== RCS file: /home/cvs/jakarta-tomcat/proposals/catalina/src/conf/server.xml,v retrieving revision 1.22 retrieving revision 1.23 diff -u -r1.22 -r1.23 --- server.xml 2000/05/12 00:47:23 1.22 +++ server.xml 2000/05/12 21:59:31 1.23 @@ -69,7 +69,8 @@ - + * * @author Craig R. McClanahan - * @version $Revision: 1.9 $ $Date: 2000/05/03 22:58:22 $ + * @version $Revision: 1.10 $ $Date: 2000/05/12 21:59:32 $ */ public interface Context extends Container { @@ -158,6 +158,20 @@ /** + * Return the reloadable flag for this web application. + */ + public boolean getReloadable(); + + + /** + * Set the reloadable flag for this web application. + * + * @param reloadable The new reloadable flag + */ + public void setReloadable(boolean reloadable); + + + /** * Return the servlet context for which this Context is a facade. */ public ServletContext getServletContext(); @@ -527,6 +541,15 @@ * @param update Update request to reflect this mapping? */ public Wrapper map(Request request, boolean update); + + + /** + * Reload this web application, if reloading is supported. + * + * @exception IllegalStateException if the reloadable + * property is set to false. + */ + public void reload(); /** 1.4 +20 -13 jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/Loader.java Index: Loader.java =================================================================== RCS file: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/Loader.java,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- Loader.java 2000/05/12 00:47:23 1.3 +++ Loader.java 2000/05/12 21:59:32 1.4 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/Loader.java,v 1.3 2000/05/12 00:47:23 craigmcc Exp $ - * $Revision: 1.3 $ - * $Date: 2000/05/12 00:47:23 $ + * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/Loader.java,v 1.4 2000/05/12 21:59:32 craigmcc Exp $ + * $Revision: 1.4 $ + * $Date: 2000/05/12 21:59:32 $ * * ==================================================================== * @@ -76,7 +76,7 @@ * repository. * * @author Craig R. McClanahan - * @version $Revision: 1.3 $ $Date: 2000/05/12 00:47:23 $ + * @version $Revision: 1.4 $ $Date: 2000/05/12 21:59:32 $ */ public interface Loader { @@ -92,13 +92,13 @@ /** - * Return the Container with which this Logger has been associated. + * Return the Container with which this Loader has been associated. */ public Container getContainer(); /** - * Set the Container with which this Logger has been associated. + * Set the Container with which this Loader has been associated. * * @param container The associated Container */ @@ -113,6 +113,20 @@ public String getInfo(); + /** + * Return the reloadable flag for this Loader. + */ + public boolean getReloadable(); + + + /** + * Set the reloadable flag for this Loader. + * + * @param reloadable The new reloadable flag + */ + public void setReloadable(boolean reloadable); + + // --------------------------------------------------------- Public Methods @@ -144,13 +158,6 @@ * such that the loaded classes should be reloaded? */ public boolean modified(); - - - /** - * Cause the underlying class loader (and therefore all of the classes - * loaded by that class loader) to be thrown away, and creates a new one. - */ - public void reload(); /** 1.13 +9 -0 jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/core/LocalStrings.properties Index: LocalStrings.properties =================================================================== RCS file: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/core/LocalStrings.properties,v retrieving revision 1.12 retrieving revision 1.13 diff -u -r1.12 -r1.13 --- LocalStrings.properties 2000/05/12 00:47:24 1.12 +++ LocalStrings.properties 2000/05/12 21:59:32 1.13 @@ -9,8 +9,17 @@ interceptorValve.notStarted=InterceptorValve has not yet been started standardContext.alreadyStarted=Context has already been started standardContext.mappingError=MAPPING configuration error for relative URI {0} +standardContext.notReloadable=Reloading is disabled on this Context standardContext.notStarted=Context has not yet been started standardContext.notWrapper=Child of a Context must be a Wrapper +standardContext.reloadingCompleted=Reloading this Context is completed +standardContext.reloadingStarted=Reloading this Context has started +standardContext.startingLoader=Exception starting Loader +standardContext.startingManager=Exception starting Manager +standardContext.startingWrapper=Exception starting Wrapper for servlet {0} +standardContext.stoppingLoader=Exception stopping Loader +standardContext.stoppingManager=Exception stopping Manager +standardContext.stoppingWrapper=Exception stopping Wrapper for servlet {0} standardEngine.alreadyStarted=Engine has already been started standardEngine.mappingError=MAPPING configuration error for server name {0} standardEngine.notHost=Child of an Engine must be a Host 1.19 +191 -4 jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/core/StandardContext.java Index: StandardContext.java =================================================================== RCS file: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/core/StandardContext.java,v retrieving revision 1.18 retrieving revision 1.19 diff -u -r1.18 -r1.19 --- StandardContext.java 2000/05/12 00:47:24 1.18 +++ StandardContext.java 2000/05/12 21:59:33 1.19 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/core/StandardContext.java,v 1.18 2000/05/12 00:47:24 craigmcc Exp $ - * $Revision: 1.18 $ - * $Date: 2000/05/12 00:47:24 $ + * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/core/StandardContext.java,v 1.19 2000/05/12 21:59:33 craigmcc Exp $ + * $Revision: 1.19 $ + * $Date: 2000/05/12 21:59:33 $ * * ==================================================================== * @@ -77,6 +77,7 @@ import org.apache.tomcat.Container; import org.apache.tomcat.Context; import org.apache.tomcat.HttpRequest; +import org.apache.tomcat.Lifecycle; import org.apache.tomcat.LifecycleException; import org.apache.tomcat.Loader; import org.apache.tomcat.Request; @@ -95,7 +96,7 @@ * requests directed to a particular servlet. * * @author Craig R. McClanahan - * @version $Revision: 1.18 $ $Date: 2000/05/12 00:47:24 $ + * @version $Revision: 1.19 $ $Date: 2000/05/12 21:59:33 $ */ public final class StandardContext @@ -190,6 +191,18 @@ /** + * The request processing pause flag (while reloading occurs) + */ + private boolean paused = false; + + + /** + * The reloadable flag for this web application. + */ + private boolean reloadable = false; + + + /** * The resource references for this web application, keyed by name. */ private Hashtable resources = new Hashtable(); @@ -375,6 +388,32 @@ /** + * Return the reloadable flag for this web application. + */ + public boolean getReloadable() { + + return (this.reloadable); + + } + + + /** + * Set the reloadable flag for this web application. + * + * @param reloadable The new reloadable flag + */ + public void setReloadable(boolean reloadable) { + + boolean oldReloadable = this.reloadable; + this.reloadable = reloadable; + support.firePropertyChange("reloadable", + new Boolean(oldReloadable), + new Boolean(this.reloadable)); + + } + + + /** * Return the servlet context for which this Context is a facade. */ public synchronized ServletContext getServletContext() { @@ -1117,6 +1156,36 @@ /** + * Process the specified Request, and generate the corresponding Response, + * according to the design of this particular Container. + * + * @param request Request to be processed + * @param response Response to be produced + * + * @exception IOException if an input/output error occurred while + * processing + * @exception ServletException if a ServletException was thrown + * while processing this request + */ + public void invoke(Request request, Response response) + throws IOException, ServletException { + + // Wait if we are reloading + while (getPaused()) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + ; + } + } + + // Normal request processing + super.invoke(request, response); + + } + + + /** * Return the Wrapper associated with the servlet that matches the * specified context-relative URI, if any; otherwise return * null. @@ -1250,6 +1319,102 @@ /** + * Reload this web application, if reloading is supported. + *

+ * IMPLEMENTATION NOTE: This method is designed to deal with + * reloads required by changes to classes in the underlying repositories + * of our class loader. It does not handle changes to the web application + * deployment descriptor. If that has occurred, you should stop this + * Context and create (and start) a new Context instance instead. + *

+ * FIXME: What about context attributes that have been created + * by servlets? ClassCastException? + * + * @exception IllegalStateException if the reloadable + * property is set to false. + */ + public void reload() { + + // Make sure reloading is enabled + if (!reloadable) + throw new IllegalStateException + (sm.getString("standardContext.notReloadable")); + log(sm.getString("standardContext.reloadingStarted")); + + // Stop accepting requests temporarily + setPaused(true); + + // Shut down the current version of the relevant components + Container children[] = findChildren(); + + for (int i = 0; i < children.length; i++) { + Wrapper wrapper = (Wrapper) children[i]; + if (wrapper instanceof Lifecycle) { + try { + ((Lifecycle) wrapper).stop(); + } catch (LifecycleException e) { + log(sm.getString("standardContext.stoppingWrapper", + wrapper.getName()), + e); + } + } + } + + if ((manager != null) && (manager instanceof Lifecycle)) { + try { + ((Lifecycle) manager).stop(); + } catch (LifecycleException e) { + log(sm.getString("standardContext.stoppingManager"), e); + } + } + + if ((loader != null) && (loader instanceof Lifecycle)) { + try { + ((Lifecycle) loader).stop(); + } catch (LifecycleException e) { + log(sm.getString("standardContext.stoppingLoader"), e); + } + } + + // Start up the new version of the relevant components + if ((loader != null) && (loader instanceof Lifecycle)) { + try { + ; // FIXME - check for new WEB-INF/lib/*.jar files? + ((Lifecycle) loader).start(); + } catch (LifecycleException e) { + log(sm.getString("standardContext.startingLoader"), e); + } + } + + if ((manager != null) && (manager instanceof Lifecycle)) { + try { + ((Lifecycle) manager).start(); + } catch (LifecycleException e) { + log(sm.getString("standardContext.startingManager"), e); + } + } + + for (int i = 0; i < children.length; i++) { + Wrapper wrapper = (Wrapper) children[i]; + if (wrapper instanceof Lifecycle) { + try { + ((Lifecycle) wrapper).start(); + } catch (LifecycleException e) { + log(sm.getString("standardContext.startingWrapper", + wrapper.getName()), + e); + } + } + } + + // Start accepting requests again + setPaused(false); + log(sm.getString("standardContext.reloadingCompleted")); + + } + + + /** * Remove the specified security constraint from this web application. * * @param constraint Constraint to be removed @@ -1478,6 +1643,28 @@ // -------------------------------------------------------- Private Methods + + + /** + * Return the request processing paused flag for this Context. + */ + private boolean getPaused() { + + return (this.paused); + + } + + + /** + * Set the request processing paused flag for this Context. + * + * @param paused The new request processing paused flag + */ + private void setPaused(boolean paused) { + + this.paused = paused; + + } /** 1.3 +20 -27 jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader/FileClassLoader.java Index: FileClassLoader.java =================================================================== RCS file: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader/FileClassLoader.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- FileClassLoader.java 2000/05/12 00:47:25 1.2 +++ FileClassLoader.java 2000/05/12 21:59:33 1.3 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader/FileClassLoader.java,v 1.2 2000/05/12 00:47:25 craigmcc Exp $ - * $Revision: 1.2 $ - * $Date: 2000/05/12 00:47:25 $ + * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader/FileClassLoader.java,v 1.3 2000/05/12 21:59:33 craigmcc Exp $ + * $Revision: 1.3 $ + * $Date: 2000/05/12 21:59:33 $ * * ==================================================================== * @@ -94,7 +94,7 @@ * modified at runtime. * * @author Craig R. McClanahan - * @version $Revision: 1.2 $ $Date: 2000/05/12 00:47:25 $ + * @version $Revision: 1.3 $ $Date: 2000/05/12 21:59:33 $ */ public final class FileClassLoader @@ -318,8 +318,8 @@ */ public boolean modified() { - if (debug >= 2) - log("modified()"); + // if (debug >= 2) + // log("modified()"); // Build a list of the classes we currently have cached Vector temp = new Vector(); @@ -338,36 +338,29 @@ continue; if (entry.origin == null) // System classes cannot be modified continue; + /* + if (debug >= 2) + log("Check classname=" + classname + + ", entry=" + + (new java.sql.Timestamp(entry.lastModified)).toString() + + ", origin=" + + (new java.sql.Timestamp(entry.origin.lastModified())).toString()); + */ if (entry.lastModified != entry.origin.lastModified()) { - if (debug >= 2) - log(" Class " + classname + " was modified"); + // if (debug >= 2) + // log(" Class " + classname + " was modified"); return (true); } } - if (debug >= 2) - log(" No classes were modified"); + // if (debug >= 2) + // log(" No classes were modified"); return (false); } /** - * Create a new ClassLoader instance based on our current repositories. - */ - public Reloader reload() { - - if (debug >= 1) - log("reload()"); - - FileClassLoader loader = new FileClassLoader(findRepositories()); - loader.setDebug(getDebug()); - return (loader); - - } - - - /** * Remove the specified repository from the set of places this ClassLoader * can look for classes to be loaded. Any classes already loaded from this * repository will remain, but no future loads from here will take place. @@ -474,8 +467,8 @@ if (directory != null) { InputStream theStream = loadStreamFromDirectory(directory, name); - if ((theStream != null) && (debug >= 2)) - log(" Returning directory resource stream"); + // if ((theStream != null) && (debug >= 2)) + // log(" Returning directory resource stream"); if (theStream != null) return (theStream); } 1.4 +7 -4 jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader/LocalStrings.properties Index: LocalStrings.properties =================================================================== RCS file: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader/LocalStrings.properties,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- LocalStrings.properties 2000/05/12 00:47:25 1.3 +++ LocalStrings.properties 2000/05/12 21:59:34 1.4 @@ -1,11 +1,14 @@ fileClassLoader.canRead=Repository {0} cannot be read fileClassLoader.exists=Repository {0} does not exist fileClassLoader.jarFile=Cannot read JAR file {0} -standardLoader.alreadyStarted=Loader has already been started standardLoader.addRepository=Adding repository {0} -standardLoader.reload=Reloading this Loader -standardLoader.reloadError=Error reloading this loader +standardLoader.alreadyStarted=Loader has already been started +standardLoader.checkInterval=Cannot set reload check interval to {0} seconds +standardLoader.notContext=Cannot auto-reload unless our Container is a Context +standardLoader.notReloadabe=Reloadable property is set to false +standardLoader.notStarted=Loader has not yet been started +standardLoader.reloadable=Cannot set reloadable property to {0} +standardLoader.reloading=Reloading checks are enabled for this Context standardLoader.removeRepository=Removing repository {0} standardLoader.starting=Starting this Loader -standardLoader.notStarted=Loader has not yet been started standardLoader.stopping=Stopping this Loader 1.5 +315 -30 jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader/StandardLoader.java Index: StandardLoader.java =================================================================== RCS file: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader/StandardLoader.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- StandardLoader.java 2000/05/12 00:47:25 1.4 +++ StandardLoader.java 2000/05/12 21:59:34 1.5 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader/StandardLoader.java,v 1.4 2000/05/12 00:47:25 craigmcc Exp $ - * $Revision: 1.4 $ - * $Date: 2000/05/12 00:47:25 $ + * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader/StandardLoader.java,v 1.5 2000/05/12 21:59:34 craigmcc Exp $ + * $Revision: 1.5 $ + * $Date: 2000/05/12 21:59:34 $ * * ==================================================================== * @@ -65,6 +65,7 @@ package org.apache.tomcat.loader; +import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.File; @@ -72,6 +73,7 @@ import java.util.Enumeration; import java.util.Vector; import org.apache.tomcat.Container; +import org.apache.tomcat.Context; import org.apache.tomcat.Lifecycle; import org.apache.tomcat.LifecycleEvent; import org.apache.tomcat.LifecycleException; @@ -95,17 +97,24 @@ * is not present, the system class loader will be used instead. * * @author Craig R. McClanahan - * @version $Revision: 1.4 $ $Date: 2000/05/12 00:47:25 $ + * @version $Revision: 1.5 $ $Date: 2000/05/12 21:59:34 $ */ public final class StandardLoader - implements Lifecycle, Loader { + implements Lifecycle, Loader, PropertyChangeListener, Runnable { // ----------------------------------------------------- Instance Variables /** + * The number of seconds between checks for modified classes, if + * automatic reloading is enabled. + */ + private int checkInterval = 15; + + + /** * The class loader being managed by this Loader component. */ private Reloader classLoader = null; @@ -146,6 +155,12 @@ /** + * The reloadable flag for this Loader. + */ + private boolean reloadable = false; + + + /** * The set of repositories associated with this class loader. */ private Vector repositories = new Vector(); @@ -170,10 +185,54 @@ protected PropertyChangeSupport support = new PropertyChangeSupport(this); + /** + * The background thread. + */ + private Thread thread = null; + + + /** + * The background thread completion semaphore. + */ + private boolean threadDone = false; + + + /** + * Name to register for the background thread. + */ + private String threadName = "StandardLoader"; + + // ------------------------------------------------------------- Properties /** + * Return the check interval for this Loader. + */ + public int getCheckInterval() { + + return (this.checkInterval); + + } + + + /** + * Set the check interval for this Loader. + * + * @param checkInterval The new check interval + */ + public void setCheckInterval(int checkInterval) { + + int oldCheckInterval = this.checkInterval; + this.checkInterval = checkInterval; + support.firePropertyChange("checkInterval", + new Integer(oldCheckInterval), + new Integer(this.checkInterval)); + + } + + + /** * Return the Java class loader to be used by this Container. */ public ClassLoader getClassLoader() { @@ -200,10 +259,21 @@ */ public void setContainer(Container container) { + // Deregister from the old Container (if any) + if ((this.container != null) && (this.container instanceof Context)) + ((Context) this.container).removePropertyChangeListener(this); + + // Process this property change Container oldContainer = this.container; this.container = container; support.firePropertyChange("container", oldContainer, this.container); + // Register with the new Container (if any) + if ((this.container != null) && (this.container instanceof Context)) { + setReloadable( ((Context) this.container).getReloadable() ); + ((Context) this.container).addPropertyChangeListener(this); + } + } @@ -266,6 +336,41 @@ } + /** + * Return the reloadable flag for this Loader. + */ + public boolean getReloadable() { + + return (this.reloadable); + + } + + + /** + * Set the reloadable flag for this Loader. + * + * @param reloadable The new reloadable flag + */ + public void setReloadable(boolean reloadable) { + + // Process this property change + boolean oldReloadable = this.reloadable; + this.reloadable = reloadable; + support.firePropertyChange("reloadable", + new Boolean(oldReloadable), + new Boolean(this.reloadable)); + + // Start or stop our background thread if required + if (!started) + return; + if (!oldReloadable && this.reloadable) + ; // FIXME - threadStart() + else if (oldReloadable && !this.reloadable) + ; // FIXME - threadStop() + + } + + // --------------------------------------------------------- Public Methods @@ -325,29 +430,6 @@ /** - * Cause the underlying class loader (and therefore all of the classes - * loaded by that class loader) to be thrown away, and creates a new one. - * - * @exception IllegalStateException if a change to the repositories for - * this class loader has rendered restart impossible - */ - public void reload() { - - if (debug >= 1) - log(sm.getString("standardLoader.reload")); - - try { - stop(); - start(); - } catch (Throwable t) { - log(sm.getString("standardLoader.reloadError"), t); - throw new IllegalStateException("reload: " + t); - } - - } - - - /** * Remove a property change listener from this component. * * @param listener The listener to remove @@ -445,6 +527,17 @@ throw new LifecycleException("start: ", t); } + // Start our background thread if we are reloadable + if (reloadable) { + log(sm.getString("standardLoader.reloading")); + try { + threadStart(); + } catch (IllegalStateException e) { + throw new LifecycleException(e); + } + } + + } @@ -464,6 +557,10 @@ lifecycle.fireLifecycleEvent(STOP_EVENT, null); started = false; + // Stop our background thread if we are reloadable + if (reloadable) + threadStop(); + // Throw away our current class loader if (classLoader instanceof Lifecycle) ((Lifecycle) classLoader).stop(); @@ -472,6 +569,35 @@ } + // ----------------------------------------- PropertyChangeListener Methods + + + /** + * Process property change events from our associated Context. + * + * @param event The property change event that has occurred + */ + public void propertyChange(PropertyChangeEvent event) { + + // Validate the source of this event + if (!(event.getSource() instanceof Context)) + return; + Context context = (Context) event.getSource(); + + // Process a relevant property change + if (event.getPropertyName().equals("reloadable")) { + try { + setReloadable + ( ((Boolean) event.getNewValue()).booleanValue() ); + } catch (NumberFormatException e) { + log(sm.getString("standardLoader.reloadable", + event.getNewValue().toString())); + } + } + + } + + // ------------------------------------------------------- Private Methods @@ -510,10 +636,10 @@ Logger logger = null; if (container != null) logger = container.getLogger(); - if (logger != null) + if (logger != null) { logger.log("StandardLoader[" + container.getName() + "] " + message, throwable); - else { + } else { String containerName = null; if (container != null) containerName = container.getName(); @@ -522,6 +648,165 @@ System.out.println("" + throwable); throwable.printStackTrace(System.out); } + + } + + + /** + * Notify our Context that a reload is appropriate. + */ + private void notifyContext() { + + ContextNotifier notifier = new ContextNotifier((Context) container); + (new Thread(notifier)).start(); + + } + + + /** + * Sleep for the duration specified by the checkInterval + * property. + */ + private void threadSleep() { + + try { + Thread.sleep(checkInterval * 1000L); + } catch (InterruptedException e) { + ; + } + + } + + + /** + * Start the background thread that will periodically check for + * session timeouts. + * + * @exception IllegalStateException if we should not be starting + * a background thread now + */ + private void threadStart() { + + // Has the background thread already been started? + if (thread != null) + return; + + // Validate our current state + if (!reloadable) + throw new IllegalStateException + (sm.getString("standardLoader.notReloadable")); + if (!(container instanceof Context)) + throw new IllegalStateException + (sm.getString("standardLoader.notContext")); + + // Start the background thread + if (debug >= 1) + log(" Starting background thread"); + threadDone = false; + threadName = "StandardLoader[" + container.getName() + "]"; + thread = new Thread(this, threadName); + thread.setDaemon(true); + thread.start(); + + } + + + /** + * Stop the background thread that is periodically checking for + * modified classes. + */ + private void threadStop() { + + if (thread == null) + return; + + if (debug >= 1) + log(" Stopping background thread"); + threadDone = true; + thread.interrupt(); + try { + thread.join(); + } catch (InterruptedException e) { + ; + } + + thread = null; + + } + + + // ------------------------------------------------------ Background Thread + + + /** + * The background thread that checks for session timeouts and shutdown. + */ + public void run() { + + if (debug >= 1) + log("BACKGROUND THREAD Starting"); + + // Loop until the termination semaphore is set + while (!threadDone) { + + // Wait for our check interval + threadSleep(); + + // Perform our modification check + if (!classLoader.modified()) + continue; + + // Handle a need for reloading + notifyContext(); + break; + + } + + if (debug >= 1) + log("BACKGROUND THREAD Stopping"); + + } + + +} + + +// ------------------------------------------------------------ Private Classes + + +/** + * Private thread class to notify our associated Context that we have + * recognized the need for a reload. + */ + +final class ContextNotifier implements Runnable { + + + /** + * The Context we will notify. + */ + private Context context = null; + + + /** + * Construct a new instance of this class. + * + * @param context The Context to be notified + */ + public ContextNotifier(Context context) { + + super(); + this.context = context; + + } + + + /** + * Perform the requested notification. + */ + public void run() { + + context.reload(); } 1.12 +9 -6 jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/session/StandardManager.java Index: StandardManager.java =================================================================== RCS file: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/session/StandardManager.java,v retrieving revision 1.11 retrieving revision 1.12 diff -u -r1.11 -r1.12 --- StandardManager.java 2000/05/05 22:45:39 1.11 +++ StandardManager.java 2000/05/12 21:59:34 1.12 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/session/StandardManager.java,v 1.11 2000/05/05 22:45:39 craigmcc Exp $ - * $Revision: 1.11 $ - * $Date: 2000/05/05 22:45:39 $ + * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/session/StandardManager.java,v 1.12 2000/05/12 21:59:34 craigmcc Exp $ + * $Revision: 1.12 $ + * $Date: 2000/05/12 21:59:34 $ * * ==================================================================== * @@ -67,7 +67,6 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; -import java.beans.PropertyChangeListener; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; @@ -105,7 +104,7 @@ * stop() methods of this class at the correct times. * * @author Craig R. McClanahan - * @version $Revision: 1.11 $ $Date: 2000/05/05 22:45:39 $ + * @version $Revision: 1.12 $ $Date: 2000/05/12 21:59:34 $ */ public final class StandardManager @@ -231,8 +230,11 @@ super.setContainer(container); // Register with the new Container (if any) - if ((this.container != null) && (this.container instanceof Context)) + if ((this.container != null) && (this.container instanceof Context)) { + setMaxInactiveInterval + ( ((Context) this.container).getSessionTimeout() ); ((Context) this.container).addPropertyChangeListener(this); + } } @@ -741,6 +743,7 @@ return; threadDone = false; + threadName = "StandardManager[" + container.getName() + "]"; thread = new Thread(this, threadName); thread.setDaemon(true); thread.start();