1 /*
2 * Copyright 2004-2025 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package com.ibatis.common.resources;
17
18 import com.ibatis.common.beans.ClassInfo;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.InputStreamReader;
24 import java.io.Reader;
25 import java.lang.reflect.InvocationTargetException;
26 import java.net.URL;
27 import java.net.URLConnection;
28 import java.nio.charset.Charset;
29 import java.nio.file.Path;
30 import java.util.Properties;
31
32 /**
33 * A class to simplify access to resources through the classloader.
34 */
35 public class Resources extends Object {
36
37 /** The default class loader. */
38 private static ClassLoader defaultClassLoader;
39
40 /**
41 * Charset to use when calling getResourceAsReader. null means use the system default.
42 */
43 private static Charset charset;
44
45 /**
46 * Instantiates a new resources.
47 */
48 private Resources() {
49 }
50
51 /**
52 * Returns the default classloader (may be null).
53 *
54 * @return The default classloader
55 */
56 public static ClassLoader getDefaultClassLoader() {
57 return defaultClassLoader;
58 }
59
60 /**
61 * Sets the default classloader.
62 *
63 * @param defaultClassLoader
64 * - the new default ClassLoader
65 */
66 public static void setDefaultClassLoader(ClassLoader defaultClassLoader) {
67 Resources.defaultClassLoader = defaultClassLoader;
68 }
69
70 /**
71 * Returns the URL of the resource on the classpath.
72 *
73 * @param resource
74 * The resource to find
75 *
76 * @return The resource
77 *
78 * @throws IOException
79 * If the resource cannot be found or read
80 */
81 public static URL getResourceURL(String resource) throws IOException {
82 return getResourceURL(getClassLoader(), resource);
83 }
84
85 /**
86 * Returns the URL of the resource on the classpath.
87 *
88 * @param loader
89 * The classloader used to load the resource
90 * @param resource
91 * The resource to find
92 *
93 * @return The resource
94 *
95 * @throws IOException
96 * If the resource cannot be found or read
97 */
98 public static URL getResourceURL(ClassLoader loader, String resource) throws IOException {
99 URL url = null;
100 if (loader != null) {
101 url = loader.getResource(resource);
102 }
103 if (url == null) {
104 url = ClassLoader.getSystemResource(resource);
105 }
106 if (url == null) {
107 throw new IOException("Could not find resource " + resource);
108 }
109 return url;
110 }
111
112 /**
113 * Returns a resource on the classpath as a Stream object.
114 *
115 * @param resource
116 * The resource to find
117 *
118 * @return The resource
119 *
120 * @throws IOException
121 * If the resource cannot be found or read
122 */
123 public static InputStream getResourceAsStream(String resource) throws IOException {
124 return getResourceAsStream(getClassLoader(), resource);
125 }
126
127 /**
128 * Returns a resource on the classpath as a Stream object.
129 *
130 * @param loader
131 * The classloader used to load the resource
132 * @param resource
133 * The resource to find
134 *
135 * @return The resource
136 *
137 * @throws IOException
138 * If the resource cannot be found or read
139 */
140 public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
141 InputStream in = null;
142 if (loader != null) {
143 in = loader.getResourceAsStream(resource);
144 }
145 if (in == null) {
146 in = ClassLoader.getSystemResourceAsStream(resource);
147 }
148 if (in == null) {
149 throw new IOException("Could not find resource " + resource);
150 }
151 return in;
152 }
153
154 /**
155 * Returns a resource on the classpath as a Properties object.
156 *
157 * @param resource
158 * The resource to find
159 *
160 * @return The resource
161 *
162 * @throws IOException
163 * If the resource cannot be found or read
164 */
165 public static Properties getResourceAsProperties(String resource) throws IOException {
166 Properties props = new Properties();
167 String propfile = resource;
168 InputStream in = getResourceAsStream(propfile);
169 props.load(in);
170 in.close();
171 return props;
172 }
173
174 /**
175 * Returns a resource on the classpath as a Properties object.
176 *
177 * @param loader
178 * The classloader used to load the resource
179 * @param resource
180 * The resource to find
181 *
182 * @return The resource
183 *
184 * @throws IOException
185 * If the resource cannot be found or read
186 */
187 public static Properties getResourceAsProperties(ClassLoader loader, String resource) throws IOException {
188 Properties props = new Properties();
189 String propfile = resource;
190 InputStream in = getResourceAsStream(loader, propfile);
191 props.load(in);
192 in.close();
193 return props;
194 }
195
196 /**
197 * Returns a resource on the classpath as a Reader object.
198 *
199 * @param resource
200 * The resource to find
201 *
202 * @return The resource
203 *
204 * @throws IOException
205 * If the resource cannot be found or read
206 */
207 public static Reader getResourceAsReader(String resource) throws IOException {
208 Reader reader;
209 if (charset == null) {
210 reader = new InputStreamReader(getResourceAsStream(resource));
211 } else {
212 reader = new InputStreamReader(getResourceAsStream(resource), charset);
213 }
214
215 return reader;
216 }
217
218 /**
219 * Returns a resource on the classpath as a Reader object.
220 *
221 * @param loader
222 * The classloader used to load the resource
223 * @param resource
224 * The resource to find
225 *
226 * @return The resource
227 *
228 * @throws IOException
229 * If the resource cannot be found or read
230 */
231 public static Reader getResourceAsReader(ClassLoader loader, String resource) throws IOException {
232 Reader reader;
233 if (charset == null) {
234 reader = new InputStreamReader(getResourceAsStream(loader, resource));
235 } else {
236 reader = new InputStreamReader(getResourceAsStream(loader, resource), charset);
237 }
238
239 return reader;
240 }
241
242 /**
243 * Returns a resource on the classpath as a File object.
244 *
245 * @param resource
246 * The resource to find
247 *
248 * @return The resource
249 *
250 * @throws IOException
251 * If the resource cannot be found or read
252 */
253 public static File getResourceAsFile(String resource) throws IOException {
254 return Path.of(getResourceURL(resource).toString()).toFile();
255 }
256
257 /**
258 * Returns a resource on the classpath as a File object.
259 *
260 * @param loader
261 * - the classloader used to load the resource
262 * @param resource
263 * - the resource to find
264 *
265 * @return The resource
266 *
267 * @throws IOException
268 * If the resource cannot be found or read
269 */
270 public static File getResourceAsFile(ClassLoader loader, String resource) throws IOException {
271 return Path.of(getResourceURL(loader, resource).toString()).toFile();
272 }
273
274 /**
275 * Gets a URL as an input stream.
276 *
277 * @param urlString
278 * - the URL to get
279 *
280 * @return An input stream with the data from the URL
281 *
282 * @throws IOException
283 * If the resource cannot be found or read
284 */
285 public static InputStream getUrlAsStream(String urlString) throws IOException {
286 URL url = new URL(urlString);
287 URLConnection conn = url.openConnection();
288 return conn.getInputStream();
289 }
290
291 /**
292 * Gets a URL as a Reader.
293 *
294 * @param urlString
295 * - the URL to get
296 *
297 * @return A Reader with the data from the URL
298 *
299 * @throws IOException
300 * If the resource cannot be found or read
301 */
302 public static Reader getUrlAsReader(String urlString) throws IOException {
303 return new InputStreamReader(getUrlAsStream(urlString));
304 }
305
306 /**
307 * Gets a URL as a Properties object.
308 *
309 * @param urlString
310 * - the URL to get
311 *
312 * @return A Properties object with the data from the URL
313 *
314 * @throws IOException
315 * If the resource cannot be found or read
316 */
317 public static Properties getUrlAsProperties(String urlString) throws IOException {
318 Properties props = new Properties();
319 String propfile = urlString;
320 InputStream in = getUrlAsStream(propfile);
321 props.load(in);
322 in.close();
323 return props;
324 }
325
326 /**
327 * Loads a class.
328 *
329 * @param className
330 * - the class to load
331 *
332 * @return The loaded class
333 *
334 * @throws ClassNotFoundException
335 * If the class cannot be found (duh!)
336 */
337 public static Class classForName(String className) throws ClassNotFoundException {
338 Class clazz = null;
339 try {
340 clazz = getClassLoader().loadClass(className);
341 } catch (Exception e) {
342 // Ignore. Failsafe below.
343 }
344 if (clazz == null) {
345 clazz = Class.forName(className);
346 }
347 return clazz;
348 }
349
350 /**
351 * Creates an instance of a class.
352 *
353 * @param className
354 * - the class to create
355 *
356 * @return An instance of the class
357 *
358 * @throws ClassNotFoundException
359 * If the class cannot be found (duh!)
360 * @throws InstantiationException
361 * If the class cannot be instantiated
362 * @throws IllegalAccessException
363 * If the class is not public, or other access problems arise
364 */
365 public static Object instantiate(String className)
366 throws ClassNotFoundException, InstantiationException, IllegalAccessException {
367 return instantiate(classForName(className));
368 }
369
370 /**
371 * Creates an instance of a class.
372 *
373 * @param clazz
374 * - the class to create
375 *
376 * @return An instance of the class
377 *
378 * @throws InstantiationException
379 * If the class cannot be instantiated
380 * @throws IllegalAccessException
381 * If the class is not public, or other access problems arise
382 */
383 public static Object instantiate(Class clazz) throws InstantiationException, IllegalAccessException {
384 try {
385 return ClassInfo.getInstance(clazz).instantiateClass();
386 } catch (Exception e) {
387 // Try alternative...theoretically should fail for the exact same
388 // reason, but in case of a weird security manager, this will help
389 // some cases.
390 try {
391 return clazz.getDeclaredConstructor().newInstance();
392 } catch (IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e1) {
393 // Should never happen, but just in case...
394 return null;
395 }
396 }
397 }
398
399 /**
400 * Gets the class loader.
401 *
402 * @return the class loader
403 */
404 private static ClassLoader getClassLoader() {
405 if (defaultClassLoader != null) {
406 return defaultClassLoader;
407 }
408 return Thread.currentThread().getContextClassLoader();
409 }
410
411 /**
412 * Gets the charset.
413 *
414 * @return the charset
415 */
416 public static Charset getCharset() {
417 return charset;
418 }
419
420 /**
421 * Use this method to set the Charset to be used when calling the getResourceAsReader methods. This will allow iBATIS
422 * to function properly when the system default encoding doesn't deal well with unicode (IBATIS-340, IBATIS-349)
423 *
424 * @param charset
425 * the new charset
426 */
427 public static void setCharset(Charset charset) {
428 Resources.charset = charset;
429 }
430
431 }