1
17 package org.apache.juli;
18
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.FilePermission;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.net.URL;
25 import java.net.URLClassLoader;
26 import java.security.AccessControlException;
27 import java.security.AccessController;
28 import java.security.Permission;
29 import java.security.PrivilegedAction;
30 import java.util.Collections;
31 import java.util.Enumeration;
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.Map;
35 import java.util.Properties;
36 import java.util.StringTokenizer;
37 import java.util.WeakHashMap;
38 import java.util.concurrent.ConcurrentHashMap;
39 import java.util.logging.Handler;
40 import java.util.logging.Level;
41 import java.util.logging.LogManager;
42 import java.util.logging.Logger;
43
44
45
52 public class ClassLoaderLogManager extends LogManager {
53
54 private static final boolean isJava9;
55
56 private static ThreadLocal<Boolean> addingLocalRootLogger = new ThreadLocal<Boolean>() {
57 @Override
58 protected Boolean initialValue() {
59 return Boolean.FALSE;
60 }
61 };
62
63 public static final String DEBUG_PROPERTY =
64 ClassLoaderLogManager.class.getName() + ".debug";
65
66 static {
67 Class<?> c = null;
68 try {
69 c = Class.forName("java.lang.Runtime$Version");
70 } catch (ClassNotFoundException e) {
71
72 }
73 isJava9 = c != null;
74 }
75
76 private final class Cleaner extends Thread {
77
78 @Override
79 public void run() {
80 if (useShutdownHook) {
81 shutdown();
82 }
83 }
84
85 }
86
87
88
89
90 public ClassLoaderLogManager() {
91 super();
92 try {
93 Runtime.getRuntime().addShutdownHook(new Cleaner());
94 } catch (IllegalStateException ise) {
95
96 }
97 }
98
99
100
101
102
103
108 protected final Map<ClassLoader, ClassLoaderLogInfo> classLoaderLoggers =
109 new WeakHashMap<>();
110
111
112
116 protected final ThreadLocal<String> prefix = new ThreadLocal<>();
117
118
119
125 protected volatile boolean useShutdownHook = true;
126
127
128
129
130
131 public boolean isUseShutdownHook() {
132 return useShutdownHook;
133 }
134
135
136 public void setUseShutdownHook(boolean useShutdownHook) {
137 this.useShutdownHook = useShutdownHook;
138 }
139
140
141
142
143
144
149 @Override
150 public synchronized boolean addLogger(final Logger logger) {
151
152 final String loggerName = logger.getName();
153
154 ClassLoader classLoader =
155 Thread.currentThread().getContextClassLoader();
156 ClassLoaderLogInfo info = getClassLoaderInfo(classLoader);
157 if (info.loggers.containsKey(loggerName)) {
158 return false;
159 }
160 info.loggers.put(loggerName, logger);
161
162
163 final String levelString = getProperty(loggerName + ".level");
164 if (levelString != null) {
165 try {
166 AccessController.doPrivileged(new PrivilegedAction<Void>() {
167 @Override
168 public Void run() {
169 logger.setLevel(Level.parse(levelString.trim()));
170 return null;
171 }
172 });
173 } catch (IllegalArgumentException e) {
174
175 }
176 }
177
178
179
180 int dotIndex = loggerName.lastIndexOf('.');
181 if (dotIndex >= 0) {
182 final String parentName = loggerName.substring(0, dotIndex);
183 Logger.getLogger(parentName);
184 }
185
186
187 LogNode node = info.rootNode.findNode(loggerName);
188 node.logger = logger;
189
190
191 Logger parentLogger = node.findParentLogger();
192 if (parentLogger != null) {
193 doSetParentLogger(logger, parentLogger);
194 }
195
196
197 node.setParentLogger(logger);
198
199
200
201 String handlers = getProperty(loggerName + ".handlers");
202 if (handlers != null) {
203 logger.setUseParentHandlers(false);
204 StringTokenizer tok = new StringTokenizer(handlers, ",");
205 while (tok.hasMoreTokens()) {
206 String handlerName = (tok.nextToken().trim());
207 Handler handler = null;
208 ClassLoader current = classLoader;
209 while (current != null) {
210 info = classLoaderLoggers.get(current);
211 if (info != null) {
212 handler = info.handlers.get(handlerName);
213 if (handler != null) {
214 break;
215 }
216 }
217 current = current.getParent();
218 }
219 if (handler != null) {
220 logger.addHandler(handler);
221 }
222 }
223 }
224
225
226
227
228 String useParentHandlersString = getProperty(loggerName + ".useParentHandlers");
229 if (Boolean.parseBoolean(useParentHandlersString)) {
230 logger.setUseParentHandlers(true);
231 }
232
233 return true;
234 }
235
236
237
246 @Override
247 public synchronized Logger getLogger(final String name) {
248 ClassLoader classLoader = Thread.currentThread()
249 .getContextClassLoader();
250 return getClassLoaderInfo(classLoader).loggers.get(name);
251 }
252
253
254
258 @Override
259 public synchronized Enumeration<String> getLoggerNames() {
260 ClassLoader classLoader = Thread.currentThread()
261 .getContextClassLoader();
262 return Collections.enumeration(getClassLoaderInfo(classLoader).loggers.keySet());
263 }
264
265
266
272 @Override
273 public String getProperty(String name) {
274
275
276
277 if (".handlers".equals(name) && !addingLocalRootLogger.get().booleanValue()) {
278 return null;
279 }
280
281 String prefix = this.prefix.get();
282 String result = null;
283
284
285 if (prefix != null) {
286 result = findProperty(prefix + name);
287 }
288
289
290
291 if (result == null) {
292 result = findProperty(name);
293 }
294
295
296 if (result != null) {
297 result = replace(result);
298 }
299 return result;
300 }
301
302
303 private synchronized String findProperty(String name) {
304 ClassLoader classLoader = Thread.currentThread()
305 .getContextClassLoader();
306 ClassLoaderLogInfo info = getClassLoaderInfo(classLoader);
307 String result = info.props.getProperty(name);
308
309
310
311 if ((result == null) && (info.props.isEmpty())) {
312 ClassLoader current = classLoader.getParent();
313 while (current != null) {
314 info = classLoaderLoggers.get(current);
315 if (info != null) {
316 result = info.props.getProperty(name);
317 if ((result != null) || (!info.props.isEmpty())) {
318 break;
319 }
320 }
321 current = current.getParent();
322 }
323 if (result == null) {
324 result = super.getProperty(name);
325 }
326 }
327 return result;
328 }
329
330 @Override
331 public void readConfiguration()
332 throws IOException, SecurityException {
333
334 checkAccess();
335
336 readConfiguration(Thread.currentThread().getContextClassLoader());
337
338 }
339
340 @Override
341 public void readConfiguration(InputStream is)
342 throws IOException, SecurityException {
343
344 checkAccess();
345 reset();
346
347 readConfiguration(is, Thread.currentThread().getContextClassLoader());
348
349 }
350
351 @Override
352 public void reset() throws SecurityException {
353 Thread thread = Thread.currentThread();
354 if (thread.getClass().getName().startsWith(
355 "java.util.logging.LogManager$")) {
356
357
358 return;
359 }
360 ClassLoader classLoader = thread.getContextClassLoader();
361 ClassLoaderLogInfo clLogInfo = getClassLoaderInfo(classLoader);
362 resetLoggers(clLogInfo);
363
364
365
366
367
368 }
369
370
373 public synchronized void shutdown() {
374
375
376 for (ClassLoaderLogInfo clLogInfo : classLoaderLoggers.values()) {
377 resetLoggers(clLogInfo);
378 }
379 }
380
381
382 private void resetLoggers(ClassLoaderLogInfo clLogInfo) {
383
384
385
386
387
388
389 synchronized (clLogInfo) {
390 for (Logger logger : clLogInfo.loggers.values()) {
391 Handler[] handlers = logger.getHandlers();
392 for (Handler handler : handlers) {
393 logger.removeHandler(handler);
394 }
395 }
396 for (Handler handler : clLogInfo.handlers.values()) {
397 try {
398 handler.close();
399 } catch (Exception e) {
400
401 }
402 }
403 clLogInfo.handlers.clear();
404 }
405 }
406
407
408
409
410
418 protected synchronized ClassLoaderLogInfo getClassLoaderInfo(ClassLoader classLoader) {
419
420 if (classLoader == null) {
421 classLoader = ClassLoader.getSystemClassLoader();
422 }
423 ClassLoaderLogInfo info = classLoaderLoggers.get(classLoader);
424 if (info == null) {
425 final ClassLoader classLoaderParam = classLoader;
426 AccessController.doPrivileged(new PrivilegedAction<Void>() {
427 @Override
428 public Void run() {
429 try {
430 readConfiguration(classLoaderParam);
431 } catch (IOException e) {
432
433 }
434 return null;
435 }
436 });
437 info = classLoaderLoggers.get(classLoader);
438 }
439 return info;
440 }
441
442
443
449 protected synchronized void readConfiguration(ClassLoader classLoader)
450 throws IOException {
451
452 InputStream is = null;
453
454
455 try {
456 if (classLoader instanceof WebappProperties) {
457 if (((WebappProperties) classLoader).hasLoggingConfig()) {
458 is = classLoader.getResourceAsStream("logging.properties");
459 }
460 } else if (classLoader instanceof URLClassLoader) {
461 URL logConfig = ((URLClassLoader)classLoader).findResource("logging.properties");
462
463 if(null != logConfig) {
464 if(Boolean.getBoolean(DEBUG_PROPERTY))
465 System.err.println(getClass().getName()
466 + ".readConfiguration(): "
467 + "Found logging.properties at "
468 + logConfig);
469
470 is = classLoader.getResourceAsStream("logging.properties");
471 } else {
472 if(Boolean.getBoolean(DEBUG_PROPERTY))
473 System.err.println(getClass().getName()
474 + ".readConfiguration(): "
475 + "Found no logging.properties");
476 }
477 }
478 } catch (AccessControlException ace) {
479
480
481 ClassLoaderLogInfo info = classLoaderLoggers.get(ClassLoader.getSystemClassLoader());
482 if (info != null) {
483 Logger log = info.loggers.get("");
484 if (log != null) {
485 Permission perm = ace.getPermission();
486 if (perm instanceof FilePermission && perm.getActions().equals("read")) {
487 log.warning("Reading " + perm.getName() + " is not permitted. See \"per context logging\" in the default catalina.policy file.");
488 }
489 else {
490 log.warning("Reading logging.properties is not permitted in some context. See \"per context logging\" in the default catalina.policy file.");
491 log.warning("Original error was: " + ace.getMessage());
492 }
493 }
494 }
495 }
496 if ((is == null) && (classLoader == ClassLoader.getSystemClassLoader())) {
497 String configFileStr = System.getProperty("java.util.logging.config.file");
498 if (configFileStr != null) {
499 try {
500 is = new FileInputStream(replace(configFileStr));
501 } catch (IOException e) {
502 System.err.println("Configuration error");
503 e.printStackTrace();
504 }
505 }
506
507 if (is == null) {
508 File defaultFile = new File(new File(System.getProperty("java.home"),
509 isJava9 ? "conf" : "lib"),
510 "logging.properties");
511 try {
512 is = new FileInputStream(defaultFile);
513 } catch (IOException e) {
514 System.err.println("Configuration error");
515 e.printStackTrace();
516 }
517 }
518 }
519
520 Logger localRootLogger = new RootLogger();
521 if (is == null) {
522
523 ClassLoader current = classLoader.getParent();
524 ClassLoaderLogInfo info = null;
525 while (current != null && info == null) {
526 info = getClassLoaderInfo(current);
527 current = current.getParent();
528 }
529 if (info != null) {
530 localRootLogger.setParent(info.rootNode.logger);
531 }
532 }
533 ClassLoaderLogInfo info =
534 new ClassLoaderLogInfo(new LogNode(null, localRootLogger));
535 classLoaderLoggers.put(classLoader, info);
536
537 if (is != null) {
538 readConfiguration(is, classLoader);
539 }
540 try {
541
542
543 addingLocalRootLogger.set(Boolean.TRUE);
544 addLogger(localRootLogger);
545 } finally {
546 addingLocalRootLogger.set(Boolean.FALSE);
547 }
548 }
549
550
551
558 protected synchronized void readConfiguration(InputStream is, ClassLoader classLoader)
559 throws IOException {
560
561 ClassLoaderLogInfo info = classLoaderLoggers.get(classLoader);
562
563 try {
564 info.props.load(is);
565 } catch (IOException e) {
566
567 System.err.println("Configuration error");
568 e.printStackTrace();
569 } finally {
570 try {
571 is.close();
572 } catch (IOException ioe) {
573
574 }
575 }
576
577
578 String rootHandlers = info.props.getProperty(".handlers");
579 String handlers = info.props.getProperty("handlers");
580 Logger localRootLogger = info.rootNode.logger;
581 if (handlers != null) {
582 StringTokenizer tok = new StringTokenizer(handlers, ",");
583 while (tok.hasMoreTokens()) {
584 String handlerName = (tok.nextToken().trim());
585 String handlerClassName = handlerName;
586 String prefix = "";
587 if (handlerClassName.length() <= 0) {
588 continue;
589 }
590
591
592 if (Character.isDigit(handlerClassName.charAt(0))) {
593 int pos = handlerClassName.indexOf('.');
594 if (pos >= 0) {
595 prefix = handlerClassName.substring(0, pos + 1);
596 handlerClassName = handlerClassName.substring(pos + 1);
597 }
598 }
599 try {
600 this.prefix.set(prefix);
601 Handler handler = (Handler) classLoader.loadClass(
602 handlerClassName).getConstructor().newInstance();
603
604
605
606 this.prefix.set(null);
607 info.handlers.put(handlerName, handler);
608 if (rootHandlers == null) {
609 localRootLogger.addHandler(handler);
610 }
611 } catch (Exception e) {
612
613 System.err.println("Handler error");
614 e.printStackTrace();
615 }
616 }
617
618 }
619
620 }
621
622
623
629 protected static void doSetParentLogger(final Logger logger,
630 final Logger parent) {
631 AccessController.doPrivileged(new PrivilegedAction<Void>() {
632 @Override
633 public Void run() {
634 logger.setParent(parent);
635 return null;
636 }
637 });
638 }
639
640
641
647 protected String replace(String str) {
648 String result = str;
649 int pos_start = str.indexOf("${");
650 if (pos_start >= 0) {
651 StringBuilder builder = new StringBuilder();
652 int pos_end = -1;
653 while (pos_start >= 0) {
654 builder.append(str, pos_end + 1, pos_start);
655 pos_end = str.indexOf('}', pos_start + 2);
656 if (pos_end < 0) {
657 pos_end = pos_start - 1;
658 break;
659 }
660 String propName = str.substring(pos_start + 2, pos_end);
661
662 String replacement = replaceWebApplicationProperties(propName);
663 if (replacement == null) {
664 replacement = propName.length() > 0 ? System.getProperty(propName) : null;
665 }
666 if (replacement != null) {
667 builder.append(replacement);
668 } else {
669 builder.append(str, pos_start, pos_end + 1);
670 }
671 pos_start = str.indexOf("${", pos_end + 1);
672 }
673 builder.append(str, pos_end + 1, str.length());
674 result = builder.toString();
675 }
676 return result;
677 }
678
679
680 private String replaceWebApplicationProperties(String propName) {
681 ClassLoader cl = Thread.currentThread().getContextClassLoader();
682 if (cl instanceof WebappProperties) {
683 WebappProperties wProps = (WebappProperties) cl;
684 if ("classloader.webappName".equals(propName)) {
685 return wProps.getWebappName();
686 } else if ("classloader.hostName".equals(propName)) {
687 return wProps.getHostName();
688 } else if ("classloader.serviceName".equals(propName)) {
689 return wProps.getServiceName();
690 } else {
691 return null;
692 }
693 } else {
694 return null;
695 }
696 }
697
698
699
700
701
702 protected static final class LogNode {
703 Logger logger;
704
705 final Map<String, LogNode> children = new HashMap<>();
706
707 final LogNode parent;
708
709 LogNode(final LogNode parent, final Logger logger) {
710 this.parent = parent;
711 this.logger = logger;
712 }
713
714 LogNode(final LogNode parent) {
715 this(parent, null);
716 }
717
718 LogNode findNode(String name) {
719 LogNode currentNode = this;
720 if (logger.getName().equals(name)) {
721 return this;
722 }
723 while (name != null) {
724 final int dotIndex = name.indexOf('.');
725 final String nextName;
726 if (dotIndex < 0) {
727 nextName = name;
728 name = null;
729 } else {
730 nextName = name.substring(0, dotIndex);
731 name = name.substring(dotIndex + 1);
732 }
733 LogNode childNode = currentNode.children.get(nextName);
734 if (childNode == null) {
735 childNode = new LogNode(currentNode);
736 currentNode.children.put(nextName, childNode);
737 }
738 currentNode = childNode;
739 }
740 return currentNode;
741 }
742
743 Logger findParentLogger() {
744 Logger logger = null;
745 LogNode node = parent;
746 while (node != null && logger == null) {
747 logger = node.logger;
748 node = node.parent;
749 }
750 return logger;
751 }
752
753 void setParentLogger(final Logger parent) {
754 for (final Iterator<LogNode> iter =
755 children.values().iterator(); iter.hasNext();) {
756 final LogNode childNode = iter.next();
757 if (childNode.logger == null) {
758 childNode.setParentLogger(parent);
759 } else {
760 doSetParentLogger(childNode.logger, parent);
761 }
762 }
763 }
764
765 }
766
767
768
769
770
771 protected static final class ClassLoaderLogInfo {
772 final LogNode rootNode;
773 final Map<String, Logger> loggers = new ConcurrentHashMap<>();
774 final Map<String, Handler> handlers = new HashMap<>();
775 final Properties props = new Properties();
776
777 ClassLoaderLogInfo(final LogNode rootNode) {
778 this.rootNode = rootNode;
779 }
780
781 }
782
783
784
785
786
787
791 protected static class RootLogger extends Logger {
792 public RootLogger() {
793 super("", null);
794 }
795 }
796
797
798 }
799