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;
018    
019    import java.io.ByteArrayOutputStream;
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.net.HttpURLConnection;
023    import java.util.ArrayList;
024    import java.util.Iterator;
025    import java.util.List;
026    
027    import javax.jbi.messaging.ExchangeStatus;
028    import javax.jbi.messaging.Fault;
029    import javax.jbi.messaging.InOnly;
030    import javax.jbi.messaging.InOptionalOut;
031    import javax.jbi.messaging.InOut;
032    import javax.jbi.messaging.MessageExchange;
033    import javax.jbi.messaging.MessagingException;
034    import javax.jbi.messaging.NormalizedMessage;
035    import javax.xml.bind.JAXBException;
036    import javax.xml.namespace.QName;
037    import javax.xml.stream.XMLStreamReader;
038    import javax.xml.transform.Source;
039    
040    import org.apache.cxf.Bus;
041    import org.apache.cxf.binding.soap.SoapMessage;
042    import org.apache.cxf.binding.soap.interceptor.MustUnderstandInterceptor;
043    import org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor;
044    import org.apache.cxf.binding.soap.interceptor.StartBodyInterceptor;
045    import org.apache.cxf.endpoint.Endpoint;
046    import org.apache.cxf.helpers.IOUtils;
047    import org.apache.cxf.interceptor.AttachmentInInterceptor;
048    import org.apache.cxf.interceptor.Interceptor;
049    import org.apache.cxf.interceptor.StaxInInterceptor;
050    import org.apache.cxf.io.CachedOutputStream;
051    import org.apache.cxf.message.Attachment;
052    import org.apache.cxf.message.Exchange;
053    import org.apache.cxf.message.ExchangeImpl;
054    import org.apache.cxf.message.Message;
055    import org.apache.cxf.message.MessageUtils;
056    import org.apache.cxf.phase.PhaseInterceptorChain;
057    import org.apache.cxf.phase.PhaseManager;
058    import org.apache.cxf.service.model.BindingOperationInfo;
059    import org.apache.cxf.service.model.EndpointInfo;
060    import org.apache.cxf.staxutils.StaxUtils;
061    import org.apache.cxf.transport.MessageObserver;
062    import org.apache.cxf.ws.addressing.AddressingProperties;
063    import org.apache.cxf.ws.addressing.Names;
064    import org.apache.cxf.ws.addressing.RelatesToType;
065    import org.apache.cxf.ws.addressing.soap.MAPCodec;
066    import org.apache.servicemix.common.JbiConstants;
067    import org.apache.servicemix.cxfbc.interceptors.JbiInWsdl1Interceptor;
068    import org.apache.servicemix.cxfbc.interceptors.SchemaValidationInInterceptor;
069    
070    import org.xml.sax.SAXException;
071    
072    import com.sun.xml.bind.v2.runtime.reflect.ListIterator;
073    
074    public class CxfBcProviderMessageObserver implements MessageObserver {
075        ByteArrayOutputStream response = new ByteArrayOutputStream();
076    
077        boolean written;
078    
079        String contentType;
080    
081    
082        private CxfBcProvider providerEndpoint;
083        
084        private MessageObserver sharedMessageObserver;
085    
086        public CxfBcProviderMessageObserver(CxfBcProvider providerEndpoint) {
087            this.providerEndpoint = providerEndpoint;
088        }
089    
090        public ByteArrayOutputStream getResponseStream() throws Exception {
091            synchronized (this) {
092                if (!written) {
093                    wait(1000000000);
094                }
095            }
096            return response;
097        }
098    
099        public String getResponseContentType() {
100            return contentType;
101        }
102    
103        public void onMessage(Message message) {
104            try {
105                // create Interceptor chain
106               
107                PhaseManager pm = providerEndpoint.getBus().getExtension(
108                        PhaseManager.class);
109                List<Interceptor<? extends Message>> inList = new ArrayList<Interceptor<? extends Message>>();
110                inList.add(new ReadHeadersInterceptor(this.providerEndpoint.getBus()));
111                inList.add(new StartBodyInterceptor());
112                inList.add(new MustUnderstandInterceptor());
113                inList.add(new StaxInInterceptor());
114                inList.add(new JbiInWsdl1Interceptor(this.providerEndpoint.isUseJBIWrapper(),
115                        this.providerEndpoint.isUseSOAPEnvelope()));
116                if (this.providerEndpoint.isSchemaValidationEnabled()) {
117                    inList.add(new SchemaValidationInInterceptor(this.providerEndpoint.isUseJBIWrapper(),
118                            this.providerEndpoint.isUseSOAPEnvelope()));
119                }
120                inList.add(new AttachmentInInterceptor());
121                PhaseInterceptorChain inChain = new PhaseInterceptorChain(pm.getInPhases());
122                inChain.add(providerEndpoint.getBus().getInInterceptors());
123                inChain.add(inList);
124                inChain.add(providerEndpoint.getInInterceptors());
125                       
126                contentType = (String) message.get(Message.CONTENT_TYPE);
127                SoapMessage soapMessage = 
128                    (SoapMessage) this.providerEndpoint.getCxfEndpoint().getBinding().createMessage(message);
129                        
130                soapMessage
131                        .put(org.apache.cxf.message.Message.REQUESTOR_ROLE, true);
132                soapMessage.setInterceptorChain(inChain);
133                MessageExchange messageExchange = soapMessage.getExchange().get(MessageExchange.class);
134                if (messageExchange == null) {
135                    // probably, that's a WS-RM Response; use the messageObserver defined in exchange
136                    MessageObserver messageObserver = message.getExchange().get(MessageObserver.class);
137                    if (messageObserver != null) {
138                        messageObserver.onMessage(message);
139                        return;
140                    } else {
141                        //decoupled endpoint case we need try to restore the exchange first;
142                        Exchange exchange = restoreExchange(soapMessage);
143                        if (exchange != null) {
144                            MessageObserver rmMessageObserver = exchange.get(MessageObserver.class);
145                            if (rmMessageObserver != null) {
146                                //means it createsequence messagee
147                                sharedMessageObserver = rmMessageObserver;
148                                rmMessageObserver.onMessage(soapMessage);
149                                return;
150                            }
151                        } else {
152                            //means it acknowlagement message
153                            if (sharedMessageObserver != null) {
154                                sharedMessageObserver.onMessage(soapMessage);
155                                return;
156                            }
157                        }
158                    }
159                }
160                if (messageExchange != null && messageExchange.getStatus() != ExchangeStatus.ACTIVE) {
161                    return;
162                }
163                          
164                
165                
166                inChain.doIntercept(soapMessage);
167                closeConnectionStream(soapMessage);
168                if (soapMessage.getContent(Exception.class) != null || soapMessage.getContent(Source.class) == null) {    
169                    Exception ex = soapMessage.getContent(Exception.class);
170                    if (!(soapMessage.getExchange().get(MessageExchange.class) instanceof InOnly) && ex != null) {
171                        messageExchange.setStatus(ExchangeStatus.ERROR);
172                        messageExchange.setError(ex);
173                        providerEndpoint.getContext().getDeliveryChannel().send(
174                                messageExchange);
175                    }
176                    return;
177                }
178              
179                messageExchange = soapMessage.getExchange().get(MessageExchange.class);
180                if (MessageUtils.isPartialResponse(soapMessage)) {
181                    //partial response for origianl channel when use decoupled endpoint
182                    return;
183                }
184                if (soapMessage.getExchange().get(BindingOperationInfo.class).getOperationInfo().isOneWay()) {
185                    messageExchange.setStatus(ExchangeStatus.DONE);
186                } else if (soapMessage.get("jbiFault") != null
187                        && soapMessage.get("jbiFault").equals(true)) {
188                    Fault fault = messageExchange.createFault();
189                    fault.setContent(soapMessage.getContent(Source.class));
190                    messageExchange.setFault(fault);
191                    if (soapMessage.get("faultstring") != null) {
192                        messageExchange.setProperty("faultstring", soapMessage.get("faultstring"));
193                    }
194                    if (soapMessage.get("faultcode") != null) {
195                        messageExchange.setProperty("faultcode", soapMessage.get("faultcode"));
196                    }
197                    if (soapMessage.get("hasdetail") != null) {
198                        messageExchange.setProperty("hasdetail", soapMessage.get("hasdetail"));
199                    }
200    
201                } else if (messageExchange instanceof InOut) {
202                    NormalizedMessage msg = messageExchange.createMessage();
203                    msg.setContent(soapMessage.getContent(Source.class));
204                    toNMSAttachments(msg, soapMessage);
205                    messageExchange.setMessage(msg, "out");
206                } else if (messageExchange instanceof InOptionalOut) {
207                    if (soapMessage.getContent(Source.class) != null) {
208                        NormalizedMessage msg = messageExchange.createMessage();
209                        msg.setContent(soapMessage.getContent(Source.class));
210                        toNMSAttachments(msg, soapMessage);
211                        messageExchange.setMessage(msg, "out");
212                    } else {
213                        messageExchange.setStatus(ExchangeStatus.DONE);
214                    }
215                } else {
216                    messageExchange.setStatus(ExchangeStatus.DONE);
217    
218                }
219                boolean txSync = messageExchange.getStatus() == ExchangeStatus.ACTIVE
220                        && messageExchange.isTransacted()
221                        && Boolean.TRUE.equals(messageExchange
222                                .getProperty(JbiConstants.SEND_SYNC));
223                if (txSync) {
224                    providerEndpoint.getContext().getDeliveryChannel().sendSync(
225                            messageExchange);
226                } else {
227                    providerEndpoint.getContext().getDeliveryChannel().send(
228                            messageExchange);
229                }
230    
231            } catch (Exception e) {
232                e.printStackTrace();
233            } finally {
234                synchronized (this) {
235                    written = true;
236                    notifyAll();
237                }
238            }
239        }
240    
241        private Exchange restoreExchange(SoapMessage message) throws IOException, SAXException, JAXBException {
242            InputStream is = message.getContent(InputStream.class);
243            //cache the message
244            CachedOutputStream bos = new CachedOutputStream();
245            IOUtils.copy(is, bos);
246            bos.flush();
247            is.close();
248            message.setContent(InputStream.class, bos.getInputStream());
249            ReadHeadersInterceptor readHeadersInterceptor = 
250                    new ReadHeadersInterceptor(this.providerEndpoint.getBus());
251            readHeadersInterceptor.handleMessage(message);
252            for (Interceptor<?> interceptor : this.providerEndpoint.getBus().getOutInterceptors()) {
253                if (interceptor.getClass().getName().equals("org.apache.cxf.ws.addressing.soap.MAPCodec")) {
254                    MAPCodec mapCodec = (MAPCodec) interceptor;
255                    AddressingProperties maps = mapCodec.unmarshalMAPs(message);
256                    if (maps != null
257                        && maps.getRelatesTo() != null
258                        && isRelationshipReply(maps.getRelatesTo())) { 
259                        Exchange correlatedExchange =
260                                mapCodec.getUncorrelatedExchanges().get(maps.getRelatesTo().getValue());
261                        message.setContent(InputStream.class, bos.getInputStream());
262                        bos.close();
263                        XMLStreamReader xmlReader = 
264                            StaxUtils.createXMLStreamReader(message.getContent(InputStream.class));
265                        message.setContent(XMLStreamReader.class, xmlReader);       
266                        message.setContent(InputStream.class, bos.getInputStream());
267                        return correlatedExchange;
268                        
269                    }
270                    
271                    
272                }
273            }
274            message.setContent(InputStream.class, bos.getInputStream());
275            bos.close();
276            XMLStreamReader xmlReader = 
277                StaxUtils.createXMLStreamReader(message.getContent(InputStream.class));
278            message.setContent(XMLStreamReader.class, xmlReader);       
279            message.setContent(InputStream.class, bos.getInputStream());
280            return null;
281        }
282    
283        private void closeConnectionStream(SoapMessage soapMessage) throws IOException {
284            InputStream is = soapMessage.getContent(InputStream.class);
285            if (is != null) {
286                CachedOutputStream bos = new CachedOutputStream();
287                IOUtils.copy(is, bos);
288                bos.flush();
289                is.close();
290                soapMessage.setContent(InputStream.class, bos.getInputStream());
291                bos.close();
292            }
293    
294        }    
295    
296        private void toNMSAttachments(NormalizedMessage normalizedMessage,
297                Message soapMessage) throws MessagingException {
298            if (soapMessage.getAttachments() != null) {
299                for (Attachment att : soapMessage.getAttachments()) {
300                    normalizedMessage.addAttachment(att.getId(), att
301                            .getDataHandler());
302                }
303            }
304        }
305    
306        private boolean isRelationshipReply(RelatesToType relatesTo) {
307            return Names.WSA_RELATIONSHIP_REPLY.equals(relatesTo.getRelationshipType());
308        }
309        
310    }