001 /*
002 * $Id: SAXHelper.java,v 1.4 2010/09/06 09:19:11 oboehm Exp $
003 *
004 * Copyright (c) 2008 by Oliver Boehm
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express orimplied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 *
018 * (c)reated 17.12.2008 by oliver (ob@oasd.de)
019 */
020 package patterntesting.exception.xml;
021
022 import java.io.*;
023
024 import org.apache.commons.io.IOUtils;
025 import org.apache.commons.lang.StringUtils;
026 import org.apache.commons.logging.*;
027 import org.xml.sax.SAXException;
028
029 import patterntesting.annotation.check.runtime.MayReturnNull;
030
031 /**
032 * The class SAXHelper was introduced to be able to provide better
033 * SAXExceptions.
034 *
035 * @author <a href="boehm@javatux.de">oliver</a>
036 * @since 17.12.2008
037 * @version $Revision: 1.4 $
038 */
039 public final class SAXHelper {
040
041 private static final Log log = LogFactory.getLog(SAXHelper.class);
042
043 /** The Constant DRIVER_PROPERTY. */
044 public static final String DRIVER_PROPERTY = "org.xml.sax.driver";
045
046 /** The Constant DRIVER_RESOURCE. */
047 public static final String DRIVER_RESOURCE = "META-INF/services/" + DRIVER_PROPERTY;
048
049 /**
050 * Transforms a SAXException with too less infos into a better SAXException
051 * with more infos.
052 * <br/>
053 * NOTE: Amonst other things the system property "org.xml.sax.driver" are
054 * used to get a better exception. Normally this should be synchronized
055 * because other threads may changes this system property so that this
056 * method may misinterpret this property. But it is very unlikely (except
057 * for testing) that this happen. So no synchronization is done to avoid
058 * blocked threads. So in some very, very rare circumstances the "better
059 * SAXException" may be perhaps confuses you (or the tester ;-).
060 *
061 * @param saxe the saxe
062 *
063 * @return the sAX exception
064 */
065 public static SAXException betterSAXException(final SAXException saxe) {
066 String msg = saxe.getMessage();
067 String className = parseMessage(msg);
068 if (className == null) {
069 if (log.isTraceEnabled()) {
070 log.trace("unknown message '" + msg + "' - returning " + saxe
071 + "...");
072 }
073 return saxe;
074 }
075 if (isDriverPropertySetWith(className)) {
076 if (log.isTraceEnabled()) {
077 log.trace("conflict with property " + DRIVER_PROPERTY + "="
078 + className + " detected");
079 }
080 msg = msg + " - check property \"" + DRIVER_PROPERTY + "\"";
081 return betterSAXException(saxe, msg);
082 } else if (isDriverResourceSetWith(className)) {
083 if (log.isTraceEnabled()) {
084 log.trace("conflict with resource " + DRIVER_RESOURCE + "="
085 + className + " detected");
086 }
087 msg = msg + " - check system resource \"" + DRIVER_RESOURCE + "\"";
088 return betterSAXException(saxe, msg);
089 } else {
090 return saxe;
091 }
092 }
093
094 /**
095 * Better sax exception.
096 *
097 * @param saxe the saxe
098 * @param newMessage the new message
099 *
100 * @return the sAX exception
101 */
102 public static SAXException betterSAXException(final SAXException saxe, final String newMessage) {
103 Exception cause = saxe.getException();
104 SAXException betterException = new SAXException(newMessage, cause);
105 betterException.setStackTrace(saxe.getStackTrace());
106 return betterException;
107 }
108
109 @MayReturnNull
110 private static String parseMessage(final String msg) {
111 String prefix = "SAX2 driver class ";
112 if (msg.startsWith(prefix)) {
113 return StringUtils.substringBetween(msg, prefix, " ");
114 } else {
115 return null;
116 }
117 }
118
119 private static boolean isDriverPropertySetWith(final String className) {
120 String prop = System.getProperty(DRIVER_PROPERTY);
121 return className.equals(prop);
122 }
123
124 private static boolean isDriverResourceSetWith(final String className) {
125 InputStream in = ClassLoader.getSystemResourceAsStream(DRIVER_RESOURCE);
126 if (in == null) {
127 return false;
128 }
129 try {
130 BufferedReader reader = new BufferedReader(new InputStreamReader(
131 in, "UTF8"));
132 String driver = reader.readLine();
133 reader.close();
134 if (StringUtils.isBlank(driver)) {
135 log.warn("no classname found in " + DRIVER_RESOURCE);
136 return false;
137 }
138 return driver.equals(className);
139 } catch (IOException ioe) {
140 log.warn("can't read " + DRIVER_RESOURCE + " (" + ioe + ")");
141 return false;
142 } finally {
143 IOUtils.closeQuietly(in);
144 }
145 }
146
147 }