001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.apache.shiro.util;
020
021 import org.slf4j.Logger;
022 import org.slf4j.LoggerFactory;
023
024 import java.io.InputStream;
025 import java.lang.reflect.Constructor;
026
027
028 /**
029 * Utility method library used to conveniently interact with <code>Class</code>es, such as acquiring them from the
030 * application <code>ClassLoader</code>s and instantiating Objects from them.
031 *
032 * @author Les Hazlewood
033 * @since 0.1
034 */
035 public class ClassUtils {
036
037 //TODO - complete JavaDoc
038
039 /**
040 * Private internal log instance.
041 */
042 private static final Logger log = LoggerFactory.getLogger(ClassUtils.class);
043
044 /**
045 * @since 1.0
046 */
047 private static final ClassLoaderAccessor THREAD_CL_ACCESSOR = new ExceptionIgnoringAccessor() {
048 @Override
049 protected ClassLoader doGetClassLoader() throws Throwable {
050 return Thread.currentThread().getContextClassLoader();
051 }
052 };
053
054 /**
055 * @since 1.0
056 */
057 private static final ClassLoaderAccessor CLASS_CL_ACCESSOR = new ExceptionIgnoringAccessor() {
058 @Override
059 protected ClassLoader doGetClassLoader() throws Throwable {
060 return ClassUtils.class.getClassLoader();
061 }
062 };
063
064 /**
065 * @since 1.0
066 */
067 private static final ClassLoaderAccessor SYSTEM_CL_ACCESSOR = new ExceptionIgnoringAccessor() {
068 @Override
069 protected ClassLoader doGetClassLoader() throws Throwable {
070 return ClassLoader.getSystemClassLoader();
071 }
072 };
073
074 /**
075 * Returns the specified resource by checking the current thread's
076 * {@link Thread#getContextClassLoader() context class loader}, then the
077 * current ClassLoader (<code>ClassUtils.class.getClassLoader()</code>), then the system/application
078 * ClassLoader (<code>ClassLoader.getSystemClassLoader()</code>, in that order, using
079 * {@link ClassLoader#getResourceAsStream(String) getResourceAsStream(name)}.
080 *
081 * @param name the name of the resource to acquire from the classloader(s).
082 * @return the InputStream of the resource found, or <code>null</code> if the resource cannot be found from any
083 * of the three mentioned ClassLoaders.
084 * @since 0.9
085 */
086 public static InputStream getResourceAsStream(String name) {
087
088 InputStream is = THREAD_CL_ACCESSOR.getResourceStream(name);
089
090 if (is == null) {
091 if (log.isTraceEnabled()) {
092 log.trace("Resource [" + name + "] was not found via the thread context ClassLoader. Trying the " +
093 "current ClassLoader...");
094 }
095 is = CLASS_CL_ACCESSOR.getResourceStream(name);
096 }
097
098 if (is == null) {
099 if (log.isTraceEnabled()) {
100 log.trace("Resource [" + name + "] was not found via the current class loader. Trying the " +
101 "system/application ClassLoader...");
102 }
103 is = SYSTEM_CL_ACCESSOR.getResourceStream(name);
104 }
105
106 if (is == null && log.isTraceEnabled()) {
107 log.trace("Resource [" + name + "] was not found via the thread context, current, or " +
108 "system/application ClassLoaders. All heuristics have been exhausted. Returning null.");
109 }
110
111 return is;
112 }
113
114 /**
115 * Attempts to load the specified class name from the current thread's
116 * {@link Thread#getContextClassLoader() context class loader}, then the
117 * current ClassLoader (<code>ClassUtils.class.getClassLoader()</code>), then the system/application
118 * ClassLoader (<code>ClassLoader.getSystemClassLoader()</code>, in that order. If any of them cannot locate
119 * the specified class, an <code>UnknownClassException</code> is thrown (our RuntimeException equivalent of
120 * the JRE's <code>ClassNotFoundException</code>.
121 *
122 * @param fqcn the fully qualified class name to load
123 * @return the located class
124 * @throws UnknownClassException if the class cannot be found.
125 */
126 public static Class forName(String fqcn) throws UnknownClassException {
127
128 Class clazz = THREAD_CL_ACCESSOR.loadClass(fqcn);
129
130 if (clazz == null) {
131 if (log.isTraceEnabled()) {
132 log.trace("Unable to load class named [" + fqcn +
133 "] from the thread context ClassLoader. Trying the current ClassLoader...");
134 }
135 clazz = CLASS_CL_ACCESSOR.loadClass(fqcn);
136 }
137
138 if (clazz == null) {
139 if (log.isTraceEnabled()) {
140 log.trace("Unable to load class named [" + fqcn + "] from the current ClassLoader. " +
141 "Trying the system/application ClassLoader...");
142 }
143 clazz = SYSTEM_CL_ACCESSOR.loadClass(fqcn);
144 }
145
146 if (clazz == null) {
147 String msg = "Unable to load class named [" + fqcn + "] from the thread context, current, or " +
148 "system/application ClassLoaders. All heuristics have been exhausted. Class could not be found.";
149 throw new UnknownClassException(msg);
150 }
151
152 return clazz;
153 }
154
155 public static boolean isAvailable(String fullyQualifiedClassName) {
156 try {
157 forName(fullyQualifiedClassName);
158 return true;
159 } catch (UnknownClassException e) {
160 return false;
161 }
162 }
163
164 public static Object newInstance(String fqcn) {
165 return newInstance(forName(fqcn));
166 }
167
168 public static Object newInstance(String fqcn, Object... args) {
169 return newInstance(forName(fqcn), args);
170 }
171
172 public static Object newInstance(Class clazz) {
173 if (clazz == null) {
174 String msg = "Class method parameter cannot be null.";
175 throw new IllegalArgumentException(msg);
176 }
177 try {
178 return clazz.newInstance();
179 } catch (Exception e) {
180 throw new InstantiationException("Unable to instantiate class [" + clazz.getName() + "]", e);
181 }
182 }
183
184 public static Object newInstance(Class clazz, Object... args) {
185 Class[] argTypes = new Class[args.length];
186 for (int i = 0; i < args.length; i++) {
187 argTypes[i] = args[i].getClass();
188 }
189 Constructor ctor = getConstructor(clazz, argTypes);
190 return instantiate(ctor, args);
191 }
192
193 public static Constructor getConstructor(Class clazz, Class... argTypes) {
194 try {
195 return clazz.getConstructor(argTypes);
196 } catch (NoSuchMethodException e) {
197 throw new IllegalStateException(e);
198 }
199
200 }
201
202 public static Object instantiate(Constructor ctor, Object... args) {
203 try {
204 return ctor.newInstance(args);
205 } catch (Exception e) {
206 String msg = "Unable to instantiate Permission instance with constructor [" + ctor + "]";
207 throw new InstantiationException(msg, e);
208 }
209 }
210
211 /**
212 * @since 1.0
213 */
214 private static interface ClassLoaderAccessor {
215 Class loadClass(String fqcn);
216 InputStream getResourceStream(String name);
217 }
218
219 /**
220 * @since 1.0
221 */
222 private static abstract class ExceptionIgnoringAccessor implements ClassLoaderAccessor {
223
224 public Class loadClass(String fqcn) {
225 Class clazz = null;
226 ClassLoader cl = getClassLoader();
227 if (cl != null) {
228 try {
229 clazz = cl.loadClass(fqcn);
230 } catch (ClassNotFoundException e) {
231 if (log.isTraceEnabled()) {
232 log.trace("Unable to load clazz named [" + fqcn + "] from class loader [" + cl + "]");
233 }
234 }
235 }
236 return clazz;
237 }
238
239 public InputStream getResourceStream(String name) {
240 InputStream is = null;
241 ClassLoader cl = getClassLoader();
242 if (cl != null) {
243 is = cl.getResourceAsStream(name);
244 }
245 return is;
246 }
247
248 protected final ClassLoader getClassLoader() {
249 try {
250 return doGetClassLoader();
251 } catch (Throwable t) {
252 if (log.isDebugEnabled()) {
253 log.debug("Unable to acquire ClassLoader.", t);
254 }
255 }
256 return null;
257 }
258
259 protected abstract ClassLoader doGetClassLoader() throws Throwable;
260 }
261 }