001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.servicemix.validation.handler;
018
019 import java.io.IOException;
020 import java.io.PrintWriter;
021 import java.io.StringWriter;
022
023 import javax.jbi.messaging.MessagingException;
024 import javax.xml.parsers.ParserConfigurationException;
025 import javax.xml.transform.TransformerException;
026 import javax.xml.transform.dom.DOMSource;
027
028 import org.xml.sax.SAXException;
029 import org.xml.sax.SAXParseException;
030
031 import org.apache.servicemix.jbi.jaxp.SourceTransformer;
032 import org.apache.servicemix.jbi.jaxp.StringSource;
033 import org.apache.servicemix.validation.ValidationEndpoint;
034
035 /**
036 * An implementation of {@link ErrorHandler} which aggregates all warnings and
037 * error messages into a StringBuffer.
038 *
039 * @version $Revision: 359186 $
040 */
041 public class MessageAggregatingErrorHandler implements MessageAwareErrorHandler {
042
043 private static final String OPEN_CDATA = "<![CDATA[";
044 private static final String CLOSE_CDATA = "]]>";
045 private static final String OPEN_ERROR = ValidationEndpoint.TAG_ERROR_START;
046 private static final String CLOSE_ERROR = ValidationEndpoint.TAG_ERROR_END;
047 private static final String OPEN_FATAL = ValidationEndpoint.TAG_FATAL_START;
048 private static final String CLOSE_FATAL = ValidationEndpoint.TAG_FATAL_END;
049 private static final String OPEN_WARNING = ValidationEndpoint.TAG_WARNING_START;
050 private static final String CLOSE_WARNING = ValidationEndpoint.TAG_WARNING_END;
051
052 private String openRootElement;
053 private String closeRootElement;
054
055 /**
056 * Number of warnings.
057 */
058 private int warningCount;
059
060 /**
061 * Number of errors.
062 */
063 private int errorCount;
064
065 /**
066 * Number of fatal errors.
067 */
068 private int fatalErrorCount;
069
070 /**
071 * The root element name for the fault xml message
072 */
073 private String rootPath;
074
075 /**
076 * The namespace for the fault xml message
077 */
078 private String namespace;
079
080 /**
081 * Determines whether or not to include stacktraces in the fault xml message
082 */
083 private boolean includeStackTraces;
084
085 /**
086 * Variable to hold the warning/error messages from the validator
087 */
088 private StringBuffer messages = new StringBuffer();
089
090 private SourceTransformer sourceTransformer = new SourceTransformer();
091
092 /**
093 * Constructor.
094 *
095 * @param rootElement
096 * The root element name of the fault xml message
097 * @param namespace
098 * The namespace for the fault xml message
099 * @param includeStackTraces
100 * Include stracktraces in the final output
101 */
102 public MessageAggregatingErrorHandler(String rootPath, String namespace, boolean includeStackTraces) throws IllegalArgumentException {
103 if (rootPath == null || rootPath.trim().length() == 0) {
104 throw new IllegalArgumentException("rootPath must not be null or an empty string");
105 }
106 this.rootPath = rootPath;
107 this.namespace = namespace;
108 this.includeStackTraces = includeStackTraces;
109 createRootElementTags();
110 }
111
112 /**
113 * Creates the root element tags for later use down to n depth.
114 * Note: the rootPath here is of the form:
115 *
116 * <code>rootElementName/elementName-1/../elementName-n</code>
117 *
118 * The namespace will be appended to the root element if it is not
119 * null or empty.
120 */
121 private void createRootElementTags() {
122 /*
123 * since the rootPath is constrained to be not null or empty
124 * then we have at least one path element.
125 */
126 String[] pathElements = rootPath.split("/");
127
128 StringBuffer openRootElementSB = new StringBuffer().append("<").append(pathElements[0]);
129 StringBuffer closeRootElementSB = new StringBuffer();
130
131 if (namespace != null && namespace.trim().length() > 0) {
132 openRootElementSB.append(" xmlns=\"").append(namespace).append("\">");
133 } else {
134 openRootElementSB.append(">");
135 }
136
137 if (pathElements.length > 0) {
138 int j = pathElements.length - 1;
139 for (int i = 1; i < pathElements.length; i++, j--) {
140 openRootElementSB.append("<").append(pathElements[i]).append(">");
141 closeRootElementSB.append("</").append(pathElements[j]).append(">");
142 }
143 }
144
145 // create the closing root element tag
146 closeRootElementSB.append("</").append(pathElements[0]).append(">");
147
148 openRootElement = openRootElementSB.toString();
149 closeRootElement = closeRootElementSB.toString();
150 }
151
152 /* (non-Javadoc)
153 * @see org.apache.servicemix.components.validation.MessageAwareErrorHandler#hasErrors()
154 */
155 public boolean hasErrors() {
156 return getErrorCount() > 0 || getFatalErrorCount() > 0;
157 }
158
159 /* (non-Javadoc)
160 * @see org.apache.servicemix.components.validation.MessageAwareErrorHandler#getWarningCount()
161 */
162 public int getWarningCount() {
163 return warningCount;
164 }
165
166 /* (non-Javadoc)
167 * @see org.apache.servicemix.components.validation.MessageAwareErrorHandler#getErrorCount()
168 */
169 public int getErrorCount() {
170 return errorCount;
171 }
172
173 /* (non-Javadoc)
174 * @see org.apache.servicemix.components.validation.MessageAwareErrorHandler#getFatalErrorCount()
175 */
176 public int getFatalErrorCount() {
177 return fatalErrorCount;
178 }
179
180 /* (non-Javadoc)
181 * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException)
182 */
183 public void warning(SAXParseException e) throws SAXException {
184 ++warningCount;
185
186 // open warning and CDATA tags
187 messages.append(OPEN_WARNING).append(OPEN_CDATA);
188
189 // append the fatal error message
190 appendErrorMessage(e);
191
192 // close CDATA and warning tags
193 messages.append(CLOSE_CDATA).append(CLOSE_WARNING);
194 }
195
196 /* (non-Javadoc)
197 * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException)
198 */
199 public void error(SAXParseException e) throws SAXException {
200 ++errorCount;
201
202 // open fatal error and CDATA tags
203 messages.append(OPEN_ERROR).append(OPEN_CDATA);
204
205 // append the error message
206 appendErrorMessage(e);
207
208 // close CDATA and error tags
209 messages.append(CLOSE_CDATA).append(CLOSE_ERROR);
210 }
211
212 /* (non-Javadoc)
213 * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException)
214 */
215 public void fatalError(SAXParseException e) throws SAXException {
216 ++fatalErrorCount;
217
218 // open fatal error and CDATA tags
219 messages.append(OPEN_FATAL).append(OPEN_CDATA);
220
221 // append the fatal error message
222 appendErrorMessage(e);
223
224 // close CDATA and fatal error tags
225 messages.append(CLOSE_CDATA).append(CLOSE_FATAL);
226 }
227
228 /**
229 * Append the error message or stacktrace to the messages attribute.
230 *
231 * @param e
232 */
233 private void appendErrorMessage(SAXParseException e) {
234 if (includeStackTraces) {
235 StringWriter sw = new StringWriter();
236 e.printStackTrace(new PrintWriter(sw));
237 messages.append(sw.toString());
238 } else {
239 messages.append(e.getLocalizedMessage());
240 }
241 }
242
243 /* (non-Javadoc)
244 * @see org.apache.servicemix.components.validation.MessageAwareErrorHandler#capturesMessages()
245 */
246 public boolean capturesMessages() {
247 return true;
248 }
249
250 /* (non-Javadoc)
251 * @see org.apache.servicemix.components.validation.MessageAwareErrorHandler#getMessagesAs(java.lang.Class)
252 */
253 public Object getMessagesAs(Class format) throws MessagingException {
254 if (format == DOMSource.class) {
255 return getDOMSource();
256 } else if (format == StringSource.class) {
257 return getStringSource();
258 } else if (format == String.class) {
259 return getMessagesWithRootElement();
260 }
261 throw new MessagingException("Unsupported message format: " + format.getName());
262 }
263
264 /* (non-Javadoc)
265 * @see org.apache.servicemix.components.validation.MessageAwareErrorHandler#supportsMessageFormat(java.lang.Class)
266 */
267 public boolean supportsMessageFormat(Class format) {
268 if (format == DOMSource.class) {
269 return true;
270 } else if (format == StringSource.class) {
271 return true;
272 } else if (format == String.class) {
273 return true;
274 }
275 return false;
276 }
277
278 /**
279 * Return the messages encapsulated with the root element.
280 *
281 * @return
282 */
283 private String getMessagesWithRootElement() {
284 return new StringBuffer().append(openRootElement).append(messages).append(closeRootElement).toString();
285 }
286
287 /**
288 * Get the error messages as a String Source.
289 *
290 * @return
291 */
292 private StringSource getStringSource() {
293 return new StringSource(getMessagesWithRootElement());
294 }
295
296 /**
297 * Get the error messages as a DOMSource.
298 *
299 * @return
300 * @throws MessagingException
301 */
302 private DOMSource getDOMSource() throws MessagingException {
303 try {
304 return sourceTransformer.toDOMSource(getStringSource());
305 } catch (ParserConfigurationException e) {
306 throw new MessagingException("Failed to create DOMSource for Schema validation messages: " + e, e);
307 } catch (IOException e) {
308 throw new MessagingException("Failed to create DOMSource for Schema validation messages: " + e, e);
309 } catch (SAXException e) {
310 throw new MessagingException("Failed to create DOMSource for Schema validation messages: " + e, e);
311 } catch (TransformerException e) {
312 throw new MessagingException("Failed to create DOMSource for Schema validation messages: " + e, e);
313 }
314 }
315
316 }