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 2014/02/06 00:22:36 UTC

svn commit: r1564995 [6/17] - in /felix/sandbox/pderop/dependencymanager: ./ core/ core/.externalToolBuilders/ core/.settings/ core/src/ core/src/main/ core/src/main/java/ core/src/main/java/org/ core/src/main/java/org/apache/ core/src/main/java/org/ap...

Added: felix/sandbox/pderop/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ServiceDependencyImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ServiceDependencyImpl.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ServiceDependencyImpl.java (added)
+++ felix/sandbox/pderop/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ServiceDependencyImpl.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,1540 @@
+/*
+ * 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.dm.impl.dependencies;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Proxy;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentDependencyDeclaration;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyService;
+import org.apache.felix.dm.InvocationUtil;
+import org.apache.felix.dm.ServiceDependency;
+import org.apache.felix.dm.ServiceUtil;
+import org.apache.felix.dm.impl.DefaultNullObject;
+import org.apache.felix.dm.impl.Logger;
+import org.apache.felix.dm.impl.SerialExecutor;
+import org.apache.felix.dm.tracker.ServiceTracker;
+import org.apache.felix.dm.tracker.ServiceTrackerCustomizer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.log.LogService;
+
+/**
+ * Service dependency that can track an OSGi service.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceDependencyImpl extends DependencyBase implements ServiceDependency, ServiceTrackerCustomizer, ComponentDependencyDeclaration
+{
+    protected final List m_services = new ArrayList();
+    protected volatile ServiceTracker m_tracker;
+    protected final BundleContext m_context;
+    protected volatile Class m_trackedServiceName;
+    protected final SerialExecutor m_serial;
+    private volatile Object m_nullObject;
+    private volatile String m_trackedServiceFilter;
+    private volatile String m_trackedServiceFilterUnmodified;
+    private volatile ServiceReference m_trackedServiceReference;
+    private volatile Object m_callbackInstance;
+    private volatile String m_callbackAdded;
+    private volatile String m_callbackChanged;
+    private volatile String m_callbackRemoved;
+    private volatile String m_callbackSwapped;
+    private volatile boolean m_autoConfig;
+    private volatile String m_autoConfigInstance;
+    private volatile boolean m_autoConfigInvoked;
+    private volatile Object m_defaultImplementation;
+    private volatile Object m_defaultImplementationInstance;
+    private volatile boolean m_isAvailable;
+    private volatile boolean m_propagate;
+    private volatile Object m_propagateCallbackInstance;
+    private volatile String m_propagateCallbackMethod;
+    private final Map m_sr = new HashMap(); /* <DependencyService, Set<Tuple<ServiceReference, Object>> */
+    private final Map m_componentByRank = new HashMap(); /* <Component, Map<Long, Map<Integer, Tuple>>> */
+    private volatile boolean m_debug = false;
+    private volatile String m_debugKey = null;
+
+    // ----------------------- Inner classes --------------------------------------------------------------
+
+    private static final Comparator COMPARATOR = new Comparator() {
+        public int getRank(ServiceReference ref) {
+            Object ranking = ref.getProperty(Constants.SERVICE_RANKING);
+            if (ranking != null && (ranking instanceof Integer)) {
+                return ((Integer) ranking).intValue();
+            }
+            return 0;
+        }
+
+        public int compare(Object a, Object b) {
+            ServiceReference ra = (ServiceReference) a, rb = (ServiceReference) b;
+            int ranka = getRank(ra);
+            int rankb = getRank(rb);
+            if (ranka < rankb) {
+                return -1;
+            }
+            else if (ranka > rankb) {
+                return 1;
+            }
+            return 0;
+        }
+    };
+
+    private static final class Tuple /* <ServiceReference, Object> */{
+        private final ServiceReference m_serviceReference;
+        private final Object m_service;
+
+        public Tuple(ServiceReference first, Object last) {
+            m_serviceReference = first;
+            m_service = last;
+        }
+
+        public ServiceReference getServiceReference() {
+            return m_serviceReference;
+        }
+
+        public Object getService() {
+            return m_service;
+        }
+
+        public boolean equals(Object obj) {
+            return ((Tuple) obj).getServiceReference().equals(getServiceReference());
+        }
+
+        public int hashCode() {
+            return m_serviceReference.hashCode();
+        }
+
+        public String toString() {
+            return "{" + m_serviceReference.getProperty(Constants.SERVICE_ID) + "=" + m_service + "}";
+        }
+    }
+
+    /**
+     * Entry to wrap service properties behind a Map.
+     */
+    private static final class ServicePropertiesMapEntry implements Map.Entry {
+        private final String m_key;
+        private Object m_value;
+
+        public ServicePropertiesMapEntry(String key, Object value) {
+            m_key = key;
+            m_value = value;
+        }
+
+        public Object getKey() {
+            return m_key;
+        }
+
+        public Object getValue() {
+            return m_value;
+        }
+
+        public String toString() {
+            return m_key + "=" + m_value;
+        }
+
+        public Object setValue(Object value) {
+            Object oldValue = m_value;
+            m_value = value;
+            return oldValue;
+        }
+
+        public boolean equals(Object o) {
+            if (!(o instanceof Map.Entry)) {
+                return false;
+            }
+            Map.Entry e = (Map.Entry) o;
+            return eq(m_key, e.getKey()) && eq(m_value, e.getValue());
+        }
+
+        public int hashCode() {
+            return ((m_key == null) ? 0 : m_key.hashCode()) ^ ((m_value == null) ? 0 : m_value.hashCode());
+        }
+
+        private static final boolean eq(Object o1, Object o2) {
+            return (o1 == null ? o2 == null : o1.equals(o2));
+        }
+    }
+
+    /**
+     * Wraps service properties behind a Map.
+     */
+    private final static class ServicePropertiesMap extends AbstractMap {
+        private final ServiceReference m_ref;
+
+        public ServicePropertiesMap(ServiceReference ref) {
+            m_ref = ref;
+        }
+
+        public Object get(Object key) {
+            return m_ref.getProperty(key.toString());
+        }
+
+        public int size() {
+            return m_ref.getPropertyKeys().length;
+        }
+
+        public Set entrySet() {
+            Set set = new HashSet();
+            String[] keys = m_ref.getPropertyKeys();
+            for (int i = 0; i < keys.length; i++) {
+                set.add(new ServicePropertiesMapEntry(keys[i], m_ref.getProperty(keys[i])));
+            }
+            return set;
+        }
+    }
+
+    // ----------------------- Public methods -----------------------------------------------------------
+
+    /**
+     * Creates a new service dependency.
+     * 
+     * @param context the bundle context
+     * @param logger the logger
+     */
+    public ServiceDependencyImpl(BundleContext context, Logger logger) {
+        super(logger);
+        m_context = context;
+        m_autoConfig = true;
+        m_serial = new SerialExecutor(logger);
+    }
+
+    /** Copying constructor that clones an existing instance. */
+    public ServiceDependencyImpl(ServiceDependencyImpl prototype) {
+        super(prototype);
+        m_context = prototype.m_context;
+        m_autoConfig = prototype.m_autoConfig;
+        m_trackedServiceName = prototype.m_trackedServiceName;
+        m_nullObject = prototype.m_nullObject;
+        m_trackedServiceFilter = prototype.m_trackedServiceFilter;
+        m_trackedServiceFilterUnmodified = prototype.m_trackedServiceFilterUnmodified;
+        m_trackedServiceReference = prototype.m_trackedServiceReference;
+        m_callbackInstance = prototype.m_callbackInstance;
+        m_callbackAdded = prototype.m_callbackAdded;
+        m_callbackChanged = prototype.m_callbackChanged;
+        m_callbackRemoved = prototype.m_callbackRemoved;
+        m_callbackSwapped = prototype.m_callbackSwapped;
+        m_autoConfigInstance = prototype.m_autoConfigInstance;
+        m_defaultImplementation = prototype.m_defaultImplementation;
+        m_serial = new SerialExecutor(prototype.m_logger);
+    }
+
+    //@Override
+    public ServiceDependency setDebug(String identifier) {
+        this.m_debug = true;
+        this.m_debugKey = identifier;
+        return this;
+    }
+
+    //@Override
+    public Dependency createCopy() {
+        return new ServiceDependencyImpl(this);
+    }
+
+    //@Override
+    public boolean isAutoConfig() {
+        return m_autoConfig;
+    }
+
+    //@Override
+    public boolean isAvailable() {
+        return m_isAvailable;
+    }
+
+    //@Override
+    public void start(final DependencyService service) {
+        boolean needsStarting = false;
+        synchronized (this) {
+            m_services.add(service);
+            if (!m_isStarted) {
+                if (m_trackedServiceName != null) {
+                    if (m_trackedServiceFilter != null) {
+                        try {
+                            m_tracker = new ServiceTracker(m_context, m_context.createFilter(m_trackedServiceFilter),
+                                this);
+                        }
+                        catch (InvalidSyntaxException e) {
+                            throw new IllegalStateException("Invalid filter definition for dependency: "
+                                + m_trackedServiceFilter);
+                        }
+                    }
+                    else if (m_trackedServiceReference != null) {
+                        m_tracker = new ServiceTracker(m_context, m_trackedServiceReference, this);
+                    }
+                    else {
+                        m_tracker = new ServiceTracker(m_context, m_trackedServiceName.getName(), this);
+                    }
+                }
+                else {
+                    throw new IllegalStateException(
+                        "Could not create tracker for dependency, no service name specified.");
+                }
+                m_isStarted = true;
+                needsStarting = true;
+            }
+        }
+
+        if (needsStarting) {
+            // when the swapped callback is set, also track the aspects
+            boolean trackAllServices = false;
+            boolean trackAllAspects = false;
+            if (m_callbackSwapped != null) {
+                trackAllAspects = true;
+            }
+            m_tracker.open(trackAllServices, trackAllAspects);
+        }
+    }
+
+    //@Override
+    public void stop(final DependencyService service) {
+        boolean needsStopping = false;
+        synchronized (this) {
+            if (m_services.size() == 1 && m_services.contains(service)) {
+                m_isStarted = false;
+                needsStopping = true;
+                // Don't remove the service from our m_services list now, because when we'll close the tracker,
+                // our removedService callback will be called, and at this point we'll need to access to our m_services list.
+            }
+        }
+        if (needsStopping) {
+            m_tracker.close(); // will invoke our removedService method synchronously. 
+            m_tracker = null;
+        }
+
+        synchronized (this) {
+            // moved this down
+            m_services.remove(service);
+        }
+    }
+
+    //@Override
+    public Object addingService(ServiceReference ref) {
+        // Check to make sure the service is actually an instance of our service
+        Object service = m_context.getService(ref);
+        if (!m_trackedServiceName.isInstance(service)) {
+            return null;
+        }
+        return service;
+    }
+
+    //@Override
+    public void addedService(final ServiceReference ref, final Object service) {
+        if (m_debug) {
+            m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] addedservice: " + ref);
+        }
+        final boolean makeAvailable = makeAvailable();
+        Object[] srv;
+        synchronized (this) {
+            srv = m_services.toArray();
+        }
+        final Object[] services = srv;
+
+        Runnable task = new Runnable() {
+            public void run() {
+                for (int i = 0; i < services.length; i++) {
+                    final DependencyService ds = (DependencyService) services[i];
+                    if (makeAvailable) {
+                        if (ds.isInstantiated() && isInstanceBound() && isRequired()) {
+                            if (m_debug) {
+                                m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invoke added: " + ref);
+                            }
+                            invokeAdded(ds, ref, service);
+                        }
+                        // The dependency callback will be deferred until all required dependency are available.
+                        if (m_debug) {
+                            m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] dependency available: " + ref);
+                        }
+                        ds.dependencyAvailable(ServiceDependencyImpl.this); // may callback invokeAdded, but  *synchronously* !
+                        if (!isRequired()) {
+                            // For optional dependency, we always invoke callback, because at this point, we know
+                            // that the service has been started, and the service start method has been called.
+                            // (See the ServiceImpl.bindService method, which will activate optional dependencies using 
+                            // startTrackingOptional() method).
+                            if (m_debug) {
+                                m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invoke added: " + ref);
+                            }
+                            invokeAdded(ds, ref, service);
+                        }
+                    }
+                    else {
+                        if (m_debug) {
+                            m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] dependency added (was available): "
+                                + ref);
+                        }
+                        // First, inject the added service in autoconfig field, if any. 
+                        ds.autoConfig(ServiceDependencyImpl.this);
+
+                        // At this point, either the dependency is optional (meaning that the service has been started,
+                        // because if not, then our dependency would not be active); or the dependency is required,
+                        // meaning that either the service is not yet started, or already started.
+                        // In all cases, we have to inject the required dependency.
+                        // we only try to invoke the method here if we are really already instantiated
+                        if (ds.isInstantiated() && ds.getCompositionInstances().length > 0) {
+                            if (m_debug) {
+                                m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invoke added: " + ref);
+                            }
+                            if (invokeAdded(ds, ref, service)) {
+                                // Propagate (if needed) all "setPropagate" dependencies to the dependency service.
+                                ds.propagate(ServiceDependencyImpl.this);
+                            }
+                        }
+                    }
+                }
+            }
+        };
+
+        m_serial.execute(task);
+    }
+
+    //@Override
+    public void modifiedService(final ServiceReference ref, final Object service) {
+        Object[] srv;
+        synchronized (this) {
+            srv = m_services.toArray();
+        }
+        final Object[] services = srv;
+
+        Runnable task = new Runnable() {
+            public void run() {
+                for (int i = 0; i < services.length; i++) {
+                    final DependencyService ds = (DependencyService) services[i];
+                    ds.autoConfig(ServiceDependencyImpl.this);
+                    if (ds.isInstantiated()) {
+                        if (invokeChanged(ds, ref, service)) {
+                            // The "change" or "swap" callback has been invoked (if not it means that the modified service 
+                            // is for a lower ranked aspect to which we are not interested in).
+                            // Now, propagate (if needed) changed properties to dependency service properties.
+                            ds.propagate(ServiceDependencyImpl.this);
+                        }
+                    }
+                }
+            }
+        };
+        m_serial.execute(task);
+    }
+
+    //@Override
+    public void removedService(final ServiceReference ref, final Object service) {
+        if (m_debug) {
+            m_logger.log(Logger.LOG_DEBUG,
+                "[" + m_debugKey + "] removedService: " + ref + ", rank: " + ref.getProperty("service.ranking"));
+        }
+        final boolean makeUnavailable = makeUnavailable();
+        if (m_debug) {
+            m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] make unavailable: " + makeUnavailable);
+        }
+        Object[] srv;
+        synchronized (this) {
+            srv = m_services.toArray();
+        }
+        final Object[] services = srv;
+
+        Runnable task = new Runnable() {
+            public void run() {
+                for (int i = 0; i < services.length; i++) {
+                    final DependencyService ds = (DependencyService) services[i];
+                    if (makeUnavailable) {
+                        ds.dependencyUnavailable(ServiceDependencyImpl.this);
+                        // when the dependency is optional or the dependency is instance bound and the component is instantiated (and the dependency is required)
+                        // then remove is invoked. In other cases the removed has been when the component was unconfigured.
+                        if (!isRequired() || (ds.isInstantiated() && isInstanceBound())) {
+                            invokeRemoved(ds, ref, service);
+                        }
+                    }
+                    else {
+                        // Some dependencies are still available: first inject the remaining highest ranked dependency
+                        // in component class field, if the dependency is configured in autoconfig mode.
+                        ds.autoConfig(ServiceDependencyImpl.this);
+                        // Next, invoke "removed" callback. If the dependency is aspect aware, we only invoke removed cb
+                        // if the removed service is the highest ranked service. Note that if the cb is not called, we don't
+                        // propagate the remaining dependency properties.
+                        if (invokeRemoved(ds, ref, service)) {
+                            // Finally, since we have lost one dependency, we have to possibly propagate the highest ranked 
+                            // dependency available.
+                            ds.propagate(ServiceDependencyImpl.this);
+                        }
+                    }
+                }
+                m_context.ungetService(ref);
+            }
+        };
+        m_serial.execute(task);
+    }
+
+    // @Override
+    public void invokeAdded(final DependencyService service) {
+        if (m_debug) {
+            m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey
+                + "] invoke added due to configure. (component is activated)");
+        }
+        ServiceReference[] refs = m_tracker.getServiceReferences();
+        if (refs != null) {
+            for (int i = 0; i < refs.length; i++) {
+                final ServiceReference sr = refs[i];
+                final Object svc = m_context.getService(sr); // May be null if service has just been unregistered !
+
+                if (svc == null) {
+                    m_logger.log(Logger.LOG_INFO, "[" + m_debugKey
+                        + "] invoke added found a null service from osgi registry for service ref:" + sr);
+                    continue; // The service has just been unregistered !
+                }
+                // We may be currently executing from our executor, that's why we "execute now".
+                m_serial.executeNow( new Runnable() {
+                    public void run() {
+                        invokeAdded(service, sr, svc);
+                   }
+                });
+            }
+        }
+    }
+
+    // @Override
+    public void invokeRemoved(final DependencyService service) {
+        Runnable task = new Runnable() {
+            public void run() {
+                if (m_debug) {
+                    m_logger.log(Logger.LOG_INFO, "[" + m_debugKey
+                        + "] invoke removed due to unconfigure. (component is destroyed)");
+                }
+                Set references = null;
+                Object[] tupleArray = null;
+                references = (Set) m_sr.get(service);
+                // is this null check necessary ??
+                if (references != null) {
+                    tupleArray = references.toArray(new Tuple[references.size()]);
+                }
+
+                Tuple[] refs = (Tuple[]) (tupleArray != null ? tupleArray : new Tuple[0]);
+
+                for (int i = 0; i < refs.length; i++) {
+                    ServiceReference sr = refs[i].getServiceReference();
+                    Object svc = refs[i].getService();
+                    if (svc == null) { // it seems that the service has just gone !
+                        m_logger.log(Logger.LOG_INFO, "[" + m_debugKey
+                            + "] invoke removed found a null service from osgi registry for service ref:" + sr);
+                        continue; 
+                    }
+
+                    invokeRemoved(service, sr, svc);
+                }
+            }
+        };
+        m_serial.executeNow(task);
+    }
+
+    //@Override
+    public String toString() {
+        return "ServiceDependency[" + m_trackedServiceName + " " + m_trackedServiceFilterUnmodified + "]";
+    }
+
+    //@Override
+    public String getAutoConfigName() {
+        return m_autoConfigInstance;
+    }
+
+    //@Override
+    public Object getAutoConfigInstance() {
+        return lookupService();
+    }
+
+    //@Override
+    public Class getAutoConfigType() {
+        return getInterface();
+    }
+
+    //@Override
+    public String getName() {
+        StringBuilder sb = new StringBuilder();
+        if (m_trackedServiceName != null) {
+            sb.append(m_trackedServiceName.getName());
+            if (m_trackedServiceFilterUnmodified != null) {
+                sb.append(' ');
+                sb.append(m_trackedServiceFilterUnmodified);
+            }
+        }
+        if (m_trackedServiceReference != null) {
+            sb.append("{service.id=" + m_trackedServiceReference.getProperty(Constants.SERVICE_ID) + "}");
+        }
+        return sb.toString();
+    }
+
+    //@Override
+    public String getType() {
+        return "service";
+    }
+
+    //@Override
+    public Dictionary getProperties() {
+        ServiceReference reference = lookupServiceReference();
+        Object service = lookupService();
+        if (reference != null) {
+            if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
+                try {
+                    return (Dictionary) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance,
+                        m_propagateCallbackMethod, new Class[][] { { ServiceReference.class, Object.class },
+                                { ServiceReference.class } }, new Object[][] { { reference, service }, { reference } });
+                }
+                catch (InvocationTargetException e) {
+                    m_logger.log(LogService.LOG_WARNING, "Exception while invoking callback method", e.getCause());
+                }
+                catch (Exception e) {
+                    m_logger.log(LogService.LOG_WARNING, "Exception while trying to invoke callback method", e);
+                }
+                throw new IllegalStateException("Could not invoke callback");
+            }
+            else {
+                Properties props = new Properties();
+                String[] keys = reference.getPropertyKeys();
+                for (int i = 0; i < keys.length; i++) {
+                    if (!(keys[i].equals(Constants.SERVICE_ID) || keys[i].equals(Constants.SERVICE_PID))) {
+                        props.put(keys[i], reference.getProperty(keys[i]));
+                    }
+                }
+                return props;
+            }
+        }
+        else {
+            throw new IllegalStateException("cannot find service reference");
+        }
+    }
+
+    //@Override
+    public boolean isPropagated() {
+        return m_propagate;
+    }
+
+    // ----- CREATION
+
+    /**
+     * Sets the name of the service that should be tracked. 
+     * 
+     * @param serviceName the name of the service
+     * @return this service dependency
+     */
+    //@Override
+    public ServiceDependency setService(Class serviceName) {
+        setService(serviceName, null, null);
+        return this;
+    }
+
+    /**
+     * Sets the name of the service that should be tracked. You can either specify
+     * only the name, only the filter, or the name and a filter.
+     * <p>
+     * If you specify name and filter, the filter is used
+     * to track the service and should only return services of the type that was specified
+     * in the name. To make sure of this, the filter is actually extended internally to
+     * filter on the correct name.
+     * <p>
+     * If you specify only the filter, the name is assumed to be a service of type
+     * <code>Object</code> which means that, when auto configuration is on, instances
+     * of that service will be injected in any field of type <code>Object</code>.
+     * 
+     * @param serviceName the name of the service
+     * @param serviceFilter the filter condition
+     * @return this service dependency
+     */
+    //@Override
+    public ServiceDependency setService(Class serviceName, String serviceFilter) {
+        setService(serviceName, null, serviceFilter);
+        return this;
+    }
+
+    /**
+     * Sets the name of the service that should be tracked. The name is assumed to be 
+     * a service of type <code>Object</code> which means that, when auto configuration 
+     * is on, instances of that service will be injected in any field of type 
+     * <code>Object</code>.
+     * 
+     * @param serviceFilter the filter condition
+     * @return this service dependency
+     */
+    //@Override
+    public ServiceDependency setService(String serviceFilter) {
+        if (serviceFilter == null) {
+            throw new IllegalArgumentException("Service filter cannot be null.");
+        }
+        setService(null, null, serviceFilter);
+        return this;
+    }
+
+    /**
+     * Sets the name of the service that should be tracked. By specifying the service
+     * reference of the service you want to track, you can directly track a single
+     * service. The name you use must match the type of service referred to by the
+     * service reference and it is up to you to make sure that is the case.
+     * 
+     * @param serviceName the name of the service
+     * @param serviceReference the service reference to track
+     * @return this service dependency
+     */
+    //@Override
+    public ServiceDependency setService(Class serviceName, ServiceReference serviceReference) {
+        setService(serviceName, serviceReference, null);
+        return this;
+    }
+
+    /**
+     * Sets the default implementation for this service dependency. You can use this to supply
+     * your own implementation that will be used instead of a Null Object when the dependency is
+     * not available. This is also convenient if the service dependency is not an interface
+     * (which would cause the Null Object creation to fail) but a class.
+     * 
+     * @param implementation the instance to use or the class to instantiate if you want to lazily
+     *     instantiate this implementation
+     * @return this service dependency
+     */
+    //@Override
+    public ServiceDependency setDefaultImplementation(Object implementation) {
+        ensureNotActive();
+        m_defaultImplementation = implementation;
+        return this;
+    }
+
+    /**
+     * Sets the required flag which determines if this service is required or not.
+     * 
+     * @param required the required flag
+     * @return this service dependency
+     */
+    //@Override
+    public ServiceDependency setRequired(boolean required) {
+        ensureNotActive();
+        setIsRequired(required);
+        return this;
+    }
+
+    //@Override
+    public ServiceDependency setInstanceBound(boolean isInstanceBound) {
+        setIsInstanceBound(isInstanceBound);
+        return this;
+    }
+
+    /**
+     * Sets auto configuration for this service. Auto configuration allows the
+     * dependency to fill in any attributes in the service implementation that
+     * are of the same type as this dependency. Default is on.
+     * 
+     * @param autoConfig the value of auto config
+     * @return this service dependency
+     */
+    //@Override
+    public ServiceDependency setAutoConfig(boolean autoConfig) {
+        ensureNotActive();
+        m_autoConfig = autoConfig;
+        m_autoConfigInvoked = true;
+        return this;
+    }
+
+    /**
+     * Sets auto configuration for this service. Auto configuration allows the
+     * dependency to fill in the attribute in the service implementation that
+     * has the same type and instance name.
+     * 
+     * @param instanceName the name of attribute to auto config
+     * @return this service dependency
+     */
+    //@Override
+    public ServiceDependency setAutoConfig(String instanceName) {
+        ensureNotActive();
+        m_autoConfig = (instanceName != null);
+        m_autoConfigInstance = instanceName;
+        m_autoConfigInvoked = true;
+        return this;
+    }
+
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added or removed. When you specify callbacks, the auto configuration 
+     * feature is automatically turned off, because we're assuming you don't need it in this 
+     * case.
+     * 
+     * @param added the method to call when a service was added
+     * @param removed the method to call when a service was removed
+     * @return this service dependency
+     */
+    //@Override
+    public ServiceDependency setCallbacks(String added, String removed) {
+        return setCallbacks((Object) null, added, null, removed);
+    }
+
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added, changed or removed. When you specify callbacks, the auto 
+     * configuration feature is automatically turned off, because we're assuming you don't 
+     * need it in this case.
+     * 
+     * @param added the method to call when a service was added
+     * @param changed the method to call when a service was changed
+     * @param removed the method to call when a service was removed
+     * @return this service dependency
+     */
+    //@Override
+    public ServiceDependency setCallbacks(String added, String changed, String removed) {
+        return setCallbacks((Object) null, added, changed, removed);
+    }
+
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added, changed or removed. When you specify callbacks, the auto 
+     * configuration feature is automatically turned off, because we're assuming you don't 
+     * need it in this case.
+     * @param added the method to call when a service was added
+     * @param changed the method to call when a service was changed
+     * @param removed the method to call when a service was removed
+     * @param swapped the method to call when the service was swapped due to addition or 
+     * removal of an aspect
+     * @return this service dependency
+     */
+    //@Override
+    public ServiceDependency setCallbacks(String added, String changed, String removed, String swapped) {
+        return setCallbacks((Object) null, added, changed, removed, swapped);
+    }
+
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added or removed. They are called on the instance you provide. When you
+     * specify callbacks, the auto configuration feature is automatically turned off, because
+     * we're assuming you don't need it in this case.
+     * 
+     * @param instance the instance to call the callbacks on
+     * @param added the method to call when a service was added
+     * @param removed the method to call when a service was removed
+     * @return this service dependency
+     */
+    //@Override
+    public ServiceDependency setCallbacks(Object instance, String added, String removed) {
+        return setCallbacks(instance, added, (String) null, removed);
+    }
+
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added, changed or removed. They are called on the instance you provide. When you
+     * specify callbacks, the auto configuration feature is automatically turned off, because
+     * we're assuming you don't need it in this case.
+     * 
+     * @param instance the instance to call the callbacks on
+     * @param added the method to call when a service was added
+     * @param changed the method to call when a service was changed
+     * @param removed the method to call when a service was removed
+     * @return this service dependency
+     */
+    //@Override
+    public ServiceDependency setCallbacks(Object instance, String added, String changed, String removed) {
+        return setCallbacks(instance, added, changed, removed, null);
+    }
+
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added, changed or removed. When you specify callbacks, the auto 
+     * configuration feature is automatically turned off, because we're assuming you don't 
+     * need it in this case.
+     * @param instance the instance to call the callbacks on
+     * @param added the method to call when a service was added
+     * @param changed the method to call when a service was changed
+     * @param removed the method to call when a service was removed
+     * @param swapped the method to call when the service was swapped due to addition or 
+     * removal of an aspect
+     * @return this service dependency
+     */
+    //@Override
+    public ServiceDependency setCallbacks(Object instance, String added, String changed, String removed, String swapped)
+    {
+        ensureNotActive();
+        // if at least one valid callback is specified, we turn off auto configuration, unless
+        // someone already explicitly invoked autoConfig
+        if ((added != null || removed != null || changed != null || swapped != null) && !m_autoConfigInvoked) {
+            setAutoConfig(false);
+        }
+        m_callbackInstance = instance;
+        m_callbackAdded = added;
+        m_callbackChanged = changed;
+        m_callbackRemoved = removed;
+        m_callbackSwapped = swapped;
+        return this;
+    }
+
+    //@Override
+    public ServiceDependency setPropagate(boolean propagate) {
+        ensureNotActive();
+        m_propagate = propagate;
+        return this;
+    }
+
+    //@Override
+    public ServiceDependency setPropagate(Object instance, String method) {
+        setPropagate(instance != null && method != null);
+        m_propagateCallbackInstance = instance;
+        m_propagateCallbackMethod = method;
+        return this;
+    }
+
+    public void invoke(final Object[] callbackInstances, final DependencyService component,
+        final ServiceReference reference, final Object service, final String name)
+    {
+        if (m_debug) {
+            m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invoke: " + name);
+        }
+        if (name != null) {
+            component.invokeCallbackMethod(callbackInstances, name, new Class[][] {
+                    { Component.class, ServiceReference.class, m_trackedServiceName },
+                    { Component.class, ServiceReference.class, Object.class },
+                    { Component.class, ServiceReference.class }, { Component.class, m_trackedServiceName },
+                    { Component.class, Object.class }, { Component.class },
+                    { Component.class, Map.class, m_trackedServiceName },
+                    { ServiceReference.class, m_trackedServiceName }, { ServiceReference.class, Object.class },
+                    { ServiceReference.class }, { m_trackedServiceName }, { Object.class }, {},
+                    { Map.class, m_trackedServiceName } }, new Object[][] { { component, reference, service },
+                    { component, reference, service }, { component, reference }, { component, service },
+                    { component, service }, { component }, { component, new ServicePropertiesMap(reference), service },
+                    { reference, service }, { reference, service }, { reference }, { service }, { service }, {},
+                    { new ServicePropertiesMap(reference), service } });
+
+        }
+    }
+
+    public void invokeSwappedCallback(final Object[] callbackInstances, final DependencyService component,
+        final ServiceReference previousReference, final Object previous,
+        final ServiceReference currentServiceReference, final Object current, final String swapCallback)
+    {
+        // sanity check on the service references
+        Integer oldRank = (Integer) previousReference.getProperty(Constants.SERVICE_RANKING);
+        Integer newRank = (Integer) currentServiceReference.getProperty(Constants.SERVICE_RANKING);
+
+        if (oldRank != null && newRank != null && oldRank.equals(newRank)) {
+            throw new IllegalStateException(
+                "Attempt to swap a service for a service with the same rank! previousReference: " + previousReference
+                    + ", currentReference: " + currentServiceReference);
+        }
+
+        component.invokeCallbackMethod(callbackInstances, swapCallback, new Class[][] {
+                { m_trackedServiceName, m_trackedServiceName },
+                { Object.class, Object.class },
+                { ServiceReference.class, m_trackedServiceName, ServiceReference.class, m_trackedServiceName },
+                { ServiceReference.class, Object.class, ServiceReference.class, Object.class },
+                { Component.class, m_trackedServiceName, m_trackedServiceName },
+                { Component.class, Object.class, Object.class },
+                { Component.class, ServiceReference.class, m_trackedServiceName, ServiceReference.class,
+                        m_trackedServiceName },
+                { Component.class, ServiceReference.class, Object.class, ServiceReference.class, Object.class } },
+            new Object[][] { { previous, current }, { previous, current },
+                    { previousReference, previous, currentServiceReference, current },
+                    { previousReference, previous, currentServiceReference, current },
+                    { component, previous, current }, { component, previous, current },
+                    { component, previousReference, previous, currentServiceReference, current },
+                    { component, previousReference, previous, currentServiceReference, current } });
+    }
+
+    // --------------------------------------- Protected methods -------------------------------------------------------------------
+
+    protected synchronized boolean makeAvailable() {
+        if (!isAvailable()) {
+            m_isAvailable = true;
+            return true;
+        }
+        return false;
+    }
+
+    protected synchronized Object getService() {
+        Object service = null;
+        if (m_isStarted) {
+            service = m_tracker.getService();
+        }
+        if (service == null && isAutoConfig()) {
+            service = getDefaultImplementation();
+            if (service == null) {
+                service = getNullObject();
+            }
+        }
+        return service;
+    }
+
+    // --------------------------------------- Private methods --------------------------------------------
+
+    private Object lookupService() {
+        Object service = null;
+        if (m_isStarted) {
+            service = getService();
+        }
+        else {
+            ServiceReference[] refs = null;
+            ServiceReference ref = null;
+            if (m_trackedServiceName != null) {
+                if (m_trackedServiceFilter != null) {
+                    try {
+                        refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
+                        if (refs != null) {
+                            Arrays.sort(refs, COMPARATOR);
+                            ref = refs[0];
+                        }
+                    }
+                    catch (InvalidSyntaxException e) {
+                        throw new IllegalStateException("Invalid filter definition for dependency.");
+                    }
+                }
+                else if (m_trackedServiceReference != null) {
+                    ref = m_trackedServiceReference;
+                }
+                else {
+                    ref = m_context.getServiceReference(m_trackedServiceName.getName());
+                }
+                if (ref != null) {
+                    service = m_context.getService(ref);
+                }
+            }
+            else {
+                throw new IllegalStateException("Could not lookup dependency, no service name specified.");
+            }
+        }
+        if (service == null && isAutoConfig()) {
+            service = getDefaultImplementation();
+            if (service == null) {
+                service = getNullObject();
+            }
+        }
+        return service;
+    }
+
+    private ServiceReference lookupServiceReference() {
+        // TODO lots of duplication in lookupService()
+        ServiceReference service = null;
+        if (m_isStarted) {
+            service = m_tracker.getServiceReference();
+        }
+        else {
+            ServiceReference[] refs = null;
+            ServiceReference ref = null;
+            if (m_trackedServiceName != null) {
+                if (m_trackedServiceFilter != null) {
+                    try {
+                        refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
+                        if (refs != null) {
+                            Arrays.sort(refs, COMPARATOR);
+                            ref = refs[0];
+                        }
+                    }
+                    catch (InvalidSyntaxException e) {
+                        throw new IllegalStateException("Invalid filter definition for dependency.");
+                    }
+                }
+                else if (m_trackedServiceReference != null) {
+                    ref = m_trackedServiceReference;
+                }
+                else {
+                    ref = m_context.getServiceReference(m_trackedServiceName.getName());
+                }
+                if (ref != null) {
+                    service = ref;
+                }
+            }
+            else {
+                throw new IllegalStateException("Could not lookup dependency, no service name specified.");
+            }
+        }
+        return service;
+    }
+
+    private Class getInterface() {
+        return m_trackedServiceName;
+    }
+
+    private Object getNullObject() {
+        if (m_nullObject == null) {
+            Class trackedServiceName;
+            synchronized (this) {
+                trackedServiceName = m_trackedServiceName;
+            }
+            try {
+                m_nullObject = Proxy.newProxyInstance(trackedServiceName.getClassLoader(),
+                    new Class[] { trackedServiceName }, new DefaultNullObject());
+            }
+            catch (Exception e) {
+                m_logger.log(Logger.LOG_ERROR, "Could not create null object for " + trackedServiceName + ".", e);
+            }
+        }
+        return m_nullObject;
+    }
+
+    private Object getDefaultImplementation() {
+        if (m_defaultImplementation != null) {
+            if (m_defaultImplementation instanceof Class) {
+                try {
+                    m_defaultImplementationInstance = ((Class) m_defaultImplementation).newInstance();
+                }
+                catch (Exception e) {
+                    m_logger.log(Logger.LOG_ERROR, "Could not create default implementation instance of class "
+                        + m_defaultImplementation + ".", e);
+                }
+            }
+            else {
+                m_defaultImplementationInstance = m_defaultImplementation;
+            }
+        }
+        return m_defaultImplementationInstance;
+    }
+
+    /**
+     * Invoke added or swap callback.
+     * @return true if one of the swap/added callbacks has been invoked, false if not, meaning that the added dependency is not the highest ranked one.
+     */
+    protected boolean invokeAdded(DependencyService dependencyService, ServiceReference reference, Object service) {
+        if (m_debug) {
+            m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invoke added");
+        }
+        boolean added = false;
+        Set set = (Set) m_sr.get(dependencyService);
+        if (set == null) {
+            set = new HashSet();
+            m_sr.put(dependencyService, set);
+        }
+        added = set.add(new Tuple(reference, service));
+        if (added) {
+            // when a changed callback is specified we might not call the added callback just yet
+            if (m_callbackSwapped != null) {
+                return handleAspectAwareAdded(dependencyService, reference, service);
+            }
+            else {
+                invoke(getCallbackInstances(dependencyService), dependencyService, reference, service, m_callbackAdded);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean invokeChanged(DependencyService dependencyService, ServiceReference reference, Object service) {
+        if (m_callbackSwapped != null) {
+            if (m_debug) {
+                m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] handleAspectAwareChanged on " + service);
+            }
+            return handleAspectAwareChanged(dependencyService, reference, service);
+        }
+        invoke(getCallbackInstances(dependencyService), dependencyService, reference, service, m_callbackChanged);
+        return true;
+    }
+
+    /*
+     * Invoke the removed or the swap callback. Called from component's executor.
+     * @return true if the swap or the removed callback has  been called, false if not.
+     */
+    private boolean invokeRemoved(DependencyService dependencyService, ServiceReference reference, Object service) {
+        if (m_debug) {
+            m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invoke removed");
+        }
+        boolean removed = false;
+        Set set = (Set) m_sr.get(dependencyService);
+        removed = (set != null && set.remove(new Tuple(reference, service)));
+        if (m_debug) {
+            m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] removed: " + removed);
+        }
+        if (removed) {
+            if (m_callbackSwapped != null) {
+                return handleAspectAwareRemoved(dependencyService, reference, service);
+            }
+            else {
+                invoke(getCallbackInstances(dependencyService), dependencyService, reference, service,
+                    m_callbackRemoved);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Invoke added or swap callback for aspect aware service dependency.
+     * @return true if one of the swap/added callbacks has been invoked, false if not, meaning that the added dependency is not the highest ranked one.
+     */
+    private boolean handleAspectAwareAdded(final DependencyService dependencyService, final ServiceReference reference,
+        final Object service)
+    {
+        if (m_debug) {
+            m_logger.log(Logger.LOG_DEBUG,
+                "[" + m_debugKey + "] aspectawareadded: " + reference.getProperty("service.ranking"));
+        }
+        if (componentIsDependencyManagerFactory(dependencyService)) {
+            // component is either aspect or adapter factory instance, these must be ignored.
+            return false;
+        }
+        boolean invokeAdded = false;
+        boolean invokeSwapped = false;
+        Integer ranking = ServiceUtil.getRankingAsInteger(reference);
+        Tuple newHighestRankedService = null;
+        Tuple prevHighestRankedService = null;
+        Map rankings = null;
+
+        Long originalServiceId = ServiceUtil.getServiceIdAsLong(reference);
+        Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+        if (componentMap == null) {
+            // create new componentMap
+            componentMap = new HashMap(); /* <Long, Map<Integer, Tuple>> */
+            m_componentByRank.put(dependencyService, componentMap);
+        }
+        rankings = (Map) componentMap.get(originalServiceId); /* <Integer, Tuple> */
+        if (rankings == null) {
+            // new component added
+            rankings = new HashMap(); /* <Integer, Tuple> */
+            componentMap.put(originalServiceId, rankings);
+            rankings.put(ranking, new Tuple(reference, service));
+            invokeAdded = true;
+        }
+
+        if (!invokeAdded) {
+            // current highest ranked
+            prevHighestRankedService = (Tuple) getHighestRankedService(dependencyService, originalServiceId).getValue();
+            newHighestRankedService = swapHighestRankedService(dependencyService, originalServiceId, reference,
+                service, ranking);
+            if (m_debug) {
+                m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] prevhigh: "
+                    + prevHighestRankedService.getServiceReference().getProperty("service.ranking") + ", new high: "
+                    + newHighestRankedService.getServiceReference().getProperty("service.ranking"));
+            }
+            if (!prevHighestRankedService.getServiceReference().equals(newHighestRankedService.getServiceReference())) {
+                // new highest ranked service
+                if (m_debug) {
+                    m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] New highest ranked to swap to");
+                }
+                invokeSwapped = true;
+            }
+            else {
+                if (m_debug) {
+                    m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] Ignoring lower ranked or irrelevant swap");
+                }
+            }
+        }
+        if (m_debug) {
+            m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] " + m_componentByRank.toString());
+        }
+
+        if (invokeAdded) {
+            if (m_debug) {
+                m_logger.log(Logger.LOG_DEBUG,
+                    "[" + m_debugKey + "] invoke added: " + reference.getProperty("service.ranking"));
+            }
+            invoke(getCallbackInstances(dependencyService), dependencyService, reference, service, m_callbackAdded);
+
+            return true;
+        }
+        else if (invokeSwapped) {
+            if (m_debug) {
+                m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invoke swapped: "
+                    + newHighestRankedService.getServiceReference().getProperty("service.ranking") + " replacing "
+                    + prevHighestRankedService.getServiceReference().getProperty("service.ranking"));
+            }
+            invokeSwappedCallback(dependencyService, prevHighestRankedService.getServiceReference(),
+                prevHighestRankedService.getService(), newHighestRankedService.getServiceReference(),
+                newHighestRankedService.getService());
+            return true;
+        }
+        return false;
+    }
+
+    private boolean componentIsDependencyManagerFactory(DependencyService dependencyService) {
+        Object component = dependencyService.getService();
+        if (component != null) {
+            String className = component.getClass().getName();
+            return className.startsWith("org.apache.felix.dm")
+                && !className.startsWith("org.apache.felix.dm.impl.AdapterServiceImpl$AdapterImpl")
+                && !className.startsWith("org.apache.felix.dm.test");
+        }
+        return false;
+    }
+
+    private Tuple swapHighestRankedService(DependencyService dependencyService, Long serviceId,
+        ServiceReference newReference, Object newService, Integer newRanking)
+    {
+        // does a component with a higher ranking exist
+        Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+        Map rankings = (Map) componentMap.get(serviceId); /* <Integer, Tuple> */
+        rankings.put(newRanking, new Tuple(newReference, newService));
+        Entry highestEntry = getHighestRankedService(dependencyService, serviceId); /* <Integer, Tuple> */
+        return (Tuple) highestEntry.getValue();
+    }
+
+    private Entry getHighestRankedService(DependencyService dependencyService, Long serviceId) { /* <Integer, Tuple> */
+        Entry highestEntry = null; /* <Integer, Tuple> */
+        Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+        Map rankings = (Map) componentMap.get(serviceId); /* <Integer, Tuple> */
+        if (rankings != null) {
+            for (Iterator entryIterator = rankings.entrySet().iterator(); entryIterator.hasNext();) { /* <Integer, Tuple> */
+                Entry mapEntry = (Entry) entryIterator.next();
+                if (highestEntry == null) {
+                    highestEntry = mapEntry;
+                }
+                else {
+                    if (((Integer) mapEntry.getKey()).intValue() > ((Integer) highestEntry.getKey()).intValue()) {
+                        highestEntry = mapEntry;
+                    }
+                }
+            }
+        }
+        return highestEntry;
+    }
+
+    private boolean isLastService(DependencyService dependencyService, ServiceReference reference, Object object,
+        Long serviceId)
+    {
+        // get the collection of rankings
+        Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+
+        Map rankings = null; /* <Integer, Tuple> */
+        if (componentMap != null) {
+            rankings = (Map) componentMap.get(serviceId);
+        }
+        // if there is only one element left in the collection of rankings
+        // and this last element has the same ranking as the supplied service (in other words, it is the same)
+        // then this is the last service
+        // NOTE: it is possible that there is only one element, but that it's not equal to the supplied service,
+        // because an aspect on top of the original service is being removed (but the original service is still
+        // there). That in turn triggers:
+        // 1) a call to added(original-service)
+        // 2) that causes a swap
+        // 3) a call to removed(aspect-service) <-- that's what we're talking about
+        if (m_debug) {
+            m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] last service: " + m_componentByRank.toString());
+        }
+        return (componentMap != null && rankings != null && rankings.size() == 1 && ((Entry) rankings.entrySet().iterator().next()).getKey().equals(
+            ServiceUtil.getRankingAsInteger(reference)));
+    }
+
+    private boolean handleAspectAwareChanged(DependencyService dependencyService, ServiceReference reference,
+        Object service)
+    {
+        if (m_debug) {
+            m_logger.log(Logger.LOG_DEBUG,
+                "[" + m_debugKey + "] aspectawareChanged: service.ranking=" + reference.getProperty("service.ranking"));
+        }
+        if (componentIsDependencyManagerFactory(dependencyService)) {
+            // component is either aspect or adapter factory instance, these must be ignored.
+            return false;
+        }
+        boolean invokeChanged = false;
+
+        // Determine current highest ranked service: we'll take into account the change event only if it
+        // comes from the highest ranked service.
+        Long serviceId = ServiceUtil.getServiceIdAsLong(reference);
+        Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+        if (componentMap != null) {
+            Map rankings = (Map) componentMap.get(serviceId); /* <Integer, Tuple> */
+            if (rankings != null) {
+                Entry highestEntry = getHighestRankedService(dependencyService, serviceId); /* <Integer, Tuple> */
+                if (m_debug) {
+                    m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] highest service ref:" + highestEntry);
+                }
+                if (highestEntry == null) {
+                    invokeChanged = true;
+                }
+                else {
+                    Tuple tuple = (Tuple) highestEntry.getValue();
+                    if (reference.equals(tuple.getServiceReference())) {
+                        // The changed service is the highest ranked service: we can handle the modification event.
+                        invokeChanged = true;
+                    }
+                }
+            }
+        }
+
+        if (m_debug) {
+            m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invokeChanged=" + invokeChanged);
+        }
+
+        if (invokeChanged) {
+            if (m_debug) {
+                m_logger.log(Logger.LOG_DEBUG,
+                    "[" + m_debugKey + "] invoke changed: ref ranking=" + reference.getProperty("service.ranking"));
+            }
+            invoke(getCallbackInstances(dependencyService), dependencyService, reference, service, m_callbackChanged);
+            return true;
+        }
+        return false;
+    }
+
+    /*
+     * handles aspect aware removed service.
+     * @return true if the swap or the removed callback has  been called, false if not.
+     */
+    private boolean handleAspectAwareRemoved(DependencyService dependencyService, ServiceReference reference,
+        Object service)
+    {
+        if (m_debug) {
+            m_logger.log(Logger.LOG_DEBUG,
+                "[" + m_debugKey + "] aspectawareremoved: " + reference.getProperty("service.ranking"));
+        }
+        if (componentIsDependencyManagerFactory(dependencyService)) {
+            // component is either aspect or adapter factory instance, these must be ignored.
+            return false;
+        }
+        // we might need to swap here too!
+        boolean invokeRemoved = false;
+        Long serviceId = ServiceUtil.getServiceIdAsLong(reference);
+        Tuple prevHighestRankedService = null;
+        Tuple newHighestRankedService = null;
+        boolean invokeSwapped = false;
+        Map rankings = null;
+        Long originalServiceId = ServiceUtil.getServiceIdAsLong(reference);
+
+        if (isLastService(dependencyService, reference, service, serviceId)) {
+            invokeRemoved = true;
+        }
+        else {
+            // not the last service, but should we swap?
+            prevHighestRankedService = (Tuple) getHighestRankedService(dependencyService, originalServiceId).getValue();
+            if (prevHighestRankedService.getServiceReference().equals(reference)) {
+                // swapping out
+                if (m_debug) {
+                    m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] Swap out on remove!");
+                }
+                invokeSwapped = true;
+            }
+        }
+        if (m_debug) {
+            m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] is last service: " + invokeRemoved);
+        }
+        // cleanup
+        Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+        if (componentMap != null) {
+            rankings = (Map) componentMap.get(originalServiceId); /* <Integer, Tuple> */
+            List rankingsToRemove = new ArrayList();
+            for (Iterator entryIterator = rankings.entrySet().iterator(); entryIterator.hasNext();) {
+                Entry mapEntry = (Entry) entryIterator.next();
+                if (((Tuple) mapEntry.getValue()).getServiceReference().equals(reference)) {
+                    // remove the reference
+                    // rankings.remove(mapEntry.getKey());
+                    rankingsToRemove.add(mapEntry.getKey());
+                }
+            }
+            for (Iterator rankingIterator = rankingsToRemove.iterator(); rankingIterator.hasNext();) {
+                rankings.remove(rankingIterator.next());
+            }
+            if (rankings.size() == 0) {
+                componentMap.remove(originalServiceId);
+            }
+            if (componentMap.size() == 0) {
+                m_componentByRank.remove(dependencyService);
+            }
+        }
+        // determine current highest ranked service
+        if (invokeSwapped) {
+            newHighestRankedService = (Tuple) getHighestRankedService(dependencyService, originalServiceId).getValue();
+        }
+        if (invokeRemoved) {
+            // handle invoke outside the sync block since we won't know what will happen there
+            if (m_debug) {
+                m_logger.log(Logger.LOG_DEBUG,
+                    "[" + m_debugKey + "] invoke removed: " + reference.getProperty("service.ranking"));
+            }
+            invoke(getCallbackInstances(dependencyService), dependencyService, reference, service, m_callbackRemoved);
+            return true;
+        }
+        else if (invokeSwapped) {
+            if (m_debug) {
+                m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invoke swapped: "
+                    + newHighestRankedService.getServiceReference().getProperty("service.ranking") + " replacing "
+                    + prevHighestRankedService.getServiceReference().getProperty("service.ranking"));
+            }
+            invokeSwappedCallback(dependencyService, prevHighestRankedService.getServiceReference(),
+                prevHighestRankedService.getService(), newHighestRankedService.getServiceReference(),
+                newHighestRankedService.getService());
+            return true;
+        }
+
+        return false;
+    }
+
+    private void invokeSwappedCallback(DependencyService component, ServiceReference previousReference,
+        Object previous, ServiceReference currentServiceReference, Object current)
+    {
+        invokeSwappedCallback(getCallbackInstances(component), component, previousReference, previous,
+            currentServiceReference, current, m_callbackSwapped);
+    }
+
+    private synchronized boolean makeUnavailable() {
+        if ((isAvailable()) && (m_isStarted == false || !m_tracker.hasReference())) {
+            m_isAvailable = false;
+            return true;
+        }
+        return false;
+    }
+
+    private synchronized Object[] getCallbackInstances(DependencyService dependencyService) {
+        if (m_callbackInstance == null) {
+            return dependencyService.getCompositionInstances();
+        }
+        else {
+            return new Object[] { m_callbackInstance };
+        }
+    }
+
+    // ----- CREATION
+
+    /** Internal method to set the name, service reference and/or filter. */
+    private void setService(Class serviceName, ServiceReference serviceReference, String serviceFilter) {
+        ensureNotActive();
+        if (serviceName == null) {
+            m_trackedServiceName = Object.class;
+        }
+        else {
+            m_trackedServiceName = serviceName;
+        }
+        if (serviceFilter != null) {
+            m_trackedServiceFilterUnmodified = serviceFilter;
+            if (serviceName == null) {
+                m_trackedServiceFilter = serviceFilter;
+            }
+            else {
+                m_trackedServiceFilter = "(&(" + Constants.OBJECTCLASS + "=" + serviceName.getName() + ")"
+                    + serviceFilter + ")";
+            }
+        }
+        else {
+            m_trackedServiceFilterUnmodified = null;
+            m_trackedServiceFilter = null;
+        }
+        if (serviceReference != null) {
+            m_trackedServiceReference = serviceReference;
+            if (serviceFilter != null) {
+                throw new IllegalArgumentException("Cannot specify both a filter and a service reference.");
+            }
+        }
+        else {
+            m_trackedServiceReference = null;
+        }
+    }
+
+    private void ensureNotActive() {
+        if (m_tracker != null) {
+            throw new IllegalStateException("Cannot modify state while active.");
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java (added)
+++ felix/sandbox/pderop/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,208 @@
+/*
+ * 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.dm.impl.dependencies;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyService;
+import org.apache.felix.dm.ServiceDependency;
+import org.apache.felix.dm.TemporalServiceDependency;
+import org.apache.felix.dm.impl.Logger;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Temporal Service dependency implementation, used to hide temporary service dependency "outage".
+ * Only works with a required dependency.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class TemporalServiceDependencyImpl extends ServiceDependencyImpl implements TemporalServiceDependency, InvocationHandler
+{
+    // Max millis to wait for service availability.
+    private long m_timeout = 30000;
+
+    // Dependency service currently used (with highest rank or highest service id).
+    private volatile Object m_cachedService;
+
+    // Framework bundle (we use it to detect if the framework is stopping)
+    private final Bundle m_frameworkBundle;
+
+    // The service proxy, which blocks when service is not available.
+    private volatile Object m_serviceInstance;
+
+    /**
+     * Creates a new Temporal Service Dependency.
+     * 
+     * @param context The bundle context of the bundle which is instantiating this dependency object
+     * @param logger the logger our Internal logger for logging events.
+     * @see DependencyActivatorBase#createTemporalServiceDependency()
+     */
+    public TemporalServiceDependencyImpl(BundleContext context, Logger logger) {
+        super(context, logger);
+        super.setRequired(true);
+        m_frameworkBundle = context.getBundle(0);
+    }
+
+    /**
+     * Sets the timeout for this temporal dependency. Specifying a timeout value of zero means that there is no timeout period,
+     * and an invocation on a missing service will fail immediately.
+     * 
+     * @param timeout the dependency timeout value greater or equals to 0
+     * @throws IllegalArgumentException if the timeout is negative
+     * @return this temporal dependency
+     */
+    public synchronized TemporalServiceDependency setTimeout(long timeout) {
+        if (timeout < 0) {
+            throw new IllegalArgumentException("Invalid timeout value: " + timeout);
+        }
+        m_timeout = timeout;
+        return this;
+    }
+
+    /**
+     * Sets the required flag which determines if this service is required or not. This method
+     * just override the superclass method in order to check if the required flag is true 
+     * (optional dependency is not supported by this class).
+     * 
+     * @param required the required flag, which must be set to true
+     * @return this service dependency
+     * @throws IllegalArgumentException if the "required" parameter is not true.
+     */
+    //@Override
+    public ServiceDependency setRequired(boolean required) {
+        if (!required) {
+            throw new IllegalArgumentException("A Temporal Service dependency can't be optional");
+        }
+        super.setRequired(required);
+        return this;
+    }
+
+    /**
+     * The ServiceTracker calls us here in order to inform about a service arrival.
+     */
+    //@Override
+    public void addedService(final ServiceReference ref, final Object service) {
+        // Update our service cache, using the tracker. We do this because the
+        // just added service might not be the service with the highest rank ...
+        m_cachedService = m_tracker.getService();
+        final boolean makeAvailable = makeAvailable();
+        if (makeAvailable) {
+            m_serviceInstance = Proxy.newProxyInstance(m_trackedServiceName.getClassLoader(),
+                new Class[] { m_trackedServiceName }, this);
+        }
+        Object[] srv;
+        synchronized (this) {
+            srv = m_services.toArray();
+        }
+        final Object[] services = srv;
+
+        Runnable task = new Runnable() {
+            public void run() {
+                for (int i = 0; i < services.length; i++) {
+                    final DependencyService ds = (DependencyService) services[i];
+                    if (makeAvailable) {
+                        if (ds.isInstantiated() && isInstanceBound() && isRequired()) {
+                            invokeAdded(ds, ref, service);
+                        }
+                        ds.dependencyAvailable(TemporalServiceDependencyImpl.this);
+                    }
+                    else {
+                        synchronized (this) {
+                            notifyAll();
+                        }
+                    }
+                }
+            }
+        };
+        m_serial.execute(task);
+    }
+
+    /**
+     * The ServiceTracker calls us here when a tracked service properties are modified.
+     */
+    //@Override
+    public void modifiedService(ServiceReference ref, Object service) {
+        // We don't care.
+    }
+
+    /**
+     * The ServiceTracker calls us here when a tracked service is lost.
+     */
+    //@Override
+    public void removedService(ServiceReference ref, Object service) {
+        // If we detect that the fwk is stopping, we behave as our superclass. That is:
+        // the lost dependency has to trigger our service deactivation, since the fwk is stopping
+        // and the lost dependency won't come up anymore.
+        if (m_frameworkBundle.getState() == Bundle.STOPPING) {
+            // Important: Notice that calling "super.removedService() might invoke our service "stop" 
+            // callback, which in turn might invoke the just removed service dependency. In this case, 
+            // our "invoke" method won't use the tracker to get the service dependency (because at this point, 
+            // the tracker has withdrawn its reference to the lost service). So, you will see that the "invoke" 
+            // method will use the "m_cachedService" instead ...
+            super.removedService(ref, service);
+        }
+        else {
+            // Unget what we got in addingService (see ServiceTracker 701.4.1)
+            m_context.ungetService(ref);
+            // Now, ask the service tracker if there is another available service (with a lower rank).
+            // If no more service dependencies are available, the tracker will then return null;
+            // and our invoke method will block the service method invocation, until another service
+            // becomes available.
+            m_cachedService = m_tracker.getService();
+        }
+    }
+
+    /**
+     * @returns our dependency instance. Unlike in ServiceDependency, we always returns our proxy.
+     */
+    //@Override
+    protected Object getService() {
+        return m_serviceInstance;
+    }
+
+    //@Override
+    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+        Object service = m_cachedService;
+        if (service == null) {
+            synchronized (this) {
+                long start = System.currentTimeMillis();
+                long waitTime = m_timeout;
+                while (service == null) {
+                    if (waitTime <= 0) {
+                        throw new IllegalStateException("Service unavailable: " + m_trackedServiceName.getName());
+                    }
+                    try {
+                        wait(waitTime);
+                    }
+                    catch (InterruptedException e) {
+                        throw new IllegalStateException("Service unavailable: " + m_trackedServiceName.getName());
+                    }
+                    waitTime = m_timeout - (System.currentTimeMillis() - start);
+                    service = m_cachedService;
+                }
+
+            }
+        }
+        try {
+            return method.invoke(service, args);
+        }
+        catch (IllegalAccessException iae) {
+            method.setAccessible(true);
+            return method.invoke(service, args);
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AbstractFactoryFilterIndex.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AbstractFactoryFilterIndex.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AbstractFactoryFilterIndex.java (added)
+++ felix/sandbox/pderop/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AbstractFactoryFilterIndex.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,95 @@
+/*
+ * 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.dm.impl.index;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.felix.dm.ServiceUtil;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class AbstractFactoryFilterIndex {
+
+	protected final Map /* <Long, SortedSet<ServiceReference>> */ m_sidToServiceReferencesMap = new HashMap();
+	protected final Map /* <ServiceListener, String> */ m_listenerToFilterMap = new HashMap();
+
+    public void addedService(ServiceReference reference, Object service) {
+        add(reference);
+    }
+
+    public void modifiedService(ServiceReference reference, Object service) {
+        modify(reference);
+    }
+
+    public void removedService(ServiceReference reference, Object service) {
+        remove(reference);
+    }
+    
+    public void add(ServiceReference reference) {
+        Long sid = ServiceUtil.getServiceIdObject(reference);
+        synchronized (m_sidToServiceReferencesMap) {
+            Set list = (Set) m_sidToServiceReferencesMap.get(sid);
+            if (list == null) {
+                list = new TreeSet();
+                m_sidToServiceReferencesMap.put(sid, list);
+            }
+            list.add(reference);
+        }
+    }
+
+    public void modify(ServiceReference reference) {
+        remove(reference);
+        add(reference);
+    }
+
+    public void remove(ServiceReference reference) {
+        Long sid = ServiceUtil.getServiceIdObject(reference);
+        synchronized (m_sidToServiceReferencesMap) {
+            Set list = (Set) m_sidToServiceReferencesMap.get(sid);
+            if (list != null) {
+                list.remove(reference);
+            }
+        }
+    }
+    
+    protected boolean referenceMatchesObjectClass(ServiceReference ref, String objectClass) {
+    	boolean matches = false;
+    	Object value = ref.getProperty(Constants.OBJECTCLASS);
+    	matches = Arrays.asList((String[])value).contains(objectClass);
+    	return matches;
+    }
+    
+    /** Structure to hold internal filter data. */
+    protected static class FilterData {
+        public long serviceId;
+        public String objectClass;
+        public int ranking;
+
+		public String toString() {
+			return "FilterData [serviceId=" + serviceId + "]";
+		}
+    }
+}