001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.core.joran.event; 015 016import static ch.qos.logback.core.CoreConstants.XML_PARSING; 017 018import java.io.IOException; 019import java.io.InputStream; 020import java.util.ArrayList; 021import java.util.List; 022 023import javax.xml.parsers.ParserConfigurationException; 024import javax.xml.parsers.SAXParser; 025import javax.xml.parsers.SAXParserFactory; 026 027import org.xml.sax.Attributes; 028import org.xml.sax.InputSource; 029import org.xml.sax.Locator; 030import org.xml.sax.SAXException; 031import org.xml.sax.SAXParseException; 032import org.xml.sax.helpers.DefaultHandler; 033 034import ch.qos.logback.core.Context; 035import ch.qos.logback.core.joran.spi.ElementPath; 036import ch.qos.logback.core.joran.spi.JoranException; 037import ch.qos.logback.core.spi.ContextAware; 038import ch.qos.logback.core.spi.ContextAwareImpl; 039import ch.qos.logback.core.status.Status; 040 041public class SaxEventRecorder extends DefaultHandler implements ContextAware { 042 043 final ContextAwareImpl contextAwareImpl; 044 final ElementPath elementPath; 045 List<SaxEvent> saxEventList = new ArrayList<SaxEvent>(); 046 Locator locator; 047 048 public SaxEventRecorder(Context context) { 049 this(context, new ElementPath()); 050 } 051 052 public SaxEventRecorder(Context context, ElementPath elementPath) { 053 contextAwareImpl = new ContextAwareImpl(context, this); 054 this.elementPath = elementPath; 055 } 056 057 final public void recordEvents(InputStream inputStream) throws JoranException { 058 recordEvents(new InputSource(inputStream)); 059 } 060 061 public void recordEvents(InputSource inputSource) throws JoranException { 062 SAXParser saxParser = buildSaxParser(); 063 try { 064 saxParser.parse(inputSource, this); 065 return; 066 } catch (IOException ie) { 067 handleError("I/O error occurred while parsing xml file", ie); 068 } catch (SAXException se) { 069 // Exception added into StatusManager via Sax error handling. No need to add it 070 // again 071 throw new JoranException("Problem parsing XML document. See previously reported errors.", se); 072 } catch (Exception ex) { 073 handleError("Unexpected exception while parsing XML document.", ex); 074 } 075 throw new IllegalStateException("This point can never be reached"); 076 } 077 078 private void handleError(String errMsg, Throwable t) throws JoranException { 079 addError(errMsg, t); 080 throw new JoranException(errMsg, t); 081 } 082 083 private SAXParser buildSaxParser() throws JoranException { 084 try { 085 SAXParserFactory spf = SAXParserFactory.newInstance(); 086 spf.setValidating(false); 087 // spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); 088 // See LOGBACK-1465 089 spf.setFeature("http://xml.org/sax/features/external-general-entities", false); 090 spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); 091 spf.setNamespaceAware(true); 092 return spf.newSAXParser(); 093 } catch (ParserConfigurationException pce) { 094 String errMsg = "Error during SAX paser configuration. See https://logback.qos.ch/codes.html#saxParserConfiguration"; 095 addError(errMsg, pce); 096 throw new JoranException(errMsg, pce); 097 } catch (SAXException pce) { 098 String errMsg = "Error during parser creation or parser configuration"; 099 addError(errMsg, pce); 100 throw new JoranException(errMsg, pce); 101 } 102 } 103 104 public void startDocument() { 105 } 106 107 public Locator getLocator() { 108 return locator; 109 } 110 111 public void setDocumentLocator(Locator l) { 112 locator = l; 113 } 114 115 protected boolean shouldIgnoreForElementPath(String tagName) { 116 return false; 117 } 118 119 public void startElement(String namespaceURI, String localName, String qName, Attributes atts) { 120 121 String tagName = getTagName(localName, qName); 122 if (!shouldIgnoreForElementPath(tagName)) { 123 elementPath.push(tagName); 124 } 125 ElementPath current = elementPath.duplicate(); 126 saxEventList.add(new StartEvent(current, namespaceURI, localName, qName, atts, getLocator())); 127 } 128 129 public void characters(char[] ch, int start, int length) { 130 String bodyStr = new String(ch, start, length); 131 SaxEvent lastEvent = getLastEvent(); 132 if (lastEvent instanceof BodyEvent) { 133 BodyEvent be = (BodyEvent) lastEvent; 134 be.append(bodyStr); 135 } else { 136 // ignore space only text if the previous event is not a BodyEvent 137 if (!isSpaceOnly(bodyStr)) { 138 saxEventList.add(new BodyEvent(bodyStr, getLocator())); 139 } 140 } 141 } 142 143 boolean isSpaceOnly(String bodyStr) { 144 String bodyTrimmed = bodyStr.trim(); 145 return (bodyTrimmed.length() == 0); 146 } 147 148 SaxEvent getLastEvent() { 149 if (saxEventList.isEmpty()) { 150 return null; 151 } 152 int size = saxEventList.size(); 153 return saxEventList.get(size - 1); 154 } 155 156 public void endElement(String namespaceURI, String localName, String qName) { 157 saxEventList.add(new EndEvent(namespaceURI, localName, qName, getLocator())); 158 String tagName = getTagName(localName, qName); 159 if (!shouldIgnoreForElementPath(tagName)) { 160 elementPath.pop(); 161 } 162 } 163 164 String getTagName(String localName, String qName) { 165 String tagName = localName; 166 if ((tagName == null) || (tagName.length() < 1)) { 167 tagName = qName; 168 } 169 return tagName; 170 } 171 172 public void error(SAXParseException spe) throws SAXException { 173 addError(XML_PARSING + " - Parsing error on line " + spe.getLineNumber() + " and column " 174 + spe.getColumnNumber()); 175 addError(spe.toString()); 176 } 177 178 public void fatalError(SAXParseException spe) throws SAXException { 179 addError(XML_PARSING + " - Parsing fatal error on line " + spe.getLineNumber() + " and column " 180 + spe.getColumnNumber()); 181 addError(spe.toString()); 182 } 183 184 public void warning(SAXParseException spe) throws SAXException { 185 addWarn(XML_PARSING + " - Parsing warning on line " + spe.getLineNumber() + " and column " 186 + spe.getColumnNumber(), spe); 187 } 188 189 public void addError(String msg) { 190 contextAwareImpl.addError(msg); 191 } 192 193 public void addError(String msg, Throwable ex) { 194 contextAwareImpl.addError(msg, ex); 195 } 196 197 public void addInfo(String msg) { 198 contextAwareImpl.addInfo(msg); 199 } 200 201 public void addInfo(String msg, Throwable ex) { 202 contextAwareImpl.addInfo(msg, ex); 203 } 204 205 public void addStatus(Status status) { 206 contextAwareImpl.addStatus(status); 207 } 208 209 public void addWarn(String msg) { 210 contextAwareImpl.addWarn(msg); 211 } 212 213 public void addWarn(String msg, Throwable ex) { 214 contextAwareImpl.addWarn(msg, ex); 215 } 216 217 public Context getContext() { 218 return contextAwareImpl.getContext(); 219 } 220 221 public void setContext(Context context) { 222 contextAwareImpl.setContext(context); 223 } 224 225 public List<SaxEvent> getSaxEventList() { 226 return saxEventList; 227 } 228 229}