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 }