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