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