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}