View Javadoc

1   /*
2    * $Id: GoodCitizenClassLoader.java 20088 2010-11-05 16:51:41Z aperepel $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.module.launcher;
12  
13  import org.mule.util.ClassUtils;
14  
15  import java.io.IOException;
16  import java.lang.reflect.Field;
17  import java.lang.reflect.Method;
18  import java.net.JarURLConnection;
19  import java.net.URL;
20  import java.net.URLClassLoader;
21  import java.net.URLStreamHandler;
22  import java.net.URLStreamHandlerFactory;
23  import java.util.Collection;
24  import java.util.Vector;
25  import java.util.jar.JarFile;
26  
27  import sun.net.www.protocol.jar.Handler;
28  
29  /**
30   * Fixes major classloader woes by:
31   * <ol>
32   *  <li>Providing a {@link #close()} method to release any connections to resources.</li>
33   *  <li>Disabling caching of jar resources fix e.g. java.util.ResourceBundle 'tagging' the app
34   *      and preventing it from being undeployed correctly (no leaving locked jars behind).</li>
35   * </ol>
36   */
37  public class GoodCitizenClassLoader extends URLClassLoader
38  {
39  
40      public GoodCitizenClassLoader(URL[] urls, ClassLoader parent)
41      {
42          super(urls, parent, new NonCachingURLStreamHandlerFactory());
43      }
44  
45      /**
46       * A workaround for http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5041014
47       */
48      public void close()
49      {
50          // jars
51          try
52          {
53              Class<URLClassLoader> clazz = URLClassLoader.class;
54              Field ucp = clazz.getDeclaredField("ucp");
55              ucp.setAccessible(true);
56              Object urlClassPath = ucp.get(this);
57              Field loaders = urlClassPath.getClass().getDeclaredField("loaders");
58              loaders.setAccessible(true);
59              Collection<?> jarLoaders = (Collection<?>) loaders.get(urlClassPath);
60              for (Object jarLoader : jarLoaders)
61              {
62                  try
63                  {
64                      Field loader = jarLoader.getClass().getDeclaredField("jar");
65                      loader.setAccessible(true);
66                      Object jarFile = loader.get(jarLoader);
67                      ((JarFile) jarFile).close();
68                  }
69                  catch (Throwable t)
70                  {
71                      // if we got this far, this is probably not a JAR loader so skip it
72                  }
73              }
74          }
75          catch (Throwable t)
76          {
77              // probably not a SUN VM
78          }
79  
80          try
81          {
82              // now native libs
83              Class<ClassLoader> clazz = ClassLoader.class;
84              Field nativeLibraries = clazz.getDeclaredField("nativeLibraries");
85              nativeLibraries.setAccessible(true);
86              Vector<?> nativelib = (Vector<?>) nativeLibraries.get(this);
87              for (Object lib : nativelib)
88              {
89                  Method finalize = lib.getClass().getDeclaredMethod("finalize");
90                  finalize.setAccessible(true);
91                  finalize.invoke(lib);
92              }
93          }
94          catch (Exception ex)
95          {
96              // ignore
97          }
98  
99          try
100         {
101             // fix groovy compiler leaks http://www.mulesoft.org/jira/browse/MULE-5125
102             final Class clazz = ClassUtils.loadClass("org.codehaus.groovy.transform.ASTTransformationVisitor", getClass());
103             final Field compUnit = clazz.getDeclaredField("compUnit");
104             compUnit.setAccessible(true);
105             // static field
106             compUnit.set(null, null);
107         }
108         catch (Throwable t)
109         {
110             // ignore
111         }
112     }
113 
114     protected static class NonCachingURLStreamHandlerFactory implements URLStreamHandlerFactory
115     {
116         public URLStreamHandler createURLStreamHandler(String protocol)
117         {
118             return new NonCachingJarResourceURLStreamHandler();
119         }
120     }
121 
122     /**
123      * Prevents jar caching for this classloader, mainly to fix the static ResourceBundle mess/cache
124      * that keeps connections open no matter what.
125      */
126     private static class NonCachingJarResourceURLStreamHandler extends Handler
127     {
128         public NonCachingJarResourceURLStreamHandler()
129         {
130             super();
131         }
132 
133         @Override
134         protected java.net.URLConnection openConnection(URL u) throws IOException
135         {
136             JarURLConnection c = new sun.net.www.protocol.jar.JarURLConnection(u, this);
137             c.setUseCaches(false);
138             return c;
139         }
140     }
141 }