You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pd...@apache.org on 2015/07/09 00:10:16 UTC

svn commit: r1689973 [12/25] - in /felix/sandbox/pderop/dependencymanager.ds: cnf/ext/ cnf/localrepo/ cnf/localrepo/org.apache.felix.framework/ cnf/releaserepo/ org.apache.felix.dependencymanager.ds.itest/ org.apache.felix.dependencymanager.ds.itest/.s...

Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ConfigurationSupport.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ConfigurationSupport.java?rev=1689973&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ConfigurationSupport.java (added)
+++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ConfigurationSupport.java Wed Jul  8 22:10:14 2015
@@ -0,0 +1,680 @@
+/*
+ * 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.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.scr.impl.Activator;
+import org.apache.felix.scr.impl.BundleComponentActivator;
+import org.apache.felix.scr.impl.ComponentRegistry;
+import org.apache.felix.scr.impl.TargetedPID;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ConfigurationEvent;
+import org.osgi.service.cm.ConfigurationListener;
+import org.osgi.service.cm.ConfigurationPermission;
+import org.osgi.service.log.LogService;
+
+public class ConfigurationSupport implements ConfigurationListener
+{
+    private static final ChangeCount changeCounter;
+    static
+    {
+        ChangeCount cc = null;
+        try
+        {
+            Configuration.class.getMethod( "getChangeCount", (Class<?>[]) null );
+            cc = new R5ChangeCount();
+        }
+        catch ( SecurityException e )
+        {
+        }
+        catch ( NoSuchMethodException e )
+        {
+        }
+        if ( cc == null )
+        {
+            cc = new R4ChangeCount();
+        }
+        changeCounter = cc;
+    }
+
+    // the registry of components to be configured
+    private final ComponentRegistry m_registry;
+
+    // the service m_registration of the ConfigurationListener service
+    private ServiceRegistration<?> m_registration;
+    
+    public ConfigurationSupport(final BundleContext bundleContext, final ComponentRegistry registry)
+    {
+        this.m_registry = registry;
+
+        // register as listener for configurations
+        Dictionary<String, Object> props = new Hashtable<String, Object>();
+        props.put(Constants.SERVICE_DESCRIPTION, "Declarative Services Configuration Support Listener");
+        props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
+        this.m_registration = bundleContext.registerService(new String[]
+            { "org.osgi.service.cm.ConfigurationListener" }, this, props);
+    }
+
+    public void dispose()
+    {
+        if (this.m_registration != null)
+        {
+            this.m_registration.unregister();
+            this.m_registration = null;
+        }
+    }
+
+    // ---------- BaseConfigurationSupport overwrites
+
+    public boolean configureComponentHolder(final ComponentHolder<?> holder)
+    {
+
+        // 112.7 configure unless configuration not required
+        if (!holder.getComponentMetadata().isConfigurationIgnored())
+        {
+            final BundleContext bundleContext = holder.getActivator().getBundleContext();
+            if ( bundleContext == null )
+            {
+                return false;// bundle was stopped concurrently with configuration deletion
+            }
+            final List<String> confPids = holder.getComponentMetadata().getConfigurationPid();
+
+            final ServiceReference<?> caRef = bundleContext.getServiceReference(ComponentRegistry.CONFIGURATION_ADMIN);
+            if (caRef != null)
+            {
+                final Object cao = bundleContext.getService(caRef);
+                if (cao != null)
+                {
+                    try
+                    {
+                        if ( cao instanceof ConfigurationAdmin )
+                        {
+                            final ConfigurationAdmin ca = ( ConfigurationAdmin ) cao;
+                            for (final String confPid : confPids )
+                            {
+                                final Collection<Configuration> factory = findFactoryConfigurations( ca, confPid,
+                                        bundleContext.getBundle() );
+                                if ( !factory.isEmpty() )
+                                {
+                                    for ( Configuration config: factory )
+                                    {
+                                        boolean created = false;
+                                        Activator.log( LogService.LOG_DEBUG, null,
+                                                "Configuring holder {0} with factory configuration {1}", new Object[] {
+                                                        holder, config }, null );
+                                        config = getConfiguration( ca, config.getPid() );
+                                        if ( checkBundleLocation( config, bundleContext.getBundle() ) )
+                                        {
+                                            long changeCount = changeCounter.getChangeCount( config, false, -1 );
+                                            created |= holder.configurationUpdated( new TargetedPID( config.getPid() ), 
+                                            		new TargetedPID( config.getFactoryPid() ),
+                                                    config.getProperties(),
+                                                    changeCount );
+                                        }
+                                        if ( !created ) 
+                                        {
+                                        	return false;
+                                        }
+                                    }
+                                }
+                                else
+                                {
+                                    // check for configuration and configure the holder
+                                    Configuration singleton = findSingletonConfiguration( ca, confPid,
+                                            bundleContext.getBundle() );
+                                    if ( singleton != null )
+                                    {
+                                        singleton = getConfiguration( ca, singleton.getPid() );
+                                        Activator.log( LogService.LOG_DEBUG, null,
+                                                "Configuring holder {0} with configuration {1}", new Object[] { holder,
+                                                        singleton }, null );
+                                        if ( singleton != null
+                                                && checkBundleLocation( singleton, bundleContext.getBundle() ) )
+                                        {
+                                            long changeCount = changeCounter.getChangeCount( singleton, false, -1 );
+                                            holder.configurationUpdated( new TargetedPID( singleton.getPid() ), null,
+                                                    singleton.getProperties(), changeCount );
+                                        }
+                                        else
+                                        {
+                                        	return false;
+                                        }
+                                    }
+                                    else 
+                                    {
+                                    	return false;
+                                    }
+                                }
+                            }
+                            return !confPids.isEmpty();
+                        }
+                        else
+                        {
+                            Activator.log( LogService.LOG_WARNING, null, "Cannot configure component {0}",
+                                 new Object[] {holder.getComponentMetadata().getName()}, null );
+                            Activator.log( LogService.LOG_WARNING, null,
+                                "Component Bundle's Configuration Admin is not compatible with "
+                                    + "ours. This happens if multiple Configuration Admin API versions "
+                                    + "are deployed and different bundles wire to different versions", null );
+                            return false;
+
+                        }
+                    }
+                    finally
+                    {
+                        try
+                        {
+                            bundleContext.ungetService( caRef );
+                        }
+                        catch ( IllegalStateException e )
+                        {
+                            // ignore, bundle context was shut down during the above.
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    // ---------- ServiceListener
+
+    public void configureComponentHolders(final ServiceReference<ConfigurationAdmin> configurationAdminReference,
+        final Object configurationAdmin)
+    {
+        if (configurationAdmin instanceof ConfigurationAdmin)
+        {
+            Configuration[] configs = findConfigurations((ConfigurationAdmin) configurationAdmin, null);
+            if (configs != null)
+            {
+                for (int i = 0; i < configs.length; i++)
+                {
+                    ConfigurationEvent cfgEvent = new ConfigurationEvent(configurationAdminReference,
+                        ConfigurationEvent.CM_UPDATED, configs[i].getFactoryPid(), configs[i].getPid());
+                    configurationEvent(cfgEvent);
+                }
+            }
+        }
+    }
+
+    // ---------- ConfigurationListener
+
+    /**
+     * Called by the Configuration Admin service if a configuration is updated
+     * or removed.
+     * <p>
+     * This method is really only called upon configuration changes; it is not
+     * called for existing configurations upon startup of the Configuration
+     * Admin service. To bridge this gap, the
+     * {@link ComponentRegistry#serviceChanged(org.osgi.framework.ServiceEvent)} method called when the
+     * Configuration Admin service is registered calls #configureComponentHolders which calls this method for all
+     * existing configurations to be able to forward existing configurations to
+     * components.
+     *
+     * @param event The configuration change event
+     */
+    public void configurationEvent(ConfigurationEvent event)
+    {
+        final TargetedPID pid = new TargetedPID( event.getPid());
+        String rawFactoryPid = event.getFactoryPid();
+        final TargetedPID factoryPid = rawFactoryPid == null? null: new TargetedPID( rawFactoryPid);
+
+        // iterate over all components which must be configured with this pid
+        // (since DS 1.2, components may specify a specific configuration PID (112.4.4 configuration-pid)
+        Collection<ComponentHolder<?>> holders;
+
+        if (factoryPid == null)
+        {
+            holders = this.m_registry.getComponentHoldersByPid(pid);
+        }
+        else
+        {
+            holders = this.m_registry.getComponentHoldersByPid(factoryPid);
+        }
+
+        Activator.log(LogService.LOG_DEBUG, null, "configurationEvent: Handling {0}  of Configuration PID={1} for component holders {2}",
+                new Object[] {getEventType(event), pid, holders},
+                null);
+
+        for  ( ComponentHolder<?> componentHolder: holders )
+        {
+            if (!componentHolder.getComponentMetadata().isConfigurationIgnored())
+            {
+                switch (event.getType()) {
+                case ConfigurationEvent.CM_DELETED:
+                    if ( factoryPid != null || !configureComponentHolder( componentHolder ) )
+                    {
+                        componentHolder.configurationDeleted( pid, factoryPid );
+                    }
+                    break;
+
+                case ConfigurationEvent.CM_UPDATED:
+                {
+                    final BundleComponentActivator activator = componentHolder.getActivator();
+                    if (activator == null)
+                    {
+                        break;
+                    }
+
+                    final BundleContext bundleContext = activator.getBundleContext();
+                    if (bundleContext == null)
+                    {
+                        break;
+                    }
+
+                    TargetedPID targetedPid = factoryPid == null? pid: factoryPid;
+                    TargetedPID oldTargetedPID = componentHolder.getConfigurationTargetedPID(pid, factoryPid);
+                    if ( factoryPid != null || targetedPid.equals(oldTargetedPID) || targetedPid.bindsStronger( oldTargetedPID ))
+                    {
+                        final ConfigurationInfo configInfo = getConfigurationInfo( pid, targetedPid, componentHolder, bundleContext );
+                        if ( checkBundleLocation( configInfo.getBundleLocation(), bundleContext.getBundle() ) )
+                        {
+                            //If this is replacing a weaker targetedPID delete the old one.
+                            if ( factoryPid == null && !targetedPid.equals(oldTargetedPID) && oldTargetedPID != null)
+                            {
+                                componentHolder.configurationDeleted( pid, factoryPid );
+                            }
+                            componentHolder.configurationUpdated( pid, factoryPid, configInfo.getProps(), configInfo.getChangeCount() );
+                        }
+                    }
+
+                    break;
+                }
+                case ConfigurationEvent.CM_LOCATION_CHANGED:
+                {
+                    //TODO is this logic correct for factory pids????
+                    final BundleComponentActivator activator = componentHolder.getActivator();
+                    if (activator == null)
+                    {
+                        break;
+                    }
+
+                    final BundleContext bundleContext = activator.getBundleContext();
+                    if (bundleContext == null)
+                    {
+                        break;
+                    }
+
+                    TargetedPID targetedPid = factoryPid == null? pid: factoryPid;
+                    TargetedPID oldTargetedPID = componentHolder.getConfigurationTargetedPID(pid, factoryPid);
+                    if ( targetedPid.equals(oldTargetedPID))
+                    {
+                        //this sets the location to this component's bundle if not already set.  OK here
+                        //since it used to be set to this bundle, ok to reset it
+                        final ConfigurationInfo configInfo = getConfigurationInfo( pid, targetedPid, componentHolder, bundleContext );
+                        Activator.log(LogService.LOG_DEBUG, null, "LocationChanged event, same targetedPID {0}, location now {1}",
+                                new Object[] {targetedPid, configInfo.getBundleLocation()},
+                                null);
+                        if (configInfo.getProps() == null)
+                        {
+                            throw new IllegalStateException("Existing Configuration with pid " + pid + 
+                                    " has had its properties set to null and location changed.  We expected a delete event first.");
+                        }
+                        //this config was used on this component.  Does it still match?
+                        if (!checkBundleLocation( configInfo.getBundleLocation(), bundleContext.getBundle() ))
+                        {
+                            //no, delete it
+                            componentHolder.configurationDeleted( pid, factoryPid );
+                            //maybe there's another match
+                            configureComponentHolder(componentHolder);
+                            
+                        }
+                        //else still matches
+                        break;
+                    }
+                    boolean better = targetedPid.bindsStronger( oldTargetedPID );
+                    if ( better )
+                    {
+                        //this sets the location to this component's bundle if not already set.  OK here
+                        //because if it is set to this bundle we will use it.
+                        final ConfigurationInfo configInfo = getConfigurationInfo( pid, targetedPid, componentHolder, bundleContext );
+                        Activator.log(LogService.LOG_DEBUG, null, "LocationChanged event, better targetedPID {0} compared to {1}, location now {2}",
+                                new Object[] {targetedPid, oldTargetedPID, configInfo.getBundleLocation()},
+                                null);
+                        if (configInfo.getProps() == null)
+                        {
+                            //location has been changed before any properties are set.  We don't care.  Wait for an updated event with the properties
+                            break;
+                        }
+                        //this component was not configured with this config.  Should it be now?
+                        if ( checkBundleLocation( configInfo.getBundleLocation(), bundleContext.getBundle() ) )
+                        {
+                            if ( oldTargetedPID != null )
+                            {
+                                //this is a better match, delete old before setting new
+                                componentHolder.configurationDeleted( pid, factoryPid );
+                            }
+                            componentHolder.configurationUpdated( pid, factoryPid,
+                                    configInfo.getProps(), configInfo.getChangeCount() );
+                        }
+                    }
+                    //else worse match, do nothing
+                    else
+                    {
+                        Activator.log(LogService.LOG_DEBUG, null, "LocationChanged event, worse targetedPID {0} compared to {1}, do nothing",
+                                new Object[] {targetedPid, oldTargetedPID},
+                                null);
+                    }
+                    break;
+                }
+                default:
+                    Activator.log(LogService.LOG_WARNING, null, "Unknown ConfigurationEvent type {0}", new Object[] {event.getType()},
+                        null);
+                }
+            }
+        }
+    }
+
+    
+    private String getEventType(ConfigurationEvent event)
+    {
+        switch (event.getType())
+        {
+        case ConfigurationEvent.CM_UPDATED:
+            return "UPDATED";
+        case ConfigurationEvent.CM_DELETED:
+            return "DELETED";
+        case ConfigurationEvent.CM_LOCATION_CHANGED:
+            return "LOCATION CHANGED";
+        default:
+            return "Unkown event type: " + event.getType();
+        }
+
+    }
+
+    private static class ConfigurationInfo
+    {
+        private final Dictionary<String, Object> props;
+        private final String bundleLocation;
+        private final long changeCount;
+        
+        public ConfigurationInfo(Dictionary<String, Object> props, String bundleLocation, long changeCount)
+        {
+            this.props = props;
+            this.bundleLocation = bundleLocation;
+            this.changeCount = changeCount;
+        }
+
+        public long getChangeCount()
+        {
+            return changeCount;
+        }
+
+        public Dictionary<String, Object> getProps()
+        {
+            return props;
+        }
+
+        public String getBundleLocation()
+        {
+            return bundleLocation;
+        }
+        
+    }
+
+    /**
+     * This gets config admin, gets the requested configuration, extracts the info we need from it, and ungets config admin.
+     * Some versions of felix config admin do not allow access to configurations after the config admin instance they were obtained from
+     * are ungot.  Extracting the info we need into "configInfo" solves this problem.
+     * 
+     * @param pid TargetedPID for the desired configuration
+     * @param targetedPid the targeted factory pid for a factory configuration or the pid for a singleton configuration
+     * @param componentHolder ComponentHolder that holds the old change count (for r4 fake change counting)
+     * @param bundleContext BundleContext to get the CA from
+     * @return ConfigurationInfo object containing the info we need from the configuration.
+     */
+    private ConfigurationInfo getConfigurationInfo(final TargetedPID pid, TargetedPID targetedPid,
+            ComponentHolder<?> componentHolder, final BundleContext bundleContext)
+    {
+        final ServiceReference caRef = bundleContext
+            .getServiceReference(ComponentRegistry.CONFIGURATION_ADMIN);
+        if (caRef != null)
+        {
+            try
+            {
+                final Object cao = bundleContext.getService(caRef);
+                if (cao != null)
+                {
+                    try
+                    {
+                        if ( cao instanceof ConfigurationAdmin )
+                        {
+                            final ConfigurationAdmin ca = ( ConfigurationAdmin ) cao;
+                            final Configuration config = getConfiguration( ca, pid.getRawPid() );
+                            return new ConfigurationInfo(config.getProperties(), config.getBundleLocation(),
+                                    changeCounter.getChangeCount( config, true, componentHolder.getChangeCount( pid, targetedPid ) ) );
+                        }
+                        else
+                        {
+                            Activator.log( LogService.LOG_WARNING, null, "Cannot reconfigure component {0}",
+                                new Object[] {componentHolder.getComponentMetadata().getName()}, null );
+                            Activator.log( LogService.LOG_WARNING, null,
+                                "Component Bundle's Configuration Admin is not compatible with " +
+                                "ours. This happens if multiple Configuration Admin API versions " +
+                                "are deployed and different bundles wire to different versions",
+                                null );
+                        }
+                    }
+                    finally
+                    {
+                        bundleContext.ungetService( caRef );
+                    }
+                }
+            }
+            catch (IllegalStateException ise)
+            {
+                // If the bundle has been stopped concurrently
+                Activator.log(LogService.LOG_WARNING, null, "Bundle in unexpected state",
+                    ise);
+            }
+        }
+        return null;
+    }
+
+    private Configuration getConfiguration(final ConfigurationAdmin ca, final String pid)
+    {
+        try
+        {
+            return ca.getConfiguration(pid);
+        }
+        catch (IOException ioe)
+        {
+            Activator.log(LogService.LOG_WARNING, null, "Failed reading configuration for pid={0}", new Object[] {pid}, ioe);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the configuration whose PID equals the given pid. If no such
+     * configuration exists, <code>null</code> is returned.
+     *
+     * @param ca Configuration Admin service
+     * @param pid Pid for desired configuration
+     * @param bundle bundle of the component we are configuring (used in targeted pids)
+     * @return configuration with the specified Pid
+     */
+    public Configuration findSingletonConfiguration(final ConfigurationAdmin ca, final String pid, Bundle bundle)
+    {
+        final String filter = getTargetedPidFilter( pid, bundle, Constants.SERVICE_PID );
+        final Configuration[] cfg = findConfigurations(ca, filter);
+        if (cfg == null)
+        {
+            return null;
+        }
+        String longest = null;
+        Configuration best = null;
+        for (Configuration config: cfg)
+        {
+            if ( checkBundleLocation( config, bundle ) )
+            {
+                String testPid = config.getPid();
+                if ( longest == null || testPid.length() > longest.length())
+                {
+                    longest = testPid;
+                    best = config;
+                }
+            }
+            
+        }
+        return best;
+    }
+
+    /**
+     * Returns all configurations whose factory PID equals the given factory PID
+     * or <code>null</code> if no such configurations exist
+     *
+     * @param ca ConfigurationAdmin service
+     * @param factoryPid factory Pid we want the configurations for
+     * @param bundle bundle we're working for (for location and location permission)
+     * @return the configurations specifying the supplied factory Pid.
+     */
+    private Collection<Configuration> findFactoryConfigurations(final ConfigurationAdmin ca, final String factoryPid, Bundle bundle)
+    {
+        final String filter = getTargetedPidFilter( factoryPid, bundle, ConfigurationAdmin.SERVICE_FACTORYPID );
+        Configuration[] configs = findConfigurations(ca, filter);
+        if (configs == null)
+        {
+            return Collections.emptyList();
+        }
+        Map<String, Configuration> configsByPid = new HashMap<String, Configuration>();
+        for (Configuration config: configs)
+        {
+            if ( checkBundleLocation( config, bundle ) )
+            {
+                Configuration oldConfig = configsByPid.get( config.getPid() );
+                if ( oldConfig == null )
+                {
+                    configsByPid.put( config.getPid(), config );
+                }
+                else
+                {
+                    String newPid = config.getFactoryPid();
+                    String oldPid = oldConfig.getFactoryPid();
+                    if ( newPid.length() > oldPid.length() )
+                    {
+                        configsByPid.put( config.getPid(), config );
+                    }
+                }
+            }
+        }
+        return configsByPid.values();
+    }
+    
+    private boolean checkBundleLocation(Configuration config, Bundle bundle)
+    {
+        if (config == null)
+        {
+            return false;
+        }
+        String configBundleLocation = config.getBundleLocation();
+        return checkBundleLocation( configBundleLocation, bundle );
+    }
+
+    private boolean checkBundleLocation(String configBundleLocation, Bundle bundle)
+    {
+        if ( configBundleLocation == null)
+        {
+            return true;
+        }
+        if (configBundleLocation.startsWith( "?" ))
+        {
+            //multilocation
+            return bundle.hasPermission(new ConfigurationPermission(configBundleLocation, ConfigurationPermission.TARGET));
+        }
+        return configBundleLocation.equals(bundle.getLocation());
+    }
+
+    private Configuration[] findConfigurations(final ConfigurationAdmin ca, final String filter)
+    {
+        try
+        {
+            return ca.listConfigurations(filter);
+        }
+        catch (IOException ioe)
+        {
+            Activator.log(LogService.LOG_WARNING, null, "Problem listing configurations for filter={0}", new Object[] {filter}, ioe);
+        }
+        catch (InvalidSyntaxException ise)
+        {
+            Activator.log(LogService.LOG_ERROR, null, "Invalid Configuration selection filter {0}", new Object[] {filter}, ise);
+        }
+
+        // no factories in case of problems
+        return null;
+    }
+    
+    private String getTargetedPidFilter(String pid, Bundle bundle, String key)
+    {
+        String bsn = bundle.getSymbolicName();
+        String version = bundle.getVersion().toString();
+        String location = escape(bundle.getLocation());
+        String f = String.format(
+                "(|(%1$s=%2$s)(%1$s=%2$s|%3$s)(%1$s=%2$s|%3$s|%4$s)(%1$s=%2$s|%3$s|%4$s|%5$s))", 
+                key, pid, bsn, version, location );
+        return f;
+    }
+    
+    /**
+     * see core spec 3.2.7.  Escape \*() with preceding \
+     * @param value
+     * @return escaped string
+     */
+    static final String escape(String value)
+    {
+        return value.replaceAll( "([\\\\\\*\\(\\)])", "\\\\$1" );
+    }
+    
+    
+    private interface ChangeCount {
+        long getChangeCount( Configuration configuration, boolean fromEvent, long previous );
+    }
+    
+    private static class R5ChangeCount implements ChangeCount {
+
+        public long getChangeCount(Configuration configuration, boolean fromEvent, long previous)
+        {
+            return configuration.getChangeCount();
+        }
+    }   
+    
+    private static class R4ChangeCount implements ChangeCount {
+
+        public long getChangeCount(Configuration configuration, boolean fromEvent, long previous)
+        {
+            return fromEvent? previous + 1:0;
+        }
+        
+    }
+}

Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ReferenceManager.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ReferenceManager.java?rev=1689973&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ReferenceManager.java (added)
+++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ReferenceManager.java Wed Jul  8 22:10:14 2015
@@ -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.
+ */
+package org.apache.felix.scr.impl.config;
+
+import java.util.List;
+
+import org.osgi.framework.ServiceReference;
+
+public interface ReferenceManager<S, T> {
+
+	/**
+	 * Returns an array of <code>ServiceReference</code> instances of all
+	 * services this instance is bound to or <code>null</code> if no services
+	 * are actually bound.
+	 */
+	List<ServiceReference<?>> getServiceReferences();
+
+	/**
+	 * Returns the name of the service reference.
+	 */
+	String getName();
+
+	/**
+	 * Returns the target filter of this dependency as a string or
+	 * <code>null</code> if this dependency has no target filter set.
+	 *
+	 * @return The target filter of this dependency or <code>null</code> if
+	 *      none is set.
+	 */
+	String getTarget();
+
+    boolean isSatisfied();
+}
\ No newline at end of file

Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ScrConfiguration.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ScrConfiguration.java?rev=1689973&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ScrConfiguration.java (added)
+++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ScrConfiguration.java Wed Jul  8 22:10:14 2015
@@ -0,0 +1,360 @@
+/*
+ * 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.apache.felix.scr.impl.Activator;
+import org.apache.felix.scr.impl.ScrCommand;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+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.
+ * <p>
+ * <b>Configuration Source</b>
+ * <p>
+ * <ol>
+ * <li>Framework properties: These are read when the Declarative Services
+ * implementation is first started.</li>
+ * <li>Configuration Admin Service: Properties are provided by means of a
+ * <code>ManagedService</code> with Service PID
+ * <code>org.apache.felix.scr.ScrService</code>. This class uses an OSGi
+ * Service Factory ({@link ScrManagedServiceServiceFactory}) to register the
+ * managed service without requiring the Configuration Admin Service API to be
+ * required upfront.
+ * </li>
+ * </ol>
+ * <p>
+ * See the <i>Configuration</i> section of the
+ * <a href="http://felix.apache.org/site/apache-felix-service-component-runtime.html">Apache Felix Service Component Runtime</a>
+ * documentation page for detailed information.
+ */
+public class ScrConfiguration
+{
+
+    private static final String VALUE_TRUE = Boolean.TRUE.toString();
+
+    public static final String PID = "org.apache.felix.scr.ScrService";
+
+    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_INFO_SERVICE = "ds.info.service";
+
+    public static final String PROP_LOCK_TIMEOUT = "ds.lock.timeout.milliseconds";
+
+    public static final String PROP_STOP_TIMEOUT = "ds.stop.timeout.milliseconds";
+
+    public static final long DEFAULT_LOCK_TIMEOUT_MILLISECONDS = 5000;
+
+    public static final long DEFAULT_STOP_TIMEOUT_MILLISECONDS = 60000;
+
+    public 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";
+
+    public static final String PROP_GLOBAL_EXTENDER="ds.global.extender";
+
+    private final Activator activator;
+
+    private int logLevel;
+
+    private boolean factoryEnabled;
+
+    private boolean keepInstances;
+    
+    private boolean infoAsService;
+    
+    private long lockTimeout = DEFAULT_LOCK_TIMEOUT_MILLISECONDS;
+
+    private long stopTimeout = DEFAULT_STOP_TIMEOUT_MILLISECONDS;
+
+    private Boolean globalExtender;
+
+    private BundleContext bundleContext;
+
+    private ServiceRegistration<ManagedService> managedService;
+    
+    private ScrCommand scrCommand;
+
+    public ScrConfiguration( Activator activator )
+    {
+        this.activator = activator;
+    }
+
+    @SuppressWarnings("unchecked")
+    public void start(final BundleContext bundleContext)
+    {
+        this.bundleContext = bundleContext;
+
+        // listen for Configuration Admin configuration
+        Dictionary<String, Object> props = new Hashtable<String, Object>();
+        props.put(Constants.SERVICE_PID, PID);
+        props.put(Constants.SERVICE_DESCRIPTION, "SCR Configurator");
+        props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
+
+
+        // Process configure from bundle context properties so they can be predictably
+        // overriden by configuration admin later.
+        // Note that if the managed service is registered first then it is random which will win since
+        // configuration may be delivered asynchronously
+        configure( null, false );
+
+        managedService = ( ServiceRegistration<ManagedService> ) bundleContext.registerService("org.osgi.service.cm.ManagedService", new ScrManagedServiceServiceFactory(this),
+            props);
+    }
+
+    public void stop() {
+        if (this.managedService != null) {
+            this.managedService.unregister();
+            this.managedService = null;
+        }
+
+        this.bundleContext = null;
+    }
+    
+    public void setScrCommand(ScrCommand scrCommand)
+    {
+        this.scrCommand = scrCommand;
+        scrCommand.update(infoAsService());
+    }
+
+    // Called from the ScrManagedService.updated method to reconfigure
+    void configure( Dictionary<String, ?> config, boolean fromConfig )
+    {
+        Boolean newGlobalExtender;
+        Boolean oldGlobalExtender;
+        synchronized (this)
+        {
+            if ( config == null )
+            {
+                if (!fromConfig)
+                {
+                    if (this.bundleContext == null)
+                    {
+                        logLevel = LogService.LOG_ERROR;
+                        factoryEnabled = false;
+                        keepInstances = false;
+                        infoAsService = false;
+                        lockTimeout = DEFAULT_LOCK_TIMEOUT_MILLISECONDS;
+                        stopTimeout = DEFAULT_STOP_TIMEOUT_MILLISECONDS;
+                        newGlobalExtender = false;
+                    }
+                    else
+                    {
+                        logLevel = getDefaultLogLevel();
+                        factoryEnabled = getDefaultFactoryEnabled();
+                        keepInstances = getDefaultKeepInstances();
+                        infoAsService = getDefaultInfoAsService();
+                        lockTimeout = getDefaultLockTimeout();
+                        stopTimeout = getDefaultStopTimeout();
+                        newGlobalExtender = getDefaultGlobalExtender();
+                    }
+                }
+                else
+                {
+                    newGlobalExtender = this.globalExtender;
+                }
+            }
+            else
+            {
+                logLevel = getLogLevel( config.get( PROP_LOGLEVEL ) );
+                factoryEnabled = VALUE_TRUE.equalsIgnoreCase( String.valueOf( config.get( PROP_FACTORY_ENABLED ) ) );
+                keepInstances = VALUE_TRUE.equalsIgnoreCase( String.valueOf( config.get( PROP_DELAYED_KEEP_INSTANCES ) ) );
+                infoAsService = VALUE_TRUE.equalsIgnoreCase( String.valueOf( config.get( PROP_INFO_SERVICE) ) );
+                Long timeout = ( Long ) config.get( PROP_LOCK_TIMEOUT );
+                lockTimeout = timeout == null? DEFAULT_LOCK_TIMEOUT_MILLISECONDS: timeout;
+                timeout = ( Long ) config.get( PROP_STOP_TIMEOUT );
+                stopTimeout = timeout == null? DEFAULT_STOP_TIMEOUT_MILLISECONDS: timeout;
+                newGlobalExtender = VALUE_TRUE.equalsIgnoreCase( String.valueOf( config.get( PROP_GLOBAL_EXTENDER) ) );
+            }
+            if ( scrCommand != null )
+            {
+                scrCommand.update( infoAsService() );
+            }
+            oldGlobalExtender = this.globalExtender;
+            this.globalExtender = newGlobalExtender;
+        }
+        if ( newGlobalExtender != oldGlobalExtender )
+        {
+            activator.restart( newGlobalExtender );
+        }
+    }
+
+    /**
+     * Returns the current log level.
+     * @return
+     */
+    public int getLogLevel()
+    {
+        return logLevel;
+    }
+
+
+    public boolean isFactoryEnabled()
+    {
+        return factoryEnabled;
+    }
+
+
+    public boolean keepInstances()
+    {
+        return keepInstances;
+    }
+    
+    public boolean infoAsService()
+    {
+        return infoAsService;
+    }
+
+    public long lockTimeout()
+    {
+        return lockTimeout;
+    }
+
+    public long stopTimeout()
+    {
+        return stopTimeout;
+    }
+
+    public boolean globalExtender()
+    {
+        return globalExtender;
+    }
+
+    private boolean getDefaultFactoryEnabled()
+    {
+        return VALUE_TRUE.equals( bundleContext.getProperty( PROP_FACTORY_ENABLED ) );
+    }
+
+
+    private boolean getDefaultKeepInstances()
+    {
+        return VALUE_TRUE.equals( bundleContext.getProperty( PROP_DELAYED_KEEP_INSTANCES ) );
+    }
+
+
+    private int getDefaultLogLevel()
+    {
+        return getLogLevel( bundleContext.getProperty( PROP_LOGLEVEL ) );
+    }
+    
+    private boolean getDefaultInfoAsService()
+    {
+        return VALUE_TRUE.equalsIgnoreCase( bundleContext.getProperty( PROP_INFO_SERVICE) );
+    }
+
+    private long getDefaultLockTimeout()
+    {
+        String val = bundleContext.getProperty( PROP_LOCK_TIMEOUT);
+        if ( val == null)
+        {
+            return DEFAULT_LOCK_TIMEOUT_MILLISECONDS;
+        }
+        return Long.parseLong( val );
+    }
+
+    private long getDefaultStopTimeout()
+    {
+        String val = bundleContext.getProperty( PROP_STOP_TIMEOUT);
+        if ( val == null)
+        {
+            return DEFAULT_STOP_TIMEOUT_MILLISECONDS;
+        }
+        return Long.parseLong( val );
+    }
+
+
+    private boolean getDefaultGlobalExtender()
+    {
+        return VALUE_TRUE.equalsIgnoreCase( bundleContext.getProperty( PROP_GLOBAL_EXTENDER) );
+    }
+
+    private int getLogLevel( final Object levelObject )
+    {
+        if ( levelObject != null )
+        {
+            if ( levelObject instanceof Number )
+            {
+                return ( ( Number ) levelObject ).intValue();
+            }
+
+            String levelString = levelObject.toString();
+            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;
+    }
+}

Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ScrManagedService.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ScrManagedService.java?rev=1689973&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ScrManagedService.java (added)
+++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ScrManagedService.java Wed Jul  8 22:10:14 2015
@@ -0,0 +1,57 @@
+/*
+ * 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 org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+
+/**
+ * The <code>ScrManagedService</code> receives configuration for the Declarative
+ * Services Runtime itself.
+ * <p>
+ * This class is instantiated in a ServiceFactory manner by the
+ * {@link ScrManagedServiceServiceFactory} when the Configuration Admin service
+ * implementation and API is available.
+ * <p>
+ * Requires OSGi Configuration Admin Service API available
+ *
+ * @see ScrManagedServiceServiceFactory
+ */
+public class ScrManagedService implements ManagedService
+{
+
+    private final ScrConfiguration scrConfiguration;
+
+    protected final ScrConfiguration getScrConfiguration()
+    {
+        return scrConfiguration;
+    }
+
+    public ScrManagedService(final ScrConfiguration scrConfiguration)
+    {
+        this.scrConfiguration = scrConfiguration;
+    }
+
+    public void updated(Dictionary<String, ?> properties) throws ConfigurationException
+    {
+        this.scrConfiguration.configure(properties, true);
+    }
+}

Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ScrManagedServiceMetaTypeProvider.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ScrManagedServiceMetaTypeProvider.java?rev=1689973&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ScrManagedServiceMetaTypeProvider.java (added)
+++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ScrManagedServiceMetaTypeProvider.java Wed Jul  8 22:10:14 2015
@@ -0,0 +1,247 @@
+/*
+ * 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.io.InputStream;
+import java.util.ArrayList;
+
+import org.osgi.service.metatype.AttributeDefinition;
+import org.osgi.service.metatype.MetaTypeProvider;
+import org.osgi.service.metatype.ObjectClassDefinition;
+
+/**
+ * The <code>ScrManagedServiceMetaTypeProvider</code> receives the Declarative
+ * Services Runtime configuration (by extending the {@link ScrManagedService}
+ * class but also provides a MetaType Service ObjectClassDefinition.
+ * <p>
+ * This class is instantiated in a ServiceFactory manner by the
+ * {@link ScrManagedServiceServiceFactory} when the Configuration Admin service
+ * implementation and API is available and if the Metatype Service API is also
+ * available.
+ * <p>
+ * Requires OSGi Metatype Service API available
+ *
+ * @see ScrManagedServiceServiceFactory
+ */
+class ScrManagedServiceMetaTypeProvider extends ScrManagedService
+    implements MetaTypeProvider
+{
+
+    static ScrManagedService create(final ScrConfiguration scrConfiguration)
+    {
+        return new ScrManagedServiceMetaTypeProvider(scrConfiguration);
+    }
+
+    private ScrManagedServiceMetaTypeProvider(final ScrConfiguration scrConfiguration)
+    {
+        super(scrConfiguration);
+    }
+
+    /**
+     * @see org.osgi.service.metatype.MetaTypeProvider#getLocales()
+     */
+    public String[] getLocales()
+    {
+        return null;
+    }
+
+    /**
+     * @see org.osgi.service.metatype.MetaTypeProvider#getObjectClassDefinition(java.lang.String, java.lang.String)
+     */
+    public ObjectClassDefinition getObjectClassDefinition( String id, String locale )
+    {
+        if ( !ScrConfiguration.PID.equals( id ) )
+        {
+            return null;
+        }
+
+        final ArrayList<AttributeDefinition> adList = new ArrayList<AttributeDefinition>();
+
+        adList.add(new AttributeDefinitionImpl(ScrConfiguration.PROP_LOGLEVEL, "SCR Log Level",
+            "Allows limiting the amount of logging information sent to the OSGi LogService."
+                + " Supported values are DEBUG, INFO, WARN, and ERROR. Default is ERROR.", AttributeDefinition.INTEGER,
+            new String[]
+                { String.valueOf(this.getScrConfiguration().getLogLevel()) }, 0, new String[]
+                { "Debug", "Information", "Warnings", "Error" }, new String[]
+                { "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()));
+
+        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 of unused components.", this
+                    .getScrConfiguration().keepInstances() ) );
+
+        adList.add( new AttributeDefinitionImpl(
+                ScrConfiguration.PROP_LOCK_TIMEOUT,
+                "Lock timeout milliseconds",
+                "How long a lock is held before releasing due to suspected deadlock",
+                AttributeDefinition.LONG,
+                new String[] { String.valueOf(this.getScrConfiguration().lockTimeout())},
+                0, null, null) );
+
+        adList.add( new AttributeDefinitionImpl(
+                ScrConfiguration.PROP_STOP_TIMEOUT,
+                "Stop timeout milliseconds",
+                "How long stopping a bundle is waited for before continuing due to suspected deadlock",
+                AttributeDefinition.LONG,
+                new String[] { String.valueOf(this.getScrConfiguration().stopTimeout())},
+                0, null, null) );
+
+        adList.add( new AttributeDefinitionImpl(
+                ScrConfiguration.PROP_GLOBAL_EXTENDER,
+                "Global Extender",
+                "Whether to extend all bundles whether or not visible to this bundle.",
+                false ) );
+
+        return new ObjectClassDefinition()
+        {
+
+            private final AttributeDefinition[] attrs = adList
+                .toArray(new AttributeDefinition[adList.size()]);
+
+            public String getName()
+            {
+                return "Apache Felix Declarative Service Implementation";
+            }
+
+            public InputStream getIcon(int arg0)
+            {
+                return null;
+            }
+
+            public String getID()
+            {
+                return ScrConfiguration.PID;
+            }
+
+            public String getDescription()
+            {
+                return "Configuration for the Apache Felix Declarative Services Implementation."
+                    + " This configuration overwrites configuration defined in framework properties of the same names.";
+            }
+
+            public AttributeDefinition[] getAttributeDefinitions(int filter)
+            {
+                return (filter == OPTIONAL) ? null : attrs;
+            }
+        };
+    }
+
+    private static class AttributeDefinitionImpl implements AttributeDefinition
+    {
+
+        private final String id;
+        private final String name;
+        private final String description;
+        private final int type;
+        private final String[] defaultValues;
+        private final int cardinality;
+        private final String[] optionLabels;
+        private final String[] optionValues;
+
+
+        AttributeDefinitionImpl( final String id, final String name, final String description, final boolean defaultValue )
+        {
+            this( id, name, description, BOOLEAN, new String[]
+                { String.valueOf(defaultValue) }, 0, null, null );
+        }
+
+        AttributeDefinitionImpl( final String id, final String name, final String description, final int type,
+            final String[] defaultValues, final int cardinality, final String[] optionLabels,
+            final String[] optionValues )
+        {
+            this.id = id;
+            this.name = name;
+            this.description = description;
+            this.type = type;
+            this.defaultValues = defaultValues;
+            this.cardinality = cardinality;
+            this.optionLabels = optionLabels;
+            this.optionValues = optionValues;
+        }
+
+
+        public int getCardinality()
+        {
+            return cardinality;
+        }
+
+
+        public String[] getDefaultValue()
+        {
+            return defaultValues;
+        }
+
+
+        public String getDescription()
+        {
+            return description;
+        }
+
+
+        public String getID()
+        {
+            return id;
+        }
+
+
+        public String getName()
+        {
+            return name;
+        }
+
+
+        public String[] getOptionLabels()
+        {
+            return optionLabels;
+        }
+
+
+        public String[] getOptionValues()
+        {
+            return optionValues;
+        }
+
+
+        public int getType()
+        {
+            return type;
+        }
+
+
+        public String validate( String arg0 )
+        {
+            return null;
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ScrManagedServiceServiceFactory.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ScrManagedServiceServiceFactory.java?rev=1689973&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ScrManagedServiceServiceFactory.java (added)
+++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/config/ScrManagedServiceServiceFactory.java Wed Jul  8 22:10:14 2015
@@ -0,0 +1,72 @@
+/*
+ * 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 org.apache.felix.scr.impl.Activator;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.log.LogService;
+
+/**
+ * The <code>ScrManagedServiceServiceFactory</code> is the ServiceFactory
+ * registered on behalf of the {@link ScrManagedService} (or
+ * {@link ScrManagedServiceMetaTypeProvider}, resp.) to create the instance on
+ * demand once it is used by the Configuration Admin Service or the MetaType
+ * Service.
+ * <p>
+ * In contrast to the {@link ScrManagedService} and
+ * {@link ScrManagedServiceMetaTypeProvider} classes, this class only requires
+ * core OSGi API and thus may be instantiated without the Configuration Admin
+ * and/or MetaType Service API actually available at the time of instantiation.
+ */
+public class ScrManagedServiceServiceFactory implements ServiceFactory<ScrManagedService>
+{
+    private final ScrConfiguration scrConfiguration;
+
+    public ScrManagedServiceServiceFactory(final ScrConfiguration scrConfiguration)
+    {
+        this.scrConfiguration = scrConfiguration;
+    }
+
+    public ScrManagedService getService(Bundle bundle, ServiceRegistration<ScrManagedService> registration)
+    {
+        try
+        {
+            return ScrManagedServiceMetaTypeProvider.create( this.scrConfiguration );
+        }
+        catch ( Throwable t )
+        {
+            // assume MetaType Service API not available
+            Activator
+                .log(
+                    LogService.LOG_ERROR,
+                    null,
+                    "Cannot create MetaType providing ManagedService; not providing Metatype information but just accepting configuration",
+                    t );
+        }
+        return new ScrManagedService( this.scrConfiguration );
+    }
+
+    public void ungetService(Bundle bundle, ServiceRegistration<ScrManagedService> registration, ScrManagedService service)
+    {
+        // nothing really todo; GC will do the rest
+    }
+
+}

Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/ActivateMethod.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/ActivateMethod.java?rev=1689973&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/ActivateMethod.java (added)
+++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/ActivateMethod.java Wed Jul  8 22:10:14 2015
@@ -0,0 +1,300 @@
+/*
+ * 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.helper;
+
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.scr.impl.metadata.DSVersion;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.log.LogService;
+
+
+public class ActivateMethod extends BaseMethod<ActivatorParameter>
+{
+
+    protected static final Class<?> COMPONENT_CONTEXT_CLASS = ComponentContext.class;
+    protected static final Class<?> BUNDLE_CONTEXT_CLASS = BundleContext.class;
+    protected static final Class<?> INTEGER_CLASS = Integer.class;
+
+    protected final boolean m_supportsInterfaces;
+
+
+    public ActivateMethod( final String methodName,
+            final boolean methodRequired,
+            final Class<?> componentClass,
+            final DSVersion dsVersion,
+            final boolean configurableServiceProperties,
+            boolean supportsInterfaces )
+    {
+        super( methodName, methodRequired, componentClass, dsVersion, configurableServiceProperties );
+        m_supportsInterfaces = supportsInterfaces;
+    }
+
+
+    @Override
+    protected Method doFindMethod( Class<?> targetClass, boolean acceptPrivate, boolean acceptPackage, SimpleLogger logger )
+        throws SuitableMethodNotAccessibleException, InvocationTargetException
+    {
+
+        boolean suitableMethodNotAccessible = false;
+
+        try
+        {
+            // find the declared method in this class
+            final Method method = getMethod( targetClass, getMethodName(), new Class[]
+                { COMPONENT_CONTEXT_CLASS }, acceptPrivate, acceptPackage, logger );
+            if ( method != null )
+            {
+                return method;
+            }
+        }
+        catch ( SuitableMethodNotAccessibleException thrown )
+        {
+            logger.log( LogService.LOG_DEBUG, "SuitableMethodNotAccessible", thrown );
+            suitableMethodNotAccessible = true;
+        }
+        if (getDSVersion().isDS11())
+        {
+            List<Method> methods = getSortedMethods( targetClass);
+            for (Method m: methods)
+            {
+                final Class<?>[] parameterTypes = m.getParameterTypes();
+                if (parameterTypes.length == 1)
+                {
+                    Class<?> type = parameterTypes[0];
+                    //single parameter method with parameter ComponentContext will already have been found.
+                    if (type == BUNDLE_CONTEXT_CLASS)
+                    {
+                        if ( accept( m, acceptPrivate, acceptPackage, returnValue() ) )
+                        {
+                            return m;
+                        }
+                        suitableMethodNotAccessible = true;
+                    }
+                    if (getDSVersion().isDS13() && isAnnotation(type))
+                    {
+                        if ( accept( m, acceptPrivate, acceptPackage, returnValue() ) )
+                        {
+                            return m;
+                        }
+                        suitableMethodNotAccessible = true;
+                    }
+                    if (type == ClassUtils.MAP_CLASS)
+                    {
+                        if ( accept( m, acceptPrivate, acceptPackage, returnValue() ) )
+                        {
+                            return m;
+                        }
+                        suitableMethodNotAccessible = true;
+                    }
+                    if (type == int.class)
+                    {
+                        if ( accept( m, acceptPrivate, acceptPackage, returnValue() ) )
+                        {
+                            return m;
+                        }
+                        suitableMethodNotAccessible = true;
+                    }
+                    if (type == Integer.class)
+                    {
+                        if ( accept( m, acceptPrivate, acceptPackage, returnValue() ) )
+                        {
+                            return m;
+                        }
+                        suitableMethodNotAccessible = true;
+                    }
+
+                }
+                else if (parameterTypes.length > 1)
+                {
+                    boolean accept = true;
+                    for (Class<?> type: parameterTypes)
+                    {
+                        accept = type == COMPONENT_CONTEXT_CLASS
+                            || type == BUNDLE_CONTEXT_CLASS
+                            || type == ClassUtils.MAP_CLASS
+                            || ( isDeactivate() && ( type == int.class || type == Integer.class))
+                            || ( getDSVersion().isDS13() && isAnnotation(type));
+                        if ( !accept )
+                        {
+                            break;
+                        }
+
+                    }
+                    if (accept)
+                    {
+                        if ( accept( m, acceptPrivate, acceptPackage, returnValue() ) )
+                        {
+                            return m;
+                        }
+                        suitableMethodNotAccessible = true;
+                    }
+
+                }
+                else //no parameters
+                {
+                    if ( accept( m, acceptPrivate, acceptPackage, returnValue() ) )
+                    {
+                        return m;
+                    }
+                    suitableMethodNotAccessible = true;
+                }
+
+            }
+        }
+
+        if ( suitableMethodNotAccessible )
+        {
+            throw new SuitableMethodNotAccessibleException();
+        }
+
+        return null;
+    }
+
+
+    boolean isDeactivate()
+    {
+        return false;
+    }
+
+
+    /**
+     * returns the declared methods of the target class, with the correct name, sorted by number of parameters ( no parameters last)
+     * @param targetClass class to examine methods of
+     * @return sorted methods of correct name;
+     */
+    List<Method> getSortedMethods(Class<?> targetClass)
+    {
+        List<Method> result = new ArrayList<Method>();
+        Method[] methods = targetClass.getDeclaredMethods();
+        for (Method m: methods)
+        {
+            if (m.getName().equals(getMethodName()))
+            {
+                result.add(m);
+            }
+        }
+        Collections.sort(result, new Comparator<Method>(){
+
+            public int compare(Method m1, Method m2)
+            {
+                final int l1 = m1.getParameterTypes().length;
+                final int l2 = m2.getParameterTypes().length;
+                if ( l1 == 0)
+                {
+                    return l2;
+                }
+                if ( l2 == 0)
+                {
+                    return -l1;
+                }
+                if (l1 == 1 && l2 == 1)
+                {
+                    final Class<?> t1 = m1.getParameterTypes()[0];
+                    final Class<?> t2 = m2.getParameterTypes()[0];
+                    //t1, t2 can't be equal
+                    if (t1 == COMPONENT_CONTEXT_CLASS) return -1;
+                    if (t2 == COMPONENT_CONTEXT_CLASS) return 1;
+                    if (t1 == BUNDLE_CONTEXT_CLASS) return -1;
+                    if (t2 == BUNDLE_CONTEXT_CLASS) return 1;
+                    if (isAnnotation(t1)) return isAnnotation(t2)? 0: -1;
+                    if (isAnnotation(t2)) return 1;
+                    if (t1 == ClassUtils.MAP_CLASS) return -1;
+                    if (t2 == ClassUtils.MAP_CLASS) return 1;
+                    if (t1 == int.class) return -1;
+                    if (t2 == int.class) return 1;
+                    if (t1 == Integer.class) return -1;
+                    if (t2 == Integer.class) return 1;
+                    return 0;
+                }
+                return l1 - l2;
+            }
+
+        });
+        return result;
+    }
+
+    private boolean isAnnotation(final Class<?> t1)
+    {
+        return t1.isAnnotation() || (m_supportsInterfaces && t1.isInterface() && !(t1 == ClassUtils.MAP_CLASS));
+    }
+
+
+    @Override
+    protected Object[] getParameters( Method method, ActivatorParameter rawParameter )
+    {
+        final Class<?>[] parameterTypes = method.getParameterTypes();
+        final ActivatorParameter ap = rawParameter;
+        final Object[] param = new Object[parameterTypes.length];
+        for ( int i = 0; i < param.length; i++ )
+        {
+            if ( parameterTypes[i] == COMPONENT_CONTEXT_CLASS )
+            {
+                param[i] = ap.getComponentContext();
+            }
+            else if ( parameterTypes[i] == BUNDLE_CONTEXT_CLASS )
+            {
+                param[i] = ap.getComponentContext().getBundleContext();
+            }
+            else if ( parameterTypes[i] == ClassUtils.MAP_CLASS )
+            {
+                // note: getProperties() returns a ReadOnlyDictionary which is a Map
+                param[i] = ap.getComponentContext().getProperties();
+            }
+            else if ( parameterTypes[i] == INTEGER_CLASS || parameterTypes[i] == Integer.TYPE )
+            {
+                param[i] = ap.getReason();
+            }
+            else
+            {
+                param[i] = Annotations.toObject(parameterTypes[i],
+                    (Map<String, Object>) ap.getComponentContext().getProperties(),
+                    ap.getComponentContext().getBundleContext().getBundle(), m_supportsInterfaces);
+            }
+        }
+
+        return param;
+    }
+
+
+    @Override
+    protected String getMethodNamePrefix()
+    {
+        return "activate";
+    }
+
+    @Override
+    public MethodResult invoke( Object componentInstance, ActivatorParameter rawParameter, final MethodResult methodCallFailureResult, SimpleLogger logger )
+    {
+        if (methodExists( logger ))
+        {
+            return super.invoke(componentInstance, rawParameter, methodCallFailureResult, logger );
+        }
+        return null;
+    }
+
+}

Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/ActivatorParameter.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/ActivatorParameter.java?rev=1689973&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/ActivatorParameter.java (added)
+++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/ActivatorParameter.java Wed Jul  8 22:10:14 2015
@@ -0,0 +1,45 @@
+/*
+ * 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.helper;
+
+import org.osgi.service.component.ComponentContext;
+
+public final class ActivatorParameter
+{
+    private final ComponentContext m_componentContext;
+    private final int m_reason;
+
+    public ActivatorParameter( ComponentContext componentContext, int reason )
+    {
+        this.m_componentContext = componentContext;
+        this.m_reason = reason;
+    }
+
+
+    public ComponentContext getComponentContext()
+    {
+        return m_componentContext;
+    }
+
+
+    public int getReason()
+    {
+        return m_reason;
+    }
+}
\ No newline at end of file

Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/Annotations.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/Annotations.java?rev=1689973&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/Annotations.java (added)
+++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/Annotations.java Wed Jul  8 22:10:14 2015
@@ -0,0 +1,227 @@
+/*
+ * 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.helper;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.osgi.framework.Bundle;
+
+public class Annotations
+{
+    
+    static public <T> T toObject(Class<T> clazz, Map<String, Object> props, Bundle b, boolean supportsInterfaces )
+    {     
+        Map<String, Object> m = new HashMap<String, Object>();
+        
+        Method[] methods = clazz.getMethods();
+        Map<String, Method> complexFields = new HashMap<String, Method>();
+        for ( Method method: methods )
+        {
+            String name = method.getName();
+            String key = fixup(name);
+            Object raw = props.get(key);
+            Class<?> returnType = method.getReturnType();
+            Object cooked;
+            if ( returnType.isInterface() || returnType.isAnnotation())
+            {
+                complexFields.put(key, method);
+                continue;
+            }
+            if (returnType.isArray())
+            {
+                Class<?> componentType = returnType.getComponentType();
+                if ( componentType.isInterface() || componentType.isAnnotation())
+                {
+                    complexFields.put(key, method);
+                    continue;
+                }
+                cooked = coerceToArray(componentType, raw, b);
+            }
+            else
+            {
+                cooked = Coercions.coerce( returnType, raw, b );
+            }
+            m.put( name, cooked );
+        }
+        if (!complexFields.isEmpty())
+        {
+            if (!supportsInterfaces )
+            {
+            //error
+                return null;//??
+            }
+            Map<String, List<Map<String, Object>>> nested = extractSubMaps(complexFields.keySet(), props);
+            for (Map.Entry<String, Method> entry: complexFields.entrySet())
+            {
+                List<Map<String, Object>> proplist = nested.get(entry.getKey());
+                Method method = entry.getValue();
+                Class<?> returnType  = method.getReturnType();
+                if (returnType.isArray())
+                {
+                    Class<?> componentType = returnType.getComponentType();
+                    Object result = Array.newInstance(componentType, proplist.size());
+                    for (int i = 0; i < proplist.size(); i++)
+                    {
+                        Map<String, Object> rawElement = proplist.get(i);
+                        Object cooked = toObject(componentType, rawElement, b, supportsInterfaces);
+                        Array.set(result, i, cooked);
+                    }
+                    m.put(method.getName(), result);
+                }
+                else
+                {
+                    if (!proplist.isEmpty())
+                    {
+                        Object cooked = toObject(returnType, proplist.get(0), b, supportsInterfaces);
+                        m.put(method.getName(), cooked);
+                    }
+                }
+            }
+        }
+        
+        InvocationHandler h = new Handler(m);
+        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[] { clazz }, h);
+    }
+    
+    private static Map<String, List<Map<String, Object>>> extractSubMaps(Collection<String> keys, Map<String, Object> map) 
+    {
+        Map<String, List<Map<String, Object>>> result = new HashMap<String, List<Map<String, Object>>>();
+        //Form a regexp to recognize all the keys as prefixes in the map keys.
+        StringBuilder b = new StringBuilder("(");
+        for (String key: keys)
+        {
+            b.append(key).append("|");
+        }
+        b.deleteCharAt(b.length() -1);
+        b.append(")\\.([0-9]*)\\.(.*)");
+        Pattern p = Pattern.compile(b.toString());
+        for (Map.Entry<String, Object> entry: map.entrySet())
+        {
+            String longKey = entry.getKey();
+            Matcher m = p.matcher(longKey);
+            if (m.matches())
+            {
+                String key = m.group(1);
+                int index = Integer.parseInt(m.group(2));
+                String subkey = m.group(3);
+                List<Map<String, Object>> subMapsForKey = result.get(key);
+                if (subMapsForKey == null)
+                {
+                    subMapsForKey = new ArrayList<Map<String, Object>>();
+                    result.put(key, subMapsForKey);
+                }
+                //make sure there is room for the possible new submap
+                for (int i = subMapsForKey.size(); i <= index; i++)
+                {
+                    subMapsForKey.add(new HashMap<String, Object>());                    
+                }
+                Map<String, Object> subMap = subMapsForKey.get(index);
+                subMap.put(subkey, entry.getValue());
+            }
+        }
+        return result;
+    }
+
+    private static Object coerceToArray(Class<?> componentType, Object raw, Bundle bundle)
+    {
+        if (raw == null)
+        {
+            return null;
+        }
+        if (raw.getClass().isArray())
+        {
+            int size = Array.getLength(raw);
+            Object result = Array.newInstance(componentType, size);
+            for (int i = 0; i < size; i++)
+            {
+                Object rawElement = Array.get(raw, i);
+                Object cooked = Coercions.coerce(componentType, rawElement, bundle);
+                Array.set(result, i, cooked);
+            }
+            return result;
+        }
+        if (raw instanceof Collection)
+        {
+            Collection raws = (Collection) raw;
+            int size = raws.size();
+            Object result = Array.newInstance(componentType, size);
+            int i = 0;
+            for (Object rawElement: raws)
+            {
+                Object cooked = Coercions.coerce(componentType, rawElement, bundle);
+                Array.set(result, i++, cooked);
+            }
+            return result;
+
+        }
+        Object cooked = Coercions.coerce(componentType, raw, bundle);
+        Object result = Array.newInstance(componentType, 1);
+        Array.set(result, 0, cooked);
+        return result;
+
+    }
+    
+    private static final Pattern p = Pattern.compile("(\\$\\$)|(\\$)|(__)|(_)");
+    
+    static String fixup(String name)
+    {
+        Matcher m = p.matcher(name);
+        StringBuffer b = new StringBuffer();
+        while (m.find())
+        {
+            String replacement = "";//null;
+            if (m.group(1) != null) replacement = "\\$";
+            if (m.group(2) != null) replacement = "";
+            if (m.group(3) != null) replacement = "_";
+            if (m.group(4) != null) replacement = ".";
+            
+            m.appendReplacement(b, replacement);
+        }
+        m.appendTail(b);
+        return b.toString();
+    }
+
+    private static class Handler implements InvocationHandler 
+    {
+        
+        private final Map<String, Object> values;
+       
+        public Handler(Map<String, Object> values)
+        {
+            this.values = values;
+        }
+
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+        {
+            return values.get(method.getName());
+        }
+        
+    }
+
+}