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.*;
16  
17  import java.io.File;
18  import java.io.FileInputStream;
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.net.URL;
22  import java.net.URLConnection;
23  import java.util.List;
24  import java.util.concurrent.locks.ReentrantLock;
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 URL getTopURL() {
57          return topURL;
58      }
59  
60      public void setTopURL(URL topURL) {
61          this.topURL = topURL;
62      }
63  
64      URL topURL;
65  
66      public final void doConfigure(URL url) throws JoranException {
67          InputStream in = null;
68          try {
69              topURL = url;
70              URLConnection urlConnection = url.openConnection();
71              // per http://jira.qos.ch/browse/LOGBACK-117  LBCORE-105
72              // per http://jira.qos.ch/browse/LOGBACK-163  LBCORE-127
73              urlConnection.setUseCaches(false);
74  
75              in = urlConnection.getInputStream();
76              doConfigure(in, url.toExternalForm());
77          } catch (IOException ioe) {
78              String errMsg = "Could not open URL [" + url + "].";
79              addError(errMsg, ioe);
80              throw new JoranException(errMsg, ioe);
81          } finally {
82              if (in != null) {
83                  try {
84                      in.close();
85                  } catch (IOException ioe) {
86                      String errMsg = "Could not close input stream";
87                      addError(errMsg, ioe);
88                      throw new JoranException(errMsg, ioe);
89                  }
90              }
91          }
92      }
93  
94      public final void doConfigure(String filename) throws JoranException {
95          doConfigure(new File(filename));
96      }
97  
98      public final void doConfigure(File file) throws JoranException {
99          FileInputStream fis = null;
100         try {
101             URL url = file.toURI().toURL();
102             topURL = url;
103             fis = new FileInputStream(file);
104             doConfigure(fis, url.toExternalForm());
105         } catch (IOException ioe) {
106             String errMsg = "Could not open [" + file.getPath() + "].";
107             addError(errMsg, ioe);
108             throw new JoranException(errMsg, ioe);
109         } finally {
110             if (fis != null) {
111                 try {
112                     fis.close();
113                 } catch (java.io.IOException ioe) {
114                     String errMsg = "Could not close [" + file.getName() + "].";
115                     addError(errMsg, ioe);
116                     throw new JoranException(errMsg, ioe);
117                 }
118             }
119         }
120     }
121 
122     /**
123      * Removed in 1.5.27 with no replacement.
124      *
125      * @deprecated
126      */
127     @Deprecated
128     public static void informContextOfURLUsedForConfiguration(Context context, URL url) {
129         //
130     }
131 
132     public final void doConfigure(InputStream inputStream) throws JoranException {
133         doConfigure(new InputSource(inputStream));
134     }
135 
136     public final void doConfigure(InputStream inputStream, String systemId) throws JoranException {
137         InputSource inputSource = new InputSource(inputStream);
138         inputSource.setSystemId(systemId);
139         doConfigure(inputSource);
140     }
141 
142     protected abstract void addElementSelectorAndActionAssociations(RuleStore rs);
143 
144     protected abstract void setImplicitRuleSupplier(SaxEventInterpreter interpreter);
145 
146     protected void addDefaultNestedComponentRegistryRules(DefaultNestedComponentRegistry registry) {
147         // nothing by default
148     }
149 
150     protected ElementPath initialElementPath() {
151         return new ElementPath();
152     }
153 
154     protected void buildSaxEventInterpreter(List<SaxEvent> saxEvents) {
155         RuleStore rs = getRuleStore();
156         addElementSelectorAndActionAssociations(rs);
157         this.saxEventInterpreter = new SaxEventInterpreter(context, rs, initialElementPath(), saxEvents);
158         SaxEventInterpretationContext interpretationContext = saxEventInterpreter.getSaxEventInterpretationContext();
159         interpretationContext.setContext(context);
160         setImplicitRuleSupplier(saxEventInterpreter);
161     }
162 
163     public RuleStore getRuleStore() {
164         if(this.ruleStore == null) {
165             this.ruleStore = new SimpleRuleStore(context);
166         }
167         return this.ruleStore;
168     }
169 
170     protected void buildModelInterpretationContext() {
171         this.modelInterpretationContext = new ModelInterpretationContext(context);
172         this.modelInterpretationContext.setTopURL(topURL);
173         addDefaultNestedComponentRegistryRules(modelInterpretationContext.getDefaultNestedComponentRegistry());
174     }
175 
176     // this is the most inner form of doConfigure whereto other doConfigure
177     // methods ultimately delegate
178     public final void doConfigure(final InputSource inputSource) throws JoranException {
179 
180         context.fireConfigurationEvent(newConfigurationStartedEvent(this));
181         long threshold = System.currentTimeMillis();
182 
183         SaxEventRecorder recorder = populateSaxEventRecorder(inputSource);
184         List<SaxEvent> saxEvents = recorder.getSaxEventList();
185         if (saxEvents.isEmpty()) {
186             addWarn("Empty sax event list");
187             return;
188         }
189         Model top = buildModelFromSaxEventList(recorder.getSaxEventList());
190         if (top == null) {
191             addError(ErrorCodes.EMPTY_MODEL_STACK);
192             return;
193         }
194         sanityCheck(top);
195         processModel(top);
196 
197         // no exceptions at this level
198         StatusUtil statusUtil = new StatusUtil(context);
199         if (statusUtil.noXMLParsingErrorsOccurred(threshold)) {
200             addInfo("Registering current configuration as safe fallback point");
201             registerSafeConfiguration(top);
202             context.fireConfigurationEvent(newConfigurationEndedSuccessfullyEvent(this));
203         } else {
204             context.fireConfigurationEvent(newConfigurationEndedWithXMLParsingErrorsEvent(this));
205         }
206 
207 
208     }
209 
210     public SaxEventRecorder populateSaxEventRecorder(final InputSource inputSource) throws JoranException {
211         SaxEventRecorder recorder = new SaxEventRecorder(context);
212         recorder.recordEvents(inputSource);
213         return recorder;
214     }
215 
216     public Model buildModelFromSaxEventList(List<SaxEvent> saxEvents) throws JoranException {
217         buildSaxEventInterpreter(saxEvents);
218         playSaxEvents();
219         Model top = saxEventInterpreter.getSaxEventInterpretationContext().peekModel();
220         return top;
221     }
222 
223     private void playSaxEvents() throws JoranException {
224         saxEventInterpreter.getEventPlayer().play();
225     }
226 
227     public void processModel(Model model) {
228         buildModelInterpretationContext();
229         this.modelInterpretationContext.setTopModel(model);
230         modelInterpretationContext.setConfiguratorHint(this);
231         DefaultProcessor defaultProcessor = new DefaultProcessor(context, this.modelInterpretationContext);
232         addModelHandlerAssociations(defaultProcessor);
233 
234         // disallow simultaneous configurations of the same context
235         ReentrantLock configurationLock   = context.getConfigurationLock();
236 
237         try {
238             configurationLock.lock();
239             defaultProcessor.process(model);
240         } finally {
241             configurationLock.unlock();
242         }
243     }
244 
245     /**
246      * Perform sanity check and issue warning if necessary.
247      *
248      * Default implementation does nothing.
249      *
250      * @param topModel
251      * @since 1.3.2 and 1.4.2
252      */
253     protected void sanityCheck(Model topModel) {
254 
255     }
256 
257     protected void addModelHandlerAssociations(DefaultProcessor defaultProcessor) {
258     }
259 
260     /**
261      * Register the current event list in currently in the interpreter as a safe
262      * configuration point.
263      *
264      * @since 0.9.30
265      */
266     public void registerSafeConfiguration(Model top) {
267         context.putObject(SAFE_JORAN_CONFIGURATION, top);
268     }
269 
270     /**
271      * Recall the event list previously registered as a safe point.
272      */
273     public Model recallSafeConfiguration() {
274         return (Model) context.getObject(SAFE_JORAN_CONFIGURATION);
275     }
276 
277 }