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 *      http://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}