    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 * <ul>
    033 * <li>If Log4J is available, return an instance of
    034 *     {@code org.apache.commons.logging.impl.Log4JLogger}.</li>
    035 * <li>If JDK 1.4 or later is available, return an instance of
    036 *     {@code org.apache.commons.logging.impl.Jdk14Logger}.</li>
    037 * <li>Otherwise, return an instance of
    038 *     {@code org.apache.commons.logging.impl.NoOpLog}.</li>
    039 * </ul>
    040 * <p>
    041 * You can change the default behavior in one of two ways:
    042 * <ul>
    043 * <li>On the startup command line, set the system property
    044 *     {@code org.apache.commons.logging.log} to the name of the
    045 *     {@code org.apache.commons.logging.Log} implementation class
    046 *     you want to use.</li>
    047 * <li>At runtime, call {@code LogSource.setLogImplementation()}.</li>
    048 * </ul>
    049 *
    050 * @deprecated Use {@link LogFactory} instead - The default factory
    051 *  implementation performs exactly the same algorithm as this class did
    052 */
    053@Deprecated
    054public class LogSource {
    055
    056    /**
    057     * Logs.
    058     */
    059    static protected Hashtable<String, Log> logs = new Hashtable<>();
    060
    061    /** Is Log4j available (in the current classpath) */
    062    static protected boolean log4jIsAvailable;
    063
    064    /**
    065     * Is JDK 1.4 logging available, always true.
    066     *
    067     * @deprecated Java 8 is the baseline and includes JUL.
    068     */
    069    @Deprecated
    070    static protected boolean jdk14IsAvailable = true;
    071
    072    /** Constructor for current log class */
    073    static protected Constructor<?> logImplctor;
    074
    075    /**
    076     * An empty immutable {@code String} array.
    077     */
    078    private static final String[] EMPTY_STRING_ARRAY = {};
    079
    080    static {
    081
    082        // Is Log4J Available?
    083        log4jIsAvailable = isClassForName("org.apache.log4j.Logger");
    084
    085        // Set the default Log implementation
    086        String name = null;
    087        try {
    088            name = System.getProperty("org.apache.commons.logging.log");
    089            if (name == null) {
    090                name = System.getProperty("org.apache.commons.logging.Log");
    091            }
    092        } catch (final Throwable ignore) {
    093            // Ignore
    094        }
    095        if (name != null) {
    096            try {
    097                setLogImplementation(name);
    098            } catch (final Throwable t) {
    099                try {
    100                    setLogImplementation("org.apache.commons.logging.impl.NoOpLog");
    101                } catch (final Throwable ignore) {
    102                    // Ignore
    103                }
    104            }
    105        } else {
    106            try {
    107                if (log4jIsAvailable) {
    108                    setLogImplementation("org.apache.commons.logging.impl.Log4JLogger");
    109                } else {
    110                    setLogImplementation("org.apache.commons.logging.impl.Jdk14Logger");
    111                }
    112            } catch (final Throwable t) {
    113                try {
    114                    setLogImplementation("org.apache.commons.logging.impl.NoOpLog");
    115                } catch (final Throwable ignore) {
    116                    // Ignore
    117                }
    118            }
    119        }
    120
    121    }
    122
    123    /**
    124     * Gets a {@code Log} instance by class.
    125     *
    126     * @param clazz a Class.
    127     * @return a {@code Log} instance.
    128     */
    129    static public Log getInstance(final Class<?> clazz) {
    130        return getInstance(clazz.getName());
    131    }
    132
    133    /**
    134     * Gets a {@code Log} instance by class name.
    135     *
    136     * @param name Class name.
    137     * @return a {@code Log} instance.
    138     */
    139    static public Log getInstance(final String name) {
    140        return logs.computeIfAbsent(name, k -> makeNewLogInstance(name));
    141    }
    142
    143    /**
    144     * Returns a {@link String} array containing the names of
    145     * all logs known to me.
    146     *
    147     * @return a {@link String} array containing the names of
    148     * all logs known to me.
    149     */
    150    static public String[] getLogNames() {
    151        return logs.keySet().toArray(EMPTY_STRING_ARRAY);
    152    }
    153
    154    private static boolean isClassForName(final String className) {
    155        try {
    156            Class.forName(className);
    157            return true;
    158        } catch (final Throwable e) {
    159            return false;
    160        }
    161    }
    162
    163    /**
    164     * Create a new {@link Log} implementation, based on the given <em>name</em>.
    165     * <p>
    166     * The specific {@link Log} implementation returned is determined by the
    167     * value of the {@code org.apache.commons.logging.log} property. The value
    168     * of {@code org.apache.commons.logging.log} may be set to the fully specified
    169     * name of a class that implements the {@link Log} interface. This class must
    170     * also have a public constructor that takes a single {@link String} argument
    171     * (containing the <em>name</em> of the {@link Log} to be constructed.
    172     * <p>
    173     * When {@code org.apache.commons.logging.log} is not set, or when no corresponding
    174     * class can be found, this method will return a Log4JLogger if the Log4j Logger
    175     * class is available in the {@link LogSource}'s classpath, or a Jdk14Logger if we
    176     * are on a JDK 1.4 or later system, or NoOpLog if neither of the above conditions is true.
    177     *
    178     * @param name the log name (or category)
    179     * @return a new instance.
    180     */
    181    static public Log makeNewLogInstance(final String name) {
    182        Log log;
    183        try {
    184            final Object[] args = { name };
    185            log = (Log) logImplctor.newInstance(args);
    186        } catch (final Throwable t) {
    187            log = null;
    188        }
    189        if (null == log) {
    190            log = new NoOpLog(name);
    191        }
    192        return log;
    193    }
    194
    195    /**
    196     * Sets the log implementation/log implementation factory by class. The given class must implement {@link Log}, and provide a constructor that takes a single
    197     * {@link String} argument (containing the name of the log).
    198     *
    199     * @param logClass class.
    200     * @throws LinkageError                if there is missing dependency.
    201     * @throws ExceptionInInitializerError unexpected exception has occurred in a static initializer.
    202     * @throws NoSuchMethodException       if a matching method is not found.
    203     * @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
    204     *                                     class loader for the current class and invocation of {@link SecurityManager#checkPackageAccess
    205     *                                     s.checkPackageAccess()} denies access to the package of this class.
    206     */
    207    static public void setLogImplementation(final Class<?> logClass)
    208            throws LinkageError, ExceptionInInitializerError, NoSuchMethodException, SecurityException {
    209        logImplctor = logClass.getConstructor(String.class);
    210    }
    211
    212    /**
    213     * Sets the log implementation/log implementation factory by the name of the class. The given class must implement {@link Log}, and provide a constructor
    214     * that takes a single {@link String} argument (containing the name of the log).
    215     *
    216     * @param className class name.
    217     * @throws LinkageError           if there is missing dependency.
    218     * @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
    219     *                                loader for the current class and invocation of {@link SecurityManager#checkPackageAccess s.checkPackageAccess()} denies
    220     *                                access to the package of this class.
    221     */
    222    static public void setLogImplementation(final String className) throws LinkageError, SecurityException {
    223        try {
    224            final Class<?> logClass = Class.forName(className);
    225            logImplctor = logClass.getConstructor(String.class);
    226        } catch (final Throwable t) {
    227            logImplctor = null;
    228        }
    229    }
    230
    231    /** Don't allow others to create instances. */
    232    private LogSource() {
    233    }
    234}