1   /*
2    * Copyright (c) 2004-2008 QOS.ch
3    * All rights reserved.
4    * 
5    * Permission is hereby granted, free  of charge, to any person obtaining
6    * a  copy  of this  software  and  associated  documentation files  (the
7    * "Software"), to  deal in  the Software without  restriction, including
8    * without limitation  the rights to  use, copy, modify,  merge, publish,
9    * distribute,  sublicense, and/or sell  copies of  the Software,  and to
10   * permit persons to whom the Software  is furnished to do so, subject to
11   * the following conditions:
12   * 
13   * The  above  copyright  notice  and  this permission  notice  shall  be
14   * included in all copies or substantial portions of the Software.
15   * 
16   * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
17   * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
18   * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
19   * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20   * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21   * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
22   * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23   */
24  
25  package org.slf4j;
26  
27  import java.util.Arrays;
28  import java.util.List;
29  
30  import org.slf4j.helpers.SubstituteLoggerFactory;
31  import org.slf4j.helpers.Util;
32  import org.slf4j.impl.StaticLoggerBinder;
33  
34  /**
35   * The <code>LoggerFactory</code> is a utility class producing Loggers for
36   * various logging APIs, most notably for log4j, logback and JDK 1.4 logging.
37   * Other implementations such as {@link org.slf4j.impl.NOPLogger NOPLogger} and
38   * {@link org.slf4j.impl.SimpleLogger SimpleLogger} are also supported.
39   * 
40   * <p>
41   * <code>LoggerFactory</code> is essentially a wrapper around an
42   * {@link ILoggerFactory} instance bound with <code>LoggerFactory</code> at
43   * compile time.
44   * 
45   * <p>
46   * Please note that all methods in <code>LoggerFactory</code> are static.
47   * 
48   * @author Ceki G&uuml;lc&uuml;
49   */
50  public final class LoggerFactory {
51  
52    static final String NO_STATICLOGGERBINDER_URL = "http://www.slf4j.org/codes.html#StaticLoggerBinder";
53    static final String NULL_LF_URL = "http://www.slf4j.org/codes.html#null_LF";
54    static final String VERSION_MISMATCH = "http://www.slf4j.org/codes.html#version_mismatch";
55    static final String SUBSTITUTE_LOGGER_URL = "http://www.slf4j.org/codes.html#substituteLogger";
56  
57    static final String UNSUCCESSFUL_INIT_URL = "http://www.slf4j.org/codes.html#unsuccessfulInit";
58    static final String UNSUCCESSFUL_INIT_MSG = "org.slf4j.LoggerFactory could not be successfully initialized. See also "
59        + UNSUCCESSFUL_INIT_URL;
60  
61    static final int UNINITIALIZED = 0;
62    static final int ONGOING_INITILIZATION = 1;
63    static final int FAILED_INITILIZATION = 2;
64    static final int SUCCESSFUL_INITILIZATION = 3;
65    
66    static final int GET_SINGLETON_INEXISTENT = 1;
67    static final int GET_SINGLETON_EXISTS = 2;
68    
69    
70    
71    static int INITIALIZATION_STATE = UNINITIALIZED;
72    static int GET_SINGLETON_METHOD = UNINITIALIZED;
73    static SubstituteLoggerFactory TEMP_FACTORY = new SubstituteLoggerFactory();
74  
75    /**
76     * It is our responsibility to track version changes and manage the
77     * compatibility list.
78     * 
79     * <p>
80     */
81    static private final String[] API_COMPATIBILITY_LIST = new String[] {
82        "1.5.5", "1.5.6" };
83  
84    // private constructor prevents instantiation
85    private LoggerFactory() {
86    }
87  
88    /**
89     * Force LoggerFactory to consider itself uninitialized.
90     * 
91     * <p>
92     * This method is intended to be called by classes (in the same package) for
93     * testing purposes. This method is internal. It can be modified, renamed or
94     * removed at any time without notice. 
95     * 
96     * You are strongly discouraged from calling this method in production code.
97     */
98    static void reset() {
99      INITIALIZATION_STATE = UNINITIALIZED;
100     GET_SINGLETON_METHOD = UNINITIALIZED;
101     TEMP_FACTORY = new SubstituteLoggerFactory();
102   }
103 
104   private final static void performInitialization() {
105     bind();
106     versionSanityCheck();
107   }
108 
109   private final static void bind() {
110     try {
111       // the next line does the binding
112       getSingleton();
113       INITIALIZATION_STATE = SUCCESSFUL_INITILIZATION;
114       emitSubstitureLoggerWarning();
115     } catch (NoClassDefFoundError ncde) {
116       INITIALIZATION_STATE = FAILED_INITILIZATION;
117       String msg = ncde.getMessage();
118       if (msg != null && msg.indexOf("org/slf4j/impl/StaticLoggerBinder") != -1) {
119         Util
120             .reportFailure("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
121         Util.reportFailure("See " + NO_STATICLOGGERBINDER_URL
122             + " for further details.");
123 
124       }
125       throw ncde;
126     } catch (Exception e) {
127       INITIALIZATION_STATE = FAILED_INITILIZATION;
128       // we should never get here
129       Util.reportFailure("Failed to instantiate logger ["
130           + getSingleton().getLoggerFactoryClassStr() + "]", e);
131     }
132   }
133 
134   private final static void emitSubstitureLoggerWarning() {
135     List loggerNameList = TEMP_FACTORY.getLoggerNameList();
136     if (loggerNameList.size() == 0) {
137       return;
138     }
139     Util
140         .reportFailure("The following loggers will not work becasue they were created");
141     Util
142         .reportFailure("during the default configuration phase of the underlying logging system.");
143     Util.reportFailure("See also " + SUBSTITUTE_LOGGER_URL);
144     for (int i = 0; i < loggerNameList.size(); i++) {
145       String loggerName = (String) loggerNameList.get(i);
146       Util.reportFailure(loggerName);
147     }
148   }
149 
150   private final static void versionSanityCheck() {
151     try {
152       String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
153 
154       boolean match = false;
155       for (int i = 0; i < API_COMPATIBILITY_LIST.length; i++) {
156         if (API_COMPATIBILITY_LIST[i].equals(requested)) {
157           match = true;
158         }
159       }
160       if (!match) {
161         Util.reportFailure("The requested version " + requested
162             + " by your slf4j binding is not compatible with "
163             + Arrays.toString(API_COMPATIBILITY_LIST));
164         Util.reportFailure("See " + VERSION_MISMATCH + " for further details.");
165       }
166     } catch (java.lang.NoSuchFieldError nsfe) {
167       // given our large user base and SLF4J's commitment to backward
168       // compatibility, we cannot cry here. Only for implementations
169       // which willingly declare a REQUESTED_API_VERSION field do we
170       // emit compatibility warnings.
171     } catch (Throwable e) {
172       // we should never reach here
173       Util.reportFailure(
174           "Unexpected problem occured during version sanity check", e);
175     }
176   }
177 
178   
179   private final static StaticLoggerBinder getSingleton() {
180     if(GET_SINGLETON_METHOD == GET_SINGLETON_INEXISTENT) {
181       return StaticLoggerBinder.SINGLETON;
182     }
183     
184     if(GET_SINGLETON_METHOD == GET_SINGLETON_EXISTS) {
185       return StaticLoggerBinder.getSingleton();
186     }
187     
188     try  {
189       StaticLoggerBinder singleton = StaticLoggerBinder.getSingleton();
190       GET_SINGLETON_METHOD = GET_SINGLETON_EXISTS;
191       return singleton;
192     } catch(NoSuchMethodError nsme) {
193       GET_SINGLETON_METHOD = GET_SINGLETON_INEXISTENT;
194       return StaticLoggerBinder.SINGLETON;
195     }
196     
197     
198   }
199   /**
200    * Return a logger named according to the name parameter using the statically
201    * bound {@link ILoggerFactory} instance.
202    * 
203    * @param name
204    *                The name of the logger.
205    * @return logger
206    */
207   public static Logger getLogger(String name) {
208     ILoggerFactory iLoggerFactory = getILoggerFactory();
209     return iLoggerFactory.getLogger(name);
210   }
211 
212   /**
213    * Return a logger named corresponding to the class passed as parameter, using
214    * the statically bound {@link ILoggerFactory} instance.
215    * 
216    * @param clazz
217    *                the returned logger will be named after clazz
218    * @return logger
219    */
220   public static Logger getLogger(Class clazz) {
221     return getLogger(clazz.getName());
222   }
223 
224   /**
225    * Return the {@link ILoggerFactory} instance in use.
226    * 
227    * <p>
228    * ILoggerFactory instance is bound with this class at compile time.
229    * 
230    * @return the ILoggerFactory instance in use
231    */
232   public static ILoggerFactory getILoggerFactory() {
233     if (INITIALIZATION_STATE == UNINITIALIZED) {
234       INITIALIZATION_STATE = ONGOING_INITILIZATION;
235       performInitialization();
236 
237     }
238     switch (INITIALIZATION_STATE) {
239     case SUCCESSFUL_INITILIZATION:
240       return getSingleton().getLoggerFactory();
241     case FAILED_INITILIZATION:
242       throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
243     case ONGOING_INITILIZATION:
244       // support re-entrant behavior.
245       // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106
246       return TEMP_FACTORY;
247     }
248     throw new IllegalStateException("Unreachable code");
249   }
250 }