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