001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.servicemix.soap.bindings.soap.interceptors;
018    
019    import java.net.URI;
020    
021    import javax.xml.namespace.QName;
022    import javax.xml.stream.XMLStreamConstants;
023    import javax.xml.stream.XMLStreamReader;
024    import javax.xml.transform.Source;
025    import javax.xml.transform.dom.DOMSource;
026    
027    import org.apache.servicemix.soap.api.Message;
028    import org.apache.servicemix.soap.bindings.soap.SoapConstants;
029    import org.apache.servicemix.soap.bindings.soap.SoapFault;
030    import org.apache.servicemix.soap.bindings.soap.SoapVersion;
031    import org.apache.servicemix.soap.core.AbstractInterceptor;
032    import org.apache.servicemix.soap.util.DomUtil;
033    import org.apache.servicemix.soap.util.stax.StaxUtil;
034    import org.w3c.dom.Element;
035    
036    /**
037     * @author <a href="mailto:gnodet [at] gmail.com">Guillaume Nodet</a>
038     */
039    public class SoapFaultInInterceptor extends AbstractInterceptor {
040    
041        public void handleMessage(Message message) {
042            XMLStreamReader xmlReader = message.getContent(XMLStreamReader.class);
043            if (xmlReader == null || xmlReader.getEventType() != XMLStreamConstants.START_ELEMENT) {
044                return;
045            }
046            SoapVersion soapVersion = message.get(SoapVersion.class);
047            if (!soapVersion.getFault().equals(xmlReader.getName())) {
048                return;
049            }
050            // Read fault as DOM
051            Element el = StaxUtil.createElement(xmlReader);
052            SoapFault fault = readFault(el);
053            message.setContent(Exception.class, fault);
054        }
055    
056        private SoapFault readFault(Element element) throws SoapFault {
057            QName code = null;
058            QName subcode = null;
059            String reason = null;
060            URI node = null;
061            URI role = null;
062            Source details = null;
063            // Parse soap 1.1 faults
064            if (SoapConstants.SOAP_11_URI.equals(element.getNamespaceURI())) {
065                // Fault code
066                Element child = DomUtil.getFirstChildElement(element);
067                checkElementName(child, SoapConstants.SOAP_11_FAULTCODE);
068                code = DomUtil.createQName(child, DomUtil.getElementText(child));
069                // Fault string
070                child = DomUtil.getNextSiblingElement(child);
071                checkElementName(child, SoapConstants.SOAP_11_FAULTSTRING);
072                reason = DomUtil.getElementText(child);
073                child = DomUtil.getNextSiblingElement(child);
074                QName childname = DomUtil.getQName(child);
075                // Fault actor
076                if (SoapConstants.SOAP_11_FAULTACTOR.equals(childname)) {
077                    node = URI.create(DomUtil.getElementText(child));
078                    child = DomUtil.getNextSiblingElement(child);
079                    childname = DomUtil.getQName(child);
080                }
081                // Fault details
082                if (SoapConstants.SOAP_11_FAULTDETAIL.equals(childname)) {
083                    Element subchild = DomUtil.getFirstChildElement(child);
084                    if (subchild != null) {
085                        details = new DOMSource(subchild);
086                        subchild = DomUtil.getNextSiblingElement(subchild);
087                        if (subchild != null) {
088                            throw new SoapFault(SoapFault.RECEIVER, "Multiple elements are not supported in Detail");
089                        }
090                    }
091                    child = DomUtil.getNextSiblingElement(child);
092                    childname = DomUtil.getQName(child);
093                }
094                // Nothing should be left
095                if (childname != null) {
096                    throw new SoapFault(SoapFault.SENDER, "Unexpected element: " + childname);
097                }
098            // Parse soap 1.2 faults
099            } else {
100                // Fault code
101                Element child = DomUtil.getFirstChildElement(element);
102                checkElementName(child, SoapConstants.SOAP_12_FAULTCODE);
103                Element subchild = DomUtil.getFirstChildElement(child);
104                checkElementName(subchild, SoapConstants.SOAP_12_FAULTVALUE);
105                code = DomUtil.createQName(subchild, DomUtil.getElementText(subchild));
106                if (!SoapConstants.SOAP_12_CODE_DATAENCODINGUNKNOWN.equals(code) &&
107                    !SoapConstants.SOAP_12_CODE_MUSTUNDERSTAND.equals(code) &&
108                    !SoapConstants.SOAP_12_CODE_RECEIVER.equals(code) &&
109                    !SoapConstants.SOAP_12_CODE_SENDER.equals(code) &&
110                    !SoapConstants.SOAP_12_CODE_VERSIONMISMATCH.equals(code)) {
111                    throw new SoapFault(SoapFault.SENDER, "Unexpected fault code: " + code); 
112                }
113                subchild = DomUtil.getNextSiblingElement(subchild);
114                if (subchild != null) {
115                    checkElementName(subchild, SoapConstants.SOAP_12_FAULTSUBCODE);
116                    Element subsubchild = DomUtil.getFirstChildElement(subchild);
117                    checkElementName(subsubchild, SoapConstants.SOAP_12_FAULTVALUE);
118                    subcode = DomUtil.createQName(subsubchild, DomUtil.getElementText(subsubchild));
119                    subsubchild = DomUtil.getNextSiblingElement(subsubchild);
120                    if (subsubchild != null) {
121                        checkElementName(subsubchild, SoapConstants.SOAP_12_FAULTSUBCODE);
122                        throw new SoapFault(SoapFault.RECEIVER, "Unsupported nested subcodes");
123                    }
124                }
125                // Fault reason
126                child = DomUtil.getNextSiblingElement(child);
127                checkElementName(child, SoapConstants.SOAP_12_FAULTREASON);
128                subchild = DomUtil.getFirstChildElement(child);
129                checkElementName(subchild, SoapConstants.SOAP_12_FAULTTEXT);
130                reason = DomUtil.getElementText(subchild);
131                subchild = DomUtil.getNextSiblingElement(subchild);
132                if (subchild != null) {
133                    throw new SoapFault(SoapFault.RECEIVER, "Unsupported multiple reasons");
134                }
135                // Fault node
136                child = DomUtil.getNextSiblingElement(child);
137                QName childname = DomUtil.getQName(child);
138                if (SoapConstants.SOAP_12_FAULTNODE.equals(childname)) {
139                    node = URI.create(DomUtil.getElementText(child));
140                    child = DomUtil.getNextSiblingElement(child);
141                    childname = DomUtil.getQName(child);
142                }
143                // Fault role
144                if (SoapConstants.SOAP_12_FAULTROLE.equals(childname)) {
145                    role = URI.create(DomUtil.getElementText(child));
146                    child = DomUtil.getNextSiblingElement(child);
147                    childname = DomUtil.getQName(child);
148                }
149                // Fault details
150                if (SoapConstants.SOAP_12_FAULTDETAIL.equals(childname)) {
151                    subchild = DomUtil.getFirstChildElement(child);
152                    if (subchild != null) {
153                        details = new DOMSource(subchild);
154                        subchild = DomUtil.getNextSiblingElement(subchild);
155                        if (subchild != null) {
156                            throw new SoapFault(SoapFault.RECEIVER, "Multiple elements are not supported in Detail");
157                        }
158                    }
159                    child = DomUtil.getNextSiblingElement(child);
160                    childname = DomUtil.getQName(child);
161                }
162                // Nothing should be left
163                if (childname != null) {
164                    throw new SoapFault(SoapFault.SENDER, "Unexpected element: " + childname);
165                }
166            }
167            return new SoapFault(code, subcode, reason, node, role, details);
168        }
169        
170        private void checkElementName(Element element, QName expected) throws SoapFault {
171            QName name= DomUtil.getQName(element);
172            if (!expected.equals(name)) {
173                throw new SoapFault(SoapFault.SENDER, "Expected element: " + expected + " but found " + name);
174            }            
175        }
176    
177    }