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