001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.jetspeed.webapp.logging;
018:
019: import java.util.HashMap;
020: import java.util.Iterator;
021: import java.util.Map;
022:
023: import org.apache.commons.logging.Log;
024: import org.apache.commons.logging.impl.Log4JLogger;
025: import org.apache.log4j.Hierarchy;
026:
027: /**
028: * IsolatedLog4JLogger routes all commons-logging logging using Log4J to an ContextClassLoader
029: * specific Hierarchy.
030: * </p>
031: * <p>
032: * For web or application servers providing (and enforcing) commons-logging and
033: * Log4J from a shared context (like JBoss), configuring Log4J loggers and appenders
034: * from within a (web) application overrides and resets the global
035: * Log4J LoggerRepository.
036: * </p>
037: * <p>
038: * Capturing root logging for logging events from within the web application
039: * for example isn't possible using a Log4J propery or xml configuration without
040: * routing <em>ALL</em> root logging through the new (web application specific)
041: * configuration.
042: * </p>
043: * <p>
044: * <em>It is possible using the Log4J API directly instead of configuration files,
045: * but that requires hardcoded knowledge about how the logging is to be done.</em>
046: * </p>
047: * <p>
048: * Furthermore, if another application later on reconfigures the root logging again, the
049: * current root logging configuration is closed, detached and rerouted to the new configuration.
050: * </p>
051: * <p>
052: * The same applies of course to common named loggers like capturing springframework logging.
053: * </p>
054: * <p>
055: * The only way to prevent this <em>stealing</em> of logging configuration is allowing only
056: * one log4J configuration for the whole web or application server.<br/>
057: * As this has to be done in a web or application server specific manner, setting up Jetspeed
058: * for different servers will become rather complex and difficult to automate.
059: * </p>
060: * <p>
061: * The IsolatedLog4JLogger solves these problems by routing all logging through a statically
062: * configured ContextClassLoader isolated LoggerRepository.
063: * </p>
064: * Using this class requires a commons-logging.properties file in the WEB-INF/classes
065: * folder containing:
066: * <pre>
067: * org.apache.commons.logging.Log=org.apache.jetspeed.util.IsolatedLog4JLogger
068: * </pre>
069: * </p>
070: * <p>
071: * During web application initialization, preferably from a ServletContextListener or
072: * a Servlet init method loaded with a load-on-startup value of 0 (zero), a new
073: * ContextClassLoader (e.g. web application) specific LoggerRepository as well as
074: * the initialization of Log4J should be configured as follows:
075: * <pre>
076: * Properties p = new Properties();
077: * p.load(new FileInputStream(log4jFile));
078: * // Create a new LoggerRepository
079: * Hierarchy h = new Hierarchy(new RootCategory(Level.INFO));
080: * // Configure the LoggerRepository with our own Log4J configuration
081: * new PropertyConfigurator().doConfigure(p,h);
082: * // set the LoggerRepository to be used for the current ContextClassLoader
083: * IsolatedLog4JLogger.setHierarchy(h);
084: * </pre>
085: * Instead of using a PropertyConfigurator a DomConfigurator could be used likewise.
086: * </p>
087: * <p>
088: * TODO: It might be useful to have this utility class available for usage by Portlet
089: * Applications too. Because this class <em>must</em> be included within a web application
090: * classpath, a separate jetspeed-tools or jetspeed-utils library will have to be created
091: * for it (and possibly other utility classes as well) which then can be put in the web
092: * application lib folder.
093: * </p>
094: * @author <a href="mailto:ate@douma.nu">Ate Douma</a>
095: * @version $Id: IsolatedLog4JLogger.java 531463 2007-04-23 13:35:58Z taylor $
096: */
097: public class IsolatedLog4JLogger implements Log {
098: private static Hierarchy hierarchy;
099: private static HashMap notIsolatedLoggers = new HashMap();
100:
101: private Log4JLogger logger; // the wrapped Log4JLogger
102:
103: public static void setHierarchy(Hierarchy hierarchy) {
104: synchronized (IsolatedLog4JLogger.class) {
105: if (IsolatedLog4JLogger.hierarchy == null) {
106: IsolatedLog4JLogger.hierarchy = hierarchy;
107: if (notIsolatedLoggers.size() > 0) {
108: // Reroute existing IsolatedLog4JLogger instances
109: // which were created before the new LoggerRepository.
110: // Note: This situation should be prevented as much as
111: // possible by calling setHierarchy from
112: // a ServletContextListener or a Servlet its init method
113: // which has a load-on-startup value of 0 (zero).
114: Iterator iter = notIsolatedLoggers.entrySet()
115: .iterator();
116: while (iter.hasNext()) {
117: Map.Entry entry = (Map.Entry) iter.next();
118: IsolatedLog4JLogger logger = (IsolatedLog4JLogger) entry
119: .getKey();
120: logger.setLogger(new Log4JLogger(hierarchy
121: .getLogger((String) entry.getValue())));
122: }
123: }
124: notIsolatedLoggers = null;
125: }
126: }
127: }
128:
129: public IsolatedLog4JLogger(String name) {
130: synchronized (IsolatedLog4JLogger.class) {
131: if (hierarchy == null) {
132: // A LogFactory.getLog(name) is called before
133: // our ContextClassLoader Hierarchy could be set.
134: // Temporarily save this instance so it can be
135: // rerouted one the Hierarchy is set.
136: logger = new Log4JLogger(name);
137: notIsolatedLoggers.put(this , name);
138: } else {
139: logger = new Log4JLogger(hierarchy.getLogger(name));
140: }
141: }
142: }
143:
144: private void setLogger(Log4JLogger logger) {
145: this .logger = logger;
146: }
147:
148: private Log4JLogger getLogger() {
149: return logger;
150: }
151:
152: public void debug(Object arg0) {
153: Log4JLogger logger = getLogger();
154: if (logger != null) {
155: logger.debug(arg0);
156: }
157: }
158:
159: public void debug(Object arg0, Throwable arg1) {
160: Log4JLogger logger = getLogger();
161: if (logger != null) {
162: logger.debug(arg0, arg1);
163: }
164: }
165:
166: public boolean equals(Object obj) {
167: Log4JLogger logger = getLogger();
168: return logger != null ? logger.equals(obj) : false;
169: }
170:
171: public void error(Object arg0) {
172: Log4JLogger logger = getLogger();
173: if (logger != null) {
174: logger.error(arg0);
175: }
176: }
177:
178: public void error(Object arg0, Throwable arg1) {
179: Log4JLogger logger = getLogger();
180: if (logger != null) {
181: logger.error(arg0, arg1);
182: }
183: }
184:
185: public void fatal(Object arg0) {
186: Log4JLogger logger = getLogger();
187: if (logger != null) {
188: logger.fatal(arg0);
189: }
190: }
191:
192: public void fatal(Object arg0, Throwable arg1) {
193: Log4JLogger logger = getLogger();
194: if (logger != null) {
195: logger.fatal(arg0, arg1);
196: }
197: }
198:
199: public void info(Object arg0) {
200: Log4JLogger logger = getLogger();
201: if (logger != null) {
202: logger.info(arg0);
203: }
204: }
205:
206: public void info(Object arg0, Throwable arg1) {
207: Log4JLogger logger = getLogger();
208: if (logger != null) {
209: logger.info(arg0, arg1);
210: }
211: }
212:
213: public boolean isDebugEnabled() {
214: Log4JLogger logger = getLogger();
215: return logger != null ? logger.isDebugEnabled() : false;
216: }
217:
218: public boolean isErrorEnabled() {
219: Log4JLogger logger = getLogger();
220: return logger != null ? logger.isErrorEnabled() : false;
221: }
222:
223: public boolean isFatalEnabled() {
224: Log4JLogger logger = getLogger();
225: return logger != null ? logger.isFatalEnabled() : false;
226: }
227:
228: public boolean isInfoEnabled() {
229: Log4JLogger logger = getLogger();
230: return logger != null ? logger.isInfoEnabled() : false;
231: }
232:
233: public boolean isTraceEnabled() {
234: Log4JLogger logger = getLogger();
235: return logger != null ? logger.isTraceEnabled() : false;
236: }
237:
238: public boolean isWarnEnabled() {
239: Log4JLogger logger = getLogger();
240: return logger != null ? logger.isWarnEnabled() : false;
241: }
242:
243: public String toString() {
244: Log4JLogger logger = getLogger();
245: return logger != null ? logger.toString() : null;
246: }
247:
248: public void trace(Object arg0) {
249: Log4JLogger logger = getLogger();
250: if (logger != null) {
251: logger.trace(arg0);
252: }
253: }
254:
255: public void trace(Object arg0, Throwable arg1) {
256: Log4JLogger logger = getLogger();
257: if (logger != null) {
258: logger.trace(arg0, arg1);
259: }
260: }
261:
262: public void warn(Object arg0) {
263: Log4JLogger logger = getLogger();
264: if (logger != null) {
265: logger.warn(arg0);
266: }
267: }
268:
269: public void warn(Object arg0, Throwable arg1) {
270: Log4JLogger logger = getLogger();
271: if (logger != null) {
272: logger.warn(arg0, arg1);
273: }
274: }
275: }
|