1 /*
2  * Copyright 2011-2020 Amazon Technologies, Inc.
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  *    http://aws.amazon.com/apache2.0
9  *
10  * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
11  * OR CONDITIONS OF ANY KIND, either express or implied. See the
12  * License for the specific language governing permissions and
13  * limitations under the License.
14  */

15 package com.amazonaws.util;
16
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.net.URL;
20
21 public enum ClassLoaderHelper {
22     ;
23     /**
24      * Retrieves the resource via the context class loader of the current
25      * thread, and if not found, via the class loaders of the optionally
26      * specified classes in the order of their specification, and if not
27      * found, from the class loader of {@link ClassLoaderHelper} as the last
28      * resort.
29      * 
30      * @param resource
31      *            resource to be loaded
32      * @param classes class loader providers
33      * @return the resource loaded as an URL or null if not found.
34      */

35     public static URL getResource(String resource, Class<?> ... classes) {
36         return getResource(resource, false, classes);
37     }
38
39     /**
40      * If classesFirst is false, retrieves the resource via the context class
41      * loader of the current thread, and if not found, via the class loaders of
42      * the optionally specified classes in the order of their specification, and
43      * if not found, from the class loader of {@link ClassLoaderHelper} as the
44      * last resort.
45      * <p>
46      * If classesFirst is true, retrieves the resource via the optionally
47      * specified classes in the order of their specification, and if not found,
48      * via the context class loader of the current thread, and if not found,
49      * from the class loader of {@link ClassLoaderHelper} as the last resort.
50      * 
51      * @param resource
52      *            resource to be loaded
53      * @param classesFirst
54      *            true if the class loaders of the optionally specified classes
55      *            take precedence over the context class loader of the current
56      *            thread; false if the opposite is true.
57      * @param classes
58      *            class loader providers
59      * @return the resource loaded as an URL or null if not found.
60      */

61     public static URL getResource(String resource, boolean classesFirst,
62             Class<?>... classes) {
63         URL url;
64         if (classesFirst) {
65             url = getResourceViaClasses(resource, classes);
66             if (url == null) {
67                 url = getResourceViaContext(resource);
68             }
69         } else {
70             url = getResourceViaContext(resource);
71             if (url == null) {
72                 url = getResourceViaClasses(resource, classes);
73             }
74         }
75         return url == null ? ClassLoaderHelper.class.getResource(resource) : url;
76     }
77
78     private static URL getResourceViaClasses(String resource, Class<?>[] classes) {
79         if (classes != null) {
80             for (Class<?> c: classes) {
81                 URL url = c.getResource(resource);
82                 if (url != null)
83                     return url;
84             }
85         }
86         return null;
87     }
88
89     private static URL getResourceViaContext(String resource) {
90         ClassLoader loader = Thread.currentThread().getContextClassLoader();
91         return loader == null ? null : loader.getResource(resource);
92     }
93
94     private static Class<?> loadClassViaClasses(String fqcn, Class<?>[] classes) {
95         if (classes != null) {
96             for (Class<?> c: classes) {
97                 ClassLoader loader = c.getClassLoader();
98                 if (loader != null) {
99                     try {
100                         return loader.loadClass(fqcn);
101                     } catch (ClassNotFoundException e) {
102                         // move on to try the next class loader
103                     }
104                 }
105             }
106         }
107         return null;
108     }
109
110     private static Class<?> loadClassViaContext(String fqcn) {
111         ClassLoader loader = Thread.currentThread().getContextClassLoader();
112         try {
113             return loader == null ? null : loader.loadClass(fqcn);
114         } catch (ClassNotFoundException e) {
115         }
116         return null;
117     }
118
119     /**
120      * Loads the class via the optionally specified classes in the order of
121      * their specification, and if not found, via the context class loader of
122      * the current thread, and if not found, from the caller class loader as the
123      * last resort.
124      * 
125      * @param fqcn
126      *            fully qualified class name of the target class to be loaded
127      * @param classes
128      *            class loader providers
129      * @return the class loaded; never null
130      * 
131      * @throws ClassNotFoundException
132      *             if failed to load the class
133      */

134     public static Class<?> loadClass(String fqcn, Class<?>... classes)
135             throws ClassNotFoundException {
136         return loadClass(fqcn, true, classes);
137     }
138
139     /**
140      * If classesFirst is false, loads the class via the context class
141      * loader of the current thread, and if not found, via the class loaders of
142      * the optionally specified classes in the order of their specification, and
143      * if not found, from the caller class loader as the
144      * last resort.
145      * <p>
146      * If classesFirst is true, loads the class via the optionally
147      * specified classes in the order of their specification, and if not found,
148      * via the context class loader of the current thread, and if not found,
149      * from the caller class loader as the last resort.
150      * 
151      * @param fqcn
152      *            fully qualified class name of the target class to be loaded
153      * @param classesFirst
154      *            true if the class loaders of the optionally specified classes
155      *            take precedence over the context class loader of the current
156      *            thread; false if the opposite is true.
157      * @param classes
158      *            class loader providers
159      * @return the class loaded; never null
160      * 
161      * @throws ClassNotFoundException if failed to load the class
162      */

163     public static Class<?> loadClass(String fqcn, boolean classesFirst,
164             Class<?>... classes) throws ClassNotFoundException {
165         Class<?> target = null;
166         if (classesFirst) {
167             target = loadClassViaClasses(fqcn, classes);
168             if (target == null) {
169                 target = loadClassViaContext(fqcn);
170             }
171         } else {
172             target = loadClassViaContext(fqcn);
173             if (target == null) {
174                 target = loadClassViaClasses(fqcn, classes);
175             }
176         }
177         return target == null ? Class.forName(fqcn) : target;
178     }
179
180     /**
181      * Retrieves the resource as an input stream via
182      * the context class loader of the current thread, and if not found, via the
183      * class loaders of the optionally specified classes in the order of their
184      * specification, and if not found, from the class loader of
185      * {@link ClassLoaderHelper} as the last resort.
186      * 
187      * @param resource
188      *            resource to be loaded
189      * @param classes
190      *            class loader providers
191      * @return the resource loaded as an input stream or null if not found.
192      */

193     public static InputStream getResourceAsStream(String resource,
194             Class<?>... classes) {
195         return getResourceAsStream(resource, false, classes);
196     }
197
198     /**
199      * If classesFirst is false, retrieves the resource as an input stream via
200      * the context class loader of the current thread, and if not found, via the
201      * class loaders of the optionally specified classes in the order of their
202      * specification, and if not found, from the class loader of
203      * {@link ClassLoaderHelper} as the last resort.
204      * <p>
205      * If classesFirst is true, retrieves the resource as an input stream via
206      * the optionally specified classes in the order of their specification, and
207      * if not found, via the context class loader of the current thread, and if
208      * not found, from the class loader of {@link ClassLoaderHelper} as the last
209      * resort.
210      * 
211      * @param resource
212      *            resource to be loaded
213      * @param classesFirst
214      *            true if the class loaders of the optionally specified classes
215      *            take precedence over the context class loader of the current
216      *            thread; false if the opposite is true.
217      * @param classes
218      *            class loader providers
219      * @return the resource loaded as an input stream or null if not found.
220      */

221     public static InputStream getResourceAsStream(String resource,
222             boolean classesFirst, Class<?>... classes) {
223         URL url = getResource(resource, classesFirst, classes);
224         try {
225             return url != null ? url.openStream() : null;
226         } catch (IOException e) {
227             return null;
228         }
229     }
230 }
231