View Javadoc

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