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: [email protected]
For additional commands, e-mail: [email protected]