1
2
3
4
5
6
7
8
9
10
11
12
13
14 package ch.qos.logback.classic.joran;
15
16 import java.io.File;
17 import java.net.URL;
18 import java.util.List;
19 import java.util.concurrent.ScheduledFuture;
20
21 import ch.qos.logback.classic.LoggerContext;
22 import ch.qos.logback.core.CoreConstants;
23 import ch.qos.logback.core.joran.spi.ConfigurationWatchList;
24 import ch.qos.logback.core.joran.spi.JoranException;
25 import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil;
26 import ch.qos.logback.core.model.Model;
27 import ch.qos.logback.core.model.ModelUtil;
28 import ch.qos.logback.core.spi.ConfigurationEvent;
29 import ch.qos.logback.core.spi.ContextAwareBase;
30 import ch.qos.logback.core.status.StatusUtil;
31
32 import static ch.qos.logback.core.spi.ConfigurationEvent.newConfigurationChangeDetectorRunningEvent;
33 import static ch.qos.logback.core.spi.ConfigurationEvent.newConfigurationEndedEvent;
34
35 public class ReconfigureOnChangeTask extends ContextAwareBase implements Runnable {
36
37 public static final String DETECTED_CHANGE_IN_CONFIGURATION_FILES = "Detected change in configuration files.";
38 static final String RE_REGISTERING_PREVIOUS_SAFE_CONFIGURATION = "Re-registering previous fallback configuration once more as a fallback configuration point";
39 static final String FALLING_BACK_TO_SAFE_CONFIGURATION = "Given previous errors, falling back to previously registered safe configuration.";
40
41 long birthdate = System.currentTimeMillis();
42 List<ReconfigureOnChangeTaskListener> listeners = null;
43
44 ScheduledFuture<?> scheduledFuture;
45
46 @Override
47 public void run() {
48 context.fireConfigurationEvent(newConfigurationChangeDetectorRunningEvent(this));
49
50 ConfigurationWatchList configurationWatchList = ConfigurationWatchListUtil.getConfigurationWatchList(context);
51 if (configurationWatchList == null) {
52 addWarn("Empty ConfigurationWatchList in context");
53 return;
54 }
55
56 List<File> filesToWatch = configurationWatchList.getCopyOfFileWatchList();
57 if (filesToWatch == null || filesToWatch.isEmpty()) {
58 addInfo("Empty watch file list. Disabling ");
59 return;
60 }
61
62 if (!configurationWatchList.changeDetected()) {
63 return;
64 }
65 context.fireConfigurationEvent(ConfigurationEvent.newConfigurationChangeDetectedEvent(this));
66 cancelFutureInvocationsOfThisTaskInstance();
67
68 URL mainConfigurationURL = configurationWatchList.getMainURL();
69
70 addInfo(DETECTED_CHANGE_IN_CONFIGURATION_FILES);
71 addInfo(CoreConstants.RESET_MSG_PREFIX + "named [" + context.getName() + "]");
72
73 LoggerContext lc = (LoggerContext) context;
74 if (mainConfigurationURL.toString().endsWith("xml")) {
75 performXMLConfiguration(lc, mainConfigurationURL);
76 } else if (mainConfigurationURL.toString().endsWith("groovy")) {
77 addError("Groovy configuration disabled due to Java 9 compilation issues.");
78 }
79
80 }
81
82 private void cancelFutureInvocationsOfThisTaskInstance() {
83 boolean result = scheduledFuture.cancel(false);
84 if(!result) {
85 addWarn("could not cancel "+ this.toString());
86 }
87 }
88
89 private void performXMLConfiguration(LoggerContext lc, URL mainConfigurationURL) {
90 JoranConfigurator jc = new JoranConfigurator();
91 jc.setContext(context);
92 StatusUtil statusUtil = new StatusUtil(context);
93 Model failsafeTop = jc.recallSafeConfiguration();
94 URL mainURL = ConfigurationWatchListUtil.getMainWatchURL(context);
95 lc.reset();
96 long threshold = System.currentTimeMillis();
97 try {
98 jc.doConfigure(mainConfigurationURL);
99
100 if (statusUtil.hasXMLParsingErrors(threshold)) {
101 fallbackConfiguration(lc, failsafeTop, mainURL);
102 }
103 } catch (JoranException e) {
104 addWarn("Exception occurred during reconfiguration", e);
105 fallbackConfiguration(lc, failsafeTop, mainURL);
106 }
107 }
108
109 private void fallbackConfiguration(LoggerContext lc, Model failsafeTop, URL mainURL) {
110
111
112
113
114 JoranConfigurator joranConfigurator = new JoranConfigurator();
115 joranConfigurator.setContext(context);
116 ConfigurationWatchList oldCWL = ConfigurationWatchListUtil.getConfigurationWatchList(context);
117 ConfigurationWatchList newCWL = oldCWL.buildClone();
118
119 if (failsafeTop == null) {
120 addWarn("No previous configuration to fall back on.");
121 return;
122 } else {
123 addWarn(FALLING_BACK_TO_SAFE_CONFIGURATION);
124 addInfo("Safe model "+failsafeTop);
125 try {
126 lc.reset();
127 ConfigurationWatchListUtil.registerConfigurationWatchList(context, newCWL);
128 ModelUtil.resetForReuse(failsafeTop);
129 joranConfigurator.processModel(failsafeTop);
130 addInfo(RE_REGISTERING_PREVIOUS_SAFE_CONFIGURATION);
131 joranConfigurator.registerSafeConfiguration(failsafeTop);
132 context.fireConfigurationEvent(newConfigurationEndedEvent(this));
133 addInfo("after registerSafeConfiguration");
134 } catch (Exception e) {
135 addError("Unexpected exception thrown by a configuration considered safe.", e);
136 }
137 }
138 }
139
140 @Override
141 public String toString() {
142 return "ReconfigureOnChangeTask(born:" + birthdate + ")";
143 }
144
145
146 public void setScheduredFuture(ScheduledFuture<?> aScheduledFuture) {
147 this.scheduledFuture = aScheduledFuture;
148 }
149 }