1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.commons.jelly.core;
17
18 import java.io.ByteArrayInputStream;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.net.URL;
22
23 import junit.framework.TestCase;
24
25 import org.apache.commons.jelly.JellyContext;
26 import org.apache.commons.jelly.JellyException;
27 import org.apache.commons.jelly.Script;
28 import org.apache.commons.jelly.XMLOutput;
29 import org.apache.commons.jelly.parser.XMLParser;
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.xml.sax.InputSource;
33 import org.xml.sax.SAXException;
34
35 /***
36 * Automates the basic process of testing a tag library for a memory leak.
37 * <p>
38 * To use it, extend it. Use the {@link runScriptManyTimes(String, int)}
39 * method in your unit tests.
40 *
41 * @author Hans Gilde
42 *
43 */
44 public abstract class BaseMemoryLeakTest extends TestCase {
45 private final static Log log = LogFactory.getLog(BaseMemoryLeakTest.class);
46
47 /***
48 * The JUnit constructor
49 *
50 * @param name
51 */
52 public BaseMemoryLeakTest(String name) {
53 super(name);
54 }
55
56 /*** Runs a script count times and reports the number of bytes "leaked".
57 * Note that "leaked" means "not collected by the GC"
58 * and can easily be different between JVM's. This is because all
59 * freed references may not be available for GC in the short time
60 * between their freeing and the completion of this test.
61 * <p/>
62 * However, running a
63 * script 10,000 or 100,000 times should be a pretty good test
64 * for a memory leak. If there's not too much memory "leaked",
65 * you're probably OK.
66 * @param scriptName The path to the script, from the classloader of the current class.
67 * @param count The number of times to run the script.
68 * @return The number of bytes "leaked"
69 * @throws IOException
70 * @throws SAXException
71 * @throws JellyException
72 */
73 public long runScriptManyTimes(String scriptName, int count)
74 throws IOException, SAXException, JellyException {
75 Runtime rt = Runtime.getRuntime();
76 JellyContext jc = new JellyContext();
77 jc.setClassLoader(getClass().getClassLoader());
78
79 XMLOutput output = XMLOutput.createDummyXMLOutput();
80
81 URL url = this.getClass().getResource(scriptName);
82
83 String exturl = url.toExternalForm();
84 int lastSlash = exturl.lastIndexOf("/");
85 String extBase = exturl.substring(0,lastSlash+1);
86 URL baseurl = new URL(extBase);
87 jc.setCurrentURL(baseurl);
88
89 InputStream is = url.openStream();
90 byte[] bytes = new byte[is.available()];
91 is.read(bytes);
92
93 InputStream scriptIStream = new ByteArrayInputStream(bytes);
94 InputSource scriptISource = new InputSource(scriptIStream);
95
96 is.close();
97 is = null;
98 bytes = null;
99
100 rt.runFinalization();
101 rt.gc();
102
103 long start = rt.totalMemory() - rt.freeMemory();
104 log.info("Starting memory test with used memory of " + start);
105
106 XMLParser parser;
107 Script script;
108
109 int outputEveryXIterations = outputEveryXIterations();
110
111 for (int i = 0; i < count; i++) {
112 scriptIStream.reset();
113 parser = new XMLParser();
114
115 script = parser.parse(scriptISource);
116 script.run(jc, output);
117
118
119
120
121
122
123
124 jc.clear();
125
126 if (outputEveryXIterations != 0 && i % outputEveryXIterations == 0) {
127 parser = null;
128 script = null;
129
130 rt.runFinalization();
131 rt.gc();
132 long middle = rt.totalMemory() - rt.freeMemory();
133 log.info("Memory test after " + i + " runs: "
134 + (middle - start));
135 }
136 }
137
138 rt.gc();
139
140 jc = null;
141 output = null;
142 parser = null;
143 script = null;
144
145 scriptIStream = null;
146 scriptISource = null;
147
148 rt.runFinalization();
149 rt.gc();
150
151 long nullsDone = rt.totalMemory() - rt.freeMemory();
152 log.info("Memory test completed, memory \"leaked\": " + (nullsDone - start));
153
154 return nullsDone - start;
155 }
156
157 protected int outputEveryXIterations() {
158 return 1000;
159 }
160
161 }