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