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.impl; 018 019import java.util.Map; 020 021import org.apache.camel.CamelContext; 022import org.apache.camel.Endpoint; 023import org.apache.camel.Exchange; 024import org.apache.camel.FailedToCreateConsumerException; 025import org.apache.camel.IsSingleton; 026import org.apache.camel.PollingConsumer; 027import org.apache.camel.RuntimeCamelException; 028import org.apache.camel.ServicePoolAware; 029import org.apache.camel.spi.ServicePool; 030import org.apache.camel.support.ServiceSupport; 031import org.apache.camel.util.CamelContextHelper; 032import org.apache.camel.util.LRUCache; 033import org.apache.camel.util.ServiceHelper; 034import org.slf4j.Logger; 035import org.slf4j.LoggerFactory; 036 037/** 038 * Cache containing created {@link org.apache.camel.Consumer}. 039 * 040 * @version 041 */ 042public class ConsumerCache extends ServiceSupport { 043 private static final Logger LOG = LoggerFactory.getLogger(ConsumerCache.class); 044 private final CamelContext camelContext; 045 private final ServicePool<Endpoint, PollingConsumer> pool; 046 private final Map<String, PollingConsumer> consumers; 047 private final Object source; 048 049 public ConsumerCache(Object source, CamelContext camelContext) { 050 this(source, camelContext, CamelContextHelper.getMaximumCachePoolSize(camelContext)); 051 } 052 053 public ConsumerCache(Object source, CamelContext camelContext, int cacheSize) { 054 this(source, camelContext, createLRUCache(cacheSize)); 055 } 056 057 public ConsumerCache(Object source, CamelContext camelContext, Map<String, PollingConsumer> cache) { 058 this(source, camelContext, cache, camelContext.getPollingConsumerServicePool()); 059 } 060 061 062 public ConsumerCache(Object source, CamelContext camelContext, Map<String, PollingConsumer> cache, ServicePool<Endpoint, PollingConsumer> pool) { 063 this.camelContext = camelContext; 064 this.consumers = cache; 065 this.source = source; 066 this.pool = pool; 067 } 068 069 /** 070 * Creates the {@link LRUCache} to be used. 071 * <p/> 072 * This implementation returns a {@link LRUCache} instance. 073 074 * @param cacheSize the cache size 075 * @return the cache 076 */ 077 protected static LRUCache<String, PollingConsumer> createLRUCache(int cacheSize) { 078 // Use a regular cache as we want to ensure that the lifecycle of the consumers 079 // being cache is properly handled, such as they are stopped when being evicted 080 // or when this cache is stopped. This is needed as some consumers requires to 081 // be stopped so they can shutdown internal resources that otherwise may cause leaks 082 return new LRUCache<String, PollingConsumer>(cacheSize); 083 } 084 085 /** 086 * Acquires a pooled PollingConsumer which you <b>must</b> release back again after usage using the 087 * {@link #releasePollingConsumer(org.apache.camel.Endpoint, org.apache.camel.PollingConsumer)} method. 088 * 089 * @param endpoint the endpoint 090 * @return the PollingConsumer 091 */ 092 public PollingConsumer acquirePollingConsumer(Endpoint endpoint) { 093 return doGetPollingConsumer(endpoint, true); 094 } 095 096 /** 097 * Releases an acquired producer back after usage. 098 * 099 * @param endpoint the endpoint 100 * @param pollingConsumer the pollingConsumer to release 101 */ 102 public void releasePollingConsumer(Endpoint endpoint, PollingConsumer pollingConsumer) { 103 if (pollingConsumer instanceof ServicePoolAware) { 104 // release back to the pool 105 pool.release(endpoint, pollingConsumer); 106 } else { 107 boolean singleton = false; 108 if (pollingConsumer instanceof IsSingleton) { 109 singleton = ((IsSingleton) pollingConsumer).isSingleton(); 110 } 111 if (!singleton) { 112 try { 113 // stop and shutdown non-singleton producers as we should not leak resources 114 ServiceHelper.stopAndShutdownService(pollingConsumer); 115 } catch (Exception ex) { 116 if (ex instanceof RuntimeCamelException) { 117 throw (RuntimeCamelException)ex; 118 } else { 119 throw new RuntimeCamelException(ex); 120 } 121 } 122 } 123 } 124 } 125 126 public PollingConsumer getConsumer(Endpoint endpoint) { 127 return doGetPollingConsumer(endpoint, true); 128 } 129 130 protected synchronized PollingConsumer doGetPollingConsumer(Endpoint endpoint, boolean pooled) { 131 String key = endpoint.getEndpointUri(); 132 PollingConsumer answer = consumers.get(key); 133 if (pooled && answer == null) { 134 pool.acquire(endpoint); 135 } 136 137 if (answer == null) { 138 try { 139 answer = endpoint.createPollingConsumer(); 140 answer.start(); 141 } catch (Exception e) { 142 throw new FailedToCreateConsumerException(endpoint, e); 143 } 144 if (pooled && answer instanceof ServicePoolAware) { 145 LOG.debug("Adding to producer service pool with key: {} for producer: {}", endpoint, answer); 146 answer = pool.addAndAcquire(endpoint, answer); 147 } else { 148 boolean singleton = false; 149 if (answer instanceof IsSingleton) { 150 singleton = ((IsSingleton) answer).isSingleton(); 151 } 152 if (singleton) { 153 LOG.debug("Adding to consumer cache with key: {} for consumer: {}", endpoint, answer); 154 consumers.put(key, answer); 155 } else { 156 LOG.debug("Consumer for endpoint: {} is not singleton and thus not added to consumer cache", key); 157 } 158 } 159 } 160 return answer; 161 162 } 163 164 public Exchange receive(Endpoint endpoint) { 165 LOG.debug("<<<< {}", endpoint); 166 PollingConsumer consumer = null; 167 try { 168 consumer = acquirePollingConsumer(endpoint); 169 return consumer.receive(); 170 } finally { 171 if (consumer != null) { 172 releasePollingConsumer(endpoint, consumer); 173 } 174 } 175 } 176 177 public Exchange receive(Endpoint endpoint, long timeout) { 178 LOG.debug("<<<< {}", endpoint); 179 PollingConsumer consumer = null; 180 try { 181 consumer = acquirePollingConsumer(endpoint); 182 return consumer.receive(timeout); 183 } finally { 184 if (consumer != null) { 185 releasePollingConsumer(endpoint, consumer); 186 } 187 } 188 } 189 190 public Exchange receiveNoWait(Endpoint endpoint) { 191 LOG.debug("<<<< {}", endpoint); 192 PollingConsumer consumer = null; 193 try { 194 consumer = doGetPollingConsumer(endpoint, true); 195 return consumer.receiveNoWait(); 196 } finally { 197 if (consumer != null) { 198 releasePollingConsumer(endpoint, consumer); 199 } 200 } 201 } 202 203 public CamelContext getCamelContext() { 204 return camelContext; 205 } 206 207 /** 208 * Gets the source which uses this cache 209 * 210 * @return the source 211 */ 212 public Object getSource() { 213 return source; 214 } 215 216 /** 217 * Returns the current size of the cache 218 * 219 * @return the current size 220 */ 221 public int size() { 222 int size = consumers.size(); 223 LOG.trace("size = {}", size); 224 return size; 225 } 226 227 /** 228 * Gets the maximum cache size (capacity). 229 * <p/> 230 * Will return <tt>-1</tt> if it cannot determine this if a custom cache was used. 231 * 232 * @return the capacity 233 */ 234 public int getCapacity() { 235 int capacity = -1; 236 if (consumers instanceof LRUCache) { 237 LRUCache<String, PollingConsumer> cache = (LRUCache<String, PollingConsumer>)consumers; 238 capacity = cache.getMaxCacheSize(); 239 } 240 return capacity; 241 } 242 243 /** 244 * Gets the cache hits statistic 245 * <p/> 246 * Will return <tt>-1</tt> if it cannot determine this if a custom cache was used. 247 * 248 * @return the hits 249 */ 250 public long getHits() { 251 long hits = -1; 252 if (consumers instanceof LRUCache) { 253 LRUCache<String, PollingConsumer> cache = (LRUCache<String, PollingConsumer>)consumers; 254 hits = cache.getHits(); 255 } 256 return hits; 257 } 258 259 /** 260 * Gets the cache misses statistic 261 * <p/> 262 * Will return <tt>-1</tt> if it cannot determine this if a custom cache was used. 263 * 264 * @return the misses 265 */ 266 public long getMisses() { 267 long misses = -1; 268 if (consumers instanceof LRUCache) { 269 LRUCache<String, PollingConsumer> cache = (LRUCache<String, PollingConsumer>)consumers; 270 misses = cache.getMisses(); 271 } 272 return misses; 273 } 274 275 /** 276 * Gets the cache evicted statistic 277 * <p/> 278 * Will return <tt>-1</tt> if it cannot determine this if a custom cache was used. 279 * 280 * @return the evicted 281 */ 282 public long getEvicted() { 283 long evicted = -1; 284 if (consumers instanceof LRUCache) { 285 LRUCache<String, PollingConsumer> cache = (LRUCache<String, PollingConsumer>)consumers; 286 evicted = cache.getEvicted(); 287 } 288 return evicted; 289 } 290 291 /** 292 * Resets the cache statistics 293 */ 294 public void resetCacheStatistics() { 295 if (consumers instanceof LRUCache) { 296 LRUCache<String, PollingConsumer> cache = (LRUCache<String, PollingConsumer>)consumers; 297 cache.resetStatistics(); 298 } 299 } 300 301 /** 302 * Purges this cache 303 */ 304 public synchronized void purge() { 305 consumers.clear(); 306 } 307 308 @Override 309 public String toString() { 310 return "ConsumerCache for source: " + source + ", capacity: " + getCapacity(); 311 } 312 313 protected void doStart() throws Exception { 314 ServiceHelper.startServices(consumers.values()); 315 } 316 317 protected void doStop() throws Exception { 318 // when stopping we intend to shutdown 319 ServiceHelper.stopAndShutdownServices(consumers.values()); 320 consumers.clear(); 321 // we need to stop the pool service here 322 ServiceHelper.stopService(pool); 323 } 324 325}