KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > test > aop > memoryleaks > MemoryLeakTestCase


1    /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.test.aop.memoryleaks;
23
24 import java.io.File JavaDoc;
25 import java.io.FileOutputStream JavaDoc;
26 import java.io.PrintStream JavaDoc;
27 import java.lang.ref.WeakReference JavaDoc;
28 import java.lang.reflect.Constructor JavaDoc;
29 import java.lang.reflect.Field JavaDoc;
30 import java.lang.reflect.Method JavaDoc;
31 import java.net.URL JavaDoc;
32 import java.net.URLClassLoader JavaDoc;
33 import java.security.AccessController JavaDoc;
34 import java.security.PrivilegedAction JavaDoc;
35 import java.util.ArrayList JavaDoc;
36 import java.util.Enumeration JavaDoc;
37 import java.util.Iterator JavaDoc;
38 import java.util.Properties JavaDoc;
39 import java.util.StringTokenizer JavaDoc;
40 import java.util.logging.LogManager JavaDoc;
41
42 import junit.framework.TestCase;
43
44 import org.jboss.profiler.jvmti.JVMTIInterface;
45
46 /**
47  *
48  * @author <a HREF="clebert.suconic@jboss.com">Clebert Suconic</a>
49  * @author <a HREF="kabir.khan@jboss.com">Kabir Khan</a>
50  */

51 public class MemoryLeakTestCase extends TestCase
52 {
53    
54    /**
55     * Constructor for UndeployTester.
56     * @param arg0
57     */

58    public MemoryLeakTestCase(String JavaDoc name)
59    {
60       super(name);
61    }
62    
63    public MemoryLeakTestCase()
64    {
65       super();
66    }
67
68    public void testWithClassLoader() throws Exception JavaDoc
69    {
70       
71       LogManager.getLogManager(); // this is just to avoid a thread to be created using this ContextClassLoader inside LogManager
72
WeakReference JavaDoc weakReferenceOnLoader = null;
73       Class JavaDoc xmlLoader = null; // we need to keep that reference, so only the customer's classLoader will go away.
74
// If the classLoader was cleared, referenceClassLoader.get() will be null on assertions.
75
// If JBossProfiler/jvmti is available (configured with -agentlib:jbossAgent) we will use it for the assertion, if not we will use this weakReference
76
try
77       {
78          String JavaDoc className = null;
79          {
80             ClassLoader JavaDoc oldloader = Thread.currentThread().getContextClassLoader();
81             ClassLoader JavaDoc loader = newClassLoader();
82             weakReferenceOnLoader = new WeakReference JavaDoc(loader);
83             
84             Thread.currentThread().setContextClassLoader(loader);
85       
86             Class JavaDoc testClass = getTestCaseClass(loader);
87             className = testClass.getName();
88             
89             Class JavaDoc aspectManagerClass = loader.loadClass("org.jboss.aop.AspectManager");
90             assertNotSame(aspectManagerClass.getClassLoader(), this.getClass().getClassLoader());
91             
92             System.out.println("oldLoader");
93    
94             xmlLoader = loader.loadClass("org.jboss.aop.AspectXmlLoader");
95             assertNotSame(xmlLoader.getClassLoader(),loader);
96    
97             ArrayList JavaDoc methods = getTestMethods(testClass);
98             Object JavaDoc testInstance = getTestInstance(testClass);
99             
100             boolean passed = runTests(testInstance, methods);
101             assertTrue(passed);
102             
103             undeploy(xmlLoader);
104    
105             passed = runTests(testInstance, methods, true);
106             System.out.println("============ Ran tests after undeploy with errors: " + passed);
107             
108             unregisterClassLoader(aspectManagerClass, loader);
109             
110             loader=null;
111             testClass=null;
112             testInstance = null;
113             methods.clear();
114             //xmlLoader = null;
115
Thread.currentThread().setContextClassLoader(oldloader);
116          }
117
118          assertEquals(1, countInstances("org.jboss.aop.AspectManager", true));
119          //checkUnload( weakReferenceOnLoader,"org.jboss.test.aop.memoryleaks.Test");
120
checkUnload( weakReferenceOnLoader, className);
121          
122          // I'm pretty sure nobody would clear that reference. I'm keeping this assertion here just to make it clear why we can't clear xmlLoader
123
assertNotNull("The masterClassLoader needs to keep a reference, only the customer's classLoader needs to go away",xmlLoader);
124       }
125       catch (Exception JavaDoc e)
126       {
127          e.printStackTrace();
128          throw e;
129       }
130       
131       System.out.println("Done!");
132    }
133
134    private Object JavaDoc getTestInstance(Class JavaDoc testClass) throws Exception JavaDoc
135    {
136       
137       Constructor JavaDoc[] constructors = testClass.getConstructors();
138       Constructor JavaDoc defaultConstructor = null;
139       Constructor JavaDoc nameConstructor = null;
140       
141       for (int i = 0 ; i < constructors.length ; i++)
142       {
143          System.out.println("found ctor " + constructors[i]);
144          Class JavaDoc[] params = constructors[i].getParameterTypes();
145          
146          if (params.length == 0)
147          {
148             defaultConstructor = constructors[i];
149          }
150          else if (params.length == 1 && params[i].equals(String JavaDoc.class))
151          {
152             nameConstructor = constructors[i];
153          }
154       }
155       
156       if (nameConstructor != null)
157       {
158          return nameConstructor.newInstance(new Object JavaDoc[] {testClass.getName()});
159       }
160       if (defaultConstructor != null)
161       {
162          return defaultConstructor.newInstance(new Object JavaDoc[0]);
163       }
164       throw new RuntimeException JavaDoc("Could not find a constructor for " + testClass.getName());
165    }
166    private ArrayList JavaDoc getTestMethods(Class JavaDoc testClass)
167    {
168       System.out.println("Determining test methods");
169       ArrayList JavaDoc testMethods = new ArrayList JavaDoc();
170       Method JavaDoc methods[] = testClass.getMethods();
171       for (int i = 0 ; i < methods.length ; i++)
172       {
173          if (methods[i].getName().indexOf("test") == 0 && methods[i].getParameterTypes().length == 0 && methods[i].getReturnType().equals(Void.TYPE))
174          {
175             testMethods.add(methods[i]);
176          }
177       }
178       return testMethods;
179    }
180    
181    private boolean runTests(Object JavaDoc testInstance, ArrayList JavaDoc methods)
182    {
183       return runTests(testInstance, methods, false);
184    }
185    
186    private boolean runTests(Object JavaDoc testInstance, ArrayList JavaDoc methods, boolean breakOnError)
187    {
188       Method JavaDoc setup = null;
189       try
190       {
191          setup = testInstance.getClass().getDeclaredMethod("setUp", new Class JavaDoc[0]);
192          setup.setAccessible(true);
193       }
194       catch (Exception JavaDoc e)
195       {
196       }
197
198       Method JavaDoc tearDown = null;
199       try
200       {
201          tearDown = testInstance.getClass().getDeclaredMethod("tearDown", new Class JavaDoc[0]);
202          tearDown.setAccessible(true);
203       }
204       catch (Exception JavaDoc e)
205       {
206       }
207       
208       boolean passed = true;
209       for (Iterator JavaDoc it = methods.iterator() ; it.hasNext() ; )
210       {
211          Method JavaDoc test = (Method JavaDoc)it.next();
212          try
213          {
214             System.out.println("============ Running test " + testInstance.getClass().getName() + "." + test.getName());
215             
216             if (setup != null)
217             {
218                setup.invoke(testInstance, new Object JavaDoc[0]);
219             }
220             
221             test.invoke(testInstance, new Object JavaDoc[0]);
222             if (!breakOnError)
223             {
224                System.out.println("============ Succeeded test " + testInstance.getClass().getName() + "." + test.getName());
225             }
226          }
227          catch(Throwable JavaDoc t)
228          {
229             passed = false;
230             if (breakOnError)
231             {
232                break;
233             }
234             else
235             {
236                System.out.println("============ Failed test " + testInstance.getClass().getName() + "." + test.getName() + "\n" + t);
237                t.printStackTrace();
238             }
239          }
240          finally
241          {
242             if (tearDown != null)
243             {
244                try
245                {
246                   tearDown.invoke(testInstance, new Object JavaDoc[0]);
247                }
248                catch(Exception JavaDoc e)
249                {
250                }
251             }
252
253          }
254       }
255       
256       return passed;
257    }
258    
259    private Class JavaDoc getTestCaseClass(ClassLoader JavaDoc loader) throws Exception JavaDoc
260    {
261       String JavaDoc className = System.getProperty("test.to.run");
262       assertNotNull("Test to be run must be passed in test.to.run system property", className);
263       
264       Class JavaDoc testClass = loader.loadClass(className);
265       assertSame("Fix your classpath, this test is not valid",loader, testClass.getClassLoader());
266       assertNotSame(testClass.getClassLoader(), this.getClass().getClassLoader());
267       return testClass;
268    }
269
270    private void checkUnload(WeakReference JavaDoc weakReferenceOnLoader, String JavaDoc className) throws Exception JavaDoc
271    {
272       JVMTIInterface jvmti = new JVMTIInterface();
273       if (jvmti.isActive())
274       {
275          
276          //clearEverySingleFieldOnInstances("org.jboss.aop.AspectManager"); // This part is not intended to be commited. It could be used during debug, and you could use to release references on purpose, just to evaluate behavior
277

278          jvmti.forceReleaseOnSoftReferences();
279          jvmti.forceGC();
280          Class JavaDoc clazz = jvmti.getClassByName(className);
281          if (clazz!=null)
282          {
283             jvmti.heapSnapshot("snapshot", "mem");
284             clazz=null;
285             
286             String JavaDoc report =jvmti.exploreClassReferences(className, 15, true, false, false, false, false);
287             
288             //System.out.println(report);
289
String JavaDoc reportDir = System.getProperty("leak.report.dir");
290             assertNotNull("You must pass in the directory for the reports as leak.report.dir", reportDir);
291             File JavaDoc outputfile = new File JavaDoc(reportDir + "/leak-reoprt-" + className + ".html");
292             FileOutputStream JavaDoc outfile = new FileOutputStream JavaDoc(outputfile);
293             PrintStream JavaDoc realoutput = new PrintStream JavaDoc(outfile);
294             realoutput.println(report);
295             realoutput.close();
296             
297             
298             jvmti.forceGC();
299             
300             clazz = jvmti.getClassByName(className);
301             
302             if (clazz==null)
303             {
304                 System.out.println("Attention: After clearing every field on AspectManager, GC could release the classLoader");
305             }
306             
307             fail ("Class " + className + " still referenced. Look at report for more details");
308          }
309       }
310       assertNull("The classLoader is supposed to be released. Something is holding a reference. If you activate -agentlib:jbossAgent this testcase will generate a report with referenceHolders.",weakReferenceOnLoader.get());
311    }
312    
313    public Field JavaDoc[] getDeclaredFields(Class JavaDoc clazz)
314    {
315       ArrayList JavaDoc list = new ArrayList JavaDoc();
316       for (Class JavaDoc classIteration = clazz;classIteration!=null;classIteration=classIteration.getSuperclass())
317       {
318           Field JavaDoc[] fields = classIteration.getDeclaredFields();
319           for (int i = 0; i < fields.length; i++)
320           {
321              fields[i].setAccessible(true);
322              list.add(fields[i]);
323           }
324           
325       }
326       
327       return (Field JavaDoc[]) list.toArray(new Field JavaDoc[list.size()]);
328    }
329    
330    
331    private void clearEverySingleFieldOnInstances(String JavaDoc className)
332    {
333       System.out.println("Clearing " + className);
334       JVMTIInterface jvmti = new JVMTIInterface();
335       Class JavaDoc classes[] = jvmti.getLoadedClasses();
336       Object JavaDoc objects[] = null;
337       
338       for (int i=0;i<classes.length;i++)
339       {
340          if (classes[i].getName().equals(className))
341          {
342             Field JavaDoc fields[] = getDeclaredFields(classes[i]);
343             objects = jvmti.getAllObjects(classes[i]);
344             for (int j=0;j<objects.length;j++)
345             {
346                resetObject(objects[j], fields);
347             }
348             if (objects.length==0)
349             {
350                resetObject(null, fields);
351             }
352          }
353       }
354       classes= null;
355       objects = null;
356    }
357
358    private static void resetObject(Object JavaDoc object, Field JavaDoc[] fields)
359    {
360       for (int fieldN=0;fieldN<fields.length;fieldN++)
361       {
362          try
363          {
364             //System.out.print("Setting "+fields[fieldN].getName());
365
fields[fieldN].set(object,null);
366             //System.out.println("...done");
367
}
368          catch (Exception JavaDoc e)
369          {
370            // System.out.println("...error " + e.getMessage());
371
//System.out.println("Exception " + e.getMessage() + " happened during setField");
372
}
373       }
374    }
375
376    private int countInstances(String JavaDoc name, boolean notSubClasses)
377    {
378       JVMTIInterface jvmti = new JVMTIInterface();
379       Object JavaDoc[] objects = jvmti.getAllObjects(name);
380       int subClasses = 0;
381       for (int i = 0 ; i < objects.length ; i++)
382       {
383          if (!objects[i].getClass().getName().equals(name))
384          {
385             subClasses++;
386          }
387       }
388       
389       return objects.length - subClasses;
390    }
391
392    private void unregisterClassLoader(Class JavaDoc aspectManagerClass, ClassLoader JavaDoc loader) throws Exception JavaDoc
393    {
394       System.out.println("============ Unregistering ClassLoader");
395       Method JavaDoc instance = aspectManagerClass.getDeclaredMethod("instance", new Class JavaDoc[0]);
396       Object JavaDoc aspectManager = instance.invoke(null, new Object JavaDoc[0]);
397       
398       Method JavaDoc unregisterClassLoader = aspectManagerClass.getDeclaredMethod("unregisterClassLoader", new Class JavaDoc[] {ClassLoader JavaDoc.class});
399       unregisterClassLoader.invoke(aspectManager, new Object JavaDoc[] {loader});
400    }
401
402    private static void printVariables()
403    {
404       Properties JavaDoc props = System.getProperties();
405       Enumeration JavaDoc iter = props.keys();
406       
407       System.out.println("properties:");
408       while (iter.hasMoreElements())
409       {
410          Object JavaDoc key = iter.nextElement();
411          System.out.println(key + "=" + props.get(key));
412          System.out.println();
413       }
414    }
415
416    private static ClassLoader JavaDoc newClassLoader() throws Exception JavaDoc {
417        //printVariables();
418
URL JavaDoc classLocation = MemoryLeakTestCase.class.getProtectionDomain().getCodeSource().getLocation();
419        StringTokenizer JavaDoc tokenString = new StringTokenizer JavaDoc(System.getProperty("java.class.path"),File.pathSeparator);
420        String JavaDoc pathIgnore = System.getProperty("path.ignore");
421        if (pathIgnore==null)
422        {
423           pathIgnore = classLocation.toString();
424        } else
425        {
426           System.out.println("pathIgnore=" + pathIgnore);
427        }
428        
429        
430        ArrayList JavaDoc urls = new ArrayList JavaDoc();
431        while (tokenString.hasMoreElements())
432        {
433           String JavaDoc value = tokenString.nextToken();
434           URL JavaDoc itemLocation = new File JavaDoc(value).toURL();
435           if (!itemLocation.equals(classLocation) && !itemLocation.toString().equals(pathIgnore))
436           {
437              //System.out.println("Location:" + itemLocation);
438
urls.add(itemLocation);
439           }
440        }
441        
442        URL JavaDoc[] urlArray= (URL JavaDoc[])urls.toArray(new URL JavaDoc[urls.size()]);
443        
444        ClassLoader JavaDoc masterClassLoader = URLClassLoader.newInstance(urlArray,null);
445        
446        
447        ClassLoader JavaDoc appClassLoader = URLClassLoader.newInstance(new URL JavaDoc[] {classLocation},masterClassLoader);
448        
449        return appClassLoader;
450     }
451        
452
453    private void undeploy(Class JavaDoc xmlLoader) throws Exception JavaDoc
454    {
455
456       String JavaDoc strurl = (String JavaDoc)AccessController.doPrivileged(new PrivilegedAction JavaDoc(){
457       
458          public Object JavaDoc run()
459          {
460             return System.getProperty("jboss.aop.path");
461          }
462       
463       });
464 // String strurl = System.getProperty("jboss.aop.path");
465
assertNotNull("Property jboss.aop.path should be defined",strurl);
466       strurl = strurl.replace('\\','/');
467       URL JavaDoc url = new URL JavaDoc("file:/" + strurl);
468       
469       Method JavaDoc method = xmlLoader.getDeclaredMethod("undeployXML", new Class JavaDoc[] {URL JavaDoc.class});
470       method.invoke(null, new Object JavaDoc[] {url});
471       
472       System.out.println("\n====================================================================");
473       System.out.println("!!!! Undeployed " + url);
474       System.out.println("=====================================================================\n");
475
476       //AspectXmlLoader.undeployXML(url); -- I need to use reflection operations as I don't want to take the chance on letting the JVM using a different classLoader
477
}
478    
479 }
480
Popular Tags