1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.logging;
18
19
20 import java.io.BufferedReader;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.InputStreamReader;
24 import java.lang.reflect.InvocationTargetException;
25 import java.lang.reflect.Method;
26 import java.security.AccessController;
27 import java.security.PrivilegedAction;
28 import java.util.Enumeration;
29 import java.util.Hashtable;
30 import java.util.Properties;
31
32
33 /***
34 * <p>Factory for creating {@link Log} instances, with discovery and
35 * configuration features similar to that employed by standard Java APIs
36 * such as JAXP.</p>
37 *
38 * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation is heavily
39 * based on the SAXParserFactory and DocumentBuilderFactory implementations
40 * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.</p>
41 *
42 * @author Craig R. McClanahan
43 * @author Costin Manolache
44 * @author Richard A. Sitze
45 * @version $Revision: 1.27 $ $Date: 2004/06/06 21:15:12 $
46 */
47
48 public abstract class LogFactory {
49
50
51
52
53
54 /***
55 * The name of the property used to identify the LogFactory implementation
56 * class name.
57 */
58 public static final String FACTORY_PROPERTY =
59 "org.apache.commons.logging.LogFactory";
60
61 /***
62 * The fully qualified class name of the fallback <code>LogFactory</code>
63 * implementation class to use, if no other can be found.
64 */
65 public static final String FACTORY_DEFAULT =
66 "org.apache.commons.logging.impl.LogFactoryImpl";
67
68 /***
69 * The name of the properties file to search for.
70 */
71 public static final String FACTORY_PROPERTIES =
72 "commons-logging.properties";
73
74 /***
75 * JDK1.3+ <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider">
76 * 'Service Provider' specification</a>.
77 *
78 */
79 protected static final String SERVICE_ID =
80 "META-INF/services/org.apache.commons.logging.LogFactory";
81
82
83
84
85
86 /***
87 * Protected constructor that is not available for public use.
88 */
89 protected LogFactory() { }
90
91
92
93
94
95 /***
96 * Return the configuration attribute with the specified name (if any),
97 * or <code>null</code> if there is no such attribute.
98 *
99 * @param name Name of the attribute to return
100 */
101 public abstract Object getAttribute(String name);
102
103
104 /***
105 * Return an array containing the names of all currently defined
106 * configuration attributes. If there are no such attributes, a zero
107 * length array is returned.
108 */
109 public abstract String[] getAttributeNames();
110
111
112 /***
113 * Convenience method to derive a name from the specified class and
114 * call <code>getInstance(String)</code> with it.
115 *
116 * @param clazz Class for which a suitable Log name will be derived
117 *
118 * @exception LogConfigurationException if a suitable <code>Log</code>
119 * instance cannot be returned
120 */
121 public abstract Log getInstance(Class clazz)
122 throws LogConfigurationException;
123
124
125 /***
126 * <p>Construct (if necessary) and return a <code>Log</code> instance,
127 * using the factory's current set of configuration attributes.</p>
128 *
129 * <p><strong>NOTE</strong> - Depending upon the implementation of
130 * the <code>LogFactory</code> you are using, the <code>Log</code>
131 * instance you are returned may or may not be local to the current
132 * application, and may or may not be returned again on a subsequent
133 * call with the same name argument.</p>
134 *
135 * @param name Logical name of the <code>Log</code> instance to be
136 * returned (the meaning of this name is only known to the underlying
137 * logging implementation that is being wrapped)
138 *
139 * @exception LogConfigurationException if a suitable <code>Log</code>
140 * instance cannot be returned
141 */
142 public abstract Log getInstance(String name)
143 throws LogConfigurationException;
144
145
146 /***
147 * Release any internal references to previously created {@link Log}
148 * instances returned by this factory. This is useful in environments
149 * like servlet containers, which implement application reloading by
150 * throwing away a ClassLoader. Dangling references to objects in that
151 * class loader would prevent garbage collection.
152 */
153 public abstract void release();
154
155
156 /***
157 * Remove any configuration attribute associated with the specified name.
158 * If there is no such attribute, no action is taken.
159 *
160 * @param name Name of the attribute to remove
161 */
162 public abstract void removeAttribute(String name);
163
164
165 /***
166 * Set the configuration attribute with the specified name. Calling
167 * this with a <code>null</code> value is equivalent to calling
168 * <code>removeAttribute(name)</code>.
169 *
170 * @param name Name of the attribute to set
171 * @param value Value of the attribute to set, or <code>null</code>
172 * to remove any setting for this attribute
173 */
174 public abstract void setAttribute(String name, Object value);
175
176
177
178
179
180 /***
181 * The previously constructed <code>LogFactory</code> instances, keyed by
182 * the <code>ClassLoader</code> with which it was created.
183 */
184 protected static Hashtable factories = new Hashtable();
185
186
187
188
189
190 /***
191 * <p>Construct (if necessary) and return a <code>LogFactory</code>
192 * instance, using the following ordered lookup procedure to determine
193 * the name of the implementation class to be loaded.</p>
194 * <ul>
195 * <li>The <code>org.apache.commons.logging.LogFactory</code> system
196 * property.</li>
197 * <li>The JDK 1.3 Service Discovery mechanism</li>
198 * <li>Use the properties file <code>commons-logging.properties</code>
199 * file, if found in the class path of this class. The configuration
200 * file is in standard <code>java.util.Properties</code> format and
201 * contains the fully qualified name of the implementation class
202 * with the key being the system property defined above.</li>
203 * <li>Fall back to a default implementation class
204 * (<code>org.apache.commons.logging.impl.LogFactoryImpl</code>).</li>
205 * </ul>
206 *
207 * <p><em>NOTE</em> - If the properties file method of identifying the
208 * <code>LogFactory</code> implementation class is utilized, all of the
209 * properties defined in this file will be set as configuration attributes
210 * on the corresponding <code>LogFactory</code> instance.</p>
211 *
212 * @exception LogConfigurationException if the implementation class is not
213 * available or cannot be instantiated.
214 */
215 public static LogFactory getFactory() throws LogConfigurationException {
216
217
218 ClassLoader contextClassLoader =
219 (ClassLoader)AccessController.doPrivileged(
220 new PrivilegedAction() {
221 public Object run() {
222 return getContextClassLoader();
223 }
224 });
225
226
227 LogFactory factory = getCachedFactory(contextClassLoader);
228 if (factory != null)
229 return factory;
230
231
232
233
234
235 Properties props=null;
236 try {
237 InputStream stream = getResourceAsStream(contextClassLoader,
238 FACTORY_PROPERTIES);
239
240 if (stream != null) {
241 props = new Properties();
242 props.load(stream);
243 stream.close();
244 }
245 } catch (IOException e) {
246 } catch (SecurityException e) {
247 }
248
249
250
251 try {
252 String factoryClass = System.getProperty(FACTORY_PROPERTY);
253 if (factoryClass != null) {
254 factory = newFactory(factoryClass, contextClassLoader);
255 }
256 } catch (SecurityException e) {
257 ;
258 }
259
260
261
262
263
264
265
266
267 if (factory == null) {
268 try {
269 InputStream is = getResourceAsStream(contextClassLoader,
270 SERVICE_ID);
271
272 if( is != null ) {
273
274
275 BufferedReader rd;
276 try {
277 rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
278 } catch (java.io.UnsupportedEncodingException e) {
279 rd = new BufferedReader(new InputStreamReader(is));
280 }
281
282 String factoryClassName = rd.readLine();
283 rd.close();
284
285 if (factoryClassName != null &&
286 ! "".equals(factoryClassName)) {
287
288 factory= newFactory( factoryClassName, contextClassLoader );
289 }
290 }
291 } catch( Exception ex ) {
292 ;
293 }
294 }
295
296
297
298
299
300
301
302
303
304
305 if (factory == null && props != null) {
306 String factoryClass = props.getProperty(FACTORY_PROPERTY);
307 if (factoryClass != null) {
308 factory = newFactory(factoryClass, contextClassLoader);
309 }
310 }
311
312
313
314
315 if (factory == null) {
316 factory = newFactory(FACTORY_DEFAULT, LogFactory.class.getClassLoader());
317 }
318
319 if (factory != null) {
320 /***
321 * Always cache using context class loader.
322 */
323 cacheFactory(contextClassLoader, factory);
324
325 if( props!=null ) {
326 Enumeration names = props.propertyNames();
327 while (names.hasMoreElements()) {
328 String name = (String) names.nextElement();
329 String value = props.getProperty(name);
330 factory.setAttribute(name, value);
331 }
332 }
333 }
334
335 return factory;
336 }
337
338
339 /***
340 * Convenience method to return a named logger, without the application
341 * having to care about factories.
342 *
343 * @param clazz Class from which a log name will be derived
344 *
345 * @exception LogConfigurationException if a suitable <code>Log</code>
346 * instance cannot be returned
347 */
348 public static Log getLog(Class clazz)
349 throws LogConfigurationException {
350
351 return (getFactory().getInstance(clazz));
352
353 }
354
355
356 /***
357 * Convenience method to return a named logger, without the application
358 * having to care about factories.
359 *
360 * @param name Logical name of the <code>Log</code> instance to be
361 * returned (the meaning of this name is only known to the underlying
362 * logging implementation that is being wrapped)
363 *
364 * @exception LogConfigurationException if a suitable <code>Log</code>
365 * instance cannot be returned
366 */
367 public static Log getLog(String name)
368 throws LogConfigurationException {
369
370 return (getFactory().getInstance(name));
371
372 }
373
374
375 /***
376 * Release any internal references to previously created {@link LogFactory}
377 * instances that have been associated with the specified class loader
378 * (if any), after calling the instance method <code>release()</code> on
379 * each of them.
380 *
381 * @param classLoader ClassLoader for which to release the LogFactory
382 */
383 public static void release(ClassLoader classLoader) {
384
385 synchronized (factories) {
386 LogFactory factory = (LogFactory) factories.get(classLoader);
387 if (factory != null) {
388 factory.release();
389 factories.remove(classLoader);
390 }
391 }
392
393 }
394
395
396 /***
397 * Release any internal references to previously created {@link LogFactory}
398 * instances, after calling the instance method <code>release()</code> on
399 * each of them. This is useful in environments like servlet containers,
400 * which implement application reloading by throwing away a ClassLoader.
401 * Dangling references to objects in that class loader would prevent
402 * garbage collection.
403 */
404 public static void releaseAll() {
405
406 synchronized (factories) {
407 Enumeration elements = factories.elements();
408 while (elements.hasMoreElements()) {
409 LogFactory element = (LogFactory) elements.nextElement();
410 element.release();
411 }
412 factories.clear();
413 }
414
415 }
416
417
418
419
420
421 /***
422 * Return the thread context class loader if available.
423 * Otherwise return null.
424 *
425 * The thread context class loader is available for JDK 1.2
426 * or later, if certain security conditions are met.
427 *
428 * @exception LogConfigurationException if a suitable class loader
429 * cannot be identified.
430 */
431 protected static ClassLoader getContextClassLoader()
432 throws LogConfigurationException
433 {
434 ClassLoader classLoader = null;
435
436 try {
437
438 Method method = Thread.class.getMethod("getContextClassLoader", null);
439
440
441 try {
442 classLoader = (ClassLoader)method.invoke(Thread.currentThread(), null);
443 } catch (IllegalAccessException e) {
444 throw new LogConfigurationException
445 ("Unexpected IllegalAccessException", e);
446 } catch (InvocationTargetException e) {
447 /***
448 * InvocationTargetException is thrown by 'invoke' when
449 * the method being invoked (getContextClassLoader) throws
450 * an exception.
451 *
452 * getContextClassLoader() throws SecurityException when
453 * the context class loader isn't an ancestor of the
454 * calling class's class loader, or if security
455 * permissions are restricted.
456 *
457 * In the first case (not related), we want to ignore and
458 * keep going. We cannot help but also ignore the second
459 * with the logic below, but other calls elsewhere (to
460 * obtain a class loader) will trigger this exception where
461 * we can make a distinction.
462 */
463 if (e.getTargetException() instanceof SecurityException) {
464 ;
465 } else {
466
467
468 throw new LogConfigurationException
469 ("Unexpected InvocationTargetException", e.getTargetException());
470 }
471 }
472 } catch (NoSuchMethodException e) {
473
474 classLoader = LogFactory.class.getClassLoader();
475 }
476
477
478 return classLoader;
479 }
480
481 /***
482 * Check cached factories (keyed by contextClassLoader)
483 */
484 private static LogFactory getCachedFactory(ClassLoader contextClassLoader)
485 {
486 LogFactory factory = null;
487
488 if (contextClassLoader != null)
489 factory = (LogFactory) factories.get(contextClassLoader);
490
491 return factory;
492 }
493
494 private static void cacheFactory(ClassLoader classLoader, LogFactory factory)
495 {
496 if (classLoader != null && factory != null)
497 factories.put(classLoader, factory);
498 }
499
500 /***
501 * Return a new instance of the specified <code>LogFactory</code>
502 * implementation class, loaded by the specified class loader.
503 * If that fails, try the class loader used to load this
504 * (abstract) LogFactory.
505 *
506 * @param factoryClass Fully qualified name of the <code>LogFactory</code>
507 * implementation class
508 * @param classLoader ClassLoader from which to load this class
509 *
510 * @exception LogConfigurationException if a suitable instance
511 * cannot be created
512 */
513 protected static LogFactory newFactory(final String factoryClass,
514 final ClassLoader classLoader)
515 throws LogConfigurationException
516 {
517 Object result = AccessController.doPrivileged(
518 new PrivilegedAction() {
519 public Object run() {
520
521
522 Class logFactoryClass = null;
523 try {
524 if (classLoader != null) {
525 try {
526
527
528
529
530 logFactoryClass = classLoader.loadClass(factoryClass);
531 return (LogFactory) logFactoryClass.newInstance();
532
533 } catch (ClassNotFoundException ex) {
534 if (classLoader == LogFactory.class.getClassLoader()) {
535
536 throw ex;
537 }
538
539 } catch (NoClassDefFoundError e) {
540 if (classLoader == LogFactory.class.getClassLoader()) {
541
542 throw e;
543 }
544
545 } catch(ClassCastException e){
546
547 if (classLoader == LogFactory.class.getClassLoader()) {
548
549 throw e;
550 }
551 }
552
553 }
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568 logFactoryClass = Class.forName(factoryClass);
569 return (LogFactory) logFactoryClass.newInstance();
570 } catch (Exception e) {
571
572 if (logFactoryClass != null
573 && !LogFactory.class.isAssignableFrom(logFactoryClass)) {
574 return new LogConfigurationException(
575 "The chosen LogFactory implementation does not extend LogFactory."
576 + " Please check your configuration.",
577 e);
578 }
579 return new LogConfigurationException(e);
580 }
581 }
582 });
583
584 if (result instanceof LogConfigurationException)
585 throw (LogConfigurationException)result;
586
587 return (LogFactory)result;
588 }
589
590 private static InputStream getResourceAsStream(final ClassLoader loader,
591 final String name)
592 {
593 return (InputStream)AccessController.doPrivileged(
594 new PrivilegedAction() {
595 public Object run() {
596 if (loader != null) {
597 return loader.getResourceAsStream(name);
598 } else {
599 return ClassLoader.getSystemResourceAsStream(name);
600 }
601 }
602 });
603 }
604 }