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.CamelContext;
022    import org.apache.camel.Endpoint;
023    import org.apache.camel.Exchange;
024    import org.apache.camel.FailedToCreateConsumerException;
025    import org.apache.camel.IsSingleton;
026    import org.apache.camel.PollingConsumer;
027    import org.apache.camel.support.ServiceSupport;
028    import org.apache.camel.util.CamelContextHelper;
029    import org.apache.camel.util.LRUCache;
030    import org.apache.camel.util.LRUSoftCache;
031    import org.apache.camel.util.ServiceHelper;
032    import org.slf4j.Logger;
033    import org.slf4j.LoggerFactory;
034    
035    /**
036     * Cache containing created {@link org.apache.camel.Consumer}.
037     *
038     * @version 
039     */
040    public class ConsumerCache extends ServiceSupport {
041        private static final transient Logger LOG = LoggerFactory.getLogger(ConsumerCache.class);
042        private final CamelContext camelContext;
043        private final Map<String, PollingConsumer> consumers;
044        private final Object source;
045    
046        public ConsumerCache(Object source, CamelContext camelContext) {
047            this(source, camelContext, CamelContextHelper.getMaximumCachePoolSize(camelContext));
048        }
049    
050        public ConsumerCache(Object source, CamelContext camelContext, int cacheSize) {
051            this(source, camelContext, createLRUCache(cacheSize));
052        }
053    
054        public ConsumerCache(Object source, CamelContext camelContext, Map<String, PollingConsumer> cache) {
055            this.camelContext = camelContext;
056            this.consumers = cache;
057            this.source = source;
058        }
059    
060        /**
061         * Creates the {@link LRUCache} to be used.
062         * <p/>
063         * This implementation returns a {@link org.apache.camel.util.LRUSoftCache} instance.
064    
065         * @param cacheSize the cache size
066         * @return the cache
067         */
068        protected static LRUCache<String, PollingConsumer> createLRUCache(int cacheSize) {
069            // We use a soft reference cache to allow the JVM to re-claim memory if it runs low on memory.
070            return new LRUSoftCache<String, PollingConsumer>(cacheSize);
071        }
072    
073        public synchronized PollingConsumer getConsumer(Endpoint endpoint) {
074            String key = endpoint.getEndpointUri();
075            PollingConsumer answer = consumers.get(key);
076            if (answer == null) {
077                try {
078                    answer = endpoint.createPollingConsumer();
079                    answer.start();
080                } catch (Exception e) {
081                    throw new FailedToCreateConsumerException(endpoint, e);
082                }
083    
084                boolean singleton = true;
085                if (answer instanceof IsSingleton) {
086                    singleton = ((IsSingleton) answer).isSingleton();
087                }
088    
089                if (singleton) {
090                    LOG.debug("Adding to consumer cache with key: {} for consumer: {}", endpoint, answer);
091                    consumers.put(key, answer);
092                } else {
093                    LOG.debug("Consumer for endpoint: {} is not singleton and thus not added to consumer cache", key);
094                }
095            }
096            return answer;
097        }
098    
099        public Exchange receive(Endpoint endpoint) {
100            LOG.debug("<<<< {}", endpoint);
101    
102            PollingConsumer consumer = getConsumer(endpoint);
103            return consumer.receive();
104        }
105    
106        public Exchange receive(Endpoint endpoint, long timeout) {
107            LOG.debug("<<<< {}", endpoint);
108    
109            PollingConsumer consumer = getConsumer(endpoint);
110            return consumer.receive(timeout);
111        }
112    
113        public Exchange receiveNoWait(Endpoint endpoint) {
114            LOG.debug("<<<< {}", endpoint);
115    
116            PollingConsumer consumer = getConsumer(endpoint);
117            return consumer.receiveNoWait();
118        }
119        
120        public CamelContext getCamelContext() {
121            return camelContext;
122        }
123    
124        /**
125         * Gets the source which uses this cache
126         *
127         * @return the source
128         */
129        public Object getSource() {
130            return source;
131        }
132    
133        /**
134         * Returns the current size of the cache
135         *
136         * @return the current size
137         */
138        public int size() {
139            int size = consumers.size();
140            LOG.trace("size = {}", size);
141            return size;
142        }
143    
144        /**
145         * Gets the maximum cache size (capacity).
146         * <p/>
147         * Will return <tt>-1</tt> if it cannot determine this if a custom cache was used.
148         *
149         * @return the capacity
150         */
151        public int getCapacity() {
152            int capacity = -1;
153            if (consumers instanceof LRUCache) {
154                LRUCache<String, PollingConsumer> cache = (LRUCache<String, PollingConsumer>)consumers;
155                capacity = cache.getMaxCacheSize();
156            }
157            return capacity;
158        }
159    
160        /**
161         * Gets the cache hits statistic
162         * <p/>
163         * Will return <tt>-1</tt> if it cannot determine this if a custom cache was used.
164         *
165         * @return the hits
166         */
167        public long getHits() {
168            long hits = -1;
169            if (consumers instanceof LRUCache) {
170                LRUCache<String, PollingConsumer> cache = (LRUCache<String, PollingConsumer>)consumers;
171                hits = cache.getHits();
172            }
173            return hits;
174        }
175    
176        /**
177         * Gets the cache misses statistic
178         * <p/>
179         * Will return <tt>-1</tt> if it cannot determine this if a custom cache was used.
180         *
181         * @return the misses
182         */
183        public long getMisses() {
184            long misses = -1;
185            if (consumers instanceof LRUCache) {
186                LRUCache<String, PollingConsumer> cache = (LRUCache<String, PollingConsumer>)consumers;
187                misses = cache.getMisses();
188            }
189            return misses;
190        }
191    
192        /**
193         * Resets the cache statistics
194         */
195        public void resetCacheStatistics() {
196            if (consumers instanceof LRUCache) {
197                LRUCache<String, PollingConsumer> cache = (LRUCache<String, PollingConsumer>)consumers;
198                cache.resetStatistics();
199            }
200        }
201    
202        /**
203         * Purges this cache
204         */
205        public synchronized void purge() {
206            consumers.clear();
207        }
208    
209        @Override
210        public String toString() {
211            return "ConsumerCache for source: " + source + ", capacity: " + getCapacity();
212        }
213    
214        protected void doStart() throws Exception {
215            ServiceHelper.startServices(consumers.values());
216        }
217    
218        protected void doStop() throws Exception {
219            // when stopping we intend to shutdown
220            ServiceHelper.stopAndShutdownServices(consumers.values());
221            consumers.clear();
222        }
223    
224    }