001: /*
002: * Copyright 2002-2005 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.web.util;
018:
019: import java.io.FileNotFoundException;
020:
021: import javax.servlet.ServletContext;
022:
023: import org.springframework.util.Log4jConfigurer;
024: import org.springframework.util.ResourceUtils;
025: import org.springframework.util.SystemPropertyUtils;
026:
027: /**
028: * Convenience class that performs custom Log4J initialization for web environments,
029: * allowing for log file paths within the web application, with the option to
030: * perform automatic refresh checks (for runtime changes in logging configuration).
031: *
032: * <p><b>WARNING: Assumes an expanded WAR file</b>, both for loading the configuration
033: * file and for writing the log files. If you want to keep your WAR unexpanded or
034: * don't need application-specific log files within the WAR directory, don't use
035: * Log4J setup within the application (thus, don't use Log4jConfigListener or
036: * Log4jConfigServlet). Instead, use a global, VM-wide Log4J setup (for example,
037: * in JBoss) or JDK 1.4's <code>java.util.logging</code> (which is global too).
038: *
039: * <p>Supports three init parameters at the servlet context level (that is,
040: * context-param entries in web.xml):
041: *
042: * <ul>
043: * <li><i>"log4jConfigLocation":</i><br>
044: * Location of the Log4J config file; either a "classpath:" location (e.g.
045: * "classpath:myLog4j.properties"), an absolute file URL (e.g. "file:C:/log4j.properties),
046: * or a plain path relative to the web application root directory (e.g.
047: * "/WEB-INF/log4j.properties"). If not specified, default Log4J initialization will
048: * apply ("log4j.properties" in the class path; see Log4J documentation for details).
049: * <li><i>"log4jRefreshInterval":</i><br>
050: * Interval between config file refresh checks, in milliseconds. If not specified,
051: * no refresh checks will happen, which avoids starting Log4J's watchdog thread.
052: * <li><i>"log4jExposeWebAppRoot":</i><br>
053: * Whether the web app root system property should be exposed, allowing for log
054: * file paths relative to the web application root directory. Default is "true";
055: * specify "false" to suppress expose of the web app root system property. See
056: * below for details on how to use this system property in log file locations.
057: * </ul>
058: *
059: * <p>Note: <code>initLogging</code> should be called before any other Spring activity
060: * (when using Log4J), for proper initialization before any Spring logging attempts.
061: *
062: * <p>Log4J's watchdog thread will asynchronously check whether the timestamp
063: * of the config file has changed, using the given interval between checks.
064: * A refresh interval of 1000 milliseconds (one second), which allows to
065: * do on-demand log level changes with immediate effect, is not unfeasible.
066:
067: * <p><b>WARNING:</b> Log4J's watchdog thread does not terminate until VM shutdown;
068: * in particular, it does not terminate on LogManager shutdown. Therefore, it is
069: * recommended to <i>not</i> use config file refreshing in a production J2EE
070: * environment; the watchdog thread would not stop on application shutdown there.
071: *
072: * <p>By default, this configurer automatically sets the web app root system property,
073: * for "${key}" substitutions within log file locations in the Log4J config file,
074: * allowing for log file paths relative to the web application root directory.
075: * The default system property key is "webapp.root", to be used in a Log4J config
076: * file like as follows:
077: *
078: * <p><code>log4j.appender.myfile.File=${webapp.root}/WEB-INF/demo.log</code>
079: *
080: * <p>Alternatively, specify a unique context-param "webAppRootKey" per web application.
081: * For example, with "webAppRootKey = "demo.root":
082: *
083: * <p><code>log4j.appender.myfile.File=${demo.root}/WEB-INF/demo.log</code>
084: *
085: * <p><b>WARNING:</b> Some containers (like Tomcat) do <i>not</i> keep system properties
086: * separate per web app. You have to use unique "webAppRootKey" context-params per web
087: * app then, to avoid clashes. Other containers like Resin do isolate each web app's
088: * system properties: Here you can use the default key (i.e. no "webAppRootKey"
089: * context-param at all) without worrying.
090: *
091: * @author Juergen Hoeller
092: * @since 12.08.2003
093: * @see org.springframework.util.Log4jConfigurer
094: * @see Log4jConfigListener
095: * @see Log4jConfigServlet
096: */
097: public abstract class Log4jWebConfigurer {
098:
099: /** Parameter specifying the location of the Log4J config file */
100: public static final String CONFIG_LOCATION_PARAM = "log4jConfigLocation";
101:
102: /** Parameter specifying the refresh interval for checking the Log4J config file */
103: public static final String REFRESH_INTERVAL_PARAM = "log4jRefreshInterval";
104:
105: /** Parameter specifying whether to expose the web app root system property */
106: public static final String EXPOSE_WEB_APP_ROOT_PARAM = "log4jExposeWebAppRoot";
107:
108: /**
109: * Initialize Log4J, including setting the web app root system property.
110: * @param servletContext the current ServletContext
111: * @see WebUtils#setWebAppRootSystemProperty
112: */
113: public static void initLogging(ServletContext servletContext) {
114: // Expose the web app root system property.
115: if (exposeWebAppRoot(servletContext)) {
116: WebUtils.setWebAppRootSystemProperty(servletContext);
117: }
118:
119: // Only perform custom Log4J initialization in case of a config file.
120: String location = servletContext
121: .getInitParameter(CONFIG_LOCATION_PARAM);
122: if (location != null) {
123: // Perform actual Log4J initialization; else rely on Log4J's default initialization.
124: try {
125: // Return a URL (e.g. "classpath:" or "file:") as-is;
126: // consider a plain file path as relative to the web application root directory.
127: if (!ResourceUtils.isUrl(location)) {
128: // Resolve system property placeholders before resolving real path.
129: location = SystemPropertyUtils
130: .resolvePlaceholders(location);
131: location = WebUtils.getRealPath(servletContext,
132: location);
133: }
134:
135: // Write log message to server log.
136: servletContext.log("Initializing Log4J from ["
137: + location + "]");
138:
139: // Check whether refresh interval was specified.
140: String intervalString = servletContext
141: .getInitParameter(REFRESH_INTERVAL_PARAM);
142: if (intervalString != null) {
143: // Initialize with refresh interval, i.e. with Log4J's watchdog thread,
144: // checking the file in the background.
145: try {
146: long refreshInterval = Long
147: .parseLong(intervalString);
148: Log4jConfigurer.initLogging(location,
149: refreshInterval);
150: } catch (NumberFormatException ex) {
151: throw new IllegalArgumentException(
152: "Invalid 'log4jRefreshInterval' parameter: "
153: + ex.getMessage());
154: }
155: } else {
156: // Initialize without refresh check, i.e. without Log4J's watchdog thread.
157: Log4jConfigurer.initLogging(location);
158: }
159: } catch (FileNotFoundException ex) {
160: throw new IllegalArgumentException(
161: "Invalid 'log4jConfigLocation' parameter: "
162: + ex.getMessage());
163: }
164: }
165: }
166:
167: /**
168: * Shut down Log4J, properly releasing all file locks
169: * and resetting the web app root system property.
170: * @param servletContext the current ServletContext
171: * @see WebUtils#removeWebAppRootSystemProperty
172: */
173: public static void shutdownLogging(ServletContext servletContext) {
174: servletContext.log("Shutting down Log4J");
175: try {
176: Log4jConfigurer.shutdownLogging();
177: } finally {
178: // Remove the web app root system property.
179: if (exposeWebAppRoot(servletContext)) {
180: WebUtils.removeWebAppRootSystemProperty(servletContext);
181: }
182: }
183: }
184:
185: /**
186: * Return whether to expose the web app root system property,
187: * checking the corresponding ServletContext init parameter.
188: * @see #EXPOSE_WEB_APP_ROOT_PARAM
189: */
190: private static boolean exposeWebAppRoot(
191: ServletContext servletContext) {
192: String exposeWebAppRootParam = servletContext
193: .getInitParameter(EXPOSE_WEB_APP_ROOT_PARAM);
194: return (exposeWebAppRootParam == null || Boolean.valueOf(
195: exposeWebAppRootParam).booleanValue());
196: }
197:
198: }
|