1 /**
2  * Logback: the reliable, generic, fast and flexible logging framework.
3  * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4  *
5  * This program and the accompanying materials are dual-licensed under
6  * either the terms of the Eclipse Public License v1.0 as published by
7  * the Eclipse Foundation
8  *
9  *   or (per the licensee's choosing)
10  *
11  * under the terms of the GNU Lesser General Public License version 2.1
12  * as published by the Free Software Foundation.
13  */

14 package ch.qos.logback.classic.joran.action;
15
16 import java.net.URL;
17 import java.util.concurrent.ScheduledExecutorService;
18 import java.util.concurrent.ScheduledFuture;
19 import java.util.concurrent.TimeUnit;
20
21 import org.xml.sax.Attributes;
22
23 import ch.qos.logback.classic.LoggerContext;
24 import ch.qos.logback.classic.joran.ReconfigureOnChangeTask;
25 import ch.qos.logback.classic.util.EnvUtil;
26 import ch.qos.logback.core.CoreConstants;
27 import ch.qos.logback.core.joran.action.Action;
28 import ch.qos.logback.core.joran.spi.InterpretationContext;
29 import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil;
30 import ch.qos.logback.core.status.OnConsoleStatusListener;
31 import ch.qos.logback.core.util.ContextUtil;
32 import ch.qos.logback.core.util.Duration;
33 import ch.qos.logback.core.util.OptionHelper;
34 import ch.qos.logback.core.util.StatusListenerConfigHelper;
35
36 public class ConfigurationAction extends Action {
37     static final String INTERNAL_DEBUG_ATTR = "debug";
38     static final String PACKAGING_DATA_ATTR = "packagingData";
39     static final String SCAN_ATTR = "scan";
40     static final String SCAN_PERIOD_ATTR = "scanPeriod";
41     static final String DEBUG_SYSTEM_PROPERTY_KEY = "logback.debug";
42
43     long threshold = 0;
44
45     public void begin(InterpretationContext ic, String name, Attributes attributes) {
46         threshold = System.currentTimeMillis();
47
48         // See LOGBACK-527 (the system property is looked up first. Thus, it overrides
49         // the equivalent property in the config file. This reversal of scope priority is justified
50         // by the use case: the admin trying to chase rogue config file
51         String debugAttrib = getSystemProperty(DEBUG_SYSTEM_PROPERTY_KEY);
52         if (debugAttrib == null) {
53             debugAttrib = ic.subst(attributes.getValue(INTERNAL_DEBUG_ATTR));
54         }
55
56         if (OptionHelper.isEmpty(debugAttrib) || debugAttrib.equalsIgnoreCase("false") || debugAttrib.equalsIgnoreCase("null")) {
57             addInfo(INTERNAL_DEBUG_ATTR + " attribute not set");
58         } else {
59             StatusListenerConfigHelper.addOnConsoleListenerInstance(context, new OnConsoleStatusListener());
60         }
61
62         processScanAttrib(ic, attributes);
63
64         LoggerContext lc = (LoggerContext) context;
65         boolean packagingData = OptionHelper.toBoolean(ic.subst(attributes.getValue(PACKAGING_DATA_ATTR)), LoggerContext.DEFAULT_PACKAGING_DATA);
66         lc.setPackagingDataEnabled(packagingData);
67
68         if (EnvUtil.isGroovyAvailable()) {
69             ContextUtil contextUtil = new ContextUtil(context);
70             contextUtil.addGroovyPackages(lc.getFrameworkPackages());
71         }
72
73         // the context is turbo filter attachable, so it is pushed on top of the
74         // stack
75         ic.pushObject(getContext());
76     }
77
78     String getSystemProperty(String name) {
79         /*
80          * LOGBACK-743: accessing a system property in the presence of a SecurityManager (e.g. applet sandbox) can
81          * result in a SecurityException.
82          */

83         try {
84             return System.getProperty(name);
85         } catch (SecurityException ex) {
86             return null;
87         }
88     }
89
90     void processScanAttrib(InterpretationContext ic, Attributes attributes) {
91         String scanAttrib = ic.subst(attributes.getValue(SCAN_ATTR));
92         if (!OptionHelper.isEmpty(scanAttrib) && !"false".equalsIgnoreCase(scanAttrib)) {
93
94             ScheduledExecutorService scheduledExecutorService = context.getScheduledExecutorService();
95             URL mainURL = ConfigurationWatchListUtil.getMainWatchURL(context);
96             if (mainURL == null) {
97                 addWarn("Due to missing top level configuration file, reconfiguration on change (configuration file scanning) cannot be done.");
98                 return;
99             }
100             ReconfigureOnChangeTask rocTask = new ReconfigureOnChangeTask();
101             rocTask.setContext(context);
102
103             context.putObject(CoreConstants.RECONFIGURE_ON_CHANGE_TASK, rocTask);
104
105             String scanPeriodAttrib = ic.subst(attributes.getValue(SCAN_PERIOD_ATTR));
106             Duration duration = getDuration(scanAttrib, scanPeriodAttrib);
107
108             if (duration == null) {
109                 return;
110             }
111
112             addInfo("Will scan for changes in [" + mainURL + "] ");
113             // Given that included files are encountered at a later phase, the complete list of files 
114             // to scan can only be determined when the configuration is loaded in full.
115             // However, scan can be active if mainURL is set. Otherwise, when changes are detected
116             // the top level config file cannot be accessed.
117             addInfo("Setting ReconfigureOnChangeTask scanning period to " + duration);
118  
119             ScheduledFuture<?> scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(rocTask, duration.getMilliseconds(), duration.getMilliseconds(),
120                             TimeUnit.MILLISECONDS);
121             context.addScheduledFuture(scheduledFuture);
122         }
123     }
124
125     private Duration getDuration(String scanAttrib, String scanPeriodAttrib) {
126         Duration duration = null;
127
128         if (!OptionHelper.isEmpty(scanPeriodAttrib)) {
129             try {
130                 duration = Duration.valueOf(scanPeriodAttrib);
131
132             } catch (NumberFormatException nfe) {
133                 addError("Error while converting [" + scanAttrib + "] to long", nfe);
134             }
135         }
136         return duration;
137     }
138
139     public void end(InterpretationContext ec, String name) {
140         addInfo("End of configuration.");
141         ec.popObject();
142     }
143 }
144