001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.core.util;
015
016import java.io.IOException;
017import java.net.URL;
018import java.security.AccessController;
019import java.security.PrivilegedAction;
020import java.util.Enumeration;
021import java.util.Set;
022import java.util.HashSet;
023
024import ch.qos.logback.core.Context;
025
026/**
027 * Load resources (or images) from various sources.
028 *
029 * @author Ceki Gülcü
030 */
031public class Loader {
032    static final String TSTR = "Caught Exception while in Loader.getResource. This may be innocuous.";
033
034    private static boolean ignoreTCL = false;
035    public static final String IGNORE_TCL_PROPERTY_NAME = "logback.ignoreTCL";
036    private static boolean HAS_GET_CLASS_LOADER_PERMISSION = false;
037
038    static {
039        String ignoreTCLProp = OptionHelper.getSystemProperty(IGNORE_TCL_PROPERTY_NAME, null);
040
041        if (ignoreTCLProp != null) {
042            ignoreTCL = OptionHelper.toBoolean(ignoreTCLProp, true);
043        }
044
045        HAS_GET_CLASS_LOADER_PERMISSION = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
046            public Boolean run() {
047                try {
048                    AccessController.checkPermission(new RuntimePermission("getClassLoader"));
049                    return true;
050                } catch (SecurityException e) {
051                    // Using SecurityException instead of AccessControlException.
052                    // See bug LOGBACK-760.
053                    return false;
054                }
055            }
056        });
057    }
058
059    /**
060     * This method is used to sanitize the <code>cl</code> argument in case it is null.
061     *
062     * @param cl a class loader, may be null
063     * @return the system class loader if the <code>cl</code> argument is null, return <code>cl</code> otherwise.
064     *
065     * @since 1.4.12
066     */
067    public static ClassLoader systemClassloaderIfNull(ClassLoader cl) {
068        if(cl == null)
069            return ClassLoader.getSystemClassLoader();
070        else
071            return cl;
072    }
073
074    /**
075     * Compute the number of occurrences a resource can be found by a class loader.
076     *
077     * @param resource
078     * @param classLoader
079     * @return
080     * @throws IOException
081     */
082    public static Set<URL> getResources(String resource, ClassLoader classLoader) throws IOException {
083        // See LBCLASSIC-159
084        Set<URL> urlSet = new HashSet<URL>();
085        Enumeration<URL> urlEnum = classLoader.getResources(resource);
086        while (urlEnum.hasMoreElements()) {
087            URL url = urlEnum.nextElement();
088            urlSet.add(url);
089        }
090        return urlSet;
091    }
092
093    /**
094     * Search for a resource using the classloader passed as parameter.
095     *
096     * @param resource    the resource name to look for
097     * @param classLoader the classloader used for the search
098     */
099    public static URL getResource(String resource, ClassLoader classLoader) {
100        try {
101            return classLoader.getResource(resource);
102        } catch (Throwable t) {
103            return null;
104        }
105    }
106
107    /**
108     * Attempt to find a resource by using the classloader that loaded this class,
109     * namely Loader.class.
110     *
111     * @param resource
112     * @return
113     */
114    public static URL getResourceBySelfClassLoader(String resource) {
115        return getResource(resource, getClassLoaderOfClass(Loader.class));
116    }
117
118    // private static URL getResourceByTCL(String resource) {
119    // return getResource(resource, getTCL());
120    // }
121
122    /**
123     * Get the Thread Context Loader which is a JDK 1.2 feature. If we are running
124     * under JDK 1.1 or anything else goes wrong the method returns {@code null}.
125     */
126    public static ClassLoader getTCL() {
127        return Thread.currentThread().getContextClassLoader();
128    }
129
130    public static Class<?> loadClass(String clazz, Context context) throws ClassNotFoundException {
131        ClassLoader cl = getClassLoaderOfObject(context);
132        return cl.loadClass(clazz);
133    }
134
135    /**
136     * Get the class loader of the object passed as argument. Return the system
137     * class loader if appropriate.
138     *
139     * @param o
140     * @return
141     */
142    public static ClassLoader getClassLoaderOfObject(Object o) {
143        if (o == null) {
144            throw new NullPointerException("Argument cannot be null");
145        }
146        return getClassLoaderOfClass(o.getClass());
147    }
148
149    /**
150     * Returns the class loader of clazz in an access privileged section.
151     *
152     * @param clazz
153     * @return
154     */
155    public static ClassLoader getClassLoaderAsPrivileged(final Class<?> clazz) {
156        if (!HAS_GET_CLASS_LOADER_PERMISSION)
157            return null;
158        else
159            return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
160                public ClassLoader run() {
161                    return clazz.getClassLoader();
162                }
163            });
164    }
165
166    /**
167     * Return the class loader which loaded the class passed as argument. Return the
168     * system class loader if the class loader of 'clazz' argument is null.
169     *
170     * @param clazz
171     * @return
172     */
173    public static ClassLoader getClassLoaderOfClass(final Class<?> clazz) {
174        ClassLoader cl = clazz.getClassLoader();
175        return systemClassloaderIfNull(cl);
176    }
177
178    /**
179     * If running under JDK 1.2 load the specified class using the
180     * <code>Thread</code> <code>contextClassLoader</code> if that fails try
181     * Class.forname. Under JDK 1.1 only Class.forName is used.
182     */
183    public static Class<?> loadClass(String clazz) throws ClassNotFoundException {
184        // Just call Class.forName(clazz) if we are running under JDK 1.1
185        // or if we are instructed to ignore the TCL.
186        if (ignoreTCL) {
187            return Class.forName(clazz);
188        } else {
189            try {
190                return getTCL().loadClass(clazz);
191            } catch (Throwable e) {
192                // we reached here because tcl was null or because of a
193                // security exception, or because clazz could not be loaded...
194                // In any case we now try one more time
195                return Class.forName(clazz);
196            }
197        }
198    }
199}