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.impl; 018 019 import java.util.Map; 020 021 import org.apache.camel.Endpoint; 022 import org.apache.camel.Exchange; 023 import org.apache.camel.ExchangePattern; 024 import org.apache.camel.FailedToCreateProducerException; 025 import org.apache.camel.Processor; 026 import org.apache.camel.Producer; 027 import org.apache.camel.ProducerCallback; 028 import org.apache.camel.ServicePoolAware; 029 import org.apache.camel.spi.ServicePool; 030 import org.apache.camel.util.LRUCache; 031 import org.apache.camel.util.ServiceHelper; 032 import org.apache.commons.logging.Log; 033 import org.apache.commons.logging.LogFactory; 034 import static org.apache.camel.util.ObjectHelper.wrapRuntimeCamelException; 035 036 /** 037 * Cache containing created {@link Producer}. 038 * 039 * @version $Revision: 792408 $ 040 */ 041 public class ProducerCache extends ServiceSupport { 042 private static final transient Log LOG = LogFactory.getLog(ProducerCache.class); 043 044 private final Map<String, Producer> producers; 045 private final ServicePool<Endpoint, Producer> pool; 046 047 // TODO: Have easy configuration of pooling in Camel 048 049 public ProducerCache(ServicePool<Endpoint, Producer> producerServicePool) { 050 this.pool = producerServicePool; 051 this.producers = new LRUCache<String, Producer>(1000); 052 } 053 054 public ProducerCache(ServicePool<Endpoint, Producer> producerServicePool, Map<String, Producer> cache) { 055 this.pool = producerServicePool; 056 this.producers = cache; 057 } 058 059 public Producer getProducer(Endpoint endpoint) { 060 // As the producer is returned outside this method we do not want to return pooled producers 061 // so we pass in false to the method. if we returned pooled producers then the user had 062 // to remember to return it back in the pool. 063 // See method doInProducer that is safe template pattern where we handle the lifecycle and 064 // thus safely can use pooled producers there 065 return doGetProducer(endpoint, false); 066 } 067 068 /** 069 * Sends the exchange to the given endpoint 070 * 071 * @param endpoint the endpoint to send the exchange to 072 * @param exchange the exchange to send 073 */ 074 public void send(Endpoint endpoint, Exchange exchange) { 075 try { 076 sendExchange(endpoint, null, null, exchange); 077 } catch (Exception e) { 078 throw wrapRuntimeCamelException(e); 079 } 080 } 081 082 /** 083 * Sends an exchange to an endpoint using a supplied 084 * {@link Processor} to populate the exchange 085 * 086 * @param endpoint the endpoint to send the exchange to 087 * @param processor the transformer used to populate the new exchange 088 * @return the exchange 089 */ 090 public Exchange send(Endpoint endpoint, Processor processor) { 091 try { 092 return sendExchange(endpoint, null, processor, null); 093 } catch (Exception e) { 094 throw wrapRuntimeCamelException(e); 095 } 096 } 097 098 /** 099 * Sends an exchange to an endpoint using a supplied 100 * {@link Processor} to populate the exchange 101 * 102 * @param endpoint the endpoint to send the exchange to 103 * @param pattern the message {@link ExchangePattern} such as 104 * {@link ExchangePattern#InOnly} or {@link ExchangePattern#InOut} 105 * @param processor the transformer used to populate the new exchange 106 * @return the exchange 107 */ 108 public Exchange send(Endpoint endpoint, ExchangePattern pattern, Processor processor) { 109 try { 110 return sendExchange(endpoint, pattern, processor, null); 111 } catch (Exception e) { 112 throw wrapRuntimeCamelException(e); 113 } 114 } 115 116 117 /** 118 * Sends an exchange to an endpoint using a supplied callback 119 * 120 * @param endpoint the endpoint to send the exchange to 121 * @param exchange the exchange, can be <tt>null</tt> if so then create a new exchange from the producer 122 * @param pattern the exchange pattern, can be <tt>null</tt> 123 * @param callback the callback 124 * @return the response from the callback 125 * @throws Exception if an internal processing error has occurred. 126 */ 127 public <T> T doInProducer(Endpoint endpoint, Exchange exchange, ExchangePattern pattern, ProducerCallback<T> callback) throws Exception { 128 // get the producer and we do not mind if its pooled as we can handle returning it back to the pool 129 Producer producer = doGetProducer(endpoint, true); 130 131 if (producer == null) { 132 if (isStopped()) { 133 LOG.warn("Ignoring exchange sent after processor is stopped: " + exchange); 134 return null; 135 } else { 136 throw new IllegalStateException("No producer, this processor has not been started: " + this); 137 } 138 } 139 140 try { 141 // invoke the callback 142 return callback.doInProducer(producer, exchange, pattern); 143 } finally { 144 if (producer instanceof ServicePoolAware) { 145 // release back to the pool 146 pool.release(endpoint, producer); 147 } else if (!producer.isSingleton()) { 148 // stop non singleton producers as we should not leak resources 149 producer.stop(); 150 } 151 } 152 } 153 154 protected Exchange sendExchange(final Endpoint endpoint, ExchangePattern pattern, 155 final Processor processor, Exchange exchange) throws Exception { 156 return doInProducer(endpoint, exchange, pattern, new ProducerCallback<Exchange>() { 157 public Exchange doInProducer(Producer producer, Exchange exchange, ExchangePattern pattern) throws Exception { 158 if (exchange == null) { 159 exchange = pattern != null ? producer.createExchange(pattern) : producer.createExchange(); 160 } 161 162 if (processor != null) { 163 // lets populate using the processor callback 164 processor.process(exchange); 165 } 166 167 // now lets dispatch 168 if (LOG.isDebugEnabled()) { 169 LOG.debug(">>>> " + endpoint + " " + exchange); 170 } 171 producer.process(exchange); 172 return exchange; 173 } 174 }); 175 } 176 177 protected synchronized Producer doGetProducer(Endpoint endpoint, boolean pooled) { 178 String key = endpoint.getEndpointUri(); 179 Producer answer = producers.get(key); 180 if (pooled && answer == null) { 181 // try acquire from connection pool 182 answer = pool.acquire(endpoint); 183 } 184 185 if (answer == null) { 186 // create a new producer 187 try { 188 answer = endpoint.createProducer(); 189 answer.start(); 190 } catch (Exception e) { 191 throw new FailedToCreateProducerException(endpoint, e); 192 } 193 194 // add producer to cache or pool if applicable 195 if (pooled && answer instanceof ServicePoolAware) { 196 if (LOG.isDebugEnabled()) { 197 LOG.debug("Adding to producer service pool with key: " + endpoint + " for producer: " + answer); 198 } 199 answer = pool.addAndAcquire(endpoint, answer); 200 } else if (answer.isSingleton()) { 201 if (LOG.isDebugEnabled()) { 202 LOG.debug("Adding to producer cache with key: " + endpoint + " for producer: " + answer); 203 } 204 producers.put(key, answer); 205 } 206 } 207 208 return answer; 209 } 210 211 protected void doStop() throws Exception { 212 // the producers will be stopped from where they are acquired 213 producers.clear(); 214 ServiceHelper.stopServices(pool); 215 } 216 217 protected void doStart() throws Exception { 218 ServiceHelper.startServices(pool); 219 } 220 221 /** 222 * Returns the current size of the producer cache 223 * 224 * @return the current size 225 */ 226 int size() { 227 return producers.size(); 228 } 229 230 }