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    *      http://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   * <ul>
33   * <li>If Log4J is available, return an instance of
34   *     {@code org.apache.commons.logging.impl.Log4JLogger}.</li>
35   * <li>If JDK 1.4 or later is available, return an instance of
36   *     {@code org.apache.commons.logging.impl.Jdk14Logger}.</li>
37   * <li>Otherwise, return an instance of
38   *     {@code org.apache.commons.logging.impl.NoOpLog}.</li>
39   * </ul>
40   * <p>
41   * You can change the default behavior in one of two ways:
42   * <ul>
43   * <li>On the startup command line, set the system property
44   *     {@code org.apache.commons.logging.log} to the name of the
45   *     {@code org.apache.commons.logging.Log} implementation class
46   *     you want to use.</li>
47   * <li>At runtime, call {@code LogSource.setLogImplementation()}.</li>
48   * </ul>
49   *
50   * @deprecated Use {@link LogFactory} instead - The default factory
51   *  implementation performs exactly the same algorithm as this class did
52   */
53  @Deprecated
54  public class LogSource {
55  
56      /**
57       * Logs.
58       */
59      static protected Hashtable<String, Log> logs = new Hashtable<>();
60  
61      /** Is Log4j available (in the current classpath) */
62      static protected boolean log4jIsAvailable;
63  
64      /**
65       * Is JDK 1.4 logging available, always true.
66       *
67       * @deprecated Java 8 is the baseline and includes JUL.
68       */
69      @Deprecated
70      static protected boolean jdk14IsAvailable = true;
71  
72      /** Constructor for current log class */
73      static protected Constructor<?> logImplctor;
74  
75      /**
76       * An empty immutable {@code String} array.
77       */
78      private static final String[] EMPTY_STRING_ARRAY = {};
79  
80      static {
81  
82          // Is Log4J Available?
83          log4jIsAvailable = isClassForName("org.apache.log4j.Logger");
84  
85          // Set the default Log implementation
86          String name = null;
87          try {
88              name = System.getProperty("org.apache.commons.logging.log");
89              if (name == null) {
90                  name = System.getProperty("org.apache.commons.logging.Log");
91              }
92          } catch (final Throwable ignore) {
93              // Ignore
94          }
95          if (name != null) {
96              try {
97                  setLogImplementation(name);
98              } catch (final Throwable t) {
99                  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 }