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  package org.apache.jetspeed.webapp.logging;
18  
19  import java.util.HashMap;
20  import java.util.Iterator;
21  import java.util.Map;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.impl.Log4JLogger;
25  import org.apache.log4j.Hierarchy;
26  
27  /***
28   * IsolatedLog4JLogger routes all commons-logging logging using Log4J to an ContextClassLoader
29   * specific Hierarchy.
30   * </p>
31   * <p>
32   * For web or application servers providing (and enforcing) commons-logging and
33   * Log4J from a shared context (like JBoss), configuring Log4J loggers and appenders
34   * from within a (web) application overrides and resets the global 
35   * Log4J LoggerRepository.
36   * </p>
37   * <p>
38   * Capturing root logging for logging events from within the web application
39   * for example isn't possible using a Log4J propery or xml configuration without
40   * routing <em>ALL</em> root logging through the new (web application specific)
41   * configuration.
42   * </p>
43   * <p>
44   * <em>It is possible using the Log4J API directly instead of configuration files, 
45   * but that requires hardcoded knowledge about how the logging is to be done.</em>
46   * </p>
47   * <p>
48   * Furthermore, if another application later on reconfigures the root logging again, the
49   * current root logging configuration is closed, detached and rerouted to the new configuration.
50   * </p>
51   * <p>
52   * The same applies of course to common named loggers like capturing springframework logging. 
53   * </p>
54   * <p>
55   * The only way to prevent this <em>stealing</em> of logging configuration is allowing only
56   * one log4J configuration for the whole web or application server.<br/>
57   * As this has to be done in a web or application server specific manner, setting up Jetspeed
58   * for different servers will become rather complex and difficult to automate.
59   * </p>
60   * <p>
61   * The IsolatedLog4JLogger solves these problems by routing all logging through a statically
62   * configured ContextClassLoader isolated LoggerRepository.
63   * </p>
64   * Using this class requires a commons-logging.properties file in the WEB-INF/classes
65   * folder containing:
66   * <pre>
67   *   org.apache.commons.logging.Log=org.apache.jetspeed.util.IsolatedLog4JLogger
68   * </pre>
69   * </p>
70   * <p>
71   * During web application initialization, preferably from a ServletContextListener or
72   * a Servlet init method loaded with a load-on-startup value of 0 (zero), a new 
73   * ContextClassLoader (e.g. web application) specific LoggerRepository as well as 
74   * the initialization of Log4J should be configured as follows:
75   * <pre>
76   *   Properties p = new Properties();
77   *   p.load(new FileInputStream(log4jFile));
78   *   // Create a new LoggerRepository
79   *   Hierarchy h = new Hierarchy(new RootCategory(Level.INFO));
80   *   // Configure the LoggerRepository with our own Log4J configuration
81   *   new PropertyConfigurator().doConfigure(p,h);
82   *   // set the LoggerRepository to be used for the current ContextClassLoader
83   *   IsolatedLog4JLogger.setHierarchy(h);
84   * </pre>
85   * Instead of using a PropertyConfigurator a DomConfigurator could be used likewise. 
86   * </p>
87   * <p>
88   * TODO: It might be useful to have this utility class available for usage by Portlet
89   * Applications too. Because this class <em>must</em> be included within a web application
90   * classpath, a separate jetspeed-tools or jetspeed-utils library will have to be created
91   * for it (and possibly other utility classes as well) which then can be put in the web
92   * application lib folder. 
93   * </p>
94   * @author <a href="mailto:ate@douma.nu">Ate Douma</a>
95   * @version $Id: IsolatedLog4JLogger.java 531463 2007-04-23 13:35:58Z taylor $
96   */
97  public class IsolatedLog4JLogger implements Log
98  {
99      private static Hierarchy hierarchy;
100     private static HashMap notIsolatedLoggers = new HashMap();
101     
102     private Log4JLogger logger; // the wrapped Log4JLogger 
103     
104     public static void setHierarchy(Hierarchy hierarchy)
105     {
106         synchronized (IsolatedLog4JLogger.class)
107         {
108             if ( IsolatedLog4JLogger.hierarchy == null )
109             {
110                 IsolatedLog4JLogger.hierarchy = hierarchy;
111                 if ( notIsolatedLoggers.size() > 0 )
112                 {
113                     // Reroute existing IsolatedLog4JLogger instances
114                     // which were created before the new LoggerRepository.
115                     // Note: This situation should be prevented as much as
116                     //       possible by calling setHierarchy from
117                     //       a ServletContextListener or a Servlet its init method
118                     //       which has a load-on-startup value of 0 (zero).
119                     Iterator iter = notIsolatedLoggers.entrySet().iterator();
120                     while (iter.hasNext())
121                     {
122                         Map.Entry entry = (Map.Entry)iter.next();
123                         IsolatedLog4JLogger logger = (IsolatedLog4JLogger)entry.getKey();
124                         logger.setLogger(new Log4JLogger(hierarchy.getLogger((String)entry.getValue())));
125                     }
126                 }
127                 notIsolatedLoggers = null;
128             }
129         }
130     }
131     
132     public IsolatedLog4JLogger(String name)
133     {
134         synchronized (IsolatedLog4JLogger.class)
135         {
136             if ( hierarchy == null )
137             {
138                 // A LogFactory.getLog(name) is called before
139                 // our ContextClassLoader Hierarchy could be set.
140                 // Temporarily save this instance so it can be
141                 // rerouted one the Hierarchy is set.
142                 logger = new Log4JLogger(name);
143                 notIsolatedLoggers.put(this,name);
144             }
145             else
146             {
147                 logger = new Log4JLogger(hierarchy.getLogger(name));               
148             }
149         }
150     }
151     
152     private void setLogger(Log4JLogger logger)
153     {
154         this.logger = logger;
155     }
156 
157     private Log4JLogger getLogger()
158     {
159         return logger;
160     }
161 
162     public void debug(Object arg0)
163     {
164         Log4JLogger logger = getLogger();
165         if ( logger != null )
166         {
167             logger.debug(arg0);
168         }
169     }
170     public void debug(Object arg0, Throwable arg1)
171     {
172         Log4JLogger logger = getLogger();
173         if ( logger != null )
174         {
175             logger.debug(arg0,arg1);
176         }
177     }
178     public boolean equals(Object obj)
179     {
180         Log4JLogger logger = getLogger();
181         return logger != null ? logger.equals(obj) : false;
182     }
183     public void error(Object arg0)
184     {
185         Log4JLogger logger = getLogger();
186         if ( logger != null )
187         {
188             logger.error(arg0);
189         }
190     }
191     public void error(Object arg0, Throwable arg1)
192     {
193         Log4JLogger logger = getLogger();
194         if ( logger != null )
195         {
196             logger.error(arg0, arg1);
197         }
198     }
199     public void fatal(Object arg0)
200     {
201         Log4JLogger logger = getLogger();
202         if ( logger != null )
203         {
204             logger.fatal(arg0);
205         }
206     }
207     public void fatal(Object arg0, Throwable arg1)
208     {
209         Log4JLogger logger = getLogger();
210         if ( logger != null )
211         {
212             logger.fatal(arg0, arg1);
213         }
214     }
215     public void info(Object arg0)
216     {
217         Log4JLogger logger = getLogger();
218         if ( logger != null )
219         {
220             logger.info(arg0);
221         }
222     }
223     public void info(Object arg0, Throwable arg1)
224     {
225         Log4JLogger logger = getLogger();
226         if ( logger != null )
227         {
228             logger.info(arg0, arg1);
229         }
230     }
231     public boolean isDebugEnabled()
232     {
233         Log4JLogger logger = getLogger();
234         return logger != null ? logger.isDebugEnabled() : false;
235     }
236     public boolean isErrorEnabled()
237     {
238         Log4JLogger logger = getLogger();
239         return logger != null ? logger.isErrorEnabled() : false;
240     }
241     public boolean isFatalEnabled()
242     {
243         Log4JLogger logger = getLogger();
244         return logger != null ? logger.isFatalEnabled() : false;
245     }
246     public boolean isInfoEnabled()
247     {
248         Log4JLogger logger = getLogger();
249         return logger != null ? logger.isInfoEnabled() : false;
250     }
251     public boolean isTraceEnabled()
252     {
253         Log4JLogger logger = getLogger();
254         return logger != null ? logger.isTraceEnabled() : false;
255     }
256     public boolean isWarnEnabled()
257     {
258         Log4JLogger logger = getLogger();
259         return logger != null ? logger.isWarnEnabled() : false;
260     }
261     public String toString()
262     {
263         Log4JLogger logger = getLogger();
264         return logger != null ? logger.toString() : null;
265     }
266     public void trace(Object arg0)
267     {
268         Log4JLogger logger = getLogger();
269         if ( logger != null )
270         {
271             logger.trace(arg0);
272         }
273     }
274     public void trace(Object arg0, Throwable arg1)
275     {
276         Log4JLogger logger = getLogger();
277         if ( logger != null )
278         {
279             logger.trace(arg0, arg1);
280         }
281     }
282     public void warn(Object arg0)
283     {
284         Log4JLogger logger = getLogger();
285         if ( logger != null )
286         {
287             logger.warn(arg0);
288         }
289     }
290     public void warn(Object arg0, Throwable arg1)
291     {
292         Log4JLogger logger = getLogger();
293         if ( logger != null )
294         {
295             logger.warn(arg0, arg1);
296         }
297     }
298 }