remm        01/12/11 10:56:03

  Modified:    catalina/src/share/org/apache/catalina/core
                        StandardWrapper.java
  Log:
  - Since I had lots of trouble getting STM right, I decided to experiment a bit more
    as an exercice. This patch implements instance pooling using a stack. This
    gives STM very good performance (it needs two syncs + two accesses to
    the stack for each request, but is otherwise equivalent to a request on a
    normal servlet).
  
  Revision  Changes    Path
  1.35      +152 -57   
jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/StandardWrapper.java
  
  Index: StandardWrapper.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/StandardWrapper.java,v
  retrieving revision 1.34
  retrieving revision 1.35
  diff -u -r1.34 -r1.35
  --- StandardWrapper.java      2001/12/11 08:28:38     1.34
  +++ StandardWrapper.java      2001/12/11 18:56:03     1.35
  @@ -1,7 +1,7 @@
   /*
  - * $Header: 
/home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/StandardWrapper.java,v
 1.34 2001/12/11 08:28:38 remm Exp $
  - * $Revision: 1.34 $
  - * $Date: 2001/12/11 08:28:38 $
  + * $Header: 
/home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/StandardWrapper.java,v
 1.35 2001/12/11 18:56:03 remm Exp $
  + * $Revision: 1.35 $
  + * $Date: 2001/12/11 18:56:03 $
    *
    * ====================================================================
    *
  @@ -70,6 +70,7 @@
   import java.net.URL;
   import java.util.Enumeration;
   import java.util.HashMap;
  +import java.util.Stack;
   import javax.servlet.Servlet;
   import javax.servlet.ServletConfig;
   import javax.servlet.ServletContext;
  @@ -98,14 +99,10 @@
    * Standard implementation of the <b>Wrapper</b> interface that represents
    * an individual servlet definition.  No child Containers are allowed, and
    * the parent Container must be a Context.
  - * <p>
  - * <b>IMPLEMENTATION NOTE</b>:  This implementation does <i>not</i> support
  - * a pool of instances for servlets that implement SingleThreadModel.  IMHO
  - * developers should not be encouraged to use this technique, so efforts to
  - * make them efficient are counter-productive.
    *
    * @author Craig R. McClanahan
  - * @version $Revision: 1.34 $ $Date: 2001/12/11 08:28:38 $
  + * @author Remy Maucherat
  + * @version $Revision: 1.35 $ $Date: 2001/12/11 18:56:03 $
    */
   
   public final class StandardWrapper
  @@ -131,12 +128,6 @@
   
   
       /**
  -     * Has our SingleThreadModel servlet instance been allocated already?
  -     */
  -    private boolean allocated = false;
  -
  -
  -    /**
        * The date and time at which this servlet will become available (in
        * milliseconds since the epoch), or zero if the servlet is available.
        * If this value equals Long.MAX_VALUE, the unavailability of this
  @@ -236,6 +227,24 @@
       private boolean unloading = false;
   
   
  +    /**
  +     * Maximum number of STM instances.
  +     */
  +    private int maxInstances = 20;
  +
  +
  +    /**
  +     * Number of instances currently loaded for a STM servlet.
  +     */
  +    private int nInstances = 0;
  +
  +
  +    /**
  +     * Stack containing the STM instances.
  +     */
  +    private Stack instancePool = null;
  +
  +
       // ------------------------------------------------------------- Properties
   
   
  @@ -410,6 +419,33 @@
   
   
       /**
  +     * Return maximum number of instances that will be allocated when a single
  +     * thread model servlet is used.
  +     */
  +    public int getMaxInstances() {
  +
  +        return (this.maxInstances);
  +
  +    }
  +
  +
  +    /**
  +     * Set the maximum number of instances that will be allocated when a single
  +     * thread model servlet is used.
  +     *
  +     * @param maxInstnces New value of maxInstances
  +     */
  +    public void setMaxInstances(int maxInstances) {
  +
  +        int oldMaxInstances = this.maxInstances;
  +        this.maxInstances = maxInstances;
  +        support.firePropertyChange("maxInstances", oldMaxInstances, 
  +                                   this.maxInstances);
  +
  +    }
  +
  +
  +    /**
        * Set the parent Container of this Wrapper, but only if it is a Context.
        *
        * @param container Proposed parent Container
  @@ -497,7 +533,7 @@
       public boolean isSingleThreadModel() {
   
           try {
  -            load();
  +            loadServlet();
           } catch (Throwable t) {
               ;
           }
  @@ -592,8 +628,6 @@
        * <code>SingleThreadModel</code>, the Wrapper implementation must ensure
        * that this instance is not allocated again until it is deallocated by a
        * call to <code>deallocate()</code>.
  -     * <p>
  -     * <b>FIXME:  Provide a way to avoid waiting forever.</b>
        *
        * @exception ServletException if the servlet init() method threw
        *  an exception
  @@ -609,42 +643,61 @@
               throw new ServletException
                 (sm.getString("standardWrapper.unloading", getName()));
   
  -        // Load and initialize our instance if necessary
  -        if (instance == null) {
  -            try {
  -                load();
  -            } catch (ServletException e) {
  -                throw e;
  -            } catch (Throwable e) {
  -                throw new ServletException
  -                    (sm.getString("standardWrapper.allocate"), e);
  -            }
  -        }
  -
           // If not SingleThreadedModel, return the same instance every time
           if (!singleThreadModel) {
  -            if (debug >= 2)
  -                log("  Returning non-STM instance");
  -            countAllocated++;
  -            return (instance);
  -        }
   
  -        // Lock and return this instance
  -        synchronized (instance) {
  -            while (allocated) {
  +            // Load and initialize our instance if necessary
  +            if (instance == null) {
  +                synchronized (this) {
  +                    if (instance == null) {
  +                        try {
  +                            instance = loadServlet();
  +                        } catch (ServletException e) {
  +                            throw e;
  +                        } catch (Throwable e) {
  +                            throw new ServletException
  +                                (sm.getString("standardWrapper.allocate"), e);
  +                        }
  +                    }
  +                }
  +            }
  +
  +            if (!singleThreadModel) {
                   if (debug >= 2)
  -                    log("  Waiting for allocated STM instance");
  -                try {
  -                    instance.wait();
  -                } catch (InterruptedException e) {
  -                    ;
  +                    log("  Returning non-STM instance");
  +                countAllocated++;
  +                return (instance);
  +            }
  +
  +        }
  +
  +        synchronized (instancePool) {
  +
  +            while (countAllocated >= nInstances) {
  +                // Allocate a new instance if possible, or else wait
  +                if (nInstances < maxInstances) {
  +                    try {
  +                        instancePool.push(loadServlet());
  +                        nInstances++;
  +                    } catch (ServletException e) {
  +                        throw e;
  +                    } catch (Throwable e) {
  +                        throw new ServletException
  +                            (sm.getString("standardWrapper.allocate"), e);
  +                    }
  +                } else {
  +                    try {
  +                        instancePool.wait();
  +                    } catch (InterruptedException e) {
  +                        ;
  +                    }
                   }
               }
               if (debug >= 2)
                   log("  Returning allocated STM instance");
  -            allocated = true;
               countAllocated++;
  -            return (instance);
  +            return (Servlet) instancePool.pop();
  +
           }
   
       }
  @@ -661,16 +714,17 @@
        */
       public void deallocate(Servlet servlet) throws ServletException {
   
  -        countAllocated--;
  -
           // If not SingleThreadModel, no action is required
  -        if (!singleThreadModel)
  +        if (!singleThreadModel) {
  +            countAllocated--;
               return;
  +        }
   
           // Unlock and free this instance
  -        synchronized (instance) {
  -            allocated = false;
  -            instance.notify();
  +        synchronized (instancePool) {
  +            countAllocated--;
  +            instancePool.push(servlet);
  +            instancePool.notify();
           }
   
       }
  @@ -752,11 +806,22 @@
        * @exception ServletException if some other loading problem occurs
        */
       public synchronized void load() throws ServletException {
  +        instance = loadServlet();
  +    }
   
  -        // Nothing to do if we already have an instance
  -        if (instance != null)
  -            return;
   
  +    /**
  +     * Load and initialize an instance of this servlet, if there is not already
  +     * at least one initialized instance.  This can be used, for example, to
  +     * load servlets that are marked in the deployment descriptor to be loaded
  +     * at server startup time.
  +     */
  +    public synchronized Servlet loadServlet() throws ServletException {
  +
  +        // Nothing to do if we already have an instance or an instance pool
  +        if (!singleThreadModel && (instance != null))
  +            return instance;
  +
           // If this "servlet" is really a JSP file, get the right class.
           // HOLD YOUR NOSE - this is a kludge that avoids having to do special
           // case Catalina-specific code in Jasper - it also requires that the
  @@ -873,9 +938,13 @@
           }
   
           // Register our newly initialized instance
  -        instance = servlet;
  -        singleThreadModel = instance instanceof SingleThreadModel;
  +        singleThreadModel = servlet instanceof SingleThreadModel;
  +        if (singleThreadModel) {
  +            if (instancePool == null)
  +                instancePool = new Stack();
  +        }
           fireContainerEvent("load", this);
  +        return servlet;
   
       }
   
  @@ -977,7 +1046,7 @@
       public synchronized void unload() throws ServletException {
   
           // Nothing to do if we have never loaded the instance
  -        if (instance == null)
  +        if (!singleThreadModel && (instance == null))
               return;
           unloading = true;
   
  @@ -1015,6 +1084,8 @@
               instanceSupport.fireInstanceEvent
                 (InstanceEvent.AFTER_DESTROY_EVENT, instance, t);
               instance = null;
  +            instancePool = null;
  +            nInstances = 0;
               fireContainerEvent("unload", this);
               unloading = false;
               throw new ServletException
  @@ -1027,6 +1098,30 @@
   
           // Deregister the destroyed instance
           instance = null;
  +
  +        if (singleThreadModel && (instancePool != null)) {
  +            try {
  +                Thread.currentThread().setContextClassLoader(classLoader);
  +                while (!instancePool.isEmpty()) {
  +                    ((Servlet) instancePool.pop()).destroy();
  +                }
  +            } catch (Throwable t) {
  +                instancePool = null;
  +                nInstances = 0;
  +                unloading = false;
  +                fireContainerEvent("unload", this);
  +                throw new ServletException
  +                    (sm.getString("standardWrapper.destroyException", 
  +                                  getName()), t);
  +            } finally {
  +                // restore the context ClassLoader
  +                Thread.currentThread().setContextClassLoader
  +                    (oldCtxClassLoader);
  +            }
  +            instancePool = null;
  +            nInstances = 0;
  +        }
  +
           unloading = false;
           fireContainerEvent("unload", this);
   
  
  
  

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

Reply via email to