View | Details | Raw Unified | Return to bug 52850
Collapse All | Expand All

(-)test/org/apache/catalina/loader/TestWebappClassLoaderThreadLocalMemoryLeak.java (+142 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
package org.apache.catalina.loader;
18
19
import java.io.IOException;
20
import java.util.Arrays;
21
import java.util.List;
22
23
import javax.servlet.ServletException;
24
import javax.servlet.http.HttpServlet;
25
import javax.servlet.http.HttpServletRequest;
26
import javax.servlet.http.HttpServletResponse;
27
28
import org.apache.catalina.Context;
29
import org.apache.catalina.startup.Tomcat;
30
import org.apache.catalina.startup.TomcatBaseTest;
31
import org.apache.tomcat.util.buf.ByteChunk;
32
import org.junit.Test;
33
34
public class TestWebappClassLoaderThreadLocalMemoryLeak extends TomcatBaseTest {
35
36
    @Test
37
    public void testThreadLocalLeak() throws Exception {
38
39
        Tomcat tomcat = getTomcatInstance();
40
41
        // Must have a real docBase - just use temp
42
        Context ctx =
43
            tomcat.addContext("", System.getProperty("java.io.tmpdir"));
44
45
        Tomcat.addServlet(ctx, "leakServlet", new LeakingServlet());
46
        ctx.addServletMapping("/leak1", "leakServlet");
47
48
        Tomcat.addServlet(ctx, "leakServlet2", new LeakingServlet2());
49
        ctx.addServletMapping("/leak2", "leakServlet2");
50
51
52
        tomcat.start();
53
54
        // This will trigger the timer & thread creation
55
        ByteChunk chunk = getUrl("http://localhost:" + getPort() + "/leak1");
56
        System.out.print("First Threadlocal test response "+ chunk.toString());
57
58
        chunk = getUrl("http://localhost:" + getPort() + "/leak2");
59
        System.out.print("Second Threadlocal test response "+ chunk.toString());
60
61
        // Stop the context
62
        ctx.stop();
63
64
        // If the thread still exists, we have a thread/memory leak
65
        try {
66
            Thread.sleep(1000);
67
        } catch(InterruptedException ie) {
68
            // ignore
69
        }
70
    }
71
72
    class LeakingServlet extends HttpServlet {
73
        private ThreadLocal<MyCounter> myThreadLocal = new ThreadLocal<MyCounter>();
74
75
        protected void doGet(HttpServletRequest request,
76
                HttpServletResponse response) throws ServletException,
77
                IOException {
78
79
            MyCounter counter = myThreadLocal.get();
80
            if (counter == null) {
81
                counter = new MyCounter();
82
                myThreadLocal.set(counter);
83
            }
84
85
            response.getWriter().println(
86
                    "The current thread served this servlet "
87
                            + counter.getCount() + " times");
88
            counter.increment();
89
        }
90
91
        @Override
92
        public void destroy() {
93
            super.destroy();
94
            // normally not needed, just to make my point
95
            myThreadLocal = null;
96
        }
97
    }
98
99
    class LeakingServlet2 extends HttpServlet {
100
101
        protected void doGet(HttpServletRequest request,
102
                        HttpServletResponse response) throws ServletException, IOException {
103
104
                List<MyCounter> counterList = (List<MyCounter>) ThreadScopedHolder.getFromHolder();
105
                MyCounter counter;
106
                if (counterList == null) {
107
                        counter = new MyCounter();
108
                        ThreadScopedHolder.saveInHolder(Arrays.asList(counter));
109
                } else {
110
                        counter = counterList.get(0);
111
                }
112
113
                response.getWriter().println(
114
                                "The current thread served this servlet " + counter.getCount()+ " times");
115
                counter.increment();
116
        }
117
    }
118
119
    static class ThreadScopedHolder {
120
        private final static ThreadLocal<Object> threadLocal = new ThreadLocal<Object>();
121
122
        public static void saveInHolder(Object o) {
123
                threadLocal.set(o);
124
        }
125
126
        public static Object getFromHolder() {
127
                return threadLocal.get();
128
        }
129
}
130
131
    class MyCounter {
132
        private int count = 0;
133
134
        public void increment() {
135
            count++;
136
        }
137
138
        public int getCount() {
139
            return count;
140
        }
141
    }
142
}
(-)test/org/apache/catalina/loader/TestWebappClassLoaderExecutorMemoryLeak.java (+120 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
package org.apache.catalina.loader;
18
19
import java.io.IOException;
20
import java.util.concurrent.LinkedBlockingQueue;
21
import java.util.concurrent.ThreadPoolExecutor;
22
import java.util.concurrent.TimeUnit;
23
24
import javax.servlet.ServletException;
25
import javax.servlet.http.HttpServlet;
26
import javax.servlet.http.HttpServletRequest;
27
import javax.servlet.http.HttpServletResponse;
28
29
import junit.framework.Assert;
30
31
import org.apache.catalina.Context;
32
import org.apache.catalina.core.StandardContext;
33
import org.apache.catalina.startup.Tomcat;
34
import org.apache.catalina.startup.TomcatBaseTest;
35
import org.junit.Test;
36
37
public class TestWebappClassLoaderExecutorMemoryLeak extends TomcatBaseTest {
38
39
    @Test
40
    public void testTimerThreadLeak() throws Exception {
41
        Tomcat tomcat = getTomcatInstance();
42
43
        // Must have a real docBase - just use temp
44
        Context ctx =
45
            tomcat.addContext("", System.getProperty("java.io.tmpdir"));
46
47
        if (ctx instanceof StandardContext) {
48
            ((StandardContext) ctx).setClearReferencesStopThreads(true);
49
        }
50
51
        Tomcat.addServlet(ctx, "taskServlet", new ExecutorServlet());
52
        ctx.addServletMapping("/", "taskServlet");
53
54
        tomcat.start();
55
56
        // This will trigger the timer & thread creation
57
        getUrl("http://localhost:" + getPort() + "/");
58
59
        // Stop the context
60
        ctx.stop();
61
62
        // If the thread still exists, we have a thread/memory leak
63
        try {
64
            Thread.sleep(1000);
65
        } catch(InterruptedException ie) {
66
            // ignore
67
        }
68
69
        Assert.assertTrue(ExecutorServlet.tpe.isShutdown());
70
        Assert.assertTrue(ExecutorServlet.tpe.isTerminated());
71
    }
72
73
    static class ExecutorServlet extends HttpServlet {
74
75
76
        int nTasks = 5;
77
        long n = 1000L;
78
        int tpSize = 10;
79
80
        public static ThreadPoolExecutor tpe;
81
82
        @Override
83
        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
84
                throws ServletException, IOException {
85
86
            resp.getWriter().println("The current thread served"+  this + "servlet ");
87
             tpe = new ThreadPoolExecutor(tpSize, tpSize, 50000L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
88
89
            Task[] tasks = new Task[nTasks];
90
            for (int i = 0; i < nTasks; i++) {
91
                tasks[i] = new Task("Task " + i);
92
                tpe.execute(tasks[i]);
93
            }
94
            resp.getWriter().write("Started "+ nTasks +"never ending tasks using the ThreadPoolExecutor \n");
95
            resp.getWriter().flush();
96
        }
97
98
99
        class Task implements Runnable {
100
101
            String _id;
102
             public Task(String id) {
103
                    this._id = id;
104
                  }
105
106
            public void run() {
107
                try {
108
                    while (!Thread.currentThread().isInterrupted()) {
109
                        Thread.sleep(20000);
110
                        System.out.println(Thread.currentThread().getClass()+" ["+ Thread.currentThread().getName() + "] executing " + this._id);
111
                    }
112
                } catch (InterruptedException e) {
113
                    System.out.println(Thread.currentThread().getClass()+" ["+ Thread.currentThread().getName() + "] EXITING");
114
                }
115
            }
116
        }
117
    }
118
119
120
}

Return to bug 52850