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 */ 017package org.apache.camel.processor; 018 019import java.net.URISyntaxException; 020import java.util.HashMap; 021 022import org.apache.camel.AsyncCallback; 023import org.apache.camel.AsyncProcessor; 024import org.apache.camel.AsyncProducerCallback; 025import org.apache.camel.CamelContext; 026import org.apache.camel.Endpoint; 027import org.apache.camel.EndpointAware; 028import org.apache.camel.Exchange; 029import org.apache.camel.ExchangePattern; 030import org.apache.camel.Producer; 031import org.apache.camel.ServicePoolAware; 032import org.apache.camel.Traceable; 033import org.apache.camel.impl.InterceptSendToEndpoint; 034import org.apache.camel.impl.ProducerCache; 035import org.apache.camel.support.ServiceSupport; 036import org.apache.camel.util.AsyncProcessorConverterHelper; 037import org.apache.camel.util.AsyncProcessorHelper; 038import org.apache.camel.util.EndpointHelper; 039import org.apache.camel.util.EventHelper; 040import org.apache.camel.util.ObjectHelper; 041import org.apache.camel.util.ServiceHelper; 042import org.apache.camel.util.StopWatch; 043import org.apache.camel.util.URISupport; 044import org.slf4j.Logger; 045import org.slf4j.LoggerFactory; 046 047/** 048 * Processor for forwarding exchanges to an endpoint destination. 049 * 050 * @version 051 */ 052public class SendProcessor extends ServiceSupport implements AsyncProcessor, Traceable, EndpointAware { 053 protected static final Logger LOG = LoggerFactory.getLogger(SendProcessor.class); 054 protected final CamelContext camelContext; 055 protected final ExchangePattern pattern; 056 protected ProducerCache producerCache; 057 protected AsyncProcessor producer; 058 protected Endpoint destination; 059 protected ExchangePattern destinationExchangePattern; 060 061 public SendProcessor(Endpoint destination) { 062 this(destination, null); 063 } 064 065 public SendProcessor(Endpoint destination, ExchangePattern pattern) { 066 ObjectHelper.notNull(destination, "destination"); 067 this.destination = destination; 068 this.camelContext = destination.getCamelContext(); 069 this.pattern = pattern; 070 try { 071 this.destinationExchangePattern = null; 072 this.destinationExchangePattern = EndpointHelper.resolveExchangePatternFromUrl(destination.getEndpointUri()); 073 } catch (URISyntaxException e) { 074 throw ObjectHelper.wrapRuntimeCamelException(e); 075 } 076 ObjectHelper.notNull(this.camelContext, "camelContext"); 077 } 078 079 @Override 080 public String toString() { 081 return "sendTo(" + destination + (pattern != null ? " " + pattern : "") + ")"; 082 } 083 084 /** 085 * @deprecated not longer supported. 086 */ 087 @Deprecated 088 public void setDestination(Endpoint destination) { 089 } 090 091 public String getTraceLabel() { 092 return URISupport.sanitizeUri(destination.getEndpointUri()); 093 } 094 095 @Override 096 public Endpoint getEndpoint() { 097 return destination; 098 } 099 100 public void process(final Exchange exchange) throws Exception { 101 AsyncProcessorHelper.process(this, exchange); 102 } 103 104 public boolean process(Exchange exchange, final AsyncCallback callback) { 105 if (!isStarted()) { 106 exchange.setException(new IllegalStateException("SendProcessor has not been started: " + this)); 107 callback.done(true); 108 return true; 109 } 110 111 112 // we should preserve existing MEP so remember old MEP 113 // if you want to permanently to change the MEP then use .setExchangePattern in the DSL 114 final ExchangePattern existingPattern = exchange.getPattern(); 115 116 // if we have a producer then use that as its optimized 117 if (producer != null) { 118 119 // record timing for sending the exchange using the producer 120 final StopWatch watch = new StopWatch(); 121 122 final Exchange target = configureExchange(exchange, pattern); 123 124 EventHelper.notifyExchangeSending(exchange.getContext(), target, destination); 125 LOG.debug(">>>> {} {}", destination, exchange); 126 127 boolean sync = true; 128 try { 129 sync = producer.process(exchange, new AsyncCallback() { 130 @Override 131 public void done(boolean doneSync) { 132 try { 133 // restore previous MEP 134 target.setPattern(existingPattern); 135 // emit event that the exchange was sent to the endpoint 136 long timeTaken = watch.stop(); 137 EventHelper.notifyExchangeSent(target.getContext(), target, destination, timeTaken); 138 } finally { 139 callback.done(doneSync); 140 } 141 } 142 }); 143 } catch (Throwable throwable) { 144 exchange.setException(throwable); 145 callback.done(sync); 146 } 147 148 return sync; 149 } 150 151 // send the exchange to the destination using the producer cache for the non optimized producers 152 return producerCache.doInAsyncProducer(destination, exchange, pattern, callback, new AsyncProducerCallback() { 153 public boolean doInAsyncProducer(Producer producer, AsyncProcessor asyncProducer, final Exchange exchange, 154 ExchangePattern pattern, final AsyncCallback callback) { 155 final Exchange target = configureExchange(exchange, pattern); 156 LOG.debug(">>>> {} {}", destination, exchange); 157 return asyncProducer.process(target, new AsyncCallback() { 158 public void done(boolean doneSync) { 159 // restore previous MEP 160 target.setPattern(existingPattern); 161 // signal we are done 162 callback.done(doneSync); 163 } 164 }); 165 } 166 }); 167 } 168 169 public Endpoint getDestination() { 170 return destination; 171 } 172 173 public ExchangePattern getPattern() { 174 return pattern; 175 } 176 177 protected Exchange configureExchange(Exchange exchange, ExchangePattern pattern) { 178 // destination exchange pattern overrides pattern 179 if (destinationExchangePattern != null) { 180 exchange.setPattern(destinationExchangePattern); 181 } else if (pattern != null) { 182 exchange.setPattern(pattern); 183 } 184 // set property which endpoint we send to 185 exchange.setProperty(Exchange.TO_ENDPOINT, destination.getEndpointUri()); 186 return exchange; 187 } 188 189 protected void doStart() throws Exception { 190 if (producerCache == null) { 191 // use a single producer cache as we need to only hold reference for one destination 192 // and use a regular HashMap as we do not want a soft reference store that may get re-claimed when low on memory 193 // as we want to ensure the producer is kept around, to ensure its lifecycle is fully managed, 194 // eg stopping the producer when we stop etc. 195 producerCache = new ProducerCache(this, camelContext, new HashMap<String, Producer>(1)); 196 // do not add as service as we do not want to manage the producer cache 197 } 198 ServiceHelper.startService(producerCache); 199 200 // the destination could since have been intercepted by a interceptSendToEndpoint so we got to 201 // lookup this before we can use the destination 202 Endpoint lookup = camelContext.hasEndpoint(destination.getEndpointKey()); 203 if (lookup instanceof InterceptSendToEndpoint) { 204 if (LOG.isDebugEnabled()) { 205 LOG.debug("Intercepted sending to {} -> {}", 206 URISupport.sanitizeUri(destination.getEndpointUri()), URISupport.sanitizeUri(lookup.getEndpointUri())); 207 } 208 destination = lookup; 209 } 210 // warm up the producer by starting it so we can fail fast if there was a problem 211 // however must start endpoint first 212 ServiceHelper.startService(destination); 213 214 // this SendProcessor is used a lot in Camel (eg every .to in the route DSL) and therefore we 215 // want to optimize for regular producers, by using the producer directly instead of the ProducerCache 216 // Only for pooled and non singleton producers we have to use the ProducerCache as it supports these 217 // kind of producer better (though these kind of producer should be rare) 218 219 Producer producer = producerCache.acquireProducer(destination); 220 if (producer instanceof ServicePoolAware || !producer.isSingleton()) { 221 // no we cannot optimize it - so release the producer back to the producer cache 222 // and use the producer cache for sending 223 producerCache.releaseProducer(destination, producer); 224 } else { 225 // yes we can optimize and use the producer directly for sending 226 this.producer = AsyncProcessorConverterHelper.convert(producer); 227 } 228 } 229 230 protected void doStop() throws Exception { 231 ServiceHelper.stopServices(producerCache, producer); 232 } 233 234 protected void doShutdown() throws Exception { 235 ServiceHelper.stopAndShutdownServices(producerCache, producer); 236 } 237}