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.cxfbc.interceptors;
018    
019    import java.util.ArrayList;
020    import java.util.Iterator;
021    import java.util.List;
022    
023    import javax.xml.stream.XMLStreamException;
024    import javax.xml.stream.XMLStreamWriter;
025    import javax.xml.transform.Source;
026    
027    import org.w3c.dom.Attr;
028    import org.w3c.dom.Element;
029    import org.w3c.dom.NamedNodeMap;
030    import org.w3c.dom.Node;
031    import org.w3c.dom.NodeList;
032    
033    import org.apache.cxf.binding.soap.SoapMessage;
034    import org.apache.cxf.binding.soap.SoapVersion;
035    import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
036    import org.apache.cxf.binding.soap.model.SoapBindingInfo;
037    import org.apache.cxf.binding.soap.model.SoapHeaderInfo;
038    import org.apache.cxf.endpoint.Endpoint;
039    import org.apache.cxf.headers.Header;
040    import org.apache.cxf.helpers.NSStack;
041    import org.apache.cxf.interceptor.Fault;
042    import org.apache.cxf.message.Message;
043    import org.apache.cxf.phase.Phase;
044    import org.apache.cxf.service.model.BindingMessageInfo;
045    import org.apache.cxf.service.model.BindingOperationInfo;
046    import org.apache.cxf.service.model.MessagePartInfo;
047    import org.apache.cxf.staxutils.StaxUtils;
048    import org.apache.servicemix.jbi.jaxp.SourceTransformer;
049    import org.apache.servicemix.jbi.util.QNameUtil;
050    import org.apache.servicemix.soap.interceptors.jbi.JbiConstants;
051    import org.apache.servicemix.soap.util.DomUtil;
052    
053    
054    
055    
056    /**
057     * @author <a href="mailto:gnodet [at] gmail.com">Guillaume Nodet</a>
058     */
059    public class JbiOutWsdl1Interceptor extends AbstractSoapInterceptor {
060        
061        private boolean useJBIWrapper = true;
062        private boolean useSOAPWrapper = true;
063        
064        public JbiOutWsdl1Interceptor(boolean useJBIWrapper, boolean useSOAPWrapper) {
065            super(Phase.MARSHAL);
066            this.useJBIWrapper = useJBIWrapper;
067            this.useSOAPWrapper = useSOAPWrapper;
068        }
069    
070        public void handleMessage(SoapMessage message) {
071            try {
072                Source source = message.getContent(Source.class);
073                if (source == null) {
074                    return;
075                }
076                
077                Element element = new SourceTransformer().toDOMElement(source);
078                XMLStreamWriter xmlWriter = message
079                    .getContent(XMLStreamWriter.class);
080                
081                if (!useJBIWrapper) {
082                    SoapVersion soapVersion = message.getVersion();                
083                    if (element != null) {                                                      
084                        // if this message is coming from the CxfBCConsumer
085                        Element bodyElement = null;
086                        if (useSOAPWrapper) {
087                            bodyElement = (Element) element.getElementsByTagNameNS(
088                                element.getNamespaceURI(),
089                                soapVersion.getBody().getLocalPart()).item(0);
090                        }
091                        if (bodyElement != null) {
092                            StaxUtils.writeElement((Element)bodyElement.getFirstChild(), xmlWriter, false);                           
093                        } else {
094                            // if this message is coming from the CxfBCProvider 
095                            StaxUtils.writeElement(element, xmlWriter, false);
096                        }
097                    }
098                    return;
099                }
100                
101                if (!JbiConstants.WSDL11_WRAPPER_NAMESPACE.equals(element
102                        .getNamespaceURI())
103                        || !JbiConstants.WSDL11_WRAPPER_MESSAGE_LOCALNAME
104                                .equals(element.getLocalName())) {
105                    throw new Fault(new Exception("Message wrapper element is '"
106                            + QNameUtil.toString(element) + "' but expected '{"
107                            + JbiConstants.WSDL11_WRAPPER_NAMESPACE + "}message'"));
108                }
109                
110                //save namespace which is potentially used by the soap message
111                List<Attr> nsList = saveLaterUsedNS(element);
112                //add the saved namespace to the soap body
113                for (Attr attr : nsList) {
114                    xmlWriter.writeAttribute(attr.getName(), attr.getValue());
115                }
116    
117                BindingOperationInfo bop = message.getExchange().get(
118                        BindingOperationInfo.class);
119                if (bop == null) {
120                    throw new Fault(
121                            new Exception("Operation not bound on this message"));
122                }
123                BindingMessageInfo msg = isRequestor(message) ? bop.getInput()
124                        : bop.getOutput();
125    
126                
127                SoapBindingInfo binding = (SoapBindingInfo) message.getExchange()
128                        .get(Endpoint.class).getEndpointInfo().getBinding();
129                String style = binding.getStyle(bop.getOperationInfo());
130                if (style == null) {
131                    style = binding.getStyle();
132                }
133    
134                if ("rpc".equals(style)) {
135                    addOperationNode(message, xmlWriter);
136                    getRPCPartWrapper(msg, element, message, xmlWriter);
137                } else {
138                    Element partWrapper = DomUtil.getFirstChildElement(element);
139                    while (partWrapper != null) {
140                        List<NodeList> partsContent = getPartsContent(message, element, partWrapper, msg); 
141                        for (NodeList nl : partsContent) {
142                            for (int i = 0; i < nl.getLength(); i++) {
143                                Node n = nl.item(i);                            
144                                StaxUtils.writeNode(n, xmlWriter, false);
145                            }
146                        }
147                        partWrapper = DomUtil.getNextSiblingElement(partWrapper);
148                    }
149                }
150    
151                if ("rpc".equals(style)) {
152                    xmlWriter.writeEndElement();
153                }
154                           
155            } catch (Fault e) {
156                throw e;
157            } catch (Exception e) {
158                throw new Fault(e);
159            }
160        }
161    
162        private List<Attr> saveLaterUsedNS(Element element) {
163            List<Attr> nsList = new ArrayList<Attr>();
164            NamedNodeMap attributes = element.getAttributes();
165            for (int i = 0; i < attributes.getLength(); i++) {
166                if (attributes.item(i) instanceof Attr) {
167                    Attr attr = (Attr) attributes.item(i);
168                    if (attr.getName().startsWith("xmlns:")
169                            && !(attr.getName().startsWith("xmlns:" + JbiConstants.WSDL11_WRAPPER_MESSAGE_PREFIX)
170                                    || attr.getName().startsWith("xmlns:" + JbiConstants.WSDL11_WRAPPER_PREFIX))) {
171                        nsList.add(attr);
172                    }
173                }
174            }
175            return nsList;
176        }
177    
178        
179        
180        private void getRPCPartWrapper(BindingMessageInfo msg, 
181                                       Element element,
182                                       SoapMessage message, 
183                                       XMLStreamWriter xmlWriter) {
184            try {
185                List<MessagePartInfo> parts = msg.getMessageParts();
186                Iterator iter = parts.iterator();
187                Element partWrapper = DomUtil.getFirstChildElement(element);
188                while (partWrapper != null) {
189                    MessagePartInfo msgPart = (MessagePartInfo) iter.next();
190                    String prefix = msgPart.getName().getPrefix();
191                    String name = msgPart.getName().getLocalPart();
192                    StaxUtils.writeStartElement(xmlWriter, prefix, name, "");
193                    List<NodeList> partsContent = getPartsContent(message, element,
194                                                                  partWrapper, msg);
195                    for (NodeList nl : partsContent) {
196                        for (int i = 0; i < nl.getLength(); i++) {
197                            Node n = nl.item(i);
198                            StaxUtils.writeNode(n, xmlWriter, false);
199                        }
200                    }
201                    xmlWriter.writeEndElement();
202                    partWrapper = DomUtil.getNextSiblingElement(partWrapper);
203                }
204            } catch (Fault e) {
205                throw e;
206            } catch (Exception e) {
207                throw new Fault(e);
208            }
209        }
210        
211        // Get each parts content
212        private List<NodeList> getPartsContent(SoapMessage message,
213                                               Element element,
214                                               Element partWrapper, 
215                                               BindingMessageInfo msg) {
216            List<NodeList> partsContent = new ArrayList<NodeList>();        
217            if (partWrapper != null) {
218                if (!JbiConstants.WSDL11_WRAPPER_NAMESPACE.equals(element.getNamespaceURI())
219                        || !JbiConstants.WSDL11_WRAPPER_PART_LOCALNAME
220                                .equals(partWrapper.getLocalName())) {
221                    throw new Fault(new Exception(
222                            "Unexpected part wrapper element '"
223                                    + QNameUtil.toString(element)
224                                    + "' expected '{"
225                                    + JbiConstants.WSDL11_WRAPPER_NAMESPACE
226                                    + "}part'"));
227                }
228                NodeList nodes = partWrapper.getChildNodes();
229                partsContent.add(nodes);
230            }
231                
232            List<Header> headerList = message.getHeaders();
233            List<SoapHeaderInfo> headers = msg.getExtensors(SoapHeaderInfo.class);
234            for (SoapHeaderInfo header : headers) {
235                if (partsContent.size() == 0) {
236                    break;
237                }
238                NodeList nl = partsContent.get(0);
239                if (header.getPart().getConcreteName().getNamespaceURI().equals(nl.item(0).getNamespaceURI())
240                        && header.getPart().getConcreteName().getLocalPart().equals(nl.item(0).getLocalName())) {
241                    headerList.add(new Header(header.getPart().getConcreteName(),
242                            nl.item(0)));
243                    partsContent.remove(0);
244                }
245                           
246            }
247            
248            return partsContent;
249        }
250        
251    
252        protected String addOperationNode(Message message, XMLStreamWriter xmlWriter)
253            throws XMLStreamException {
254            String responseSuffix = !isRequestor(message) ? "Response" : "";
255            BindingOperationInfo boi = message.getExchange().get(
256                    BindingOperationInfo.class);
257            String ns = boi.getName().getNamespaceURI();
258            NSStack nsStack = new NSStack();
259            nsStack.push();
260            nsStack.add(ns);
261            String prefix = nsStack.getPrefix(ns);
262            StaxUtils.writeStartElement(xmlWriter, prefix, boi.getName()
263                    .getLocalPart()
264                    + responseSuffix, ns);
265            return ns;
266        }
267    
268    }