Author: markt
Date: Sun Apr 25 12:19:19 2010
New Revision: 937787

URL: http://svn.apache.org/viewvc?rev=937787&view=rev
Log:
Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=48358
Add the ability to limit the number of JSPs loaded at any one time.

Added:
    tomcat/trunk/java/org/apache/jasper/util/Entry.java   (with props)
    tomcat/trunk/java/org/apache/jasper/util/JspQueue.java   (with props)
Modified:
    tomcat/trunk/conf/web.xml
    tomcat/trunk/java/org/apache/jasper/EmbeddedServletOptions.java
    tomcat/trunk/java/org/apache/jasper/JspC.java
    tomcat/trunk/java/org/apache/jasper/Options.java
    tomcat/trunk/java/org/apache/jasper/compiler/JspRuntimeContext.java
    tomcat/trunk/java/org/apache/jasper/resources/LocalStrings.properties
    tomcat/trunk/java/org/apache/jasper/servlet/JspServlet.java
    tomcat/trunk/java/org/apache/jasper/servlet/JspServletWrapper.java
    tomcat/trunk/webapps/docs/changelog.xml
    tomcat/trunk/webapps/docs/jasper-howto.xml

Modified: tomcat/trunk/conf/web.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/conf/web.xml?rev=937787&r1=937786&r2=937787&view=diff
==============================================================================
--- tomcat/trunk/conf/web.xml (original)
+++ tomcat/trunk/conf/web.xml Sun Apr 25 12:19:19 2010
@@ -185,6 +185,14 @@
   <!--                       print statement per input line, to ease        -->
   <!--                       debugging?  [true]                             -->
   <!--                                                                      -->
+  <!--   maxLoadedJsps       The maximum number of JSPs that will be loaded -->
+  <!--                       for a web application. If more than this       -->
+  <!--                       number of JSPs are loaded, the least recently  -->
+  <!--                       used JSPs will be unloaded so that the number  -->
+  <!--                       of JSPs loaded at any one time does not exceed -->
+  <!--                       this limit. A value of zero or less indicates  -->
+  <!--                       no limit. [-1]                                 -->
+  <!--                                                                      -->
   <!--   modificationTestInterval                                           -->
   <!--                       Causes a JSP (and its dependent files) to not  -->
   <!--                       be checked for modification during the         -->

Modified: tomcat/trunk/java/org/apache/jasper/EmbeddedServletOptions.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/EmbeddedServletOptions.java?rev=937787&r1=937786&r2=937787&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/jasper/EmbeddedServletOptions.java (original)
+++ tomcat/trunk/java/org/apache/jasper/EmbeddedServletOptions.java Sun Apr 25 
12:19:19 2010
@@ -186,6 +186,12 @@ public final class EmbeddedServletOption
     private boolean displaySourceFragment = true;
 
     
+    /**
+     * The maxim number of loaded jsps per web-application. If there are more
+     * jsps loaded, they will be unloaded.
+     */
+    private int maxLoadedJsps = -1;
+
     public String getProperty(String name ) {
         return settings.getProperty( name );
     }
@@ -383,6 +389,14 @@ public final class EmbeddedServletOption
     }
 
     /**
+     * Should any jsps be unloaded? If set to a value greater than 0 eviction 
of jsps
+     * is started. Default: -1
+     * */
+    public int getMaxLoadedJsps() {
+        return maxLoadedJsps;
+    }
+
+    /**
      * Create an EmbeddedServletOptions object using data available from
      * ServletConfig and ServletContext. 
      */
@@ -663,6 +677,17 @@ public final class EmbeddedServletOption
             }
         }
         
+        String maxLoadedJsps = config.getInitParameter("maxLoadedJsps");
+        if (maxLoadedJsps != null) {
+            try {
+                this.maxLoadedJsps = Integer.parseInt(maxLoadedJsps);
+            } catch(NumberFormatException ex) {
+                if (log.isWarnEnabled()) {
+                    log.warn(Localizer.getMessage("jsp.warning.maxLoadedJsps", 
""+this.maxLoadedJsps));
+                }
+            }
+        }
+
         // Setup the global Tag Libraries location cache for this
         // web-application.
         tldLocationsCache = new TldLocationsCache(context);

Modified: tomcat/trunk/java/org/apache/jasper/JspC.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/JspC.java?rev=937787&r1=937786&r2=937787&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/jasper/JspC.java (original)
+++ tomcat/trunk/java/org/apache/jasper/JspC.java Sun Apr 25 12:19:19 2010
@@ -444,6 +444,10 @@ public class JspC implements Options {
         return true;
     }
 
+    public int getMaxLoadedJsps() {
+        return -1;
+    }
+
     /**
      * {...@inheritdoc}
      */

Modified: tomcat/trunk/java/org/apache/jasper/Options.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/Options.java?rev=937787&r1=937786&r2=937787&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/jasper/Options.java (original)
+++ tomcat/trunk/java/org/apache/jasper/Options.java Sun Apr 25 12:19:19 2010
@@ -221,4 +221,10 @@ public interface Options {
      */
     public Map<String, TagLibraryInfo> getCache();
     
+    /**
+     * The maxim number of loaded jsps per web-application. If there are more
+     * jsps loaded, they will be unloaded. If unset or less than 0, no jsps
+     * are unloaded.
+     */
+    public int getMaxLoadedJsps();
 }

Modified: tomcat/trunk/java/org/apache/jasper/compiler/JspRuntimeContext.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/compiler/JspRuntimeContext.java?rev=937787&r1=937786&r2=937787&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/jasper/compiler/JspRuntimeContext.java 
(original)
+++ tomcat/trunk/java/org/apache/jasper/compiler/JspRuntimeContext.java Sun Apr 
25 12:19:19 2010
@@ -40,9 +40,11 @@ import org.apache.jasper.Options;
 import org.apache.jasper.runtime.JspFactoryImpl;
 import org.apache.jasper.security.SecurityClassLoad;
 import org.apache.jasper.servlet.JspServletWrapper;
+import org.apache.jasper.util.JspQueue;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 
+
 /**
  * Class for tracking JSP compile time file dependencies when the
  * &060;%...@include file="..."%&062; directive is used.
@@ -171,7 +173,11 @@ public final class JspRuntimeContext {
      * Maps JSP pages to their JspServletWrapper's
      */
     private Map<String, JspServletWrapper> jsps = new 
ConcurrentHashMap<String, JspServletWrapper>();
- 
+
+    /**
+     * Keeps JSP pages ordered by last access. 
+     */
+    private JspQueue<JspServletWrapper> jspQueue = new 
JspQueue<JspServletWrapper>();
 
     // ------------------------------------------------------ Public Methods
 
@@ -205,6 +211,30 @@ public final class JspRuntimeContext {
     }
 
     /**
+     * Push a newly compiled JspServletWrapper into the queue at first
+     * execution of jsp.
+     *
+     * @param jsw Servlet wrapper for jsp.
+     * @return a ticket that can be pushed to front of queue at later 
execution times.
+     * */
+    public org.apache.jasper.util.Entry<JspServletWrapper> 
push(JspServletWrapper jsw) {
+        synchronized (jspQueue) {
+            return jspQueue.push(jsw);
+        }
+    }
+    
+    /**
+     * Push ticket for JspServletWrapper to front of the queue.
+     *
+     * @param ticket the ticket for the jsp.
+     * */
+    public void makeFirst(org.apache.jasper.util.Entry<JspServletWrapper> 
ticket) {
+        synchronized( jspQueue ) {
+            jspQueue.makeYoungest(ticket);
+        }
+    }
+    
+    /**
      * Returns the number of JSPs for which JspServletWrappers exist, i.e.,
      * the number of JSPs that have been loaded into the webapp.
      *
@@ -468,5 +498,48 @@ public final class JspRuntimeContext {
         return new SecurityHolder(source, permissions);
     }
 
+    /** Returns a JspServletWrapper that should be destroyed. Default 
strategy: Least recently used. */
+    public JspServletWrapper getJspForUnload(final int maxLoadedJsps) {
+        if( jsps.size() > maxLoadedJsps ) {
+            synchronized( jsps ) {
+                JspServletWrapper oldest;
+                synchronized( jspQueue) {
+                    oldest = jspQueue.pop();
+                }
+                if (oldest != null) {
+                    removeWrapper(oldest.getJspUri());
+                    return oldest;
+                }
+            }
+        }
+        return null;
+    }
 
+    /**
+     * Method used by background thread to check if any JSP's should be 
destroyed.
+     * If JSP's to be unloaded are found, they will be destroyed.
+     * Uses the lastCheck time from background compiler to determine if it is 
time to unload JSP's.
+     */
+    public void checkUnload() {        
+        if (options.getMaxLoadedJsps() > 0) {
+            long now = System.currentTimeMillis();
+            if (now > (lastCheck + (options.getCheckInterval() * 1000L))) {
+                while (unloadJsp());
+            }
+        }
+    }
+    
+    /**
+     * Checks whether there is a jsp to unload, if one is found, it is 
destroyed. 
+     * */
+    public boolean unloadJsp() {
+        JspServletWrapper jsw = getJspForUnload(options.getMaxLoadedJsps());
+        if( null != jsw ) {
+            synchronized(jsw) {
+                jsw.destroy();
+                return true;
+            }
+        }
+        return false;
+    }
 }

Modified: tomcat/trunk/java/org/apache/jasper/resources/LocalStrings.properties
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/resources/LocalStrings.properties?rev=937787&r1=937786&r2=937787&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/jasper/resources/LocalStrings.properties 
(original)
+++ tomcat/trunk/java/org/apache/jasper/resources/LocalStrings.properties Sun 
Apr 25 12:19:19 2010
@@ -176,6 +176,7 @@ jsp.warning.dumpSmap=Warning: Invalid va
 jsp.warning.genchararray=Warning: Invalid value for the initParam 
genStrAsCharArray. Will use the default value of \"false\"
 jsp.warning.suppressSmap=Warning: Invalid value for the initParam 
suppressSmap. Will use the default value of \"false\"
 jsp.warning.displaySourceFragment=Warning: Invalid value for the initParam 
displaySourceFragment. Will use the default value of \"true\"
+jsp.warning.maxLoadedJsps=Warning: Invalid value for the initParam 
maxLoadedJsps. Will use the default value of \"-1\"
 jsp.error.badtaglib=Unable to open taglibrary {0} : {1}
 jsp.error.badGetReader=Cannot create a reader when the stream is not buffered
 jsp.warning.unknown.element.in.taglib=Unknown element ({0}) in taglib

Modified: tomcat/trunk/java/org/apache/jasper/servlet/JspServlet.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/servlet/JspServlet.java?rev=937787&r1=937786&r2=937787&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/jasper/servlet/JspServlet.java (original)
+++ tomcat/trunk/java/org/apache/jasper/servlet/JspServlet.java Sun Apr 25 
12:19:19 2010
@@ -286,6 +286,7 @@ public class JspServlet extends HttpServ
 
 
     public void periodicEvent() {
+        rctxt.checkUnload();
         rctxt.checkCompile();
     }
 

Modified: tomcat/trunk/java/org/apache/jasper/servlet/JspServletWrapper.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/servlet/JspServletWrapper.java?rev=937787&r1=937786&r2=937787&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/jasper/servlet/JspServletWrapper.java 
(original)
+++ tomcat/trunk/java/org/apache/jasper/servlet/JspServletWrapper.java Sun Apr 
25 12:19:19 2010
@@ -40,6 +40,7 @@ import org.apache.jasper.compiler.JspRun
 import org.apache.jasper.compiler.Localizer;
 import org.apache.jasper.runtime.InstanceManagerFactory;
 import org.apache.jasper.runtime.JspSourceDependent;
+import org.apache.jasper.util.Entry;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.InstanceManager;
@@ -81,6 +82,7 @@ public class JspServletWrapper {
     private JasperException compileException;
     private long servletClassLastModifiedTime;
     private long lastModificationTest = 0L;
+    private Entry<JspServletWrapper> ticket;
 
     /*
      * JspServletWrapper for JSP pages.
@@ -273,6 +275,10 @@ public class JspServletWrapper {
         return tripCount--;
     }
 
+    public String getJspUri() {
+        return jspUri;
+    }
+
     public void service(HttpServletRequest request, 
                         HttpServletResponse response,
                         boolean precompile)
@@ -306,6 +312,10 @@ public class JspServletWrapper {
 
                     // The following sets reload to true, if necessary
                     ctxt.compile();
+                    
+                    if (options.getMaxLoadedJsps() > 0) {
+                        ctxt.getRuntimeContext().unloadJsp();
+                    }
                 }
             } else {
                 if (compileException != null) {
@@ -367,7 +377,14 @@ public class JspServletWrapper {
             } else {
                 theServlet.service(request, response);
             }
-
+            if (options.getMaxLoadedJsps() > 0) {
+                synchronized(this) {
+                    if (ticket == null)
+                        ticket = ctxt.getRuntimeContext().push(this);
+                    else
+                        ctxt.getRuntimeContext().makeFirst(ticket);
+                }
+            }
         } catch (UnavailableException ex) {
             String includeRequestUri = (String)
                 request.getAttribute("javax.servlet.include.request_uri");

Added: tomcat/trunk/java/org/apache/jasper/util/Entry.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/util/Entry.java?rev=937787&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/jasper/util/Entry.java (added)
+++ tomcat/trunk/java/org/apache/jasper/util/Entry.java Sun Apr 25 12:19:19 2010
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jasper.util;
+
+/**
+ * Implementation of a list entry. It exposes links to previous and next
+ * elements on package level only.
+ */
+public class Entry<T> {
+
+    /** The content this entry is valid for. */
+    private final T content;
+    /** Pointer to next element in queue. */
+    private Entry<T> next;
+    /** Pointer to previous element in queue. */
+    private Entry<T> previous;
+
+    public Entry(T object) {
+        content = object;
+    }
+
+    protected void setNext(final Entry<T> next) {
+        this.next = next;
+    }
+
+    protected void setPrevious(final Entry<T> previous) {
+        this.previous = previous;
+    }
+
+    public T getContent() {
+        return content;
+    }
+
+    public Entry<T> getPrevious() {
+        return previous;
+    }
+
+    public Entry<T> getNext() {
+        return next;
+    }
+
+    @Override
+    public String toString() {
+        return content.toString();
+    }
+}

Propchange: tomcat/trunk/java/org/apache/jasper/util/Entry.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/java/org/apache/jasper/util/JspQueue.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/util/JspQueue.java?rev=937787&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/jasper/util/JspQueue.java (added)
+++ tomcat/trunk/java/org/apache/jasper/util/JspQueue.java Sun Apr 25 12:19:19 
2010
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jasper.util;
+
+/**
+ * 
+ * The JspQueue is supposed to hold a set of instances in sorted order. Sorting
+ * order is determined by the instances' content. As this content may change
+ * during instance lifetime, the Queue must be cheap to update - ideally in
+ * constant time.
+ * 
+ * Access to the first element in the queue must happen in constant time.
+ * 
+ * Only a minimal set of operations is implemented.
+ */
+public class JspQueue<T> {
+
+    /** Head of the queue. */
+    private Entry<T> head;
+    /** Last element of the queue. */
+    private Entry<T> last;
+
+    /** Initialize empty queue. */
+    public JspQueue() {
+        head = null;
+        last = null;
+    }
+
+    /**
+     * Adds an object to the end of the queue and returns the entry created for
+     * said object. The entry can later be reused for moving the entry back to
+     * the front of the list.
+     * 
+     * @param object
+     *            the object to append to the end of the list.
+     * @return a ticket for use when the object should be moved back to the
+     *         front.
+     * */
+    public Entry<T> push(final T object) {
+        Entry<T> entry = new Entry<T>(object);
+        if (head == null) {
+            head = last = entry;
+        } else {
+            last.setPrevious(entry);
+            entry.setNext(last);
+            last = entry;
+        }
+
+        return entry;
+    }
+
+    /**
+     * Removes the head of the queue and returns its content.
+     * 
+     * @return the content of the head of the queue.
+     **/
+    public T pop() {
+        T content = null;
+        if (head != null) {
+            content = head.getContent();
+            if (head.getPrevious() != null)
+                head.getPrevious().setNext(null);
+            head = head.getPrevious();
+        }
+        return content;
+    }
+
+    /**
+     * Moves the candidate to the front of the queue.
+     * 
+     * @param candidate
+     *            the entry to move to the front of the queue.
+     * */
+    public void makeYoungest(final Entry<T> candidate) {
+        if (candidate.getPrevious() != null) {
+            Entry<T> candidateNext = candidate.getNext();
+            Entry<T> candidatePrev = candidate.getPrevious();
+            candidatePrev.setNext(candidateNext);
+            if (candidateNext != null)
+                candidateNext.setPrevious(candidatePrev);
+            else
+                head = candidatePrev;
+            candidate.setNext(last);
+            candidate.setPrevious(null);
+            last.setPrevious(candidate);
+            last = candidate;
+        }
+    }
+}

Propchange: tomcat/trunk/java/org/apache/jasper/util/JspQueue.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=937787&r1=937786&r2=937787&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Sun Apr 25 12:19:19 2010
@@ -154,6 +154,10 @@
         <rev>787978</rev> Use "1.6" as the default value for compilerSourceVM 
and compilerTargetVM
         options of Jasper. (kkolinko)
       </update>
+      <add>
+        <bug>48358</bug>: Add support for limiting the number of JSPs that are
+        loaded at any one time. Based on a patch by Isabel Drost. (markt)
+      </add>
     </changelog>
   </subsection>
   <subsection name="High Availability">

Modified: tomcat/trunk/webapps/docs/jasper-howto.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/jasper-howto.xml?rev=937787&r1=937786&r2=937787&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/jasper-howto.xml (original)
+++ tomcat/trunk/webapps/docs/jasper-howto.xml Sun Apr 25 12:19:19 2010
@@ -157,6 +157,12 @@ code for each page instead of deleting i
 print statement per input line, to ease debugging?
 <code>true</code> or <code>false</code>, default <code>true</code>.</li>
 
+<li><strong>maxLoadedJsps</strong> - The maximum number of JSPs that will be
+loaded for a web application. If more than this number of JSPs are loaded, the
+least recently used JSPs will be unloaded so that the number of JSPs loaded at
+any one time does not exceed this limit. A value of zero or less indicates no
+limit. Default <code>-1</code></li>
+
 <li><strong>modificationTestInterval</strong> - Causes a JSP (and its dependent
 files) to not be checked for modification during the specified time interval
 (in seconds) from the last time the JSP was checked for modification. A value 
of



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to