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.event;
15  
16  import static ch.qos.logback.core.CoreConstants.XML_PARSING;
17  
18  import java.io.IOException;
19  import java.io.InputStream;
20  import java.util.ArrayList;
21  import java.util.List;
22  
23  import javax.xml.parsers.ParserConfigurationException;
24  import javax.xml.parsers.SAXParser;
25  import javax.xml.parsers.SAXParserFactory;
26  
27  import org.xml.sax.Attributes;
28  import org.xml.sax.InputSource;
29  import org.xml.sax.Locator;
30  import org.xml.sax.SAXException;
31  import org.xml.sax.SAXParseException;
32  import org.xml.sax.helpers.DefaultHandler;
33  
34  import ch.qos.logback.core.Context;
35  import ch.qos.logback.core.joran.spi.ElementPath;
36  import ch.qos.logback.core.joran.spi.JoranException;
37  import ch.qos.logback.core.spi.ContextAware;
38  import ch.qos.logback.core.spi.ContextAwareImpl;
39  import ch.qos.logback.core.status.Status;
40  
41  public class SaxEventRecorder extends DefaultHandler implements ContextAware {
42  
43      final ContextAwareImpl contextAwareImpl;
44      final ElementPath elementPath;
45      List<SaxEvent> saxEventList = new ArrayList<SaxEvent>();
46      Locator locator;
47  
48      public SaxEventRecorder(Context context) {
49          this(context, new ElementPath());
50      }
51  
52      public SaxEventRecorder(Context context, ElementPath elementPath) {
53          contextAwareImpl = new ContextAwareImpl(context, this);
54          this.elementPath = elementPath;
55      }
56  
57      final public void recordEvents(InputStream inputStream) throws JoranException {
58          recordEvents(new InputSource(inputStream));
59      }
60  
61      public void recordEvents(InputSource inputSource) throws JoranException {
62          SAXParser saxParser = buildSaxParser();
63          try {
64              saxParser.parse(inputSource, this);
65              return;
66          } catch (IOException ie) {
67              handleError("I/O error occurred while parsing xml file", ie);
68          } catch (SAXException se) {
69              // Exception added into StatusManager via Sax error handling. No need to add it again
70              throw new JoranException("Problem parsing XML document. See previously reported errors.", se);
71          } catch (Exception ex) {
72              handleError("Unexpected exception while parsing XML document.", ex);
73          }
74          throw new IllegalStateException("This point can never be reached");
75      }
76  
77      private void handleError(String errMsg, Throwable t) throws JoranException {
78          addError(errMsg, t);
79          throw new JoranException(errMsg, t);
80      }
81  
82      private SAXParser buildSaxParser() throws JoranException {
83          try {
84              SAXParserFactory spf = SAXParserFactory.newInstance();
85              spf.setValidating(false);
86              // spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
87              // See LOGBACK-1465
88              spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
89              spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
90              spf.setNamespaceAware(true);
91              return spf.newSAXParser();
92          } catch (ParserConfigurationException pce) {
93              String errMsg = "Error during SAX paser configuration. See https://logback.qos.ch/codes.html#saxParserConfiguration";
94              addError(errMsg, pce);
95              throw new JoranException(errMsg, pce);
96          }  catch (SAXException pce) {
97              String errMsg = "Error during parser creation or parser configuration";
98              addError(errMsg, pce);
99              throw new JoranException(errMsg, pce);
100         } 
101     }
102 
103     public void startDocument() {
104     }
105 
106     public Locator getLocator() {
107         return locator;
108     }
109 
110     public void setDocumentLocator(Locator l) {
111         locator = l;
112     }
113 
114     protected boolean shouldIgnoreForElementPath(String tagName) {
115         return false;
116     }
117 
118     public void startElement(String namespaceURI, String localName, String qName, Attributes atts) {
119 
120         String tagName = getTagName(localName, qName);
121         if (!shouldIgnoreForElementPath(tagName)) {
122             elementPath.push(tagName);
123         }
124         ElementPath current = elementPath.duplicate();
125         saxEventList.add(new StartEvent(current, namespaceURI, localName, qName, atts, getLocator()));
126     }
127 
128     public void characters(char[] ch, int start, int length) {
129         String bodyStr = new String(ch, start, length);
130         SaxEvent lastEvent = getLastEvent();
131         if (lastEvent instanceof BodyEvent) {
132             BodyEvent be = (BodyEvent) lastEvent;
133             be.append(bodyStr);
134         } else {
135             // ignore space only text if the previous event is not a BodyEvent
136             if (!isSpaceOnly(bodyStr)) {
137                 saxEventList.add(new BodyEvent(bodyStr, getLocator()));
138             }
139         }
140     }
141 
142     boolean isSpaceOnly(String bodyStr) {
143         String bodyTrimmed = bodyStr.trim();
144         return (bodyTrimmed.length() == 0);
145     }
146 
147     SaxEvent getLastEvent() {
148         if (saxEventList.isEmpty()) {
149             return null;
150         }
151         int size = saxEventList.size();
152         return saxEventList.get(size - 1);
153     }
154 
155     public void endElement(String namespaceURI, String localName, String qName) {
156         saxEventList.add(new EndEvent(namespaceURI, localName, qName, getLocator()));
157         String tagName = getTagName(localName, qName);
158         if (!shouldIgnoreForElementPath(tagName)) {
159             elementPath.pop();
160         }
161     }
162 
163     String getTagName(String localName, String qName) {
164         String tagName = localName;
165         if ((tagName == null) || (tagName.length() < 1)) {
166             tagName = qName;
167         }
168         return tagName;
169     }
170 
171     public void error(SAXParseException spe) throws SAXException {
172         addError(XML_PARSING + " - Parsing error on line " + spe.getLineNumber() + " and column "
173                 + spe.getColumnNumber());
174         addError(spe.toString());
175     }
176 
177     public void fatalError(SAXParseException spe) throws SAXException {
178         addError(XML_PARSING + " - Parsing fatal error on line " + spe.getLineNumber() + " and column "
179                 + spe.getColumnNumber());
180         addError(spe.toString());
181     }
182 
183     public void warning(SAXParseException spe) throws SAXException {
184         addWarn(XML_PARSING + " - Parsing warning on line " + spe.getLineNumber() + " and column "
185                 + spe.getColumnNumber(), spe);
186     }
187 
188     public void addError(String msg) {
189         contextAwareImpl.addError(msg);
190     }
191 
192     public void addError(String msg, Throwable ex) {
193         contextAwareImpl.addError(msg, ex);
194     }
195 
196     public void addInfo(String msg) {
197         contextAwareImpl.addInfo(msg);
198     }
199 
200     public void addInfo(String msg, Throwable ex) {
201         contextAwareImpl.addInfo(msg, ex);
202     }
203 
204     public void addStatus(Status status) {
205         contextAwareImpl.addStatus(status);
206     }
207 
208     public void addWarn(String msg) {
209         contextAwareImpl.addWarn(msg);
210     }
211 
212     public void addWarn(String msg, Throwable ex) {
213         contextAwareImpl.addWarn(msg, ex);
214     }
215 
216     public Context getContext() {
217         return contextAwareImpl.getContext();
218     }
219 
220     public void setContext(Context context) {
221         contextAwareImpl.setContext(context);
222     }
223 
224     public List<SaxEvent> getSaxEventList() {
225         return saxEventList;
226     }
227 
228 }