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    }