    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      https://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.logging;
    019
    020import java.lang.reflect.Constructor;
    021import java.util.Hashtable;
    022
    023import org.apache.commons.logging.impl.NoOpLog;
    024
    025/**
    026 * Factory for creating {@link Log} instances.  Applications should call
    027 * the {@code makeNewLogInstance()} method to instantiate new instances
    028 * of the configured {@link Log} implementation class.
    029 * <p>
    030 * By default, calling {@code getInstance()} will use the following
    031 * algorithm:
    032 * </p>
    033 * <ul>
    034 * <li>If Log4J is available, return an instance of
    035 *     {@code org.apache.commons.logging.impl.Log4JLogger}.</li>
    036 * <li>If JDK 1.4 or later is available, return an instance of
    037 *     {@code org.apache.commons.logging.impl.Jdk14Logger}.</li>
    038 * <li>Otherwise, return an instance of
    039 *     {@code org.apache.commons.logging.impl.NoOpLog}.</li>
    040 * </ul>
    041 * <p>
    042 * You can change the default behavior in one of two ways:
    043 * </p>
    044 * <ul>
    045 * <li>On the startup command line, set the system property
    046 *     {@code org.apache.commons.logging.log} to the name of the
    047 *     {@code org.apache.commons.logging.Log} implementation class
    048 *     you want to use.</li>
    049 * <li>At runtime, call {@code LogSource.setLogImplementation()}.</li>
    050 * </ul>
    051 *
    052 * @deprecated Use {@link LogFactory} instead. The default factory
    053 *  implementation performs exactly the same algorithm as this class did
    054 */
    055@Deprecated
    056public class LogSource {
    057
    058    /**
    059     * Logs.
    060     */
    061    static protected Hashtable<String, Log> logs = new Hashtable<>();
    062
    063    /** Is Log4j available (in the current classpath) */
    064    static protected boolean log4jIsAvailable;
    065
    066    /**
    067     * Is JDK 1.4 logging available, always true.
    068     *
    069     * @deprecated Java 8 is the baseline and includes JUL.
    070     */
    071    @Deprecated
    072    static protected boolean jdk14IsAvailable = true;
    073
    074    /** Constructor for current log class */
    075    static protected Constructor<?> logImplctor;
    076
    077    /**
    078     * An empty immutable {@code String} array.
    079     */
    080    private static final String[] EMPTY_STRING_ARRAY = {};
    081
    082    static {
    083
    084        // Is Log4J Available?
    085        log4jIsAvailable = isClassForName("org.apache.log4j.Logger");
    086
    087        // Set the default Log implementation
    088        String name = null;
    089        try {
    090            name = System.getProperty("org.apache.commons.logging.log");
    091            if (name == null) {
    092                name = System.getProperty("org.apache.commons.logging.Log");
    093            }
    094        } catch (final Throwable ignore) {
    095            // Ignore
    096        }
    097        if (name != null) {
    098            try {
    099                setLogImplementation(name);
    100            } catch (final Throwable t) {
    101                try {
    102                    setLogImplementation("org.apache.commons.logging.impl.NoOpLog");
    103                } catch (final Throwable ignore) {
    104                    // Ignore
    105                }
    106            }
    107        } else {
    108            try {
    109                if (log4jIsAvailable) {
    110                    setLogImplementation("org.apache.commons.logging.impl.Log4JLogger");
    111                } else {
    112                    setLogImplementation("org.apache.commons.logging.impl.Jdk14Logger");
    113                }
    114            } catch (final Throwable t) {
    115                try {
    116                    setLogImplementation("org.apache.commons.logging.impl.NoOpLog");
    117                } catch (final Throwable ignore) {
    118                    // Ignore
    119                }
    120            }
    121        }
    122
    123    }
    124
    125    /**
    126     * Gets a {@code Log} instance by class.
    127     *
    128     * @param clazz a Class.
    129     * @return a {@code Log} instance.
    130     */
    131    static public Log getInstance(final Class<?> clazz) {
    132        return getInstance(clazz.getName());
    133    }
    134
    135    /**
    136     * Gets a {@code Log} instance by class name.
    137     *
    138     * @param name Class name.
    139     * @return a {@code Log} instance.
    140     */
    141    static public Log getInstance(final String name) {
    142        return logs.computeIfAbsent(name, k -> makeNewLogInstance(name));
    143    }
    144
    145    /**
    146     * Returns a {@link String} array containing the names of
    147     * all logs known to me.
    148     *
    149     * @return a {@link String} array containing the names of
    150     * all logs known to me.
    151     */
    152    static public String[] getLogNames() {
    153        return logs.keySet().toArray(EMPTY_STRING_ARRAY);
    154    }
    155
    156    private static boolean isClassForName(final String className) {
    157        try {
    158            Class.forName(className);
    159            return true;
    160        } catch (final Throwable e) {
    161            return false;
    162        }
    163    }
    164
    165    /**
    166     * Create a new {@link Log} implementation, based on the given <em>name</em>.
    167     * <p>
    168     * The specific {@link Log} implementation returned is determined by the
    169     * value of the {@code org.apache.commons.logging.log} property. The value
    170     * of {@code org.apache.commons.logging.log} may be set to the fully specified
    171     * name of a class that implements the {@link Log} interface. This class must
    172     * also have a public constructor that takes a single {@link String} argument
    173     * (containing the <em>name</em> of the {@link Log} to be constructed.
    174     * <p>
    175     * When {@code org.apache.commons.logging.log} is not set, or when no corresponding
    176     * class can be found, this method will return a Log4JLogger if the Log4j Logger
    177     * class is available in the {@link LogSource}'s classpath, or a Jdk14Logger if we
    178     * are on a JDK 1.4 or later system, or NoOpLog if neither of the above conditions is true.
    179     *
    180     * @param name the log name (or category)
    181     * @return a new instance.
    182     */
    183    static public Log makeNewLogInstance(final String name) {
    184        Log log;
    185        try {
    186            final Object[] args = { name };
    187            log = (Log) logImplctor.newInstance(args);
    188        } catch (final Throwable t) {
    189            log = null;
    190        }
    191        if (null == log) {
    192            log = new NoOpLog(name);
    193        }
    194        return log;
    195    }
    196
    197    /**
    198     * Sets the log implementation/log implementation factory by class. The given class must implement {@link Log}, and provide a constructor that takes a single
    199     * {@link String} argument (containing the name of the log).
    200     *
    201     * @param logClass class.
    202     * @throws LinkageError                if there is missing dependency.
    203     * @throws ExceptionInInitializerError unexpected exception has occurred in a static initializer.
    204     * @throws NoSuchMethodException       if a matching method is not found.
    205     * @throws SecurityException           If a security manager, <em>s</em>, is present and the caller's class loader is not the same as or an ancestor of the
    206     *                                     class loader for the current class and invocation of {@link SecurityManager#checkPackageAccess
    207     *                                     s.checkPackageAccess()} denies access to the package of this class.
    208     */
    209    static public void setLogImplementation(final Class<?> logClass)
    210            throws LinkageError, ExceptionInInitializerError, NoSuchMethodException, SecurityException {
    211        logImplctor = logClass.getConstructor(String.class);
    212    }
    213
    214    /**
    215     * Sets the log implementation/log implementation factory by the name of the class. The given class must implement {@link Log}, and provide a constructor
    216     * that takes a single {@link String} argument (containing the name of the log).
    217     *
    218     * @param className class name.
    219     * @throws LinkageError           if there is missing dependency.
    220     * @throws SecurityException      If a security manager, <em>s</em>, is present and the caller's class loader is not the same as or an ancestor of the class
    221     *                                loader for the current class and invocation of {@link SecurityManager#checkPackageAccess s.checkPackageAccess()} denies
    222     *                                access to the package of this class.
    223     */
    224    static public void setLogImplementation(final String className) throws LinkageError, SecurityException {
    225        try {
    226            final Class<?> logClass = Class.forName(className);
    227            logImplctor = logClass.getConstructor(String.class);
    228        } catch (final Throwable t) {
    229            logImplctor = null;
    230        }
    231    }
    232
    233    /** Don't allow others to create instances. */
    234    private LogSource() {
    235    }
    236}