001/*
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2026, 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 v2.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.util.Enumeration;
019import java.util.Set;
020import java.util.HashSet;
021
022import ch.qos.logback.core.Context;
023
024/**
025 * Load resources (or images) from various sources.
026 *
027 * @author Ceki Gülcü
028 */
029public class Loader {
030    static final String TSTR = "Caught Exception while in Loader.getResource. This may be innocuous.";
031
032    private static boolean ignoreTCL = false;
033    public static final String IGNORE_TCL_PROPERTY_NAME = "logback.ignoreTCL";
034
035    static {
036        String ignoreTCLProp = OptionHelper.getSystemProperty(IGNORE_TCL_PROPERTY_NAME, null);
037
038        if (ignoreTCLProp != null) {
039            ignoreTCL = OptionHelper.toBoolean(ignoreTCLProp, true);
040        }
041    }
042
043    /**
044     * This method is used to sanitize the <code>cl</code> argument in case it is null.
045     *
046     * @param cl a class loader, may be null
047     * @return the system class loader if the <code>cl</code> argument is null, return <code>cl</code> otherwise.
048     *
049     * @since 1.4.12
050     */
051    public static ClassLoader systemClassloaderIfNull(ClassLoader cl) {
052        if(cl == null)
053            return ClassLoader.getSystemClassLoader();
054        else
055            return cl;
056    }
057
058    /**
059     * Compute the number of occurrences a resource can be found by a class loader.
060     *
061     * @param resource
062     * @param classLoader
063     * @return
064     * @throws IOException
065     */
066    public static Set<URL> getResources(String resource, ClassLoader classLoader) throws IOException {
067        // See LBCLASSIC-159
068        Set<URL> urlSet = new HashSet<URL>();
069        Enumeration<URL> urlEnum = classLoader.getResources(resource);
070        while (urlEnum.hasMoreElements()) {
071            URL url = urlEnum.nextElement();
072            urlSet.add(url);
073        }
074        return urlSet;
075    }
076
077    /**
078     * Search for a resource using the classloader passed as parameter.
079     *
080     * @param resource    the resource name to look for
081     * @param classLoader the classloader used for the search
082     */
083    public static URL getResource(String resource, ClassLoader classLoader) {
084        try {
085            return classLoader.getResource(resource);
086        } catch (Throwable t) {
087            return null;
088        }
089    }
090
091    /**
092     * Attempt to find a resource by using the classloader that loaded this class,
093     * namely Loader.class.
094     *
095     * @param resource
096     * @return
097     */
098    public static URL getResourceBySelfClassLoader(String resource) {
099        return getResource(resource, getClassLoaderOfClass(Loader.class));
100    }
101
102    // private static URL getResourceByTCL(String resource) {
103    // return getResource(resource, getTCL());
104    // }
105
106    /**
107     * Get the Thread Context Loader which is a JDK 1.2 feature. If we are running
108     * under JDK 1.1 or anything else goes wrong the method returns {@code null}.
109     */
110    public static ClassLoader getTCL() {
111        return Thread.currentThread().getContextClassLoader();
112    }
113
114    public static Class<?> loadClass(String clazz, Context context) throws ClassNotFoundException {
115        ClassLoader cl = getClassLoaderOfObject(context);
116        return cl.loadClass(clazz);
117    }
118
119    /**
120     * Get the class loader of the object passed as argument. Return the system
121     * class loader if appropriate.
122     *
123     * @param o
124     * @return
125     */
126    public static ClassLoader getClassLoaderOfObject(Object o) {
127        if (o == null) {
128            throw new NullPointerException("Argument cannot be null");
129        }
130        return getClassLoaderOfClass(o.getClass());
131    }
132
133    /**
134     * Check whether a given class is loadable by the class loader that loaded the context parameter.
135     *
136     * @param className the class to check for availability
137     * @param context the context object used to find the class loader
138     * @return true if className is available, false otherwise
139     * @since 1.5.18
140     */
141    public static boolean isClassLoadable(String className, Context context) {
142        try {
143            Class<?> aClass = Loader.loadClass(className, context);
144            return true;
145        } catch (ClassNotFoundException e) {
146            return false;
147        }
148    }
149
150
151    /**
152     * Return the class loader which loaded the class passed as argument. Return the
153     * system class loader if the class loader of 'clazz' argument is null.
154     *
155     * @param clazz
156     * @return
157     */
158    public static ClassLoader getClassLoaderOfClass(final Class<?> clazz) {
159        ClassLoader cl = clazz.getClassLoader();
160        return systemClassloaderIfNull(cl);
161    }
162
163    /**
164     * If running under JDK 1.2 load the specified class using the
165     * <code>Thread</code> <code>contextClassLoader</code> if that fails try
166     * Class.forname. Under JDK 1.1 only Class.forName is used.
167     */
168    public static Class<?> loadClass(String clazz) throws ClassNotFoundException {
169        // Just call Class.forName(clazz) if we are running under JDK 1.1
170        // or if we are instructed to ignore the TCL.
171        if (ignoreTCL) {
172            return Class.forName(clazz);
173        } else {
174            try {
175                return getTCL().loadClass(clazz);
176            } catch (Throwable e) {
177                // we reached here because tcl was null or because of a
178                // security exception, or because clazz could not be loaded...
179                // In any case we now try one more time
180                return Class.forName(clazz);
181            }
182        }
183    }
184}