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.camel.component.cxf.transport;
018    
019    import java.io.IOException;
020    import java.io.OutputStream;
021    
022    import org.apache.camel.CamelContext;
023    import org.apache.camel.Consumer;
024    import org.apache.camel.Endpoint;
025    import org.apache.camel.Exchange;
026    import org.apache.camel.FailedToCreateConsumerException;
027    import org.apache.camel.NoSuchEndpointException;
028    import org.apache.camel.Processor;
029    import org.apache.camel.component.cxf.common.header.CxfHeaderHelper;
030    import org.apache.camel.component.cxf.common.message.DefaultCxfMesssageMapper;
031    import org.apache.camel.spi.HeaderFilterStrategy;
032    import org.apache.camel.util.ObjectHelper;
033    import org.apache.camel.util.ServiceHelper;
034    import org.apache.cxf.Bus;
035    import org.apache.cxf.common.logging.LogUtils;
036    import org.apache.cxf.configuration.Configurable;
037    import org.apache.cxf.configuration.Configurer;
038    import org.apache.cxf.io.CachedOutputStream;
039    import org.apache.cxf.message.Message;
040    import org.apache.cxf.message.MessageImpl;
041    import org.apache.cxf.service.model.EndpointInfo;
042    import org.apache.cxf.transport.AbstractConduit;
043    import org.apache.cxf.transport.AbstractDestination;
044    import org.apache.cxf.transport.Conduit;
045    import org.apache.cxf.transport.ConduitInitiator;
046    import org.apache.cxf.transport.MessageObserver;
047    import org.apache.cxf.ws.addressing.EndpointReferenceType;
048    import org.apache.cxf.wsdl.EndpointReferenceUtils;
049    import org.slf4j.Logger;
050    import org.slf4j.LoggerFactory;
051    
052    /**
053     * @version 
054     *
055     * Forwards messages from Camel to CXF and the CXF response back to Camel
056     */
057    public class CamelDestination extends AbstractDestination implements Configurable {
058        protected static final String BASE_BEAN_NAME_SUFFIX = ".camel-destination";
059        private static final Logger LOG = LoggerFactory.getLogger(CamelDestination.class);
060        // used for places where CXF requires JUL
061        private static final java.util.logging.Logger JUL_LOG = LogUtils.getL7dLogger(CamelDestination.class);
062        final ConduitInitiator conduitInitiator;
063        CamelContext camelContext;
064        Consumer consumer;
065        String camelDestinationUri;
066    
067        private Endpoint destinationEndpoint;
068        private HeaderFilterStrategy headerFilterStrategy;
069        private boolean checkException;
070    
071        public CamelDestination(CamelContext camelContext, Bus bus, ConduitInitiator ci, EndpointInfo info) throws IOException {
072            this(camelContext, bus, ci, info, null, false);
073        }
074    
075        public CamelDestination(CamelContext camelContext, Bus bus, ConduitInitiator ci, EndpointInfo info,
076                                HeaderFilterStrategy headerFilterStrategy, boolean checkException) throws IOException {
077            super(bus, getTargetReference(info, bus), info);
078            this.camelContext = camelContext;
079            conduitInitiator = ci;
080            camelDestinationUri = endpointInfo.getAddress().substring(CamelTransportConstants.CAMEL_TRANSPORT_PREFIX.length());
081            if (camelDestinationUri.startsWith("//")) {
082                camelDestinationUri = camelDestinationUri.substring(2);
083            }
084            initConfig();
085            this.headerFilterStrategy = headerFilterStrategy;
086            this.checkException = checkException;
087        }
088    
089        protected java.util.logging.Logger getLogger() {
090            return JUL_LOG;
091        }
092    
093        public void setCheckException(boolean exception) {
094            checkException = exception;
095        }
096    
097        public boolean isCheckException() {
098            return checkException;
099        }
100    
101        /**
102         * @param inMessage the incoming message
103         * @return the inbuilt backchannel
104         */
105        protected Conduit getInbuiltBackChannel(Message inMessage) {
106            //we can pass the message back by looking up the camelExchange from inMessage
107            return new BackChannelConduit(inMessage);
108        }
109    
110        public void activate() {
111            LOG.debug("CamelDestination activate().... ");
112            ObjectHelper.notNull(camelContext, "CamelContext", this);
113            try {
114                LOG.debug("establishing Camel connection");
115                destinationEndpoint = getCamelContext().getEndpoint(camelDestinationUri);
116                if (destinationEndpoint == null) {
117                    throw new NoSuchEndpointException(camelDestinationUri);
118                }
119                consumer = destinationEndpoint.createConsumer(new ConsumerProcessor());
120                ServiceHelper.startService(consumer);
121            } catch (NoSuchEndpointException nex) {
122                throw nex;
123            } catch (Exception ex) {
124                if (destinationEndpoint == null) {
125                    throw new FailedToCreateConsumerException(camelDestinationUri, ex);
126                }
127                throw new FailedToCreateConsumerException(destinationEndpoint, ex);
128            }
129        }
130    
131        public void deactivate() {
132            try {
133                ServiceHelper.stopService(consumer);
134            } catch (Exception e) {
135                LOG.warn("Error stopping consumer", e);
136            }
137        }
138    
139        public void shutdown() {
140            LOG.debug("CamelDestination shutdown()");
141            this.deactivate();
142        }
143    
144        public CamelContext getCamelContext() {
145            return camelContext;
146        }
147    
148        public void setCamelContext(CamelContext camelContext) {
149            this.camelContext = camelContext;
150        }
151    
152        protected void incoming(org.apache.camel.Exchange camelExchange) {
153            LOG.debug("server received request: ", camelExchange);
154            DefaultCxfMesssageMapper beanBinding = new DefaultCxfMesssageMapper();
155            org.apache.cxf.message.Message inMessage =
156                beanBinding.createCxfMessageFromCamelExchange(camelExchange, headerFilterStrategy);
157    
158            inMessage.put(CamelTransportConstants.CAMEL_EXCHANGE, camelExchange);
159            ((MessageImpl) inMessage).setDestination(this);
160    
161            // Handling the incoming message
162            // The response message will be send back by the outgoing chain
163            incomingObserver.onMessage(inMessage);
164        }
165    
166        public String getBeanName() {
167            if (endpointInfo == null || endpointInfo.getName() == null) {
168                return "default" + BASE_BEAN_NAME_SUFFIX;
169            }
170            return endpointInfo.getName().toString() + BASE_BEAN_NAME_SUFFIX;
171        }
172    
173        public String getCamelDestinationUri() {
174            return camelDestinationUri;
175        }
176    
177        private void initConfig() {
178            //we could configure the camel context here
179            if (bus != null) {
180                Configurer configurer = bus.getExtension(Configurer.class);
181                if (null != configurer) {
182                    configurer.configureBean(this);
183                }
184            }
185        }
186    
187        protected class ConsumerProcessor implements Processor {
188            public void process(Exchange exchange) {
189                try {
190                    incoming(exchange);
191                } catch (Throwable ex) {
192                    exchange.setException(ex);
193                }
194            }
195        }
196    
197        // this should deal with the cxf message
198        protected class BackChannelConduit extends AbstractConduit {
199            protected Message inMessage;
200            Exchange camelExchange;
201            org.apache.cxf.message.Exchange cxfExchange;
202    
203            BackChannelConduit(Message message) {
204                super(EndpointReferenceUtils.getAnonymousEndpointReference());
205                inMessage = message;
206                cxfExchange = inMessage.getExchange();
207                camelExchange = cxfExchange.get(Exchange.class);
208            }
209    
210            /**
211             * Register a message observer for incoming messages.
212             *
213             * @param observer the observer to notify on receipt of incoming
214             */
215            public void setMessageObserver(MessageObserver observer) {
216                // shouldn't be called for a back channel conduit
217            }
218    
219            /**
220             * Send an outbound message, assumed to contain all the name-value
221             * mappings of the corresponding input message (if any).
222             *
223             * @param message the message to be sent.
224             */
225            public void prepare(Message message) throws IOException {
226                message.put(CamelTransportConstants.CAMEL_EXCHANGE, inMessage.get(CamelTransportConstants.CAMEL_EXCHANGE));
227                message.setContent(OutputStream.class, new CamelOutputStream(message));
228            }
229    
230            protected java.util.logging.Logger getLogger() {
231                return JUL_LOG;
232            }
233    
234        }
235    
236        /**
237         * Mark message as a partial message.
238         *
239         * @param partialResponse the partial response message
240         * @param decoupledTarget the decoupled target
241         * @return <tt>true</tt> if partial responses is supported
242         */
243        protected boolean markPartialResponse(Message partialResponse,
244                                              EndpointReferenceType decoupledTarget) {
245            return true;
246        }
247    
248        /**
249         * @return the associated conduit initiator
250         */
251        protected ConduitInitiator getConduitInitiator() {
252            return conduitInitiator;
253        }
254    
255        protected void propagateResponseHeadersToCamel(Message outMessage, Exchange camelExchange) {
256            // copy the camel in message header to the out message
257            camelExchange.getOut().getHeaders().putAll(camelExchange.getIn().getHeaders());
258            CxfHeaderHelper.propagateCxfToCamel(headerFilterStrategy, outMessage,
259                                                camelExchange.getOut().getHeaders(), camelExchange);
260        }
261    
262        /**
263         * Receives a response from CXF and forwards it to the camel route the request came in from
264         */
265        private class CamelOutputStream extends CachedOutputStream {
266            private Message outMessage;
267    
268            public CamelOutputStream(Message m) {
269                super();
270                outMessage = m;
271            }
272    
273            // Prepare the message and get the send out message
274            private void commitOutputMessage() throws IOException {
275                Exchange camelExchange = (Exchange)outMessage.get(CamelTransportConstants.CAMEL_EXCHANGE);
276    
277                propagateResponseHeadersToCamel(outMessage, camelExchange);
278    
279                // check if the outMessage has the exception
280                Exception exception = outMessage.getContent(Exception.class);
281                if (checkException && exception != null) {
282                    camelExchange.setException(exception);
283                }
284    
285                CachedOutputStream outputStream = (CachedOutputStream)outMessage.getContent(OutputStream.class);
286                camelExchange.getOut().setBody(outputStream.getInputStream());
287                LOG.debug("send the response message: {}", outputStream);
288            }
289    
290            @Override
291            protected void doFlush() throws IOException {
292                // Do nothing here
293            }
294    
295            @Override
296            protected void doClose() throws IOException {
297                commitOutputMessage();
298            }
299    
300            @Override
301            protected void onWrite() throws IOException {
302                // Do nothing here
303            }
304        }
305    
306    }