1
2
3
4
5
6
7
8
9
10
11 package org.mule.module.launcher.log4j;
12
13 import org.mule.module.launcher.MuleApplicationClassLoader;
14 import org.mule.module.reboot.MuleContainerBootstrapUtils;
15 import org.mule.module.reboot.MuleContainerSystemClassLoader;
16
17 import java.io.File;
18 import java.io.IOException;
19 import java.net.URL;
20 import java.util.concurrent.ConcurrentHashMap;
21 import java.util.concurrent.ConcurrentMap;
22
23 import org.apache.log4j.DailyRollingFileAppender;
24 import org.apache.log4j.Hierarchy;
25 import org.apache.log4j.Level;
26 import org.apache.log4j.Logger;
27 import org.apache.log4j.PatternLayout;
28 import org.apache.log4j.PropertyConfigurator;
29 import org.apache.log4j.helpers.LogLog;
30 import org.apache.log4j.spi.LoggerRepository;
31 import org.apache.log4j.spi.RepositorySelector;
32 import org.apache.log4j.spi.RootLogger;
33 import org.apache.log4j.xml.DOMConfigurator;
34
35 public class ApplicationAwareRepositorySelector implements RepositorySelector
36 {
37 protected static final String PATTERN_LAYOUT = "%-5p %d [%t] %c: %m%n";
38
39 protected static final Integer NO_CCL_CLASSLOADER = 0;
40
41 protected LoggerRepositoryCache cache = new LoggerRepositoryCache();
42
43
44 protected Logger logger = Logger.getLogger(getClass());
45
46 @Override
47 public LoggerRepository getLoggerRepository()
48 {
49 final ClassLoader ccl = Thread.currentThread().getContextClassLoader();
50
51 LoggerRepository repository = cache.getLoggerRepository(ccl);
52 if (repository == null)
53 {
54 final RootLogger root = new RootLogger(Level.INFO);
55 repository = new Hierarchy(root);
56
57 try
58 {
59 ConfigWatchDog configWatchDog = null;
60 if (ccl instanceof MuleApplicationClassLoader)
61 {
62 MuleApplicationClassLoader muleCL = (MuleApplicationClassLoader) ccl;
63
64
65
66 URL appLogConfig = muleCL.findResource("log4j.xml");
67 if (appLogConfig == null)
68 {
69 appLogConfig = muleCL.findResource("log4j.properties");
70 }
71 final String appName = muleCL.getAppName();
72 if (appLogConfig == null)
73 {
74
75 String logName = String.format("mule-app-%s.log", appName);
76 File logDir = new File(MuleContainerBootstrapUtils.getMuleHome(), "logs");
77 File logFile = new File(logDir, logName);
78 DailyRollingFileAppender fileAppender = new DailyRollingFileAppender(new PatternLayout(PATTERN_LAYOUT), logFile.getAbsolutePath(), "'.'yyyy-MM-dd");
79 fileAppender.setAppend(true);
80 fileAppender.activateOptions();
81 root.addAppender(fileAppender);
82 }
83 else
84 {
85 configureFrom(appLogConfig, repository);
86 if (appLogConfig.toExternalForm().startsWith("file:"))
87 {
88
89 configWatchDog = new ConfigWatchDog(muleCL, appLogConfig.getFile(), repository);
90 configWatchDog.setName(String.format("[%s].log4j.config.monitor", appName));
91 }
92 else
93 {
94 if (logger.isInfoEnabled())
95 {
96 logger.info(String.format("Logging config %s is not an external file, will not be monitored for changes", appLogConfig));
97 }
98 }
99 }
100 }
101 else
102 {
103
104 File defaultSystemLog = new File(MuleContainerBootstrapUtils.getMuleHome(), "conf/log4j.xml");
105 if (!defaultSystemLog.exists() && !defaultSystemLog.canRead())
106 {
107 defaultSystemLog = new File(MuleContainerBootstrapUtils.getMuleHome(), "conf/log4j.properties");
108 }
109 configureFrom(defaultSystemLog.toURL(), repository);
110
111
112
113 if (ccl instanceof MuleContainerSystemClassLoader)
114 {
115 configWatchDog = new ConfigWatchDog(ccl, defaultSystemLog.getAbsolutePath(), repository);
116 configWatchDog.setName("Mule.system.log4j.config.monitor");
117 }
118 }
119
120 final LoggerRepository previous = cache.storeLoggerRepository(ccl, repository);
121 if (previous != null)
122 {
123 repository = previous;
124 }
125
126 if (configWatchDog != null)
127 {
128 configWatchDog.start();
129 }
130 }
131 catch (IOException e)
132 {
133 throw new RuntimeException(e);
134 }
135 }
136
137 return repository;
138 }
139
140 protected void configureFrom(URL url, LoggerRepository repository)
141 {
142 if (url.toExternalForm().endsWith(".xml"))
143 {
144 new DOMConfigurator().doConfigure(url, repository);
145 }
146 else
147 {
148 new PropertyConfigurator().doConfigure(url, repository);
149 }
150 }
151
152 protected static class LoggerRepositoryCache
153 {
154 protected ConcurrentMap<Integer, LoggerRepository> repositories = new ConcurrentHashMap<Integer, LoggerRepository>();
155
156 public LoggerRepository getLoggerRepository(ClassLoader classLoader)
157 {
158 return repositories.get(computeKey(classLoader));
159 }
160
161 public LoggerRepository storeLoggerRepository(ClassLoader classLoader, LoggerRepository repository)
162 {
163 return repositories.putIfAbsent(computeKey(classLoader), repository);
164 }
165
166 public void remove(ClassLoader classLoader)
167 {
168 repositories.remove(computeKey(classLoader));
169 }
170
171 protected Integer computeKey(ClassLoader classLoader)
172 {
173 return classLoader == null ? NO_CCL_CLASSLOADER : classLoader.hashCode();
174 }
175 }
176
177
178
179 protected class ConfigWatchDog extends Thread
180 {
181 protected LoggerRepository repository;
182 protected File file;
183 protected long lastModif = 0;
184 protected boolean warnedAlready = false;
185 protected volatile boolean interrupted = false;
186
187
188
189
190
191 static final public long DEFAULT_DELAY = 60000;
192
193
194
195 protected String filename;
196
197
198
199
200
201 protected long delay = DEFAULT_DELAY;
202
203 public ConfigWatchDog(final ClassLoader classLoader, String filename, LoggerRepository repository)
204 {
205 if (classLoader instanceof MuleApplicationClassLoader)
206 {
207 ((MuleApplicationClassLoader) classLoader).addShutdownListener(new MuleApplicationClassLoader.ShutdownListener()
208 {
209 @Override
210 public void execute()
211 {
212 final ClassLoader ccl = Thread.currentThread().getContextClassLoader();
213 ApplicationAwareRepositorySelector.this.cache.remove(ccl);
214 interrupted = true;
215 }
216 });
217 }
218 this.filename = filename;
219 this.file = new File(filename);
220 this.lastModif = file.lastModified();
221 setDaemon(true);
222 this.repository = repository;
223 this.delay = 10000;
224 }
225
226 public void doOnChange()
227 {
228 if (logger.isInfoEnabled())
229 {
230 logger.info("Reconfiguring logging from: " + filename);
231 }
232 if (filename.endsWith(".xml"))
233 {
234 new DOMConfigurator().doConfigure(filename, repository);
235 }
236 else
237 {
238 new PropertyConfigurator().doConfigure(filename, repository);
239 }
240 }
241
242
243
244
245 public void setDelay(long delay)
246 {
247 this.delay = delay;
248 }
249
250 protected void checkAndConfigure()
251 {
252 boolean fileExists;
253 try
254 {
255 fileExists = file.exists();
256 }
257 catch (SecurityException e)
258 {
259 LogLog.warn("Was not allowed to read check file existence, file:[" + filename + "].");
260 interrupted = true;
261 return;
262 }
263
264 if (fileExists)
265 {
266 long l = file.lastModified();
267 if (l > lastModif)
268 {
269 lastModif = l;
270 doOnChange();
271 warnedAlready = false;
272 }
273 }
274 else
275 {
276 if (!warnedAlready)
277 {
278 LogLog.debug("[" + filename + "] does not exist.");
279 warnedAlready = true;
280 }
281 }
282 }
283
284 @Override
285 public void run()
286 {
287 while (!interrupted)
288 {
289 try
290 {
291 Thread.sleep(delay);
292 }
293 catch (InterruptedException e)
294 {
295 interrupted = true;
296 Thread.currentThread().interrupt();
297 break;
298 }
299 checkAndConfigure();
300 }
301 if (logger.isDebugEnabled())
302 {
303 logger.debug(getName() + " terminated successfully");
304 }
305 }
306
307 }
308 }
309