standardWrapper.unloadException=Servlet {0} threw unload() exception
standardWrapper.unloading=Cannot allocate servlet {0} because it is being unloaded
standardWrapper.waiting=Waiting for {0} instance(s) to be deallocated
+threadLocalLeakPreventionListener.lifecycleEvent.error=Exception processing lifecycle event {0}
+threadLocalLeakPreventionListener.containerEvent.error=Exception processing container event {0}
defaultInstanceManager.restrictedServletsResource=Restricted servlets property file not found
defaultInstanceManager.privilegedServlet=Servlet of class {0} is privileged and cannot be loaded by this web application
return this.renewThreadsWhenStoppingContext;
}
- public void setRenewThreadsWhenStoppingContext(boolean renewThreadsWhenStoppingContext) {
+ public void setRenewThreadsWhenStoppingContext(
+ boolean renewThreadsWhenStoppingContext) {
boolean oldRenewThreadsWhenStoppingContext =
- this.renewThreadsWhenStoppingContext;
+ this.renewThreadsWhenStoppingContext;
this.renewThreadsWhenStoppingContext = renewThreadsWhenStoppingContext;
support.firePropertyChange("renewThreadsWhenStoppingContext",
oldRenewThreadsWhenStoppingContext,
* renewing all threads at the same time, this delay is observed between 2
* threads being renewed.
*/
- protected long threadRenewalDelay = 1000L;
+ protected long threadRenewalDelay =
+ org.apache.tomcat.util.threads.Constants.DEFAULT_THREAD_RENEWAL_DELAY;
private TaskQueue taskqueue = null;
// ---------------------------------------------- Constructors
import org.apache.coyote.ProtocolHandler;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
import org.apache.tomcat.util.threads.ThreadPoolExecutor;
/**
+ * <p>
* A {@link LifecycleListener} that triggers the renewal of threads in Executor
* pools when a {@link Context} is being stopped to avoid thread-local related
- * memory leaks.<br/>
+ * memory leaks.
+ * </p>
+ * <p>
* Note : active threads will be renewed one by one when they come back to the
* pool after executing their task, see
- * {@link org.apache.tomcat.util.threads.ThreadPoolExecutor}.afterExecute().<br/>
+ * {@link org.apache.tomcat.util.threads.ThreadPoolExecutor}.afterExecute().
+ * </p>
*
* This listener must be declared in server.xml to be active.
*
- * @author slaurent
- *
*/
public class ThreadLocalLeakPreventionListener implements LifecycleListener,
- ContainerListener {
- private static final Log log = LogFactory
- .getLog(ThreadLocalLeakPreventionListener.class);
+ ContainerListener {
+ private static final Log log =
+ LogFactory.getLog(ThreadLocalLeakPreventionListener.class);
+
+ /**
+ * The string manager for this package.
+ */
+ protected static final StringManager sm =
+ StringManager.getManager(Constants.Package);
/**
* Listens for {@link LifecycleEvent} for the start of the {@link Server} to
try {
Lifecycle lifecycle = event.getLifecycle();
if (Lifecycle.AFTER_START_EVENT.equals(event.getType())
- && lifecycle instanceof Server) {
+ && lifecycle instanceof Server) {
// when the server starts, we register ourself as listener for
// all context
// as well as container event listener so that we know when new
}
if (Lifecycle.AFTER_STOP_EVENT.equals(event.getType())
- && lifecycle instanceof Context) {
+ && lifecycle instanceof Context) {
stopIdleThreads((Context) lifecycle);
}
} catch (Exception e) {
- log.error("Exception processing event " + event, e);
+ String msg =
+ sm.getString(
+ "threadLocalLeakPreventionListener.lifecycleEvent.error",
+ event);
+ log.error(msg, e);
}
}
String type = event.getType();
if (Container.ADD_CHILD_EVENT.equals(type)) {
processContainerAddChild(event.getContainer(),
- (Container) event.getData());
+ (Container) event.getData());
} else if (Container.REMOVE_CHILD_EVENT.equals(type)) {
processContainerRemoveChild(event.getContainer(),
- (Container) event.getData());
+ (Container) event.getData());
}
} catch (Exception e) {
- log.error("Exception processing event " + event, e);
+ String msg =
+ sm.getString(
+ "threadLocalLeakPreventionListener.containerEvent.error",
+ event);
+ log.error(msg, e);
}
}
protected void processContainerAddChild(Container parent, Container child) {
if (log.isDebugEnabled())
log.debug("Process addChild[parent=" + parent + ",child=" + child
- + "]");
-
- try {
- if (child instanceof Context) {
- registerContextListener((Context) child);
- } else if (child instanceof Engine) {
- registerListenersForEngine((Engine) child);
- } else if (child instanceof Host) {
- registerListenersForHost((Host) child);
- }
- } catch (Throwable t) {
- log.error("processContainerAddChild: Throwable", t);
+ + "]");
+
+ if (child instanceof Context) {
+ registerContextListener((Context) child);
+ } else if (child instanceof Engine) {
+ registerListenersForEngine((Engine) child);
+ } else if (child instanceof Host) {
+ registerListenersForHost((Host) child);
}
}
- protected void processContainerRemoveChild(Container parent, Container child) {
+ protected void processContainerRemoveChild(Container parent,
+ Container child) {
if (log.isDebugEnabled())
log.debug("Process removeChild[parent=" + parent + ",child="
- + child + "]");
-
- try {
- if (child instanceof Context) {
- Context context = (Context) child;
- context.removeLifecycleListener(this);
- } else if (child instanceof Host) {
- Host host = (Host) child;
- host.removeContainerListener(this);
- } else if (child instanceof Engine) {
- Engine engine = (Engine) child;
- engine.removeContainerListener(this);
- }
- } catch (Throwable t) {
- log.error("processContainerRemoveChild: Throwable", t);
+ + child + "]");
+
+ if (child instanceof Context) {
+ Context context = (Context) child;
+ context.removeLifecycleListener(this);
+ } else if (child instanceof Host) {
+ Host host = (Host) child;
+ host.removeContainerListener(this);
+ } else if (child instanceof Engine) {
+ Engine engine = (Engine) child;
+ engine.removeContainerListener(this);
}
-
}
/**
* of its parent Service.
*/
private void stopIdleThreads(Context context) {
- if (context instanceof StandardContext
- && !((StandardContext) context)
- .getRenewThreadsWhenStoppingContext()) {
+ if (context instanceof StandardContext &&
+ !((StandardContext) context).getRenewThreadsWhenStoppingContext()) {
log.debug("Not renewing threads when the context is stopping, it is configured not to do it.");
return;
}
}
if (executor instanceof ThreadPoolExecutor) {
- ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
+ ThreadPoolExecutor threadPoolExecutor =
+ (ThreadPoolExecutor) executor;
threadPoolExecutor.contextStopping();
} else if (executor instanceof StandardThreadExecutor) {
- StandardThreadExecutor stdThreadExecutor = (StandardThreadExecutor) executor;
+ StandardThreadExecutor stdThreadExecutor =
+ (StandardThreadExecutor) executor;
stdThreadExecutor.contextStopping();
}
} catch (IllegalAccessException e) {
log.warn(sm.getString("webappClassLoader.checkThreadLocalsForLeaksFail",
contextName), e);
- } catch (NoSuchMethodException e) {
- log.warn(sm.getString("webappClassLoader.checkThreadLocalsForLeaksFail",
- contextName), e);
- } catch (InvocationTargetException e) {
- log.warn(sm.getString("webappClassLoader.checkThreadLocalsForLeaksFail",
- contextName), e);
}
}
- /*
+ /**
* Analyzes the given thread local map object. Also pass in the field that
* points to the internal table to save re-calculating it on every
* call to this method.
*/
- private void checkThreadLocalMapForLeaks(Object map, Field internalTableField)
- throws NoSuchMethodException, IllegalAccessException,
- NoSuchFieldException, InvocationTargetException {
+ private void checkThreadLocalMapForLeaks(Object map,
+ Field internalTableField) throws IllegalAccessException,
+ NoSuchFieldException {
if (map != null) {
Object[] table = (Object[]) internalTableField.get(map);
if (table != null) {
public static final String Package = "org.apache.tomcat.util.threads";
+ public static final long DEFAULT_THREAD_RENEWAL_DELAY = 1000L;
}
@Override
- public Runnable poll(long timeout, TimeUnit unit) throws InterruptedException {
+ public Runnable poll(long timeout, TimeUnit unit)
+ throws InterruptedException {
Runnable runnable = super.poll(timeout, unit);
if (runnable == null && parent != null) {
// the poll timed out, it gives an opportunity to stop the current
}
return runnable;
}
-
@Override
public Runnable take() throws InterruptedException {
if (parent != null && parent.currentThreadShouldBeStopped()) {
- return poll(parent.getKeepAliveTime(TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS);
- //yes, this may return null (in case of timeout) which normally does not occur with take()
- //but the ThreadPoolExecutor implementation allows this
+ return poll(parent.getKeepAliveTime(TimeUnit.MILLISECONDS),
+ TimeUnit.MILLISECONDS);
+ // yes, this may return null (in case of timeout) which normally
+ // does not occur with take()
+ // but the ThreadPoolExecutor implementation allows this
}
return super.take();
}
@Override
public int remainingCapacity() {
- if(forcedRemainingCapacity != null) {
+ if (forcedRemainingCapacity != null) {
// ThreadPoolExecutor.setCorePoolSize checks that
// remainingCapacity==0 to allow to interrupt idle threads
// I don't see why, but this hack allows to conform to this
/**
* A Thread implementation that records the time at which it was created.
*
- * @author slaurent
- *
*/
public class TaskThread extends Thread {
/**
* Delay in ms between 2 threads being renewed. If negative, do not renew threads.
*/
- private long threadRenewalDelay = 1000L;
+ private long threadRenewalDelay = Constants.DEFAULT_THREAD_RENEWAL_DELAY;
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
}
protected boolean currentThreadShouldBeStopped() {
- if (threadRenewalDelay >= 0 && Thread.currentThread() instanceof TaskThread) {
+ if (threadRenewalDelay >= 0
+ && Thread.currentThread() instanceof TaskThread) {
TaskThread currentTaskThread = (TaskThread) Thread.currentThread();
- if (currentTaskThread.getCreationTime() < this.lastContextStoppedTime.longValue()) {
+ if (currentTaskThread.getCreationTime() <
+ this.lastContextStoppedTime.longValue()) {
return true;
}
}
* thread, at the discretion of the <tt>Executor</tt> implementation.
* If no threads are available, it will be added to the work queue.
* If the work queue is full, the system will wait for the specified
- * time and it throw a RejectedExecutionException if the queue is still full after that.
+ * time and it throw a RejectedExecutionException if the queue is still
+ * full after that.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
// save the current pool parameters to restore them later
int savedCorePoolSize = this.getCorePoolSize();
- TaskQueue taskQueue = getQueue() instanceof TaskQueue ? (TaskQueue) getQueue() : null;
+ TaskQueue taskQueue =
+ getQueue() instanceof TaskQueue ? (TaskQueue) getQueue() : null;
if (taskQueue != null) {
// note by slaurent : quite oddly threadPoolExecutor.setCorePoolSize
// checks that queue.remainingCapacity()==0. I did not understand
this.setCorePoolSize(0);
// wait a little so that idle threads wake and poll the queue again,
- // this time always with a timeout (queue.poll() instead of queue.take())
- // even if we did not wait enough, TaskQueue.take() takes care of timing out
- // so that we are sure that all threads of the pool are renewed in a limited
- // time, something like (threadKeepAlive + longest request time)
+ // this time always with a timeout (queue.poll() instead of
+ // queue.take())
+ // even if we did not wait enough, TaskQueue.take() takes care of timing
+ // out, so that we are sure that all threads of the pool are renewed in
+ // a limited time, something like
+ // (threadKeepAlive + longest request time)
try {
Thread.sleep(200L);
} catch (InterruptedException e) {
- //yes, ignore
+ // yes, ignore
}
-
+
if (taskQueue != null) {
// ok, restore the state of the queue and pool
taskQueue.setForcedRemainingCapacity(null);