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