blob: fe32ceebda9e27523ae10dbb04fe2d05b226e3ba [file] [log] [blame]
/*
* 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.loader;
import java.io.IOException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
public class TestWebappClassLoaderExecutorMemoryLeak extends TomcatBaseTest {
@Test
public void testTimerThreadLeak() throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
if (ctx instanceof StandardContext) {
((StandardContext) ctx).setClearReferencesStopThreads(true);
}
ExecutorServlet executorServlet = new ExecutorServlet();
Tomcat.addServlet(ctx, "taskServlet", executorServlet);
ctx.addServletMappingDecoded("/", "taskServlet");
tomcat.start();
// This will trigger the timer & thread creation
getUrl("http://localhost:" + getPort() + "/");
// Stop the context
ctx.stop();
// Should be shutdown once the stop() method above exists
Assert.assertTrue(executorServlet.tpe.isShutdown());
// The time taken to shutdown the executor can vary between systems. Try
// to avoid false test failures due to timing issues. Give the executor
// upto 10 seconds to close down.
int count = 0;
while (count < 100 && !executorServlet.tpe.isTerminated()) {
count++;
Thread.sleep(100);
}
// If the executor has not terminated, there is a thread/memory leak
Assert.assertTrue(executorServlet.tpe.isTerminated());
}
static class ExecutorServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
int nTasks = 5;
long n = 1000L;
int tpSize = 10;
public volatile ThreadPoolExecutor tpe;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().println(
"The current thread served " + this + " servlet");
tpe = new ThreadPoolExecutor(tpSize, tpSize, 50000L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
Task[] tasks = new Task[nTasks];
for (int i = 0; i < nTasks; i++) {
tasks[i] = new Task("Task " + i);
tpe.execute(tasks[i]);
}
resp.getWriter().println("Started " + nTasks +
" never ending tasks using the ThreadPoolExecutor");
resp.getWriter().flush();
}
class Task implements Runnable {
String _id;
public Task(String id) {
this._id = id;
}
@Override
public void run() {
try {
while (!Thread.currentThread().isInterrupted()) {
Thread.sleep(20000);
System.out.println(Thread.currentThread().getClass()
+ " [" + Thread.currentThread().getName()
+ "] executing " + this._id);
}
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getClass() + " ["
+ Thread.currentThread().getName() + "] EXITING");
}
}
}
}
}