[SCM] tomcat7: Servlet and JSP engine branch, upstream, updated. upstream/7.0.22
Miguel Landaeta
miguel at miguel.cc
Tue Oct 4 23:08:02 UTC 2011
The following commit has been merged in the upstream branch:
commit 761f73ee75ab7852e2194fe111567fea425007ba
Author: Miguel Landaeta <miguel at miguel.cc>
Date: Tue Oct 4 18:37:45 2011 -0430
Imported Upstream version 7.0.22
diff --git a/build.properties.default b/build.properties.default
index 9951386..816a77b 100644
--- a/build.properties.default
+++ b/build.properties.default
@@ -21,13 +21,13 @@
# modules that Tomcat depends on. Copy this file to "build.properties"
# in the top-level source directory, and customize it as needed.
#
-# $Id: build.properties.default 1162976 2011-08-29 20:14:36Z markt $
+# $Id: build.properties.default 1176597 2011-09-27 20:24:31Z markt $
# -----------------------------------------------------------------------------
# ----- Version Control Flags -----
version.major=7
version.minor=0
-version.build=21
+version.build=22
version.patch=0
version.suffix=
diff --git a/build.xml b/build.xml
index 25a1fee..f1be936 100644
--- a/build.xml
+++ b/build.xml
@@ -1055,21 +1055,23 @@
<target name="test" description="Runs the JUnit test cases"
depends="test-bio,test-nio,test-apr" >
+ <fail if="test.result.error" message='Some tests completed with an Error. See ${tomcat.build}/logs for details, search for "FAILED".' />
+ <fail if="test.result.failure" message='Some tests completed with a Failure. See ${tomcat.build}/logs for details, search for "FAILED".' />
</target>
- <target name="test-bio" description="Runs the JUnit test cases for BIO"
+ <target name="test-bio" description="Runs the JUnit test cases for BIO. Does not stop on errors."
depends="test-compile,deploy" if="${execute.test.bio}">
<runtests protocol="org.apache.coyote.http11.Http11Protocol"
extension=".BIO.txt" />
</target>
- <target name="test-nio" description="Runs the JUnit test cases for NIO"
+ <target name="test-nio" description="Runs the JUnit test cases for NIO. Does not stop on errors."
depends="test-compile,deploy" if="${execute.test.nio}">
<runtests protocol="org.apache.coyote.http11.Http11NioProtocol"
extension=".NIO.txt" />
</target>
- <target name="test-apr" description="Runs the JUnit test cases for APR"
+ <target name="test-apr" description="Runs the JUnit test cases for APR. Does not stop on errors."
depends="test-compile,deploy,test-apr-exists"
if="${apr.exists}">
<runtests protocol="org.apache.coyote.http11.Http11AprProtocol"
@@ -1082,14 +1084,18 @@
</target>
<macrodef name="runtests"
- description="Runs the unit tests using the specified connector" >
+ description="Runs the unit tests using the specified connector.
+ Does not stop on errors, but sets 'test.result.error' and 'test.result.failure' properties.">
<attribute name="protocol"
description="The class name for the connector protocol"/>
<attribute name="extension"
description="The extension to use to distinguish the output"/>
<sequential>
- <junit printsummary="yes" fork="yes" dir="." showoutput="yes">
+ <junit printsummary="yes" fork="yes" dir="." showoutput="yes"
+ errorproperty="test.result.error"
+ failureproperty="test.result.failure"
+ haltonfailure="${test.haltonfailure}" >
<jvmarg value="${test.jvmarg.egd}"/>
<jvmarg value="-Djava.library.path=${test.apr.loc}"/>
@@ -1106,11 +1112,7 @@
<!-- If test.entry is defined, run a single test, otherwise run all valid tests -->
<test todir="${tomcat.build}/logs" name="${test.entry}" if="test.entry"/>
- <batchtest todir="${tomcat.build}/logs"
- unless="test.entry"
- errorproperty="test.result.error"
- failureproperty="test.result.failure"
- haltonfailure="${test.haltonfailure}">
+ <batchtest todir="${tomcat.build}/logs" unless="test.entry">
<fileset dir="test" >
<!-- Include all by default -->
<include name="${test.name}" />
@@ -1121,9 +1123,6 @@
</fileset>
</batchtest>
</junit>
-
- <fail if="test.result.error" message="Some tests completed with an Error. See ${tomcat.build}/logs for details." />
- <fail if="test.result.failure" message="Some tests completed with a Failure. See ${tomcat.build}/logs for details." />
</sequential>
</macrodef>
diff --git a/conf/catalina.properties b/conf/catalina.properties
index e00d277..19ea25b 100644
--- a/conf/catalina.properties
+++ b/conf/catalina.properties
@@ -105,6 +105,7 @@ jmx-tools.jar,jta*.jar,log4j*.jar,mail*.jar,slf4j*.jar,\
xercesImpl.jar,xmlParserAPIs.jar,xml-apis.jar,\
dnsns.jar,ldapsec.jar,localedata.jar,sunjce_provider.jar,sunmscapi.jar,\
sunpkcs11.jar,jhall.jar,tools.jar,\
+sunec.jar,zipfs.jar,\
apple_provider.jar,AppleScriptEngine.jar,CoreAudio.jar,dns_sd.jar,\
j3daudio.jar,j3dcore.jar,j3dutils.jar,jai_core.jar,jai_codec.jar,\
mlibwrapper_jai.jar,MRJToolkit.jar,vecmath.jar,\
diff --git a/java/javax/el/BeanELResolver.java b/java/javax/el/BeanELResolver.java
index ddaa8e2..307ba62 100644
--- a/java/javax/el/BeanELResolver.java
+++ b/java/javax/el/BeanELResolver.java
@@ -414,14 +414,16 @@ public class BeanELResolver extends ELResolver {
}
Method[] methods = clazz.getMethods();
for (Method m : methods) {
- if (methodName.equals(m.getName()) &&
- m.getParameterTypes().length == paramCount) {
- // Same number of parameters - use the first match
- matchingMethod = getMethod(clazz, m);
- break;
- }
- if (m.isVarArgs()) {
- matchingMethod = getMethod(clazz, m);
+ if (methodName.equals(m.getName())) {
+ if (m.getParameterTypes().length == paramCount) {
+ // Same number of parameters - use the first match
+ matchingMethod = getMethod(clazz, m);
+ break;
+ }
+ if (m.isVarArgs()
+ && paramCount > m.getParameterTypes().length - 2) {
+ matchingMethod = getMethod(clazz, m);
+ }
}
}
if (matchingMethod == null) {
@@ -440,21 +442,21 @@ public class BeanELResolver extends ELResolver {
if (matchingMethod.isVarArgs()) {
int varArgIndex = parameterTypes.length - 1;
// First argCount-1 parameters are standard
- for (int i = 0; (i < varArgIndex - 1); i++) {
+ for (int i = 0; (i < varArgIndex); i++) {
parameters[i] = factory.coerceToType(params[i],
parameterTypes[i]);
}
- // Last parameter is the varags
+ // Last parameter is the varargs
Class<?> varArgClass =
parameterTypes[varArgIndex].getComponentType();
+ final Object varargs = Array.newInstance(
+ varArgClass,
+ (paramCount - varArgIndex));
for (int i = (varArgIndex); i < paramCount; i++) {
- Object varargs = Array.newInstance(
- parameterTypes[paramCount],
- (paramCount - varArgIndex));
- Array.set(varargs, i,
+ Array.set(varargs, i - varArgIndex,
factory.coerceToType(params[i], varArgClass));
- parameters[varArgIndex] = varargs;
}
+ parameters[varArgIndex] = varargs;
} else {
parameters = new Object[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
diff --git a/java/org/apache/catalina/Globals.java b/java/org/apache/catalina/Globals.java
index 47adac1..4806d05 100644
--- a/java/org/apache/catalina/Globals.java
+++ b/java/org/apache/catalina/Globals.java
@@ -23,7 +23,7 @@ package org.apache.catalina;
* Global constants that are applicable to multiple packages within Catalina.
*
* @author Craig R. McClanahan
- * @version $Id: Globals.java 1087416 2011-03-31 19:33:04Z markt $
+ * @version $Id: Globals.java 1166689 2011-09-08 13:49:50Z markt $
*/
public final class Globals {
@@ -98,9 +98,15 @@ public final class Globals {
* for this SSL connection (as an object of type java.lang.String).
*/
public static final String SSL_SESSION_ID_ATTR =
+ "javax.servlet.request.ssl_session_id";
+ /**
+ * Tomcat specific attribute as used in Tomcat 6.
+ * @deprecated
+ */
+ @Deprecated
+ public static final String SSL_SESSION_ID_TOMCAT_ATTR =
"javax.servlet.request.ssl_session";
-
/**
* The request attribute key for the session manager.
* This one is a Tomcat extension to the Servlet spec.
diff --git a/java/org/apache/catalina/Host.java b/java/org/apache/catalina/Host.java
index 16c9374..eecb8b8 100644
--- a/java/org/apache/catalina/Host.java
+++ b/java/org/apache/catalina/Host.java
@@ -40,7 +40,7 @@ import java.util.regex.Pattern;
* of Context (representing an individual servlet context).
*
* @author Craig R. McClanahan
- * @version $Id: Host.java 1058357 2011-01-12 23:49:18Z markt $
+ * @version $Id: Host.java 1163999 2011-09-01 11:02:52Z markt $
*/
public interface Host extends Container {
@@ -83,7 +83,7 @@ public interface Host extends Container {
*/
public void setXmlBase(String xmlBase);
- /**
+ /**
* Return the application root for this Host. This can be an absolute
* pathname, a relative pathname, or a URL.
*/
diff --git a/java/org/apache/catalina/connector/CoyoteAdapter.java b/java/org/apache/catalina/connector/CoyoteAdapter.java
index 1c0cd85..fcf2177 100644
--- a/java/org/apache/catalina/connector/CoyoteAdapter.java
+++ b/java/org/apache/catalina/connector/CoyoteAdapter.java
@@ -57,7 +57,7 @@ import org.apache.tomcat.util.res.StringManager;
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Id: CoyoteAdapter.java 1158158 2011-08-16 08:58:35Z markt $
+ * @version $Id: CoyoteAdapter.java 1176592 2011-09-27 20:19:01Z markt $
*/
public class CoyoteAdapter implements Adapter {
@@ -466,10 +466,8 @@ public class CoyoteAdapter implements Adapter {
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
- boolean create = false;
if (request == null) {
- create = true;
// Create objects
request = connector.createRequest();
request.setCoyoteRequest(req);
@@ -511,9 +509,7 @@ public class CoyoteAdapter implements Adapter {
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.warn(sm.getString("coyoteAdapter.accesslogFail"), t);
- }
-
- if (create) {
+ } finally {
request.recycle();
response.recycle();
}
diff --git a/java/org/apache/catalina/connector/Request.java b/java/org/apache/catalina/connector/Request.java
index c2872bd..5e6c2ae 100644
--- a/java/org/apache/catalina/connector/Request.java
+++ b/java/org/apache/catalina/connector/Request.java
@@ -100,7 +100,7 @@ import org.apache.tomcat.util.res.StringManager;
*
* @author Remy Maucherat
* @author Craig R. McClanahan
- * @version $Id: Request.java 1146005 2011-07-13 13:28:24Z markt $
+ * @version $Id: Request.java 1166689 2011-09-08 13:49:50Z markt $
*/
public class Request
@@ -965,6 +965,7 @@ public class Request
attr = coyoteRequest.getAttribute(Globals.SSL_SESSION_ID_ATTR);
if(attr != null) {
attributes.put(Globals.SSL_SESSION_ID_ATTR, attr);
+ attributes.put(Globals.SSL_SESSION_ID_TOMCAT_ATTR, attr);
}
attr = coyoteRequest.getAttribute(Globals.SSL_SESSION_MGR_ATTR);
if(attr != null) {
@@ -985,6 +986,7 @@ public class Request
Globals.CIPHER_SUITE_ATTR.equals(name) ||
Globals.KEY_SIZE_ATTR.equals(name) ||
Globals.SSL_SESSION_ID_ATTR.equals(name) ||
+ Globals.SSL_SESSION_ID_TOMCAT_ATTR.equals(name) ||
Globals.SSL_SESSION_MGR_ATTR.equals(name);
}
@@ -1003,6 +1005,8 @@ public class Request
* <li>{@link Globals#CIPHER_SUITE_ATTR} (SSL connections only)</li>
* <li>{@link Globals#KEY_SIZE_ATTR} (SSL connections only)</li>
* <li>{@link Globals#SSL_SESSION_ID_ATTR} (SSL connections only)</li>
+ * <li>{@link Globals#SSL_SESSION_ID_TOMCAT_ATTR} (SSL connections only)
+ * </li>
* <li>{@link Globals#SSL_SESSION_MGR_ATTR} (SSL connections only)</li>
* </ul>
* The underlying connector may also expose request attributes. These all
diff --git a/java/org/apache/catalina/core/ApplicationContextFacade.java b/java/org/apache/catalina/core/ApplicationContextFacade.java
index 56f2ae5..e7ade3d 100644
--- a/java/org/apache/catalina/core/ApplicationContextFacade.java
+++ b/java/org/apache/catalina/core/ApplicationContextFacade.java
@@ -27,7 +27,6 @@ import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
-import java.util.EnumSet;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.HashMap;
@@ -56,7 +55,7 @@ import org.apache.tomcat.util.ExceptionUtils;
*
* @author Remy Maucherat
* @author Jean-Francois Arcand
- * @version $Id: ApplicationContextFacade.java 1104557 2011-05-17 20:57:46Z kkolinko $
+ * @version $Id: ApplicationContextFacade.java 1166080 2011-09-07 09:24:40Z markt $
*/
public class ApplicationContextFacade implements ServletContext {
@@ -118,7 +117,7 @@ public class ApplicationContextFacade implements ServletContext {
classCache.put("getRealPath", clazz);
classCache.put("getAttribute", clazz);
classCache.put("log", clazz);
- classCache.put("setSessionTrackingModes", new Class[]{EnumSet.class} );
+ classCache.put("setSessionTrackingModes", new Class[]{Set.class} );
}
@@ -571,7 +570,7 @@ public class ApplicationContextFacade implements ServletContext {
@SuppressWarnings("unchecked") // doPrivileged() returns the correct type
public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
if (SecurityUtil.isPackageProtectionEnabled()) {
- return (EnumSet<SessionTrackingMode>)
+ return (Set<SessionTrackingMode>)
doPrivileged("getDefaultSessionTrackingModes", null);
} else {
return context.getDefaultSessionTrackingModes();
@@ -582,7 +581,7 @@ public class ApplicationContextFacade implements ServletContext {
@SuppressWarnings("unchecked") // doPrivileged() returns the correct type
public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
if (SecurityUtil.isPackageProtectionEnabled()) {
- return (EnumSet<SessionTrackingMode>)
+ return (Set<SessionTrackingMode>)
doPrivileged("getEffectiveSessionTrackingModes", null);
} else {
return context.getEffectiveSessionTrackingModes();
diff --git a/java/org/apache/catalina/core/DefaultInstanceManager.java b/java/org/apache/catalina/core/DefaultInstanceManager.java
index 1d1fed7..6f3ce03 100644
--- a/java/org/apache/catalina/core/DefaultInstanceManager.java
+++ b/java/org/apache/catalina/core/DefaultInstanceManager.java
@@ -21,6 +21,8 @@ package org.apache.catalina.core;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -29,8 +31,12 @@ import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Map;
import java.util.Properties;
+import java.util.WeakHashMap;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@@ -52,7 +58,7 @@ import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.res.StringManager;
/**
- * @version $Id: DefaultInstanceManager.java 1145981 2011-07-13 12:45:57Z markt $
+ * @version $Id: DefaultInstanceManager.java 1176588 2011-09-27 20:13:32Z markt $
*/
public class DefaultInstanceManager implements InstanceManager {
@@ -65,6 +71,8 @@ public class DefaultInstanceManager implements InstanceManager {
private Properties restrictedFilters = new Properties();
private Properties restrictedListeners = new Properties();
private Properties restrictedServlets = new Properties();
+ private final Map<Class<?>,WeakReference<List<AnnotationCacheEntry>>> annotationCache =
+ new WeakHashMap<Class<?>, WeakReference<List<AnnotationCacheEntry>>>();
public DefaultInstanceManager(Context context, Map<String, Map<String, String>> injectionMap, org.apache.catalina.Context catalinaContext, ClassLoader containerClassLoader) {
classLoader = catalinaContext.getLoader().getClassLoader();
@@ -134,6 +142,7 @@ public class DefaultInstanceManager implements InstanceManager {
private Object newInstance(Object instance, Class<?> clazz) throws IllegalAccessException, InvocationTargetException, NamingException {
if (!ignoreAnnotations) {
Map<String, String> injections = injectionMap.get(clazz.getName());
+ populateAnnotationsCache(clazz, injections);
processAnnotations(instance, injections);
postConstruct(instance, clazz);
}
@@ -158,46 +167,33 @@ public class DefaultInstanceManager implements InstanceManager {
*/
protected void postConstruct(Object instance, final Class<?> clazz)
throws IllegalAccessException, InvocationTargetException {
+ if (context == null) {
+ // No resource injection
+ return;
+ }
+
Class<?> superClass = clazz.getSuperclass();
if (superClass != Object.class) {
postConstruct(instance, superClass);
}
- Method[] methods = null;
- if (Globals.IS_SECURITY_ENABLED) {
- methods = AccessController.doPrivileged(
- new PrivilegedAction<Method[]>(){
- @Override
- public Method[] run(){
- return clazz.getDeclaredMethods();
- }
- });
- } else {
- methods = clazz.getDeclaredMethods();
- }
- Method postConstruct = null;
- for (Method method : methods) {
- if (method.isAnnotationPresent(PostConstruct.class)) {
- if ((postConstruct != null)
- || (method.getParameterTypes().length != 0)
- || (Modifier.isStatic(method.getModifiers()))
- || (method.getExceptionTypes().length > 0)
- || (!method.getReturnType().getName().equals("void"))) {
- throw new IllegalArgumentException("Invalid PostConstruct annotation");
- }
- postConstruct = method;
- }
- }
-
// At the end the postconstruct annotated
// method is invoked
- if (postConstruct != null) {
- boolean accessibility = postConstruct.isAccessible();
- postConstruct.setAccessible(true);
- postConstruct.invoke(instance);
- postConstruct.setAccessible(accessibility);
+ List<AnnotationCacheEntry> annotations;
+ synchronized (annotationCache) {
+ annotations = annotationCache.get(clazz).get();
+ }
+ for (AnnotationCacheEntry entry : annotations) {
+ if (entry.getType() == AnnotationCacheEntryType.POST_CONSTRUCT) {
+ Method postConstruct = (Method) entry.getAccessibleObject();
+ boolean accessibility = postConstruct.isAccessible();
+ synchronized (postConstruct) {
+ postConstruct.setAccessible(true);
+ postConstruct.invoke(instance);
+ postConstruct.setAccessible(accessibility);
+ }
+ }
}
-
}
@@ -217,41 +213,222 @@ public class DefaultInstanceManager implements InstanceManager {
preDestroy(instance, superClass);
}
- Method[] methods;
- if (Globals.IS_SECURITY_ENABLED) {
- methods = AccessController.doPrivileged(
- new PrivilegedAction<Method[]>(){
- @Override
- public Method[] run(){
- return clazz.getDeclaredMethods();
- }
- });
- } else {
- methods = clazz.getDeclaredMethods();
- }
- Method preDestroy = null;
- for (Method method : methods) {
- if (method.isAnnotationPresent(PreDestroy.class)) {
- if ((method.getParameterTypes().length != 0)
- || (Modifier.isStatic(method.getModifiers()))
- || (method.getExceptionTypes().length > 0)
- || (!method.getReturnType().getName().equals("void"))) {
- throw new IllegalArgumentException("Invalid PreDestroy annotation");
+ // At the end the postconstruct annotated
+ // method is invoked
+ List<AnnotationCacheEntry> annotations = null;
+ synchronized (annotationCache) {
+ WeakReference<List<AnnotationCacheEntry>> ref =
+ annotationCache.get(clazz);
+ if (ref != null) {
+ annotations = ref.get();
+ }
+ }
+ if (annotations == null) {
+ // instance not created through the instance manager
+ return;
+ }
+ for (AnnotationCacheEntry entry : annotations) {
+ if (entry.getType() == AnnotationCacheEntryType.PRE_DESTROY) {
+ Method preDestroy = (Method) entry.getAccessibleObject();
+ boolean accessibility = preDestroy.isAccessible();
+ synchronized (preDestroy) {
+ preDestroy.setAccessible(true);
+ preDestroy.invoke(instance);
+ preDestroy.setAccessible(accessibility);
}
- preDestroy = method;
- break;
}
}
+ }
- // At the end the postconstruct annotated
- // method is invoked
- if (preDestroy != null) {
- boolean accessibility = preDestroy.isAccessible();
- preDestroy.setAccessible(true);
- preDestroy.invoke(instance);
- preDestroy.setAccessible(accessibility);
+
+ /**
+ * Make sure that the annotations cache has been populated for the provided
+ * class.
+ *
+ * @param clazz clazz to populate annotations for
+ * @param injections map of injections for this class from xml deployment
+ * descriptor
+ * @throws IllegalAccessException if injection target is inaccessible
+ * @throws javax.naming.NamingException if value cannot be looked up in jndi
+ * @throws java.lang.reflect.InvocationTargetException
+ * if injection fails
+ */
+ protected void populateAnnotationsCache(Class<?> clazz,
+ Map<String, String> injections) throws IllegalAccessException,
+ InvocationTargetException, NamingException {
+
+ if (context == null) {
+ // No resource injection
+ return;
}
+ while (clazz != null) {
+ List<AnnotationCacheEntry> annotations = null;
+ synchronized (annotationCache) {
+ WeakReference<List<AnnotationCacheEntry>> ref =
+ annotationCache.get(clazz);
+ if (ref != null) {
+ annotations = ref.get();
+ }
+ }
+ if (annotations == null) {
+ annotations = new ArrayList<AnnotationCacheEntry>();
+ // Initialize fields annotations
+ Field[] fields = null;
+ if (Globals.IS_SECURITY_ENABLED) {
+ final Class<?> clazz2 = clazz;
+ fields = AccessController.doPrivileged(
+ new PrivilegedAction<Field[]>(){
+ @Override
+ public Field[] run(){
+ return clazz2.getDeclaredFields();
+ }
+ });
+ } else {
+ fields = clazz.getDeclaredFields();
+ }
+ for (Field field : fields) {
+ if (injections != null && injections.containsKey(field.getName())) {
+ annotations.add(new AnnotationCacheEntry(field,
+ injections.get(field.getName()),
+ AnnotationCacheEntryType.FIELD));
+ } else if (field.isAnnotationPresent(Resource.class)) {
+ Resource annotation = field.getAnnotation(Resource.class);
+ annotations.add(new AnnotationCacheEntry(field,
+ annotation.name(),
+ AnnotationCacheEntryType.FIELD));
+ } else if (field.isAnnotationPresent(EJB.class)) {
+ EJB annotation = field.getAnnotation(EJB.class);
+ annotations.add(new AnnotationCacheEntry(field,
+ annotation.name(),
+ AnnotationCacheEntryType.FIELD));
+ } else if (field.isAnnotationPresent(WebServiceRef.class)) {
+ WebServiceRef annotation =
+ field.getAnnotation(WebServiceRef.class);
+ annotations.add(new AnnotationCacheEntry(field,
+ annotation.name(),
+ AnnotationCacheEntryType.FIELD));
+ } else if (field.isAnnotationPresent(PersistenceContext.class)) {
+ PersistenceContext annotation =
+ field.getAnnotation(PersistenceContext.class);
+ annotations.add(new AnnotationCacheEntry(field,
+ annotation.name(),
+ AnnotationCacheEntryType.FIELD));
+ } else if (field.isAnnotationPresent(PersistenceUnit.class)) {
+ PersistenceUnit annotation =
+ field.getAnnotation(PersistenceUnit.class);
+ annotations.add(new AnnotationCacheEntry(field,
+ annotation.name(),
+ AnnotationCacheEntryType.FIELD));
+ }
+ }
+
+ // Initialize methods annotations
+ Method[] methods = null;
+ if (Globals.IS_SECURITY_ENABLED) {
+ final Class<?> clazz2 = clazz;
+ methods = AccessController.doPrivileged(
+ new PrivilegedAction<Method[]>(){
+ @Override
+ public Method[] run(){
+ return clazz2.getDeclaredMethods();
+ }
+ });
+ } else {
+ methods = clazz.getDeclaredMethods();
+ }
+ Method postConstruct = null;
+ Method preDestroy = null;
+ for (Method method : methods) {
+ String methodName = method.getName();
+ if (injections != null && methodName.startsWith("set") && methodName.length() > 3) {
+ String fieldName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
+ if (injections.containsKey(fieldName)) {
+ annotations.add(new AnnotationCacheEntry(method,
+ injections.get(method.getName()),
+ AnnotationCacheEntryType.FIELD));
+ break;
+ }
+ }
+ if (method.isAnnotationPresent(Resource.class)) {
+ Resource annotation = method.getAnnotation(Resource.class);
+ annotations.add(new AnnotationCacheEntry(method,
+ annotation.name(),
+ AnnotationCacheEntryType.FIELD));
+ } else if (method.isAnnotationPresent(EJB.class)) {
+ EJB annotation = method.getAnnotation(EJB.class);
+ annotations.add(new AnnotationCacheEntry(method,
+ annotation.name(),
+ AnnotationCacheEntryType.FIELD));
+ } else if (method.isAnnotationPresent(WebServiceRef.class)) {
+ WebServiceRef annotation =
+ method.getAnnotation(WebServiceRef.class);
+ annotations.add(new AnnotationCacheEntry(method,
+ annotation.name(),
+ AnnotationCacheEntryType.FIELD));
+ } else if (method.isAnnotationPresent(PersistenceContext.class)) {
+ PersistenceContext annotation =
+ method.getAnnotation(PersistenceContext.class);
+ annotations.add(new AnnotationCacheEntry(method,
+ annotation.name(),
+ AnnotationCacheEntryType.FIELD));
+ } else if (method.isAnnotationPresent(PersistenceUnit.class)) {
+ PersistenceUnit annotation =
+ method.getAnnotation(PersistenceUnit.class);
+ annotations.add(new AnnotationCacheEntry(method,
+ annotation.name(),
+ AnnotationCacheEntryType.FIELD));
+ }
+
+ if (method.isAnnotationPresent(PostConstruct.class)) {
+ if ((postConstruct != null) ||
+ (method.getParameterTypes().length != 0) ||
+ (Modifier.isStatic(method.getModifiers())) ||
+ (method.getExceptionTypes().length > 0) ||
+ (!method.getReturnType().getName().equals("void"))) {
+ throw new IllegalArgumentException(
+ "Invalid PostConstruct annotation");
+ }
+ postConstruct = method;
+ }
+
+ if (method.isAnnotationPresent(PreDestroy.class)) {
+ if ((preDestroy != null ||
+ method.getParameterTypes().length != 0) ||
+ (Modifier.isStatic(method.getModifiers())) ||
+ (method.getExceptionTypes().length > 0) ||
+ (!method.getReturnType().getName().equals("void"))) {
+ throw new IllegalArgumentException(
+ "Invalid PreDestroy annotation");
+ }
+ preDestroy = method;
+ }
+ }
+ if (postConstruct != null) {
+ annotations.add(new AnnotationCacheEntry(postConstruct,
+ null, AnnotationCacheEntryType.POST_CONSTRUCT));
+ }
+ if (preDestroy != null) {
+ annotations.add(new AnnotationCacheEntry(preDestroy,
+ null, AnnotationCacheEntryType.PRE_DESTROY));
+ }
+ if (annotations.size() == 0) {
+ // Use common empty list to save memory
+ annotations = Collections.emptyList();
+ }
+ synchronized (annotationCache) {
+ annotationCache.put(clazz,
+ new WeakReference<List<AnnotationCacheEntry>>(
+ annotations));
+ }
+ } else {
+ // If the annotations for this class have been cached, the
+ // annotations for all the super classes will have been cachced
+ // as well
+ break;
+ }
+ clazz = clazz.getSuperclass();
+ }
}
@@ -276,102 +453,35 @@ public class DefaultInstanceManager implements InstanceManager {
Class<?> clazz = instance.getClass();
while (clazz != null) {
- // Initialize fields annotations
- Field[] fields = null;
- if (Globals.IS_SECURITY_ENABLED) {
- final Class<?> clazz2 = clazz;
- fields = AccessController.doPrivileged(
- new PrivilegedAction<Field[]>(){
- @Override
- public Field[] run(){
- return clazz2.getDeclaredFields();
- }
- });
- } else {
- fields = clazz.getDeclaredFields();
+ List<AnnotationCacheEntry> annotations;
+ synchronized (annotationCache) {
+ annotations = annotationCache.get(clazz).get();
}
- for (Field field : fields) {
- if (injections != null && injections.containsKey(field.getName())) {
- lookupFieldResource(context, instance, field,
- injections.get(field.getName()), clazz);
- } else if (field.isAnnotationPresent(Resource.class)) {
- Resource annotation = field.getAnnotation(Resource.class);
- lookupFieldResource(context, instance, field,
- annotation.name(), clazz);
- } else if (field.isAnnotationPresent(EJB.class)) {
- EJB annotation = field.getAnnotation(EJB.class);
- lookupFieldResource(context, instance, field,
- annotation.name(), clazz);
- } else if (field.isAnnotationPresent(WebServiceRef.class)) {
- WebServiceRef annotation =
- field.getAnnotation(WebServiceRef.class);
- lookupFieldResource(context, instance, field,
- annotation.name(), clazz);
- } else if (field.isAnnotationPresent(PersistenceContext.class)) {
- PersistenceContext annotation =
- field.getAnnotation(PersistenceContext.class);
- lookupFieldResource(context, instance, field,
- annotation.name(), clazz);
- } else if (field.isAnnotationPresent(PersistenceUnit.class)) {
- PersistenceUnit annotation =
- field.getAnnotation(PersistenceUnit.class);
- lookupFieldResource(context, instance, field,
- annotation.name(), clazz);
- }
- }
-
- // Initialize methods annotations
- Method[] methods = null;
- if (Globals.IS_SECURITY_ENABLED) {
- final Class<?> clazz2 = clazz;
- methods = AccessController.doPrivileged(
- new PrivilegedAction<Method[]>(){
- @Override
- public Method[] run(){
- return clazz2.getDeclaredMethods();
+ for (AnnotationCacheEntry entry : annotations) {
+ if (entry.getType() == AnnotationCacheEntryType.FIELD) {
+ if (entry.getAccessibleObject() instanceof Method) {
+ lookupMethodResource(context, instance,
+ (Method) entry.getAccessibleObject(),
+ entry.getName(), clazz);
+ } else {
+ lookupFieldResource(context, instance,
+ (Field) entry.getAccessibleObject(),
+ entry.getName(), clazz);
}
- });
- } else {
- methods = clazz.getDeclaredMethods();
- }
- for (Method method : methods) {
- String methodName = method.getName();
- if (injections != null && methodName.startsWith("set") && methodName.length() > 3) {
- String fieldName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
- if (injections.containsKey(fieldName)) {
- lookupMethodResource(context, instance, method,
- injections.get(fieldName), clazz);
- break;
- }
- }
- if (method.isAnnotationPresent(Resource.class)) {
- Resource annotation = method.getAnnotation(Resource.class);
- lookupMethodResource(context, instance, method,
- annotation.name(), clazz);
- } else if (method.isAnnotationPresent(EJB.class)) {
- EJB annotation = method.getAnnotation(EJB.class);
- lookupMethodResource(context, instance, method,
- annotation.name(), clazz);
- } else if (method.isAnnotationPresent(WebServiceRef.class)) {
- WebServiceRef annotation =
- method.getAnnotation(WebServiceRef.class);
- lookupMethodResource(context, instance, method,
- annotation.name(), clazz);
- } else if (method.isAnnotationPresent(PersistenceContext.class)) {
- PersistenceContext annotation =
- method.getAnnotation(PersistenceContext.class);
- lookupMethodResource(context, instance, method,
- annotation.name(), clazz);
- } else if (method.isAnnotationPresent(PersistenceUnit.class)) {
- PersistenceUnit annotation =
- method.getAnnotation(PersistenceUnit.class);
- lookupMethodResource(context, instance, method,
- annotation.name(), clazz);
}
}
clazz = clazz.getSuperclass();
}
+ }
+
+ /**
+ * Makes cache size available to unit tests.
+ */
+ protected int getAnnotationCacheSize() {
+ synchronized (annotationCache) {
+ return annotationCache.size();
+ }
}
@@ -420,6 +530,10 @@ public class DefaultInstanceManager implements InstanceManager {
if (Filter.class.isAssignableFrom(clazz)) {
checkAccess(clazz, restrictedFilters);
} else if (Servlet.class.isAssignableFrom(clazz)) {
+ if (ContainerServlet.class.isAssignableFrom(clazz)) {
+ throw new SecurityException("Restricted (ContainerServlet) " +
+ clazz);
+ }
checkAccess(clazz, restrictedServlets);
} else {
checkAccess(clazz, restrictedListeners);
@@ -464,9 +578,11 @@ public class DefaultInstanceManager implements InstanceManager {
}
accessibility = field.isAccessible();
- field.setAccessible(true);
- field.set(instance, lookedupResource);
- field.setAccessible(accessibility);
+ synchronized (field) {
+ field.setAccessible(true);
+ field.set(instance, lookedupResource);
+ field.setAccessible(accessibility);
+ }
}
/**
@@ -506,9 +622,11 @@ public class DefaultInstanceManager implements InstanceManager {
}
accessibility = method.isAccessible();
- method.setAccessible(true);
- method.invoke(instance, lookedupResource);
- method.setAccessible(accessibility);
+ synchronized (method) {
+ method.setAccessible(true);
+ method.invoke(instance, lookedupResource);
+ method.setAccessible(accessibility);
+ }
}
public static String getName(Method setter) {
@@ -529,4 +647,32 @@ public class DefaultInstanceManager implements InstanceManager {
}
return jndiName;
}
+
+ private static final class AnnotationCacheEntry {
+ private final AccessibleObject accessibleObject;
+ private final String name;
+ private final AnnotationCacheEntryType type;
+
+ public AnnotationCacheEntry(AccessibleObject accessibleObject,
+ String name, AnnotationCacheEntryType type) {
+ this.accessibleObject = accessibleObject;
+ this.name = name;
+ this.type = type;
+ }
+
+ public AccessibleObject getAccessibleObject() {
+ return accessibleObject;
+ }
+
+ public String getName() {
+ return name;
+ }
+ public AnnotationCacheEntryType getType() {
+ return type;
+ }
+ }
+
+ private static enum AnnotationCacheEntryType {
+ FIELD, POST_CONSTRUCT, PRE_DESTROY
+ }
}
diff --git a/java/org/apache/catalina/core/JreMemoryLeakPreventionListener.java b/java/org/apache/catalina/core/JreMemoryLeakPreventionListener.java
index 37b13e8..22b0a48 100644
--- a/java/org/apache/catalina/core/JreMemoryLeakPreventionListener.java
+++ b/java/org/apache/catalina/core/JreMemoryLeakPreventionListener.java
@@ -24,6 +24,7 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.sql.DriverManager;
+import java.util.StringTokenizer;
import javax.imageio.ImageIO;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -81,6 +82,19 @@ public class JreMemoryLeakPreventionListener implements LifecycleListener {
}
/**
+ * Protect against the memory leak caused when the
+ * <code>sun.java2d.Disposer</code> class is loaded by a web application.
+ * Defaults to <code>false</code> because a new Thread is launched.
+ */
+ private boolean java2dDisposerProtection = false;
+ public boolean isJava2DDisposerProtection() {
+ return java2dDisposerProtection;
+ }
+ public void setJava2DDisposerProtection(boolean java2dDisposerProtection) {
+ this.java2dDisposerProtection = java2dDisposerProtection;
+ }
+
+ /**
* Protect against the memory leak caused when the first call to
* <code>sun.misc.GC.requestLatency(long)</code> is triggered by a web
* application. This first call will start a GC Daemon thread with the
@@ -189,7 +203,21 @@ public class JreMemoryLeakPreventionListener implements LifecycleListener {
public void setDriverManagerProtection(boolean driverManagerProtection) {
this.driverManagerProtection = driverManagerProtection;
}
-
+
+ /**
+ * List of comma-separated fully qualified class names to load and initialize during
+ * the startup of this Listener. This allows to pre-load classes that are known to
+ * provoke classloader leaks if they are loaded during a request processing.
+ */
+ private String classesToInitialize = null;
+ public String getClassesToInitialize() {
+ return classesToInitialize;
+ }
+ public void setClassesToInitialize(String classesToInitialize) {
+ this.classesToInitialize = classesToInitialize;
+ }
+
+
@Override
public void lifecycleEvent(LifecycleEvent event) {
// Initialise these classes when Tomcat starts
@@ -237,6 +265,18 @@ public class JreMemoryLeakPreventionListener implements LifecycleListener {
java.awt.Toolkit.getDefaultToolkit();
}
+ // Trigger the creation of the "Java2D Disposer" thread.
+ // See https://issues.apache.org/bugzilla/show_bug.cgi?id=51687
+ if(java2dDisposerProtection) {
+ try {
+ Class.forName("sun.java2d.Disposer");
+ }
+ catch (ClassNotFoundException cnfe) {
+ // Ignore this case: we must be running on a
+ // non-Sun-based JRE.
+ }
+ }
+
/*
* Several components end up calling:
* sun.misc.GC.requestLatency(long)
@@ -394,6 +434,22 @@ public class JreMemoryLeakPreventionListener implements LifecycleListener {
}
}
}
+
+ if (classesToInitialize != null) {
+ StringTokenizer strTok =
+ new StringTokenizer(classesToInitialize, ", \r\n\t");
+ while (strTok.hasMoreTokens()) {
+ String classNameToLoad = strTok.nextToken();
+ try {
+ Class.forName(classNameToLoad);
+ } catch (ClassNotFoundException e) {
+ log.error(
+ sm.getString("jreLeakListener.classToInitializeFail",
+ classNameToLoad), e);
+ // continue with next class to load
+ }
+ }
+ }
} finally {
Thread.currentThread().setContextClassLoader(loader);
diff --git a/java/org/apache/catalina/core/LocalStrings.properties b/java/org/apache/catalina/core/LocalStrings.properties
index 5581523..839005c 100644
--- a/java/org/apache/catalina/core/LocalStrings.properties
+++ b/java/org/apache/catalina/core/LocalStrings.properties
@@ -83,6 +83,7 @@ jreLeakListener.jarUrlConnCacheFail=Failed to disable Jar URL connection caching
jreLeakListener.xmlParseFail=Error whilst attempting to prevent memory leaks during XML parsing
jreLeakListener.authPolicyFail=Error whilst attempting to prevent memory leak in javax.security.auth.Policy class
jreLeakListener.ldapPoolManagerFail=Failed to trigger creation of the com.sun.jndi.ldap.LdapPoolManager class during Tomcat start to prevent possible memory leaks. This is expected on non-Sun JVMs.
+jreLeakListener.classToInitializeFail=Failed to load class {0} during Tomcat start to prevent possible memory leaks.
naming.wsdlFailed=Failed to find wsdl file: {0}
naming.bindFailed=Failed to bind object: {0}
naming.jmxRegistrationFailed=Failed to register in JMX: {0}
diff --git a/java/org/apache/catalina/core/StandardContextValve.java b/java/org/apache/catalina/core/StandardContextValve.java
index da3bc83..f8917e2 100644
--- a/java/org/apache/catalina/core/StandardContextValve.java
+++ b/java/org/apache/catalina/core/StandardContextValve.java
@@ -21,24 +21,16 @@ package org.apache.catalina.core;
import java.io.IOException;
-import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.Container;
-import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.comet.CometEvent;
-import org.apache.catalina.connector.ClientAbortException;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
-import org.apache.catalina.deploy.ErrorPage;
import org.apache.catalina.valves.ValveBase;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.buf.MessageBytes;
/**
@@ -49,13 +41,11 @@ import org.apache.tomcat.util.buf.MessageBytes;
* when processing HTTP requests.
*
* @author Craig R. McClanahan
- * @version $Id: StandardContextValve.java 1157904 2011-08-15 16:36:27Z markt $
+ * @version $Id: StandardContextValve.java 1164481 2011-09-02 11:30:56Z markt $
*/
final class StandardContextValve extends ValveBase {
- private static final Log log = LogFactory.getLog(StandardHostValve.class);
-
//------------------------------------------------------ Constructor
public StandardContextValve() {
super(true);
@@ -124,7 +114,7 @@ final class StandardContextValve extends ValveBase {
|| (requestPathMB.equalsIgnoreCase("/META-INF"))
|| (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))
|| (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
- error(request, response, HttpServletResponse.SC_NOT_FOUND);
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
@@ -151,13 +141,13 @@ final class StandardContextValve extends ValveBase {
// Select the Wrapper to be used for this Request
Wrapper wrapper = request.getWrapper();
if (wrapper == null) {
- error(request, response, HttpServletResponse.SC_NOT_FOUND);
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
} else if (wrapper.isUnavailable()) {
// May be as a result of a reload, try and find the new wrapper
wrapper = (Wrapper) container.findChild(wrapper.getName());
if (wrapper == null) {
- error(request, response, HttpServletResponse.SC_NOT_FOUND);
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
}
@@ -169,47 +159,14 @@ final class StandardContextValve extends ValveBase {
container.getLogger().error(sm.getString(
"standardContextValve.acknowledgeException"), ioe);
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ioe);
- error(request, response,
- HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
- // Don't fire listeners during async processing
- // If a request init listener throws an exception, the request is
- // aborted
- boolean asyncAtStart = request.isAsync();
- if (asyncAtStart || context.fireRequestInitEvent(request)) {
- if (request.isAsyncSupported()) {
- request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported());
- }
- wrapper.getPipeline().getFirst().invoke(request, response);
-
- // If the request was async at the start and an error occurred then
- // the async error handling will kick-in and that will fire the
- // request destroyed event *after* the error handling has taken
- // place
- if (!(request.isAsync() || (asyncAtStart && request.getAttribute(
- RequestDispatcher.ERROR_EXCEPTION) != null))) {
- // Protect against NPEs if context was destroyed during a long
- // running request.
- StandardContext c = context;
- if (c != null && c.getState().isAvailable()) {
- // Error page processing
- response.setSuspended(false);
-
- Throwable t = (Throwable) request.getAttribute(
- RequestDispatcher.ERROR_EXCEPTION);
-
- if (t != null) {
- throwable(request, response, t);
- } else {
- status(request, response);
- }
-
- context.fireRequestDestroyEvent(request);
- }
- }
+ if (request.isAsyncSupported()) {
+ request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported());
}
+ wrapper.getPipeline().getFirst().invoke(request, response);
}
@@ -233,273 +190,5 @@ final class StandardContextValve extends ValveBase {
Wrapper wrapper = request.getWrapper();
wrapper.getPipeline().getFirst().event(request, response, event);
-
- // Error page processing
- response.setSuspended(false);
-
- Throwable t = (Throwable) request.getAttribute(
- RequestDispatcher.ERROR_EXCEPTION);
-
- if (t != null) {
- throwable(request, response, t);
- } else {
- status(request, response);
- }
- }
-
-
- // -------------------------------------------------------- Private Methods
-
-
- /**
- * Report an error for the specified resource.
- *
- * @param response The response we are creating
- */
- private void error(Request request, Response response, int status) {
-
- context.fireRequestInitEvent(request);
-
- try {
- response.sendError(status);
- } catch (IllegalStateException e) {
- // Ignore
- } catch (IOException e) {
- // Ignore
- }
-
- response.setSuspended(false);
- status(request, response);
-
- context.fireRequestDestroyEvent(request);
- }
-
-
- /**
- * Handle the HTTP status code (and corresponding message) generated
- * while processing the specified Request to produce the specified
- * Response. Any exceptions that occur during generation of the error
- * report are logged and swallowed.
- *
- * @param request The request being processed
- * @param response The response being generated
- */
- private void status(Request request, Response response) {
-
- int statusCode = response.getStatus();
-
- // Handle a custom error page for this status code
- Context context = request.getContext();
- if (context == null)
- return;
-
- /* Only look for error pages when isError() is set.
- * isError() is set when response.sendError() is invoked. This
- * allows custom error pages without relying on default from
- * web.xml.
- */
- if (!response.isError())
- return;
-
- ErrorPage errorPage = context.findErrorPage(statusCode);
- if (errorPage != null) {
- response.setAppCommitted(false);
- request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,
- Integer.valueOf(statusCode));
-
- String message = response.getMessage();
- if (message == null)
- message = "";
- request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message);
- request.setAttribute
- (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
- errorPage.getLocation());
- request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
- DispatcherType.ERROR);
-
-
- Wrapper wrapper = request.getWrapper();
- if (wrapper != null)
- request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,
- wrapper.getName());
- request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,
- request.getRequestURI());
- if (custom(request, response, errorPage)) {
- try {
- response.flushBuffer();
- } catch (ClientAbortException e) {
- // Ignore
- } catch (IOException e) {
- container.getLogger().warn("Exception Processing " + errorPage, e);
- }
- }
- }
- }
-
-
- /**
- * Handle the specified Throwable encountered while processing
- * the specified Request to produce the specified Response. Any
- * exceptions that occur during generation of the exception report are
- * logged and swallowed.
- *
- * @param request The request being processed
- * @param response The response being generated
- * @param throwable The exception that occurred (which possibly wraps
- * a root cause exception
- */
- private void throwable(Request request, Response response,
- Throwable throwable) {
- Context context = request.getContext();
- if (context == null)
- return;
-
- Throwable realError = throwable;
-
- if (realError instanceof ServletException) {
- realError = ((ServletException) realError).getRootCause();
- if (realError == null) {
- realError = throwable;
- }
- }
-
- // If this is an aborted request from a client just log it and return
- if (realError instanceof ClientAbortException ) {
- if (log.isDebugEnabled()) {
- log.debug
- (sm.getString("standardHost.clientAbort",
- realError.getCause().getMessage()));
- }
- return;
- }
-
- ErrorPage errorPage = findErrorPage(context, throwable);
- if ((errorPage == null) && (realError != throwable)) {
- errorPage = findErrorPage(context, realError);
- }
-
- if (errorPage != null) {
- response.setAppCommitted(false);
- request.setAttribute
- (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
- errorPage.getLocation());
- request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
- DispatcherType.ERROR);
- request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,
- new Integer(HttpServletResponse.SC_INTERNAL_SERVER_ERROR));
- request.setAttribute(RequestDispatcher.ERROR_MESSAGE,
- throwable.getMessage());
- request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,
- realError);
- Wrapper wrapper = request.getWrapper();
- if (wrapper != null)
- request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,
- wrapper.getName());
- request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,
- request.getRequestURI());
- request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,
- realError.getClass());
- if (custom(request, response, errorPage)) {
- try {
- response.flushBuffer();
- } catch (IOException e) {
- container.getLogger().warn("Exception Processing " + errorPage, e);
- }
- }
- } else {
- // A custom error-page has not been defined for the exception
- // that was thrown during request processing. Check if an
- // error-page for error code 500 was specified and if so,
- // send that page back as the response.
- response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- // The response is an error
- response.setError();
-
- status(request, response);
- }
- }
-
-
- /**
- * Handle an HTTP status code or Java exception by forwarding control
- * to the location included in the specified errorPage object. It is
- * assumed that the caller has already recorded any request attributes
- * that are to be forwarded to this page. Return <code>true</code> if
- * we successfully utilized the specified error page location, or
- * <code>false</code> if the default error report should be rendered.
- *
- * @param request The request being processed
- * @param response The response being generated
- * @param errorPage The errorPage directive we are obeying
- */
- private boolean custom(Request request, Response response,
- ErrorPage errorPage) {
-
- if (container.getLogger().isDebugEnabled())
- container.getLogger().debug("Processing " + errorPage);
-
- request.setPathInfo(errorPage.getLocation());
-
- try {
- // Forward control to the specified location
- ServletContext servletContext =
- request.getContext().getServletContext();
- RequestDispatcher rd =
- servletContext.getRequestDispatcher(errorPage.getLocation());
-
- if (response.isCommitted()) {
- // Response is committed - including the error page is the
- // best we can do
- rd.include(request.getRequest(), response.getResponse());
- } else {
- // Reset the response (keeping the real error code and message)
- response.resetBuffer(true);
-
- rd.forward(request.getRequest(), response.getResponse());
-
- // If we forward, the response is suspended again
- response.setSuspended(false);
- }
-
- // Indicate that we have successfully processed this custom page
- return (true);
-
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- // Report our failure to process this custom page
- container.getLogger().error("Exception Processing " + errorPage, t);
- return (false);
-
- }
- }
-
-
- /**
- * Find and return the ErrorPage instance for the specified exception's
- * class, or an ErrorPage instance for the closest superclass for which
- * there is such a definition. If no associated ErrorPage instance is
- * found, return <code>null</code>.
- *
- * @param context The Context in which to search
- * @param exception The exception for which to find an ErrorPage
- */
- private static ErrorPage findErrorPage
- (Context context, Throwable exception) {
-
- if (exception == null)
- return (null);
- Class<?> clazz = exception.getClass();
- String name = clazz.getName();
- while (!Object.class.equals(clazz)) {
- ErrorPage errorPage = context.findErrorPage(name);
- if (errorPage != null)
- return (errorPage);
- clazz = clazz.getSuperclass();
- if (clazz == null)
- break;
- name = clazz.getName();
- }
- return (null);
-
}
}
diff --git a/java/org/apache/catalina/core/StandardHostValve.java b/java/org/apache/catalina/core/StandardHostValve.java
index 1b58672..5d1a18c 100644
--- a/java/org/apache/catalina/core/StandardHostValve.java
+++ b/java/org/apache/catalina/core/StandardHostValve.java
@@ -23,15 +23,24 @@ import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedAction;
+import javax.servlet.DispatcherType;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
+import org.apache.catalina.Wrapper;
import org.apache.catalina.comet.CometEvent;
+import org.apache.catalina.connector.ClientAbortException;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
+import org.apache.catalina.deploy.ErrorPage;
import org.apache.catalina.valves.ValveBase;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.res.StringManager;
@@ -44,11 +53,13 @@ import org.apache.tomcat.util.res.StringManager;
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Id: StandardHostValve.java 1157904 2011-08-15 16:36:27Z markt $
+ * @version $Id: StandardHostValve.java 1164482 2011-09-02 11:33:05Z markt $
*/
final class StandardHostValve extends ValveBase {
+ private static final Log log = LogFactory.getLog(StandardHostValve.class);
+
protected static final boolean STRICT_SERVLET_COMPLIANCE;
protected static final boolean ACCESS_SESSION;
@@ -146,9 +157,46 @@ final class StandardHostValve extends ValveBase {
request.setAsyncSupported(context.getPipeline().isAsyncSupported());
}
-
- // Ask this Context to process this request
- context.getPipeline().getFirst().invoke(request, response);
+ // Don't fire listeners during async processing
+ // If a request init listener throws an exception, the request is
+ // aborted
+ boolean asyncAtStart = request.isAsync();
+ if (asyncAtStart || context.fireRequestInitEvent(request)) {
+
+ // Ask this Context to process this request
+ try {
+ context.getPipeline().getFirst().invoke(request, response);
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
+ throwable(request, response, t);
+ }
+
+ // If the request was async at the start and an error occurred then
+ // the async error handling will kick-in and that will fire the
+ // request destroyed event *after* the error handling has taken
+ // place
+ if (!(request.isAsync() || (asyncAtStart && request.getAttribute(
+ RequestDispatcher.ERROR_EXCEPTION) != null))) {
+ // Protect against NPEs if context was destroyed during a long
+ // running request.
+ if (context.getState().isAvailable()) {
+ // Error page processing
+ response.setSuspended(false);
+
+ Throwable t = (Throwable) request.getAttribute(
+ RequestDispatcher.ERROR_EXCEPTION);
+
+ if (t != null) {
+ throwable(request, response, t);
+ } else {
+ status(request, response);
+ }
+
+ context.fireRequestDestroyEvent(request);
+ }
+ }
+ }
// Access a session (if present) to update last accessed time, based on a
// strict interpretation of the specification
@@ -165,7 +213,6 @@ final class StandardHostValve extends ValveBase {
Thread.currentThread().setContextClassLoader
(StandardHostValve.class.getClassLoader());
}
-
}
@@ -197,6 +244,19 @@ final class StandardHostValve extends ValveBase {
// Ask this Context to process this request
context.getPipeline().getFirst().event(request, response, event);
+
+ // Error page processing
+ response.setSuspended(false);
+
+ Throwable t = (Throwable) request.getAttribute(
+ RequestDispatcher.ERROR_EXCEPTION);
+
+ if (t != null) {
+ throwable(request, response, t);
+ } else {
+ status(request, response);
+ }
+
// Access a session (if present) to update last accessed time, based on a
// strict interpretation of the specification
if (ACCESS_SESSION) {
@@ -210,6 +270,237 @@ final class StandardHostValve extends ValveBase {
}
+ // -------------------------------------------------------- Private Methods
+
+ /**
+ * Handle the HTTP status code (and corresponding message) generated
+ * while processing the specified Request to produce the specified
+ * Response. Any exceptions that occur during generation of the error
+ * report are logged and swallowed.
+ *
+ * @param request The request being processed
+ * @param response The response being generated
+ */
+ private void status(Request request, Response response) {
+
+ int statusCode = response.getStatus();
+
+ // Handle a custom error page for this status code
+ Context context = request.getContext();
+ if (context == null)
+ return;
+
+ /* Only look for error pages when isError() is set.
+ * isError() is set when response.sendError() is invoked. This
+ * allows custom error pages without relying on default from
+ * web.xml.
+ */
+ if (!response.isError())
+ return;
+
+ ErrorPage errorPage = context.findErrorPage(statusCode);
+ if (errorPage != null) {
+ response.setAppCommitted(false);
+ request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,
+ Integer.valueOf(statusCode));
+
+ String message = response.getMessage();
+ if (message == null)
+ message = "";
+ request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message);
+ request.setAttribute
+ (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
+ errorPage.getLocation());
+ request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
+ DispatcherType.ERROR);
+
+
+ Wrapper wrapper = request.getWrapper();
+ if (wrapper != null)
+ request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,
+ wrapper.getName());
+ request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,
+ request.getRequestURI());
+ if (custom(request, response, errorPage)) {
+ try {
+ response.flushBuffer();
+ } catch (ClientAbortException e) {
+ // Ignore
+ } catch (IOException e) {
+ container.getLogger().warn("Exception Processing " + errorPage, e);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Handle the specified Throwable encountered while processing
+ * the specified Request to produce the specified Response. Any
+ * exceptions that occur during generation of the exception report are
+ * logged and swallowed.
+ *
+ * @param request The request being processed
+ * @param response The response being generated
+ * @param throwable The exception that occurred (which possibly wraps
+ * a root cause exception
+ */
+ private void throwable(Request request, Response response,
+ Throwable throwable) {
+ Context context = request.getContext();
+ if (context == null)
+ return;
+
+ Throwable realError = throwable;
+
+ if (realError instanceof ServletException) {
+ realError = ((ServletException) realError).getRootCause();
+ if (realError == null) {
+ realError = throwable;
+ }
+ }
+
+ // If this is an aborted request from a client just log it and return
+ if (realError instanceof ClientAbortException ) {
+ if (log.isDebugEnabled()) {
+ log.debug
+ (sm.getString("standardHost.clientAbort",
+ realError.getCause().getMessage()));
+ }
+ return;
+ }
+
+ ErrorPage errorPage = findErrorPage(context, throwable);
+ if ((errorPage == null) && (realError != throwable)) {
+ errorPage = findErrorPage(context, realError);
+ }
+
+ if (errorPage != null) {
+ response.setAppCommitted(false);
+ request.setAttribute
+ (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
+ errorPage.getLocation());
+ request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
+ DispatcherType.ERROR);
+ request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,
+ new Integer(HttpServletResponse.SC_INTERNAL_SERVER_ERROR));
+ request.setAttribute(RequestDispatcher.ERROR_MESSAGE,
+ throwable.getMessage());
+ request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,
+ realError);
+ Wrapper wrapper = request.getWrapper();
+ if (wrapper != null)
+ request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,
+ wrapper.getName());
+ request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,
+ request.getRequestURI());
+ request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,
+ realError.getClass());
+ if (custom(request, response, errorPage)) {
+ try {
+ response.flushBuffer();
+ } catch (IOException e) {
+ container.getLogger().warn("Exception Processing " + errorPage, e);
+ }
+ }
+ } else {
+ // A custom error-page has not been defined for the exception
+ // that was thrown during request processing. Check if an
+ // error-page for error code 500 was specified and if so,
+ // send that page back as the response.
+ response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ // The response is an error
+ response.setError();
+
+ status(request, response);
+ }
+ }
+
+
+ /**
+ * Handle an HTTP status code or Java exception by forwarding control
+ * to the location included in the specified errorPage object. It is
+ * assumed that the caller has already recorded any request attributes
+ * that are to be forwarded to this page. Return <code>true</code> if
+ * we successfully utilized the specified error page location, or
+ * <code>false</code> if the default error report should be rendered.
+ *
+ * @param request The request being processed
+ * @param response The response being generated
+ * @param errorPage The errorPage directive we are obeying
+ */
+ private boolean custom(Request request, Response response,
+ ErrorPage errorPage) {
+
+ if (container.getLogger().isDebugEnabled())
+ container.getLogger().debug("Processing " + errorPage);
+
+ request.setPathInfo(errorPage.getLocation());
+
+ try {
+ // Forward control to the specified location
+ ServletContext servletContext =
+ request.getContext().getServletContext();
+ RequestDispatcher rd =
+ servletContext.getRequestDispatcher(errorPage.getLocation());
+
+ if (response.isCommitted()) {
+ // Response is committed - including the error page is the
+ // best we can do
+ rd.include(request.getRequest(), response.getResponse());
+ } else {
+ // Reset the response (keeping the real error code and message)
+ response.resetBuffer(true);
+
+ rd.forward(request.getRequest(), response.getResponse());
+
+ // If we forward, the response is suspended again
+ response.setSuspended(false);
+ }
+
+ // Indicate that we have successfully processed this custom page
+ return (true);
+
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ // Report our failure to process this custom page
+ container.getLogger().error("Exception Processing " + errorPage, t);
+ return (false);
+
+ }
+ }
+
+
+ /**
+ * Find and return the ErrorPage instance for the specified exception's
+ * class, or an ErrorPage instance for the closest superclass for which
+ * there is such a definition. If no associated ErrorPage instance is
+ * found, return <code>null</code>.
+ *
+ * @param context The Context in which to search
+ * @param exception The exception for which to find an ErrorPage
+ */
+ private static ErrorPage findErrorPage
+ (Context context, Throwable exception) {
+
+ if (exception == null)
+ return (null);
+ Class<?> clazz = exception.getClass();
+ String name = clazz.getName();
+ while (!Object.class.equals(clazz)) {
+ ErrorPage errorPage = context.findErrorPage(name);
+ if (errorPage != null)
+ return (errorPage);
+ clazz = clazz.getSuperclass();
+ if (clazz == null)
+ break;
+ name = clazz.getName();
+ }
+ return (null);
+
+ }
+
+
private static class PrivilegedSetTccl implements PrivilegedAction<Void> {
private ClassLoader cl;
diff --git a/java/org/apache/catalina/ha/session/BackupManager.java b/java/org/apache/catalina/ha/session/BackupManager.java
index 2d0a53a..26c3d7d 100644
--- a/java/org/apache/catalina/ha/session/BackupManager.java
+++ b/java/org/apache/catalina/ha/session/BackupManager.java
@@ -24,7 +24,6 @@ import org.apache.catalina.DistributedManager;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Session;
-import org.apache.catalina.ha.CatalinaCluster;
import org.apache.catalina.ha.ClusterManager;
import org.apache.catalina.ha.ClusterMessage;
import org.apache.catalina.tribes.Channel;
@@ -53,15 +52,6 @@ public class BackupManager extends ClusterManagerBase
protected String name;
/**
- * A reference to the cluster
- */
- protected CatalinaCluster cluster;
-
- /**
- * Should listeners be notified?
- */
- private boolean notifyListenersOnReplication;
- /**
*
*/
private int mapSendOptions = Channel.SEND_OPTIONS_SYNCHRONIZED_ACK|Channel.SEND_OPTIONS_USE_ACK;
@@ -93,13 +83,6 @@ public class BackupManager extends ClusterManagerBase
mExpireSessionsOnShutdown = expireSessionsOnShutdown;
}
- @Override
- public void setCluster(CatalinaCluster cluster) {
- if(log.isDebugEnabled())
- log.debug("Cluster associated with BackupManager");
- this.cluster = cluster;
- }
-
public boolean getExpireSessionsOnShutdown()
{
return mExpireSessionsOnShutdown;
@@ -160,9 +143,8 @@ public class BackupManager extends ClusterManagerBase
try {
cluster.registerManager(this);
- CatalinaCluster catclust = cluster;
LazyReplicatedMap map = new LazyReplicatedMap(this,
- catclust.getChannel(),
+ cluster.getChannel(),
rpcTimeout,
getMapName(),
getClassLoaders());
@@ -176,8 +158,7 @@ public class BackupManager extends ClusterManagerBase
}
public String getMapName() {
- CatalinaCluster catclust = cluster;
- String name = catclust.getManagerName(getName(),this)+"-"+"map";
+ String name = cluster.getManagerName(getName(),this)+"-"+"map";
if ( log.isDebugEnabled() ) log.debug("Backup manager, Setting map name to:"+name);
return name;
}
@@ -219,26 +200,11 @@ public class BackupManager extends ClusterManagerBase
public void setName(String name) {
this.name = name;
}
- @Override
- public boolean isNotifyListenersOnReplication() {
- return notifyListenersOnReplication;
- }
- public void setNotifyListenersOnReplication(boolean notifyListenersOnReplication) {
- this.notifyListenersOnReplication = notifyListenersOnReplication;
- }
public void setMapSendOptions(int mapSendOptions) {
this.mapSendOptions = mapSendOptions;
}
- /*
- * @see org.apache.catalina.ha.ClusterManager#getCluster()
- */
- @Override
- public CatalinaCluster getCluster() {
- return cluster;
- }
-
public int getMapSendOptions() {
return mapSendOptions;
}
@@ -259,12 +225,9 @@ public class BackupManager extends ClusterManagerBase
@Override
public ClusterManager cloneFromTemplate() {
BackupManager result = new BackupManager();
+ clone(result);
result.mExpireSessionsOnShutdown = mExpireSessionsOnShutdown;
- result.name = "Clone-from-"+name;
- result.cluster = cluster;
- result.notifyListenersOnReplication = notifyListenersOnReplication;
result.mapSendOptions = mapSendOptions;
- result.maxActiveSessions = maxActiveSessions;
result.rpcTimeout = rpcTimeout;
return result;
}
diff --git a/java/org/apache/catalina/ha/session/ClusterManagerBase.java b/java/org/apache/catalina/ha/session/ClusterManagerBase.java
index dcdec13..ff5b08d 100644
--- a/java/org/apache/catalina/ha/session/ClusterManagerBase.java
+++ b/java/org/apache/catalina/ha/session/ClusterManagerBase.java
@@ -19,9 +19,11 @@ package org.apache.catalina.ha.session;
import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.util.regex.Pattern;
import org.apache.catalina.Container;
import org.apache.catalina.Loader;
+import org.apache.catalina.ha.CatalinaCluster;
import org.apache.catalina.ha.ClusterManager;
import org.apache.catalina.session.ManagerBase;
import org.apache.catalina.tribes.io.ReplicationStream;
@@ -29,12 +31,101 @@ import org.apache.catalina.tribes.io.ReplicationStream;
/**
*
* @author Filip Hanik
- * @version $Id: ClusterManagerBase.java 1026793 2010-10-24 13:25:36Z markt $
+ * @version $Id: ClusterManagerBase.java 1175159 2011-09-24 11:16:50Z rjung $
*/
public abstract class ClusterManagerBase extends ManagerBase
implements ClusterManager {
+ /**
+ * A reference to the cluster
+ */
+ protected CatalinaCluster cluster = null;
+
+ /**
+ * Should listeners be notified?
+ */
+ private boolean notifyListenersOnReplication = true;
+
+ /**
+ * The pattern used for including session attributes to
+ * replication, e.g. <code>^(userName|sessionHistory)$</code>.
+ * If not set, all session attributes will be eligible for replication.
+ */
+ private String sessionAttributeFilter = null;
+
+ /**
+ * The compiled pattern used for including session attributes to
+ * replication, e.g. <code>^(userName|sessionHistory)$</code>.
+ * If not set, all session attributes will be eligible for replication.
+ */
+ private Pattern sessionAttributePattern = null;
+
+ /*
+ * @see org.apache.catalina.ha.ClusterManager#getCluster()
+ */
+ @Override
+ public CatalinaCluster getCluster() {
+ return cluster;
+ }
+
+ @Override
+ public void setCluster(CatalinaCluster cluster) {
+ this.cluster = cluster;
+ }
+
+ @Override
+ public boolean isNotifyListenersOnReplication() {
+ return notifyListenersOnReplication;
+ }
+
+ public void setNotifyListenersOnReplication(boolean notifyListenersOnReplication) {
+ this.notifyListenersOnReplication = notifyListenersOnReplication;
+ }
+
+ /**
+ * Return the string pattern used for including session attributes
+ * to replication.
+ *
+ * @return the sessionAttributeFilter
+ */
+ public String getSessionAttributeFilter() {
+ return sessionAttributeFilter;
+ }
+
+ /**
+ * Set the pattern used for including session attributes to replication.
+ * If not set, all session attributes will be eligible for replication.
+ * <p>
+ * E.g. <code>^(userName|sessionHistory)$</code>
+ * </p>
+ *
+ * @param sessionAttributeFilter
+ * the filter name pattern to set
+ */
+ public void setSessionAttributeFilter(String sessionAttributeFilter) {
+ if (sessionAttributeFilter == null
+ || sessionAttributeFilter.trim().equals("")) {
+ this.sessionAttributeFilter = null;
+ sessionAttributePattern = null;
+ } else {
+ this.sessionAttributeFilter = sessionAttributeFilter;
+ sessionAttributePattern = Pattern.compile(sessionAttributeFilter);
+ }
+ }
+
+ /**
+ * Check whether the given session attribute should be distributed
+ *
+ * @return true if the attribute should be distributed
+ */
+ public boolean willAttributeDistribute(String name) {
+ if (sessionAttributePattern == null) {
+ return true;
+ }
+ return sessionAttributePattern.matcher(name).matches();
+ }
+
public static ClassLoader[] getClassLoaders(Container container) {
Loader loader = null;
ClassLoader classLoader = null;
@@ -88,4 +179,13 @@ public abstract class ClusterManagerBase extends ManagerBase
public void unload() {
// NOOP
}
-}
\ No newline at end of file
+
+ protected void clone(ClusterManagerBase copy) {
+ copy.setName("Clone-from-" + getName());
+ copy.setCluster(getCluster());
+ copy.maxActiveSessions = getMaxActiveSessions();
+ copy.setNotifyListenersOnReplication(isNotifyListenersOnReplication());
+ copy.setSessionAttributeFilter(getSessionAttributeFilter());
+ }
+
+}
diff --git a/java/org/apache/catalina/ha/session/DeltaManager.java b/java/org/apache/catalina/ha/session/DeltaManager.java
index 9feb1a2..868d80c 100644
--- a/java/org/apache/catalina/ha/session/DeltaManager.java
+++ b/java/org/apache/catalina/ha/session/DeltaManager.java
@@ -62,7 +62,7 @@ import org.apache.tomcat.util.res.StringManager;
* @author Craig R. McClanahan
* @author Jean-Francois Arcand
* @author Peter Rossbach
- * @version $Id: DeltaManager.java 1141441 2011-06-30 08:39:53Z kfujino $
+ * @version $Id: DeltaManager.java 1174181 2011-09-22 15:01:08Z rjung $
*/
public class DeltaManager extends ClusterManagerBase{
@@ -87,7 +87,6 @@ public class DeltaManager extends ClusterManagerBase{
*/
protected static String managerName = "DeltaManager";
protected String name = null;
- private CatalinaCluster cluster = null;
/**
* cached replication valve cluster container!
@@ -95,7 +94,6 @@ public class DeltaManager extends ClusterManagerBase{
private volatile ReplicationValve replicationValve = null ;
private boolean expireSessionsOnShutdown = false;
- private boolean notifyListenersOnReplication = true;
private boolean notifySessionListenersOnReplication = true;
private boolean notifyContainerListenersOnReplication = true;
private volatile boolean stateTransfered = false ;
@@ -412,15 +410,6 @@ public class DeltaManager extends ClusterManagerBase{
this.expireSessionsOnShutdown = expireSessionsOnShutdown;
}
- @Override
- public boolean isNotifyListenersOnReplication() {
- return notifyListenersOnReplication;
- }
-
- public void setNotifyListenersOnReplication(boolean notifyListenersOnReplication) {
- this.notifyListenersOnReplication = notifyListenersOnReplication;
- }
-
public boolean isNotifyContainerListenersOnReplication() {
return notifyContainerListenersOnReplication;
}
@@ -430,16 +419,6 @@ public class DeltaManager extends ClusterManagerBase{
this.notifyContainerListenersOnReplication = notifyContainerListenersOnReplication;
}
- @Override
- public CatalinaCluster getCluster() {
- return cluster;
- }
-
- @Override
- public void setCluster(CatalinaCluster cluster) {
- this.cluster = cluster;
- }
-
// --------------------------------------------------------- Public Methods
/**
@@ -1340,7 +1319,7 @@ public class DeltaManager extends ClusterManagerBase{
try {
session.lock();
DeltaRequest dreq = deserializeDeltaRequest(session, delta);
- dreq.execute(session, notifyListenersOnReplication);
+ dreq.execute(session, isNotifyListenersOnReplication());
session.setPrimarySession(false);
}finally {
session.unlock();
@@ -1500,12 +1479,9 @@ public class DeltaManager extends ClusterManagerBase{
@Override
public ClusterManager cloneFromTemplate() {
DeltaManager result = new DeltaManager();
- result.name = "Clone-from-"+name;
- result.cluster = cluster;
+ clone(result);
result.replicationValve = replicationValve;
- result.maxActiveSessions = maxActiveSessions;
result.expireSessionsOnShutdown = expireSessionsOnShutdown;
- result.notifyListenersOnReplication = notifyListenersOnReplication;
result.notifySessionListenersOnReplication = notifySessionListenersOnReplication;
result.notifyContainerListenersOnReplication = notifyContainerListenersOnReplication;
result.stateTransferTimeout = stateTransferTimeout;
diff --git a/java/org/apache/catalina/ha/session/DeltaSession.java b/java/org/apache/catalina/ha/session/DeltaSession.java
index 9182fd6..7e5174f 100644
--- a/java/org/apache/catalina/ha/session/DeltaSession.java
+++ b/java/org/apache/catalina/ha/session/DeltaSession.java
@@ -53,7 +53,7 @@ import org.apache.tomcat.util.res.StringManager;
* track of deltas during a request.
*
* @author Filip Hanik
- * @version $Id: DeltaSession.java 1135069 2011-06-13 09:44:33Z kfujino $
+ * @version $Id: DeltaSession.java 1175194 2011-09-24 16:32:19Z rjung $
*/
public class DeltaSession extends StandardSession implements Externalizable,ClusterSession,ReplicatedMapEntry {
@@ -559,6 +559,37 @@ public class DeltaSession extends StandardSession implements Externalizable,Clus
/**
+ * Check whether the Object can be distributed.
+ * The object is always distributable, if the cluster manager
+ * decides to never distribute it.
+ * @param name The name of the attribute to check
+ * @param value The value of the attribute to check
+ * @return true if the attribute is distributable, false otherwise
+ */
+ @Override
+ protected boolean isAttributeDistributable(String name, Object value) {
+ if (manager instanceof ClusterManagerBase &&
+ !((ClusterManagerBase)manager).willAttributeDistribute(name))
+ return true;
+ return super.isAttributeDistributable(name, value);
+ }
+
+ /**
+ * Exclude attributes from replication.
+ * @param name the attribute's name
+ * @return true if attribute should not be replicated
+ */
+ @Override
+ protected boolean exclude(String name) {
+
+ if (super.exclude(name))
+ return true;
+ if (manager instanceof ClusterManagerBase)
+ return !((ClusterManagerBase)manager).willAttributeDistribute(name);
+ return false;
+ }
+
+ /**
* Remove the object bound with the specified name from this session. If the
* session does not have an object bound with this name, this method does
* nothing.
@@ -635,6 +666,7 @@ public class DeltaSession extends StandardSession implements Externalizable,Clus
// -------------------------------------------- HttpSession Private Methods
+
/**
* Read a serialized version of this session object from the specified
* object input stream.
diff --git a/java/org/apache/catalina/loader/WebappClassLoader.java b/java/org/apache/catalina/loader/WebappClassLoader.java
index d9de6b4..e5ec41c 100644
--- a/java/org/apache/catalina/loader/WebappClassLoader.java
+++ b/java/org/apache/catalina/loader/WebappClassLoader.java
@@ -116,7 +116,7 @@ import org.apache.tomcat.util.res.StringManager;
*
* @author Remy Maucherat
* @author Craig R. McClanahan
- * @version $Id: WebappClassLoader.java 1162172 2011-08-26 17:12:33Z markt $
+ * @version $Id: WebappClassLoader.java 1163802 2011-08-31 20:35:22Z slaurent $
*/
public class WebappClassLoader
extends URLClassLoader
@@ -2898,11 +2898,22 @@ public class WebappClassLoader
// Note : Not getting an exception here means the resource was
// found
- entry = findResourceInternal(files[i], path);
ResourceAttributes attributes =
(ResourceAttributes) resources.getAttributes(fullPath);
contentLength = (int) attributes.getContentLength();
+ String canonicalPath = attributes.getCanonicalPath();
+ if (canonicalPath != null) {
+ // we create the ResourceEntry based on the information returned
+ // by the DirContext rather than just using the path to the
+ // repository. This allows to have smart DirContext implementations
+ // that "virtualize" the docbase (e.g. Eclipse WTP)
+ entry = findResourceInternal(new File(canonicalPath), "");
+ } else {
+ // probably a resource not in the filesystem (e.g. in a
+ // packaged war)
+ entry = findResourceInternal(files[i], path);
+ }
entry.lastModified = attributes.getLastModified();
if (resource != null) {
diff --git a/java/org/apache/catalina/loader/WebappLoader.java b/java/org/apache/catalina/loader/WebappLoader.java
index 891700d..3c11f0f 100644
--- a/java/org/apache/catalina/loader/WebappLoader.java
+++ b/java/org/apache/catalina/loader/WebappLoader.java
@@ -77,7 +77,7 @@ import org.apache.tomcat.util.res.StringManager;
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Id: WebappLoader.java 1162172 2011-08-26 17:12:33Z markt $
+ * @version $Id: WebappLoader.java 1174921 2011-09-23 17:51:56Z kkolinko $
*/
public class WebappLoader extends LifecycleMBeanBase
@@ -1036,7 +1036,6 @@ public class WebappLoader extends LifecycleMBeanBase
// Assemble the class path information from our class loader chain
ClassLoader loader = getClassLoader();
- int layers = 0;
int n = 0;
while (loader != null) {
if (!(loader instanceof URLClassLoader)) {
@@ -1073,7 +1072,6 @@ public class WebappLoader extends LifecycleMBeanBase
n++;
}
loader = loader.getParent();
- layers++;
}
this.classpath=classpath.toString();
diff --git a/java/org/apache/catalina/security/SecurityClassLoad.java b/java/org/apache/catalina/security/SecurityClassLoad.java
index 28d64e2..549a1e8 100644
--- a/java/org/apache/catalina/security/SecurityClassLoad.java
+++ b/java/org/apache/catalina/security/SecurityClassLoad.java
@@ -25,7 +25,7 @@ package org.apache.catalina.security;
*
* @author Glenn L. Nielsen
* @author Jean-Francois Arcand
- * @version $Id: SecurityClassLoad.java 1144031 2011-07-07 20:53:51Z markt $
+ * @version $Id: SecurityClassLoad.java 1174064 2011-09-22 11:23:57Z markt $
*/
public final class SecurityClassLoad {
@@ -87,10 +87,10 @@ public final class SecurityClassLoad {
"DefaultInstanceManager$3");
loader.loadClass
(basePackage +
- "DefaultInstanceManager$4");
+ "DefaultInstanceManager$AnnotationCacheEntry");
loader.loadClass
(basePackage +
- "DefaultInstanceManager$5");
+ "DefaultInstanceManager$AnnotationCacheEntryType");
loader.loadClass
(basePackage +
"ApplicationHttpRequest$AttributeNamesEnumerator");
diff --git a/java/org/apache/catalina/servlets/DefaultServlet.java b/java/org/apache/catalina/servlets/DefaultServlet.java
index 9ab8558..96be826 100644
--- a/java/org/apache/catalina/servlets/DefaultServlet.java
+++ b/java/org/apache/catalina/servlets/DefaultServlet.java
@@ -114,7 +114,7 @@ import org.apache.tomcat.util.res.StringManager;
* </p>
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Id: DefaultServlet.java 1145380 2011-07-11 22:08:06Z markt $
+ * @version $Id: DefaultServlet.java 1174914 2011-09-23 17:47:08Z kkolinko $
*/
public class DefaultServlet
@@ -1379,13 +1379,6 @@ public class DefaultServlet
String name = cacheEntry.name;
- // Number of characters to trim from the beginnings of filenames
- int trim = name.length();
- if (!name.endsWith("/"))
- trim += 1;
- if (name.equals("/"))
- trim = 1;
-
// Prepare a writer to a buffered area
ByteArrayOutputStream stream = new ByteArrayOutputStream();
OutputStreamWriter osWriter = new OutputStreamWriter(stream, "UTF8");
diff --git a/java/org/apache/catalina/session/ManagerBase.java b/java/org/apache/catalina/session/ManagerBase.java
index 6656303..99fbf93 100644
--- a/java/org/apache/catalina/session/ManagerBase.java
+++ b/java/org/apache/catalina/session/ManagerBase.java
@@ -55,7 +55,7 @@ import org.apache.tomcat.util.res.StringManager;
* be subclassed to create more sophisticated Manager implementations.
*
* @author Craig R. McClanahan
- * @version $Id: ManagerBase.java 1097899 2011-04-29 17:49:37Z markt $
+ * @version $Id: ManagerBase.java 1175277 2011-09-24 22:37:12Z kkolinko $
*/
public abstract class ManagerBase extends LifecycleMBeanBase
@@ -86,6 +86,12 @@ public abstract class ManagerBase extends LifecycleMBeanBase
/**
+ * The descriptive name of this Manager implementation (for logging).
+ */
+ private static final String name = "ManagerBase";
+
+
+ /**
* The default maximum inactive interval for Sessions created by
* this Manager.
*/
@@ -99,12 +105,6 @@ public abstract class ManagerBase extends LifecycleMBeanBase
/**
- * The descriptive name of this Manager implementation (for logging).
- */
- protected static String name = "ManagerBase";
-
-
- /**
* The Java class name of the secure random number generator class to be
* used when generating session identifiers. The random number generator
* class must be self-seeding and have a zero-argument constructor. If not
diff --git a/java/org/apache/catalina/session/StandardSession.java b/java/org/apache/catalina/session/StandardSession.java
index c5b4388..3b5935b 100644
--- a/java/org/apache/catalina/session/StandardSession.java
+++ b/java/org/apache/catalina/session/StandardSession.java
@@ -77,7 +77,7 @@ import org.apache.tomcat.util.res.StringManager;
* @author Craig R. McClanahan
* @author Sean Legassick
* @author <a href="mailto:jon at latchkey.com">Jon S. Stevens</a>
- * @version $Id: StandardSession.java 1094069 2011-04-16 22:25:28Z markt $
+ * @version $Id: StandardSession.java 1172259 2011-09-18 11:44:45Z rjung $
*/
public class StandardSession implements HttpSession, Session, Serializable {
@@ -1449,7 +1449,7 @@ public class StandardSession implements HttpSession, Session, Serializable {
throw new IllegalStateException(sm.getString(
"standardSession.setAttribute.ise", getIdInternal()));
if ((manager != null) && manager.getDistributable() &&
- !(value instanceof Serializable))
+ !isAttributeDistributable(name, value))
throw new IllegalArgumentException
(sm.getString("standardSession.setAttribute.iae", name));
// Construct an event with the new value
@@ -1558,6 +1558,19 @@ public class StandardSession implements HttpSession, Session, Serializable {
return (this.isValid || this.expiring);
}
+ /**
+ * Check whether the Object can be distributed. This implementation
+ * simply checks for serializability. Derived classes might use other
+ * distribution technology not based on serialization and can extend
+ * this check.
+ * @param name The name of the attribute to check
+ * @param value The value of the attribute to check
+ * @return true if the attribute is distributable, false otherwise
+ */
+ protected boolean isAttributeDistributable(String name, Object value) {
+ return value instanceof Serializable;
+ }
+
/**
* Read a serialized version of this session object from the specified
@@ -1694,7 +1707,7 @@ public class StandardSession implements HttpSession, Session, Serializable {
/**
- * Exclude attribute that cannot be serialized.
+ * Exclude standard attributes that cannot be serialized.
* @param name the attribute's name
*/
protected boolean exclude(String name){
diff --git a/java/org/apache/catalina/startup/Embedded.java b/java/org/apache/catalina/startup/Embedded.java
index 2d96973..34d6001 100644
--- a/java/org/apache/catalina/startup/Embedded.java
+++ b/java/org/apache/catalina/startup/Embedded.java
@@ -98,7 +98,7 @@ import org.apache.tomcat.util.res.StringManager;
* of how Tomcat is set up and launched as an Embedded application.
*
* @author Craig R. McClanahan
- * @version $Id: Embedded.java 1134042 2011-06-09 19:04:37Z markt $
+ * @version $Id: Embedded.java 1175897 2011-09-26 15:01:21Z markt $
*
* @deprecated Use {@link Tomcat} instead.
*/
@@ -160,7 +160,7 @@ public class Embedded extends StandardService {
/**
* Custom mappings of login methods to authenticators
*/
- protected HashMap<String,Authenticator> authenticators;
+ protected volatile HashMap<String,Authenticator> authenticators;
/**
diff --git a/java/org/apache/catalina/startup/ExpandWar.java b/java/org/apache/catalina/startup/ExpandWar.java
index eb9504e..5175004 100644
--- a/java/org/apache/catalina/startup/ExpandWar.java
+++ b/java/org/apache/catalina/startup/ExpandWar.java
@@ -30,6 +30,7 @@ import java.nio.channels.FileChannel;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
+import java.util.zip.ZipException;
import org.apache.catalina.Globals;
import org.apache.catalina.Host;
@@ -44,7 +45,7 @@ import org.apache.tomcat.util.res.StringManager;
* @author Craig R. McClanahan
* @author Remy Maucherat
* @author Glenn L. Nielsen
- * @version $Revision: 1162172 $
+ * @version $Revision: 1173343 $
*/
public class ExpandWar {
@@ -137,6 +138,9 @@ public class ExpandWar {
}
input = jarFile.getInputStream(jarEntry);
+ if(null == input)
+ throw new ZipException(sm.getString("expandWar.missingJarEntry", jarEntry.getName()));
+
// Bugzilla 33636
expand(input, expandedFile);
long lastModified = jarEntry.getTime();
diff --git a/java/org/apache/catalina/startup/LocalStrings.properties b/java/org/apache/catalina/startup/LocalStrings.properties
index b1f0e8a..2208705 100644
--- a/java/org/apache/catalina/startup/LocalStrings.properties
+++ b/java/org/apache/catalina/startup/LocalStrings.properties
@@ -73,6 +73,7 @@ expandWar.copy=Error copying {0} to {1}
expandWar.createFailed=Unable to create the directory [{0}]
expandWar.deleteFailed=[{0}] could not be completely deleted. The presence of the remaining files may cause problems
expandWar.illegalPath=The archive [{0}] is malformed and will be ignored: an entry contains an illegal path [{1}] which was not expanded to [{2}] since that is outside of the defined docBase [{3}]
+expandWar.missingJarEntry=Cannot get input stream for JarEntry "{0}" - broken WAR file?
hostConfig.appBase=Application base [{1}] for host [{0}] does not exist or is not a directory. deployOnStartUp and autoDeploy have been set to false to prevent deployment errors. Other errors may still occur.
hostConfig.canonicalizing=Error delete redeploy resources from context [{0}]
hostConfig.cce=Lifecycle event data object {0} is not a Host
diff --git a/java/org/apache/catalina/valves/SSLValve.java b/java/org/apache/catalina/valves/SSLValve.java
index 4ef7d31..84d8cc5 100644
--- a/java/org/apache/catalina/valves/SSLValve.java
+++ b/java/org/apache/catalina/valves/SSLValve.java
@@ -125,6 +125,7 @@ public class SSLValve extends ValveBase {
strcert0 = mygetHeader(request, "ssl_session_id");
if (strcert0 != null) {
request.setAttribute(Globals.SSL_SESSION_ID_ATTR, strcert0);
+ request.setAttribute(Globals.SSL_SESSION_ID_TOMCAT_ATTR, strcert0);
}
strcert0 = mygetHeader(request, "ssl_cipher_usekeysize");
if (strcert0 != null) {
diff --git a/java/org/apache/catalina/valves/StuckThreadDetectionValve.java b/java/org/apache/catalina/valves/StuckThreadDetectionValve.java
index 911da42..b0c64ea 100644
--- a/java/org/apache/catalina/valves/StuckThreadDetectionValve.java
+++ b/java/org/apache/catalina/valves/StuckThreadDetectionValve.java
@@ -177,7 +177,7 @@ public class StuckThreadDetectionValve extends ValveBase {
// Keeping a reference to the thread object here does not prevent
// GC'ing, as the reference is removed from the Map in the finally clause
- Long key = new Long(Thread.currentThread().getId());
+ Long key = Long.valueOf(Thread.currentThread().getId());
StringBuffer requestUrl = request.getRequestURL();
if(request.getQueryString()!=null) {
requestUrl.append("?");
@@ -240,7 +240,7 @@ public class StuckThreadDetectionValve extends ValveBase {
return result;
}
- private class MonitoredThread {
+ private static class MonitoredThread {
/**
* Reference to the thread to get a stack trace from background task
@@ -288,7 +288,7 @@ public class StuckThreadDetectionValve extends ValveBase {
}
}
- private class CompletedStuckThread {
+ private static class CompletedStuckThread {
private String threadName;
private long totalActiveTime;
diff --git a/java/org/apache/coyote/AsyncStateMachine.java b/java/org/apache/coyote/AsyncStateMachine.java
index 1160f4a..00af3a3 100644
--- a/java/org/apache/coyote/AsyncStateMachine.java
+++ b/java/org/apache/coyote/AsyncStateMachine.java
@@ -325,7 +325,7 @@ public class AsyncStateMachine {
}
- public void recycle() {
+ public synchronized void recycle() {
asyncCtxt = null;
state = AsyncState.DISPATCHED;
}
diff --git a/java/org/apache/coyote/ajp/AbstractAjpProcessor.java b/java/org/apache/coyote/ajp/AbstractAjpProcessor.java
index fceefdd..056c6ca 100644
--- a/java/org/apache/coyote/ajp/AbstractAjpProcessor.java
+++ b/java/org/apache/coyote/ajp/AbstractAjpProcessor.java
@@ -759,7 +759,6 @@ public abstract class AbstractAjpProcessor<S> extends AbstractProcessor<S> {
secret = true;
if (!tmpMB.equals(requiredSecret)) {
response.setStatus(403);
- adapter.log(request, response, 0);
error = true;
}
}
@@ -776,7 +775,6 @@ public abstract class AbstractAjpProcessor<S> extends AbstractProcessor<S> {
// Check if secret was submitted if required
if ((requiredSecret != null) && !secret) {
response.setStatus(403);
- adapter.log(request, response, 0);
error = true;
}
@@ -810,6 +808,9 @@ public abstract class AbstractAjpProcessor<S> extends AbstractProcessor<S> {
MessageBytes valueMB = request.getMimeHeaders().getValue("host");
parseHost(valueMB);
+ if (error) {
+ adapter.log(request, response, 0);
+ }
}
@@ -825,7 +826,6 @@ public abstract class AbstractAjpProcessor<S> extends AbstractProcessor<S> {
request.serverName().duplicate(request.localName());
} catch (IOException e) {
response.setStatus(400);
- adapter.log(request, response, 0);
error = true;
}
return;
@@ -877,7 +877,6 @@ public abstract class AbstractAjpProcessor<S> extends AbstractProcessor<S> {
error = true;
// 400 - Bad request
response.setStatus(400);
- adapter.log(request, response, 0);
break;
}
port = port + (charValue * mult);
diff --git a/java/org/apache/coyote/ajp/AjpAprProcessor.java b/java/org/apache/coyote/ajp/AjpAprProcessor.java
index d6b0b56..eca67e1 100644
--- a/java/org/apache/coyote/ajp/AjpAprProcessor.java
+++ b/java/org/apache/coyote/ajp/AjpAprProcessor.java
@@ -113,6 +113,7 @@ public class AjpAprProcessor extends AbstractAjpProcessor<Long> {
long socketRef = socket.getSocket().longValue();
Socket.setrbb(socketRef, inputBuffer);
Socket.setsbb(socketRef, outputBuffer);
+ boolean cping = false;
// Error flag
error = false;
@@ -120,7 +121,6 @@ public class AjpAprProcessor extends AbstractAjpProcessor<Long> {
boolean keptAlive = false;
while (!error && !endpoint.isPaused()) {
-
// Parsing the request header
try {
// Get first message of the request
@@ -134,6 +134,11 @@ public class AjpAprProcessor extends AbstractAjpProcessor<Long> {
// not regular request processing
int type = requestHeaderMessage.getByte();
if (type == Constants.JK_AJP13_CPING_REQUEST) {
+ if (endpoint.isPaused()) {
+ recycle(true);
+ break;
+ }
+ cping = true;
if (Socket.send(socketRef, pongMessageArray, 0,
pongMessageArray.length) < 0) {
error = true;
@@ -148,7 +153,6 @@ public class AjpAprProcessor extends AbstractAjpProcessor<Long> {
error = true;
break;
}
-
keptAlive = true;
request.setStartTime(System.currentTimeMillis());
} catch (IOException e) {
@@ -178,6 +182,14 @@ public class AjpAprProcessor extends AbstractAjpProcessor<Long> {
}
}
+ if (!error && !cping && endpoint.isPaused()) {
+ // 503 - Service unavailable
+ response.setStatus(503);
+ adapter.log(request, response, 0);
+ error = true;
+ }
+ cping = false;
+
// Process the request in the adapter
if (!error) {
try {
@@ -221,13 +233,15 @@ public class AjpAprProcessor extends AbstractAjpProcessor<Long> {
}
rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
-
- if (error || endpoint.isPaused()) {
- return SocketState.CLOSED;
- } else if (isAsync()) {
- return SocketState.LONG;
+
+ if (!error && !endpoint.isPaused()) {
+ if (isAsync()) {
+ return SocketState.LONG;
+ } else {
+ return SocketState.OPEN;
+ }
} else {
- return SocketState.OPEN;
+ return SocketState.CLOSED;
}
}
@@ -250,7 +264,7 @@ public class AjpAprProcessor extends AbstractAjpProcessor<Long> {
SocketStatus.OPEN);
}
} else if (actionCode == ActionCode.ASYNC_SETTIMEOUT) {
- if (param==null) return;
+ if (param == null) return;
long timeout = ((Long)param).longValue();
socket.setTimeout(timeout);
} else if (actionCode == ActionCode.ASYNC_DISPATCH) {
@@ -259,8 +273,6 @@ public class AjpAprProcessor extends AbstractAjpProcessor<Long> {
SocketStatus.OPEN);
}
}
-
-
}
diff --git a/java/org/apache/coyote/ajp/AjpAprProtocol.java b/java/org/apache/coyote/ajp/AjpAprProtocol.java
index d314cb2..aa873d8 100644
--- a/java/org/apache/coyote/ajp/AjpAprProtocol.java
+++ b/java/org/apache/coyote/ajp/AjpAprProtocol.java
@@ -126,7 +126,7 @@ public class AjpAprProtocol extends AbstractAjpProtocol {
recycledProcessors.offer(processor);
if (addToPoller) {
((AprEndpoint)proto.endpoint).getPoller().add(
- socket.getSocket().longValue());
+ socket.getSocket().longValue(), true);
}
}
diff --git a/java/org/apache/coyote/ajp/AjpNioProcessor.java b/java/org/apache/coyote/ajp/AjpNioProcessor.java
index bd53f13..29f5135 100644
--- a/java/org/apache/coyote/ajp/AjpNioProcessor.java
+++ b/java/org/apache/coyote/ajp/AjpNioProcessor.java
@@ -98,6 +98,7 @@ public class AjpNioProcessor extends AbstractAjpProcessor<NioChannel> {
this.socket = socket.getSocket();
long soTimeout = endpoint.getSoTimeout();
+ boolean cping = false;
// Error flag
error = false;
@@ -118,6 +119,11 @@ public class AjpNioProcessor extends AbstractAjpProcessor<NioChannel> {
// not regular request processing
int type = requestHeaderMessage.getByte();
if (type == Constants.JK_AJP13_CPING_REQUEST) {
+ if (endpoint.isPaused()) {
+ recycle(true);
+ break;
+ }
+ cping = true;
try {
output(pongMessageArray, 0, pongMessageArray.length);
} catch (IOException e) {
@@ -163,12 +169,13 @@ public class AjpNioProcessor extends AbstractAjpProcessor<NioChannel> {
}
}
- if (endpoint.isPaused()) {
+ if (!error && !cping && endpoint.isPaused()) {
// 503 - Service unavailable
response.setStatus(503);
adapter.log(request, response, 0);
error = true;
}
+ cping = false;
// Process the request in the adapter
if (!error) {
@@ -186,7 +193,7 @@ public class AjpNioProcessor extends AbstractAjpProcessor<NioChannel> {
error = true;
}
}
-
+
if (isAsync() && !error) {
break;
}
@@ -216,7 +223,7 @@ public class AjpNioProcessor extends AbstractAjpProcessor<NioChannel> {
recycle(false);
}
-
+
rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
if (!error && !endpoint.isPaused()) {
@@ -228,10 +235,9 @@ public class AjpNioProcessor extends AbstractAjpProcessor<NioChannel> {
} else {
return SocketState.CLOSED;
}
-
}
-
+
// ----------------------------------------------------- ActionHook Methods
diff --git a/java/org/apache/coyote/ajp/AjpProcessor.java b/java/org/apache/coyote/ajp/AjpProcessor.java
index cdebea6..5998989 100644
--- a/java/org/apache/coyote/ajp/AjpProcessor.java
+++ b/java/org/apache/coyote/ajp/AjpProcessor.java
@@ -111,6 +111,7 @@ public class AjpProcessor extends AbstractAjpProcessor<Socket> {
if (keepAliveTimeout > 0) {
soTimeout = socket.getSocket().getSoTimeout();
}
+ boolean cping = false;
// Error flag
error = false;
@@ -136,6 +137,11 @@ public class AjpProcessor extends AbstractAjpProcessor<Socket> {
// not regular request processing
int type = requestHeaderMessage.getByte();
if (type == Constants.JK_AJP13_CPING_REQUEST) {
+ if (endpoint.isPaused()) {
+ recycle(true);
+ break;
+ }
+ cping = true;
try {
output.write(pongMessageArray);
} catch (IOException e) {
@@ -179,12 +185,13 @@ public class AjpProcessor extends AbstractAjpProcessor<Socket> {
}
}
- if (endpoint.isPaused()) {
+ if (!error && !cping && endpoint.isPaused()) {
// 503 - Service unavailable
response.setStatus(503);
adapter.log(request, response, 0);
error = true;
}
+ cping = false;
// Process the request in the adapter
if (!error) {
diff --git a/java/org/apache/coyote/http11/AbstractHttp11JsseProtocol.java b/java/org/apache/coyote/http11/AbstractHttp11JsseProtocol.java
index 2f79756..8cf49f6 100644
--- a/java/org/apache/coyote/http11/AbstractHttp11JsseProtocol.java
+++ b/java/org/apache/coyote/http11/AbstractHttp11JsseProtocol.java
@@ -103,10 +103,10 @@ public abstract class AbstractHttp11JsseProtocol
return endpoint.getAllowUnsafeLegacyRenegotiation();
}
- private String sslImplemenationName = null;
- public String getSslImplemenationName() { return sslImplemenationName; }
- public void setSslImplemenationName(String s) {
- this.sslImplemenationName = s;
+ private String sslImplementationName = null;
+ public String getSslImplementationName() { return sslImplementationName; }
+ public void setSslImplementationName(String s) {
+ this.sslImplementationName = s;
}
// ------------------------------------------------------- Lifecycle methods
@@ -115,7 +115,7 @@ public abstract class AbstractHttp11JsseProtocol
public void init() throws Exception {
// SSL implementation needs to be in place before end point is
// initialized
- sslImplementation = SSLImplementation.getInstance(sslImplemenationName);
+ sslImplementation = SSLImplementation.getInstance(sslImplementationName);
super.init();
}
}
diff --git a/java/org/apache/coyote/http11/AbstractHttp11Processor.java b/java/org/apache/coyote/http11/AbstractHttp11Processor.java
index 0baadc0..0e46c06 100644
--- a/java/org/apache/coyote/http11/AbstractHttp11Processor.java
+++ b/java/org/apache/coyote/http11/AbstractHttp11Processor.java
@@ -47,6 +47,7 @@ import org.apache.tomcat.util.http.MimeHeaders;
import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
import org.apache.tomcat.util.net.SocketStatus;
+import org.apache.tomcat.util.net.SocketWrapper;
import org.apache.tomcat.util.res.StringManager;
public abstract class AbstractHttp11Processor<S> extends AbstractProcessor<S> {
@@ -79,6 +80,36 @@ public abstract class AbstractHttp11Processor<S> extends AbstractProcessor<S> {
/**
+ * Flag used to indicate that the socket should be kept open (e.g. for keep
+ * alive or send file.
+ */
+ protected boolean openSocket = false;
+
+
+ /**
+ * Flag used to indicate that the socket should treat the next request
+ * processed like a keep-alive connection - i.e. one where there may not be
+ * any data to process. The initial value of this flag on entering the
+ * process method is different for connectors that use polling (NIO / APR -
+ * data is always expected) compared to those that use blocking (BIO - data
+ * is only expected if the connection isn't in the keep-alive state).
+ */
+ protected boolean keptAlive;
+
+
+ /**
+ * Flag that indicates that send file processing is in progress and that the
+ * socket should not be returned to the poller (where a poller is used).
+ */
+ protected boolean sendfileInProgress = false;
+
+
+ /**
+ * Flag that indicates if the request headers have been completely read.
+ */
+ protected boolean readComplete = true;
+
+ /**
* HTTP/1.1 flag.
*/
protected boolean http11 = true;
@@ -607,6 +638,12 @@ public abstract class AbstractHttp11Processor<S> extends AbstractProcessor<S> {
status == 501 /* SC_NOT_IMPLEMENTED */;
}
+
+ /**
+ * Allows the super class to set the socket wrapper being used.
+ */
+ protected abstract void setSocketWrapper(SocketWrapper<S> socketWrapper);
+
/**
* Exposes input buffer to super class to allow better code re-use.
@@ -747,6 +784,7 @@ public abstract class AbstractHttp11Processor<S> extends AbstractProcessor<S> {
InputFilter savedBody = new SavedRequestInputFilter(body);
savedBody.setRequest(request);
+ @SuppressWarnings("unchecked")
AbstractInputBuffer<S> internalBuffer = (AbstractInputBuffer<S>)
request.getInputBuffer();
internalBuffer.addActiveFilter(savedBody);
@@ -786,6 +824,231 @@ public abstract class AbstractHttp11Processor<S> extends AbstractProcessor<S> {
/**
+ * Configures the timeout to be used for reading the request line.
+ */
+ protected abstract void setRequestLineReadTimeout() throws IOException;
+
+
+ /**
+ * Defines how a connector handles an incomplete request line read.
+ *
+ * @return <code>true</code> if the processor should break out of the
+ * processing loop, otherwise <code>false</code>.
+ */
+ protected abstract boolean handleIncompleteRequestLineRead();
+
+
+ /**
+ * Set the socket timeout.
+ */
+ protected abstract void setSocketTimeout(int timeout) throws IOException;
+
+
+ /**
+ * Process pipelined HTTP requests using the specified input and output
+ * streams.
+ *
+ * @param socketWrapper Socket from which the HTTP requests will be read
+ * and the HTTP responses will be written.
+ *
+ * @throws IOException error during an I/O operation
+ */
+ @Override
+ public SocketState process(SocketWrapper<S> socketWrapper)
+ throws IOException {
+ RequestInfo rp = request.getRequestProcessor();
+ rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
+
+ // Setting up the I/O
+ setSocketWrapper(socketWrapper);
+ getInputBuffer().init(socketWrapper, endpoint);
+ getOutputBuffer().init(socketWrapper, endpoint);
+
+ // Flags
+ error = false;
+ keepAlive = true;
+ comet = false;
+ openSocket = false;
+ sendfileInProgress = false;
+ readComplete = true;
+ if (endpoint.getUsePolling()) {
+ keptAlive = false;
+ } else {
+ keptAlive = socketWrapper.isKeptAlive();
+ }
+
+ if (disableKeepAlive()) {
+ socketWrapper.setKeepAliveLeft(0);
+ }
+
+ while (!error && keepAlive && !comet && !isAsync() &&
+ !endpoint.isPaused()) {
+
+ // Parsing the request header
+ try {
+ setRequestLineReadTimeout();
+
+ if (!getInputBuffer().parseRequestLine(keptAlive)) {
+ if (handleIncompleteRequestLineRead()) {
+ break;
+ }
+ }
+
+ if (endpoint.isPaused()) {
+ // 503 - Service unavailable
+ response.setStatus(503);
+ error = true;
+ } else {
+ request.setStartTime(System.currentTimeMillis());
+ keptAlive = true;
+ // Currently only NIO will ever return false here
+ if (!getInputBuffer().parseHeaders()) {
+ // We've read part of the request, don't recycle it
+ // instead associate it with the socket
+ openSocket = true;
+ readComplete = false;
+ break;
+ }
+ if (!disableUploadTimeout) {
+ setSocketTimeout(connectionUploadTimeout);
+ }
+ }
+ } catch (IOException e) {
+ if (getLog().isDebugEnabled()) {
+ getLog().debug(
+ sm.getString("http11processor.header.parse"), e);
+ }
+ error = true;
+ break;
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ if (getLog().isDebugEnabled()) {
+ getLog().debug(
+ sm.getString("http11processor.header.parse"), t);
+ }
+ // 400 - Bad Request
+ response.setStatus(400);
+ adapter.log(request, response, 0);
+ error = true;
+ }
+
+ if (!error) {
+ // Setting up filters, and parse some request headers
+ rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
+ try {
+ prepareRequest();
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ if (getLog().isDebugEnabled()) {
+ getLog().debug(sm.getString(
+ "http11processor.request.prepare"), t);
+ }
+ // 400 - Internal Server Error
+ response.setStatus(400);
+ adapter.log(request, response, 0);
+ error = true;
+ }
+ }
+
+ if (maxKeepAliveRequests == 1) {
+ keepAlive = false;
+ } else if (maxKeepAliveRequests > 0 &&
+ socketWrapper.decrementKeepAlive() <= 0) {
+ keepAlive = false;
+ }
+
+ // Process the request in the adapter
+ if (!error) {
+ try {
+ rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
+ adapter.service(request, response);
+ // Handle when the response was committed before a serious
+ // error occurred. Throwing a ServletException should both
+ // set the status to 500 and set the errorException.
+ // If we fail here, then the response is likely already
+ // committed, so we can't try and set headers.
+ if(keepAlive && !error) { // Avoid checking twice.
+ error = response.getErrorException() != null ||
+ (!isAsync() &&
+ statusDropsConnection(response.getStatus()));
+ }
+ setCometTimeouts(socketWrapper);
+ } catch (InterruptedIOException e) {
+ error = true;
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ getLog().error(sm.getString(
+ "http11processor.request.process"), t);
+ // 500 - Internal Server Error
+ response.setStatus(500);
+ adapter.log(request, response, 0);
+ error = true;
+ }
+ }
+
+ // Finish the handling of the request
+ rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
+
+ if (!isAsync() && !comet) {
+ if (error) {
+ // If we know we are closing the connection, don't drain
+ // input. This way uploading a 100GB file doesn't tie up the
+ // thread if the servlet has rejected it.
+ getInputBuffer().setSwallowInput(false);
+ }
+ endRequest();
+ }
+
+ rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
+
+ // If there was an error, make sure the request is counted as
+ // and error, and update the statistics counter
+ if (error) {
+ response.setStatus(500);
+ }
+ request.updateCounters();
+
+ if (!isAsync() && !comet || error) {
+ getInputBuffer().nextRequest();
+ getOutputBuffer().nextRequest();
+ }
+
+ if (!disableUploadTimeout) {
+ setSocketTimeout(endpoint.getSoTimeout());
+ }
+
+ rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
+
+ if (breakKeepAliveLoop(socketWrapper)) {
+ break;
+ }
+ }
+
+ rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
+
+ if (error || endpoint.isPaused()) {
+ return SocketState.CLOSED;
+ } else if (isAsync() || comet) {
+ return SocketState.LONG;
+ } else {
+ if (sendfileInProgress) {
+ return SocketState.SENDFILE;
+ } else {
+ if (openSocket) {
+ if (readComplete) {
+ return SocketState.OPEN;
+ } else {
+ return SocketState.LONG;
+ }
+ } else {
+ return SocketState.CLOSED;
+ }
+ }
+ }
+ }
+
+
+ /**
* After reading the request headers, we have to setup the request filters.
*/
protected void prepareRequest() {
@@ -823,7 +1086,6 @@ public abstract class AbstractHttp11Processor<S> extends AbstractProcessor<S> {
" Unsupported HTTP version \""+protocolMB+"\"");
}
response.setStatus(505);
- adapter.log(request, response, 0);
}
MessageBytes methodMB = request.method();
@@ -919,7 +1181,6 @@ public abstract class AbstractHttp11Processor<S> extends AbstractProcessor<S> {
error = true;
// 501 - Unimplemented
response.setStatus(501);
- adapter.log(request, response, 0);
}
startPos = commaPos + 1;
commaPos = transferEncodingValue.indexOf(',', startPos);
@@ -935,7 +1196,6 @@ public abstract class AbstractHttp11Processor<S> extends AbstractProcessor<S> {
" Unsupported transfer encoding \""+encodingName+"\"");
}
response.setStatus(501);
- adapter.log(request, response, 0);
}
}
@@ -958,7 +1218,6 @@ public abstract class AbstractHttp11Processor<S> extends AbstractProcessor<S> {
" host header missing");
}
response.setStatus(400);
- adapter.log(request, response, 0);
}
parseHost(valueMB);
@@ -988,6 +1247,10 @@ public abstract class AbstractHttp11Processor<S> extends AbstractProcessor<S> {
request.setAttribute("org.apache.tomcat.comet.timeout.support",
Boolean.TRUE);
}
+
+ if (error) {
+ adapter.log(request, response, 0);
+ }
}
@@ -1207,7 +1470,6 @@ public abstract class AbstractHttp11Processor<S> extends AbstractProcessor<S> {
error = true;
// 400 - Bad request
response.setStatus(400);
- adapter.log(request, response, 0);
break;
}
port = port + (charValue * mult);
@@ -1271,6 +1533,12 @@ public abstract class AbstractHttp11Processor<S> extends AbstractProcessor<S> {
protected abstract void resetTimeouts();
+ /**
+ * Provides a mechanism for those connectors (currently only NIO) that need
+ * that need to set comet timeouts.
+ */
+ protected abstract void setCometTimeouts(SocketWrapper<S> socketWrapper);
+
public void endRequest() {
// Finish the handling of the request
@@ -1282,8 +1550,9 @@ public abstract class AbstractHttp11Processor<S> extends AbstractProcessor<S> {
ExceptionUtils.handleThrowable(t);
getLog().error(sm.getString("http11processor.request.finish"), t);
// 500 - Internal Server Error
+ // Can't add a 500 to the access log since that has already been
+ // written in the Adapter.service method.
response.setStatus(500);
- adapter.log(request, response, 0);
error = true;
}
try {
@@ -1297,7 +1566,18 @@ public abstract class AbstractHttp11Processor<S> extends AbstractProcessor<S> {
}
}
-
+
+
+ /**
+ * Checks to see if the keep-alive loop should be broken, performing any
+ * processing (e.g. sendfile handling) that may have an impact on whether
+ * or not the keep-alive loop should be broken.
+ * @return true if the keep-alive loop should be broken
+ */
+ protected abstract boolean breakKeepAliveLoop(
+ SocketWrapper<S> socketWrapper);
+
+
public final void recycle() {
getInputBuffer().recycle();
getOutputBuffer().recycle();
diff --git a/java/org/apache/coyote/http11/Http11AprProcessor.java b/java/org/apache/coyote/http11/Http11AprProcessor.java
index 3b19181..9918980 100644
--- a/java/org/apache/coyote/http11/Http11AprProcessor.java
+++ b/java/org/apache/coyote/http11/Http11AprProcessor.java
@@ -157,207 +157,91 @@ public class Http11AprProcessor extends AbstractHttp11Processor<Long> {
}
}
- /**
- * Process pipelined HTTP requests using the specified input and output
- * streams.
- *
- * @param socketWrapper Socket from which the HTTP requests will be read
- * and the HTTP responses will be written.
- *
- * @throws IOException error during an I/O operation
- */
@Override
- public SocketState process(SocketWrapper<Long> socketWrapper)
- throws IOException {
- RequestInfo rp = request.getRequestProcessor();
- rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
+ protected boolean disableKeepAlive() {
+ return false;
+ }
- // Setting up the socket
- this.socket = socketWrapper;
- inputBuffer.init(socketWrapper, endpoint);
- outputBuffer.init(socketWrapper, endpoint);
- // Error flag
- error = false;
- keepAlive = true;
- comet = false;
+ @Override
+ protected void setRequestLineReadTimeout() throws IOException {
+ // Timeouts while in the poller are handled entirely by the poller
+ // Only need to be concerned with socket timeouts
+
+ // APR uses simulated blocking so if some request line data is present
+ // then it must all be presented (with the normal socket timeout).
+
+ // When entering the processing loop for the first time there will
+ // always be some data to read so the keep-alive timeout is not required
+
+ // For the second and subsequent executions of the processing loop, if
+ // there is no request line data present then no further data will be
+ // read from the socket. If there is request line data present then it
+ // must all be presented (with the normal socket timeout)
+
+ // When the socket is created it is given the correct timeout.
+ // sendfile may change the timeout but will restore it
+ // This processor may change the timeout for uploads but will restore it
+
+ // NO-OP
+ }
- int soTimeout = endpoint.getSoTimeout();
- if (disableKeepAlive()) {
- socketWrapper.setKeepAliveLeft(0);
+ @Override
+ protected boolean handleIncompleteRequestLineRead() {
+ // This means that no data is available right now
+ // (long keepalive), so that the processor should be recycled
+ // and the method should return true
+ openSocket = true;
+ if (endpoint.isPaused()) {
+ // 503 - Service unavailable
+ response.setStatus(503);
+ adapter.log(request, response, 0);
+ error = true;
+ } else {
+ return true;
}
+ return false;
+ }
- boolean keptAlive = false;
- boolean openSocket = false;
- boolean sendfileInProgress = false;
- long socketRef = socketWrapper.getSocket().longValue();
+ @Override
+ protected void setSocketTimeout(int timeout) {
+ Socket.timeoutSet(socket.getSocket().longValue(), timeout * 1000);
+ }
+
+
+ @Override
+ protected void setCometTimeouts(SocketWrapper<Long> socketWrapper) {
+ // NO-OP for APR/native
+ }
- while (!error && keepAlive && !comet && !isAsync() && !endpoint.isPaused()) {
- // Parsing the request header
- try {
- if( !disableUploadTimeout && keptAlive && soTimeout > 0 ) {
- Socket.timeoutSet(socketRef, soTimeout * 1000);
- }
- if (!inputBuffer.parseRequestLine(keptAlive)) {
- // This means that no data is available right now
- // (long keepalive), so that the processor should be recycled
- // and the method should return true
- openSocket = true;
- if (endpoint.isPaused()) {
- // 503 - Service unavailable
- response.setStatus(503);
- adapter.log(request, response, 0);
- error = true;
- } else {
- break;
- }
- }
- if (!endpoint.isPaused()) {
- request.setStartTime(System.currentTimeMillis());
- keptAlive = true;
- if (!disableUploadTimeout) {
- Socket.timeoutSet(socketRef,
- connectionUploadTimeout * 1000);
- }
- inputBuffer.parseHeaders();
- }
- } catch (IOException e) {
- error = true;
- break;
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("http11processor.header.parse"), t);
- }
- // 400 - Bad Request
- response.setStatus(400);
- adapter.log(request, response, 0);
- error = true;
- }
-
- if (!error) {
- // Setting up filters, and parse some request headers
- rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
- try {
- prepareRequest();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
+ @Override
+ protected boolean breakKeepAliveLoop(SocketWrapper<Long> socketWrapper) {
+ openSocket = keepAlive;
+ // Do sendfile as needed: add socket to sendfile and end
+ if (sendfileData != null && !error) {
+ sendfileData.socket = socketWrapper.getSocket().longValue();
+ sendfileData.keepAlive = keepAlive;
+ if (!((AprEndpoint)endpoint).getSendfile().add(sendfileData)) {
+ // Didn't send all of the data to sendfile.
+ if (sendfileData.socket == 0) {
+ // The socket is no longer set. Something went wrong.
+ // Close the connection. Too late to set status code.
if (log.isDebugEnabled()) {
- log.debug(sm.getString("http11processor.request.prepare"), t);
+ log.debug(sm.getString(
+ "http11processor.sendfile.error"));
}
- // 400 - Internal Server Error
- response.setStatus(400);
- adapter.log(request, response, 0);
error = true;
+ } else {
+ // The sendfile Poller will add the socket to the main
+ // Poller once sendfile processing is complete
+ sendfileInProgress = true;
}
- }
-
- if (maxKeepAliveRequests == 1) {
- keepAlive = false;
- } else if (maxKeepAliveRequests > 0 &&
- socketWrapper.decrementKeepAlive() <= 0) {
- keepAlive = false;
- }
-
- // Process the request in the adapter
- if (!error) {
- try {
- rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
- adapter.service(request, response);
- // Handle when the response was committed before a serious
- // error occurred. Throwing a ServletException should both
- // set the status to 500 and set the errorException.
- // If we fail here, then the response is likely already
- // committed, so we can't try and set headers.
- if(keepAlive && !error) { // Avoid checking twice.
- error = response.getErrorException() != null ||
- (!isAsync() &&
- statusDropsConnection(response.getStatus()));
- }
- } catch (InterruptedIOException e) {
- error = true;
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.error(sm.getString("http11processor.request.process"), t);
- // 500 - Internal Server Error
- response.setStatus(500);
- adapter.log(request, response, 0);
- error = true;
- }
- }
-
- // Finish the handling of the request
- if (!comet && !isAsync()) {
- // If we know we are closing the connection, don't drain input.
- // This way uploading a 100GB file doesn't tie up the thread
- // if the servlet has rejected it.
- if(error)
- inputBuffer.setSwallowInput(false);
- endRequest();
- }
-
- // If there was an error, make sure the request is counted as
- // and error, and update the statistics counter
- if (error) {
- response.setStatus(500);
- }
- request.updateCounters();
-
- if (!comet && !isAsync()) {
- // Next request
- inputBuffer.nextRequest();
- outputBuffer.nextRequest();
- }
-
- // Do sendfile as needed: add socket to sendfile and end
- if (sendfileData != null && !error) {
- sendfileData.socket = socketRef;
- sendfileData.keepAlive = keepAlive;
- if (!((AprEndpoint)endpoint).getSendfile().add(sendfileData)) {
- // Didn't send all of the data to sendfile.
- if (sendfileData.socket == 0) {
- // The socket is no longer set. Something went wrong.
- // Close the connection. Too late to set status code.
- if (log.isDebugEnabled()) {
- log.debug(sm.getString(
- "http11processor.sendfile.error"));
- }
- error = true;
- } else {
- // The sendfile Poller will add the socket to the main
- // Poller once sendfile processing is complete
- sendfileInProgress = true;
- }
- break;
- }
- }
-
- rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
-
- }
-
- rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
-
- if (error || endpoint.isPaused()) {
- return SocketState.CLOSED;
- } else if (comet || isAsync()) {
- return SocketState.LONG;
- } else {
- if (sendfileInProgress) {
- return SocketState.SENDFILE;
- } else {
- return (openSocket) ? SocketState.OPEN : SocketState.CLOSED;
+ return true;
}
}
-
- }
-
-
- @Override
- protected boolean disableKeepAlive() {
return false;
}
@@ -633,6 +517,11 @@ public class Http11AprProcessor extends AbstractHttp11Processor<Long> {
}
@Override
+ protected void setSocketWrapper(SocketWrapper<Long> socketWrapper) {
+ this.socket = socketWrapper;
+ }
+
+ @Override
protected AbstractInputBuffer<Long> getInputBuffer() {
return inputBuffer;
}
diff --git a/java/org/apache/coyote/http11/Http11AprProtocol.java b/java/org/apache/coyote/http11/Http11AprProtocol.java
index 47a6ac4..163c4a4 100644
--- a/java/org/apache/coyote/http11/Http11AprProtocol.java
+++ b/java/org/apache/coyote/http11/Http11AprProtocol.java
@@ -215,9 +215,9 @@ public class Http11AprProtocol extends AbstractHttp11Protocol {
boolean addToPoller) {
processor.recycle();
recycledProcessors.offer(processor);
- if (addToPoller) {
+ if (addToPoller && proto.endpoint.isRunning()) {
((AprEndpoint)proto.endpoint).getPoller().add(
- socket.getSocket().longValue());
+ socket.getSocket().longValue(), true);
}
}
@@ -234,9 +234,9 @@ public class Http11AprProtocol extends AbstractHttp11Protocol {
if (processor.isAsync()) {
socket.setAsync(true);
- } else if (processor.comet) {
+ } else if (processor.comet && proto.endpoint.isRunning()) {
((AprEndpoint) proto.endpoint).getCometPoller().add(
- socket.getSocket().longValue());
+ socket.getSocket().longValue(), false);
}
}
diff --git a/java/org/apache/coyote/http11/Http11NioProcessor.java b/java/org/apache/coyote/http11/Http11NioProcessor.java
index afdddfb..d89f906 100644
--- a/java/org/apache/coyote/http11/Http11NioProcessor.java
+++ b/java/org/apache/coyote/http11/Http11NioProcessor.java
@@ -137,7 +137,7 @@ public class Http11NioProcessor extends AbstractHttp11Processor<NioChannel> {
if (comettimeout != null) attach.setTimeout(comettimeout.longValue());
} else {
//reset the timeout
- if (keepAlive && keepAliveTimeout>0) {
+ if (keepAlive) {
attach.setTimeout(keepAliveTimeout);
} else {
attach.setTimeout(soTimeout);
@@ -177,7 +177,7 @@ public class Http11NioProcessor extends AbstractHttp11Processor<NioChannel> {
long soTimeout = endpoint.getSoTimeout();
//reset the timeout
- if (keepAlive && keepAliveTimeout>0) {
+ if (keepAlive) {
attach.setTimeout(keepAliveTimeout);
} else {
attach.setTimeout(soTimeout);
@@ -186,224 +186,102 @@ public class Http11NioProcessor extends AbstractHttp11Processor<NioChannel> {
}
- /**
- * Process pipelined HTTP requests using the specified input and output
- * streams.
- *
- * @param socketWrapper Socket from which the HTTP requests will be read
- * and the HTTP responses will be written.
- *
- * @throws IOException error during an I/O operation
- */
@Override
- public SocketState process(SocketWrapper<NioChannel> socketWrapper)
- throws IOException {
- RequestInfo rp = request.getRequestProcessor();
- rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
+ protected boolean disableKeepAlive() {
+ return false;
+ }
- // Setting up the socket
- this.socket = socketWrapper;
- inputBuffer.init(socketWrapper, endpoint);
- outputBuffer.init(socketWrapper, endpoint);
- // Error flag
- error = false;
- keepAlive = true;
- comet = false;
+ @Override
+ protected void setRequestLineReadTimeout() throws IOException {
+ // socket.setTimeout()
+ // - timeout used by poller
+ // socket.getSocket().getIOChannel().socket().setSoTimeout()
+ // - timeout used for blocking reads
+
+ // When entering the processing loop there will always be data to read
+ // so no point changing timeouts at this point
- int soTimeout = endpoint.getSoTimeout();
-
- if (disableKeepAlive()) {
- socketWrapper.setKeepAliveLeft(0);
- }
-
- boolean keptAlive = false;
- boolean openSocket = false;
- boolean readComplete = true;
+ // For the second and subsequent executions of the processing loop, a
+ // non-blocking read is used so again no need to set the timeouts
- while (!error && keepAlive && !comet && !isAsync() && !endpoint.isPaused()) {
- //always default to our soTimeout
- socketWrapper.setTimeout(soTimeout);
- // Parsing the request header
- try {
- if( !disableUploadTimeout && keptAlive && soTimeout > 0 ) {
- socketWrapper.getSocket().getIOChannel().socket().setSoTimeout(soTimeout);
- }
- if (!inputBuffer.parseRequestLine(keptAlive)) {
- // Haven't finished reading the request so keep the socket
- // open
- openSocket = true;
- // Check to see if we have read any of the request line yet
- if (inputBuffer.getParsingRequestLinePhase()<2) {
- // No data read, OK to recycle the processor
- // Continue to use keep alive timeout
- if (keepAliveTimeout>0) {
- socketWrapper.setTimeout(keepAliveTimeout);
- }
- } else {
- // Started to read request line. Need to keep processor
- // associated with socket
- readComplete = false;
- }
- if (endpoint.isPaused()) {
- // 503 - Service unavailable
- response.setStatus(503);
- adapter.log(request, response, 0);
- error = true;
- } else {
- break;
- }
- }
- if (!endpoint.isPaused()) {
- keptAlive = true;
- if ( !inputBuffer.parseHeaders() ) {
- //we've read part of the request, don't recycle it
- //instead associate it with the socket
- openSocket = true;
- readComplete = false;
- break;
- }
- request.setStartTime(System.currentTimeMillis());
- if (!disableUploadTimeout) { //only for body, not for request headers
- socketWrapper.getSocket().getIOChannel().socket().setSoTimeout(
- connectionUploadTimeout);
- }
- }
- } catch (IOException e) {
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("http11processor.header.parse"), e);
- }
- error = true;
- break;
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("http11processor.header.parse"), t);
- }
- // 400 - Bad Request
- response.setStatus(400);
- adapter.log(request, response, 0);
- error = true;
- }
+ // Because NIO supports non-blocking reading of the request line and
+ // headers the timeouts need to be set when returning the socket to
+ // the poller rather than here.
+
+ // NO-OP
+ }
- if (!error) {
- // Setting up filters, and parse some request headers
- rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
- try {
- prepareRequest();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("http11processor.request.prepare"), t);
- }
- // 400 - Internal Server Error
- response.setStatus(400);
- adapter.log(request, response, 0);
- error = true;
- }
- }
-
- if (maxKeepAliveRequests == 1) {
- keepAlive = false;
- } else if (maxKeepAliveRequests > 0 &&
- socketWrapper.decrementKeepAlive() <= 0) {
- keepAlive = false;
- }
- // Process the request in the adapter
- if (!error) {
- try {
- rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
- adapter.service(request, response);
- // Handle when the response was committed before a serious
- // error occurred. Throwing a ServletException should both
- // set the status to 500 and set the errorException.
- // If we fail here, then the response is likely already
- // committed, so we can't try and set headers.
- if(keepAlive && !error) { // Avoid checking twice.
- error = response.getErrorException() != null ||
- (!isAsync() &&
- statusDropsConnection(response.getStatus()));
- }
- // Comet support
- SelectionKey key = socketWrapper.getSocket().getIOChannel().keyFor(
- socketWrapper.getSocket().getPoller().getSelector());
- if (key != null) {
- NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment) key.attachment();
- if (attach != null) {
- attach.setComet(comet);
- if (comet) {
- Integer comettimeout = (Integer) request.getAttribute("org.apache.tomcat.comet.timeout");
- if (comettimeout != null) attach.setTimeout(comettimeout.longValue());
- }
- }
- }
- } catch (InterruptedIOException e) {
- error = true;
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.error(sm.getString("http11processor.request.process"), t);
- // 500 - Internal Server Error
- response.setStatus(500);
- adapter.log(request, response, 0);
- error = true;
- }
+ @Override
+ protected boolean handleIncompleteRequestLineRead() {
+ // Haven't finished reading the request so keep the socket
+ // open
+ openSocket = true;
+ // Check to see if we have read any of the request line yet
+ if (inputBuffer.getParsingRequestLinePhase() < 2) {
+ if (socket.getLastAccess() > -1 || keptAlive) {
+ // Haven't read the request line and have previously processed a
+ // request. Must be keep-alive. Make sure poller uses keepAlive.
+ socket.setTimeout(endpoint.getKeepAliveTimeout());
}
+ } else {
+ // Started to read request line. Need to keep processor
+ // associated with socket
+ readComplete = false;
+ // Make sure poller uses soTimeout from here onwards
+ socket.setTimeout(endpoint.getSoTimeout());
+ }
+ if (endpoint.isPaused()) {
+ // 503 - Service unavailable
+ response.setStatus(503);
+ adapter.log(request, response, 0);
+ error = true;
+ } else {
+ return true;
+ }
+ return false;
+ }
- // Finish the handling of the request
- if (!comet && !isAsync()) {
- // If we know we are closing the connection, don't drain input.
- // This way uploading a 100GB file doesn't tie up the thread
- // if the servlet has rejected it.
- if(error)
- inputBuffer.setSwallowInput(false);
- endRequest();
- }
- // If there was an error, make sure the request is counted as
- // and error, and update the statistics counter
- if (error) {
- response.setStatus(500);
- }
- request.updateCounters();
+ @Override
+ protected void setSocketTimeout(int timeout) throws IOException {
+ socket.getSocket().getIOChannel().socket().setSoTimeout(timeout);
+ }
- if (!comet && !isAsync()) {
- // Next request
- inputBuffer.nextRequest();
- outputBuffer.nextRequest();
- }
-
- // Do sendfile as needed: add socket to sendfile and end
- if (sendfileData != null && !error) {
- ((KeyAttachment) socketWrapper).setSendfileData(sendfileData);
- sendfileData.keepAlive = keepAlive;
- SelectionKey key = socketWrapper.getSocket().getIOChannel().keyFor(
- socketWrapper.getSocket().getPoller().getSelector());
- //do the first write on this thread, might as well
- openSocket = socketWrapper.getSocket().getPoller().processSendfile(key,
- (KeyAttachment) socketWrapper, true, true);
- break;
+
+ @Override
+ protected void setCometTimeouts(SocketWrapper<NioChannel> socketWrapper) {
+ // Comet support
+ SelectionKey key = socketWrapper.getSocket().getIOChannel().keyFor(
+ socketWrapper.getSocket().getPoller().getSelector());
+ if (key != null) {
+ NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment) key.attachment();
+ if (attach != null) {
+ attach.setComet(comet);
+ if (comet) {
+ Integer comettimeout = (Integer) request.getAttribute("org.apache.tomcat.comet.timeout");
+ if (comettimeout != null) attach.setTimeout(comettimeout.longValue());
+ }
}
-
-
- rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
-
- }//while
-
- rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
- if (error || endpoint.isPaused()) {
- return SocketState.CLOSED;
- } else if (comet || isAsync()) {
- return SocketState.LONG;
- } else {
- return (openSocket) ? (readComplete?SocketState.OPEN:SocketState.LONG) : SocketState.CLOSED;
}
-
}
@Override
- protected boolean disableKeepAlive() {
+ protected boolean breakKeepAliveLoop(
+ SocketWrapper<NioChannel> socketWrapper) {
+ // Do sendfile as needed: add socket to sendfile and end
+ if (sendfileData != null && !error) {
+ ((KeyAttachment) socketWrapper).setSendfileData(sendfileData);
+ sendfileData.keepAlive = keepAlive;
+ SelectionKey key = socketWrapper.getSocket().getIOChannel().keyFor(
+ socketWrapper.getSocket().getPoller().getSelector());
+ //do the first write on this thread, might as well
+ openSocket = socketWrapper.getSocket().getPoller().processSendfile(key,
+ (KeyAttachment) socketWrapper, true, true);
+ return true;
+ }
return false;
}
@@ -654,6 +532,11 @@ public class Http11NioProcessor extends AbstractHttp11Processor<NioChannel> {
}
@Override
+ protected void setSocketWrapper(SocketWrapper<NioChannel> socketWrapper) {
+ this.socket = socketWrapper;
+ }
+
+ @Override
protected AbstractInputBuffer<NioChannel> getInputBuffer() {
return inputBuffer;
}
diff --git a/java/org/apache/coyote/http11/Http11NioProtocol.java b/java/org/apache/coyote/http11/Http11NioProtocol.java
index 1ff59ff..a0f2498 100644
--- a/java/org/apache/coyote/http11/Http11NioProtocol.java
+++ b/java/org/apache/coyote/http11/Http11NioProtocol.java
@@ -74,9 +74,6 @@ public class Http11NioProtocol extends AbstractHttp11JsseProtocol {
// -------------------- Properties--------------------
-
- private int socketCloseDelay=-1;
-
private Http11ConnectionHandler cHandler;
// -------------------- Pool setup --------------------
@@ -96,8 +93,6 @@ public class Http11NioProtocol extends AbstractHttp11JsseProtocol {
public long getSelectorTimeout() {
return ((NioEndpoint)endpoint).getSelectorTimeout();
}
- // *
-
public void setAcceptorThreadPriority(int threadPriority) {
((NioEndpoint)endpoint).setAcceptorThreadPriority(threadPriority);
@@ -125,14 +120,6 @@ public class Http11NioProtocol extends AbstractHttp11JsseProtocol {
}
// -------------------- Tcp setup --------------------
- public int getSocketCloseDelay() {
- return socketCloseDelay;
- }
-
- public void setSocketCloseDelay( int d ) {
- socketCloseDelay=d;
- }
-
public void setOomParachute(int oomParachute) {
((NioEndpoint)endpoint).setOomParachute(oomParachute);
}
diff --git a/java/org/apache/coyote/http11/Http11Processor.java b/java/org/apache/coyote/http11/Http11Processor.java
index 25bbea3..e157284 100644
--- a/java/org/apache/coyote/http11/Http11Processor.java
+++ b/java/org/apache/coyote/http11/Http11Processor.java
@@ -18,16 +18,13 @@ package org.apache.coyote.http11;
import java.io.EOFException;
import java.io.IOException;
-import java.io.InterruptedIOException;
import java.net.InetAddress;
import java.net.Socket;
import org.apache.coyote.ActionCode;
-import org.apache.coyote.RequestInfo;
import org.apache.coyote.http11.filters.BufferedInputFilter;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
import org.apache.tomcat.util.net.JIoEndpoint;
import org.apache.tomcat.util.net.SSLSupport;
@@ -121,265 +118,101 @@ public class Http11Processor extends AbstractHttp11Processor<Socket> {
}
- /**
- * Process pipelined HTTP requests using the specified input and output
- * streams.
- *
- * @param socketWrapper Socket from which the HTTP requests will be read
- * and the HTTP responses will be written.
- *
- * @throws IOException error during an I/O operation
- */
@Override
- public SocketState process(SocketWrapper<Socket> socketWrapper)
- throws IOException {
- RequestInfo rp = request.getRequestProcessor();
- rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
-
- // Setting up the I/O
- this.socket = socketWrapper;
- inputBuffer.init(socketWrapper, endpoint);
- outputBuffer.init(socketWrapper, endpoint);
-
- // Error flag
- error = false;
- keepAlive = true;
- comet = false;
-
- int soTimeout = endpoint.getSoTimeout();
-
- if (disableKeepAlive()) {
- socketWrapper.setKeepAliveLeft(0);
+ protected boolean disableKeepAlive() {
+ int threadRatio = -1;
+ // These may return zero or negative values
+ // Only calculate a thread ratio when both are >0 to ensure we get a
+ // sensible result
+ int maxThreads, threadsBusy;
+ if ((maxThreads = endpoint.getMaxThreads()) > 0
+ && (threadsBusy = endpoint.getCurrentThreadsBusy()) > 0) {
+ threadRatio = (threadsBusy * 100) / maxThreads;
}
-
- try {
- socket.getSocket().setSoTimeout(soTimeout);
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.debug(sm.getString("http11processor.socket.timeout"), t);
- error = true;
+ // Disable keep-alive if we are running low on threads
+ if (threadRatio > getDisableKeepAlivePercentage()) {
+ return true;
}
+
+ return false;
+ }
- boolean keptAlive = socketWrapper.isKeptAlive();
-
- while (!error && keepAlive && !endpoint.isPaused()) {
- // Parsing the request header
- try {
- int standardTimeout = 0;
- if (keptAlive) {
- if (keepAliveTimeout > 0) {
- standardTimeout = keepAliveTimeout;
- } else if (soTimeout > 0) {
- standardTimeout = soTimeout;
- }
- }
- /*
- * When there is no data in the buffer and this is not the first
- * request on this connection and timeouts are being used the
- * first read for this request may need a different timeout to
- * take account of time spent waiting for a processing thread.
- *
- * This is a little hacky but better than exposing the socket
- * and the timeout info to the InputBuffer
- */
- if (inputBuffer.lastValid == 0 &&
- socketWrapper.getLastAccess() > -1 &&
- standardTimeout > 0) {
-
- long queueTime = System.currentTimeMillis() -
- socketWrapper.getLastAccess();
- int firstReadTimeout;
- if (queueTime >= standardTimeout) {
- // Queued for longer than timeout but there might be
- // data so use shortest possible timeout
- firstReadTimeout = 1;
- } else {
- // Cast is safe since queueTime must be less than
- // standardTimeout which is an int
- firstReadTimeout = standardTimeout - (int) queueTime;
- }
- socket.getSocket().setSoTimeout(firstReadTimeout);
- if (!inputBuffer.fill()) {
- throw new EOFException(sm.getString("iib.eof.error"));
- }
- }
- if (standardTimeout > 0) {
- socket.getSocket().setSoTimeout(standardTimeout);
- }
+ @Override
+ protected void setRequestLineReadTimeout() throws IOException {
+
+ /*
+ * When there is no data in the buffer and this is not the first
+ * request on this connection and timeouts are being used the
+ * first read for this request may need a different timeout to
+ * take account of time spent waiting for a processing thread.
+ *
+ * This is a little hacky but better than exposing the socket
+ * and the timeout info to the InputBuffer
+ */
+ if (inputBuffer.lastValid == 0 && socket.getLastAccess() > -1) {
+ int firstReadTimeout;
+ if (keepAliveTimeout == -1) {
+ firstReadTimeout = 0;
+ } else {
+ long queueTime =
+ System.currentTimeMillis() - socket.getLastAccess();
- inputBuffer.parseRequestLine(false);
- if (endpoint.isPaused()) {
- // 503 - Service unavailable
- response.setStatus(503);
- adapter.log(request, response, 0);
- error = true;
+ if (queueTime >= keepAliveTimeout) {
+ // Queued for longer than timeout but there might be
+ // data so use shortest possible timeout
+ firstReadTimeout = 1;
} else {
- request.setStartTime(System.currentTimeMillis());
- keptAlive = true;
- if (disableUploadTimeout) {
- socket.getSocket().setSoTimeout(soTimeout);
- } else {
- socket.getSocket().setSoTimeout(connectionUploadTimeout);
- }
- inputBuffer.parseHeaders();
- }
- } catch (IOException e) {
- error = true;
- break;
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("http11processor.header.parse"), t);
- }
- // 400 - Bad Request
- response.setStatus(400);
- adapter.log(request, response, 0);
- error = true;
- }
-
- if (!error) {
- // Setting up filters, and parse some request headers
- rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
- try {
- prepareRequest();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("http11processor.request.prepare"), t);
- }
- // 400 - Internal Server Error
- response.setStatus(400);
- adapter.log(request, response, 0);
- error = true;
+ // Cast is safe since queueTime must be less than
+ // keepAliveTimeout which is an int
+ firstReadTimeout = keepAliveTimeout - (int) queueTime;
}
}
-
- if (maxKeepAliveRequests == 1) {
- keepAlive = false;
- } else if (maxKeepAliveRequests > 0 &&
- socketWrapper.decrementKeepAlive() <= 0) {
- keepAlive = false;
- }
-
- // Process the request in the adapter
- if (!error) {
- try {
- rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
- adapter.service(request, response);
- // Handle when the response was committed before a serious
- // error occurred. Throwing a ServletException should both
- // set the status to 500 and set the errorException.
- // If we fail here, then the response is likely already
- // committed, so we can't try and set headers.
- if(keepAlive && !error) { // Avoid checking twice.
- error = response.getErrorException() != null ||
- (!isAsync() &&
- statusDropsConnection(response.getStatus()));
- }
-
- } catch (InterruptedIOException e) {
- error = true;
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.error(sm.getString("http11processor.request.process"), t);
- // 500 - Internal Server Error
- response.setStatus(500);
- adapter.log(request, response, 0);
- error = true;
- }
- }
-
- // Finish the handling of the request
- try {
- rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
- // If we know we are closing the connection, don't drain input.
- // This way uploading a 100GB file doesn't tie up the thread
- // if the servlet has rejected it.
-
- if(error && !isAsync())
- inputBuffer.setSwallowInput(false);
- if (!isAsync())
- endRequest();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.error(sm.getString("http11processor.request.finish"), t);
- // 500 - Internal Server Error
- response.setStatus(500);
- adapter.log(request, response, 0);
- error = true;
- }
- try {
- rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.error(sm.getString("http11processor.response.finish"), t);
- error = true;
+ socket.getSocket().setSoTimeout(firstReadTimeout);
+ if (!inputBuffer.fill()) {
+ throw new EOFException(sm.getString("iib.eof.error"));
}
+ // Once the first byte has been read, the standard timeout should be
+ // used so restore it here.
+ socket.getSocket().setSoTimeout(endpoint.getSoTimeout());
+ }
+ }
- // If there was an error, make sure the request is counted as
- // and error, and update the statistics counter
- if (error) {
- response.setStatus(500);
- }
- request.updateCounters();
- rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
+ @Override
+ protected boolean handleIncompleteRequestLineRead() {
+ // Not used with BIO since it uses blocking reads
+ return false;
+ }
- // Don't reset the param - we'll see it as ended. Next request
- // will reset it
- // thrA.setParam(null);
- // Next request
- if (!isAsync() || error) {
- inputBuffer.nextRequest();
- outputBuffer.nextRequest();
- }
- // If we don't have a pipe-lined request allow this thread to be
- // used by another connection
- if (isAsync() || error || inputBuffer.lastValid == 0) {
- break;
- }
- }
-
- rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
- if (error || endpoint.isPaused()) {
- return SocketState.CLOSED;
- } else if (isAsync()) {
- return SocketState.LONG;
- } else {
- if (!keepAlive) {
- return SocketState.CLOSED;
- } else {
- return SocketState.OPEN;
- }
- }
+ @Override
+ protected void setSocketTimeout(int timeout) throws IOException {
+ socket.getSocket().setSoTimeout(timeout);
}
@Override
- protected boolean disableKeepAlive() {
- int threadRatio = -1;
- // These may return zero or negative values
- // Only calculate a thread ratio when both are >0 to ensure we get a
- // sensible result
- if (endpoint.getCurrentThreadsBusy() >0 &&
- endpoint.getMaxThreads() >0) {
- threadRatio = (endpoint.getCurrentThreadsBusy() * 100)
- / endpoint.getMaxThreads();
- }
- // Disable keep-alive if we are running low on threads
- if (threadRatio > getDisableKeepAlivePercentage()) {
+ protected void setCometTimeouts(SocketWrapper<Socket> socketWrapper) {
+ // NO-OP for BIO
+ }
+
+
+ @Override
+ protected boolean breakKeepAliveLoop(SocketWrapper<Socket> socketWrapper) {
+ openSocket = keepAlive;
+ // If we don't have a pipe-lined request allow this thread to be
+ // used by another connection
+ if (inputBuffer.lastValid == 0) {
return true;
}
-
return false;
}
-
+
@Override
protected void resetTimeouts() {
- // NOOP for APR
+ // NOOP for BIO
}
@@ -564,6 +397,11 @@ public class Http11Processor extends AbstractHttp11Processor<Socket> {
}
@Override
+ protected void setSocketWrapper(SocketWrapper<Socket> socketWrapper) {
+ this.socket = socketWrapper;
+ }
+
+ @Override
protected AbstractInputBuffer<Socket> getInputBuffer() {
return inputBuffer;
}
diff --git a/java/org/apache/coyote/http11/InternalNioOutputBuffer.java b/java/org/apache/coyote/http11/InternalNioOutputBuffer.java
index 938b7cf..dca3fae 100644
--- a/java/org/apache/coyote/http11/InternalNioOutputBuffer.java
+++ b/java/org/apache/coyote/http11/InternalNioOutputBuffer.java
@@ -176,7 +176,6 @@ public class InternalNioOutputBuffer extends AbstractOutputBuffer<NioChannel> {
if ( selector != null ) pool.put(selector);
}
if ( block ) bytebuffer.clear(); //only clear
- this.total = 0;
return written;
}
@@ -212,7 +211,6 @@ public class InternalNioOutputBuffer extends AbstractOutputBuffer<NioChannel> {
}
- private int total = 0;
private synchronized void addToBB(byte[] buf, int offset, int length) throws IOException {
while (length > 0) {
int thisTime = length;
@@ -227,7 +225,6 @@ public class InternalNioOutputBuffer extends AbstractOutputBuffer<NioChannel> {
socket.getBufHandler().getWriteBuffer().put(buf, offset, thisTime);
length = length - thisTime;
offset = offset + thisTime;
- total += thisTime;
}
NioEndpoint.KeyAttachment ka = (NioEndpoint.KeyAttachment)socket.getAttachment(false);
if ( ka!= null ) ka.access();//prevent timeouts for just doing client writes
diff --git a/java/org/apache/el/parser/ELParser.java b/java/org/apache/el/parser/ELParser.java
index c594ec0..e29b082 100644
--- a/java/org/apache/el/parser/ELParser.java
+++ b/java/org/apache/el/parser/ELParser.java
@@ -3,6 +3,8 @@ package org.apache.el.parser;
import java.io.StringReader;
import javax.el.ELException;
+
+ at SuppressWarnings("all") // Ignore warnings in generated code
public class ELParser/*@bgen(jjtree)*/implements ELParserTreeConstants, ELParserConstants {/*@bgen(jjtree)*/
protected JJTELParserState jjtree = new JJTELParserState();public static Node parse(String ref) throws ELException
{
diff --git a/java/org/apache/el/parser/ELParserTokenManager.java b/java/org/apache/el/parser/ELParserTokenManager.java
index 92fdae8..e277cf8 100644
--- a/java/org/apache/el/parser/ELParserTokenManager.java
+++ b/java/org/apache/el/parser/ELParserTokenManager.java
@@ -2,6 +2,7 @@
package org.apache.el.parser;
/** Token Manager. */
+ at SuppressWarnings("all") // Ignore warnings in generated code
public class ELParserTokenManager implements ELParserConstants
{
diff --git a/java/org/apache/el/parser/JJTELParserState.java b/java/org/apache/el/parser/JJTELParserState.java
index 9615b87..3ba4d77 100644
--- a/java/org/apache/el/parser/JJTELParserState.java
+++ b/java/org/apache/el/parser/JJTELParserState.java
@@ -1,6 +1,7 @@
/* Generated By:JavaCC: Do not edit this line. JJTELParserState.java Version 5.0 */
package org.apache.el.parser;
+ at SuppressWarnings("all") // Ignore warnings in generated code
public class JJTELParserState {
private java.util.List<Node> nodes;
private java.util.List<Integer> marks;
diff --git a/java/org/apache/el/parser/SimpleCharStream.java b/java/org/apache/el/parser/SimpleCharStream.java
index e484965..0e7f31e 100644
--- a/java/org/apache/el/parser/SimpleCharStream.java
+++ b/java/org/apache/el/parser/SimpleCharStream.java
@@ -6,7 +6,7 @@ package org.apache.el.parser;
* An implementation of interface CharStream, where the stream is assumed to
* contain only ASCII characters (without unicode processing).
*/
-
+ at SuppressWarnings("all") // Ignore warnings in generated code
public class SimpleCharStream
{
/** Whether parser is static. */
diff --git a/java/org/apache/el/parser/Token.java b/java/org/apache/el/parser/Token.java
index 3aa02cc..a3fe367 100644
--- a/java/org/apache/el/parser/Token.java
+++ b/java/org/apache/el/parser/Token.java
@@ -5,7 +5,7 @@ package org.apache.el.parser;
/**
* Describes the input token stream.
*/
-
+ at SuppressWarnings("all") // Ignore warnings in generated code
public class Token implements java.io.Serializable {
/**
diff --git a/java/org/apache/el/parser/TokenMgrError.java b/java/org/apache/el/parser/TokenMgrError.java
index 2964ed0..7fee656 100644
--- a/java/org/apache/el/parser/TokenMgrError.java
+++ b/java/org/apache/el/parser/TokenMgrError.java
@@ -3,6 +3,7 @@
package org.apache.el.parser;
/** Token Manager Error. */
+ at SuppressWarnings("all") // Ignore warnings in generated code
public class TokenMgrError extends Error
{
diff --git a/java/org/apache/jasper/compiler/JspRuntimeContext.java b/java/org/apache/jasper/compiler/JspRuntimeContext.java
index bdc6822..893541a 100644
--- a/java/org/apache/jasper/compiler/JspRuntimeContext.java
+++ b/java/org/apache/jasper/compiler/JspRuntimeContext.java
@@ -57,7 +57,7 @@ import org.apache.juli.logging.LogFactory;
* Only used if a web application context is a directory.
*
* @author Glenn L. Nielsen
- * @version $Revision: 1081391 $
+ * @version $Revision: 1172614 $
*/
public final class JspRuntimeContext {
@@ -250,6 +250,7 @@ public final class JspRuntimeContext {
replaced.getJspUri(), context.getContextPath()));
}
unloadJspServletWrapper(replaced);
+ entry.clearReplaced();
}
return entry;
}
diff --git a/java/org/apache/jasper/util/FastRemovalDequeue.java b/java/org/apache/jasper/util/FastRemovalDequeue.java
index 74a65fd..b43d7e4 100644
--- a/java/org/apache/jasper/util/FastRemovalDequeue.java
+++ b/java/org/apache/jasper/util/FastRemovalDequeue.java
@@ -284,6 +284,10 @@ public class FastRemovalDequeue<T> {
this.replaced = replaced;
}
+ public final void clearReplaced() {
+ this.replaced = null;
+ }
+
private final Entry getNext() {
return next;
}
diff --git a/java/org/apache/juli/OneLineFormatter.java b/java/org/apache/juli/OneLineFormatter.java
index f090c73..1ee6557 100644
--- a/java/org/apache/juli/OneLineFormatter.java
+++ b/java/org/apache/juli/OneLineFormatter.java
@@ -76,6 +76,12 @@ public class OneLineFormatter extends Formatter {
sb.append(' ');
sb.append(record.getLevel());
+ // Thread
+ sb.append(' ');
+ sb.append('[');
+ sb.append(Thread.currentThread().getName());
+ sb.append(']');
+
// Source
sb.append(' ');
sb.append(record.getSourceClassName());
diff --git a/java/org/apache/naming/NamingContext.java b/java/org/apache/naming/NamingContext.java
index cabfd8d..ae2de7f 100644
--- a/java/org/apache/naming/NamingContext.java
+++ b/java/org/apache/naming/NamingContext.java
@@ -44,7 +44,7 @@ import javax.naming.spi.NamingManager;
* Catalina JNDI Context implementation.
*
* @author Remy Maucherat
- * @version $Id: NamingContext.java 1033925 2010-11-11 13:00:52Z markt $
+ * @version $Id: NamingContext.java 1163633 2011-08-31 14:20:17Z markt $
*/
public class NamingContext implements Context {
@@ -743,8 +743,8 @@ public class NamingContext implements Context {
* @exception NamingException if a naming exception is encountered
*/
@Override
- public void close()
- throws NamingException {
+ public void close() throws NamingException {
+ checkWritable();
env.clear();
}
diff --git a/java/org/apache/tomcat/util/digester/Digester.java b/java/org/apache/tomcat/util/digester/Digester.java
index 3090120..6576e3d 100644
--- a/java/org/apache/tomcat/util/digester/Digester.java
+++ b/java/org/apache/tomcat/util/digester/Digester.java
@@ -102,7 +102,7 @@ public class Digester extends DefaultHandler {
initialized = true;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
- LogFactory.getLog("org.apache.commons.digester.Digester").
+ LogFactory.getLog("org.apache.tomcat.util.digester.Digester").
error("Unable to load property source["+className+"].",t);
}
}
@@ -330,14 +330,14 @@ public class Digester extends DefaultHandler {
* The Log to which most logging calls will be made.
*/
protected Log log =
- LogFactory.getLog("org.apache.commons.digester.Digester");
+ LogFactory.getLog("org.apache.tomcat.util.digester.Digester");
/**
* The Log to which all SAX event related logging calls will be made.
*/
protected Log saxLog =
- LogFactory.getLog("org.apache.commons.digester.Digester.sax");
+ LogFactory.getLog("org.apache.tomcat.util.digester.Digester.sax");
/** Stacks used for interrule communication, indexed by name String */
@@ -2530,8 +2530,8 @@ public class Digester extends DefaultHandler {
return;
}
- log = LogFactory.getLog("org.apache.commons.digester.Digester");
- saxLog = LogFactory.getLog("org.apache.commons.digester.Digester.sax");
+ log = LogFactory.getLog("org.apache.tomcat.util.digester.Digester");
+ saxLog = LogFactory.getLog("org.apache.tomcat.util.digester.Digester.sax");
// Perform lazy configuration as needed
initialize(); // call hook method for subclasses that want to be initialized once only
diff --git a/java/org/apache/tomcat/util/digester/GenericParser.java b/java/org/apache/tomcat/util/digester/GenericParser.java
index f75e671..02e41a4 100644
--- a/java/org/apache/tomcat/util/digester/GenericParser.java
+++ b/java/org/apache/tomcat/util/digester/GenericParser.java
@@ -41,7 +41,7 @@ public class GenericParser{
* The Log to which all SAX event related logging calls will be made.
*/
private static final Log log =
- LogFactory.getLog("org.apache.commons.digester.Digester.sax");
+ LogFactory.getLog("org.apache.tomcat.util.digester.Digester.sax");
/**
* The JAXP 1.2 property required to set up the schema location.
diff --git a/java/org/apache/tomcat/util/digester/XercesParser.java b/java/org/apache/tomcat/util/digester/XercesParser.java
index 5abdd74..b337539 100644
--- a/java/org/apache/tomcat/util/digester/XercesParser.java
+++ b/java/org/apache/tomcat/util/digester/XercesParser.java
@@ -46,7 +46,7 @@ public class XercesParser{
* The Log to which all SAX event related logging calls will be made.
*/
private static final Log log =
- LogFactory.getLog("org.apache.commons.digester.Digester.sax");
+ LogFactory.getLog("org.apache.tomcat.util.digester.Digester.sax");
/**
diff --git a/java/org/apache/tomcat/util/modeler/BaseModelMBean.java b/java/org/apache/tomcat/util/modeler/BaseModelMBean.java
index 251a42c..7d5145b 100644
--- a/java/org/apache/tomcat/util/modeler/BaseModelMBean.java
+++ b/java/org/apache/tomcat/util/modeler/BaseModelMBean.java
@@ -499,6 +499,8 @@ public class BaseModelMBean implements DynamicMBean, MBeanRegistration, ModelMBe
*
* @exception InstanceNotFoundException if the managed resource object
* cannot be found
+ * @exception InvalidTargetObjectTypeException if the managed resource
+ * object is of the wrong type
* @exception MBeanException if the initializer of the object throws
* an exception
* @exception RuntimeOperationsException if the managed resource or the
diff --git a/java/org/apache/tomcat/util/modeler/ManagedBean.java b/java/org/apache/tomcat/util/modeler/ManagedBean.java
index 768771d..b19e753 100644
--- a/java/org/apache/tomcat/util/modeler/ManagedBean.java
+++ b/java/org/apache/tomcat/util/modeler/ManagedBean.java
@@ -42,7 +42,7 @@ import javax.management.ServiceNotFoundException;
* descriptor.</p>
*
* @author Craig R. McClanahan
- * @version $Id: ManagedBean.java 1038846 2010-11-24 22:08:38Z markt $
+ * @version $Id: ManagedBean.java 1176157 2011-09-27 01:21:08Z kkolinko $
*/
public class ManagedBean implements java.io.Serializable {
@@ -483,148 +483,141 @@ public class ManagedBean implements java.io.Serializable {
}
Method getGetter(String aname, BaseModelMBean mbean, Object resource)
- throws AttributeNotFoundException, MBeanException, ReflectionException {
- // TODO: do we need caching ? JMX is for management, it's not supposed to require lots of performance.
- Method m=null; // (Method)getAttMap.get( name );
-
- if( m==null ) {
- AttributeInfo attrInfo = attributes.get(aname);
- // Look up the actual operation to be used
- if (attrInfo == null)
- throw new AttributeNotFoundException(" Cannot find attribute " + aname + " for " + resource);
-
- String getMethod = attrInfo.getGetMethod();
- if (getMethod == null)
- throw new AttributeNotFoundException("Cannot find attribute " + aname + " get method name");
-
- Object object = null;
- NoSuchMethodException exception = null;
+ throws AttributeNotFoundException, ReflectionException {
+
+ Method m = null;
+
+ AttributeInfo attrInfo = attributes.get(aname);
+ // Look up the actual operation to be used
+ if (attrInfo == null)
+ throw new AttributeNotFoundException(" Cannot find attribute " + aname + " for " + resource);
+
+ String getMethod = attrInfo.getGetMethod();
+ if (getMethod == null)
+ throw new AttributeNotFoundException("Cannot find attribute " + aname + " get method name");
+
+ Object object = null;
+ NoSuchMethodException exception = null;
+ try {
+ object = mbean;
+ m = object.getClass().getMethod(getMethod, NO_ARGS_PARAM_SIG);
+ } catch (NoSuchMethodException e) {
+ exception = e;
+ }
+ if( m== null && resource != null ) {
try {
- object = mbean;
+ object = resource;
m = object.getClass().getMethod(getMethod, NO_ARGS_PARAM_SIG);
+ exception=null;
} catch (NoSuchMethodException e) {
exception = e;
}
- if( m== null && resource != null ) {
- try {
- object = resource;
- m = object.getClass().getMethod(getMethod, NO_ARGS_PARAM_SIG);
- exception=null;
- } catch (NoSuchMethodException e) {
- exception = e;
- }
- }
- if( exception != null )
- throw new ReflectionException(exception,
- "Cannot find getter method " + getMethod);
- //getAttMap.put( name, m );
}
+ if( exception != null )
+ throw new ReflectionException(exception,
+ "Cannot find getter method " + getMethod);
return m;
}
public Method getSetter(String aname, BaseModelMBean bean, Object resource)
- throws AttributeNotFoundException, MBeanException, ReflectionException {
- // Cache may be needed for getters, but it is a really bad idea for setters, this is far
- // less frequent.
- Method m=null;//(Method)setAttMap.get( name );
+ throws AttributeNotFoundException, ReflectionException {
- if( m==null ) {
- AttributeInfo attrInfo = attributes.get(aname);
- if (attrInfo == null)
- throw new AttributeNotFoundException(" Cannot find attribute " + aname);
+ Method m = null;
- // Look up the actual operation to be used
- String setMethod = attrInfo.getSetMethod();
- if (setMethod == null)
- throw new AttributeNotFoundException("Cannot find attribute " + aname + " set method name");
+ AttributeInfo attrInfo = attributes.get(aname);
+ if (attrInfo == null)
+ throw new AttributeNotFoundException(" Cannot find attribute " + aname);
- String argType=attrInfo.getType();
+ // Look up the actual operation to be used
+ String setMethod = attrInfo.getSetMethod();
+ if (setMethod == null)
+ throw new AttributeNotFoundException("Cannot find attribute " + aname + " set method name");
- Class<?> signature[] =
- new Class[] { BaseModelMBean.getAttributeClass( argType ) };
+ String argType=attrInfo.getType();
- Object object = null;
- NoSuchMethodException exception = null;
+ Class<?> signature[] =
+ new Class[] { BaseModelMBean.getAttributeClass( argType ) };
+
+ Object object = null;
+ NoSuchMethodException exception = null;
+ try {
+ object = bean;
+ m = object.getClass().getMethod(setMethod, signature);
+ } catch (NoSuchMethodException e) {
+ exception = e;
+ }
+ if( m== null && resource != null ) {
try {
- object = bean;
+ object = resource;
m = object.getClass().getMethod(setMethod, signature);
+ exception=null;
} catch (NoSuchMethodException e) {
exception = e;
}
- if( m== null && resource != null ) {
- try {
- object = resource;
- m = object.getClass().getMethod(setMethod, signature);
- exception=null;
- } catch (NoSuchMethodException e) {
- exception = e;
- }
- }
- if( exception != null )
- throw new ReflectionException(exception,
- "Cannot find setter method " + setMethod +
- " " + resource);
- //setAttMap.put( name, m );
}
+ if( exception != null )
+ throw new ReflectionException(exception,
+ "Cannot find setter method " + setMethod +
+ " " + resource);
return m;
}
public Method getInvoke(String aname, Object[] params, String[] signature, BaseModelMBean bean, Object resource)
throws MBeanException, ReflectionException {
+
Method method = null;
- if (method == null) {
- if (params == null)
- params = new Object[0];
- if (signature == null)
- signature = new String[0];
- if (params.length != signature.length)
- throw new RuntimeOperationsException(
- new IllegalArgumentException(
- "Inconsistent arguments and signature"),
- "Inconsistent arguments and signature");
-
- // Acquire the ModelMBeanOperationInfo information for
- // the requested operation
- OperationInfo opInfo = operations.get(aname);
- if (opInfo == null)
- throw new MBeanException(new ServiceNotFoundException(
- "Cannot find operation " + aname),
- "Cannot find operation " + aname);
-
- // Prepare the signature required by Java reflection APIs
- // FIXME - should we use the signature from opInfo?
- Class<?> types[] = new Class[signature.length];
- for (int i = 0; i < signature.length; i++) {
- types[i] = BaseModelMBean.getAttributeClass(signature[i]);
- }
+
+ if (params == null)
+ params = new Object[0];
+ if (signature == null)
+ signature = new String[0];
+ if (params.length != signature.length)
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException(
+ "Inconsistent arguments and signature"),
+ "Inconsistent arguments and signature");
+
+ // Acquire the ModelMBeanOperationInfo information for
+ // the requested operation
+ OperationInfo opInfo = operations.get(aname);
+ if (opInfo == null)
+ throw new MBeanException(new ServiceNotFoundException(
+ "Cannot find operation " + aname),
+ "Cannot find operation " + aname);
+
+ // Prepare the signature required by Java reflection APIs
+ // FIXME - should we use the signature from opInfo?
+ Class<?> types[] = new Class[signature.length];
+ for (int i = 0; i < signature.length; i++) {
+ types[i] = BaseModelMBean.getAttributeClass(signature[i]);
+ }
- // Locate the method to be invoked, either in this MBean itself
- // or in the corresponding managed resource
- // FIXME - Accessible methods in superinterfaces?
- Object object = null;
- Exception exception = null;
- try {
- object = bean;
+ // Locate the method to be invoked, either in this MBean itself
+ // or in the corresponding managed resource
+ // FIXME - Accessible methods in superinterfaces?
+ Object object = null;
+ Exception exception = null;
+ try {
+ object = bean;
+ method = object.getClass().getMethod(aname, types);
+ } catch (NoSuchMethodException e) {
+ exception = e;
+ }
+ try {
+ if ((method == null) && (resource != null)) {
+ object = resource;
method = object.getClass().getMethod(aname, types);
- } catch (NoSuchMethodException e) {
- exception = e;
}
- try {
- if ((method == null) && (resource != null)) {
- object = resource;
- method = object.getClass().getMethod(aname, types);
- }
- } catch (NoSuchMethodException e) {
- exception = e;
- }
- if (method == null) {
- throw new ReflectionException(exception, "Cannot find method "
- + aname + " with this signature");
- }
- // invokeAttMap.put(mkey, method);
+ } catch (NoSuchMethodException e) {
+ exception = e;
+ }
+ if (method == null) {
+ throw new ReflectionException(exception, "Cannot find method "
+ + aname + " with this signature");
}
+
return method;
}
diff --git a/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsDigesterSource.java b/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsDigesterSource.java
index 4e8d858..dd4d00e 100644
--- a/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsDigesterSource.java
+++ b/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsDigesterSource.java
@@ -44,7 +44,7 @@ public class MbeansDescriptorsDigesterSource extends ModelerSource
List<ObjectName> mbeans = new ArrayList<ObjectName>();
protected static volatile Digester digester = null;
- protected static Digester createDigester(Registry registry) {
+ protected static Digester createDigester() {
Digester digester = new Digester();
digester.setNamespaceAware(false);
@@ -223,7 +223,7 @@ public class MbeansDescriptorsDigesterSource extends ModelerSource
InputStream stream = (InputStream) source;
if (digester == null) {
- digester = createDigester(registry);
+ digester = createDigester();
}
ArrayList<ManagedBean> loadedMbeans = new ArrayList<ManagedBean>();
diff --git a/java/org/apache/tomcat/util/modeler/modules/MbeansSource.java b/java/org/apache/tomcat/util/modeler/modules/MbeansSource.java
index b244ac0..a9e4a0a 100644
--- a/java/org/apache/tomcat/util/modeler/modules/MbeansSource.java
+++ b/java/org/apache/tomcat/util/modeler/modules/MbeansSource.java
@@ -340,8 +340,7 @@ public class MbeansSource extends ModelerSource implements MbeansSourceMBean
" " + value);
ObjectName oname=new ObjectName(objectName);
// find the type
- if( type==null )
- type=registry.getType( oname, attName );
+ type=registry.getType( oname, attName );
if( type==null ) {
log.info("Can't find attribute " + objectName + " " + attName );
diff --git a/java/org/apache/tomcat/util/net/AbstractEndpoint.java b/java/org/apache/tomcat/util/net/AbstractEndpoint.java
index f606817..27ef98b 100644
--- a/java/org/apache/tomcat/util/net/AbstractEndpoint.java
+++ b/java/org/apache/tomcat/util/net/AbstractEndpoint.java
@@ -170,12 +170,18 @@ public abstract class AbstractEndpoint {
private BindState bindState = BindState.UNBOUND;
/**
- * Keepalive timeout, if lesser or equal to 0 then soTimeout will be used.
+ * Keepalive timeout, if not set the soTimeout is used.
*/
- private int keepAliveTimeout = -1;
- public int getKeepAliveTimeout() { return keepAliveTimeout;}
+ private Integer keepAliveTimeout = null;
+ public int getKeepAliveTimeout() {
+ if (keepAliveTimeout == null) {
+ return getSoTimeout();
+ } else {
+ return keepAliveTimeout.intValue();
+ }
+ }
public void setKeepAliveTimeout(int keepAliveTimeout) {
- this.keepAliveTimeout = keepAliveTimeout;
+ this.keepAliveTimeout = Integer.valueOf(keepAliveTimeout);
}
@@ -559,9 +565,12 @@ public abstract class AbstractEndpoint {
protected abstract Log getLog();
// Flags to indicate optional feature support
+ // Some of these are always hard-coded, some are hard-coded to false (i.e.
+ // the endpoint does not support them) and some are configurable.
public abstract boolean getUseSendfile();
public abstract boolean getUseComet();
public abstract boolean getUseCometTimeout();
+ public abstract boolean getUsePolling();
protected LimitLatch initializeConnectionLatch() {
if (connectionLimitLatch==null) {
diff --git a/java/org/apache/tomcat/util/net/AprEndpoint.java b/java/org/apache/tomcat/util/net/AprEndpoint.java
index 4bc9a9a..7534cfb 100644
--- a/java/org/apache/tomcat/util/net/AprEndpoint.java
+++ b/java/org/apache/tomcat/util/net/AprEndpoint.java
@@ -160,6 +160,8 @@ public class AprEndpoint extends AbstractEndpoint {
public boolean getUseComet() { return useComet; }
@Override
public boolean getUseCometTimeout() { return false; } // Not supported
+ @Override
+ public boolean getUsePolling() { return true; } // Always supported
/**
@@ -750,8 +752,7 @@ public class AprEndpoint extends AbstractEndpoint {
Socket.optSet(socket, Socket.APR_SO_LINGER, socketProperties.getSoLingerTime());
if (socketProperties.getTcpNoDelay())
Socket.optSet(socket, Socket.APR_TCP_NODELAY, (socketProperties.getTcpNoDelay() ? 1 : 0));
- if (socketProperties.getSoTimeout() > 0)
- Socket.timeoutSet(socket, socketProperties.getSoTimeout() * 1000);
+ Socket.timeoutSet(socket, socketProperties.getSoTimeout() * 1000);
// 2: SSL handshake
step = 2;
@@ -1097,14 +1098,20 @@ public class AprEndpoint extends AbstractEndpoint {
*/
public class Poller extends Thread {
- protected long serverPollset = 0;
- protected long pool = 0;
- protected long[] desc;
+ // Need two pollsets since the socketTimeout and the keep-alive timeout
+ // can have different values.
+ private long connectionPollset = 0;
+ private long keepAlivePollset = 0;
+ private long pool = 0;
+ private long[] desc;
- protected long[] addS;
- protected volatile int addCount = 0;
+ private long[] addSocket;
+ private boolean[] addSocketKeepAlive;
+
+ private volatile int addCount = 0;
- protected boolean comet = true;
+ private boolean comet = true;
+ private boolean separateKeepAlive = false;
protected volatile int keepAliveCount = 0;
public int getKeepAliveCount() { return keepAliveCount; }
@@ -1114,28 +1121,41 @@ public class AprEndpoint extends AbstractEndpoint {
}
/**
- * Create the poller. With some versions of APR, the maximum poller size will
- * be 62 (recompiling APR is necessary to remove this limitation).
+ * Create the poller. With some versions of APR, the maximum poller size
+ * will be 62 (recompiling APR is necessary to remove this limitation).
*/
protected void init() {
pool = Pool.create(serverSockPool);
int size = getMaxConnections() / pollerThreadCount;
- int timeout = getKeepAliveTimeout();
- if (timeout <= 0) {
- timeout = socketProperties.getSoTimeout();
+ int keepAliveTimeout = getKeepAliveTimeout();
+ int socketTimeout = socketProperties.getSoTimeout();
+ if (keepAliveTimeout != socketTimeout && !comet) {
+ separateKeepAlive = true;
+ }
+ connectionPollset = allocatePoller(size, pool, socketTimeout);
+ if (separateKeepAlive) {
+ keepAlivePollset = allocatePoller(size, pool, keepAliveTimeout);
}
- serverPollset = allocatePoller(size, pool, timeout);
- if (serverPollset == 0 && size > 1024) {
+ if (connectionPollset == 0 && size > 1024) {
size = 1024;
- serverPollset = allocatePoller(size, pool, timeout);
+ connectionPollset = allocatePoller(size, pool, socketTimeout);
+ if (separateKeepAlive) {
+ keepAlivePollset =
+ allocatePoller(size, pool, keepAliveTimeout);
+ }
}
- if (serverPollset == 0) {
+ if (connectionPollset == 0) {
size = 62;
- serverPollset = allocatePoller(size, pool, timeout);
+ connectionPollset = allocatePoller(size, pool, socketTimeout);
+ if (separateKeepAlive) {
+ keepAlivePollset =
+ allocatePoller(size, pool, keepAliveTimeout);
+ }
}
desc = new long[size * 2];
keepAliveCount = 0;
- addS = new long[size];
+ addSocket = new long[size];
+ addSocketKeepAlive = new boolean[size];
addCount = 0;
}
@@ -1147,21 +1167,15 @@ public class AprEndpoint extends AbstractEndpoint {
// Close all sockets in the add queue
for (int i = 0; i < addCount; i++) {
if (comet) {
- processSocket(addS[i], SocketStatus.STOP);
+ processSocket(addSocket[i], SocketStatus.STOP);
} else {
- destroySocket(addS[i]);
+ destroySocket(addSocket[i]);
}
}
- // Close all sockets still in the poller
- int rv = Poll.pollset(serverPollset, desc);
- if (rv > 0) {
- for (int n = 0; n < rv; n++) {
- if (comet) {
- processSocket(desc[n*2+1], SocketStatus.STOP);
- } else {
- destroySocket(desc[n*2+1]);
- }
- }
+ // Close all sockets still in the pollers
+ closePollset(connectionPollset);
+ if (separateKeepAlive) {
+ closePollset(keepAlivePollset);
}
Pool.destroy(pool);
keepAliveCount = 0;
@@ -1176,6 +1190,19 @@ public class AprEndpoint extends AbstractEndpoint {
}
}
+ private void closePollset(long pollset) {
+ int rv = Poll.pollset(pollset, desc);
+ if (rv > 0) {
+ for (int n = 0; n < rv; n++) {
+ if (comet) {
+ processSocket(desc[n*2+1], SocketStatus.STOP);
+ } else {
+ destroySocket(desc[n*2+1]);
+ }
+ }
+ }
+ }
+
/**
* Add specified socket and associated pool to the poller. The socket will
* be added to a temporary array, and polled first after a maximum amount
@@ -1184,11 +1211,11 @@ public class AprEndpoint extends AbstractEndpoint {
*
* @param socket to add to the poller
*/
- public void add(long socket) {
+ public void add(long socket, boolean keepAlive) {
synchronized (this) {
// Add socket to the list. Newly added sockets will wait
// at most for pollTime before being polled
- if (addCount >= addS.length) {
+ if (addCount >= addSocket.length) {
// Can't do anything: close the socket right away
if (comet) {
processSocket(socket, SocketStatus.ERROR);
@@ -1197,7 +1224,8 @@ public class AprEndpoint extends AbstractEndpoint {
}
return;
}
- addS[addCount] = socket;
+ addSocket[addCount] = socket;
+ addSocketKeepAlive[addCount] = keepAlive;
addCount++;
this.notify();
}
@@ -1249,16 +1277,22 @@ public class AprEndpoint extends AbstractEndpoint {
int successCount = 0;
try {
for (int i = (addCount - 1); i >= 0; i--) {
- int rv = Poll.add
- (serverPollset, addS[i], Poll.APR_POLLIN);
+ int rv;
+ if (separateKeepAlive && addSocketKeepAlive[i]) {
+ rv = Poll.add(keepAlivePollset,
+ addSocket[i], Poll.APR_POLLIN);
+ } else {
+ rv = Poll.add(connectionPollset,
+ addSocket[i], Poll.APR_POLLIN);
+ }
if (rv == Status.APR_SUCCESS) {
successCount++;
} else {
// Can't do anything: close the socket right away
if (comet) {
- processSocket(addS[i], SocketStatus.ERROR);
+ processSocket(addSocket[i], SocketStatus.ERROR);
} else {
- destroySocket(addS[i]);
+ destroySocket(addSocket[i]);
}
}
}
@@ -1270,54 +1304,22 @@ public class AprEndpoint extends AbstractEndpoint {
}
maintainTime += pollTime;
- // Pool for the specified interval
- int rv = Poll.poll(serverPollset, pollTime, desc, true);
- if (rv > 0) {
- keepAliveCount -= rv;
- for (int n = 0; n < rv; n++) {
- // Check for failed sockets and hand this socket off to a worker
- if (((desc[n*2] & Poll.APR_POLLHUP) == Poll.APR_POLLHUP)
- || ((desc[n*2] & Poll.APR_POLLERR) == Poll.APR_POLLERR)
- || (comet && (!processSocket(desc[n*2+1], SocketStatus.OPEN)))
- || (!comet && (!processSocket(desc[n*2+1])))) {
- // Close socket and clear pool
- if (comet) {
- processSocket(desc[n*2+1], SocketStatus.DISCONNECT);
- } else {
- destroySocket(desc[n*2+1]);
- }
- continue;
- }
- }
- } else if (rv < 0) {
- int errn = -rv;
- /* Any non timeup or interrupted error is critical */
- if ((errn != Status.TIMEUP) && (errn != Status.EINTR)) {
- if (errn > Status.APR_OS_START_USERERR) {
- errn -= Status.APR_OS_START_USERERR;
- }
- log.error(sm.getString("endpoint.poll.fail", "" + errn, Error.strerror(errn)));
- // Handle poll critical failure
- synchronized (this) {
- destroy();
- init();
- }
- continue;
- }
+ // Poll for the specified interval
+ if (doPoll(connectionPollset)) {
+ continue;
}
- if (socketProperties.getSoTimeout() > 0 && maintainTime > 1000000L && running) {
- rv = Poll.maintain(serverPollset, desc, true);
+ if (separateKeepAlive && doPoll(keepAlivePollset)) {
+ continue;
+ }
+
+ // Check timeouts (much less frequently that polling)
+ if (maintainTime > 1000000L && running) {
maintainTime = 0;
- if (rv > 0) {
- keepAliveCount -= rv;
- for (int n = 0; n < rv; n++) {
- // Close socket and clear pool
- if (comet) {
- processSocket(desc[n], SocketStatus.TIMEOUT);
- } else {
- destroySocket(desc[n]);
- }
- }
+ if (socketProperties.getSoTimeout() > 0) {
+ doTimeout(connectionPollset);
+ }
+ if (separateKeepAlive) {
+ doTimeout(keepAlivePollset);
}
}
} catch (Throwable t) {
@@ -1333,10 +1335,59 @@ public class AprEndpoint extends AbstractEndpoint {
}
- }
-
+ private boolean doPoll(long pollset) {
+ int rv = Poll.poll(pollset, pollTime, desc, true);
+ if (rv > 0) {
+ keepAliveCount -= rv;
+ for (int n = 0; n < rv; n++) {
+ // Check for failed sockets and hand this socket off to a worker
+ if (((desc[n*2] & Poll.APR_POLLHUP) == Poll.APR_POLLHUP)
+ || ((desc[n*2] & Poll.APR_POLLERR) == Poll.APR_POLLERR)
+ || (comet && (!processSocket(desc[n*2+1], SocketStatus.OPEN)))
+ || (!comet && (!processSocket(desc[n*2+1])))) {
+ // Close socket and clear pool
+ if (comet) {
+ processSocket(desc[n*2+1], SocketStatus.DISCONNECT);
+ } else {
+ destroySocket(desc[n*2+1]);
+ }
+ return true;
+ }
+ }
+ } else if (rv < 0) {
+ int errn = -rv;
+ /* Any non timeup or interrupted error is critical */
+ if ((errn != Status.TIMEUP) && (errn != Status.EINTR)) {
+ if (errn > Status.APR_OS_START_USERERR) {
+ errn -= Status.APR_OS_START_USERERR;
+ }
+ log.error(sm.getString("endpoint.poll.fail", "" + errn, Error.strerror(errn)));
+ // Handle poll critical failure
+ synchronized (this) {
+ destroy();
+ init();
+ }
+ return true;
+ }
+ }
+ return false;
+ }
- // ----------------------------------------------------- Worker Inner Class
+ private void doTimeout(long pollset) {
+ int rv = Poll.maintain(pollset, desc, true);
+ if (rv > 0) {
+ keepAliveCount -= rv;
+ for (int n = 0; n < rv; n++) {
+ // Close socket and clear pool
+ if (comet) {
+ processSocket(desc[n], SocketStatus.TIMEOUT);
+ } else {
+ destroySocket(desc[n]);
+ }
+ }
+ }
+ }
+ }
// ----------------------------------------------- SendfileData Inner Class
@@ -1622,7 +1673,7 @@ public class AprEndpoint extends AbstractEndpoint {
Socket.timeoutSet(state.socket, socketProperties.getSoTimeout() * 1000);
// If all done put the socket back in the poller for
// processing of further requests
- getPoller().add(state.socket);
+ getPoller().add(state.socket, true);
} else {
// Close the socket since this is
// the end of not keep-alive request.
@@ -1713,7 +1764,7 @@ public class AprEndpoint extends AbstractEndpoint {
synchronized (socket) {
if (!deferAccept) {
if (setSocketOptions(socket.getSocket().longValue())) {
- getPoller().add(socket.getSocket().longValue());
+ getPoller().add(socket.getSocket().longValue(), false);
} else {
// Close socket and pool
destroySocket(socket.getSocket().longValue());
diff --git a/java/org/apache/tomcat/util/net/JIoEndpoint.java b/java/org/apache/tomcat/util/net/JIoEndpoint.java
index 3ef6a0c..fc3bbef 100644
--- a/java/org/apache/tomcat/util/net/JIoEndpoint.java
+++ b/java/org/apache/tomcat/util/net/JIoEndpoint.java
@@ -108,6 +108,8 @@ public class JIoEndpoint extends AbstractEndpoint {
public boolean getUseCometTimeout() { return false; } // Not supported
@Override
public boolean getDeferAccept() { return false; } // Not supported
+ @Override
+ public boolean getUsePolling() { return false; } // Not supported
// ------------------------------------------------ Handler Inner Interface
diff --git a/java/org/apache/tomcat/util/net/NioEndpoint.java b/java/org/apache/tomcat/util/net/NioEndpoint.java
index cb628df..6138ba6 100644
--- a/java/org/apache/tomcat/util/net/NioEndpoint.java
+++ b/java/org/apache/tomcat/util/net/NioEndpoint.java
@@ -332,6 +332,8 @@ public class NioEndpoint extends AbstractEndpoint {
public boolean getUseComet() { return useComet; }
@Override
public boolean getUseCometTimeout() { return getUseComet(); }
+ @Override
+ public boolean getUsePolling() { return true; } // Always supported
/**
@@ -968,13 +970,19 @@ public class NioEndpoint extends AbstractEndpoint {
else r.reset(socket,null,interestOps);
addEvent(r);
}
-
+
+ /**
+ * Processes events in the event queue of the Poller.
+ *
+ * @return <code>true</code> if some events were processed,
+ * <code>false</code> if queue was empty
+ */
public boolean events() {
boolean result = false;
Runnable r = null;
- result = (events.size() > 0);
while ( (r = events.poll()) != null ) {
+ result = true;
try {
r.run();
if ( r instanceof PollerEvent ) {
@@ -1079,13 +1087,11 @@ public class NioEndpoint extends AbstractEndpoint {
}
try {
if ( !close ) {
- if (wakeupCounter.get()>0) {
+ if (wakeupCounter.getAndSet(-1) > 0) {
//if we are here, means we have other stuff to do
//do a non blocking select
keyCount = selector.selectNow();
- }else {
- keyCount = selector.keys().size();
- wakeupCounter.set(-1);
+ } else {
keyCount = selector.select(selectorTimeout);
}
wakeupCounter.set(0);
@@ -1160,7 +1166,7 @@ public class NioEndpoint extends AbstractEndpoint {
boolean result = true;
try {
if ( close ) {
- cancelledKey(sk, SocketStatus.STOP, false);
+ cancelledKey(sk, SocketStatus.STOP, attachment.comet);
} else if ( sk.isValid() && attachment != null ) {
attachment.access();//make sure we don't time out valid sockets
sk.attach(attachment);//cant remember why this is here
@@ -1338,7 +1344,7 @@ public class NioEndpoint extends AbstractEndpoint {
(ka.interestOps()&SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) {
//only timeout sockets that we are waiting for a read from
long delta = now - ka.getLastAccess();
- long timeout = (ka.getTimeout()==-1)?((long) socketProperties.getSoTimeout()):(ka.getTimeout());
+ long timeout = ka.getTimeout();
boolean isTimedout = timeout > 0 && delta > timeout;
if ( close ) {
key.interestOps(0);
@@ -1348,7 +1354,7 @@ public class NioEndpoint extends AbstractEndpoint {
key.interestOps(0);
ka.interestOps(0); //avoid duplicate timeout calls
cancelledKey(key, SocketStatus.TIMEOUT,true);
- } else {
+ } else if (timeout > -1) {
long nextTime = now+(timeout-delta);
nextExpiration = (nextTime < nextExpiration)?nextTime:nextExpiration;
}
diff --git a/java/org/apache/tomcat/util/net/SSLSupport.java b/java/org/apache/tomcat/util/net/SSLSupport.java
index e569ca5..f28db68 100644
--- a/java/org/apache/tomcat/util/net/SSLSupport.java
+++ b/java/org/apache/tomcat/util/net/SSLSupport.java
@@ -45,7 +45,7 @@ public interface SSLSupport {
* This one is a Tomcat extension to the Servlet spec.
*/
public static final String SESSION_ID_KEY =
- "javax.servlet.request.ssl_session";
+ "javax.servlet.request.ssl_session_id";
/**
* The request attribute key for the session manager.
diff --git a/java/org/apache/tomcat/util/net/jsse/JSSEKeyManager.java b/java/org/apache/tomcat/util/net/jsse/JSSEKeyManager.java
index a06a42a..bebacba 100644
--- a/java/org/apache/tomcat/util/net/jsse/JSSEKeyManager.java
+++ b/java/org/apache/tomcat/util/net/jsse/JSSEKeyManager.java
@@ -22,6 +22,8 @@ import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509KeyManager;
/**
@@ -31,7 +33,7 @@ import javax.net.ssl.X509KeyManager;
*
* @author Jan Luehe
*/
-public final class JSSEKeyManager implements X509KeyManager {
+public final class JSSEKeyManager extends X509ExtendedKeyManager {
private X509KeyManager delegate;
private String serverKeyAlias;
@@ -44,6 +46,7 @@ public final class JSSEKeyManager implements X509KeyManager {
* supporting certificate chain
*/
public JSSEKeyManager(X509KeyManager mgr, String serverKeyAlias) {
+ super();
this.delegate = mgr;
this.serverKeyAlias = serverKeyAlias;
}
@@ -74,12 +77,9 @@ public final class JSSEKeyManager implements X509KeyManager {
* Returns this key manager's server key alias that was provided in the
* constructor.
*
- * @param keyType The key algorithm type name (ignored)
- * @param issuers The list of acceptable CA issuer subject names, or null
- * if it does not matter which issuers are used (ignored)
- * @param socket The socket to be used for this connection. This parameter
- * can be null, in which case this method will return the most generic
- * alias to use (ignored)
+ * @param keyType Ignored
+ * @param issuers Ignored
+ * @param socket Ignored
*
* @return Alias name for the desired key
*/
@@ -148,4 +148,40 @@ public final class JSSEKeyManager implements X509KeyManager {
public PrivateKey getPrivateKey(String alias) {
return delegate.getPrivateKey(alias);
}
+
+ /**
+ * Choose an alias to authenticate the client side of a secure socket,
+ * given the public key type and the list of certificate issuer authorities
+ * recognized by the peer (if any).
+ *
+ * @param keyType The key algorithm type name(s), ordered with the
+ * most-preferred key type first
+ * @param issuers The list of acceptable CA issuer subject names, or null
+ * if it does not matter which issuers are used
+ * @param engine Ignored
+ *
+ * @return The alias name for the desired key, or null if there are no
+ * matches
+ */
+ @Override
+ public String chooseEngineClientAlias(String[] keyType, Principal[] issuers,
+ SSLEngine engine) {
+ return delegate.chooseClientAlias(keyType, issuers, null);
+ }
+
+ /**
+ * Returns this key manager's server key alias that was provided in the
+ * constructor.
+ *
+ * @param keyType Ignored
+ * @param issuers Ignored
+ * @param engine Ignored
+ *
+ * @return Alias name for the desired key
+ */
+ @Override
+ public String chooseEngineServerAlias(String keyType, Principal[] issuers,
+ SSLEngine engine) {
+ return serverKeyAlias;
+ }
}
diff --git a/java/org/apache/tomcat/util/net/jsse/JSSESupport.java b/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
index 757b7b8..f0fcfd9 100644
--- a/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
+++ b/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
@@ -35,6 +35,7 @@ import javax.security.cert.X509Certificate;
import org.apache.tomcat.util.net.SSLSessionManager;
import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.res.StringManager;
/** JSSESupport
@@ -56,6 +57,9 @@ class JSSESupport implements SSLSupport, SSLSessionManager {
private static final org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(JSSESupport.class);
+ private static final StringManager sm =
+ StringManager.getManager("org.apache.tomcat.util.net.jsse.res");
+
private static final Map<SSLSession,Integer> keySizeCache =
new WeakHashMap<SSLSession, Integer>();
@@ -94,7 +98,7 @@ class JSSESupport implements SSLSupport, SSLSessionManager {
try {
certs = session.getPeerCertificates();
} catch( Throwable t ) {
- log.debug("Error getting client certs",t);
+ log.debug(sm.getString("jsseSupport.clientCertError"), t);
return null;
}
if( certs==null ) return null;
@@ -115,7 +119,8 @@ class JSSESupport implements SSLSupport, SSLSessionManager {
x509Certs[i] = (java.security.cert.X509Certificate)
cf.generateCertificate(stream);
} catch(Exception ex) {
- log.info("Error translating cert " + certs[i], ex);
+ log.info(sm.getString(
+ "jseeSupport.certTranslationError", certs[i]), ex);
return null;
}
}
@@ -153,7 +158,7 @@ class JSSESupport implements SSLSupport, SSLSessionManager {
protected void handShake() throws IOException {
if( ssl.getWantClientAuth() ) {
- log.debug("No client cert sent for want");
+ log.debug(sm.getString("jsseSupport.noCertWant"));
} else {
ssl.setNeedClientAuth(true);
}
@@ -161,7 +166,7 @@ class JSSESupport implements SSLSupport, SSLSessionManager {
if (ssl.getEnabledCipherSuites().length == 0) {
// Handshake is never going to be successful.
// Assume this is because handshakes are disabled
- log.warn("SSL server initiated renegotiation is disabled, closing connection");
+ log.warn(sm.getString("jsseSupport.serverRenegDisabled"));
session.invalidate();
ssl.close();
return;
@@ -170,7 +175,7 @@ class JSSESupport implements SSLSupport, SSLSessionManager {
InputStream in = ssl.getInputStream();
int oldTimeout = ssl.getSoTimeout();
ssl.setSoTimeout(1000);
- byte[] b = new byte[0];
+ byte[] b = new byte[1];
listener.reset();
ssl.startHandshake();
int maxTries = 60; // 60 * 1000 = example 1 minute time out
@@ -178,9 +183,16 @@ class JSSESupport implements SSLSupport, SSLSessionManager {
if (log.isTraceEnabled())
log.trace("Reading for try #" + i);
try {
- in.read(b);
+ int read = in.read(b);
+ if (read > 0) {
+ // Shouldn't happen as all input should have been swallowed
+ // before trying to do the handshake. If it does, something
+ // went wrong so lets bomb out now.
+ throw new SSLException(
+ sm.getString("jsseSupport.unexpectedData"));
+ }
} catch(SSLException sslex) {
- log.info("SSL Error getting client Certs",sslex);
+ log.info(sm.getString("jsseSupport.clientCertError"), sslex);
throw sslex;
} catch (IOException e) {
// ignore - presumably the timeout
@@ -221,7 +233,7 @@ class JSSESupport implements SSLSupport, SSLSessionManager {
break;
}
}
- keySize = new Integer(size);
+ keySize = Integer.valueOf(size);
synchronized(keySizeCache) {
keySizeCache.put(session, keySize);
}
diff --git a/java/org/apache/tomcat/util/net/jsse/res/LocalStrings.properties b/java/org/apache/tomcat/util/net/jsse/res/LocalStrings.properties
index 5e8ce34..a01e0c9 100644
--- a/java/org/apache/tomcat/util/net/jsse/res/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/net/jsse/res/LocalStrings.properties
@@ -17,4 +17,9 @@ jsse.alias_no_key_entry=Alias name {0} does not identify a key entry
jsse.keystore_load_failed=Failed to load keystore type {0} with path {1} due to {2}
jsse.invalid_ssl_conf=SSL configuration is invalid due to {0}
jsse.invalid_truststore_password=The provided trust store password could not be used to unlock and/or validate the trust store. Retrying to access the trust store with a null password which will skip validation.
-jsse.invalidTrustManagerClassName=The trustManagerClassName provided [{0}] does not implement javax.net.ssl.TrustManager
\ No newline at end of file
+jsse.invalidTrustManagerClassName=The trustManagerClassName provided [{0}] does not implement javax.net.ssl.TrustManager
+jsseSupport.clientCertError=Error trying to obtain a certificate from the client
+jseeSupport.certTranslationError=Error translating certificate [{0}]
+jsseSupport.noCertWant=No client certificate sent for want
+jsseSupport.serverRenegDisabled=SSL server initiated renegotiation is disabled, closing connection
+jsseSupport.unexpectedData=Unexpected data read from input stream
\ No newline at end of file
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
index b2a7644..232a829 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
@@ -394,22 +394,6 @@ public class ConnectionPool {
*/
protected void init(PoolConfiguration properties) throws SQLException {
poolProperties = properties;
- //make space for 10 extra in case we flow over a bit
- busy = new ArrayBlockingQueue<PooledConnection>(properties.getMaxActive(),false);
- //busy = new FairBlockingQueue<PooledConnection>();
- //make space for 10 extra in case we flow over a bit
- if (properties.isFairQueue()) {
- idle = new FairBlockingQueue<PooledConnection>();
- //idle = new MultiLockFairBlockingQueue<PooledConnection>();
- } else {
- idle = new ArrayBlockingQueue<PooledConnection>(properties.getMaxActive(),properties.isFairQueue());
- }
-
- //if the evictor thread is supposed to run, start it now
- if (properties.isPoolSweeperEnabled()) {
- poolCleaner = new PoolCleaner("[Pool-Cleaner]:" + properties.getName(), this, properties.getTimeBetweenEvictionRunsMillis());
- poolCleaner.start();
- } //end if
//make sure the pool is properly configured
if (properties.getMaxActive()<1) {
@@ -432,6 +416,23 @@ public class ConnectionPool {
log.warn("maxIdle is smaller than minIdle, setting maxIdle to: "+properties.getMinIdle());
properties.setMaxIdle(properties.getMinIdle());
}
+
+ //make space for 10 extra in case we flow over a bit
+ busy = new ArrayBlockingQueue<PooledConnection>(properties.getMaxActive(),false);
+ //busy = new FairBlockingQueue<PooledConnection>();
+ //make space for 10 extra in case we flow over a bit
+ if (properties.isFairQueue()) {
+ idle = new FairBlockingQueue<PooledConnection>();
+ //idle = new MultiLockFairBlockingQueue<PooledConnection>();
+ } else {
+ idle = new ArrayBlockingQueue<PooledConnection>(properties.getMaxActive(),properties.isFairQueue());
+ }
+
+ //if the evictor thread is supposed to run, start it now
+ if (properties.isPoolSweeperEnabled()) {
+ poolCleaner = new PoolCleaner("[Pool-Cleaner]:" + properties.getName(), this, properties.getTimeBetweenEvictionRunsMillis());
+ poolCleaner.start();
+ } //end if
//create JMX MBean
if (this.getPoolProperties().isJmxEnabled()) createMBean();
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
index 32e5a4e..1228b1d 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
@@ -142,6 +142,7 @@ public class DataSourceFactory implements ObjectFactory {
PROP_URL,
PROP_USERNAME,
PROP_VALIDATIONQUERY,
+ PROP_VALIDATOR_CLASS_NAME,
PROP_VALIDATIONINTERVAL,
PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED,
PROP_REMOVEABANDONED,
@@ -161,6 +162,7 @@ public class DataSourceFactory implements ObjectFactory {
PROP_USE_CON_LOCK,
PROP_DATASOURCE,
PROP_DATASOURCE_JNDI,
+ PROP_SUSPECT_TIMEOUT,
PROP_ALTERNATE_USERNAME_ALLOWED
};
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
index 4ab72fd..d8f3503 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
@@ -309,7 +309,7 @@ public class PooledConnection {
* @param finalize if set to true, a call to {@link ConnectionPool#finalize(PooledConnection)} is called.
*/
private void disconnect(boolean finalize) {
- if (isDiscarded()) {
+ if (isDiscarded() && connection == null) {
return;
}
setDiscarded(true);
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ProxyConnection.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ProxyConnection.java
index 76d17e3..1c36d7c 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ProxyConnection.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ProxyConnection.java
@@ -94,7 +94,7 @@ public class ProxyConnection extends JdbcInterceptor {
return Boolean.valueOf(isClosed());
}
if (compare(CLOSE_VAL,method)) {
- if (isClosed()) return null; //noop for already closed.
+ if (connection==null) return null; //noop for already closed.
PooledConnection poolc = this.connection;
this.connection = null;
pool.returnConnection(poolc);
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
index 922205e..c48474c 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
@@ -127,7 +127,7 @@ public class StatementDecoratorInterceptor extends AbstractCreateStatementInterc
result = constructor.newInstance(new Object[] { statementProxy });
statementProxy.setActualProxy(result);
statementProxy.setConnection(proxy);
- statementProxy.setConnection(constructor);
+ statementProxy.setConstructor(constructor);
return result;
}
diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/AbandonPercentageTest.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/AbandonPercentageTest.java
index a86e6e9..2e1f620 100644
--- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/AbandonPercentageTest.java
+++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/AbandonPercentageTest.java
@@ -108,7 +108,7 @@ public class AbandonPercentageTest extends DefaultTestCase {
assertEquals("Number of connections active/busy should be "+con.length,con.length,datasource.getPool().getActive());
this.datasource.getPoolProperties().setRemoveAbandonedTimeout(1);
Thread.sleep(2500);
- this.assertTrue("Number of connections should be less than 50.", (datasource.getPool().getActive()<50));
+ assertTrue("Number of connections should be less than 50.", (datasource.getPool().getActive()<50));
this.datasource.getPoolProperties().setAbandonWhenPercentageFull(0);
Thread.sleep(2500);
assertEquals("Number of connections active/busy should be "+0,0,datasource.getPool().getActive());
diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/AlternateUsernameTest.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/AlternateUsernameTest.java
index 0ea3e2a..c6915b7 100644
--- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/AlternateUsernameTest.java
+++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/AlternateUsernameTest.java
@@ -20,7 +20,6 @@ package org.apache.tomcat.jdbc.test;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
-import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -107,6 +106,7 @@ public class AlternateUsernameTest extends DefaultTestCase {
useuser = user!=null;
}
+ @Override
public TestResult call() {
TestResult test = new TestResult();
PooledConnection pcon = null;
diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/Async0IdleTestBug50477.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/Async0IdleTestBug50477.java
index cfc9210..3b65667 100644
--- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/Async0IdleTestBug50477.java
+++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/Async0IdleTestBug50477.java
@@ -40,7 +40,7 @@ public class Async0IdleTestBug50477 extends DefaultTestCase {
this.datasource.getPoolProperties().setInitialSize(0);
try {
Future<Connection> cf = ((DataSourceProxy)datasource).getConnectionAsync();
- Connection con = cf.get(5, TimeUnit.SECONDS);
+ cf.get(5, TimeUnit.SECONDS);
}finally {
tearDown();
}
diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/BorrowWaitTest.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/BorrowWaitTest.java
index 1244140..3775ef8 100644
--- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/BorrowWaitTest.java
+++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/BorrowWaitTest.java
@@ -39,7 +39,9 @@ public class BorrowWaitTest extends DefaultTestCase {
}catch (SQLException x) {
long delta = System.currentTimeMillis();
boolean inrange = Math.abs(wait-delta) < 1000;
- assertTrue("Connection should have been acquired within +/- 1 second.",true);
+ assertTrue(
+ "Connection should have been acquired within +/- 1 second.",
+ inrange);
}
con.close();
}
@@ -49,6 +51,7 @@ public class BorrowWaitTest extends DefaultTestCase {
System.err.println("testWaitTimeInfinite() test is disabled.");
return;//this would lock up the test suite
}
+ /*
int wait = -1;
this.init();
this.datasource.setMaxActive(1);
@@ -64,6 +67,7 @@ public class BorrowWaitTest extends DefaultTestCase {
assertTrue("Connection should have been acquired within +/- 1 second.",true);
}
con.close();
+ */
}
diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/ConnectCountTest.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/ConnectCountTest.java
index 339e1aa..bcf4ad8 100644
--- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/ConnectCountTest.java
+++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/ConnectCountTest.java
@@ -38,8 +38,8 @@ public class ConnectCountTest extends DefaultTestCase {
}
protected boolean run = true;
- protected long sleep = Long.getLong("sleep", 10);
- protected long complete = Long.getLong("complete",20000);
+ protected long sleep = Long.getLong("sleep", 10).longValue();
+ protected long complete = Long.getLong("complete",20000).longValue();
protected boolean printthread = Boolean.getBoolean("printthread");
CountDownLatch latch = null;
@@ -67,8 +67,7 @@ public class ConnectCountTest extends DefaultTestCase {
protected void printThreadResults(TestThread[] threads, String name, int active, int expected) {
long minfetch = Long.MAX_VALUE, maxfetch = Long.MIN_VALUE, totalfetch = 0;
- long maxwait = 0, minwait = Long.MAX_VALUE, averagewait = 0, totalwait = 0;
- float avgfetch = 0;
+ long maxwait = 0, minwait = Long.MAX_VALUE, totalwait = 0;
for (int i=0; i<threads.length; i++) {
TestThread t = threads[i];
totalfetch += t.nroffetch;
@@ -78,11 +77,11 @@ public class ConnectCountTest extends DefaultTestCase {
minfetch = Math.min(minfetch, t.nroffetch);
maxfetch = Math.max(maxfetch, t.nroffetch);
if (ConnectCountTest.this.printthread)
- System.out.println(t.getName()+" : Nr-of-fetch:"+t.nroffetch+ " Max fetch Time:"+(((float)t.maxwait)/1000000f)+"ms. :Max close time:"+(((float)t.cmax)/1000000f)+"ms.");
+ System.out.println(t.getName()+" : Nr-of-fetch:"+t.nroffetch+ " Max fetch Time:"+t.maxwait/1000000f+"ms. :Max close time:"+t.cmax/1000000f+"ms.");
}
System.out.println("["+name+"] Max fetch:"+(maxfetch)+" Min fetch:"+(minfetch)+" Average fetch:"+
(((float)totalfetch))/(float)threads.length);
- System.out.println("["+name+"] Max wait:"+(((float)maxwait)/1000000f)+"ms. Min wait:"+(((float)minwait)/1000000f)+"ms. Average wait:"+(((((float)totalwait))/(float)totalfetch)/1000000f)+" ms.");
+ System.out.println("["+name+"] Max wait:"+maxwait/1000000f+"ms. Min wait:"+minwait/1000000f+"ms. Average wait:"+(((((float)totalwait))/(float)totalfetch)/1000000f)+" ms.");
System.out.println("["+name+"] Max active:"+active+" Expected Active:"+expected);
@@ -112,6 +111,7 @@ public class ConnectCountTest extends DefaultTestCase {
this.run = false;
long delta = System.currentTimeMillis() - start;
printThreadResults(threads,"testDBCPThreads20Connections10",Driver.connectCount.get(),10);
+ System.out.println("Test completed in: " + delta + "ms.");
tearDown();
}
@@ -139,6 +139,7 @@ public class ConnectCountTest extends DefaultTestCase {
this.run = false;
long delta = System.currentTimeMillis() - start;
printThreadResults(threads,"testPoolThreads20Connections10",Driver.connectCount.get(),10);
+ System.out.println("Test completed in: " + delta + "ms.");
tearDown();
}
@@ -168,6 +169,7 @@ public class ConnectCountTest extends DefaultTestCase {
this.run = false;
long delta = System.currentTimeMillis() - start;
printThreadResults(threads,"testPoolThreads20Connections10Fair",Driver.connectCount.get(),10);
+ System.out.println("Test completed in: " + delta + "ms.");
tearDown();
}
@@ -198,6 +200,7 @@ public class ConnectCountTest extends DefaultTestCase {
this.run = false;
long delta = System.currentTimeMillis() - start;
printThreadResults(threads,"testPoolThreads20Connections10FairAsync",Driver.connectCount.get(),10);
+ System.out.println("Test completed in: " + delta + "ms.");
tearDown();
}
@@ -286,13 +289,13 @@ public class ConnectCountTest extends DefaultTestCase {
}
if (System.getProperty("print-thread-stats")!=null) {
System.out.println("["+getName()+"] "+
- "\n\tMax time to retrieve connection:"+(((float)maxwait)/1000f/1000f)+" ms."+
- "\n\tTotal time to retrieve connection:"+(((float)totalwait)/1000f/1000f)+" ms."+
- "\n\tAverage time to retrieve connection:"+(((float)totalwait)/1000f/1000f)/(float)nroffetch+" ms."+
- "\n\tMax time to close connection:"+(((float)cmax)/1000f/1000f)+" ms."+
- "\n\tTotal time to close connection:"+(((float)totalcmax)/1000f/1000f)+" ms."+
- "\n\tAverage time to close connection:"+(((float)totalcmax)/1000f/1000f)/(float)nroffetch+" ms."+
- "\n\tRun time:"+(((float)totalruntime)/1000f/1000f)+" ms."+
+ "\n\tMax time to retrieve connection:"+maxwait/1000000f+" ms."+
+ "\n\tTotal time to retrieve connection:"+totalwait/1000000f+" ms."+
+ "\n\tAverage time to retrieve connection:"+totalwait/1000000f/nroffetch+" ms."+
+ "\n\tMax time to close connection:"+cmax/1000000f+" ms."+
+ "\n\tTotal time to close connection:"+totalcmax/1000000f+" ms."+
+ "\n\tAverage time to close connection:"+totalcmax/1000000f/nroffetch+" ms."+
+ "\n\tRun time:"+totalruntime/1000000f+" ms."+
"\n\tNr of fetch:"+nroffetch);
}
}
diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/DefaultProperties.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/DefaultProperties.java
index 235b555..4cd25b4 100644
--- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/DefaultProperties.java
+++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/DefaultProperties.java
@@ -45,8 +45,8 @@ public class DefaultProperties extends PoolProperties {
username = System.getProperty("username","root");
validationQuery = System.getProperty("validationQuery","SELECT 1");
- defaultAutoCommit = true;
- defaultReadOnly = false;
+ defaultAutoCommit = Boolean.TRUE;
+ defaultReadOnly = Boolean.FALSE;
defaultTransactionIsolation = DataSourceFactory.UNKNOWN_TRANSACTIONISOLATION;
connectionProperties = null;
defaultCatalog = null;
@@ -67,7 +67,7 @@ public class DefaultProperties extends PoolProperties {
logAbandoned = true;
validationInterval = 0; //always validate
initSQL = null;
- testOnConnect = false;;
+ testOnConnect = false;
dbProperties.setProperty("user",username);
dbProperties.setProperty("password",password);
}
diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/DefaultTestCase.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/DefaultTestCase.java
index 401c380..9b45e42 100644
--- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/DefaultTestCase.java
+++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/DefaultTestCase.java
@@ -236,6 +236,8 @@ public class DefaultTestCase extends TestCase {
PROP_REMOVEABANDONED,
PROP_REMOVEABANDONEDTIMEOUT,
PROP_LOGABANDONED,
+ PROP_POOLPREPAREDSTATEMENTS,
+ PROP_MAXOPENPREPAREDSTATEMENTS,
PROP_CONNECTIONPROPERTIES
};
diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/FairnessTest.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/FairnessTest.java
index b296eee..a5fe9fa 100644
--- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/FairnessTest.java
+++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/FairnessTest.java
@@ -37,14 +37,13 @@ public class FairnessTest extends DefaultTestCase {
}
protected boolean run = true;
- protected long sleep = Long.getLong("sleep", 10);
- protected long complete = Long.getLong("complete",20000);
+ protected long sleep = Long.getLong("sleep", 10).longValue();
+ protected long complete = Long.getLong("complete",20000).longValue();
protected boolean printthread = Boolean.getBoolean("printthread");
CountDownLatch latch = null;
protected void printThreadResults(TestThread[] threads, String name, int active, int expected) {
long minfetch = Long.MAX_VALUE, maxfetch = Long.MIN_VALUE, totalfetch = 0;
- long maxwait = 0, minwait = Long.MAX_VALUE, averagewait = 0, totalwait = 0;
- float avgfetch = 0;
+ long maxwait = 0, minwait = Long.MAX_VALUE, totalwait = 0;
for (int i=0; i<threads.length; i++) {
TestThread t = threads[i];
totalfetch += t.nroffetch;
@@ -54,11 +53,11 @@ public class FairnessTest extends DefaultTestCase {
minfetch = Math.min(minfetch, t.nroffetch);
maxfetch = Math.max(maxfetch, t.nroffetch);
if (FairnessTest.this.printthread)
- System.out.println(t.getName()+" : Nr-of-fetch:"+t.nroffetch+ " Max fetch Time:"+(((float)t.maxwait)/1000000f)+"ms. :Max close time:"+(((float)t.cmax)/1000000f)+"ms.");
+ System.out.println(t.getName()+" : Nr-of-fetch:"+t.nroffetch+ " Max fetch Time:"+t.maxwait/1000000f+"ms. :Max close time:"+t.cmax/1000000f+"ms.");
}
System.out.println("["+name+"] Max fetch:"+(maxfetch)+" Min fetch:"+(minfetch)+" Average fetch:"+
(((float)totalfetch))/(float)threads.length);
- System.out.println("["+name+"] Max wait:"+(((float)maxwait)/1000000f)+"ms. Min wait:"+(((float)minwait)/1000000f)+"ms. Average wait:"+(((((float)totalwait))/(float)totalfetch)/1000000f)+" ms.");
+ System.out.println("["+name+"] Max wait:"+maxwait/1000000f+"ms. Min wait:"+minwait/1000000f+"ms. Average wait:"+(((((float)totalwait))/(float)totalfetch)/1000000f)+" ms.");
System.out.println("["+name+"] Max active:"+active+" Expected Active:"+expected);
@@ -89,6 +88,7 @@ public class FairnessTest extends DefaultTestCase {
this.run = false;
long delta = System.currentTimeMillis() - start;
printThreadResults(threads,"testDBCPThreads20Connections10",this.tDatasource.getNumActive(),10);
+ System.out.println("Test completed in: " + delta + "ms.");
tearDown();
}
@@ -118,8 +118,8 @@ public class FairnessTest extends DefaultTestCase {
this.run = false;
long delta = System.currentTimeMillis() - start;
printThreadResults(threads,"testPoolThreads20Connections10",this.datasource.getSize(),10);
+ System.out.println("Test completed in: " + delta + "ms.");
tearDown();
-
}
public void testPoolThreads20Connections10Fair() throws Exception {
@@ -148,6 +148,7 @@ public class FairnessTest extends DefaultTestCase {
this.run = false;
long delta = System.currentTimeMillis() - start;
printThreadResults(threads,"testPoolThreads20Connections10Fair",this.datasource.getSize(),10);
+ System.out.println("Test completed in: " + delta + "ms.");
tearDown();
}
@@ -178,6 +179,7 @@ public class FairnessTest extends DefaultTestCase {
this.run = false;
long delta = System.currentTimeMillis() - start;
printThreadResults(threads,"testPoolThreads20Connections10FairAsync",this.datasource.getSize(),10);
+ System.out.println("Test completed in: " + delta + "ms.");
tearDown();
}
@@ -268,13 +270,13 @@ public class FairnessTest extends DefaultTestCase {
}
if (System.getProperty("print-thread-stats")!=null) {
System.out.println("["+getName()+"] "+
- "\n\tMax time to retrieve connection:"+(((float)maxwait)/1000f/1000f)+" ms."+
- "\n\tTotal time to retrieve connection:"+(((float)totalwait)/1000f/1000f)+" ms."+
- "\n\tAverage time to retrieve connection:"+(((float)totalwait)/1000f/1000f)/(float)nroffetch+" ms."+
- "\n\tMax time to close connection:"+(((float)cmax)/1000f/1000f)+" ms."+
- "\n\tTotal time to close connection:"+(((float)totalcmax)/1000f/1000f)+" ms."+
- "\n\tAverage time to close connection:"+(((float)totalcmax)/1000f/1000f)/(float)nroffetch+" ms."+
- "\n\tRun time:"+(((float)totalruntime)/1000f/1000f)+" ms."+
+ "\n\tMax time to retrieve connection:"+maxwait/1000000f+" ms."+
+ "\n\tTotal time to retrieve connection:"+totalwait/1000000f+" ms."+
+ "\n\tAverage time to retrieve connection:"+totalwait/1000000f/nroffetch+" ms."+
+ "\n\tMax time to close connection:"+cmax/1000000f+" ms."+
+ "\n\tTotal time to close connection:"+totalcmax/1000000f+" ms."+
+ "\n\tAverage time to close connection:"+totalcmax/1000000f/nroffetch+" ms."+
+ "\n\tRun time:"+totalruntime/1000000f+" ms."+
"\n\tNr of fetch:"+nroffetch);
}
}
diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestConcurrency.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestConcurrency.java
index 6a22d0a..617d486 100644
--- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestConcurrency.java
+++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestConcurrency.java
@@ -63,6 +63,7 @@ public class TestConcurrency extends DefaultTestCase {
final int iter = 1000 * 10;
final AtomicInteger loopcount = new AtomicInteger(0);
final Runnable run = new Runnable() {
+ @Override
public void run() {
try {
while (loopcount.incrementAndGet() < iter) {
@@ -117,6 +118,7 @@ public class TestConcurrency extends DefaultTestCase {
final int iter = 100000 * 10;
final AtomicInteger loopcount = new AtomicInteger(0);
final Runnable run = new Runnable() {
+ @Override
public void run() {
try {
while (loopcount.incrementAndGet() < iter) {
@@ -167,6 +169,7 @@ public class TestConcurrency extends DefaultTestCase {
final int iter = 100000 * 10;
final AtomicInteger loopcount = new AtomicInteger(0);
final Runnable run = new Runnable() {
+ @Override
public void run() {
try {
while (loopcount.incrementAndGet() < iter) {
diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestSizePreservation.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestSizePreservation.java
index 183532e..89c4d7c 100644
--- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestSizePreservation.java
+++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestSizePreservation.java
@@ -90,6 +90,7 @@ public class TestSizePreservation extends TestCase {
final int iterations = 1000;
final AtomicInteger loopcount = new AtomicInteger(0);
final Runnable run = new Runnable() {
+ @Override
public void run() {
try {
while (loopcount.incrementAndGet() < iterations) {
diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestSlowQueryReport.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestSlowQueryReport.java
index 33dbec6..2f24ad7 100644
--- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestSlowQueryReport.java
+++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestSlowQueryReport.java
@@ -159,14 +159,13 @@ public class TestSlowQueryReport extends DefaultTestCase {
this.datasource.setJdbcInterceptors(SlowQueryReport.class.getName());
Connection con = this.datasource.getConnection();
String slowSql = "select 1 from non_existent";
- int exceptionCount = 0;
for (int i=0; i<count; i++) {
Statement st = con.createStatement();
try {
ResultSet rs = st.executeQuery(slowSql);
rs.close();
}catch (Exception x) {
- exceptionCount++;
+ // NO-OP
}
st.close();
@@ -186,6 +185,7 @@ public class TestSlowQueryReport extends DefaultTestCase {
public class ClientListener implements NotificationListener {
volatile int notificationCount = 0;
+ @Override
public void handleNotification(Notification notification,
Object handback) {
notificationCount++;
diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestStatementCache.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestStatementCache.java
index d3d5740..cc32aee 100644
--- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestStatementCache.java
+++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestStatementCache.java
@@ -33,8 +33,7 @@ public class TestStatementCache extends DefaultTestCase {
@Override
protected void tearDown() throws Exception {
- // TODO Auto-generated method stub
- this.interceptor = null;
+ interceptor = null;
super.tearDown();
}
diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/driver/Connection.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/driver/Connection.java
index 8fc3888..c3846bf 100644
--- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/driver/Connection.java
+++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/driver/Connection.java
@@ -50,187 +50,236 @@ public class Connection implements java.sql.Connection {
return info.getProperty(PooledConnection.PROP_PASSWORD);
}
+ @Override
public void clearWarnings() throws SQLException {
}
+ @Override
public void close() throws SQLException {
Driver.disconnectCount.incrementAndGet();
}
+ @Override
public void commit() throws SQLException {
}
+ @Override
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
return null;
}
+ @Override
public Blob createBlob() throws SQLException {
return null;
}
+ @Override
public Clob createClob() throws SQLException {
return null;
}
+ @Override
public NClob createNClob() throws SQLException {
return null;
}
+ @Override
public SQLXML createSQLXML() throws SQLException {
return null;
}
+ @Override
public Statement createStatement() throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
+ @Override
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
+ @Override
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
+ @Override
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
return null;
}
+ @Override
public boolean getAutoCommit() throws SQLException {
return false;
}
+ @Override
public String getCatalog() throws SQLException {
return null;
}
+ @Override
public Properties getClientInfo() throws SQLException {
return null;
}
+ @Override
public String getClientInfo(String name) throws SQLException {
return null;
}
+ @Override
public int getHoldability() throws SQLException {
return 0;
}
+ @Override
public DatabaseMetaData getMetaData() throws SQLException {
return null;
}
+ @Override
public int getTransactionIsolation() throws SQLException {
return 0;
}
+ @Override
public Map<String, Class<?>> getTypeMap() throws SQLException {
return null;
}
+ @Override
public SQLWarning getWarnings() throws SQLException {
return null;
}
+ @Override
public boolean isClosed() throws SQLException {
return false;
}
+ @Override
public boolean isReadOnly() throws SQLException {
return false;
}
+ @Override
public boolean isValid(int timeout) throws SQLException {
return false;
}
+ @Override
public String nativeSQL(String sql) throws SQLException {
return null;
}
+ @Override
public CallableStatement prepareCall(String sql) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
+ @Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
+ @Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
+ @Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
+ @Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
+ @Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
+ @Override
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
+ @Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
+ @Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return new org.apache.tomcat.jdbc.test.driver.Statement();
}
+ @Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
}
+ @Override
public void rollback() throws SQLException {
}
+ @Override
public void rollback(Savepoint savepoint) throws SQLException {
}
+ @Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
}
+ @Override
public void setCatalog(String catalog) throws SQLException {
}
+ @Override
public void setClientInfo(Properties properties) throws SQLClientInfoException {
}
+ @Override
public void setClientInfo(String name, String value) throws SQLClientInfoException {
}
+ @Override
public void setHoldability(int holdability) throws SQLException {
}
+ @Override
public void setReadOnly(boolean readOnly) throws SQLException {
}
+ @Override
public Savepoint setSavepoint() throws SQLException {
return null;
}
+ @Override
public Savepoint setSavepoint(String name) throws SQLException {
return null;
}
+ @Override
public void setTransactionIsolation(int level) throws SQLException {
}
+ @Override
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
}
+ @Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
+ @Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/driver/Driver.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/driver/Driver.java
index 0b3d8cd..52c5830 100644
--- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/driver/Driver.java
+++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/driver/Driver.java
@@ -45,27 +45,33 @@ public class Driver implements java.sql.Driver {
public Driver() {
}
+ @Override
public boolean acceptsURL(String url) throws SQLException {
return url!=null && url.equals(Driver.url);
}
+ @Override
public Connection connect(String url, Properties info) throws SQLException {
connectCount.addAndGet(1);
return new org.apache.tomcat.jdbc.test.driver.Connection(info);
}
+ @Override
public int getMajorVersion() {
return 0;
}
+ @Override
public int getMinorVersion() {
return 0;
}
+ @Override
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
return null;
}
+ @Override
public boolean jdbcCompliant() {
return false;
}
diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/driver/ResultSet.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/driver/ResultSet.java
index 9986c48..398c733 100644
--- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/driver/ResultSet.java
+++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/driver/ResultSet.java
@@ -41,39 +41,50 @@ import java.util.Map;
public class ResultSet implements java.sql.ResultSet {
boolean hasNext = true;
+ @Override
public boolean absolute(int row) throws SQLException {
return false;
}
+ @Override
public void afterLast() throws SQLException {
}
+ @Override
public void beforeFirst() throws SQLException {
}
+ @Override
public void cancelRowUpdates() throws SQLException {
}
+ @Override
public void clearWarnings() throws SQLException {
}
+ @Override
public void close() throws SQLException {
}
+ @Override
public void deleteRow() throws SQLException {
}
+ @Override
public int findColumn(String columnLabel) throws SQLException {
return 0;
}
+ @Override
public boolean first() throws SQLException {
return hasNext;
}
+ @Override
public Array getArray(int columnIndex) throws SQLException {
return null;
}
+ @Override
public Array getArray(String columnLabel) throws SQLException {
return null;
}
diff --git a/res/maven/mvn.properties.default b/res/maven/mvn.properties.default
index 360b895..0fa41b8 100644
--- a/res/maven/mvn.properties.default
+++ b/res/maven/mvn.properties.default
@@ -33,12 +33,12 @@ maven.snapshot.repo.repositoryId=apache.snapshots
#Maven release properties for Tomcat staging
maven.release.repo.url=scp://people.apache.org/www/tomcat.apache.org/dev/dist/m2-repository
maven.release.repo.repositoryId=tomcat-staging
-maven.release.deploy.version=7.0.21
+maven.release.deploy.version=7.0.22
#Maven release properties for the main ASF repo
maven.asf.release.repo.url=scp://people.apache.org/www/people.apache.org/repo/m2-ibiblio-rsync-repository
maven.asf.release.repo.repositoryId=apache.releases
-maven.asf.release.deploy.version=7.0.21
+maven.asf.release.deploy.version=7.0.22
#Where do we load the libraries from
diff --git a/res/maven/tomcat-jdbc.pom b/res/maven/tomcat-jdbc.pom
index 100b383..5162923 100644
--- a/res/maven/tomcat-jdbc.pom
+++ b/res/maven/tomcat-jdbc.pom
@@ -24,7 +24,7 @@
<dependencies>
<dependency>
<groupId>org.apache.tomcat</groupId>
- <artifactId>tomcat-jdbc</artifactId>
+ <artifactId>tomcat-juli</artifactId>
<version>@MAVEN.DEPLOY.VERSION@</version>
<scope>compile</scope>
</dependency>
diff --git a/test/javax/el/TestBeanELResolverVarargsInvocation.java b/test/javax/el/TestBeanELResolverVarargsInvocation.java
new file mode 100644
index 0000000..d7abfa1
--- /dev/null
+++ b/test/javax/el/TestBeanELResolverVarargsInvocation.java
@@ -0,0 +1,119 @@
+/*
+ * 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 javax.el;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestBeanELResolverVarargsInvocation {
+ public static class Foo {
+
+ public String joinDelimited(String delim, String... strings) {
+ StringBuilder result = new StringBuilder();
+ if (strings != null) {
+ for (String s : strings) {
+ if (delim != null && result.length() > 0) {
+ result.append(delim);
+ }
+ result.append(s);
+ }
+ }
+ return result.toString();
+ }
+
+ public String join(String... strings) {
+ return joinDelimited(null, strings);
+ }
+ }
+
+ private Foo foo;
+ private ELContext elContext;
+ private BeanELResolver beanELResolver;
+
+ @Before
+ public void setup() {
+ foo = new Foo();
+ beanELResolver = new BeanELResolver();
+ elContext = new ELContext() {
+ private VariableMapper variableMapper = new VariableMapper() {
+ private Map<String, ValueExpression> vars =
+ new HashMap<String, ValueExpression>();
+
+ @Override
+ public ValueExpression setVariable(String arg0,
+ ValueExpression arg1) {
+ return vars.put(arg0, arg1);
+ }
+
+ @Override
+ public ValueExpression resolveVariable(String arg0) {
+ return vars.get(arg0);
+ }
+ };
+ private FunctionMapper functionMapper = new FunctionMapper() {
+
+ @Override
+ public Method resolveFunction(String arg0, String arg1) {
+ return null;
+ }
+ };
+
+ @Override
+ public VariableMapper getVariableMapper() {
+ return variableMapper;
+ }
+
+ @Override
+ public FunctionMapper getFunctionMapper() {
+ return functionMapper;
+ }
+
+ @Override
+ public ELResolver getELResolver() {
+ return beanELResolver;
+ }
+ };
+ }
+
+ /**
+ * Tests varargs that come after an opening argument.
+ */
+ @Test
+ public void testJoinDelimited() {
+ Assert.assertEquals(foo.joinDelimited("-", "foo", "bar", "baz"),
+ beanELResolver.invoke(elContext, foo, "joinDelimited", null,
+ new Object[] { "-", "foo", "bar", "baz" }));
+ }
+
+ /**
+ * Tests varargs that constitute a method's only parameters, as well as
+ * bogus results due to improper matching of ANY vararg method, and
+ * depending on the order in which reflected methods are encountered.
+ */
+ @Test
+ public void testJoin() {
+ Assert.assertEquals(foo.join("foo", "bar", "baz"),
+ beanELResolver.invoke(elContext, foo, "join", null,
+ new Object[] { "foo", "bar", "baz" }));
+ }
+
+}
\ No newline at end of file
diff --git a/test/org/apache/catalina/comet/TestCometProcessor.java b/test/org/apache/catalina/comet/TestCometProcessor.java
index e369552..c6c6105 100644
--- a/test/org/apache/catalina/comet/TestCometProcessor.java
+++ b/test/org/apache/catalina/comet/TestCometProcessor.java
@@ -151,15 +151,32 @@ public class TestCometProcessor extends TomcatBaseTest {
Thread.sleep(3000);
tomcat.getConnector().stop();
+ // Allow the executor a chance to send the end event
+ Thread.sleep(100);
tomcat.getConnector().destroy();
- // Sleep for a couple of seconds to give enough time for the connector
- // stop message to be processed
- Thread.sleep(2000);
+ // Wait for the write thread to stop
+ int count = 0;
+ while (writeThread.isAlive() && count < 50) {
+ Thread.sleep(100);
+ count ++;
+ }
+
+ // Wait for the read thread to stop
+ while (readThread.isAlive() && count < 50) {
+ Thread.sleep(100);
+ count ++;
+ }
// Write should trigger an exception once the connector stops since the
// socket should be closed
- assertNotNull(writeThread.getException());
+ assertNotNull("No exception in writing thread",
+ writeThread.getException());
+
+ // Termination of Read thread varies by platform and protocol
+ // In all cases, the END event should be sent.
+ assertTrue("Comet END event not received",
+ readThread.getResponse().contains("Client: END"));
}
private boolean isCometSupported() {
@@ -215,7 +232,7 @@ public class TestCometProcessor extends TomcatBaseTest {
private int pingCount;
private OutputStream os;
- private Exception e = null;
+ private volatile Exception e = null;
public PingWriterThread(int pingCount, OutputStream os) {
this.pingCount = pingCount;
@@ -247,7 +264,6 @@ public class TestCometProcessor extends TomcatBaseTest {
private InputStream is;
private StringBuilder response = new StringBuilder();
- private Exception e = null;
public ResponseReaderThread(InputStream is) {
this.is = is;
@@ -257,10 +273,6 @@ public class TestCometProcessor extends TomcatBaseTest {
return response.toString();
}
- public Exception getException() {
- return e;
- }
-
@Override
public void run() {
try {
@@ -270,7 +282,7 @@ public class TestCometProcessor extends TomcatBaseTest {
c = is.read();
}
} catch (Exception e) {
- this.e = e;
+ // Ignore
}
}
}
diff --git a/test/org/apache/catalina/connector/TestCoyoteAdapter.java b/test/org/apache/catalina/connector/TestCoyoteAdapter.java
index 53d6c8e..8e05a00 100644
--- a/test/org/apache/catalina/connector/TestCoyoteAdapter.java
+++ b/test/org/apache/catalina/connector/TestCoyoteAdapter.java
@@ -87,6 +87,7 @@ public class TestCoyoteAdapter extends TomcatBaseTest {
// Create the folder that will trigger the redirect
File foo = new File(docBase, "foo");
+ addDeleteOnTearDown(foo);
if (!foo.mkdirs() && !foo.isDirectory()) {
fail("Unable to create foo directory in docBase");
}
diff --git a/test/org/apache/catalina/connector/TestMaxConnections.java b/test/org/apache/catalina/connector/TestMaxConnections.java
index c9b45ca..5723b02 100644
--- a/test/org/apache/catalina/connector/TestMaxConnections.java
+++ b/test/org/apache/catalina/connector/TestMaxConnections.java
@@ -60,7 +60,7 @@ public class TestMaxConnections extends TomcatBaseTest {
}
assertTrue("The number of successful requests should have been 4-5, actual "+passcount,4==passcount || 5==passcount);
-
+ System.out.println("There were [" + connectfail + "] connection failures");
}
private static class ConnectThread extends Thread {
diff --git a/test/org/apache/catalina/core/TestAsyncContextImpl.java b/test/org/apache/catalina/core/TestAsyncContextImpl.java
index 66d129a..3d9b941 100644
--- a/test/org/apache/catalina/core/TestAsyncContextImpl.java
+++ b/test/org/apache/catalina/core/TestAsyncContextImpl.java
@@ -411,6 +411,7 @@ public class TestAsyncContextImpl extends TomcatBaseTest {
// Create the folder that will trigger the redirect
File foo = new File(docBase, "async");
+ addDeleteOnTearDown(foo);
if (!foo.mkdirs() && !foo.isDirectory()) {
fail("Unable to create async directory in docBase");
}
diff --git a/test/org/apache/catalina/core/TestDefaultInstanceManager.java b/test/org/apache/catalina/core/TestDefaultInstanceManager.java
new file mode 100644
index 0000000..e2c1bef
--- /dev/null
+++ b/test/org/apache/catalina/core/TestDefaultInstanceManager.java
@@ -0,0 +1,86 @@
+/*
+ * 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.catalina.core;
+
+import java.io.File;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+
+
+public class TestDefaultInstanceManager extends TomcatBaseTest {
+
+ @Test
+ public void testClassUnloading() throws Exception {
+
+ DefaultInstanceManager instanceManager = doClassUnloadingPrep();
+
+ // Request a JSP page (that doesn't load any tag libraries etc.)
+ // This page does use @PostConstruct to ensure that the cache does not
+ // retain strong references
+ getUrl("http://localhost:" + getPort() + "/test/annotations.jsp");
+ // Request a second JSP (again, no tag libraries etc.)
+ getUrl("http://localhost:" + getPort() + "/test/bug36923.jsp");
+
+ // Check the number of classes in the cache
+ int count = instanceManager.getAnnotationCacheSize();
+
+ // Request a third JSP (again, no tag libraries etc.)
+ getUrl("http://localhost:" + getPort() + "/test/bug51544.jsp");
+
+ // Force a GC to clear out unloaded class (first JSP)
+ System.gc();
+
+ // Spin a while until GC happens or we wait too long
+ int loop = 0;
+ while (loop < 10) {
+ if (instanceManager.getAnnotationCacheSize() == count) {
+ break;
+ }
+ Thread.sleep(100);
+ loop++;
+ }
+
+ // First JSP should be unloaded and replaced by third (second left
+ // alone) so no change in overall count
+ assertEquals(count, instanceManager.getAnnotationCacheSize());
+ }
+
+ private DefaultInstanceManager doClassUnloadingPrep() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // Create the context (don't use addWebapp as we want to modify the
+ // JSP Servlet settings).
+ File appDir = new File("test/webapp-3.0");
+ StandardContext ctxt = (StandardContext) tomcat.addContext(
+ null, "/test", appDir.getAbsolutePath());
+
+ // Configure the defaults and then tweak the JSP servlet settings
+ // Note: Min value for maxLoadedJsps is 2
+ Tomcat.initWebappDefaults(ctxt);
+ Wrapper w = (Wrapper) ctxt.findChild("jsp");
+ w.addInitParameter("maxLoadedJsps", "2");
+
+ tomcat.start();
+
+ return (DefaultInstanceManager) ctxt.getInstanceManager();
+ }
+}
diff --git a/test/org/apache/catalina/mbeans/TestRegistration.java b/test/org/apache/catalina/mbeans/TestRegistration.java
index 893f007..7336c33 100644
--- a/test/org/apache/catalina/mbeans/TestRegistration.java
+++ b/test/org/apache/catalina/mbeans/TestRegistration.java
@@ -116,9 +116,9 @@ public class TestRegistration extends TomcatBaseTest {
final Tomcat tomcat = getTomcatInstance();
final File contextDir = new File(getTemporaryDirectory(), "webappFoo");
- if (!contextDir.exists()) {
- if (!contextDir.mkdir())
- fail("Failed to create: [" + contextDir.toString() + "]");
+ addDeleteOnTearDown(contextDir);
+ if (!contextDir.mkdirs() && !contextDir.isDirectory()) {
+ fail("Failed to create: [" + contextDir.toString() + "]");
}
tomcat.addContext(contextName, contextDir.getAbsolutePath());
tomcat.start();
@@ -172,9 +172,9 @@ public class TestRegistration extends TomcatBaseTest {
tomcat.getEngine().addChild(host);
final File contextDir2 = new File(getTemporaryDirectory(), "webappFoo2");
- if (!contextDir2.exists()) {
- if (!contextDir2.mkdir())
- fail("Failed to create: [" + contextDir2.toString() + "]");
+ addDeleteOnTearDown(contextDir2);
+ if (!contextDir2.mkdirs() && !contextDir2.isDirectory()) {
+ fail("Failed to create: [" + contextDir2.toString() + "]");
}
tomcat.addContext(host, contextName + "2", contextDir2.getAbsolutePath());
diff --git a/test/org/apache/catalina/servlets/TestDefaultServlet.java b/test/org/apache/catalina/servlets/TestDefaultServlet.java
index 5ac2f11..d0339db 100644
--- a/test/org/apache/catalina/servlets/TestDefaultServlet.java
+++ b/test/org/apache/catalina/servlets/TestDefaultServlet.java
@@ -164,6 +164,7 @@ public class TestDefaultServlet extends TomcatBaseTest {
public void testCustomErrorPage() throws Exception {
File appDir = new File(getTemporaryDirectory(), "MyApp");
File webInf = new File(appDir, "WEB-INF");
+ addDeleteOnTearDown(appDir);
if (!webInf.mkdirs() && !webInf.isDirectory()) {
fail("Unable to create directory [" + webInf + "]");
}
@@ -249,6 +250,7 @@ public class TestDefaultServlet extends TomcatBaseTest {
public void testCustomErrorPageMissing() throws Exception {
File appDir = new File(getTemporaryDirectory(), "MyApp");
File webInf = new File(appDir, "WEB-INF");
+ addDeleteOnTearDown(appDir);
if (!webInf.mkdirs() && !webInf.isDirectory()) {
fail("Unable to create directory [" + webInf + "]");
}
diff --git a/test/org/apache/catalina/session/Benchmarks.java b/test/org/apache/catalina/session/Benchmarks.java
index 9376dba..1e6c545 100644
--- a/test/org/apache/catalina/session/Benchmarks.java
+++ b/test/org/apache/catalina/session/Benchmarks.java
@@ -152,7 +152,7 @@ public class Benchmarks {
* 16 threads - ~45,600ms
*/
@Test
- public void testManagerBaseCreateSession() throws LifecycleException {
+ public void testManagerBaseCreateSession() {
doTestManagerBaseCreateSession(1, 1000000);
doTestManagerBaseCreateSession(2, 1000000);
doTestManagerBaseCreateSession(4, 1000000);
@@ -163,8 +163,8 @@ public class Benchmarks {
}
- private void doTestManagerBaseCreateSession(int threadCount, int iterCount)
- throws LifecycleException {
+ private void doTestManagerBaseCreateSession(int threadCount,
+ int iterCount) {
// Create a default session manager
StandardManager mgr = new StandardManager();
diff --git a/java/org/apache/naming/resources/ImmutableNameNotFoundException.java b/test/org/apache/catalina/startup/FastNonSecureRandom.java
similarity index 50%
copy from java/org/apache/naming/resources/ImmutableNameNotFoundException.java
copy to test/org/apache/catalina/startup/FastNonSecureRandom.java
index a33da75..45a196f 100644
--- a/java/org/apache/naming/resources/ImmutableNameNotFoundException.java
+++ b/test/org/apache/catalina/startup/FastNonSecureRandom.java
@@ -13,40 +13,48 @@
* 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.naming.resources;
+ */
+package org.apache.catalina.startup;
-import javax.naming.Name;
-import javax.naming.NameNotFoundException;
+import java.security.SecureRandom;
+import java.util.Random;
-/**
- * Immutable exception to avoid useless object creation by the proxy context.
- * This should be used only by the proxy context. Actual contexts should return
- * properly populated exceptions.
- *
- * @author <a href="mailto:remm at apache.org">Remy Maucherat</a>
- * @version $Revision: 1044987 $
- */
-public class ImmutableNameNotFoundException
- extends NameNotFoundException {
+public class FastNonSecureRandom extends SecureRandom {
private static final long serialVersionUID = 1L;
-
- @Override
- public void appendRemainingComponent(String name) {/*NOOP*/}
+
+ private final Random random = new Random();
+
@Override
- public void appendRemainingName(Name name) {/*NOOP*/}
+ public String getAlgorithm() {
+ return "INSECURE";
+ }
+
@Override
- public void setRemainingName(Name name) {/*NOOP*/}
+ public synchronized void setSeed(byte[] seed) {
+ // Not implemented
+ }
+
@Override
- public void setResolvedName(Name name) {/*NOOP*/}
+ public synchronized void setSeed(long seed) {
+ // The super class constructor calls this method earlier than our
+ // fields are initialized. Ignore the call.
+ if (random == null) {
+ return;
+ }
+ random.setSeed(seed);
+ }
+
@Override
- public void setRootCause(Throwable e) {/*NOOP*/}
+ public synchronized void nextBytes(byte[] bytes) {
+ random.nextBytes(bytes);
+ }
@Override
- public synchronized Throwable fillInStackTrace() {
- // This class does not provide a stack trace
- return this;
+ public byte[] generateSeed(int numBytes) {
+ byte[] value = new byte[numBytes];
+ nextBytes(value);
+ return value;
}
-}
+
+}
\ No newline at end of file
diff --git a/test/org/apache/catalina/startup/TomcatBaseTest.java b/test/org/apache/catalina/startup/TomcatBaseTest.java
index 32d4d48..3720bf3 100644
--- a/test/org/apache/catalina/startup/TomcatBaseTest.java
+++ b/test/org/apache/catalina/startup/TomcatBaseTest.java
@@ -24,6 +24,7 @@ import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -37,10 +38,15 @@ import static org.junit.Assert.fail;
import org.junit.After;
import org.junit.Before;
+import org.apache.catalina.Container;
+import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
+import org.apache.catalina.Server;
+import org.apache.catalina.Service;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.AprLifecycleListener;
import org.apache.catalina.core.StandardServer;
+import org.apache.catalina.session.StandardManager;
import org.apache.catalina.valves.AccessLogValve;
import org.apache.tomcat.util.buf.ByteChunk;
@@ -56,6 +62,8 @@ public abstract class TomcatBaseTest {
public static final String TEMP_DIR = System.getProperty("java.io.tmpdir");
+ private List<File> deleteOnTearDown = new ArrayList<File>();
+
/**
* Make Tomcat instance accessible to sub-classes.
*/
@@ -80,8 +88,15 @@ public abstract class TomcatBaseTest {
/**
* Helper method that returns the path of the temporary directory used by
- * the test runs. The directory is configured during {@link #setUp()} and is
- * deleted at {@link #tearDown()}.
+ * the test runs. The directory is configured during {@link #setUp()}.
+ *
+ * <p>
+ * It is used as <code>${catalina.base}</code> for the instance of Tomcat
+ * that is being started, but can be used to store other temporary files as
+ * well. Its <code>work</code> and <code>webapps</code> subdirectories are
+ * deleted at {@link #tearDown()}. If you have other files or directories
+ * that have to be deleted on cleanup, register them with
+ * {@link #addDeleteOnTearDown(File)}.
*/
public File getTemporaryDirectory() {
return tempDir;
@@ -104,6 +119,17 @@ public abstract class TomcatBaseTest {
return accessLogEnabled;
}
+ /**
+ * Schedule the given file or directory to be deleted during after-test
+ * cleanup.
+ *
+ * @param file
+ * File or directory
+ */
+ public void addDeleteOnTearDown(File file) {
+ deleteOnTearDown.add(file);
+ }
+
@Before
public void setUp() throws Exception {
// Need to use JULI so log messages from the tests are visible
@@ -126,7 +152,7 @@ public abstract class TomcatBaseTest {
fail("Unable to create appBase for test");
}
- tomcat = new Tomcat();
+ tomcat = new TomcatWithFastSessionIDs();
String protocol = getProtocol();
Connector connector = new Connector(protocol);
@@ -184,7 +210,15 @@ public abstract class TomcatBaseTest {
}
tomcat.destroy();
}
- ExpandWar.delete(tempDir);
+ // Cannot delete the whole tempDir, because logs are there,
+ // and they might be open for writing.
+ // Delete known subdirectories of it.
+ deleteOnTearDown.add(new File(tempDir, "webapps"));
+ deleteOnTearDown.add(new File(tempDir, "work"));
+ for (File file : deleteOnTearDown) {
+ ExpandWar.delete(file);
+ }
+ deleteOnTearDown.clear();
}
/**
@@ -358,4 +392,27 @@ public abstract class TomcatBaseTest {
return rc;
}
+ private static class TomcatWithFastSessionIDs extends Tomcat {
+
+ @Override
+ public void start() throws LifecycleException {
+ // Use fast, insecure session ID generation for all tests
+ Server server = getServer();
+ for (Service service : server.findServices()) {
+ Container e = service.getContainer();
+ for (Container h : e.findChildren()) {
+ for (Container c : h.findChildren()) {
+ StandardManager m = (StandardManager) c.getManager();
+ if (m == null) {
+ m = new StandardManager();
+ m.setSecureRandomClass(
+ "org.apache.catalina.startup.FastNonSecureRandom");
+ c.setManager(m);
+ }
+ }
+ }
+ }
+ super.start();
+ }
+ }
}
diff --git a/test/org/apache/catalina/tribes/test/channel/TestDataIntegrity.java b/test/org/apache/catalina/tribes/test/channel/TestDataIntegrity.java
index 2a77a5c..d863525 100644
--- a/test/org/apache/catalina/tribes/test/channel/TestDataIntegrity.java
+++ b/test/org/apache/catalina/tribes/test/channel/TestDataIntegrity.java
@@ -49,9 +49,6 @@ public class TestDataIntegrity {
private GroupChannel channel2;
private Listener listener1;
- @SuppressWarnings("unused")
- private int threadCounter = 0;
-
@Before
public void setUp() throws Exception {
channel1 = new GroupChannel();
@@ -85,8 +82,6 @@ public class TestDataIntegrity {
}catch ( Exception x ) {
x.printStackTrace();
return;
- } finally {
- threadCounter++;
}
}
};
@@ -115,8 +110,6 @@ public class TestDataIntegrity {
}catch ( Exception x ) {
x.printStackTrace();
return;
- } finally {
- threadCounter++;
}
}
};
diff --git a/test/org/apache/catalina/tribes/test/channel/TestMulticastPackages.java b/test/org/apache/catalina/tribes/test/channel/TestMulticastPackages.java
index b325ddc..354d5cd 100644
--- a/test/org/apache/catalina/tribes/test/channel/TestMulticastPackages.java
+++ b/test/org/apache/catalina/tribes/test/channel/TestMulticastPackages.java
@@ -49,9 +49,6 @@ public class TestMulticastPackages {
private GroupChannel channel2;
private Listener listener1;
- @SuppressWarnings("unused")
- private int threadCounter = 0;
-
@Before
public void setUp() throws Exception {
channel1 = new GroupChannel();
@@ -128,8 +125,6 @@ public class TestMulticastPackages {
}catch ( Exception x ) {
x.printStackTrace();
return;
- } finally {
- threadCounter++;
}
}
};
diff --git a/test/org/apache/catalina/tribes/test/channel/TestUdpPackages.java b/test/org/apache/catalina/tribes/test/channel/TestUdpPackages.java
index e3a66e5..0c9a2aa 100644
--- a/test/org/apache/catalina/tribes/test/channel/TestUdpPackages.java
+++ b/test/org/apache/catalina/tribes/test/channel/TestUdpPackages.java
@@ -49,9 +49,6 @@ public class TestUdpPackages {
private GroupChannel channel2;
private Listener listener1;
- @SuppressWarnings("unused")
- private int threadCounter = 0;
-
@Before
public void setUp() throws Exception {
channel1 = new GroupChannel();
@@ -120,8 +117,6 @@ public class TestUdpPackages {
}catch ( Exception x ) {
x.printStackTrace();
return;
- } finally {
- threadCounter++;
}
}
};
@@ -171,8 +166,6 @@ public class TestUdpPackages {
}catch ( Exception x ) {
x.printStackTrace();
return;
- } finally {
- threadCounter++;
}
}
};
diff --git a/test/org/apache/coyote/ajp/TesterAjpMessage.java b/test/org/apache/coyote/ajp/TesterAjpMessage.java
index 2a84f9a..8835c64 100644
--- a/test/org/apache/coyote/ajp/TesterAjpMessage.java
+++ b/test/org/apache/coyote/ajp/TesterAjpMessage.java
@@ -17,7 +17,7 @@
package org.apache.coyote.ajp;
/**
- * Extends {@link AjpMessage} to provide additional methods for writing to the
+ * Extends {@link AjpMessage} to provide additional methods for reading from the
* message.
* TODO: See if it makes sense for any/all of these methods to be transferred to
* AjpMessage
diff --git a/test/org/apache/naming/resources/TestNamingContext.java b/test/org/apache/naming/resources/TestNamingContext.java
index 64720ec..8777ded 100644
--- a/test/org/apache/naming/resources/TestNamingContext.java
+++ b/test/org/apache/naming/resources/TestNamingContext.java
@@ -214,5 +214,61 @@ public class TestNamingContext extends TomcatBaseTest {
}
}
+ @Test
+ public void testBug51744() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+ tomcat.enableNaming();
+
+ // Must have a real docBase - just use temp
+ StandardContext ctx = (StandardContext)
+ tomcat.addContext("", System.getProperty("java.io.tmpdir"));
+
+ // Map the test Servlet
+ Bug51744Servlet bug51744Servlet = new Bug51744Servlet();
+ Tomcat.addServlet(ctx, "bug51744Servlet", bug51744Servlet);
+ ctx.addServletMapping("/", "bug51744Servlet");
+
+ tomcat.start();
+
+ ByteChunk bc = new ByteChunk();
+ int rc = getUrl("http://localhost:" + getPort() + "/", bc, null);
+ assertEquals(200, rc);
+ assertEquals(Bug51744Servlet.EXPECTED, bc.toString());
+ }
+
+ public static final class Bug51744Servlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ public static final String EXPECTED = "TestValue";
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+
+ resp.setContentType("text/plain;UTF-8");
+ PrintWriter out = resp.getWriter();
+
+ try {
+ Context ctx1 = new InitialContext();
+ Context env1 = (Context) ctx1.lookup("java:comp/env");
+ env1.addToEnvironment("TestName", EXPECTED);
+
+ boolean error = false;
+ try {
+ env1.close();
+ } catch (NamingException ne) {
+ error = true;
+ }
+ if (!error) {
+ throw new ServletException(
+ "No error when one was expected");
+ }
+
+ out.print(env1.getEnvironment().get("TestName"));
+ } catch (NamingException ne) {
+ ne.printStackTrace(out);
+ }
+ }
+ }
}
diff --git a/test/org/apache/tomcat/util/net/TestCustomSsl.java b/test/org/apache/tomcat/util/net/TestCustomSsl.java
index 0cdaf6b..ea10715 100644
--- a/test/org/apache/tomcat/util/net/TestCustomSsl.java
+++ b/test/org/apache/tomcat/util/net/TestCustomSsl.java
@@ -54,7 +54,7 @@ public class TestCustomSsl extends TomcatBaseTest {
return;
}
- connector.setProperty("sslImplemenationName",
+ connector.setProperty("sslImplementationName",
"org.apache.tomcat.util.net.jsse.TesterBug50640SslImpl");
connector.setProperty(TesterBug50640SslImpl.PROPERTY_NAME,
TesterBug50640SslImpl.PROPERTY_VALUE);
diff --git a/test/org/apache/tomcat/util/threads/TestLimitLatch.java b/test/org/apache/tomcat/util/threads/TestLimitLatch.java
index 391b4a9..e9638e7 100644
--- a/test/org/apache/tomcat/util/threads/TestLimitLatch.java
+++ b/test/org/apache/tomcat/util/threads/TestLimitLatch.java
@@ -92,7 +92,7 @@ public class TestLimitLatch {
assertFalse("No threads should be waiting", latch.hasQueuedThreads());
}
- private class TestThread extends Thread {
+ private static class TestThread extends Thread {
private int holdTime;
private LimitLatch latch;
diff --git a/test/webapp-3.0-servletsecurity2/protected.jsp b/test/webapp-3.0/annotations.jsp
similarity index 82%
copy from test/webapp-3.0-servletsecurity2/protected.jsp
copy to test/webapp-3.0/annotations.jsp
index 535c611..180743d 100644
--- a/test/webapp-3.0-servletsecurity2/protected.jsp
+++ b/test/webapp-3.0/annotations.jsp
@@ -14,10 +14,15 @@
See the License for the specific language governing permissions and
limitations under the License.
--%>
+<%@page import="javax.annotation.PostConstruct"%>
<html>
- <head><title>Protected page</title></head>
+ <head><title>Annotations test case</title></head>
<body>
- <p>00-OK</p>
+ <p>Hello world</p>
</body>
</html>
-
+<%!
+ @PostConstruct
+ public void doNothing() {
+ }
+%>
\ No newline at end of file
diff --git a/webapps/docs/appdev/sample/src/mypackage/Hello.java b/webapps/docs/appdev/sample/src/mypackage/Hello.java
index 3a2341e..ea109ce 100644
--- a/webapps/docs/appdev/sample/src/mypackage/Hello.java
+++ b/webapps/docs/appdev/sample/src/mypackage/Hello.java
@@ -36,6 +36,7 @@ import javax.servlet.http.HttpServletResponse;
public final class Hello extends HttpServlet {
+ private static final long serialVersionUID = 1L;
/**
* Respond to a GET request for the content produced by
@@ -47,6 +48,7 @@ public final class Hello extends HttpServlet {
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet error occurs
*/
+ @Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index f130b2f..12267a7 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -53,7 +53,232 @@
They eventually become mixed with the numbered issues. (I.e., numbered
issues to not "pop up" wrt. others).
-->
-<section name="Tomcat 7.0.21 (markt)">
+<section name="Tomcat 7.0.22 (markt)">
+ <subsection name="Catalina">
+ <changelog>
+ <fix>
+ <bug>51550</bug>: An additional change that ensures any exceptions
+ thrown by an Authenticator (or any other Valve configured for the
+ Context) will be handled by the custom error pages for the Context if an
+ appropriate error page is configured. (markt)
+ </fix>
+ <fix>
+ <bug>51580</bug>: Added a nicer error message when a WAR file contains
+ filenames not properly encoded in UTF-8. (schultz)
+ </fix>
+ <fix>
+ <bug>51687</bug>: Added (optional) protection against
+ sun.java2d.Disposer thread pinning a WebappClassLoader into memory
+ in the JreMemoryLeakPreventionListener. (schultz)
+ </fix>
+ <add>
+ <bug>51741</bug>: Fixes a problem with Eclipse WTP "Serve modules
+ without publishing" feature where applications failed to access
+ resources when using getResource() on the classloader. (slaurent)
+ </add>
+ <fix>
+ <bug>51744</bug>: Prevent application code from closing the associated
+ JNDI context while the application is running. (markt)
+ </fix>
+ <fix>
+ Correct a regression with the fix for <bug>51653</bug> that broke custom
+ error pages for 4xx responses from the Authenticators. Error handling
+ and request listeners are now handled in the StandardHostValve to ensure
+ they wrap all Context level activity. (markt)
+ </fix>
+ <fix>
+ <bug>51758</bug>: The digester (used for processing XML files) used the
+ logger name <code>org.apache.commons.digester.Digester</code> rather
+ than the expected <code>org.apache.tomcat.util.digester.Digester</code>.
+ The digester has been changed to use the expected logger name.
+ (markt/kkolinko)
+ </fix>
+ <fix>
+ <bug>51774</bug>: Fix incorrect cached method signature that prevented
+ session tracking modes from being defined in web.xml when running under
+ a security manager. (markt)
+ </fix>
+ <add>
+ Add an annotation cache to the <code>DefaultInstanceManager</code> that
+ improves performance for applications that make use of a lot of
+ non-poolable objects (e.g. tag files) that need to be scanned for
+ annotations when created. (markt)
+ </add>
+ <fix>
+ Use the specification compliant request attribute of
+ <code>javax.servlet.request.ssl_session_id</code> to access the SSL
+ session ID and deprecated the Tomcat specific request attribute. (markt)
+ </fix>
+ <add>
+ Allow to overwrite the check for distributability
+ of session attributes by session implementations. (rjung)
+ </add>
+ <update>
+ Add Java 7 sunec.jar and zipfs.jar to the list of JARs
+ to skip when scanning for TLDs and web fragments. (rjung)
+ </update>
+ <add>
+ <bug>51862</bug>: Added a <code>classesToInitialize</code> attribute to
+ <code>JreMemoryLeakPreventionListener</code> to allow pre-loading of configurable
+ classes to avoid some classloader leaks. (slaurent)
+ </add>
+ <fix>
+ Reduce visibility of static field <code>ManagerBase.name</code> and
+ make it final. (kkolinko)
+ </fix>
+ <update>
+ Add thread name to juli OneLineFormatter. (rjung)
+ </update>
+ <fix>
+ Ensure Servlets that implement ContainerServlet always get treated as
+ restricted. (markt)
+ </fix>
+ <fix>
+ Ensure that the access log always uses the correct value for the remote
+ IP address associated with the request and that requests with multiple
+ errors do not result in multiple entires in the access log. (markt)
+ </fix>
+ </changelog>
+ </subsection>
+ <subsection name="Coyote">
+ <changelog>
+ <scode>
+ Remove unused and undocumented socketCloseDelay attribute from NIO
+ connector. (markt)
+ </scode>
+ <fix>
+ <bug>49683</bug>: Support separate connection and keep-alive timeouts
+ for the APR/native connector HTTP and AJP connectors. (markt)
+ </fix>
+ <scode>
+ Further re-factoring of the HTTP connectors to align the BIO, NIO and
+ APR implementations. (markt)
+ </scode>
+ <fix>
+ <bug>51794</bug>: Fix race condition in NioEndpoint. (fhanik)
+ </fix>
+ <fix>
+ <bug>51811</bug>: Correct SSL configuration property name from
+ sslImplemenationName to sslImplementationName. (rjung)
+ </fix>
+ <fix>
+ Fix a timing issue in NIO connector that meant that stopping a connector
+ did not trigger a Comet END event if the associated processor was
+ processing a READ event when the connector was stopped. (markt)
+ </fix>
+ <fix>
+ Replace unneeded call that iterated events queue in NioEndpoint.Poller.
+ (kkolinko)
+ </fix>
+ <fix>
+ <bug>51860</bug>: Fix issues if using NIO with a custom
+ SSLImplementation. Based on a suggestion by Roman Tsirulnikov. (markt)
+ </fix>
+ <fix>
+ Allow the BIO HTTP connector to be used with SSL when running under Java
+ 7. (markt)
+ </fix>
+ <update>
+ Don't send AJP CPONG if endpoint is already paused. (rjung)
+ </update>
+ <update>
+ Align APR AJP connector with NIO one. Send 503 if endpoint is paused.
+ (rjung)
+ </update>
+ <update>
+ Accept AJP request even if endpoint is paused, if CPING was successful.
+ (rjung)
+ </update>
+ </changelog>
+ </subsection>
+ <subsection name="Jasper">
+ <changelog>
+ <fix>
+ When unloading JSPs due to configuration of the
+ <code>maxLoadedJsps</code> initialisation parameter, the unloading code
+ was retaining a reference to the to the unloaded JSP preventing the
+ associated class from being unloaded until the JSP that replaced it was
+ itself unloaded. (markt)
+ </fix>
+ <fix>
+ <bug>51852</bug>: Correct two problems in the handling of varargs
+ methods with the BeanELResolver. The first meant the wrong method was
+ sometimes called and the second that an ArrayIndexOutOfBoundsExceptions
+ could be thrown. Patch (including a test case) provided by Matt Benson.
+ (markt)
+ </fix>
+ </changelog>
+ </subsection>
+ <subsection name="Cluster">
+ <changelog>
+ <update>
+ Refactor cluster manager configuration: move handling of common
+ attributes to base class. (kfujino, rjung)
+ </update>
+ <add>
+ New cluster manager attribute <code>sessionAttributeFilter</code>
+ allows to filter which session attributes are replicated using a
+ regular expression applied to the attribute name. (rjung)
+ </add>
+ </changelog>
+ </subsection>
+ <subsection name="Web applications">
+ <changelog>
+ <fix>
+ Correct the documentation for <code>connectionLinger</code> attribute
+ for the AJP and HTTP connectors. (markt)
+ </fix>
+ <update>
+ Document caveat of using <code>RemoteAddrValve</code> with IPv6
+ addresses. (kkolinko)
+ </update>
+ </changelog>
+ </subsection>
+ <subsection name="Other">
+ <changelog>
+ <fix>
+ In jdbc-pool: Avoid IllegalArgumentException when setting maxActive
+ less than or equal to 0.
+ ArrayBlockingQueue doesn't allow capacity of 0 or less. (kfujino)
+ </fix>
+ <fix>
+ <bug>48392</bug> (<rev>1169796</rev>): In jdbc-pool: Fix typo in
+ <code>StatementDecoratorInterceptor</code>. (fhanik)
+ </fix>
+ <fix>
+ <bug>51139</bug>:
+ In jdbc-pool: validatorClassName and suspectTimeout are ignored.
+ In order to support them correctly, validatorClassName and
+ suspectTimeout are added to a property list. (kfujino)
+ </fix>
+ <fix>
+ <bug>51786</bug>:
+ In jdbc-pool: Discarded connection is not active in a pool any longer.
+ It removes from the active connection list. (kfujino)
+ </fix>
+ <fix>
+ <bug>51871</bug>: In jdbc-pool: Fix dependency in Maven POM file of
+ tomcat-jbdc. (kkolinko)
+ </fix>
+ <update>
+ Update the "test" target in the default build file to report a test
+ failure only after all available connector variants (bio, nio, apr)
+ have been tested. Do not stop after first connector that fails.
+ (kkolinko)
+ </update>
+ <update>
+ <bug>51887</bug>: When running the unit tests, use a fast but insecure
+ random number source for session ID generation to reduce the delays
+ caused by waiting for entropy. (kkolinko/markt)
+ </update>
+ <scode>
+ Code clean-up to further reduce the number of warnings reported by
+ Eclipse, FindBugs and CheckStyle. (markt/kkolinko)
+ </scode>
+ </changelog>
+ </subsection>
+</section>
+<section name="Tomcat 7.0.21 (markt)" rtext="released 2011-09-01">
<subsection name="Catalina">
<changelog>
<add>
@@ -1416,10 +1641,6 @@
suggested by Werner Donn. (markt)
</add>
<fix>
- <bug>50929</bug>: When wrapping an exception, include the root cause.
- Patch provided by sebb. (markt)
- </fix>
- <fix>
Make the CSRF nonce cache serializable so that it can be replicated
across a cluster and/or persisted across Tomcat restarts. (markt)
</fix>
@@ -1458,6 +1679,10 @@
since this too cannot be reliably determined. (markt)
</fix>
<fix>
+ <bug>50929</bug>: When wrapping an exception, include the root cause.
+ Patch provided by sebb. (markt)
+ </fix>
+ <fix>
<bug>50991</bug>: Fix regression in fix for <bug>25060</bug> that called
close on a JNDI resource while it was still available to the
application. (markt)
@@ -1485,8 +1710,8 @@
Don't register non-singelton DataSource resources with JMX. (markt)
</fix>
<add>
- Provide additional configuration options for the DIGEST authenticator.
- (markt)
+ CVE-2011-1184: Provide additional configuration options for the DIGEST
+ authenticator. (markt)
</add>
<fix>
Provide a workaround for Tomcat hanging during shutdown when running the
@@ -1985,7 +2210,8 @@
</fix>
<fix>
Improve HTTP specification compliance in support of
- <code>Accept-Language</code> header. (kkolinko)
+ <code>Accept-Language</code> header. This protects from known exploit
+ of the Oracle JVM bug that triggers a DoS, CVE-2010-4476. (kkolinko)
</fix>
</changelog>
</subsection>
diff --git a/webapps/docs/config/ajp.xml b/webapps/docs/config/ajp.xml
index 443ca3a..149b078 100644
--- a/webapps/docs/config/ajp.xml
+++ b/webapps/docs/config/ajp.xml
@@ -292,9 +292,9 @@
</attribute>
<attribute name="connectionLinger" required="false">
- <p>The number of milliseconds during which the sockets used by this
+ <p>The number of seconds during which the sockets used by this
<strong>Connector</strong> will linger when they are closed.
- The default value <code>-1</code> which disables this option.</p>
+ If not specified, the JVM default will be used.</p>
</attribute>
<attribute name="connectionTimeout" required="false">
diff --git a/webapps/docs/config/cluster-manager.xml b/webapps/docs/config/cluster-manager.xml
index 8e92c71..107f189 100644
--- a/webapps/docs/config/cluster-manager.xml
+++ b/webapps/docs/config/cluster-manager.xml
@@ -83,6 +83,15 @@
sessions to expire on all nodes when a shutdown occurs on one node, set
this value to <code>true</code>. Default value is <code>false</code>.
</attribute>
+ <attribute name="sessionAttributeFilter" required="false">
+ A regular expression used to filter, which session attributes will
+ be replicated. An attribute will only be replicated, if its name
+ matches this pattern. If the pattern is not set (default), all
+ attributes are eligible for replication. As an example, the value
+ <code>^(userName|sessionHistory)$</code> will only replicate the two
+ session attributes named <code>userName</code> and
+ <code>sessionHistory</code>.
+ </attribute>
</attributes>
</subsection>
<subsection name="org.apache.catalina.ha.session.DeltaManager Attributes">
diff --git a/webapps/docs/config/filter.xml b/webapps/docs/config/filter.xml
index 49bd27f..707c667 100644
--- a/webapps/docs/config/filter.xml
+++ b/webapps/docs/config/filter.xml
@@ -531,6 +531,15 @@ FINE: Request "/docs/config/manager.html" with response status "200" content-typ
package. Please consult the Java documentation for details of the
expressions supported.</p>
+ <p><strong>Note:</strong> There is a caveat when using this filter with
+ IPv6 addresses. Format of the IP address that this valve is processing
+ depends on the API that was used to obtain it. If the address was obtained
+ from Java socket using Inet6Address class, its format will be
+ <code>x:x:x:x:x:x:x:x</code>. That is, the IP address for localhost
+ will be <code>0:0:0:0:0:0:0:1</code> instead of the more widely used
+ <code>::1</code>. Consult your access logs for the actual value.</p>
+
+ <p>See also: <a href="#Remote_Host_Filter">Remote Host Filter</a>.</p>
</subsection>
<subsection name="Filter Class Name">
@@ -569,6 +578,24 @@ FINE: Request "/docs/config/manager.html" with response status "200" content-typ
</subsection>
+ <subsection name="Example">
+ <p>To allow access only for the clients connecting from localhost:</p>
+<pre>
+ <filter>
+ <filter-name>Remote Address Filter</filter-name>
+ <filter-class>org.apache.catalina.filters.RemoteAddrFilter</filter-class>
+ <init-param>
+ <param-name>allow</param-name>
+ <param-value>127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1</param-value>
+ </init-param>
+ </filter>
+ <filter-mapping>
+ <filter-name>Remote Address Filter</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+</pre>
+ </subsection>
+
</section>
@@ -586,6 +613,7 @@ FINE: Request "/docs/config/manager.html" with response status "200" content-typ
package. Please consult the Java documentation for details of the
expressions supported.</p>
+ <p>See also: <a href="#Remote_Address_Filter">Remote Address Filter</a>.</p>
</subsection>
<subsection name="Filter Class Name">
diff --git a/webapps/docs/config/http.xml b/webapps/docs/config/http.xml
index 7219c4f..3e34cd3 100644
--- a/webapps/docs/config/http.xml
+++ b/webapps/docs/config/http.xml
@@ -309,15 +309,18 @@
</attribute>
<attribute name="connectionLinger" required="false">
- <p>The number of milliseconds during which the sockets used by this
+ <p>The number of seconds during which the sockets used by this
<strong>Connector</strong> will linger when they are closed.
- The default value <code>-1</code> which disables this option.</p>
+ If not specified, the JVM default will be used.</p>
</attribute>
<attribute name="connectionTimeout" required="false">
<p>The number of milliseconds this <strong>Connector</strong> will wait,
after accepting a connection, for the request URI line to be
- presented. The default value is 60000 (i.e. 60 seconds).</p>
+ presented. Use a value of -1 to indicate no (i.e. infinite) timeout.
+ The default value is 60000 (i.e. 60 seconds) but note that the standard
+ server.xml that ships with Tomcat sets this to 20000 (i.e. 20 seconds).
+ </p>
</attribute>
<attribute name="connectionUploadTimeout" required="false">
@@ -347,7 +350,8 @@
<p>The number of milliseconds this <strong>Connector</strong> will wait
for another HTTP request before closing the connection. The default value
is to use the value that has been set for the
- <strong>connectionTimeout</strong> attribute.</p>
+ <strong>connectionTimeout</strong> attribute.
+ Use a value of -1 to indicate no (i.e. infinite) timeout.</p>
</attribute>
<attribute name="maxConnections" required="false">
@@ -986,7 +990,7 @@
used.</p>
</attribute>
- <attribute name="sslImplemenationName" required="false">
+ <attribute name="sslImplementationName" required="false">
<p>The class name of the SSL implementation to use. If not specified, the
default of <code>org.apache.tomcat.util.net.jsse.JSSEImplementation</code>
will be used which wraps JVM's default JSSE provider. Note that the
diff --git a/webapps/docs/config/listeners.xml b/webapps/docs/config/listeners.xml
index 1900e7b..ef29c3c 100644
--- a/webapps/docs/config/listeners.xml
+++ b/webapps/docs/config/listeners.xml
@@ -181,6 +181,16 @@
Defaults to <code>false</code> because an AWT thread is launched.</p>
</attribute>
+ <attribute name="classesToInitialize" required="false">
+ <p>List of comma-separated fully qualified class names to load and initialize
+ during the startup of this Listener. This allows to pre-load classes that are
+ known to provoke classloader leaks if they are loaded during a request
+ processing. Non-JRE classes may be referenced, like
+ <code>oracle.jdbc.driver.OracleTimeoutThreadPerVM</code>.
+ The default value is empty, but specific JRE classes are loaded by other leak
+ protection features managed by other attributes of this Listener.</p>
+ </attribute>
+
<attribute name="driverManagerProtection" required="false">
<p>The first use of <code>java.sql.DriverManager</code> will trigger the
loading of JDBNC Driver in the the current class loader. The web
@@ -199,6 +209,13 @@
startup on non-Sun JVMs. The default is <code>true</code>.</p>
</attribute>
+ <attribute name="java2DDisposerProtection" required="false">
+ <p>Enables protection so that loading the
+ <code>sun.java2d.Disposer</code> class by a web application does not
+ result in a memory leak.
+ Defaults to <code>false</code> because a thread is launched.</p>
+ </attribute>
+
<attribute name="ldapPoolProtection" required="false">
<p>Enables protection so that the PoolCleaner thread started by
<code>com.sun.jndi.ldap.LdapPoolManager</code> does not result in a
diff --git a/webapps/docs/config/valve.xml b/webapps/docs/config/valve.xml
index 3272733..9572e6a 100644
--- a/webapps/docs/config/valve.xml
+++ b/webapps/docs/config/valve.xml
@@ -509,6 +509,16 @@
package. Please consult the Java documentation for details of the
expressions supported.</p>
+ <p><strong>Note:</strong> There is a caveat when using this valve with
+ IPv6 addresses. Format of the IP address that this valve is processing
+ depends on the API that was used to obtain it. If the address was obtained
+ from Java socket using Inet6Address class, its format will be
+ <code>x:x:x:x:x:x:x:x</code>. That is, the IP address for localhost
+ will be <code>0:0:0:0:0:0:0:1</code> instead of the more widely used
+ <code>::1</code>. Consult your access logs for the actual value.</p>
+
+ <p>See also: <a href="#Remote_Host_Filter">Remote Host Filter</a>,
+ <a href="#Remote_IP_Valve">Remote IP Valve</a>.</p>
</subsection>
<subsection name="Attributes">
@@ -544,6 +554,14 @@
</subsection>
+ <subsection name="Example">
+ <p>To allow access only for the clients connecting from localhost:</p>
+<pre>
+ <Valve className="org.apache.catalina.valves.RemoteAddrValve"
+ allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1"/>
+</pre>
+ </subsection>
+
</section>
@@ -565,6 +583,7 @@
package. Please consult the Java documentation for details of the
expressions supported.</p>
+ <p>See also: <a href="#Remote_Address_Filter">Remote Address Filter</a>.</p>
</subsection>
<subsection name="Attributes">
diff --git a/webapps/docs/manager-howto.xml b/webapps/docs/manager-howto.xml
index 81ae2b0..bcfd7fd 100644
--- a/webapps/docs/manager-howto.xml
+++ b/webapps/docs/manager-howto.xml
@@ -159,7 +159,9 @@ the role <strong>manager-script</strong>.</p>
<p>In addition to the password restrictions the Manager web application
could be restricted by the remote IP address or host by adding a
-<code>RemoteAddrValve</code> or <code>RemoteHostValve</code>. Here is
+<code>RemoteAddrValve</code> or <code>RemoteHostValve</code>.
+See <a href="config/valve.html#Remote_Address_Filter">valves documentation</a>
+for details. Here is
an example of restricting access to the localhost by IP address:</p>
<pre>
<Context privileged="true">
diff --git a/webapps/docs/ssl-howto.xml b/webapps/docs/ssl-howto.xml
index 8451b1a..7d74d22 100644
--- a/webapps/docs/ssl-howto.xml
+++ b/webapps/docs/ssl-howto.xml
@@ -582,7 +582,7 @@ public class SessionTrackingModeListener implements ServletContextListener {
<p>To access the SSL session ID from the request, use:<br />
<code>
- String sslID = (String)request.getAttribute("javax.servlet.request.ssl_session");
+ String sslID = (String)request.getAttribute("javax.servlet.request.ssl_session_id");
</code>
<br />
For additional discussion on this area, please see
diff --git a/webapps/docs/tomcat-docs.xsl b/webapps/docs/tomcat-docs.xsl
index ce43110..3ec0550 100644
--- a/webapps/docs/tomcat-docs.xsl
+++ b/webapps/docs/tomcat-docs.xsl
@@ -17,7 +17,7 @@
-->
<!-- Content Stylesheet for "tomcat-docs" Documentation -->
-<!-- $Id: tomcat-docs.xsl 1036949 2010-11-19 17:18:04Z markt $ -->
+<!-- $Id: tomcat-docs.xsl 1175614 2011-09-25 22:48:41Z kkolinko $ -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
@@ -109,10 +109,8 @@
</xsl:if>
</td>
<td>
- <font face="arial,helvetica,sanserif">
- <h1><xsl:value-of select="$project/title"/></h1>
- Version <xsl:value-of select="$version"/>, <xsl:value-of select="$build-date"/>
- </font>
+ <h1><font face="arial,helvetica,sanserif"><xsl:value-of select="$project/title"/></font></h1>
+ <font face="arial,helvetica,sanserif">Version <xsl:value-of select="$version"/>, <xsl:value-of select="$build-date"/></font>
</td>
<td>
<xsl:comment>APACHE LOGO</xsl:comment>
diff --git a/webapps/docs/tribes/tomcat-docs.xsl b/webapps/docs/tribes/tomcat-docs.xsl
deleted file mode 100644
index c839346..0000000
--- a/webapps/docs/tribes/tomcat-docs.xsl
+++ /dev/null
@@ -1,452 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
-<!--
- 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.
--->
-<!-- Content Stylesheet for "tomcat-docs" Documentation -->
-
-<!-- $Id: tomcat-docs.xsl 1054620 2011-01-03 14:00:29Z rjung $ -->
-
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- version="1.0">
-
-
- <!-- Output method -->
- <xsl:output method="html"
- encoding="iso-8859-1"
- indent="no"/>
-
-
- <!-- Defined parameters (overrideable) -->
- <xsl:param name="home-name" select="'The Tomcat Project'"/>
- <xsl:param name="home-href" select="'http://tomcat.apache.org/'"/>
- <xsl:param name="home-logo" select="'/images/tomcat.gif'"/>
- <xsl:param name="printer-logo" select="'/images/printer.gif'"/>
- <xsl:param name="apache-logo" select="'/images/asf-logo.gif'"/>
- <xsl:param name="relative-path" select="'.'"/>
- <xsl:param name="void-image" select="'/images/void.gif'"/>
- <xsl:param name="project-menu" select="'menu'"/>
- <xsl:param name="standalone" select="''"/>
- <xsl:param name="buglink" select="'http://issues.apache.org/bugzilla/show_bug.cgi?id='"/>
-
- <!-- Defined variables (non-overrideable) -->
- <xsl:variable name="body-bg" select="'#ffffff'"/>
- <xsl:variable name="body-fg" select="'#000000'"/>
- <xsl:variable name="body-link" select="'#525D76'"/>
- <xsl:variable name="banner-bg" select="'#525D76'"/>
- <xsl:variable name="banner-fg" select="'#ffffff'"/>
- <xsl:variable name="sub-banner-bg" select="'#828DA6'"/>
- <xsl:variable name="sub-banner-fg" select="'#ffffff'"/>
- <xsl:variable name="source-color" select="'#023264'"/>
- <xsl:variable name="attributes-color" select="'#023264'"/>
- <xsl:variable name="table-th-bg" select="'#039acc'"/>
- <xsl:variable name="table-td-bg" select="'#a0ddf0'"/>
-
- <!-- Process an entire document into an HTML page -->
- <xsl:template match="document">
- <xsl:variable name="project"
- select="document('project.xml')/project"/>
- <html>
- <head>
- <title><xsl:value-of select="project/title"/> - <xsl:value-of select="properties/title"/></title>
- <xsl:for-each select="properties/author">
- <xsl:variable name="name">
- <xsl:value-of select="."/>
- </xsl:variable>
- <xsl:variable name="email">
- <xsl:value-of select="@email"/>
- </xsl:variable>
- <meta name="author" value="{$name}"/>
- <meta name="email" value="{$email}"/>
- </xsl:for-each>
- </head>
-
- <body bgcolor="{$body-bg}" text="{$body-fg}" link="{$body-link}"
- alink="{$body-link}" vlink="{$body-link}">
-
- <table border="0" width="100%" cellspacing="0">
-
- <xsl:comment>PAGE HEADER</xsl:comment>
- <tr>
- <td>
- <xsl:if test="project/logo">
- <xsl:variable name="alt">
- <xsl:value-of select="project/logo"/>
- </xsl:variable>
- <xsl:variable name="home">
- <xsl:value-of select="project/@href"/>
- </xsl:variable>
- <xsl:variable name="src">
- <!--<xsl:value-of select="$relative-path"/>--><xsl:value-of select="project/logo/@href"/>
- </xsl:variable>
-
- <xsl:comment>PROJECT LOGO</xsl:comment>
- <a href="{$home}">
- <img src="{$src}" align="right" alt="{$alt}" border="0"/>
- </a>
- </xsl:if>
- </td>
- <td>
- <font face="arial,helvetica,sanserif">
- <h1><xsl:value-of select="$project/title"/></h1>
- </font>
- </td>
- <td>
- <xsl:comment>APACHE LOGO</xsl:comment>
- <xsl:variable name="src">
- <xsl:value-of select="$relative-path"/><xsl:value-of select="$apache-logo"/>
- </xsl:variable>
- <a href="http://www.apache.org/">
- <img src="http://tomcat.apache.org/tomcat-5.5-doc/images/asf-logo.gif" align="right" alt="Apache Logo" border="0"/>
- </a>
- </td>
- </tr>
- </table>
-
- <table border="0" width="100%" cellspacing="4">
-
- <xsl:comment>HEADER SEPARATOR</xsl:comment>
- <tr>
- <td colspan="2">
- <hr noshade="noshade" size="1"/>
- </td>
- </tr>
-
- <tr>
-
- <!-- Don't generate a menu if styling printer friendly docs -->
- <xsl:if test="$project-menu = 'menu'">
- <xsl:comment>LEFT SIDE NAVIGATION</xsl:comment>
- <td width="20%" valign="top" nowrap="true">
- <xsl:apply-templates select="project/body/menu"/>
- </td>
- </xsl:if>
-
- <xsl:comment>RIGHT SIDE MAIN BODY</xsl:comment>
- <td width="80%" valign="top" align="left">
- <table border="0" width="100%" cellspacing="4">
- <tr>
- <td align="left" valign="top">
- <h1><xsl:value-of select="project/title"/></h1>
- <h2><xsl:value-of select="properties/title"/></h2>
- </td>
- <td align="right" valign="top" nowrap="true">
- <!-- Add the printer friendly link for docs with a menu -->
- <xsl:if test="$project-menu = 'menu'">
- <xsl:variable name="src">
- <xsl:value-of select="$relative-path"/><xsl:value-of select="$printer-logo"/>
- </xsl:variable>
- <xsl:variable name="url">
- <xsl:value-of select="/document/@url"/>
- </xsl:variable>
- <small>
- <a href="printer/{$url}">
- <img src="{$src}" border="0" alt="Printer Friendly Version"/>
- <br />print-friendly<br />version
- </a>
- </small>
- </xsl:if>
- <xsl:if test="$project-menu != 'menu'">
- <xsl:variable name="void">
- <xsl:value-of select="$relative-path"/><xsl:value-of select="$void-image"/>
- </xsl:variable>
- <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
- </xsl:if>
- </td>
- </tr>
- </table>
- <xsl:apply-templates select="body/section"/>
- </td>
-
- </tr>
-
- <xsl:comment>FOOTER SEPARATOR</xsl:comment>
- <tr>
- <td colspan="2">
- <hr noshade="noshade" size="1"/>
- </td>
- </tr>
-
- <xsl:comment>PAGE FOOTER</xsl:comment>
- <tr><td colspan="2">
- <div align="center"><font color="{$body-link}" size="-1"><em>
- Copyright © 1999-2011, Apache Software Foundation
- </em></font></div>
- </td></tr>
-
- </table>
- </body>
- </html>
-
- </xsl:template>
-
-
- <!-- Process a menu for the navigation bar -->
- <xsl:template match="menu">
- <p><strong><xsl:value-of select="@name"/></strong></p>
- <ul>
- <xsl:apply-templates select="item"/>
- </ul>
- </xsl:template>
-
-
- <!-- Process a menu item for the navigation bar -->
- <xsl:template match="item">
- <xsl:variable name="href">
- <xsl:value-of select="@href"/>
- </xsl:variable>
- <li><a href="{$href}"><xsl:value-of select="@name"/></a></li>
- </xsl:template>
-
-
- <!-- Process a documentation section -->
- <xsl:template match="section">
- <xsl:variable name="name">
- <xsl:value-of select="@name"/>
- </xsl:variable>
- <table border="0" cellspacing="0" cellpadding="2">
- <!-- Section heading -->
- <tr><td bgcolor="{$banner-bg}">
- <font color="{$banner-fg}" face="arial,helvetica.sanserif">
- <a name="{$name}">
- <strong><xsl:value-of select="@name"/></strong></a></font>
- </td></tr>
- <!-- Section body -->
- <tr><td><blockquote>
- <xsl:apply-templates/>
- </blockquote></td></tr>
- </table>
- </xsl:template>
-
-
- <!-- Process a documentation subsection -->
- <xsl:template match="subsection">
- <xsl:variable name="name">
- <xsl:value-of select="@name"/>
- </xsl:variable>
- <table border="0" cellspacing="0" cellpadding="2">
- <!-- Subsection heading -->
- <tr><td bgcolor="{$sub-banner-bg}">
- <font color="{$sub-banner-fg}" face="arial,helvetica.sanserif">
- <a name="{$name}">
- <strong><xsl:value-of select="@name"/></strong></a></font>
- </td></tr>
- <!-- Subsection body -->
- <tr><td><blockquote>
- <xsl:apply-templates/>
- </blockquote></td></tr>
- </table>
- </xsl:template>
-
-
- <!-- Process a source code example -->
- <xsl:template match="source">
- <xsl:variable name="void">
- <xsl:value-of select="$relative-path"/><xsl:value-of select="$void-image"/>
- </xsl:variable>
- <div align="left">
- <table cellspacing="4" cellpadding="0" border="0">
- <tr>
- <td bgcolor="{$source-color}" width="1" height="1">
- <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
- </td>
- <td bgcolor="{$source-color}" height="1">
- <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
- </td>
- <td bgcolor="{$source-color}" width="1" height="1">
- <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
- </td>
- </tr>
- <tr>
- <td bgcolor="{$source-color}" width="1">
- <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
- </td>
- <td bgcolor="#ffffff" height="1"><pre>
- <xsl:value-of select="."/>
- </pre></td>
- <td bgcolor="{$source-color}" width="1">
- <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
- </td>
- </tr>
- <tr>
- <td bgcolor="{$source-color}" width="1" height="1">
- <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
- </td>
- <td bgcolor="{$source-color}" height="1">
- <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
- </td>
- <td bgcolor="{$source-color}" width="1" height="1">
- <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
- </td>
- </tr>
- </table>
- </div>
- </xsl:template>
-
-
- <!-- Process an attributes list with nested attribute elements -->
- <xsl:template match="attributes">
- <table border="1" cellpadding="5">
- <tr>
- <th width="15%" bgcolor="{$attributes-color}">
- <font color="#ffffff">Attribute</font>
- </th>
- <th width="85%" bgcolor="{$attributes-color}">
- <font color="#ffffff">Description</font>
- </th>
- </tr>
- <xsl:for-each select="attribute">
- <tr>
- <td align="left" valign="center">
- <xsl:if test="@required = 'true'">
- <strong><code><xsl:value-of select="@name"/></code></strong>
- </xsl:if>
- <xsl:if test="@required != 'true'">
- <code><xsl:value-of select="@name"/></code>
- </xsl:if>
- </td>
- <td align="left" valign="center">
- <xsl:apply-templates/>
- </td>
- </tr>
- </xsl:for-each>
- </table>
- </xsl:template>
-
- <!-- Fix relative links in printer friendly versions of the docs -->
- <xsl:template match="a">
- <xsl:variable name="href" select="@href"/>
- <xsl:choose>
- <xsl:when test="$standalone = 'standalone'">
- <xsl:apply-templates/>
- </xsl:when>
- <xsl:when test="$project-menu != 'menu' and starts-with(@href,'../')">
- <a href="../{$href}"><xsl:apply-templates/></a>
- </xsl:when>
- <xsl:when test="$project-menu != 'menu' and starts-with(@href,'./') and contains(substring(@href,3),'/')">
- <a href=".{$href}"><xsl:apply-templates/></a>
- </xsl:when>
- <xsl:when test="$project-menu != 'menu' and not(contains(@href,'//')) and not(starts-with(@href,'/')) and not(starts-with(@href,'#')) and contains(@href,'/')">
- <a href="../{$href}"><xsl:apply-templates/></a>
- </xsl:when>
- <xsl:when test="$href != ''">
- <a href="{$href}"><xsl:apply-templates/></a>
- </xsl:when>
- <xsl:otherwise>
- <xsl:variable name="name" select="@name"/>
- <a name="{$name}"><xsl:apply-templates/></a>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:template>
-
- <!-- Changelog related tags -->
- <xsl:template match="changelog">
- <table border="0" cellpadding="2" cellspacing="2">
- <xsl:apply-templates/>
- </table>
- </xsl:template>
-
- <xsl:template match="changelog/add">
- <tr>
- <xsl:variable name="src"><xsl:value-of select="$relative-path"/>/images/add.gif</xsl:variable>
- <td><img alt="add" class="icon" src="{$src}"/></td>
- <td><xsl:apply-templates/></td>
- </tr>
- </xsl:template>
-
- <xsl:template match="changelog/update">
- <tr>
- <xsl:variable name="src"><xsl:value-of select="$relative-path"/>/images/update.gif</xsl:variable>
- <td><img alt="update" class="icon" src="{$src}"/></td>
- <td><xsl:apply-templates/></td>
- </tr>
- </xsl:template>
-
- <xsl:template match="changelog/design">
- <tr>
- <xsl:variable name="src"><xsl:value-of select="$relative-path"/>/images/design.gif</xsl:variable>
- <td><img alt="design" class="icon" src="{$src}"/></td>
- <td><xsl:apply-templates/></td>
- </tr>
- </xsl:template>
-
- <xsl:template match="changelog/docs">
- <tr>
- <xsl:variable name="src"><xsl:value-of select="$relative-path"/>/images/docs.gif</xsl:variable>
- <td><img alt="docs" class="icon" src="{$src}"/></td>
- <td><xsl:apply-templates/></td>
- </tr>
- </xsl:template>
-
- <xsl:template match="changelog/fix">
- <tr>
- <xsl:variable name="src"><xsl:value-of select="$relative-path"/>/images/fix.gif</xsl:variable>
- <td><img alt="fix" class="icon" src="{$src}"/></td>
- <td><xsl:apply-templates/></td>
- </tr>
- </xsl:template>
-
- <xsl:template match="changelog/scode">
- <tr>
- <xsl:variable name="src"><xsl:value-of select="$relative-path"/>/images/code.gif</xsl:variable>
- <td><img alt="code" class="icon" src="{$src}"/></td>
- <td><xsl:apply-templates/></td>
- </tr>
- </xsl:template>
-
- <!-- Process an attributes list with nested attribute elements -->
- <xsl:template match="status">
- <table border="1" cellpadding="5">
- <tr>
- <th width="15%" bgcolor="{$attributes-color}">
- <font color="#ffffff">Priority</font>
- </th>
- <th width="50%" bgcolor="{$attributes-color}">
- <font color="#ffffff">Action Item</font>
- </th>
- <th width="25%" bgcolor="{$attributes-color}">
- <font color="#ffffff">Volunteers</font>
- </th>
- <xsl:for-each select="item">
- <tr>
- <td align="left" valign="center">
- <xsl:value-of select="@priority"/>
- </td>
- <td align="left" valign="center">
- <xsl:apply-templates/>
- </td>
- <td align="left" valign="center">
- <xsl:value-of select="@owner"/>
- </td>
- </tr>
- </xsl:for-each>
- </tr>
- </table>
- </xsl:template>
-
- <!-- Link to a bug report -->
- <xsl:template match="bug">
- <xsl:variable name="link"><xsl:value-of select="$buglink"/><xsl:value-of select="text()"/></xsl:variable>
- <a href="{$link}"><xsl:apply-templates/></a>
- </xsl:template>
-
- <!-- Process everything else by just passing it through -->
- <xsl:template match="*|@*">
- <xsl:copy>
- <xsl:apply-templates select="@*|*|text()"/>
- </xsl:copy>
- </xsl:template>
-
-</xsl:stylesheet>
diff --git a/webapps/host-manager/META-INF/context.xml b/webapps/host-manager/META-INF/context.xml
index 1019dc4..3390e96 100644
--- a/webapps/host-manager/META-INF/context.xml
+++ b/webapps/host-manager/META-INF/context.xml
@@ -22,6 +22,6 @@
-->
<!--
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
- allow="127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}|::1" />
+ allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
-->
</Context>
\ No newline at end of file
diff --git a/webapps/manager/META-INF/context.xml b/webapps/manager/META-INF/context.xml
index 99276a2..21d9bac 100644
--- a/webapps/manager/META-INF/context.xml
+++ b/webapps/manager/META-INF/context.xml
@@ -22,6 +22,6 @@
-->
<!--
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
- allow="127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}|::1" />
+ allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
-->
</Context>
--
tomcat7: Servlet and JSP engine
More information about the pkg-java-commits
mailing list