/* * 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.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 , 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 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){ elements[i] = XMLUtils.newDocument().createElement("text"); elements[i].appendChild(node); } } 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; } }