001: /*
002: * Copyright 2002,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.jelly.core;
017:
018: import java.io.ByteArrayInputStream;
019: import java.io.IOException;
020: import java.io.InputStream;
021: import java.net.URL;
022:
023: import junit.framework.TestCase;
024:
025: import org.apache.commons.jelly.JellyContext;
026: import org.apache.commons.jelly.JellyException;
027: import org.apache.commons.jelly.Script;
028: import org.apache.commons.jelly.XMLOutput;
029: import org.apache.commons.jelly.parser.XMLParser;
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032: import org.xml.sax.InputSource;
033: import org.xml.sax.SAXException;
034:
035: /**
036: * Automates the basic process of testing a tag library for a memory leak.
037: * <p>
038: * To use it, extend it. Use the {@link runScriptManyTimes(String, int)}
039: * method in your unit tests.
040: *
041: * @author Hans Gilde
042: *
043: */
044: public class BaseMemoryLeakTest extends TestCase {
045: private final static Log log = LogFactory
046: .getLog(BaseMemoryLeakTest.class);
047:
048: /**
049: * The JUnit constructor
050: *
051: * @param name
052: */
053: public BaseMemoryLeakTest(String name) {
054: super (name);
055: }
056:
057: /** Runs a script count times and reports the number of bytes "leaked".
058: * Note that "leaked" means "not collected by the GC"
059: * and can easily be different between JVM's. This is because all
060: * freed references may not be available for GC in the short time
061: * between their freeing and the completion of this test.
062: * <p/>
063: * However, running a
064: * script 10,000 or 100,000 times should be a pretty good test
065: * for a memory leak. If there's not too much memory "leaked",
066: * you're probably OK.
067: * @param scriptName The path to the script, from the classloader of the current class.
068: * @param count The number of times to run the script.
069: * @return The number of bytes "leaked"
070: * @throws IOException
071: * @throws SAXException
072: * @throws JellyException
073: */
074: public long runScriptManyTimes(String scriptName, int count)
075: throws IOException, SAXException, JellyException {
076: Runtime rt = Runtime.getRuntime();
077: JellyContext jc = new JellyContext();
078: jc.setClassLoader(getClass().getClassLoader());
079:
080: XMLOutput output = XMLOutput.createDummyXMLOutput();
081:
082: URL url = this .getClass().getResource(scriptName);
083:
084: String exturl = url.toExternalForm();
085: int lastSlash = exturl.lastIndexOf("/");
086: String extBase = exturl.substring(0, lastSlash + 1);
087: URL baseurl = new URL(extBase);
088: jc.setCurrentURL(baseurl);
089:
090: InputStream is = url.openStream();
091: byte[] bytes = new byte[is.available()];
092: is.read(bytes);
093:
094: InputStream scriptIStream = new ByteArrayInputStream(bytes);
095: InputSource scriptISource = new InputSource(scriptIStream);
096:
097: is.close();
098: is = null;
099: bytes = null;
100:
101: rt.runFinalization();
102: rt.gc();
103:
104: long start = rt.totalMemory() - rt.freeMemory();
105: log.info("Starting memory test with used memory of " + start);
106:
107: XMLParser parser;
108: Script script;
109:
110: int outputEveryXIterations = outputEveryXIterations();
111:
112: for (int i = 0; i < count; i++) {
113: scriptIStream.reset();
114: parser = new XMLParser();
115:
116: script = parser.parse(scriptISource);
117: script.run(jc, output);
118: // PL: I don't see why but removing the clear here
119: // does make the test fail!
120: // As if the WeakHashMap wasn't weak enough...
121:
122: //Hans: The structure of the relationship
123: // between TagScript and Tag prevents WeakHashMap
124: // from working in this case, which is why I removed it.
125: jc.clear();
126:
127: if (outputEveryXIterations != 0
128: && i % outputEveryXIterations == 0) {
129: parser = null;
130: script = null;
131:
132: rt.runFinalization();
133: rt.gc();
134: long middle = rt.totalMemory() - rt.freeMemory();
135: log.info("Memory test after " + i + " runs: "
136: + (middle - start));
137: }
138: }
139:
140: rt.gc();
141:
142: jc = null;
143: output = null;
144: parser = null;
145: script = null;
146:
147: scriptIStream = null;
148: scriptISource = null;
149:
150: rt.runFinalization();
151: rt.gc();
152:
153: long nullsDone = rt.totalMemory() - rt.freeMemory();
154: log.info("Memory test completed, memory \"leaked\": "
155: + (nullsDone - start));
156:
157: return nullsDone - start;
158: }
159:
160: protected int outputEveryXIterations() {
161: return 1000;
162: }
163:
164: }
|