1 package ch.qos.logback.classic.joran;
2
3 import java.io.File;
4 import java.net.URL;
5 import java.util.ArrayList;
6 import java.util.List;
7
8 import ch.qos.logback.classic.LoggerContext;
9 import ch.qos.logback.classic.gaffer.GafferUtil;
10 import ch.qos.logback.classic.util.EnvUtil;
11 import ch.qos.logback.core.CoreConstants;
12 import ch.qos.logback.core.joran.event.SaxEvent;
13 import ch.qos.logback.core.joran.spi.ConfigurationWatchList;
14 import ch.qos.logback.core.joran.spi.JoranException;
15 import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil;
16 import ch.qos.logback.core.spi.ContextAwareBase;
17 import ch.qos.logback.core.status.StatusUtil;
18
19 public class ReconfigureOnChangeTask extends ContextAwareBase implements Runnable {
20
21 public static final String DETECTED_CHANGE_IN_CONFIGURATION_FILES = "Detected change in configuration files.";
22 static final String RE_REGISTERING_PREVIOUS_SAFE_CONFIGURATION = "Re-registering previous fallback configuration once more as a fallback configuration point";
23 static final String FALLING_BACK_TO_SAFE_CONFIGURATION = "Given previous errors, falling back to previously registered safe configuration.";
24
25
26
27 long birthdate = System.currentTimeMillis();
28 List<ReconfigureOnChangeTaskListener> listeners;
29
30
31 void addListener(ReconfigureOnChangeTaskListener listener) {
32 if(listeners==null)
33 listeners = new ArrayList<ReconfigureOnChangeTaskListener>();
34 listeners.add(listener);
35 }
36
37 @Override
38 public void run() {
39 fireEnteredRunMethod();
40
41 ConfigurationWatchList configurationWatchList = ConfigurationWatchListUtil.getConfigurationWatchList(context);
42 if (configurationWatchList == null) {
43 addWarn("Empty ConfigurationWatchList in context");
44 return;
45 }
46
47 List<File> filesToWatch = configurationWatchList.getCopyOfFileWatchList();
48 if (filesToWatch == null || filesToWatch.isEmpty()) {
49 addInfo("Empty watch file list. Disabling ");
50 return;
51 }
52
53 if (!configurationWatchList.changeDetected()) {
54 return;
55 }
56
57 fireChangeDetected();
58 URL mainConfigurationURL = configurationWatchList.getMainURL();
59
60 addInfo(DETECTED_CHANGE_IN_CONFIGURATION_FILES);
61 addInfo(CoreConstants.RESET_MSG_PREFIX + "named [" + context.getName() + "]");
62
63 LoggerContext lc = (LoggerContext) context;
64 if (mainConfigurationURL.toString().endsWith("xml")) {
65 performXMLConfiguration(lc, mainConfigurationURL);
66 } else if (mainConfigurationURL.toString().endsWith("groovy")) {
67 if (EnvUtil.isGroovyAvailable()) {
68 lc.reset();
69
70
71 GafferUtil.runGafferConfiguratorOn(lc, this, mainConfigurationURL);
72 } else {
73 addError("Groovy classes are not available on the class path. ABORTING INITIALIZATION.");
74 }
75 }
76 fireDoneReconfiguring();
77 }
78
79 private void fireEnteredRunMethod() {
80 if(listeners == null)
81 return;
82
83 for(ReconfigureOnChangeTaskListener listener: listeners)
84 listener.enteredRunMethod();
85 }
86
87 private void fireChangeDetected() {
88 if(listeners == null)
89 return;
90
91 for(ReconfigureOnChangeTaskListener listener: listeners)
92 listener.changeDetected();
93 }
94
95
96 private void fireDoneReconfiguring() {
97 if(listeners == null)
98 return;
99
100 for(ReconfigureOnChangeTaskListener listener: listeners)
101 listener.doneReconfiguring();
102 }
103
104 private void performXMLConfiguration(LoggerContext lc, URL mainConfigurationURL) {
105 JoranConfigurator jc = new JoranConfigurator();
106 jc.setContext(context);
107 StatusUtil statusUtil = new StatusUtil(context);
108 List<SaxEvent> eventList = jc.recallSafeConfiguration();
109
110 URL mainURL = ConfigurationWatchListUtil.getMainWatchURL(context);
111 lc.reset();
112 long threshold = System.currentTimeMillis();
113 try {
114 jc.doConfigure(mainConfigurationURL);
115 if (statusUtil.hasXMLParsingErrors(threshold)) {
116 fallbackConfiguration(lc, eventList, mainURL);
117 }
118 } catch (JoranException e) {
119 fallbackConfiguration(lc, eventList, mainURL);
120 }
121 }
122
123 private List<SaxEvent> removeIncludeEvents(List<SaxEvent> unsanitizedEventList) {
124 List<SaxEvent> sanitizedEvents = new ArrayList<SaxEvent>();
125 if (unsanitizedEventList == null)
126 return sanitizedEvents;
127
128 for (SaxEvent e : unsanitizedEventList) {
129 if (!"include".equalsIgnoreCase(e.getLocalName()))
130 sanitizedEvents.add(e);
131
132 }
133 return sanitizedEvents;
134 }
135
136 private void fallbackConfiguration(LoggerContext lc, List<SaxEvent> eventList, URL mainURL) {
137
138
139
140 List<SaxEvent> failsafeEvents = removeIncludeEvents(eventList);
141 JoranConfigurator joranConfigurator = new JoranConfigurator();
142 joranConfigurator.setContext(context);
143 ConfigurationWatchList oldCWL = ConfigurationWatchListUtil.getConfigurationWatchList(context);
144 ConfigurationWatchList newCWL = oldCWL.buildClone();
145
146 if (failsafeEvents == null || failsafeEvents.isEmpty()) {
147 addWarn("No previous configuration to fall back on.");
148 } else {
149 addWarn(FALLING_BACK_TO_SAFE_CONFIGURATION);
150 try {
151 lc.reset();
152 ConfigurationWatchListUtil.registerConfigurationWatchList(context, newCWL);
153 joranConfigurator.doConfigure(failsafeEvents);
154 addInfo(RE_REGISTERING_PREVIOUS_SAFE_CONFIGURATION);
155 joranConfigurator.registerSafeConfiguration(eventList);
156
157 addInfo("after registerSafeConfiguration: " + eventList);
158 } catch (JoranException e) {
159 addError("Unexpected exception thrown by a configuration considered safe.", e);
160 }
161 }
162 }
163
164 @Override
165 public String toString() {
166 return "ReconfigureOnChangeTask(born:" + birthdate + ")";
167 }
168 }
169