View Javadoc

1   /* This file is part of the project "Hilbert II" - http://www.qedeq.org" target="alexandria_uri">http://www.qedeq.org
2    *
3    * Copyright 2000-2014,  Michael Meyling <mime@qedeq.org>.
4    *
5    * "Hilbert II" is free software; you can redistribute
6    * it and/or modify it under the terms of the GNU General Public
7    * License as published by the Free Software Foundation; either
8    * version 2 of the License, or (at your option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13   * GNU General Public License for more details.
14   */
15  
16  package org.qedeq.base.io;
17  
18  import java.io.File;
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.lang.reflect.InvocationTargetException;
22  import java.lang.reflect.Method;
23  import java.net.URL;
24  
25  import org.qedeq.base.trace.Trace;
26  
27  
28  /**
29   * Utility methods for accessing classes and resources using an appropriate class loader.
30   * Adapted from org.apache.myfaces.trinidad.util.ClassLoaderUtils.
31   *
32   * @author  Michael Meyling
33   */
34  public final class ResourceLoaderUtility {
35  
36      /** This class. */
37      private static final Class CLASS = ResourceLoaderUtility.class;
38  
39      /**
40       * Constructor, should never be called.
41       */
42      private ResourceLoaderUtility() {
43          // don't call me
44      }
45  
46      /**
47       * Loads the class with the specified name. For Java 2 callers, the current thread's context
48       * class loader is preferred, falling back on the system class loader of the caller when the
49       * current thread's context is not set, or the caller is pre Java 2.
50       *
51       * @param   name    Name of class to load.
52       * @return  The resulting <code>Class</code> object
53       * @exception ClassNotFoundException    Class was not found.
54       */
55      public static Class loadClass(final String name) throws ClassNotFoundException {
56          return loadClass(name, null);
57      }
58  
59      /**
60       * Locates the resource with the specified name. For Java 2 callers, the current thread's
61       * context class loader is preferred, falling back on the system class loader of the caller when
62       * the current thread's context is not set, or the caller is pre Java 2.
63       *
64       * @param   name  Resource name.
65       * @return  Resulting <code>URL</code> object. Maybe <code>null</code>.
66       */
67      public static URL getResourceUrl(final String name) {
68          return getResourceUrl(name, ResourceLoaderUtility.class.getClassLoader());
69      }
70  
71      /**
72       * Locates the stream resource with the specified name. For Java 2 callers, the current thread's
73       * context class loader is preferred, falling back on the system class loader of the caller when
74       * the current thread's context is not set, or the caller is pre Java 2.
75       *
76       * @param name the name of the resource
77       * @return the resulting <code>InputStream</code> object
78       */
79      public static InputStream getResourceAsStream(final String name) {
80          return getResourceAsStream(name, null);
81      }
82  
83      /**
84       * Loads the class with the specified name. For Java 2 callers, the current thread's context
85       * class loader is preferred, falling back on the class loader of the caller when the current
86       * thread's context is not set, or the caller is pre Java 2. If the callerClassLoader is null,
87       * then fall back on the system class loader.
88       *
89       * @param name the name of the class
90       * @param callerClassLoader the calling class loader context
91       * @return the resulting <code>Class</code> object
92       * @exception ClassNotFoundException if the class was not found
93       */
94      public static Class loadClass(final String name, final ClassLoader callerClassLoader)
95              throws ClassNotFoundException {
96          Class clazz = null;
97  
98          try {
99              final ClassLoader loader = getContextClassLoader();
100 
101             if (loader != null) {
102                 clazz = loader.loadClass(name);
103             }
104         } catch (ClassNotFoundException e) {
105             // treat as though loader not set
106         }
107 
108         if (clazz == null) {
109             if (callerClassLoader != null) {
110                 clazz = callerClassLoader.loadClass(name);
111             } else {
112                 clazz = Class.forName(name);
113             }
114         }
115 
116         return clazz;
117     }
118 
119     /**
120      * Locates the resource with the specified name. For Java 2 callers, the current thread's
121      * context class loader is preferred, falling back on the class loader of the caller when the
122      * current thread's context is not set, or the caller is pre Java 2. If the callerClassLoader is
123      * null, then fall back on the system class loader.
124      *
125      * @param name the name of the resource
126      * @param callerClassLoader the calling class loader context
127      * @return the resulting <code>URL</code> object
128      */
129     public static URL getResourceUrl(final String name, final ClassLoader callerClassLoader) {
130         checkResourceName(name);
131 
132         URL url = null;
133 
134         final ClassLoader loader = getContextClassLoader();
135 
136         if (loader != null) {
137             url = loader.getResource(name);
138         }
139 
140         if (url == null) {
141             // no success, now we try the given class loader
142             if (callerClassLoader != null) {
143                 url = callerClassLoader.getResource(name);
144             } else {
145                 // last try: get resource via classpath
146                 url = ClassLoader.getSystemResource(name);
147             }
148         }
149         return url;
150     }
151 
152     /**
153      * Locates the resource stream with the specified name. For Java 2 callers, the current thread's
154      * context class loader is preferred, falling back on the class loader of the caller when the
155      * current thread's context is not set, or the caller is pre Java 2. If the callerClassLoader is
156      * null, then fall back on the system class loader.
157      *
158      * @param name the name of the resource
159      * @param callerClassLoader the calling class loader context
160      * @return the resulting <code>InputStream</code> object
161      */
162     public static InputStream getResourceAsStream(final String name,
163             final ClassLoader callerClassLoader) {
164         checkResourceName(name);
165 
166         InputStream stream = null;
167 
168         final ClassLoader loader = getContextClassLoader();
169 
170         if (loader != null) {
171             stream = loader.getResourceAsStream(name);
172         }
173         if (stream == null) {
174             if (callerClassLoader != null) {
175                 stream = callerClassLoader.getResourceAsStream(name);
176             } else {
177                 stream = ClassLoader.getSystemResourceAsStream(name);
178             }
179         }
180 
181         return stream;
182     }
183 
184     /**
185      * Dynamically accesses the current context class loader. Returns <code>null</code> if there is
186      * no per-thread context class loader. Also if the JRE is below 1.2 or something else went wrong
187      * the method returns <code>null</code>.
188      *
189      * @return ClassLoader.
190      */
191     public static ClassLoader getContextClassLoader() {
192         try {
193             final Method method = Thread.class.getMethod("getContextClassLoader", null);
194             return (ClassLoader) method.invoke(Thread.currentThread(), null);
195         } catch (RuntimeException e) {
196             return null;
197         } catch (IllegalAccessException e) {
198             return null;
199         } catch (InvocationTargetException e) {
200             return null;
201         } catch (NoSuchMethodException e) {
202             return null;
203         }
204     }
205 
206     private static void checkResourceName(final String name) {
207         if ((name != null) && name.startsWith("/")) {
208             Trace.info(CLASS, "ClassLoaderUtility", "checkResourceName",
209                 "resource name not portable: " + name);
210 
211         }
212     }
213 
214     /**
215      * Get resource file. The resource is located within the file system if it exists already.
216      * If not it is loaded as resource and then saved as a file.
217      *
218      * @param   startDirectory          Start looking from this directory.
219      * @param   resourceDirectoryName   Within this directory
220      *                                  (relative to <code>startDirectory</code>).
221      * @param   resourceName            Look for this resource file.
222      * @return  Resource file.
223      */
224     public static File getResourceFile(final File startDirectory,
225             final String resourceDirectoryName, final String resourceName) {
226         final File resourceDir = new File(startDirectory, resourceDirectoryName);
227         final File resource = new File(resourceDir, resourceName);
228         if (!resource.exists()) {
229             final URL url = getResourceUrl(resourceDirectoryName + "/" + resourceName);
230             if (url == null) {
231                 Trace.info(ResourceLoaderUtility.class, "getResourceUrlAndMakeLocalCopy",
232                     "URL not found for: " + resourceDirectoryName + "/" + resourceName);
233                 return null;
234             }
235             try {
236                 if (!resourceDir.exists()) {
237                     if (!resourceDir.mkdirs()) {
238                         Trace.info(ResourceLoaderUtility.class, "getResourceUrlAndMakeLocalCopy",
239                             "creation failed: " + resourceDir);
240                     }
241                 }
242                 IoUtility.saveFile(url, resource);
243             } catch (IOException e) {
244                 Trace.fatal(ResourceLoaderUtility.class, "getResourceUrlAndMakeLocalCopy",
245                     "resource can not be saved", e);
246             }
247         }
248         return resource;
249     }
250 
251 }