--- a/java/org/apache/catalina/Container.java
+++ a/java/org/apache/catalina/Container.java
@@ -467,4 +467,21 @@ public interface Container extends Lifecycle {
* that the request/response still appears in the correct access logs.
*/
public AccessLog getAccessLog();
+
+
+ /**
+ * Returns the number of threads available for starting and stopping any
+ * children associated with this container. This allows start/stop calls to
+ * children to be processed in parallel.
+ */
+ public int getStartStopThreads();
+
+
+ /**
+ * Sets the number of threads available for starting and stopping any
+ * children associated with this container. This allows start/stop calls to
+ * children to be processed in parallel.
+ * @param startStopThreads The new number of threads to be used
+ */
+ public void setStartStopThreads(int startStopThreads);
}
--- a/java/org/apache/catalina/Host.java
+++ a/java/org/apache/catalina/Host.java
@@ -17,6 +17,7 @@
package org.apache.catalina;
import java.io.File;
+import java.util.concurrent.ExecutorService;
import java.util.regex.Pattern;
@@ -180,9 +181,16 @@ public interface Host extends Container {
public void setDeployIgnore(String deployIgnore);
- // --------------------------------------------------------- Public Methods
+ /**
+ * Return the executor that is used for starting and stopping contexts. This
+ * is primarily for use by components deploying contexts that want to do
+ * this in a multi-threaded manner.
+ */
+ public ExecutorService getStartStopExecutor();
+ // --------------------------------------------------------- Public Methods
+
/**
* Add an alias name that should be mapped to this same Host.
*
--- a/java/org/apache/catalina/core/ContainerBase.java
+++ a/java/org/apache/catalina/core/ContainerBase.java
@@ -26,6 +26,13 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
import javax.management.ObjectName;
import javax.naming.directory.DirContext;
@@ -60,7 +67,8 @@ import org.apache.tomcat.util.res.StringManager;
/**
* Abstract implementation of the Container interface, providing common
* functionality required by nearly every implementation. Classes extending
- * this base class must may implement a replacement for invoke()
.
+ * this base class must implement getInfo()
, and may implement
+ * a replacement for invoke()
.
*
* All subclasses of this abstract base class will include support for a
* Pipeline object that defines the processing to be performed for each request
@@ -273,8 +281,55 @@ public abstract class ContainerBase extends LifecycleMBeanBase
protected volatile AccessLog accessLog = null;
private volatile boolean accessLogScanComplete = false;
+
+ /**
+ * The number of threads available to process start and stop events for any
+ * children associated with this container.
+ */
+ private int startStopThreads = 1;
+ protected ThreadPoolExecutor startStopExecutor;
+
// ------------------------------------------------------------- Properties
+ @Override
+ public int getStartStopThreads() {
+ return startStopThreads;
+ }
+
+ /**
+ * Handles the special values.
+ */
+ private int getStartStopThreadsInternal() {
+ int result = getStartStopThreads();
+
+ // Positive values are unchanged
+ if (result > 0) {
+ return result;
+ }
+
+ // Zero == Runtime.getRuntime().availableProcessors()
+ // -ve == Runtime.getRuntime().availableProcessors() + value
+ // These two are the same
+ result = Runtime.getRuntime().availableProcessors() + result;
+ if (result < 1) {
+ result = 1;
+ }
+ return result;
+ }
+
+ @Override
+ public void setStartStopThreads(int startStopThreads) {
+ this.startStopThreads = startStopThreads;
+
+ // Use local copies to ensure thread safety
+ ThreadPoolExecutor executor = startStopExecutor;
+ if (executor != null) {
+ int newThreads = getStartStopThreadsInternal();
+ executor.setMaximumPoolSize(newThreads);
+ executor.setCorePoolSize(newThreads);
+ }
+ }
+
/**
* Get the delay between the invocation of the backgroundProcess method on
@@ -989,6 +1044,19 @@ public abstract class ContainerBase extends LifecycleMBeanBase
}
+ @Override
+ protected void initInternal() throws LifecycleException {
+ BlockingQueueDigester
s available to process web deployment descriptor
- * files.
- */
- protected static Digester[] webDigesters = new Digester[4];
-
-
- /**
- * The Digester
s available to process web fragment deployment
- * descriptor files.
- */
- protected static Digester[] webFragmentDigesters = new Digester[4];
-
-
- /**
- * The Rule
s used to parse the web.xml
- */
- protected static WebRuleSet webRuleSet = new WebRuleSet(false);
-
-
- /**
- * The Rule
s used to parse the web-fragment.xml
- */
- protected static WebRuleSet webFragmentRuleSet = new WebRuleSet(true);
-
-
- /**
* Deployment count.
*/
protected static long deploymentCount = 0L;
@@ -236,12 +204,14 @@ public class ContextConfig
* deployment descriptor files.
*/
protected Digester webDigester = null;
+ protected WebRuleSet webRuleSet = null;
/**
* The Digester
we will use to process web fragment
* deployment descriptor files.
*/
protected Digester webFragmentDigester = null;
+ protected WebRuleSet webFragmentRuleSet = null;
// ------------------------------------------------------------- Properties
@@ -486,60 +456,21 @@ public class ContextConfig
/**
- * Create (if necessary) and return a Digester configured to process the
+ * Create and return a Digester configured to process the
* web application deployment descriptor (web.xml).
*/
public void createWebXmlDigester(boolean namespaceAware,
boolean validation) {
- if (!namespaceAware && !validation) {
- if (webDigesters[0] == null) {
- webDigesters[0] = DigesterFactory.newDigester(validation,
- namespaceAware, webRuleSet);
- webFragmentDigesters[0] = DigesterFactory.newDigester(validation,
- namespaceAware, webFragmentRuleSet);
- webDigesters[0].getParser();
- webFragmentDigesters[0].getParser();
- }
- webDigester = webDigesters[0];
- webFragmentDigester = webFragmentDigesters[0];
-
- } else if (!namespaceAware && validation) {
- if (webDigesters[1] == null) {
- webDigesters[1] = DigesterFactory.newDigester(validation,
- namespaceAware, webRuleSet);
- webFragmentDigesters[1] = DigesterFactory.newDigester(validation,
- namespaceAware, webFragmentRuleSet);
- webDigesters[1].getParser();
- webFragmentDigesters[1].getParser();
- }
- webDigester = webDigesters[1];
- webFragmentDigester = webFragmentDigesters[1];
-
- } else if (namespaceAware && !validation) {
- if (webDigesters[2] == null) {
- webDigesters[2] = DigesterFactory.newDigester(validation,
- namespaceAware, webRuleSet);
- webFragmentDigesters[2] = DigesterFactory.newDigester(validation,
- namespaceAware, webFragmentRuleSet);
- webDigesters[2].getParser();
- webFragmentDigesters[2].getParser();
- }
- webDigester = webDigesters[2];
- webFragmentDigester = webFragmentDigesters[2];
+ webRuleSet = new WebRuleSet(false);
+ webDigester = DigesterFactory.newDigester(validation,
+ namespaceAware, webRuleSet);
+ webDigester.getParser();
- } else {
- if (webDigesters[3] == null) {
- webDigesters[3] = DigesterFactory.newDigester(validation,
- namespaceAware, webRuleSet);
- webFragmentDigesters[3] = DigesterFactory.newDigester(validation,
- namespaceAware, webFragmentRuleSet);
- webDigesters[3].getParser();
- webFragmentDigesters[3].getParser();
- }
- webDigester = webDigesters[3];
- webFragmentDigester = webFragmentDigesters[3];
- }
+ webFragmentRuleSet = new WebRuleSet(true);
+ webFragmentDigester = DigesterFactory.newDigester(validation,
+ namespaceAware, webFragmentRuleSet);
+ webFragmentDigester.getParser();
}
@@ -577,7 +508,7 @@ public class ContextConfig
/**
* Process the default configuration file, if it exists.
*/
- protected void contextConfig() {
+ protected void contextConfig(Digester digester) {
// Open the default context.xml file, if it exists
if( defaultContextXml==null && context instanceof StandardContext ) {
@@ -596,7 +527,7 @@ public class ContextConfig
if (defaultContextFile.exists()) {
try {
URL defaultContextUrl = defaultContextFile.toURI().toURL();
- processContextConfig(defaultContextUrl);
+ processContextConfig(digester, defaultContextUrl);
} catch (MalformedURLException e) {
log.error(sm.getString(
"contextConfig.badUrl", defaultContextFile), e);
@@ -608,7 +539,7 @@ public class ContextConfig
if (hostContextFile.exists()) {
try {
URL hostContextUrl = hostContextFile.toURI().toURL();
- processContextConfig(hostContextUrl);
+ processContextConfig(digester, hostContextUrl);
} catch (MalformedURLException e) {
log.error(sm.getString(
"contextConfig.badUrl", hostContextFile), e);
@@ -616,7 +547,7 @@ public class ContextConfig
}
}
if (context.getConfigFile() != null) {
- processContextConfig(context.getConfigFile());
+ processContextConfig(digester, context.getConfigFile());
}
}
@@ -625,7 +556,7 @@ public class ContextConfig
/**
* Process a context.xml.
*/
- protected void processContextConfig(URL contextXml) {
+ protected void processContextConfig(Digester digester, URL contextXml) {
if (log.isDebugEnabled()) {
log.debug("Processing context [" + context.getName()
@@ -653,45 +584,43 @@ public class ContextConfig
if (source == null) {
return;
}
- synchronized (contextDigester) {
- try {
- source.setByteStream(stream);
- contextDigester.setClassLoader(this.getClass().getClassLoader());
- contextDigester.setUseContextClassLoader(false);
- contextDigester.push(context.getParent());
- contextDigester.push(context);
- XmlErrorHandler errorHandler = new XmlErrorHandler();
- contextDigester.setErrorHandler(errorHandler);
- contextDigester.parse(source);
- if (errorHandler.getWarnings().size() > 0 ||
- errorHandler.getErrors().size() > 0) {
- errorHandler.logFindings(log, contextXml.toString());
- ok = false;
- }
- if (log.isDebugEnabled()) {
- log.debug("Successfully processed context [" + context.getName()
- + "] configuration file [" + contextXml + "]");
- }
- } catch (SAXParseException e) {
- log.error(sm.getString("contextConfig.contextParse",
- context.getName()), e);
- log.error(sm.getString("contextConfig.defaultPosition",
- "" + e.getLineNumber(),
- "" + e.getColumnNumber()));
- ok = false;
- } catch (Exception e) {
- log.error(sm.getString("contextConfig.contextParse",
- context.getName()), e);
+
+ try {
+ source.setByteStream(stream);
+ digester.setClassLoader(this.getClass().getClassLoader());
+ digester.setUseContextClassLoader(false);
+ digester.push(context.getParent());
+ digester.push(context);
+ XmlErrorHandler errorHandler = new XmlErrorHandler();
+ digester.setErrorHandler(errorHandler);
+ digester.parse(source);
+ if (errorHandler.getWarnings().size() > 0 ||
+ errorHandler.getErrors().size() > 0) {
+ errorHandler.logFindings(log, contextXml.toString());
ok = false;
- } finally {
- contextDigester.reset();
- try {
- if (stream != null) {
- stream.close();
- }
- } catch (IOException e) {
- log.error(sm.getString("contextConfig.contextClose"), e);
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("Successfully processed context [" + context.getName()
+ + "] configuration file [" + contextXml + "]");
+ }
+ } catch (SAXParseException e) {
+ log.error(sm.getString("contextConfig.contextParse",
+ context.getName()), e);
+ log.error(sm.getString("contextConfig.defaultPosition",
+ "" + e.getLineNumber(),
+ "" + e.getColumnNumber()));
+ ok = false;
+ } catch (Exception e) {
+ log.error(sm.getString("contextConfig.contextParse",
+ context.getName()), e);
+ ok = false;
+ } finally {
+ try {
+ if (stream != null) {
+ stream.close();
}
+ } catch (IOException e) {
+ log.error(sm.getString("contextConfig.contextClose"), e);
}
}
}
@@ -846,10 +775,8 @@ public class ContextConfig
protected void init() {
// Called from StandardContext.init()
- if (contextDigester == null){
- contextDigester = createContextDigester();
- contextDigester.getParser();
- }
+ Digester contextDigester = createContextDigester();
+ contextDigester.getParser();
if (log.isDebugEnabled()) {
log.debug(sm.getString("contextConfig.init"));
@@ -857,7 +784,7 @@ public class ContextConfig
context.setConfigured(false);
ok = true;
- contextConfig();
+ contextConfig(contextDigester);
createWebXmlDigester(context.getXmlNamespaceAware(),
context.getXmlValidation());
@@ -1416,8 +1343,9 @@ public class ContextConfig
}
// Parsing global web.xml is relatively expensive. Use a sync block to
- // make sure it only happens once
- synchronized (host) {
+ // make sure it only happens once. Use the pipeline since a lock will
+ // already be held on the host by another thread
+ synchronized (host.getPipeline()) {
entry = hostWebXmlCache.get(host);
if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
entry.getHostTimeStamp() == hostTimeStamp) {
@@ -1679,7 +1607,6 @@ public class ContextConfig
return getWebXmlSource(defaultWebXml, getBaseDir());
}
-
/**
* Identify the host web.xml to be used and obtain an input source for
* it.
@@ -1806,9 +1733,6 @@ public class ContextConfig
XmlErrorHandler handler = new XmlErrorHandler();
- // Web digesters and rulesets are shared between contexts but are not
- // thread safe. Whilst there should only be one thread at a time
- // processing a config, play safe and sync.
Digester digester;
WebRuleSet ruleSet;
if (fragment) {
@@ -1819,41 +1743,36 @@ public class ContextConfig
ruleSet = webRuleSet;
}
- // Sync on the ruleSet since the same ruleSet is shared across all four
- // digesters
- synchronized(ruleSet) {
+ digester.push(dest);
+ digester.setErrorHandler(handler);
- digester.push(dest);
- digester.setErrorHandler(handler);
-
- if(log.isDebugEnabled()) {
- log.debug(sm.getString("contextConfig.applicationStart",
- source.getSystemId()));
- }
+ if(log.isDebugEnabled()) {
+ log.debug(sm.getString("contextConfig.applicationStart",
+ source.getSystemId()));
+ }
- try {
- digester.parse(source);
+ try {
+ digester.parse(source);
- if (handler.getWarnings().size() > 0 ||
- handler.getErrors().size() > 0) {
- ok = false;
- handler.logFindings(log, source.getSystemId());
- }
- } catch (SAXParseException e) {
- log.error(sm.getString("contextConfig.applicationParse",
- source.getSystemId()), e);
- log.error(sm.getString("contextConfig.applicationPosition",
- "" + e.getLineNumber(),
- "" + e.getColumnNumber()));
+ if (handler.getWarnings().size() > 0 ||
+ handler.getErrors().size() > 0) {
ok = false;
- } catch (Exception e) {
- log.error(sm.getString("contextConfig.applicationParse",
- source.getSystemId()), e);
- ok = false;
- } finally {
- digester.reset();
- ruleSet.recycle();
- }
+ handler.logFindings(log, source.getSystemId());
+ }
+ } catch (SAXParseException e) {
+ log.error(sm.getString("contextConfig.applicationParse",
+ source.getSystemId()), e);
+ log.error(sm.getString("contextConfig.applicationPosition",
+ "" + e.getLineNumber(),
+ "" + e.getColumnNumber()));
+ ok = false;
+ } catch (Exception e) {
+ log.error(sm.getString("contextConfig.applicationParse",
+ source.getSystemId()), e);
+ ok = false;
+ } finally {
+ digester.reset();
+ ruleSet.recycle();
}
}
@@ -2244,7 +2163,8 @@ public class ContextConfig
/**
* process filter annotation and merge with existing one!
- * FIXME: refactoring method to long and has redundant subroutines with processAnnotationWebServlet!
+ * FIXME: refactoring method too long and has redundant subroutines with
+ * processAnnotationWebServlet!
* @param className
* @param ae
* @param fragment
--- a/java/org/apache/catalina/startup/HostConfig.java
+++ a/java/org/apache/catalina/startup/HostConfig.java
@@ -14,8 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-
package org.apache.catalina.startup;
@@ -33,7 +31,11 @@ import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
@@ -138,8 +140,8 @@ public class HostConfig
/**
* Map of deployed applications.
*/
- protected HashMap socket, P processor);
protected abstract void longPoll(SocketWrapper socket, P processor);
--- a/java/org/apache/tomcat/util/threads/DedicatedThreadExecutor.java
+++ a/java/org/apache/tomcat/util/threads/DedicatedThreadExecutor.java
@@ -1,137 +0,0 @@
-/*
- * 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.tomcat.util.threads;
-
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.ThreadFactory;
-
-/**
- * A utility class to execute a {@link Callable} in a dedicated thread.
- * It can be used either with an instance to reuse the same thread for each call
- * to {@link #execute(Callable)} or with the static method
- * {@link #executeInOwnThread(Callable)}. When using an instance,
- * {@link #shutdown()} must be called when the instance is no longer needed to
- * dispose of the dedicated thread.
- */
-public class DedicatedThreadExecutor {
- private final SingleThreadFactory singleThreadFactory =
- new SingleThreadFactory();
- private final ExecutorService executorService =
- Executors.newSingleThreadExecutor(singleThreadFactory);
-
- /**
- * Executes the given {@link Callable} with the thread spawned for the
- * current {@link DedicatedThreadExecutor} instance, and returns its result.
- *
- * @param
The number of threads this Engine will use to start
+ child Host elements in parallel. The special
+ value of 0 will result in the value of
+ Runtime.getRuntime().availableProcessors()
being used.
+ Negative values will result in
+ Runtime.getRuntime().availableProcessors() + value
being
+ used unless this is less than 1 in which case 1 thread will be used. If
+ not specified, the default value of 1 will be used.
The number of threads this Host will use to start
+ child Context elements in parallel. The same
+ thread pool will be used to deploy new
+ Contexts if automatic deployment is being
+ used. The special value of 0 will result in the value of
+ Runtime.getRuntime().availableProcessors()
being used.
+ Negative values will result in
+ Runtime.getRuntime().availableProcessors() + value
being
+ used unless this is less than 1 in which case 1 thread will be used. If
+ not specified, the default value of 1 will be used.