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 }