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.CoreConstants.PROPERTIES_FILE_EXTENSION;
33 import static ch.qos.logback.core.spi.ConfigurationEvent.*;
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 public static final String RE_REGISTERING_PREVIOUS_SAFE_CONFIGURATION = "Re-registering previous fallback configuration once more as a fallback configuration point";
39 public 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 if (configurationWatchList.emptyWatchLists()) {
57 addInfo("Both watch lists are empty. Disabling ");
58 return;
59 }
60
61 File changedFile = configurationWatchList.changeDetectedInFile();
62 URL changedURL = configurationWatchList.changeDetectedInURL();
63
64 if (changedFile == null && changedURL == null) {
65 return;
66 }
67
68 context.fireConfigurationEvent(ConfigurationEvent.newConfigurationChangeDetectedEvent(this));
69 addInfo(DETECTED_CHANGE_IN_CONFIGURATION_FILES);
70 if(changedFile != null) {
71 changeInFile(changedFile, configurationWatchList);
72 }
73
74 if(changedURL != null) {
75 changeInURL(changedURL);
76 }
77 }
78
79 private void changeInURL(URL url) {
80 String path = url.getPath();
81 if(path.endsWith(PROPERTIES_FILE_EXTENSION)) {
82 runPropertiesConfigurator(url);
83 }
84 }
85 private void changeInFile(File changedFile, ConfigurationWatchList configurationWatchList) {
86
87 if(changedFile.getName().endsWith(PROPERTIES_FILE_EXTENSION)) {
88 runPropertiesConfigurator(changedFile);
89 return;
90 }
91
92
93 addInfo(CoreConstants.RESET_MSG_PREFIX + "named [" + context.getName() + "]");
94 cancelFutureInvocationsOfThisTaskInstance();
95 URL mainConfigurationURL = configurationWatchList.getTopURL();
96
97 LoggerContext lc = (LoggerContext) context;
98 if (mainConfigurationURL.toString().endsWith("xml")) {
99 performXMLConfiguration(lc, mainConfigurationURL);
100 }
101 }
102
103 private void runPropertiesConfigurator(Object changedObject) {
104 addInfo("Will run PropertyConfigurator on "+changedObject);
105 PropertiesConfigurator propertiesConfigurator = new PropertiesConfigurator();
106 propertiesConfigurator.setContext(context);
107 try {
108 if(changedObject instanceof File) {
109 File changedFile = (File) changedObject;
110 propertiesConfigurator.doConfigure(changedFile);
111 } else if(changedObject instanceof URL) {
112 URL changedURL = (URL) changedObject;
113 propertiesConfigurator.doConfigure(changedURL);
114 }
115 context.fireConfigurationEvent(newPartialConfigurationEndedSuccessfullyEvent(this));
116 } catch (JoranException e) {
117 addError("Failed to reload "+ changedObject);
118 }
119 }
120
121 private void cancelFutureInvocationsOfThisTaskInstance() {
122 boolean result = scheduledFuture.cancel(false);
123 if(!result) {
124 addWarn("could not cancel "+ this.toString());
125 }
126 }
127
128 private void performXMLConfiguration(LoggerContext loggerContext, URL mainConfigurationURL) {
129
130 JoranConfigurator jc = new JoranConfigurator();
131 jc.setContext(loggerContext);
132 StatusUtil statusUtil = new StatusUtil(loggerContext);
133 Model failsafeTop = jc.recallSafeConfiguration();
134 URL topURL = ConfigurationWatchListUtil.getMainWatchURL(context);
135 addInfo("Resetting loggerContext ["+loggerContext.getName()+"]");
136 loggerContext.reset();
137 long threshold = System.currentTimeMillis();
138 try {
139 jc.doConfigure(mainConfigurationURL);
140
141 if (statusUtil.hasXMLParsingErrors(threshold)) {
142 fallbackConfiguration(loggerContext, failsafeTop, topURL);
143 }
144 } catch (JoranException e) {
145 addWarn("Exception occurred during reconfiguration", e);
146 fallbackConfiguration(loggerContext, failsafeTop, topURL);
147 }
148 }
149
150 private void fallbackConfiguration(LoggerContext loggerContext, Model failsafeTopModel, URL topURL) {
151
152
153
154
155 JoranConfigurator joranConfigurator = new JoranConfigurator();
156 joranConfigurator.setContext(loggerContext);
157 joranConfigurator.setTopURL(topURL);
158
159
160
161
162
163 if (failsafeTopModel == null) {
164 addWarn("No previous configuration to fall back on.");
165 return;
166 } else {
167 addWarn(FALLING_BACK_TO_SAFE_CONFIGURATION);
168 addInfo("Safe model "+failsafeTopModel);
169 try {
170 loggerContext.reset();
171
172 ModelUtil.resetForReuse(failsafeTopModel);
173 joranConfigurator.processModel(failsafeTopModel);
174 addInfo(RE_REGISTERING_PREVIOUS_SAFE_CONFIGURATION);
175 joranConfigurator.registerSafeConfiguration(failsafeTopModel);
176 context.fireConfigurationEvent(newConfigurationEndedSuccessfullyEvent(this));
177 } catch (Exception e) {
178 addError("Unexpected exception thrown by a configuration considered safe.", e);
179 }
180 }
181 }
182
183 @Override
184 public String toString() {
185 return "ReconfigureOnChangeTask(born:" + birthdate + ")";
186 }
187
188
189
190
191
192
193 @Deprecated
194 public void setScheduredFuture(ScheduledFuture<?> aScheduledFuture) {
195 setScheduledFuture(aScheduledFuture);
196 }
197
198
199
200
201
202
203 public void setScheduledFuture(ScheduledFuture<?> aScheduledFuture) {
204 this.scheduledFuture = aScheduledFuture;
205 }
206 }