You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2011/10/28 09:37:34 UTC
svn commit: r1190185 - in /tomcat/trunk: java/org/apache/catalina/
java/org/apache/catalina/core/ java/org/apache/catalina/startup/
java/org/apache/coyote/ java/org/apache/tomcat/util/threads/
test/org/apache/tomcat/util/threads/ webapps/docs/config/
Author: markt
Date: Fri Oct 28 07:37:34 2011
New Revision: 1190185
URL: http://svn.apache.org/viewvc?rev=1190185&view=rev
Log:
Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=46264
Implement threaded start/stop for containers
Removed:
tomcat/trunk/java/org/apache/tomcat/util/threads/DedicatedThreadExecutor.java
tomcat/trunk/test/org/apache/tomcat/util/threads/DedicatedThreadExecutorTest.java
Modified:
tomcat/trunk/java/org/apache/catalina/Container.java
tomcat/trunk/java/org/apache/catalina/Host.java
tomcat/trunk/java/org/apache/catalina/core/ContainerBase.java
tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties
tomcat/trunk/java/org/apache/catalina/core/StandardContext.java
tomcat/trunk/java/org/apache/catalina/core/StandardHost.java
tomcat/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml
tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java
tomcat/trunk/java/org/apache/catalina/startup/HostConfig.java
tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties
tomcat/trunk/java/org/apache/coyote/AbstractProtocol.java
tomcat/trunk/webapps/docs/config/engine.xml
tomcat/trunk/webapps/docs/config/host.xml
Modified: tomcat/trunk/java/org/apache/catalina/Container.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/Container.java?rev=1190185&r1=1190184&r2=1190185&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/Container.java (original)
+++ tomcat/trunk/java/org/apache/catalina/Container.java Fri Oct 28 07:37:34 2011
@@ -461,4 +461,21 @@ public interface Container extends Lifec
* 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);
}
Modified: tomcat/trunk/java/org/apache/catalina/Host.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/Host.java?rev=1190185&r1=1190184&r2=1190185&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/Host.java (original)
+++ tomcat/trunk/java/org/apache/catalina/Host.java Fri Oct 28 07:37:34 2011
@@ -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.
*
Modified: tomcat/trunk/java/org/apache/catalina/core/ContainerBase.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/ContainerBase.java?rev=1190185&r1=1190184&r2=1190185&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/ContainerBase.java (original)
+++ tomcat/trunk/java/org/apache/catalina/core/ContainerBase.java Fri Oct 28 07:37:34 2011
@@ -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;
@@ -273,8 +280,55 @@ public abstract class ContainerBase exte
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
@@ -980,6 +1034,19 @@ public abstract class ContainerBase exte
}
+ @Override
+ protected void initInternal() throws LifecycleException {
+ BlockingQueue<Runnable> startStopQueue =
+ new LinkedBlockingQueue<Runnable>();
+ startStopExecutor = new ThreadPoolExecutor(
+ getStartStopThreadsInternal(),
+ getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
+ startStopQueue);
+ startStopExecutor.allowCoreThreadTimeOut(true);
+ super.initInternal();
+ }
+
+
/**
* Start this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
@@ -1008,8 +1075,24 @@ public abstract class ContainerBase exte
// Start our child containers, if any
Container children[] = findChildren();
+ List<Future<Void>> results = new ArrayList<Future<Void>>();
for (int i = 0; i < children.length; i++) {
- children[i].start();
+ results.add(startStopExecutor.submit(new StartChild(children[i])));
+ }
+
+ boolean fail = false;
+ for (Future<Void> result : results) {
+ try {
+ result.get();
+ } catch (Exception e) {
+ log.error(sm.getString("containerBase.threadedStartFailed"), e);
+ fail = true;
+ }
+
+ }
+ if (fail) {
+ throw new LifecycleException(
+ sm.getString("containerBase.threadedStartFailed"));
}
// Start the Valves in our pipeline (including the basic), if any
@@ -1048,8 +1131,23 @@ public abstract class ContainerBase exte
// Stop our child containers, if any
Container children[] = findChildren();
+ List<Future<Void>> results = new ArrayList<Future<Void>>();
for (int i = 0; i < children.length; i++) {
- children[i].stop();
+ results.add(startStopExecutor.submit(new StopChild(children[i])));
+ }
+
+ boolean fail = false;
+ for (Future<Void> result : results) {
+ try {
+ result.get();
+ } catch (Exception e) {
+ log.error(sm.getString("containerBase.threadedStopFailed"), e);
+ fail = true;
+ }
+ }
+ if (fail) {
+ throw new LifecycleException(
+ sm.getString("containerBase.threadedStopFailed"));
}
// Stop our subordinate components, if any
@@ -1092,6 +1190,8 @@ public abstract class ContainerBase exte
parent.removeChild(this);
}
+ startStopExecutor.shutdownNow();
+
super.destroyInternal();
}
@@ -1391,4 +1491,37 @@ public abstract class ContainerBase exte
}
}
}
+
+
+ // ----------------------------- Inner classes used with start/stop Executor
+
+ private static class StartChild implements Callable<Void> {
+
+ private Container child;
+
+ public StartChild(Container child) {
+ this.child = child;
+ }
+
+ @Override
+ public Void call() throws LifecycleException {
+ child.start();
+ return null;
+ }
+ }
+
+ private static class StopChild implements Callable<Void> {
+
+ private Container child;
+
+ public StopChild(Container child) {
+ this.child = child;
+ }
+
+ @Override
+ public Void call() throws LifecycleException {
+ child.stop();
+ return null;
+ }
+ }
}
Modified: tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties?rev=1190185&r1=1190184&r2=1190185&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties (original)
+++ tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties Fri Oct 28 07:37:34 2011
@@ -53,6 +53,8 @@ aprListener.sslInit=Failed to initialize
aprListener.tcnValid=Loaded APR based Apache Tomcat Native library {0}.
aprListener.flags=APR capabilities: IPv6 [{0}], sendfile [{1}], accept filters [{2}], random [{3}].
asyncContextImpl.requestEnded=The request associated with the AsyncContext has already completed processing.
+containerBase.threadedStartFailed=A child container failed during start
+containerBase.threadedStopFailed=A child container failed during stop
containerBase.backgroundProcess.cluster=Exception processing cluster {0} background process
containerBase.backgroundProcess.loader=Exception processing loader {0} background process
containerBase.backgroundProcess.manager=Exception processing manager {0} background process
Modified: tomcat/trunk/java/org/apache/catalina/core/StandardContext.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/StandardContext.java?rev=1190185&r1=1190184&r2=1190185&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/StandardContext.java (original)
+++ tomcat/trunk/java/org/apache/catalina/core/StandardContext.java Fri Oct 28 07:37:34 2011
@@ -37,7 +37,6 @@ import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;
-import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicLong;
import javax.management.ListenerNotFoundException;
@@ -118,7 +117,6 @@ import org.apache.tomcat.JarScanner;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.modeler.Registry;
import org.apache.tomcat.util.scan.StandardJarScanner;
-import org.apache.tomcat.util.threads.DedicatedThreadExecutor;
/**
* Standard implementation of the <b>Context</b> interface. Each
@@ -5101,9 +5099,7 @@ public class StandardContext extends Con
}
}
- DedicatedThreadExecutor temporaryExecutor = new DedicatedThreadExecutor();
try {
-
// Create context attributes that will be required
if (ok) {
getServletContext().setAttribute(
@@ -5128,22 +5124,7 @@ public class StandardContext extends Con
// Configure and call application event listeners
if (ok) {
- // we do it in a dedicated thread for memory leak protection, in
- // case the Listeners registers some ThreadLocals that they
- // forget to cleanup
- Boolean listenerStarted =
- temporaryExecutor.execute(new Callable<Boolean>() {
- @Override
- public Boolean call() throws Exception {
- ClassLoader old = bindThread();
- try {
- return Boolean.valueOf(listenerStart());
- } finally {
- unbindThread(old);
- }
- }
- });
- if (!listenerStarted.booleanValue()) {
+ if (!listenerStart()) {
log.error( "Error listenerStart");
ok = false;
}
@@ -5164,22 +5145,7 @@ public class StandardContext extends Con
// Configure and call application filters
if (ok) {
- // we do it in a dedicated thread for memory leak protection, in
- // case the Filters register some ThreadLocals that they forget
- // to cleanup
- Boolean filterStarted =
- temporaryExecutor.execute(new Callable<Boolean>() {
- @Override
- public Boolean call() throws Exception {
- ClassLoader old = bindThread();
- try {
- return Boolean.valueOf(filterStart());
- } finally {
- unbindThread(old);
- }
- }
- });
- if (!filterStarted.booleanValue()) {
+ if (!filterStart()) {
log.error("Error filterStart");
ok = false;
}
@@ -5187,27 +5153,12 @@ public class StandardContext extends Con
// Load and initialize all "load on startup" servlets
if (ok) {
- // we do it in a dedicated thread for memory leak protection, in
- // case the Servlets register some ThreadLocals that they forget
- // to cleanup
- temporaryExecutor.execute(new Callable<Void>() {
- @Override
- public Void call() throws Exception {
- ClassLoader old = bindThread();
- try {
- loadOnStartup(findChildren());
- return null;
- } finally {
- unbindThread(old);
- }
- }
- });
+ loadOnStartup(findChildren());
}
} finally {
// Unbinding thread
unbindThread(oldCCL);
- temporaryExecutor.shutdown();
}
// Set available status depending upon startup success
@@ -5347,61 +5298,28 @@ public class StandardContext extends Con
// Stop our child containers, if any
final Container[] children = findChildren();
- // we do it in a dedicated thread for memory leak protection, in
- // case some webapp code registers some ThreadLocals that they
- // forget to cleanup
- // TODO Figure out why DedicatedThreadExecutor hangs randomly in the
- // unit tests if used here
- RunnableWithLifecycleException stop =
- new RunnableWithLifecycleException() {
- @Override
- public void run() {
- ClassLoader old = bindThread();
- try {
- for (int i = 0; i < children.length; i++) {
- try {
- children[i].stop();
- } catch (LifecycleException e) {
- le = e;
- return;
- }
- }
- // Stop our filters
- filterStop();
+ ClassLoader old = bindThread();
+ try {
+ for (int i = 0; i < children.length; i++) {
+ children[i].stop();
+ }
- // Stop ContainerBackgroundProcessor thread
- threadStop();
+ // Stop our filters
+ filterStop();
- if (manager != null && manager instanceof Lifecycle &&
- ((Lifecycle) manager).getState().isAvailable()) {
- try {
- ((Lifecycle) manager).stop();
- } catch (LifecycleException e) {
- le = e;
- return;
- }
- }
+ // Stop ContainerBackgroundProcessor thread
+ threadStop();
- // Stop our application listeners
- listenerStop();
- }finally{
- unbindThread(old);
- }
+ if (manager != null && manager instanceof Lifecycle &&
+ ((Lifecycle) manager).getState().isAvailable()) {
+ ((Lifecycle) manager).stop();
}
- };
- Thread t = new Thread(stop);
- t.setName("stop children - " + getObjectName().toString());
- t.start();
- try {
- t.join();
- } catch (InterruptedException e) {
- // Shouldn't happen
- throw new LifecycleException(e);
- }
- if (stop.getLifecycleException() != null) {
- throw stop.getLifecycleException();
+ // Stop our application listeners
+ listenerStop();
+ } finally{
+ unbindThread(old);
}
// Finalize our character set mapper
@@ -6396,14 +6314,4 @@ public class StandardContext extends Con
public long getStartTime() {
return startTime;
}
-
- private abstract static class RunnableWithLifecycleException
- implements Runnable {
-
- protected LifecycleException le = null;
-
- public LifecycleException getLifecycleException() {
- return le;
- }
- }
}
Modified: tomcat/trunk/java/org/apache/catalina/core/StandardHost.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/StandardHost.java?rev=1190185&r1=1190184&r2=1190185&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/StandardHost.java (original)
+++ tomcat/trunk/java/org/apache/catalina/core/StandardHost.java Fri Oct 28 07:37:34 2011
@@ -24,6 +24,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.WeakHashMap;
+import java.util.concurrent.ExecutorService;
import java.util.regex.Pattern;
import org.apache.catalina.Container;
@@ -177,6 +178,11 @@ public class StandardHost extends Contai
// ------------------------------------------------------------- Properties
+ @Override
+ public ExecutorService getStartStopExecutor() {
+ return startStopExecutor;
+ }
+
/**
* Return the application root for this Host. This can be an absolute
Modified: tomcat/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml?rev=1190185&r1=1190184&r2=1190185&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml (original)
+++ tomcat/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml Fri Oct 28 07:37:34 2011
@@ -1011,6 +1011,10 @@
description="Will children be started automatically when they are added."
type="boolean"/>
+ <attribute name="startStopThreads"
+ description="The number of threads to use when starting and stopping child Hosts"
+ type="int"/>
+
<attribute name="stateName"
description="The name of the LifecycleState that this component is currently in"
type="java.lang.String"
@@ -1198,6 +1202,10 @@
description="Will children be started automatically when they are added?"
type="boolean"/>
+ <attribute name="startStopThreads"
+ description="The number of threads to use when starting, stopping and deploying child Contexts"
+ type="int"/>
+
<attribute name="stateName"
description="The name of the LifecycleState that this component is currently in"
type="java.lang.String"
Modified: tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java?rev=1190185&r1=1190184&r2=1190185&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java (original)
+++ tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java Fri Oct 28 07:37:34 2011
@@ -127,12 +127,6 @@ public class ContextConfig
protected static final LoginConfig DUMMY_LOGIN_CONFIG =
new LoginConfig("NONE", null, null, null);
- /**
- * The <code>Digester</code> we will use to process web application
- * context files.
- */
- protected static Digester contextDigester = null;
-
/**
* The set of Authenticators that we know how to configure. The key is
@@ -143,32 +137,6 @@ public class ContextConfig
/**
- * The <code>Digester</code>s available to process web deployment descriptor
- * files.
- */
- protected static Digester[] webDigesters = new Digester[4];
-
-
- /**
- * The <code>Digester</code>s available to process web fragment deployment
- * descriptor files.
- */
- protected static Digester[] webFragmentDigesters = new Digester[4];
-
-
- /**
- * The <code>Rule</code>s used to parse the web.xml
- */
- protected static WebRuleSet webRuleSet = new WebRuleSet(false);
-
-
- /**
- * The <code>Rule</code>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 <code>Digester</code> 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];
-
- } 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];
- }
+ webRuleSet = new WebRuleSet(false);
+ webDigester = DigesterFactory.newDigester(validation,
+ namespaceAware, webRuleSet);
+ webDigester.getParser();
+
+ 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);
-
- if(log.isDebugEnabled()) {
- log.debug(sm.getString("contextConfig.applicationStart",
- source.getSystemId()));
- }
+ digester.push(dest);
+ digester.setErrorHandler(handler);
- try {
- digester.parse(source);
+ if(log.isDebugEnabled()) {
+ log.debug(sm.getString("contextConfig.applicationStart",
+ source.getSystemId()));
+ }
- 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()));
- ok = false;
- } catch (Exception e) {
- log.error(sm.getString("contextConfig.applicationParse",
- source.getSystemId()), e);
+ try {
+ digester.parse(source);
+
+ if (handler.getWarnings().size() > 0 ||
+ handler.getErrors().size() > 0) {
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
Modified: tomcat/trunk/java/org/apache/catalina/startup/HostConfig.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/HostConfig.java?rev=1190185&r1=1190184&r2=1190185&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/startup/HostConfig.java (original)
+++ tomcat/trunk/java/org/apache/catalina/startup/HostConfig.java Fri Oct 28 07:37:34 2011
@@ -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<String, DeployedApplication> deployed =
- new HashMap<String, DeployedApplication>();
+ protected Map<String, DeployedApplication> deployed =
+ new ConcurrentHashMap<String, DeployedApplication>();
/**
@@ -497,6 +499,10 @@ public class HostConfig
ContextName cn = new ContextName(name);
String baseName = cn.getBaseName();
+ if (deploymentExists(baseName)) {
+ return;
+ }
+
// Deploy XML descriptors from configBase
File xml = new File(configBase, baseName + ".xml");
if (xml.exists())
@@ -520,17 +526,29 @@ public class HostConfig
if (files == null)
return;
+ ExecutorService es = host.getStartStopExecutor();
+ List<Future<?>> results = new ArrayList<Future<?>>();
+
for (int i = 0; i < files.length; i++) {
File contextXml = new File(configBase, files[i]);
if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".xml")) {
ContextName cn = new ContextName(files[i]);
- String name = cn.getName();
- if (isServiced(name))
+ if (isServiced(cn.getName()) || deploymentExists(cn.getName()))
continue;
- deployDescriptor(cn, contextXml);
+ results.add(
+ es.submit(new DeployDescriptor(this, cn, contextXml)));
+ }
+ }
+
+ for (Future<?> result : results) {
+ try {
+ result.get();
+ } catch (Exception e) {
+ log.error(sm.getString(
+ "hostConfig.deployDescriptor.threaded.error"), e);
}
}
}
@@ -541,9 +559,6 @@ public class HostConfig
* @param contextXml
*/
protected void deployDescriptor(ContextName cn, File contextXml) {
- if (deploymentExists(cn.getName())) {
- return;
- }
DeployedApplication deployedApp = new DeployedApplication(cn.getName());
@@ -670,6 +685,9 @@ public class HostConfig
if (files == null)
return;
+ ExecutorService es = host.getStartStopExecutor();
+ List<Future<?>> results = new ArrayList<Future<?>>();
+
for (int i = 0; i < files.length; i++) {
if (files[i].equalsIgnoreCase("META-INF"))
@@ -690,10 +708,19 @@ public class HostConfig
continue;
}
- if (isServiced(cn.getName()))
+ if (isServiced(cn.getName()) || deploymentExists(cn.getName()))
continue;
- deployWAR(cn, war);
+ results.add(es.submit(new DeployWar(this, cn, war)));
+ }
+ }
+
+ for (Future<?> result : results) {
+ try {
+ result.get();
+ } catch (Exception e) {
+ log.error(sm.getString(
+ "hostConfig.deployWar.threaded.error"), e);
}
}
}
@@ -741,9 +768,6 @@ public class HostConfig
*/
protected void deployWAR(ContextName cn, File war) {
- if (deploymentExists(cn.getName()))
- return;
-
// Checking for a nested /META-INF/context.xml
JarFile jar = null;
JarEntry entry = null;
@@ -935,6 +959,9 @@ public class HostConfig
if (files == null)
return;
+ ExecutorService es = host.getStartStopExecutor();
+ List<Future<?>> results = new ArrayList<Future<?>>();
+
for (int i = 0; i < files.length; i++) {
if (files[i].equalsIgnoreCase("META-INF"))
@@ -945,10 +972,19 @@ public class HostConfig
if (dir.isDirectory()) {
ContextName cn = new ContextName(files[i]);
- if (isServiced(cn.getName()))
+ if (isServiced(cn.getName()) || deploymentExists(cn.getName()))
continue;
- deployDirectory(cn, dir);
+ results.add(es.submit(new DeployDirectory(this, cn, dir)));
+ }
+ }
+
+ for (Future<?> result : results) {
+ try {
+ result.get();
+ } catch (Exception e) {
+ log.error(sm.getString(
+ "hostConfig.deployDir.threaded.error"), e);
}
}
}
@@ -960,9 +996,6 @@ public class HostConfig
*/
protected void deployDirectory(ContextName cn, File dir) {
- if (deploymentExists(cn.getName()))
- return;
-
DeployedApplication deployedApp = new DeployedApplication(cn.getName());
// Deploy the application in this directory
@@ -1450,7 +1483,61 @@ public class HostConfig
/**
* Instant where the application was last put in service.
*/
- public long timestamp = System.currentTimeMillis();
+ public long timestamp = System.currentTimeMillis();
+ }
+
+ private static class DeployDescriptor implements Runnable {
+
+ private HostConfig config;
+ private ContextName cn;
+ private File descriptor;
+
+ public DeployDescriptor(HostConfig config, ContextName cn,
+ File descriptor) {
+ this.config = config;
+ this.cn = cn;
+ this.descriptor= descriptor;
+ }
+
+ @Override
+ public void run() {
+ config.deployDescriptor(cn, descriptor);
+ }
+ }
+
+ private static class DeployWar implements Runnable {
+
+ private HostConfig config;
+ private ContextName cn;
+ private File war;
+
+ public DeployWar(HostConfig config, ContextName cn, File war) {
+ this.config = config;
+ this.cn = cn;
+ this.war = war;
+ }
+
+ @Override
+ public void run() {
+ config.deployWAR(cn, war);
+ }
}
+ private static class DeployDirectory implements Runnable {
+
+ private HostConfig config;
+ private ContextName cn;
+ private File dir;
+
+ public DeployDirectory(HostConfig config, ContextName cn, File dir) {
+ this.config = config;
+ this.cn = cn;
+ this.dir = dir;
+ }
+
+ @Override
+ public void run() {
+ config.deployDirectory(cn, dir);
+ }
+ }
}
Modified: tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties?rev=1190185&r1=1190184&r2=1190185&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties (original)
+++ tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties Fri Oct 28 07:37:34 2011
@@ -80,11 +80,19 @@ hostConfig.context.restart=Error during
hostConfig.createDirs=Unable to create directory for deployment: {0}
hostConfig.deployDescriptor=Deploying configuration descriptor {0}
hostConfig.deployDescriptor.error=Error deploying configuration descriptor {0}
+hostConfig.deployDescriptor.threaded.error=Error waiting for multi-thread deployment of context descriptors to complete
hostConfig.deployDescriptor.localDocBaseSpecified=A docBase {0} inside the host appBase has been specified, and will be ignored
hostConfig.deployDir=Deploying web application directory {0}
hostConfig.deployDir.error=Error deploying web application directory {0}
+hostConfig.deployDir.threaded.error=Error waiting for multi-thread deployment of directories to completehostConfig.deployWar=Deploying web application archive {0}
hostConfig.deployWar=Deploying web application archive {0}
hostConfig.deployWar.error=Error deploying web application archive {0}
+hostConfig.deployWar.threaded.error=Error waiting for multi-thread deployment of WAR files to complete
+hostConfig.deploy.error=Exception while deploying web application directory {0}
+hostConfig.deploying=Deploying discovered web applications
+hostConfig.expand=Expanding web application archive {0}
+hostConfig.expand.error=Exception while expanding web application archive {0}
+hostConfig.expanding=Expanding discovered web application archives
hostConfig.ignorePath=Ignoring path [{0}] in appBase for automatic deployment
hostConfig.illegalWarName=The war name [{0}] is invalid. The archive will be ignored.
hostConfig.jmx.register=Register context [{0}] failed
Modified: tomcat/trunk/java/org/apache/coyote/AbstractProtocol.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/AbstractProtocol.java?rev=1190185&r1=1190184&r2=1190185&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/AbstractProtocol.java (original)
+++ tomcat/trunk/java/org/apache/coyote/AbstractProtocol.java Fri Oct 28 07:37:34 2011
@@ -493,6 +493,12 @@ public abstract class AbstractProtocol i
SocketStatus status) {
P processor = connections.remove(socket.getSocket());
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("process() entry " +
+ "Socket: [" + logHashcode(socket.getSocket()) + "], " +
+ "Processor [" + logHashcode(processor) + "]");
+ }
+
socket.setAsync(false);
try {
@@ -503,20 +509,51 @@ public abstract class AbstractProtocol i
processor = createProcessor();
}
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("process() gotProcessor " +
+ "Socket: [" + logHashcode(socket.getSocket()) + "], " +
+ "Processor [" + logHashcode(processor) + "]");
+ }
+
initSsl(socket, processor);
SocketState state = SocketState.CLOSED;
do {
if (processor.isAsync() || state == SocketState.ASYNC_END) {
state = processor.asyncDispatch(status);
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("process() asyncDispatch " +
+ "Socket: [" + logHashcode(socket.getSocket()) + "], " +
+ "Processor: [" + logHashcode(processor) + "], " +
+ "State: [" + state.toString() + "]");
+ }
} else if (processor.isComet()) {
state = processor.event(status);
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("process() event " +
+ "Socket: [" + logHashcode(socket.getSocket()) + "], " +
+ "Processor: [" + logHashcode(processor) + "], " +
+ "State: [" + state.toString() + "]");
+ }
} else {
state = processor.process(socket);
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("process() process " +
+ "Socket: [" + logHashcode(socket.getSocket()) + "], " +
+ "Processor: [" + logHashcode(processor) + "], " +
+ "State: [" + state.toString() + "]");
+ }
}
if (state != SocketState.CLOSED && processor.isAsync()) {
state = processor.asyncPostProcess();
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("process() asyncPostProcess " +
+ "Socket: [" + logHashcode(socket.getSocket()) + "], " +
+ "Processor: [" + logHashcode(processor) + "], " +
+ "State: [" + state.toString() + "]");
+ }
+
}
} while (state == SocketState.ASYNC_END);
@@ -562,6 +599,14 @@ public abstract class AbstractProtocol i
return SocketState.CLOSED;
}
+ private String logHashcode (Object o) {
+ if (o == null) {
+ return "null";
+ } else {
+ return Integer.toString(o.hashCode());
+ }
+ }
+
protected abstract P createProcessor();
protected abstract void initSsl(SocketWrapper<S> socket, P processor);
protected abstract void longPoll(SocketWrapper<S> socket, P processor);
Modified: tomcat/trunk/webapps/docs/config/engine.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/engine.xml?rev=1190185&r1=1190184&r2=1190185&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/config/engine.xml (original)
+++ tomcat/trunk/webapps/docs/config/engine.xml Fri Oct 28 07:37:34 2011
@@ -102,6 +102,17 @@
name.</em></p>
</attribute>
+ <attribute name="startStopThreads" required="false">
+ <p>The number of threads this <strong>Engine</strong> will use to start
+ child <a href="host.html">Host</a> elements in parallel. The special
+ value of 0 will result in the value of
+ <code>Runtime.getRuntime().availableProcessors()</code> being used.
+ Negative values will result in
+ <code>Runtime.getRuntime().availableProcessors() + value</code> 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. </p>
+ </attribute>
+
</attributes>
</subsection>
Modified: tomcat/trunk/webapps/docs/config/host.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/host.xml?rev=1190185&r1=1190184&r2=1190185&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/config/host.xml (original)
+++ tomcat/trunk/webapps/docs/config/host.xml Fri Oct 28 07:37:34 2011
@@ -188,6 +188,19 @@
virtual host.</p>
</attribute>
+ <attribute name="startStopThreads" required="false">
+ <p>The number of threads this <strong>Host</strong> will use to start
+ child <a href="context.html">Context</a> elements in parallel. The same
+ thread pool will be used to deploy new
+ <a href="context.html">Context</a>s if automatic deployment is being
+ used. The special value of 0 will result in the value of
+ <code>Runtime.getRuntime().availableProcessors()</code> being used.
+ Negative values will result in
+ <code>Runtime.getRuntime().availableProcessors() + value</code> 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.</p>
+ </attribute>
+
</attributes>
</subsection>
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org