| AxisFault.java |
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the docs/licenses/apache-1.1.txt file.
*/
package org.jboss.axis;
import org.jboss.axis.encoding.SerializationContext;
import org.jboss.axis.encoding.Serializer;
import org.jboss.axis.message.SOAPEnvelopeAxisImpl;
import org.jboss.axis.message.SOAPFaultImpl;
import org.jboss.axis.message.SOAPHeaderElementAxisImpl;
import org.jboss.axis.soap.SOAPConstants;
import org.jboss.axis.utils.JavaUtils;
import org.jboss.axis.utils.XMLUtils;
import org.jboss.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
import javax.xml.namespace.QName;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.rpc.JAXRPCException;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Vector;
/**
* An exception which maps cleanly to a SOAP fault.
* This is a base class for exceptions which are mapped to faults.
* SOAP faults contain
* <ol>
* <li>A fault string
* <li>A fault code
* <li>A fault actor
* <li>Fault details; an xml tree of fault specific stuff
* </ol>
*
* @author Doug Davis (dug@us.ibm.com)
* @author James Snell (jasnell@us.ibm.com)
* @author Steve Loughran
*/
public class AxisFault extends java.rmi.RemoteException
{
private static Logger log = Logger.getLogger(AxisFault.class.getName());
protected QName faultCode;
/**
* SOAP1.2 addition: subcodes of faults; a Vector of QNames
*/
protected Vector faultSubCode;
protected String faultString = "";
protected String faultActor;
protected Vector faultDetails; // vector of Element's
protected String faultNode;
/** SOAP headers which should be serialized with the Fault
* List<SOAPHeaderElementAxisImpl>
*/
protected ArrayList faultHeaders;
/**
* Make an AxisFault based on a passed Exception. If the Exception is
* already an AxisFault, simply use that. Otherwise, wrap it in an
* AxisFault. If the Exception is an InvocationTargetException (which
* already wraps another Exception), get the wrapped Exception out from
* there and use that instead of the passed one.
*/
public static AxisFault makeFault(Exception e)
{
if (e instanceof InvocationTargetException)
{
InvocationTargetException ite = (InvocationTargetException)e;
Throwable t = ite.getTargetException();
if (t instanceof Exception)
e = (Exception)t;
}
if (e instanceof JAXRPCException)
{
JAXRPCException rte = (JAXRPCException)e;
Throwable t = rte.getLinkedCause();
if (t instanceof Exception)
e = (Exception)t;
}
if (e instanceof AxisFault)
{
return (AxisFault)e;
}
return new AxisFault(e);
}
/**
* make a fault
*
* @param code fault code which will be passed into the Axis namespace
* @param faultString fault string
* @param actor fault actor
* @param details details; if null the current stack trace and classname is
* inserted into the details.
*/
public AxisFault(String code, String faultString, String actor, Element[] details)
{
this(new QName(Constants.NS_URI_AXIS, code),
faultString, actor, details);
}
/**
* make a fault in any namespace
*
* @param code fault code which will be passed into the Axis namespace
* @param faultString fault string
* @param actor fault actor
* @param details details; if null the current stack trace and classname is
* inserted into the details.
*/
public AxisFault(QName code, String faultString, String actor, Element[] details)
{
super(faultString);
setFaultCode(code);
setFaultString(faultString);
setFaultActor(actor);
setFaultDetail(details);
}
/**
* make a fault in any namespace
*
* @param code fault code which will be passed into the Axis namespace
* @param subcodes fault subcodes which will be pased into the Axis namespace
* @param faultString fault string
* @param actor fault actor, same as fault role in SOAP 1.2
* @param node which node caused the fault on the SOAP path
* @param details details; if null the current stack trace and classname is
* inserted into the details.
* @since axis1.1
*/
public AxisFault(QName code, QName[] subcodes, String faultString,
String actor, String node, Element[] details)
{
super(faultString);
setFaultCode(code);
if (subcodes != null)
{
for (int i = 0; i < subcodes.length; i++)
{
addFaultSubCode(subcodes[i]);
}
}
setFaultString(faultString);
setFaultActor(actor);
setFaultNode(node);
setFaultDetail(details);
}
/**
* Wrap an AxisFault around an existing Exception - this is private
* to force everyone to use makeFault() above, which sanity-checks us.
*/
protected AxisFault(Exception target)
{
super(target.getMessage(), assertTarget(target));
// ? SOAP 1.2 or 1.1 ?
setFaultCodeAsString(Constants.FAULT_SERVER_USER);
initFromException(target);
}
/**
* Check for nested AxisFault
* [todo] TDI 07-June-2004
*/
private static Throwable assertTarget(Throwable target)
{
// if (target instanceof AxisFault)
// log.warn("Nested AxisFault detected", new IllegalArgumentException());
return target;
}
/**
* create a simple axis fault from the message. Classname and stack trace
* go into the fault details.
*
* @param message
*/
public AxisFault(String message)
{
super(message);
setFaultCodeAsString(Constants.FAULT_SERVER_GENERAL);
setFaultString(message);
}
/**
* No-arg constructor for building one from an XML stream.
*/
public AxisFault()
{
super();
setFaultCodeAsString(Constants.FAULT_SERVER_GENERAL);
}
/**
* create a fault from any throwable;
* When faulting a throwable (as opposed to an exception),
* stack trace information does not go into the fault.
*
* @param message any extra text to with the fault
* @param target whatever is to be turned into a fault
*/
public AxisFault(String message, Throwable target)
{
super(message, assertTarget(target));
setFaultCodeAsString(Constants.FAULT_SERVER_GENERAL);
setFaultString(getMessage());
}
/**
* fill in soap fault details from the exception, unless
* this object already has a stack trace in its details. Which, given
* the way this private method is invoked, is a pretty hard situation to ever achieve.
* This method adds classname of the exception and the stack trace.
*
* @param target what went wrong
*/
private void initFromException(Exception target)
{
assertTarget(target);
// Set the exception message (if any) as the fault string
String faultString = target.getMessage();
setFaultString(faultString != null ? faultString : target.toString());
Throwable cause = target;
if (target instanceof AxisFault)
cause = ((AxisFault)target).detail;
// Add the stack trace as detail element
if (cause != null)
{
//add stack trace
addFaultDetail(Constants.QNAME_FAULTDETAIL_STACKTRACE, JavaUtils.stackToString(cause));
}
}
/**
* init the fault details data structure; does nothing
* if this exists already
*/
private void initFaultDetails()
{
if (faultDetails == null)
{
faultDetails = new Vector();
}
}
/**
* clear the fault details list
*/
public void clearFaultDetails()
{
faultDetails = null;
}
/**
* dump the fault info to the log at debug level
*/
public void dump()
{
log.debug(dumpToString());
}
/**
* turn the fault and details into a string, with XML escaping.
* subclassers: for security (cross-site-scripting) reasons,
* escape everything that could contain caller-supplied data.
*
* @return stringified fault details
*/
public String dumpToString()
{
String details = new String();
if (faultDetails != null)
{
for (int i = 0; i < faultDetails.size(); i++)
{
Element e = (Element)faultDetails.get(i);
String namespace = e.getNamespaceURI();
if (namespace == null)
{
namespace = "";
}
String partname = e.getLocalName();
if (partname == null)
{
partname = e.getNodeName();
}
details += JavaUtils.LS
+ "\t{" + namespace + "}"
+ partname + ": "
+ XMLUtils.getInnerXMLString(e);
}
}
String subCodes = new String();
if (faultSubCode != null)
{
for (int i = 0; i < faultSubCode.size(); i++)
{
subCodes += JavaUtils.LS
+ (QName)faultSubCode.elementAt(i);
}
}
//encode everything except details and subcodes, which are already
//dealt with one way or another.
String code = XMLUtils.xmlEncodeString(faultCode.toString());
String errorString = XMLUtils.xmlEncodeString(faultString);
String actor = XMLUtils.xmlEncodeString(faultActor);
String node = XMLUtils.xmlEncodeString(faultNode);
return "AxisFault" + JavaUtils.LS
+ " faultCode: " + code + JavaUtils.LS
+ " faultSubcode: " + subCodes + JavaUtils.LS
+ " faultString: " + errorString + JavaUtils.LS
+ " faultActor: " + actor + JavaUtils.LS
+ " faultNode: " + node + JavaUtils.LS
+ " faultDetail: " + details + JavaUtils.LS
;
}
/**
* set the fault code
*
* @param code a new fault code
*/
public void setFaultCode(QName code)
{
faultCode = code;
}
/**
* Set the fault code (as a String).
*
* @deprecated expect to see this go away after 1.1, use
* setFaultCodeAsString instead!
*/
public void setFaultCode(String code)
{
setFaultCodeAsString(code);
}
/**
* set a fault code string that is turned into a qname
* in the SOAP 1.1 or 1.2 namespace, depending on the current context
*
* @param code fault code
*/
public void setFaultCodeAsString(String code)
{
SOAPConstants soapConstants = MessageContext.getCurrentContext() == null ?
SOAPConstants.SOAP11_CONSTANTS :
MessageContext.getCurrentContext().getSOAPConstants();
faultCode = new QName(soapConstants.getEnvelopeURI(), code);
}
/**
* get the fault code
*
* @return fault code QName or null if there is none yet.
*/
public QName getFaultCode()
{
return (faultCode);
}
/**
* This is new in SOAP 1.2, ignored in SOAP 1.1
*
* @since axis1.1
*/
public void addFaultSubCodeAsString(String code)
{
initFaultSubCodes();
faultSubCode.add(new QName(Constants.NS_URI_AXIS, code));
}
/**
* do whatever is needed to create the fault subcodes
* data structure, if it is needed
*/
protected void initFaultSubCodes()
{
if (faultSubCode == null)
{
faultSubCode = new Vector();
}
}
/**
* This is new in SOAP 1.2, ignored in SOAP 1.1
*
* @since axis1.1
*/
public void addFaultSubCode(QName code)
{
initFaultSubCodes();
faultSubCode.add(code);
}
/**
* This is new in SOAP 1.2, ignored in SOAP 1.1
*
* @since axis1.1
*/
public void clearFaultSubCodes()
{
faultSubCode = null;
}
/**
* get the fault subcode list; only used in SOAP 1.2
*
* @return null for no subcodes, or a QName array
* @since axis1.1
*/
public QName[] getFaultSubCodes()
{
if (faultSubCode == null)
{
return null;
}
QName[] q = new QName[faultSubCode.size()];
return (QName[])faultSubCode.toArray(q);
}
/**
* set a fault string;
*
* @param str new fault string; null is turned into ""
*/
public void setFaultString(String str)
{
if (str != null)
{
faultString = str;
}
else
{
faultString = "";
}
}
/**
* get the fault string; this will never be null but may be the
* empty string
*
* @return a fault string
*/
public String getFaultString()
{
return (faultString);
}
/**
* This is SOAP 1.2 equivalent of {@link #setFaultString(java.lang.String)}
*
* @since axis1.1
*/
public void setFaultReason(String str)
{
setFaultString(str);
}
/**
* This is SOAP 1.2 equivalent of {@link #getFaultString()}
*
* @return
* @since axis1.1
*/
public String getFaultReason()
{
return getFaultString();
}
/**
* set the fault actor
*
* @param actor fault actor
*/
public void setFaultActor(String actor)
{
faultActor = actor;
}
/**
* get the fault actor
*
* @return actor or null
*/
public String getFaultActor()
{
return (faultActor);
}
/**
* This is SOAP 1.2 equivalent of {@link #getFaultActor()}
*
* @return
* @since axis1.1
*/
public String getFaultRole()
{
return getFaultActor();
}
/**
* This is SOAP 1.2 equivalent of {@link #setFaultActor(java.lang.String)}
*
* @since axis1.1
*/
public void setFaultRole(String role)
{
setFaultActor(role);
}
/**
* This is new in SOAP 1.2
*
* @return
* @since axis1.1
*/
public String getFaultNode()
{
return (faultNode);
}
/**
* This is new in SOAP 1.2
*
* @since axis1.1
*/
public void setFaultNode(String node)
{
faultNode = node;
}
/**
* set the fault detail element to the arrary of details
*
* @param details list of detail elements, can be null
*/
public void setFaultDetail(Element[] details)
{
if (details == null)
{
faultDetails = null;
return;
}
faultDetails = new Vector(details.length);
for (int loop = 0; loop < details.length; loop++)
{
faultDetails.add(details[loop]);
}
}
/**
* set the fault details to a string element.
*
* @param details XML fragment
*/
public void setFaultDetailString(String details)
{
clearFaultDetails();
addFaultDetailString(details);
}
/**
* add a string tag to the fault details.
*
* @param detail XML fragment
*/
public void addFaultDetailString(String detail)
{
initFaultDetails();
try
{
Document doc = XMLUtils.newDocument();
Element element = doc.createElement("string");
Text text = doc.createTextNode(detail);
element.appendChild(text);
faultDetails.add(element);
}
catch (ParserConfigurationException e)
{
// This should not occur
throw new InternalException(e);
}
}
/**
* append an element to the fault detail list
*
* @param detail the new element to add
* @since Axis1.1
*/
public void addFaultDetail(Element detail)
{
initFaultDetails();
faultDetails.add(detail);
}
/**
* create an element of the given qname and add it to the details
*
* @param qname qname of the element
* @param body string to use as body
*/
public void addFaultDetail(QName qname, String body)
{
Element detail = XMLUtils.StringToElement(qname.getNamespaceURI(),
qname.getLocalPart(),
body);
addFaultDetail(detail);
}
/**
* get all the fault details
*
* @return an array of fault details, or null for none
*/
public Element[] getFaultDetails()
{
if (faultDetails == null)
{
return null;
}
Element result[] = new Element[faultDetails.size()];
for (int i = 0; i < result.length; i++)
result[i] = (Element)faultDetails.elementAt(i);
return result;
}
/**
* Find a fault detail element by its qname
*
* @param qname name of the node to look for
* @return the matching element or null
* @since axis1.1
*/
public Element lookupFaultDetail(QName qname)
{
if (faultDetails != null)
{
//extract details from the qname. the empty namespace is represented
//by the empty string
String searchNamespace = qname.getNamespaceURI();
String searchLocalpart = qname.getLocalPart();
//now spin through the elements, seeking a match
Iterator it = faultDetails.iterator();
while (it.hasNext())
{
Element e = (Element)it.next();
String localpart = e.getLocalName();
if (localpart == null)
{
localpart = e.getNodeName();
}
String namespace = e.getNamespaceURI();
if (namespace == null)
{
namespace = "";
}
//we match on matching namespace and local part; empty namespace
//in an element may be null, which matches QName's ""
if (searchNamespace.equals(namespace)
&& searchLocalpart.equals(localpart))
{
return e;
}
}
}
return null;
}
/**
* find and remove a specified fault detail element
*
* @param qname qualified name of detail
* @return true if it was found and removed
* @since axis1.1
*/
public boolean removeFaultDetail(QName qname)
{
Element elt = lookupFaultDetail(qname);
if (elt == null)
{
return false;
}
else
{
return faultDetails.remove(elt);
}
}
/**
* add this fault and any needed headers to the output context
*
* @param context
* @throws Exception
*/
public void output(SerializationContext context) throws Exception
{
SOAPConstants soapConstants = Constants.DEFAULT_SOAP_VERSION;
if (context.getMessageContext() != null)
soapConstants = context.getMessageContext().getSOAPConstants();
SOAPEnvelopeAxisImpl envelope = new SOAPEnvelopeAxisImpl(soapConstants);
SOAPFaultImpl fault = new SOAPFaultImpl(this);
envelope.addBodyElement(fault);
// add any headers we need
if (faultHeaders != null)
{
for (Iterator i = faultHeaders.iterator(); i.hasNext();)
{
SOAPHeaderElementAxisImpl header = (SOAPHeaderElementAxisImpl)i.next();
envelope.addHeader(header);
}
}
envelope.output(context);
}
/**
* string operator
*
* @return the current fault string; may be empty but never null
*/
public String toString()
{
return getClass().getName() + ": " + faultString;
}
/**
* The override of the base class method prints out the
* fault info before the stack trace
*
* @param ps where to print
*/
public void printStackTrace(PrintStream ps)
{
ps.println(dumpToString());
super.printStackTrace(ps);
}
/**
* The override of the base class method prints out the
* fault info before the stack trace
*
* @param pw where to print
*/
public void printStackTrace(java.io.PrintWriter pw)
{
pw.println(dumpToString());
super.printStackTrace(pw);
}
/**
* Add a SOAP header which should be serialized along with the fault.
*
* @param header a SOAPHeaderElement containing some fault-relevant stuff
*/
public void addHeader(SOAPHeaderElementAxisImpl header)
{
if (faultHeaders == null)
{
faultHeaders = new ArrayList();
}
faultHeaders.add(header);
}
/**
* Get the SOAP headers associated with this fault.
*
* @return an ArrayList containing any headers associated with this fault
*/
public ArrayList getHeaders()
{
return faultHeaders;
}
/**
* clear all fault headers
*/
public void clearHeaders()
{
faultHeaders = null;
}
/**
* Writes any exception data to the faultDetails
* This can be overrided (and is) by emitted exception clases.
* The base implementation will attempt to serialize exception data
* the fault was created from an Exception and a type mapping is found for it.
*/
public void writeDetails(QName qname, SerializationContext context) throws java.io.IOException
{
Object detailObject = this.detail;
if (detailObject == null)
return;
Serializer ser = context.getSerializerForJavaType(detailObject.getClass());
if (ser != null)
{
boolean oldMR = context.getDoMultiRefs();
context.setDoMultiRefs(false);
context.serialize(qname, null, detailObject);
context.setDoMultiRefs(oldMR);
}
}
}
| AxisFault.java |