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     * @since 0.1
033     */
034    public class ClassUtils {
035    
036        //TODO - complete JavaDoc
037    
038        /**
039         * Private internal log instance.
040         */
041        private static final Logger log = LoggerFactory.getLogger(ClassUtils.class);
042    
043        /**
044         * @since 1.0
045         */
046        private static final ClassLoaderAccessor THREAD_CL_ACCESSOR = new ExceptionIgnoringAccessor() {
047            @Override
048            protected ClassLoader doGetClassLoader() throws Throwable {
049                return Thread.currentThread().getContextClassLoader();
050            }
051        };
052    
053        /**
054         * @since 1.0
055         */
056        private static final ClassLoaderAccessor CLASS_CL_ACCESSOR = new ExceptionIgnoringAccessor() {
057            @Override
058            protected ClassLoader doGetClassLoader() throws Throwable {
059                return ClassUtils.class.getClassLoader();
060            }
061        };
062    
063        /**
064         * @since 1.0
065         */
066        private static final ClassLoaderAccessor SYSTEM_CL_ACCESSOR = new ExceptionIgnoringAccessor() {
067            @Override
068            protected ClassLoader doGetClassLoader() throws Throwable {
069                return ClassLoader.getSystemClassLoader();
070            }
071        };
072    
073        /**
074         * Returns the specified resource by checking the current thread's
075         * {@link Thread#getContextClassLoader() context class loader}, then the
076         * current ClassLoader (<code>ClassUtils.class.getClassLoader()</code>), then the system/application
077         * ClassLoader (<code>ClassLoader.getSystemClassLoader()</code>, in that order, using
078         * {@link ClassLoader#getResourceAsStream(String) getResourceAsStream(name)}.
079         *
080         * @param name the name of the resource to acquire from the classloader(s).
081         * @return the InputStream of the resource found, or <code>null</code> if the resource cannot be found from any
082         *         of the three mentioned ClassLoaders.
083         * @since 0.9
084         */
085        public static InputStream getResourceAsStream(String name) {
086    
087            InputStream is = THREAD_CL_ACCESSOR.getResourceStream(name);
088    
089            if (is == null) {
090                if (log.isTraceEnabled()) {
091                    log.trace("Resource [" + name + "] was not found via the thread context ClassLoader.  Trying the " +
092                            "current ClassLoader...");
093                }
094                is = CLASS_CL_ACCESSOR.getResourceStream(name);
095            }
096    
097            if (is == null) {
098                if (log.isTraceEnabled()) {
099                    log.trace("Resource [" + name + "] was not found via the current class loader.  Trying the " +
100                            "system/application ClassLoader...");
101                }
102                is = SYSTEM_CL_ACCESSOR.getResourceStream(name);
103            }
104    
105            if (is == null && log.isTraceEnabled()) {
106                log.trace("Resource [" + name + "] was not found via the thread context, current, or " +
107                        "system/application ClassLoaders.  All heuristics have been exhausted.  Returning null.");
108            }
109    
110            return is;
111        }
112    
113        /**
114         * Attempts to load the specified class name from the current thread's
115         * {@link Thread#getContextClassLoader() context class loader}, then the
116         * current ClassLoader (<code>ClassUtils.class.getClassLoader()</code>), then the system/application
117         * ClassLoader (<code>ClassLoader.getSystemClassLoader()</code>, in that order.  If any of them cannot locate
118         * the specified class, an <code>UnknownClassException</code> is thrown (our RuntimeException equivalent of
119         * the JRE's <code>ClassNotFoundException</code>.
120         *
121         * @param fqcn the fully qualified class name to load
122         * @return the located class
123         * @throws UnknownClassException if the class cannot be found.
124         */
125        public static Class forName(String fqcn) throws UnknownClassException {
126    
127            Class clazz = THREAD_CL_ACCESSOR.loadClass(fqcn);
128    
129            if (clazz == null) {
130                if (log.isTraceEnabled()) {
131                    log.trace("Unable to load class named [" + fqcn +
132                            "] from the thread context ClassLoader.  Trying the current ClassLoader...");
133                }
134                clazz = CLASS_CL_ACCESSOR.loadClass(fqcn);
135            }
136    
137            if (clazz == null) {
138                if (log.isTraceEnabled()) {
139                    log.trace("Unable to load class named [" + fqcn + "] from the current ClassLoader.  " +
140                            "Trying the system/application ClassLoader...");
141                }
142                clazz = SYSTEM_CL_ACCESSOR.loadClass(fqcn);
143            }
144    
145            if (clazz == null) {
146                String msg = "Unable to load class named [" + fqcn + "] from the thread context, current, or " +
147                        "system/application ClassLoaders.  All heuristics have been exhausted.  Class could not be found.";
148                throw new UnknownClassException(msg);
149            }
150    
151            return clazz;
152        }
153    
154        public static boolean isAvailable(String fullyQualifiedClassName) {
155            try {
156                forName(fullyQualifiedClassName);
157                return true;
158            } catch (UnknownClassException e) {
159                return false;
160            }
161        }
162    
163        public static Object newInstance(String fqcn) {
164            return newInstance(forName(fqcn));
165        }
166    
167        public static Object newInstance(String fqcn, Object... args) {
168            return newInstance(forName(fqcn), args);
169        }
170    
171        public static Object newInstance(Class clazz) {
172            if (clazz == null) {
173                String msg = "Class method parameter cannot be null.";
174                throw new IllegalArgumentException(msg);
175            }
176            try {
177                return clazz.newInstance();
178            } catch (Exception e) {
179                throw new InstantiationException("Unable to instantiate class [" + clazz.getName() + "]", e);
180            }
181        }
182    
183        public static Object newInstance(Class clazz, Object... args) {
184            Class[] argTypes = new Class[args.length];
185            for (int i = 0; i < args.length; i++) {
186                argTypes[i] = args[i].getClass();
187            }
188            Constructor ctor = getConstructor(clazz, argTypes);
189            return instantiate(ctor, args);
190        }
191    
192        public static Constructor getConstructor(Class clazz, Class... argTypes) {
193            try {
194                return clazz.getConstructor(argTypes);
195            } catch (NoSuchMethodException e) {
196                throw new IllegalStateException(e);
197            }
198    
199        }
200    
201        public static Object instantiate(Constructor ctor, Object... args) {
202            try {
203                return ctor.newInstance(args);
204            } catch (Exception e) {
205                String msg = "Unable to instantiate Permission instance with constructor [" + ctor + "]";
206                throw new InstantiationException(msg, e);
207            }
208        }
209    
210        /**
211         * @since 1.0
212         */
213        private static interface ClassLoaderAccessor {
214            Class loadClass(String fqcn);
215            InputStream getResourceStream(String name);
216        }
217    
218        /**
219         * @since 1.0
220         */
221        private static abstract class ExceptionIgnoringAccessor implements ClassLoaderAccessor {
222    
223            public Class loadClass(String fqcn) {
224                Class clazz = null;
225                ClassLoader cl = getClassLoader();
226                if (cl != null) {
227                    try {
228                        clazz = cl.loadClass(fqcn);
229                    } catch (ClassNotFoundException e) {
230                        if (log.isTraceEnabled()) {
231                            log.trace("Unable to load clazz named [" + fqcn + "] from class loader [" + cl + "]");
232                        }
233                    }
234                }
235                return clazz;
236            }
237    
238            public InputStream getResourceStream(String name) {
239                InputStream is = null;
240                ClassLoader cl = getClassLoader();
241                if (cl != null) {
242                    is = cl.getResourceAsStream(name);
243                }
244                return is;
245            }
246    
247            protected final ClassLoader getClassLoader() {
248                try {
249                    return doGetClassLoader();
250                } catch (Throwable t) {
251                    if (log.isDebugEnabled()) {
252                        log.debug("Unable to acquire ClassLoader.", t);
253                    }
254                }
255                return null;
256            }
257    
258            protected abstract ClassLoader doGetClassLoader() throws Throwable;
259        }
260    }