1 | /* This file is part of the project "Hilbert II" - 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 | } |