1/* 2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.xml.internal.ws.fault; 27 28import com.sun.xml.internal.bind.marshaller.NamespacePrefixMapper; 29import com.sun.xml.internal.ws.developer.ServerSideException; 30import org.w3c.dom.Element; 31import org.w3c.dom.Node; 32 33import javax.xml.bind.JAXBContext; 34import javax.xml.bind.JAXBException; 35import javax.xml.bind.Marshaller; 36import javax.xml.bind.PropertyException; 37import javax.xml.bind.annotation.XmlAttribute; 38import javax.xml.bind.annotation.XmlElement; 39import javax.xml.bind.annotation.XmlElementWrapper; 40import javax.xml.bind.annotation.XmlRootElement; 41import java.util.ArrayList; 42import java.util.List; 43 44/** 45 * JAXB-bound bean that captures the exception and its call stack. 46 * 47 * <p> 48 * This is used to capture the stack trace of the server side error and 49 * send that over to the client. 50 * 51 * @author Kohsuke Kawaguchi 52 */ 53@XmlRootElement(namespace=ExceptionBean.NS,name=ExceptionBean.LOCAL_NAME) 54final class ExceptionBean { 55 /** 56 * Converts the given {@link Throwable} into an XML representation 57 * and put that as a DOM tree under the given node. 58 */ 59 public static void marshal( Throwable t, Node parent ) throws JAXBException { 60 Marshaller m = JAXB_CONTEXT.createMarshaller(); 61 try { 62 m.setProperty("com.sun.xml.internal.bind.namespacePrefixMapper",nsp); 63 } catch (PropertyException pe) {} 64 m.marshal(new ExceptionBean(t), parent ); 65 } 66 67 /** 68 * Does the reverse operation of {@link #marshal(Throwable, Node)}. Constructs an 69 * {@link Exception} object from the XML. 70 */ 71 public static ServerSideException unmarshal( Node xml ) throws JAXBException { 72 ExceptionBean e = (ExceptionBean) JAXB_CONTEXT.createUnmarshaller().unmarshal(xml); 73 return e.toException(); 74 } 75 76 @XmlAttribute(name="class") 77 public String className; 78 @XmlElement 79 public String message; 80 @XmlElementWrapper(namespace=NS,name="stackTrace") 81 @XmlElement(namespace=NS,name="frame") 82 public List<StackFrame> stackTrace = new ArrayList<StackFrame>(); 83 @XmlElement(namespace=NS,name="cause") 84 public ExceptionBean cause; 85 86 // so that people noticed this fragment can turn it off 87 @XmlAttribute 88 public String note = "To disable this feature, set "+SOAPFaultBuilder.CAPTURE_STACK_TRACE_PROPERTY+" system property to false"; 89 90 ExceptionBean() {// for JAXB 91 } 92 93 /** 94 * Creates an {@link ExceptionBean} tree that represents the given {@link Throwable}. 95 */ 96 private ExceptionBean(Throwable t) { 97 this.className = t.getClass().getName(); 98 this.message = t.getMessage(); 99 100 for (StackTraceElement f : t.getStackTrace()) { 101 stackTrace.add(new StackFrame(f)); 102 } 103 104 Throwable cause = t.getCause(); 105 if(t!=cause && cause!=null) 106 this.cause = new ExceptionBean(cause); 107 } 108 109 private ServerSideException toException() { 110 ServerSideException e = new ServerSideException(className,message); 111 if(stackTrace!=null) { 112 StackTraceElement[] ste = new StackTraceElement[stackTrace.size()]; 113 for( int i=0; i<stackTrace.size(); i++ ) 114 ste[i] = stackTrace.get(i).toStackTraceElement(); 115 e.setStackTrace(ste); 116 } 117 if(cause!=null) 118 e.initCause(cause.toException()); 119 return e; 120 } 121 122 /** 123 * Captures one stack frame. 124 */ 125 static final class StackFrame { 126 @XmlAttribute(name="class") 127 public String declaringClass; 128 @XmlAttribute(name="method") 129 public String methodName; 130 @XmlAttribute(name="file") 131 public String fileName; 132 @XmlAttribute(name="line") 133 public String lineNumber; 134 135 StackFrame() {// for JAXB 136 } 137 138 public StackFrame(StackTraceElement ste) { 139 this.declaringClass = ste.getClassName(); 140 this.methodName = ste.getMethodName(); 141 this.fileName = ste.getFileName(); 142 this.lineNumber = box(ste.getLineNumber()); 143 } 144 145 private String box(int i) { 146 if(i>=0) return String.valueOf(i); 147 if(i==-2) return "native"; 148 return "unknown"; 149 } 150 151 private int unbox(String v) { 152 try { 153 return Integer.parseInt(v); 154 } catch (NumberFormatException e) { 155 if ("native".equals(v)) { 156 return -2; 157 } 158 return -1; 159 } 160 } 161 162 private StackTraceElement toStackTraceElement() { 163 return new StackTraceElement(declaringClass,methodName,fileName,unbox(lineNumber)); 164 } 165 } 166 167 /** 168 * Checks if the given element is the XML representation of {@link ExceptionBean}. 169 */ 170 public static boolean isStackTraceXml(Element n) { 171 return LOCAL_NAME.equals(n.getLocalName()) && NS.equals(n.getNamespaceURI()); 172 } 173 174 private static final JAXBContext JAXB_CONTEXT; 175 176 /** 177 * Namespace URI. 178 */ 179 /*package*/ static final String NS = "http://jax-ws.dev.java.net/"; 180 181 /*package*/ static final String LOCAL_NAME = "exception"; 182 183 static { 184 try { 185 JAXB_CONTEXT = JAXBContext.newInstance(ExceptionBean.class); 186 } catch (JAXBException e) { 187 // this must be a bug in our code 188 throw new Error(e); 189 } 190 } 191 192 private static final NamespacePrefixMapper nsp = new NamespacePrefixMapper() { 193 public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) { 194 if (NS.equals(namespaceUri)) { 195 return ""; 196 } 197 return suggestion; 198 } 199 }; 200} 201