View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.logging;
19  
20  import java.lang.reflect.Constructor;
21  import java.util.Hashtable;
22  
23  import org.apache.commons.logging.impl.NoOpLog;
24  
25  /**
26   * Factory for creating {@link Log} instances.  Applications should call
27   * the {@code makeNewLogInstance()} method to instantiate new instances
28   * of the configured {@link Log} implementation class.
29   * <p>
30   * By default, calling {@code getInstance()} will use the following
31   * algorithm:
32   * </p>
33   * <ul>
34   * <li>If Log4J is available, return an instance of
35   *     {@code org.apache.commons.logging.impl.Log4JLogger}.</li>
36   * <li>If JDK 1.4 or later is available, return an instance of
37   *     {@code org.apache.commons.logging.impl.Jdk14Logger}.</li>
38   * <li>Otherwise, return an instance of
39   *     {@code org.apache.commons.logging.impl.NoOpLog}.</li>
40   * </ul>
41   * <p>
42   * You can change the default behavior in one of two ways:
43   * </p>
44   * <ul>
45   * <li>On the startup command line, set the system property
46   *     {@code org.apache.commons.logging.log} to the name of the
47   *     {@code org.apache.commons.logging.Log} implementation class
48   *     you want to use.</li>
49   * <li>At runtime, call {@code LogSource.setLogImplementation()}.</li>
50   * </ul>
51   *
52   * @deprecated Use {@link LogFactory} instead. The default factory
53   *  implementation performs exactly the same algorithm as this class did
54   */
55  @Deprecated
56  public class LogSource {
57  
58      /**
59       * Logs.
60       */
61      static protected Hashtable<String, Log> logs = new Hashtable<>();
62  
63      /** Is Log4j available (in the current classpath) */
64      static protected boolean log4jIsAvailable;
65  
66      /**
67       * Is JDK 1.4 logging available, always true.
68       *
69       * @deprecated Java 8 is the baseline and includes JUL.
70       */
71      @Deprecated
72      static protected boolean jdk14IsAvailable = true;
73  
74      /** Constructor for current log class */
75      static protected Constructor<?> logImplctor;
76  
77      /**
78       * An empty immutable {@code String} array.
79       */
80      private static final String[] EMPTY_STRING_ARRAY = {};
81  
82      static {
83  
84          // Is Log4J Available?
85          log4jIsAvailable = isClassForName("org.apache.log4j.Logger");
86  
87          // Set the default Log implementation
88          String name = null;
89          try {
90              name = System.getProperty("org.apache.commons.logging.log");
91              if (name == null) {
92                  name = System.getProperty("org.apache.commons.logging.Log");
93              }
94          } catch (final Throwable ignore) {
95              // Ignore
96          }
97          if (name != null) {
98              try {
99                  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 }