FELIX-3039 Add new ds.delayed.keepInstances configuration property to prevent cleanup of unused delayed components.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1185082 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfiguration.java b/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfiguration.java
index dec743a..a988a70 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfiguration.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfiguration.java
@@ -53,11 +53,15 @@
 public class ScrConfiguration
 {
 
-    private static final String VALUE_TRUE = "true";
+    private static final String VALUE_TRUE = Boolean.TRUE.toString();
 
-    static final String PROP_FACTORY_ENABLED = "ds.factory.enabled";
+    public static final String PID = "org.apache.felix.scr.ScrService";
 
-    static final String PROP_LOGLEVEL = "ds.loglevel";
+    public static final String PROP_FACTORY_ENABLED = "ds.factory.enabled";
+
+    public static final String PROP_DELAYED_KEEP_INSTANCES = "ds.delayed.keepInstances";
+
+    public static final String PROP_LOGLEVEL = "ds.loglevel";
 
     // framework property to enable the CT workarounds (see FELIX-2526)
     private static final String PROP_CT_WORKAROUND = "ds.ctworkaround";
@@ -80,7 +84,7 @@
 
     private boolean factoryEnabled;
 
-    static final String PID = "org.apache.felix.scr.ScrService";
+    private boolean keepInstances;
 
     public ScrConfiguration( BundleContext bundleContext )
     {
@@ -105,11 +109,13 @@
         {
             logLevel = getDefaultLogLevel();
             factoryEnabled = getDefaultFactoryEnabled();
+            keepInstances = getDefaultKeepInstances();
         }
         else
         {
             logLevel = getLogLevel( config.get( PROP_LOGLEVEL ) );
-            factoryEnabled = VALUE_TRUE.equals( String.valueOf( config.get( PROP_FACTORY_ENABLED ) ) );
+            factoryEnabled = VALUE_TRUE.equalsIgnoreCase( String.valueOf( config.get( PROP_FACTORY_ENABLED ) ) );
+            keepInstances = VALUE_TRUE.equalsIgnoreCase( String.valueOf( config.get( PROP_DELAYED_KEEP_INSTANCES ) ) );
         }
     }
 
@@ -129,6 +135,12 @@
     }
 
 
+    public boolean keepInstances()
+    {
+        return keepInstances;
+    }
+
+
     public static boolean hasCtWorkaround( final BundleContext bundleContext )
     {
         boolean ctWorkaround = VALUE_TRUE.equals( bundleContext.getProperty( PROP_CT_WORKAROUND ) );
@@ -157,6 +169,12 @@
     }
 
 
+    private boolean getDefaultKeepInstances()
+    {
+        return VALUE_TRUE.equals( bundleContext.getProperty( PROP_DELAYED_KEEP_INSTANCES ) );
+    }
+
+
     private int getDefaultLogLevel()
     {
         return getLogLevel( bundleContext.getProperty( PROP_LOGLEVEL ) );
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/config/ScrManagedServiceMetaTypeProvider.java b/scr/src/main/java/org/apache/felix/scr/impl/config/ScrManagedServiceMetaTypeProvider.java
index 64300d3..84e9480 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/config/ScrManagedServiceMetaTypeProvider.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/config/ScrManagedServiceMetaTypeProvider.java
@@ -83,14 +83,24 @@
                 { "4", "3", "2", "1" }));
 
         adList
-            .add(new AttributeDefinitionImpl(
-                ScrConfiguration.PROP_FACTORY_ENABLED,
-                "Extended Factory Components",
-                "Whether or not to enable the support for creating Factory Component instances based on factory configuration."
-                    + " This is an Apache Felix SCR specific extension, explicitly not supported by the Declarative Services "
-                    + "specification. Reliance on this feature prevent the component from being used with other Declarative "
-                    + "Services implementations. The default value is false to disable this feature.", this
-                    .getScrConfiguration().isFactoryEnabled()));
+        .add(new AttributeDefinitionImpl(
+            ScrConfiguration.PROP_FACTORY_ENABLED,
+            "Extended Factory Components",
+            "Whether or not to enable the support for creating Factory Component instances based on factory configuration."
+                + " This is an Apache Felix SCR specific extension, explicitly not supported by the Declarative Services "
+                + "specification. Reliance on this feature prevent the component from being used with other Declarative "
+                + "Services implementations. The default value is false to disable this feature.", this
+                .getScrConfiguration().isFactoryEnabled()));
+
+        adList.add( new AttributeDefinitionImpl(
+            ScrConfiguration.PROP_DELAYED_KEEP_INSTANCES,
+            "Keep Component Instances",
+            "Whether or not to keep instances of delayed components once they are not referred to any more. The "
+                + "Declarative Services specifications suggests that instances of delayed components are disposed off "
+                + "if there is not used any longer. Setting this flag causes the components to not be disposed off "
+                + "and thus prevent them from being constantly recreated if often used. Examples of such components "
+                + "may be EventHandler services. The default is to dispose off unused components.", this
+                .getScrConfiguration().keepInstances() ) );
 
         return new ObjectClassDefinition()
         {
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/DelayedComponentManager.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/DelayedComponentManager.java
index ba8a1a8..c8466d7 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/DelayedComponentManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/DelayedComponentManager.java
@@ -107,8 +107,9 @@
                 m_useCount--;
 
                 // unget the service instance if no bundle is using it
-                // any longer
-                if ( m_useCount == 0 )
+                // any longer unless delayed component instances have to
+                // be kept (FELIX-3039)
+                if ( m_useCount == 0 && !getActivator().getConfiguration().keepInstances() )
                 {
                     state().ungetService( this );
                 }
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/ServiceComponentTest.java b/scr/src/test/java/org/apache/felix/scr/integration/ServiceComponentTest.java
index 9f1c240..5c6b017 100644
--- a/scr/src/test/java/org/apache/felix/scr/integration/ServiceComponentTest.java
+++ b/scr/src/test/java/org/apache/felix/scr/integration/ServiceComponentTest.java
@@ -19,15 +19,21 @@
 package org.apache.felix.scr.integration;
 
 
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
 import junit.framework.TestCase;
 
 import org.apache.felix.scr.Component;
+import org.apache.felix.scr.impl.config.ScrConfiguration;
 import org.apache.felix.scr.integration.components.SimpleComponent;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.ops4j.pax.exam.junit.JUnit4TestRunner;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.Configuration;
 
 
 @RunWith(JUnit4TestRunner.class)
@@ -170,4 +176,58 @@
         TestCase.assertEquals( Component.STATE_REGISTERED, component.getState() );
         TestCase.assertNull( SimpleComponent.INSTANCE );
     }
+
+    @Test
+    public void test_DelayedSimpleComponent_service_keep_instance() throws IOException
+    {
+        // configure SCR to keep instances
+        Configuration scrConfig = getConfigurationAdmin().getConfiguration( ScrConfiguration.PID, null );
+        Dictionary props = scrConfig.getProperties();
+        if ( props == null )
+        {
+            props = new Hashtable();
+        }
+        props.put( ScrConfiguration.PROP_DELAYED_KEEP_INSTANCES, Boolean.TRUE.toString() );
+        scrConfig.update( props );
+        delay();
+
+        final String pid = "DelayedServiceComponent";
+
+        // one single component exists without configuration
+        final Component component = findComponentByName( pid );
+        TestCase.assertNotNull( component );
+        TestCase.assertEquals( Component.STATE_DISABLED, component.getState() );
+
+        component.enable();
+        delay();
+
+        // the delayed service is expected to only be registered before use
+        TestCase.assertEquals( Component.STATE_REGISTERED, component.getState() );
+        TestCase.assertNull( SimpleComponent.INSTANCE );
+
+        // get the service
+        ServiceReference reference = bundleContext.getServiceReference( "java.lang.Object" );
+        TestCase.assertNotNull( reference );
+        try
+        {
+            final Object theService = bundleContext.getService( reference );
+
+            // service must now be active
+            TestCase.assertEquals( Component.STATE_ACTIVE, component.getState() );
+
+            // and of course we expect the instance
+            TestCase.assertEquals( SimpleComponent.INSTANCE, theService );
+        }
+        finally
+        {
+            bundleContext.ungetService( reference );
+        }
+
+        // component instance must not be disposed off (due to config)
+        TestCase.assertEquals( Component.STATE_ACTIVE, component.getState() );
+        TestCase.assertNotNull( SimpleComponent.INSTANCE );
+
+        // delete the SCR configuration again
+        scrConfig.delete();
+    }
 }