felix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pde...@apache.org
Subject svn commit: r1587056 [2/2] - in /felix/sandbox/pderop/dependencymanager-prototype/dm.runtime: ./ .settings/ src/ src/dm/ src/dm/runtime/ test/
Date Sun, 13 Apr 2014 17:26:10 GMT
Added: felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/FactorySet.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/FactorySet.java?rev=1587056&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/FactorySet.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/FactorySet.java Sun Apr 13 17:26:09 2014
@@ -0,0 +1,540 @@
+/*
+ * 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 dm.runtime;
+
+import java.lang.reflect.Method;
+import java.util.AbstractSet;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.osgi.framework.Bundle;
+
+import dm.Component;
+import dm.Dependency;
+import dm.DependencyManager;
+
+/**
+ * This class implements a <code>java.util.Set</code> which acts as a service factory.
+ * When a <code>Service</annotation> contains a <code>factory</code> attribute, this class is provided
+ * into the OSGi registry with a <code>dm.factory.name</code> service property. So, another factory component
+ * may be injected with this Set. And each time a Dictionary configuration is registered in the Set,
+ * then a new Service instance will be instantiated, and will be provided with the Dictionary passed to the
+ * Service instance.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings( { "unchecked" })
+public class FactorySet extends AbstractSet<Dictionary>
+{
+    /**
+     * The actual Service instance that is allocated for each dictionaries added in this Set.
+     */
+    private Object m_impl;
+
+    /**
+     * The Service provided in the OSGi registry.
+     */
+    private final String[] m_provide;
+
+    /**
+     * The properties to be provided by the Service.
+     */
+    private final Dictionary m_serviceProperties;
+
+    /**
+     * The configure Service callback used to pass configuration added in this Set.
+     */
+    private final String m_configure;
+
+    /**
+     * The map between our Dictionaries and corresponding Service instances.
+     */
+    private final ConcurrentHashMap<ServiceKey, Object> m_services = new ConcurrentHashMap<ServiceKey, Object>();
+
+    /**
+     * The list of Dependencies which are applied in the Service.
+     */
+    private MetaData m_srvMeta;
+
+    /**
+     * The list of Dependencies which are applied in the Service.
+     */
+    private List<MetaData> m_depsMeta;
+
+    /**
+     * The DependencyManager which is used to create Service instances.
+     */
+    private DependencyManager m_dm;
+
+    /**
+     * This class is used to serialize concurrent method calls, and allow to leave our methods unsynchronized.
+     * This is required because some of our methods may invoke some Service callbacks, which must be called outside 
+     * synchronized block (in order to avoid potential dead locks).
+     */
+    private SerialExecutor m_serialExecutor = new SerialExecutor();
+
+    /**
+     * Flag used to check if our service is Active.
+     */
+    private volatile boolean m_active;
+
+    /**
+     * The bundle containing the Service annotated with the factory attribute.
+     */
+    private Bundle m_bundle;
+
+    /**
+     * Flag used to check if a service is being created
+     */
+    private final static Object SERVICE_CREATING = new Object();
+
+    /**
+     * When a Dictionary is registered in a factory Set, we use this special 
+     * property key, whose value may provide the instance to use when
+     * creating a service.
+     */
+    private final static String DM_FACTORY_INSTANCE = "dm.factory.instance";
+
+    /**
+     * This class wraps <tt>Dictionary</tt>, allowing to store the dictionary into a Map, using
+     * reference-equality in place of object-equality when getting the Dictionary from the Map.
+     */
+    private static class ServiceKey
+    {
+        private Dictionary m_dictionary;
+
+        public ServiceKey(Dictionary dictionary)
+        {
+            m_dictionary = dictionary;
+        }
+
+        Dictionary getDictionary()
+        {
+            return m_dictionary;
+        }
+
+        @Override
+        public boolean equals(Object that)
+        {
+            return that instanceof ServiceKey ? (((ServiceKey) that).getDictionary() == m_dictionary)
+                : false;
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return System.identityHashCode(m_dictionary);
+        }
+
+        @Override
+        public String toString()
+        {
+            return Dictionary.class.getName() + "@" + System.identityHashCode(m_dictionary);
+        }
+    }
+
+    /**
+     * Sole constructor.
+     * @param b the bundle containing the Service annotated with the factory attribute
+     * @param impl The Service implementation class
+     * @param serviceProperties The Service properties
+     * @param provides The Services provided by this Service
+     * @param factoryConfigure The configure callback invoked in order to pass configurations added in this Set.
+     */
+    public FactorySet(Bundle b, MetaData srvMeta, List<MetaData> depsMeta)
+    {
+        m_serviceProperties = srvMeta.getDictionary(Params.properties, null);
+        m_provide = srvMeta.getStrings(Params.provides, null);
+        m_configure = srvMeta.getString(Params.factoryConfigure, null);
+        m_bundle = b;
+        m_srvMeta = srvMeta;
+        m_depsMeta = depsMeta;
+    }
+
+    /**
+     * Our Service is starting. 
+     */
+    public void start(Component c)
+    {
+        m_active = true;
+        m_dm = c.getDependencyManager();
+    }
+
+    /**
+     * Our Service is stopping: we have to remove all Service instances that we have created.
+     */
+    public void stop()
+    {
+        try
+        {
+            clear();
+        }
+        finally
+        {
+            m_active = false;
+        }
+    }
+
+    /**
+     * Create or Update a Service.
+     */
+    @Override
+    @SuppressWarnings("synthetic-access")
+    public boolean add(final Dictionary configuration)
+    {
+        // Check parameter validity
+        if (configuration == null)
+        {
+            throw new NullPointerException("configuration parameter can't be null");
+        }
+
+        // Check if our service is running.
+        checkServiceAvailable();
+
+        // Check if service is being created
+        ServiceKey serviceKey = new ServiceKey(configuration);
+        boolean creating = false;
+        synchronized (this)
+        {
+            if (!m_services.containsKey(serviceKey))
+            {
+                m_services.put(serviceKey, SERVICE_CREATING);
+                creating = true;
+            }
+            // Create or Update the Service.
+            m_serialExecutor.enqueue(new Runnable()
+            {
+                public void run()
+                {
+                    doAdd(configuration);
+                }
+            });
+        }
+
+        m_serialExecutor.execute();
+        return creating;
+    }
+
+    /**
+     * Another Service wants to remove an existing Service.
+     * This method is not synchronized but uses a SerialExecutor for serializing concurrent method call.
+     * (This avoid potential dead locks, especially when Service callback methods are invoked).
+     */
+    @Override
+    @SuppressWarnings("synthetic-access")
+    public boolean remove(final Object configuration)
+    {
+        // Check parameter validity.
+        if (configuration == null)
+        {
+            throw new NullPointerException("configuration parameter can't be null");
+        }
+        if (!(configuration instanceof Dictionary))
+        {
+            throw new IllegalArgumentException("configuration must be an instance of a Dictionary");
+        }
+
+        // Check if our service is active.
+        checkServiceAvailable();
+
+        // Check if service is created (or creating)
+        boolean found = m_services.containsKey(new ServiceKey((Dictionary) configuration));
+        if (found)
+        {
+            // Create or Update the Service.
+            m_serialExecutor.enqueue(new Runnable()
+            {
+                public void run()
+                {
+                    doRemove((Dictionary) configuration);
+                }
+            });
+            m_serialExecutor.execute();
+        }
+        return found;
+    }
+
+    /**
+     * Another Service wants to remove all existing Services.
+     * This method is not synchronized but uses a SerialExecutor for serializing concurrent method call.
+     * (This avoid potential dead locks, especially when Service callback methods are invoked).
+     */
+    @Override
+    @SuppressWarnings("synthetic-access")
+    public void clear()
+    {
+        if (!m_active)
+        {
+            return;
+        }
+
+        // Create or Update the Service.
+        m_serialExecutor.enqueue(new Runnable()
+        {
+            public void run()
+            {
+                doClear();
+            }
+        });
+        m_serialExecutor.execute();
+    }
+
+    /**
+     * returns the list of active Service instances configurations.
+     */
+    @Override
+    public Iterator<Dictionary> iterator()
+    {
+        throw new UnsupportedOperationException(
+            "iterator method is not supported by DependencyManager Set's service factories");
+    }
+
+    @Override
+    public String toString()
+    {
+        return FactorySet.class.getName() + "(" + m_services.size() + " active instances)";
+    }
+
+    /**
+     * Returns the number of active Service instances.
+     */
+    @Override
+    public int size()
+    {
+        if (!m_active)
+        {
+            return 0;
+        }
+        return m_services.size();
+    }
+
+    /**
+     * Checks if our Service is available (we are not stopped").
+     */
+    private void checkServiceAvailable()
+    {
+        if (!m_active)
+        {
+            throw new IllegalStateException("Service not available");
+        }
+    }
+
+    /**
+     * Add or create a new Service instance, given its configuration. This method is invoked by the
+     * SerialExecutor, hence it's thread safe and we'll invoke Service's callbacks without being
+     * synchronized (hence this will avoid potential dead locks).
+     */
+    private void doAdd(Dictionary configuration)
+    {
+        // Check if the service exists.
+        ServiceKey serviceKey = new ServiceKey(configuration);
+        Object service = m_services.get(serviceKey);
+        if (service == null || service == SERVICE_CREATING)
+        {
+            try
+            {
+                // Create the Service / impl, unless it is explicitly provided from the
+                // configuration (using the specific key "dm.factory.instance").
+                Component s = m_dm.createComponent();
+                Class implClass = m_bundle.loadClass(m_srvMeta.getString(Params.impl));
+                Object impl = configuration.get(DM_FACTORY_INSTANCE);
+                if (impl == null)
+                {
+                    String factoryMethod = m_srvMeta.getString(Params.factoryMethod, null);
+                    if (factoryMethod == null)
+                    {
+                        m_impl = implClass.newInstance();
+                    }
+                    else
+                    {
+                        Method m = implClass.getDeclaredMethod(factoryMethod);
+                        m.setAccessible(true);
+                        m_impl = m.invoke(null);
+                    }
+                }
+                else
+                {
+                    m_impl = impl;
+                }
+
+                // Invoke "configure" callback
+                if (m_configure != null)
+                {
+                    invokeConfigure(m_impl, m_configure, configuration);
+                }
+
+                // Create Service
+                s.setImplementation(m_impl);
+                if (m_provide != null)
+                {
+                     // Merge service properties with the configuration provided by the factory.
+                    Dictionary serviceProperties = mergeSettings(m_serviceProperties, configuration);
+                    s.setInterface(m_provide, serviceProperties);
+                }
+
+                s.setComposition(m_srvMeta.getString(Params.composition, null));
+                ServiceLifecycleHandler lfcleHandler = new ServiceLifecycleHandler(s, m_bundle, m_dm,
+                    m_srvMeta, m_depsMeta);
+                // The dependencies will be plugged by our lifecycle handler.
+                s.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
+
+                // Adds dependencies (except named dependencies, which are managed by the lifecycle handler).
+                for (MetaData dependency: m_depsMeta)
+                {
+                    String name = dependency.getString(Params.name, null);
+                    if (name == null)
+                    {
+                        DependencyBuilder depBuilder = new DependencyBuilder(dependency);
+                        Log.instance().info("ServiceLifecycleHandler.init: adding dependency %s into service %s",
+                                            dependency, m_srvMeta);
+                        Dependency d = depBuilder.build(m_bundle, m_dm);
+                        s.add(d);
+                    }
+                }
+
+                // Register the Service instance, and keep track of it.
+                Log.instance().info("ServiceFactory: created service %s", m_srvMeta);
+                m_dm.add(s);
+                m_services.put(serviceKey, s);
+            }
+            catch (Throwable t)
+            {
+                // Make sure the SERVICE_CREATING flag is also removed
+                m_services.remove(serviceKey);
+                Log.instance().error("ServiceFactory: could not instantiate service %s",
+                                     t, m_srvMeta);
+            }
+        }
+        else
+        {
+            // Reconfigure an already existing Service.
+            if (m_configure != null)
+            {
+                Log.instance().info("ServiceFactory: updating service %s", m_impl);
+                invokeConfigure(m_impl, m_configure, configuration);
+            }
+
+            // Update service properties
+            if (m_provide != null)
+            {
+                Dictionary settings = mergeSettings(m_serviceProperties, configuration);
+                ((Component) service).setServiceProperties(settings);
+            }
+        }
+    }
+
+    private void doRemove(Dictionary configuraton)
+    {
+        Log.instance().info("ServiceFactory: removing service %s", m_srvMeta);
+        ServiceKey serviceKey = new ServiceKey(configuraton);
+        Object service = m_services.remove(serviceKey);
+        if (service != null && service != SERVICE_CREATING)
+        {
+            m_dm.remove((Component) service);
+        }
+    }
+
+    private void doClear()
+    {
+        try
+        {
+            for (Object service: m_services.values())
+            {
+                if (service instanceof Component)
+                {
+                    m_dm.remove((Component) service);
+                }
+            }
+        }
+        finally
+        {
+            m_services.clear();
+        }
+    }
+
+    /**
+     * Merge factory configuration settings with the service properties. The private factory configuration 
+     * settings are ignored. A factory configuration property is private if its name starts with a dot (".").
+     * 
+     * @param serviceProperties
+     * @param factoryConfiguration
+     * @return
+     */
+    private Dictionary mergeSettings(Dictionary serviceProperties, Dictionary factoryConfiguration)
+    {
+        Dictionary props = new Hashtable();
+
+        if (serviceProperties != null)
+        {
+            Enumeration keys = serviceProperties.keys();
+            while (keys.hasMoreElements())
+            {
+                Object key = keys.nextElement();
+                Object val = serviceProperties.get(key);
+                props.put(key, val);
+            }
+        }
+
+        Enumeration keys = factoryConfiguration.keys();
+        while (keys.hasMoreElements())
+        {
+            Object key = keys.nextElement();
+            if (!key.toString().startsWith("."))
+            {
+                // public properties are propagated
+                Object val = factoryConfiguration.get(key);
+                props.put(key, val);
+            }
+        }
+        return props;
+    }
+
+    /**
+     * Invokes the configure callback method on the service instance implemenatation.
+     * @param impl
+     * @param configure
+     * @param config
+     */
+    private void invokeConfigure(Object impl, String configure, Dictionary config)
+    {
+        try
+        {
+            InvocationUtil.invokeCallbackMethod(impl, configure,
+                                                new Class[][] { { Dictionary.class } },
+                                                new Object[][] { { config } });
+        }
+
+        catch (Throwable t)
+        {
+            if (t instanceof RuntimeException)
+            {
+                throw (RuntimeException) t;
+            }
+            else
+            {
+                throw new RuntimeException("Could not invoke method " + configure
+                                           + " on object " + impl, t);
+            }
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/InvocationUtil.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/InvocationUtil.java?rev=1587056&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/InvocationUtil.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/InvocationUtil.java Sun Apr 13 17:26:09 2014
@@ -0,0 +1,111 @@
+/*
+ * 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 dm.runtime;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Java reflexion utility methods.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class InvocationUtil
+{
+    public static Object invokeCallbackMethod(Object instance,
+                                              String methodName,
+                                              Class<?>[][] signatures,
+                                              Object[][] parameters)
+        throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException,
+        InvocationTargetException
+    {
+        Class<?> currentClazz = instance.getClass();
+        while (currentClazz != null)
+        {
+            try
+            {
+                return invokeMethod(instance, currentClazz, methodName, signatures, parameters, false);
+            }
+            catch (NoSuchMethodException nsme)
+            {
+                // ignore
+            }
+            currentClazz = currentClazz.getSuperclass();
+        }
+        throw new NoSuchMethodException(methodName);
+    }
+
+    public static Object invokeMethod(Object object,
+                                      Class<?> clazz,
+                                      String name,
+                                      Class<?>[][] signatures,
+                                      Object[][] parameters,
+                                      boolean isSuper)
+        throws NoSuchMethodException, InvocationTargetException, IllegalArgumentException,
+        IllegalAccessException
+    {
+        if (object == null)
+        {
+            throw new IllegalArgumentException("Instance cannot be null");
+        }
+        if (clazz == null)
+        {
+            throw new IllegalArgumentException("Class cannot be null");
+        }
+
+        // If we're talking to a proxy here, dig one level deeper to expose the
+        // underlying invocation handler ...
+
+        if (Proxy.isProxyClass(clazz))
+        {
+            object = Proxy.getInvocationHandler(object);
+            clazz = object.getClass();
+        }
+
+        for (int i = 0; i < signatures.length; i++)
+        {
+            Class<?>[] signature = signatures[i];
+            try
+            {
+                final Method m = clazz.getDeclaredMethod(name, signature);
+                if (!(isSuper && Modifier.isPrivate(m.getModifiers())))
+                {
+                    AccessController.doPrivileged(new PrivilegedAction<Object>()
+                    {
+                        public Object run()
+                        {
+                            m.setAccessible(true);
+                            return null;
+                        }
+                    });
+                    return m.invoke(object, parameters[i]);
+                }
+            }
+            catch (NoSuchMethodException e)
+            {
+                // ignore this and keep looking
+            }
+        }
+        throw new NoSuchMethodException(name);
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/JSONMetaData.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/JSONMetaData.java?rev=1587056&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/JSONMetaData.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/JSONMetaData.java Sun Apr 13 17:26:09 2014
@@ -0,0 +1,481 @@
+/*
+ * 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 dm.runtime;
+
+import java.lang.reflect.Array;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Thsi class represents the parsed data found from meta-inf dependencymanager descriptors.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class JSONMetaData implements MetaData, Cloneable
+{
+    /**
+     * The parsed Dependency or Service metadata. The map value is either a String, a String[],
+     * or a Dictionary, whose values are String or String[]. 
+     */
+    private HashMap<String, Object> m_metadata = new HashMap<String, Object>();
+
+    /**
+     * Decodes Json metadata for either a Service or a Dependency descriptor entry.
+     * The JSON object has the following form:
+     * 
+     * entry            ::= String | String[] | dictionary
+     * dictionary       ::= key-value-pair*
+     * key-value-pair   ::= key value
+     * value            ::= String | String[] | value-type
+     * value-type       ::= jsonObject with value-type-info
+     * value-type-info  ::= "type"=primitive java type
+     *                      "value"=String|String[]
+     *                      
+     * Exemple:
+     * 
+     * {"string-param" : "string-value",
+     *  "string-array-param" : ["string1", "string2"],
+     *  "properties" : {
+     *      "string-param" : "string-value",
+     *      "string-array-param" : ["str1", "str2],
+     *      "long-param" : {"type":"java.lang.Long", "value":"1"}}
+     *      "long-array-param" : {"type":"java.lang.Long", "value":["1"]}}
+     *  }
+     * }
+     *   
+     * @param jso the JSON object that corresponds to a dependency manager descriptor entry line.
+     * @throws JSONException 
+     */
+    @SuppressWarnings("unchecked")
+    public JSONMetaData(JSONObject jso) throws JSONException
+    {
+        // Decode json object into our internal map.
+        Iterator<String> it = jso.keys();
+        while (it.hasNext())
+        {
+            String key = it.next();
+            Object value = jso.get(key);
+            if (value instanceof String)
+            {
+                m_metadata.put(key, value);
+            }
+            else if (value instanceof JSONArray)
+            {
+                m_metadata.put(key, decodeStringArray((JSONArray) value));
+            }
+            else if (value instanceof JSONObject)
+            {
+                m_metadata.put(key, parseProperties((JSONObject) value));
+            }
+        }
+    }
+
+    private Hashtable<String, Object> parseProperties(JSONObject properties) throws JSONException {
+        Hashtable<String, Object> parsedProps = new Hashtable<String, Object>();
+        Iterator<String> it = properties.keys();
+        while (it.hasNext())
+        {
+            String key = it.next();
+            Object value = properties.get(key);
+            if (value instanceof String)
+            {
+                // This property type is a simple string
+                parsedProps.put(key, value);
+            }
+            else if (value instanceof JSONArray)
+            {
+                // This property type is a simple string array
+                parsedProps.put(key, decodeStringArray((JSONArray) value));
+            }
+            else if (value instanceof JSONObject)
+            {
+                // This property type is a typed value, encoded as a JSONObject with two keys: "type"/"value"
+                JSONObject json = ((JSONObject) value);
+                String type = json.getString("type");
+                Object typeValue = json.get("value");
+
+                if (type == null)
+                {
+                    throw new JSONException("missing type attribute in json metadata for key " + key);
+                }
+                if (typeValue == null)
+                {
+                    throw new JSONException("missing type value attribute in json metadata for key " + key);
+                }
+
+                Class<?> typeClass;
+                try
+                {
+                    typeClass = Class.forName(type);
+                }
+                catch (ClassNotFoundException e)
+                {
+                    throw new JSONException("invalid type attribute (" + type + ") in json metadata for key "
+                        + key);
+                }
+
+                if (typeValue instanceof JSONArray)
+                {
+                    parsedProps.put(key, toPrimitiveTypeArray(typeClass, (JSONArray) typeValue));
+                }
+                else
+                {
+                    parsedProps.put(key, toPrimitiveType(typeClass, typeValue.toString()));
+                }
+            }
+        }
+        return parsedProps;
+    }
+
+    private Object toPrimitiveType(Class<?> type, String value) throws JSONException {
+        if (type.equals(String.class))
+        {
+            return value;
+        }
+        else if (type.equals(Long.class))
+        {
+            return Long.parseLong(value);
+        }
+        else if (type.equals(Double.class))
+        {
+            return Double.valueOf(value);
+        }
+        else if (type.equals(Float.class))
+        {
+            return Float.valueOf(value);
+        }
+        else if (type.equals(Integer.class))
+        {
+            return Integer.valueOf(value);
+        }
+        else if (type.equals(Byte.class))
+        {
+            return Byte.valueOf(value);
+        }
+        else if (type.equals(Character.class))
+        {
+            return Character.valueOf((char) Integer.parseInt(value));
+        }
+        else if (type.equals(Boolean.class))
+        {
+            return Boolean.valueOf(value);
+        }
+        else if (type.equals(Short.class))
+        {
+            return Short.valueOf(value);
+        }
+        else
+        {
+            throw new JSONException("invalid type (" + type + ") attribute in json metadata");
+        }
+    }
+
+    private Object toPrimitiveTypeArray(Class<?> type, JSONArray array) throws JSONException {
+        int len = array.length();
+        Object result = Array.newInstance(type, len);
+
+        if (type.equals(String.class))
+        {
+            for (int i = 0; i < len; i ++) {
+                Array.set(result, i, array.getString(i));
+            }
+        } 
+        else if (type.equals(Long.class))
+        {
+            for (int i = 0; i < len; i ++) {
+                Array.set(result, i, Long.valueOf(array.getString(i)));
+            }
+        }
+        else if (type.equals(Double.class))
+        {
+            for (int i = 0; i < len; i ++) {
+                Array.set(result, i, Double.valueOf(array.getString(i)));
+            }
+        } 
+        else if (type.equals(Float.class))
+        {
+            for (int i = 0; i < len; i ++) {
+                Array.set(result, i, Float.valueOf(array.getString(i)));
+            }
+        }
+        else if (type.equals(Integer.class))
+        {
+            for (int i = 0; i < len; i ++) {
+                Array.set(result, i, Integer.valueOf(array.getString(i)));
+            }
+        }
+        else if (type.equals(Byte.class))
+        {
+            for (int i = 0; i < len; i ++) {
+                Array.set(result, i, Byte.valueOf(array.getString(i)));
+            }
+        }
+        else if (type.equals(Character.class))
+        {
+            for (int i = 0; i < len; i ++) {
+                Array.set(result, i,  Character.valueOf((char) Integer.parseInt(array.getString(i))));
+            }
+        }
+        else if (type.equals(Boolean.class))
+        {
+            for (int i = 0; i < len; i ++) {
+                Array.set(result, i, Boolean.valueOf(array.getString(i)));
+            }
+        } 
+        else if (type.equals(Short.class))
+        {
+            for (int i = 0; i < len; i ++) {
+                Array.set(result, i, Short.valueOf(array.getString(i)));
+            }
+        }
+        else 
+        {
+            throw new JSONException("invalid type (" + type + ") attribute in json metadata");
+        }   
+        return result;
+    }
+
+    /**
+     * Close this class instance to another one.
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public Object clone() throws CloneNotSupportedException
+    {
+        JSONMetaData clone = (JSONMetaData) super.clone();
+        clone.m_metadata = (HashMap<String, Object>) m_metadata.clone();
+        return clone;
+    }
+
+    public String getString(Params key)
+    {
+        Object value = m_metadata.get(key.toString());
+        if (value == null)
+        {
+            throw new IllegalArgumentException("Parameter " + key + " not found");
+        }
+        return value.toString();
+    }
+
+    public String getString(Params key, String def)
+    {
+        try
+        {
+            return getString(key);
+        }
+        catch (IllegalArgumentException e)
+        {
+            return def;
+        }
+    }
+
+    public int getInt(Params key)
+    {
+        Object value = m_metadata.get(key.toString());
+        if (value != null)
+        {
+            try
+            {
+                if (value instanceof Integer) {
+                    return ((Integer) value).intValue();
+                }
+                return Integer.parseInt(value.toString());
+            }
+            catch (NumberFormatException e)
+            {
+                throw new IllegalArgumentException("parameter " + key
+                    + " is not an int value: "
+                    + value);
+            }
+        }
+        else
+        {
+            throw new IllegalArgumentException("missing " + key
+                + " parameter from annotation");
+        }
+    }
+
+    public int getInt(Params key, int def)
+    {
+        Object value = m_metadata.get(key.toString());
+        if (value != null)
+        {
+            try
+            {
+                if (value instanceof Integer) {
+                    return ((Integer) value).intValue();
+                }
+                return Integer.parseInt(value.toString());
+            }
+            catch (NumberFormatException e)
+            {
+                throw new IllegalArgumentException("parameter " + key
+                    + " is not an int value: "
+                    + value);
+            }
+        }
+        else
+        {
+            return def;
+        }
+    }
+
+    public long getLong(Params key)
+    {
+        Object value = m_metadata.get(key.toString());
+        if (value != null)
+        {
+            try
+            {
+                if (value instanceof Long) {
+                    return ((Long) value).longValue();
+                }
+                return Long.parseLong(value.toString());
+            }
+            catch (NumberFormatException e)
+            {
+                throw new IllegalArgumentException("parameter " + key
+                    + " is not a long value: "
+                    + value);
+            }
+        }
+        else
+        {
+            throw new IllegalArgumentException("missing " + key
+                + " parameter from annotation");
+        }
+    }
+
+    public long getLong(Params key, long def)
+    {
+        Object value = m_metadata.get(key.toString());
+        if (value != null)
+        {
+            try
+            {
+                if (value instanceof Long) {
+                    return (Long) value;
+                }
+                return Long.parseLong(value.toString());
+            }
+            catch (NumberFormatException e)
+            {
+                throw new IllegalArgumentException("parameter " + key
+                    + " is not a long value: "
+                    + value);
+            }
+        }
+        else
+        {
+            return def;
+        }
+    }
+
+    public String[] getStrings(Params key)
+    {
+        Object array = m_metadata.get(key.toString());
+        if (array == null)
+        {
+            throw new IllegalArgumentException("Parameter " + key + " not found");
+        }
+
+        if (!(array instanceof String[]))
+        {
+            throw new IllegalArgumentException("Parameter " + key + " is not a String[] (" + array.getClass()
+                + ")");
+        }
+        return (String[]) array;
+    }
+
+    public String[] getStrings(Params key, String[] def)
+    {
+        try
+        {
+            return getStrings(key);
+        }
+        catch (IllegalArgumentException t)
+        {
+            return def;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public Dictionary<String, Object> getDictionary(Params key,
+        Dictionary<String, Object> def)
+    {
+        Object dictionary = m_metadata.get(key.toString());
+        if (dictionary == null)
+        {
+            return def;
+        }
+
+        if (!(dictionary instanceof Dictionary<?, ?>))
+        {
+            throw new IllegalArgumentException("Parameter " + key + " is not a Dictionary ("
+                + dictionary.getClass() + ")");
+        }
+
+        return (Dictionary<String, Object>) dictionary;
+    }
+
+    @Override
+    public String toString()
+    {
+        return m_metadata.toString();
+    }
+
+    public void setDictionary(Params key, Dictionary<String, Object> dictionary)
+    {
+        m_metadata.put(key.toString(), dictionary);
+    }
+
+    public void setString(Params key, String value)
+    {
+        m_metadata.put(key.toString(), value);
+    }
+
+    public void setStrings(Params key, String[] values)
+    {
+        m_metadata.put(key.toString(), values);
+    }
+    
+    /**
+     * Decodes a JSONArray into a String array (all JSON array values are supposed to be strings).
+     */
+    private String[] decodeStringArray(JSONArray array) throws JSONException
+    {
+        String[] arr = new String[array.length()];
+        for (int i = 0; i < array.length(); i++)
+        {
+            Object value = array.get(i);
+            if (!(value instanceof String))
+            {
+                throw new IllegalArgumentException("JSON array is not an array of Strings: " + array);
+            }
+            arr[i] = value.toString();
+        }
+        return arr;
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/Log.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/Log.java?rev=1587056&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/Log.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/Log.java Sun Apr 13 17:26:09 2014
@@ -0,0 +1,80 @@
+/*
+ * 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 dm.runtime;
+
+import org.osgi.service.log.LogService;
+
+/**
+ * This class logs some formattable strings into the OSGi Log Service.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Log
+{
+    /** The wrap log service which is actually used (Injected by Activator) */
+    private volatile LogService m_logService;
+    
+    /** Our sole instance */
+    private static Log m_instance = new Log();
+    
+    public static Log instance()
+    {
+        return m_instance;
+    }
+    
+    public void error(String format, Object ... args) 
+    {
+        m_logService.log(LogService.LOG_ERROR, String.format(format, args));
+    }
+    
+    public void error(String format, Throwable t, Object ... args) 
+    {
+        m_logService.log(LogService.LOG_ERROR, String.format(format, args), t);
+    }
+    
+    public void warn(String format, Object ... args) 
+    {
+        m_logService.log(LogService.LOG_WARNING, String.format(format, args));
+    }
+    
+    public void warn(String format, Throwable t, Object ... args) 
+    {
+        m_logService.log(LogService.LOG_WARNING, String.format(format, args), t);
+    }
+    
+    public void info(String format, Object ... args) 
+    {
+        m_logService.log(LogService.LOG_INFO, String.format(format, args));
+    }
+    
+    public void info(String format, Throwable t, Object ... args) 
+    {
+        m_logService.log(LogService.LOG_INFO, String.format(format, args), t);
+    }
+    
+    public void debug(String format, Object ... args) 
+    {
+        m_logService.log(LogService.LOG_DEBUG, String.format(format, args));
+    }
+    
+    public void debug(String format, Throwable t, Object ... args) 
+    {
+        m_logService.log(LogService.LOG_DEBUG, String.format(format, args), t);
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/MetaData.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/MetaData.java?rev=1587056&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/MetaData.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/MetaData.java Sun Apr 13 17:26:09 2014
@@ -0,0 +1,94 @@
+/*
+ * 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 dm.runtime;
+
+import java.util.Dictionary;
+
+/**
+ * This class represents the meta data parsed from a descriptor entry (json) line.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface MetaData extends Cloneable
+{
+    /**
+     * Returns a String descriptor entry parameter value.
+     */
+    String getString(Params key);
+
+    /**
+     * Returns a String descriptor entry parameter value.
+     */
+    String getString(Params key, String def);
+
+    /**
+     * Returns a String descriptor entry parameter value.
+     */
+    int getInt(Params key);
+
+    /**
+     * Returns a String descriptor entry parameter value.
+     */
+    int getInt(Params key, int def);
+    
+    /**
+     * Returns a String descriptor entry parameter value.
+     */
+    long getLong(Params key);
+
+    /**
+     * Returns a String descriptor entry parameter value.
+     */
+    long getLong(Params key, long def);
+
+    /**
+     * Returns a String array descriptor entry parameter value.
+     */
+    String[] getStrings(Params key);
+
+    /**
+     * Returns a String array descriptor entry parameter value.
+     */
+    String[] getStrings(Params key, String[] def);
+
+    /**
+     * Returns a descriptor entry value which is a complex value.
+     */
+    Dictionary<String, Object> getDictionary(Params key, Dictionary<String, Object> def);
+    
+    /**
+     * Modifies a key Sring value
+     */
+    void setString(Params key, String value);
+    
+    /**
+     * Modifies a String[] value.
+     */
+    void setStrings(Params key, String[] values);
+    
+    /**
+     * Modifies a String[] value.
+     */
+    void setDictionary(Params key, Dictionary<String, Object> dictionary);
+    
+    /**
+     * Clone this MetaData object.
+     */
+    Object clone() throws CloneNotSupportedException;
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/Params.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/Params.java?rev=1587056&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/Params.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/Params.java Sun Apr 13 17:26:09 2014
@@ -0,0 +1,67 @@
+/*
+ * 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 dm.runtime;
+
+/**
+ * List of descriptor parameters.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public enum Params
+{
+    type,
+    init,
+    start,
+    stop,
+    destroy,
+    impl,
+    provides,
+    properties,
+    composition,
+    service,
+    filter,
+    defaultImpl,
+    required,
+    added,
+    changed,
+    removed,
+    swap,
+    autoConfig,
+    pid,
+    propagate,
+    updated,
+    timeout,
+    adapteeService,
+    adapteeFilter,
+    stateMask,
+    ranking,
+    factoryPid,    
+    factorySet,
+    factoryConfigure,
+    factoryMethod,
+    name,
+    field,
+    starter,
+    stopper, 
+    bundleContextField, 
+    dependencyManagerField, 
+    componentField,
+    registered, 
+    unregistered
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/ResourceAdapterServiceBuilder.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/ResourceAdapterServiceBuilder.java?rev=1587056&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/ResourceAdapterServiceBuilder.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/ResourceAdapterServiceBuilder.java Sun Apr 13 17:26:09 2014
@@ -0,0 +1,75 @@
+/*
+ * 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 dm.runtime;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import org.osgi.framework.Bundle;
+
+import dm.Component;
+import dm.DependencyManager;
+
+/**
+ * Class used to build a resource adapter service using metadata found from DependencyManager runtime
+ * meta-inf descriptor.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResourceAdapterServiceBuilder extends AbstractBuilder
+{
+    private final static String TYPE = "ResourceAdapterService";
+
+    @Override
+    public String getType()
+    {
+        return TYPE;
+    }
+
+    @Override
+    public void build(MetaData srvMeta, List<MetaData> depsMeta, Bundle b, DependencyManager dm) 
+        throws Exception
+    {
+        String filter = srvMeta.getString(Params.filter, null);
+        Class<?> implClass = b.loadClass(srvMeta.getString(Params.impl));
+        String[] provides = srvMeta.getStrings(Params.provides, null);
+        Dictionary<String, Object> properties = srvMeta.getDictionary(Params.properties, null);
+        boolean propagate = "true".equals(srvMeta.getString(Params.propagate, "false"));
+        String changed = srvMeta.getString(Params.changed, null /* no change callback if not specified explicitly */);
+        Component c = dm.createResourceAdapterService(filter, propagate, null, changed);
+        c.setInterface(provides, properties);
+        String factoryMethod = srvMeta.getString(Params.factoryMethod, null);
+        if (factoryMethod == null)
+        {
+            c.setImplementation(implClass);
+        } 
+        else
+        {
+            c.setFactory(implClass, factoryMethod);
+        }
+        setCommonServiceParams(c, srvMeta);
+        c.setComposition(srvMeta.getString(Params.composition, null));
+        ServiceLifecycleHandler lfcleHandler = new ServiceLifecycleHandler(c, b, dm, srvMeta, depsMeta);
+        // The dependencies will be plugged by our lifecycle handler.
+        c.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
+        // Adds dependencies (except named dependencies, which are managed by the lifecycle handler).
+        addUnamedDependencies(b, dm, c, srvMeta, depsMeta);
+        dm.add(c);
+    }    
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/SerialExecutor.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/SerialExecutor.java?rev=1587056&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/SerialExecutor.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/SerialExecutor.java Sun Apr 13 17:26:09 2014
@@ -0,0 +1,92 @@
+/*
+ * 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 dm.runtime;
+
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+
+/**
+ * Allows you to enqueue tasks from multiple threads and then execute
+ * them on one thread sequentially. It assumes more than one thread will
+ * try to execute the tasks and it will make an effort to pick the first
+ * task that comes along whilst making sure subsequent tasks return
+ * without waiting.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public final class SerialExecutor {
+	private static final Runnable DUMMY_RUNNABLE = new Runnable() { public void run() {}; };
+    private final LinkedList m_workQueue = new LinkedList();
+    private Runnable m_active;
+    
+    /**
+     * Enqueue a new task for later execution. This method is
+     * thread-safe, so multiple threads can contribute tasks.
+     * 
+     * @param runnable the runnable containing the actual task
+     */
+    public synchronized void enqueue(final Runnable runnable) {
+    	m_workQueue.addLast(new Runnable() {
+			public void run() {
+				try {
+					runnable.run();
+				}
+				finally {
+					scheduleNext();
+				}
+			}
+		});
+    }
+    
+    /**
+     * Execute any pending tasks. This method is thread safe,
+     * so multiple threads can try to execute the pending
+     * tasks, but only the first will be used to actually do
+     * so. Other threads will return immediately.
+     */
+    public void execute() {
+    	Runnable active;
+    	synchronized (this) {
+    		active = m_active;
+    		// for now just put some non-null value in there so we can never
+    		// get a race condition when two threads enter this section after
+    		// one another (causing sheduleNext() to be invoked twice below)
+    		m_active = DUMMY_RUNNABLE;
+    	}
+    	if (active == null) {
+    		scheduleNext();
+    	}
+    }
+
+    private void scheduleNext() {
+    	Runnable active;
+    	synchronized (this) {
+    		try {
+    			m_active = (Runnable) m_workQueue.removeFirst();
+    		}
+    		catch (NoSuchElementException e) {
+    			m_active = null;
+    		}
+    		active = m_active;
+    	}
+    	if (active != null) {
+            active.run();
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/ServiceLifecycleHandler.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/ServiceLifecycleHandler.java?rev=1587056&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/ServiceLifecycleHandler.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/ServiceLifecycleHandler.java Sun Apr 13 17:26:09 2014
@@ -0,0 +1,452 @@
+/*
+ * 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 dm.runtime;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.osgi.framework.Bundle;
+
+import dm.Component;
+import dm.Dependency;
+import dm.DependencyManager;
+
+/**
+ * Allow Services to configure dynamically their dependency filters from their init() method.
+ * Basically, this class acts as a service implementation lifecycle handler. When we detect that the Service is
+ * called in its init() method, and if init() returns a Map, then the Map is assumed to contain
+ * dependency filters, which will be applied to all named dependencies. The Map optionally returned by
+ * Service's init method may contain the following keys:
+ * <ul>
+ *   <li>name.filter: the value must be a valid OSGi filter, and the "name" prefix must match a ServiceDependency 
+ *   name attribute</li>
+ *   <li>name.required: the value is a boolean ("true"|"false") and the "name" prefix must match a 
+ *   ServiceDependency name attribute</li>
+ * </ul>
+ * 
+ * <p>Dependencies which provide a name attribute will be activated after the init method returns. Other
+ * dependencies are injected before the init method.
+ * 
+ * <p>Example of a Service whose dependency filter is configured from ConfigAdmin:
+ * 
+ * <blockquote><pre>
+ *  &#47;**
+ *    * A Service whose service dependency filter/require attribute may be configured from ConfigAdmin
+ *    *&#47;
+ *  &#64;Service
+ *  class X {
+ *      private Dictionary m_config;
+ *      
+ *      &#64;ConfigurationDependency(pid="MyPid")
+ *      void configure(Dictionary conf) {
+ *           // Initialize our service from config ...
+ *           
+ *           // And store the config for later usage (from our init method)
+ *           m_config = config;
+ *      }
+ *      
+ *      &#64;ServiceDependency(name="dependency1") 
+ *      void bindOtherService(OtherService other) {
+ *         // the filter and required flag will be configured from our init method.
+ *      }
+ *
+ *      // The returned Map will be used to configure our "dependency1" Dependency.
+ *      &#64;Init
+ *      Map init() {
+ *          return new HashMap() {{
+ *              put("dependency1.filter", m_config.get("filter"));
+ *              put("dependency1.required", m_config.get("required"));
+ *          }};
+ *      } 
+ *  }
+ *  </pre></blockquote>
+ *  
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceLifecycleHandler
+{
+    private final String m_init;
+    private final String m_start;
+    private final String m_stop;
+    private final String m_destroy;
+    private final MetaData m_srvMeta;
+    private final List<MetaData> m_depsMeta;
+    private final List<Dependency> m_namedDeps = new ArrayList<Dependency>();
+    private final Bundle m_bundle;
+    private volatile ToggleServiceDependency m_toggle;
+    private final static Object SYNC = new Object();
+
+    /**
+     * Makes a new ServiceLifecycleHandler object. This objects allows to decorate the "init" service callback, in
+     * order to see if "init" callback returns a dependency customization map.
+     * 
+     * @param srv The Service for the annotated class
+     * @param srvBundle the Service bundle
+     * @param dm The DependencyManager that was used to create the service
+     * @param srvMeta The Service MetaData
+     * @param depMeta The Dependencies MetaData
+     */
+    public ServiceLifecycleHandler(Component srv, Bundle srvBundle, DependencyManager dm,
+                                   MetaData srvMeta, List<MetaData> depMeta)
+    {
+        m_srvMeta = srvMeta;
+        m_init = srvMeta.getString(Params.init, null);
+        m_start = srvMeta.getString(Params.start, null);
+        m_stop = srvMeta.getString(Params.stop, null);
+        m_destroy = srvMeta.getString(Params.destroy, null);
+        m_bundle = srvBundle;
+        m_depsMeta = depMeta;
+    }
+
+    /**
+     * Handles an "init" lifecycle service callback. We just catch the "init" method, and callback 
+     * the actual Service' init method, to see if a dependency customization map is returned.
+     * We also check if a Lifecycle Controller is used. In this case, we add a hidden custom dependency,
+     * allowing to take control of when the component is actually started/stopped.
+     * We also handle an edge case described in FELIX-4050, where component state calculation 
+     * may mess up if some dependencies are added using the API from the init method.
+     * 
+     * @param c The Annotated Component
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public void init(Component c)
+        throws Exception
+    {
+        Object serviceInstance = c.getService();
+        DependencyManager dm = c.getDependencyManager();
+
+        // Check if a lifecycle controller is defined for this service. If true, then 
+        // We'll use the ToggleServiceDependency in order to manually activate/deactivate 
+        // the component ...
+        String starter = m_srvMeta.getString(Params.starter, null);
+        String stopper = m_srvMeta.getString(Params.stopper, null);
+
+        if (starter != null)
+        {
+            // We'll inject two runnables: one that will start or service, when invoked, and the other
+            // that will stop our service, when invoked. We'll use a shared atomic boolean in order to
+            // synchronize both runnables.
+            Log.instance().debug("Setting up a lifecycle controller for service %s", serviceInstance);
+            String componentName = serviceInstance.getClass().getName();
+            // Create a toggle service, used to start/stop our service.
+            m_toggle = new ToggleServiceDependency();
+            AtomicBoolean startFlag = new AtomicBoolean(false);
+            // Add the toggle to the service (we'll remove it from our destroy emthod).
+            c.add(m_toggle);
+            // Inject the runnable that will start our service, when invoked.
+            setField(serviceInstance, starter, Runnable.class, new ComponentStarter(componentName, m_toggle, startFlag));
+            if (stopper != null) {
+                // Inject the runnable that will stop our service, when invoked.
+                setField(serviceInstance, stopper, Runnable.class, new ComponentStopper(componentName, m_toggle, startFlag));
+            }
+        }
+
+        // Before invoking an optional init method, we have to handle an edge case (FELIX-4050), where
+        // init may add dependencies using the API and also return a map for configuring some
+        // named dependencies. We have to add a hidden toggle dependency in the component, which we'll 
+        // active *after* the init method is called, and possibly *after* named dependencies are configured.
+        
+        ToggleServiceDependency initToggle = null;
+        if (m_init != null) 
+        {
+            initToggle = new ToggleServiceDependency();
+            c.add(initToggle); 
+        }
+        
+        // Invoke component and all composites init methods, and for each one, check if a dependency
+        // customization map is returned by the method. This map will be used to configure 
+        // some dependency filters (or required flag).
+
+        Map<String, String> customization = new HashMap<String, String>();
+        Object[] composites = c.getInstances();
+        for (Object composite: composites)
+        {
+            Object o = invokeMethod(composite, m_init, dm, c);
+            if (o != null && Map.class.isAssignableFrom(o.getClass()))
+            {
+                customization.putAll((Map) o);
+            }
+        }
+
+        Log.instance().debug("ServiceLifecycleHandler.init: invoked init method from service %s " +
+                             ", returned map: %s", serviceInstance, customization);
+        
+        // Apply name dependency filters possibly returned by the init() method.
+        
+        for (MetaData dependency: m_depsMeta)
+        {
+            // Check if this dependency has a name, and if we find the name from the 
+            // customization map, then apply filters and required flag from the map into it.
+
+            String name = dependency.getString(Params.name, null);
+            if (name != null)
+            {
+                String filter = customization.get(name + ".filter");
+                String required = customization.get(name + ".required");
+
+                if (filter != null || required != null)
+                {
+                    dependency = (MetaData) dependency.clone();
+                    if (filter != null)
+                    {
+                        dependency.setString(Params.filter, filter);
+                    }
+                    if (required != null)
+                    {
+                        dependency.setString(Params.required, required);
+                    }
+                }
+
+                DependencyBuilder depBuilder = new DependencyBuilder(dependency);
+                Log.instance().info("ServiceLifecycleHandler.init: adding dependency %s into service %s",
+                                   dependency, m_srvMeta);
+                Dependency d = depBuilder.build(m_bundle, dm);
+                m_namedDeps.add(d);
+            }            
+        }
+        
+        // Add all extra dependencies in one shot, in order to calculate state changes for all dependencies at a time.
+        if (m_namedDeps.size() > 0) 
+        {
+            Log.instance().info("ServiceLifecycleHandler.init: adding extra/named dependencies %s",
+                                m_namedDeps);
+            c.add(m_namedDeps.toArray(new Dependency[m_namedDeps.size()]));
+        }
+        
+        // init method fully handled, and all possible named dependencies have been configured. Now, activate the 
+        // hidden toggle, and then remove it from the component, because we don't need it anymore.
+        if (initToggle != null) 
+        {
+            initToggle.activate(true);
+            //c.remove(initToggle);
+        }
+    }
+
+    /**
+     * Handles the Service's start lifecycle callback. We just invoke the service "start" service callback on 
+     * the service instance, as well as on all eventual service composites.
+     * We take care to check if a start callback returns a Map, which is meant to contain
+     * some additional properties which must be appended to existing service properties.
+     * Such extra properties takes precedence over existing service properties.
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public void start(Component service)
+        throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+    {
+        // Check if some extra service properties are returned by start method.
+        
+        DependencyManager dm = service.getDependencyManager();
+        Map<String, String> extraProperties = new HashMap<String, String>();
+        Object[] composites = service.getInstances();
+        for (Object composite: composites)
+        {
+            Object o = invokeMethod(composite, m_start, dm, service);
+            if (o != null && Map.class.isAssignableFrom(o.getClass()))
+            {
+                extraProperties.putAll((Map) o);
+            }
+        }
+
+        if (extraProperties.size() > 0)
+        {
+            // Store extra properties returned by start callbacks into existing service properties
+            Dictionary existingProperties = service.getServiceProperties();
+            if (existingProperties != null)
+            {
+                Hashtable props = new Hashtable();
+                Enumeration e = existingProperties.keys();
+                while (e.hasMoreElements())
+                {
+                    Object key = e.nextElement();
+                    props.put(key, existingProperties.get(key));
+                }
+                props.putAll(extraProperties);
+                service.setServiceProperties(props);
+            }
+            else
+            {
+                service.setServiceProperties(new Hashtable(extraProperties));
+            }
+        }        
+    }
+
+    /**
+     * Handles the Service's stop lifecycle callback. We just invoke the service "stop" callback on 
+     * the service instance, as well as on all eventual service composites.
+     */
+    public void stop(Component service)
+        throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+    {
+        callbackComposites(service, m_stop);
+    }
+
+    /**
+     * Handles the Service's destroy lifecycle callback. We just invoke the service "destroy" callback on 
+     * the service instance, as well as on all eventual service composites.
+     */
+    public void destroy(Component service)
+        throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+    {
+        // Clear named dependencies eventuall returned by our service init callback. 
+        m_namedDeps.clear();
+        if (m_toggle != null)
+        {
+            // If we created a toggle for our service, just remove it from the service.
+            service.remove(m_toggle);
+            m_toggle = null;
+        }
+        callbackComposites(service, m_destroy);
+    }
+
+    /**
+     * Invoke a callback on all Service compositions.
+     */
+    private void callbackComposites(Component service, String callback)
+        throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+    {
+        Object[] composites = service.getInstances();
+        for (Object composite: composites)
+        {
+            invokeMethod(composite, callback, service.getDependencyManager(), service);
+        }
+    }
+
+    /**
+     * Invoke a callback on an Object instance.
+     */
+    private Object invokeMethod(Object serviceInstance, String method, DependencyManager dm, Component c)
+        throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+    {
+        if (method != null)
+        {
+            try
+            {
+                return InvocationUtil.invokeCallbackMethod(serviceInstance, method,
+                                                           new Class[][] { { Component.class }, {} },
+                                                           new Object[][] { { c }, {} }
+                    );
+            }
+
+            catch (NoSuchMethodException e)
+            {
+                // ignore this
+            }
+
+            // Other exception will be thrown up to the ServiceImpl.invokeCallbackMethod(), which is 
+            // currently invoking our method. So, no need to log something here, since the invokeCallbackMethod 
+            // method is already logging any thrown exception.
+        }
+        return null;
+    }
+
+    /**
+     * Sets a field of an object by reflexion.
+     */
+    private void setField(Object instance, String fieldName, Class<?> fieldClass, Object fieldValue)
+    {
+        Object serviceInstance = instance;
+        Class<?> serviceClazz = serviceInstance.getClass();
+        if (Proxy.isProxyClass(serviceClazz))
+        {
+            serviceInstance = Proxy.getInvocationHandler(serviceInstance);
+            serviceClazz = serviceInstance.getClass();
+        }
+        while (serviceClazz != null)
+        {
+            Field[] fields = serviceClazz.getDeclaredFields();
+            for (int j = 0; j < fields.length; j++)
+            {
+                Field field = fields[j];
+                Class<?> type = field.getType();
+                if (field.getName().equals(fieldName) && type.isAssignableFrom(fieldClass))
+                {
+                    try
+                    {
+                        field.setAccessible(true);
+                        // synchronized makes sure the field is actually written to immediately
+                        synchronized (SYNC)
+                        {
+                            field.set(serviceInstance, fieldValue);
+                        }
+                    }
+                    catch (Throwable e)
+                    {
+                        throw new RuntimeException("Could not set field " + field, e);
+                    }
+                }
+            }
+            serviceClazz = serviceClazz.getSuperclass();
+        }
+    }
+    
+    private static class ComponentStarter implements Runnable {
+        private final String m_componentName;
+        private final ToggleServiceDependency m_toggle;
+        private final AtomicBoolean m_startFlag;
+
+        public ComponentStarter(String name, ToggleServiceDependency toggle, AtomicBoolean startFlag)
+        {
+            m_componentName = name;
+            m_toggle = toggle;
+            m_startFlag = startFlag;
+        }
+
+        @SuppressWarnings("synthetic-access")
+        public void run()
+        {
+            if (m_startFlag.compareAndSet(false, true)) {
+                Log.instance().debug("Lifecycle controller is activating the component %s",
+                                     m_componentName);
+                m_toggle.activate(true);
+            }
+        }
+    }
+    
+    private static class ComponentStopper implements Runnable {
+        private final Object m_componentName;
+        private final ToggleServiceDependency m_toggle;
+        private final AtomicBoolean m_startFlag;
+
+        public ComponentStopper(String componentName, ToggleServiceDependency toggle, AtomicBoolean startFlag)
+        {
+            m_componentName = componentName;
+            m_toggle = toggle;
+            m_startFlag = startFlag;
+        }
+
+        @SuppressWarnings("synthetic-access")
+        public void run()
+        {
+            if (m_startFlag.compareAndSet(true, false)) {
+                Log.instance().debug("Lifecycle controller is deactivating the component %s",
+                                    m_componentName);
+                m_toggle.activate(false);
+            }
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/ToggleServiceDependency.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/ToggleServiceDependency.java?rev=1587056&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/ToggleServiceDependency.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/src/dm/runtime/ToggleServiceDependency.java Sun Apr 13 17:26:09 2014
@@ -0,0 +1,228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package dm.runtime;
+
+import java.util.Dictionary;
+
+import org.osgi.framework.BundleContext;
+
+import dm.ComponentDependencyDeclaration;
+import dm.Dependency;
+import dm.context.ComponentContext;
+import dm.context.DependencyContext;
+import dm.context.Event;
+
+/**
+ * This is a custom DependencyManager Dependency, allowing to take control of
+ * when the dependency is available or not. It's used in the context of the
+ * LifecycleController class, in order to activate/deactivate a Component on
+ * demand.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ToggleServiceDependency implements Dependency, ComponentDependencyDeclaration, DependencyContext {
+    private volatile boolean m_isAvailable;
+    protected volatile ComponentContext m_component;
+    protected boolean m_instanceBound;
+    private volatile boolean m_isStarted; // volatile because accessed by getState method
+    
+    public static class ToggleEvent implements Event {
+        @Override
+        public boolean equals(Object e) {
+            if (e instanceof ToggleEvent) {
+                return true;
+            }
+            return false;
+        }
+        
+        @Override
+        public int hashCode() {
+            return ToggleEvent.class.hashCode();
+        }
+        
+        @Override
+        public int compareTo(Object o) {
+            return 0;
+        }
+
+        @Override
+        public void close(BundleContext context) {
+        }
+    }
+
+    public ToggleServiceDependency() {
+    }
+
+    public ToggleServiceDependency(ToggleServiceDependency other) {
+        m_component = other.m_component;
+        m_instanceBound = other.m_instanceBound;
+        m_isAvailable = other.m_isAvailable;
+        m_isStarted = other.m_isStarted;
+    }
+
+    public void activate(boolean active) {
+        if (active) {
+            add(new ToggleEvent());
+        } else {
+            remove(new ToggleEvent());
+        }
+    }
+    
+    @Override
+    public void setAvailable(boolean available) {
+        m_isAvailable = available;
+    }
+
+    @Override
+    public void invokeAdd(Event e) {
+    }
+
+    @Override
+    public void invokeChange(Event e) {
+    }
+
+    @Override
+    public void invokeRemove(Event e) {
+    }
+
+    @Override
+    public void add(final Event e) {
+        m_component.getExecutor().execute(new Runnable() {
+            @Override
+            public void run() {
+                m_component.handleAdded(ToggleServiceDependency.this, e);
+            }
+        });
+    }
+
+    @Override
+    public void change(Event e) {
+    }
+
+    @Override
+    public void remove(final Event e) {
+        m_component.getExecutor().execute(new Runnable() {
+            @Override
+            public void run() {
+                m_component.handleRemoved(ToggleServiceDependency.this, e);
+            }
+        });
+    }
+
+    @Override
+    public void add(ComponentContext component) {
+        m_component = component;
+    }
+
+    @Override
+    public void remove(ComponentContext component) {
+        m_component = null;
+    }
+
+    @Override
+    public void start() {
+        m_isStarted = true;
+    }
+
+    @Override
+    public void stop() {
+        m_isStarted = false;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return m_isAvailable;
+    }
+
+    @Override
+    public boolean isRequired() {
+        return true;
+    }
+
+    @Override
+    public boolean isInstanceBound() {
+        return m_instanceBound;
+    }
+
+    @Override
+    public void setInstanceBound(boolean instanceBound) {
+        m_instanceBound = instanceBound;
+    }
+
+    @Override
+    public boolean needsInstance() {
+        return false;
+    }
+
+    @Override
+    public Class getAutoConfigType() {
+        return null;
+    }
+
+    @Override
+    public Object getAutoConfigInstance() {
+        return null;
+    }
+
+    @Override
+    public boolean isAutoConfig() {
+        return false;
+    }
+
+    @Override
+    public String getAutoConfigName() {
+        return null;
+    }
+
+    @Override
+    public DependencyContext createCopy() {
+        return new ToggleServiceDependency(this);
+    }
+
+    @Override
+    public boolean isPropagated() {
+        return false;
+    }
+
+    @Override
+    public Dictionary getProperties() {
+        return null;
+    }
+
+    @Override
+    public String getName() {
+        return "" + m_isAvailable;
+    }
+
+    @Override
+    public String getType() {
+        return "toggle";
+    }
+
+    @Override
+    public int getState() {
+        if (m_isStarted) {
+            return (isAvailable() ? 1 : 0) + (isRequired() ? 2 : 0);
+        }
+        else {
+            return isRequired() ? ComponentDependencyDeclaration.STATE_REQUIRED
+                : ComponentDependencyDeclaration.STATE_OPTIONAL;
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/test/.gitignore
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.runtime/test/.gitignore?rev=1587056&view=auto
==============================================================================
    (empty)



Mime
View raw message