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