blob: 35666db978faef76d11bc2739aca7c433b72c4a5 [file] [log] [blame]
/*
* Copyright 2001-2004 The Apache Software Foundation.
*
* Licensed 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.axis.message;
import org.apache.axis.AxisFault;
import org.apache.axis.Constants;
import org.apache.axis.encoding.Callback;
import org.apache.axis.encoding.CallbackTarget;
import org.apache.axis.encoding.DeserializationContext;
import org.apache.axis.encoding.Deserializer;
import org.apache.axis.soap.SOAPConstants;
import org.apache.axis.utils.Messages;
import org.apache.axis.utils.XMLUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import javax.xml.namespace.QName;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;
/**
* Build a Fault body element.
*
* @author Sam Ruby (rubys@us.ibm.com)
* @author Glen Daniels (gdaniels@apache.org)
* @author Tom Jordahl (tomj@macromedia.com)
*/
public class SOAPFaultBuilder extends SOAPHandler implements Callback
{
boolean waiting = false;
boolean passedEnd = false;
protected SOAPFault element;
protected DeserializationContext context;
static HashMap fields_soap11 = new HashMap();
static HashMap fields_soap12 = new HashMap();
// Fault data
protected QName faultCode = null;
protected QName[] faultSubCode = null;
protected String faultString = null;
protected String faultActor = null;
protected Element[] faultDetails;
protected String faultNode = null;
protected SOAPFaultCodeBuilder code;
protected Class faultClass = null;
protected Object faultData = null;
static {
fields_soap11.put(Constants.ELEM_FAULT_CODE, Constants.XSD_QNAME);
fields_soap11.put(Constants.ELEM_FAULT_STRING, Constants.XSD_STRING);
fields_soap11.put(Constants.ELEM_FAULT_ACTOR, Constants.XSD_STRING);
fields_soap11.put(Constants.ELEM_FAULT_DETAIL, null);
}
static {
fields_soap12.put(Constants.ELEM_FAULT_REASON_SOAP12, null);
fields_soap12.put(Constants.ELEM_FAULT_ROLE_SOAP12, Constants.XSD_STRING);
fields_soap12.put(Constants.ELEM_FAULT_NODE_SOAP12, Constants.XSD_STRING);
fields_soap12.put(Constants.ELEM_FAULT_DETAIL_SOAP12, null);
}
public SOAPFaultBuilder(SOAPFault element,
DeserializationContext context) {
this.element = element;
this.context = context;
}
public void startElement(String namespace, String localName,
String prefix, Attributes attributes,
DeserializationContext context)
throws SAXException
{
SOAPConstants soapConstants = context.getSOAPConstants();
if (soapConstants == SOAPConstants.SOAP12_CONSTANTS &&
attributes.getValue(Constants.URI_SOAP12_ENV, Constants.ATTR_ENCODING_STYLE) != null) {
AxisFault fault = new AxisFault(Constants.FAULT_SOAP12_SENDER,
null, Messages.getMessage("noEncodingStyleAttrAppear", "Fault"), null, null, null);
throw new SAXException(fault);
}
super.startElement(namespace, localName, prefix, attributes, context);
}
void setFaultData(Object data) {
faultData = data;
if (waiting && passedEnd) {
// This happened after the end of the <soap:Fault>, so make
// sure we set up the fault.
createFault();
}
waiting = false;
}
public void setFaultClass(Class faultClass) {
this.faultClass = faultClass;
}
/**
* Final call back where we can populate the exception with data.
*/
public void endElement(String namespace, String localName,
DeserializationContext context)
throws SAXException {
super.endElement(namespace, localName, context);
if (!waiting) {
createFault();
} else {
passedEnd = true;
}
}
void setWaiting(boolean waiting) {
this.waiting = waiting;
}
/**
* When we're sure we have everything, this gets called.
*/
private void createFault() {
AxisFault f = null;
SOAPConstants soapConstants = context.getMessageContext() == null ?
SOAPConstants.SOAP11_CONSTANTS :
context.getMessageContext().getSOAPConstants();
if (faultClass != null) {
// Custom fault handling
try {
// If we have an element which is fault data, It can be:
// 1. A simple type that needs to be passed in to the constructor
// 2. A complex type that is the exception itself
if (faultData != null) {
if (faultData instanceof AxisFault) {
// This is our exception class
f = (AxisFault) faultData;
} else {
// We need to create the exception,
// passing the data to the constructor.
Class argClass = ConvertWrapper(faultData.getClass());
try {
Constructor con =
faultClass.getConstructor(
new Class[] { argClass });
f = (AxisFault) con.newInstance(new Object[] { faultData });
} catch(Exception e){
// Don't do anything here, since a problem above means
// we'll just fall through and use a plain AxisFault.
}
if (f == null && faultData instanceof Exception) {
f = AxisFault.makeFault((Exception)faultData);
}
}
}
// If we have an AxisFault, set the fields
if (AxisFault.class.isAssignableFrom(faultClass)) {
if (f == null) {
// this is to support the <exceptionName> detail
f = (AxisFault) faultClass.newInstance();
}
if (soapConstants == SOAPConstants.SOAP12_CONSTANTS) {
f.setFaultCode(code.getFaultCode());
SOAPFaultCodeBuilder c = code;
while ((c = c.getNext()) != null) {
f.addFaultSubCode(c.getFaultCode());
}
} else {
f.setFaultCode(faultCode);
}
f.setFaultString(faultString);
f.setFaultActor(faultActor);
f.setFaultNode(faultNode);
f.setFaultDetail(faultDetails);
}
} catch (Exception e) {
// Don't do anything here, since a problem above means
// we'll just fall through and use a plain AxisFault.
}
}
if (f == null) {
if (soapConstants == SOAPConstants.SOAP12_CONSTANTS) {
faultCode = code.getFaultCode();
if (code.getNext() != null)
{
Vector v = new Vector();
SOAPFaultCodeBuilder c = code;
while ((c = c.getNext()) != null)
v.add(c.getFaultCode());
faultSubCode = (QName[])v.toArray(new QName[v.size()]);
}
}
f = new AxisFault(faultCode,
faultSubCode,
faultString,
faultActor,
faultNode,
faultDetails);
try {
Vector headers = element.getEnvelope().getHeaders();
for (int i = 0; i < headers.size(); i++) {
SOAPHeaderElement header =
(SOAPHeaderElement) headers.elementAt(i);
f.addHeader(header);
}
} catch (AxisFault axisFault) {
// What to do here?
}
}
element.setFault(f);
}
public SOAPHandler onStartChild(String namespace,
String name,
String prefix,
Attributes attributes,
DeserializationContext context)
throws SAXException
{
SOAPHandler retHandler = null;
SOAPConstants soapConstants = context.getMessageContext() == null ?
SOAPConstants.SOAP11_CONSTANTS :
context.getMessageContext().getSOAPConstants();
QName qName;
// If we found the type for this field, get the deserializer
// otherwise, if this is the details element, use the special
// SOAPFaultDetailsBuilder handler to take care of custom fault data
if (soapConstants == SOAPConstants.SOAP12_CONSTANTS) {
qName = (QName)fields_soap12.get(name);
if (qName == null) {
QName thisQName = new QName(namespace, name);
if (thisQName.equals(Constants.QNAME_FAULTCODE_SOAP12))
return (code = new SOAPFaultCodeBuilder());
else if (thisQName.equals(Constants.QNAME_FAULTREASON_SOAP12))
return new SOAPFaultReasonBuilder(this);
else if (thisQName.equals(Constants.QNAME_FAULTDETAIL_SOAP12))
return new SOAPFaultDetailsBuilder(this);
}
} else {
qName = (QName)fields_soap11.get(name);
if (qName == null && name.equals(Constants.ELEM_FAULT_DETAIL))
return new SOAPFaultDetailsBuilder(this);
}
if (qName != null) {
Deserializer currentDeser = context.getDeserializerForType(qName);
if (currentDeser != null) {
currentDeser.registerValueTarget(new CallbackTarget(this, new QName(namespace, name)));
}
retHandler = (SOAPHandler) currentDeser;
}
return retHandler;
}
public void onEndChild(String namespace, String localName,
DeserializationContext context)
throws SAXException {
if (Constants.ELEM_FAULT_DETAIL.equals(localName)) {
MessageElement el = context.getCurElement();
List children = el.getChildren();
if (children != null) {
Element [] elements = new Element [children.size()];
for (int i = 0; i < elements.length; i++) {
try {
Node node = (Node) children.get(i);
if (node instanceof MessageElement) {
elements[i] = ((MessageElement) node).getAsDOM();
} else if(node instanceof Text){
Document tempDoc = XMLUtils.newDocument();
elements[i] = tempDoc.createElement("text");
elements[i].appendChild(tempDoc.importNode(node,true));
}
} catch (Exception e) {
throw new SAXException(e);
}
}
faultDetails = elements;
}
}
}
/*
* Defined by Callback.
* This method gets control when the callback is invoked.
* @param is the value to set.
* @param hint is an Object that provide additional hint information.
*/
public void setValue(Object value, Object hint)
{
String local = ((QName)hint).getLocalPart();
if (((QName)hint).getNamespaceURI().equals(Constants.URI_SOAP12_ENV)) {
if (local.equals(Constants.ELEM_FAULT_ROLE_SOAP12)) {
faultActor = (String) value;
} else if (local.equals(Constants.ELEM_TEXT_SOAP12)) {
faultString = (String) value;
} else if (local.equals(Constants.ELEM_FAULT_NODE_SOAP12)) {
faultNode = (String) value;
}
} else {
if (local.equals(Constants.ELEM_FAULT_CODE)) {
faultCode = (QName)value;
} else if (local.equals(Constants.ELEM_FAULT_STRING)) {
faultString = (String) value;
} else if (local.equals(Constants.ELEM_FAULT_ACTOR)) {
faultActor = (String) value;
}
}
}
/**
* A simple map of holder objects and their primitive types
*/
private static HashMap TYPES = new HashMap(7);
static {
TYPES.put(java.lang.Integer.class, int.class);
TYPES.put(java.lang.Float.class, float.class);
TYPES.put(java.lang.Boolean.class, boolean.class);
TYPES.put(java.lang.Double.class, double.class);
TYPES.put(java.lang.Byte.class, byte.class);
TYPES.put(java.lang.Short.class, short.class);
TYPES.put(java.lang.Long.class, long.class);
}
/**
* Internal method to convert wrapper classes to their base class
*/
private Class ConvertWrapper(Class cls) {
Class ret = (Class) TYPES.get(cls);
if (ret != null) {
return ret;
}
return cls;
}
}