1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.web;
18
19 import java.net.URI;
20 import java.net.URL;
21 import java.util.Map;
22 import java.util.concurrent.ConcurrentHashMap;
23 import javax.servlet.ServletContext;
24
25 import org.apache.logging.log4j.LogManager;
26 import org.apache.logging.log4j.Logger;
27 import org.apache.logging.log4j.core.AbstractLifeCycle;
28 import org.apache.logging.log4j.core.LoggerContext;
29 import org.apache.logging.log4j.core.config.Configurator;
30 import org.apache.logging.log4j.core.impl.ContextAnchor;
31 import org.apache.logging.log4j.core.impl.Log4jContextFactory;
32 import org.apache.logging.log4j.core.lookup.Interpolator;
33 import org.apache.logging.log4j.core.lookup.StrSubstitutor;
34 import org.apache.logging.log4j.core.selector.ContextSelector;
35 import org.apache.logging.log4j.core.selector.NamedContextSelector;
36 import org.apache.logging.log4j.core.util.FileUtils;
37 import org.apache.logging.log4j.core.util.Loader;
38 import org.apache.logging.log4j.core.util.NetUtils;
39 import org.apache.logging.log4j.core.util.SetUtils;
40 import org.apache.logging.log4j.spi.LoggerContextFactory;
41 import org.apache.logging.log4j.status.StatusLogger;
42
43
44
45
46 final class Log4jWebInitializerImpl extends AbstractLifeCycle implements Log4jWebLifeCycle {
47
48 private static final Logger LOGGER = StatusLogger.getLogger();
49
50 private static final long serialVersionUID = 1L;
51
52 static {
53 if (Loader.isClassAvailable("org.apache.logging.log4j.core.web.JNDIContextFilter")) {
54 throw new IllegalStateException("You are using Log4j 2 in a web application with the old, extinct " +
55 "log4j-web artifact. This is not supported and could cause serious runtime problems. Please" +
56 "remove the log4j-web JAR file from your application.");
57 }
58 }
59
60 private final Map<String, String> map = new ConcurrentHashMap<String, String>();
61 private final StrSubstitutor substitutor = new StrSubstitutor(new Interpolator(map));
62 private final ServletContext servletContext;
63
64 private String name;
65 private NamedContextSelector namedContextSelector;
66 private LoggerContext loggerContext;
67
68 private Log4jWebInitializerImpl(final ServletContext servletContext) {
69 this.servletContext = servletContext;
70 this.map.put("hostName", NetUtils.getLocalHostname());
71 }
72
73
74
75
76
77
78
79
80
81
82 protected static Log4jWebInitializerImpl initialize(final ServletContext servletContext) {
83 final Log4jWebInitializerImpl initializer = new Log4jWebInitializerImpl(servletContext);
84 servletContext.setAttribute(SUPPORT_ATTRIBUTE, initializer);
85 return initializer;
86 }
87
88 @Override
89 public synchronized void start() {
90 if (this.isStopped() || this.isStopping()) {
91 throw new IllegalStateException("Cannot start this Log4jWebInitializerImpl after it was stopped.");
92 }
93
94
95 if (this.isInitialized()) {
96 super.setStarting();
97
98 this.name = this.substitutor.replace(this.servletContext.getInitParameter(LOG4J_CONTEXT_NAME));
99 final String location =
100 this.substitutor.replace(this.servletContext.getInitParameter(LOG4J_CONFIG_LOCATION));
101 final boolean isJndi =
102 "true".equalsIgnoreCase(this.servletContext.getInitParameter(IS_LOG4J_CONTEXT_SELECTOR_NAMED));
103
104 if (isJndi) {
105 this.initializeJndi(location);
106 } else {
107 this.initializeNonJndi(location);
108 }
109
110 this.servletContext.setAttribute(CONTEXT_ATTRIBUTE, this.loggerContext);
111 super.setStarted();
112 }
113 }
114
115 private void initializeJndi(final String location) {
116 final URI configLocation = getConfigURI(location);
117
118 if (this.name == null) {
119 throw new IllegalStateException("A log4jContextName context parameter is required");
120 }
121
122 LoggerContext context;
123 final LoggerContextFactory factory = LogManager.getFactory();
124 if (factory instanceof Log4jContextFactory) {
125 final ContextSelector selector = ((Log4jContextFactory) factory).getSelector();
126 if (selector instanceof NamedContextSelector) {
127 this.namedContextSelector = (NamedContextSelector) selector;
128 context = this.namedContextSelector.locateContext(this.name, this.servletContext, configLocation);
129 ContextAnchor.THREAD_CONTEXT.set(context);
130 if (context.isInitialized()) {
131 context.start();
132 }
133 ContextAnchor.THREAD_CONTEXT.remove();
134 } else {
135 LOGGER.warn("Potential problem: Selector is not an instance of NamedContextSelector.");
136 return;
137 }
138 } else {
139 LOGGER.warn("Potential problem: LoggerContextFactory is not an instance of Log4jContextFactory.");
140 return;
141 }
142 this.loggerContext = context;
143 LOGGER.debug("Created logger context for [{}] using [{}].", this.name, context.getClass().getClassLoader());
144 }
145
146 private void initializeNonJndi(final String location) {
147 if (this.name == null) {
148 this.name = this.servletContext.getServletContextName();
149 }
150
151 if (this.name == null && location == null) {
152 LOGGER.error("No Log4j context configuration provided. This is very unusual.");
153 return;
154 }
155
156 final URI uri = getConfigURI(location);
157 this.loggerContext = Configurator.initialize(this.name, this.getClassLoader(), uri, this.servletContext);
158 }
159
160 private URI getConfigURI(final String location) {
161 try {
162 String configLocation = location;
163 if (configLocation == null) {
164 final String[] paths = SetUtils.prefixSet(servletContext.getResourcePaths("/WEB-INF/"), "/WEB-INF/log4j2");
165 if (paths.length == 1) {
166 configLocation = paths[0];
167 } else if (paths.length > 1) {
168 final String prefix = "/WEB-INF/log4j2-" + this.name + ".";
169 boolean found = false;
170 for (final String str : paths) {
171 if (str.startsWith(prefix)) {
172 configLocation = str;
173 found = true;
174 break;
175 }
176 }
177 if (!found) {
178 configLocation = paths[0];
179 }
180 }
181 }
182 if (configLocation != null) {
183 final URL url = servletContext.getResource(configLocation);
184 if (url != null) {
185 return url.toURI();
186 }
187 }
188 } catch (final Exception ex) {
189
190 }
191 if (location != null) {
192 try {
193 return FileUtils.getCorrectedFilePathUri(location);
194 } catch (final Exception e) {
195 LOGGER.error("Unable to convert configuration location [{}] to a URI", location, e);
196 }
197 }
198 return null;
199 }
200
201 @Override
202 public synchronized void stop() {
203 if (!this.isStarted() && !this.isStopped()) {
204 throw new IllegalStateException("Cannot stop this Log4jWebInitializer because it has not started.");
205 }
206
207
208 if (this.isStarted()) {
209 this.setStopping();
210 if (this.loggerContext != null) {
211 LOGGER.debug("Removing LoggerContext for [{}].", this.name);
212 this.servletContext.removeAttribute(CONTEXT_ATTRIBUTE);
213 if (this.namedContextSelector != null) {
214 this.namedContextSelector.removeContext(this.name);
215 }
216 this.loggerContext.stop();
217 this.loggerContext.setExternalContext(null);
218 this.loggerContext = null;
219 }
220 this.setStopped();
221 }
222 }
223
224 @Override
225 public void setLoggerContext() {
226 if (this.loggerContext != null) {
227 ContextAnchor.THREAD_CONTEXT.set(this.loggerContext);
228 }
229 }
230
231 @Override
232 public void clearLoggerContext() {
233 ContextAnchor.THREAD_CONTEXT.remove();
234 }
235
236 @Override
237 public void wrapExecution(final Runnable runnable) {
238 this.setLoggerContext();
239
240 try {
241 runnable.run();
242 } finally {
243 this.clearLoggerContext();
244 }
245 }
246
247 private ClassLoader getClassLoader() {
248 try {
249
250
251
252 return this.servletContext.getClassLoader();
253 } catch (final Throwable ignore) {
254
255 return Log4jWebInitializerImpl.class.getClassLoader();
256 }
257 }
258
259 }