| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.cocoon.generation; |
| |
| import java.io.IOException; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.avalon.framework.parameters.Parameters; |
| import org.apache.cocoon.ProcessingException; |
| import org.apache.cocoon.environment.ObjectModelHelper; |
| import org.apache.cocoon.environment.SourceResolver; |
| import org.apache.cocoon.util.location.LocatableException; |
| import org.apache.cocoon.util.location.Location; |
| import org.apache.cocoon.util.location.LocationUtils; |
| import org.apache.cocoon.util.location.MultiLocatable; |
| import org.apache.cocoon.xml.AttributesImpl; |
| import org.apache.commons.lang.SystemUtils; |
| import org.apache.commons.lang.exception.ExceptionUtils; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.ContentHandler; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * A generator that dumps an XML representation of the exception raised during a pipeline execution. |
| * <p> |
| * The Cocoon stack trace is produced, reflecting all locations the original exception went through, |
| * along with the root exception stacktrace and the full exception stacktrace. |
| * |
| * @since 2.1.8 |
| * @version $Id$ |
| */ |
| public class ExceptionGenerator extends AbstractGenerator { |
| |
| private Throwable thr; |
| |
| public static final String EXCEPTION_NS = "http://apache.org/cocoon/exception/1.0"; |
| |
| public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par) throws ProcessingException, SAXException, IOException { |
| super.setup(resolver, objectModel, src, par); |
| thr = (Throwable)objectModel.get(ObjectModelHelper.THROWABLE_OBJECT); |
| if (thr == null) { |
| throw new ProcessingException("ExceptionGenerator should be used in <map:handle-errors>"); |
| } |
| } |
| |
| public void generate() throws IOException, SAXException, ProcessingException { |
| this.contentHandler.startDocument(); |
| toSAX(thr, this.contentHandler); |
| this.contentHandler.endDocument(); |
| } |
| |
| public static void toSAX(Throwable thr, ContentHandler handler) throws SAXException { |
| Throwable root = ExceptionUtils.getRootCause(thr); |
| if (root == null) root = thr; |
| |
| AttributesImpl attr = new AttributesImpl(); |
| handler.startPrefixMapping("ex", EXCEPTION_NS); |
| attr.addCDATAAttribute("class", root.getClass().getName()); |
| handler.startElement(EXCEPTION_NS, "exception-report", "ex:exception-report", attr); |
| |
| // Root exception location |
| Location loc = LocationUtils.getLocation(root); |
| if (LocationUtils.isKnown(loc)) { |
| attr.clear(); |
| dumpLocation(loc, attr, handler); |
| } |
| |
| // Root exception message |
| attr.clear(); |
| String message = root instanceof LocatableException ? ((LocatableException)root).getRawMessage() : root.getMessage(); |
| simpleElement("message", attr, message, handler); |
| |
| // Cocoon stacktrace: dump all located exceptions in the exception stack |
| handler.startElement(EXCEPTION_NS, "cocoon-stacktrace", "ex:cocoon-stacktrace", attr); |
| Throwable current = thr; |
| while (current != null) { |
| loc = LocationUtils.getLocation(current); |
| if (LocationUtils.isKnown(loc)) { |
| // One or more locations: dump it |
| handler.startElement(EXCEPTION_NS, "exception", "ex:exception", attr); |
| |
| message = current instanceof LocatableException ? ((LocatableException)current).getRawMessage() : current.getMessage(); |
| simpleElement("message", attr, message, handler); |
| |
| attr.clear(); |
| handler.startElement(EXCEPTION_NS, "locations", "ex:locations", attr); |
| dumpLocation(loc, attr, handler); |
| |
| if (current instanceof MultiLocatable) { |
| List locations = ((MultiLocatable)current).getLocations(); |
| for (int i = 1; i < locations.size(); i++) { // start at 1 because we already dumped the first one |
| attr.clear(); |
| dumpLocation((Location)locations.get(i), attr, handler); |
| } |
| } |
| handler.endElement(EXCEPTION_NS, "locations", "ex:locations"); |
| handler.endElement(EXCEPTION_NS, "exception", "ex:exception"); |
| } |
| |
| |
| // Dump parent location |
| current = ExceptionUtils.getCause(current); |
| } |
| |
| handler.endElement(EXCEPTION_NS, "cocoon-stacktrace", "ex:cocoon-stacktrace"); |
| |
| // Root exception stacktrace |
| attr.clear(); |
| simpleElement("stacktrace", attr, ExceptionUtils.getStackTrace(root), handler); |
| |
| // Full stack trace (if exception is chained) |
| if (thr != root) { |
| String trace = SystemUtils.isJavaVersionAtLeast(140) ? |
| ExceptionUtils.getStackTrace(thr) : |
| ExceptionUtils.getFullStackTrace(thr); |
| |
| simpleElement("full-stacktrace", attr, trace, handler); |
| } |
| |
| handler.endElement(EXCEPTION_NS, "exception-report", "ex:exception-report"); |
| handler.endPrefixMapping("ex"); |
| } |
| |
| private static void dumpLocation(Location loc, AttributesImpl attr, ContentHandler handler) throws SAXException { |
| attr.addCDATAAttribute("uri", loc.getURI()); |
| attr.addCDATAAttribute("line", Integer.toString(loc.getLineNumber())); |
| attr.addCDATAAttribute("column", Integer.toString(loc.getColumnNumber())); |
| simpleElement("location", attr, loc.getDescription(), handler); |
| } |
| |
| private static void simpleElement(String name, Attributes attr, String value, ContentHandler handler) throws SAXException { |
| handler.startElement(EXCEPTION_NS, name, "ex:" + name, attr); |
| if (value != null && value.length() > 0) { |
| handler.characters(value.toCharArray(), 0, value.length()); |
| } |
| handler.endElement(EXCEPTION_NS, name, "ex:" + name); |
| } |
| } |