View Javadoc
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 }