ResourceLoaderUtility.java
001 /* This file is part of the project "Hilbert II" - http://www.qedeq.org
002  *
003  * Copyright 2000-2011,  Michael Meyling <mime@qedeq.org>.
004  *
005  * "Hilbert II" is free software; you can redistribute
006  * it and/or modify it under the terms of the GNU General Public
007  * License as published by the Free Software Foundation; either
008  * version 2 of the License, or (at your option) any later version.
009  *
010  * This program is distributed in the hope that it will be useful,
011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013  * GNU General Public License for more details.
014  */
015 
016 package org.qedeq.base.io;
017 
018 import java.io.File;
019 import java.io.IOException;
020 import java.io.InputStream;
021 import java.lang.reflect.InvocationTargetException;
022 import java.lang.reflect.Method;
023 import java.net.URL;
024 
025 import org.qedeq.base.trace.Trace;
026 
027 
028 /**
029  * Utility methods for accessing classes and resources using an appropriate class loader.
030  * Adapted from org.apache.myfaces.trinidad.util.ClassLoaderUtils.
031  *
032  @author  Michael Meyling
033  */
034 public final class ResourceLoaderUtility {
035 
036     /** This class. */
037     private static final Class CLASS = ResourceLoaderUtility.class;
038 
039     /**
040      * Constructor, should never be called.
041      */
042     private ResourceLoaderUtility() {
043         // don't call me
044     }
045 
046     /**
047      * Loads the class with the specified name. For Java 2 callers, the current thread's context
048      * class loader is preferred, falling back on the system class loader of the caller when the
049      * current thread's context is not set, or the caller is pre Java 2.
050      *
051      @param   name    Name of class to load.
052      @return  The resulting <code>Class</code> object
053      @exception ClassNotFoundException    Class was not found.
054      */
055     public static Class loadClass(final String namethrows ClassNotFoundException {
056         return loadClass(name, null);
057     }
058 
059     /**
060      * Locates the resource with the specified name. For Java 2 callers, the current thread's
061      * context class loader is preferred, falling back on the system class loader of the caller when
062      * the current thread's context is not set, or the caller is pre Java 2.
063      *
064      @param   name  Resource name.
065      @return  Resulting <code>URL</code> object. Maybe <code>null</code>.
066      */
067     public static URL getResourceUrl(final String name) {
068         return getResourceUrl(name, ResourceLoaderUtility.class.getClassLoader());
069     }
070 
071     /**
072      * Locates the stream resource with the specified name. For Java 2 callers, the current thread's
073      * context class loader is preferred, falling back on the system class loader of the caller when
074      * the current thread's context is not set, or the caller is pre Java 2.
075      *
076      @param name the name of the resource
077      @return the resulting <code>InputStream</code> object
078      */
079     public static InputStream getResourceAsStream(final String name) {
080         return getResourceAsStream(name, null);
081     }
082 
083     /**
084      * Loads the class with the specified name. For Java 2 callers, the current thread's context
085      * class loader is preferred, falling back on the class loader of the caller when the current
086      * thread's context is not set, or the caller is pre Java 2. If the callerClassLoader is null,
087      * then fall back on the system class loader.
088      *
089      @param name the name of the class
090      @param callerClassLoader the calling class loader context
091      @return the resulting <code>Class</code> object
092      @exception ClassNotFoundException if the class was not found
093      */
094     public static Class loadClass(final String name, final ClassLoader callerClassLoader)
095             throws ClassNotFoundException {
096         Class clazz = null;
097 
098         try {
099             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 (ClassLoadermethod.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 }