KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > logicalcobwebs > logging > LogFactory


1 /*
2  * $Header: /cvsroot/proxool/proxool/src/java/org/logicalcobwebs/logging/LogFactory.java,v 1.2 2003/02/08 14:27:50 chr32 Exp $
3  * $Revision: 1.2 $
4  * $Date: 2003/02/08 14:27:50 $
5  *
6  * ====================================================================
7  *
8  * The Apache Software License, Version 1.1
9  *
10  * Copyright (c) 1999-2002 The Apache Software Foundation. All rights
11  * reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  *
17  * 1. Redistributions of source code must retain the above copyright
18  * notice, this list of conditions and the following disclaimer.
19  *
20  * 2. Redistributions in binary form must reproduce the above copyright
21  * notice, this list of conditions and the following disclaimer in
22  * the documentation and/or other materials provided with the
23  * distribution.
24  *
25  * 3. The end-user documentation included with the redistribution, if
26  * any, must include the following acknowlegement:
27  * "This product includes software developed by the
28  * Apache Software Foundation (http://www.apache.org/)."
29  * Alternately, this acknowlegement may appear in the software itself,
30  * if and wherever such third-party acknowlegements normally appear.
31  *
32  * 4. The names "The Jakarta Project", "Commons", and "Apache Software
33  * Foundation" must not be used to endorse or promote products derived
34  * from this software without prior written permission. For written
35  * permission, please contact apache@apache.org.
36  *
37  * 5. Products derived from this software may not be called "Apache"
38  * nor may "Apache" appear in their names without prior written
39  * permission of the Apache Group.
40  *
41  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
42  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
45  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
47  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
48  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
49  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
51  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52  * SUCH DAMAGE.
53  * ====================================================================
54  *
55  * This software consists of voluntary contributions made by many
56  * individuals on behalf of the Apache Software Foundation. For more
57  * information on the Apache Software Foundation, please see
58  * <http://www.apache.org/>.
59  *
60  */

61
62 package org.logicalcobwebs.logging;
63
64 import java.io.BufferedReader JavaDoc;
65 import java.io.IOException JavaDoc;
66 import java.io.InputStream JavaDoc;
67 import java.io.InputStreamReader JavaDoc;
68 import java.lang.reflect.InvocationTargetException JavaDoc;
69 import java.lang.reflect.Method JavaDoc;
70 import java.security.AccessController JavaDoc;
71 import java.security.PrivilegedAction JavaDoc;
72 import java.util.Enumeration JavaDoc;
73 import java.util.Hashtable JavaDoc;
74 import java.util.Properties JavaDoc;
75
76 /**
77  * <p>Factory for creating {@link org.logicalcobwebs.logging.Log} instances, with discovery and
78  * configuration features similar to that employed by standard Java APIs
79  * such as JAXP.</p>
80  *
81  * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation is heavily
82  * based on the SAXParserFactory and DocumentBuilderFactory implementations
83  * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.</p>
84  *
85  * @author Craig R. McClanahan
86  * @author Costin Manolache
87  * @version $Revision: 1.2 $ $Date: 2003/02/08 14:27:50 $
88  */

89
90 public abstract class LogFactory {
91
92     // ----------------------------------------------------- Manifest Constants
93

94
95     /**
96      * The name of the property used to identify the LogFactory implementation
97      * class name.
98      */

99     public static final String JavaDoc FACTORY_PROPERTY =
100             "org.logicalcobwebs.logging.LogFactory";
101
102     /**
103      * The fully qualified class name of the fallback <code>LogFactory</code>
104      * implementation class to use, if no other can be found.
105      */

106     public static final String JavaDoc FACTORY_DEFAULT =
107             "org.logicalcobwebs.logging.impl.LogFactoryImpl";
108
109     /**
110      * The name of the properties file to search for.
111      */

112     public static final String JavaDoc FACTORY_PROPERTIES =
113             "commons-logging.properties";
114
115     /**
116      * JDK1.3+ 'Service Provider' specification
117      * (http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html)
118      */

119     protected static final String JavaDoc SERVICE_ID =
120             "META-INF/services/org.logicalcobwebs.logging.LogFactory";
121
122
123     // ----------------------------------------------------------- Constructors
124

125
126     /**
127      * Protected constructor that is not available for public use.
128      */

129     protected LogFactory () {
130     }
131
132
133     // --------------------------------------------------------- Public Methods
134

135
136     /**
137      * Return the configuration attribute with the specified name (if any),
138      * or <code>null</code> if there is no such attribute.
139      *
140      * @param name Name of the attribute to return
141      */

142     public abstract Object JavaDoc getAttribute (String JavaDoc name);
143
144     /**
145      * Return an array containing the names of all currently defined
146      * configuration attributes. If there are no such attributes, a zero
147      * length array is returned.
148      */

149     public abstract String JavaDoc[] getAttributeNames ();
150
151     /**
152      * Convenience method to derive a name from the specified class and
153      * call <code>getInstance(String)</code> with it.
154      *
155      * @param clazz Class for which a suitable Log name will be derived
156      *
157      * @exception org.logicalcobwebs.logging.LogConfigurationException if a suitable <code>Log</code>
158      * instance cannot be returned
159      */

160     public abstract Log getInstance (Class JavaDoc clazz)
161             throws LogConfigurationException;
162
163     /**
164      * <p>Construct (if necessary) and return a <code>Log</code> instance,
165      * using the factory's current set of configuration attributes.</p>
166      *
167      * <p><strong>NOTE</strong> - Depending upon the implementation of
168      * the <code>LogFactory</code> you are using, the <code>Log</code>
169      * instance you are returned may or may not be local to the current
170      * application, and may or may not be returned again on a subsequent
171      * call with the same name argument.</p>
172      *
173      * @param name Logical name of the <code>Log</code> instance to be
174      * returned (the meaning of this name is only known to the underlying
175      * logging implementation that is being wrapped)
176      *
177      * @exception org.logicalcobwebs.logging.LogConfigurationException if a suitable <code>Log</code>
178      * instance cannot be returned
179      */

180     public abstract Log getInstance (String JavaDoc name)
181             throws LogConfigurationException;
182
183     /**
184      * Release any internal references to previously created {@link org.logicalcobwebs.logging.Log}
185      * instances returned by this factory. This is useful environments
186      * like servlet containers, which implement application reloading by
187      * throwing away a ClassLoader. Dangling references to objects in that
188      * class loader would prevent garbage collection.
189      */

190     public abstract void release ();
191
192     /**
193      * Remove any configuration attribute associated with the specified name.
194      * If there is no such attribute, no action is taken.
195      *
196      * @param name Name of the attribute to remove
197      */

198     public abstract void removeAttribute (String JavaDoc name);
199
200     /**
201      * Set the configuration attribute with the specified name. Calling
202      * this with a <code>null</code> value is equivalent to calling
203      * <code>removeAttribute(name)</code>.
204      *
205      * @param name Name of the attribute to set
206      * @param value Value of the attribute to set, or <code>null</code>
207      * to remove any setting for this attribute
208      */

209     public abstract void setAttribute (String JavaDoc name, Object JavaDoc value);
210
211
212     // ------------------------------------------------------- Static Variables
213

214
215     /**
216      * The previously constructed <code>LogFactory</code> instances, keyed by
217      * the <code>ClassLoader</code> with which it was created.
218      */

219     private static Hashtable JavaDoc factories = new Hashtable JavaDoc ();
220
221
222     // --------------------------------------------------------- Static Methods
223

224
225     /**
226      * <p>Construct (if necessary) and return a <code>LogFactory</code>
227      * instance, using the following ordered lookup procedure to determine
228      * the name of the implementation class to be loaded.</p>
229      * <ul>
230      * <li>The <code>org.logicalcobwebs.logging.LogFactory</code> system
231      * property.</li>
232      * <li>Use the properties file <code>commons-logging.properties</code>
233      * file, if found in the class path of this class. The configuration
234      * file is in standard <code>java.util.Propertis</code> format and
235      * contains the fully qualified name of the implementation class
236      * with the key being the system property defined above.</li>
237      * <li>Fall back to a default implementation class
238      * (<code>org.logicalcobwebs.logging.impl.LogFactoryImpl</code>).</li>
239      * </ul>
240      *
241      * <p><em>NOTE</em> - If the properties file method of identifying the
242      * <code>LogFactory</code> implementation class is utilized, all of the
243      * properties defined in this file will be set as configuration attributes
244      * on the corresponding <code>LogFactory</code> instance.</p>
245      *
246      * @exception org.logicalcobwebs.logging.LogConfigurationException if the implementation class is not
247      * available or cannot be instantiated.
248      */

249     public static LogFactory getFactory () throws LogConfigurationException {
250
251         // Identify the class loader we will be using
252
ClassLoader JavaDoc contextClassLoader =
253                 (ClassLoader JavaDoc) AccessController.doPrivileged (
254                         new PrivilegedAction JavaDoc () {
255                             public Object JavaDoc run () {
256                                 return getContextClassLoader ();
257                             }
258                         });
259
260         // Return any previously registered factory for this class loader
261
LogFactory factory = getCachedFactory (contextClassLoader);
262         if (factory != null) {
263             return factory;
264         }
265
266
267         // First, try the system property
268
try {
269             String JavaDoc factoryClass = System.getProperty (FACTORY_PROPERTY);
270             if (factoryClass != null) {
271                 factory = newFactory (factoryClass, contextClassLoader);
272             }
273         } catch (SecurityException JavaDoc e) {
274             ; // ignore
275
}
276
277         // Second, try to find a service by using the JDK1.3 jar
278
// discovery mechanism. This will allow users to plug a logger
279
// by just placing it in the lib/ directory of the webapp (or in
280
// CLASSPATH or equivalent). This is similar with the second
281
// step, except that it uses the (standard?) jdk1.3 location in the jar.
282

283         if (factory == null) {
284             try {
285                 InputStream JavaDoc is = (contextClassLoader == null
286                         ? ClassLoader.getSystemResourceAsStream (SERVICE_ID)
287                         : contextClassLoader.getResourceAsStream (SERVICE_ID));
288
289                 if (is != null) {
290                     // This code is needed by EBCDIC and other strange systems.
291
// It's a fix for bugs reported in xerces
292
BufferedReader JavaDoc rd;
293                     try {
294                         rd = new BufferedReader JavaDoc (new InputStreamReader JavaDoc (is, "UTF-8"));
295                     } catch (java.io.UnsupportedEncodingException JavaDoc e) {
296                         rd = new BufferedReader JavaDoc (new InputStreamReader JavaDoc (is));
297                     }
298
299                     String JavaDoc factoryClassName = rd.readLine ();
300                     rd.close ();
301
302                     if (factoryClassName != null && !"".equals (factoryClassName)) {
303                         factory = newFactory (factoryClassName, contextClassLoader);
304                     }
305                 }
306             } catch (Exception JavaDoc ex) {
307                 ;
308             }
309         }
310
311         Properties JavaDoc props = null;
312
313         // Third try a properties file.
314
// If the properties file exists, it'll be read and the properties
315
// used. IMHO (costin) System property and JDK1.3 jar service
316
// should be enough for detecting the class name. The properties
317
// should be used to set the attributes (which may be specific to
318
// the webapp, even if a default logger is set at JVM level by a
319
// system property)
320

321         try {
322             InputStream JavaDoc stream = (contextClassLoader == null
323                     ? ClassLoader.getSystemResourceAsStream (FACTORY_PROPERTIES)
324                     : contextClassLoader.getResourceAsStream (FACTORY_PROPERTIES));
325             if (stream != null) {
326                 props = new Properties JavaDoc ();
327                 props.load (stream);
328                 stream.close ();
329                 String JavaDoc factoryClass = props.getProperty (FACTORY_PROPERTY);
330                 if (factory == null) {
331                     if (factoryClass == null) {
332                         factoryClass = FACTORY_DEFAULT;
333                     }
334                     factory = newFactory (factoryClass, contextClassLoader);
335                 }
336             }
337             // the properties will be set at the end.
338
} catch (IOException JavaDoc e) {
339             // oh well
340
} catch (SecurityException JavaDoc e) {
341             // oh well
342
}
343
344         // Fourth, try the fallback implementation class
345
if (factory == null) {
346             factory = newFactory (FACTORY_DEFAULT, LogFactory.class.getClassLoader ());
347         }
348
349         if (factory != null) {
350             /**
351              * Always cache using context class loader..
352              */

353             cacheFactory (contextClassLoader, factory);
354         }
355
356         if (props != null) {
357             Enumeration JavaDoc names = props.propertyNames ();
358             while (names.hasMoreElements ()) {
359                 String JavaDoc name = (String JavaDoc) names.nextElement ();
360                 String JavaDoc value = props.getProperty (name);
361                 factory.setAttribute (name, value);
362             }
363         }
364
365         return factory;
366     }
367
368     /**
369      * Convenience method to return a named logger, without the application
370      * having to care about factories.
371      *
372      * @param clazz Class for which a log name will be derived
373      *
374      * @exception org.logicalcobwebs.logging.LogConfigurationException if a suitable <code>Log</code>
375      * instance cannot be returned
376      */

377     public static Log getLog (Class JavaDoc clazz)
378             throws LogConfigurationException {
379
380         return (getFactory ().getInstance (clazz));
381
382     }
383
384     /**
385      * Convenience method to return a named logger, without the application
386      * having to care about factories.
387      *
388      * @param name Logical name of the <code>Log</code> instance to be
389      * returned (the meaning of this name is only known to the underlying
390      * logging implementation that is being wrapped)
391      *
392      * @exception org.logicalcobwebs.logging.LogConfigurationException if a suitable <code>Log</code>
393      * instance cannot be returned
394      */

395     public static Log getLog (String JavaDoc name)
396             throws LogConfigurationException {
397
398         return (getFactory ().getInstance (name));
399
400     }
401
402     /**
403      * Release any internal references to previously created {@link org.logicalcobwebs.logging.LogFactory}
404      * instances, after calling the instance method <code>release()</code> on
405      * each of them. This is useful environments like servlet containers,
406      * which implement application reloading by throwing away a ClassLoader.
407      * Dangling references to objects in that class loader would prevent
408      * garbage collection.
409      */

410     public static void releaseAll () {
411
412         synchronized (factories) {
413             Enumeration JavaDoc elements = factories.elements ();
414             while (elements.hasMoreElements ()) {
415                 LogFactory element = (LogFactory) elements.nextElement ();
416                 element.release ();
417             }
418             factories.clear ();
419         }
420
421     }
422
423
424     // ------------------------------------------------------ Protected Methods
425

426
427     /**
428      * Return the thread context class loader if available.
429      * Otherwise return null.
430      *
431      * The thread context class loader is available for JDK 1.2
432      * or later, if certain security conditions are met.
433      *
434      * @exception org.logicalcobwebs.logging.LogConfigurationException if a suitable class loader
435      * cannot be identified.
436      */

437     protected static ClassLoader JavaDoc getContextClassLoader ()
438             throws LogConfigurationException {
439         ClassLoader JavaDoc classLoader = null;
440
441         try {
442             // Are we running on a JDK 1.2 or later system?
443
Method JavaDoc method = Thread JavaDoc.class.getMethod ("getContextClassLoader", null);
444
445             // Get the thread context class loader (if there is one)
446
try {
447                 classLoader = (ClassLoader JavaDoc) method.invoke (Thread.currentThread (), null);
448             } catch (IllegalAccessException JavaDoc e) {
449                 throw new LogConfigurationException
450                         ("Unexpected IllegalAccessException", e);
451             } catch (InvocationTargetException JavaDoc e) {
452                 /**
453                  * InvocationTargetException is thrown by 'invoke' when
454                  * the method being invoked (getContextClassLoader) throws
455                  * an exception.
456                  *
457                  * getContextClassLoader() throws SecurityException when
458                  * the context class loader isn't an ancestor of the
459                  * calling class's class loader, or if security
460                  * permissions are restricted.
461                  *
462                  * In the first case (not related), we want to ignore and
463                  * keep going. We cannot help but also ignore the second
464                  * with the logic below, but other calls elsewhere (to
465                  * obtain a class loader) will trigger this exception where
466                  * we can make a distinction.
467                  */

468                 if (e.getTargetException () instanceof SecurityException JavaDoc) {
469                     ; // ignore
470
} else {
471                     // Capture 'e.getTargetException()' exception for details
472
// alternate: log 'e.getTargetException()', and pass back 'e'.
473
throw new LogConfigurationException
474                             ("Unexpected InvocationTargetException", e.getTargetException ());
475                 }
476             }
477         } catch (NoSuchMethodException JavaDoc e) {
478             // Assume we are running on JDK 1.1
479
classLoader = LogFactory.class.getClassLoader ();
480         }
481
482         // Return the selected class loader
483
return classLoader;
484     }
485
486     /**
487      * Check cached factories (keyed by classLoader)
488      */

489     private static LogFactory getCachedFactory (ClassLoader JavaDoc contextClassLoader) {
490         LogFactory factory = null;
491
492         if (contextClassLoader != null) {
493             factory = (LogFactory) factories.get (contextClassLoader);
494         }
495         return factory;
496     }
497
498     private static void cacheFactory (ClassLoader JavaDoc classLoader, LogFactory factory) {
499         if (classLoader != null && factory != null) {
500             factories.put (classLoader, factory);
501         }
502
503     }
504
505     /**
506      * Return a new instance of the specified <code>LogFactory</code>
507      * implementation class, loaded by the specified class loader.
508      * If that fails, try the class loader used to load this
509      * (abstract) LogFactory.
510      *
511      * @param factoryClass Fully qualified name of the <code>LogFactory</code>
512      * implementation class
513      * @param classLoader ClassLoader from which to load this class
514      *
515      * @exception org.logicalcobwebs.logging.LogConfigurationException if a suitable instance
516      * cannot be created
517      */

518     protected static LogFactory newFactory (String JavaDoc factoryClass,
519                                             ClassLoader JavaDoc classLoader)
520             throws LogConfigurationException {
521
522         try {
523             if (classLoader == null) {
524                 classLoader = LogFactory.class.getClassLoader ();
525             }
526             Class JavaDoc clazz = null;
527             try {
528                 // first the thread class loader
529
clazz = classLoader.loadClass (factoryClass);
530             } catch (ClassNotFoundException JavaDoc ex) {
531                 // if this failed (i.e. no implementation is
532
// found in the webapp), try the caller's loader
533
// if we haven't already...
534
if (classLoader != LogFactory.class.getClassLoader ()) {
535                     classLoader = LogFactory.class.getClassLoader ();
536                     clazz = classLoader.loadClass (factoryClass);
537                 }
538             }
539
540             LogFactory factory = (LogFactory) clazz.newInstance ();
541
542             return factory;
543         } catch (Exception JavaDoc e) {
544             throw new LogConfigurationException (e);
545         }
546
547     }
548 }
549
550
Popular Tags