You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2011/05/02 15:59:46 UTC

svn commit: r1098574 - in /camel/trunk/camel-core/src: main/java/org/apache/camel/ main/java/org/apache/camel/component/bean/ main/java/org/apache/camel/component/properties/ main/java/org/apache/camel/impl/ main/java/org/apache/camel/impl/converter/ m...

Author: davsclaus
Date: Mon May  2 13:59:45 2011
New Revision: 1098574

URL: http://svn.apache.org/viewvc?rev=1098574&view=rev
Log:
CAMEL-3922: Added LRUSoftCache for soft reference cache. This is more ideal cache for the JVM so it can GC values in case its low on memory.

Added:
    camel/trunk/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedConsumerCache.java
      - copied, changed from r1098295, camel/trunk/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedProducerCache.java
    camel/trunk/camel-core/src/main/java/org/apache/camel/util/LRUSoftCache.java
      - copied, changed from r1098295, camel/trunk/camel-core/src/main/java/org/apache/camel/util/LRUCache.java
    camel/trunk/camel-core/src/test/java/org/apache/camel/management/ManagedConsumerCacheTest.java
      - copied, changed from r1098295, camel/trunk/camel-core/src/test/java/org/apache/camel/management/ManagedProducerCacheTest.java
    camel/trunk/camel-core/src/test/java/org/apache/camel/util/LRUSoftCacheTest.java
Modified:
    camel/trunk/camel-core/src/main/java/org/apache/camel/CamelContext.java
    camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/MethodInfoCache.java
    camel/trunk/camel-core/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java
    camel/trunk/camel-core/src/main/java/org/apache/camel/impl/ConsumerCache.java
    camel/trunk/camel-core/src/main/java/org/apache/camel/impl/DefaultConsumerTemplate.java
    camel/trunk/camel-core/src/main/java/org/apache/camel/impl/EndpointRegistry.java
    camel/trunk/camel-core/src/main/java/org/apache/camel/impl/ProducerCache.java
    camel/trunk/camel-core/src/main/java/org/apache/camel/impl/converter/PropertyEditorTypeConverter.java
    camel/trunk/camel-core/src/main/java/org/apache/camel/management/DefaultManagementLifecycleStrategy.java
    camel/trunk/camel-core/src/test/java/org/apache/camel/impl/DefaultConsumerCacheTest.java

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/CamelContext.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/CamelContext.java?rev=1098574&r1=1098573&r2=1098574&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/CamelContext.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/CamelContext.java Mon May  2 13:59:45 2011
@@ -626,8 +626,6 @@ public interface CamelContext extends Su
     /**
      * Creates a new {@link ProducerTemplate} which is <b>started</b> and therefore ready to use right away.
      * <p/>
-     * You <b>must</b> start the template before its being used.
-     * <p/>
      * See this FAQ before use: <a href="http://camel.apache.org/why-does-camel-use-too-many-threads-with-producertemplate.html">
      * Why does Camel use too many threads with ProducerTemplate?</a>
      *

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/MethodInfoCache.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/MethodInfoCache.java?rev=1098574&r1=1098573&r2=1098574&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/MethodInfoCache.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/MethodInfoCache.java Mon May  2 13:59:45 2011
@@ -21,11 +21,11 @@ import java.util.Map;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.util.CastUtils;
-import org.apache.camel.util.LRUCache;
+import org.apache.camel.util.LRUSoftCache;
 
 /**
  * Represents a cache of {@link MethodInfo} objects to avoid the expense of introspection for each
- * invocation of a method via a proxy,
+ * invocation of a method via a proxy.
  *
  * @version 
  */
@@ -77,7 +77,8 @@ public class MethodInfoCache {
     }
 
     protected static Map<Object, Object> createLruCache(int size) {
-        return new LRUCache<Object, Object>(size);
+        // use a soft cache
+        return new LRUSoftCache<Object, Object>(size);
     }
 
     private static Map<Class<?>, BeanInfo> createClassCache(int size) {

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java?rev=1098574&r1=1098573&r2=1098574&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java Mon May  2 13:59:45 2011
@@ -25,6 +25,7 @@ import org.apache.camel.Endpoint;
 import org.apache.camel.impl.DefaultComponent;
 import org.apache.camel.util.LRUCache;
 import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.ServiceHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -144,8 +145,14 @@ public class PropertiesComponent extends
     }
 
     @Override
+    protected void doStart() throws Exception {
+        ServiceHelper.startService(cacheMap);
+        super.doStart();
+    }
+
+    @Override
     protected void doStop() throws Exception {
-        cacheMap.clear();
+        ServiceHelper.stopService(cacheMap);
         super.doStop();
     }
 

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/impl/ConsumerCache.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/impl/ConsumerCache.java?rev=1098574&r1=1098573&r2=1098574&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/impl/ConsumerCache.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/impl/ConsumerCache.java Mon May  2 13:59:45 2011
@@ -26,6 +26,7 @@ import org.apache.camel.IsSingleton;
 import org.apache.camel.PollingConsumer;
 import org.apache.camel.util.CamelContextHelper;
 import org.apache.camel.util.LRUCache;
+import org.apache.camel.util.LRUSoftCache;
 import org.apache.camel.util.ServiceHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -39,18 +40,33 @@ public class ConsumerCache extends Servi
     private static final transient Logger LOG = LoggerFactory.getLogger(ConsumerCache.class);
     private final CamelContext camelContext;
     private final Map<String, PollingConsumer> consumers;
+    private final Object source;
 
-    public ConsumerCache(CamelContext camelContext) {
-        this(camelContext, CamelContextHelper.getMaximumCachePoolSize(camelContext));
+    public ConsumerCache(Object source, CamelContext camelContext) {
+        this(source, camelContext, CamelContextHelper.getMaximumCachePoolSize(camelContext));
     }
 
-    public ConsumerCache(CamelContext camelContext, int maximumCacheSize) {
-        this(camelContext, new LRUCache<String, PollingConsumer>(maximumCacheSize));
+    public ConsumerCache(Object source, CamelContext camelContext, int cacheSize) {
+        this(source, camelContext, createLRUCache(cacheSize));
     }
 
-    public ConsumerCache(CamelContext camelContext, Map<String, PollingConsumer> cache) {
+    public ConsumerCache(Object source, CamelContext camelContext, Map<String, PollingConsumer> cache) {
         this.camelContext = camelContext;
         this.consumers = cache;
+        this.source = source;
+    }
+
+    /**
+     * Creates the {@link LRUCache} to be used.
+     * <p/>
+     * This implementation returns a {@link org.apache.camel.util.LRUSoftCache} instance.
+
+     * @param cacheSize the cache size
+     * @return the cache
+     */
+    protected static LRUCache<String, PollingConsumer> createLRUCache(int cacheSize) {
+        // We use a soft reference cache to allow the JVM to re-claim memory if it runs low on memory.
+        return new LRUSoftCache<String, PollingConsumer>(cacheSize);
     }
 
     public synchronized PollingConsumer getConsumer(Endpoint endpoint) {
@@ -104,22 +120,56 @@ public class ConsumerCache extends Servi
         return camelContext;
     }
 
-    protected void doStart() throws Exception {
-        ServiceHelper.startServices(consumers);
+    /**
+     * Gets the source which uses this cache
+     *
+     * @return the source
+     */
+    public Object getSource() {
+        return source;
     }
 
-    protected void doStop() throws Exception {
-        ServiceHelper.stopServices(consumers);
-        consumers.clear();
+    /**
+     * Returns the current size of the cache
+     *
+     * @return the current size
+     */
+    public int size() {
+        int size = consumers.size();
+        LOG.trace("size = {}", size);
+        return size;
     }
 
     /**
-     * Returns the current size of the consumer cache
+     * Gets the maximum cache size (capacity).
+     * <p/>
+     * Will return <tt>-1</tt> if it cannot determine this if a custom cache was used.
      *
-     * @return the current size
+     * @return the capacity
+     */
+    public int getCapacity() {
+        int capacity = -1;
+        if (consumers instanceof LRUCache) {
+            LRUCache cache = (LRUCache) consumers;
+            capacity = cache.getMaxCacheSize();
+        }
+        return capacity;
+    }
+
+    /**
+     * Purges this cache
      */
-    int size() {
-        return consumers.size();
+    public synchronized void purge() {
+        consumers.clear();
+    }
+
+    protected void doStart() throws Exception {
+        ServiceHelper.startServices(consumers);
+    }
+
+    protected void doStop() throws Exception {
+        ServiceHelper.stopServices(consumers);
+        consumers.clear();
     }
 
 }

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/impl/DefaultConsumerTemplate.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/impl/DefaultConsumerTemplate.java?rev=1098574&r1=1098573&r2=1098574&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/impl/DefaultConsumerTemplate.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/impl/DefaultConsumerTemplate.java Mon May  2 13:59:45 2011
@@ -254,9 +254,9 @@ public class DefaultConsumerTemplate ext
     protected void doStart() throws Exception {
         if (consumerCache == null) {
             if (maximumCacheSize > 0) {
-                consumerCache = new ConsumerCache(context, maximumCacheSize);
+                consumerCache = new ConsumerCache(this, context, maximumCacheSize);
             } else {
-                consumerCache = new ConsumerCache(context);
+                consumerCache = new ConsumerCache(this, context);
             }
         }
         ServiceHelper.startService(consumerCache);

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/impl/EndpointRegistry.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/impl/EndpointRegistry.java?rev=1098574&r1=1098573&r2=1098574&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/impl/EndpointRegistry.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/impl/EndpointRegistry.java Mon May  2 13:59:45 2011
@@ -21,14 +21,16 @@ import java.util.Map;
 import org.apache.camel.CamelContext;
 import org.apache.camel.Endpoint;
 import org.apache.camel.util.CamelContextHelper;
-import org.apache.camel.util.LRUCache;
+import org.apache.camel.util.LRUSoftCache;
 
 /**
- * Endpoint registry which is a based on a {@link org.apache.camel.util.LRUCache}.
+ * Endpoint registry which is a based on a {@link org.apache.camel.util.LRUSoftCache}.
+ * <p/>
+ * We use a soft reference cache to allow the JVM to re-claim memory if it runs low on memory.
  *
  * @version 
  */
-public class EndpointRegistry extends LRUCache<EndpointKey, Endpoint> {
+public class EndpointRegistry extends LRUSoftCache<EndpointKey, Endpoint> {
 
     private final CamelContext context;
 

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/impl/ProducerCache.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/impl/ProducerCache.java?rev=1098574&r1=1098573&r2=1098574&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/impl/ProducerCache.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/impl/ProducerCache.java Mon May  2 13:59:45 2011
@@ -36,6 +36,7 @@ import org.apache.camel.spi.ServicePool;
 import org.apache.camel.util.CamelContextHelper;
 import org.apache.camel.util.EventHelper;
 import org.apache.camel.util.LRUCache;
+import org.apache.camel.util.LRUSoftCache;
 import org.apache.camel.util.ServiceHelper;
 import org.apache.camel.util.StopWatch;
 import org.slf4j.Logger;
@@ -59,7 +60,7 @@ public class ProducerCache extends Servi
     }
 
     public ProducerCache(Object source, CamelContext camelContext, int cacheSize) {
-        this(source, camelContext, camelContext.getProducerServicePool(), new LRUCache<String, Producer>(cacheSize));
+        this(source, camelContext, camelContext.getProducerServicePool(), createLRUCache(cacheSize));
     }
 
     public ProducerCache(Object source, CamelContext camelContext, ServicePool<Endpoint, Producer> producerServicePool, Map<String, Producer> cache) {
@@ -68,7 +69,20 @@ public class ProducerCache extends Servi
         this.pool = producerServicePool;
         this.producers = cache;
     }
-    
+
+    /**
+     * Creates the {@link LRUCache} to be used.
+     * <p/>
+     * This implementation returns a {@link LRUSoftCache} instance.
+
+     * @param cacheSize the cache size
+     * @return the cache
+     */
+    protected static LRUCache<String, Producer> createLRUCache(int cacheSize) {
+        // We use a soft reference cache to allow the JVM to re-claim memory if it runs low on memory.
+        return new LRUSoftCache<String, Producer>(cacheSize);
+    }
+
     public CamelContext getCamelContext() {
         return camelContext;
     }

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/impl/converter/PropertyEditorTypeConverter.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/impl/converter/PropertyEditorTypeConverter.java?rev=1098574&r1=1098573&r2=1098574&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/impl/converter/PropertyEditorTypeConverter.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/impl/converter/PropertyEditorTypeConverter.java Mon May  2 13:59:45 2011
@@ -24,8 +24,9 @@ import java.util.Map;
 import org.apache.camel.Exchange;
 import org.apache.camel.Service;
 import org.apache.camel.TypeConverter;
-import org.apache.camel.util.LRUCache;
+import org.apache.camel.util.LRUSoftCache;
 import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.ServiceHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -38,9 +39,9 @@ import org.slf4j.LoggerFactory;
 public class PropertyEditorTypeConverter implements TypeConverter, Service {
 
     private static final Logger LOG = LoggerFactory.getLogger(PropertyEditorTypeConverter.class);
-    // use a bound cache to avoid using too much memory in case a lot of different classes
+    // use a soft bound cache to avoid using too much memory in case a lot of different classes
     // is being converted to string
-    private final Map<Class, Class> misses = new LRUCache<Class, Class>(1000);
+    private final Map<Class, Class> misses = new LRUSoftCache<Class, Class>(1000);
     // we don't anticipate so many property editors so we have unbounded map
     private final Map<Class, PropertyEditor> cache = new HashMap<Class, PropertyEditor>();
 
@@ -116,11 +117,12 @@ public class PropertyEditorTypeConverter
     }
 
     public void start() throws Exception {
+        ServiceHelper.startService(misses);
     }
 
     public void stop() throws Exception {
         cache.clear();
-        misses.clear();
+        ServiceHelper.stopService(misses);
     }
 
 }

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/management/DefaultManagementLifecycleStrategy.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/management/DefaultManagementLifecycleStrategy.java?rev=1098574&r1=1098573&r2=1098574&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/management/DefaultManagementLifecycleStrategy.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/management/DefaultManagementLifecycleStrategy.java Mon May  2 13:59:45 2011
@@ -41,6 +41,7 @@ import org.apache.camel.Service;
 import org.apache.camel.VetoCamelContextStartException;
 import org.apache.camel.builder.ErrorHandlerBuilder;
 import org.apache.camel.component.bean.BeanProcessor;
+import org.apache.camel.impl.ConsumerCache;
 import org.apache.camel.impl.DefaultCamelContextNameStrategy;
 import org.apache.camel.impl.EndpointRegistry;
 import org.apache.camel.impl.EventDrivenConsumerRoute;
@@ -53,6 +54,7 @@ import org.apache.camel.management.mbean
 import org.apache.camel.management.mbean.ManagedCamelContext;
 import org.apache.camel.management.mbean.ManagedComponent;
 import org.apache.camel.management.mbean.ManagedConsumer;
+import org.apache.camel.management.mbean.ManagedConsumerCache;
 import org.apache.camel.management.mbean.ManagedDelayer;
 import org.apache.camel.management.mbean.ManagedEndpoint;
 import org.apache.camel.management.mbean.ManagedEndpointRegistry;
@@ -407,6 +409,8 @@ public class DefaultManagementLifecycleS
             return getManagedObjectForProcessor(context, (Processor) service, route);
         } else if (service instanceof ThrottlingInflightRoutePolicy) {
             answer = new ManagedThrottlingInflightRoutePolicy(context, (ThrottlingInflightRoutePolicy) service);
+        } else if (service instanceof ConsumerCache) {
+            answer = new ManagedConsumerCache(context, (ConsumerCache) service);
         } else if (service instanceof ProducerCache) {
             answer = new ManagedProducerCache(context, (ProducerCache) service);
         } else if (service instanceof EndpointRegistry) {

Copied: camel/trunk/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedConsumerCache.java (from r1098295, camel/trunk/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedProducerCache.java)
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedConsumerCache.java?p2=camel/trunk/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedConsumerCache.java&p1=camel/trunk/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedProducerCache.java&r1=1098295&r2=1098574&rev=1098574&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedProducerCache.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedConsumerCache.java Mon May  2 13:59:45 2011
@@ -17,7 +17,7 @@
 package org.apache.camel.management.mbean;
 
 import org.apache.camel.CamelContext;
-import org.apache.camel.impl.ProducerCache;
+import org.apache.camel.impl.ConsumerCache;
 import org.springframework.jmx.export.annotation.ManagedAttribute;
 import org.springframework.jmx.export.annotation.ManagedOperation;
 import org.springframework.jmx.export.annotation.ManagedResource;
@@ -25,40 +25,40 @@ import org.springframework.jmx.export.an
 /**
  * @version 
  */
-@ManagedResource(description = "Managed ProducerCache")
-public class ManagedProducerCache extends ManagedService {
-    private final ProducerCache producerCache;
+@ManagedResource(description = "Managed ConsumerCache")
+public class ManagedConsumerCache extends ManagedService {
+    private final ConsumerCache consumerCache;
 
-    public ManagedProducerCache(CamelContext context, ProducerCache producerCache) {
-        super(context, producerCache);
-        this.producerCache = producerCache;
+    public ManagedConsumerCache(CamelContext context, ConsumerCache consumerCache) {
+        super(context, consumerCache);
+        this.consumerCache = consumerCache;
     }
 
-    public ProducerCache getProducerCache() {
-        return producerCache;
+    public ConsumerCache getConsumerCache() {
+        return consumerCache;
     }
 
     @ManagedAttribute(description = "Source")
     public String getSource() {
-        if (producerCache.getSource() != null) {
-            return producerCache.getSource().toString();
+        if (consumerCache.getSource() != null) {
+            return consumerCache.getSource().toString();
         }
         return null;
     }
 
     @ManagedAttribute(description = "Number of elements cached")
     public Integer getSize() {
-        return producerCache.size();
+        return consumerCache.size();
     }
 
     @ManagedAttribute(description = "Maximum cache size (capacity)")
     public Integer getMaximumCacheSize() {
-        return producerCache.getCapacity();
+        return consumerCache.getCapacity();
     }
 
     @ManagedOperation(description = "Purges the cache")
     public void purge() {
-        producerCache.purge();
+        consumerCache.purge();
     }
 
 }

Copied: camel/trunk/camel-core/src/main/java/org/apache/camel/util/LRUSoftCache.java (from r1098295, camel/trunk/camel-core/src/main/java/org/apache/camel/util/LRUCache.java)
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/util/LRUSoftCache.java?p2=camel/trunk/camel-core/src/main/java/org/apache/camel/util/LRUSoftCache.java&p1=camel/trunk/camel-core/src/main/java/org/apache/camel/util/LRUCache.java&r1=1098295&r2=1098574&rev=1098574&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/util/LRUCache.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/util/LRUSoftCache.java Mon May  2 13:59:45 2011
@@ -16,66 +16,174 @@
  */
 package org.apache.camel.util;
 
-import java.util.LinkedHashMap;
+import java.lang.ref.SoftReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashSet;
 import java.util.Map;
-
-import org.apache.camel.Service;
+import java.util.Set;
 
 /**
- * A Least Recently Used Cache
+ * A Least Recently Used Cache which uses {@link SoftReference}.
+ * <p/>
+ * This implementation uses {@link java.lang.ref.SoftReference} for stored values in the cache, to support the JVM
+ * when it wants to reclaim objects when its running out of memory. Therefore this implementation does
+ * not support <b>all</b> the {@link java.util.Map} methods.
+ * <p/>
+ * The following methods is <b>only</b> be be used:
+ * <ul>
+ *   <li>containsKey - To determine if the key is in the cache and refers to a value</li>
+ *   <li>entrySet - To return a set of all the entries (as key/value paris)</li>
+ *   <li>get - To get a value from the cache</li>
+ *   <li>isEmpty - To determine if the cache contains any values</li>
+ *   <li>keySet - To return a set of the current keys which refers to a value</li>
+ *   <li>put - To add a value to the cache</li>
+ *   <li>putAll - To add values to the cache</li>
+ *   <li>remove - To remove a value from the cache by its key</li>
+ *   <li>size - To get the current size</li>
+ *   <li>values - To return a copy of all the value in a list</li>
+ * </ul>
+ * <p/>
+ * The {@link #containsValue(Object)} method should <b>not</b> be used as it's not adjusted to check
+ * for the existence of a value without catering for the soft references.
  *
- * @version 
+ * @see LRUCache
  */
-public class LRUCache<K, V> extends LinkedHashMap<K, V> implements Service {
-    private static final long serialVersionUID = -342098639681884413L;
-    private int maxCacheSize = 10000;
-
-    public LRUCache(int maximumCacheSize) {
-        this(maximumCacheSize, maximumCacheSize, 0.75f, true);
-    }
-
-    /**
-     * Constructs an empty <tt>LRUCache</tt> instance with the
-     * specified initial capacity, maximumCacheSize,load factor and ordering mode.
-     *
-     * @param initialCapacity  the initial capacity.
-     * @param maximumCacheSize the max capacity.
-     * @param loadFactor       the load factor.
-     * @param accessOrder      the ordering mode - <tt>true</tt> for
-     *                         access-order, <tt>false</tt> for insertion-order.
-     * @throws IllegalArgumentException if the initial capacity is negative
-     *                                  or the load factor is non positive.
-     */
-    public LRUCache(int initialCapacity, int maximumCacheSize, float loadFactor, boolean accessOrder) {
-        super(initialCapacity, loadFactor, accessOrder);
-        this.maxCacheSize = maximumCacheSize;
-    }
-
-    /**
-     * Returns the maxCacheSize.
-     */
-    public int getMaxCacheSize() {
-        return maxCacheSize;
-    }
-
-    protected boolean removeEldestEntry(Map.Entry<K, V> entry) {
-        return size() > maxCacheSize;
-    }
-
-    public void start() throws Exception {
-        // noop
-    }
-
-    public void stop() throws Exception {
-        // stop the value and clear the cache
-        if (!isEmpty()) {
-            ServiceHelper.stopServices(values());
-            clear();
+public class LRUSoftCache<K, V> extends LRUCache<K, V> {
+
+    public LRUSoftCache(int maximumCacheSize) {
+        super(maximumCacheSize);
+    }
+
+    public LRUSoftCache(int initialCapacity, int maximumCacheSize, float loadFactor, boolean accessOrder) {
+        super(initialCapacity, maximumCacheSize, loadFactor, accessOrder);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public V put(K key, V value) {
+        SoftReference<V> put = new SoftReference<V>(value);
+        SoftReference<V> prev = (SoftReference<V>) super.put(key, (V) put);
+        return prev != null ? prev.get() : null;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public V get(Object o) {
+        SoftReference<V> ref = (SoftReference<V>) super.get(o);
+        return ref != null ? ref.get() : null;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void putAll(Map<? extends K, ? extends V> map) {
+        // do not use entrySet but copy one by one
+        for (K key : map.keySet()) {
+            put(key, map.get(key));
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public V remove(Object o) {
+        SoftReference<V> ref = (SoftReference<V>) super.remove(o);
+        return ref != null ? ref.get() : null;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public Collection<V> values() {
+        // return a copy of all the active values
+        Collection<SoftReference<V>> col = (Collection<SoftReference<V>>) super.values();
+        Collection<V> answer = new ArrayList<V>(col.size());
+        for (SoftReference<V> ref : col) {
+            V value = ref.get();
+            if (value != null) {
+                answer.add(value);
+            }
         }
+        return answer;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public int size() {
+        // only count as a size if there is a value
+        int size = 0;
+        Collection<SoftReference<V>> col = (Collection<SoftReference<V>>) super.values();
+        for (SoftReference<V> ref : col) {
+            if (ref.get() != null) {
+                size++;
+            }
+        }
+        return size;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return size() == 0;
+    }
+
+    @Override
+    public boolean containsKey(Object o) {
+        // must lookup if the key has a value, as we only regard a key to be contained
+        // if the value is still there (the JVM can remove the soft reference if it need memory)
+        return get(o) != null;
+    }
+
+    @Override
+    public Set<K> keySet() {
+        // must use a copy of the keys to avoid concurrent modifications
+        Set<K> keys = new LinkedHashSet<K>(super.keySet());
+
+        // filter out un referenced values
+        Set<K> answer = new LinkedHashSet<K>();
+        for (K key : keys) {
+            if (containsKey(key)) {
+                answer.add(key);
+            }
+        }
+        return answer;
+    }
+
+    @Override
+    public Set<Map.Entry<K, V>> entrySet() {
+        Set<Map.Entry<K, V>> original = super.entrySet();
+
+        // must use a copy to avoid concurrent modifications and be able to get/set value using
+        // the soft reference so the returned set is without the soft reference, and thus is
+        // use able for the caller to use
+        Set<Map.Entry<K, V>> answer = new LinkedHashSet<Map.Entry<K, V>>(original.size());
+        for (final Map.Entry<K, V> entry : original) {
+            Map.Entry<K, V> view = new Map.Entry<K, V>() {
+                @Override
+                public K getKey() {
+                    return entry.getKey();
+                }
+
+                @Override
+                @SuppressWarnings("unchecked")
+                public V getValue() {
+                    SoftReference<V> ref = (SoftReference<V>) entry.getValue();
+                    return ref != null ? ref.get() : null;
+                }
+
+                @Override
+                @SuppressWarnings("unchecked")
+                public V setValue(V v) {
+                    V put = (V) new SoftReference<V>(v);
+                    SoftReference<V> prev = (SoftReference<V>) entry.setValue(put);
+                    return prev != null ? prev.get() : null;
+                }
+            };
+            answer.add(view);
+        }
+
+        return answer;
     }
 
     @Override
     public String toString() {
-        return "LRUCache@" + ObjectHelper.getIdentityHashCode(this);
+        return "LRUSoftCache@" + ObjectHelper.getIdentityHashCode(this);
     }
 }
\ No newline at end of file

Modified: camel/trunk/camel-core/src/test/java/org/apache/camel/impl/DefaultConsumerCacheTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/impl/DefaultConsumerCacheTest.java?rev=1098574&r1=1098573&r2=1098574&view=diff
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/impl/DefaultConsumerCacheTest.java (original)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/impl/DefaultConsumerCacheTest.java Mon May  2 13:59:45 2011
@@ -26,7 +26,7 @@ import org.apache.camel.PollingConsumer;
 public class DefaultConsumerCacheTest extends ContextTestSupport {
 
     public void testCacheConsumers() throws Exception {
-        ConsumerCache cache = new ConsumerCache(context);
+        ConsumerCache cache = new ConsumerCache(this, context);
         cache.start();
 
         assertEquals("Size should be 0", 0, cache.size());

Copied: camel/trunk/camel-core/src/test/java/org/apache/camel/management/ManagedConsumerCacheTest.java (from r1098295, camel/trunk/camel-core/src/test/java/org/apache/camel/management/ManagedProducerCacheTest.java)
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/management/ManagedConsumerCacheTest.java?p2=camel/trunk/camel-core/src/test/java/org/apache/camel/management/ManagedConsumerCacheTest.java&p1=camel/trunk/camel-core/src/test/java/org/apache/camel/management/ManagedProducerCacheTest.java&r1=1098295&r2=1098574&rev=1098574&view=diff
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/management/ManagedProducerCacheTest.java (original)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/management/ManagedConsumerCacheTest.java Mon May  2 13:59:45 2011
@@ -22,34 +22,42 @@ import java.util.Set;
 import javax.management.MBeanServer;
 import javax.management.ObjectName;
 
+import org.apache.camel.Exchange;
 import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.impl.ConsumerCache;
 
 /**
  * @version 
  */
-public class ManagedProducerCacheTest extends ManagementTestSupport {
+public class ManagedConsumerCacheTest extends ManagementTestSupport {
 
-    public void testManageProducerCache() throws Exception {
-        getMockEndpoint("mock:result").expectedMessageCount(1);
+    public void testManageConsumerCache() throws Exception {
+        // always register services in JMX so we can enlist our consumer template/cache
+        context.getManagementStrategy().getManagementAgent().setRegisterAlways(true);
+
+        ConsumerCache cache = new ConsumerCache(this, context);
+        context.addService(cache);
 
         template.sendBody("direct:start", "Hello World");
 
-        assertMockEndpointsSatisfied();
+        Exchange out = cache.getConsumer(context.getEndpoint("seda:queue")).receive(3000);
+        assertNotNull("Should got an exchange", out);
+        assertEquals("Hello World", out.getIn().getBody());
 
         // get the stats for the route
         MBeanServer mbeanServer = getMBeanServer();
         Set<ObjectName> set = mbeanServer.queryNames(new ObjectName("*:type=services,*"), null);
-        assertEquals(7, set.size());
+        assertEquals(8, set.size());
         List<ObjectName> list = new ArrayList<ObjectName>(set);
         ObjectName on = null;
         for (ObjectName name : list) {
-            if (name.getCanonicalName().contains("ProducerCache")) {
+            if (name.getCanonicalName().contains("ConsumerCache")) {
                 on = name;
                 break;
             }
         }
 
-        assertNotNull("Should have found ProducerCache", on);
+        assertNotNull("Should have found ConsumerCache", on);
 
         Integer max = (Integer) mbeanServer.getAttribute(on, "MaximumCacheSize");
         assertEquals(1000, max.intValue());
@@ -58,7 +66,8 @@ public class ManagedProducerCacheTest ex
         assertEquals(1, current.intValue());
 
         String source = (String) mbeanServer.getAttribute(on, "Source");
-        assertEquals("sendTo(Endpoint[mock://result])", source);
+        assertNotNull(source);
+        assertTrue(source.contains("testManageConsumerCache"));
 
         // purge
         mbeanServer.invoke(on, "purge", null, null);
@@ -72,7 +81,7 @@ public class ManagedProducerCacheTest ex
         return new RouteBuilder() {
             @Override
             public void configure() throws Exception {
-                from("direct:start").to("mock:result");
+                from("direct:start").to("seda:queue");
             }
         };
     }

Added: camel/trunk/camel-core/src/test/java/org/apache/camel/util/LRUSoftCacheTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/util/LRUSoftCacheTest.java?rev=1098574&view=auto
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/util/LRUSoftCacheTest.java (added)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/util/LRUSoftCacheTest.java Mon May  2 13:59:45 2011
@@ -0,0 +1,228 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.TestSupport;
+
+/**
+ *
+ */
+public class LRUSoftCacheTest extends TestSupport {
+
+    public void testLRUSoftCacheGetAndPut() throws Exception {
+        LRUSoftCache<Integer, Object> cache = new LRUSoftCache<Integer, Object>(1000);
+        cache.start();
+
+        cache.put(1, "foo");
+        cache.put(2, "bar");
+
+        assertEquals("foo", cache.get(1));
+        assertEquals("bar", cache.get(2));
+        assertEquals(null, cache.get(3));
+
+        assertEquals(2, cache.size());
+
+        cache.stop();
+    }
+
+    public void testLRUSoftCachePutOverride() throws Exception {
+        LRUSoftCache<Integer, Object> cache = new LRUSoftCache<Integer, Object>(1000);
+        cache.start();
+
+        Object old = cache.put(1, "foo");
+        assertNull(old);
+        old = cache.put(2, "bar");
+        assertNull(old);
+
+        assertEquals("foo", cache.get(1));
+        assertEquals("bar", cache.get(2));
+
+        old = cache.put(1, "changed");
+        assertEquals("foo", old);
+        assertEquals("changed", cache.get(1));
+
+        assertEquals(2, cache.size());
+
+        cache.stop();
+    }
+
+    public void testLRUSoftCachePutAll() throws Exception {
+        LRUSoftCache<Integer, Object> cache = new LRUSoftCache<Integer, Object>(1000);
+        cache.start();
+
+        Map<Integer, Object> map = new HashMap<Integer, Object>();
+        map.put(1, "foo");
+        map.put(2, "bar");
+
+        cache.putAll(map);
+
+        assertEquals("foo", cache.get(1));
+        assertEquals("bar", cache.get(2));
+        assertEquals(null, cache.get(3));
+        assertEquals(2, cache.size());
+
+        cache.stop();
+    }
+
+    public void testLRUSoftCacheRemove() throws Exception {
+        LRUSoftCache<Integer, Object> cache = new LRUSoftCache<Integer, Object>(1000);
+        cache.start();
+
+        cache.put(1, "foo");
+        cache.put(2, "bar");
+
+        assertEquals("bar", cache.get(2));
+        cache.remove(2);
+        assertEquals(null, cache.get(2));
+
+        cache.stop();
+    }
+
+    public void testLRUSoftCacheValues() throws Exception {
+        LRUSoftCache<Integer, Object> cache = new LRUSoftCache<Integer, Object>(1000);
+        cache.start();
+
+        cache.put(1, "foo");
+        cache.put(2, "bar");
+
+        Collection col = cache.values();
+        assertEquals(2, col.size());
+
+        Iterator it = col.iterator();
+        assertEquals("foo", it.next());
+        assertEquals("bar", it.next());
+
+        cache.stop();
+    }
+
+    public void testLRUSoftCacheEmpty() throws Exception {
+        LRUSoftCache<Integer, Object> cache = new LRUSoftCache<Integer, Object>(1000);
+        cache.start();
+
+        assertTrue(cache.isEmpty());
+
+        cache.put(1, "foo");
+        assertFalse(cache.isEmpty());
+
+        cache.put(2, "bar");
+        assertFalse(cache.isEmpty());
+
+        cache.remove(2);
+        assertFalse(cache.isEmpty());
+
+        cache.clear();
+        assertTrue(cache.isEmpty());
+
+        cache.stop();
+        assertTrue(cache.isEmpty());
+    }
+
+    public void testLRUSoftCacheStopEmpty() throws Exception {
+        LRUSoftCache<Integer, Object> cache = new LRUSoftCache<Integer, Object>(1000);
+        cache.start();
+
+        assertTrue(cache.isEmpty());
+
+        cache.put(1, "foo");
+        cache.put(2, "bar");
+        assertFalse(cache.isEmpty());
+
+        cache.stop();
+        assertTrue(cache.isEmpty());
+    }
+
+    public void testLRUSoftCacheContainsKey() throws Exception {
+        LRUSoftCache<Integer, Object> cache = new LRUSoftCache<Integer, Object>(1000);
+        cache.start();
+
+        assertFalse(cache.containsKey(1));
+        cache.put(1, "foo");
+        assertTrue(cache.containsKey(1));
+
+        assertFalse(cache.containsKey(2));
+        cache.put(2, "foo");
+        assertTrue(cache.containsKey(2));
+
+        cache.stop();
+        assertFalse(cache.containsKey(1));
+        assertFalse(cache.containsKey(2));
+    }
+
+    public void testLRUSoftCacheKeySet() throws Exception {
+        LRUSoftCache<Integer, Object> cache = new LRUSoftCache<Integer, Object>(1000);
+        cache.start();
+
+        cache.put(1, "foo");
+        cache.put(2, "foo");
+
+        Set<Integer> keys = cache.keySet();
+        assertEquals(2, keys.size());
+
+        Iterator<Integer> it = keys.iterator();
+        assertEquals(1, it.next().intValue());
+        assertEquals(2, it.next().intValue());
+
+        cache.stop();
+    }
+
+    public void testLRUSoftCacheNotRunOutOfMemory() throws Exception {
+        // we should not run out of memory using the soft cache
+        // if you run this test with LRUCache then you will run out of memory
+        LRUSoftCache<Integer, Object> cache = new LRUSoftCache<Integer, Object>(250);
+        cache.start();
+
+        for (int i = 0; i < 1000; i++) {
+            Object data = createData();
+            Integer key = new Integer(i);
+            log.info("Putting {}", key);
+            cache.put(key, data);
+        }
+
+        int size = cache.size();
+        log.info("Cache size {}", size);
+        assertTrue("Cache size should not be max, was: " + size, size < cache.getMaxCacheSize());
+
+        // should be the last keys
+        List<Integer> list = new ArrayList<Integer>(cache.keySet());
+        log.info("Keys: " + list);
+
+        assertTrue("Cache size should not be max, was: " + list.size(), list.size() < cache.getMaxCacheSize());
+
+        // first key should not be 0
+        int first = list.get(0).intValue();
+        assertTrue("First key should not be 0, was: " + first, first != 0);
+
+        // last key should be 999
+        assertEquals(999, list.get(list.size() - 1).intValue());
+
+        cache.stop();
+    }
+
+    private Object createData() {
+        // 1mb data
+        byte[] buf = new byte[1 * 1024 * 1024];
+        return buf;
+    }
+}