Committed the initial version of the dependency manager.
Added a "pom.xml" but since I'm fairly new at maven, this was copied from other examples.
Changed the package name to o.a.f.dependencymanager.
Modified a couple of classes so the code compiles with Java 1.4 (but we want to go for the minimum execution environment ultimately).

git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@388822 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/DefaultNullObject.java b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/DefaultNullObject.java
new file mode 100644
index 0000000..5b1acd1
--- /dev/null
+++ b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/DefaultNullObject.java
@@ -0,0 +1,69 @@
+/*
+ *   Copyright 2006 The Apache Software Foundation
+ *
+ *   Licensed 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.dependencymanager;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+
+/**
+ * Default null object implementation. Uses a dynamic proxy. Null objects are used
+ * as placeholders for services that are not available.
+ * 
+ * @author Marcel Offermans
+ */
+public class DefaultNullObject implements InvocationHandler {
+    private static final Boolean DEFAULT_BOOLEAN = Boolean.FALSE;
+    private static final Byte DEFAULT_BYTE = new Byte((byte) 0);
+    private static final Short DEFAULT_SHORT = new Short((short) 0);
+    private static final Integer DEFAULT_INT = new Integer(0);
+    private static final Long DEFAULT_LONG = new Long(0);
+    private static final Float DEFAULT_FLOAT = new Float(0.0f);
+    private static final Double DEFAULT_DOUBLE = new Double(0.0);
+    
+    /**
+     * Invokes a method on this null object. The method will return a default
+     * value without doing anything.
+     */
+    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+        Class returnType = method.getReturnType();
+        if (returnType.equals(Boolean.class) || returnType.equals(Boolean.TYPE)) {
+            return DEFAULT_BOOLEAN;
+        }
+        else if (returnType.equals(Byte.class) || returnType.equals(Byte.TYPE)) {
+            return DEFAULT_BYTE;
+        } 
+        else if (returnType.equals(Short.class) || returnType.equals(Short.TYPE)) {
+            return DEFAULT_SHORT;
+        } 
+        else if (returnType.equals(Integer.class) || returnType.equals(Integer.TYPE)) {
+            return DEFAULT_INT;
+        } 
+        else if (returnType.equals(Long.class) || returnType.equals(Long.TYPE)) {
+            return DEFAULT_LONG;
+        } 
+        else if (returnType.equals(Float.class) || returnType.equals(Float.TYPE)) {
+            return DEFAULT_FLOAT;
+        } 
+        else if (returnType.equals(Double.class) || returnType.equals(Double.TYPE)) {
+            return DEFAULT_DOUBLE;
+        } 
+        else {
+            return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/Dependency.java b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/Dependency.java
new file mode 100644
index 0000000..97a0866
--- /dev/null
+++ b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/Dependency.java
@@ -0,0 +1,66 @@
+/*
+ *   Copyright 2006 The Apache Software Foundation
+ *
+ *   Licensed 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.dependencymanager;
+
+/**
+ * Generic dependency for a service. A dependency can be required or not.
+ * A dependency will be activated by the service it belongs to. The service
+ * will call the <code>start(Service service)</code> and 
+ * <code>stop(Service service)</code> methods.
+ * 
+ * After it has been started, a dependency must callback
+ * the associated service's <code>dependencyAvailable()</code> and 
+ * <code>dependencyUnavailable()</code>
+ * methods. State changes of the dependency itself may only be made as long as
+ * the dependency is not 'active', meaning it is associated with a running service.
+ * 
+ * @author Marcel Offermans
+ */
+public interface Dependency {
+    /**
+     * Returns <code>true</code> if this a required dependency. Required dependencies
+     * are dependencies that must be available before the service can be activated.
+     * 
+     * @return <code>true</code> if the dependency is required
+     */
+    public boolean isRequired();
+    
+    /**
+     * Returns <code>true</code> if the dependency is available.
+     * 
+     * @return <code>true</code> if the dependency is available
+     */
+    public boolean isAvailable();
+    
+    /**
+     * Starts tracking the dependency. This activates some implementation
+     * specific mechanism to do the actual tracking. If the tracking discovers
+     * that the dependency becomes available, it should call 
+     * <code>dependencyAvailable()</code> on the service.
+     * 
+     * @param service the service that is associated with this dependency
+     */
+    public void start(Service service);
+    
+    /**
+     * Stops tracking the dependency. This deactivates the tracking. If the
+     * dependency was available, the tracker should call 
+     * <code>dependencyUnavaible()</code> before stopping itself to ensure
+     * that dependencies that aren't "active" are unavailable.
+     */
+    public void stop(Service service);
+}
diff --git a/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/DependencyActivatorBase.java b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/DependencyActivatorBase.java
new file mode 100644
index 0000000..38847b0
--- /dev/null
+++ b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/DependencyActivatorBase.java
@@ -0,0 +1,123 @@
+/*
+ *   Copyright 2006 The Apache Software Foundation
+ *
+ *   Licensed 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.dependencymanager;
+
+import java.util.List;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Base bundle activator class. Subclass this activator if you want to use dependency
+ * management in your bundle. There are two methods you should implement:
+ * <code>init()</code> and <code>destroy()</code>. Both methods take two arguments,
+ * the bundle context and the dependency manager. The dependency manager can be used
+ * to define all the dependencies.
+ * 
+ * @author Marcel Offermans
+ */
+public abstract class DependencyActivatorBase implements BundleActivator {
+    private BundleContext m_context;
+    private DependencyManager m_manager;
+    
+    /**
+     * Initialize the dependency manager. Here you can add all services and their dependencies.
+     * If something goes wrong and you do not want your bundle to be started, you can throw an
+     * exception. This exception will be passed on to the <code>start()</code> method of the
+     * bundle activator, causing the bundle not to start.
+     * 
+     * @param context the bundle context
+     * @param manager the dependency manager
+     * @throws Exception if the initialization fails
+     */
+    public abstract void init(BundleContext context, DependencyManager manager) throws Exception;
+    
+    /**
+     * Destroy the dependency manager. Here you can remove all services and their dependencies.
+     * Actually, the base class will clean up your dependencies anyway, so most of the time you
+     * don't need to do anything here.
+     * If something goes wrong and you do not want your bundle to be stopped, you can throw an
+     * exception. This exception will be passed on to the <code>stop()</code> method of the
+     * bundle activator, causing the bundle not to stop.
+     * 
+     * @param context the bundle context
+     * @param manager the dependency manager
+     * @throws Exception if the destruction fails
+     */
+    public abstract void destroy(BundleContext context, DependencyManager manager) throws Exception;
+
+    /**
+     * Start method of the bundle activator. Initializes the dependency manager
+     * and calls <code>init()</code>.
+     * 
+     * @param context the bundle context
+     */
+    public void start(BundleContext context) throws Exception {
+        m_context = context;
+        m_manager = new DependencyManager(context);
+        init(m_context, m_manager);
+    }
+
+    /**
+     * Stop method of the bundle activator. Calls the <code>destroy()</code> method
+     * and cleans up all left over dependencies.
+     * 
+     * @param context the bundle context
+     */
+    public void stop(BundleContext context) throws Exception {
+        destroy(m_context, m_manager);
+        cleanup(m_manager);
+        m_manager = null;
+        m_context = null;
+    }
+    
+    /**
+     * Creates a new service.
+     * 
+     * @return the new service
+     */
+    public Service createService() {
+        return new ServiceImpl(m_context);
+    }
+    
+    /**
+     * Creates a new service dependency.
+     * 
+     * @return the service dependency
+     */
+    public ServiceDependency createServiceDependency() {
+        return new ServiceDependency(m_context);
+    }
+
+    /**
+     * Cleans up all services and their dependencies.
+     * 
+     * @param manager the dependency manager
+     */
+    private void cleanup(DependencyManager manager) {
+        List services = manager.getServices();
+        for (int i = services.size() - 1; i >= 0; i--) {
+            Service service = (Service) services.get(i);
+            manager.remove(service);
+            // remove any state listeners that are still registered
+            if (service instanceof ServiceImpl) {
+                ServiceImpl si = (ServiceImpl) service;
+                si.removeStateListeners();
+            }
+        }
+    }
+}
diff --git a/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/DependencyManager.java b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/DependencyManager.java
new file mode 100644
index 0000000..e641615
--- /dev/null
+++ b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/DependencyManager.java
@@ -0,0 +1,92 @@
+/*
+ *   Copyright 2006 The Apache Software Foundation
+ *
+ *   Licensed 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.dependencymanager;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.osgi.framework.BundleContext;
+
+/**
+ * The dependency manager. Manages all services and their dependencies.
+ * 
+ * @author Marcel Offermans
+ */
+public class DependencyManager {
+    private BundleContext m_context;
+    private List m_services = Collections.synchronizedList(new ArrayList());
+
+    /**
+     * Creates a new dependency manager.
+     * 
+     * @param context the bundle context
+     */
+    public DependencyManager(BundleContext context) {
+        m_context = context;
+    }
+    
+    /**
+     * Adds a new service to the dependency manager. After the service was added
+     * it will be started immediately.
+     * 
+     * @param service the service to add
+     */
+    public void add(Service service) {
+        m_services.add(service);
+        service.start();
+    }
+
+    /**
+     * Removes a service from the dependency manager. Before the service is removed
+     * it is stopped first.
+     * 
+     * @param service the service to remove
+     */
+    public void remove(Service service) {
+        service.stop();
+        m_services.remove(service);
+    }
+
+    /**
+     * Creates a new service.
+     * 
+     * @return the new service
+     */
+    public Service createService() {
+        return new ServiceImpl(m_context);
+    }
+    
+    /**
+     * Creates a new service dependency.
+     * 
+     * @return the service dependency
+     */
+    public ServiceDependency createServiceDependency() {
+        return new ServiceDependency(m_context);
+    }
+    
+    /**
+     * Returns a list of services.
+     * 
+     * @return a list of services
+     */
+    public List getServices() {
+        return Collections.unmodifiableList(m_services);
+    }
+
+}
diff --git a/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/Service.java b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/Service.java
new file mode 100644
index 0000000..85a6c4a
--- /dev/null
+++ b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/Service.java
@@ -0,0 +1,195 @@
+/*
+ *   Copyright 2006 The Apache Software Foundation
+ *
+ *   Licensed 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.dependencymanager;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Service interface.
+ * 
+ * @author Marcel Offermans
+ */
+public interface Service {
+    /**
+     * Adds a new dependency to this service.
+     * 
+     * @param dependency the dependency to add
+     * @return this service
+     */
+    public Service add(Dependency dependency);
+    
+    /**
+     * Removes a dependency from this service.
+     * 
+     * @param dependency the dependency to remove
+     * @return this service
+     */
+    public Service remove(Dependency dependency);
+
+    /**
+     * Sets the public interface under which this service should be registered
+     * in the OSGi service registry.
+     *  
+     * @param serviceName the name of the service interface
+     * @param properties the properties for this service
+     * @return this service
+     */
+    public Service setInterface(String serviceName, Dictionary properties);
+    /**
+     * Sets the public interfaces under which this service should be registered
+     * in the OSGi service registry.
+     *  
+     * @param serviceNames the names of the service interface
+     * @param properties the properties for this service
+     * @return this service
+     */
+    public Service setInterface(String[] serviceNames, Dictionary properties);
+    
+    /**
+     * Sets the implementation for this service. You can actually specify
+     * an instance you have instantiated manually, or a <code>Class</code>
+     * that will be instantiated using its default constructor when the
+     * required dependencies are resolved (effectively giving you a lazy
+     * instantiation mechanism).
+     * 
+     * There are four special methods that are called when found through
+     * reflection to give you some life-cycle management options:
+     * <ol>
+     * <li><code>init()</code> when the implementation should be initialized,
+     * before it is actually registered as a service (if at all)</li>
+     * <li><code>start()</code> when the implementation has been registered
+     * as a service (if at all)</li>
+     * <li><code>stop()</code> when the implementation will be stopped, just
+     * before the service will go away (if it had been registered)</li>
+     * <li><code>destroy()</code>after the service has gone away (if it had
+     * been registered)</li>
+     * </ol>
+     * In short, this allows you to initialize your instance before it is
+     * registered, perform some post-initialization and pre-destruction code
+     * as well as final cleanup. If a method is not defined, it simply is not
+     * called, so you can decide which one(s) you need.
+     * 
+     * @param implementation the implementation
+     * @return this service
+     */
+    public Service setImplementation(Object implementation);
+    
+    /**
+     * Returns a list of dependencies.
+     * 
+     * @return a list of dependencies
+     */
+    public List getDependencies();
+    
+    /**
+     * Returns the service registration for this service. The method
+     * will return <code>null</code> if no service registration is
+     * available.
+     * 
+     * @return the service registration
+     */
+    public ServiceRegistration getServiceRegistration();
+    
+    /**
+     * Returns the service instance for this service. The method will
+     * return <code>null</code> if no service instance is available.
+     * 
+     * @return the service instance
+     */
+    public Object getService();
+
+    /**
+     * Returns the service properties associated with the service.
+     * 
+     * @return the properties or <code>null</code> if there are none
+     */
+    public Dictionary getServiceProperties();
+    
+    /**
+     * Sets the service properties associated with the service. If the service
+     * was already registered, it will be updated.
+     * 
+     * @param serviceProperties the properties
+     */
+    public void setServiceProperties(Dictionary serviceProperties);
+    
+    /**
+     * Sets the names of the methods used as callbacks. These methods, when found, are
+     * invoked as part of the life-cycle management of the service implementation. The
+     * methods should not have any parameters.
+     * 
+     * @param init the name of the init method
+     * @param start the name of the start method
+     * @param stop the name of the stop method
+     * @param destroy the name of the destroy method
+     * @return the service instance
+     */
+    public Service setCallbacks(String init, String start, String stop, String destroy);
+
+    // listener
+    /**
+     * Adds a service state listener to this service.
+     * 
+     * @param listener the state listener
+     */
+    public void addStateListener(ServiceStateListener listener);
+
+    /**
+     * Removes a service state listener from this service.
+     * 
+     * @param listener the state listener
+     */
+    public void removeStateListener(ServiceStateListener listener);
+    
+    // events, must be fired when the dependency is started/active
+    
+    /**
+     * Will be called when the dependency becomes available.
+     * 
+     * @param dependency the dependency
+     */
+    public void dependencyAvailable(Dependency dependency);
+    
+    /**
+     * Will be called when the dependency changes.
+     * 
+     * @param dependency the dependency
+     */
+    public void dependencyUnavailable(Dependency dependency);
+    
+    /**
+     * Will be called when the dependency becomes unavailable.
+     * 
+     * @param dependency the dependency
+     */
+    public void dependencyChanged(Dependency dependency);
+
+    /**
+     * Starts the service. This activates the dependency tracking mechanism
+     * for this service.
+     */
+    public void start();
+    
+    /**
+     * Stops the service. This deactivates the dependency tracking mechanism
+     * for this service.
+     */
+    public void stop();
+}
diff --git a/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceDependency.java b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceDependency.java
new file mode 100644
index 0000000..6f0d97f
--- /dev/null
+++ b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceDependency.java
@@ -0,0 +1,429 @@
+/*
+ *   Copyright 2006 The Apache Software Foundation
+ *
+ *   Licensed 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.dependencymanager;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Service dependency that can track an OSGi service.
+ * 
+ * @author Marcel Offermans
+ */
+public class ServiceDependency implements Dependency, ServiceTrackerCustomizer {
+    private boolean m_isRequired;
+    private Service m_service;
+    private ServiceTracker m_tracker;
+    private BundleContext m_context;
+    private boolean m_isAvailable;
+    private Class m_trackedServiceName;
+    private Object m_nullObject;
+    private String m_trackedServiceFilter;
+    private ServiceReference m_trackedServiceReference;
+    private boolean m_isStarted;
+    private Object m_callbackInstance;
+    private String m_callbackAdded;
+    private String m_callbackChanged;
+    private String m_callbackRemoved;
+    private boolean m_autoConfig;
+    private ServiceReference m_reference;
+    private Object m_serviceInstance;
+    
+    /**
+     * Creates a new service dependency.
+     * 
+     * @param context the bundle context
+     */
+    public ServiceDependency(BundleContext context) {
+        m_context = context;
+        m_autoConfig = true;
+    }
+
+    public boolean isRequired() {
+        return m_isRequired;
+    }
+
+    public boolean isAvailable() {
+        return m_isAvailable;
+    }
+    
+    public boolean isAutoConfig() {
+        return m_autoConfig;
+    }
+
+    public synchronized Object getService() {
+        Object service = null;
+        if (m_isStarted) {
+            service = m_tracker.getService();
+        }
+        if (service == null) {
+            service = getNullObject(); 
+        }
+        return service;
+    }
+
+    private Object getNullObject() {
+        if (m_nullObject == null) {
+            m_nullObject = Proxy.newProxyInstance(m_trackedServiceName.getClassLoader(), new Class[] {m_trackedServiceName}, new DefaultNullObject()); 
+        }
+        return m_nullObject;
+    }
+    
+    public Class getInterface() {
+        return m_trackedServiceName;
+    }
+
+    public synchronized void start(Service service) {
+        if (m_isStarted) {
+            throw new IllegalStateException("Service dependency was already started." + m_trackedServiceName);
+        }
+        m_service = service;
+        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.");
+                }
+            }
+            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;
+        m_tracker.open();
+    }
+
+    public synchronized void stop(Service service) {
+        if (!m_isStarted) {
+            throw new IllegalStateException("Service dependency was not started.");
+        }
+        m_tracker.close();
+        m_isStarted = false;
+        m_tracker = null;
+    }
+
+    public Object addingService(ServiceReference ref) {
+        Object service = m_context.getService(ref);
+        // we remember these for future reference, needed for required service callbacks
+        m_reference = ref;
+        m_serviceInstance = service;
+        return service;
+    }
+
+    public void addedService(ServiceReference ref, Object service) {
+        if (makeAvailable()) {
+            m_service.dependencyAvailable(this);
+        }
+        else {
+            m_service.dependencyChanged(this);
+        }
+        // try to invoke callback, if specified, but only for optional dependencies
+        // because callbacks for required dependencies are handled differently
+        if (!isRequired()) {
+            invokeAdded(ref, service);
+        }
+    }
+
+    public void invokeAdded() {
+        invokeAdded(m_reference, m_serviceInstance);
+    }
+    
+    public void invokeAdded(ServiceReference reference, Object serviceInstance) {
+        Object callbackInstance = getCallbackInstance();
+        if ((callbackInstance != null) && (m_callbackAdded != null)) {
+            try {
+                invokeCallbackMethod(callbackInstance, m_callbackAdded, reference, serviceInstance);
+            } catch (NoSuchMethodException e) {
+                // silently ignore this
+            }
+        }
+    }
+
+    public void modifiedService(ServiceReference ref, Object service) {
+        m_reference = ref;
+        m_serviceInstance = service;
+        m_service.dependencyChanged(this);
+        // only invoke the changed callback if the service itself is "active"
+        if (((ServiceImpl) m_service).isRegistered()) {
+            invokeChanged(ref, service);
+        }
+    }
+
+    public void invokeChanged(ServiceReference reference, Object serviceInstance) {
+        Object callbackInstance = getCallbackInstance();
+        if ((callbackInstance != null) && (m_callbackChanged != null)) {
+            try {
+                if (m_reference == null) {
+                    Thread.dumpStack();
+                }
+                invokeCallbackMethod(callbackInstance, m_callbackChanged, reference, serviceInstance);
+            }
+            catch (NoSuchMethodException e) {
+                // ignore when the service has no such method
+            }
+        }
+    }
+
+    public void removedService(ServiceReference ref, Object service) {
+        if (makeUnavailable()) {
+            m_service.dependencyUnavailable(this);
+        }
+        // try to invoke callback, if specified, but only for optional dependencies
+        // because callbacks for required dependencies are handled differently
+        if (!isRequired()) {
+            invokeRemoved(ref, service);
+        }
+        m_context.ungetService(ref);
+    }
+
+    public void invokeRemoved() {
+        invokeRemoved(m_reference, m_serviceInstance);
+    }
+    
+    public void invokeRemoved(ServiceReference reference, Object serviceInstance) {
+        Object callbackInstance = getCallbackInstance();
+        if ((callbackInstance != null) && (m_callbackRemoved != null)) {
+            try {
+                if (m_reference == null) {
+                    Thread.dumpStack();
+                }
+                invokeCallbackMethod(callbackInstance, m_callbackRemoved, reference, serviceInstance);
+            }
+            catch (NoSuchMethodException e) {
+                // ignore when the service has no such method
+            }
+        }
+    }
+    
+    private synchronized boolean makeAvailable() {
+        if (!m_isAvailable) {
+            m_isAvailable = true;
+            return true;
+        }
+        return false;
+    }
+    
+    private synchronized boolean makeUnavailable() {
+        if ((m_isAvailable) && (m_tracker.getServiceReference() == null)) {
+            m_isAvailable = false;
+            return true;
+        }
+        return false;
+    }
+    
+    private Object getCallbackInstance() {
+        Object callbackInstance = m_callbackInstance;
+        if (callbackInstance == null) {
+            callbackInstance = m_service.getService();
+        }
+        return callbackInstance;
+    }
+    
+    // TODO a lot of things in this method can be cached instead of done each time
+    // TODO Richard had an example where he could not invoke a private method
+    private void invokeCallbackMethod(Object instance, String methodName, ServiceReference reference, Object service) throws NoSuchMethodException {
+        Method method = null;
+        Class clazz = instance.getClass();
+        AccessibleObject.setAccessible(clazz.getDeclaredMethods(), true);
+        try {
+            try {
+                method = clazz.getDeclaredMethod(methodName, new Class[] {ServiceReference.class, Object.class});
+                method.invoke(instance, new Object[] {reference, service});
+            }
+            catch (NoSuchMethodException e) {
+                try {
+                    method = clazz.getDeclaredMethod(methodName, new Class[] {ServiceReference.class});
+                    method.invoke(instance, new Object[] {reference});
+                } 
+                catch (NoSuchMethodException e1) {
+                    try {
+                        method = clazz.getDeclaredMethod(methodName, new Class[] {Object.class});
+                        method.invoke(instance, new Object[] {service});
+                    } 
+                    catch (NoSuchMethodException e2) {
+                        try {
+                            method = clazz.getDeclaredMethod(methodName, new Class[] {m_trackedServiceName});
+                            method.invoke(instance, new Object[] {service});
+                        } 
+                        catch (NoSuchMethodException e3) {
+                            method = clazz.getDeclaredMethod(methodName, null);
+                            method.invoke(instance, null);
+                        }
+                    }
+                }
+            }
+        } catch (IllegalArgumentException e1) {
+            // TODO handle this exception, probably best to ignore it
+            e1.printStackTrace();
+        } catch (IllegalAccessException e1) {
+            // TODO handle this exception, probably best to ignore it
+            e1.printStackTrace();
+        } catch (InvocationTargetException e1) {
+            // TODO handle this exception, probably best to ignore it
+            e1.printStackTrace();
+        }
+    }
+    
+    // ----- CREATION
+
+    /**
+     * Sets the name of the service that should be tracked. 
+     * 
+     * @param serviceName the name of the service
+     * @return this service dependency
+     */
+    public synchronized ServiceDependency setService(Class serviceName) {
+        ensureNotActive();
+        if (serviceName == null) {
+            throw new IllegalArgumentException("Service name cannot be null.");
+        }
+        m_trackedServiceName = serviceName;
+        m_trackedServiceReference = null;
+        m_trackedServiceFilter = null;
+        return this;
+    }
+    
+    /**
+     * Sets the name of the service that should be tracked. You can either specify
+     * only the name, or the name and a filter. In the latter case, the filter is used
+     * to track the service and should only return services of the type that was specified
+     * in the name.
+     * 
+     * @param serviceName the name of the service
+     * @param serviceFilter the filter condition
+     * @return this service dependency
+     */
+    public synchronized ServiceDependency setService(Class serviceName, String serviceFilter) {
+        ensureNotActive();
+        if (serviceName == null) {
+            throw new IllegalArgumentException("Service name cannot be null.");
+        }
+        m_trackedServiceName = serviceName;
+        m_trackedServiceFilter = serviceFilter;
+        m_trackedServiceReference = null;
+        return this;
+    }
+
+    /**
+     * Sets the name of the service that should be tracked. You can either specify
+     * only the name, or the name and a reference. In the latter case, the service reference
+     * is used to track the service and should only return services of the type that was 
+     * specified in the name.
+     * 
+     * @param serviceName the name of the service
+     * @param serviceReference the service reference to track
+     * @return this service dependency
+     */
+    public synchronized ServiceDependency setService(Class serviceName, ServiceReference serviceReference) {
+        ensureNotActive();
+        if (serviceName == null) {
+            throw new IllegalArgumentException("Service name cannot be null.");
+        }
+        m_trackedServiceName = serviceName;
+        m_trackedServiceReference = serviceReference;
+        m_trackedServiceFilter = null;
+        return this;
+    }
+
+    /**
+     * Sets the required flag which determines if this service is required or not.
+     * 
+     * @param required the required flag
+     * @return this service dependency
+     */
+    public synchronized ServiceDependency setRequired(boolean required) {
+        ensureNotActive();
+        m_isRequired = required;
+        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
+     */
+    public synchronized ServiceDependency setAutoConfig(boolean autoConfig) {
+        ensureNotActive();
+        m_autoConfig = autoConfig;
+        return this;
+    }
+    
+    /**
+     * 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 service implementation.
+     * 
+     * @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
+     */
+    public synchronized ServiceDependency setCallbacks(String added, String removed) {
+        return setCallbacks(null, added, null, removed);
+    }
+    public synchronized ServiceDependency setCallbacks(String added, String changed, String removed) {
+        return setCallbacks(null, added, changed, removed);
+    }
+    public synchronized ServiceDependency setCallbacks(Object instance, String added, String removed) {
+        return setCallbacks(instance, added, null, removed);
+    }
+    
+    /**
+     * 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.
+     * 
+     * @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
+     */
+    public synchronized ServiceDependency setCallbacks(Object instance, String added, String changed, String removed) {
+        ensureNotActive();
+        m_callbackInstance = instance;
+        m_callbackAdded = added;
+        m_callbackChanged = changed;
+        m_callbackRemoved = removed;
+        return this;
+    }
+    
+    
+    private void ensureNotActive() {
+        if (m_tracker != null) {
+            throw new IllegalStateException("Cannot modify state while active.");
+        }
+    }
+    
+    public String toString() {
+        return "ServiceDependency[" + m_trackedServiceName + " " + m_trackedServiceFilter + " " + m_isRequired + "] for " + m_service;
+    }
+}
diff --git a/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceImpl.java b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceImpl.java
new file mode 100644
index 0000000..26a6a4f
--- /dev/null
+++ b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceImpl.java
@@ -0,0 +1,565 @@
+/*
+ *   Copyright 2006 The Apache Software Foundation
+ *
+ *   Licensed 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.dependencymanager;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Service implementation.
+ * 
+ * @author Marcel Offermans
+ */
+public class ServiceImpl implements Service {
+    private static final ServiceRegistration NULL_REGISTRATION;
+    private static final int STARTING = 1;
+    private static final int WAITING_FOR_REQUIRED = 2;
+    private static final int TRACKING_OPTIONAL = 3;
+    private static final int STOPPING = 4;
+    private static final String[] STATE_NAMES = {
+        "(unknown)", 
+        "starting", 
+        "waiting for required dependencies", 
+        "tracking optional dependencies", 
+        "stopping"};
+    
+    private BundleContext m_context;
+    private ServiceRegistration m_registration;
+    
+    private String m_callbackInit;
+    private String m_callbackStart;
+    private String m_callbackStop;
+    private String m_callbackDestroy;
+    
+    private List m_listeners = new ArrayList();
+    private ArrayList m_dependencies = new ArrayList();
+    private int m_state;
+    
+    private Object m_serviceInstance;
+    private Object m_implementation;
+    private Object m_serviceName;
+    private Dictionary m_serviceProperties;
+
+    public ServiceImpl(BundleContext context) {
+        m_state = STARTING;
+        m_context = context;
+        m_callbackInit = "init";
+        m_callbackStart = "start";
+        m_callbackStop = "stop";
+        m_callbackDestroy = "destroy";
+    }
+    
+    public Service add(Dependency dependency) {
+        synchronized (m_dependencies) {
+            m_dependencies.add(dependency);
+        }
+        if (m_state == WAITING_FOR_REQUIRED) {
+            // if we're waiting for required dependencies, and
+            // this is a required dependency, start tracking it
+            // ...otherwise, we don't need to do anything yet
+            if (dependency.isRequired()) {
+                dependency.start(this);
+            }
+        }
+        else if (m_state == TRACKING_OPTIONAL) {
+            // start tracking the dependency
+            dependency.start(this);
+            if (dependency.isRequired() && !dependency.isAvailable()) {
+                // if this is a required dependency and it can not
+                // be resolved right away, then we need to go back to 
+                // the waiting for required state, until this
+                // dependency is available
+                deactivateService();
+            }
+        }
+        return this;
+    }
+
+    public Service remove(Dependency dependency) {
+        synchronized (m_dependencies) {
+            m_dependencies.remove(dependency);
+        }
+        if (m_state == TRACKING_OPTIONAL) {
+            // if we're tracking optional dependencies, then any
+            // dependency that is removed can be stopped without
+            // causing state changes
+            dependency.stop(this);
+        }
+        else if (m_state == WAITING_FOR_REQUIRED) {
+            // if we're waiting for required dependencies, then
+            // we only need to stop tracking the dependency if it
+            // too is required; this might trigger a state change
+            dependency.stop(this);
+            if (allRequiredDependenciesAvailable()) {
+                activateService();
+            }
+        }
+        return this;
+    }
+
+    public List getDependencies() {
+        List list;
+        synchronized (m_dependencies) {
+            list = (List) m_dependencies.clone();
+        }
+        return list;
+    }
+    
+    public ServiceRegistration getServiceRegistration() {
+        return m_registration;
+    }
+    
+    public Object getService() {
+        return m_serviceInstance;
+    }
+    
+    public void dependencyAvailable(Dependency dependency) {
+        if ((dependency.isRequired()) 
+            && (m_state == WAITING_FOR_REQUIRED) 
+            && (allRequiredDependenciesAvailable())) {
+            activateService();
+        }
+        if ((!dependency.isRequired()) && (m_state == TRACKING_OPTIONAL)) {
+            updateInstance(dependency);
+        }
+    }
+
+    public void dependencyChanged(Dependency dependency) {
+        if (m_state == TRACKING_OPTIONAL) {
+            updateInstance(dependency);
+        }
+    }
+    
+    public void dependencyUnavailable(Dependency dependency) {
+        if (dependency.isRequired()) {
+            if (m_state == TRACKING_OPTIONAL) {
+                if (!allRequiredDependenciesAvailable()) {
+                    deactivateService();
+                }
+            }
+        }
+        else {
+            // optional dependency
+        }
+        if (m_state == TRACKING_OPTIONAL) {
+            updateInstance(dependency);
+        }
+    }
+
+    public synchronized void start() {
+        if ((m_state != STARTING) && (m_state != STOPPING)) {
+            throw new IllegalStateException("Cannot start from state " + STATE_NAMES[m_state]);
+        }
+        startTrackingRequired();
+        if (allRequiredDependenciesAvailable() && (m_state == WAITING_FOR_REQUIRED)) {
+            activateService();
+        }
+    }
+
+    public synchronized void stop() {
+        if ((m_state != WAITING_FOR_REQUIRED) && (m_state != TRACKING_OPTIONAL)) {
+            if ((m_state > 0) && (m_state < STATE_NAMES.length)) {
+                throw new IllegalStateException("Cannot stop from state " + STATE_NAMES[m_state]);
+            }
+            throw new IllegalStateException("Cannot stop from unknown state.");
+        }
+        if (m_state == TRACKING_OPTIONAL) {
+            deactivateService();
+        }
+        stopTrackingRequired();
+    }
+
+    private void activateService() {
+        // service activation logic, first we initialize the service instance itself
+        // meaning it is created if necessary and the bundle context is set
+        initService();
+        // then we invoke the init callback so the service can further initialize
+        // itself
+        invoke(m_callbackInit);
+        // now is the time to configure the service, meaning all required
+        // dependencies will be set and any callbacks called
+        configureService();
+        // inform the state listeners we're starting
+        stateListenersStarting();
+        // start tracking optional services
+        startTrackingOptional();
+        // invoke the start callback, since we're now ready to be used
+        invoke(m_callbackStart);
+        // register the service in the framework's service registry
+        registerService();
+        // inform the state listeners we've started
+        stateListenersStarted();
+    }
+
+    private void deactivateService() {
+        // service deactivation logic, first inform the state listeners
+        // we're stopping
+        stateListenersStopping();
+        // then, unregister the service from the framework
+        unregisterService();
+        // invoke the stop callback
+        invoke(m_callbackStop);
+        // stop tracking optional services
+        stopTrackingOptional();
+        // inform the state listeners we've stopped
+        stateListenersStopped();
+        // invoke the destroy callback
+        invoke(m_callbackDestroy);
+        // destroy the service instance
+        destroyService();
+    }
+
+    private void invoke(String name) {
+        if (name != null) {
+            // invoke method if it exists
+            AccessibleObject.setAccessible(m_serviceInstance.getClass().getDeclaredMethods(), true);
+            try {
+                m_serviceInstance.getClass().getDeclaredMethod(name, null).invoke(m_serviceInstance, null);
+            }
+            catch (NoSuchMethodException e) {
+                // ignore this, we don't care if the method does not exist
+            }
+            catch (Exception e) {
+                // TODO handle this exception
+                e.printStackTrace();
+            }
+        }
+    }
+    
+    private synchronized void stateListenersStarting() {
+        Iterator i = m_listeners.iterator();
+        while (i.hasNext()) {
+            ServiceStateListener ssl = (ServiceStateListener) i.next();
+            ssl.starting(this);
+        }
+    }
+
+    private synchronized void stateListenersStarted() {
+        Iterator i = m_listeners.iterator();
+        while (i.hasNext()) {
+            ServiceStateListener ssl = (ServiceStateListener) i.next();
+            ssl.started(this);
+        }
+    }
+
+    private synchronized void stateListenersStopping() {
+        Iterator i = m_listeners.iterator();
+        while (i.hasNext()) {
+            ServiceStateListener ssl = (ServiceStateListener) i.next();
+            ssl.stopping(this);
+        }
+    }
+
+    private synchronized void stateListenersStopped() {
+        Iterator i = m_listeners.iterator();
+        while (i.hasNext()) {
+            ServiceStateListener ssl = (ServiceStateListener) i.next();
+            ssl.stopped(this);
+        }
+    }
+
+    private boolean allRequiredDependenciesAvailable() {
+        Iterator i = getDependencies().iterator();
+        while (i.hasNext()) {
+            Dependency dependency = (Dependency) i.next();
+            if (dependency.isRequired() && !dependency.isAvailable()) {
+                return false;
+            }
+        }
+        return true;
+    }
+    
+    private void startTrackingOptional() {
+        m_state = TRACKING_OPTIONAL;
+        Iterator i = getDependencies().iterator();
+        while (i.hasNext()) {
+            Dependency dependency = (Dependency) i.next();
+            if (!dependency.isRequired()) {
+                dependency.start(this);
+            }
+        }
+    }
+
+    private void stopTrackingOptional() {
+        m_state = WAITING_FOR_REQUIRED;
+        Iterator i = getDependencies().iterator();
+        while (i.hasNext()) {
+            Dependency dependency = (Dependency) i.next();
+            if (!dependency.isRequired()) {
+                dependency.stop(this);
+            }
+        }
+    }
+
+    private void startTrackingRequired() {
+        m_state = WAITING_FOR_REQUIRED;
+        Iterator i = getDependencies().iterator();
+        while (i.hasNext()) {
+            Dependency dependency = (Dependency) i.next();
+            if (dependency.isRequired()) {
+                dependency.start(this);
+            }
+        }
+    }
+
+    private void stopTrackingRequired() {
+        m_state = STOPPING;
+        Iterator i = getDependencies().iterator();
+        while (i.hasNext()) {
+            Dependency dependency = (Dependency) i.next();
+            if (dependency.isRequired()) {
+                dependency.stop(this);
+            }
+        }
+    }
+
+    private void initService() {
+        if (m_implementation instanceof Class) {
+            // instantiate
+            try {
+                m_serviceInstance = ((Class) m_implementation).newInstance();
+            } catch (InstantiationException e) {
+                // TODO handle this exception
+                e.printStackTrace();
+            } catch (IllegalAccessException e) {
+                // TODO handle this exception
+                e.printStackTrace();
+            }
+        }
+        else {
+            m_serviceInstance = m_implementation;
+        }
+        // configure the bundle context
+        configureImplementation(BundleContext.class, m_context);
+        configureImplementation(ServiceRegistration.class, NULL_REGISTRATION);
+        
+    }
+    
+    private void configureService() {
+        // configure all services (the optional dependencies might be configured
+        // as null objects but that's what we want at this point)
+        configureServices();
+    }
+
+    private void destroyService() {
+        unconfigureServices();
+        m_serviceInstance = null;
+    }
+    
+    private void registerService() {
+        if (m_serviceName != null) {
+            ServiceRegistrationImpl wrapper = new ServiceRegistrationImpl();
+            m_registration = wrapper;
+            configureImplementation(ServiceRegistration.class, wrapper);
+            // service name can either be a string or an array of strings
+            ServiceRegistration registration;
+            if (m_serviceName instanceof String) {
+                registration = m_context.registerService((String) m_serviceName, m_serviceInstance, m_serviceProperties);
+            }
+            else {
+                registration = m_context.registerService((String[]) m_serviceName, m_serviceInstance, m_serviceProperties);
+            }
+            wrapper.setServiceRegistration(registration);
+        }
+    }
+    
+    private void unregisterService() {
+        if (m_serviceName != null) {
+            m_registration.unregister();
+            configureImplementation(ServiceRegistration.class, NULL_REGISTRATION);
+        }
+    }
+    
+    private void updateInstance(Dependency dependency) {
+        if (dependency instanceof ServiceDependency) {
+            ServiceDependency sd = (ServiceDependency) dependency;
+            // update the dependency in the service instance (it will use
+            // a null object if necessary)
+            if (sd.isAutoConfig()) {
+                configureImplementation(sd.getInterface(), sd.getService());
+            }
+        }
+    }
+    
+    /**
+     * Configure a field in the service implementation. The service implementation
+     * is searched for fields that have the same type as the class that was specified
+     * and for each of these fields, the specified instance is filled in.
+     * 
+     * @param clazz the class to search for
+     * @param instance the instance to fill in
+     */
+    private void configureImplementation(Class clazz, Object instance) {
+        Class serviceClazz = m_serviceInstance.getClass();
+        while (serviceClazz != null) {
+            Field[] fields = serviceClazz.getDeclaredFields();
+            AccessibleObject.setAccessible(fields, true);
+            for (int j = 0; j < fields.length; j++) {
+                if (fields[j].getType().equals(clazz)) {
+                    try {
+                        // synchronized makes sure the field is actually written to immediately
+                        synchronized (new Object()) {
+                            fields[j].set(m_serviceInstance, instance);
+                        }
+                    }
+                    catch (Exception e) {
+                        System.err.println("Exception while trying to set " + fields[j].getName() +
+                            " of type " + fields[j].getType().getName() +
+                            " by classloader " + fields[j].getType().getClassLoader() +
+                            " which should equal type " + clazz.getName() +
+                            " by classloader " + clazz.getClassLoader() +
+                            " of type " + serviceClazz.getName() +
+                            " by classloader " + serviceClazz.getClassLoader() +
+                            " on " + m_serviceInstance + 
+                            " by classloader " + m_serviceInstance.getClass().getClassLoader() +
+                            "\nDumping stack:"
+                        );
+                        e.printStackTrace();
+                        System.out.println("C: " + clazz);
+                        System.out.println("I: " + instance);
+                        System.out.println("I:C: " + instance.getClass().getClassLoader());
+                        Class[] classes = instance.getClass().getInterfaces();
+                        for (int i = 0; i < classes.length; i++) {
+                            Class c = classes[i];
+                            System.out.println("I:C:I: " + c);
+                            System.out.println("I:C:I:C: " + c.getClassLoader());
+                        }
+                        System.out.println("F: " + fields[j]);
+                        throw new IllegalStateException("Could not set field " + fields[j].getName() + " on " + m_serviceInstance);
+                    }
+                }
+            }
+            serviceClazz = serviceClazz.getSuperclass();
+        }
+    }
+
+    private void configureServices() {
+        Iterator i = getDependencies().iterator();
+        while (i.hasNext()) {
+            Dependency dependency = (Dependency) i.next();
+            if (dependency instanceof ServiceDependency) {
+                ServiceDependency sd = (ServiceDependency) dependency;
+                if (sd.isAutoConfig()) {
+                    configureImplementation(sd.getInterface(), sd.getService());
+                }
+                // for required dependencies, we invoke any callbacks here
+                if (sd.isRequired()) {
+                    sd.invokeAdded();
+                }
+            }
+        }
+    }
+    
+    private void unconfigureServices() {
+        Iterator i = getDependencies().iterator();
+        while (i.hasNext()) {
+            Dependency dependency = (Dependency) i.next();
+            if (dependency instanceof ServiceDependency) {
+                ServiceDependency sd = (ServiceDependency) dependency;
+                // for required dependencies, we invoke any callbacks here
+                if (sd.isRequired()) {
+                    sd.invokeRemoved();
+                }
+            }
+        }
+    }
+
+    public synchronized void addStateListener(ServiceStateListener listener) {
+        m_listeners.add(listener);
+        if (m_state == TRACKING_OPTIONAL) {
+        	listener.starting(this);
+        	listener.started(this);
+        }
+    }
+
+    public synchronized void removeStateListener(ServiceStateListener listener) {
+        m_listeners.remove(listener);
+    }
+
+    synchronized void removeStateListeners() {
+        m_listeners.clear();
+    }
+    
+    public synchronized Service setInterface(String serviceName, Dictionary properties) {
+        ensureNotActive();
+        m_serviceName = serviceName;
+        m_serviceProperties = properties;
+        return this;
+    }
+
+    public synchronized Service setInterface(String[] serviceName, Dictionary properties) {
+        ensureNotActive();
+        m_serviceName = serviceName;
+        m_serviceProperties = properties;
+        return this;
+    }
+    
+    public synchronized Service setCallbacks(String init, String start, String stop, String destroy) {
+        ensureNotActive();
+        m_callbackInit = init;
+        m_callbackStart = start;
+        m_callbackStop = stop;
+        m_callbackDestroy = destroy;
+        return this;
+    }
+    
+    public synchronized Service setImplementation(Object implementation) {
+        ensureNotActive();
+        m_implementation = implementation;
+        return this;
+    }
+    
+    private void ensureNotActive() {
+        if ((m_state == TRACKING_OPTIONAL) || (m_state == WAITING_FOR_REQUIRED)) {
+            throw new IllegalStateException("Cannot modify state while active.");
+        }
+    }
+    boolean isRegistered() {
+        return (m_state == TRACKING_OPTIONAL);
+    }
+    
+    public String toString() {
+        return "ServiceImpl[" + m_serviceName + " " + m_implementation + "]";
+    }
+
+    public synchronized Dictionary getServiceProperties() {
+        if (m_serviceProperties != null) {
+            return (Dictionary) ((Hashtable) m_serviceProperties).clone();
+        }
+        return null;
+    }
+
+    public synchronized void setServiceProperties(Dictionary serviceProperties) {
+        m_serviceProperties = serviceProperties;
+        if (isRegistered() && (m_serviceName != null) && (m_serviceProperties != null)) {
+            m_registration.setProperties(m_serviceProperties);
+        }
+    }
+    
+    static {
+        NULL_REGISTRATION = (ServiceRegistration) Proxy.newProxyInstance(ServiceImpl.class.getClassLoader(), new Class[] {ServiceRegistration.class}, new DefaultNullObject()); 
+    }
+}
diff --git a/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceRegistrationImpl.java b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceRegistrationImpl.java
new file mode 100644
index 0000000..6e5189d
--- /dev/null
+++ b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceRegistrationImpl.java
@@ -0,0 +1,85 @@
+/*
+ *   Copyright 2006 The Apache Software Foundation
+ *
+ *   Licensed 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.dependencymanager;
+
+import java.util.Dictionary;
+
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * A wrapper around a service registration that blocks until the
+ * service registration is available.
+ * 
+ * @author Marcel Offermans
+ */
+public class ServiceRegistrationImpl implements ServiceRegistration {
+    private ServiceRegistration m_registration;
+
+    public ServiceRegistrationImpl() {
+        m_registration = null;
+    }
+    
+    public ServiceReference getReference() {
+        ensureRegistration();
+        return m_registration.getReference();
+    }
+
+    public void setProperties(Dictionary dictionary) {
+        ensureRegistration();
+        m_registration.setProperties(dictionary);
+    }
+
+    public void unregister() {
+        ensureRegistration();
+        m_registration.unregister();
+    }
+
+    public boolean equals(Object obj) {
+        ensureRegistration();
+        return m_registration.equals(obj);
+    }
+
+    public int hashCode() {
+        ensureRegistration();
+        return m_registration.hashCode();
+    }
+
+    public String toString() {
+        ensureRegistration();
+        return m_registration.toString();
+    }
+    
+    private synchronized void ensureRegistration() {
+        while (m_registration == null) {
+            try {
+                wait();
+            }
+            catch (InterruptedException ie) {
+                // we were interrupted so hopefully we will now have a
+                // service registration ready; if not we wait again
+            }
+        }
+    }
+
+    void setServiceRegistration(ServiceRegistration registration) {
+        m_registration = registration;
+        synchronized (this) {
+            notifyAll();
+        }
+    }
+}
diff --git a/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceStateListener.java b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceStateListener.java
new file mode 100644
index 0000000..468a266
--- /dev/null
+++ b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceStateListener.java
@@ -0,0 +1,52 @@
+/*
+ *   Copyright 2006 The Apache Software Foundation
+ *
+ *   Licensed 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.dependencymanager;
+
+/**
+ * This interface can be used to register a service state listener. Service
+ * state listeners are called whenever a service state changes. You get notified
+ * when the service is starting, started, stopping and stopped. Each callback
+ * includes a reference to the service in question.
+ * 
+ * @author Marcel Offermans
+ */
+public interface ServiceStateListener {
+    /**
+     * Called when the service is starting.
+     * 
+     * @param service the service
+     */
+    public void starting(Service service);
+    /**
+     * Called when the service is started.
+     * 
+     * @param service the service
+     */
+    public void started(Service service);
+    /**
+     * Called when the service is stopping.
+     * 
+     * @param service the service
+     */
+    public void stopping(Service service);
+    /**
+     * Called when the service is stopped.
+     * 
+     * @param service the service
+     */
+    public void stopped(Service service);
+}
diff --git a/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceTracker.java b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceTracker.java
new file mode 100644
index 0000000..1135c80
--- /dev/null
+++ b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceTracker.java
@@ -0,0 +1,847 @@
+/*
+ *   Copyright 2006 The Apache Software Foundation
+ *
+ *   Licensed 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.dependencymanager;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * TODO copied this from the OSGi specification, but it's not clear if that
+ * is allowed or not, for now I modified as little as possible but I might
+ * integrate only the parts I want as soon as this code is finished. Perhaps
+ * it would be better to borrow the Knopflerfish implementation here.
+ * 
+ * @author Marcel Offermans
+ */
+public class ServiceTracker implements ServiceTrackerCustomizer
+{
+    /**
+	 * Bundle context this <tt>ServiceTracker</tt> object is tracking against.
+	 */
+    protected final BundleContext context;
+
+    /**
+	 * Filter specifying search criteria for the services to track.
+	 * @since 1.1
+	 */
+    protected final Filter filter;
+
+    /**
+	 * Tracked services: <tt>ServiceReference</tt> object -> customized Object
+	 * and <tt>ServiceListener</tt> object
+	 */
+    private Tracked tracked;
+
+    /** <tt>ServiceTrackerCustomizer</tt> object for this tracker. */
+    private ServiceTrackerCustomizer customizer;
+
+    /**
+	 * Create a <tt>ServiceTracker</tt> object on the specified <tt>ServiceReference</tt> object.
+	 *
+	 * <p>The service referenced by the specified <tt>ServiceReference</tt> object
+	 * will be tracked by this <tt>ServiceTracker</tt> object.
+	 *
+	 * @param context   <tt>BundleContext</tt> object against which the tracking is done.
+	 * @param reference <tt>ServiceReference</tt> object for the service to be tracked.
+	 * @param customizer The customizer object to call when services are
+	 * added, modified, or removed in this <tt>ServiceTracker</tt> object.
+	 * If customizer is <tt>null</tt>, then this <tt>ServiceTracker</tt> object will be used
+	 * as the <tt>ServiceTrackerCustomizer</tt> object and the <tt>ServiceTracker</tt>
+	 * object will call the <tt>ServiceTrackerCustomizer</tt> methods on itself.
+	 */
+    public ServiceTracker(BundleContext context, ServiceReference reference,
+                          ServiceTrackerCustomizer customizer)
+    {
+        this.context = context;
+        this.customizer = (customizer == null) ? this : customizer;
+
+        try
+        {
+            this.filter = context.createFilter("("+Constants.SERVICE_ID+"="+reference.getProperty(Constants.SERVICE_ID).toString()+")");
+        }
+        catch (InvalidSyntaxException e)
+        {
+            throw new RuntimeException("unexpected InvalidSyntaxException: "+e.getMessage());
+        }
+    }
+
+    /**
+	 * Create a <tt>ServiceTracker</tt> object on the specified class name.
+	 *
+	 * <p>Services registered under the specified class name will be tracked
+	 * by this <tt>ServiceTracker</tt> object.
+	 *
+	 * @param context   <tt>BundleContext</tt> object against which the tracking is done.
+	 * @param clazz     Class name of the services to be tracked.
+	 * @param customizer The customizer object to call when services are
+	 * added, modified, or removed in this <tt>ServiceTracker</tt> object.
+	 * If customizer is <tt>null</tt>, then this <tt>ServiceTracker</tt> object will be used
+	 * as the <tt>ServiceTrackerCustomizer</tt> object and the <tt>ServiceTracker</tt> object
+	 * will call the <tt>ServiceTrackerCustomizer</tt> methods on itself.
+	 */
+    public ServiceTracker(BundleContext context, String clazz,
+                          ServiceTrackerCustomizer customizer)
+    {
+        this.context = context;
+        this.customizer = (customizer == null) ? this : customizer;
+
+        try
+        {
+            this.filter = context.createFilter("("+Constants.OBJECTCLASS+"="+clazz+")");
+        }
+        catch (InvalidSyntaxException e)
+        {
+            throw new RuntimeException("unexpected InvalidSyntaxException: "+e.getMessage());
+        }
+
+        if (clazz == null)
+        {
+            throw new NullPointerException();
+        }
+    }
+
+    /**
+	 * Create a <tt>ServiceTracker</tt> object on the specified <tt>Filter</tt> object.
+	 *
+	 * <p>Services which match the specified <tt>Filter</tt> object will be tracked
+	 * by this <tt>ServiceTracker</tt> object.
+	 *
+	 * @param context   <tt>BundleContext</tt> object against which the tracking is done.
+	 * @param filter    <tt>Filter</tt> object to select the services to be tracked.
+	 * @param customizer The customizer object to call when services are
+	 * added, modified, or removed in this <tt>ServiceTracker</tt> object.
+	 * If customizer is null, then this <tt>ServiceTracker</tt> object will be used
+	 * as the <tt>ServiceTrackerCustomizer</tt> object and the <tt>ServiceTracker</tt>
+	 * object will call the <tt>ServiceTrackerCustomizer</tt> methods on itself.
+	 * @since 1.1
+	 */
+    public ServiceTracker(BundleContext context, Filter filter,
+                          ServiceTrackerCustomizer customizer)
+    {
+        this.context = context;
+        this.filter = filter;
+        this.customizer = (customizer == null) ? this : customizer;
+
+        if ((context == null) || (filter == null))
+        {
+            throw new NullPointerException();
+        }
+    }
+
+    /**
+	 * Open this <tt>ServiceTracker</tt> object and begin tracking services.
+	 *
+	 * <p>Services which match the search criteria specified when
+	 * this <tt>ServiceTracker</tt> object was created are now tracked
+	 * by this <tt>ServiceTracker</tt> object.
+	 *
+	 * @throws java.lang.IllegalStateException if the <tt>BundleContext</tt>
+	 * object with which this <tt>ServiceTracker</tt> object was created is no longer valid.
+	 */
+    public synchronized void open()
+    {
+        if (tracked == null)
+        {
+            tracked = new Tracked(customizer, filter);
+
+            ServiceReference[] references;
+
+            synchronized (tracked)
+            {
+                context.addServiceListener(tracked);
+
+                try
+                {
+                    references = context.getServiceReferences(null, filter.toString());
+                }
+                catch (InvalidSyntaxException e)
+                {
+                    throw new RuntimeException("unexpected InvalidSyntaxException");
+                }
+            }
+
+            /* Call tracked outside of synchronized region */
+            if (references != null)
+            {
+                int size = references.length;
+
+                for (int i=0; i < size; i++)
+                {
+                    ServiceReference reference = references[i];
+
+                    /* if the service is still registered */
+                    if (reference.getBundle() != null)
+                    {
+                        tracked.track(reference);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+	 * Close this <tt>ServiceTracker</tt> object.
+	 *
+	 * <p>This method should be called when this <tt>ServiceTracker</tt> object
+	 * should end the tracking of services.
+	 */
+
+    public synchronized void close()
+    {
+        if (tracked != null)
+        {
+            tracked.close();
+
+            ServiceReference references[] = getServiceReferences();
+
+            Tracked outgoing = tracked;
+            tracked = null;
+
+            try
+            {
+                context.removeServiceListener(outgoing);
+            }
+            catch (IllegalStateException e)
+            {
+                /* In case the context was stopped. */
+            }
+
+            if (references != null)
+            {
+                for (int i = 0; i < references.length; i++)
+                {
+                    outgoing.untrack(references[i]);
+                }
+            }
+        }
+    }
+
+    /**
+	 * Properly close this <tt>ServiceTracker</tt> object when finalized.
+	 * This method calls the <tt>close</tt> method to close this <tt>ServiceTracker</tt> object
+	 * if it has not already been closed.
+	 *
+	 */
+    protected void finalize() throws Throwable
+    {
+        close();
+    }
+
+    /**
+	 * Default implementation of the <tt>ServiceTrackerCustomizer.addingService</tt> method.
+	 *
+	 * <p>This method is only called when this <tt>ServiceTracker</tt> object
+	 * has been constructed with a <tt>null ServiceTrackerCustomizer</tt> argument.
+	 *
+	 * The default implementation returns the result of
+	 * calling <tt>getService</tt>, on the
+	 * <tt>BundleContext</tt> object with which this <tt>ServiceTracker</tt> object was created,
+	 * passing the specified <tt>ServiceReference</tt> object.
+	 * <p>This method can be overridden in a subclass to customize
+	 * the service object to be tracked for the service
+	 * being added. In that case, take care not
+	 * to rely on the default implementation of removedService that will unget the service.
+	 *
+	 * @param reference Reference to service being added to this
+	 * <tt>ServiceTracker</tt> object.
+	 * @return The service object to be tracked for the service
+	 * added to this <tt>ServiceTracker</tt> object.
+	 * @see ServiceTrackerCustomizer
+	 */
+    public Object addingService(ServiceReference reference)
+    {
+        return context.getService(reference);
+    }
+    public void addedService(ServiceReference ref, Object service) {
+        // do nothing
+    }
+    
+    /**
+	 * Default implementation of the <tt>ServiceTrackerCustomizer.modifiedService</tt> method.
+	 *
+	 * <p>This method is only called when this <tt>ServiceTracker</tt> object
+	 * has been constructed with a <tt>null ServiceTrackerCustomizer</tt> argument.
+	 *
+	 * The default implementation does nothing.
+	 *
+	 * @param reference Reference to modified service.
+	 * @param service The service object for the modified service.
+	 * @see ServiceTrackerCustomizer
+	 */
+    public void modifiedService(ServiceReference reference, Object service)
+    {
+    }
+
+    /**
+	 * Default implementation of the <tt>ServiceTrackerCustomizer.removedService</tt> method.
+	 *
+	 * <p>This method is only called when this <tt>ServiceTracker</tt> object
+	 * has been constructed with a <tt>null ServiceTrackerCustomizer</tt> argument.
+	 *
+	 * The default implementation
+	 * calls <tt>ungetService</tt>, on the
+	 * <tt>BundleContext</tt> object with which this <tt>ServiceTracker</tt> object was created,
+	 * passing the specified <tt>ServiceReference</tt> object.
+	 * <p>This method can be overridden in a subclass. If the default
+	 * implementation of <tt>addingService</tt> method was used, this method must unget the service.
+	 *
+	 * @param reference Reference to removed service.
+	 * @param object The service object for the removed service.
+	 * @see ServiceTrackerCustomizer
+	 */
+    public void removedService(ServiceReference reference, Object object)
+    {
+        context.ungetService(reference);
+    }
+
+    /**
+	 * Wait for at least one service to be tracked by this <tt>ServiceTracker</tt> object.
+	 * <p>It is strongly recommended that <tt>waitForService</tt> is not used
+	 * during the calling of the <tt>BundleActivator</tt> methods. <tt>BundleActivator</tt> methods are
+	 * expected to complete in a short period of time.
+	 *
+	 * @param timeout time interval in milliseconds to wait.  If zero,
+	 * the method will wait indefinately.
+	 * @return Returns the result of <tt>getService()</tt>.
+	 * @throws IllegalArgumentException If the value of timeout is
+	 * negative.
+	 */
+    public Object waitForService(long timeout) throws InterruptedException
+    {
+        if (timeout < 0)
+        {
+            throw new IllegalArgumentException("timeout value is negative");
+        }
+
+        Object object = getService();
+
+        while (object == null)
+        {
+            Tracked tracked = this.tracked;     /* use local var since we are not synchronized */
+
+            if (tracked == null)    /* if ServiceTracker is not open */
+            {
+                return null;
+            }
+
+            synchronized (tracked)
+            {
+                if (tracked.size() == 0)
+                {
+                    tracked.wait(timeout);
+                }
+            }
+
+            object = getService();
+
+            if (timeout > 0)
+            {
+                return object;
+            }
+        }
+
+        return object;
+    }
+
+    /**
+	 * Return an array of <tt>ServiceReference</tt> objects for all services
+	 * being tracked by this <tt>ServiceTracker</tt> object.
+	 *
+	 * @return Array of <tt>ServiceReference</tt> objects or <tt>null</tt> if no service
+	 * are being tracked.
+	 */
+    public ServiceReference[] getServiceReferences()
+    {
+        Tracked tracked = this.tracked;     /* use local var since we are not synchronized */
+
+        if (tracked == null)    /* if ServiceTracker is not open */
+        {
+            return null;
+        }
+
+        synchronized (tracked)
+        {
+            int size = tracked.size();
+
+            if (size == 0)
+            {
+                return null;
+            }
+
+            ServiceReference references[] = new ServiceReference[size];
+
+            Enumeration trackedServiceRefs = tracked.keys();
+
+            for (int i = 0; i < size; i++)
+            {
+                references[i] = (ServiceReference)trackedServiceRefs.nextElement();
+            }
+
+            return references;
+        }
+    }
+
+    /**
+	 * Return an array of service objects for all services
+	 * being tracked by this <tt>ServiceTracker</tt> object.
+	 *
+	 * @return Array of service objects or <tt>null</tt> if no service
+	 * are being tracked.
+	 */
+    public Object[] getServices()
+    {
+        Tracked tracked = this.tracked;     /* use local var since we are not synchronized */
+
+        if (tracked == null)    /* if ServiceTracker is not open */
+        {
+            return null;
+        }
+
+        synchronized (tracked)
+        {
+            int size = tracked.size();
+
+            if (size == 0)
+            {
+                return null;
+            }
+
+            Object objects[] = new Object[size];
+
+            Enumeration trackedServices = tracked.elements();
+
+            for (int i = 0; i < size; i++)
+            {
+                objects[i] = trackedServices.nextElement();
+            }
+
+            return objects;
+        }
+    }
+
+    /**
+	 * Returns a <tt>ServiceReference</tt> object for one of the services
+	 * being tracked by this <tt>ServiceTracker</tt> object.
+	 *
+	 * <p>If multiple services are being tracked, the service
+	 * with the highest ranking (as specified in its <tt>service.ranking</tt> property) is
+	 * returned.
+	 *
+	 * <p>If there is a tie in ranking, the service with the lowest
+	 * service ID (as specified in its <tt>service.id</tt> property); that is,
+	 * the service that was registered first is returned.
+	 * <p>This is the same algorithm used by <tt>BundleContext.getServiceReference</tt>.
+	 *
+	 * @return <tt>ServiceReference</tt> object or <tt>null</tt> if no service is being tracked.
+	 * @since 1.1
+	 */
+    public ServiceReference getServiceReference()
+    {
+        ServiceReference[] references = getServiceReferences();
+
+        int length = (references == null) ? 0 : references.length;
+
+        if (length > 0)         /* if a service is being tracked */
+        {
+            int index = 0;
+
+            if (length > 1)     /* if more than one service, select highest ranking */
+            {
+                int rankings[] = new int[length];
+                int count = 0;
+                int maxRanking = Integer.MIN_VALUE;
+
+                for (int i = 0 ; i < length; i++)
+                {
+                    Object property = references[i].getProperty(Constants.SERVICE_RANKING);
+
+                    int ranking = (property instanceof Integer)
+                                    ? ((Integer)property).intValue() : 0;
+
+                    rankings[i] = ranking;
+
+                    if (ranking > maxRanking)
+                    {
+                        index = i;
+                        maxRanking = ranking;
+                        count = 1;
+                    }
+                    else
+                    {
+                        if (ranking == maxRanking)
+                        {
+                            count++;
+                        }
+                    }
+                }
+
+                if (count > 1)  /* if still more than one service, select lowest id */
+                {
+                    long minId = Long.MAX_VALUE;
+
+                    for (int i = 0 ; i < length; i++)
+                    {
+                        if (rankings[i] == maxRanking)
+                        {
+                            long id = ((Long)(references[i].getProperty(Constants.SERVICE_ID))).longValue();
+
+                            if (id < minId)
+                            {
+                                index = i;
+                                minId = id;
+                            }
+                        }
+                    }
+                }
+            }
+
+            return references[index];
+        }
+
+        return null;
+    }
+
+    /**
+	 * Returns the service object for the specified <tt>ServiceReference</tt> object
+	 * if the referenced service is
+	 * being tracked by this <tt>ServiceTracker</tt> object.
+	 *
+	 * @param reference Reference to the desired service.
+	 * @return Service object or <tt>null</tt> if the service referenced by the
+	 * specified <tt>ServiceReference</tt> object is not being tracked.
+	 */
+    public Object getService(ServiceReference reference)
+    {
+        Tracked tracked = this.tracked;     /* use local var since we are not synchronized */
+
+        if (tracked == null)    /* if ServiceTracker is not open */
+        {
+            return null;
+        }
+
+        return tracked.get(reference);
+    }
+
+    /**
+	 * Returns a service object for one of the services
+	 * being tracked by this <tt>ServiceTracker</tt> object.
+	 *
+	 * <p>If any services are being tracked, this method returns the result
+	 * of calling <tt>getService(getServiceReference())</tt>.
+	 *
+	 * @return Service object or <tt>null</tt> if no service is being tracked.
+	 */
+    public Object getService()
+    {
+        ServiceReference reference = getServiceReference();
+
+        if (reference != null)
+        {
+            return getService(reference);
+        }
+
+        return null;
+    }
+
+    /**
+	 * Remove a service from this <tt>ServiceTracker</tt> object.
+	 *
+	 * The specified service will be removed from this
+	 * <tt>ServiceTracker</tt> object.
+	 * If the specified service was being tracked then the
+	 * <tt>ServiceTrackerCustomizer.removedService</tt> method will be
+	 * called for that service.
+	 *
+	 * @param reference Reference to the service to be removed.
+	 */
+    public void remove(ServiceReference reference)
+    {
+        Tracked tracked = this.tracked;     /* use local var since we are not synchronized */
+
+        if (tracked == null)    /* if ServiceTracker is not open */
+        {
+            return;
+        }
+
+        tracked.untrack(reference);
+    }
+
+    /**
+	 * Return the number of services being tracked by this <tt>ServiceTracker</tt> object.
+	 *
+	 * @return Number of services being tracked.
+	 */
+
+    public int size()
+    {
+        Tracked tracked = this.tracked;     /* use local var since we are not synchronized */
+
+        if (tracked == null)    /* if ServiceTracker is not open */
+        {
+            return 0;
+        }
+
+        return tracked.size();
+    }
+
+    /**
+	 * Returns the tracking count for this <tt>ServiceTracker</tt> object.
+	 *
+	 * The tracking count is initialized to 0 when this
+	 * <tt>ServiceTracker</tt> object is opened. Every time a service is
+	 * added or removed from this <tt>ServiceTracker</tt> object
+	 * the tracking count is incremented.
+	 *
+	 * <p>The tracking count can
+	 * be used to determine if this <tt>ServiceTracker</tt> object
+	 * has added or removed a service by comparing a tracking count value
+	 * previously collected with the current tracking count value. If the value
+	 * has not changed, then no service has been added or removed from
+	 * this <tt>ServiceTracker</tt> object
+	 * since the previous tracking count was collected.
+	 *
+	 * @since 1.2
+	 * @return The tracking count for this <tt>ServiceTracker</tt> object
+	 * or -1 if this <tt>ServiceTracker</tt> object is not open.
+	 */
+    public int getTrackingCount()
+    {
+        Tracked tracked = this.tracked;     /* use local var since we are not synchronized */
+
+        if (tracked == null)    /* if ServiceTracker is not open */
+        {
+            return -1;
+        }
+
+        return tracked.getTrackingCount();
+    }
+
+    /**
+	 * Inner class to track the services.
+	 * This class is a hashtable mapping <tt>ServiceReference</tt> object -> customized Object.
+	 * This class also implements the <tt>ServiceListener</tt> interface for the tracker.
+	 * This is not a public class. It is only for use by the implementation
+	 * of the <tt>ServiceTracker</tt> class.
+	 *
+	 */
+    static class Tracked extends Hashtable implements ServiceListener {
+        private ServiceTrackerCustomizer customizer;    /** ServiceTrackerCustomizer object for this tracker. */
+        private Filter filter;      /** The filter used to track */
+        private Vector adding;      /** list of ServiceReferences currently being added */
+        private boolean closed;     /** true if the tracked object is closed */
+        private int trackingCount;  /** modification count */
+
+
+        /**
+		 * Tracked constructor.
+		 *
+		 * @param customizer Customizer object from parent <tt>ServiceTracker</tt> object.
+		 * @param filter <tt>Filter</tt> object from parent <tt>ServiceTracker</tt> object.
+		 */
+        protected Tracked(ServiceTrackerCustomizer customizer, Filter filter)
+        {
+            super();
+            this.customizer = customizer;
+            this.filter = filter;
+            closed = false;
+            trackingCount = 0;
+            adding = new Vector(10, 10);
+        }
+
+        /**
+		 * Called by the parent <tt>ServiceTracker</tt> object when it is closed.
+		 */
+        protected void close()
+        {
+            closed = true;
+        }
+
+        /**
+		 * Called by the parent <tt>ServiceTracker</tt> object to get
+		 * the modification count.
+		 *
+		 * @since 1.2
+		 * @return modification count.
+		 */
+        protected int getTrackingCount()
+        {
+            return trackingCount;
+        }
+
+        /**
+		 * <tt>ServiceListener</tt> method for the <tt>ServiceTracker</tt> class.
+		 * This method must NOT be synchronized to avoid deadlock potential.
+		 *
+		 * @param event <tt>ServiceEvent</tt> object from the framework.
+		 */
+        public void serviceChanged(ServiceEvent event)
+        {
+            /* Check if we had a delayed call (which could happen when we close). */
+            if (closed)
+            {
+                return;
+            }
+
+            ServiceReference reference = event.getServiceReference();
+
+            switch (event.getType())
+            {
+                case ServiceEvent.REGISTERED:
+                case ServiceEvent.MODIFIED:
+                    if (filter.match(reference))
+                    {
+                        track(reference);
+                        /* If the customizer throws an unchecked exception, it is safe to let it propagate */
+                    }
+                    else
+                    {
+                        untrack(reference);
+                        /* If the customizer throws an unchecked exception, it is safe to let it propagate */
+                    }
+
+                    break;
+
+                case ServiceEvent.UNREGISTERING:
+                    untrack(reference);
+                    /* If the customizer throws an unchecked exception, it is safe to let it propagate */
+
+                    break;
+            }
+        }
+
+        /**
+		 * Begin to track the referenced service.
+		 *
+		 * @param reference Reference to a service to be tracked.
+		 */
+        protected void track(ServiceReference reference)
+        {
+            Object object = get(reference);
+
+            if (object != null)     /* we are already tracking the service */
+            {
+                /* Call customizer outside of synchronized region */
+                customizer.modifiedService(reference, object);
+                /* If the customizer throws an unchecked exception, it is safe to let it propagate */
+
+                return;
+            }
+
+            synchronized (this)
+            {
+                if (adding.indexOf(reference, 0) != -1) /* if this service is already
+                                                         * in the process of being added. */
+                {
+                    return;
+                }
+
+                adding.addElement(reference);   /* mark this service is being added */
+            }
+
+            boolean becameUntracked = false;
+
+            /* Call customizer outside of synchronized region */
+            try
+            {
+                object = customizer.addingService(reference);
+                /* If the customizer throws an unchecked exception, it will propagate after the finally */
+            }
+            finally
+            {
+                boolean needToCallback = false;
+                synchronized (this)
+                {
+                    if (adding.removeElement(reference))    /* if the service was not untracked
+                                                             * during the customizer callback */
+                    {
+                        if (object != null)
+                        {
+                            put(reference, object);
+
+                            trackingCount++;            /* increment modification count */
+
+                            notifyAll();
+                            
+                            // Marrs: extra callback added, will be invoked after the synchronized block
+                            needToCallback = true;
+                        }
+                    }
+                    else
+                    {
+                        becameUntracked = true;
+                    }
+                }
+                if (needToCallback) {
+                    customizer.addedService(reference, object);
+                }
+            }
+
+            /* The service became untracked during
+			 * the customizer callback.
+			 */
+            if (becameUntracked)
+            {
+                /* Call customizer outside of synchronized region */
+                customizer.removedService(reference, object);
+                /* If the customizer throws an unchecked exception, it is safe to let it propagate */
+            }
+        }
+
+        /**
+		 * Discontinue tracking the referenced service.
+		 *
+		 * @param reference Reference to the tracked service.
+		 */
+        protected void untrack(ServiceReference reference)
+        {
+            Object object;
+
+            synchronized (this)
+            {
+                if (adding.removeElement(reference)) /* if the service is in the process
+                                                      * of being added */
+                {
+                    return;                          /* in case the service is untracked
+                                                      * while in the process of adding */
+                }
+
+                object = this.remove(reference);     /* must remove from tracker before calling
+                                                      * customizer callback */
+
+                if (object == null)             /* are we actually tracking the service */
+                {
+                    return;
+                }
+
+                trackingCount++;                /* increment modification count */
+            }
+
+            /* Call customizer outside of synchronized region */
+            customizer.removedService(reference, object);
+            /* If the customizer throws an unchecked exception, it is safe to let it propagate */
+        }
+    }
+}
diff --git a/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceTrackerCustomizer.java b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceTrackerCustomizer.java
new file mode 100644
index 0000000..0a8c778
--- /dev/null
+++ b/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceTrackerCustomizer.java
@@ -0,0 +1,33 @@
+/*
+ *   Copyright 2006 The Apache Software Foundation
+ *
+ *   Licensed 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.dependencymanager;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * TODO modified version of a normal service tracker customizer, this one has an
+ * extra callback "addedservice" that is invoked after the service has been added
+ * to the tracker (and therefore is accessible through the tracker API)
+ * 
+ * @author Marcel Offermans
+ */
+public interface ServiceTrackerCustomizer {
+    public Object addingService(ServiceReference ref);
+    public void addedService(ServiceReference ref, Object service);
+    public void modifiedService(ServiceReference ref, Object service);
+    public void removedService(ServiceReference ref, Object service);
+}