FELIX-1897 Add support for Configuration Admin configuration (in addition to the existing bundle context properties)

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@884484 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/pom.xml b/scr/pom.xml
index e594202..beb59d6 100644
--- a/scr/pom.xml
+++ b/scr/pom.xml
@@ -140,6 +140,20 @@
     <build>
         <directory>${bundle.build.name}</directory>
         <plugins>
+            <!-- generate the MetaType descriptor for the configuration -->
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+                <version>1.4.0</version>
+                <executions>
+                    <execution>
+                        <id>generate-scr-scrdescriptor</id>
+                        <goals>
+                            <goal>scr</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
             <plugin>
                 <groupId>org.apache.felix</groupId>
                 <artifactId>maven-bundle-plugin</artifactId>
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/Activator.java b/scr/src/main/java/org/apache/felix/scr/impl/Activator.java
index 5638db5..d0465b7 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/Activator.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/Activator.java
@@ -25,6 +25,7 @@
 import java.util.Map;
 
 import org.apache.felix.scr.impl.config.ConfigurationComponentRegistry;
+import org.apache.felix.scr.impl.config.ScrConfiguration;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
@@ -46,6 +47,9 @@
     //  name of the LogService class (this is a string to not create a reference to the class)
     static final String LOGSERVICE_CLASS = "org.osgi.service.log.LogService";
 
+    // Our configuration from bundle context properties and Config Admin
+    private ScrConfiguration m_configuration;
+
     // Flag that sets error messages
     private static int m_logLevel = LogService.LOG_ERROR;
 
@@ -64,7 +68,6 @@
     //  thread acting upon configurations
     private ComponentActorThread m_componentActor;
 
-
     /**
      * Registers this instance as a (synchronous) bundle listener and loads the
      * components of already registered bundles.
@@ -82,13 +85,15 @@
         m_logService = new ServiceTracker( context, LOGSERVICE_CLASS, null );
         m_logService.open();
 
+        // get the configuration
+        m_configuration = new ScrConfiguration( context );
+
         // configure logging from context properties
-        m_logLevel = getLogLevel( context );
-        if ( "true".equalsIgnoreCase( context.getProperty( "ds.showversion" ) ) )
-        {
-            log( LogService.LOG_INFO, context.getBundle(), " Version = "
-                + context.getBundle().getHeaders().get( Constants.BUNDLE_VERSION ), null );
-        }
+        m_logLevel = m_configuration.getLogLevel();
+
+        // log SCR startup
+        log( LogService.LOG_INFO, context.getBundle(), " Version = "
+            + context.getBundle().getHeaders().get( Constants.BUNDLE_VERSION ), null );
 
         // create and start the component actor
         m_componentActor = new ComponentActorThread();
@@ -110,7 +115,7 @@
             // Register "scr" impl command service as a
             // wrapper for the bundle repository service.
             context.registerService( org.apache.felix.shell.Command.class.getName(), new ScrCommand( m_context,
-                m_componentRegistry ), null );
+                m_componentRegistry, m_configuration ), null );
         }
         catch ( Throwable th )
         {
@@ -231,7 +236,7 @@
         try
         {
             BundleComponentActivator ga = new BundleComponentActivator( m_componentRegistry, m_componentActor, context,
-                m_logLevel );
+                m_configuration );
             m_componentBundles.put( new Long( bundle.getBundleId() ), ga );
         }
         catch ( Exception e )
@@ -316,56 +321,6 @@
     }
 
 
-    private static int getLogLevel( BundleContext bundleContext )
-    {
-        String levelString = bundleContext.getProperty( "ds.loglevel" );
-        if ( levelString != null )
-        {
-            try
-            {
-                return Integer.parseInt( levelString );
-            }
-            catch ( NumberFormatException nfe )
-            {
-                // might be a descriptive name
-            }
-
-            if ( "debug".equalsIgnoreCase( levelString ) )
-            {
-                return LogService.LOG_DEBUG;
-            }
-            else if ( "info".equalsIgnoreCase( levelString ) )
-            {
-                return LogService.LOG_INFO;
-            }
-            else if ( "warn".equalsIgnoreCase( levelString ) )
-            {
-                return LogService.LOG_WARNING;
-            }
-            else if ( "error".equalsIgnoreCase( levelString ) )
-            {
-                return LogService.LOG_ERROR;
-            }
-        }
-
-        // check ds.showtrace property
-        levelString = bundleContext.getProperty( "ds.trace" );
-        if ( "true".equalsIgnoreCase( bundleContext.getProperty( "ds.showtrace" ) ) )
-        {
-            return LogService.LOG_DEBUG;
-        }
-
-        // next check ds.showerrors property
-        if ( "false".equalsIgnoreCase( bundleContext.getProperty( "ds.showerrors" ) ) )
-        {
-            return -1; // no logging at all !!
-        }
-
-        // default log level (errors only)
-        return LogService.LOG_ERROR;
-    }
-
-
     /**
      * Method to actually emit the log message. If the LogService is available,
      * the message will be logged through the LogService. Otherwise the message
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java b/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java
index 1b1a3fe..2d6df6a 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java
@@ -32,6 +32,7 @@
 import java.util.StringTokenizer;
 
 import org.apache.felix.scr.impl.config.ComponentHolder;
+import org.apache.felix.scr.impl.config.ScrConfiguration;
 import org.apache.felix.scr.impl.helper.Logger;
 import org.apache.felix.scr.impl.manager.AbstractComponentManager;
 import org.apache.felix.scr.impl.metadata.ComponentMetadata;
@@ -69,8 +70,8 @@
     // true as long as the dispose method is not called
     private boolean m_active;
 
-    // the logging level
-    private int m_logLevel;
+    // the configuration
+    private ScrConfiguration m_configuration;
 
 
     /**
@@ -85,7 +86,7 @@
      * @throws ComponentException if any error occurrs initializing this class
      */
     BundleComponentActivator( ComponentRegistry componentRegistry,
-        ComponentActorThread componentActor, BundleContext context, int logLevel ) throws ComponentException
+        ComponentActorThread componentActor, BundleContext context, ScrConfiguration configuration ) throws ComponentException
     {
         // keep the parameters for later
         m_componentRegistry = componentRegistry;
@@ -98,7 +99,7 @@
         // have the LogService handy (if available)
         m_logService = new ServiceTracker( context, Activator.LOGSERVICE_CLASS, null );
         m_logService.open();
-        m_logLevel = logLevel;
+        m_configuration = configuration;
 
         // Get the Metadata-Location value from the manifest
         String descriptorLocations = ( String ) m_context.getBundle().getHeaders().get( "Service-Component" );
@@ -366,6 +367,12 @@
     }
 
 
+    public ScrConfiguration getConfiguration()
+    {
+        return m_configuration;
+    }
+
+
     /**
      * Implements the <code>ComponentContext.enableComponent(String)</code>
      * method by first finding the component(s) for the <code>name</code> and
@@ -533,7 +540,7 @@
      */
     public boolean isLogEnabled( int level )
     {
-        return m_logLevel >= level;
+        return m_configuration.getLogLevel() >= level;
     }
 
 
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/ScrCommand.java b/scr/src/main/java/org/apache/felix/scr/impl/ScrCommand.java
index 75ee6ec..1d0b2ab 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/ScrCommand.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/ScrCommand.java
@@ -30,6 +30,7 @@
 import org.apache.felix.scr.Component;
 import org.apache.felix.scr.Reference;
 import org.apache.felix.scr.ScrService;
+import org.apache.felix.scr.impl.config.ScrConfiguration;
 import org.apache.felix.shell.Command;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
@@ -43,15 +44,18 @@
     private static final String INFO_CMD = "info";
     private static final String ENABLE_CMD = "enable";
     private static final String DISABLE_CMD = "disable";
+    private static final String CONFIG_CMD = "config";
 
     private final BundleContext bundleContext;
     private final ScrService scrService;
+    private final ScrConfiguration scrConfiguration;
 
 
-    ScrCommand( BundleContext bundleContext, ScrService scrService )
+    ScrCommand( BundleContext bundleContext, ScrService scrService, ScrConfiguration scrConfiguration )
     {
         this.bundleContext = bundleContext;
         this.scrService = scrService;
+        this.scrConfiguration = scrConfiguration;
     }
 
 
@@ -113,6 +117,10 @@
             {
                 change( st, out, err, false );
             }
+            else if ( command.equals( CONFIG_CMD ) )
+            {
+                config( out );
+            }
             else
             {
                 err.println( "Unknown command: " + command );
@@ -340,6 +348,17 @@
     }
 
 
+    private void config( PrintStream out )
+    {
+        out.print( "Log Level: " );
+        out.println( scrConfiguration.getLogLevel() );
+        out.print( "Component Factory with Factory Configuration: " );
+        out.println( scrConfiguration.isFactoryEnabled() ? "Supported" : "Unsupported" );
+        out.print( "Update bound Service Properties: " );
+        out.println( scrConfiguration.isRebindEnabled() ? "Yes" : "No" );
+    }
+
+
     private void help( PrintStream out, StringTokenizer st )
     {
         String command = HELP_CMD;
@@ -381,6 +400,14 @@
             out.println( "This command disables the component whose component ID\n" + "is given as command argument." );
             out.println( "" );
         }
+        else if ( command.equals( CONFIG_CMD ) )
+        {
+            out.println( "" );
+            out.println( "scr " + CONFIG_CMD );
+            out.println( "" );
+            out.println( "This command lists the current SCR configuration." );
+            out.println( "" );
+        }
         else
         {
             out.println( "scr " + HELP_CMD + " [" + LIST_CMD + "]" );
@@ -388,6 +415,7 @@
             out.println( "scr " + INFO_CMD + " <componentId>" );
             out.println( "scr " + ENABLE_CMD + " <componentId>" );
             out.println( "scr " + DISABLE_CMD + " <componentId>" );
+            out.println( "scr " + CONFIG_CMD );
         }
     }
 
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
new file mode 100644
index 0000000..c72503b
--- /dev/null
+++ b/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfiguration.java
@@ -0,0 +1,195 @@
+/*
+ * 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.felix.scr.impl.config;
+
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.log.LogService;
+
+
+/**
+ * The <code>ScrConfiguration</code> class conveys configuration for the
+ * Felix DS implementation bundle. The basic configuration is retrieved from
+ * bundle context properties. In addition, this class registers a ManagedService
+ * service to receive configuration supplied from the Configuration Admin
+ * service overlaying the static context properties.
+ *
+ * @scr.component ds="false" name="org.apache.felix.scr.ScrService"
+ */
+public class ScrConfiguration
+{
+
+    private static final String VALUE_TRUE = "true";
+
+    private static final String PROP_FACTORY_ENABLED = "ds.factory.enabled";
+
+    private static final String PROP_REBIND_ENABLED = "ds.rebind.enabled";
+
+    private static final String PROP_LOGLEVEL = "ds.loglevel";
+
+    private static final String LOG_LEVEL_DEBUG = "debug";
+
+    private static final String LOG_LEVEL_INFO = "info";
+
+    private static final String LOG_LEVEL_WARN = "warn";
+
+    private static final String LOG_LEVEL_ERROR = "error";
+
+    private static final String PROP_SHOWTRACE = "ds.showtrace";
+
+    private static final String PROP_SHOWERRORS = "ds.showerrors";
+
+    private final BundleContext bundleContext;
+
+    /**
+     * @scr.property nameRef="PROP_LOGLEVEL" valueRef="LogService.LOG_ERROR"
+     *      type="Integer"
+     *      options 4="Debug" 3="Information" 2="Warnings" 1="Error"
+     */
+    private int logLevel;
+
+    /**
+     * @scr.property nameRef="PROP_FACTORY_ENABLED" value="false" type="Boolean"
+     */
+    private boolean factoryEnabled;
+
+    /**
+     * @scr.property nameRef="PROP_REBIND_ENABLED" value="false" type="Boolean"
+     */
+    private boolean rebindEnabled;
+
+
+    public ScrConfiguration( BundleContext bundleContext )
+    {
+        this.bundleContext = bundleContext;
+
+        // default configuration
+        configure( null );
+
+        // listen for Configuration Admin configuration
+        try
+        {
+            Object service = new ManagedService()
+            {
+                public void updated( Dictionary properties ) throws ConfigurationException
+                {
+                    configure( properties );
+                }
+            };
+            Dictionary props = new Hashtable();
+            props.put( Constants.SERVICE_PID, "org.apache.felix.scr.ScrService" );
+            bundleContext.registerService( ManagedService.class.getName(), service, props );
+        }
+        catch ( Throwable t )
+        {
+            // don't care
+        }
+    }
+
+
+    void configure( Dictionary config )
+    {
+        if ( config == null )
+        {
+
+            logLevel = getLogLevel( bundleContext );
+            factoryEnabled = VALUE_TRUE.equals( bundleContext.getProperty( PROP_FACTORY_ENABLED ) );
+            rebindEnabled = VALUE_TRUE.equals( bundleContext.getProperty( PROP_REBIND_ENABLED ) );
+        }
+        else
+        {
+            logLevel = ( ( Integer ) config.get( PROP_LOGLEVEL ) ).intValue();
+            factoryEnabled = ( ( Boolean ) config.get( PROP_FACTORY_ENABLED ) ).booleanValue();
+            rebindEnabled = ( ( Boolean ) config.get( PROP_REBIND_ENABLED ) ).booleanValue();
+        }
+    }
+
+
+    public int getLogLevel()
+    {
+        return logLevel;
+    }
+
+
+    public boolean isFactoryEnabled()
+    {
+        return factoryEnabled;
+    }
+
+
+    public boolean isRebindEnabled()
+    {
+        return rebindEnabled;
+    }
+
+
+    private static int getLogLevel( BundleContext bundleContext )
+    {
+        String levelString = bundleContext.getProperty( PROP_LOGLEVEL );
+        if ( levelString != null )
+        {
+            try
+            {
+                return Integer.parseInt( levelString );
+            }
+            catch ( NumberFormatException nfe )
+            {
+                // might be a descriptive name
+            }
+
+            if ( LOG_LEVEL_DEBUG.equalsIgnoreCase( levelString ) )
+            {
+                return LogService.LOG_DEBUG;
+            }
+            else if ( LOG_LEVEL_INFO.equalsIgnoreCase( levelString ) )
+            {
+                return LogService.LOG_INFO;
+            }
+            else if ( LOG_LEVEL_WARN.equalsIgnoreCase( levelString ) )
+            {
+                return LogService.LOG_WARNING;
+            }
+            else if ( LOG_LEVEL_ERROR.equalsIgnoreCase( levelString ) )
+            {
+                return LogService.LOG_ERROR;
+            }
+        }
+
+        // check ds.showtrace property
+        if ( VALUE_TRUE.equalsIgnoreCase( bundleContext.getProperty( PROP_SHOWTRACE ) ) )
+        {
+            return LogService.LOG_DEBUG;
+        }
+
+        // next check ds.showerrors property
+        if ( "false".equalsIgnoreCase( bundleContext.getProperty( PROP_SHOWERRORS ) ) )
+        {
+            return -1; // no logging at all !!
+        }
+
+        // default log level (errors only)
+        return LogService.LOG_ERROR;
+    }
+}
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/ComponentFactoryImpl.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/ComponentFactoryImpl.java
index 649a4f2..db888b6 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/ComponentFactoryImpl.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/ComponentFactoryImpl.java
@@ -92,7 +92,7 @@
     {
         super( activator, metadata );
         m_componentInstances = new IdentityHashMap();
-        m_isConfigurationFactory = "true".equals( activator.getBundleContext().getProperty( "ds.factory.enabled" ) );
+        m_isConfigurationFactory = activator.getConfiguration().isFactoryEnabled();
     }
 
 
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java
index 542c1ac..04bd06b 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java
@@ -932,9 +932,7 @@
      */
     private void update( final ServiceReference ref )
     {
-        //        if ( m_componentManager.getActivator().getConfiguration().isRebindEnabled() )
-        if ( "true".equalsIgnoreCase( m_componentManager.getBundle().getBundleContext().getProperty(
-            "ds.rebind.enabled" ) ) )
+        if ( m_componentManager.getActivator().getConfiguration().isRebindEnabled() )
         {
             // The updated method is only invoked if the implementation object is not
             // null. This is valid for both immediate and delayed components
diff --git a/scr/src/main/resources/OSGI-INF/metatype/metatype.properties b/scr/src/main/resources/OSGI-INF/metatype/metatype.properties
new file mode 100644
index 0000000..7d1c849
--- /dev/null
+++ b/scr/src/main/resources/OSGI-INF/metatype/metatype.properties
@@ -0,0 +1,49 @@
+#
+#  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.
+#
+
+
+#
+# This file contains localization strings for configuration labels and
+# descriptions as used in the metatype.xml descriptor generated by the
+# the Sling SCR plugin
+
+org.apache.felix.scr.ScrService.name = Apache Felix Declarative Service Implementation
+org.apache.felix.scr.ScrService.description = Configuration for the Apache \
+ Felix Declarative Services Implementation. This configuration overwrites \
+ configuration defined in framework properties of the same names.
+ 
+ds.loglevel.name = SCR Log Level
+ds.loglevel.description = Allows limiting the amount of logging information \
+ sent to the OSGi LogService. Supported values are DEBUG, INFO, WARN, and \
+ ERROR. Default is ERROR.
+ 
+ds.factory.enabled.name = Extended Factory Components
+ds.factory.enabled.description = 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.
+
+ds.rebind.enabled.name = Rebind Support
+ds.rebind.enabled.description = Whether or not to enable support for rebinding \
+ of bound services whose registration properties have been modified. This is \
+ an Apache Felix SCR specific extension filling the gap of the specification \
+ which does not desribe whether and how such property updates should be \
+ propagated. The default value is false to disable this feature.