Author: markt Date: Tue Jul 29 08:59:01 2014 New Revision: 1614288 URL: http://svn.apache.org/r1614288 Log: Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=56648 Don't block requests while a Context is started
Modified: tomcat/tc6.0.x/trunk/STATUS.txt tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/ContainerBase.java tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml Modified: tomcat/tc6.0.x/trunk/STATUS.txt URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/STATUS.txt?rev=1614288&r1=1614287&r2=1614288&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/STATUS.txt (original) +++ tomcat/tc6.0.x/trunk/STATUS.txt Tue Jul 29 08:59:01 2014 @@ -68,12 +68,6 @@ PATCHES PROPOSED TO BACKPORT: +1: markt, kkolinko, schultz -1: -* Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=56648 - Don't block requests while a Context is started - http://people.apache.org/~markt/patches/2014-07-08-bug56648-tc6-v1.patch - +1: markt, kkolinko, schultz - -1: - * Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=56661 Support using AJP request attribute AJP_LOCAL_ADDR to fix getLocalAddr(). Backport of r1609593 from trunk resp. r1609606 from tc7. Modified: tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/ContainerBase.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/ContainerBase.java?rev=1614288&r1=1614287&r2=1614288&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/ContainerBase.java (original) +++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/ContainerBase.java Tue Jul 29 08:59:01 2014 @@ -5,9 +5,9 @@ * 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. @@ -193,7 +193,7 @@ public abstract class ContainerBase * Associated logger name. */ protected String logName = null; - + /** * The Manager implementation with which this Container is associated. @@ -206,7 +206,7 @@ public abstract class ContainerBase */ protected Cluster cluster = null; - + /** * The human-readable name of this Container. */ @@ -293,10 +293,10 @@ public abstract class ContainerBase /** * Get the delay between the invocation of the backgroundProcess method on * this container and its children. Child containers will not be invoked - * if their delay value is not negative (which would mean they are using - * their own thread). Setting this to a positive value will cause - * a thread to be spawn. After waiting the specified amount of time, - * the thread will invoke the executePeriodic method on this container + * if their delay value is not negative (which would mean they are using + * their own thread). Setting this to a positive value will cause + * a thread to be spawn. After waiting the specified amount of time, + * the thread will invoke the executePeriodic method on this container * and all its children. */ public int getBackgroundProcessorDelay() { @@ -307,8 +307,8 @@ public abstract class ContainerBase /** * Set the delay between the invocation of the execute method on this * container and its children. - * - * @param delay The delay in seconds between the invocation of + * + * @param delay The delay in seconds between the invocation of * backgroundProcess methods */ public void setBackgroundProcessorDelay(int delay) { @@ -732,8 +732,8 @@ public abstract class ContainerBase */ public synchronized void setResources(DirContext resources) { // Called from StandardContext.setResources() - // <- StandardContext.start() - // <- ContainerBase.addChildInternal() + // <- StandardContext.start() + // <- ContainerBase.addChildInternal() // Change components if necessary DirContext oldResources = this.resources; @@ -791,27 +791,30 @@ public abstract class ContainerBase "' is not unique"); child.setParent(this); // May throw IAE children.put(child.getName(), child); + } - // Start child - if (started && startChildren && (child instanceof Lifecycle)) { - boolean success = false; - try { - ((Lifecycle) child).start(); - success = true; - } catch (LifecycleException e) { - log.error("ContainerBase.addChild: start: ", e); - throw new IllegalStateException - ("ContainerBase.addChild: start: " + e); - } finally { - if (!success) { + // Start child + // Don't do this inside sync block - start can be a slow process and + // locking the children object can cause problems elsewhere + if (started && startChildren && (child instanceof Lifecycle)) { + boolean success = false; + try { + ((Lifecycle) child).start(); + success = true; + } catch (LifecycleException e) { + log.error("ContainerBase.addChild: start: ", e); + throw new IllegalStateException + ("ContainerBase.addChild: start: " + e); + } finally { + if (!success) { + synchronized(children) { children.remove(child.getName()); } } } - - fireContainerEvent(ADD_CHILD_EVENT, child); } + fireContainerEvent(ADD_CHILD_EVENT, child); } @@ -851,7 +854,7 @@ public abstract class ContainerBase if (name == null) return (null); - synchronized (children) { // Required by post-start changes + synchronized (children) { return ((Container) children.get(name)); } @@ -880,7 +883,7 @@ public abstract class ContainerBase public ContainerListener[] findContainerListeners() { synchronized (listeners) { - ContainerListener[] results = + ContainerListener[] results = new ContainerListener[listeners.size()]; return ((ContainerListener[]) listeners.toArray(results)); } @@ -922,13 +925,13 @@ public abstract class ContainerBase if (child == null) { return; } - + synchronized(children) { if (children.get(child.getName()) == null) return; children.remove(child.getName()); } - + if (started && (child instanceof Lifecycle)) { try { if( child instanceof ContainerBase ) { @@ -942,9 +945,9 @@ public abstract class ContainerBase log.error("ContainerBase.removeChild: stop: ", e); } } - + fireContainerEvent(REMOVE_CHILD_EVENT, child); - + // child.setParent(null); } @@ -992,7 +995,7 @@ public abstract class ContainerBase /** - * Get the lifecycle listeners associated with this lifecycle. If this + * Get the lifecycle listeners associated with this lifecycle. If this * Lifecycle has no listeners registered, a zero-length array is returned. */ public LifecycleListener[] findLifecycleListeners() { @@ -1028,7 +1031,7 @@ public abstract class ContainerBase log.info(sm.getString("containerBase.alreadyStarted", logName())); return; } - + // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null); @@ -1141,15 +1144,15 @@ public abstract class ContainerBase } /** Init method, part of the MBean lifecycle. - * If the container was added via JMX, it'll register itself with the + * If the container was added via JMX, it'll register itself with the * parent, using the ObjectName conventions to locate the parent. - * + * * If the container was added directly and it doesn't have an ObjectName, - * it'll create a name and register itself with the JMX console. On destroy(), + * it'll create a name and register itself with the JMX console. On destroy(), * the object will unregister. - * + * * @throws Exception - */ + */ public void init() throws Exception { if( this.getParent() == null ) { @@ -1157,8 +1160,8 @@ public abstract class ContainerBase ObjectName parentName=getParentName(); //log.info("Register " + parentName ); - if( parentName != null && - mserver.isRegistered(parentName)) + if( parentName != null && + mserver.isRegistered(parentName)) { mserver.invoke(parentName, "addChild", new Object[] { this }, new String[] {"org.apache.catalina.Container"}); @@ -1166,11 +1169,11 @@ public abstract class ContainerBase } initialized=true; } - + public ObjectName getParentName() throws MalformedObjectNameException { return null; } - + public void destroy() throws Exception { if( started ) { stop(); @@ -1200,10 +1203,10 @@ public abstract class ContainerBase for (int i = 0; i < children.length; i++) { removeChild(children[i]); } - + } - + /** * Check this container for an access log and if none is found, look to the * parent. If there is no parent and still none is found, use the NoOp @@ -1211,14 +1214,14 @@ public abstract class ContainerBase */ public void logAccess(Request request, Response response, long time, boolean useDefault) { - + boolean logged = false; - + if (getAccessLog() != null) { getAccessLog().log(request, response, time); logged = true; } - + if (getParent() != null) { // No need to use default logger once request/response has been logged // once @@ -1227,11 +1230,11 @@ public abstract class ContainerBase } public AccessLog getAccessLog() { - + if (accessLogScanComplete) { return accessLog; } - + Valve valves[] = getPipeline().getValves(); for (Valve valve : valves) { if (valve instanceof AccessLog) { @@ -1273,7 +1276,7 @@ public abstract class ContainerBase public ObjectName[] getValveObjectNames() { return ((StandardPipeline)pipeline).getValveObjectNames(); } - + /** * <p>Return the Valve instance that has been distinguished as the basic * Valve for this Pipeline (if any). @@ -1345,7 +1348,7 @@ public abstract class ContainerBase * throwables will be caught and logged. */ public void backgroundProcess() { - + if (!started) return; @@ -1353,28 +1356,28 @@ public abstract class ContainerBase try { cluster.backgroundProcess(); } catch (Exception e) { - log.warn(sm.getString("containerBase.backgroundProcess.cluster", cluster), e); + log.warn(sm.getString("containerBase.backgroundProcess.cluster", cluster), e); } } if (loader != null) { try { loader.backgroundProcess(); } catch (Exception e) { - log.warn(sm.getString("containerBase.backgroundProcess.loader", loader), e); + log.warn(sm.getString("containerBase.backgroundProcess.loader", loader), e); } } if (manager != null) { try { manager.backgroundProcess(); } catch (Exception e) { - log.warn(sm.getString("containerBase.backgroundProcess.manager", manager), e); + log.warn(sm.getString("containerBase.backgroundProcess.manager", manager), e); } } if (realm != null) { try { realm.backgroundProcess(); } catch (Exception e) { - log.warn(sm.getString("containerBase.backgroundProcess.realm", realm), e); + log.warn(sm.getString("containerBase.backgroundProcess.realm", realm), e); } } Valve current = pipeline.getFirst(); @@ -1382,7 +1385,7 @@ public abstract class ContainerBase try { current.backgroundProcess(); } catch (Exception e) { - log.warn(sm.getString("containerBase.backgroundProcess.valve", current), e); + log.warn(sm.getString("containerBase.backgroundProcess.valve", current), e); } current = current.getNext(); } @@ -1431,16 +1434,16 @@ public abstract class ContainerBase if ((name == null) || (name.equals(""))) { name = "/"; } - loggerName = "[" + name + "]" + loggerName = "[" + name + "]" + ((loggerName != null) ? ("." + loggerName) : ""); current = current.getParent(); } logName = ContainerBase.class.getName() + "." + loggerName; return logName; - + } - + // -------------------- JMX and Registration -------------------- protected String type; protected String domain; @@ -1452,7 +1455,7 @@ public abstract class ContainerBase public ObjectName getJmxName() { return oname; } - + public String getObjectName() { if (oname != null) { return oname.toString(); @@ -1468,7 +1471,7 @@ public abstract class ContainerBase } if( parent instanceof StandardEngine ) { domain=((StandardEngine)parent).getDomain(); - } + } } return domain; } @@ -1476,7 +1479,7 @@ public abstract class ContainerBase public void setDomain(String domain) { this.domain=domain; } - + public String getType() { return type; } @@ -1547,9 +1550,9 @@ public abstract class ContainerBase Container context=null; Container host=null; Container servlet=null; - + StringBuffer suffix=new StringBuffer(); - + if( container instanceof StandardHost ) { host=container; } else if( container instanceof StandardContext ) { @@ -1563,7 +1566,7 @@ public abstract class ContainerBase if( context!=null ) { String path=((StandardContext)context).getPath(); suffix.append(",path=").append((path.equals("")) ? "/" : path); - } + } if( host!=null ) suffix.append(",host=").append( host.getName() ); if( servlet != null ) { String name=container.getName(); @@ -1620,7 +1623,7 @@ public abstract class ContainerBase /** - * Private thread class to invoke the backgroundProcess method + * Private thread class to invoke the backgroundProcess method * of this container and its children after a fixed delay. */ protected class ContainerBackgroundProcessor implements Runnable { @@ -1634,7 +1637,7 @@ public abstract class ContainerBase } if (!threadDone) { Container parent = (Container) getMappingObject(); - ClassLoader cl = + ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (parent.getLoader() != null) { cl = parent.getLoader().getClassLoader(); Modified: tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml?rev=1614288&r1=1614287&r2=1614288&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml (original) +++ tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml Tue Jul 29 08:59:01 2014 @@ -50,6 +50,11 @@ <bug>56600</bug>: In WebdavServlet: Do not waste time generating response for broken PROPFIND request. (kkolinko) </fix> + <fix> + <bug>56648</bug>: Reduce scope of synchronization when adding children to + a container (e.g. adding a Context to a Host) to prevent blocking + requests to other children while the new child starts. (markt) + </fix> </changelog> </subsection> <subsection name="Coyote"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org