View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights
3    * reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License
6    * v1.0 as published by the Eclipse Foundation
7    *
8    * or (per the licensee's choosing)
9    *
10   * under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation.
11   */
12  package ch.qos.logback.core.joran;
13  
14  import static ch.qos.logback.core.CoreConstants.SAFE_JORAN_CONFIGURATION;
15  import static ch.qos.logback.core.spi.ConfigurationEvent.newConfigurationEndedEvent;
16  import static ch.qos.logback.core.spi.ConfigurationEvent.newConfigurationStartedEvent;
17  
18  import java.io.File;
19  import java.io.FileInputStream;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.net.URL;
23  import java.net.URLConnection;
24  import java.util.List;
25  
26  import org.xml.sax.InputSource;
27  
28  import ch.qos.logback.core.Context;
29  import ch.qos.logback.core.joran.event.SaxEvent;
30  import ch.qos.logback.core.joran.event.SaxEventRecorder;
31  import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry;
32  import ch.qos.logback.core.joran.spi.ElementPath;
33  import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext;
34  import ch.qos.logback.core.joran.spi.JoranException;
35  import ch.qos.logback.core.joran.spi.RuleStore;
36  import ch.qos.logback.core.joran.spi.SaxEventInterpreter;
37  import ch.qos.logback.core.joran.spi.SimpleRuleStore;
38  import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil;
39  import ch.qos.logback.core.model.Model;
40  import ch.qos.logback.core.model.processor.DefaultProcessor;
41  import ch.qos.logback.core.model.processor.ModelInterpretationContext;
42  import ch.qos.logback.core.spi.ContextAwareBase;
43  import ch.qos.logback.core.spi.ErrorCodes;
44  import ch.qos.logback.core.status.StatusUtil;
45  
46  public abstract class GenericXMLConfigurator extends ContextAwareBase {
47  
48      protected SaxEventInterpreter saxEventInterpreter;
49      protected ModelInterpretationContext modelInterpretationContext;
50  
51      public ModelInterpretationContext getModelInterpretationContext() {
52          return this.modelInterpretationContext;
53      }
54      private RuleStore ruleStore;
55  
56      public final void doConfigure(URL url) throws JoranException {
57          InputStream in = null;
58          try {
59              informContextOfURLUsedForConfiguration(getContext(), url);
60              URLConnection urlConnection = url.openConnection();
61              // per http://jira.qos.ch/browse/LBCORE-105
62              // per http://jira.qos.ch/browse/LBCORE-127
63              urlConnection.setUseCaches(false);
64  
65              in = urlConnection.getInputStream();
66              doConfigure(in, url.toExternalForm());
67          } catch (IOException ioe) {
68              String errMsg = "Could not open URL [" + url + "].";
69              addError(errMsg, ioe);
70              throw new JoranException(errMsg, ioe);
71          } finally {
72              if (in != null) {
73                  try {
74                      in.close();
75                  } catch (IOException ioe) {
76                      String errMsg = "Could not close input stream";
77                      addError(errMsg, ioe);
78                      throw new JoranException(errMsg, ioe);
79                  }
80              }
81          }
82      }
83  
84      public final void doConfigure(String filename) throws JoranException {
85          doConfigure(new File(filename));
86      }
87  
88      public final void doConfigure(File file) throws JoranException {
89          FileInputStream fis = null;
90          try {
91              URL url = file.toURI().toURL();
92              informContextOfURLUsedForConfiguration(getContext(), url);
93              fis = new FileInputStream(file);
94              doConfigure(fis, url.toExternalForm());
95          } catch (IOException ioe) {
96              String errMsg = "Could not open [" + file.getPath() + "].";
97              addError(errMsg, ioe);
98              throw new JoranException(errMsg, ioe);
99          } finally {
100             if (fis != null) {
101                 try {
102                     fis.close();
103                 } catch (java.io.IOException ioe) {
104                     String errMsg = "Could not close [" + file.getName() + "].";
105                     addError(errMsg, ioe);
106                     throw new JoranException(errMsg, ioe);
107                 }
108             }
109         }
110     }
111 
112     public static void informContextOfURLUsedForConfiguration(Context context, URL url) {
113         ConfigurationWatchListUtil.setMainWatchURL(context, url);
114     }
115 
116     public final void doConfigure(InputStream inputStream) throws JoranException {
117         doConfigure(new InputSource(inputStream));
118     }
119 
120     public final void doConfigure(InputStream inputStream, String systemId) throws JoranException {
121         InputSource inputSource = new InputSource(inputStream);
122         inputSource.setSystemId(systemId);
123         doConfigure(inputSource);
124     }
125 
126     protected abstract void addElementSelectorAndActionAssociations(RuleStore rs);
127 
128     protected abstract void setImplicitRuleSupplier(SaxEventInterpreter interpreter);
129 
130     protected void addDefaultNestedComponentRegistryRules(DefaultNestedComponentRegistry registry) {
131         // nothing by default
132     }
133 
134     protected ElementPath initialElementPath() {
135         return new ElementPath();
136     }
137 
138     protected void buildSaxEventInterpreter(List<SaxEvent> saxEvents) {
139         RuleStore rs = getRuleStore();
140         addElementSelectorAndActionAssociations(rs);
141         this.saxEventInterpreter = new SaxEventInterpreter(context, rs, initialElementPath(), saxEvents);
142         SaxEventInterpretationContext interpretationContext = saxEventInterpreter.getSaxEventInterpretationContext();
143         interpretationContext.setContext(context);
144         setImplicitRuleSupplier(saxEventInterpreter);
145     }
146 
147     public RuleStore getRuleStore() {
148         if(this.ruleStore == null) {
149             this.ruleStore = new SimpleRuleStore(context);
150         }
151         return this.ruleStore;
152     }
153 
154     protected void buildModelInterpretationContext() {
155         this.modelInterpretationContext = new ModelInterpretationContext(context);
156         addDefaultNestedComponentRegistryRules(modelInterpretationContext.getDefaultNestedComponentRegistry());
157     }
158 
159     // this is the most inner form of doConfigure whereto other doConfigure
160     // methods ultimately delegate
161     public final void doConfigure(final InputSource inputSource) throws JoranException {
162 
163         context.fireConfigurationEvent(newConfigurationStartedEvent(this));
164         long threshold = System.currentTimeMillis();
165 
166         SaxEventRecorder recorder = populateSaxEventRecorder(inputSource);
167         List<SaxEvent> saxEvents = recorder.getSaxEventList();
168         if (saxEvents.isEmpty()) {
169             addWarn("Empty sax event list");
170             return;
171         }
172         Model top = buildModelFromSaxEventList(recorder.getSaxEventList());
173         if (top == null) {
174             addError(ErrorCodes.EMPTY_MODEL_STACK);
175             return;
176         }
177         sanityCheck(top);
178         processModel(top);
179 
180         // no exceptions at this level
181         StatusUtil statusUtil = new StatusUtil(context);
182         if (statusUtil.noXMLParsingErrorsOccurred(threshold)) {
183             addInfo("Registering current configuration as safe fallback point");
184             registerSafeConfiguration(top);
185         }
186         context.fireConfigurationEvent(newConfigurationEndedEvent(this));
187 
188     }
189 
190     public SaxEventRecorder populateSaxEventRecorder(final InputSource inputSource) throws JoranException {
191         SaxEventRecorder recorder = new SaxEventRecorder(context);
192         recorder.recordEvents(inputSource);
193         return recorder;
194     }
195 
196     public Model buildModelFromSaxEventList(List<SaxEvent> saxEvents) throws JoranException {
197         buildSaxEventInterpreter(saxEvents);
198         playSaxEvents();
199         Model top = saxEventInterpreter.getSaxEventInterpretationContext().peekModel();
200         return top;
201     }
202 
203     private void playSaxEvents() throws JoranException {
204         saxEventInterpreter.getEventPlayer().play();
205     }
206 
207     public void processModel(Model model) {
208         buildModelInterpretationContext();
209         this.modelInterpretationContext.setTopModel(model);
210         modelInterpretationContext.setConfiguratorHint(this);
211         DefaultProcessor defaultProcessor = new DefaultProcessor(context, this.modelInterpretationContext);
212         addModelHandlerAssociations(defaultProcessor);
213 
214         // disallow simultaneous configurations of the same context
215         synchronized (context.getConfigurationLock()) {
216             defaultProcessor.process(model);
217         }
218     }
219 
220     /**
221      * Perform sanity check and issue warning if necessary.
222      *
223      * Default implementation does nothing.
224      *
225      * @param topModel
226      * @since 1.3.2 and 1.4.2
227      */
228     protected void sanityCheck(Model topModel) {
229 
230     }
231 
232     protected void addModelHandlerAssociations(DefaultProcessor defaultProcessor) {
233     }
234 
235     /**
236      * Register the current event list in currently in the interpreter as a safe
237      * configuration point.
238      *
239      * @since 0.9.30
240      */
241     public void registerSafeConfiguration(Model top) {
242         context.putObject(SAFE_JORAN_CONFIGURATION, top);
243     }
244 
245     /**
246      * Recall the event list previously registered as a safe point.
247      */
248     public Model recallSafeConfiguration() {
249         return (Model) context.getObject(SAFE_JORAN_CONFIGURATION);
250     }
251 }