View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.joran.action;
15  
16  import java.io.File;
17  import java.io.IOException;
18  import java.io.InputStream;
19  import java.net.MalformedURLException;
20  import java.net.URI;
21  import java.net.URL;
22  import java.util.List;
23  
24  import org.xml.sax.Attributes;
25  
26  import ch.qos.logback.core.joran.event.SaxEvent;
27  import ch.qos.logback.core.joran.event.SaxEventRecorder;
28  import ch.qos.logback.core.joran.spi.ActionException;
29  import ch.qos.logback.core.joran.spi.InterpretationContext;
30  import ch.qos.logback.core.joran.spi.JoranException;
31  import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil;
32  import ch.qos.logback.core.util.Loader;
33  import ch.qos.logback.core.util.OptionHelper;
34  
35  public class IncludeAction extends Action {
36  
37      private static final String INCLUDED_TAG = "included";
38      private static final String FILE_ATTR = "file";
39      private static final String URL_ATTR = "url";
40      private static final String RESOURCE_ATTR = "resource";
41      private static final String OPTIONAL_ATTR = "optional";
42  
43      private String attributeInUse;
44      private boolean optional;
45  
46      @Override
47      public void begin(InterpretationContext ec, String name, Attributes attributes) throws ActionException {
48  
49          SaxEventRecorder recorder = new SaxEventRecorder(context);
50  
51          this.attributeInUse = null;
52          this.optional = OptionHelper.toBoolean(attributes.getValue(OPTIONAL_ATTR), false);
53  
54          if (!checkAttributes(attributes)) {
55              return;
56          }
57  
58          InputStream in = getInputStream(ec, attributes);
59  
60          try {
61              if (in != null) {
62                  parseAndRecord(in, recorder);
63                  // remove the <included> tag from the beginning and </included> from the end
64                  trimHeadAndTail(recorder);
65  
66                  // offset = 2, because we need to get past this element as well as the end element
67                  ec.getJoranInterpreter().getEventPlayer().addEventsDynamically(recorder.saxEventList, 2);
68              }
69          } catch (JoranException e) {
70              addError("Error while parsing  " + attributeInUse, e);
71          } finally {
72              close(in);
73          }
74  
75      }
76  
77      void close(InputStream in) {
78          if (in != null) {
79              try {
80                  in.close();
81              } catch (IOException e) {
82              }
83          }
84      }
85  
86      private boolean checkAttributes(Attributes attributes) {
87          String fileAttribute = attributes.getValue(FILE_ATTR);
88          String urlAttribute = attributes.getValue(URL_ATTR);
89          String resourceAttribute = attributes.getValue(RESOURCE_ATTR);
90  
91          int count = 0;
92  
93          if (!OptionHelper.isEmpty(fileAttribute)) {
94              count++;
95          }
96          if (!OptionHelper.isEmpty(urlAttribute)) {
97              count++;
98          }
99          if (!OptionHelper.isEmpty(resourceAttribute)) {
100             count++;
101         }
102 
103         if (count == 0) {
104             addError("One of \"path\", \"resource\" or \"url\" attributes must be set.");
105             return false;
106         } else if (count > 1) {
107             addError("Only one of \"file\", \"url\" or \"resource\" attributes should be set.");
108             return false;
109         } else if (count == 1) {
110             return true;
111         }
112         throw new IllegalStateException("Count value [" + count + "] is not expected");
113     }
114 
115     URL attributeToURL(String urlAttribute) {
116         try {
117             return new URL(urlAttribute);
118         } catch (MalformedURLException mue) {
119             String errMsg = "URL [" + urlAttribute + "] is not well formed.";
120             addError(errMsg, mue);
121             return null;
122         }
123     }
124 
125     InputStream openURL(URL url) {
126         try {
127             return url.openStream();
128         } catch (IOException e) {
129             optionalWarning("Failed to open [" + url.toString() + "]");
130             return null;
131         }
132     }
133 
134     URL resourceAsURL(String resourceAttribute) {
135         URL url = Loader.getResourceBySelfClassLoader(resourceAttribute);
136         if (url == null) {
137             optionalWarning("Could not find resource corresponding to [" + resourceAttribute + "]");
138             return null;
139         } else
140             return url;
141     }
142 
143     private void optionalWarning(String msg) {
144         if (!optional) {
145             addWarn(msg);
146         }
147     }
148 
149     URL filePathAsURL(String path) {
150         URI uri = new File(path).toURI();
151         try {
152             return uri.toURL();
153         } catch (MalformedURLException e) {
154             // impossible to get here
155             e.printStackTrace();
156             return null;
157         }
158     }
159 
160     URL getInputURL(InterpretationContext ec, Attributes attributes) {
161         String fileAttribute = attributes.getValue(FILE_ATTR);
162         String urlAttribute = attributes.getValue(URL_ATTR);
163         String resourceAttribute = attributes.getValue(RESOURCE_ATTR);
164 
165         if (!OptionHelper.isEmpty(fileAttribute)) {
166             this.attributeInUse = ec.subst(fileAttribute);
167             return filePathAsURL(attributeInUse);
168         }
169 
170         if (!OptionHelper.isEmpty(urlAttribute)) {
171             this.attributeInUse = ec.subst(urlAttribute);
172             return attributeToURL(attributeInUse);
173         }
174 
175         if (!OptionHelper.isEmpty(resourceAttribute)) {
176             this.attributeInUse = ec.subst(resourceAttribute);
177             return resourceAsURL(attributeInUse);
178         }
179         // given previous checkAttributes() check we cannot reach this line
180         throw new IllegalStateException("A URL stream should have been returned");
181 
182     }
183 
184     InputStream getInputStream(InterpretationContext ec, Attributes attributes) {
185         URL inputURL = getInputURL(ec, attributes);
186         if (inputURL == null)
187             return null;
188 
189         ConfigurationWatchListUtil.addToWatchList(context, inputURL);
190         return openURL(inputURL);
191     }
192 
193     private void trimHeadAndTail(SaxEventRecorder recorder) {
194         // Let's remove the two <included> events before
195         // adding the events to the player.
196 
197         List<SaxEvent> saxEventList = recorder.saxEventList;
198 
199         if (saxEventList.size() == 0) {
200             return;
201         }
202 
203         SaxEvent first = saxEventList.get(0);
204         if (first != null && first.qName.equalsIgnoreCase(INCLUDED_TAG)) {
205             saxEventList.remove(0);
206         }
207 
208         SaxEvent last = saxEventList.get(recorder.saxEventList.size() - 1);
209         if (last != null && last.qName.equalsIgnoreCase(INCLUDED_TAG)) {
210             saxEventList.remove(recorder.saxEventList.size() - 1);
211         }
212     }
213 
214     private void parseAndRecord(InputStream inputSource, SaxEventRecorder recorder) throws JoranException {
215         recorder.setContext(context);
216         recorder.recordEvents(inputSource);
217     }
218 
219     @Override
220     public void end(InterpretationContext ec, String name) throws ActionException {
221         // do nothing
222     }
223 
224 }