moved new dm4 from sandbox to trunk.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1663056 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/org.apache.felix.dependencymanager/.classpath b/dependencymanager/org.apache.felix.dependencymanager/.classpath
new file mode 100644
index 0000000..f89ae43
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" output="bin" path="src"/>
+	<classpathentry kind="src" output="bin_test" path="test"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
+	<classpathentry kind="con" path="aQute.bnd.classpath.container"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/dependencymanager/org.apache.felix.dependencymanager/.gitignore b/dependencymanager/org.apache.felix.dependencymanager/.gitignore
new file mode 100644
index 0000000..90dde36
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/.gitignore
@@ -0,0 +1,3 @@
+/bin/
+/bin_test/
+/generated/
diff --git a/dependencymanager/org.apache.felix.dependencymanager/.project b/dependencymanager/org.apache.felix.dependencymanager/.project
new file mode 100644
index 0000000..8640bab
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.apache.felix.dependencymanager</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>bndtools.core.bndbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>bndtools.core.bndnature</nature>
+	</natures>
+</projectDescription>
diff --git a/dependencymanager/org.apache.felix.dependencymanager/.settings/org.eclipse.jdt.core.prefs b/dependencymanager/org.apache.felix.dependencymanager/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..7341ab1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,11 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.7
diff --git a/dependencymanager/org.apache.felix.dependencymanager/bnd.bnd b/dependencymanager/org.apache.felix.dependencymanager/bnd.bnd
new file mode 100644
index 0000000..435bc97
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/bnd.bnd
@@ -0,0 +1,43 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+-buildpath:  \
+	osgi.core;version=4.2,\
+	osgi.cmpn;version=4.2,\
+	org.mockito.mockito-all;version=1.9,\
+	${junit}
+Private-Package: \
+	org.apache.felix.dm.impl,\
+	org.apache.felix.dm.impl.index,\
+	org.apache.felix.dm.impl.index.multiproperty,\
+	org.apache.felix.dm.impl.metatype
+Export-Package: \
+	org.apache.felix.dm,\
+	org.apache.felix.dm.tracker,\
+	org.apache.felix.dm.context
+Include-Resource: META-INF/=resources/LICENSE,\
+	META-INF/=resources/NOTICE,\
+	META-INF/=resources/DEPENDENCIES,\
+	META-INF/=resources/changelog.txt
+Import-Package: !org.junit,!org.mockito.*,*
+Bundle-Activator: org.apache.felix.dm.impl.Activator
+Bundle-Version: 4.0.0
+Bundle-Name: Apache Felix Dependency Manager
+Bundle-Description: Provides dynamic service and component dependency management
+Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+Bundle-DocURL: http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager.html
+Bundle-Vendor: The Apache Software Foundation
+Bundle-Category: osgi
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager/design.txt b/dependencymanager/org.apache.felix.dependencymanager/design.txt
new file mode 100644
index 0000000..94dfce7
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/design.txt
@@ -0,0 +1,47 @@
+7 feb 2014 (marrs & uiterlix):
+
+This prototype demonstrates the new concurrency principles that form the basis for the DM:
+
+ * All external events that influence the state of dependencies are recorded and given 
+   to the serial executor of the component. We record whatever data comes in, so when the
+   actual job is run by the serial executor, we still have access to the original data
+   without having to access other sources whose state might have changed since.
+ * The serial executor of a component will execute a job immediately if it is being called
+   by the thread that is already executing jobs.
+ * If the serial executor of a component had not yet started a job, it will queue and start
+   it on the current thread.
+ * If the serial executor gets invoked from a different thread than the one currently
+   executing jobs, the job will be put at the end of the queue. As mentioned before, any
+   data associated with the event will also be recorded so it is available when the job
+   executes.
+ * State in the component and dependency can only be modified via the serial executor
+   thread. This means we don't need explicit synchronization anywhere.
+
+20 sept 2014 (pderop):
+
+ * Added support for parallelism: To allow components to be started and handled in parallel, you can
+   now register in the OSGi service registry a ComponentExecutorFactory service that is used to get
+   an Executor for the management of all components dependencies/lifecycle callbacks. See javadoc
+   from the org.apache.felix.dm.ComponentExecutorFactory interface for more informations.
+   
+   You can also take a look at the the org.apache.felix.dependencymanager.samples project, which is
+   registering a ComponentExecutorFactory from org.apache.felix.dependencymanager.samples.tpool
+   bundle.
+  
+   See also the following property in the org.apache.felix.dependencymanager.samples/bnd.bnd 
+
+	org.apache.felix.dependencymanager.parallel=\
+		'!org.apache.felix.dependencymanager.samples.tpool, *',\
+
+  Here, all components will be handled by Executors provided by the ComponentExecutorFactory,
+  except those having a package starting with "org.apache.felix.dependencymanager.samples.tpool"
+  (because the threadpool is itself defined using the Dependency Manager API).
+
+
+
+
+
+
+
+
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager/resources/DEPENDENCIES b/dependencymanager/org.apache.felix.dependencymanager/resources/DEPENDENCIES
new file mode 100644
index 0000000..bb3ee11
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/resources/DEPENDENCIES
@@ -0,0 +1,24 @@
+Apache Felix Dependency Manager
+Copyright 2011-2015 The Apache Software Foundation
+
+This software was developed at the Apache Software Foundation
+(http://www.apache.org) and may have dependencies on other
+Apache software licensed under Apache License 2.0.
+
+I. Included Third-Party Software
+
+This product includes software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2014).
+Licensed under the Apache License 2.0.
+
+II. Used Third-Party Software
+
+This product uses software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2014).
+Licensed under the Apache License 2.0.
+
+III. Overall License Summary
+
+- Apache License 2.0
diff --git a/dependencymanager/org.apache.felix.dependencymanager/resources/LICENSE b/dependencymanager/org.apache.felix.dependencymanager/resources/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/resources/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
diff --git a/dependencymanager/org.apache.felix.dependencymanager/resources/NOTICE b/dependencymanager/org.apache.felix.dependencymanager/resources/NOTICE
new file mode 100644
index 0000000..7df085c
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/resources/NOTICE
@@ -0,0 +1,12 @@
+Apache Felix Dependency Manager
+Copyright 2011-2015 The Apache Software Foundation
+
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2015).
+Licensed under the Apache License 2.0.
diff --git a/dependencymanager/org.apache.felix.dependencymanager/resources/changelog.txt b/dependencymanager/org.apache.felix.dependencymanager/resources/changelog.txt
new file mode 100644
index 0000000..3cda464
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/resources/changelog.txt
@@ -0,0 +1,85 @@
+Release 4.0.0:
+-------------
+
+FELIX-2706: Support callback delegation for Configuration Dependencies
+FELIX-3914: Log unsuccessful field injections
+FELIX-4158: ComponentDeclaration should give access to component information
+FELIX-4304: DependencyManager ComponentImpl should not assume all service properties are stored in a Hashtable
+FELIX-4394: Race problems in DependencyManager Configuration Dependency
+FELIX-4426: Allow DM to manage collections of services
+FELIX-4588: createCopy method ConfigurationDependency produces a malfunctioning clone
+FELIX-4594: Propagation from dependencies overwrites service properties
+FELIX-4598: BundleDependency can effectively track only one bundle
+FELIX-4600: Cherrypicking of propagated properties
+FELIX-4602: TemporalServiceDependency does not properly propagate RuntimeExceptions
+FELIX-4667: "top" command for the Dependency Manager Shell
+FELIX-4672: Allow callbacks to third party instance for adapters
+FELIX-4673: Log any error thrown when trying to create a null object
+FELIX-4680: Add more DM ServiceDependency callback signatures
+FELIX-4807: New thread model for Dependency Manager
+
+Release 3.2.0:
+--------------
+
+FELIX-3910: Race conditions in DependencyManager
+FELIX-4334: ServiceDependency properties change callback issue
+FELIX-4285: Remove abstract modifier from DependencyActivatorBase.destroy()
+FELIX-4294: Dependency Manager Shell improvements
+FELIX-4305: DependencyMananer Adapters - service properties propagation
+FELIX-4002: ComponentStateListener.started is invoked twice when the listener is added in the start method.
+FELIX-4395: DependencyManager Configuration Dependency does not clone some class fields.
+FELIX-4014: handleAspectAwareRemoved in ServiceDependencyImpl can cause a possible deadlock
+FELIX-4097: Allow debug logging for specific instances of service dependencies to debug wiring issues.
+FELIX-4098: Aspect swap sometimes invokes the callbacks in the wrong order in a multithreaded application.
+FELIX-4099: Add support for negations in the multi property filter index.
+FELIX-4186: NPE in DependencyManager Logger
+FELIX-4226: Add option to have the dependency manager log against a single BundleContext's LogService.
+FELIX-4361: Possible ConcurrentModificationException in DependencyManager.getComponents()
+
+Release 3.1.0
+-------------
+
+FELIX-303 - Support for compositions
+FELIX-1201 - Issue with DM and CM
+FELIX-1278 - DM/ AutoConfig is active event if setCallbacks method has been invoked
+FELIX-1464 - issue when using a negation in ldap service dependency filter
+FELIX-1546 - DM/Temporal Dependency/Bound Service Replacement features
+FELIX-2078 - Not all callbacks invoked when declaring a service as required and starting it after dependencies
+FELIX-2344 - DM / callback method is not invoked when an extra dependency is defined within an "init" component method.
+FELIX-2348 - DM/ ResourceAdapter NPE
+FELIX-2369 - DM/ Service start method is invoked even if an extra required dependency is unavailable
+FELIX-2816 - dependency manager calls init() twice
+FELIX-2947 - Filter indices must use service trackers that track all services and aspects.
+FELIX-2953 - Make the cache that InvocationUtil uses configurable.
+FELIX-2954 - DM/ annotated component factory does not allow to provide a component instance explicitly
+FELIX-2955 - IllegalStateException when doing a 'dm notavail' shell command.
+FELIX-2956 - DM/ json should be embedded in the annotation scanner plugin
+FELIX-2964 - DM/ NPE on some dependency manager adapters, when "auto-configuration" mode is disabled.
+FELIX-2970 - DM/ Factory Configuration Adapter Service does not copy adapter dependencies
+FELIX-2976 - InvocationUtil cache is not used properly for determining that methods do not exist in a class
+FELIX-2987 - DependencyManager ConfigurationDependency update isn't propagated to super classes
+FELIX-3008 - NPE in ServiceRegistryCache when dependency manager bundle is not started first
+FELIX-3042 - [PATCH] Add a convenience clear() method on DependencyManager
+FELIX-3057 - getServiceReferences() should not return an empty array
+FELIX-3186 - Adapter services do not get their adapted services transparently replaced when an aspect is added to them.
+FELIX-3201 - Offer more functional callback methods for services that have aspects on them.
+FELIX-3218 - ServiceTracker performance is not optimal with a service dependency that results in n-thousands of injected services.
+FELIX-3264 - Dependency manager shell should not print the state of optional dependencies when not all required ones are available
+FELIX-3292 - Allow passing of resource properties to a resource handler for use with resource adapters.
+FELIX-3337 - DependencyManager/Updated configuration dependency does not propagate to provided service properties
+FELIX-3402 - DependencyManager stop can trigger IndexOutOfBoundsException
+FELIX-3423 - AdapterImpl copies the DependencyManager.ASPECT service property when adapting an aspect.
+FELIX-3424 - Add support for changed callbacks on Aspect services.
+FELIX-3425 - Provide a filter index for adapter services.
+FELIX-3475 - DependencyManager compatibility bundle - ServiceDependencyImpl does not override toString
+FELIX-3564 - Memory leak in Filterindex / ServiceRegistryCache
+FELIX-3592 - ServiceDependencyImpl does not copy the swapped callback in it's constructor.
+FELIX-3617 - Missing toString methods in DependencyManager compat bundle
+FELIX-3682 - Dependency Manager Annotation-3.0.0 module doesn't expose OSGI meta information in MANIFEST.MF
+FELIX-3828 - Aspect and Adapter filter indices to not handle components that have been bound with multiple interfaces correctly.
+
+
+Release 3.0.0
+-------------
+
+Major, backward incompatible release. Start of recorded changes.
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/.gitignore b/dependencymanager/org.apache.felix.dependencymanager/src/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/.gitignore
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/BundleDependency.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/BundleDependency.java
new file mode 100644
index 0000000..76bb73b
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/BundleDependency.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface BundleDependency extends Dependency, ComponentDependencyDeclaration {
+    /**
+     * Sets the callbacks for this dependency. These callbacks can be used as hooks whenever a dependency is added or removed.
+     * When you specify callbacks, the auto configuration feature is automatically turned off, because we're assuming you don't
+     * need it in this case.
+     * 
+     * @param added the method to call when a bundle was added
+     * @param removed the method to call when a bundle was removed
+     * @return the bundle dependency
+     */
+    public BundleDependency setCallbacks(String added, String removed);
+
+    /**
+     * Sets the callbacks for this dependency. These callbacks can be used as hooks whenever a dependency is added, changed or
+     * removed. When you specify callbacks, the auto configuration feature is automatically turned off, because we're assuming
+     * you don't need it in this case.
+     * 
+     * @param added the method to call when a bundle was added
+     * @param changed the method to call when a bundle was changed
+     * @param removed the method to call when a bundle was removed
+     * @return the bundle dependency
+     */
+    public BundleDependency setCallbacks(String added, String changed, String removed);
+
+    /**
+     * Sets the callbacks for this dependency. These callbacks can be used as hooks whenever a dependency is added or removed.
+     * They are called on the instance you provide. When you specify callbacks, the auto configuration feature is automatically
+     * turned off, because we're assuming you don't need it in this case.
+     * 
+     * @param instance the instance to call the callbacks on
+     * @param added the method to call when a bundle was added
+     * @param removed the method to call when a bundle was removed
+     * @return the bundle dependency
+     */
+    public BundleDependency setCallbacks(Object instance, String added, String removed);
+
+    /**
+     * Sets the callbacks for this dependency. These callbacks can be used as hooks whenever a dependency is added, changed or
+     * removed. They are called on the instance you provide. When you specify callbacks, the auto configuration feature is
+     * automatically turned off, because we're assuming you don't need it in this case.
+     * 
+     * @param instance the instance to call the callbacks on
+     * @param added the method to call when a bundle was added
+     * @param changed the method to call when a bundle was changed
+     * @param removed the method to call when a bundle was removed
+     * @return the bundle dependency
+     */
+    public BundleDependency setCallbacks(Object instance, String added, String changed, String removed);
+
+    /**
+     * Enables auto configuration for this dependency. This means the component implementation (composition) will be
+     * injected with this bundle dependency automatically.
+     * 
+     * @param autoConfig <code>true</code> to enable auto configuration
+     * @return the bundle dependency
+     */
+    public BundleDependency setAutoConfig(boolean autoConfig);
+
+    /**
+     * Sets the dependency to be required.
+     * 
+     * @param required <code>true</code> if this bundle dependency is required
+     * @return the bundle dependency
+     */
+    public BundleDependency setRequired(boolean required);
+
+    /**
+     * Sets the bundle to depend on directly.
+     * 
+     * @param bundle the bundle to depend on
+     * @return the bundle dependency
+     */
+    public BundleDependency setBundle(Bundle bundle);
+
+    /**
+     * Sets the filter condition to depend on. Filters are matched against the full manifest of a bundle.
+     * 
+     * @param filter the filter condition
+     * @return the bundle dependency
+     * @throws IllegalArgumentException if the filter is invalid
+     */
+    public BundleDependency setFilter(String filter) throws IllegalArgumentException;
+
+    /**
+     * Sets the bundle state mask to depend on. The OSGi BundleTracker explains this mask in more detail, but
+     * it is basically a mask with flags for each potential state a bundle can be in.
+     * 
+     * @param mask the mask to use
+     * @return the bundle dependency
+     */
+    public BundleDependency setStateMask(int mask);
+
+    /**
+     * Sets property propagation. If set to <code>true</code> any bundle manifest properties will be added
+     * to the service properties of the component that has this dependency (if it registers as a service).
+     * 
+     * @param propagate <code>true</code> to propagate the bundle manifest properties
+     * @return the bundle dependency
+     */
+    public BundleDependency setPropagate(boolean propagate);
+    
+    /**
+     * Sets an Object instance and a callback method used to propagate some properties to the provided service properties.
+     * The method will be invoked on the specified object instance and must have one of the following signatures:
+     * <ul><li>Dictionary callback(ServiceReference, Object service) 
+     * <li>Dictionary callback(ServiceReference)
+     * </ul>
+     * @param instance the Object instance which is used to retrieve propagated service properties 
+     * @param method the method to invoke for retrieving the properties to be propagated to the service properties.
+     * @return this service dependency.
+     */
+    public BundleDependency setPropagate(Object instance, String method);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/Component.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/Component.java
new file mode 100644
index 0000000..0b618ad
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/Component.java
@@ -0,0 +1,292 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm;
+
+import java.util.Dictionary;
+
+import org.osgi.framework.ServiceRegistration;
+
+
+/**
+ * Component interface. Components are the main building blocks for OSGi applications.
+ * They can publish themselves as a service, and they can have dependencies. These
+ * dependencies will influence their life cycle as component will only be activated
+ * when all required dependencies are available.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface Component {
+    /**
+     * Sets the implementation for this component. 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 life cycle management options:
+     * <ol>
+     * <li><code>init()</code> is invoked after the instance has been
+     * created and dependencies have been resolved, and can be used to
+     * initialize the internal state of the instance or even to add more
+     * dependencies based on runtime state</li>
+     * <li><code>start()</code> is invoked right before the service is 
+     * registered</li>
+     * <li><code>stop()</code> is invoked right after the service is
+     * unregistered</li>
+     * <li><code>destroy()</code> is invoked after all dependencies are
+     * removed</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. If you need even more
+     * fine-grained control, you can register as a service state listener too.
+     * 
+     * @param implementation the implementation
+     * @return this component
+     * @see ComponentStateListener
+     */
+	public Component setImplementation(Object implementation);
+
+    /**
+     * Adds dependency(ies) to this component, atomically. If the component is already active or if you add
+     * dependencies from the init method, then you should add all the dependencies in one single add method call 
+     * (using the varargs argument), because this method may trigger component activation (like
+     * the ServiceTracker.open() method does).
+     * 
+     * @param dependencies the dependencies to add.
+     * @return this component
+     */
+	public Component add(Dependency ... dependencies);
+	
+	/**
+	 * Removes a dependency from the component.
+	 * @param d the dependency to remove
+	 * @return this component
+	 */
+	public Component remove(Dependency d);
+
+    /**
+     * Adds a component state listener to this component.
+     * 
+     * @param listener the state listener
+     */
+	public Component add(ComponentStateListener listener);
+
+    /**
+     * Removes a component state listener from this component.
+     * 
+     * @param listener the state listener
+     */
+	public Component remove(ComponentStateListener listener);
+
+    /**
+     * Sets the public interface under which this component 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 component
+     */
+	public Component setInterface(String serviceName, Dictionary<?,?> properties);
+
+    /**
+     * Sets the public interfaces under which this component should be registered
+     * in the OSGi service registry.
+     *  
+     * @param serviceNames the names of the service interface
+     * @param properties the properties for these services
+     * @return this component
+     */
+	public Component setInterface(String[] serviceNames, Dictionary<?, ?> properties);
+
+    /**
+     * Configures auto configuration of injected classes in the component instance.
+     * The following injections are currently performed, unless you explicitly
+     * turn them off:
+     * <dl>
+     * <dt>BundleContext</dt><dd>the bundle context of the bundle</dd>
+     * <dt>ServiceRegistration</dt><dd>the service registration used to register your service</dd>
+     * <dt>DependencyManager</dt><dd>the dependency manager instance</dd>
+     * <dt>Component</dt><dd>the component instance of the dependency manager</dd>
+     * </dl>
+     * 
+     * @param clazz the class (from the list above)
+     * @param autoConfig <code>false</code> to turn off auto configuration
+     */
+	public Component setAutoConfig(Class<?> clazz, boolean autoConfig);
+
+    /**
+     * Configures auto configuration of injected classes in the component instance.
+     * 
+     * @param clazz the class (from the list above)
+     * @param instanceName the name of the instance to inject the class into
+     * @see #setAutoConfig(Class, boolean)
+     */
+	public Component setAutoConfig(Class<?> clazz, String instanceName);
+
+    /**
+     * Returns the service registration for this component. The method
+     * will return <code>null</code> if no service registration is
+     * available, for example if this component is not registered as a
+     * service at all.
+     * 
+     * @return the service registration
+     */
+	public ServiceRegistration getServiceRegistration();
+
+    /**
+     * Returns the instance that make up this component. If the component has a composition of instances,
+     * then the first instance of the composition is returned. Null is returned if the component has not 
+     * even been instantiated.
+     * 
+     * @return the component instances
+     */
+	public <T> T getInstance();
+
+    /**
+     * Returns the composition instances that make up this component, or just the
+     * component instance if it does not have a composition, or an empty array if
+     * the component has not even been instantiated.
+     * 
+     * @return the component instances
+     */
+	public Object[] getInstances();
+
+    /**
+     * Returns the service properties associated with the component.
+     * 
+     * @return the properties or <code>null</code> if there are none
+     */
+	public <K,V> Dictionary<K,V> getServiceProperties();
+
+    /**
+     * Sets the service properties associated with the component. If the service
+     * was already registered, it will be updated.
+     * 
+     * @param serviceProperties the properties
+     */
+	public Component 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 component implementation. The
+     * dependency manager will look for a method of this name with the following signatures,
+     * in this order:
+     * <ol>
+     * <li>method(Component component)</li>
+     * <li>method()</li>
+     * </ol>
+     * 
+     * @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 component
+     */
+	public Component setCallbacks(String init, String start, String stop, String destroy);
+
+    /**
+     * Sets the names of the methods used as callbacks. These methods, when found, are
+     * invoked on the specified instance as part of the life cycle management of the component
+     * implementation.
+     * <p>
+     * See setCallbacks(String init, String start, String stop, String destroy) for more
+     * information on the signatures. Specifying an instance means you can create a manager
+     * that will be invoked whenever the life cycle of a component changes and this manager
+     * can then decide how to expose this life cycle to the actual component, offering an
+     * important indirection when developing your own component models.
+     *
+     * @return this component
+     */
+	public Component setCallbacks(Object instance, String init, String start, String stop, String destroy);
+
+    /**
+     * Sets the factory to use to create the implementation. You can specify
+     * both the factory class and method to invoke. The method should return
+     * the implementation, and can use any method to create it. Actually, this
+     * can be used together with <code>setComposition</code> to create a
+     * composition of instances that work together to implement a component. The
+     * factory itself can also be instantiated lazily by not specifying an
+     * instance, but a <code>Class</code>.
+     * 
+     * @param factory the factory instance or class
+     * @param createMethod the name of the create method
+     * @return this component
+     */
+	public Component setFactory(Object factory, String createMethod);
+
+    /**
+     * Sets the factory to use to create the implementation. You specify the
+     * method to invoke. The method should return the implementation, and can
+     * use any method to create it. Actually, this can be used together with
+     * <code>setComposition</code> to create a composition of instances that
+     * work together to implement a component.
+     * <p>
+     * Note that currently, there is no default for the factory, so please use
+     * <code>setFactory(factory, createMethod)</code> instead.
+     * 
+     * @param createMethod the name of the create method
+     * @return this component
+     */
+	public Component setFactory(String createMethod);
+
+    /**
+     * Sets the instance and method to invoke to get back all instances that
+     * are part of a composition and need dependencies injected. All of them
+     * will be searched for any of the dependencies. The method that is
+     * invoked must return an <code>Object[]</code>.
+     * 
+     * @param instance the instance that has the method
+     * @param getMethod the method to invoke
+     * @return this component
+     */
+	public Component setComposition(Object instance, String getMethod);
+
+    /**
+     * Sets the method to invoke on the service implementation to get back all
+     * instances that are part of a composition and need dependencies injected.
+     * All of them will be searched for any of the dependencies. The method that
+     * is invoked must return an <code>Object[]</code>.
+     * 
+     * @param getMethod the method to invoke
+     * @return this component
+     */
+	public Component setComposition(String getMethod);
+
+    /**
+     * Returns the dependency manager associated with this component.
+     * @return the dependency manager associated with this component.
+     */
+	public DependencyManager getDependencyManager();
+
+	/**
+	 * Returns the component description (dependencies, service provided, etc ...).
+	 * @return the component description (dependencies, service provided, etc ...).
+	 */
+	public ComponentDeclaration getComponentDeclaration();
+	
+	/**
+	 * Activate debug for this component. Informations related to dependency processing will be displayed
+	 * using osgi log service, our to standard output if no log service is currently available.
+	 * @param label
+	 */
+	public Component setDebug(String label);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentDeclaration.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentDeclaration.java
new file mode 100644
index 0000000..85353ef
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentDeclaration.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm;
+
+import java.util.Dictionary;
+import java.util.Map;
+
+import org.osgi.framework.BundleContext;
+
+/**
+ * Describes a component. Component declarations form descriptions of components
+ * that are managed by the dependency manager. They can be used to query their state
+ * for monitoring tools. The dependency manager shell command is an example of
+ * such a tool.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ComponentDeclaration {
+    /** Names for the states of this component. */
+    public static final String[] STATE_NAMES = { "unregistered", "registered" };
+    /** State constant for an unregistered component. */
+    public static final int STATE_UNREGISTERED = 0;
+    /** State constant for a registered component. */
+    public static final int STATE_REGISTERED = 1;
+    /** Returns a list of dependencies associated with this component. */
+    public ComponentDependencyDeclaration[] getComponentDependencies();
+    /** Returns the description of this component (the classname or the provided service(s)) */
+    public String getName();
+    /** Returns the class name of the Component implementation. */
+    public String getClassName();
+    /** Returns the service optionally provided by this component, or null */
+    public String[] getServices();
+    /** Returns the service properties, or null */
+    public <K,V> Dictionary<K, V> getServiceProperties();     
+    /** Returns the state of this component. */
+    public int getState();
+    /** Returns the instance id of this component. */
+    public long getId();
+    /** Returns the bundle context associated with this component. */
+    public BundleContext getBundleContext();
+    /** Returns the dependency manager for this component */
+    public DependencyManager getDependencyManager();
+    /** Returns the execution time in nanos for each component callbacks (init/start/stop/destroy) */
+    public Map<String, Long> getCallbacksTime(); 
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentDependencyDeclaration.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentDependencyDeclaration.java
new file mode 100644
index 0000000..b86f390
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentDependencyDeclaration.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm;
+
+/**
+ * Describes a component dependency. They form descriptions of dependencies
+ * that are managed by the dependency manager. They can be used to query their state
+ * for monitoring tools. The dependency manager shell command is an example of
+ * such a tool.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ComponentDependencyDeclaration {
+    /** Names for the states of this dependency. */
+    public static final String[] STATE_NAMES = { 
+        "optional unavailable", 
+        "optional available", 
+        "required unavailable", 
+        "required available",
+        "optional (not tracking)",
+        "required (not tracking)"
+        };
+    /** State constant for an unavailable, optional dependency. */
+    public static final int STATE_UNAVAILABLE_OPTIONAL = 0;
+    /** State constant for an available, optional dependency. */
+    public static final int STATE_AVAILABLE_OPTIONAL = 1;
+    /** State constant for an unavailable, required dependency. */
+    public static final int STATE_UNAVAILABLE_REQUIRED = 2;
+    /** State constant for an available, required dependency. */
+    public static final int STATE_AVAILABLE_REQUIRED = 3;
+    /** State constant for an optional dependency that has not been started yet. */
+    public static final int STATE_OPTIONAL = 4;
+    /** State constant for a required dependency that has not been started yet. */
+    public static final int STATE_REQUIRED = 5;
+    /** Returns the name of this dependency (a generic name with optional info separated by spaces)*/
+    public String getName();
+    /** Returns the simple dependency name (service classname for example) */
+    public String getSimpleName();
+    /** Returns the Dependency filter or null */
+    public String getFilter();
+    /** Returns the name of the type of this dependency. */
+    public String getType();
+    /** Returns the state of this dependency. */
+    public int getState();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentExecutorFactory.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentExecutorFactory.java
new file mode 100644
index 0000000..17bdbfd
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentExecutorFactory.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm;
+
+import java.util.concurrent.Executor;
+
+/**
+ * A <code>ComponentExecutorFactory</code> service can be registered by any management agent bundle 
+ * in order to enable parallel activation of Components.<p>
+ * 
+ * A <code>ComponentExecutorFactory</code> is part of the new concurrency model that forms the basis 
+ * of Dependency Manager 4.0. Let's first give a brief overview of the default thread model used when 
+ * no ComponentExecutorFactory is used. Then we'll explain the rationale and the usage of a 
+ * <code>ComponentExecutorFactory</code> service.
+ * <p> 
+ * 
+ * <h3>Default Thread Model</h3>
+ * 
+ * By default, Dependency Manager uses a <b>lock-free/single thread</b> model:
+ * <p><ul>
+ * 
+ * <li> When an external event that influence the state of a Component is taking place (for example, 
+ * when a service dependency on which the Component is depending on is registered in the registry by 
+ * a given thread), then DependencyManager does not perform any locking for the handling of the event. 
+ * Instead of that, a job that will handle the event is inserted in an internal lock-free 
+ * <b><code>Serial Queue</code></b> which is internally maintained in each Component.
+ * 
+ * <li> all jobs scheduled in the <code>Serial Queue</code> are then executed in FIFO order, by the first
+ * thread which has triggered the first event. This avoid to use some blocking locks in DM internals, and 
+ * also it simplifies the development of DM components, because all lifecycle callbacks 
+ * (init/start/stop/destroy) and dependency injections are scheduled through the <code>Serial Queue</code>: 
+ * This means that your component is not concurrently called in lifecycle callbacks and in dependency injection 
+ * methods.
+ * 
+ * <li> Now let's describe which thread is executing the jobs scheduled in a Component <code>Serial Queue</code>: 
+ * When a job (J1) is scheduled in the queue while it is empty, then the current thread becomes the "master"
+ * and will immediately execute the </code>Serial Queue</code> tasks (synchronously). And if another thread 
+ * triggers another event concurrently while the "master" thread is executing the job J1, then a job (J2) 
+ * for this new event is just enqueued in the <code>Serial Queue</code>, but the other thread returns 
+ * immediately to the caller, and the job J2 will then be executed by the "master" thread (after J1).
+ * </ul>
+ * 
+ * <p>
+ * This mechanism allows to serially handle all Component events (service dependencies) in FIFO order 
+ * without maintaining any locks.
+ * 
+ * <h3>Enabling parallelism with a <code>ComponentExecutorFactory</code></h3>
+ *  
+ * As described above, all the external events that influence the state of a given component are handed by 
+ * jobs scheduled in the <code>Serial Queue</code> of the Component, and the jobs are getting executed serially 
+ * by a single "master" thread. So usually, bundles are started from a single thread, meaning that all Components
+ * are then activated synchronously.
+ * <p>
+ * 
+ * But when you register in the OSGi service registry a <code>ComponentExecutorFactory</code>, that factory 
+ * will be used by DependencyManager to create an Executor of your choice for each Component, typically a shared 
+ * threadpool configured by yourself. And all the Component <code>Serial Queues</code> will be executed using 
+ * the Executor returned by the {@link #getExecutorFor(Component)} method.
+ * However, jobs scheduled in the <code>Serial Queue</code> of a given Component are still executed one at a 
+ * time, in FIFO order and the Component remains single threaded, and <b>independent Components 
+ * may then each be managed and activated concurrently with respect to each other</b>.
+ * <p>
+ * If you want to ensure that all Components are initialized <b>after</b> the ComponentExecutorFactory is 
+ * registered in the OSGI registry, you can use the "org.apache.felix.dependencymanager.parallel" OSGi 
+ * system property which specifies the list of components which must wait for the ComponentExecutorFactory 
+ * service. This property value can be set to a wildcard ("*"), or a list of components implementation class 
+ * prefixes (comma separated). So, all components whose class name starts with the specified prefixes will be cached 
+ * until the ComponentExecutorFactory service is registered (In this way, it is not necessary to use
+ * the StartLevel service if you want to ensure that all components are started concurrently).
+ * <p>
+ * 
+ * Some class name prefixes can also be negated (using "!"), in order to exclude some components from the 
+ * list of components using the ComponentExecutorFactory service.
+ * <p>
+ * 
+ * Notice that if the ComponentExecutorFactory itself and all its dependent services are defined using 
+ * the Dependency Manager API, then you have to list the package of such components with a "!" 
+ * prefix, in order to indicate that those components must not wait for a ComponentExecutorFactory service
+ * (since they are part of the ComponentExecutorFactory implementation !).
+ * <p>
+ * 
+ * <h3>Examples for the usage of the "org.apache.felix.dependencymanager.parallel" property:</h3>
+ * 
+ * <blockquote><pre>
+ * org.apache.felix.dependencymanager.parallel=*   
+ *      -> means all components must be cached until a ComponentExecutorFactory comes up.
+ * 
+ * org.apache.felix.dependencymanager.parallel=foo.bar, foo.zoo
+ *      -> means only components whose implementation class names are starting with "foo.bar" or "foo.zoo" 
+ *      must be handled using an Executor returned by the ComponentExecutorFactory service. Other Components
+ *      will be handled normally, as when there is no ComponentExecutorFactory available.
+ * 
+ * org.apache.felix.dependencymanager.parallel=!foo.threadpool, *
+ *      -> means all components must be delayed until the ComponentExecutorFactory comes up, except the 
+ *      components whose implementations class names are starting with "foo.threadpool" prefix). 
+ * </pre></blockquote>
+ * 
+ * <h3>Examples of a ComponentExecutorFactory that provides a shared threadpool:</h3>
+ * 
+ * First, we define the OSGi bundle context system property to enable parallelism for all DM Components
+ * excepts the one which declares the ComponentExecutorFactory:
+ * 
+ * <blockquote> <pre>
+ *   org.apache.felix.dependencymanager.parallel=!com.acme.management.threadpool, *
+ * </pre></blockquote>
+ * 
+ * Next, here is the Activator which declares the ComponentExecutorFactory:
+ * 
+ * <blockquote> <pre>
+ *   package com.acme.management.threadpool;
+ *   import org.apache.felix.dm.*;
+ *   
+ *   public class Activator extends DependencyActivatorBase {      
+ *      public void init(BundleContext context, DependencyManager mgr) throws Exception {
+ *         mgr.add(createComponent()
+ *            .setInterface(ComponentExecutorFactory.class.getName(), null)
+ *            .setImplementation(ComponentExecutorFactoryImpl.class)
+ *            .add(createConfigurationDependency()
+ *                 .setPid("com.acme.management.threadpool.ComponentExecutorFactoryImpl")));
+ *      }
+ *   }
+ * </pre></blockquote>
+ * 
+ * And here is the implementation for our ComponentExecutorFactory:
+ * 
+ * <blockquote> <pre>
+ *   package com.acme.management.threadpool;
+ *   import org.apache.felix.dm.*;
+ *
+ *  public class ComponentExecutorFactoryImpl implements ComponentExecutorFactory {
+ *      volatile Executor m_threadPool;
+ *      
+ *      void updated(Dictionary conf) {
+ *          m_sharedThreadPool = Executors.newFixedThreadPool(Integer.parseInt("threadpool.size"));
+ *      }
+ *
+ *      &#64;Override
+ *      public Executor getExecutorFor(Component component) {
+ *          return m_sharedThreadPool; // Use a shared threadpool for all Components
+ *      }
+ *  }
+ * </pre></blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ * @since 4.0.0
+ */
+public interface ComponentExecutorFactory {
+    /**
+     * Returns an Executor (typically a shared thread pool) used to manage a given DependencyManager Component.
+     * 
+     * @param component the Component to be managed by the returned Executor
+     * @return an Executor used to manage the given component, or null if the component must not be managed using any executor.
+     */
+    Executor getExecutorFor(Component component);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentState.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentState.java
new file mode 100644
index 0000000..1afb437
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentState.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm;
+
+/**
+ * Component states. Any state listeners registered using @link {@link Component#add(ComponentStateListener)} method
+ * are notified with the following stated whenever the component state changes.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public enum ComponentState {
+    /**
+     * The component is not currently started, and is inactive.
+     */
+	INACTIVE, 
+	
+	/**
+	 * The component is waiting for some required dependencies.
+	 */
+	WAITING_FOR_REQUIRED, 
+	
+	/**
+	 * The component has all its initial required dependencies available, but is now waiting for some extra required
+	 * dependencies which have been added after the component have been started (like from the component init method for example).
+	 */
+	INSTANTIATED_AND_WAITING_FOR_REQUIRED, 
+	
+	/**
+	 * The component is active, and is now tracking available optional dependencies.
+	 */
+	TRACKING_OPTIONAL
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentStateListener.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentStateListener.java
new file mode 100644
index 0000000..f3d3291
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentStateListener.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm;
+
+/**
+ * This interface can be used to register a component state listener. Component
+ * state listeners are called whenever a component state changes. You get notified
+ * when the component is starting, started, stopping and stopped. Each callback
+ * includes a reference to the component in question.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ComponentStateListener {
+    public void changed(Component c, ComponentState state);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ConfigurationDependency.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ConfigurationDependency.java
new file mode 100644
index 0000000..a58b2ef
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ConfigurationDependency.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm;
+
+/**
+ * Configuration dependency that can track the availability of a (valid) configuration. To use
+ * it, specify a PID for the configuration. The dependency is always required, because if it is
+ * not, it does not make sense to use the dependency manager. In that scenario, simply register
+ * your component as a <code>ManagedService(Factory)</code> and handle everything yourself. Also,
+ * only managed services are supported, not factories. There are a couple of things you need to
+ * be aware of when implementing the <code>updated(Dictionary)</code> method:
+ * <ul>
+ * <li>Make sure it throws a <code>ConfigurationException</code> when you get a configuration
+ * that is invalid. In this case, the dependency will not change: if it was not available, it
+ * will still not be. If it was available, it will remain available and implicitly assume you
+ * keep working with your old configuration.</li>
+ * <li>This method will be called before all required dependencies are available. Make sure you
+ * do not depend on these to parse your settings.</li>
+ * </ul>
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ConfigurationDependency extends Dependency, ComponentDependencyDeclaration {
+    /**
+     * Sets the name of the callback method that should be invoked when a configuration
+     * is available. The contract for this method is identical to that of
+     * <code>ManagedService.updated(Dictionary) throws ConfigurationException</code>.
+     * 
+     * @param callback the name of the callback method
+     */
+	ConfigurationDependency setCallback(String callback);
+
+    /**
+     * Sets the name of the callback method that should be invoked when a configuration
+     * is available. The contract for this method is identical to that of
+     * <code>ManagedService.updated(Dictionary) throws ConfigurationException</code>.
+     * 
+     * @param instance the instance to call the callbacks on
+     * @param callback the name of the callback method
+     */
+    ConfigurationDependency setCallback(Object instance, String callback);
+
+    /**
+     * Sets the <code>service.pid</code> of the configuration you are depending
+     * on.
+     */
+	ConfigurationDependency setPid(String pid);
+
+    /**
+     * Sets propagation of the configuration properties to the service
+     * properties. Any additional service properties specified directly are
+     * merged with these.
+     */
+	ConfigurationDependency setPropagate(boolean propagate);
+
+    /**
+     * The label used to display the tab name (or section) where the properties
+     * are displayed. Example: "Printer Service".
+     * 
+     * @return The label used to display the tab name where the properties are
+     *         displayed (may be localized)
+     */
+	ConfigurationDependency setHeading(String heading);
+
+    /**
+     * A human readable description of the PID this configuration is associated
+     * with. Example: "Configuration for the PrinterService bundle".
+     * 
+     * @return A human readable description of the PID this configuration is
+     *         associated with (may be localized)
+     */
+	ConfigurationDependency setDescription(String description);
+
+    /**
+     * Points to the basename of the Properties file that can localize the Meta
+     * Type informations. The default localization base name for the properties
+     * is OSGI-INF/l10n/bundle, but can be overridden by the manifest
+     * Bundle-Localization header (see core specification, in section
+     * Localization on page 68). You can specify a specific localization
+     * basename file using this method (e.g.
+     * <code>setLocalization("person")</code> will match person_du_NL.properties
+     * in the root bundle directory.
+     */
+	ConfigurationDependency setLocalization(String path);
+
+    /**
+     * Adds a MetaData regarding a given configuration property.
+     */
+	ConfigurationDependency add(PropertyMetaData properties);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/Dependency.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/Dependency.java
new file mode 100644
index 0000000..7014ebb
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/Dependency.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm;
+
+import java.util.Dictionary;
+
+/**
+ * Generic dependency for a component. 
+ * Can be added to a single component. Can be available, or not.. 
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface Dependency {
+    /**
+     * Returns <code>true</code> if this a required dependency. Required dependencies
+     * are dependencies that must be available before the component 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();
+
+    /**
+     * Returns <code>true</code> if auto configuration is enabled for this dependency.
+     * Auto configuration means that a dependency is injected in the component instance
+     * when it's available, and if it's unavailable, a "null object" will be inserted
+     * instead.
+     * 
+     * @return true if auto configuration is enabled for this dependency
+     */
+    public boolean isAutoConfig();
+    
+    /**
+     * Returns the name of the member in the class of the component instance
+     * to inject into. If you specify this, not all members of the right
+     * type will be injected, only the member whose name matches.
+     * 
+     * @return the name of the member in the class of the component instance to inject into
+     */
+    public String getAutoConfigName();
+    
+    /**
+     * Determines if the properties associated with this dependency should be propagated to
+     * the properties of the service registered by the component they belong to.
+     * 
+     * @see Dependency#getProperties()
+     * 
+     * @return <code>true</code> if the properties should be propagated
+     */
+    public boolean isPropagated();
+
+    /**
+     * Returns the properties associated with this dependency.
+     * 
+     * @see Dependency#isPropagated()
+     * 
+     * @return the properties
+     */
+    public <K,V> Dictionary<K,V> getProperties();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyActivatorBase.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyActivatorBase.java
new file mode 100644
index 0000000..d06b91a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyActivatorBase.java
@@ -0,0 +1,345 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm;
+
+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 <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class DependencyActivatorBase implements BundleActivator {
+    private BundleContext m_context;
+    private DependencyManager m_manager;
+    private Logger m_logger;
+    
+    /**
+     * Initialize the dependency manager. Here you can add all components 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 components 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.
+     * <p>
+     * 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 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_logger = new Logger(context);
+        m_manager = new DependencyManager(context, m_logger);
+        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);
+        m_manager.clear();
+        m_manager = null;
+        m_context = null;
+    }
+    
+    /**
+     * Returns the bundle context that is associated with this bundle.
+     * 
+     * @return the bundle context
+     */
+    public BundleContext getBundleContext() {
+        return m_context;
+    }
+
+    /**
+     * Returns the dependency manager that is associated with this bundle.
+     * 
+     * @return the dependency manager
+     */
+    public DependencyManager getDependencyManager() {
+        return m_manager;
+    }
+    
+    /**
+     * Returns the logger that is associated with this bundle. A logger instance
+     * is a proxy that will log to a real OSGi logservice if available and standard
+     * out if not.
+     * 
+     * @return the logger
+     */
+    public Logger getLogger() {
+        return m_logger;
+    }
+    
+    /**
+     * Creates a new component.
+     * 
+     * @return the new component
+     */
+    public Component createComponent() {
+        return m_manager.createComponent();
+    }
+    
+    /**
+     * Creates a new service dependency.
+     * 
+     * @return the service dependency
+     */
+    public ServiceDependency createServiceDependency() {
+        return m_manager.createServiceDependency();
+    }
+    
+    /**
+     * Creates a new temporal service dependency.
+     * 
+     * @param timeout the max number of milliseconds to wait for a service availability.
+     * @return the service dependency
+     */
+    public ServiceDependency createTemporalServiceDependency(long timeout) {
+        return m_manager.createTemporalServiceDependency(timeout);
+    }
+    
+    /**
+     * Creates a new configuration dependency.
+     * 
+     * @return the configuration dependency
+     */
+    public ConfigurationDependency createConfigurationDependency() {
+    	return m_manager.createConfigurationDependency();
+    }
+    
+    /**
+     * Creates a new configuration property metadata.
+     * 
+     * @return the configuration property metadata
+     */
+    public PropertyMetaData createPropertyMetaData() {
+        return m_manager.createPropertyMetaData();
+    }
+
+    /**
+     * Creates a new bundle dependency.
+     * 
+     * @return the bundle dependency
+     */
+    public BundleDependency createBundleDependency() {
+        return m_manager.createBundleDependency();
+    }
+
+    /**
+     * Creates a new resource dependency.
+     * 
+     * @return the resource dependency
+     */
+    public ResourceDependency createResourceDependency() {
+        return m_manager.createResourceDependency();
+    }
+
+    /**
+     * Creates a new aspect service.
+     * 
+     * @return the aspect service
+     * @see DependencyManager#createAspectService(Class, String, int, String)
+     */
+    public Component createAspectService(Class<?> serviceInterface, String serviceFilter, int ranking, String attributeName) {
+        return m_manager.createAspectService(serviceInterface, serviceFilter, ranking, attributeName);
+    }
+    
+    /**
+     * Creates a new aspect service.
+     * 
+     * @return the aspect service
+     * @see DependencyManager#createAspectService(Class, String, int)
+     */
+    public Component createAspectService(Class<?> serviceInterface, String serviceFilter, int ranking) {
+        return m_manager.createAspectService(serviceInterface, serviceFilter, ranking);
+    }
+    
+    /**
+     * Creates a new aspect service.
+     * 
+     * @return the aspect service
+     * @see DependencyManager#createAspectService(Class, String, int, String, String, String)
+     */
+    public Component createAspectService(Class<?> serviceInterface, String serviceFilter, int ranking, String add, String change, String remove) {
+        return m_manager.createAspectService(serviceInterface, serviceFilter, ranking, add, change, remove);
+    }
+
+    /**
+     * Creates a new aspect service.
+     * 
+     * @return the aspect service
+     * @see DependencyManager#createAspectService(Class, String, int, String, String, String, String)
+     */
+    public Component createAspectService(Class<?> serviceInterface, String serviceFilter, int ranking, String add, String change, String remove, String swap) {    
+        return m_manager.createAspectService(serviceInterface, serviceFilter, ranking, add, change, remove, swap);
+    }
+    	
+    /**
+     * Creates a new adapter service.
+     * 
+     * @return the adapter service
+     * @see DependencyManager#createAdapterService(Class, String)
+     */
+    public Component createAdapterService(Class<?> serviceInterface, String serviceFilter) {
+        return m_manager.createAdapterService(serviceInterface, serviceFilter);
+    }
+    
+    /**
+     * Creates a new adapter service.
+     * 
+     * @return the adapter service
+     * @see DependencyManager#createAdapterService(Class, String, String)
+     */
+    public Component createAdapterService(Class<?> serviceInterface, String serviceFilter, String autoConfig) {
+        return m_manager.createAdapterService(serviceInterface, serviceFilter, autoConfig);
+    }
+    
+    /**
+     * Creates a new adapter service.
+     * 
+     * @return the adapter service
+     * @see DependencyManager#createAdapterService(Class, String, String, String, String)
+     */
+    public Component createAdapterService(Class<?> serviceInterface, String serviceFilter, String add, String change, String remove) {
+        return m_manager.createAdapterService(serviceInterface, serviceFilter, add, change, remove);
+    }
+    
+    /**
+     * Creates a new adapter service.
+     * @return the adapter service
+     * @see DependencyManager#createAdapterService(Class, String, String, String, String, String)
+     */
+    public Component createAdapterService(Class<?> serviceInterface, String serviceFilter, String add, String change, String remove, String swap) {
+        return m_manager.createAdapterService(serviceInterface, serviceFilter, add, change, remove, swap);
+    }  
+    
+    /**
+     * Creates a new adapter service.
+     * @return the adapter service
+     * @see DependencyManager#createAdapterService(Class, String, String, Object, String, String, String, String, boolean)
+     */
+    public Component createAdapterService(Class<?> serviceInterface, String serviceFilter, 
+        String autoConfig, Object callbackInstance, String add, String change, String remove, String swap) {
+       return m_manager.createAdapterService(serviceInterface, serviceFilter, autoConfig, callbackInstance, add, change, remove, swap, true);
+    }
+
+    /**
+     * Creates a new adapter service.
+     * @return the adapter service
+     * @see DependencyManager#createAdapterService(Class, String, String, Object, String, String, String, String, boolean)
+     */
+    public Component createAdapterService(Class<?> serviceInterface, String serviceFilter, 
+        String autoConfig, Object callbackInstance, String add, String change, String remove, String swap, boolean propagate) {
+       return m_manager.createAdapterService(serviceInterface, serviceFilter, autoConfig, callbackInstance, add, change, remove, swap, propagate);
+    }
+
+   /**
+     * Creates a new resource adapter service.
+     * 
+     * @return the resource adapter service
+     */
+    public Component createResourceAdapter(String resourceFilter, boolean propagate, Object callbackInstance, String callbackChanged) {
+        return m_manager.createResourceAdapterService(resourceFilter, propagate, callbackInstance, callbackChanged);
+    }
+
+    /**
+     * Creates a new resource adapter service.
+     * 
+     * @return the resource adapter service
+     */
+    public Component createResourceAdapter(String resourceFilter, boolean propagate, Object callbackInstance, String callbackSet, String callbackChanged) {
+        return m_manager.createResourceAdapterService(resourceFilter, propagate, callbackInstance, callbackSet, callbackChanged);
+    }
+    
+    /**
+     * Creates a new resource adapter service.
+     * 
+     * @return the resource adapter service
+     */
+    public Component createResourceAdapter(String resourceFilter, Object propagateCallbackInstance, String propagateCallbackMethod, Object callbackInstance, String callbackChanged) {
+        return m_manager.createResourceAdapterService(resourceFilter, propagateCallbackInstance, propagateCallbackMethod, callbackInstance, null, callbackChanged);
+    }
+    
+    /**
+     * Creates a new resource adapter service.
+     * 
+     * @return the resource adapter service
+     */
+    public Component createResourceAdapter(String resourceFilter, Object propagateCallbackInstance, String propagateCallbackMethod, Object callbackInstance, String callbackSet, String callbackChanged) {
+        return m_manager.createResourceAdapterService(resourceFilter, propagateCallbackInstance, propagateCallbackMethod, callbackInstance, callbackSet, callbackChanged);
+    }
+    
+    /**
+     * Creates a new bundle adapter service.
+     * 
+     * @return the bundle adapter service
+     */
+    public Component createBundleAdapterService(int bundleStateMask, String bundleFilter, boolean propagate) {
+        return m_manager.createBundleAdapterService(bundleStateMask, bundleFilter, propagate);
+    }
+
+    /**
+     * Creates a new factory configuration adapter service.
+     * 
+     * @return the factory configuration adapter service
+     */
+    public Component createFactoryConfigurationAdapterService(String factoryPid, String update, boolean propagate) {
+        return m_manager.createFactoryConfigurationAdapterService(factoryPid, update, propagate);
+    }
+    
+    /**
+     * Creates a new factory configuration adapter service.
+     * 
+     * @return the factory configuration adapter service
+     */
+    public Component createFactoryConfigurationAdapterService(String factoryPid, String update, boolean propagate, String heading, String desc, String localization, PropertyMetaData[] propertiesMetaData) {
+        return m_manager.createAdapterFactoryConfigurationService(factoryPid, update, propagate, heading, desc, localization, propertiesMetaData);
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyManager.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyManager.java
new file mode 100644
index 0000000..336f8ae
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyManager.java
@@ -0,0 +1,707 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.felix.dm.impl.AdapterServiceImpl;
+import org.apache.felix.dm.impl.AspectServiceImpl;
+import org.apache.felix.dm.impl.BundleAdapterImpl;
+import org.apache.felix.dm.impl.BundleDependencyImpl;
+import org.apache.felix.dm.impl.ComponentImpl;
+import org.apache.felix.dm.impl.ComponentScheduler;
+import org.apache.felix.dm.impl.ConfigurationDependencyImpl;
+import org.apache.felix.dm.impl.FactoryConfigurationAdapterImpl;
+import org.apache.felix.dm.impl.ResourceAdapterImpl;
+import org.apache.felix.dm.impl.ResourceDependencyImpl;
+import org.apache.felix.dm.impl.ServiceDependencyImpl;
+import org.apache.felix.dm.impl.TemporalServiceDependencyImpl;
+import org.apache.felix.dm.impl.index.AdapterFilterIndex;
+import org.apache.felix.dm.impl.index.AspectFilterIndex;
+import org.apache.felix.dm.impl.index.ServiceRegistryCache;
+import org.apache.felix.dm.impl.index.multiproperty.MultiPropertyFilterIndex;
+import org.apache.felix.dm.impl.metatype.PropertyMetaDataImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkUtil;
+
+/**
+ * The dependency manager manages all components and their dependencies. Using 
+ * this API you can declare all components and their dependencies. Under normal
+ * circumstances, you get passed an instance of this class through the
+ * <code>DependencyActivatorBase</code> subclass you use as your
+ * <code>BundleActivator</code>, but it is also possible to create your
+ * own instance.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DependencyManager {    
+    /**
+     * The DependencyManager Activator will wait for a threadpool before creating any DM components if the following
+     * OSGi system property is set to true.
+     */
+    public final static String PARALLEL = "org.apache.felix.dependencymanager.parallel";
+
+    public static final String ASPECT = "org.apache.felix.dependencymanager.aspect";
+    public static final String SERVICEREGISTRY_CACHE_INDICES = "org.apache.felix.dependencymanager.filterindex";
+    public static final String METHOD_CACHE_SIZE = "org.apache.felix.dependencymanager.methodcache";
+    
+    private final BundleContext m_context;
+    private final Logger m_logger;
+    private final ConcurrentHashMap<Component, Component> m_components = new ConcurrentHashMap<>();
+
+    // service registry cache
+    private static ServiceRegistryCache m_serviceRegistryCache;
+    private static final Set<WeakReference<DependencyManager>> m_dependencyManagers = new HashSet<>();
+    static {
+        try {
+	    	Bundle bundle = FrameworkUtil.getBundle(DependencyManager.class);
+	        if (bundle != null && bundle.getState() != Bundle.ACTIVE) {
+	            bundle.start();
+	            BundleContext bundleContext = bundle.getBundleContext();
+	            String index = bundleContext.getProperty(SERVICEREGISTRY_CACHE_INDICES);
+	            if (index != null) {
+	            	m_serviceRegistryCache = new ServiceRegistryCache(bundleContext);
+	            	m_serviceRegistryCache.open(); // TODO close it somewhere
+	            	String[] props = index.split(";");
+	            	for (int i = 0; i < props.length; i++) {
+	            		if (props[i].equals("*aspect*")) {
+	            			m_serviceRegistryCache.addFilterIndex(new AspectFilterIndex());
+	            		}
+	            		else if (props[i].equals("*adapter*")) {
+	            			m_serviceRegistryCache.addFilterIndex(new AdapterFilterIndex());
+	            		}
+	            		else {
+	            			m_serviceRegistryCache.addFilterIndex(new MultiPropertyFilterIndex(props[i]));
+	            		}
+	            	}
+	            }
+	        }
+        }
+        catch (BundleException e) {
+        	// if we cannot start ourselves, we cannot use the indices
+        	e.printStackTrace();
+        }
+    }
+
+    /**
+     * Creates a new dependency manager. You need to supply the
+     * <code>BundleContext</code> to be used by the dependency
+     * manager to register services and communicate with the 
+     * framework.
+     * 
+     * @param context the bundle context
+     */
+    public DependencyManager(BundleContext context) {
+        this(context, new Logger(context));
+    }
+
+    DependencyManager(BundleContext context, Logger logger) {
+        m_context = createContext(context);
+        m_logger = logger;
+        synchronized (m_dependencyManagers) {
+            m_dependencyManagers.add(new WeakReference<DependencyManager>(this));
+        }
+    }
+    
+    /**
+     * Returns the list of currently created dependency managers.
+     * @return the list of currently created dependency managers
+     */
+    public static List<DependencyManager> getDependencyManagers() {
+        List<DependencyManager> result = new ArrayList<>();
+        synchronized (m_dependencyManagers) {
+            Iterator<WeakReference<DependencyManager>> iterator = m_dependencyManagers.iterator();
+            while (iterator.hasNext()) {
+                WeakReference<DependencyManager> reference = iterator.next();
+                DependencyManager manager = reference.get();
+                if (manager != null) {
+                    try {
+                        manager.getBundleContext().getBundle();
+                        result.add(manager);
+                        continue;
+                    }
+                    catch (IllegalStateException e) {
+                    }
+                }
+                iterator.remove();
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Returns the bundle context associated with this dependency manager.
+     * @return the bundle context associated with this dependency manager.
+     */
+    public BundleContext getBundleContext() {
+        return m_context;
+    }
+
+    /**
+     * Adds a new component to the dependency manager. After the service is added
+     * it will be started immediately.
+     * 
+     * @param c the service to add
+     */
+    public void add(Component c) {
+        m_components.put(c, c);
+        ComponentScheduler.instance().add(c);
+    }
+
+    /**
+     * Removes a service from the dependency manager. Before the service is removed
+     * it is stopped first.
+     * 
+     * @param c the component to remove
+     */
+    public void remove(Component c) {
+        ComponentScheduler.instance().remove(c);
+        m_components.remove(c);
+    }
+
+    /**
+     * Creates a new component.
+     * 
+     * @return the new component
+     */
+    public Component createComponent() {
+        return new ComponentImpl(m_context, this, m_logger);
+    }
+
+    /**
+     * Creates a new service dependency.
+     * 
+     * @return the service dependency
+     */
+    public ServiceDependency createServiceDependency() {
+        return new ServiceDependencyImpl();
+    }
+
+    /**
+     * Creates a new configuration dependency.
+     * 
+     * @return the configuration dependency
+     */
+    public ConfigurationDependency createConfigurationDependency() {
+        return new ConfigurationDependencyImpl(m_context, m_logger);
+    }
+
+    /**
+     * Creates a new bundle dependency.
+     * 
+     * @return a new BundleDependency instance.
+     */
+    public BundleDependency createBundleDependency() {
+        return new BundleDependencyImpl();
+    }
+
+    /**
+     * Creates a new resource dependency.
+     * 
+     * @return the resource dependency
+     */
+    public ResourceDependency createResourceDependency() {
+        return new ResourceDependencyImpl();
+    }
+
+    /**
+     * Creates a new timed required service dependency. A timed dependency blocks the invoker thread is the required dependency
+     * is currently unavailable, until it comes up again. 
+     * 
+     * @return a new timed service dependency
+     */
+    public ServiceDependency createTemporalServiceDependency(long timeout) {
+        return new TemporalServiceDependencyImpl(m_context, timeout);
+    }
+
+    /**
+     * Creates a new adapter. The adapter will be applied to any service that
+     * matches the specified interface and filter. For each matching service
+     * an adapter will be created based on the adapter implementation class.
+     * The adapter will be registered with the specified interface and existing properties
+     * from the original service plus any extra properties you supply here.
+     * It will also inherit all dependencies, and if you declare the original
+     * service as a member it will be injected.
+     * 
+     * <h3>Usage Example</h3>
+     * 
+     * <blockquote><pre>
+     * manager.createAdapterService(AdapteeService.class, "(foo=bar)")
+     *     .setInterface(AdapterService.class, new Hashtable() {{ put("extra", "property"); }})
+     *     .setImplementation(AdapterImpl.class);
+     * </pre></blockquote>
+     * 
+     * @param serviceInterface the service interface to apply the adapter to
+     * @param serviceFilter the filter condition to use with the service interface
+     * @return a service that acts as a factory for generating adapters
+     */
+    public Component createAdapterService(Class<?> serviceInterface, String serviceFilter) {
+        return new AdapterServiceImpl(this, serviceInterface, serviceFilter, null, null, null, null, null, null, true);
+    }
+
+    /**
+     * Creates a new adapter. The adapter will be applied to any service that
+     * matches the specified interface and filter. For each matching service
+     * an adapter will be created based on the adapter implementation class.
+     * The adapter will be registered with the specified interface and existing properties
+     * from the original service plus any extra properties you supply here.
+     * It will also inherit all dependencies, and if you declare the original
+     * service as a member it will be injected.
+     * 
+     * <h3>Usage Example</h3>
+     * 
+     * <blockquote><pre>
+     * manager.createAdapterService(AdapteeService.class, "(foo=bar)", "m_service")
+     *     .setInterface(AdapterService.class, new Hashtable() {{ put("extra", "property"); }})
+     *     .setImplementation(AdapterImpl.class);
+     * </pre></blockquote>
+     * 
+     * @param serviceInterface the service interface to apply the adapter to
+     * @param serviceFilter the filter condition to use with the service interface
+     * @param autoConfig the name of the member to inject the service into
+     * @return a service that acts as a factory for generating adapters
+     */
+    public Component createAdapterService(Class<?> serviceInterface, String serviceFilter, String autoConfig) {
+        return new AdapterServiceImpl(this, serviceInterface, serviceFilter, autoConfig, null, null, null, null, null, true);
+    }
+
+    /**
+     * Creates a new adapter. The adapter will be applied to any service that
+     * matches the specified interface and filter. For each matching service
+     * an adapter will be created based on the adapter implementation class.
+     * The adapter will be registered with the specified interface and existing properties
+     * from the original service plus any extra properties you supply here.
+     * It will also inherit all dependencies, and if you declare the original
+     * service as a member it will be injected.
+     * 
+     * <h3>Usage Example</h3>
+     * 
+     * <blockquote><pre>
+     * manager.createAdapterService(AdapteeService.class, "(foo=bar)", "add", "change", "remove")
+     *     .setInterface(AdapterService.class, new Hashtable() {{ put("extra", "property"); }})
+     *     .setImplementation(AdapterImpl.class);
+     * </pre></blockquote>
+     * 
+     * @param serviceInterface the service interface to apply the adapter to
+     * @param serviceFilter the filter condition to use with the service interface
+     * @param add name of the callback method to invoke on add
+     * @param change name of the callback method to invoke on change
+     * @param remove name of the callback method to invoke on remove
+     * @return a service that acts as a factory for generating adapters
+     */
+    public Component createAdapterService(Class<?> serviceInterface, String serviceFilter, String add, String change,
+        String remove)
+    {
+        return new AdapterServiceImpl(this, serviceInterface, serviceFilter, null, null, add, change, remove, null, true);
+    }
+
+    /**
+     * Creates a new adapter. The adapter will be applied to any service that
+     * matches the specified interface and filter. For each matching service
+     * an adapter will be created based on the adapter implementation class.
+     * The adapter will be registered with the specified interface and existing properties
+     * from the original service plus any extra properties you supply here.
+     * It will also inherit all dependencies, and if you declare the original
+     * service as a member it will be injected.
+     * 
+     * <h3>Usage Example</h3>
+     * 
+     * <blockquote><pre>
+     * manager.createAdapterService(AdapteeService.class, "(foo=bar)", "add", "change", "remove", "swap")
+     *     .setInterface(AdapterService.class, new Hashtable() {{ put("extra", "property"); }})
+     *     .setImplementation(AdapterImpl.class);
+     * </pre></blockquote>
+     * 
+     * @param serviceInterface the service interface to apply the adapter to
+     * @param serviceFilter the filter condition to use with the service interface
+     * @param add name of the callback method to invoke on add
+     * @param change name of the callback method to invoke on change
+     * @param remove name of the callback method to invoke on remove
+     * @param swap name of the callback method to invoke on swap
+     * @return a service that acts as a factory for generating adapters
+     */
+    public Component createAdapterService(Class<?> serviceInterface, String serviceFilter, String add, String change,
+        String remove, String swap)
+    {
+        return new AdapterServiceImpl(this, serviceInterface, serviceFilter, null, null, add, change, remove, swap, true);
+    }
+
+    /**
+     * Creates a new adapter. The adapter will be applied to any service that
+     * matches the specified interface and filter. For each matching service
+     * an adapter will be created based on the adapter implementation class.
+     * The adapter will be registered with the specified interface (and existing properties
+     * from the original service if you set the propagate flag) plus any extra properties you supply here.
+     * It will also inherit all dependencies, and if you declare the original
+     * service as a member it will be injected.
+     * 
+     * <h3>Usage Example</h3>
+     * 
+     * <blockquote><pre>
+     * manager.createAdapterService(AdapteeService.class, "(foo=bar)", "add", "change", "remove", "swap")
+     *     .setInterface(AdapterService.class, new Hashtable() {{ put("extra", "property"); }})
+     *     .setImplementation(AdapterImpl.class);
+     * </pre></blockquote>
+     * 
+     * @param serviceInterface the service interface to apply the adapter to
+     * @param serviceFilter the filter condition to use with the service interface
+     * @param autoConfig the name of the member to inject the service into, or null.
+     * @param callbackInstance the instance to invoke the callbacks on, or null if the callbacks have to be invoked on the adapter itself
+     * @param add name of the callback method to invoke on add
+     * @param change name of the callback method to invoke on change
+     * @param remove name of the callback method to invoke on remove
+     * @param swap name of the callback method to invoke on swap
+     * @param propagate true if the adaptee service properties should be propagated to the adapter service consumers
+     * @return a service that acts as a factory for generating adapters
+     */
+    public Component createAdapterService(Class<?> serviceInterface, String serviceFilter, 
+        String autoConfig, Object callbackInstance, String add, String change, String remove, 
+        String swap, boolean propagate)
+    {
+        return new AdapterServiceImpl(this, serviceInterface, serviceFilter, autoConfig, callbackInstance, add, change, remove, swap, propagate);
+    }
+
+    /**
+     * Creates a new Managed Service Factory Configuration Adapter. For each new Config Admin factory configuration matching
+     * the factoryPid, an adapter will be created based on the adapter implementation class.
+     * The adapter will be registered with the specified interface, and with the specified adapter service properties.
+     * Depending on the <code>propagate</code> parameter, every public factory configuration properties 
+     * (which don't start with ".") will be propagated along with the adapter service properties. 
+     * It will also inherit all dependencies.
+     * 
+     * <h3>Usage Example</h3>
+     * 
+     * <blockquote><pre>
+     *  manager.createFactoryConfigurationAdapterService("MyFactoryPid",  "update", true)
+     *         // The interface to use when registering adapter
+     *         .setInterface(AdapterService.class.getName(), new Hashtable() {{ put("foo", "bar"); }})
+     *         // the implementation of the adapter
+     *         .setImplementation(AdapterServiceImpl.class);
+     * </pre></blockquote>
+     * 
+     * @param factoryPid the pid matching the factory configuration
+     * @param update the adapter method name that will be notified when the factory configuration is created/updated.
+     * @param propagate true if public factory configuration should be propagated to the adapter service properties
+     * @return a service that acts as a factory for generating the managed service factory configuration adapter
+     */
+    public Component createFactoryConfigurationAdapterService(String factoryPid, String update, boolean propagate) {
+        return new FactoryConfigurationAdapterImpl(this, factoryPid, update, propagate);
+    }
+
+    /**
+     * Creates a new Managed Service Factory Configuration Adapter with meta type support. For each new Config Admin 
+     * factory configuration matching the factoryPid, an adapter will be created based on the adapter implementation 
+     * class. The adapter will be registered with the specified interface, and with the specified adapter service 
+     * properties. Depending on the <code>propagate</code> parameter, every public factory configuration properties 
+     * (which don't start with ".") will be propagated along with the adapter service properties. 
+     * It will also inherit all dependencies.
+     * 
+     * <h3>Usage Example</h3>
+     * 
+     * <blockquote><pre>
+     *       PropertyMetaData[] propertiesMetaData = new PropertyMetaData[] {
+     *            manager.createPropertyMetaData()
+     *               .setCardinality(Integer.MAX_VALUE)
+     *               .setType(String.class)
+     *               .setHeading("English words")
+     *               .setDescription("Declare here some valid english words")
+     *               .setDefaults(new String[] {"hello", "world"})
+     *               .setId("words")
+     *       };
+     * 
+     *       manager.add(createFactoryConfigurationAdapterService("FactoryPid", 
+     *                                                            "updated",
+     *                                                            true, // propagate CM settings
+     *                                                            "EnglishDictionary",
+     *                                                            "English dictionary configuration properties",
+     *                                                            null,
+     *                                                            propertiesMetaData)
+     *               .setImplementation(Adapter.class));
+     * </pre></blockquote>
+     * 
+     * @param factoryPid the pid matching the factory configuration
+     * @param update the adapter method name that will be notified when the factory configuration is created/updated.
+     * @param propagate true if public factory configuration should be propagated to the adapter service properties
+     * @param heading The label used to display the tab name (or section) where the properties are displayed. 
+     *        Example: "Printer Service"
+     * @param desc A human readable description of the factory PID this configuration is associated with. 
+     *        Example: "Configuration for the PrinterService bundle"
+     * @param localization Points to the basename of the Properties file that can localize the Meta Type informations.
+     *        The default localization base name for the properties is OSGI-INF/l10n/bundle, but can
+     *        be overridden by the manifest Bundle-Localization header (see core specification, in section Localization 
+     *        on page 68). You can specify a specific localization basename file using this parameter 
+     *        (e.g. <code>"person"</code> will match person_du_NL.properties in the root bundle directory).
+     * @param propertiesMetaData Array of MetaData regarding configuration properties
+     * @return a service that acts as a factory for generating the managed service factory configuration adapter
+     */
+    public Component createAdapterFactoryConfigurationService(String factoryPid, String update, boolean propagate,
+        String heading, String desc, String localization, PropertyMetaData[] propertiesMetaData)
+    {
+        return new FactoryConfigurationAdapterImpl(this, factoryPid, update, propagate, m_context, m_logger, heading,
+            desc, localization, propertiesMetaData);
+    }
+
+    /**
+     * Creates a new bundle adapter. The adapter will be applied to any bundle that
+     * matches the specified bundle state mask and filter condition. For each matching
+     * bundle an adapter will be created based on the adapter implementation class.
+     * The adapter will be registered with the specified interface
+     * 
+     * TODO and existing properties from the original resource plus any extra properties you supply here.
+     * It will also inherit all dependencies, and if you declare the original
+     * service as a member it will be injected.
+     * 
+     * <h3>Usage Example</h3>
+     * 
+     * <blockquote><pre>
+     *  manager.createBundleAdapterService(Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE, 
+     *                                     "(Bundle-SymbolicName=org.apache.felix.dependencymanager)",
+     *                                     true)
+     *         // The interface to use when registering adapter
+     *         .setInterface(AdapterService.class.getName(), new Hashtable() {{ put("foo", "bar"); }})
+     *         // the implementation of the adapter
+     *         .setImplementation(AdapterServiceImpl.class);
+     * </pre></blockquote>
+     * 
+     * @param bundleStateMask the bundle state mask to apply
+     * @param bundleFilter the filter to apply to the bundle manifest
+     * @param propagate <code>true</code> if properties from the bundle should be propagated to the service
+     * @return a service that acts as a factory for generating bundle adapters
+     */
+    public Component createBundleAdapterService(int bundleStateMask, String bundleFilter, boolean propagate) {
+        return new BundleAdapterImpl(this, bundleStateMask, bundleFilter, propagate);
+    }
+
+    /**
+     * Creates a new resource adapter. The adapter will be applied to any resource that
+     * matches the specified filter condition. For each matching resource
+     * an adapter will be created based on the adapter implementation class.
+     * The adapter will be registered with the specified interface and existing properties
+     * from the original resource plus any extra properties you supply here.
+     * It will also inherit all dependencies, and if you declare the original
+     * service as a member it will be injected.
+     * 
+     * <h3>Usage Example</h3>
+     * 
+     * <blockquote><pre>
+     *  manager.createResourceAdapterService("(&(path=/test)(repository=TestRepository))", true)
+     *         // The interface to use when registering adapter
+     *         .setInterface(AdapterService.class.getName(), new Hashtable() {{ put("foo", "bar"); }})
+     *         // the implementation of the adapter
+     *         .setImplementation(AdapterServiceImpl.class);
+     * </pre></blockquote>
+     *
+     * @param resourceFilter the filter condition to use with the resource
+     * @param propagate <code>true</code> if properties from the resource should be propagated to the service
+     * @param callbackInstance instance to invoke the callback on
+     * @param callbackChanged the name of the callback method
+     * @return a service that acts as a factory for generating resource adapters
+     */
+    public Component createResourceAdapterService(String resourceFilter, boolean propagate, Object callbackInstance,
+        String callbackChanged)
+    {
+        return new ResourceAdapterImpl(this, resourceFilter, propagate, callbackInstance, null, callbackChanged);
+    }
+
+    /** @see DependencyManager#createResourceAdapterService(String, boolean, Object, String) */
+    public Component createResourceAdapterService(String resourceFilter, boolean propagate, Object callbackInstance,
+        String callbackSet, String callbackChanged)
+    {
+        return new ResourceAdapterImpl(this, resourceFilter, propagate, callbackInstance, callbackSet, callbackChanged);
+    }
+
+    /** @see DependencyManager#createResourceAdapterService(String, boolean, Object, String) */
+    public Component createResourceAdapterService(String resourceFilter, Object propagateCallbackInstance,
+        String propagateCallbackMethod, Object callbackInstance, String callbackChanged)
+    {
+        return new ResourceAdapterImpl(this, resourceFilter, propagateCallbackInstance, propagateCallbackMethod,
+            callbackInstance, null, callbackChanged);
+    }
+
+    /** @see DependencyManager#createResourceAdapterService(String, boolean, Object, String) */
+    public Component createResourceAdapterService(String resourceFilter, Object propagateCallbackInstance,
+        String propagateCallbackMethod, Object callbackInstance, String callbackSet, String callbackChanged)
+    {
+        return new ResourceAdapterImpl(this, resourceFilter, propagateCallbackInstance, propagateCallbackMethod,
+            callbackInstance, callbackSet, callbackChanged);
+    }
+
+    /**
+     * Returns a list of components.
+     * 
+     * @return a list of components
+     */
+    public List<Component> getComponents() {
+        return Collections.list(m_components.elements());
+    }
+
+    /**
+     * Creates a new aspect. The aspect will be applied to any service that
+     * matches the specified interface and filter. For each matching service
+     * an aspect will be created based on the aspect implementation class.
+     * The aspect will be registered with the same interface and properties
+     * as the original service, plus any extra properties you supply here.
+     * It will also inherit all dependencies, and if you declare the original
+     * service as a member it will be injected.
+     * 
+     * <h3>Usage Example</h3>
+     * 
+     * <blockquote><pre>
+     * manager.createAspectService(ExistingService.class, "(foo=bar)", 10, "m_service")
+     *     .setImplementation(ExistingServiceAspect.class)
+     * );
+     * </pre></blockquote>
+     * 
+     * @param serviceInterface the service interface to apply the aspect to
+     * @param serviceFilter the filter condition to use with the service interface
+     * @param ranking the level used to organize the aspect chain ordering
+     * @param autoConfig the aspect implementation field name where to inject original service. 
+     *     If null, any field matching the original service will be injected.
+     * @return a service that acts as a factory for generating aspects
+     */
+    public Component createAspectService(Class<?> serviceInterface, String serviceFilter, int ranking, String autoConfig) {
+        return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, autoConfig, null, null, null, null);
+    }
+
+    /**
+     * Creates a new aspect. The aspect will be applied to any service that
+     * matches the specified interface and filter. For each matching service
+     * an aspect will be created based on the aspect implementation class.
+     * The aspect will be registered with the same interface and properties
+     * as the original service, plus any extra properties you supply here.
+     * It will also inherit all dependencies, and if you declare the original
+     * service as a member it will be injected.
+     * 
+     * <h3>Usage Example</h3>
+     * 
+     * <blockquote><pre>
+     * manager.createAspectService(ExistingService.class, "(foo=bar)", 10)
+     *     .setImplementation(ExistingServiceAspect.class)
+     * );
+     * </pre></blockquote>
+     * 
+     * @param serviceInterface the service interface to apply the aspect to
+     * @param serviceFilter the filter condition to use with the service interface
+     * @param ranking the level used to organize the aspect chain ordering
+     * @return a service that acts as a factory for generating aspects
+     */
+    public Component createAspectService(Class<?> serviceInterface, String serviceFilter, int ranking) {
+        return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, null, null, null, null, null);
+    }
+
+    /**
+     * Creates a new aspect. The aspect will be applied to any service that
+     * matches the specified interface and filter. For each matching service
+     * an aspect will be created based on the aspect implementation class.
+     * The aspect will be registered with the same interface and properties
+     * as the original service, plus any extra properties you supply here.
+     * It will also inherit all dependencies, and if you declare the original
+     * service as a member it will be injected.
+     * 
+     * <h3>Usage Example</h3>
+     * 
+     * <blockquote><pre>
+     * manager.createAspectService(ExistingService.class, "(foo=bar)", 10, "add", "change", "remove")
+     *     .setImplementation(ExistingServiceAspect.class)
+     * );
+     * </pre></blockquote>
+     * 
+     * @param serviceInterface the service interface to apply the aspect to
+     * @param serviceFilter the filter condition to use with the service interface
+     * @param ranking the level used to organize the aspect chain ordering
+     * @param add name of the callback method to invoke on add
+     * @param change name of the callback method to invoke on change
+     * @param remove name of the callback method to invoke on remove
+     * @return a service that acts as a factory for generating aspects
+     */
+    public Component createAspectService(Class<?> serviceInterface, String serviceFilter, int ranking, String add,
+        String change, String remove)
+    {
+        return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, null, add, change, remove, null);
+    }
+
+    /**
+     * Creates a new aspect. The aspect will be applied to any service that
+     * matches the specified interface and filter. For each matching service
+     * an aspect will be created based on the aspect implementation class.
+     * The aspect will be registered with the same interface and properties
+     * as the original service, plus any extra properties you supply here.
+     * It will also inherit all dependencies, and if you declare the original
+     * service as a member it will be injected.
+     * 
+     * <h3>Usage Example</h3>
+     * 
+     * <blockquote><pre>
+     * manager.createAspectService(ExistingService.class, "(foo=bar)", 10, "add", "change", "remove")
+     *     .setImplementation(ExistingServiceAspect.class)
+     * );
+     * </pre></blockquote>
+     * 
+     * @param serviceInterface the service interface to apply the aspect to
+     * @param serviceFilter the filter condition to use with the service interface
+     * @param ranking the level used to organize the aspect chain ordering
+     * @param add name of the callback method to invoke on add
+     * @param change name of the callback method to invoke on change
+     * @param remove name of the callback method to invoke on remove
+     * @param swap name of the callback method to invoke on swap
+     * @return a service that acts as a factory for generating aspects
+     */
+    public Component createAspectService(Class<?> serviceInterface, String serviceFilter, int ranking, String add,
+        String change, String remove, String swap)
+    {
+        return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, null, add, change, remove, swap);
+    }
+
+    /**
+     * Removes all components and their dependencies.
+     */
+    public void clear() {
+        for (Component component : m_components.keySet()) {
+            remove(component);
+        }
+        m_components.clear();
+    }
+
+    /**
+     * Creates a new configuration property metadata.
+     * 
+     * @return the configuration property metadata.
+     */
+    public PropertyMetaData createPropertyMetaData() {
+        return new PropertyMetaDataImpl();
+    }
+
+    private BundleContext createContext(BundleContext context) {
+        if (m_serviceRegistryCache != null) {
+            return m_serviceRegistryCache.createBundleContextInterceptor(context);
+        }
+        else {
+            return context;
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/FilterIndex.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/FilterIndex.java
new file mode 100644
index 0000000..23a4b07
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/FilterIndex.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm;
+
+import java.util.List;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * A filter index is an interface you can implement to create your own, optimized index for specific filter expressions.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface FilterIndex {
+    /** Opens this filter index. */
+    public void open(BundleContext context);
+    /** Closes this filter index. */
+    public void close();
+    /** Determines if the combination of class and filter is applicable for this filter index. */
+    public boolean isApplicable(String clazz, String filter);
+    /** Returns all service references that match the specified class and filter. Never returns null. */
+    public List<ServiceReference> getAllServiceReferences(String clazz, String filter);
+    /** Invoked whenever a service event occurs. */
+    public void serviceChanged(ServiceEvent event);
+    /** Adds a service listener to this filter index. */
+    public void addServiceListener(ServiceListener listener, String filter);
+    /** Removes a service listener from this filter index. If the listener is not present in the filter index, this method does nothing. */
+    public void removeServiceListener(ServiceListener listener);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/Logger.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/Logger.java
new file mode 100644
index 0000000..65d6e9f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/Logger.java
@@ -0,0 +1,310 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.log.LogService;
+
+/**
+ * This class mimics the standard OSGi <tt>LogService</tt> interface. An
+ * instance of this class is used by the dependency manager for all logging. 
+ * By default this class logs messages to standard out. The log level can be set to
+ * control the amount of logging performed, where a higher number results in
+ * more logging. A log level of zero turns off logging completely.
+ * 
+ * The log levels match those specified in the OSGi Log Service.
+ * This class also tracks log services and will use the highest ranking 
+ * log service, if present, as a back end instead of printing to standard
+ * out. The class uses reflection to invoking the log service's method to 
+ * avoid a dependency on the log interface, which is also why it does not
+ * actually implement <code>LogService</code>. This class is in many ways 
+ * similar to the one used in the system bundle for that same purpose.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Logger implements ServiceListener {
+	private static final String LOG_SINGLE_CONTEXT = "org.apache.felix.dependencymanager.singleContextLog";
+    public static final int LOG_ERROR = 1;
+    public static final int LOG_WARNING = 2;
+    public static final int LOG_INFO = 3;
+    public static final int LOG_DEBUG = 4;
+
+    private final BundleContext m_context;
+
+    private final static int LOGGER_OBJECT_IDX = 0;
+    private final static int LOGGER_METHOD_IDX = 1;
+    private static final String ENABLED_LOG_LEVEL = "org.apache.felix.dependencymanager.loglevel";
+    private ServiceReference m_logRef = null;
+    private Object[] m_logger = null;
+    private int m_enabledLevel = LogService.LOG_WARNING;
+    private String m_debugKey;
+
+    public Logger(BundleContext context) {
+    	if (context != null && "true".equals(context.getProperty(LOG_SINGLE_CONTEXT))) {
+    		m_context = FrameworkUtil.getBundle(DependencyManager.class).getBundleContext();
+    	} else {
+    		m_context = context;
+    	}
+		if (m_context != null) {
+		    String enabledLevel = m_context.getProperty(ENABLED_LOG_LEVEL);
+		    if (enabledLevel != null) {
+		        try {
+		            m_enabledLevel = Integer.valueOf(enabledLevel);
+		        } catch (NumberFormatException e) {}
+		    }
+		    startListeningForLogService();
+		}
+    }
+    
+    public final void log(int level, String msg) {
+        _log(null, level, msg, null);
+    }
+
+    public final void log(int level, String msg, Throwable throwable) {
+        _log(null, level, msg, throwable);
+    }
+
+    public final void log(ServiceReference sr, int level, String msg) {
+        _log(sr, level, msg, null);
+    }
+
+    public final void log(ServiceReference sr, int level, String msg, Throwable throwable) {
+        _log(sr, level, msg, throwable);
+    }
+
+    protected void doLog(ServiceReference sr, int level, String msg, Throwable throwable) {
+        String s = (sr == null) ? null : "SvcRef " + sr;
+        s = (s == null) ? msg : s + " " + msg;
+        s = (throwable == null) ? s : s + " (" + throwable + ")";
+        switch (level) {
+            case LOG_DEBUG:
+                System.out.println("DEBUG: " + s);
+                break;
+            case LOG_ERROR:
+                System.out.println("ERROR: " + s);
+                if (throwable != null) {
+                    if ((throwable instanceof BundleException) && (((BundleException) throwable).getNestedException() != null)) {
+                        throwable = ((BundleException) throwable).getNestedException();
+                    }
+                    throwable.printStackTrace();
+                }
+                break;
+            case LOG_INFO:
+                System.out.println("INFO: " + s);
+                break;
+            case LOG_WARNING:
+                System.out.println("WARNING: " + s);
+                break;
+            default:
+                System.out.println("UNKNOWN[" + level + "]: " + s);
+        }
+    }
+
+    private void _log(ServiceReference sr, int level, String msg, Throwable throwable) {
+        if (level <= m_enabledLevel) {
+            StringBuilder sb = new StringBuilder("[");
+            if (m_debugKey != null) {
+                sb.append(m_debugKey).append(" - ");
+            }
+            sb.append(Thread.currentThread().getName());
+            sb.append("] ");
+            sb.append(msg);
+            
+            // Save our own copy just in case it changes. We could try to do
+            // more conservative locking here, but let's be optimistic.
+            Object[] logger = m_logger;
+            // Use the log service if available.
+            if (logger != null) {
+                _logReflectively(logger, sr, level, sb.toString(), throwable);
+            }
+            // Otherwise, default logging action.
+            else {
+                doLog(sr, level, sb.toString(), throwable);
+            }
+        }
+    }
+
+    private void _logReflectively(Object[] logger, ServiceReference sr, int level, String msg, Throwable throwable) {
+        if (logger != null) {
+            Object[] params = { sr, new Integer(level), msg, throwable };
+            try {
+                ((Method) logger[LOGGER_METHOD_IDX]).invoke(logger[LOGGER_OBJECT_IDX], params);
+            }
+            catch (InvocationTargetException ex) {
+                System.err.println("Logger: " + ex);
+            }
+            catch (IllegalAccessException ex) {
+                System.err.println("Logger: " + ex);
+            }
+        }
+    }
+
+    /**
+     * This method is called when the bundle context is set;
+     * it simply adds a service listener so that the bundle can track
+     * log services to be used as the back end of the logging mechanism. It also
+     * attempts to get an existing log service, if present, but in general
+     * there will never be a log service present since the system bundle is
+     * started before every other bundle.
+     */
+    private synchronized void startListeningForLogService() {
+        try {
+            // add a service listener for log services, carefully avoiding any code dependency on it
+            m_context.addServiceListener(this, "(objectClass=org.osgi.service.log.LogService)");
+        }
+        catch (InvalidSyntaxException ex) {
+            // this will never happen since the filter is hard coded
+        }
+        // try to get an existing log service
+        m_logRef = m_context.getServiceReference("org.osgi.service.log.LogService");
+        // get the service object if available and set it in the logger
+        if (m_logRef != null) {
+            setLogger(m_context.getService(m_logRef));
+        }
+    }
+
+    /**
+     * This method implements the callback for the ServiceListener interface.
+     * It is public as a byproduct of implementing the interface and should
+     * not be called directly. This method tracks run-time changes to log
+     * service availability. If the log service being used by the framework's
+     * logging mechanism goes away, then this will try to find an alternative.
+     * If a higher ranking log service is registered, then this will switch
+     * to the higher ranking log service.
+     */
+    public final synchronized void serviceChanged(ServiceEvent event) {
+        // if no logger is in use, then grab this one
+        if ((event.getType() == ServiceEvent.REGISTERED) && (m_logRef == null)) {
+            m_logRef = event.getServiceReference();
+            // get the service object and set it in the logger
+            setLogger(m_context.getService(m_logRef));
+        }
+        // if a logger is in use, but this one has a higher ranking, then swap
+        // it for the existing logger
+        else if ((event.getType() == ServiceEvent.REGISTERED) && (m_logRef != null)) {
+            ServiceReference ref = m_context.getServiceReference("org.osgi.service.log.LogService");
+            if (!ref.equals(m_logRef)) {
+                m_context.ungetService(m_logRef);
+                m_logRef = ref;
+                setLogger(m_context.getService(m_logRef));
+            }
+        }
+        // if the current logger is going away, release it and try to
+        // find another one
+        else if ((event.getType() == ServiceEvent.UNREGISTERING) && m_logRef != null && m_logRef.equals(event.getServiceReference())) {
+            // Unget the service object.
+            m_context.ungetService(m_logRef);
+            // Try to get an existing log service.
+            m_logRef = m_context.getServiceReference("org.osgi.service.log.LogService");
+            // get the service object if available and set it in the logger
+            if (m_logRef != null) {
+                setLogger(m_context.getService(m_logRef));
+            }
+            else {
+                setLogger(null);
+            }
+        }
+    }
+
+    /**
+     * This method sets the new log service object. It also caches the method to
+     * invoke. The service object and method are stored in array to optimistically
+     * eliminate the need to locking when logging.
+     */
+    private void setLogger(Object logObj) {
+        if (logObj == null) {
+            m_logger = null;
+        }
+        else {
+            Class<?>[] formalParams = { ServiceReference.class, Integer.TYPE, String.class, Throwable.class };
+            try {
+                Method logMethod = logObj.getClass().getMethod("log", formalParams);
+                logMethod.setAccessible(true);
+                m_logger = new Object[] { logObj, logMethod };
+            }
+            catch (NoSuchMethodException ex) {
+                System.err.println("Logger: " + ex);
+                m_logger = null;
+            }
+        }
+    }
+
+    public void setEnabledLevel(int enabledLevel) {
+        m_enabledLevel = enabledLevel;
+    }
+    
+    public void setDebugKey(String debugKey) {
+        m_debugKey = debugKey;
+    }
+    
+    public String getDebugKey() {
+        return m_debugKey;
+    }
+
+    // --------------- Convenient helper log methods --------------------------------------------
+    
+    public void err(String format, Object... params) {
+        log(LogService.LOG_ERROR, String.format(format, params));        
+    }
+
+    public void err(String format, Throwable err, Object... params) {
+        log(LogService.LOG_ERROR, String.format(format, params), err);        
+    }
+
+    public void warn(String format, Object... params) {
+        log(LogService.LOG_WARNING, String.format(format, params));        
+    }
+
+    public void warn(String format, Throwable err, Object... params) {
+        log(LogService.LOG_WARNING, String.format(format, params), err);        
+    }
+
+    public boolean info() {
+        return m_enabledLevel >= LogService.LOG_INFO;
+    }
+
+    public void info(String format, Object... params) {
+        log(LogService.LOG_INFO, String.format(format, params));        
+    }
+
+    public void info(String format, Throwable err, Object... params) {
+        log(LogService.LOG_INFO, String.format(format, params), err);        
+    }
+
+    public boolean debug() {
+        return m_enabledLevel >= LogService.LOG_DEBUG;
+    }
+
+    public void debug(String format, Object... params) {
+        log(LogService.LOG_DEBUG, String.format(format, params));        
+    }
+
+    public void debug(String format, Throwable err, Object... params) {
+        log(LogService.LOG_DEBUG, String.format(format, params), err);        
+    }
+}
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/PropertyMetaData.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/PropertyMetaData.java
new file mode 100644
index 0000000..66a4a41
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/PropertyMetaData.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless
+ * required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm;
+
+/**
+ * This interface defines meta data regarding a given configuration property.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface PropertyMetaData {
+    /**
+     * The label used to display the property. Example: "Log Level".
+     * 
+     * @return The label used to display the property (may be localized)
+     */
+    public PropertyMetaData setHeading(String heading);
+
+    /**
+     * The key of a ConfigurationAdmin property. Example: "printer.logLevel"
+     * 
+     * @return The Configuration Admin property name
+     */
+    public PropertyMetaData setId(String id);
+
+    /**
+     * Returns the property primitive type. If must be either one of the following types:<p>
+     * <ul>
+     *    <li>String.class</li>
+     *    <li>Long.class</li>
+     *    <li>Integer.class</li>
+     *    <li>Character.class</li>
+     *    <li>Byte.class</li>
+     *    <li>Double.class</li>
+     *    <li>Float.class</li>
+     *    <li>Boolean.class</li>
+     * </ul>
+     */
+    public PropertyMetaData setType(Class<?> type);
+
+    /**
+     * Returns a default for this property. The object must be of the appropriate type as defined by the cardinality and getType(). 
+     * The return type is a list of String  objects that can be converted to the appropriate type. The cardinality of the return 
+     * array must follow the absolute cardinality of this type. E.g. if the cardinality = 0, the array must contain 1 element. 
+     * If the cardinality is 1, it must contain 0 or 1 elements. If it is -5, it must contain from 0 to max 5 elements. Note that 
+     * the special case of a 0 cardinality, meaning a single value, does not allow arrays or vectors of 0 elements. 
+     */
+    public PropertyMetaData setDefaults(String[] defaults);
+
+    /**
+     * Returns the property description. The description may be localized and must describe the semantics of this type and any 
+     * constraints. Example: "Select the log level for the Printer Service".
+     * 
+     * @return a localizable description of the property.
+     */
+    public PropertyMetaData setDescription(String description);
+
+    /**
+     * Return the cardinality of this property. The OSGi environment handles multi valued properties in arrays ([]) or in Vector objects. 
+     * The return value is defined as follows:<p>
+     *
+     * <ul>
+     * <li> x = Integer.MIN_VALUE    no limit, but use Vector</li>
+     * <li> x < 0                    -x = max occurrences, store in Vector</li>
+     * <li> x > 0                     x = max occurrences, store in array []</li>
+     * <li> x = Integer.MAX_VALUE    no limit, but use array []</li>
+     * <li> x = 0                     1 occurrence required</li>
+     * </ul>
+     */
+    public PropertyMetaData setCardinality(int cardinality);
+
+    /**
+     * Tells if this property is required or not.
+     */
+    public PropertyMetaData setRequired(boolean required);
+
+    /**
+     * Return a list of valid options for this property (the labels may be localized).
+     * 
+     * @return the list of valid options for this property.
+     */
+    public PropertyMetaData addOption(String optionLabel, String optionValue);
+}
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ResourceDependency.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ResourceDependency.java
new file mode 100644
index 0000000..d14cc19
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ResourceDependency.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm;
+
+import java.net.URL;
+
+/**
+ * A resource dependency is a dependency on a resource. A resource in this context is an object that is
+ * identified by a URL. Resources should somehow be provided by an external component, the resource
+ * provider. These dependencies then react on them becoming available or not. Use cases for such dependencies
+ * are resources that are embedded in bundles, in a workspace or some remote or local repository, etc.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ResourceDependency extends Dependency, ComponentDependencyDeclaration, ResourceHandler {
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added or removed. When you specify callbacks, the auto configuration 
+     * feature is automatically turned off, because we're assuming you don't need it in this 
+     * case.
+     * 
+     * @param added the method to call when a service was added
+     * @param removed the method to call when a service was removed
+     * @return this service dependency
+     */
+    public ResourceDependency setCallbacks(String added, String removed);
+
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added, changed or removed. When you specify callbacks, the auto 
+     * configuration feature is automatically turned off, because we're assuming you don't 
+     * need it in this case.
+     * 
+     * @param added the method to call when a service was added
+     * @param changed the method to call when a service was changed
+     * @param removed the method to call when a service was removed
+     * @return this service dependency
+     */
+     public ResourceDependency setCallbacks(String added, String changed, String 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. When you
+     * specify callbacks, the auto configuration feature is automatically turned off, because
+     * we're assuming you don't need it in this case.
+     * 
+     * @param instance the instance to call the callbacks on
+     * @param added the method to call when a service was added
+     * @param removed the method to call when a service was removed
+     * @return this service dependency
+     */
+     public ResourceDependency setCallbacks(Object instance, String added, String removed);
+    
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added, changed or removed. They are called on the instance you provide. When you
+     * specify callbacks, the auto configuration feature is automatically turned off, because
+     * we're assuming you don't need it in this case.
+     * 
+     * @param instance the instance to call the callbacks on
+     * @param added the method to call when a service was added
+     * @param changed the method to call when a service was changed
+     * @param removed the method to call when a service was removed
+     * @return this service dependency
+     */
+     public ResourceDependency setCallbacks(Object instance, String added, String changed, String removed);
+        
+    /**
+     * 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 ResourceDependency setAutoConfig(boolean autoConfig);
+    
+    /**
+     * Sets auto configuration for this service. Auto configuration allows the
+     * dependency to fill in the attribute in the service implementation that
+     * has the same type and instance name.
+     * 
+     * @param instanceName the name of attribute to auto config
+     * @return this service dependency
+     */
+     public ResourceDependency setAutoConfig(String instanceName);
+
+     /**
+      * Sets the resource for this dependency.
+      * 
+      * @param resource the URL of the resource
+      */
+     public ResourceDependency setResource(URL resource);
+
+     /**
+      * Determines if this is a required dependency or not.
+      * 
+      * @param required <code>true</code> if the dependency is required
+      */
+     public ResourceDependency setRequired(boolean required);
+
+     /**
+      * Sets the filter condition for this resource dependency.
+      * 
+      * @param resourceFilter the filter condition
+      */
+     public ResourceDependency setFilter(String resourceFilter);
+
+     /** @see ResourceDependency#setPropagate(Object, String) */
+     public ResourceDependency setPropagate(boolean propagate);
+
+     /**
+      * Sets an Object instance and a callback method used to propagate some properties to the provided service properties.
+      * The method will be invoked on the specified object instance and must have one of the following signatures:<p>
+      * <ul><li>Dictionary callback(ServiceReference, Object service) 
+      * <li>Dictionary callback(ServiceReference)
+      * </ul>
+      * @param instance the Object instance which is used to retrieve propagated service properties 
+      * @param method the method to invoke for retrieving the properties to be propagated to the service properties.
+      * @return this service dependency.
+      */
+     public ResourceDependency setPropagate(Object instance, String method);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ResourceHandler.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ResourceHandler.java
new file mode 100644
index 0000000..5534580
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ResourceHandler.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm;
+
+import java.net.URL;
+import java.util.Dictionary;
+
+/** 
+ * Service interface for anybody wanting to be notified of changes to resources. 
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ResourceHandler {
+    /** Name of the property that's used to describe the filter condition for a resource. */
+    public static final String FILTER = "filter";
+    /** Exact URL that this handler is looking for. Can be used instead of a filter to be very explicit about the resource you're looking for. */
+    public static final String URL = "url";
+    /** The host part of the URL. */
+    public static final String HOST = "host";
+    /** The path part of the URL. */
+    public static final String PATH = "path";
+    /** The protocol part of the URL. */
+    public static final String PROTOCOL = "protocol";
+    /** The port part of the URL. */
+    public static final String PORT = "port";
+
+    /**
+     * @deprecated Please use {@link #added(URL, Dictionary)} instead. When both are specified,
+     *     the new method takes precedence and the deprecated one is not invoked.
+     */
+    public void added(URL resource);
+    
+    /**
+     * Invoked whenever a new resource is added.
+     */
+    public void added(URL resource, Dictionary<?, ?> resourceProperties);
+    
+    /**
+     * @deprecated Please use {@link #changed(URL, Dictionary)} instead. When both are specified,
+     *     the new method takes precedence and the deprecated one is not invoked.
+     */
+    public void changed(URL resource);
+    
+    /**
+     * Invoked whenever an existing resource changes.
+     */
+    public void changed(URL resource, Dictionary<?, ?> resourceProperties);
+    
+    /**
+     * @deprecated Please use {@link #removed(URL, Dictionary)} instead. When both are specified,
+     *     the new method takes precedence and the deprecated one is not invoked.
+     */
+    public void removed(URL resource);
+    
+    /**
+     * Invoked whenever an existing resource is removed.
+     */
+    public void removed(URL resource, Dictionary<?, ?> resourceProperties);
+}
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ResourceUtil.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ResourceUtil.java
new file mode 100644
index 0000000..2112ffe
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ResourceUtil.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm;
+
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+/**
+ * Utility class for resource handling.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResourceUtil {
+	/**
+	 * Creates a set of properties for a resource based on its URL.
+	 * 
+	 * @param url the URL
+	 * @return a set of properties
+	 */
+    public static Dictionary<?, ?> createProperties(URL url) {
+        Hashtable<String, Object> props = new Hashtable<>();
+        props.put(ResourceHandler.PROTOCOL, url.getProtocol());
+        props.put(ResourceHandler.HOST, url.getHost());
+        props.put(ResourceHandler.PORT, Integer.toString(url.getPort()));
+        props.put(ResourceHandler.PATH, url.getPath());
+        return props;
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ServiceDependency.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ServiceDependency.java
new file mode 100644
index 0000000..7cda8a8
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ServiceDependency.java
@@ -0,0 +1,285 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Service dependency that can track an OSGi service.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ServiceDependency extends Dependency, ComponentDependencyDeclaration {
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added or removed. When you specify callbacks, the auto configuration 
+     * feature is automatically turned off, because we're assuming you don't need it in this 
+     * case.
+     * 
+     * @param add the method to call when a service was added
+     * @param remove the method to call when a service was removed
+     * @return this service dependency
+     */
+	public ServiceDependency setCallbacks(String add, String remove);
+
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added, changed or removed. When you specify callbacks, the auto 
+     * configuration feature is automatically turned off, because we're assuming you don't 
+     * need it in this case.
+     * 
+     * @param add the method to call when a service was added
+     * @param change the method to call when a service was changed
+     * @param remove the method to call when a service was removed
+     * @return this service dependency
+     */
+	public ServiceDependency setCallbacks(String add, String change, String remove);
+
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added, changed or removed. When you specify callbacks, the auto 
+     * configuration feature is automatically turned off, because we're assuming you don't 
+     * need it in this case.
+     * @param add the method to call when a service was added
+     * @param change the method to call when a service was changed
+     * @param remove the method to call when a service was removed
+     * @param swap the method to call when the service was swapped due to addition or 
+     * removal of an aspect
+     * @return this service dependency
+     */
+	public ServiceDependency setCallbacks(String add, String change, String remove, String swap);
+
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added or removed. They are called on the instance you provide. When you
+     * specify callbacks, the auto configuration feature is automatically turned off, because
+     * we're assuming you don't need it in this case.
+     * 
+     * @param instance the instance to call the callbacks on
+     * @param add the method to call when a service was added
+     * @param remove the method to call when a service was removed
+     * @return this service dependency
+     */
+	public ServiceDependency setCallbacks(Object instance, String add, String remove);
+
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added, changed or removed. They are called on the instance you provide. When you
+     * specify callbacks, the auto configuration feature is automatically turned off, because
+     * we're assuming you don't need it in this case.
+     * 
+     * @param instance the instance to call the callbacks on
+     * @param add the method to call when a service was added
+     * @param change the method to call when a service was changed
+     * @param remove the method to call when a service was removed
+     * @return this service dependency
+     */
+	public ServiceDependency setCallbacks(Object instance, String add, String change, String remove);
+
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added, changed or removed. When you specify callbacks, the auto 
+     * configuration feature is automatically turned off, because we're assuming you don't 
+     * need it in this case.
+     * @param instance the instance to call the callbacks on
+     * @param added the method to call when a service was added
+     * @param changed the method to call when a service was changed
+     * @param removed the method to call when a service was removed
+     * @param swapped the method to call when the service was swapped due to addition or 
+     * removal of an aspect
+     * @return this service dependency
+     */    
+	public ServiceDependency setCallbacks(Object instance, String added, String changed, String removed, String swapped);
+
+    /**
+     * Sets the required flag which determines if this service is required or not.
+     * A ServiceDependency is false by default.
+     * 
+     * @param required the required flag
+     * @return this service dependency
+     */
+	public ServiceDependency setRequired(boolean required);
+
+    /**
+     * Sets auto configuration for this service. Auto configuration allows the
+     * dependency to fill in the attribute in the service implementation that
+     * has the same type and instance name. Dependency services will be injected
+     * in the following kind of fields:<p>
+     * <ul>
+     * <li> a field having the same type as the dependency. If the field may be accessed by anythread, then
+     * the field should be declared volatile, in order to ensure visibility when the field is auto injected concurrently.
+     * 
+     * <li> a field which is assignable to an <code>Iterable&#60;T&#62;</code> where T must match the dependency type. 
+     * In this case, an Iterable will be injected by DependencyManager before the start callback is called.
+     * The Iterable field may then be traversed to inspect the currently available dependency services. The Iterable 
+     * can possibly be set to a final value so you can choose the Iterable implementation of your choice
+     * (for example, a CopyOnWrite ArrayList, or a ConcurrentLinkedQueue).
+     * 
+     * <li> a <code>Map&#60;K,V&#62;</code> where K must match the dependency type and V must exactly equals <code>Dictionary</code>. 
+     * In this case, a ConcurrentHashMap will be injected by DependencyManager before the start callback is called.
+     * The Map may then be consulted to lookup current available dependency services, including the dependency service
+     * properties (the map key holds the dependency service, and the map value holds the dependency service properties).
+     * 
+     * The Map field may be set to a final value so you can choose a Map of your choice (Typically a ConcurrentHashMap).
+     * 
+     * A ConcurrentHashMap is "weakly consistent", meaning that when traversing 
+     * the elements, you may or may not see any concurrent updates made on the map. So, take care to traverse 
+     * the map using an iterator on the map entry set, which allows to atomically lookup pairs of Dependency service/Service properties.
+     * </ul> 
+     * 
+     * <p> Here are some example using an Iterable:
+     * <blockquote>
+     * 
+     * <pre>
+     * 
+     * public class SpellChecker {
+     *    // can be traversed to inspect currently available dependencies
+     *    final Iterable&#60;DictionaryService&#62; dictionaries = new ConcurrentLinkedQueue<>();
+     *    
+     *    Or
+     *    
+     *    // will be injected by DM automatically and can be traversed any time to inspect all currently available dependencies.
+     *    volatile Iterable&#60;DictionaryService&#62; dictionaries = null;
+     * }
+     * 
+     * </pre>
+     * </blockquote>
+     * 
+     * Here are some example using a Map:
+     * <blockquote>
+     * 
+     * <pre>
+     * 
+     * public class SpellChecker {
+     *    // can be traversed to inspect currently available dependencies
+     *    final Map&#60;DictionaryService, Dictionary&#62; dictionaries = new ConcurrentLinkedQueue<>();
+     *    
+     *    or
+     *    
+     *    // will be injected by DM automatically and can be traversed to inspect currently available dependencies
+     *    volatile Map&#60;DictionaryService, Dictionary&#62; dictionaries = null;
+     *
+     *    void iterateOnAvailableServices() {                 
+     *       for (Map.Entry<MyService, Dictionary> entry : this.services.entrySet()) {
+     *           MyService currentService = entry.getKey();
+     *           Dictionary currentServiceProperties = entry.getValue();
+     *           // ...
+     *       }
+     *    }
+     * }
+     * 
+     * </pre>
+     * </blockquote>
+     * 
+     * @param autoConfig the name of attribute to auto configure
+     * @return this service dependency
+     */
+	public ServiceDependency setAutoConfig(boolean autoConfig);
+
+    /**
+     * Sets auto configuration for this service. Auto configuration allows the
+     * dependency to fill in the attribute in the service implementation that
+     * has the same type and instance name.
+     * 
+     * @param instanceName the name of attribute to auto config
+     * @return this service dependency
+     * @see #setAutoConfig(boolean)
+     */
+	public ServiceDependency setAutoConfig(String instanceName);
+
+    /**
+     * Sets the name of the service that should be tracked. 
+     * 
+     * @param serviceName the name of the service
+     * @return this service dependency
+     */
+    public ServiceDependency setService(Class<?> serviceName);
+
+    /**
+     * 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. To make sure of this, the filter is actually extended internally to
+     * filter on the correct name.
+     * 
+     * @param serviceName the name of the service
+     * @param serviceFilter the filter condition
+     * @return this service dependency
+     */
+    public ServiceDependency setService(Class<?> serviceName, String serviceFilter);
+    
+    /**
+     * Sets the filter for the services that should be tracked. Any service object
+     * matching the filter will be returned, without any additional filter on the
+     * class.
+     * 
+     * @param serviceFilter the filter condition
+     * @return this service dependency
+     */
+    public ServiceDependency setService(String serviceFilter);
+    
+    /**
+     * 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 ServiceDependency setService(Class<?> serviceName, ServiceReference serviceReference);
+    
+    /**
+     * Sets the default implementation for this service dependency. You can use this to supply
+     * your own implementation that will be used instead of a Null Object when the dependency is
+     * not available. This is also convenient if the service dependency is not an interface
+     * (which would cause the Null Object creation to fail) but a class.
+     * 
+     * @param implementation the instance to use or the class to instantiate if you want to lazily
+     *     instantiate this implementation
+     * @return this service dependency
+     */
+    public ServiceDependency setDefaultImplementation(Object implementation);
+    
+    /**
+     * Sets propagation of the service dependency properties to the provided service properties. Any additional
+     * service properties specified directly are merged with these.
+     */
+    public ServiceDependency setPropagate(boolean propagate);
+    
+    /**
+     * Sets an Object instance and a callback method used to propagate some properties to the provided service properties.
+     * The method will be invoked on the specified object instance and must have one of the following signatures:<p>
+     * <ul><li>Dictionary callback(ServiceReference, Object service) 
+     * <li>Dictionary callback(ServiceReference)
+     * </ul>
+     * @param instance the Object instance which is used to retrieve propagated service properties 
+     * @param method the method to invoke for retrieving the properties to be propagated to the service properties.
+     * @return this service dependency.
+     */
+    public ServiceDependency setPropagate(Object instance, String method);
+    
+    /**
+     * Enabled debug logging for this dependency instance. The logging is prefixed with the given identifier.
+     * @param debugKey a prefix log identifier
+     * @return this service dependency.
+     */
+    public ServiceDependency setDebug(String debugKey);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/AbstractDependency.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/AbstractDependency.java
new file mode 100644
index 0000000..d3890bd
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/AbstractDependency.java
@@ -0,0 +1,559 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.context;
+
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.dm.ComponentDependencyDeclaration;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.ServiceDependency;
+
+/**
+ * Abstract class for implementing Dependencies.
+ * You can extends this class in order to supply your own custom dependencies to any Dependency Manager Component.
+ *
+ * @param <T> The type of the interface representing a Dependency Manager Dependency (must extends the Dependency interface).
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class AbstractDependency<T extends Dependency> implements
+    Dependency, DependencyContext, ComponentDependencyDeclaration {
+
+    /**
+     * The Component implementation is exposed to Dependencies through this interface.
+     */
+    protected ComponentContext m_component;
+
+    /**
+     * Is this Dependency available ? Volatile because the getState method (part of the 
+     * {@link ComponentDependencyDeclaration} interface) may be called by any thread, at any time.
+     */
+    protected volatile boolean m_available;
+
+    /**
+     * Is this Dependency "instance bound" ? A dependency is "instance bound" if it is defined within the component's 
+     * init method, meaning that it won't deactivate the component if it is not currently available when being added
+     * from the component's init method.
+     */
+    protected boolean m_instanceBound;
+
+    /**
+     * Is this dependency required (false by default) ?
+     */
+    protected volatile boolean m_required;
+
+    /**
+     * Component callback used to inject an added dependency.
+     */
+    protected String m_add;
+
+    /**
+     * Component callback invoked when the dependency has changed.
+     */
+    protected String m_change;
+
+    /**
+     * Component callback invoked when the dependency becomes unavailable.
+     */
+    protected String m_remove;
+
+    /**
+     * Can this Dependency be auto configured in the component instance fields ?
+     */
+    protected boolean m_autoConfig = true;
+
+    /**
+     * The Component field name where the Dependency can be injected (null means any field with a compatible type
+     * will be injected).
+     */
+    protected String m_autoConfigInstance;
+
+    /**
+     * Indicates if the setAutoConfig method has been invoked. This flag is used to force autoconfig to "false" 
+     * when the setCallbacks method is invoked, unless the setAutoConfig method has been called.
+     */
+    protected boolean m_autoConfigInvoked;
+
+    /**
+     * Has this Dependency been started by the Component implementation ? Volatile because the getState method 
+     * (part of the {@link ComponentDependencyDeclaration} interface) may be called by any thread, at any time.
+     */
+    protected volatile boolean m_isStarted;
+
+    /**
+     * The object instance on which the dependency callbacks are invoked on. Null means the dependency will be
+     * injected to the Component implementation instance(s).
+     */
+    protected Object m_callbackInstance;
+
+    /**
+     * Tells if the dependency service properties have to be propagated to the Component service properties.
+     */
+    protected boolean m_propagate;
+
+    /**
+     * The propagate callback instance that is invoked in order to supply dynamically some dependency service properties.
+     */
+    protected Object m_propagateCallbackInstance;
+
+    /**
+     * The propagate callback method that is invoked in order to supply dynamically some dependency service properties.
+     * @see {@link #m_propagateCallbackInstance}
+     */
+    protected volatile String m_propagateCallbackMethod;
+
+    /**
+     * Default empty dependency properties.
+     */
+    protected final static Dictionary<Object, Object> EMPTY_PROPERTIES = new Hashtable<>(0);
+
+    /**
+     * Creates a new Dependency. By default, the dependency is optional and autoconfig.
+     */
+    public AbstractDependency() {
+    }
+
+    /**
+     * Create a clone of a given Dependency.
+     * @param prototype all the fields of the prototype will be copied to this dependency.
+     */
+    public AbstractDependency(AbstractDependency<T> prototype) {
+        m_instanceBound = prototype.m_instanceBound;
+        m_required = prototype.m_required;
+        m_add = prototype.m_add;
+        m_change = prototype.m_change;
+        m_remove = prototype.m_remove;
+        m_autoConfig = prototype.m_autoConfig;
+        m_autoConfigInstance = prototype.m_autoConfigInstance;
+        m_autoConfigInvoked = prototype.m_autoConfigInvoked;
+        m_callbackInstance = prototype.m_callbackInstance;
+        m_propagate = prototype.m_propagate;
+        m_propagateCallbackInstance = prototype.m_propagateCallbackInstance;
+        m_propagateCallbackMethod = prototype.m_propagateCallbackMethod;
+    }
+    
+    @Override
+    public String toString() {
+        return new StringBuilder(getType()).append(" dependency [").append(getName()).append("]").toString();
+    }
+
+    // ----------------------- Dependency interface -----------------------------
+
+    /**
+     * Is this Dependency required (false by default) ?
+     */
+    @Override
+    public boolean isRequired() {
+        return m_required;
+    }
+
+    /**
+     * Is this Dependency satisfied and available ?
+     */
+    @Override
+    public boolean isAvailable() {
+        return m_available;
+    }
+
+    /**
+     * Can this dependency be injected in a component class field (by reflexion, true by default) ?
+     */
+    @Override
+    public boolean isAutoConfig() {
+        return m_autoConfig;
+    }
+
+    /**
+     * Returns the field name when the dependency can be injected to.
+     */
+    @Override
+    public String getAutoConfigName() {
+        return m_autoConfigInstance;
+    }
+
+    /**
+     * Returns the propagate callback method that is invoked in order to supply dynamically some dependency service properties.
+     * @see {@link #m_propagateCallbackInstance}
+     */
+    @Override
+    public boolean isPropagated() {
+        return m_propagate;
+    }
+
+    /**
+     * Returns the dependency service properties (empty by default).
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public <K,V> Dictionary<K, V> getProperties() {
+        return (Dictionary<K, V>) EMPTY_PROPERTIES;
+    }
+
+    // -------------- DependencyContext interface -----------------------------------------------
+
+    /**
+     * Called by the Component implementation before the Dependency can be started.
+     */
+    @Override
+    public void setComponentContext(ComponentContext component) {
+        m_component = component;
+    }
+
+    /**
+     * A Component callback must be invoked with dependency event(s).
+     * @param type the dependency event type
+     * @param events the dependency service event to inject in the component. 
+     * The number of events depends on the dependency event type: ADDED/CHANGED/REMOVED types only has one event parameter, 
+     * but the SWAPPED type has two event parameters: the first one is the old event which must be replaced by the second one.
+     */
+    @Override
+    public void invokeCallback(EventType type, Event ... events) {
+    }
+
+    /**
+     * Starts this dependency. Subclasses can override this method but must then call super.start().
+     */
+    @Override
+    public void start() {
+        m_isStarted = true;
+    }
+
+    /**
+     * Starts this dependency. Subclasses can override this method but must then call super.stop().
+     */
+    @Override
+    public void stop() {
+        m_isStarted = false;
+    }
+
+    /**
+     * Indicates if this dependency has been started by the Component implementation.
+     */
+    @Override
+    public boolean isStarted() {
+        return m_isStarted;
+    }
+
+    /**
+     * Called by the Component implementation when the dependency is considered to be available.
+     */
+    @Override
+    public void setAvailable(boolean available) {
+        m_available = available;
+    }
+
+    /**
+     * Is this Dependency "instance bound" (has been defined within the component's init method) ?
+     */
+    public boolean isInstanceBound() {
+        return m_instanceBound;
+    }
+
+    /**
+     * Called by the Component implementation when the dependency is declared within the Component's init method.
+     */
+    public void setInstanceBound(boolean instanceBound) {
+        m_instanceBound = instanceBound;
+    }
+
+    /**
+     * Tells if the Component must be first instantiated before starting this dependency (false by default).
+     */
+    @Override
+    public boolean needsInstance() {
+        return false;
+    }
+
+    /**
+     * Returns the type of the field where this dependency can be injected (auto config), or return null
+     * if autoconfig is not supported.
+     */
+    @Override
+    public abstract Class<?> getAutoConfigType();
+
+    /**
+     * Get the highest ranked available dependency service, or null.
+     */
+    @Override
+    public Event getService() {
+        Event event = m_component.getDependencyEvent(this);
+        if (event == null) {
+            Object defaultService = getDefaultService(true);
+            if (defaultService != null) {
+                event = new Event(defaultService);
+            }
+        }
+        return event;
+    }
+
+    /**
+     * Copy all dependency service instances to the given collection.
+     */
+    @Override
+    public void copyToCollection(Collection<Object> services) {
+        Set<Event> events = m_component.getDependencyEvents(this);
+        if (events.size() > 0) {
+            for (Event e : events) {
+                services.add(e.getEvent());
+            }
+        } else {
+            Object defaultService = getDefaultService(false);
+            if (defaultService != null) {
+                services.add(defaultService);
+            }
+        }
+    }
+
+    /**
+     * Copy all dependency service instances to the given map (key = dependency service, value = dependency service properties.
+     */
+    @Override
+    public void copyToMap(Map<Object, Dictionary<?, ?>> map) {
+        Set<Event> events = m_component.getDependencyEvents(this);
+        if (events.size() > 0) {
+            for (Event e : events) {
+                map.put(e.getEvent(), e.getProperties());
+            }
+        } else {
+            Object defaultService = getDefaultService(false);
+            if (defaultService != null) {
+                map.put(defaultService, EMPTY_PROPERTIES);
+            }
+        }
+    }
+
+    /**
+     * Creates a copy of this Dependency.
+     */
+    @Override
+    public abstract DependencyContext createCopy();
+
+    // -------------- ComponentDependencyDeclaration -----------------------------------------------
+
+    /**
+     * Returns a description of this dependency (like the dependency service class name with associated filters)
+     */
+    @Override
+    public String getName() {
+        return getSimpleName();
+    }
+
+    /**
+     * Returns a simple name for this dependency (like the dependency service class name).
+     */
+    @Override
+    public abstract String getSimpleName();
+
+    /**
+     * Returns the dependency symbolic type.
+     */
+    @Override
+    public abstract String getType();
+
+    /**
+     * Returns the dependency filter, if any.
+     */
+    @Override
+    public String getFilter() {
+        return null;
+    }
+
+    /**
+     * Returns this dependency state.
+     */
+    @Override
+    public int getState() { // Can be called from any threads, but our class attributes are volatile
+        if (m_isStarted) {
+            return (isAvailable() ? 1 : 0) + (isRequired() ? 2 : 0);
+        } else {
+            return isRequired() ? ComponentDependencyDeclaration.STATE_REQUIRED
+                : ComponentDependencyDeclaration.STATE_OPTIONAL;
+        }
+    }
+
+    // -------------- Methods common to sub interfaces of Dependendency
+
+    /**
+     * Activates Dependency service properties propagation (to the service properties of the component to which this
+     * dependency is added).
+     * 
+     * @param propagate true if the dependency service properties must be propagated to the service properties of 
+     * the component to which this dependency is added. 
+     * @return this dependency instance
+     */
+    @SuppressWarnings("unchecked")
+    public T setPropagate(boolean propagate) {
+        ensureNotActive();
+        m_propagate = propagate;
+        return (T) this;
+    }
+
+    /**
+     * Sets a callback instance which can ba invoked with the given method in order to dynamically retrieve the 
+     * dependency service properties. 
+     * 
+     * @param instance the callback instance
+     * @param method the method to invoke on the callback instance
+     * @return this dependency instance
+     */
+    @SuppressWarnings("unchecked")
+    public T setPropagate(Object instance, String method) {
+        setPropagate(instance != null && method != null);
+        m_propagateCallbackInstance = instance;
+        m_propagateCallbackMethod = method;
+        return (T) this;
+    }
+
+    /**
+     * Sets the add/remove callbacks.
+     * @param add the callback to invoke when a dependency is added
+     * @param remove the callback to invoke when a dependency is removed
+     * @return this dependency instance
+     */
+    public T setCallbacks(String add, String remove) {
+        return setCallbacks(add, null, remove);
+    }
+
+    /**
+     * Sets the add/change/remove callbacks.
+     * @param add the callback to invoke when a dependency is added
+     * @param change the callback to invoke when a dependency has changed
+     * @param remove the callback to invoke when a dependency is removed
+     * @return this dependency instance
+     */
+    public T setCallbacks(String add, String change, String remove) {
+        return setCallbacks(null, add, change, remove);
+    }
+
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added or removed. They are called on the instance you provide. When you
+     * specify callbacks, the auto configuration feature is automatically turned off, because
+     * we're assuming you don't need it in this case.
+     * 
+     * @param instance the instance to call the callbacks on
+     * @param add the method to call when a service was added
+     * @param remove the method to call when a service was removed
+     * @return this service dependency
+     */
+    public T setCallbacks(Object instance, String add, String remove) {
+        return setCallbacks(instance, add, null, remove);
+    }
+
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added, changed or removed. They are called on the instance you provide. When you
+     * specify callbacks, the auto configuration feature is automatically turned off, because
+     * we're assuming you don't need it in this case.
+     * 
+     * @param instance the instance to call the callbacks on
+     * @param add the method to call when a service was added
+     * @param change the method to call when a service was changed
+     * @param remove the method to call when a service was removed
+     * @return this service dependency
+     */
+    @SuppressWarnings("unchecked")
+    public T setCallbacks(Object instance, String add, String change, String remove) {
+        if ((add != null || change != null || remove != null) && !m_autoConfigInvoked) {
+            setAutoConfig(false);
+        }
+        m_callbackInstance = instance;
+        m_add = add;
+        m_change = change;
+        m_remove = remove;
+        return (T) this;
+    }
+
+    /**
+     * Returns the dependency callback instances
+     * @return the dependency callback instances
+     */
+    public Object[] getInstances() {
+        if (m_callbackInstance == null) {
+            return m_component.getInstances();
+        } else {
+            return new Object[] { m_callbackInstance };
+        }
+    }
+
+    /**
+     * @see {@link ServiceDependency#setRequired(boolean)}
+     */
+    @SuppressWarnings("unchecked")
+    public T setRequired(boolean required) {
+        m_required = required;
+        return (T) this;
+    }
+
+    /**
+     * @see {@link ServiceDependency#setAutoConfig(boolean)}
+     */
+    @SuppressWarnings("unchecked")
+    public T setAutoConfig(boolean autoConfig) {
+        if (autoConfig && getAutoConfigType() == null) {
+            throw new IllegalStateException("Dependency does not support auto config mode");
+        }
+        m_autoConfig = autoConfig;
+        m_autoConfigInvoked = true;
+        return (T) this;
+    }
+
+    /**
+     * @see {@link ServiceDependency#setAutoConfig(String instanceName)}
+     */
+    @SuppressWarnings("unchecked")
+    public T setAutoConfig(String instanceName) {
+        if (instanceName != null && getAutoConfigType() == null) {
+            throw new IllegalStateException("Dependency does not support auto config mode");
+        }
+        m_autoConfig = (instanceName != null);
+        m_autoConfigInstance = instanceName;
+        m_autoConfigInvoked = true;
+        return (T) this;
+    }
+
+    /**
+     * Returns the component implementation context
+     * @return the component implementation context
+     */
+    public ComponentContext getComponentContext() {
+        return m_component;
+    }
+
+    /**
+     * Returns the default service, or null.
+     * @param nullObject if true, a null object may be returned.
+     * @return the default service
+     */
+    protected Object getDefaultService(boolean nullObject) {
+        return null;
+    }
+
+    /**
+     * Checks if the component dependency is not started.
+     */
+    protected void ensureNotActive() {
+        if (isStarted()) {
+            throw new IllegalStateException("Cannot modify state while active.");
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/ComponentContext.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/ComponentContext.java
new file mode 100644
index 0000000..a62619c
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/ComponentContext.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.context;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.Logger;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * This interface is the entry point to the Component implementation context.
+ * It is used by all DependencyManager Dependency implementations.
+ * 
+ * @see DependencyContext interface
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ComponentContext extends Component {
+    /**
+     * Returns the logger which can be used by the DependencyManager Dependencies implementations.
+     */
+    public Logger getLogger();
+    
+    /**
+     * Returns the Component's bundle context
+     * @return the Component's bundle context
+     */
+    public BundleContext getBundleContext();
+    
+    /**
+     * Returns the Compoent's bundle.
+     * @return the Compoent's bundle.
+     */
+    public Bundle getBundle();
+    
+    /**
+     * Sets a threadpool that the component will use when handling external events
+     * @param threadPool a threadpool used to handle component events and invoke the component's lifecycle callbacks
+     */
+    public void setThreadPool(Executor threadPool);
+    
+    /**
+     * Starts the component. All initial dependencies previously added to the component will be started.
+     */
+    public void start();
+    
+    /**
+     * Stops the component.
+     */
+    public void stop();
+    
+    /**
+     * Is this component already started ?
+     * @return true if this component has been started
+     */
+    public boolean isActive();
+    
+    /**
+     * Is this component available (all required dependencies are available) ?
+     * @return true if this component is available (all dependencies are available), or false
+     */
+    public boolean isAvailable();
+    
+    /**
+     * Notifies the Component about a dependency event.
+     * An event is for example fired when:<p>
+     * <ul>
+     * <li> a dependency service becomes available {@link EventType#ADDED}) 
+     * <li> a dependenc service has changed is changed {@link EventType#CHANGED}) 
+     * <li> a dependency service has been lost {@link EventType#REMOVED}) 
+     * <li> a dependency service has been swapped by another {@link EventType#SWAPPED}) 
+     * </ul> 
+     * @param dc the dependency
+     * @param type the dependency event type
+     * @param e the dependency event
+     * @see EventType
+     */
+    public void handleEvent(DependencyContext dc, EventType type, Event ... event);
+   
+    /**
+     * Returns the list of dependencies that has been registered on this component
+     * @return the list of dependencies that has been registered on this component
+     */
+    public List<DependencyContext> getDependencies();
+    
+    /**
+     * Invoke a component callback method with a given dependency service instance
+     * @param instances the component instances
+     * @param methodName the method name
+     * @param signatures the method signatures (types)
+     * @param parameters the method parameters
+     */
+    public void invokeCallbackMethod(Object[] instances, String methodName, Class<?>[][] signatures, Object[][] parameters);
+    
+    /**
+     * Returns the component instances
+     * @return the component instances
+     */
+    public Object[] getInstances();
+    
+    /**
+     * Returns the component instance field that is assignable to a given class type
+     * @param clazz the type of an object that has to be injected in the component instance field
+     * @return the name of the component instance field that can be assigned to an object having the same type as 
+     * the "clazz" parameter
+     */
+    public String getAutoConfigInstance(Class<?> clazz);
+    
+    /**
+     * Indicates if an object of the given class can be injected in one field of the component
+     * @param clazz the class of an object that has to be injected in one of the component fields
+     * @return true if the component can be injected with an object having the specified "clazz" type.
+     */
+    public boolean getAutoConfig(Class<?> clazz);
+    
+    /**
+     * Returns the highest ranked dependency service instance for a given dependency
+     * @param dc the dependency 
+     * @return the highest ranked dependency service instance for a given dependency
+     */
+    public Event getDependencyEvent(DependencyContext dc);
+    
+    /**
+     * Returns all the available dependency services for a given dependency
+     * @param dc the dependency 
+     * @return all the available dependency services for a given dependency
+     */
+    public Set<Event> getDependencyEvents(DependencyContext dc);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/DependencyContext.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/DependencyContext.java
new file mode 100644
index 0000000..42043e8
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/DependencyContext.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.context;
+
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Map;
+
+import org.apache.felix.dm.Dependency;
+
+/**
+ * Every DependencyManager Dependency implementations must implement this interface.
+ * 
+ * @see {@link AbstractDependency} which already implements most of the methods from this interface.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface DependencyContext extends Dependency {
+    /**
+     * Stores the Component implementation context in the Dependency Implementation. This object is the entry point to
+     * the Component implementation.
+     * @param component the Component implementation context
+     */
+    public void setComponentContext(ComponentContext component);
+    
+    /**
+     * Returns the Component implementation context associated to this Dependency context.
+     */
+    public ComponentContext getComponentContext();
+    
+    /**
+     * The Component implementation asks this dependency to invoke a component dependency callback.
+     * 
+     * @param type the type of the callback to invoke (add/change/remove/swap ...)
+     * @param events the dependency service event(s) that has previously been submitted to the component implementation using
+     * the ComponentContext.handleEvent method. The number of events depends on the event type: one event for ADDED/CHANGED/REMOVED,
+     * and two events for the SWAPPED event.
+     * @see ComponentContext#handleEvent(DependencyContext, EventType, Event...)
+     * @see EventType
+     */
+	public void invokeCallback(EventType type, Event ... events);
+			
+	/**
+	 *  Invoked by the component context when the dependency should start working. 
+	 **/
+	public void start();
+	
+	/** 
+	 * Invoked by the component context when the dependency should stop working.
+	 **/
+	public void stop();
+	
+	/**
+	 * Returns true if the dependency has been started, false if not
+	 * @return true if the dependency has been started, false if not
+	 */
+	public boolean isStarted();
+	
+	/**
+	 * Sets this dependency as available, meaning that at least one dependency service is available.
+	 * @param available true to mark this dependency as available, false to mark it as unavailable
+	 */
+	public void setAvailable(boolean available);
+		
+	/**
+     * Sets this dependency as "instance bound". A dependency is "instance bound" if it is defined from the 
+     * component's init method. 
+     * @param true if the dependency has to be marked as "intance bound", false if not.
+     */
+	public void setInstanceBound(boolean instanceBound);
+	
+	/**
+	 * Is this dependency instance bound ?
+	 * @return true if this dependency is instance bound, false if not
+	 */
+	public boolean isInstanceBound();
+
+	/** 
+	 * Does this dependency need the component instances to determine if the dependency is available or not.
+	 * @return true if the dependency need the component instances before it can be started, false if not.
+	 **/
+	public boolean needsInstance();
+	
+	/**
+	 * Returns the type of the field which can be injected with the dependency service.
+	 * @return the type of the field which can be injected with the dependency service, or null if the dependency does not 
+	 * support auto config mode.
+	 */
+    public Class<?> getAutoConfigType();
+    
+    /**
+     * Returns the highest ranked available dependency service instance, or null if the dependency is unavailable. 
+     * @return the highest ranked available dependency service instance, or null
+     */
+    public Event getService();
+    
+    /**
+     * Copies all the dependency service instances to the given collection.
+     * @param coll the collection where the dependency service instances will be copied
+     */
+    public void copyToCollection(Collection<Object> coll);
+    
+    /**
+     * Copies all the dependency service instances to the given map (key = dependency service, value = dependency servie properties).
+     * @param map the map where the dependency service instances (with the corresponding service properties)
+     */
+    public void copyToMap(Map<Object, Dictionary<?, ?>> map);
+    
+    /**
+     * Creates a clone of this dependency.
+     * @return a clone of this dependency.
+     */
+    public DependencyContext createCopy();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/Event.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/Event.java
new file mode 100644
index 0000000..1d48eaf
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/Event.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.context;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+/** 
+ * An event holds all data that belongs to some external event as it comes in via
+ * the 'changed' callback of a dependency.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Event implements Comparable<Event> {
+    protected final static Dictionary<Object, Object> EMPTY_PROPERTIES = new Hashtable<>();
+    private final Object m_event;    // the actual event object (a Service, a Bundle, a Configuration, etc ...)
+    
+    public Event(Object event) {
+        m_event = event;
+    }
+    
+    /**
+     * Returns the actual event object wrapped by this event (a Service Dependency, a Bundle for Bundle Dependency, etc...).
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T getEvent() {
+        return (T) m_event;
+    }
+    
+    /**
+     * Returns the properties of the actual event object wrapped by this event (Service Dependency properties, ...).
+     */
+    @SuppressWarnings("unchecked")
+    public <K,V> Dictionary<K,V> getProperties() {
+        return (Dictionary<K,V>) EMPTY_PROPERTIES;
+    }
+
+    @Override
+    public int hashCode() {
+        return m_event.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        // an instanceof check here is not "strong" enough with subclasses overriding the
+        // equals: we need to be sure that a.equals(b) == b.equals(a) at all times
+        if (obj != null && obj.getClass().equals(Event.class)) {
+            return (((Event) obj).m_event).equals(m_event);
+        }
+        return false;
+    }
+    
+    @Override
+    public int compareTo(Event o) {
+        return 0;
+    }
+    
+    /**
+     * Release the resources this event is holding (like service reference for example).
+     */
+    public void close() {
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/EventType.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/EventType.java
new file mode 100644
index 0000000..28f514f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/EventType.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.context;
+
+/**
+ * Types of dependency events
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public enum EventType {
+    /**
+     * A Dependency service becomes available.
+     */
+    ADDED, 
+    
+    /**
+     * A Dependency service has changed.    
+     */
+    CHANGED, 
+    
+    /**
+     * A Dependency service becomes unavailable.
+     */
+    REMOVED,
+    
+    /**
+     * A Dependency service has been swapped by another one.
+     */
+    SWAPPED
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/packageinfo b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/packageinfo
new file mode 100644
index 0000000..6af07c8
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/packageinfo
@@ -0,0 +1 @@
+version 4.0.0
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AbstractDecorator.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AbstractDecorator.java
new file mode 100644
index 0000000..044166b
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AbstractDecorator.java
@@ -0,0 +1,221 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentStateListener;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.context.ComponentContext;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ConfigurationException;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class AbstractDecorator  {
+    protected volatile DependencyManager m_manager;
+    private final Map<Object, Component> m_services = new ConcurrentHashMap<>();
+    
+    public abstract Component createService(Object[] properties);
+    
+    /**
+     * Catches our DependencyManager handle from our component init method.
+     */
+    public void init(Component c) {
+        m_manager = c.getDependencyManager();
+    }
+    
+    /**
+     * Extra method, which may be used by sub-classes, when adaptee has changed.
+     * For now, it's only used by the FactoryConfigurationAdapterImpl class, 
+     * but it might also make sense to use this for Resource Adapters ...
+     */
+    public void updateService(Object[] properties) {
+        throw new NoSuchMethodError("Method updateService not implemented");
+    }
+    
+    /**
+     * Set some service properties to all already instantiated services.
+     */
+    public void setServiceProperties(Dictionary<?,?> serviceProperties) {
+        for (Component component : m_services.values()) {
+            component.setServiceProperties(serviceProperties);
+        }
+    }
+    
+    /**
+     * Remove a StateListener from all already instantiated services.
+     */
+    public void addStateListener(ComponentStateListener listener) {
+        for (Component component : m_services.values()) {
+            component.add(listener);
+        }
+    }
+
+    /**
+     * Remove a StateListener from all already instantiated services.
+     */
+    public void removeStateListener(ComponentStateListener listener) {
+        for (Component component : m_services.values()) {
+            component.remove(listener);
+        }
+    }
+    
+    /**
+     * Add a Dependency to all already instantiated services.
+     */
+    public void addDependency(Dependency ... dependencies) {
+        for (Component component : m_services.values()) {
+            component.add(dependencies);
+        }
+    }
+    
+    /**
+     * Remove a Dependency from all instantiated services.
+     */
+    public void removeDependency(Dependency d) {
+        for (Component component : m_services.values()) {
+            component.remove(d);
+        }
+    }
+    
+    // callbacks for FactoryConfigurationAdapterImpl
+    public void updated(String pid, @SuppressWarnings("rawtypes") Dictionary properties) throws ConfigurationException {
+        try {
+            Component service = m_services.get(pid);
+            if (service == null) { 
+                service = createService(new Object[] { properties });
+                m_services.put(pid, service);
+                m_manager.add(service);
+            }
+            else {
+                updateService(new Object[] { properties, service });
+            }
+        }
+        catch (Throwable t) {
+            if (t instanceof ConfigurationException) {
+                throw (ConfigurationException) t;
+            }
+            else if (t.getCause() instanceof ConfigurationException) {
+                throw (ConfigurationException) t.getCause();
+            }
+            else {
+                throw new ConfigurationException(null, "Could not create service for ManagedServiceFactory Pid " + pid, t);
+            }
+        }
+    }
+
+    public void deleted(String pid) {
+        Component service = m_services.remove(pid);
+        if (service != null) {
+            m_manager.remove(service);
+        }
+    }
+
+    // callbacks for resources
+    public void added(URL resource) {
+        Component newService = createService(new Object[] { resource });
+        m_services.put(resource, newService);
+        m_manager.add(newService);
+    }
+
+    public void removed(URL resource) {
+        Component newService = m_services.remove(resource);
+        if (newService == null) {
+            throw new IllegalStateException("Service should not be null here.");
+        }
+        m_manager.remove(newService);
+    }
+    
+    // callbacks for services
+    public void added(ServiceReference ref, Object service) {
+        Component newService = createService(new Object[] { ref, service });
+        m_services.put(ref, newService);
+        m_manager.add(newService);
+    }
+    
+    public void removed(ServiceReference ref, Object service) {
+        Component newService;
+        newService = (Component) m_services.remove(ref);
+        if (newService == null) {
+            throw new IllegalStateException("Service should not be null here.");
+        }
+        m_manager.remove(newService);
+    }
+    
+    public void swapped(ServiceReference oldRef, Object oldService, ServiceReference newRef, Object newService) {
+        Component service = (Component) m_services.remove(oldRef);
+        if (service == null) {
+            throw new IllegalStateException("Service should not be null here.");
+        }           
+        m_services.put(newRef, service);
+    }
+    
+    // callbacks for bundles
+    public void added(Bundle bundle) {
+        Component newService = createService(new Object[] { bundle });
+        m_services.put(bundle, newService);
+        m_manager.add(newService);
+    }
+    
+    public void removed(Bundle bundle) {
+        Component newService;
+        newService = (Component) m_services.remove(bundle);
+        if (newService == null) {
+            throw new IllegalStateException("Service should not be null here.");
+        }
+        m_manager.remove(newService);
+    }
+      
+    public void stop() {
+        for (Component component : m_services.values()) {
+            m_manager.remove(component);
+        }
+    }    
+    
+    public void configureAutoConfigState(Component target, ComponentContext source) {
+        configureAutoConfigState(target, source, BundleContext.class);
+        configureAutoConfigState(target, source, ServiceRegistration.class);
+        configureAutoConfigState(target, source, DependencyManager.class);
+        configureAutoConfigState(target, source, Component.class);
+    }
+    
+    public Map<Object, Component> getServices() {
+        return m_services;
+    }
+
+    private void configureAutoConfigState(Component target, ComponentContext source, Class<?> clazz) {
+        String name = source.getAutoConfigInstance(clazz);
+        if (name != null) {
+            target.setAutoConfig(clazz, name);
+        }
+        else {
+            target.setAutoConfig(clazz, source.getAutoConfig(clazz));
+        }
+    }    
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/Activator.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/Activator.java
new file mode 100644
index 0000000..a3fa10c
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/Activator.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import org.apache.felix.dm.ComponentExecutorFactory;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+/**
+ * DependencyManager Activator used to track a ComponentExecutorFactory service optionally registered by 
+ * a management agent bundle.
+ * 
+ * @see {@link ComponentExecutorFactory}
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Activator implements BundleActivator, ServiceTrackerCustomizer {
+    private BundleContext m_context;
+    
+	@Override
+	public void start(BundleContext context) throws Exception {
+		m_context = context;
+        Filter filter = context.createFilter("(objectClass=" + ComponentExecutorFactory.class.getName() + ")");
+        ServiceTracker tracker = new ServiceTracker(context, filter, this);
+        tracker.open();
+	}
+
+	@Override
+	public void stop(BundleContext context) throws Exception {
+	}
+
+	@Override
+	public Object addingService(ServiceReference reference) {
+		ComponentExecutorFactory factory = (ComponentExecutorFactory) m_context.getService(reference);
+		ComponentScheduler.instance().bind(factory);
+		return factory;
+	}
+
+	@Override
+	public void modifiedService(ServiceReference reference, Object service) {
+	}
+
+	@Override
+	public void removedService(ServiceReference reference, Object service) {
+		ComponentExecutorFactory factory = (ComponentExecutorFactory) service;
+		ComponentScheduler.instance().unbind(factory);
+	}
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AdapterServiceImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AdapterServiceImpl.java
new file mode 100644
index 0000000..6ee3264
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AdapterServiceImpl.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentStateListener;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ServiceDependency;
+import org.apache.felix.dm.context.DependencyContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Adapter Service implementation. This class extends the FilterService in order to catch
+ * some Service methods for configuring actual adapter service implementation.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterServiceImpl extends FilterComponent {
+    /**
+     * Creates a new Adapter Service implementation.
+     * 
+     * @param dm the dependency manager used to create our internal adapter service
+     * @param adapteeInterface the service interface to apply the adapter to
+     * @param adapteeFilter the filter condition to use with the service interface
+     * @param autoConfig the name of the member to inject the service into
+     * @param callbackInstance the instance to invoke the callback on, or null 
+     * @param add name of the callback method to invoke on add
+     * @param change name of the callback method to invoke on change
+     * @param remove name of the callback method to invoke on remove
+     * @param swap name of the callback method to invoke on swap
+     * @param propagate true if the adaptee service properties should be propagated to the adapter service consumers
+     */
+    public AdapterServiceImpl(DependencyManager dm, Class<?> adapteeInterface, String adapteeFilter, String autoConfig, 
+        Object callbackInstance, String add, String change, String remove, String swap, boolean propagate)
+    {
+        super(dm.createComponent()); // This service will be filtered by our super class, allowing us to take control.
+        m_component.setImplementation(new AdapterImpl(adapteeInterface, adapteeFilter, autoConfig, callbackInstance, add, 
+            change, remove, swap, propagate))
+                 .add(dm.createServiceDependency()
+                      .setService(adapteeInterface, adapteeFilter)
+                      .setAutoConfig(false)
+                      .setCallbacks("added", null, "removed", "swapped"))
+                 .setCallbacks("init", null, "stop", null);
+    }	
+	    
+    public class AdapterImpl extends AbstractDecorator {
+        private final Class<?> m_adapteeInterface;
+        private final String m_adapteeFilter;
+        private final Object m_dependencyCallbackInstance;
+        private final String m_add;
+        private final String m_change;
+        private final String m_remove;
+        private final String m_swap;
+        private final String m_autoConfig;
+        private final boolean m_propagate;
+        
+        public AdapterImpl(Class<?> adapteeInterface, String adapteeFilter, String autoConfig, Object callbackInstance, String add, 
+            String change, String remove, String swap, boolean propagate) {
+            m_adapteeInterface = adapteeInterface;
+            m_adapteeFilter = adapteeFilter;
+            m_autoConfig = autoConfig;
+            m_dependencyCallbackInstance = callbackInstance;
+            m_add = add;
+            m_change = change;
+            m_swap = swap;
+            m_remove = remove;
+            m_propagate = propagate;
+        }
+        
+        public Component createService(Object[] properties) {
+            ServiceReference ref = (ServiceReference) properties[0]; 
+            Object aspect = ref.getProperty(DependencyManager.ASPECT);            
+            String serviceIdToTrack = (aspect != null) ? aspect.toString() : ref.getProperty(Constants.SERVICE_ID).toString();
+            List<DependencyContext> dependencies = m_component.getDependencies();
+            dependencies.remove(0);
+            ServiceDependency dependency = m_manager.createServiceDependency()
+            	 // create a dependency on both the service id we're adapting and possible aspects for this given service id
+            	 .setService(m_adapteeInterface, "(|(" + Constants.SERVICE_ID + "=" + serviceIdToTrack 
+            			 	+ ")(" + DependencyManager.ASPECT + "=" + serviceIdToTrack + "))")
+                 .setRequired(true);
+            if (m_add != null || m_change != null || m_remove != null || m_swap != null) {
+                dependency.setCallbacks(m_dependencyCallbackInstance, m_add, m_change, m_remove, m_swap);
+            }
+            if (m_autoConfig != null) {
+                dependency.setAutoConfig(m_autoConfig);
+            } else {
+                // enable auto configuration if there is no add callback or if there is one on a callbackInstance
+                dependency.setAutoConfig(m_add == null || (m_add != null && m_dependencyCallbackInstance != null));
+            }
+            
+            if (m_propagate) {
+                dependency.setPropagate(this, "propagateAdapteeProperties");
+            }
+            
+//            dependency.setDebug("AdapterDependency#" + m_adapteeInterface.getSimpleName());
+
+            Component service = m_manager.createComponent()
+                .setInterface(m_serviceInterfaces, getServiceProperties(ref))
+                .setImplementation(m_serviceImpl)
+                .setFactory(m_factory, m_factoryCreateMethod) // if not set, no effect
+                .setComposition(m_compositionInstance, m_compositionMethod) // if not set, no effect
+                .setCallbacks(m_callbackObject, m_init, m_start, m_stop, m_destroy) // if not set, no effect
+                .add(dependency);
+            
+            configureAutoConfigState(service, m_component);
+            
+            for (DependencyContext dc : dependencies) {
+                service.add((Dependency) dc.createCopy());
+            }
+            
+            for (ComponentStateListener stateListener : m_stateListeners) {
+                service.add(stateListener);
+            }
+            return service;
+        }
+        
+        public String toString() {
+            return "Adapter for " + m_adapteeInterface + ((m_adapteeFilter != null) ? " with filter " + m_adapteeFilter : "");
+        }
+        
+        public Dictionary<String, Object> getServiceProperties(ServiceReference ref) {
+            Dictionary<String, Object> props = new Hashtable<>();
+            if (m_serviceProperties != null) {
+                Enumeration<String> e = m_serviceProperties.keys();
+                while (e.hasMoreElements()) {
+                    String key = e.nextElement();
+                    props.put(key, m_serviceProperties.get(key));
+                }
+            }
+            return props;
+        }
+        
+        public Dictionary<String, Object> propagateAdapteeProperties(ServiceReference ref) {
+            Dictionary<String, Object> props = new Hashtable<>();
+            String[] keys = ref.getPropertyKeys();
+            for (int i = 0; i < keys.length; i++) {
+                String key = keys[i];
+                if (key.equals(DependencyManager.ASPECT) || key.equals(Constants.SERVICE_ID) || key.equals(Constants.SERVICE_RANKING) || key.equals(Constants.OBJECTCLASS)) {
+                    // do not copy these either
+                }
+                else {
+                    props.put(key, ref.getProperty(key));
+                }
+            }
+            return props;
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AspectServiceImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AspectServiceImpl.java
new file mode 100644
index 0000000..8adbe12
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AspectServiceImpl.java
@@ -0,0 +1,258 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentStateListener;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ServiceDependency;
+import org.apache.felix.dm.context.DependencyContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectServiceImpl extends FilterComponent {
+	
+	private final String m_add;
+	private final String m_change;
+	private final String m_remove;
+	private final String m_swap;
+	private int m_ranking;
+
+	public AspectServiceImpl(DependencyManager dm, Class<?> aspectInterface, String aspectFilter, int ranking, String autoConfig, String add, String change, String remove, String swap) {
+		super(dm.createComponent());
+		this.m_ranking = ranking;
+		this.m_add = add;
+		this.m_change = change;
+		this.m_remove = remove;
+		this.m_swap = swap;
+		
+		m_component.setImplementation(new AspectImpl(aspectInterface, autoConfig))
+			.add(dm.createServiceDependency()
+					.setService(aspectInterface, createDependencyFilterForAspect(aspectFilter))
+					.setAutoConfig(false)
+					.setCallbacks("added", "removed"))
+					.setCallbacks("init", null, "stop", null);
+		
+//		m_component.setDebug("aspectfactory-" + m_ranking);
+	}
+	
+	private String createDependencyFilterForAspect(String aspectFilter) {
+        // we only want to match services which are not themselves aspects
+        if (aspectFilter == null || aspectFilter.length() == 0) {
+            return "(!(" + DependencyManager.ASPECT + "=*))";
+        }
+        else {
+            return "(&(!(" + DependencyManager.ASPECT + "=*))" + aspectFilter + ")";
+        }  
+	}
+	
+    private Hashtable<String, Object> getServiceProperties(ServiceReference ref) {
+        Hashtable<String, Object> props = new Hashtable<>();
+        String[] keys = ref.getPropertyKeys();
+        for (int i = 0; i < keys.length; i++) {
+            String key = keys[i];
+            if (key.equals(Constants.SERVICE_ID) || key.equals(Constants.SERVICE_RANKING) || key.equals(DependencyManager.ASPECT) || key.equals(Constants.OBJECTCLASS)) {
+                // do not copy these
+            }
+            else {
+                props.put(key, ref.getProperty(key));
+            }
+        }
+        if (m_serviceProperties != null) {
+            Enumeration<String> e = m_serviceProperties.keys();
+            while (e.hasMoreElements()) {
+                String key = e.nextElement();
+                props.put(key, m_serviceProperties.get(key));
+            }
+        }
+        // finally add our aspect property
+        props.put(DependencyManager.ASPECT, ref.getProperty(Constants.SERVICE_ID));
+        // and the ranking
+        props.put(Constants.SERVICE_RANKING, Integer.valueOf(m_ranking));
+        return props;
+    }
+    	
+	class AspectImpl extends AbstractDecorator {
+
+		private final Class<?> m_aspectInterface;
+		private final String m_autoConfig;
+
+		public AspectImpl(Class<?> aspectInterface, String autoConfig) {
+			this.m_aspectInterface = aspectInterface;
+			this.m_autoConfig = autoConfig;
+		}
+
+        /**
+         * Creates an aspect implementation component for a new original service.
+         * @param param First entry contains the ref to the original service
+         */
+		@Override
+        public Component createService(Object[] params) {
+            // Get the new original service reference.
+            ServiceReference ref = (ServiceReference) params[0];
+            List<DependencyContext> dependencies = m_component.getDependencies();
+            // remove our internal dependency, replace it with one that points to the specific service that just was passed in.
+            dependencies.remove(0);
+            Hashtable<String, Object> serviceProperties = getServiceProperties(ref);
+            String[] serviceInterfaces = getServiceInterfaces();
+            
+            ServiceDependency aspectDependency = (ServiceDependencyImpl) 
+                    m_manager.createServiceDependency().setService(m_aspectInterface, createAspectFilter(ref)).setRequired(true);
+            //aspectDependency.setDebug("aspect " + m_ranking);
+
+            aspectDependency.setCallbacks(new CallbackProxy(aspectDependency, ref), 
+                            m_add != null ? "addAspect" : null, 
+                            "changeAspect", // We have to propagate in case aspect does not have a change callback
+                            m_remove != null ? "removeAspect" : null, 
+                            m_swap != null ? "swapAspect" : null);
+            
+            if (m_autoConfig != null) {
+                aspectDependency.setAutoConfig(m_autoConfig);
+            } else if (m_add == null && m_change == null && m_remove == null && m_swap == null) {
+                // Since we have set callbacks, we must reactivate setAutoConfig because user has not specified any callbacks.
+                aspectDependency.setAutoConfig(true);
+            }
+            
+            Component service = m_manager.createComponent()
+                .setInterface(serviceInterfaces, serviceProperties)
+                .setImplementation(m_serviceImpl)
+                .setFactory(m_factory, m_factoryCreateMethod) // if not set, no effect
+                .setComposition(m_compositionInstance, m_compositionMethod) // if not set, no effect
+                .setCallbacks(m_callbackObject, m_init, m_start, m_stop, m_destroy) // if not set, no effect
+                .add(aspectDependency);
+            
+            //service.setDebug("aspectimpl-" + m_ranking);
+            
+            configureAutoConfigState(service, m_component);
+            
+            for (DependencyContext dc : dependencies) {
+                service.add((Dependency) dc.createCopy());
+            }
+
+            for (int i = 0; i < m_stateListeners.size(); i++) {
+                service.add((ComponentStateListener) m_stateListeners.get(i));
+            }
+            return service;                
+        }
+        
+        /**
+         * Modify some specific aspect service properties.
+         */
+		@Override
+        public void setServiceProperties(Dictionary<?,?> props) {
+		    for (Map.Entry<Object, Component> e : super.getServices().entrySet()) {
+		        ServiceReference originalServiceRef = (ServiceReference) e.getKey();
+                Component c = e.getValue();
+                // m_serviceProperties is already set to the new service properties; and the getServiceProperties will
+                // merge m_serviceProperties with the original service properties.
+                Dictionary<String, Object> newProps = getServiceProperties(originalServiceRef);
+                c.setServiceProperties(newProps);
+            }
+        }
+                
+        private String[] getServiceInterfaces() {
+            List<String> serviceNames = new ArrayList<>();
+            // Of course, we provide the aspect interface.
+            serviceNames.add(m_aspectInterface.getName());
+            // But also append additional aspect implementation interfaces.
+            if (m_serviceInterfaces != null) {
+                for (int i = 0; i < m_serviceInterfaces.length; i ++) {
+                    if (!m_serviceInterfaces[i].equals(m_aspectInterface.getName())) {
+                        serviceNames.add(m_serviceInterfaces[i]);
+                    }
+                }
+            }
+            return (String[]) serviceNames.toArray(new String[serviceNames.size()]);
+        }
+        
+        private String createAspectFilter(ServiceReference ref) {
+            Long sid = (Long) ref.getProperty(Constants.SERVICE_ID);
+            return "(&(|(!(" + Constants.SERVICE_RANKING + "=*))(" + Constants.SERVICE_RANKING + "<=" + (m_ranking - 1) + "))(|(" + Constants.SERVICE_ID + "=" + sid + ")(" + DependencyManager.ASPECT + "=" + sid + ")))";
+        }
+		
+        public String toString() {
+            return "Aspect for " + m_aspectInterface.getName();
+        }
+	}
+	
+    class CallbackProxy {
+        private final ServiceDependencyImpl m_aspectDependency;
+        private final ServiceReference m_originalServiceRef;
+
+        CallbackProxy(ServiceDependency aspectDependency, ServiceReference originalServiceRef) {
+            m_aspectDependency = (ServiceDependencyImpl) aspectDependency;
+            m_originalServiceRef = originalServiceRef;
+        }
+
+        @SuppressWarnings("unused")
+		private void addAspect(Component c, ServiceReference ref, Object service) {
+            // Just forward "add" service dependency callback.
+        	
+        	// Invoke is done on dependency.getInstances() which unfortunately returns this callback instance...
+        	ServiceEventImpl event = new ServiceEventImpl(ref, service);
+        	m_aspectDependency.invoke(m_add, event, m_aspectDependency.getComponentContext().getInstances());
+        }
+
+        @SuppressWarnings("unused")
+		private void changeAspect(Component c, ServiceReference ref, Object service) {
+            // Invoke "change" service dependency callback
+            if (m_change != null) {
+            	ServiceEventImpl event = new ServiceEventImpl(ref, service);
+                m_aspectDependency.invoke(m_change, event, m_aspectDependency.getComponentContext().getInstances());
+            }
+            // Propagate change to immediate higher aspect, or to client using our aspect.
+            // We always propagate our own properties, and the ones from the original service, but we don't inherit
+            // from lower ranked aspect service properties.
+            Dictionary<String, Object> props = getServiceProperties(m_originalServiceRef);
+            c.setServiceProperties(props);
+        }
+
+        @SuppressWarnings("unused")
+		private void removeAspect(Component c, ServiceReference ref, Object service) {
+            // Just forward "remove" service dependency callback.
+        	ServiceEventImpl event = new ServiceEventImpl(ref, service);
+        	m_aspectDependency.invoke(m_remove, event, m_aspectDependency.getComponentContext().getInstances());
+        }
+
+        @SuppressWarnings("unused")
+		private void swapAspect(Component c, ServiceReference prevRef, Object prev, ServiceReference currRef,
+                                Object curr) {
+        	Object[] instances = m_aspectDependency.getComponentContext().getInstances();        	        	
+            // Just forward "swap" service dependency callback.
+        	m_aspectDependency.invokeSwap(m_swap, prevRef, prev, currRef, curr, m_aspectDependency.getComponentContext().getInstances());
+        }
+        
+        @Override
+        public String toString() {
+        	return "CallbackProxy";
+        }
+    }
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleAdapterImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleAdapterImpl.java
new file mode 100644
index 0000000..c883ebf
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleAdapterImpl.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentStateListener;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.context.DependencyContext;
+import org.osgi.framework.Bundle;
+
+/**
+ * Bundle Adapter Service implementation. This class extends the FilterService in order to catch
+ * some Service methods for configuring actual adapter service implementation.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class BundleAdapterImpl extends FilterComponent
+{
+    /**
+     * Creates a new Bundle Adapter Service implementation.
+     */
+    public BundleAdapterImpl(DependencyManager dm, int bundleStateMask, String bundleFilter, boolean propagate)
+    {
+        super(dm.createComponent()); // This service will be filtered by our super class, allowing us to take control.
+        m_component.setImplementation(new BundleAdapterDecorator(bundleStateMask, propagate))
+                 .add(dm.createBundleDependency()
+                      .setFilter(bundleFilter)
+                      .setStateMask(bundleStateMask)
+                      .setCallbacks("added", "removed"))
+                 .setCallbacks("init", null, "stop", null);
+    }
+
+    public class BundleAdapterDecorator extends AbstractDecorator {
+        private final boolean m_propagate;
+        private final int m_bundleStateMask;
+
+        public BundleAdapterDecorator(int bundleStateMask, boolean propagate) {
+            m_bundleStateMask = bundleStateMask;
+            m_propagate = propagate;
+        }
+        
+        public Component createService(Object[] properties) {
+            Bundle bundle = (Bundle) properties[0];
+            Hashtable<String, Object> props = new Hashtable<>();
+            if (m_serviceProperties != null) {
+                Enumeration<String> e = m_serviceProperties.keys();
+                while (e.hasMoreElements()) {
+                    String key = e.nextElement();
+                    props.put(key, m_serviceProperties.get(key));
+                }
+            }
+            List<DependencyContext> dependencies = m_component.getDependencies();
+            // the first dependency is always the dependency on the bundle, which
+            // will be replaced with a more specific dependency below
+            dependencies.remove(0);
+            Component service = m_manager.createComponent()
+                .setInterface(m_serviceInterfaces, props)
+                .setImplementation(m_serviceImpl)
+                .setFactory(m_factory, m_factoryCreateMethod) // if not set, no effect
+                .setComposition(m_compositionInstance, m_compositionMethod) // if not set, no effect
+                .setCallbacks(m_callbackObject, m_init, m_start, m_stop, m_destroy) // if not set, no effect
+                .add(m_manager.createBundleDependency()
+                    .setBundle(bundle)
+                    .setStateMask(m_bundleStateMask)
+                    .setPropagate(m_propagate)
+                    .setCallbacks(null, "changed", null)
+                    .setAutoConfig(true)
+                    .setRequired(true));
+
+            for (DependencyContext dc : dependencies) {
+                service.add((Dependency) dc.createCopy());
+            }
+
+            for (ComponentStateListener stateListener : m_stateListeners) {
+                service.add(stateListener);
+            }
+            configureAutoConfigState(service, m_component);
+            return service;
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleDependencyImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleDependencyImpl.java
new file mode 100644
index 0000000..6e5dbd1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleDependencyImpl.java
@@ -0,0 +1,298 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Proxy;
+import java.util.Dictionary;
+
+import org.apache.felix.dm.BundleDependency;
+import org.apache.felix.dm.ComponentDependencyDeclaration;
+import org.apache.felix.dm.context.AbstractDependency;
+import org.apache.felix.dm.context.DependencyContext;
+import org.apache.felix.dm.context.Event;
+import org.apache.felix.dm.context.EventType;
+import org.apache.felix.dm.tracker.BundleTracker;
+import org.apache.felix.dm.tracker.BundleTrackerCustomizer;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class BundleDependencyImpl extends AbstractDependency<BundleDependency> implements BundleDependency, BundleTrackerCustomizer, ComponentDependencyDeclaration {
+    private BundleTracker m_tracker;
+    private int m_stateMask = Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE;
+    private Bundle m_bundleInstance;
+    private Filter m_filter;
+    private long m_bundleId = -1;
+    private Object m_nullObject;
+    private boolean m_propagate;
+    private Object m_propagateCallbackInstance;
+    private String m_propagateCallbackMethod;
+
+    public BundleDependencyImpl() {
+    }
+    
+    public BundleDependencyImpl(BundleDependencyImpl prototype) {
+        super(prototype);
+        m_stateMask = prototype.m_stateMask;
+        m_nullObject = prototype.m_nullObject;
+        m_bundleInstance = prototype.m_bundleInstance;
+        m_filter = prototype.m_filter;
+        m_bundleId = prototype.m_bundleId;
+        m_propagate = prototype.m_propagate;
+        m_propagateCallbackInstance = prototype.m_propagateCallbackInstance;
+        m_propagateCallbackMethod = prototype.m_propagateCallbackMethod;       
+    }
+    
+    @Override
+    public DependencyContext createCopy() {
+        return new BundleDependencyImpl(this);
+    }
+    
+    @Override
+    public void start() {
+        m_tracker = new BundleTracker(m_component.getBundleContext(), m_stateMask, this);
+        m_tracker.open();
+        super.start();
+    }
+
+    @Override
+    public void stop() {
+        m_tracker.close();
+        m_tracker = null;
+        super.stop();
+    }
+    
+    @Override
+    public String getName() {
+        StringBuilder sb = new StringBuilder();
+        getSimpleName(sb);
+        if (m_filter != null) {
+            sb.append(" ");
+            sb.append(m_filter.toString());
+        }
+        if (m_bundleId != -1) {
+            sb.append("{bundle.id=" + m_bundleId + "}");
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public String getSimpleName() {
+        // Return the state mask, but don't include the filter or bundle id.
+        StringBuilder sb = new StringBuilder();
+        if ((m_stateMask & Bundle.ACTIVE) != 0) {
+            sb.append("active");
+        }
+        if ((m_stateMask & Bundle.INSTALLED) != 0) {
+            if (sb.length() > 0) {
+                sb.append(" ");
+            }
+            sb.append("installed");
+        }
+        if ((m_stateMask & Bundle.RESOLVED) != 0) {
+            if (sb.length() > 0) {
+                sb.append(" ");
+            }
+            sb.append("resolved");
+        }
+        return sb.toString();
+    }
+    
+    @Override
+    public String getFilter() {
+        if (m_filter != null || m_bundleId != -1) {
+            StringBuilder sb = new StringBuilder();
+            if (m_filter != null) {
+                sb.append(m_filter.toString());
+            }
+            if (m_bundleId != -1) {
+                sb.append("{bundle.id=" + m_bundleId + "}");
+            }
+            return sb.toString();
+        }
+        return null;
+    }
+
+    @Override
+    public String getType() {
+        return "bundle";
+    }
+
+    public Object addingBundle(Bundle bundle, BundleEvent event) {
+        // if we don't like a bundle, we could reject it here by returning null
+        long bundleId = bundle.getBundleId();
+        if (m_bundleId >= 0 && m_bundleId != bundleId) {
+            return null;
+        }
+        Filter filter = m_filter;
+        if (filter != null) {
+            Dictionary<?,?> headers = bundle.getHeaders();
+            if (!m_filter.match(headers)) {
+                return null;
+            }
+        }
+        return bundle;
+    }
+    
+    public void addedBundle(Bundle bundle, BundleEvent event, Object object) {
+        m_component.handleEvent(this, EventType.ADDED, new BundleEventImpl(bundle, event));
+    }
+        
+    public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) {
+        m_component.handleEvent(this, EventType.CHANGED, new BundleEventImpl(bundle, event));
+    }
+
+    public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
+        m_component.handleEvent(this, EventType.REMOVED, new BundleEventImpl(bundle, event));
+    }
+    
+    @Override
+    public void invokeCallback(EventType type, Event ... e) {
+        switch (type) {
+        case ADDED:
+            if (m_add != null) {
+                invoke(m_add, e[0]);
+            }
+            break;
+        case CHANGED:
+            if (m_change != null) {
+                invoke (m_change, e[0]);
+            }
+            break;
+        case REMOVED:
+            if (m_remove != null) {
+                invoke (m_remove, e[0]);
+            }
+            break;
+        default:
+            break;
+        }
+    }
+            
+    private void invoke(String method, Event e) {
+        BundleEventImpl be = (BundleEventImpl) e;
+        m_component.invokeCallbackMethod(getInstances(), method,
+            new Class[][] {{Bundle.class}, {Object.class}, {}},             
+            new Object[][] {{be.getBundle()}, {be.getBundle()}, {}});
+    }
+    
+    public BundleDependency setBundle(Bundle bundle) {
+        m_bundleId = bundle.getBundleId();
+        return this;
+    }
+
+    public BundleDependency setFilter(String filter) throws IllegalArgumentException {
+        if (filter != null) {
+            try {
+                m_filter = FrameworkUtil.createFilter(filter);
+            } 
+            catch (InvalidSyntaxException e) {
+                throw new IllegalArgumentException(e.getMessage());
+            }
+        }
+        return this;
+    }
+    
+    public BundleDependency setStateMask(int mask) {
+        m_stateMask = mask;
+        return this;
+    }
+    
+    @Override
+    public Class<?> getAutoConfigType() {
+        return Bundle.class;
+    }
+        
+    @SuppressWarnings("unchecked")
+    @Override
+    public Dictionary<String, Object> getProperties() {
+        Event event = getService();
+        if (event != null) {
+            Bundle bundle = (Bundle) event.getEvent();
+            if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
+                try {
+                    return (Dictionary<String, Object>) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, new Class[][] {{ Bundle.class }}, new Object[][] {{ bundle }});
+                }
+                catch (InvocationTargetException e) {
+                    m_component.getLogger().warn("Exception while invoking callback method", e.getCause());
+                }
+                catch (Throwable e) {
+                    m_component.getLogger().warn("Exception while trying to invoke callback method", e);
+                }
+                throw new IllegalStateException("Could not invoke callback");
+            }
+            else {
+                return (Dictionary<String, Object>) bundle.getHeaders();
+            }
+        }
+        else {
+            throw new IllegalStateException("cannot find bundle");
+        }
+    }
+    
+    @Override
+    public Object getDefaultService(boolean nullObject) {
+        Object service = null;
+        if (isAutoConfig()) {
+            // TODO does it make sense to add support for custom bundle impls?
+            // service = getDefaultImplementation();
+            if (service == null && nullObject) {
+                service = getNullObject();
+            }
+        }
+        return service;
+    }
+
+    private Bundle getNullObject() {
+        if (m_nullObject == null) {
+            try {
+                m_nullObject = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { Bundle.class }, new DefaultNullObject()); 
+            }
+            catch (Throwable e) {
+                m_component.getLogger().err("Could not create null object for Bundle.", e);
+            }
+        }
+        return (Bundle) m_nullObject;
+    }
+    
+    private void getSimpleName(StringBuilder sb) {
+        if ((m_stateMask & Bundle.ACTIVE) != 0) {
+            sb.append("active");
+        }
+        if ((m_stateMask & Bundle.INSTALLED) != 0) {
+            if (sb.length() > 0) {
+                sb.append(" ");
+            }
+            sb.append("installed");
+        }
+        if ((m_stateMask & Bundle.RESOLVED) != 0) {
+            if (sb.length() > 0) {
+                sb.append(" ");
+            }
+            sb.append("resolved");
+        }
+    }
+}
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleEventImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleEventImpl.java
new file mode 100644
index 0000000..187165f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleEventImpl.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.util.Dictionary;
+
+import org.apache.felix.dm.context.Event;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleEvent;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class BundleEventImpl extends Event {
+    final BundleEvent m_event;
+    
+    public BundleEventImpl(Bundle bundle, BundleEvent event) {
+        super(bundle);
+        m_event = event;
+    }
+        
+    public Bundle getBundle() {
+        return getEvent();
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Override
+    public Dictionary<String, Object> getProperties() {
+        return getBundle().getHeaders();
+    }
+    
+    public BundleEvent getBundleEvent() {
+        return m_event;
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof BundleEventImpl) {
+            return getBundle().getBundleId() == ((BundleEventImpl) obj).getBundle().getBundleId();
+        }
+        return false;
+    }
+    
+    @Override
+    public int hashCode() {
+        return getBundle().hashCode();
+    }
+
+    @Override
+    public int compareTo(Event b) {
+        return Long.compare(getBundle().getBundleId(), ((BundleEventImpl) b).getBundle().getBundleId());
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentImpl.java
new file mode 100644
index 0000000..f08d4c1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentImpl.java
@@ -0,0 +1,1345 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import static org.apache.felix.dm.ComponentState.INACTIVE;
+import static org.apache.felix.dm.ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED;
+import static org.apache.felix.dm.ComponentState.TRACKING_OPTIONAL;
+import static org.apache.felix.dm.ComponentState.WAITING_FOR_REQUIRED;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentDeclaration;
+import org.apache.felix.dm.ComponentDependencyDeclaration;
+import org.apache.felix.dm.ComponentState;
+import org.apache.felix.dm.ComponentStateListener;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.Logger;
+import org.apache.felix.dm.context.ComponentContext;
+import org.apache.felix.dm.context.DependencyContext;
+import org.apache.felix.dm.context.EventType;
+import org.apache.felix.dm.context.Event;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.log.LogService;
+
+/**
+ * Dependency Manager Component implementation.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ComponentImpl implements Component, ComponentContext, ComponentDeclaration {
+	private static final ServiceRegistration NULL_REGISTRATION = (ServiceRegistration) Proxy
+			.newProxyInstance(ComponentImpl.class.getClassLoader(),
+					new Class[] { ServiceRegistration.class },
+					new DefaultNullObject());
+    private static final Class<?>[] VOID = new Class[] {};
+	private volatile Executor m_executor = new SerialExecutor(new Logger(null));
+	private ComponentState m_state = ComponentState.INACTIVE;
+	private final CopyOnWriteArrayList<DependencyContext> m_dependencies = new CopyOnWriteArrayList<>();
+	private final List<ComponentStateListener> m_listeners = new CopyOnWriteArrayList<>();
+	private boolean m_isStarted;
+    private final Logger m_logger;
+    private final BundleContext m_context;
+    private final DependencyManager m_manager;
+    private Object m_componentDefinition;
+	private Object m_componentInstance;
+    private volatile Object m_serviceName;
+    private volatile Dictionary<Object, Object> m_serviceProperties;
+    private volatile ServiceRegistration m_registration;
+    private final Map<Class<?>, Boolean> m_autoConfig = new ConcurrentHashMap<>();
+    private final Map<Class<?>, String> m_autoConfigInstance = new ConcurrentHashMap<>();
+    private final Map<String, Long> m_stopwatch = new ConcurrentHashMap<>();
+    private final long m_id;
+    private static AtomicLong m_idGenerator = new AtomicLong();
+    // Holds all the services of a given dependency context. Caution: the last entry in the skiplist is the highest ranked service.
+    private final Map<DependencyContext, ConcurrentSkipListSet<Event>> m_dependencyEvents = new HashMap<>();
+    private final AtomicBoolean m_active = new AtomicBoolean(false);
+        
+    public Component setDebug(String debugKey) {
+    	// Force debug level in our logger
+        m_logger.setEnabledLevel(LogService.LOG_DEBUG);
+        m_logger.setDebugKey(debugKey);
+    	return this;
+    }
+
+    // configuration (static)
+    private volatile String m_callbackInit;
+    private volatile String m_callbackStart;
+    private volatile String m_callbackStop;
+    private volatile String m_callbackDestroy;
+    private volatile Object m_callbackInstance;
+    
+    // instance factory
+	private volatile Object m_instanceFactory;
+	private volatile String m_instanceFactoryCreateMethod;
+	
+	// composition manager
+	private volatile Object m_compositionManager;
+	private volatile String m_compositionManagerGetMethod;
+	private volatile Object m_compositionManagerInstance;
+    private final Bundle m_bundle;
+	
+    static class SCDImpl implements ComponentDependencyDeclaration {
+        private final String m_name;
+        private final int m_state;
+        private final String m_type;
+
+        public SCDImpl(String name, int state, String type) {
+            m_name = name;
+            m_state = state;
+            m_type = type;
+        }
+
+        public String getName() {
+            return m_name;
+        }
+        
+        public String getSimpleName() {
+            return m_name;
+        }
+        
+        public String getFilter() {
+            return null;
+        }
+
+        public int getState() {
+            return m_state;
+        }
+
+        public String getType() {
+            return m_type;
+        }
+    }
+
+    public ComponentImpl() {
+	    this(null, null, new Logger(null));
+	}
+	
+    public ComponentImpl(BundleContext context, DependencyManager manager, Logger logger) {
+        m_context = context;
+        m_bundle = context != null ? context.getBundle() : null;
+        m_manager = manager;
+        m_logger = logger;
+        m_autoConfig.put(BundleContext.class, Boolean.TRUE);
+        m_autoConfig.put(ServiceRegistration.class, Boolean.TRUE);
+        m_autoConfig.put(DependencyManager.class, Boolean.TRUE);
+        m_autoConfig.put(Component.class, Boolean.TRUE);
+        m_callbackInit = "init";
+        m_callbackStart = "start";
+        m_callbackStop = "stop";
+        m_callbackDestroy = "destroy";
+        m_id = m_idGenerator.getAndIncrement();
+    }
+
+	@Override
+	public Component add(final Dependency ... dependencies) {
+		getExecutor().execute(new Runnable() {
+			@Override
+			public void run() {
+				List<DependencyContext> instanceBoundDeps = new ArrayList<>();
+				for (Dependency d : dependencies) {
+					DependencyContext dc = (DependencyContext) d;
+					if (dc.getComponentContext() != null) {
+                        m_logger.err("%s can't be added to %s (dependency already added to another component).", dc,
+                            ComponentImpl.this);
+                        continue;
+					}
+					m_dependencyEvents.put(dc,  new ConcurrentSkipListSet<Event>());
+					m_dependencies.add(dc);
+					dc.setComponentContext(ComponentImpl.this);
+					if (!(m_state == ComponentState.INACTIVE)) {
+						dc.setInstanceBound(true);
+						instanceBoundDeps.add(dc);
+					}
+				}
+				startDependencies(instanceBoundDeps);
+				handleChange();
+			}
+		});
+		return this;
+	}
+
+	@Override
+	public Component remove(final Dependency d) {
+		getExecutor().execute(new Runnable() {
+			@Override
+			public void run() {
+				DependencyContext dc = (DependencyContext) d;
+				// First remove this dependency from the dependency list
+                m_dependencies.remove(d);
+                // Now we can stop the dependency (our component won't be deactivated, it will only be unbound with
+                // the removed dependency).
+				if (!(m_state == ComponentState.INACTIVE)) {
+					dc.stop();
+				}
+				// Finally, cleanup the dependency events.
+                m_dependencyEvents.remove(d);
+				handleChange();
+			}
+		});
+		return this;
+	}
+
+	public void start() {
+	    if (m_active.compareAndSet(false, true)) {
+            getExecutor().execute(new Runnable() {
+                @Override
+                public void run() {
+                    m_isStarted = true;
+                    handleChange();
+                }
+            });
+	    }
+	}
+	
+	public void stop() {           
+	    if (m_active.compareAndSet(true, false)) {
+	        Executor executor = getExecutor();
+
+	        // First, declare the task that will stop our component in our executor.
+	        final Runnable stopTask = new Runnable() {
+                @Override
+                public void run() {
+                	m_isStarted = false;
+                	handleChange();
+                }
+            };
+            
+            // Now, we have to schedule our stopTask in our component executor. But we have to handle a special case:
+            // if the component bundle is stopping *AND* if the executor is a parallel dispatcher, then we want 
+            // to invoke our stopTask synchronously, in order to make sure that the bundle context is valid while our 
+            // component is being deactivated (if we stop the component asynchronously, the bundle context may be invalidated
+            // before our component is stopped, and we don't want to be in this situation).
+            
+            boolean stopping = m_bundle != null /* null only in tests env */ && m_bundle.getState() == Bundle.STOPPING;
+            if (stopping && executor instanceof DispatchExecutor) {
+            	((DispatchExecutor) executor).execute(stopTask, false /* try to  execute synchronously, not using threadpool */);
+            } else {
+            	executor.execute(stopTask);
+            }
+	    }
+	}
+
+	@SuppressWarnings("unchecked")
+    @Override
+	public Component setInterface(String serviceName, Dictionary<?, ?> properties) {
+		ensureNotActive();
+	    m_serviceName = serviceName;
+	    m_serviceProperties = (Dictionary<Object, Object>) properties;
+	    return this;
+	}
+
+	@SuppressWarnings("unchecked")
+    @Override
+	public Component setInterface(String[] serviceName, Dictionary<?, ?> properties) {
+	    ensureNotActive();
+	    m_serviceName = serviceName;
+	    m_serviceProperties = (Dictionary<Object, Object>) properties;
+	    return this;
+	}
+	
+	@Override
+    public void handleEvent(final DependencyContext dc, final EventType type, final Event... event) {
+        // since this method can be invoked by anyone from any thread, we need to
+        // pass on the event to a runnable that we execute using the component's
+        // executor
+        getExecutor().execute(new Runnable() {
+            @Override
+            public void run() {
+                switch (type) {
+                case ADDED:
+                    handleAdded(dc, event[0]);
+                    break;
+                case CHANGED:
+                    handleChanged(dc, event[0]);
+                    break;
+                case REMOVED:
+                    handleRemoved(dc, event[0]);
+                    break;
+                case SWAPPED:
+                    handleSwapped(dc, event[0], event[1]);
+                    break;
+                }
+            }
+        });
+	}
+
+    @Override
+    public Event getDependencyEvent(DependencyContext dc) {
+        ConcurrentSkipListSet<Event> events = m_dependencyEvents.get(dc);
+        return events.size() > 0 ? events.last() : null;
+    }
+    
+    @Override
+    public Set<Event> getDependencyEvents(DependencyContext dc) {
+        return m_dependencyEvents.get(dc);
+    }
+
+    public Component setAutoConfig(Class<?> clazz, boolean autoConfig) {
+        m_autoConfig.put(clazz, Boolean.valueOf(autoConfig));
+        return this;
+    }
+    
+    public Component setAutoConfig(Class<?> clazz, String instanceName) {
+        m_autoConfig.put(clazz, Boolean.valueOf(instanceName != null));
+        m_autoConfigInstance.put(clazz, instanceName);
+        return this;
+    }
+    
+    public boolean getAutoConfig(Class<?> clazz) {
+        Boolean result = (Boolean) m_autoConfig.get(clazz);
+        return (result != null && result.booleanValue());
+    }
+    
+    public String getAutoConfigInstance(Class<?> clazz) {
+        return (String) m_autoConfigInstance.get(clazz);
+    }
+
+    private void handleAdded(DependencyContext dc, Event e) {
+        if (! m_isStarted) {
+            return;
+        }
+        m_logger.debug("handleAdded %s", e);
+        
+        Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
+        dependencyEvents.add(e);        
+        dc.setAvailable(true);
+        
+        // Recalculate state changes. We only do this if the dependency is started. If the dependency is not started,
+        // it means it is actually starting. And in this case, we don't recalculate state changes now. We'll do it 
+        // once all currently available services are found, and then after, we'll recalculate state change 
+        // (see the startDependencies method).
+        // All this is done for two reasons:
+        // 1- optimization: it is preferable to recalculate state changes once we know about all currently available dependency services
+        //    (after the tracker has returned from its open method).
+        // 2- This also allows to determine the list of currently available dependency services from within the component start method callback
+        //    (this will be extremely useful when porting the Felix SCR on top of DM4).
+        
+        if (dc.isStarted()) {
+            switch (m_state) {
+            case WAITING_FOR_REQUIRED:
+                if (dc.isRequired())
+                    handleChange();
+                break;
+            case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
+                if (!dc.isInstanceBound()) {
+                    if (dc.isRequired()) {
+                        dc.invokeCallback(EventType.ADDED, e);
+                    }
+                    updateInstance(dc, e, false, true);
+                }
+                else {
+                    if (dc.isRequired())
+                        handleChange();
+                }
+                break;
+            case TRACKING_OPTIONAL:
+                dc.invokeCallback(EventType.ADDED, e);
+                updateInstance(dc, e, false, true);
+                break;
+            default:
+            }
+        }
+    }
+        
+    private void handleChanged(final DependencyContext dc, final Event e) {
+        if (! m_isStarted) {
+            return;
+        }
+        Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
+        dependencyEvents.remove(e);
+        dependencyEvents.add(e);
+        
+        if (dc.isStarted()) {
+            switch (m_state) {
+            case TRACKING_OPTIONAL:
+                dc.invokeCallback(EventType.CHANGED, e);
+                updateInstance(dc, e, true, false);
+                break;
+
+            case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
+                if (!dc.isInstanceBound()) {
+                    dc.invokeCallback(EventType.CHANGED, e);
+                    updateInstance(dc, e, true, false);
+                }
+                break;
+            default:
+                // noop
+            }
+        }
+    }
+    
+    private void handleRemoved(DependencyContext dc, Event e) {
+        if (! m_isStarted) {
+            return;
+        }
+        // Check if the dependency is still available.
+        Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
+        int size = dependencyEvents.size();
+        if (dependencyEvents.contains(e)) {
+            size--; // the dependency is currently registered and is about to be removed.
+        }
+        dc.setAvailable(size > 0);
+        
+        // If the dependency is now unavailable, we have to recalculate state change. This will result in invoking the
+        // "removed" callback with the removed dependency (which we have not yet removed from our dependency events list.).
+        // But we don't recalculate the state if the dependency is not started (if not started, it means that it is currently starting,
+        // and the tracker is detecting a removed service).
+        if (size == 0 && dc.isStarted()) {
+            handleChange();
+        }
+        
+        // Now, really remove the dependency event.
+        dependencyEvents.remove(e);    
+        
+        // Only check if the component instance has to be updated if the dependency is really started.
+        if (dc.isStarted()) {
+            // Depending on the state, we possible have to invoke the callbacks and update the component instance.        
+            switch (m_state) {
+            case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
+                if (!dc.isInstanceBound()) {
+                    if (dc.isRequired()) {
+                        dc.invokeCallback(EventType.REMOVED, e);
+                    }
+                    updateInstance(dc, e, false, false);
+                }
+                break;
+            case TRACKING_OPTIONAL:
+                dc.invokeCallback(EventType.REMOVED, e);
+                updateInstance(dc, e, false, false);
+                break;
+            default:
+            }
+        }
+    }
+    
+    private void handleSwapped(DependencyContext dc, Event oldEvent, Event newEvent) {
+        if (! m_isStarted) {
+            return;
+        }
+        Set<Event> dependencyEvents = m_dependencyEvents.get(dc);        
+        dependencyEvents.remove(oldEvent);
+        dependencyEvents.add(newEvent);
+                
+        if (dc.isStarted()) {
+            // Depending on the state, we possible have to invoke the callbacks and update the component instance.        
+            switch (m_state) {
+            case WAITING_FOR_REQUIRED:
+                // No need to swap, we don't have yet injected anything
+                break;
+            case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
+                // Only swap *non* instance-bound dependencies
+                if (!dc.isInstanceBound()) {
+                    if (dc.isRequired()) {
+                        dc.invokeCallback(EventType.SWAPPED, oldEvent, newEvent); 
+                    }
+                }
+                break;
+            case TRACKING_OPTIONAL:
+                dc.invokeCallback(EventType.SWAPPED, oldEvent, newEvent);
+                break;
+            default:
+            }
+        }
+    }
+    
+	private void handleChange() {
+	    m_logger.debug("handleChanged");
+		try {
+			ComponentState oldState;
+			ComponentState newState;
+			do {
+				oldState = m_state;
+				newState = calculateNewState(oldState);
+				m_logger.debug("%s -> %s", oldState, newState);
+				m_state = newState;
+			} while (performTransition(oldState, newState));
+		} finally {
+		    m_logger.debug("end handling change.");
+		}
+	}
+    
+	/** Based on the current state, calculate the new state. */
+	private ComponentState calculateNewState(ComponentState currentState) {
+		if (currentState == INACTIVE) {
+			if (m_isStarted) {
+				return WAITING_FOR_REQUIRED;
+			}
+		}
+		if (currentState == WAITING_FOR_REQUIRED) {
+			if (!m_isStarted) {
+				return INACTIVE;
+			}
+			if (allRequiredAvailable()) {
+				return INSTANTIATED_AND_WAITING_FOR_REQUIRED;
+			}
+		}
+		if (currentState == INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
+			if (m_isStarted && allRequiredAvailable()) {
+				if (allInstanceBoundAvailable()) {
+					return TRACKING_OPTIONAL;
+				}
+				return currentState;
+			}
+			return WAITING_FOR_REQUIRED;
+		}
+		if (currentState == TRACKING_OPTIONAL) {
+			if (m_isStarted && allRequiredAvailable() && allInstanceBoundAvailable()) {
+				return currentState;
+			}
+			return INSTANTIATED_AND_WAITING_FOR_REQUIRED;
+		}
+		return currentState;
+	}
+
+	/** Perform all the actions associated with state transitions. Returns true if a transition was performed. */
+	private boolean performTransition(ComponentState oldState, ComponentState newState) {
+//		System.out.println("transition from " + oldState + " to " + newState);
+		if (oldState == ComponentState.INACTIVE && newState == ComponentState.WAITING_FOR_REQUIRED) {
+		    startDependencies(m_dependencies);
+			notifyListeners(newState);
+			return true;
+		}
+		if (oldState == ComponentState.WAITING_FOR_REQUIRED && newState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
+			instantiateComponent();
+            invokeAutoConfigDependencies();
+			invokeAddRequiredDependencies();
+			ComponentState stateBeforeCallingInit = m_state;
+	        invoke(m_callbackInit); 
+	        if (stateBeforeCallingInit == m_state) {
+	            notifyListeners(newState); // init did not change current state, we can notify about this new state
+	        }
+			return true;
+		}
+		if (oldState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED && newState == ComponentState.TRACKING_OPTIONAL) {
+            invokeAutoConfigInstanceBoundDependencies();
+			invokeAddRequiredInstanceBoundDependencies();
+			invoke(m_callbackStart);
+			invokeAddOptionalDependencies();
+			registerService();
+			notifyListeners(newState);
+			return true;
+		}
+		if (oldState == ComponentState.TRACKING_OPTIONAL && newState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
+		    unregisterService();
+		    invokeRemoveOptionalDependencies();
+			invoke(m_callbackStop);
+			invokeRemoveInstanceBoundDependencies();
+			notifyListeners(newState);
+			return true;
+		}
+		if (oldState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED && newState == ComponentState.WAITING_FOR_REQUIRED) {
+			invoke(m_callbackDestroy);
+			invokeRemoveRequiredDependencies();
+			notifyListeners(newState);
+			if (! someDependenciesNeedInstance()) {
+                destroyComponent();
+            }
+			return true;
+		}
+		if (oldState == ComponentState.WAITING_FOR_REQUIRED && newState == ComponentState.INACTIVE) {
+		    stopDependencies();
+            destroyComponent();
+			notifyListeners(newState);
+			return true;
+		}
+		return false;
+	}
+	
+    private boolean allRequiredAvailable() {
+        boolean available = true;
+        for (DependencyContext d : m_dependencies) {
+            if (d.isRequired() && !d.isInstanceBound()) {
+                if (!d.isAvailable()) {
+                    available = false;
+                    break;
+                }
+            }
+        }
+        return available;
+    }
+
+    private boolean allInstanceBoundAvailable() {
+        boolean available = true;
+        for (DependencyContext d : m_dependencies) {
+            if (d.isRequired() && d.isInstanceBound()) {
+                if (!d.isAvailable()) {
+                    available = false;
+                    break;
+                }
+            }
+        }
+        return available;
+    }
+
+    private boolean someDependenciesNeedInstance() {
+        for (DependencyContext d : m_dependencies) {
+            if (d.needsInstance()) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    /**
+     * Updates the component instance(s).
+     * @param dc the dependency context for the updating dependency service
+     * @param event the event holding the updating service (service + properties)
+     * @param update true if dependency service properties are updating, false if not. If false, it means
+     *        that a dependency service is being added or removed. (see the "add" flag).
+     * @param add true if the dependency service has been added, false if it has been removed. This flag is 
+     *        ignored if the "update" flag is true (because the dependency properties are just being updated).
+     */
+    private void updateInstance(DependencyContext dc, Event event, boolean update, boolean add) {
+        if (dc.isAutoConfig()) {
+            updateImplementation(dc.getAutoConfigType(), dc, dc.getAutoConfigName(), event, update, add);
+        }
+        if (dc.isPropagated() && m_registration != null) {
+            m_registration.setProperties(calculateServiceProperties());
+        }
+    }
+    
+    private void startDependencies(List<DependencyContext> dependencies) {
+        // Start first optional dependencies first.
+        m_logger.debug("startDependencies.");
+        List<DependencyContext> requiredDeps = new ArrayList<>();
+        for (DependencyContext d : dependencies) {
+            if (d.isRequired()) {
+                requiredDeps.add(d);
+                continue;
+            }
+            if (d.needsInstance()) {
+                instantiateComponent();
+            }
+            d.start();
+        }
+        // now, start required dependencies.
+        for (DependencyContext d : requiredDeps) {
+            if (d.needsInstance()) {
+                instantiateComponent();
+            }
+            d.start();
+        }
+        // The started dependencies has probably called our handleAdded method: we now have to run our state machine.
+        handleChange();
+    }
+    
+    private void stopDependencies() {
+        for (DependencyContext d : m_dependencies) {
+            d.stop();
+        }
+    }
+
+    private void registerService() {
+        if (m_context != null && m_serviceName != null) {
+            ServiceRegistrationImpl wrapper = new ServiceRegistrationImpl();
+            m_registration = wrapper;
+            autoConfigureImplementation(ServiceRegistration.class, m_registration);
+            
+            // service name can either be a string or an array of strings
+            ServiceRegistration registration;
+
+            // determine service properties
+            Dictionary<?,?> properties = calculateServiceProperties();
+
+            // register the service
+            try {
+                if (m_serviceName instanceof String) {
+                    registration = m_context.registerService((String) m_serviceName, m_componentInstance, properties);
+                }
+                else {
+                    registration = m_context.registerService((String[]) m_serviceName, m_componentInstance, properties);
+                }
+                wrapper.setServiceRegistration(registration);
+            }
+            catch (IllegalArgumentException iae) {
+                m_logger.log(Logger.LOG_ERROR, "Could not register service " + m_componentInstance, iae);
+                // set the registration to an illegal state object, which will make all invocations on this
+                // wrapper fail with an ISE (which also occurs when the SR becomes invalid)
+                wrapper.setIllegalState();
+            }
+        }
+    }
+
+    private void unregisterService() {
+        if (m_serviceName != null && m_registration != null) {
+            try {
+                if (m_bundle != null && m_bundle.getState() == Bundle.ACTIVE) {
+                    m_registration.unregister();
+                }
+            } catch (IllegalStateException e) { /* Should we really log this ? */}
+            autoConfigureImplementation(ServiceRegistration.class, NULL_REGISTRATION);
+            m_registration = null;
+        }
+    }
+    
+    private Dictionary<Object, Object> calculateServiceProperties() {
+		Dictionary<Object, Object> properties = new Hashtable<>();
+		for (int i = 0; i < m_dependencies.size(); i++) {
+			DependencyContext d = (DependencyContext) m_dependencies.get(i);
+			if (d.isPropagated() && d.isAvailable()) {
+				Dictionary<Object, Object> dict = d.getProperties();
+				addTo(properties, dict);
+			}
+		}
+		// our service properties must not be overriden by propagated dependency properties, so we add our service
+		// properties after having added propagated dependency properties.
+		addTo(properties, m_serviceProperties);
+		if (properties.size() == 0) {
+			properties = null;
+		}
+		return properties;
+	}
+
+	private void addTo(Dictionary<Object, Object> properties, Dictionary<Object, Object> additional) {
+		if (properties == null) {
+			throw new IllegalArgumentException("Dictionary to add to cannot be null.");
+		}
+		if (additional != null) {
+			Enumeration<Object> e = additional.keys();
+			while (e.hasMoreElements()) {
+				Object key = e.nextElement();
+				properties.put(key, additional.get(key));
+			}
+		}
+	}
+
+	private void instantiateComponent() {
+        m_logger.debug("instantiating component.");
+
+		// TODO add more complex factory instantiations of one or more components in a composition here
+	    if (m_componentInstance == null) {
+            if (m_componentDefinition instanceof Class) {
+                try {
+                    m_componentInstance = createInstance((Class<?>) m_componentDefinition);
+                }
+                catch (Exception e) {
+                    m_logger.log(Logger.LOG_ERROR, "Could not instantiate class " + m_componentDefinition, e);
+                }
+            }
+            else {
+	        	if (m_instanceFactoryCreateMethod != null) {
+	        		Object factory = null;
+		        	if (m_instanceFactory != null) {
+		        		if (m_instanceFactory instanceof Class) {
+		        			try {
+								factory = createInstance((Class<?>) m_instanceFactory);
+							}
+		                    catch (Exception e) {
+		                        m_logger.log(Logger.LOG_ERROR, "Could not create factory instance of class " + m_instanceFactory + ".", e);
+		                    }
+		        		}
+		        		else {
+		        			factory = m_instanceFactory;
+		        		}
+		        	}
+		        	else {
+		        		// TODO review if we want to try to default to something if not specified
+		        	    // for now the JavaDoc of setFactory(method) reflects the fact that we need
+		        	    // to review it
+		        	}
+		        	if (factory == null) {
+                        m_logger.log(Logger.LOG_ERROR, "Factory cannot be null.");
+		        	}
+		        	else {
+    		        	try {
+    		        		m_componentInstance = InvocationUtil.invokeMethod(factory, factory.getClass(), m_instanceFactoryCreateMethod, new Class[][] {{}}, new Object[][] {{}}, false);
+    					}
+    		        	catch (Exception e) {
+    	                    m_logger.log(Logger.LOG_ERROR, "Could not create service instance using factory " + factory + " method " + m_instanceFactoryCreateMethod + ".", e);
+    					}
+		        	}
+	        	}
+            }
+            
+            if (m_componentInstance == null) {
+                m_componentInstance = m_componentDefinition;
+            }
+            
+	        // configure the bundle context
+            autoConfigureImplementation(BundleContext.class, m_context);
+            autoConfigureImplementation(ServiceRegistration.class, NULL_REGISTRATION);
+            autoConfigureImplementation(DependencyManager.class, m_manager);
+            autoConfigureImplementation(Component.class, this);
+	    }
+	}
+	
+	private void destroyComponent() {
+		m_componentInstance = null;
+	}
+	
+	private void invokeAddRequiredDependencies() {
+		for (DependencyContext d : m_dependencies) {
+			if (d.isRequired() && !d.isInstanceBound()) {
+			    for (Event e : m_dependencyEvents.get(d)) {
+			        d.invokeCallback(EventType.ADDED, e);
+			    }
+			}
+		}
+	}
+	
+    private void invokeAutoConfigDependencies() {
+        for (DependencyContext d : m_dependencies) {
+            if (d.isAutoConfig() && !d.isInstanceBound()) {
+                configureImplementation(d.getAutoConfigType(), d, d.getAutoConfigName());
+            }
+        }
+    }
+    
+    private void invokeAutoConfigInstanceBoundDependencies() {
+        for (DependencyContext d : m_dependencies) {
+            if (d.isAutoConfig() && d.isInstanceBound()) {
+                configureImplementation(d.getAutoConfigType(), d, d.getAutoConfigName());
+            }
+        }
+    }
+	
+	private void invokeAddRequiredInstanceBoundDependencies() {
+		for (DependencyContext d : m_dependencies) {
+			if (d.isRequired() && d.isInstanceBound()) {
+	             for (Event e : m_dependencyEvents.get(d)) {
+	                 d.invokeCallback(EventType.ADDED, e);
+	             }
+			}
+		}
+	}
+	
+    private void invokeAddOptionalDependencies() {
+        for (DependencyContext d : m_dependencies) {
+            if (! d.isRequired()) {
+                for (Event e : m_dependencyEvents.get(d)) {
+                    d.invokeCallback(EventType.ADDED, e);
+                }
+            }
+        }
+    }
+
+    private void invokeRemoveRequiredDependencies() { 
+		for (DependencyContext d : m_dependencies) {
+			if (!d.isInstanceBound() && d.isRequired()) {
+                for (Event e : m_dependencyEvents.get(d)) {
+                    d.invokeCallback(EventType.REMOVED, e);
+                }
+			}
+		}
+	}
+
+    private void invokeRemoveOptionalDependencies() { 
+        for (DependencyContext d : m_dependencies) {
+            if (! d.isRequired()) {
+                for (Event e : m_dependencyEvents.get(d)) {
+                    d.invokeCallback(EventType.REMOVED, e);
+                }
+            }
+        }
+    }
+
+	private void invokeRemoveInstanceBoundDependencies() {
+		for (DependencyContext d : m_dependencies) {
+			if (d.isInstanceBound()) {
+                for (Event e : m_dependencyEvents.get(d)) {
+                    d.invokeCallback(EventType.REMOVED, e);
+                }
+			}
+		}
+	}
+
+	private void invoke(String name) {
+        if (name != null) {
+            // if a callback instance was specified, look for the method there, if not,
+            // ask the service for its composition instances
+            Object[] instances = m_callbackInstance != null ? new Object[] { m_callbackInstance } : getCompositionInstances();
+
+            long t1 = System.nanoTime();
+            try {
+                invokeCallbackMethod(instances, name, 
+                    new Class[][] {{ Component.class }, {}}, 
+                    new Object[][] {{ this }, {}},
+                    false);
+            } finally {
+                long t2 = System.nanoTime();
+                m_stopwatch.put(name, t2 - t1);
+            }
+        }
+    }
+    
+	@SuppressWarnings("unchecked")
+    public <T> T getInstance() {     
+	    Object[] instances  = getCompositionInstances();
+	    return instances.length == 0 ? null : (T) instances[0]; 
+    }
+
+    public Object[] getInstances() {
+    	return getCompositionInstances();
+    }
+    
+    public void invokeCallbackMethod(Object[] instances, String methodName, Class<?>[][] signatures, Object[][] parameters) {
+        invokeCallbackMethod(instances, methodName, signatures, parameters, true);
+    }
+
+    public void invokeCallbackMethod(Object[] instances, String methodName, Class<?>[][] signatures,
+        Object[][] parameters, boolean logIfNotFound) {
+        boolean callbackFound = false;
+        for (int i = 0; i < instances.length; i++) {
+            try {
+                InvocationUtil.invokeCallbackMethod(instances[i], methodName, signatures, parameters);
+                callbackFound |= true;
+            }
+            catch (NoSuchMethodException e) {
+                // if the method does not exist, ignore it
+            }
+            catch (InvocationTargetException e) {
+                // the method itself threw an exception, log that
+                m_logger.log(Logger.LOG_ERROR, "Invocation of '" + methodName + "' failed.", e.getCause());
+            }
+            catch (Throwable e) {
+                m_logger.log(Logger.LOG_ERROR, "Could not invoke '" + methodName + "'.", e);
+            }
+        }
+        
+        // If the callback is not found, we don't log if the method is on an AbstractDecorator.
+        // (Aspect or Adapter are not interested in user dependency callbacks)        
+        if (logIfNotFound && ! callbackFound && ! (getInstance() instanceof AbstractDecorator)) {
+            if (m_logger == null) {
+                System.out.println("Callback \"" + methodName + "\" not found on componnent instances "
+                    + Arrays.toString(getInstances()));
+            } else {
+                m_logger.log(LogService.LOG_ERROR, "Callback \"" + methodName + "\" callback not found on componnent instances "
+                    + Arrays.toString(getInstances()));
+            }
+
+        }
+    }
+
+   private Object createInstance(Class<?> clazz) throws SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+		Constructor<?> constructor = clazz.getConstructor(VOID);
+		constructor.setAccessible(true);
+        return constructor.newInstance();
+    }
+
+	private void notifyListeners(ComponentState state) {
+		for (ComponentStateListener l : m_listeners) {
+			l.changed(this, state);
+		}
+	}
+	
+	@Override
+	public boolean isAvailable() {
+		return m_state == TRACKING_OPTIONAL;
+	}
+	
+	@Override
+	public boolean isActive() {
+	    return m_active.get();
+	}
+	
+	@Override
+	public Component add(final ComponentStateListener l) {
+	    m_listeners.add(l);
+	    return this;
+	}
+
+	@Override
+	public Component remove(ComponentStateListener l) {
+	    m_listeners.remove(l);
+	    return this;
+	}
+
+	@SuppressWarnings("unchecked")
+    @Override
+	public List<DependencyContext> getDependencies() {
+		return (List<DependencyContext>) m_dependencies.clone();
+	}
+
+	@Override
+	public Component setImplementation(Object implementation) {
+		m_componentDefinition = implementation;
+		return this;
+	}
+	
+    private void autoConfigureImplementation(Class<?> clazz, Object instance) {
+        if (((Boolean) m_autoConfig.get(clazz)).booleanValue()) {
+            configureImplementation(clazz, instance, (String) m_autoConfigInstance.get(clazz));
+        }
+    }
+    
+   /**
+     * 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 object to fill in the implementation class(es) field
+     * @param instanceName the name of the instance to fill in, or <code>null</code> if not used
+     */
+    private void configureImplementation(Class<?> clazz, Object instance, String fieldName) {
+        Object[] targets = getInstances();
+        if (! FieldUtil.injectField(targets, fieldName, clazz, instance, m_logger) && fieldName != null) {
+            // If the target is an abstract decorator (i.e: an adapter, or an aspect), we must not log warnings
+            // if field has not been injected.
+            if (! (getInstance() instanceof AbstractDecorator)) {
+                m_logger.log(Logger.LOG_ERROR, "Could not inject " + instance + " to field \"" + fieldName
+                    + "\" at any of the following component instances: " + Arrays.toString(targets));
+            }
+        }
+    }
+
+    private void configureImplementation(Class<?> clazz, DependencyContext dc, String fieldName) {
+        Object[] targets = getInstances();
+        if (! FieldUtil.injectDependencyField(targets, fieldName, clazz, dc, m_logger) && fieldName != null) {
+            // If the target is an abstract decorator (i.e: an adapter, or an aspect), we must not log warnings
+            // if field has not been injected.
+            if (! (getInstance() instanceof AbstractDecorator)) {
+                m_logger.log(Logger.LOG_ERROR, "Could not inject dependency " + clazz.getName() + " to field \""
+                    + fieldName + "\" at any of the following component instances: " + Arrays.toString(targets));
+            }
+        }
+    }
+
+    /**
+     * Update the component instances.
+     *
+     * @param clazz the class of the dependency service to inject in the component instances
+     * @param dc the dependency context for the updating dependency service
+     * @param fieldName the component instances fieldname to fill in with the updated dependency service
+     * @param event the event holding the updating service (service + properties)
+     * @param update true if dependency service properties are updating, false if not. If false, it means
+     *        that a dependency service is being added or removed. (see the "add" flag).
+     * @param add true if the dependency service has been added, false if it has been removed. This flag is 
+     *        ignored if the "update" flag is true (because the dependency properties are just being updated).
+     */
+    private void updateImplementation(Class<?> clazz, DependencyContext dc, String fieldName, Event event, boolean update,
+        boolean add)
+    {
+        Object[] targets = getInstances();
+        FieldUtil.updateDependencyField(targets, fieldName, update, add, clazz, event, dc, m_logger);
+    }
+
+    @Override
+	public ServiceRegistration getServiceRegistration() {
+        return m_registration;
+	}
+
+	@SuppressWarnings("unchecked")
+    @Override
+	public <K,V> Dictionary<K, V> getServiceProperties() {
+		if (m_serviceProperties != null) {
+			// Applied patch from FELIX-4304
+			Hashtable<Object, Object> serviceProperties = new Hashtable<>();
+			addTo(serviceProperties, m_serviceProperties);
+			return (Dictionary<K, V>) serviceProperties;
+		}
+		return null;
+	}
+
+	@Override
+    @SuppressWarnings("unchecked")
+	public Component setServiceProperties(final Dictionary<?, ?> serviceProperties) {
+	    getExecutor().execute(new Runnable() {
+            @Override
+			public void run() {
+			    Dictionary<Object, Object> properties = null;
+		        m_serviceProperties = (Dictionary<Object, Object>) serviceProperties;
+		        if ((m_registration != null) && (m_serviceName != null)) {
+		            properties = calculateServiceProperties();
+			        m_registration.setProperties(properties);
+		        }
+			}
+		});
+	    return this;
+	}
+	
+	public Component 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 Component setCallbacks(Object instance, String init, String start, String stop, String destroy) {
+	    ensureNotActive();
+        m_callbackInstance = instance;
+        m_callbackInit = init;
+        m_callbackStart = start;
+        m_callbackStop = stop;
+        m_callbackDestroy = destroy;
+        return this;
+    }
+
+	@Override
+	public Component setFactory(Object factory, String createMethod) {
+	    ensureNotActive();
+		m_instanceFactory = factory;
+		m_instanceFactoryCreateMethod = createMethod;
+		return this;
+	}
+
+	@Override
+	public Component setFactory(String createMethod) {
+		return setFactory(null, createMethod);
+	}
+
+	@Override
+	public Component setComposition(Object instance, String getMethod) {
+	    ensureNotActive();
+		m_compositionManager = instance;
+		m_compositionManagerGetMethod = getMethod;
+		return this;
+	}
+
+	@Override
+	public Component setComposition(String getMethod) {
+		return setComposition(null, getMethod);
+	}
+
+	private Object[] getCompositionInstances() {
+        Object[] instances = null;
+        if (m_compositionManagerGetMethod != null) {
+            if (m_compositionManager != null) {
+                m_compositionManagerInstance = m_compositionManager;
+            }
+            else {
+                m_compositionManagerInstance = m_componentInstance;
+            }
+            if (m_compositionManagerInstance != null) {
+                try {
+                    instances = (Object[]) InvocationUtil.invokeMethod(m_compositionManagerInstance, m_compositionManagerInstance.getClass(), m_compositionManagerGetMethod, new Class[][] {{}}, new Object[][] {{}}, false);
+                }
+                catch (Exception e) {
+                    m_logger.log(Logger.LOG_ERROR, "Could not obtain instances from the composition manager.", e);
+                    instances = m_componentInstance == null ? new Object[] {} : new Object[] { m_componentInstance };
+                }
+            }
+        }
+        else {
+            instances = m_componentInstance == null ? new Object[] {} : new Object[] { m_componentInstance };
+        }
+        return instances;
+	}
+
+	@Override
+	public DependencyManager getDependencyManager() {
+        return m_manager;
+	}
+	
+    public ComponentDependencyDeclaration[] getComponentDependencies() {
+        List<DependencyContext> deps = getDependencies();
+        if (deps != null) {
+            ComponentDependencyDeclaration[] result = new ComponentDependencyDeclaration[deps.size()];
+            for (int i = 0; i < result.length; i++) {
+                DependencyContext dep = (DependencyContext) deps.get(i);
+                if (dep instanceof ComponentDependencyDeclaration) {
+                    result[i] = (ComponentDependencyDeclaration) dep;
+                }
+                else {
+                    result[i] = new SCDImpl(dep.toString(), (dep.isAvailable() ? 1 : 0) + (dep.isRequired() ? 2 : 0), dep.getClass().getName());
+                }
+            }
+            return result;
+        }
+        return null;
+    }
+    
+    public String getName() {
+        StringBuffer sb = new StringBuffer();
+        Object serviceName = m_serviceName;
+        if (serviceName instanceof String[]) {
+            String[] names = (String[]) serviceName;
+            for (int i = 0; i < names.length; i++) {
+                if (i > 0) {
+                    sb.append(", ");
+                }
+                sb.append(names[i]);
+            }
+            appendProperties(sb);
+        } else if (serviceName instanceof String) {
+            sb.append(serviceName.toString());
+            appendProperties(sb);
+        } else {
+            Object implementation = m_componentDefinition;
+            if (implementation != null) {
+                if (implementation instanceof Class) {
+                    sb.append(((Class<?>) implementation).getName());
+                } else {
+                    // If the implementation instance does not override "toString", just display
+                    // the class name, else display the component using its toString method
+                    try {
+                	Method m = implementation.getClass().getMethod("toString", new Class[0]);
+                        if (m.getDeclaringClass().equals(Object.class)) {
+                            sb.append(implementation.getClass().getName());
+                        } else {
+                            sb.append(implementation.toString());
+                        }
+                    }  catch (java.lang.NoSuchMethodException e) {
+                        // Just display the class name
+                        sb.append(implementation.getClass().getName());
+                    }
+                }
+            } else {
+                sb.append(super.toString());
+            }
+        }
+        return sb.toString();
+    }
+    
+    private void appendProperties(StringBuffer result) {
+        Dictionary<Object, Object> properties = calculateServiceProperties();
+        if (properties != null) {
+            result.append("(");
+            Enumeration<?> enumeration = properties.keys();
+            while (enumeration.hasMoreElements()) {
+                Object key = enumeration.nextElement();
+                result.append(key.toString());
+                result.append('=');
+                Object value = properties.get(key);
+                if (value instanceof String[]) {
+                    String[] values = (String[]) value;
+                    result.append('{');
+                    for (int i = 0; i < values.length; i++) {
+                        if (i > 0) {
+                            result.append(',');
+                        }
+                        result.append(values[i].toString());
+                    }
+                    result.append('}');
+                }
+                else {
+                    result.append(value.toString());
+                }
+                if (enumeration.hasMoreElements()) {
+                    result.append(',');
+                }
+            }
+            result.append(")");
+        }
+    }
+    
+    @Override
+    public BundleContext getBundleContext() {
+        return m_context;
+    }
+    
+    @Override
+    public Bundle getBundle() {
+        return m_bundle;
+    }
+
+    public long getId() {
+        return m_id;
+    }
+    
+    public String getClassName() {
+        Object serviceInstance = m_componentInstance;
+        if (serviceInstance != null) {
+            return serviceInstance.getClass().getName();
+        } 
+        
+        Object implementation = m_componentDefinition;
+        if (implementation != null) {
+            if (implementation instanceof Class) {
+                return ((Class<?>) implementation).getName();
+            }
+            return implementation.getClass().getName();
+        } 
+        
+        Object instanceFactory = m_instanceFactory;
+        if (instanceFactory != null) {
+            return instanceFactory.getClass().getName();
+        } else {
+            // unexpected.
+            return ComponentImpl.class.getName();
+        }
+    }
+    
+    public String[] getServices() {
+        if (m_serviceName instanceof String[]) {
+            return (String[]) m_serviceName;
+        } else if (m_serviceName instanceof String) {
+            return new String[] { (String) m_serviceName };
+        } else {
+            return null;
+        }
+    }
+    
+    public int getState() {
+        return (isAvailable() ? ComponentDeclaration.STATE_REGISTERED : ComponentDeclaration.STATE_UNREGISTERED);
+    }
+
+    public void ensureNotActive() {
+        if (m_active.get()) {
+            throw new IllegalStateException("Can't modify an already started component.");
+        }
+    }
+    
+    public ComponentDeclaration getComponentDeclaration() {
+        return this;
+    }
+    
+    @Override
+    public String toString() {
+    	if (m_logger.getDebugKey() != null) {
+    		return m_logger.getDebugKey();
+    	}
+    	return getClassName();
+    }
+    
+    @Override
+    public void setThreadPool(Executor threadPool) {
+        ensureNotActive();
+        m_executor = new DispatchExecutor(threadPool, m_logger);
+    }
+    
+    @Override
+    public Logger getLogger() {
+        return m_logger;
+    }
+
+    @Override
+    public Map<String, Long> getCallbacksTime() {
+        return m_stopwatch;
+    }
+    
+    private Executor getExecutor() {
+        return m_executor;
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentScheduler.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentScheduler.java
new file mode 100644
index 0000000..eb2c0ea
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentScheduler.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executor;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentDeclaration;
+import org.apache.felix.dm.ComponentExecutorFactory;
+import org.apache.felix.dm.context.ComponentContext;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentFactory;
+
+/**
+ * The Dependency Manager delegates all components addition/removal to this class.
+ * If a ComponentExecutorFactory is registered in the OSGi registry, this class will use it to get an 
+ * Executor used for components management and lifecycle callbacks.
+ * 
+ * @see {@link ComponentFactory}
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ComponentScheduler {
+    private final static ComponentScheduler m_instance = new ComponentScheduler();
+    private final static String PARALLEL = "org.apache.felix.dependencymanager.parallel";
+    private volatile ComponentExecutorFactory m_componentExecutorFactory;
+    private final Executor m_serial = new SerialExecutor(null);
+    private ConcurrentMap<Component, Component> m_pending = new ConcurrentHashMap<>();
+
+    public static ComponentScheduler instance() {
+        return m_instance;
+    }
+
+    protected void bind(final ComponentExecutorFactory componentExecutorFactory) {
+        m_componentExecutorFactory = componentExecutorFactory;
+        m_serial.execute(new Runnable() {
+            @Override
+            public void run() {
+                for (Component c : m_pending.keySet()) {
+                    createComponentExecutor(m_componentExecutorFactory, c);
+                    ((ComponentContext) c).start();
+                }
+                m_pending.clear();
+            }
+        });
+    }
+
+    protected void unbind(ComponentExecutorFactory threadPool) {
+        m_componentExecutorFactory = null;
+    }
+
+    public void add(final Component c) {
+        if (mayStartNow(c)) {
+            ((ComponentContext) c).start();
+        }
+        else {
+            // The component requires a threadpool: delay execution until one is available.
+            m_serial.execute(new Runnable() {
+                @Override
+                public void run() {
+                    ComponentExecutorFactory execFactory = m_componentExecutorFactory;
+                    if (execFactory == null) {
+                        m_pending.put(c, c);
+                    }
+                    else {
+                        createComponentExecutor(execFactory, c);
+                        ((ComponentContext) c).start();
+                    }
+                }
+            });
+        }
+    }
+
+    public void remove(final Component c) {
+        m_pending.remove(c);
+        ((ComponentContext) c).stop();
+    }
+
+    private boolean mayStartNow(Component c) {
+        ComponentExecutorFactory execFactory = m_componentExecutorFactory;
+        BundleContext ctx = c.getDependencyManager().getBundleContext();
+        String parallel = ctx.getProperty(PARALLEL);
+
+        if (execFactory == null) {
+            // No ComponentExecutorFactory available. If a "parallel" OSGi system property is specified, 
+            // we have to wait for a ComponentExecutorFactory servoce if the component class name is matching one of the 
+            // prefixes specified in the "parallel" system property.
+            if (parallel != null && requiresThreadPool(c, parallel)) {
+                return false; // wait for a threadpool
+            } else {
+                return true; // no threadpool required, start the component now, synchronously
+            }
+        }
+        else {
+            // A threadpool is there. If the "parallel" OSGi system property is not specified, we can start the component
+            // now and we'll use the threadpool for it.
+            // But if the "parallel" system property is specified, the component will use the threadpool only if it's
+            // classname is starting with one of the prefixes specified in the property.
+            if (parallel == null || requiresThreadPool(c, parallel)) {
+                ((ComponentContext) c).setThreadPool(execFactory.getExecutorFor(c));
+            }
+            return true; // start the component now, possibly using the threadpool (see above).
+        }
+    }
+
+    private boolean requiresThreadPool(Component c, String parallel) {
+        // The component declared from our DM Activator can not be parallel.
+        ComponentDeclaration decl = c.getComponentDeclaration();
+        if (ComponentScheduler.class.getName().equals(decl.getName())) {
+            return false;
+        }
+
+        for (String prefix : parallel.trim().split(",")) {
+            prefix = prefix.trim();
+            boolean not = prefix.startsWith("!");
+            if (not) {
+                prefix = prefix.substring(1).trim();
+            }
+            if ("*".equals(prefix) || c.getComponentDeclaration().getClassName().startsWith(prefix)) {
+                return !not;
+            }
+        }
+        return false;
+    }
+    
+    private void createComponentExecutor(ComponentExecutorFactory execFactory, Component c) {
+        Executor exec = execFactory.getExecutorFor(c);
+        if (exec != null) {
+            ((ComponentContext) c).setThreadPool(exec);
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationDependencyImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationDependencyImpl.java
new file mode 100644
index 0000000..db93df2
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationDependencyImpl.java
@@ -0,0 +1,311 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Dictionary;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.felix.dm.ConfigurationDependency;
+import org.apache.felix.dm.Logger;
+import org.apache.felix.dm.PropertyMetaData;
+import org.apache.felix.dm.context.AbstractDependency;
+import org.apache.felix.dm.context.DependencyContext;
+import org.apache.felix.dm.context.Event;
+import org.apache.felix.dm.context.EventType;
+import org.apache.felix.dm.impl.metatype.MetaTypeProviderImpl;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+
+/**
+ * Implementation for a configuration dependency.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ConfigurationDependencyImpl extends AbstractDependency<ConfigurationDependency> implements ConfigurationDependency, ManagedService {
+    private Dictionary<String, Object> m_settings;
+	private String m_pid;
+	private ServiceRegistration m_registration;
+    private MetaTypeProviderImpl m_metaType;
+	private final AtomicBoolean m_updateInvokedCache = new AtomicBoolean();
+	private final Logger m_logger;
+	private final BundleContext m_context;
+
+    public ConfigurationDependencyImpl() {
+        this(null, null);
+    }
+	
+    public ConfigurationDependencyImpl(BundleContext context, Logger logger) {
+        m_context = context;
+    	m_logger = logger;
+        setRequired(true);
+        setCallback("updated");
+    }
+    
+	public ConfigurationDependencyImpl(ConfigurationDependencyImpl prototype) {
+	    super(prototype);
+	    m_context = prototype.m_context;
+	    m_pid = prototype.m_pid;
+	    m_logger = prototype.m_logger;
+        m_metaType = prototype.m_metaType != null ? new MetaTypeProviderImpl(prototype.m_metaType, this, null) : null;
+	}
+	
+    @Override
+    public Class<?> getAutoConfigType() {
+        return null; // we don't support auto config mode.
+    }
+
+	@Override
+	public DependencyContext createCopy() {
+	    return new ConfigurationDependencyImpl(this);
+	}
+
+    public ConfigurationDependencyImpl setCallback(String callback) {
+        super.setCallbacks(callback, null);
+        return this;
+    }
+    
+    public ConfigurationDependencyImpl setCallback(Object instance, String callback) {
+        super.setCallbacks(instance, callback, null);
+        return this;
+    }
+
+    @Override
+    public boolean needsInstance() {
+        return m_callbackInstance == null;
+    }
+
+    @Override
+    public void start() {
+        BundleContext context = m_component.getBundleContext();
+        if (context != null) { // If null, we are in a test environment
+	        Properties props = new Properties();
+	        props.put(Constants.SERVICE_PID, m_pid);
+	        ManagedService ms = this;
+	        if (m_metaType != null) {
+	            ms = m_metaType;
+	        }
+	        m_registration = context.registerService(ManagedService.class.getName(), ms, props);
+        }
+        super.start();
+    }
+
+    @Override
+    public void stop() {
+        if (m_registration != null) {
+            try {
+                m_registration.unregister();
+            } catch (IllegalStateException e) {}
+        	m_registration = null;
+        }
+        super.stop();
+    }
+    
+	public ConfigurationDependency setPid(String pid) {
+		ensureNotActive();
+		m_pid = pid;
+		return this;
+	}
+		
+    @Override
+    public String getSimpleName() {
+        return m_pid;
+    }
+    
+    @Override
+    public String getFilter() {
+        return null;
+    }
+
+    public String getType() {
+        return "configuration";
+    }
+            
+    public ConfigurationDependency add(PropertyMetaData properties)
+    {
+        createMetaTypeImpl();
+        m_metaType.add(properties);
+        return this;
+    }
+
+    public ConfigurationDependency setDescription(String description)
+    {
+        createMetaTypeImpl();
+        m_metaType.setDescription(description);
+        return this;
+    }
+
+    public ConfigurationDependency setHeading(String heading)
+    {
+        createMetaTypeImpl();
+        m_metaType.setName(heading);
+        return this;
+    }
+    
+    public ConfigurationDependency setLocalization(String path)
+    {
+        createMetaTypeImpl();
+        m_metaType.setLocalization(path);
+        return this;
+    }
+    
+	@SuppressWarnings("unchecked")
+	@Override
+	public Dictionary<String, Object> getProperties() {
+		if (m_settings == null) {
+            throw new IllegalStateException("cannot find configuration");
+		}
+		return m_settings;
+	}
+    
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    @Override
+    public void updated(Dictionary settings) throws ConfigurationException {
+    	m_updateInvokedCache.set(false);
+        Dictionary<String, Object> oldSettings = null;
+        synchronized (this) {
+            oldSettings = m_settings;
+        }
+
+        if (oldSettings == null && settings == null) {
+            // CM has started but our configuration is not still present in the CM database: ignore
+            return;
+        }
+
+        // If this is initial settings, or a configuration update, we handle it synchronously.
+        // We'll conclude that the dependency is available only if invoking updated did not cause
+        // any ConfigurationException.
+        if (settings != null) {
+            Object[] instances = m_component.getInstances();
+            if (instances != null) {
+                try {
+                    invokeUpdated(settings);
+                } catch (ConfigurationException e) {
+                    logConfigurationException(e);
+                    throw e;
+                }
+            }
+        }
+        
+        // At this point, we have accepted the configuration.
+        synchronized (this) {
+            m_settings = settings;
+        }
+
+        if ((oldSettings == null) && (settings != null)) {
+            // Notify the component that our dependency is available.
+            m_component.handleEvent(this, EventType.ADDED, new ConfigurationEventImpl(m_pid, settings));
+        }
+        else if ((oldSettings != null) && (settings != null)) {
+            // Notify the component that our dependency has changed.
+            m_component.handleEvent(this, EventType.CHANGED, new ConfigurationEventImpl(m_pid, settings));
+        }
+        else if ((oldSettings != null) && (settings == null)) {
+            // Notify the component that our dependency has been removed.
+            // Notice that the component will be stopped, and then all required dependencies will be unbound
+            // (including our configuration dependency).
+            m_component.handleEvent(this, EventType.REMOVED, new ConfigurationEventImpl(m_pid, oldSettings));
+        }
+    }
+
+    @Override
+    public void invokeCallback(EventType type, Event ... event) {
+        switch (type) {
+        case ADDED:
+            try {
+                invokeUpdated(m_settings);
+            } catch (ConfigurationException e) {
+                logConfigurationException(e);
+            }
+            break;
+        case CHANGED:
+            // We already did that synchronously, from our updated method
+            break;
+        case REMOVED:
+            // The state machine is stopping us. We have to invoke updated(null).
+            try {
+                m_updateInvokedCache.set(false);
+                invokeUpdated(null);
+            } catch (ConfigurationException e) {
+                logConfigurationException(e);
+            } finally {
+                // Reset for the next time the state machine calls invokeAdd
+                m_updateInvokedCache.set(false);
+            }
+            break;
+        default:
+            break;
+        }
+    }
+    
+    private void invokeUpdated(Dictionary<?,?> settings) throws ConfigurationException {
+    	if (m_updateInvokedCache.compareAndSet(false, true)) {
+			Object[] instances = super.getInstances(); // either the callback instance or the component instances
+			if (instances != null) {
+				for (int i = 0; i < instances.length; i++) {
+					try {
+						InvocationUtil.invokeCallbackMethod(instances[i],
+								m_add, new Class[][] {
+										{ Dictionary.class }, {} },
+								new Object[][] { { settings }, {} });
+					}
+
+					catch (InvocationTargetException e) {
+						// The component has thrown an exception during it's
+						// callback invocation.
+						if (e.getTargetException() instanceof ConfigurationException) {
+							// the callback threw an OSGi
+							// ConfigurationException: just re-throw it.
+							throw (ConfigurationException) e
+									.getTargetException();
+						} else {
+							// wrap the callback exception into a
+							// ConfigurationException.
+							throw new ConfigurationException(null,
+									"Configuration update failed",
+									e.getTargetException());
+						}
+					} catch (NoSuchMethodException e) {
+						// if the method does not exist, ignore it
+					} catch (Throwable t) {
+						// wrap any other exception as a ConfigurationException.
+						throw new ConfigurationException(null,
+								"Configuration update failed", t);
+					}
+				}
+			}
+    	}
+    }
+    
+    private synchronized void createMetaTypeImpl() {
+        if (m_metaType == null) {
+            m_metaType = new MetaTypeProviderImpl(m_pid, m_context, m_logger, this, null);
+        }
+    }
+    
+    private void logConfigurationException(ConfigurationException e) {
+        if (m_logger != null) {
+            m_logger.log(Logger.LOG_ERROR, "Got exception while handling configuration update for pid " + m_pid, e);
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationEventImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationEventImpl.java
new file mode 100644
index 0000000..c5f64d5
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationEventImpl.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.util.Dictionary;
+
+import org.apache.felix.dm.context.Event;
+
+/**
+ * Implementation for a configuration event.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ConfigurationEventImpl extends Event {
+    private final String m_pid;
+    
+    public ConfigurationEventImpl(String pid, Dictionary<String, Object> conf) {
+        super(conf);
+        m_pid = pid;
+    }
+    
+    public String getPid() {
+        return m_pid;
+    }
+        
+    @Override
+    public int compareTo(Event other) {
+        return m_pid.compareTo(((ConfigurationEventImpl) other).m_pid);
+    }
+
+    @SuppressWarnings("unchecked")
+	@Override
+    public Dictionary<String, Object> getProperties() {
+        return getEvent();
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/DefaultNullObject.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/DefaultNullObject.java
new file mode 100644
index 0000000..eede17d
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/DefaultNullObject.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+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 <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public final 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/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/DispatchExecutor.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/DispatchExecutor.java
new file mode 100644
index 0000000..a780231
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/DispatchExecutor.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.felix.dm.Logger;
+import org.osgi.service.log.LogService;
+
+/**   
+ * A DispatchExecutor is a queue that can execute FIFO tasks in a shared threadpool configured for the dispatcher.
+ * Each task scheduled in a given DispatchExecutor will be executed serially in FIFO order; and multiple 
+ * DispatchExecutor instances may each run concurrently with respect to each other.
+ * <p>
+ * 
+ * This class also supports synchronous scheduling, like the @link {@link SerialExecutor} class; and in this case,
+ * only one caller thread will execute the tasks scheduled in the DispatchQueue (and the internal 
+ * threadpool won't be used).
+ * 
+ * <p> 
+ * 
+ * This class is <b>lock free</b> by design and ensures <b>"safe object publication"</b> between scheduling threads and
+ * actual executing thread: if one thread T1 schedules a task, but another thread T2 actually 
+ * executes it, then all the objects from the T1 thread will be "safely published" to the executing T2 thread.
+ * Safe publication is ensured  because we are using a ConcurrentLinkedQueue, and volatile attributes.
+ * (see [1], chapter 3.5.3 (Safe publication idioms). 
+ * 
+ * [1] Java Concurrency In Practice, Addison Wesley
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DispatchExecutor implements Executor, Runnable {
+	/**
+	 * The threadpool used for the execution of the tasks that are scheduled in this queue.
+	 */
+	private final Executor m_threadPool;
+
+	/** 
+	 * List of tasks scheduled in our queue.
+	 */
+	protected final ConcurrentLinkedQueue<Runnable> m_tasks = new ConcurrentLinkedQueue<>();
+
+    /**
+     * Marker used to remember the id of the thread currently executing this dispatch queue.
+     */
+    private volatile Thread m_executingThread;
+
+    /** 
+     * Flag telling if this dispatch queue is already scheduled for execution in the threadpool.
+     */
+    private final AtomicBoolean m_scheduled = new AtomicBoolean();
+
+    /** 
+	 * Logger used to log exceptions thrown by scheduled tasks. 
+	 */
+	private final Logger m_logger;
+	
+	/**
+	 * Creates a new DispatchQueue, which can be executed within a fixed thread pool. Multiple queue
+	 * can be executed concurrently, but all runnables scheduled in a given queue will be executed serially, 
+	 * in FIFO order. 
+	 * 
+	 * @param threadPool the executor (typically a threadpool) used to execute this DispatchExecutor.
+	 * @param logger the Logger used when errors are taking place
+	 */
+    public DispatchExecutor(Executor threadPool, Logger logger) {
+		m_logger = logger;
+		m_threadPool = threadPool;
+	}
+	
+    /**
+     * Enqueues a task for later execution. You must call {@link #execute()} in order
+     * to trigger the actual execution of all scheduled tasks (in FIFO order).
+     */
+    public void schedule(Runnable task) {
+        m_tasks.add(task);
+    }
+
+	/**
+	 * Submits a task in this queue, and schedule the execution of this DispatchQueue in the threadpool. 	 
+	 * The task is immediately executed (inline execution) if the queue is currently being executed by 
+	 * the current thread.
+	 */
+	public void execute(Runnable task) {
+	    execute(task, true);
+	}
+	
+	/**
+     * Schedules a task in this queue.
+     * If the queue is currently being executed by the current thread, then the task is executed immediately.
+     * @tasks the task to schedule
+     * @threadpool true if the queue should be executed in the threadpool, false if the queue must be executed by
+     * only one caller thread.
+     */
+    public void execute(Runnable task, boolean threadpool) {
+        Thread currThread = Thread.currentThread();
+        if (m_executingThread == currThread) {
+            runTask(task);
+        } else {
+            schedule(task);
+            execute(threadpool);
+        }
+    }
+	
+    /**
+     * Schedules the execution of this DispatchQueue in the threadpool.
+     */
+	public void execute() {
+	    execute(true);
+	}
+	
+    /**
+     * Schedules the execution of this DispatchQueue in the threadpool, or from a single caller thread.
+     * 
+     * @param threadpool true means the DispatchQueue is executed in the threadpool, false means the queue is executed from the
+     * caller thread.
+     */
+    public void execute(boolean threadpool) {
+        if (m_scheduled.compareAndSet(false, true)) { // schedules our run method in the tpool.
+            try {
+                if (threadpool) {
+                    m_threadPool.execute(this);
+                } else {
+                    run(); // run all queue tasks from the caller thread
+                }
+            } catch (RejectedExecutionException e) {
+                // The threadpool seems stopped (maybe the framework is being stopped). Anyway, just execute our tasks
+                // from the current thread.
+                run();
+            }
+        }
+    }
+
+	/**
+	 * Run all tasks scheduled in this queue, in FIFO order. This method may be executed either in the threadpool, or from
+	 * the caller thread.
+	 */
+	@Override
+	public void run() {
+        try {
+            // We do a memory barrier in order to ensure consistent per-thread
+            // memory visibility
+            m_executingThread = Thread.currentThread();
+            Runnable task;
+            while ((task = m_tasks.poll()) != null) {
+                runTask(task);
+            }
+        } finally {
+            m_scheduled.set(false);
+            m_executingThread = null;
+            if (m_tasks.peek() != null) {
+                execute();
+            }
+        }
+	}
+
+	/**
+	 * Runs a given task
+	 * @param task the task to execute
+	 */
+    private void runTask(Runnable task) {
+		try {
+		    task.run();
+		} catch (Throwable t) {
+			m_logger.log(LogService.LOG_ERROR, "Error processing tasks", t);
+		}
+	}
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FactoryConfigurationAdapterImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FactoryConfigurationAdapterImpl.java
new file mode 100644
index 0000000..f96214a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FactoryConfigurationAdapterImpl.java
@@ -0,0 +1,302 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.Logger;
+import org.apache.felix.dm.PropertyMetaData;
+import org.apache.felix.dm.context.DependencyContext;
+import org.apache.felix.dm.impl.metatype.MetaTypeProviderImpl;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ManagedServiceFactory;
+import org.osgi.service.metatype.MetaTypeProvider;
+import org.osgi.service.metatype.ObjectClassDefinition;
+
+/**
+ * Factory configuration adapter service implementation. This class extends the FilterService in order to catch
+ * some Service methods for configuring actual adapter service implementation.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FactoryConfigurationAdapterImpl extends FilterComponent {
+    // Our Managed Service Factory PID
+    protected final String m_factoryPid;
+    
+    // Our logger
+    protected final Logger m_logger;
+    
+    public FactoryConfigurationAdapterImpl(DependencyManager dm, String factoryPid, String update, boolean propagate) {
+        super(dm.createComponent()); // This service will be filtered by our super class, allowing us to take control.
+        m_factoryPid = factoryPid;
+        m_logger = ((ComponentImpl) m_component).getLogger();
+
+        Hashtable<String, Object> props = new Hashtable<>();
+        props.put(Constants.SERVICE_PID, factoryPid);
+        m_component
+            .setInterface(ManagedServiceFactory.class.getName(), props)
+            .setImplementation(new AdapterImpl(update, propagate))
+            .setCallbacks("init", null, "stop", null);
+    }
+    
+    public FactoryConfigurationAdapterImpl(DependencyManager dm, String factoryPid, String update, boolean propagate,
+        BundleContext bctx, Logger logger, String heading, String description, String localization, PropertyMetaData[] properyMetaData) {
+        super(dm.createComponent()); // This service will be filtered by our super class, allowing us to take control.
+        m_factoryPid = factoryPid;
+        m_logger = logger;
+        Hashtable<String, Object> props = new Hashtable<>();
+        props.put(Constants.SERVICE_PID, factoryPid);
+        m_component
+            .setInterface(ManagedServiceFactory.class.getName(), props)
+            .setImplementation(new MetaTypeAdapterImpl(update, propagate,
+                bctx, logger, heading, description,
+                localization, properyMetaData))
+            .setCallbacks("init", null, "stop", null);
+    }
+    
+    public String getName() {
+        return "Adapter for factory pid " + m_factoryPid;
+    }
+    
+    /**
+     * Creates, updates, or removes a service, when a ConfigAdmin factory configuration is created/updated or deleted.
+     */
+    public class AdapterImpl extends AbstractDecorator implements ManagedServiceFactory {        
+        // The adapter "update" method used to provide the configuration
+        protected final String m_update;
+
+        // Tells if the CM config must be propagated along with the adapter service properties
+        protected final boolean m_propagate;
+
+        /**
+         * Creates a new CM factory configuration adapter.
+         * 
+         * @param factoryPid
+         * @param updateMethod
+         * @param adapterInterface
+         * @param adapterImplementation
+         * @param adapterProperties
+         * @param propagate
+         */
+        public AdapterImpl(String updateMethod, boolean propagate) {
+            m_update = updateMethod;
+            m_propagate = propagate;
+        }
+
+        /**
+         * Returns the managed service factory name.
+         */
+        public String getName() {
+            return m_factoryPid;
+        }
+      
+        /**
+         * Method called from our superclass, when we need to create a service.
+         */
+        @SuppressWarnings("unchecked")
+        public Component createService(Object[] properties) {
+            Dictionary<String, ?> settings = (Dictionary<String, ?>) properties[0];     
+            Component newService = m_manager.createComponent();        
+            Object impl = null;
+            
+            try {
+                if (m_serviceImpl != null) {
+                    impl = (m_serviceImpl instanceof Class) ? ((Class<?>) m_serviceImpl).newInstance() : m_serviceImpl;
+                }
+                else {
+                    impl = instantiateFromFactory(m_factory, m_factoryCreateMethod);
+                }
+                InvocationUtil.invokeCallbackMethod(impl, m_update, 
+                    new Class[][] {{ Dictionary.class }, {}}, 
+                    new Object[][] {{ settings }, {}});
+            }
+            
+            catch (Throwable t) {
+               handleException(t);
+            }
+
+            // Merge adapter service properties, with CM settings 
+            Dictionary<String, Object> serviceProperties = getServiceProperties(settings);
+            newService.setInterface(m_serviceInterfaces, serviceProperties);
+            newService.setImplementation(impl);
+            newService.setComposition(m_compositionInstance, m_compositionMethod); // if not set, no effect
+            newService.setCallbacks(m_callbackObject, m_init, m_start, m_stop, m_destroy); // if not set, no effect
+            configureAutoConfigState(newService, m_component);
+            
+            for (DependencyContext dc : m_component.getDependencies()) {
+                newService.add((Dependency) dc.createCopy());
+            }
+            
+            for (int i = 0; i < m_stateListeners.size(); i ++) {
+                newService.add(m_stateListeners.get(i));
+            }
+            
+            return newService;
+        }
+
+        /**
+         * Method called from our superclass, when we need to update a Service, because 
+         * the configuration has changed.
+         */
+        @SuppressWarnings("unchecked")
+        public void updateService(Object[] properties) {
+            Dictionary<String, ?> cmSettings = (Dictionary<String, ?>) properties[0];
+            Component service = (Component) properties[1];
+            Object impl = service.getInstances()[0];
+
+            try {
+                InvocationUtil.invokeCallbackMethod(impl, m_update, 
+                    new Class[][] {{ Dictionary.class }, {}}, 
+                    new Object[][] {{ cmSettings }, {}});
+                if (m_serviceInterfaces != null && m_propagate == true) {
+                    Dictionary<String, ?> serviceProperties = getServiceProperties(cmSettings);
+                    service.setServiceProperties(serviceProperties);
+                }
+            }
+            
+            catch (Throwable t) {
+                handleException(t);
+            }
+        }   
+
+        /**
+         * Merge CM factory configuration setting with the adapter service properties. The private CM factory configuration 
+         * settings are ignored. A CM factory configuration property is private if its name starts with a dot (".").
+         * 
+         * @param adapterProperties
+         * @param settings
+         * @return
+         */
+        private Dictionary<String, Object> getServiceProperties(Dictionary<String, ?> settings) {
+            Dictionary<String, Object> props = new Hashtable<>();
+            
+            // Add adapter Service Properties
+            if (m_serviceProperties != null) {
+                Enumeration<String> keys = m_serviceProperties.keys();
+                while (keys.hasMoreElements()) {
+                    String key = keys.nextElement();
+                    Object val = m_serviceProperties.get(key);
+                    props.put(key, val);
+                }
+            }
+
+            if (m_propagate) {
+                // Add CM setting into adapter service properties.
+                // (CM setting will override existing adapter service properties).
+                Enumeration<String> keys = settings.keys();
+                while (keys.hasMoreElements()) {
+                    String key = keys.nextElement();
+                    if (! key.toString().startsWith(".")) {
+                        // public properties are propagated
+                        Object val = settings.get(key);
+                        props.put(key, val);
+                    }
+                }
+            }
+
+            
+            return props;
+        }
+    
+        private Object instantiateFromFactory(Object mFactory, String mFactoryCreateMethod) {
+            Object factory = null;
+            if (m_factory instanceof Class) {
+                try {
+                    factory = createInstance((Class<?>) m_factory);
+                }
+                catch (Throwable t) {
+                    handleException(t);
+                }
+            }
+            else {
+                factory = m_factory;
+            }
+
+            try {
+                return InvocationUtil.invokeMethod(factory, factory.getClass(), m_factoryCreateMethod, new Class[][] { {} }, new Object[][] { {} }, false);
+            }
+            catch (Throwable t) {
+                handleException(t);
+                return null;
+            }
+        }
+
+        private Object createInstance(Class<?> clazz) throws SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException {
+            Constructor<?> constructor = clazz.getConstructor(new Class[] {});
+            constructor.setAccessible(true);
+            return clazz.newInstance();
+        }
+    
+        private void handleException(Throwable t) {
+            m_logger.log(Logger.LOG_ERROR, "Got exception while handling configuration update for factory pid " + m_factoryPid, t);
+            if (t instanceof InvocationTargetException) {
+                // Our super class will check if the target exception is itself a ConfigurationException.
+                // In this case, it will simply re-thrown.
+                throw new RuntimeException(((InvocationTargetException) t).getTargetException());
+            }
+            else if (t instanceof RuntimeException) {
+                throw (RuntimeException) t;
+            }
+            else {
+                throw new RuntimeException(t);
+            }
+        }
+    }
+
+    
+    /**
+     * Extends AdapterImpl for MetaType support.
+     */
+    class MetaTypeAdapterImpl extends AdapterImpl implements MetaTypeProvider {
+        // Our MetaType Provider for describing our properties metadata
+        private final MetaTypeProviderImpl m_metaType;
+        
+        public MetaTypeAdapterImpl(String updateMethod, boolean propagate,
+                                   BundleContext bctx, Logger logger, String heading, 
+                                   String description, String localization,
+                                   PropertyMetaData[] properyMetaData) {
+            super(updateMethod, propagate);
+            m_metaType = new MetaTypeProviderImpl(m_factoryPid, bctx, logger, null, this);
+            m_metaType.setName(heading);
+            m_metaType.setDescription(description);
+            if (localization != null) {
+                m_metaType.setLocalization(localization);
+            }
+            for (int i = 0; i < properyMetaData.length; i++) {
+                m_metaType.add(properyMetaData[i]);
+            }
+        }
+        
+        public String[] getLocales() {
+            return m_metaType.getLocales();
+        }
+
+        public ObjectClassDefinition getObjectClassDefinition(String id, String locale) {
+            return m_metaType.getObjectClassDefinition(id, locale);
+        }
+    }    
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FieldUtil.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FieldUtil.java
new file mode 100644
index 0000000..dbaa123
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FieldUtil.java
@@ -0,0 +1,351 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import org.apache.felix.dm.Logger;
+import org.apache.felix.dm.context.DependencyContext;
+import org.apache.felix.dm.context.Event;
+
+/**
+ * Reflection Helper methods, used to inject autoconfig fields in component instances.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FieldUtil {
+    /**
+     * Callbacks for fields to be injected
+     */
+    private interface FieldFunction {
+        // Inject an updated service in the given field for the the given target.
+        void injectField(Field f, Object target);
+
+        // Inject an Iterable Field in the given target 
+        void injectIterableField(Field f, Object target);
+
+        // Inject a Map field in the given target (key = dependency service, value = Dictionary with dependency service properties).
+        void injectMapField(Field f, Object target);
+    }
+
+    /**
+     * Injects some component instances (on a given field, if provided), with an object of a given class.
+     * @param targets the component instances to fill in
+     * @param fieldName the fieldname, or null. If null, the field must exaclty match the injected service classname.
+     * @param clazz the injected service class
+     * @param service the injected service
+     * @param logger the component logger.
+     */
+    public static boolean injectField(Object[] targets, String fieldName, Class<?> clazz, final Object service,
+        final Logger logger)
+    {
+        if (service == null) {
+            return true; // TODO why service can be null ?
+        }
+        return mapField(true, clazz, targets, fieldName, logger, new FieldFunction() {
+            public void injectField(Field f, Object target) {
+                try {
+                    f.setAccessible(true);
+                    f.set(target, service);
+                } catch (Throwable e) {
+                    logger.log(Logger.LOG_ERROR, "Could not set field " + f + " in class "
+                        + target.getClass().getName(), e);
+                }
+            }
+
+            public void injectIterableField(Field f, Object target) { // never called
+            }
+
+            public void injectMapField(Field f, Object target) { // never called
+            }
+        });
+    }
+
+    /**
+     * Injects a dependency service in some component instances.
+     * Here, we'll inject the dependency services in the component if the field is of the same type of the injected services,
+     * or if the field is a Collection of the injected service, or if the field is a Map<Injected Service class, Dictionary).
+     * @param targets the component instances to fill in
+     * @param fieldName the fieldname, or null. If null, the field must exaclty match the injected service classname.
+     * @param clazz the injected service class
+     * @param service the injected service
+     * @param logger the component logger.
+     */
+    public static boolean injectDependencyField(Object[] targets, String fieldName, Class<?> clazz,
+        final DependencyContext dc, final Logger logger)
+    {
+        final Event event = dc.getService();
+        if (event == null) {
+            return true; // TODO check why event can be null
+        }
+        return mapField(false, clazz, targets, fieldName, logger, new FieldFunction() {
+            public void injectField(Field f, Object target) {
+                try {
+                    f.setAccessible(true);
+                    f.set(target, event.getEvent());
+                } catch (Throwable e) {
+                    logger.log(Logger.LOG_ERROR, "Could not set field " + f + " in class "
+                        + target.getClass().getName(), e);
+                }
+            }
+
+            @SuppressWarnings("unchecked")
+            public void injectIterableField(Field f, Object target) {
+                f.setAccessible(true);
+
+                try {
+                    Iterable<Object> iter = (Iterable<Object>) f.get(target);
+                    if (iter == null) {
+                        iter = new ConcurrentLinkedQueue<Object>();
+                        f.set(target, iter);
+                    }
+                    dc.copyToCollection((Collection<Object>) iter);
+                } catch (Throwable e) {
+                    logger.log(Logger.LOG_ERROR, "Could not set field " + f + " in class "
+                        + target.getClass().getName(), e);
+                }
+            }
+
+            @SuppressWarnings({ "unchecked", "rawtypes" })
+            @Override
+            public void injectMapField(Field f, Object target) {
+                f.setAccessible(true);
+                try {
+                    Map<Object, Dictionary<?, ?>> map = (Map) f.get(target);
+                    if (map == null) {
+                        map = new ConcurrentHashMap<>();
+                        f.set(target, map);
+                    }
+                    dc.copyToMap(map);
+                } catch (Throwable e) {
+                    logger.log(Logger.LOG_ERROR, "Could not set field " + f + " in class "
+                        + target.getClass().getName(), e);
+                }
+            }
+        });
+    }
+
+    /**
+     * Adds, or removes, or update some component instances with an updated dependency service
+     * @param targets the component instances to fill in with the updated service
+     * @param fieldName the component instance fieldname 
+     * @param update true if it's a dependency service update, false if the dependency service is added or removed
+     * @param add true if the dependency service has been added, false the dependency service has been removed. 
+     * This flag is ignored if the "update" parameter is "true". 
+     * @param clazz the clazz of the dependency service
+     * @param event the event holding the dependency service
+     * @param dc the dependency service context
+     * @param logger the logger used when problems occure.
+     */
+    public static void updateDependencyField(Object[] targets, String fieldName, final boolean update,
+        final boolean add, Class<?> clazz, final Event event, final DependencyContext dc, final Logger logger)
+    {
+        mapField(false, clazz, targets, fieldName, logger, new FieldFunction() {
+            public void injectField(Field f, Object target) {
+                try {
+                    f.setAccessible(true);
+                    f.set(target, dc.getService().getEvent());
+                } catch (Throwable e) {
+                    logger.log(Logger.LOG_ERROR, "Could not set field " + f + " in class "
+                        + target.getClass().getName(), e);
+                }
+            }
+
+            @SuppressWarnings("unchecked")
+            public void injectIterableField(Field f, Object target) {
+                if (update) {
+                    return;
+                }
+
+                f.setAccessible(true);
+
+                try {
+                    Collection<Object> coll = (Collection<Object>) f.get(target);
+                    if (add) {
+                        coll.add(event.getEvent());
+                    } else {
+                        coll.remove(event.getEvent());
+                    }
+                } catch (Throwable e) {
+                    logger.log(Logger.LOG_ERROR, "Could not set field " + f + " in class "
+                        + target.getClass().getName(), e);
+                }
+            }
+
+            @SuppressWarnings({ "rawtypes", "unchecked" })
+            @Override
+            public void injectMapField(Field f, Object target) {
+                f.setAccessible(true);
+
+                try {
+                    Map<Object, Dictionary<?, ?>> map = (Map) f.get(target);
+                    if (add) {
+                        map.put(event.getEvent(), event.getProperties());
+                    } else {
+                        map.remove(event.getEvent());
+                    }
+                } catch (Throwable e) {
+                    logger.log(Logger.LOG_ERROR, "Could not set field " + f + " in class "
+                        + target.getClass().getName(), e);
+                }
+            }
+        });
+    }
+
+    /**
+     * Scans component instances for fields having either the same dependency service type, or being a 
+     * Collection of the dependency service, or being a Map<Dependency Service class, Dictionary> (the Dictionary
+     * corresponds to the dependency service properties).
+     * @param strict true if we are only looking for fields having exactly the same type as the dependency service. 
+     * In other words, if strict = true, we don't lookup for fields of "Collection" or "Map" types.
+     * @param clazz the dependency service class
+     * @param targets the component instances
+     * @param fieldName the component instances field name or null
+     * @param logger a logger used when exceptions are occuring
+     * @param func the callback used to notify when we find either a field with the same dependency service type, or
+     * with a "Collection" type, or with a "Map" type.
+     */
+    private static boolean mapField(boolean strict, Class<?> clazz, Object[] targets, String fieldName, Logger logger,
+        FieldFunction func)
+    {
+        boolean injected = false;
+        if (targets != null && clazz != null) {
+            for (int i = 0; i < targets.length; i++) {
+                Object target = targets[i];
+                Class<?> targetClass = target.getClass();
+                if (Proxy.isProxyClass(targetClass)) {
+                    target = Proxy.getInvocationHandler(target);
+                    targetClass = target.getClass();
+                }
+                while (targetClass != null) {
+                    Field[] fields = targetClass.getDeclaredFields();
+                    for (int j = 0; j < fields.length; j++) {
+                        Field field = fields[j];
+                        Class<?> fieldType = field.getType();
+
+                        if (fieldName == null) {
+                            // Field type class must match injected service type
+                            if (fieldType.equals(clazz)) {
+                                injected = true;
+                                func.injectField(field, target);
+                            } else if (!strict && mayInjectToIterable(clazz, field, true)) {
+                                injected = true;
+                                func.injectIterableField(field, target);
+                            } else if (!strict && mayInjectToMap(clazz, field, true)) {
+                                injected = true;
+                                func.injectMapField(field, target);
+                            }
+                        } else if (field.getName().equals(fieldName)) {
+                            // Field type may be a superclass of the service type
+                            if (fieldType.isAssignableFrom(clazz)) {
+                                injected = true;
+                                func.injectField(field, target);
+                            } else if (!strict && mayInjectToIterable(clazz, field, false)) {
+                                injected = true;
+                                func.injectIterableField(field, target);
+                            } else if (!strict && mayInjectToMap(clazz, field, false)) {
+                                injected = true;
+                                func.injectMapField(field, target);
+                            } else {
+                                logger.log(
+                                    Logger.LOG_ERROR,
+                                    "Could not set field " + field + " in class " + target.getClass().getName()
+                                        + ": the type of the field type should be either assignable from "
+                                        + clazz.getName() + " or Collection, or Map");
+                            }
+                        }
+                    }
+                    targetClass = targetClass.getSuperclass();
+                }
+            }
+        }
+        return injected;
+    }
+
+    private static boolean mayInjectToIterable(Class<?> clazz, Field field, boolean strictClassEquality) {
+        Class<?> fieldType = field.getType();
+        if (Iterable.class.isAssignableFrom(fieldType)) {
+            ParameterizedType parameterType = (ParameterizedType) field.getGenericType();
+            if (parameterType == null) {
+                return false;
+            }
+            Type[] types = parameterType.getActualTypeArguments();
+            if (types == null || types.length == 0) {
+            	return false;
+            }
+            if (types[0] instanceof Class<?>) {
+            	Class<?> parameterizedTypeClass = (Class<?>) types[0];
+            	return strictClassEquality ? parameterizedTypeClass.equals(clazz)
+            			: parameterizedTypeClass.isAssignableFrom(clazz);
+            }	
+        }
+        return false;
+    }
+
+    private static boolean mayInjectToMap(Class<?> clazz, Field field, boolean strictClassEquality) {
+        Class<?> fieldType = field.getType();
+        if (Map.class.isAssignableFrom(fieldType)) {
+            // The field must be a parameterized map (generics).
+            if (! (field.getGenericType() instanceof ParameterizedType)) {
+                return false;
+            }
+            ParameterizedType parameterType = (ParameterizedType) field.getGenericType();
+            if (parameterType == null) {
+                return false;
+            }
+            
+            Type[] types = parameterType.getActualTypeArguments();
+            if (types == null || types.length < 2) {
+            	return false;
+            }
+                   
+            // The map field generic key parameter must be "Class".
+            if (! (types[0] instanceof Class<?>)) {
+                return false;
+            }
+            
+            // The map generic value parameter must be Dictionary, or Dictionary<String, ...>
+            if (types[1] instanceof Class<?>) {
+                // The map field is in the form "Map m_field<Class, Dictionary>"
+                Class<?> mapValueGenericType = (Class<?>) types[1];
+                if (! mapValueGenericType.equals(Dictionary.class)) {
+                    return false;
+                }
+            } else if (types[1] instanceof ParameterizedType) {
+                // The map field is in the form "Map m_field<Class, Dictionary<String, ...>"
+                ParameterizedType mapValueGenericType = (ParameterizedType) types[1];
+                if (! mapValueGenericType.getRawType().equals(Dictionary.class)) {
+                    return false;
+                }
+            }
+            
+            Class<?> K = (Class<?>) types[0];
+            return strictClassEquality ? K.equals(clazz) : K.isAssignableFrom(clazz);
+        }
+        return false;
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FilterComponent.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FilterComponent.java
new file mode 100644
index 0000000..33bf464
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FilterComponent.java
@@ -0,0 +1,354 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentDeclaration;
+import org.apache.felix.dm.ComponentDependencyDeclaration;
+import org.apache.felix.dm.ComponentStateListener;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.Logger;
+import org.apache.felix.dm.context.ComponentContext;
+import org.apache.felix.dm.context.DependencyContext;
+import org.apache.felix.dm.context.Event;
+import org.apache.felix.dm.context.EventType;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * This class allows to filter a Component interface. All Aspect/Adapters extend this class
+ * in order to add functionality to the default Component implementation.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FilterComponent implements Component, ComponentContext, ComponentDeclaration {
+    protected volatile ComponentImpl m_component;
+    protected volatile List<ComponentStateListener> m_stateListeners = new CopyOnWriteArrayList<>();
+    protected volatile String m_init = "init";
+    protected volatile String m_start = "start";
+    protected volatile String m_stop = "stop";
+    protected volatile String m_destroy = "destroy";
+    protected volatile Object m_callbackObject;
+    protected volatile Object m_compositionInstance;
+    protected volatile String m_compositionMethod;
+    protected volatile String[] m_serviceInterfaces;
+    protected volatile Object m_serviceImpl;
+    protected volatile Object m_factory;
+    protected volatile String m_factoryCreateMethod;
+    protected volatile Dictionary<String, Object> m_serviceProperties;
+
+    public FilterComponent(Component service) {
+        m_component = (ComponentImpl) service;
+    }
+    
+    @Override
+    public String toString() {
+        return m_component.toString();
+    }
+
+    public Component add(Dependency ... dependencies) {
+        m_component.add(dependencies);
+        // Add the dependencies to all already instantiated services.
+        // If one dependency from the list is required, we have nothing to do, since our internal
+        // service will be stopped/restarted.
+        for (Dependency dependency : dependencies) {
+            if (((DependencyContext) dependency).isRequired()) {
+                return this;
+            }
+        }
+        // Ok, the list contains no required dependencies: add optionals dependencies in already instantiated services.
+        Object[] instances = m_component.getInstances();
+        if (instances.length > 0) {
+            AbstractDecorator ad = (AbstractDecorator) instances[0];
+            if (ad != null) {
+                ad.addDependency(dependencies);
+            }
+        }
+        return this;
+    }
+
+    public Component add(ComponentStateListener listener) {
+        m_stateListeners.add(listener);
+        // Add the listener to all already instantiated services.
+        Object[] instances = m_component.getInstances();
+        if (instances.length > 0) {
+            AbstractDecorator ad = (AbstractDecorator) instances[0];
+            if (ad != null) {
+                ad.addStateListener(listener);
+            }
+        }
+        return this;
+    }
+
+    public List<DependencyContext> getDependencies() {
+        return m_component.getDependencies();
+    }
+
+    public String getClassName() {
+        return m_component.getClassName();
+    }
+    
+    @SuppressWarnings("unchecked")
+	public Dictionary<String, Object> getServiceProperties() {
+        return m_serviceProperties;
+    }
+
+    public ServiceRegistration getServiceRegistration() {
+        return m_component.getServiceRegistration();
+    }
+
+    public Component remove(Dependency dependency) {
+        m_component.remove(dependency);
+        // Remove the dependency (if optional) from all already instantiated services.
+        // If the dependency is required, our internal service will be stopped, so in this case
+        // we have nothing to do.
+        if (!((DependencyContext) dependency).isRequired())
+        {
+            Object[] instances = m_component.getInstances();
+            if (instances.length > 0) {
+                AbstractDecorator ad = (AbstractDecorator) instances[0];
+                if (ad != null) {
+                    ad.removeDependency(dependency);
+                }
+            }
+        }
+        return this;
+    }
+
+    public Component remove(ComponentStateListener listener) {
+        m_stateListeners.remove(listener);
+        // Remove the listener from all already instantiated services.
+        Object[] instances = m_component.getInstances();
+        if (instances.length > 0) {
+            AbstractDecorator ad = (AbstractDecorator) instances[0];
+            if (ad != null) {
+                ad.removeStateListener(listener);
+            }
+        }
+        return this;
+    }
+
+    public Component setCallbacks(Object instance, String init, String start, String stop, String destroy) {
+        m_component.ensureNotActive();
+        m_callbackObject = instance;
+        m_init = init;
+        m_start = start;
+        m_stop = stop;
+        m_destroy = destroy;
+        return this;
+    }
+
+    public Component setCallbacks(String init, String start, String stop, String destroy) {
+        setCallbacks(null, init, start, stop, destroy);
+        return this;
+    }
+
+    public Component setComposition(Object instance, String getMethod) {
+        m_component.ensureNotActive();
+        m_compositionInstance = instance;
+        m_compositionMethod = getMethod;
+        return this;
+    }
+
+    public Component setComposition(String getMethod) {
+        m_component.ensureNotActive();
+        m_compositionMethod = getMethod;
+        return this;
+    }
+
+    public Component setFactory(Object factory, String createMethod) {
+        m_component.ensureNotActive();
+        m_factory = factory;
+        m_factoryCreateMethod = createMethod;
+        return this;
+    }
+
+    public Component setFactory(String createMethod) {
+        return setFactory(null, createMethod);
+    }
+
+    public Component setImplementation(Object implementation) {
+        m_component.ensureNotActive();
+        m_serviceImpl = implementation;
+        return this;
+    }
+
+    public Component setInterface(String serviceName, Dictionary<?, ?> properties) {
+        return setInterface(new String[] { serviceName }, properties);
+    }
+
+    @SuppressWarnings("unchecked")
+    public Component setInterface(String[] serviceInterfaces, Dictionary<?, ?> properties) {
+        m_component.ensureNotActive();
+        if (serviceInterfaces != null) {
+            m_serviceInterfaces = new String[serviceInterfaces.length];
+            System.arraycopy(serviceInterfaces, 0, m_serviceInterfaces, 0, serviceInterfaces.length);
+            m_serviceProperties = (Dictionary<String, Object>) properties;
+        }
+        return this;
+    }
+
+    @SuppressWarnings("unchecked")
+    public Component setServiceProperties(Dictionary<?, ?> serviceProperties) {
+        m_serviceProperties = (Dictionary<String, Object>) serviceProperties;
+        // Set the properties to all already instantiated services.
+        if (serviceProperties != null) {
+            Object[] instances = m_component.getInstances();
+            if (instances.length > 0) {
+                AbstractDecorator ad = (AbstractDecorator) instances[0];
+                if (ad != null) {
+                    ad.setServiceProperties(serviceProperties);
+                }
+            }
+        }
+        return this;
+    }
+
+    public void start() {
+        m_component.start();
+    }
+
+    public void stop() {
+        m_component.stop();
+    }
+    
+    public void invokeCallbackMethod(Object[] instances, String methodName, Class<?>[][] signatures, Object[][] parameters) {
+        m_component.invokeCallbackMethod(instances, methodName, signatures, parameters);
+    }
+        
+    public DependencyManager getDependencyManager() {
+        return m_component.getDependencyManager();
+    }
+
+    public Component setAutoConfig(Class<?> clazz, boolean autoConfig) {
+        m_component.setAutoConfig(clazz, autoConfig);
+        return this;
+    }
+
+    public Component setAutoConfig(Class<?> clazz, String instanceName) {
+        m_component.setAutoConfig(clazz, instanceName);
+        return this;
+    }
+    
+    public boolean getAutoConfig(Class<?> clazz) {
+        return m_component.getAutoConfig(clazz);
+    }
+    
+    public String getAutoConfigInstance(Class<?> clazz) {
+        return m_component.getAutoConfigInstance(clazz);
+    }
+
+    public ComponentDependencyDeclaration[] getComponentDependencies() {
+        return m_component.getComponentDependencies();
+    }
+
+    public String getName() {
+        return m_component.getName();
+    }
+
+    public int getState() {
+        return m_component.getState();
+    }
+    
+    public long getId() {
+        return m_component.getId();
+    }
+
+    public String[] getServices() {
+        return m_component.getServices();
+    }
+    
+    public BundleContext getBundleContext() {
+        return m_component.getBundleContext();
+    }
+        
+    @Override
+    public boolean isActive() {
+        return m_component.isActive();
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return m_component.isAvailable();
+    }
+
+    @Override
+    public void handleEvent(DependencyContext dc, EventType type, Event ... e) {
+        m_component.handleEvent(dc, type, e);
+    }
+    
+    @Override
+    public <T> T getInstance() {
+        return m_component.getInstance();
+    }
+
+    @Override
+    public Object[] getInstances() {
+        return m_component.getInstances();
+    }
+    
+    @Override
+    public Event getDependencyEvent(DependencyContext dc) {
+        return m_component.getDependencyEvent(dc);
+    }
+    
+    @Override
+    public Set<Event> getDependencyEvents(DependencyContext dc) {
+        return m_component.getDependencyEvents(dc);
+    }
+    
+    public ComponentDeclaration getComponentDeclaration() {
+        return this;
+    }
+
+	@Override
+	public Component setDebug(String label) {
+		m_component.setDebug(label);
+		return this;
+	}
+	
+    @Override
+    public void setThreadPool(Executor threadPool) {
+        m_component.setThreadPool(threadPool);
+    }
+
+    @Override
+    public Map<String, Long> getCallbacksTime() {
+        return m_component.getCallbacksTime();
+    }
+
+    @Override
+    public Bundle getBundle() {
+        return m_component.getBundle();
+    }
+
+    @Override
+    public Logger getLogger() {
+        return m_component.getLogger();
+    }
+}
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/InvocationUtil.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/InvocationUtil.java
new file mode 100644
index 0000000..880eba2
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/InvocationUtil.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Utility methods for invoking callbacks. Lookups of callbacks are accellerated by using a LRU cache.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class InvocationUtil {
+    private static final Map<Key, Method> m_methodCache;
+    static {
+        int size = 2048;
+        // TODO enable this again
+//        try {
+//            String value = System.getProperty(DependencyManager.METHOD_CACHE_SIZE);
+//            if (value != null) {
+//                size = Integer.parseInt(value);
+//            }
+//        }
+//        catch (Exception e) {}
+        m_methodCache = new LRUMap(Math.max(size, 64));
+    }
+    
+    /**
+     * Invokes a callback method on an instance. The code will search for a callback method with
+     * the supplied name and any of the supplied signatures in order, invoking the first one it finds.
+     * 
+     * @param instance the instance to invoke the method on
+     * @param methodName the name of the method
+     * @param signatures the ordered list of signatures to look for
+     * @param parameters the parameter values to use for each potential signature
+     * @return whatever the method returns
+     * @throws NoSuchMethodException when no method could be found
+     * @throws IllegalArgumentException when illegal values for this methods arguments are supplied 
+     * @throws IllegalAccessException when the method cannot be accessed
+     * @throws InvocationTargetException when the method that was invoked throws an exception
+     */
+    public static Object invokeCallbackMethod(Object instance, String methodName, Class<?>[][] signatures, Object[][] parameters) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+        Class<?> currentClazz = instance.getClass();
+        while (currentClazz != null && currentClazz != Object.class) {
+            try {
+                return invokeMethod(instance, currentClazz, methodName, signatures, parameters, false);
+            }
+            catch (NoSuchMethodException nsme) {
+                // ignore
+            }
+            currentClazz = currentClazz.getSuperclass();
+        }
+        throw new NoSuchMethodException(methodName);
+    }
+
+    /**
+     * Invoke a method on an instance.
+     * 
+     * @param object the instance to invoke the method on
+     * @param clazz the class of the instance
+     * @param name the name of the method
+     * @param signatures the signatures to look for in order
+     * @param parameters the parameter values for the signatures
+     * @param isSuper <code>true</code> if this is a superclass and we should therefore not look for private methods
+     * @return whatever the method returns
+     * @throws NoSuchMethodException when no method could be found
+     * @throws IllegalArgumentException when illegal values for this methods arguments are supplied 
+     * @throws IllegalAccessException when the method cannot be accessed
+     * @throws InvocationTargetException when the method that was invoked throws an exception
+     */
+    public static Object invokeMethod(Object object, Class<?> clazz, String name, Class<?>[][] signatures, Object[][] parameters, boolean isSuper) throws NoSuchMethodException, InvocationTargetException, IllegalArgumentException, IllegalAccessException {
+        if (object == null) {
+            throw new IllegalArgumentException("Instance cannot be null");
+        }
+        if (clazz == null) {
+            throw new IllegalArgumentException("Class cannot be null");
+        }
+        
+        // if we're talking to a proxy here, dig one level deeper to expose the
+        // underlying invocation handler (we do the same for injecting instances)
+        if (Proxy.isProxyClass(clazz)) {
+            object = Proxy.getInvocationHandler(object);
+            clazz = object.getClass();
+        }
+        
+        Method m = null;
+        for (int i = 0; i < signatures.length; i++) {
+            Class<?>[] signature = signatures[i];
+            m = getDeclaredMethod(clazz, name, signature, isSuper);
+            if (m != null) {
+                return m.invoke(object, parameters[i]);
+            }
+        }
+        throw new NoSuchMethodException(name);
+    }
+    
+    private static Method getDeclaredMethod(Class<?> clazz, String name, Class<?>[] signature, boolean isSuper) {
+        // first check our cache
+        Key key = new Key(clazz, name, signature);
+        Method m = null;
+        synchronized (m_methodCache) {
+            m = (Method) m_methodCache.get(key);
+            if (m != null) {
+                return m;
+            }
+            else if (m_methodCache.containsKey(key)) {
+                // the key is in our cache, it just happens to have a null value
+                return null;
+            }
+        }
+        // then do a lookup
+        try {
+            m = clazz.getDeclaredMethod(name, signature);
+            if (!(isSuper && Modifier.isPrivate(m.getModifiers()))) {
+                m.setAccessible(true);
+            }
+        }
+        catch (NoSuchMethodException e) {
+        }
+        synchronized (m_methodCache) {
+            m_methodCache.put(key, m);
+        }
+        return m;
+    }
+    
+    public static class Key {
+        private final Class<?> m_clazz;
+        private final String m_name;
+        private final Class<?>[] m_signature;
+
+        public Key(Class<?> clazz, String name, Class<?>[] signature) {
+            m_clazz = clazz;
+            m_name = name;
+            m_signature = signature;
+        }
+
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((m_clazz == null) ? 0 : m_clazz.hashCode());
+            result = prime * result + ((m_name == null) ? 0 : m_name.hashCode());
+            result = prime * result + Arrays.hashCode(m_signature);
+            return result;
+        }
+
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            Key other = (Key) obj;
+            if (m_clazz == null) {
+                if (other.m_clazz != null)
+                    return false;
+            }
+            else if (!m_clazz.equals(other.m_clazz))
+                return false;
+            if (m_name == null) {
+                if (other.m_name != null)
+                    return false;
+            }
+            else if (!m_name.equals(other.m_name))
+                return false;
+            if (!Arrays.equals(m_signature, other.m_signature))
+                return false;
+            return true;
+        }
+    }
+    
+    @SuppressWarnings("serial")
+    public static class LRUMap extends LinkedHashMap<Key, Method> {
+        private final int m_size;
+        
+        public LRUMap(int size) {
+            m_size = size;
+        }
+        
+        protected boolean removeEldestEntry(java.util.Map.Entry<Key, Method> eldest) {
+            return size() > m_size;
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceAdapterImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceAdapterImpl.java
new file mode 100644
index 0000000..9bcc696
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceAdapterImpl.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentStateListener;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ResourceDependency;
+import org.apache.felix.dm.context.DependencyContext;
+
+/**
+ * Resource adapter service implementation. This class extends the FilterService in order to catch
+ * some Service methods for configuring actual resource adapter service implementation.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResourceAdapterImpl extends FilterComponent {
+    private Object m_callbackInstance = null;
+    private String m_callbackChanged = "changed";
+    private String m_callbackAdded = "setResource";
+    private final String m_resourceFilter;
+    
+    /**
+     * Creates a new Resource Adapter Service implementation.
+     * @param dm the dependency manager used to create our internal adapter service
+     */
+    public ResourceAdapterImpl(DependencyManager dm, String resourceFilter, boolean propagate, Object callbackInstance, String callbackSet, String callbackChanged) {
+        super(dm.createComponent()); // This service will be filtered by our super class, allowing us to take control.
+        m_callbackInstance = callbackInstance;
+        m_callbackAdded = callbackSet;
+        m_callbackChanged = callbackChanged;
+        m_resourceFilter = resourceFilter;
+        m_component.setImplementation(new ResourceAdapterDecorator(propagate))
+            .add(dm.createResourceDependency()
+                 .setFilter(resourceFilter)
+                 .setAutoConfig(false)
+                 .setCallbacks("added", "removed"))
+            .setCallbacks("init", null, "stop", null);
+    }
+    
+    public ResourceAdapterImpl(DependencyManager dm, String resourceFilter, Object propagateCallbackInstance, String propagateCallbackMethod, Object callbackInstance, String callbackSet, String callbackChanged) {
+        super(dm.createComponent()); // This service will be filtered by our super class, allowing us to take control.
+        m_callbackInstance = callbackInstance;
+        m_callbackAdded = callbackSet;
+        m_callbackChanged = callbackChanged;
+        m_resourceFilter = resourceFilter;
+        m_component.setImplementation(new ResourceAdapterDecorator(propagateCallbackInstance, propagateCallbackMethod))
+            .add(dm.createResourceDependency()
+                 .setFilter(resourceFilter)
+                 .setAutoConfig(false)
+                 .setCallbacks("added", "removed"))
+            .setCallbacks("init", null, "stop", null);
+    }   
+    
+    public String getName() {
+        return "Resource Adapter" + ((m_resourceFilter != null) ? " with filter " + m_resourceFilter : "");
+    }
+
+    public class ResourceAdapterDecorator extends AbstractDecorator {
+        private final boolean m_propagate;
+        private final Object m_propagateCallbackInstance;
+        private final String m_propagateCallbackMethod;
+
+        public ResourceAdapterDecorator(boolean propagate) {
+            this(propagate, null, null);
+        }
+
+        public ResourceAdapterDecorator(Object propagateCallbackInstance, String propagateCallbackMethod) {
+            this(true, propagateCallbackInstance, propagateCallbackMethod);
+        }
+        
+        private ResourceAdapterDecorator(boolean propagate, Object propagateCallbackInstance, String propagateCallbackMethod) {
+            m_propagate = propagate;
+            m_propagateCallbackInstance = propagateCallbackInstance;
+            m_propagateCallbackMethod = propagateCallbackMethod;
+        }
+
+        public Component createService(Object[] properties) {
+            URL resource = (URL) properties[0]; 
+            Hashtable<String, Object> props = new Hashtable<>();
+            if (m_serviceProperties != null) {
+                Enumeration<String> e = m_serviceProperties.keys();
+                while (e.hasMoreElements()) {
+                    String key = e.nextElement();
+                    props.put(key, m_serviceProperties.get(key));
+                }
+            }
+            List<DependencyContext> dependencies = m_component.getDependencies();
+            // the first dependency is always the dependency on the resource, which
+            // will be replaced with a more specific dependency below
+            dependencies.remove(0);
+            ResourceDependency resourceDependency = m_manager.createResourceDependency()
+                 .setResource(resource)
+                 .setCallbacks(m_callbackInstance, m_callbackAdded, m_callbackChanged, null)
+                 .setAutoConfig(m_callbackAdded == null)
+                 .setRequired(true);
+            if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
+                resourceDependency.setPropagate(m_propagateCallbackInstance, m_propagateCallbackMethod);
+            } else {
+                resourceDependency.setPropagate(m_propagate);
+            }
+            Component service = m_manager.createComponent()
+                .setInterface(m_serviceInterfaces, props)
+                .setImplementation(m_serviceImpl)
+                .setFactory(m_factory, m_factoryCreateMethod) // if not set, no effect
+                .setComposition(m_compositionInstance, m_compositionMethod) // if not set, no effect
+                .setCallbacks(m_callbackObject, m_init, m_start, m_stop, m_destroy) // if not set, no effect
+                .add(resourceDependency);
+            
+            configureAutoConfigState(service, m_component);
+
+            for (DependencyContext dc : dependencies) {
+                service.add((Dependency) dc.createCopy());
+            }
+
+            for (ComponentStateListener stateListener : m_stateListeners) {
+                service.add(stateListener);
+            }
+            return service;
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceDependencyImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceDependencyImpl.java
new file mode 100644
index 0000000..effd588
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceDependencyImpl.java
@@ -0,0 +1,264 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.lang.reflect.InvocationTargetException;
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentDependencyDeclaration;
+import org.apache.felix.dm.ResourceDependency;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.context.AbstractDependency;
+import org.apache.felix.dm.context.DependencyContext;
+import org.apache.felix.dm.context.Event;
+import org.apache.felix.dm.context.EventType;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResourceDependencyImpl extends AbstractDependency<ResourceDependency> implements ResourceDependency, ResourceHandler, ComponentDependencyDeclaration {
+    private volatile ServiceRegistration m_registration;
+    private volatile String m_resourceFilter;
+    private volatile URL m_trackedResource;
+
+    public ResourceDependencyImpl() {
+    }
+    
+    public ResourceDependencyImpl(ResourceDependencyImpl prototype) {
+        super(prototype);
+        m_resourceFilter = prototype.m_resourceFilter;
+        m_trackedResource = prototype.m_trackedResource;
+    }
+    
+    @Override
+    public DependencyContext createCopy() {
+        return new ResourceDependencyImpl(this);
+    }
+    
+    @Override
+    public void start() {
+        Dictionary<String, Object> props = null;
+        if (m_trackedResource != null) {
+            props = new Hashtable<>();
+            props.put(ResourceHandler.URL, m_trackedResource);
+        } else {
+            if (m_resourceFilter != null) {
+                props = new Hashtable<>();
+                props.put(ResourceHandler.FILTER, m_resourceFilter);
+            }
+        }
+        m_registration = m_component.getBundleContext().registerService(ResourceHandler.class.getName(), this, props);
+        super.start();
+    }
+
+    @Override
+    public void stop() {
+        m_registration.unregister();
+        m_registration = null;
+        super.stop();
+    }
+
+    public void added(URL resource) {
+        if (m_trackedResource == null || m_trackedResource.equals(resource)) {
+            getComponentContext().handleEvent(this, EventType.ADDED, new ResourceEventImpl(resource, null));
+        }
+    }
+    
+    public void added(URL resource, Dictionary<?, ?> resourceProperties) {
+        if (m_trackedResource == null || m_trackedResource.equals(resource)) {
+            getComponentContext().handleEvent(this, EventType.ADDED, new ResourceEventImpl(resource, resourceProperties));
+        }
+    }
+        
+    public void changed(URL resource) {
+        if (m_trackedResource == null || m_trackedResource.equals(resource)) {
+            m_component.handleEvent(this, EventType.CHANGED, new ResourceEventImpl(resource, null));
+        }
+    }
+    
+    public void changed(URL resource, Dictionary<?, ?> resourceProperties) {
+        if (m_trackedResource == null || m_trackedResource.equals(resource)) {
+            m_component.handleEvent(this, EventType.CHANGED, new ResourceEventImpl(resource, resourceProperties));
+        }
+    }
+
+    public void removed(URL resource) {
+        if (m_trackedResource == null || m_trackedResource.equals(resource)) {
+            m_component.handleEvent(this, EventType.REMOVED, new ResourceEventImpl(resource, null));
+        }
+    }
+    
+    public void removed(URL resource, Dictionary<?, ?> resourceProperties) {
+        if (m_trackedResource == null || m_trackedResource.equals(resource)) {
+            m_component.handleEvent(this, EventType.REMOVED, new ResourceEventImpl(resource, resourceProperties));
+        }
+    }
+    
+    @Override
+    public void invokeCallback(EventType type, Event ... e) {
+        switch (type) {
+        case ADDED:
+            if (m_add != null) {
+                invoke(m_add, e[0]);
+            }
+            break;
+        case CHANGED:
+            if (m_change != null) {
+                invoke (m_change, e[0]);
+            }
+            break;
+        case REMOVED:
+            if (m_remove != null) {
+                invoke (m_remove, e[0]);
+            }
+            break;
+        default:
+            break;
+        }
+    }
+    
+    private void invoke(String method, Event e) {
+        ResourceEventImpl re = (ResourceEventImpl) e;
+        URL serviceInstance = re.getResource();
+        Dictionary<?,?> resourceProperties = re.getProperties();
+       
+        m_component.invokeCallbackMethod(getInstances(), method,
+            new Class[][] {
+                    { Component.class, URL.class, Dictionary.class }, 
+                    { Component.class, URL.class },
+                    { Component.class },  
+                    { URL.class, Dictionary.class }, 
+                    { URL.class },
+                    { Object.class }, 
+                    {}},
+            new Object[][] {
+                    { m_component, serviceInstance, resourceProperties }, 
+                    { m_component, serviceInstance }, 
+                    { m_component }, 
+                    { serviceInstance, resourceProperties },
+                    { serviceInstance },
+                    { serviceInstance }, 
+                    {}}
+        );
+
+    }  
+                    
+    public ResourceDependency setResource(URL resource) {
+        m_trackedResource = resource;
+        return this;
+    }
+    
+    public ResourceDependency setFilter(String resourceFilter) {
+        ensureNotActive();
+        m_resourceFilter = resourceFilter;
+        return this;
+    }
+    
+    public ResourceDependency setFilter(String resourceFilter, String resourcePropertiesFilter) {
+        ensureNotActive();
+        m_resourceFilter = resourceFilter;
+        return this;
+    }
+    
+    @Override
+    public Class<?> getAutoConfigType() {
+        return URL.class;
+    }
+        
+    @SuppressWarnings("unchecked")
+    public Dictionary<String, Object> getProperties() {
+        ResourceEventImpl re = (ResourceEventImpl) m_component.getDependencyEvent(this);
+        if (re != null) {
+            URL resource = re.getResource();
+            Dictionary<String, Object> resourceProperties = re.getProperties();
+            if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
+                try {
+                    return (Dictionary<String, Object>) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, new Class[][] {{ URL.class }}, new Object[][] {{ resource }});
+                }
+                catch (InvocationTargetException e) {
+                    m_component.getLogger().warn("Exception while invoking callback method", e.getCause());
+                }
+                catch (Throwable e) {
+                    m_component.getLogger().warn("Exception while trying to invoke callback method", e);
+                }
+                throw new IllegalStateException("Could not invoke callback");
+            }
+            else {
+                Hashtable<String, Object> props = new Hashtable<>();
+                props.put(ResourceHandler.HOST, resource.getHost());
+                props.put(ResourceHandler.PATH, resource.getPath());
+                props.put(ResourceHandler.PROTOCOL, resource.getProtocol());
+                props.put(ResourceHandler.PORT, Integer.toString(resource.getPort()));
+                // add the custom resource properties
+                if (resourceProperties != null) {
+                    Enumeration<String> properyKeysEnum = resourceProperties.keys(); 
+                    while (properyKeysEnum.hasMoreElements()) {
+                        String key = properyKeysEnum.nextElement();
+                        if (!key.equals(ResourceHandler.HOST) &&
+                                !key.equals(ResourceHandler.PATH) &&
+                                !key.equals(ResourceHandler.PROTOCOL) &&
+                                !key.equals(ResourceHandler.PORT)) {
+                            props.put(key, resourceProperties.get(key).toString());
+                        } else {
+                            m_component.getLogger().warn(
+                                "Custom resource property is overlapping with the default resource property for key: %s",
+                                key);
+                        }
+                    }
+                }
+                return props;
+            }
+        }
+        else {
+            throw new IllegalStateException("cannot find resource");
+        }
+    }
+    
+    @Override
+    public String getName() {
+        StringBuilder sb = new StringBuilder();
+        if (m_trackedResource != null) {
+            sb.append(m_trackedResource.toString());
+        }
+        if (m_resourceFilter != null) {
+            sb.append(m_resourceFilter);
+        }
+        return sb.toString();
+    }
+    
+    @Override
+    public String getSimpleName() {
+        return m_trackedResource != null ? m_trackedResource.toString() : null;
+    }
+
+    @Override
+    public String getFilter() {
+        return m_resourceFilter;
+    }
+
+    @Override
+    public String getType() {
+        return "resource";
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceEventImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceEventImpl.java
new file mode 100644
index 0000000..b655bed
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceEventImpl.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.net.URL;
+import java.util.Dictionary;
+
+import org.apache.felix.dm.context.Event;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResourceEventImpl extends Event {
+    final Dictionary<Object, Object> m_resourceProperties;
+    
+    @SuppressWarnings("unchecked")
+    public ResourceEventImpl(URL resource, Dictionary<?, ?> resourceProperties) {
+        super(resource);
+        m_resourceProperties = (Dictionary<Object, Object>) resourceProperties;
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Override
+    public <K,V> Dictionary<K,V> getProperties() {
+        return (Dictionary<K, V>) ((Dictionary<K,V>) m_resourceProperties == null ? EMPTY_PROPERTIES : m_resourceProperties);
+    }
+
+    public URL getResource() {
+        return getEvent();
+    }
+        
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof ResourceEventImpl) {
+            ResourceEventImpl r1 = this;
+            ResourceEventImpl r2 = (ResourceEventImpl) obj;
+            boolean match = r1.getResource().equals(r2.getResource());
+            if (match) {
+                Dictionary<?,?> d1 = getProperties();
+                Dictionary<?,?> d2 = r2.getProperties();
+                
+                if (d1 == null) {
+                	return d2 == null ? match : false;
+                }
+                else {
+                	return d1.equals(d2);
+                }
+            }
+        }
+        return false;
+    }
+    
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + getResource().hashCode();
+        result = prime * result + ((getProperties() == null) ? 0 : getProperties().hashCode());
+        return result;
+    }
+
+    @Override
+    public int compareTo(Event that) {
+        if (this.equals(that)) {
+            return 0;
+        }
+        
+        // Sort by resource name.
+        return getResource().toString().compareTo(((ResourceEventImpl) that).getResource().toString());
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/SerialExecutor.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/SerialExecutor.java
new file mode 100644
index 0000000..9ef6c9f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/SerialExecutor.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.felix.dm.Logger;
+import org.osgi.service.log.LogService;
+
+/**
+ * Allows you to enqueue tasks from multiple threads and then execute
+ * them on one thread sequentially. It assumes no more than one thread will
+ * try to execute the tasks and it will make an effort to pick the first
+ * task that comes along whilst making sure subsequent tasks return
+ * without waiting. <p>
+ * 
+ * This class is <b>lock free</b> by design and ensures <b>"safe object publication"</b> between scheduling threads and
+ * actual executing thread: if one thread T1 schedules a task, but another thread T2 actually 
+ * executes it, then all the objects from the T1 thread will be "safely published" to the executing T2 thread.
+ * Safe publication is ensured  because we are using a ConcurrentLinkedQueue.
+ * (see [1], chapter 3.5.3 (Safe publication idioms). 
+ * 
+ * [1] Java Concurrency In Practice, Addison Wesley
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class SerialExecutor implements Executor {
+    /** 
+     * All tasks scheduled are stored there and only one thread may run them.
+     **/
+    protected final ConcurrentLinkedQueue<Runnable> m_tasks = new ConcurrentLinkedQueue<Runnable>();
+
+    /** 
+     * Thread currently executing the task queue. 
+     **/
+    protected final AtomicReference<Thread> m_runningThread = new AtomicReference<>();
+
+    /** 
+     * Logger used when a task execution throws an exception 
+     **/
+    private final Logger m_logger;
+
+    /**
+     * Makes a new SerialExecutor
+     * @param logger used when a task execution throws an exception. Can be null if no exception should be logger.
+     */
+    public SerialExecutor(Logger logger) {
+        m_logger = logger;
+    }
+
+    /**
+     * Enqueues a task for later execution. You must call {@link #execute()} in order
+     * to trigger the task execution, which may or may not be executed by
+     * your current thread.
+     */
+    public void schedule(Runnable task) {
+        m_tasks.add(task); // No need to synchronize, m_tasks is a concurrent linked queue.
+    }
+
+    /**
+     * Executes any pending tasks, enqueued using the {@link SerialExecutor#schedule(Runnable)} method. 
+     * This method is thread safe, so multiple threads can try to execute the pending
+     * tasks, but only the first will be used to actually do so. Other threads will return immediately.
+     */
+    public void execute() {
+        Thread currentThread = Thread.currentThread();
+        if (m_runningThread.compareAndSet(null, currentThread)) {
+            runTasks(currentThread);
+        }
+    }
+
+    /**
+     * Schedules a task for execution, and then attempts to execute it. This method is thread safe, so 
+     * multiple threads can try to execute a task but only the first will be executed, other threads will 
+     * return immediately, and the first thread will execute the tasks scheduled by the other threads.<p>
+     * <p>
+     * This method is reentrant: if the current thread is currently being executed by this executor, then 
+     * the task passed to this method will be executed immediately, from the current invoking thread
+     * (inline execution).
+     */
+    public void execute(Runnable task) {
+        Thread currentThread = Thread.currentThread();
+        if (m_runningThread.get() == currentThread) {
+            runTask(task);
+        } else {
+            schedule(task);  
+            execute();
+        }
+    }
+    
+    /**
+     * Run all pending tasks
+     * @param currentRunninghread the current executing thread
+     */
+    private void runTasks(Thread currentRunninghread) {
+        do {
+            try {
+                Runnable task;
+                ConcurrentLinkedQueue<Runnable> tasks = m_tasks;
+
+                while ((task = tasks.poll()) != null) {
+                    runTask(task);
+                }
+            }
+            finally {
+                m_runningThread.set(null);
+            }
+        }
+        // We must test again if some tasks have been scheduled after our "while" loop above, but before the
+        // m_runningThread reference has been reset to null.
+        while (!m_tasks.isEmpty() && m_runningThread.compareAndSet(null, currentRunninghread));
+    }
+
+    /**
+     * Run a given task.
+     * @param task the task to execute.
+     */
+    void runTask(Runnable command) {
+        try {
+            command.run();
+        }
+        catch (Throwable t) {
+            if (m_logger != null) {
+                m_logger.log(LogService.LOG_ERROR, "Error processing tasks", t);
+            } else {
+                t.printStackTrace();
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "[Executor: queue size: " + m_tasks.size() + "]";
+    }
+}
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ServiceDependencyImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ServiceDependencyImpl.java
new file mode 100644
index 0000000..ab4daf4
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ServiceDependencyImpl.java
@@ -0,0 +1,554 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Proxy;
+import java.util.AbstractMap;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentDeclaration;
+import org.apache.felix.dm.ServiceDependency;
+import org.apache.felix.dm.context.AbstractDependency;
+import org.apache.felix.dm.context.DependencyContext;
+import org.apache.felix.dm.context.Event;
+import org.apache.felix.dm.context.EventType;
+import org.apache.felix.dm.tracker.ServiceTracker;
+import org.apache.felix.dm.tracker.ServiceTrackerCustomizer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceDependencyImpl extends AbstractDependency<ServiceDependency> implements ServiceDependency, ServiceTrackerCustomizer {
+	protected volatile ServiceTracker m_tracker;
+    protected String m_swap;
+    protected volatile Class<?> m_trackedServiceName;
+    private volatile String m_trackedServiceFilter;
+    private volatile String m_trackedServiceFilterUnmodified;
+    private volatile ServiceReference m_trackedServiceReference;
+    private volatile Object m_defaultImplementation;
+    private volatile Object m_defaultImplementationInstance;
+    private volatile Object m_nullObject;
+    private boolean m_debug = false;
+    private String m_debugKey;
+    private long m_trackedServiceReferenceId;
+    
+    public ServiceDependency setDebug(String debugKey) {
+    	m_debugKey = debugKey;
+    	m_debug = true;
+    	return this;
+    }
+
+    /**
+     * Entry to wrap service properties behind a Map.
+     */
+    private static final class ServicePropertiesMapEntry implements Map.Entry<String, Object> {
+        private final String m_key;
+        private Object m_value;
+
+        public ServicePropertiesMapEntry(String key, Object value) {
+            m_key = key;
+            m_value = value;
+        }
+
+        public String getKey() {
+            return m_key;
+        }
+
+        public Object getValue() {
+            return m_value;
+        }
+
+        public String toString() {
+            return m_key + "=" + m_value;
+        }
+
+        public Object setValue(Object value) {
+            Object oldValue = m_value;
+            m_value = value;
+            return oldValue;
+        }
+
+        @SuppressWarnings("unchecked")
+        public boolean equals(Object o) {
+            if (!(o instanceof Map.Entry)) {
+                return false;
+            }
+            Map.Entry<String, Object> e = (Map.Entry<String, Object>) o;
+            return eq(m_key, e.getKey()) && eq(m_value, e.getValue());
+        }
+
+        public int hashCode() {
+            return ((m_key == null) ? 0 : m_key.hashCode()) ^ ((m_value == null) ? 0 : m_value.hashCode());
+        }
+
+        private static final boolean eq(Object o1, Object o2) {
+            return (o1 == null ? o2 == null : o1.equals(o2));
+        }
+    }
+
+    /**
+     * Wraps service properties behind a Map.
+     */
+    private final static class ServicePropertiesMap extends AbstractMap<String, Object> {
+        private final ServiceReference m_ref;
+
+        public ServicePropertiesMap(ServiceReference ref) {
+            m_ref = ref;
+        }
+
+        public Object get(Object key) {
+            return m_ref.getProperty(key.toString());
+        }
+
+        public int size() {
+            return m_ref.getPropertyKeys().length;
+        }
+
+        public Set<Map.Entry<String, Object>> entrySet() {
+            Set<Map.Entry<String, Object>> set = new HashSet<>();
+            String[] keys = m_ref.getPropertyKeys();
+            for (int i = 0; i < keys.length; i++) {
+                set.add(new ServicePropertiesMapEntry(keys[i], m_ref.getProperty(keys[i])));
+            }
+            return set;
+        }
+    }
+    
+	public ServiceDependencyImpl() {
+	}
+	
+	public ServiceDependencyImpl(ServiceDependencyImpl prototype) {
+		super(prototype);
+        m_trackedServiceName = prototype.m_trackedServiceName;
+        m_nullObject = prototype.m_nullObject;
+        m_trackedServiceFilter = prototype.m_trackedServiceFilter;
+        m_trackedServiceFilterUnmodified = prototype.m_trackedServiceFilterUnmodified;
+        m_trackedServiceReference = prototype.m_trackedServiceReference;
+        m_autoConfigInstance = prototype.m_autoConfigInstance;
+        m_defaultImplementation = prototype.m_defaultImplementation;
+        m_autoConfig = prototype.m_autoConfig;
+	}
+	    	    
+    // --- CREATION
+    			
+    public ServiceDependency setCallbacks(Object instance, String added, String changed, String removed, String swapped) {
+        setCallbacks(instance, added, changed, removed);
+        m_swap = swapped;
+        return this;
+    }
+    
+    public ServiceDependency setCallbacks(String added, String changed, String removed, String swapped) {
+        setCallbacks(added, changed, removed);
+        m_swap = swapped;
+        return this;
+    }
+		
+    @Override
+    public ServiceDependency setDefaultImplementation(Object implementation) {
+        ensureNotActive();
+        m_defaultImplementation = implementation;
+        return this;
+    }   
+
+    @Override
+   	public ServiceDependency setService(Class<?> serviceName) {
+        setService(serviceName, null, null);
+        return this;
+    }
+
+    public ServiceDependency setService(Class<?> serviceName, String serviceFilter) {
+        setService(serviceName, null, serviceFilter);
+        return this;
+    }
+
+    public ServiceDependency setService(String serviceFilter) {
+        if (serviceFilter == null) {
+            throw new IllegalArgumentException("Service filter cannot be null.");
+        }
+        setService(null, null, serviceFilter);
+        return this;
+    }
+
+    public ServiceDependency setService(Class<?> serviceName, ServiceReference serviceReference) {
+        setService(serviceName, serviceReference, null);
+        return this;
+    }
+
+	@Override
+	public void start() {
+        if (m_trackedServiceName != null) {
+            BundleContext ctx = m_component.getBundleContext();
+            if (m_trackedServiceFilter != null) {
+                try {
+                    m_tracker = new ServiceTracker(ctx, ctx.createFilter(m_trackedServiceFilter), this);
+                } catch (InvalidSyntaxException e) {
+                    throw new IllegalStateException("Invalid filter definition for dependency: "
+                        + m_trackedServiceFilter);
+                }
+            } else if (m_trackedServiceReference != null) {
+                m_tracker = new ServiceTracker(ctx, m_trackedServiceReference, this);
+            } else {
+                m_tracker = new ServiceTracker(ctx, m_trackedServiceName.getName(), this);
+            }
+        } else {
+            throw new IllegalStateException("Could not create tracker for dependency, no service name specified.");
+        }
+        if (m_debug) {
+            m_tracker.setDebug(m_debugKey);
+        }
+        m_tracker.open();
+        super.start();
+	}
+	
+	@Override
+	public void stop() {
+	    m_tracker.close();
+	    m_tracker = null;
+	    super.stop();
+	}
+
+	@Override
+	public Object addingService(ServiceReference reference) {
+		try {
+		    return m_component.getBundleContext().getService(reference);
+		} catch (IllegalStateException e) {
+		    // most likely our bundle is being stopped. Only log an exception if our component is enabled.
+		    if (m_component.isActive()) {
+                m_component.getLogger().warn("could not handle service dependency for component %s", e,
+                    m_component.getComponentDeclaration().getClassName());
+		    }
+		    return null;		    
+		}
+	}
+
+	@Override
+	public void addedService(ServiceReference reference, Object service) {
+		if (m_debug) {
+			System.out.println(m_debugKey + " addedService: ref=" + reference + ", service=" + service);
+		}
+        m_component.handleEvent(this, EventType.ADDED,
+            new ServiceEventImpl(m_component.getBundle(), m_component.getBundleContext(), reference, service));
+	}
+
+	@Override
+	public void modifiedService(ServiceReference reference, Object service) {
+        m_component.handleEvent(this, EventType.CHANGED,
+            new ServiceEventImpl(m_component.getBundle(), m_component.getBundleContext(), reference, service));
+	}
+
+	@Override
+	public void removedService(ServiceReference reference, Object service) {
+        m_component.handleEvent(this, EventType.REMOVED,
+            new ServiceEventImpl(m_component.getBundle(), m_component.getBundleContext(), reference, service));
+	}
+	
+    @Override
+    public void invokeCallback(EventType type, Event ... events) {
+        switch (type) {
+        case ADDED:
+            if (m_add != null) {
+                invoke (m_add, events[0], getInstances());
+            }
+            break;
+        case CHANGED:
+            if (m_change != null) {
+                invoke (m_change, events[0], getInstances());
+            }
+            break;
+        case REMOVED:
+            if (m_remove != null) {
+                invoke (m_remove, events[0], getInstances());
+            }
+            break;
+        case SWAPPED:
+            if (m_swap != null) {
+                ServiceEventImpl oldE = (ServiceEventImpl) events[0];
+                ServiceEventImpl newE = (ServiceEventImpl) events[1];
+                invokeSwap(m_swap, oldE.getReference(), oldE.getEvent(), newE.getReference(), newE.getEvent(),
+                    getInstances());
+            }
+            break;
+        }
+    }
+
+	@Override
+    public Class<?> getAutoConfigType() {
+        return m_trackedServiceName;
+    }
+	
+    @Override
+    public DependencyContext createCopy() {
+        return new ServiceDependencyImpl(this);
+    }
+    
+    @Override
+    public String getName() {
+        StringBuilder sb = new StringBuilder();
+        if (m_trackedServiceName != null) {
+            sb.append(m_trackedServiceName.getName());
+            if (m_trackedServiceFilterUnmodified != null) {
+                sb.append(' ');
+                sb.append(m_trackedServiceFilterUnmodified);
+            }
+        }
+        if (m_trackedServiceReference != null) {
+            sb.append("{service.id=" + m_trackedServiceReference.getProperty(Constants.SERVICE_ID) + "}");
+        }
+        return sb.toString();
+    }
+    
+    @Override
+    public String getSimpleName() {
+        if (m_trackedServiceName != null) {
+            return m_trackedServiceName.getName();
+        }
+        return null;
+    }
+
+    @Override
+    public String getFilter() {
+        if (m_trackedServiceFilterUnmodified != null) {
+            return m_trackedServiceFilterUnmodified;
+        } else if (m_trackedServiceReference != null) {
+            return new StringBuilder("(").append(Constants.SERVICE_ID).append("=").append(
+                String.valueOf(m_trackedServiceReferenceId)).append(")").toString();
+        } else {
+            return null;
+        }
+    }
+    
+    @Override
+    public String getType() {
+        return "service";
+    }
+
+	@SuppressWarnings("unchecked")
+    @Override
+    public Dictionary<String, Object> getProperties() {
+        ServiceEventImpl se = (ServiceEventImpl) m_component.getDependencyEvent(this);
+        if (se != null) {
+            if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
+                try {
+                    return (Dictionary<String, Object>) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod,
+                            new Class[][]{{ServiceReference.class, Object.class}, {ServiceReference.class}}, new Object[][]{
+                                    {se.getReference(), se.getEvent()}, {se.getReference()}});
+                } catch (InvocationTargetException e) {
+                    m_component.getLogger().warn("Exception while invoking callback method", e.getCause());
+                } catch (Throwable e) {
+                    m_component.getLogger().warn("Exception while trying to invoke callback method", e);
+                }
+                throw new IllegalStateException("Could not invoke callback");
+            } else {
+                Hashtable<String, Object> props = new Hashtable<>();
+                String[] keys = se.getReference().getPropertyKeys();
+                for (int i = 0; i < keys.length; i++) {
+                    if (!(keys[i].equals(Constants.SERVICE_ID) || keys[i].equals(Constants.SERVICE_PID))) {
+                        props.put(keys[i], se.getReference().getProperty(keys[i]));
+                    }
+                }
+                return props;
+            }
+        } else {
+            throw new IllegalStateException("cannot find service reference");
+        }
+    }	
+        
+    /** Internal method to set the name, service reference and/or filter. */
+    private void setService(Class<?> serviceName, ServiceReference serviceReference, String serviceFilter) {
+        ensureNotActive();
+        if (serviceName == null) {
+            m_trackedServiceName = Object.class;
+        }
+        else {
+            m_trackedServiceName = serviceName;
+        }
+        if (serviceFilter != null) {
+            m_trackedServiceFilterUnmodified = serviceFilter;
+            if (serviceName == null) {
+                m_trackedServiceFilter = serviceFilter;
+            }
+            else {
+                m_trackedServiceFilter = "(&(" + Constants.OBJECTCLASS + "=" + serviceName.getName() + ")"
+                    + serviceFilter + ")";
+            }
+        }
+        else {
+            m_trackedServiceFilterUnmodified = null;
+            m_trackedServiceFilter = null;
+        }
+        if (serviceReference != null) {
+            m_trackedServiceReference = serviceReference;
+            if (serviceFilter != null) {
+                throw new IllegalArgumentException("Cannot specify both a filter and a service reference.");
+            }
+            m_trackedServiceReferenceId = (Long) m_trackedServiceReference.getProperty(Constants.SERVICE_ID);
+        }
+        else {
+            m_trackedServiceReference = null;
+        }
+    }
+    
+    @Override
+    public Object getDefaultService(boolean nullObject) {
+        Object service = null;
+        if (isAutoConfig()) {
+            service = getDefaultImplementation();
+            if (service == null && nullObject) {
+                service = getNullObject();
+            }
+        }
+        return service;
+    }
+        
+    private Object getNullObject() {
+        if (m_nullObject == null) {
+            Class<?> trackedServiceName;
+            trackedServiceName = m_trackedServiceName;
+            try {
+                m_nullObject = Proxy.newProxyInstance(trackedServiceName.getClassLoader(),
+                    new Class[] { trackedServiceName }, new DefaultNullObject());
+            }
+            catch (Throwable err) {
+                m_component.getLogger().err("Could not create null object for %s.", err, trackedServiceName);
+            }
+        }
+        return m_nullObject;
+    }
+
+    private Object getDefaultImplementation() {
+        if (m_defaultImplementation != null) {
+            if (m_defaultImplementation instanceof Class) {
+                try {
+                    m_defaultImplementationInstance = ((Class<?>) m_defaultImplementation).newInstance();
+                }
+                catch (Throwable e) {
+                    m_component.getLogger().err("Could not create default implementation instance of class %s.", e,
+                        m_defaultImplementation);
+                }
+            }
+            else {
+                m_defaultImplementationInstance = m_defaultImplementation;
+            }
+        }
+        return m_defaultImplementationInstance;
+    }
+
+    public void invoke(String method, Event e, Object[] instances) {
+        ServiceEventImpl se = (ServiceEventImpl) e;
+        ServicePropertiesMap propertiesMap = new ServicePropertiesMap(se.getReference());
+        Dictionary<?,?> properties = se.getProperties();
+        m_component.invokeCallbackMethod(instances, method,
+            new Class[][]{
+                {Component.class, ServiceReference.class, m_trackedServiceName},
+                {Component.class, ServiceReference.class, Object.class}, 
+                {Component.class, ServiceReference.class},
+                {Component.class, m_trackedServiceName}, 
+                {Component.class, Object.class}, 
+                {Component.class},
+                {Component.class, Map.class, m_trackedServiceName},
+                {ServiceReference.class, m_trackedServiceName},
+                {ServiceReference.class, Object.class}, 
+                {ServiceReference.class},
+                {m_trackedServiceName}, 
+                {m_trackedServiceName, Map.class}, 
+                {Map.class, m_trackedServiceName}, 
+                {m_trackedServiceName, Dictionary.class}, 
+                {Dictionary.class, m_trackedServiceName}, 
+                {Object.class}, 
+                {}},
+            
+            new Object[][]{
+                {m_component, se.getReference(), se.getEvent()},
+                {m_component, se.getReference(), se.getEvent()}, 
+                {m_component, se.getReference()}, 
+                {m_component, se.getEvent()},
+                {m_component, se.getEvent()},
+                {m_component},
+                {m_component, propertiesMap, se.getEvent()},
+                {se.getReference(), se.getEvent()},
+                {se.getReference(), se.getEvent()}, 
+                {se.getReference()}, 
+                {se.getEvent()}, 
+                {se.getEvent(), propertiesMap},
+                {propertiesMap, se.getEvent()},
+                {se.getEvent(), properties},
+                {properties, se.getEvent()},
+                {se.getEvent()}, 
+                {}}
+        );
+    }
+        
+    public void invokeSwap(String swapMethod, ServiceReference previousReference, Object previous,
+			ServiceReference currentReference, Object current, Object[] instances) {
+    	if (m_debug) {
+    		System.out.println("invoke swap: " + swapMethod + " on component " + m_component + ", instances: " + Arrays.toString(instances) + " - " + ((ComponentDeclaration)m_component).getState());
+    	}
+    	try {
+		m_component.invokeCallbackMethod(instances, swapMethod,
+				new Class[][]{
+            		{m_trackedServiceName, m_trackedServiceName}, 
+            		{Object.class, Object.class},
+            		{ServiceReference.class, m_trackedServiceName, ServiceReference.class, m_trackedServiceName},
+            		{ServiceReference.class, Object.class, ServiceReference.class, Object.class},
+            		{Component.class, m_trackedServiceName, m_trackedServiceName}, 
+            		{Component.class, Object.class, Object.class},
+            		{Component.class, ServiceReference.class, m_trackedServiceName, ServiceReference.class, m_trackedServiceName},
+            		{Component.class, ServiceReference.class, Object.class, ServiceReference.class, Object.class}}, 
+	            
+            	new Object[][]{
+                    {previous, current}, 
+                    {previous, current}, 
+                    {previousReference, previous, currentReference, current},
+                    {previousReference, previous, currentReference, current}, {m_component, previous, current},
+                    {m_component, previous, current}, {m_component, previousReference, previous, currentReference, current},
+                    {m_component, previousReference, previous, currentReference, current}}
+			);
+    	} catch (Throwable e) {
+    	    m_component.getLogger().err("Could not invoke swap callback", e);
+    	}
+	}
+
+	@Override
+	public void swappedService(final ServiceReference reference, final Object service,
+			final ServiceReference newReference, final Object newService) {
+		if (m_swap != null) {
+			// it will not trigger a state change, but the actual swap should be scheduled to prevent things
+			// getting out of order.		    		    
+		    // We delegate the swap handling to the ComponentImpl, which is the class responsible for state management.
+		    // The ComponentImpl will first check if the component is in the proper state so the swap method can be invoked.		    
+            m_component.handleEvent(this, EventType.SWAPPED,
+                new ServiceEventImpl(m_component.getBundle(), m_component.getBundleContext(), reference, service),
+                new ServiceEventImpl(m_component.getBundle(), m_component.getBundleContext(), newReference, newService));
+		} else {
+			addedService(newReference, newService);
+			removedService(reference, service);
+		}
+	}	
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ServiceEventImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ServiceEventImpl.java
new file mode 100644
index 0000000..a27147d
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ServiceEventImpl.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.util.Dictionary;
+
+import org.apache.felix.dm.context.Event;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceEventImpl extends Event {
+    /**
+     * The service reference on which a service dependency depends on
+     */
+	private final ServiceReference m_reference; 
+		
+    /**
+     * The bundle context of the bundle which has created the service dependency. If not null, 
+     * will be used in close method when ugetting the service reference of the dependency.
+     */
+	private final BundleContext m_bundleContext;
+	
+    /**
+     * The bundle which has created the service dependency. If not null, will be used to ensure that the bundle is still active before
+     * ungetting the service reference of the dependency. (ungetting a service reference on a bundle which is not
+     * active triggers an exception, and this may degrade performance, especially when doing some benchmarks).
+     */
+	private final Bundle m_bundle;
+	
+	public ServiceEventImpl(ServiceReference reference, Object service) {
+	    this(null, null, reference, service);
+	}
+
+    public ServiceEventImpl(Bundle bundle, BundleContext bundleContext, ServiceReference reference, Object service) {
+	    super(service);
+	    m_bundle = bundle;
+	    m_bundleContext = bundleContext;
+		m_reference = reference;
+	}
+	
+	/**
+	 * Returns the bundle which has declared a service dependency.
+	 */
+	public Bundle getBundle() {
+	    return m_bundle;
+	}
+	
+    /**
+     * Returns the context of the bundle which has declared a service dependency.
+     */
+	public BundleContext getBundleContext() {
+	    return m_bundleContext;
+	}
+
+	/**
+	 * Returns the reference service dependency.
+	 */
+	public ServiceReference getReference() {
+		return m_reference;
+	}
+	    
+    @SuppressWarnings("unchecked")
+	@Override
+    public Dictionary<String, Object> getProperties() {
+        return ServiceUtil.propertiesToDictionary(m_reference);
+    }
+	
+	@Override
+	public boolean equals(Object obj) {
+		if (obj instanceof ServiceEventImpl) {
+			return getReference().equals(((ServiceEventImpl) obj).getReference());
+		}
+		return false;
+	}
+	
+	@Override
+	public int hashCode() {
+		return getReference().hashCode();
+	}
+
+    @Override
+    public int compareTo(Event b) {
+    	return getReference().compareTo(((ServiceEventImpl) b).getReference());
+    }
+        
+    @Override
+    public String toString() {
+    	return getEvent().toString();
+    }
+
+    @Override
+    public void close() {
+        if (m_bundleContext != null) {
+            try {
+                // Optimization: don't call ungetService if the bundle referring to the service is not active. 
+                // This optim is important when doing benchmarks where the referring bundle is being stopped 
+                // while some dependencies are lost concurrently (here we want to avoid having many exception thrown).
+                if (m_bundle == null || m_bundle.getState() == Bundle.ACTIVE) {
+                    m_bundleContext.ungetService(m_reference);
+                }
+            } catch (IllegalStateException e) {}
+        }
+    }
+}
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ServiceRegistrationImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ServiceRegistrationImpl.java
new file mode 100644
index 0000000..4604acb
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ServiceRegistrationImpl.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+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 <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public final class ServiceRegistrationImpl implements ServiceRegistration {
+    public static final ServiceRegistrationImpl ILLEGAL_STATE = new ServiceRegistrationImpl();
+    private volatile ServiceRegistration m_registration;
+
+    public ServiceRegistrationImpl() {
+        m_registration = null;
+    }
+    
+    public ServiceReference getReference() {
+        return ensureRegistration().getReference();
+    }
+
+    @SuppressWarnings("rawtypes")
+    public void setProperties(Dictionary dictionary) {
+        ensureRegistration().setProperties(dictionary);
+    }
+
+    public void unregister() {
+        ensureRegistration().unregister();
+    }
+
+    public boolean equals(Object obj) {
+        return ensureRegistration().equals(obj);
+    }
+
+    public int hashCode() {
+        return ensureRegistration().hashCode();
+    }
+
+    public String toString() {
+        return ensureRegistration().toString();
+    }
+    
+    private synchronized ServiceRegistration 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
+            }
+        }
+        // check if we're in an illegal state and throw an exception
+        if (ILLEGAL_STATE == m_registration) {
+            throw new IllegalStateException("Service is not registered.");
+        }
+        return m_registration;
+    }
+
+    /**
+     * Sets the service registration and notifies all waiting parties.
+     */
+    void setServiceRegistration(ServiceRegistration registration) {
+        synchronized (this) {
+        	m_registration = registration;
+            notifyAll();
+        }
+    }
+
+    /**
+     * Sets this wrapper to an illegal state, which will cause all threads
+     * that are waiting for this service registration to fail.
+     */
+	void setIllegalState() {
+        setServiceRegistration(ServiceRegistrationImpl.ILLEGAL_STATE);
+	}
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ServiceUtil.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ServiceUtil.java
new file mode 100644
index 0000000..cb7bfb6
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ServiceUtil.java
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * OSGi service utilities.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceUtil {
+    /**
+     * Useful when needing to provide empty service properties.
+     */
+    public final static Dictionary<String, Object> EMPTY_PROPERTIES = new Hashtable<>();
+
+    /**
+     * Returns the service ranking of a service, based on its service reference. If
+     * the service has a property specifying its ranking, that will be returned. If
+     * not, the default ranking of zero will be returned.
+     * 
+     * @param ref the service reference to determine the ranking for
+     * @return the ranking
+     */
+    public static int getRanking(ServiceReference ref) {
+        return getRankingAsInteger(ref).intValue();
+    }
+    
+    /**
+     * Returns the service ranking of a service, based on its service reference. If
+     * the service has a property specifying its ranking, that will be returned. If
+     * not, the default ranking of zero will be returned.
+     * 
+     * @param ref the service reference to determine the ranking for
+     * @return the ranking
+     */
+    public static Integer getRankingAsInteger(ServiceReference ref) {
+        Integer rank = (Integer) ref.getProperty(Constants.SERVICE_RANKING);
+        if (rank != null) {
+            return rank;
+        }
+        return new Integer(0);
+    }
+    
+    /**
+     * Returns the service ID of a service, based on its service reference. This
+     * method is aware of service aspects as defined by the dependency manager and
+     * will return the ID of the orginal service if you give it an aspect.
+     * 
+     * @param ref the service reference to determine the service ID of
+     * @return the service ID
+     */
+    public static long getServiceId(ServiceReference ref) {
+        return getServiceIdAsLong(ref).longValue();
+    }
+    
+    /**
+     * Returns the service ID of a service, based on its service reference. This
+     * method is aware of service aspects as defined by the dependency manager and
+     * will return the ID of the orginal service if you give it an aspect.
+     * 
+     * @param ref the service reference to determine the service ID of
+     * @return the service ID
+     */
+    public static Long getServiceIdAsLong(ServiceReference ref) {
+    	return getServiceIdObject(ref);
+    }
+    
+    public static Long getServiceIdObject(ServiceReference ref) {
+        Long aid = (Long) ref.getProperty(DependencyManager.ASPECT);
+        if (aid != null) {
+            return aid;
+        }
+        Long sid = (Long) ref.getProperty(Constants.SERVICE_ID);
+        if (sid != null) {
+            return sid;
+        }
+        throw new IllegalArgumentException("Invalid service reference, no service ID found");
+    }
+
+    /**
+     * Determines if the service is an aspect as defined by the dependency manager.
+     * Aspects are defined by a property and this method will check for its presence.
+     * 
+     * @param ref the service reference
+     * @return <code>true</code> if it's an aspect, <code>false</code> otherwise
+     */
+    public static boolean isAspect(ServiceReference ref) {
+        Long aid = (Long) ref.getProperty(DependencyManager.ASPECT);
+        return (aid != null);
+    }
+    
+    /**
+     * Converts a service reference to a string, listing both the bundle it was
+     * registered from and all properties.
+     * 
+     * @param ref the service reference
+     * @return a string representation of the service
+     */
+    public static String toString(ServiceReference ref) {
+        if (ref == null) {
+            return "ServiceReference[null]";
+        }
+        else {
+            StringBuffer buf = new StringBuffer();
+            Bundle bundle = ref.getBundle();
+            if (bundle != null) {
+                buf.append("ServiceReference[");
+                buf.append(bundle.getBundleId());
+                buf.append("]{");
+            }
+            else {
+                buf.append("ServiceReference[unregistered]{");
+            }
+            buf.append(propertiesToString(ref, null));
+            buf.append("}");
+            return buf.toString();
+        }
+    }
+    
+    /**
+     * Converts the properties of a service reference to a string.
+     * 
+     * @param ref the service reference
+     * @param exclude a list of properties to exclude, or <code>null</code> to show everything
+     * @return a string representation of the service properties
+     */
+    public static String propertiesToString(ServiceReference ref, List<String> exclude) {
+        StringBuffer buf = new StringBuffer();
+        String[] keys = ref.getPropertyKeys();
+        for (int i = 0; i < keys.length; i++) {
+            if (i > 0) { 
+                buf.append(','); 
+            }
+            buf.append(keys[i]);
+            buf.append('=');
+            Object val = ref.getProperty(keys[i]);
+            if (exclude == null || !exclude.contains(val)) {
+                if (val instanceof String[]) {
+                    String[] valArray = (String[]) val;
+                    StringBuffer valBuf = new StringBuffer();
+                    valBuf.append('{');
+                    for (int j = 0; j < valArray.length; j++) {
+                        if (valBuf.length() > 1) {
+                            valBuf.append(',');
+                        }
+                        valBuf.append(valArray[j].toString());
+                    }
+                    valBuf.append('}');
+                    buf.append(valBuf);
+                }
+                else {
+                    buf.append(val.toString());
+                }
+            }
+        }
+        return buf.toString();
+    }
+    
+    /**
+     * Wraps ServiceReference properties behind a Dictionary object.
+     * @param ref the ServiceReference to wrap
+     * @return a new Dictionary used to wrap the ServiceReference properties
+     */
+    public static Dictionary<String, Object> propertiesToDictionary(final ServiceReference ref) {
+        return new Dictionary<String, Object>() {
+            private Dictionary<String, Object> m_wrapper;
+            
+            @Override
+            public int size() {
+                return getWrapper().size();
+            }
+
+            @Override
+            public boolean isEmpty() {
+                return getWrapper().isEmpty();
+            }
+
+            @Override
+            public Enumeration<String> keys() {
+                return getWrapper().keys();
+            }
+
+            @Override
+            public Enumeration<Object> elements() {
+                return getWrapper().elements();
+            }
+
+            @Override
+            public Object get(Object key) {
+                return ref.getProperty(key.toString());
+            }
+
+            @Override
+            public Object put(String key, Object value) {
+                throw new UnsupportedOperationException("Unmodified Dictionary.");
+            }
+
+            @Override
+            public Object remove(Object key) {
+                throw new UnsupportedOperationException("Unmodified Dictionary.");
+            }
+            
+            @Override
+            public String toString() {
+                return getWrapper().toString();
+            }
+            
+            private synchronized Dictionary<String, Object> getWrapper() {
+                if (m_wrapper == null) {
+                    m_wrapper = new Hashtable<String, Object>();
+                    String[] keys = ref.getPropertyKeys();
+                    for (String key : keys) {
+                        m_wrapper.put(key, ref.getProperty(key));
+                    }                    
+                }
+                return m_wrapper;
+            }
+        };
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/TemporalServiceDependencyImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/TemporalServiceDependencyImpl.java
new file mode 100644
index 0000000..c329307
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/TemporalServiceDependencyImpl.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.ServiceDependency;
+import org.apache.felix.dm.context.EventType;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Temporal Service dependency implementation, used to hide temporary service dependency "outage".
+ * Only works with a required dependency.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class TemporalServiceDependencyImpl extends ServiceDependencyImpl implements ServiceDependency, InvocationHandler {
+    // Max millis to wait for service availability.
+    private final long m_timeout;
+        
+    // Framework bundle (we use it to detect if the framework is stopping)
+    private final Bundle m_frameworkBundle;
+
+    // The service proxy, which blocks when service is not available.
+    private volatile Object m_serviceInstance;
+
+    /**
+     * Creates a new Temporal Service Dependency.
+     * 
+     * @param context The bundle context of the bundle which is instantiating this dependency object
+     * @param logger the logger our Internal logger for logging events.
+     * @see DependencyActivatorBase#createTemporalServiceDependency()
+     */
+    public TemporalServiceDependencyImpl(BundleContext context, long timeout) {
+        super.setRequired(true);
+        if (timeout < 0) {
+            throw new IllegalArgumentException("Invalid timeout value: " + timeout);
+        }
+        m_timeout = timeout;
+        m_frameworkBundle = context.getBundle(0);
+    }
+
+    /**
+     * Sets the required flag which determines if this service is required or not. This method
+     * just override the superclass method in order to check if the required flag is true 
+     * (optional dependency is not supported by this class).
+     * 
+     * @param required the required flag, which must be set to true
+     * @return this service dependency
+     * @throws IllegalArgumentException if the "required" parameter is not true.
+     */
+    @Override
+    public ServiceDependency setRequired(boolean required) {
+        if (! required) {
+            throw new IllegalArgumentException("A Temporal Service dependency can't be optional");
+        }
+        super.setRequired(required);
+        return this;
+    }
+    
+    /**
+     * The ServiceTracker calls us here in order to inform about a service arrival.
+     */
+    @Override
+    public void addedService(ServiceReference ref, Object service) {
+        // Update our service cache, using the tracker. We do this because the
+        // just added service might not be the service with the highest rank ...
+        boolean makeAvailable = false;
+        synchronized (this) {
+            if (m_serviceInstance == null) {
+                m_serviceInstance = Proxy.newProxyInstance(m_trackedServiceName.getClassLoader(), new Class[] { m_trackedServiceName }, this);
+                makeAvailable = true;
+            }
+        }
+        if (makeAvailable) {
+            getComponentContext().handleEvent(this, EventType.ADDED,
+                new ServiceEventImpl(m_component.getBundle(), m_component.getBundleContext(), ref, m_serviceInstance));
+        } else {
+            // This added will possibly unblock our invoke() method (if it's blocked in m_tracker.waitForService method).
+        }
+    }
+
+    /**
+     * The ServiceTracker calls us here when a tracked service properties are modified.
+     */
+    @Override
+    public void modifiedService(ServiceReference ref, Object service) {
+        // We don't care.
+    }
+
+    /**
+     * The ServiceTracker calls us here when a tracked service is lost.
+     */
+    @Override
+    public void removedService(ServiceReference ref, Object service) {
+        // If we detect that the fwk is stopping, we behave as our superclass. That is:
+        // the lost dependency has to trigger our service deactivation, since the fwk is stopping
+        // and the lost dependency won't come up anymore.
+        if (m_frameworkBundle.getState() == Bundle.STOPPING) {
+            // Important: Notice that calling "super.removedService() might invoke our service "stop" 
+            // callback, which in turn might invoke the just removed service dependency. In this case, 
+            // our "invoke" method won't use the tracker to get the service dependency (because at this point, 
+            // the tracker has withdrawn its reference to the lost service). So, you will see that the "invoke" 
+            // method will use the "m_cachedService" instead ...
+            boolean makeUnavailable = false;
+            synchronized (this) {
+                if (m_tracker.getService() == null) {
+                    makeUnavailable = true;
+                }
+            }
+            if (makeUnavailable) {
+                // the event.close method will unget the service.
+                m_component.handleEvent(this, EventType.REMOVED, new ServiceEventImpl(m_component.getBundle(),
+                    m_component.getBundleContext(), ref, m_serviceInstance));
+            }
+        } else {
+            // Unget what we got in addingService (see ServiceTracker 701.4.1)
+            m_component.getBundleContext().ungetService(ref);
+            // if there is no available services, the next call to invoke() method will block until another service
+            // becomes available. Else the next call to invoke() will return that highest ranked available service.
+        }
+    }
+
+    @Override
+    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+        Object service = null;
+        try {
+            service = m_tracker.waitForService(m_timeout);
+        } catch (InterruptedException e) {            
+        }
+        
+        if (service == null) {
+            throw new IllegalStateException("Service unavailable: " + m_trackedServiceName.getName());
+        }
+        
+        try {
+			try {
+				return method.invoke(service, args);
+			} catch (IllegalAccessException iae) {
+				method.setAccessible(true);
+				return method.invoke(service, args);
+			}
+        } catch (InvocationTargetException e) {
+        	throw e.getTargetException();
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/AbstractFactoryFilterIndex.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/AbstractFactoryFilterIndex.java
new file mode 100644
index 0000000..1e72f31
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/AbstractFactoryFilterIndex.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.index;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.apache.felix.dm.impl.ServiceUtil;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class AbstractFactoryFilterIndex {
+	protected final Map<Long, SortedSet<ServiceReference>> m_sidToServiceReferencesMap = new HashMap<>();
+	protected final Map <ServiceListener, String> m_listenerToFilterMap = new HashMap<>();
+
+    public void addedService(ServiceReference reference, Object service) {
+        add(reference);
+    }
+
+    public void modifiedService(ServiceReference reference, Object service) {
+        modify(reference);
+    }
+
+    public void removedService(ServiceReference reference, Object service) {
+        remove(reference);
+    }
+    
+	public void swappedService(ServiceReference reference, Object service,
+			ServiceReference newReference, Object newService) {
+		addedService(newReference, newService);
+		removedService(reference, service);
+	}
+    
+	public void add(ServiceReference reference) {
+        Long sid = ServiceUtil.getServiceIdObject(reference);
+        synchronized (m_sidToServiceReferencesMap) {
+            SortedSet<ServiceReference> list = m_sidToServiceReferencesMap.get(sid);
+            if (list == null) {
+                list = new TreeSet<ServiceReference>();
+                m_sidToServiceReferencesMap.put(sid, list);
+            }
+            list.add(reference);
+        }
+    }
+
+    public void modify(ServiceReference reference) {
+        remove(reference);
+        add(reference);
+    }
+
+	public void remove(ServiceReference reference) {
+        Long sid = ServiceUtil.getServiceIdObject(reference);
+        synchronized (m_sidToServiceReferencesMap) {
+            SortedSet<ServiceReference> list = m_sidToServiceReferencesMap.get(sid);
+            if (list != null) {
+                list.remove(reference);
+            }
+        }
+    }
+    
+    protected boolean referenceMatchesObjectClass(ServiceReference ref, String objectClass) {
+    	boolean matches = false;
+    	Object value = ref.getProperty(Constants.OBJECTCLASS);
+    	matches = Arrays.asList((String[])value).contains(objectClass);
+    	return matches;
+    }
+    
+    /** Structure to hold internal filter data. */
+    protected static class FilterData {
+        public long m_serviceId;
+        public String m_objectClass;
+        public int m_ranking;
+
+		public String toString() {
+			return "FilterData [serviceId=" + m_serviceId + "]";
+		}
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/AdapterFilterIndex.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/AdapterFilterIndex.java
new file mode 100644
index 0000000..74699b9
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/AdapterFilterIndex.java
@@ -0,0 +1,223 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.index;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.FilterIndex;
+import org.apache.felix.dm.impl.ServiceUtil;
+import org.apache.felix.dm.tracker.ServiceTracker;
+import org.apache.felix.dm.tracker.ServiceTrackerCustomizer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterFilterIndex extends AbstractFactoryFilterIndex implements FilterIndex, ServiceTrackerCustomizer {
+	// (&(objectClass=foo.Bar)(|(service.id=18233)(org.apache.felix.dependencymanager.aspect=18233)))
+	private static final String FILTER_REGEXP = "\\(&\\(" + Constants.OBJECTCLASS + "=([a-zA-Z\\.\\$0-9]*)\\)\\(\\|\\(" 
+									+ Constants.SERVICE_ID + "=([0-9]*)\\)\\(" 
+									+ DependencyManager.ASPECT + "=([0-9]*)\\)\\)\\)";
+	private static final Pattern PATTERN = Pattern.compile(FILTER_REGEXP);
+    private final Object m_lock = new Object();
+    private ServiceTracker m_tracker;
+    private BundleContext m_context;
+	private final Map<Object, List<ServiceListener>> m_sidToListenersMap = new HashMap<>();
+	protected final Map<ServiceListener, String> m_listenerToObjectClassMap = new HashMap<>();
+
+    public void open(BundleContext context) {
+        synchronized (m_lock) {
+            if (m_context != null) {
+                throw new IllegalStateException("Filter already open.");
+            }
+            try {
+                m_tracker = new ServiceTracker(context, context.createFilter("(" + Constants.OBJECTCLASS + "=*)"), this);
+            }
+            catch (InvalidSyntaxException e) {
+                throw new Error();
+            }
+            m_context = context;
+        }
+        m_tracker.open(true, true);
+    }
+
+    public void close() {
+        ServiceTracker tracker;
+        synchronized (m_lock) {
+            if (m_context == null) {
+                throw new IllegalStateException("Filter already closed.");
+            }
+            tracker = m_tracker;
+            m_tracker = null;
+            m_context = null;
+        }
+        tracker.close();
+    }
+
+    public boolean isApplicable(String clazz, String filter) {
+        return getFilterData(clazz, filter) != null;
+    }
+
+    /** Returns a value object with the relevant filter data, or <code>null</code> if this filter was not valid. */
+    private FilterData getFilterData(String clazz, String filter) {
+        // something like:
+    	// (&(objectClass=foo.Bar)(|(service.id=18233)(org.apache.felix.dependencymanager.aspect=18233)))  
+    	FilterData resultData = null;
+    	if (filter != null) {
+	    	Matcher matcher = PATTERN.matcher(filter);
+	    	if (matcher.matches()) {
+	    		String sid = matcher.group(2);
+	    		String sid2 = matcher.group(3);
+	    		if (sid.equals(sid2)) {
+	    			resultData = new FilterData();
+	    			resultData.m_serviceId = Long.parseLong(sid);
+	    		}
+	    	}
+    	}
+    	return resultData;
+    }
+
+	public List<ServiceReference> getAllServiceReferences(String clazz, String filter) {
+		List<ServiceReference> result = new ArrayList<>();
+		Matcher matcher = PATTERN.matcher(filter);
+		if (matcher.matches()) {
+			FilterData data = getFilterData(clazz, filter);
+			if (data != null) {
+				SortedSet<ServiceReference> list = null;
+				synchronized (m_sidToServiceReferencesMap) {
+					list = m_sidToServiceReferencesMap.get(Long.valueOf(data.m_serviceId));
+					if (list != null) {
+						Iterator<ServiceReference> iterator = list.iterator();
+						while (iterator.hasNext()) {
+							ServiceReference ref = (ServiceReference) iterator.next();
+							String objectClass = matcher.group(1);
+							if (referenceMatchesObjectClass(ref, objectClass)) {
+								result.add(ref);
+							}
+						}
+					}
+				}
+			}
+		}
+		return result;
+	}
+    
+	public void serviceChanged(ServiceEvent event) {
+        ServiceReference reference = event.getServiceReference();
+        Long sid = ServiceUtil.getServiceIdObject(reference);
+        List<ServiceListener> notificationList = new ArrayList<>();
+        synchronized (m_sidToListenersMap) {
+            List<ServiceListener> list = m_sidToListenersMap.get(sid);
+            if (list != null) {
+            	Iterator<ServiceListener> iterator = list.iterator();
+            	while (iterator.hasNext()) {
+                	ServiceListener listener = (ServiceListener) iterator.next();
+                	String objectClass = m_listenerToObjectClassMap.get(listener);
+                	if (referenceMatchesObjectClass(reference, objectClass)) {
+                		notificationList.add(listener);
+                	} 
+            	}
+            }
+        }
+        // notify
+        Iterator<ServiceListener> iterator = notificationList.iterator();
+        while (iterator.hasNext()) {
+        	ServiceListener listener = (ServiceListener) iterator.next();
+        	listener.serviceChanged(event);
+        }
+    }
+
+	public void addServiceListener(ServiceListener listener, String filter) {
+        FilterData data = getFilterData(null, filter);
+        if (data != null) {
+            Long sidObject = Long.valueOf(data.m_serviceId);
+            synchronized (m_sidToListenersMap) {
+            	List<ServiceListener> listeners = m_sidToListenersMap.get(sidObject);
+            	if (listeners == null) {
+            		listeners = new ArrayList<>();
+            		m_sidToListenersMap.put(sidObject, listeners);
+            	}
+            	listeners.add(listener);
+            	m_listenerToFilterMap.put(listener, filter);
+        		Matcher matcher = PATTERN.matcher(filter);
+        		if (matcher.matches()) {
+        			String objectClass = matcher.group(1);
+        			m_listenerToObjectClassMap.put(listener, objectClass);
+        		} else {
+        			throw new IllegalArgumentException("Filter string does not match index pattern");
+        		}
+
+            }
+        }
+    }
+
+	public void removeServiceListener(ServiceListener listener) {
+        synchronized (m_sidToListenersMap) {
+        	m_listenerToObjectClassMap.remove(listener);
+            String filter = (String) m_listenerToFilterMap.remove(listener);
+            if (filter != null) {
+            	// the listener does exist
+            	FilterData data = getFilterData(null, filter);
+            	if (data != null) {
+            		Long sidObject = Long.valueOf(data.m_serviceId);
+            		List<ServiceListener> listeners = m_sidToListenersMap.get(sidObject);
+            		if (listeners != null) {
+            			listeners.remove(listener);
+            		}
+            	}
+            }
+        }
+    }
+
+    public Object addingService(ServiceReference reference) {
+        BundleContext context;
+        synchronized (m_lock) {
+            context = m_context;
+        }
+        if (context != null) {
+            return context.getService(reference);
+        }
+        else {
+            throw new IllegalStateException("No valid bundle context.");
+        }
+    }
+
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("AdapterFilterIndex[");
+        sb.append("S2L: " + m_sidToListenersMap.size());
+        sb.append(", S2SR: " + m_sidToServiceReferencesMap.size());
+        sb.append(", L2F: " + m_listenerToFilterMap.size());
+        sb.append("]");
+        return sb.toString();
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/AspectFilterIndex.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/AspectFilterIndex.java
new file mode 100644
index 0000000..f14e884
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/AspectFilterIndex.java
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.index;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.FilterIndex;
+import org.apache.felix.dm.impl.ServiceUtil;
+import org.apache.felix.dm.tracker.ServiceTracker;
+import org.apache.felix.dm.tracker.ServiceTrackerCustomizer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectFilterIndex extends AbstractFactoryFilterIndex implements FilterIndex, ServiceTrackerCustomizer {
+	// (&(objectClass=foo.Bar)(|(!(service.ranking=*))(service.ranking<=99))(|(service.id=4451)(org.apache.felix.dependencymanager.aspect=4451)))
+    private static final String FILTER_START = "(&(" + Constants.OBJECTCLASS + "=";
+    private static final String FILTER_SUBSTRING_0 = ")(&(|(!(" + Constants.SERVICE_RANKING + "=*))(" + Constants.SERVICE_RANKING + "<=";
+    private static final String FILTER_SUBSTRING_1 = "))(|(" + Constants.SERVICE_ID + "=";
+    private static final String FILTER_SUBSTRING_2 = ")(" + DependencyManager.ASPECT + "=";
+    private static final String FILTER_END = "))))";
+    private final Object m_lock = new Object();
+    private ServiceTracker m_tracker;
+    private BundleContext m_context;
+    
+	private final Map<Long, Map<String, SortedMap<Integer, List<ServiceListener>>>> m_sidToObjectClassToRankingToListenersMap = new HashMap<>();
+
+    public void open(BundleContext context) {
+        synchronized (m_lock) {
+            if (m_context != null) {
+                throw new IllegalStateException("Filter already open.");
+            }
+            try {
+                m_tracker = new ServiceTracker(context, context.createFilter("(" + Constants.OBJECTCLASS + "=*)"), this);
+            }
+            catch (InvalidSyntaxException e) {
+                throw new Error();
+            }
+            m_context = context;
+        }
+        m_tracker.open(true, true);
+    }
+
+    public void close() {
+        ServiceTracker tracker;
+        synchronized (m_lock) {
+            if (m_context == null) {
+                throw new IllegalStateException("Filter already closed.");
+            }
+            tracker = m_tracker;
+            m_tracker = null;
+            m_context = null;
+        }
+        tracker.close();
+    }
+
+    public boolean isApplicable(String clazz, String filter) {
+        return getFilterData(clazz, filter) != null;
+    }
+
+    /** Returns a value object with the relevant filter data, or <code>null</code> if this filter was not valid. */
+    private FilterData getFilterData(String clazz, String filter) {
+        // something like:
+        // (&(objectClass=foo.Bar)(&(|(!(service.ranking=*))(service.ranking<=9))(|(service.id=37)(org.apache.felix.dependencymanager.aspect=37))))
+        if ((filter != null)
+            && (filter.startsWith(FILTER_START)) // (&(objectClass=
+            && (filter.endsWith(FILTER_END)) // ))))
+            ) {
+            int i0 = filter.indexOf(FILTER_SUBSTRING_0);
+            if (i0 == -1) {
+                return null;
+            }
+            int i1 = filter.indexOf(FILTER_SUBSTRING_1);
+            if (i1 == -1 || i1 <= i0) {
+                return null;
+            }
+            int i2 = filter.indexOf(FILTER_SUBSTRING_2);
+            if (i2 == -1 || i2 <= i1) {
+                return null;
+            }
+            long sid = Long.parseLong(filter.substring(i1 + FILTER_SUBSTRING_1.length(), i2));
+            long sid2 = Long.parseLong(filter.substring(i2 + FILTER_SUBSTRING_2.length(), filter.length() - FILTER_END.length()));
+            if (sid != sid2) {
+                return null;
+            }
+            FilterData result = new FilterData();
+            result.m_objectClass = filter.substring(FILTER_START.length(), i0);
+            result.m_serviceId = sid;
+            result.m_ranking = Integer.parseInt(filter.substring(i0 + FILTER_SUBSTRING_0.length(), i1));
+            return result;
+        }
+        return null;
+    }
+
+	public List<ServiceReference> getAllServiceReferences(String clazz, String filter) {
+        List<ServiceReference> result = new ArrayList<>();
+        FilterData data = getFilterData(clazz, filter);
+        if (data != null) {
+        	SortedSet<ServiceReference> list = null;
+        	synchronized (m_sidToServiceReferencesMap) {
+        		list = m_sidToServiceReferencesMap.get(Long.valueOf(data.m_serviceId));
+        		if (list != null) {
+        			Iterator<ServiceReference> iterator = list.iterator();
+        			while (iterator.hasNext()) {
+        				ServiceReference reference = (ServiceReference) iterator.next();
+        				if (referenceMatchesObjectClass(reference, data.m_objectClass) && ServiceUtil.getRanking(reference) <= data.m_ranking) {
+        					result.add(reference);
+        				}
+        			}
+        		}
+			}
+        }
+        return result;
+    }
+
+	public void serviceChanged(ServiceEvent event) {
+        List<ServiceListener> list = new ArrayList<>();
+        ServiceReference reference = event.getServiceReference();
+        Long sidObject = ServiceUtil.getServiceIdObject(reference);
+        int ranking = ServiceUtil.getRanking(reference);
+        String[] objectClasses = (String[]) reference.getProperty(Constants.OBJECTCLASS);
+        
+        synchronized (m_sidToObjectClassToRankingToListenersMap) {
+        	for (int i = 0; i < objectClasses.length; i++) {
+        		// handle each of the object classes separately since aspects only work on one object class at a time
+        		String objectClass = objectClasses[i];
+        		Map<String, SortedMap<Integer, List<ServiceListener>>> objectClassToRankingToListenersMap = m_sidToObjectClassToRankingToListenersMap.get(sidObject);
+        		if (objectClassToRankingToListenersMap != null) {
+        			SortedMap<Integer, List<ServiceListener>> rankingToListenersMap = objectClassToRankingToListenersMap.get(objectClass);
+        			if (rankingToListenersMap != null) {
+        				Iterator<Entry<Integer, List<ServiceListener>>> iterator = rankingToListenersMap.entrySet().iterator();
+        				while (iterator.hasNext()) {
+        					Entry<Integer, List<ServiceListener>> entry = iterator.next();
+        					if (ranking <= ((Integer) entry.getKey()).intValue()) {
+        						list.addAll(entry.getValue());
+        					}
+        				}
+        			}
+        		}
+        	}
+		}
+        Iterator<ServiceListener> iterator = list.iterator();
+        while (iterator.hasNext()) {
+            ServiceListener listener = iterator.next();
+            listener.serviceChanged(event);
+        }
+    }
+
+	public void addServiceListener(ServiceListener listener, String filter) {
+        FilterData data = getFilterData(null, filter);
+        if (data != null) {
+            Long sidObject = Long.valueOf(data.m_serviceId);
+            synchronized (m_sidToObjectClassToRankingToListenersMap) {
+            	Map<String, SortedMap<Integer, List<ServiceListener>>> objectClassToRankingToListenersMap = m_sidToObjectClassToRankingToListenersMap.get(sidObject);
+            	if (objectClassToRankingToListenersMap == null) {
+            		objectClassToRankingToListenersMap = new TreeMap<>();
+            		m_sidToObjectClassToRankingToListenersMap.put(sidObject, objectClassToRankingToListenersMap);
+            	}
+            	
+            	SortedMap<Integer, List<ServiceListener>> rankingToListenersMap = objectClassToRankingToListenersMap.get(data.m_objectClass);
+                if (rankingToListenersMap == null) {
+                    rankingToListenersMap = new TreeMap<>();
+                    objectClassToRankingToListenersMap.put(data.m_objectClass, rankingToListenersMap);
+                }            	
+            	
+            	List<ServiceListener> listeners = rankingToListenersMap.get(Integer.valueOf(data.m_ranking));
+            	if (listeners == null) {
+            		listeners = new ArrayList<>();
+            		rankingToListenersMap.put(Integer.valueOf(data.m_ranking), listeners);
+            	}
+            	
+            	listeners.add(listener);
+                m_listenerToFilterMap.put(listener, filter);
+            }
+        }
+    }
+
+	public void removeServiceListener(ServiceListener listener) {
+    	synchronized (m_sidToObjectClassToRankingToListenersMap) {
+    		String filter = (String) m_listenerToFilterMap.remove(listener);
+    		if (filter != null) {
+    			// the listener does exist
+    			FilterData data = getFilterData(null, filter);
+    			if (data != null) {
+    				// this index is applicable
+    				Long sidObject = Long.valueOf(data.m_serviceId);
+                	Map<String, SortedMap<Integer, List<ServiceListener>>> objectClassToRankingToListenersMap = m_sidToObjectClassToRankingToListenersMap.get(sidObject);
+                	if (objectClassToRankingToListenersMap != null) {
+                		SortedMap<Integer, List<ServiceListener>> rankingToListenersMap = objectClassToRankingToListenersMap.get(data.m_objectClass);
+                		if (rankingToListenersMap != null) {
+                			List<ServiceListener> listeners = rankingToListenersMap.get(Integer.valueOf(data.m_ranking));
+                			if (listeners != null) {
+                				listeners.remove(listener);
+                			}
+                			// cleanup 
+                			if (listeners != null && listeners.isEmpty()) {
+                				rankingToListenersMap.remove(Integer.valueOf(data.m_ranking));
+                			}
+                			if (rankingToListenersMap.isEmpty()) {
+                				objectClassToRankingToListenersMap.remove(data.m_objectClass);
+                			}
+                			if (objectClassToRankingToListenersMap.isEmpty()) {
+                				m_sidToObjectClassToRankingToListenersMap.remove(sidObject);
+                			}
+                		}
+                	}
+    			}
+    		}
+		}
+    }
+
+    public Object addingService(ServiceReference reference) {
+        BundleContext context;
+        synchronized (m_lock) {
+            context = m_context;
+        }
+        if (context != null) {
+            return context.getService(reference);
+        }
+        else {
+            throw new IllegalStateException("No valid bundle context.");
+        }
+    }
+    
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("AspectFilterIndex[");
+        sb.append("S2R2L: " + m_sidToObjectClassToRankingToListenersMap.size());
+        sb.append(", S2SR: " + m_sidToServiceReferencesMap.size());
+        sb.append(", L2F: " + m_listenerToFilterMap.size());
+        sb.append("]");
+        return sb.toString();
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/BundleContextInterceptor.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/BundleContextInterceptor.java
new file mode 100644
index 0000000..11cf401
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/BundleContextInterceptor.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.index;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.felix.dm.FilterIndex;
+import org.apache.felix.dm.Logger;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class BundleContextInterceptor extends BundleContextInterceptorBase {
+	protected static final String INDEX_LOG_TRESHOLD = "org.apache.felix.dm.index.log.treshold";
+    private final ServiceRegistryCache m_cache;
+    private final boolean m_perfmon;
+	private Logger m_logger;
+	private long m_threshold;
+
+    public BundleContextInterceptor(ServiceRegistryCache cache, BundleContext context) {
+        super(context);
+        m_cache = cache;
+        m_perfmon = context.getProperty(INDEX_LOG_TRESHOLD) != null;
+		if (m_perfmon) {
+			m_threshold = Long.parseLong(context.getProperty(INDEX_LOG_TRESHOLD));
+			m_logger = new Logger(context);
+		}
+    }
+
+    public void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException {
+        FilterIndex filterIndex = m_cache.hasFilterIndexFor(null, filter);
+        if (filterIndex != null) {
+            filterIndex.addServiceListener(listener, filter);
+        }
+        else {
+            m_context.addServiceListener(listener, filter);
+        }
+    }
+
+    public void addServiceListener(ServiceListener listener) {
+        FilterIndex filterIndex = m_cache.hasFilterIndexFor(null, null);
+        if (filterIndex != null) {
+            filterIndex.addServiceListener(listener, null);
+        }
+        else {
+            m_context.addServiceListener(listener);
+        }
+    }
+
+	public void removeServiceListener(ServiceListener listener) {
+    	// remove servicelistener. although it would be prettier to find the correct filterindex first it's
+    	// probaby faster to do a brute force removal.
+    	Iterator<FilterIndex> filterIndexIterator = m_cache.getFilterIndices().iterator();
+    	while (filterIndexIterator.hasNext()) {
+    		filterIndexIterator.next().removeServiceListener(listener);
+    	}
+    	m_context.removeServiceListener(listener);
+    }
+
+	public ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+    	long start = 0L;
+    	if (m_perfmon) {
+    		start = System.currentTimeMillis();
+    	}
+        // first we ask the cache if there is an index for our request (class and filter combination)
+        FilterIndex filterIndex = m_cache.hasFilterIndexFor(clazz, filter);
+        if (filterIndex != null) {
+            List<ServiceReference> result = filterIndex.getAllServiceReferences(clazz, filter);
+            Iterator<ServiceReference> iterator = result.iterator();
+            while (iterator.hasNext()) {
+                ServiceReference reference = iterator.next();
+                String[] list = (String[]) reference.getProperty(Constants.OBJECTCLASS);
+                for (int i = 0; i < list.length; i++) {
+                    if (!reference.isAssignableTo(m_context.getBundle(), list[i])) {
+                        iterator.remove();
+                        break;
+                    }
+                }
+            }
+            if (m_perfmon) {
+	        	long duration = System.currentTimeMillis() - start;
+	        	if (duration > m_threshold) {
+	        		m_logger.log(Logger.LOG_DEBUG, "Indexed filter exceeds lookup time threshold (" + duration + " ms): " + clazz + " " + filter);
+	        	}
+            }
+            if (result.size() == 0) {
+                return null;
+            }
+            return (ServiceReference[]) result.toArray(new ServiceReference[result.size()]);
+        }
+        else {
+            // if they don't know, we ask the real bundle context instead
+            ServiceReference[] serviceReferences = m_context.getServiceReferences(clazz, filter);
+            if (m_perfmon) {
+	        	long duration = System.currentTimeMillis() - start;
+	        	if (duration > m_threshold) {
+	        		m_logger.log(Logger.LOG_DEBUG, "Unindexed filter exceeds lookup time threshold (" + duration + " ms): " + clazz + " " + filter);
+	        	}
+            }
+        	return serviceReferences;
+        }
+    }
+
+	public ServiceReference[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+        // first we ask the cache if there is an index for our request (class and filter combination)
+        FilterIndex filterIndex = m_cache.hasFilterIndexFor(clazz, filter);
+        if (filterIndex != null) {
+            List<ServiceReference> result = filterIndex.getAllServiceReferences(clazz, filter);
+            if (result == null || result.size() == 0) {
+                return null;
+            }
+            return (ServiceReference[]) result.toArray(new ServiceReference[result.size()]);
+        }
+        else {
+            // if they don't know, we ask the real bundle context instead
+            return m_context.getAllServiceReferences(clazz, filter);
+        }
+    }
+
+    public ServiceReference getServiceReference(String clazz) {
+        ServiceReference[] references;
+        try {
+            references = getServiceReferences(clazz, null);
+            if (references == null || references.length == 0) {
+                return null;
+            }
+            Arrays.sort(references);
+            return references[references.length - 1];
+        }
+        catch (InvalidSyntaxException e) {
+            throw new Error("Invalid filter syntax thrown for null filter.", e);
+        }
+    }
+
+    public void serviceChanged(ServiceEvent event) {
+        m_cache.serviceChangedForFilterIndices(event);
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/BundleContextInterceptorBase.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/BundleContextInterceptorBase.java
new file mode 100644
index 0000000..498fd16
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/BundleContextInterceptorBase.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.index;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Base class for bundle context interceptors that keep track of service listeners and delegate incoming changes to them.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class BundleContextInterceptorBase implements BundleContext, ServiceListener {
+    protected final BundleContext m_context;
+    /** Keeps track of all service listeners and their optional filters. */
+	private final Map<ServiceListener, String> m_serviceListenerFilterMap = new HashMap<>();
+    private long m_currentVersion = 0;
+    private long m_entryVersion = -1;
+	private Entry<ServiceListener, String>[] m_serviceListenerFilterMapEntries;
+
+    public BundleContextInterceptorBase(BundleContext context) {
+        m_context = context;
+    }
+
+    public String getProperty(String key) {
+        return m_context.getProperty(key);
+    }
+
+    public Bundle getBundle() {
+        return m_context.getBundle();
+    }
+
+    public Bundle installBundle(String location) throws BundleException {
+        return m_context.installBundle(location);
+    }
+
+    public Bundle installBundle(String location, InputStream input) throws BundleException {
+        return m_context.installBundle(location, input);
+    }
+
+    public Bundle getBundle(long id) {
+        return m_context.getBundle(id);
+    }
+
+    public Bundle[] getBundles() {
+        return m_context.getBundles();
+    }
+
+	public void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException {
+        synchronized (m_serviceListenerFilterMap) {
+            m_serviceListenerFilterMap.put(listener, filter);
+            m_currentVersion++;
+        }
+    }
+
+	public void addServiceListener(ServiceListener listener) {
+        synchronized (m_serviceListenerFilterMap) {
+            m_serviceListenerFilterMap.put(listener, null);
+            m_currentVersion++;
+        }
+    }
+
+    public void removeServiceListener(ServiceListener listener) {
+        synchronized (m_serviceListenerFilterMap) {
+            m_serviceListenerFilterMap.remove(listener);
+            m_currentVersion++;
+        }
+    }
+
+    public void addBundleListener(BundleListener listener) {
+        m_context.addBundleListener(listener);
+    }
+
+    public void removeBundleListener(BundleListener listener) {
+        m_context.removeBundleListener(listener);
+    }
+
+    public void addFrameworkListener(FrameworkListener listener) {
+        m_context.addFrameworkListener(listener);
+    }
+
+    public void removeFrameworkListener(FrameworkListener listener) {
+        m_context.removeFrameworkListener(listener);
+    }
+
+    public ServiceRegistration registerService(String[] clazzes, Object service, @SuppressWarnings("rawtypes") Dictionary properties) {
+        return m_context.registerService(clazzes, service, properties);
+    }
+
+    public ServiceRegistration registerService(String clazz, Object service, @SuppressWarnings("rawtypes") Dictionary properties) {
+        return m_context.registerService(clazz, service, properties);
+    }
+
+    public ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+        return m_context.getServiceReferences(clazz, filter);
+    }
+
+    public ServiceReference[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+        return m_context.getAllServiceReferences(clazz, filter);
+    }
+
+    public ServiceReference getServiceReference(String clazz) {
+        return m_context.getServiceReference(clazz);
+    }
+
+    public Object getService(ServiceReference reference) {
+        return m_context.getService(reference);
+    }
+
+    public boolean ungetService(ServiceReference reference) {
+        return m_context.ungetService(reference);
+    }
+
+    public File getDataFile(String filename) {
+        return m_context.getDataFile(filename);
+    }
+
+    public Filter createFilter(String filter) throws InvalidSyntaxException {
+        return m_context.createFilter(filter);
+    }
+
+	@SuppressWarnings("unchecked")
+	protected Entry<ServiceListener, String>[] synchronizeCollection() {
+        // lazy copy on write: we make a new copy only if writes have changed the collection
+        synchronized (m_serviceListenerFilterMap) {
+            if (m_currentVersion != m_entryVersion) {
+                m_serviceListenerFilterMapEntries = (Entry<ServiceListener, String>[]) m_serviceListenerFilterMap.entrySet().toArray(new Entry[m_serviceListenerFilterMap.size()]);
+                m_entryVersion = m_currentVersion;
+            }
+        }
+        return m_serviceListenerFilterMapEntries;
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/FilterIndexBundleContext.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/FilterIndexBundleContext.java
new file mode 100644
index 0000000..9a6a266
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/FilterIndexBundleContext.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.index;
+
+import java.util.Map.Entry;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FilterIndexBundleContext extends BundleContextInterceptorBase {
+    public FilterIndexBundleContext(BundleContext context) {
+        super(context);
+    }
+
+	public void serviceChanged(ServiceEvent event) {
+        Entry<ServiceListener, String>[] entries = synchronizeCollection();
+        for (int i = 0; i < entries.length; i++) {
+            Entry<ServiceListener, String> serviceListenerFilterEntry = entries[i];
+            ServiceListener serviceListener = serviceListenerFilterEntry.getKey();
+            String filter = serviceListenerFilterEntry.getValue();
+            if (filter == null) {
+                serviceListener.serviceChanged(event);
+            }
+            else {
+                // call service changed on the listener if the filter matches the event
+                // TODO review if we can be smarter here
+                try {
+                    if ("(objectClass=*)".equals(filter)) {
+                        serviceListener.serviceChanged(event);
+                    }
+                    else {
+                        if (m_context.createFilter(filter).match(event.getServiceReference())) {
+                            serviceListener.serviceChanged(event);
+                        }
+                    }
+                }
+                catch (InvalidSyntaxException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/ServiceRegistryCache.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/ServiceRegistryCache.java
new file mode 100644
index 0000000..8848e11
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/ServiceRegistryCache.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.index;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.felix.dm.FilterIndex;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceRegistryCache implements ServiceListener/*, CommandProvider*/ {
+	private final List<FilterIndex> m_filterIndexList = new CopyOnWriteArrayList<>();
+    private final BundleContext m_context;
+    private final FilterIndexBundleContext m_filterIndexBundleContext;
+	private final Map<BundleContext, BundleContextInterceptor> m_bundleContextInterceptorMap = new HashMap<>();
+    private long m_currentVersion = 0;
+    private long m_arrayVersion = -1;
+    
+    public ServiceRegistryCache(BundleContext context) {
+        m_context = context;
+        m_filterIndexBundleContext = new FilterIndexBundleContext(m_context);
+    }
+    
+    public void open() {
+        m_context.addServiceListener(this);
+    }
+    
+    public void close() {
+        m_context.removeServiceListener(this);
+    }
+    
+    public void addFilterIndex(FilterIndex index) {
+        m_filterIndexList.add(index);
+        index.open(m_filterIndexBundleContext);
+    }
+    
+    public void removeFilterIndex(FilterIndex index) {
+        index.close();
+        m_filterIndexList.remove(index);
+    }
+
+    public void serviceChanged(ServiceEvent event) {
+        // any incoming event is first dispatched to the list of filter indices
+        m_filterIndexBundleContext.serviceChanged(event);
+        // and then all the other listeners can access it
+        synchronized (m_bundleContextInterceptorMap) {
+            if (m_currentVersion != m_arrayVersion) {
+                // if our copy is out of date, we make a new one
+                m_arrayVersion = m_currentVersion;
+            }
+        }
+        
+        serviceChangedForFilterIndices(event);
+    }
+    
+    /** Creates an interceptor for a bundle context that uses our cache. */
+    public BundleContext createBundleContextInterceptor(BundleContext context) {
+        synchronized (m_bundleContextInterceptorMap) {
+            BundleContextInterceptor bundleContextInterceptor = m_bundleContextInterceptorMap.get(context);
+            if (bundleContextInterceptor == null) {
+                bundleContextInterceptor = new BundleContextInterceptor(this, context);
+                m_bundleContextInterceptorMap.put(context, bundleContextInterceptor);
+                m_currentVersion++;
+                // TODO figure out a good way to clean up bundle contexts that are no longer valid so they can be garbage collected
+            }
+            return bundleContextInterceptor;
+        }
+    }
+
+    public FilterIndex hasFilterIndexFor(String clazz, String filter) {
+        Iterator<FilterIndex> iterator = m_filterIndexList.iterator();
+        while (iterator.hasNext()) {
+            FilterIndex filterIndex = iterator.next();
+            if (filterIndex.isApplicable(clazz, filter)) {
+                return filterIndex;
+            }
+        }
+        return null;
+    }
+
+    public void serviceChangedForFilterIndices(ServiceEvent event) {
+        Iterator<FilterIndex> iterator = m_filterIndexList.iterator();
+        while (iterator.hasNext()) {
+            FilterIndex filterIndex = iterator.next();
+            filterIndex.serviceChanged(event);
+        }
+    }
+    
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("ServiceRegistryCache[");
+        sb.append("FilterIndices: " + m_filterIndexList.size());
+        sb.append(", BundleContexts intercepted: " + m_bundleContextInterceptorMap.size());
+        sb.append("]");
+        return sb.toString();
+    }
+
+	public List<FilterIndex> getFilterIndices() {
+		return m_filterIndexList;
+	}
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/Filter.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/Filter.java
new file mode 100644
index 0000000..602b4cc
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/Filter.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.index.multiproperty;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Filter {
+	private boolean m_valid = true;
+	private Map<String, Property> m_properties = new HashMap<>();
+	private Set<String> m_propertyKeys = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
+	
+	private Filter() {
+	}
+	
+	// Sample valid filter string (&(objectClass=OBJECTCLASS)(&(model=MODEL)(concept=CONCEPT)(role=ROLE)(!(context=*))))
+	public static Filter parse(String filterString) {
+		Filter filter = new Filter();
+		StringTokenizer tokenizer = new StringTokenizer(filterString, "(&|=)", true);
+		
+		String token = null;
+		String prevToken = null;
+		String key = null;
+		StringBuilder valueBuilder = new StringBuilder();
+		boolean negate = false;
+
+		while (tokenizer.hasMoreTokens()) {
+			prevToken = token;
+			token = tokenizer.nextToken();
+			if (token.equals("|")) {
+				// we're not into OR's
+				filter.m_valid = false;
+				break;
+			}
+			if (token.equals("!")) {
+				negate = true;
+			} else if (token.equals("=")) {
+				key = prevToken.toLowerCase();
+			} else if (key != null) {
+				if (!token.equals(")")) {
+					valueBuilder.append(token); // might be superseded by a &
+				}
+				if (token.equals(")")) {
+					// set complete
+					if (filter.m_properties.containsKey(key)) {
+						// set current property to multivalue
+						Property property = filter.m_properties.get(key);
+						property.addValue(valueBuilder.toString(), negate);
+					} else {
+						Property property = new Property(negate, key, valueBuilder.toString());
+						filter.m_properties.put(key, property);
+						filter.m_propertyKeys.add(key);
+					}
+					negate = false;
+					key = null;
+					valueBuilder = new StringBuilder();
+				}
+			} 
+		}
+		return filter;
+	}
+	
+	public boolean containsProperty(String propertyKey) {
+		return m_properties.containsKey(propertyKey);
+	}
+	
+	public Set<String> getPropertyKeys() {
+		return m_properties.keySet();
+	}
+	
+	public Property getProperty(String key) {
+		return m_properties.get(key);
+	}
+	
+	public boolean isValid() {
+		if (!m_valid) {
+			return m_valid;
+		} else {
+			// also check the properties
+			Iterator<Property> propertiesIterator = m_properties.values().iterator();
+			while (propertiesIterator.hasNext()) {
+				Property property = propertiesIterator.next();
+				if (!property.isValid()) {
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+	
+	public static void main(String args[]) {
+		Filter parser = Filter.parse("(&(objectClass=OBJECTCLASS)(&(a=x)(a=n)(a=y)(b=y)(c=z)))");
+		System.out.println("key: " + parser.createKey());
+	}
+
+	protected String createKey() {
+		StringBuilder builder = new StringBuilder();
+		Iterator<String> keys = m_propertyKeys.iterator();
+		
+		while (keys.hasNext()) {
+			String key = keys.next();
+			Property prop = m_properties.get(key);
+			if (!prop.isWildcard()) {
+				Iterator<String> values = prop.getValues().iterator();
+				while (values.hasNext()) {
+					String value = values.next();
+					builder.append(key);
+					builder.append("=");
+					builder.append(value);
+					if (keys.hasNext() || values.hasNext()) {
+						builder.append(";");
+					}
+				}
+			}
+		}
+		// strip the final ';' in case the last key was a wildcard property
+		if (builder.charAt(builder.length() - 1) == ';') {
+			return builder.toString().substring(0, builder.length() - 1);
+		} else {
+			return builder.toString();
+		}
+	}
+	
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/MultiPropertyFilterIndex.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/MultiPropertyFilterIndex.java
new file mode 100644
index 0000000..42cb5b7
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/MultiPropertyFilterIndex.java
@@ -0,0 +1,492 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.index.multiproperty;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.felix.dm.FilterIndex;
+import org.apache.felix.dm.tracker.ServiceTracker;
+import org.apache.felix.dm.tracker.ServiceTrackerCustomizer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class MultiPropertyFilterIndex implements FilterIndex, ServiceTrackerCustomizer {
+
+    private final Object m_lock = new Object();
+    private ServiceTracker m_tracker;
+    private BundleContext m_context;
+	private Map<String, Property> m_configProperties = new LinkedHashMap<>();
+	private List<String> m_negatePropertyKeys = new ArrayList<>();
+    private final Map<String, List<ServiceReference>> m_keyToServiceReferencesMap = new HashMap<>();
+    private final Map<String, List<ServiceListener>> m_keyToListenersMap = new HashMap<>();
+    private final Map<ServiceListener, String> m_listenerToFilterMap = new HashMap<>();
+
+	public MultiPropertyFilterIndex(String configString) {
+		parseConfig(configString);
+	}
+	
+	public boolean isApplicable(String clazz, String filterString) {
+		Filter filter = createFilter(clazz, filterString);
+		
+		if (!filter.isValid()) {
+			return false;
+		}
+		// compare property keys to the ones in the configuration
+		Set<String> filterPropertyKeys = filter.getPropertyKeys();
+		if (m_configProperties.size() != filterPropertyKeys.size()) {
+			return false;
+		}
+		Iterator<String> filterPropertyKeysIterator = filterPropertyKeys.iterator();
+		while (filterPropertyKeysIterator.hasNext()) {
+			String filterPropertyKey = filterPropertyKeysIterator.next();
+			if (!m_configProperties.containsKey(filterPropertyKey)) {
+				return false;
+			} else if ((m_configProperties.get(filterPropertyKey)).isNegate() != filter.getProperty(filterPropertyKey).isNegate()) {
+				// negation should be equal
+				return false;
+			} else if (!filter.getProperty(filterPropertyKey).isNegate() && filter.getProperty(filterPropertyKey).getValue().equals("*")) {
+				// no wildcards without negation allowed
+				return false;
+			} 
+		}
+		// our properties match so we're applicable
+		return true;
+	}
+	
+    public boolean isApplicable(ServiceReference ref) {
+    	String[] propertyKeys = ref.getPropertyKeys();
+        TreeSet<String> referenceProperties = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
+        for (int i = 0; i < propertyKeys.length; i++) {
+            referenceProperties.add(propertyKeys[i]);
+        }
+        Iterator<String> iterator = m_configProperties.keySet().iterator();
+        while (iterator.hasNext()) {
+            String item = iterator.next();
+            Property configProperty = m_configProperties.get(item);
+            if (!configProperty.isNegate() && !(referenceProperties.contains(item))) {
+                return false;
+            } else if (configProperty.isNegate() && referenceProperties.contains(item)) {
+            	return false;
+            }
+        }
+        return true;
+    }
+	
+	private void parseConfig(String configString) {
+		String[] propertyConfigs = configString.split(",");
+		for (int i = 0; i < propertyConfigs.length; i++) {
+			String propertyConfig = propertyConfigs[i];
+			Property property = new Property();
+			String key;
+			String value = null;
+			if (propertyConfig.startsWith("!")) {
+				property.setNegate(true);
+				key = propertyConfig.substring(1);
+			} else {
+				key = propertyConfig;
+			}
+			if (key.endsWith("*")) {
+				key = key.substring(0, key.indexOf("*"));
+				value = "*";
+			}
+			property.setKey(key.toLowerCase());
+			property.addValue(value, property.isNegate());
+			m_configProperties.put(key.toLowerCase(), property);
+			if (property.isNegate()) {
+				m_negatePropertyKeys.add(key);
+			}
+		}
+	}
+	
+	protected Collection<Property> getProperties() {
+		return m_configProperties.values();
+	}
+	
+    protected String createKeyFromFilter(String clazz, String filterString) {
+    	return createFilter(clazz, filterString).createKey();
+    }
+    
+    private Filter createFilter(String clazz, String filterString) {
+		String filterStringWithObjectClass = filterString;
+		if (clazz != null) {
+			if (filterString != null) {
+				if (!filterStringWithObjectClass.startsWith("(&(objectClass=")) {
+					filterStringWithObjectClass = "(&(objectClass=" + clazz + ")" + filterString + ")";
+				}
+			} else {
+				filterStringWithObjectClass = "(objectClass=" + clazz + ")";
+			}
+		}
+		Filter filter = Filter.parse(filterStringWithObjectClass);
+		return filter;
+    }
+    
+    protected List<String> createKeys(ServiceReference reference) {
+    	List<String> results = new ArrayList<>();
+    	List<List<String>> sets = new ArrayList<>();   	
+    	String[] keys = reference.getPropertyKeys();
+    	Arrays.sort(keys, String.CASE_INSENSITIVE_ORDER);
+    	for (int i = 0; i < keys.length; i++) {
+    		List<String> set = new ArrayList<>();
+    		String key = keys[i].toLowerCase();
+    		if (m_configProperties.containsKey(key)) {
+	    		Object valueObject = reference.getProperty(key);
+	    		if (valueObject instanceof String[]) {
+	    			set.addAll(getPermutations(key, (String[]) valueObject));
+	    		} else {
+	    			set.add(toKey(key, valueObject));
+	    		}
+	    		sets.add(set);
+    		}
+    	}
+    	
+    	List<List<String>> reversedSets = new ArrayList<>();
+    	int size = sets.size();
+    	for (int i = size - 1; i > -1; i--) {
+    		reversedSets.add(sets.get(i));
+    	}
+    	List<List<String>> products = carthesianProduct(0, reversedSets);
+    	// convert sets into strings
+    	for (int i = 0; i < products.size(); i++) {
+    		List<String> set = products.get(i);
+    		StringBuilder b = new StringBuilder();
+    		for (int j = 0; j < set.size(); j++) {
+    			String item = set.get(j);
+    			b.append(item);
+    			if (j < set.size() - 1) {
+    				b.append(";");
+    			}
+    		}
+    		results.add(b.toString());
+    	}
+    	
+    	return results;
+    }
+    
+    /**
+     * Note that we calculate the carthesian product for multi value properties. Use filters on these sparingly since memory
+     * consumption can get really high when multiple properties have a lot of values.
+     * 
+     * @param index
+     * @param sets
+     * @return
+     */
+    private List<List<String>> carthesianProduct(int index, List<List<String>> sets) {
+    	List<List<String>> result = new ArrayList<>();
+    	if (index == sets.size()) {
+    		result.add(new ArrayList<String>());
+    	} else {
+			List<String> set = sets.get(index);
+			for (int i = 0; i < set.size(); i++) {
+				String object = set.get(i);
+    			List<List<String>> pSets = carthesianProduct(index + 1, sets);
+    			for (int j = 0; j < pSets.size(); j++) {
+    				List<String> pSet = pSets.get(j);
+    				pSet.add(object);
+    				result.add(pSet);
+    			}
+    		}
+    	}
+    	return result;
+    }
+    
+    List<String> getPermutations(String key, String[] values) {
+    	List<String> results = new ArrayList<>();
+		Arrays.sort(values, String.CASE_INSENSITIVE_ORDER);
+		for (int v = 0; v < values.length; v++) {
+			String processValue = values[v];
+			List<String> items = new ArrayList<>();
+			items.add(processValue);
+			// per value get combinations
+			List<String> subItems = new ArrayList<>(items);
+			for (int w = v; w < values.length; w++) {
+				// make a copy of the current list
+				subItems = new ArrayList<>(subItems);
+				if (w != v) {
+					String value = values[w];
+					subItems.add(value);
+				}
+				results.add(toKey(key, subItems));
+			}
+		}
+		return results;
+    }
+    
+    protected String toKey(String key, List<String> values) {
+    	StringBuilder builder = new StringBuilder();
+    	for (int i = 0; i < values.size(); i++) {
+    		builder.append(toKey(key, values.get(i)));
+    		if (i < values.size() - 1) {
+    			builder.append(";");
+    		}
+    	}
+    	return builder.toString();
+    }
+    
+    protected String toKey(String key, Object value) {
+    	StringBuilder builder = new StringBuilder();
+    	builder.append(key);
+		builder.append("=");
+		builder.append(value.toString());
+		return builder.toString();
+    }
+    
+    public Object addingService(ServiceReference reference) {
+        BundleContext context;
+        synchronized (m_lock) {
+            context = m_context;
+        }
+        if (context != null) {
+            return context.getService(reference);
+        }
+        else {
+            throw new IllegalStateException("No valid bundle context.");
+        }
+    }
+
+    public void addedService(ServiceReference reference, Object service) {
+        if (isApplicable(reference) && shouldBeIndexed(reference)) {
+            handleServiceAdd(reference);
+        }
+    }
+
+    public void modifiedService(ServiceReference reference, Object service) {
+        if (isApplicable(reference)) {
+            handleServicePropertiesChange(reference);
+        }
+    }
+
+    public void removedService(ServiceReference reference, Object service) {
+        if (isApplicable(reference) && shouldBeIndexed(reference)) {
+            handleServiceRemove(reference);
+        }
+    }
+    
+    protected void handleServiceAdd(ServiceReference reference) {
+        List<String> keys = createKeys(reference);
+        synchronized (m_keyToServiceReferencesMap) {
+            for (int i = 0; i < keys.size(); i++) {
+                List<ServiceReference> references = m_keyToServiceReferencesMap.get(keys.get(i));
+                if (references == null) {
+                    references = new ArrayList<>();
+                    m_keyToServiceReferencesMap.put(keys.get(i), references);
+                }
+                references.add(reference);
+            }
+        }
+    }
+
+    protected void handleServicePropertiesChange(ServiceReference reference) {
+        
+        synchronized (m_keyToServiceReferencesMap) {
+            // TODO this is a quite expensive linear scan over the existing collection
+            // because we first need to remove any existing references and they can be
+            // all over the place :)
+            Iterator<List<ServiceReference>> iterator = m_keyToServiceReferencesMap.values().iterator();
+            while (iterator.hasNext()) {
+                List<ServiceReference> list = iterator.next();
+                if (list != null) {
+                    Iterator<ServiceReference> i2 = list.iterator();
+                    while (i2.hasNext()) {
+                        ServiceReference ref = i2.next();
+                        if (ref.equals(reference)) {
+                            i2.remove();
+                        }
+                    }
+                }
+            }
+            // only re-add the reference when it is still applicable for this filter index
+            if (shouldBeIndexed(reference)) {
+            	List<String> keys = createKeys(reference);
+	            for (int i = 0; i < keys.size(); i++) {
+	                List<ServiceReference> references = m_keyToServiceReferencesMap.get(keys.get(i));
+	                if (references == null) {
+	                    references = new ArrayList<>();
+	                    m_keyToServiceReferencesMap.put(keys.get(i), references);
+	                }
+	                references.add(reference);
+	            }
+            }
+        }
+    }
+
+    protected void handleServiceRemove(ServiceReference reference) {
+        List<String> keys = createKeys(reference);
+        synchronized (m_keyToServiceReferencesMap) {
+            for (int i = 0; i < keys.size(); i++) {
+                List<ServiceReference> references = m_keyToServiceReferencesMap.get(keys.get(i));
+                if (references != null) {
+                    references.remove(reference);
+                    if (references.isEmpty()) {
+                    	m_keyToServiceReferencesMap.remove(keys.get(i));
+                    }
+                }
+            }
+        }
+    }
+    
+    protected boolean shouldBeIndexed(ServiceReference reference) {
+    	// is already applicable, so we should only check whether there's a negate field in the filter which has a value in the reference
+    	Iterator<String> negatePropertyKeyIterator = m_negatePropertyKeys.iterator();
+    	while (negatePropertyKeyIterator.hasNext()) {
+    		String negatePropertyKey = negatePropertyKeyIterator.next();
+    		if (reference.getProperty(negatePropertyKey) != null) {
+    			return false;
+    		}
+    	}
+    	return true;
+    }
+
+    public void open(BundleContext context) {
+        synchronized (m_lock) {
+            if (m_context != null) {
+                throw new IllegalStateException("Filter already open.");
+            }
+            try {
+                m_tracker = new ServiceTracker(context, context.createFilter("(" + Constants.OBJECTCLASS + "=*)"), this);
+            }
+            catch (InvalidSyntaxException e) {
+                throw new Error();
+            }
+            m_context = context;
+        }
+        m_tracker.open(true, true);
+    }
+
+	public void close() {
+        ServiceTracker tracker;
+        synchronized (m_lock) {
+            if (m_context == null) {
+                throw new IllegalStateException("Filter already closed.");
+            }
+            tracker = m_tracker;
+            m_tracker = null;
+            m_context = null;
+        }
+        tracker.close();
+	}
+
+    public List<ServiceReference> getAllServiceReferences(String clazz, String filter) {
+        List<ServiceReference> result = new ArrayList<>();
+        String key = createKeyFromFilter(clazz, filter);
+        synchronized (m_keyToServiceReferencesMap) {
+            List<ServiceReference> references = m_keyToServiceReferencesMap.get(key);
+            if (references != null) {
+                result.addAll(references);
+            }
+        }
+        return result;
+    }
+
+    public void serviceChanged(ServiceEvent event) {
+        if (isApplicable(event.getServiceReference())) {
+            List<String> keys = createKeys(event.getServiceReference());
+            List<ServiceListener> list = new ArrayList<ServiceListener>();
+            synchronized (m_keyToListenersMap) {
+                for (int i = 0; i < keys.size(); i++) {
+                    String key = keys.get(i);
+                    List<ServiceListener> listeners = m_keyToListenersMap.get(key);
+                    if (listeners != null) {
+                        list.addAll(listeners);
+                    }
+                }
+            }
+            if (list != null) {
+                Iterator<ServiceListener> iterator = list.iterator();
+                while (iterator.hasNext()) {
+                    ServiceListener listener = iterator.next();
+                    listener.serviceChanged(event);
+                }
+            }
+        }
+    }
+
+    public void addServiceListener(ServiceListener listener, String filter) {
+        String key = createKeyFromFilter(null, filter);
+        synchronized (m_keyToListenersMap) {
+            List<ServiceListener> listeners = m_keyToListenersMap.get(key);
+            if (listeners == null) {
+                listeners = new CopyOnWriteArrayList<ServiceListener>();
+                m_keyToListenersMap.put(key, listeners);
+            }
+            listeners.add(listener);
+            m_listenerToFilterMap.put(listener, filter);
+        }
+    }
+
+    public void removeServiceListener(ServiceListener listener) {
+        synchronized (m_keyToListenersMap) {
+            String filter = m_listenerToFilterMap.remove(listener);
+            if (filter != null) {
+            	// the listener does exist
+        		String key = createKeyFromFilter(null, filter);
+        		
+        		boolean result = filter != null;
+        		if (result) {
+        			List<ServiceListener> listeners = m_keyToListenersMap.get(key);
+        			if (listeners != null) {
+        				listeners.remove(listener);
+        				if (listeners.isEmpty()) {
+        					m_keyToListenersMap.remove(key);
+        				}
+        			}
+        			// TODO actually, if listeners == null that would be strange....
+        		}
+            }
+        }
+    }
+    
+    protected Collection<ServiceListener> getServiceListeners() {
+    	return m_listenerToFilterMap.keySet();
+    }
+    
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append(" dMultiPropertyExactFilter[");
+        sb.append("K2L: " + m_keyToListenersMap.size());
+        sb.append(", K2SR: " + m_keyToServiceReferencesMap.size());
+        sb.append(", L2F: " + m_listenerToFilterMap.size());
+        sb.append("]");
+        return sb.toString();
+    }
+
+	@Override
+	public void swappedService(ServiceReference reference, Object service,
+			ServiceReference newReference, Object newService) {
+		addedService(newReference, newService);
+		removedService(reference, service);
+	}
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/Property.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/Property.java
new file mode 100644
index 0000000..deb31c4
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/Property.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.index.multiproperty;
+
+import java.util.Set;
+import java.util.TreeSet;
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+
+public class Property {
+	boolean m_negate;
+	boolean m_valid = true;
+	String m_key;
+	String m_value;
+	Set<String> m_values = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
+	
+	public Property() {
+	}
+	
+	public Property(boolean negate, String key, String value) {
+		super();
+		m_negate = negate;
+		m_key = key.toLowerCase();
+		m_values.add(value);
+		m_value = value;
+	}
+
+	public void setNegate(boolean negate) {
+		this.m_negate = negate;
+	}
+	
+	public void setKey(String key) {
+		this.m_key = key.toLowerCase();
+	}
+	
+	public void addValue(String value, boolean negate) {
+		if (this.m_negate != negate) {
+			// multiproperty with different negations, causes invalid configuration.
+			m_valid = false;
+		}
+		if (this.m_value == null) {
+			// value has not bee set yet
+			this.m_value = value;
+		}
+		if (value != null) {
+			m_values.add(value);
+		}
+	}
+	
+	public boolean isNegate() {
+		return m_negate;
+	}
+	
+	public String getKey() {
+		return m_key;
+	}
+	
+	public String getValue() {
+		return m_value;
+	}
+	
+	public Set<String> getValues() {
+		return m_values;
+	}
+	
+	public boolean isWildcard() {
+		return "*".equals(m_value);
+	}
+	
+	public boolean isMultiValue() {
+		return m_values.size() > 1;
+	}
+
+	public String toString() {
+		return "Property [negate=" + m_negate + ", key=" + m_key + ", values="
+				+ m_values + "]";
+	}
+	
+	public boolean isValid() {
+		return m_valid;
+	}
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/metatype/AttributeDefinitionImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/metatype/AttributeDefinitionImpl.java
new file mode 100644
index 0000000..3a72c95
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/metatype/AttributeDefinitionImpl.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.metatype;
+
+import org.osgi.service.metatype.AttributeDefinition;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AttributeDefinitionImpl implements AttributeDefinition {
+    private PropertyMetaDataImpl m_propertyMetaData;
+    private Resource m_resource;
+
+    public AttributeDefinitionImpl(PropertyMetaDataImpl propertyMetaData, Resource resource) {
+        m_propertyMetaData = propertyMetaData;
+        m_resource = resource;
+    }
+
+    public int getCardinality() {
+        return m_propertyMetaData.getCardinality();
+    }
+
+    public String[] getDefaultValue() {
+        return m_propertyMetaData.getDefaults();
+    }
+
+    public String getDescription() {
+        return m_resource.localize(m_propertyMetaData.getDescription());
+    }
+
+    public String getID() {
+        return m_propertyMetaData.getId();
+    }
+
+    public String getName() {
+        return m_resource.localize(m_propertyMetaData.getHeading());
+    }
+
+    public String[] getOptionLabels() {
+        String[] labels = m_propertyMetaData.getOptionLabels();
+        if (labels != null) {
+            for (int i = 0; i < labels.length; i++) {
+                labels[i] = m_resource.localize(labels[i]);
+            }
+        }
+        return labels;
+    }
+
+    public String[] getOptionValues() {
+        return m_propertyMetaData.getOptionValues();
+    }
+
+    public int getType() {
+        return m_propertyMetaData.getType();
+    }
+
+    public String validate(String value) {
+        return null;
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/metatype/MetaTypeProviderImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/metatype/MetaTypeProviderImpl.java
new file mode 100644
index 0000000..d3bf2e2
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/metatype/MetaTypeProviderImpl.java
@@ -0,0 +1,275 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.metatype;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+import org.apache.felix.dm.Logger;
+import org.apache.felix.dm.PropertyMetaData;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.cm.ManagedServiceFactory;
+import org.osgi.service.log.LogService;
+import org.osgi.service.metatype.MetaTypeProvider;
+import org.osgi.service.metatype.ObjectClassDefinition;
+
+/**
+ * When a ConfigurationDepdendency is configured with properties metadata, we provide
+ * a specific ManagedService which also implements the MetaTypeProvider interface. This interface
+ * allows the MetaTypeService to retrieve our properties metadata, which will then be handled by webconsole.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class MetaTypeProviderImpl implements MetaTypeProvider, ManagedService, ManagedServiceFactory {
+    private ManagedService m_managedServiceDelegate;
+    private ManagedServiceFactory m_managedServiceFactoryDelegate;
+    private ArrayList<PropertyMetaData> m_propertiesMetaData = new ArrayList<>();
+    private String m_description;
+    private String m_heading;
+    private String m_localization;
+    private HashMap<String, Properties> m_localesProperties = new HashMap<>();
+    private Logger m_logger;
+    private BundleContext m_bctx;
+    private String m_pid;
+
+    public MetaTypeProviderImpl(String pid, BundleContext ctx, Logger logger, ManagedService msDelegate, ManagedServiceFactory msfDelegate) {
+        m_pid = pid;
+        m_bctx = ctx;
+        m_logger = logger;
+        m_managedServiceDelegate = msDelegate;
+        m_managedServiceFactoryDelegate = msfDelegate;
+        // Set the default localization file base name (see core specification, in section Localization on page 68).
+        // By default, this file can be stored in OSGI-INF/l10n/bundle.properties (and corresponding localized version
+        // in OSGI-INF/l10n/bundle_en_GB_welsh.properties,  OSGI-INF/l10n/bundle_en_GB.properties, etc ...
+        // This default localization property file name can be overriden using the PropertyMetaData.setLocalization method.
+        m_localization = (String) m_bctx.getBundle().getHeaders().get(Constants.BUNDLE_LOCALIZATION);
+        if (m_localization == null) {
+            m_localization = Constants.BUNDLE_LOCALIZATION_DEFAULT_BASENAME;
+        }
+    }
+    
+    @SuppressWarnings("unchecked")
+    public MetaTypeProviderImpl(MetaTypeProviderImpl prototype, ManagedService msDelegate, ManagedServiceFactory msfDelegate) {
+        m_pid = prototype.m_pid;
+        m_bctx = prototype.m_bctx;
+        m_logger = prototype.m_logger;
+        m_localization = prototype.m_localization;
+        m_propertiesMetaData = prototype.m_propertiesMetaData != null ? (ArrayList<PropertyMetaData>) prototype.m_propertiesMetaData.clone() : null;
+        m_description = prototype.m_description;
+        m_heading = prototype.m_heading;
+        m_localization = prototype.m_localization;
+        m_localesProperties = prototype.m_localesProperties != null ? (HashMap<String, Properties>) prototype.m_localesProperties.clone() : null;
+        m_managedServiceDelegate = msDelegate;
+        m_managedServiceFactoryDelegate = msfDelegate;
+    }
+
+
+    /**
+     * Registers the metatype information of a given configuration property
+     * @param property
+     */
+    public void add(PropertyMetaData property) {
+        m_propertiesMetaData.add(property);
+    }
+
+    /**
+     * A human readable description of the PID this annotation is associated with. Example: "Configuration for the PrinterService bundle".
+     * @return A human readable description of the PID this annotation is associated with (may be localized)
+     */
+    public void setDescription(String description) {
+        m_description = description;
+    }
+
+    /**
+     * The label used to display the tab name (or section) where the properties are displayed. Example: "Printer Service".
+     * @return The label used to display the tab name where the properties are displayed (may be localized)
+     */
+    public void setName(String heading) {
+        m_heading = heading;
+    }
+
+    /**
+     * Points to the basename of the Properties file that can localize the Meta Type informations.
+     * By default, (e.g. <code>setLocalization("person")</code> would match person_du_NL.properties in the root bundle directory.
+     * The default localization base name for the properties is OSGI-INF/l10n/bundle, but can
+     * be overridden by the manifest Bundle-Localization header (see core specification, in section Localization on page 68).
+     */
+    public void setLocalization(String path) {
+        if (path.endsWith(".properties")) {
+            throw new IllegalArgumentException(
+                "path must point to the base name of the propertie file, "
+                + "excluding local suffixes. For example: "
+                + "foo/bar/person is valid and matches the property file \"foo/bar/person_bundle_en_GB_welsh.properties\"");
+        }
+        m_localization = path.startsWith("/") ? path.substring(1) : path;
+    }
+
+    // --------------- MetaTypeProvider interface -------------------------------------------------
+
+    /**
+     * Returns all the Locales our bundle is containing. For instance, if our bundle contains the following localization files:
+     * OSGI-INF/l10n/bundle_en_GB_welsh.properties and OSGI-INF/l10n/bundle_en_GB.properties, then this method will return
+     * "en_GB", "en_GB_welsh" ...
+     * @return the list of Locale supported by our bundle.
+     */
+    @SuppressWarnings("rawtypes")
+    public String[] getLocales() {
+        int lastSlash = m_localization.lastIndexOf("/");
+        String path = (lastSlash == -1) ? "/" : ("/" + m_localization.substring(0, lastSlash - 1));
+        String base = (lastSlash == -1) ? m_localization : m_localization.substring(lastSlash + 1);
+        Enumeration e = m_bctx.getBundle().findEntries(path,
+            base + "*.properties", false);
+        if (e == null) {
+            return null;
+        }
+        
+        TreeSet<String> set = new TreeSet<>();
+        while (e.hasMoreElements()) {
+            // We have found a locale property file in the form of "path/file[_language[_ country[_variation]].properties"
+            // And now, we have to get the "language[_country[_variation]]" part ...
+            URL url = (URL) e.nextElement();
+            String name = url.getPath();
+            name = name.substring(name.lastIndexOf("/") + 1);
+            int underscore = name.indexOf("_");
+            if (underscore != -1) {
+                name = name.substring(underscore + 1, name.length() - ".properties".length());
+            }
+            if (name.length() > 0) {
+                set.add(name);
+            }
+        }
+
+        String[] locales = (String[]) set.toArray(new String[set.size()]);
+        return locales.length == 0 ? null : locales;
+    }
+
+    /**
+     * Returns the ObjectClassDefinition for a given Pid/Locale.
+     */
+    public ObjectClassDefinition getObjectClassDefinition(String id, String locale) {
+        try {
+            // Check if the id matches our PID
+            if (!id.equals(m_pid)) {
+                m_logger.log(LogService.LOG_ERROR, "id " + id + " does not match pid " + m_pid);
+                return null;
+            }
+
+            Properties localeProperties = getLocaleProperties(locale);
+            return new ObjectClassDefinitionImpl(m_pid, m_heading,
+                m_description, m_propertiesMetaData, new Resource(localeProperties));
+        }
+
+        catch (Throwable t) {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                "Unexpected exception while geting ObjectClassDefinition for " + id + " (locale="
+                    + locale + ")", t);
+            return null;
+        }
+    }
+
+    /**
+     * We also implements the ManagedService and we just delegates the configuration handling to
+     * our associated ConfigurationDependency.
+     */
+    @SuppressWarnings("rawtypes")
+    public void updated(Dictionary properties) throws ConfigurationException {
+        m_managedServiceDelegate.updated(properties);
+    }
+
+    /**
+     * Gets the properties for a given Locale.
+     * @param locale
+     * @return
+     * @throws IOException
+     */
+    private synchronized Properties getLocaleProperties(String locale) throws IOException {
+        locale = locale == null ? Locale.getDefault().toString() : locale;
+        Properties properties = (Properties) m_localesProperties.get(locale);
+        if (properties == null) {
+            properties = new Properties();
+            URL url = m_bctx.getBundle().getEntry(m_localization + ".properties");
+            if (url != null) {
+                loadLocale(properties, url);
+            }
+
+            String path = m_localization;
+            StringTokenizer tok = new StringTokenizer(locale, "_");
+            while (tok.hasMoreTokens()) {
+                path += "_" + tok.nextToken();
+                url = m_bctx.getBundle().getEntry(path + ".properties");
+                if (url != null) {
+                    properties = new Properties(properties);
+                    loadLocale(properties, url);
+                }
+            }
+            m_localesProperties.put(locale, properties);
+        }
+        return properties;
+    }
+
+    /**
+     * Loads a Locale Properties file.
+     * @param properties
+     * @param url
+     * @throws IOException
+     */
+    private void loadLocale(Properties properties, URL url) throws IOException {
+        InputStream in = null;
+        try {
+            in = url.openStream();
+            properties.load(in);
+        }
+        finally {
+            if (in != null) {
+                try {
+                    in.close();
+                }
+                catch (IOException ignored) {
+                }
+            }
+        }
+    }
+
+    // ManagedServiceFactory implementation
+    public void deleted(String pid) {
+        m_managedServiceFactoryDelegate.deleted(pid);
+    }
+
+    public String getName() {
+        return m_pid;
+    }
+
+    @SuppressWarnings("rawtypes")
+    public void updated(String pid, Dictionary properties) throws ConfigurationException {
+        m_managedServiceFactoryDelegate.updated(pid, properties);
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/metatype/ObjectClassDefinitionImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/metatype/ObjectClassDefinitionImpl.java
new file mode 100644
index 0000000..bd0a980
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/metatype/ObjectClassDefinitionImpl.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.metatype;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.felix.dm.PropertyMetaData;
+import org.osgi.service.metatype.AttributeDefinition;
+import org.osgi.service.metatype.ObjectClassDefinition;
+
+/**
+ * ObjectClassDefinition implementation.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ObjectClassDefinitionImpl implements ObjectClassDefinition {
+    // Our OCD name (may be localized)
+    private String m_name;
+    
+    // Our OCD description (may be localized)
+    private String m_description;
+    
+    // Our OCD id
+    private String m_id;
+    
+    // The list of Properties MetaData objects (from DependencyManager API)
+    private final List<PropertyMetaData> m_propertiesMetaData;
+    
+    // The localized resource that can be used when localizing some parameters
+    private Resource m_resource;
+
+    public ObjectClassDefinitionImpl(String id, String name, String description, List<PropertyMetaData> propertiesMetaData, Resource resource) {
+        m_id = id;
+        m_name = name;
+        m_description = description;
+        m_propertiesMetaData = propertiesMetaData;
+        m_resource = resource;
+    }
+
+    // --------------------- ObjectClassDefinition ----------------------------------------
+
+    public AttributeDefinition[] getAttributeDefinitions(int filter) {
+        List<AttributeDefinition> attrs = new ArrayList<>();
+        for (int i = 0; i < m_propertiesMetaData.size(); i++) {
+            PropertyMetaDataImpl metaData = (PropertyMetaDataImpl) m_propertiesMetaData.get(i);
+            switch (filter) {
+                case ObjectClassDefinition.ALL:
+                    attrs.add(new AttributeDefinitionImpl(metaData, m_resource));
+                    break;
+                case ObjectClassDefinition.OPTIONAL:
+                    if (!metaData.isRequired()) {
+                        attrs.add(new AttributeDefinitionImpl(metaData, m_resource));
+                    }
+                    break;
+                case ObjectClassDefinition.REQUIRED:
+                    if (metaData.isRequired()) {
+                        attrs.add(new AttributeDefinitionImpl(metaData, m_resource));
+                    }
+                    break;
+                default:
+            }
+        }
+
+        AttributeDefinition[] array = new AttributeDefinitionImpl[attrs.size()];
+        return attrs.toArray(array);
+    }
+
+    public String getDescription() {
+        return m_resource.localize(m_description);
+    }
+
+    public String getID() {
+        return m_id;
+    }
+
+    public InputStream getIcon(int size) throws IOException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public String getName() {
+        return m_resource.localize(m_name);
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/metatype/PropertyMetaDataImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/metatype/PropertyMetaDataImpl.java
new file mode 100644
index 0000000..583b014
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/metatype/PropertyMetaDataImpl.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.metatype;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.dm.PropertyMetaData;
+import org.osgi.service.metatype.AttributeDefinition;
+
+/**
+ * DependencyManager PropertyMetaData Implementation. This class describes meta informations regarding
+ * one given configuration property.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class PropertyMetaDataImpl implements PropertyMetaData {
+    /**
+     * List of option labels (may be localized)
+     */
+    List<String> m_optionsLabels = new ArrayList<>();
+    
+    /**
+     * List of option values
+     */
+    List<String> m_optionsValues = new ArrayList<>();
+    
+    /**
+     * Property cardinality.
+     * @see {@link AttributeDefinition#getCardinality()}
+     */
+    private int m_cardinality;
+    
+    /**
+     * Valid default property values
+     */
+    private String[] m_defaults;
+    
+    /**
+     * Property description.
+     */
+    private String m_description;
+    
+    /**
+     * Property title.
+     */
+    private String m_heading;
+    
+    /**
+     * Property unique Id
+     */
+    private String m_id;
+    
+    /**
+     * Required flag.
+     */
+    private boolean m_required;
+    
+    /**
+     * Property Type.
+     * @see {@link AttributeDefinition#getType()}
+     */
+    private int m_type = AttributeDefinition.STRING;
+    
+    /**
+     * Mapping between java types and valid MetaType types.
+     * @see {@link AttributeDefinition#getType()}
+     */
+    @SuppressWarnings({ "unchecked", "serial", "rawtypes" })
+    private final static Map<Class<?>, Integer> m_typeMapping = new HashMap() {{
+        put(Boolean.class, new Integer(AttributeDefinition.BOOLEAN));
+        put(Byte.class, new Integer(AttributeDefinition.BYTE));
+        put(Character.class, new Integer(AttributeDefinition.CHARACTER));
+        put(Double.class, new Integer(AttributeDefinition.FLOAT));
+        put(Integer.class, new Integer(AttributeDefinition.INTEGER));
+        put(Long.class, new Integer(AttributeDefinition.LONG));
+        put(Short.class, new Integer(AttributeDefinition.SHORT));
+        put(String.class, new Integer(AttributeDefinition.STRING));
+    }};
+
+    public PropertyMetaData addOption(String optionLabel, String optionValue) {
+        m_optionsLabels.add(optionLabel);
+        m_optionsValues.add(optionValue);
+        return this;
+    }
+
+    public PropertyMetaData setCardinality(int cardinality) {
+        m_cardinality = cardinality;
+        return this;
+    }
+
+    public PropertyMetaData setDefaults(String[] defaults) {
+        m_defaults = defaults;
+        return this;
+    }
+
+    public PropertyMetaData setDescription(String description) {
+        m_description = description;
+        return this;
+    }
+
+    public PropertyMetaData setHeading(String heading) {
+        m_heading = heading;
+        return this;
+    }
+
+    public PropertyMetaData setId(String id) {
+        m_id = id;
+        return this;
+    }
+
+    public PropertyMetaData setRequired(boolean required) {
+        m_required = required;
+        return this;
+    }
+
+    public PropertyMetaData setType(Class<?> classType) {
+        Integer type = (Integer) m_typeMapping.get(classType);
+        if (type == null) {
+            throw new IllegalArgumentException("Invalid type: " + classType + ". Valid types are "
+                + m_typeMapping.keySet());
+        }
+        m_type = type.intValue();
+        return this;
+    }
+
+    public String[] getOptionLabels() {
+        String[] optionLabels = new String[m_optionsLabels.size()];
+        return (String[]) m_optionsLabels.toArray(optionLabels);
+    }
+
+    public String[] getOptionValues() {
+        String[] optionValues = new String[m_optionsValues.size()];
+        return (String[]) m_optionsValues.toArray(optionValues);
+    }
+
+    public int getCardinality() {
+        return m_cardinality;
+    }
+
+    public String[] getDefaults() {
+        return m_defaults;
+    }
+
+    public String getDescription() {
+        return m_description;
+    }
+
+    public String getHeading() {
+        return m_heading;
+    }
+
+    public String getId() {
+        return m_id;
+    }
+
+    public boolean isRequired() {
+        return m_required;
+    }
+
+    public int getType() {
+        return m_type;
+    }
+    
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("cardinality=").append(m_cardinality);
+        sb.append("; defaults="); 
+        for (int i = 0; i < m_defaults.length; i ++) {
+            sb.append(m_defaults[i]).append(" ");
+        }
+        sb.append("; description=").append(m_description);
+        sb.append("; heading=").append(m_heading);
+        sb.append("; id=").append(m_id);
+        sb.append("; required=").append(m_required);
+        sb.append("; type=").append(getType());
+        sb.append("; optionLabels=");
+        for (int i = 0; i < m_optionsLabels.size(); i ++) {
+            sb.append(m_optionsLabels.get(i)).append(" ");
+        }
+        sb.append("; optionValues=");
+        for (int i = 0; i < m_optionsValues.size(); i ++) {
+            sb.append(m_optionsValues.get(i)).append(" ");
+        }
+        return sb.toString();
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/metatype/Resource.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/metatype/Resource.java
new file mode 100644
index 0000000..c588a0c
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/metatype/Resource.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.metatype;
+
+import java.util.Properties;
+
+/**
+ * Helper class used to localize a given Property Meta Data.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Resource {
+    private Properties m_properties;
+
+    public Resource(Properties properties) {
+        m_properties = properties;
+    }
+    
+    public String localize(String param) {
+        if (m_properties != null && param != null && param.startsWith("%")) {
+            param = param.substring(1);
+            return m_properties.getProperty(param);
+        }
+        return param;
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/packageinfo b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/packageinfo
new file mode 100644
index 0000000..bd33369
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/packageinfo
@@ -0,0 +1 @@
+version 4.0.0
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/AbstractCustomizerActionSet.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/AbstractCustomizerActionSet.java
new file mode 100644
index 0000000..4add2c2
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/AbstractCustomizerActionSet.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.tracker;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Actions which can be performed on a given customizer interface.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class AbstractCustomizerActionSet {
+
+	enum Type { ADDED, MODIFIED, REMOVED }
+
+	final List<CustomizerAction> m_actions = new ArrayList<>();
+
+	public void addCustomizerAdded(Object item, Object related, Object object) {
+		m_actions.add(new CustomizerAction(Type.ADDED, item, related, object));
+	}
+	
+	public void addCustomizerModified(Object item, Object related, Object object) {
+		m_actions.add(new CustomizerAction(Type.MODIFIED, item, related, object));
+	}
+	
+	public void addCustomizerRemoved(Object item, Object related, Object object) {
+		m_actions.add(new CustomizerAction(Type.REMOVED, item, related, object));
+	}
+	
+	public void appendActionSet(AbstractCustomizerActionSet actionSet) {
+		for (CustomizerAction action : actionSet.getActions()) {
+			m_actions.add(action);
+		}
+	}
+	
+	abstract void execute();
+	
+	public List<CustomizerAction> getActions() {
+		return m_actions;
+	}
+	
+	@Override
+	public String toString() {
+		return "AbstractCustomizerActionSet [m_actions=" + m_actions + "]";
+	}
+
+	static class CustomizerAction {
+		private final Type m_type;
+		private final Object m_item;
+		private final Object m_related;
+		private final Object m_object;
+		
+		public CustomizerAction(Type type, Object item, Object related, Object object) {
+			m_type = type;
+			m_item = item;
+			m_related = related;
+			m_object = object;
+		}
+		
+		public Type getType() {
+			return m_type;
+		}
+		
+		public Object getItem() {
+			return m_item;
+		}
+		
+		public Object getRelated() {
+			return m_related;
+		}
+		
+		public Object getObject() {
+			return m_object;
+		}
+
+		@Override
+		public String toString() {
+			return "CustomizerAction [m_type=" + m_type + ", m_item=" + m_item
+					+ ", m_related=" + m_related + ", m_object=" + m_object
+					+ "]";
+		}
+	}
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/AbstractTracked.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/AbstractTracked.java
new file mode 100644
index 0000000..5e254ad
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/AbstractTracked.java
@@ -0,0 +1,486 @@
+package org.apache.felix.dm.tracker;
+/*
+ * Copyright (c) OSGi Alliance (2007, 2008). All Rights Reserved.
+ * 
+ * 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.
+ */
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.dm.impl.SerialExecutor;
+
+/**
+ * Abstract class to track items. If a Tracker is reused (closed then reopened),
+ * then a new AbstractTracked object is used. This class acts a map of tracked
+ * item -> customized object. Subclasses of this class will act as the listener
+ * object for the tracker. This class is used to synchronize access to the
+ * tracked items. This is not a public class. It is only for use by the
+ * implementation of the Tracker class.
+ * 
+ * @ThreadSafe
+ * @version $Revision: 5871 $
+ * @since 1.4
+ */
+@SuppressWarnings({"rawtypes", "unchecked"})
+abstract class AbstractTracked {
+	/* set this to true to compile in debug messages */
+	private static final boolean		DEBUG	= false;
+
+	/**
+	 * Map of tracked items to customized objects.
+	 * 
+	 * @GuardedBy this
+	 */
+	private Map			tracked;
+
+	/**
+	 * Modification count. This field is initialized to zero and incremented by
+	 * modified.
+	 * 
+	 * @GuardedBy this
+	 */
+	private int					trackingCount;
+
+	/**
+	 * List of items in the process of being added. This is used to deal with
+	 * nesting of events. Since events may be synchronously delivered, events
+	 * can be nested. For example, when processing the adding of a service and
+	 * the customizer causes the service to be unregistered, notification to the
+	 * nested call to untrack that the service was unregistered can be made to
+	 * the track method.
+	 * 
+	 * Since the ArrayList implementation is not synchronized, all access to
+	 * this list must be protected by the same synchronized object for
+	 * thread-safety.
+	 * 
+	 * @GuardedBy this
+	 */
+	private final List			adding;
+
+	/**
+	 * true if the tracked object is closed.
+	 * 
+	 * This field is volatile because it is set by one thread and read by
+	 * another.
+	 */
+	volatile boolean			closed;
+
+	/**
+	 * Initial list of items for the tracker. This is used to correctly process
+	 * the initial items which could be modified before they are tracked. This
+	 * is necessary since the initial set of tracked items are not "announced"
+	 * by events and therefore the event which makes the item untracked could be
+	 * delivered before we track the item.
+	 * 
+	 * An item must not be in both the initial and adding lists at the same
+	 * time. An item must be moved from the initial list to the adding list
+	 * "atomically" before we begin tracking it.
+	 * 
+	 * Since the LinkedList implementation is not synchronized, all access to
+	 * this list must be protected by the same synchronized object for
+	 * thread-safety.
+	 * 
+	 * @GuardedBy this
+	 */
+	private final LinkedList	initial;
+	
+	private final SerialExecutor m_executor = new SerialExecutor(null); 
+
+	/**
+	 * AbstractTracked constructor.
+	 */
+    AbstractTracked() {
+	    this.tracked = new HashMap();
+	    trackingCount = 0;
+	    adding = new ArrayList(6);
+	    initial = new LinkedList();
+	    closed = false;
+	}
+	
+	void setTracked(HashMap map) {
+	    this.tracked = map;
+	}
+
+	/**
+	 * Set initial list of items into tracker before events begin to be
+	 * received.
+	 * 
+	 * This method must be called from Tracker's open method while synchronized
+	 * on this object in the same synchronized block as the add listener call.
+	 * 
+	 * @param list The initial list of items to be tracked. <code>null</code>
+	 *        entries in the list are ignored.
+	 * @GuardedBy this
+	 */
+    void setInitial(Object[] list) {
+		if (list == null) {
+			return;
+		}
+		int size = list.length;
+		for (int i = 0; i < size; i++) {
+			Object item = list[i];
+			if (item == null) {
+				continue;
+			}
+			if (DEBUG) {
+				System.out.println("AbstractTracked.setInitial: " + item); //$NON-NLS-1$
+			}
+			initial.add(item);
+		}
+	}
+
+	/**
+	 * Track the initial list of items. This is called after events can begin to
+	 * be received.
+	 * 
+	 * This method must be called from Tracker's open method while not
+	 * synchronized on this object after the add listener call.
+	 * 
+	 */
+	void trackInitial() {
+		while (true) {
+			Object item;
+			synchronized (this) {
+				if (closed || (initial.size() == 0)) {
+					/*
+					 * if there are no more initial items
+					 */
+					return; /* we are done */
+				}
+				/*
+				 * move the first item from the initial list to the adding list
+				 * within this synchronized block.
+				 */
+				item = initial.removeFirst();
+				if (tracked.get(item) != null) {
+					/* if we are already tracking this item */
+					if (DEBUG) {
+						System.out
+								.println("AbstractTracked.trackInitial[already tracked]: " + item); //$NON-NLS-1$
+					}
+					continue; /* skip this item */
+				}
+				if (adding.contains(item)) {
+					/*
+					 * if this item is already in the process of being added.
+					 */
+					if (DEBUG) {
+						System.out
+								.println("AbstractTracked.trackInitial[already adding]: " + item); //$NON-NLS-1$
+					}
+					continue; /* skip this item */
+				}
+				adding.add(item);
+				final AbstractCustomizerActionSet actionSet = trackAdding(item, null);
+				m_executor.schedule(new Runnable() {
+
+					@Override
+					public void run() {
+						actionSet.execute();
+						
+					}
+					
+				});
+			}
+			if (DEBUG) {
+				System.out.println("AbstractTracked.trackInitial: " + item); //$NON-NLS-1$
+			}
+		}
+	}
+
+	/**
+	 * Called by the owning Tracker object when it is closed.
+	 */
+	void close() {
+		closed = true;
+	}
+
+	abstract AbstractCustomizerActionSet createCustomizerActionSet();
+	
+	/**
+	 * Begin to track an item.
+	 * 
+	 * @param item Item to be tracked.
+	 * @param related Action related object.
+	 */
+	AbstractCustomizerActionSet track(final Object item, final Object related) {
+		final Object object;
+		final AbstractCustomizerActionSet actionSet = createCustomizerActionSet();
+		synchronized (this) {
+			if (closed) {
+				return actionSet;
+			}
+			object = tracked.get(item);
+			if (object == null) { /* we are not tracking the item */
+				if (adding.contains(item)) {
+					/* if this item is already in the process of being added. */
+					if (DEBUG) {
+						System.out
+								.println("AbstractTracked.track[already adding]: " + item); //$NON-NLS-1$
+					}
+					return actionSet;
+				}
+				adding.add(item); /* mark this item is being added */
+			}
+			else { /* we are currently tracking this item */
+				if (DEBUG) {
+					System.out
+							.println("AbstractTracked.track[modified]: " + item); //$NON-NLS-1$
+				}
+				modified(); /* increment modification count */
+			}
+		}
+
+		if (object == null) { /* we are not tracking the item */
+			actionSet.appendActionSet(trackAdding(item, related));
+		}
+		else {
+			/* Call customizer outside of synchronized region */
+			actionSet.addCustomizerModified(item, related, object);
+			/*
+			 * If the customizer throws an unchecked exception, it is safe to
+			 * let it propagate
+			 */
+		}
+		return actionSet;
+	}
+
+	/**
+	 * Common logic to add an item to the tracker used by track and
+	 * trackInitial. The specified item must have been placed in the adding list
+	 * before calling this method.
+	 * 
+	 * @param item Item to be tracked.
+	 * @param related Action related object.
+	 */
+	private AbstractCustomizerActionSet trackAdding(final Object item, final Object related) {
+		final AbstractCustomizerActionSet actionSet = createCustomizerActionSet();
+		if (DEBUG) {
+			System.out.println("AbstractTracked.trackAdding: " + item); //$NON-NLS-1$
+		}
+		Object object = null;
+		boolean becameUntracked = false;
+		/* Call customizer outside of synchronized region */
+		try {
+			object = customizerAdding(item, related);
+			/*
+			 * If the customizer throws an unchecked exception, it will
+			 * propagate after the finally
+			 */
+		}
+		finally {
+			boolean needToCallback = false;
+			synchronized (this) {
+				if (adding.remove(item) && !closed) {
+					/*
+					 * if the item was not untracked during the customizer
+					 * callback
+					 */
+					if (object != null) {
+						tracked.put(item, object);
+						modified(); /* increment modification count */
+						notifyAll(); /* notify any waiters */
+						needToCallback = true; /* marrs: invoke added callback */
+					}
+				}
+				else {
+					becameUntracked = true;
+				}
+			}
+			if (needToCallback) {
+				actionSet.addCustomizerAdded(item, related, object);
+			}
+		}
+		/*
+		 * The item became untracked during the customizer callback.
+		 */
+		if (becameUntracked && (object != null)) {
+			if (DEBUG) {
+				System.out
+						.println("AbstractTracked.trackAdding[removed]: " + item); //$NON-NLS-1$
+			}
+			/* Call customizer outside of synchronized region */
+			actionSet.addCustomizerRemoved(item, related, object);
+			/*
+			 * If the customizer throws an unchecked exception, it is safe to
+			 * let it propagate
+			 */
+		}
+		return actionSet;
+	}
+
+	/**
+	 * Discontinue tracking the item.
+	 * 
+	 * @param item Item to be untracked.
+	 * @param related Action related object.
+	 */
+	AbstractCustomizerActionSet untrack(final Object item, final Object related) {
+		AbstractCustomizerActionSet actionSet = createCustomizerActionSet();
+		final Object object;
+		synchronized (this) {
+			if (initial.remove(item)) { /*
+										 * if this item is already in the list
+										 * of initial references to process
+										 */
+				if (DEBUG) {
+					System.out
+							.println("AbstractTracked.untrack[removed from initial]: " + item); //$NON-NLS-1$
+				}
+				return actionSet; /*
+						 * we have removed it from the list and it will not be
+						 * processed
+						 */
+			}
+
+			if (adding.remove(item)) { /*
+										 * if the item is in the process of
+										 * being added
+										 */
+				if (DEBUG) {
+					System.out
+							.println("AbstractTracked.untrack[being added]: " + item); //$NON-NLS-1$
+				}
+				return actionSet; /*
+						 * in case the item is untracked while in the process of
+						 * adding
+						 */
+			}
+			object = tracked.remove(item); /*
+											 * must remove from tracker before
+											 * calling customizer callback
+											 */
+			if (object == null) { /* are we actually tracking the item */
+				return actionSet;
+			}
+			modified(); /* increment modification count */
+		}
+		if (DEBUG) {
+			System.out.println("AbstractTracked.untrack[removed]: " + item); //$NON-NLS-1$
+		}
+		/* Call customizer outside of synchronized region */
+		actionSet.addCustomizerRemoved(item, related, object);
+		/*
+		 * If the customizer throws an unchecked exception, it is safe to let it
+		 * propagate
+		 */
+		return actionSet;
+	}
+
+	/**
+	 * Returns the number of tracked items.
+	 * 
+	 * @return The number of tracked items.
+	 * 
+	 * @GuardedBy this
+	 */
+	int size() {
+		return tracked.size();
+	}
+
+	/**
+	 * Return the customized object for the specified item
+	 * 
+	 * @param item The item to lookup in the map
+	 * @return The customized object for the specified item.
+	 * 
+	 * @GuardedBy this
+	 */
+	Object getCustomizedObject(final Object item) {
+		return tracked.get(item);
+	}
+
+	/**
+	 * Return the list of tracked items.
+	 * 
+	 * @param list An array to contain the tracked items.
+	 * @return The specified list if it is large enough to hold the tracked
+	 *         items or a new array large enough to hold the tracked items.
+	 * @GuardedBy this
+	 */
+	Object[] getTracked(final Object[] list) {
+		return tracked.keySet().toArray(list);
+	}
+
+	/**
+	 * Increment the modification count. If this method is overridden, the
+	 * overriding method MUST call this method to increment the tracking count.
+	 * 
+	 * @GuardedBy this
+	 */
+	void modified() {
+		trackingCount++;
+	}
+
+	/**
+	 * Returns the tracking count for this <code>ServiceTracker</code> object.
+	 * 
+	 * The tracking count is initialized to 0 when this object is opened. Every
+	 * time an item is added, modified or removed from this object the tracking
+	 * count is incremented.
+	 * 
+	 * @GuardedBy this
+	 * @return The tracking count for this object.
+	 */
+	int getTrackingCount() {
+		return trackingCount;
+	}
+	
+	/**
+	 * Returns the serial executor used by this tracked.
+	 * @return
+	 */
+	SerialExecutor getExecutor() {
+		return m_executor;
+	}
+
+	/**
+	 * Call the specific customizer adding method. This method must not be
+	 * called while synchronized on this object.
+	 * 
+	 * @param item Item to be tracked.
+	 * @param related Action related object.
+	 * @return Customized object for the tracked item or <code>null</code> if
+	 *         the item is not to be tracked.
+	 */
+	abstract Object customizerAdding(final Object item, final Object related);
+
+	/** marrs: Call the specific customizer added method. */
+	abstract void customizerAdded(final Object item, final Object related, final Object object);
+	
+	/**
+	 * Call the specific customizer modified method. This method must not be
+	 * called while synchronized on this object.
+	 * 
+	 * @param item Tracked item.
+	 * @param related Action related object.
+	 * @param object Customized object for the tracked item.
+	 */
+	abstract void customizerModified(final Object item, final Object related,
+			final Object object);
+
+	/**
+	 * Call the specific customizer removed method. This method must not be
+	 * called while synchronized on this object.
+	 * 
+	 * @param item Tracked item.
+	 * @param related Action related object.
+	 * @param object Customized object for the tracked item.
+	 */
+	abstract void customizerRemoved(final Object item, final Object related,
+			final Object object);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/BundleTracker.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/BundleTracker.java
new file mode 100644
index 0000000..e3b1677
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/BundleTracker.java
@@ -0,0 +1,505 @@
+package org.apache.felix.dm.tracker;
+/*
+ * Copyright (c) OSGi Alliance (2007, 2008). All Rights Reserved.
+ * 
+ * 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.
+ */
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.SynchronousBundleListener;
+
+/**
+ * The <code>BundleTracker</code> class simplifies tracking bundles much like
+ * the <code>ServiceTracker</code> simplifies tracking services.
+ * <p>
+ * A <code>BundleTracker</code> is constructed with state criteria and a
+ * <code>BundleTrackerCustomizer</code> object. A <code>BundleTracker</code> can
+ * use the <code>BundleTrackerCustomizer</code> to select which bundles are
+ * tracked and to create a customized object to be tracked with the bundle. The
+ * <code>BundleTracker</code> can then be opened to begin tracking all bundles
+ * whose state matches the specified state criteria.
+ * <p>
+ * The <code>getBundles</code> method can be called to get the
+ * <code>Bundle</code> objects of the bundles being tracked. The
+ * <code>getObject</code> method can be called to get the customized object for
+ * a tracked bundle.
+ * <p>
+ * The <code>BundleTracker</code> class is thread-safe. It does not call a
+ * <code>BundleTrackerCustomizer</code> while holding any locks.
+ * <code>BundleTrackerCustomizer</code> implementations must also be
+ * thread-safe.
+ * 
+ * @ThreadSafe
+ * @version $Revision: 5894 $
+ * @since 1.4
+ */
+public class BundleTracker implements BundleTrackerCustomizer {
+	/* set this to true to compile in debug messages */
+	static final boolean			DEBUG	= false;
+
+	/**
+	 * The Bundle Context used by this <code>BundleTracker</code>.
+	 */
+	protected final BundleContext	context;
+
+	/**
+	 * The <code>BundleTrackerCustomizer</code> object for this tracker.
+	 */
+	final BundleTrackerCustomizer	customizer;
+
+	/**
+	 * Tracked bundles: <code>Bundle</code> object -> customized Object and
+	 * <code>BundleListener</code> object
+	 */
+	private volatile Tracked		tracked;
+
+	/**
+	 * Accessor method for the current Tracked object. This method is only
+	 * intended to be used by the unsynchronized methods which do not modify the
+	 * tracked field.
+	 * 
+	 * @return The current Tracked object.
+	 */
+	private Tracked tracked() {
+		return tracked;
+	}
+
+	/**
+	 * State mask for bundles being tracked. This field contains the ORed values
+	 * of the bundle states being tracked.
+	 */
+	final int						mask;
+
+	/**
+	 * Create a <code>BundleTracker</code> for bundles whose state is present in
+	 * the specified state mask.
+	 * 
+	 * <p>
+	 * Bundles whose state is present on the specified state mask will be
+	 * tracked by this <code>BundleTracker</code>.
+	 * 
+	 * @param context The <code>BundleContext</code> against which the tracking
+	 *        is done.
+	 * @param stateMask The bit mask of the <code>OR</code>ing of the bundle
+	 *        states to be tracked.
+	 * @param customizer The customizer object to call when bundles are added,
+	 *        modified, or removed in this <code>BundleTracker</code>. If
+	 *        customizer is <code>null</code>, then this
+	 *        <code>BundleTracker</code> will be used as the
+	 *        <code>BundleTrackerCustomizer</code> and this
+	 *        <code>BundleTracker</code> will call the
+	 *        <code>BundleTrackerCustomizer</code> methods on itself.
+	 * @see Bundle#getState()
+	 */
+	public BundleTracker(BundleContext context, int stateMask,
+			BundleTrackerCustomizer customizer) {
+		this.context = context;
+		this.mask = stateMask;
+		this.customizer = (customizer == null) ? this : customizer;
+	}
+
+	/**
+	 * Open this <code>BundleTracker</code> and begin tracking bundles.
+	 * 
+	 * <p>
+	 * Bundle which match the state criteria specified when this
+	 * <code>BundleTracker</code> was created are now tracked by this
+	 * <code>BundleTracker</code>.
+	 * 
+	 * @throws java.lang.IllegalStateException If the <code>BundleContext</code>
+	 *         with which this <code>BundleTracker</code> was created is no
+	 *         longer valid.
+	 * @throws java.lang.SecurityException If the caller and this class do not
+	 *         have the appropriate
+	 *         <code>AdminPermission[context bundle,LISTENER]</code>, and the
+	 *         Java Runtime Environment supports permissions.
+	 */
+	public void open() {
+		final Tracked t;
+		synchronized (this) {
+			if (tracked != null) {
+				return;
+			}
+			if (DEBUG) {
+				System.out.println("BundleTracker.open"); //$NON-NLS-1$
+			}
+			t = new Tracked();
+			synchronized (t) {
+				context.addBundleListener(t);
+				Bundle[] bundles = context.getBundles();
+				if (bundles != null) {
+					int length = bundles.length;
+					for (int i = 0; i < length; i++) {
+						int state = bundles[i].getState();
+						if ((state & mask) == 0) {
+							/* null out bundles whose states are not interesting */
+							bundles[i] = null;
+						}
+					}
+					/* set tracked with the initial bundles */
+					t.setInitial(bundles); 
+				}
+			}
+			tracked = t;
+		}
+		/* Call tracked outside of synchronized region */
+		t.trackInitial(); /* process the initial references */
+	}
+
+	/**
+	 * Close this <code>BundleTracker</code>.
+	 * 
+	 * <p>
+	 * This method should be called when this <code>BundleTracker</code> should
+	 * end the tracking of bundles.
+	 * 
+	 * <p>
+	 * This implementation calls {@link #getBundles()} to get the list of
+	 * tracked bundles to remove.
+	 */
+	public void close() {
+		final Bundle[] bundles;
+		final Tracked outgoing;
+		synchronized (this) {
+			outgoing = tracked;
+			if (outgoing == null) {
+				return;
+			}
+			if (DEBUG) {
+				System.out.println("BundleTracker.close"); //$NON-NLS-1$
+			}
+			outgoing.close();
+			bundles = getBundles();
+			tracked = null;
+			try {
+				context.removeBundleListener(outgoing);
+			}
+			catch (IllegalStateException e) {
+				/* In case the context was stopped. */
+			}
+		}
+		if (bundles != null) {
+			for (int i = 0; i < bundles.length; i++) {
+				outgoing.untrack(bundles[i], null);
+			}
+		}
+	}
+
+	/**
+	 * Default implementation of the
+	 * <code>BundleTrackerCustomizer.addingBundle</code> method.
+	 * 
+	 * <p>
+	 * This method is only called when this <code>BundleTracker</code> has been
+	 * constructed with a <code>null BundleTrackerCustomizer</code> argument.
+	 * 
+	 * <p>
+	 * This implementation simply returns the specified <code>Bundle</code>.
+	 * 
+	 * <p>
+	 * This method can be overridden in a subclass to customize the object to be
+	 * tracked for the bundle being added.
+	 * 
+	 * @param bundle The <code>Bundle</code> being added to this
+	 *        <code>BundleTracker</code> object.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or <code>null</code> if there is no bundle event associated
+	 *        with the call to this method.
+	 * @return The specified bundle.
+	 * @see BundleTrackerCustomizer#addingBundle(Bundle, BundleEvent)
+	 */
+	public Object addingBundle(Bundle bundle, BundleEvent event) {
+		return bundle;
+	}
+	
+	public void addedBundle(Bundle bundle, BundleEvent event, Object object) {
+		/* do nothing */
+	}
+
+	/**
+	 * Default implementation of the
+	 * <code>BundleTrackerCustomizer.modifiedBundle</code> method.
+	 * 
+	 * <p>
+	 * This method is only called when this <code>BundleTracker</code> has been
+	 * constructed with a <code>null BundleTrackerCustomizer</code> argument.
+	 * 
+	 * <p>
+	 * This implementation does nothing.
+	 * 
+	 * @param bundle The <code>Bundle</code> whose state has been modified.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or <code>null</code> if there is no bundle event associated
+	 *        with the call to this method.
+	 * @param object The customized object for the specified Bundle.
+	 * @see BundleTrackerCustomizer#modifiedBundle(Bundle, BundleEvent, Object)
+	 */
+	public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) {
+		/* do nothing */
+	}
+
+	/**
+	 * Default implementation of the
+	 * <code>BundleTrackerCustomizer.removedBundle</code> method.
+	 * 
+	 * <p>
+	 * This method is only called when this <code>BundleTracker</code> has been
+	 * constructed with a <code>null BundleTrackerCustomizer</code> argument.
+	 * 
+	 * <p>
+	 * This implementation does nothing.
+	 * 
+	 * @param bundle The <code>Bundle</code> being removed.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or <code>null</code> if there is no bundle event associated
+	 *        with the call to this method.
+	 * @param object The customized object for the specified bundle.
+	 * @see BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Object)
+	 */
+	public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
+		/* do nothing */
+	}
+
+	/**
+	 * Return an array of <code>Bundle</code>s for all bundles being tracked by
+	 * this <code>BundleTracker</code>.
+	 * 
+	 * @return An array of <code>Bundle</code>s or <code>null</code> if no
+	 *         bundles are being tracked.
+	 */
+	public Bundle[] getBundles() {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return null;
+		}
+		synchronized (t) {
+			int length = t.size();
+			if (length == 0) {
+				return null;
+			}
+			return (Bundle[]) t.getTracked(new Bundle[length]);
+		}
+	}
+
+	/**
+	 * Returns the customized object for the specified <code>Bundle</code> if
+	 * the specified bundle is being tracked by this <code>BundleTracker</code>.
+	 * 
+	 * @param bundle The <code>Bundle</code> being tracked.
+	 * @return The customized object for the specified <code>Bundle</code> or
+	 *         <code>null</code> if the specified <code>Bundle</code> is not
+	 *         being tracked.
+	 */
+	public Object getObject(Bundle bundle) {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return null;
+		}
+		synchronized (t) {
+			return t.getCustomizedObject(bundle);
+		}
+	}
+
+	/**
+	 * Remove a bundle from this <code>BundleTracker</code>.
+	 * 
+	 * The specified bundle will be removed from this <code>BundleTracker</code>
+	 * . If the specified bundle was being tracked then the
+	 * <code>BundleTrackerCustomizer.removedBundle</code> method will be called
+	 * for that bundle.
+	 * 
+	 * @param bundle The <code>Bundle</code> to be removed.
+	 */
+	public void remove(Bundle bundle) {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return;
+		}
+		t.untrack(bundle, null);
+	}
+
+	/**
+	 * Return the number of bundles being tracked by this
+	 * <code>BundleTracker</code>.
+	 * 
+	 * @return The number of bundles being tracked.
+	 */
+	public int size() {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return 0;
+		}
+		synchronized (t) {
+			return t.size();
+		}
+	}
+
+	/**
+	 * Returns the tracking count for this <code>BundleTracker</code>.
+	 * 
+	 * The tracking count is initialized to 0 when this
+	 * <code>BundleTracker</code> is opened. Every time a bundle is added,
+	 * modified or removed from this <code>BundleTracker</code> the tracking
+	 * count is incremented.
+	 * 
+	 * <p>
+	 * The tracking count can be used to determine if this
+	 * <code>BundleTracker</code> has added, modified or removed a bundle by
+	 * comparing a tracking count value previously collected with the current
+	 * tracking count value. If the value has not changed, then no bundle has
+	 * been added, modified or removed from this <code>BundleTracker</code>
+	 * since the previous tracking count was collected.
+	 * 
+	 * @return The tracking count for this <code>BundleTracker</code> or -1 if
+	 *         this <code>BundleTracker</code> is not open.
+	 */
+	public int getTrackingCount() {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return -1;
+		}
+		synchronized (t) {
+			return t.getTrackingCount();
+		}
+	}
+
+	/**
+	 * Inner class which subclasses AbstractTracked. This class is the
+	 * <code>SynchronousBundleListener</code> object for the tracker.
+	 * 
+	 * @ThreadSafe
+	 * @since 1.4
+	 */
+	class Tracked extends AbstractTracked implements SynchronousBundleListener {
+		/**
+		 * Tracked constructor.
+		 */
+		Tracked() {
+			super();
+		}
+
+		/**
+		 * <code>BundleListener</code> method for the <code>BundleTracker</code>
+		 * class. This method must NOT be synchronized to avoid deadlock
+		 * potential.
+		 * 
+		 * @param event <code>BundleEvent</code> object from the framework.
+		 */
+		public void bundleChanged(final BundleEvent event) {
+			/*
+			 * Check if we had a delayed call (which could happen when we
+			 * close).
+			 */
+			if (closed) {
+				return;
+			}
+			final Bundle bundle = event.getBundle();
+			final int state = bundle.getState();
+			if (DEBUG) {
+				System.out
+						.println("BundleTracker.Tracked.bundleChanged[" + state + "]: " + bundle); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+
+			if ((state & mask) != 0) {
+				track(bundle, event);
+				/*
+				 * If the customizer throws an unchecked exception, it is safe
+				 * to let it propagate
+				 */
+			}
+			else {
+				untrack(bundle, event);
+				/*
+				 * If the customizer throws an unchecked exception, it is safe
+				 * to let it propagate
+				 */
+			}
+		}
+
+		/**
+		 * Call the specific customizer adding method. This method must not be
+		 * called while synchronized on this object.
+		 * 
+		 * @param item Item to be tracked.
+		 * @param related Action related object.
+		 * @return Customized object for the tracked item or <code>null</code>
+		 *         if the item is not to be tracked.
+		 */
+		Object customizerAdding(final Object item,
+				final Object related) {
+			return customizer
+					.addingBundle((Bundle) item, (BundleEvent) related);
+		}
+		
+		void customizerAdded(final Object item, final Object related, final Object object) {
+			customizer.addedBundle((Bundle) item, (BundleEvent) related, object);
+		}
+
+		/**
+		 * Call the specific customizer modified method. This method must not be
+		 * called while synchronized on this object.
+		 * 
+		 * @param item Tracked item.
+		 * @param related Action related object.
+		 * @param object Customized object for the tracked item.
+		 */
+		void customizerModified(final Object item,
+				final Object related, final Object object) {
+			customizer.modifiedBundle((Bundle) item, (BundleEvent) related,
+					object);
+		}
+
+		/**
+		 * Call the specific customizer removed method. This method must not be
+		 * called while synchronized on this object.
+		 * 
+		 * @param item Tracked item.
+		 * @param related Action related object.
+		 * @param object Customized object for the tracked item.
+		 */
+		void customizerRemoved(final Object item,
+				final Object related, final Object object) {
+			customizer.removedBundle((Bundle) item, (BundleEvent) related,
+					object);
+		}
+
+		@Override
+		AbstractCustomizerActionSet createCustomizerActionSet() {
+			return new AbstractCustomizerActionSet() {
+				
+				@Override
+				public void addCustomizerAdded(Object item, Object related, Object object) {
+					customizerAdded(item, related, object);
+				}
+
+				@Override
+				public void addCustomizerModified(Object item, Object related,
+						Object object) {
+					customizerModified(item, related, object);
+				}
+				@Override
+				public void addCustomizerRemoved(Object item, Object related,
+						Object object) {
+					customizerRemoved(item, related, object);
+				}
+						
+				@Override
+				void execute() {
+					// nothing to be done here, since this actionSet executes the actions immediately.
+				}
+			};
+		}
+	}
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/BundleTrackerCustomizer.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/BundleTrackerCustomizer.java
new file mode 100644
index 0000000..0fd340e
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/BundleTrackerCustomizer.java
@@ -0,0 +1,106 @@
+package org.apache.felix.dm.tracker;
+/*
+ * Copyright (c) OSGi Alliance (2007, 2008). All Rights Reserved.
+ * 
+ * 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.
+ */
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleEvent;
+
+/**
+ * The <code>BundleTrackerCustomizer</code> interface allows a
+ * <code>BundleTracker</code> to customize the <code>Bundle</code>s that are
+ * tracked. A <code>BundleTrackerCustomizer</code> is called when a bundle is
+ * being added to a <code>BundleTracker</code>. The
+ * <code>BundleTrackerCustomizer</code> can then return an object for the
+ * tracked bundle. A <code>BundleTrackerCustomizer</code> is also called when a
+ * tracked bundle is modified or has been removed from a
+ * <code>BundleTracker</code>.
+ * 
+ * <p>
+ * The methods in this interface may be called as the result of a
+ * <code>BundleEvent</code> being received by a <code>BundleTracker</code>.
+ * Since <code>BundleEvent</code>s are received synchronously by the
+ * <code>BundleTracker</code>, it is highly recommended that implementations of
+ * these methods do not alter bundle states while being synchronized on any
+ * object.
+ * 
+ * <p>
+ * The <code>BundleTracker</code> class is thread-safe. It does not call a
+ * <code>BundleTrackerCustomizer</code> while holding any locks.
+ * <code>BundleTrackerCustomizer</code> implementations must also be
+ * thread-safe.
+ * 
+ * @ThreadSafe
+ * @version $Revision: 5874 $
+ * @since 1.4
+ */
+public interface BundleTrackerCustomizer {
+	/**
+	 * A bundle is being added to the <code>BundleTracker</code>.
+	 * 
+	 * <p>
+	 * This method is called before a bundle which matched the search parameters
+	 * of the <code>BundleTracker</code> is added to the
+	 * <code>BundleTracker</code>. This method should return the object to be
+	 * tracked for the specified <code>Bundle</code>. The returned object is
+	 * stored in the <code>BundleTracker</code> and is available from the
+	 * {@link BundleTracker#getObject(Bundle) getObject} method.
+	 * 
+	 * @param bundle The <code>Bundle</code> being added to the
+	 *        <code>BundleTracker</code>.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or <code>null</code> if there is no bundle event associated
+	 *        with the call to this method.
+	 * @return The object to be tracked for the specified <code>Bundle</code>
+	 *         object or <code>null</code> if the specified <code>Bundle</code>
+	 *         object should not be tracked.
+	 */
+	public Object addingBundle(Bundle bundle, BundleEvent event);
+	
+	/** marrs: A bundle has been added to the BundleTracker. */
+	public void addedBundle(Bundle bundle, BundleEvent event, Object object);
+
+	/**
+	 * A bundle tracked by the <code>BundleTracker</code> has been modified.
+	 * 
+	 * <p>
+	 * This method is called when a bundle being tracked by the
+	 * <code>BundleTracker</code> has had its state modified.
+	 * 
+	 * @param bundle The <code>Bundle</code> whose state has been modified.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or <code>null</code> if there is no bundle event associated
+	 *        with the call to this method.
+	 * @param object The tracked object for the specified bundle.
+	 */
+	public void modifiedBundle(Bundle bundle, BundleEvent event,
+			Object object);
+
+	/**
+	 * A bundle tracked by the <code>BundleTracker</code> has been removed.
+	 * 
+	 * <p>
+	 * This method is called after a bundle is no longer being tracked by the
+	 * <code>BundleTracker</code>.
+	 * 
+	 * @param bundle The <code>Bundle</code> that has been removed.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or <code>null</code> if there is no bundle event associated
+	 *        with the call to this method.
+	 * @param object The tracked object for the specified bundle.
+	 */
+	public void removedBundle(Bundle bundle, BundleEvent event,
+			Object object);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/ServiceTracker.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/ServiceTracker.java
new file mode 100644
index 0000000..f682bbd
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/ServiceTracker.java
@@ -0,0 +1,1477 @@
+package org.apache.felix.dm.tracker;
+/*
+ * Copyright (c) OSGi Alliance (2000, 2009). All Rights Reserved.
+ * 
+ * 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.
+ */
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.felix.dm.impl.ServiceUtil;
+import org.osgi.framework.AllServiceListener;
+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;
+import org.osgi.framework.Version;
+
+/**
+ * The <code>ServiceTracker</code> class simplifies using services from the
+ * Framework's service registry.
+ * <p>
+ * A <code>ServiceTracker</code> object is constructed with search criteria and
+ * a <code>ServiceTrackerCustomizer</code> object. A <code>ServiceTracker</code>
+ * can use a <code>ServiceTrackerCustomizer</code> to customize the service
+ * objects to be tracked. The <code>ServiceTracker</code> can then be opened to
+ * begin tracking all services in the Framework's service registry that match
+ * the specified search criteria. The <code>ServiceTracker</code> correctly
+ * handles all of the details of listening to <code>ServiceEvent</code>s and
+ * getting and ungetting services.
+ * <p>
+ * The <code>getServiceReferences</code> method can be called to get references
+ * to the services being tracked. The <code>getService</code> and
+ * <code>getServices</code> methods can be called to get the service objects for
+ * the tracked service.
+ * <p>
+ * The <code>ServiceTracker</code> class is thread-safe. It does not call a
+ * <code>ServiceTrackerCustomizer</code> while holding any locks.
+ * <code>ServiceTrackerCustomizer</code> implementations must also be
+ * thread-safe.
+ * 
+ * @ThreadSafe
+ * @version $Revision: 6386 $
+ */
+public class ServiceTracker implements ServiceTrackerCustomizer {
+	/* set this to true to compile in debug messages */
+	static final boolean				DEBUG			= false;
+	/**
+	 * The Bundle Context used by this <code>ServiceTracker</code>.
+	 */
+	protected final BundleContext		context;
+	/**
+	 * The Filter used by this <code>ServiceTracker</code> which specifies the
+	 * search criteria for the services to track.
+	 * 
+	 * @since 1.1
+	 */
+	protected final Filter				filter;
+	/**
+	 * The <code>ServiceTrackerCustomizer</code> for this tracker.
+	 */
+	final ServiceTrackerCustomizer		customizer;
+	/**
+	 * Filter string for use when adding the ServiceListener. If this field is
+	 * set, then certain optimizations can be taken since we don't have a user
+	 * supplied filter.
+	 */
+	final String						listenerFilter;
+	/**
+	 * Class name to be tracked. If this field is set, then we are tracking by
+	 * class name.
+	 */
+	private final String				trackClass;
+	/**
+	 * Reference to be tracked. If this field is set, then we are tracking a
+	 * single ServiceReference.
+	 */
+	private final ServiceReference		trackReference;
+	/**
+	 * Tracked services: <code>ServiceReference</code> -> customized Object and
+	 * <code>ServiceListener</code> object
+	 */
+	private volatile Tracked			tracked;
+
+	/**
+	 * Accessor method for the current Tracked object. This method is only
+	 * intended to be used by the unsynchronized methods which do not modify the
+	 * tracked field.
+	 * 
+	 * @return The current Tracked object.
+	 */
+	private Tracked tracked() {
+		return tracked;
+	}
+
+	/**
+	 * Cached ServiceReference for getServiceReference.
+	 * 
+	 * This field is volatile since it is accessed by multiple threads.
+	 */
+	private volatile ServiceReference	cachedReference;
+	/**
+	 * Cached service object for getService.
+	 * 
+	 * This field is volatile since it is accessed by multiple threads.
+	 */
+	private volatile Object				cachedService;
+
+	/**
+	 * org.osgi.framework package version which introduced
+	 * {@link ServiceEvent#MODIFIED_ENDMATCH}
+	 */
+	private static final Version		endMatchVersion	= new Version(1, 5, 0);
+
+	/**
+	 * Flag that gets set when opening the tracker, determines if the tracker should
+	 * track all aspects or just the highest ranked ones.
+	 */
+    public boolean m_trackAllAspects;
+    
+    private boolean debug = false;
+    private String debugKey;
+    
+    public void setDebug(String debugKey) {
+    	this.debug = true;
+    	this.debugKey = debugKey;
+    }
+
+	/**
+	 * Create a <code>ServiceTracker</code> on the specified
+	 * <code>ServiceReference</code>.
+	 * 
+	 * <p>
+	 * The service referenced by the specified <code>ServiceReference</code>
+	 * will be tracked by this <code>ServiceTracker</code>.
+	 * 
+	 * @param context The <code>BundleContext</code> against which the tracking
+	 *        is done.
+	 * @param reference The <code>ServiceReference</code> for the service to be
+	 *        tracked.
+	 * @param customizer The customizer object to call when services are added,
+	 *        modified, or removed in this <code>ServiceTracker</code>. If
+	 *        customizer is <code>null</code>, then this
+	 *        <code>ServiceTracker</code> will be used as the
+	 *        <code>ServiceTrackerCustomizer</code> and this
+	 *        <code>ServiceTracker</code> will call the
+	 *        <code>ServiceTrackerCustomizer</code> methods on itself.
+	 */
+	public ServiceTracker(final BundleContext context,
+			final ServiceReference reference,
+			final ServiceTrackerCustomizer customizer) {
+		this.context = context;
+		this.trackReference = reference;
+		this.trackClass = null;
+		this.customizer = (customizer == null) ? this : customizer;
+		this.listenerFilter = "(" + Constants.SERVICE_ID + "="
+				+ reference.getProperty(Constants.SERVICE_ID).toString() + ")"; 
+		try {
+			this.filter = context.createFilter(listenerFilter);
+		}
+		catch (InvalidSyntaxException e) {
+			/*
+			 * we could only get this exception if the ServiceReference was
+			 * invalid
+			 */
+			IllegalArgumentException iae = new IllegalArgumentException(
+					"unexpected InvalidSyntaxException: " + e.getMessage());
+			iae.initCause(e);
+			throw iae;
+		}
+	}
+
+	/**
+	 * Create a <code>ServiceTracker</code> on the specified class name.
+	 * 
+	 * <p>
+	 * Services registered under the specified class name will be tracked by
+	 * this <code>ServiceTracker</code>.
+	 * 
+	 * @param context The <code>BundleContext</code> against which the tracking
+	 *        is done.
+	 * @param clazz The class name of the services to be tracked.
+	 * @param customizer The customizer object to call when services are added,
+	 *        modified, or removed in this <code>ServiceTracker</code>. If
+	 *        customizer is <code>null</code>, then this
+	 *        <code>ServiceTracker</code> will be used as the
+	 *        <code>ServiceTrackerCustomizer</code> and this
+	 *        <code>ServiceTracker</code> will call the
+	 *        <code>ServiceTrackerCustomizer</code> methods on itself.
+	 */
+	public ServiceTracker(final BundleContext context, final String clazz,
+			final ServiceTrackerCustomizer customizer) {
+		this.context = context;
+		this.trackReference = null;
+		this.trackClass = clazz;
+		this.customizer = (customizer == null) ? this : customizer;
+		// we call clazz.toString to verify clazz is non-null!
+		this.listenerFilter = "(" + Constants.OBJECTCLASS + "="
+				+ clazz.toString() + ")"; 
+		try {
+			this.filter = context.createFilter(listenerFilter);
+		}
+		catch (InvalidSyntaxException e) {
+			/*
+			 * we could only get this exception if the clazz argument was
+			 * malformed
+			 */
+			IllegalArgumentException iae = new IllegalArgumentException(
+					"unexpected InvalidSyntaxException: " + e.getMessage());
+			iae.initCause(e);
+			throw iae;
+		}
+	}
+
+	/**
+	 * Create a <code>ServiceTracker</code> on the specified <code>Filter</code>
+	 * object.
+	 * 
+	 * <p>
+	 * Services which match the specified <code>Filter</code> object will be
+	 * tracked by this <code>ServiceTracker</code>.
+	 * 
+	 * @param context The <code>BundleContext</code> against which the tracking
+	 *        is done.
+	 * @param filter The <code>Filter</code> to select the services to be
+	 *        tracked.
+	 * @param customizer The customizer object to call when services are added,
+	 *        modified, or removed in this <code>ServiceTracker</code>. If
+	 *        customizer is null, then this <code>ServiceTracker</code> will be
+	 *        used as the <code>ServiceTrackerCustomizer</code> and this
+	 *        <code>ServiceTracker</code> will call the
+	 *        <code>ServiceTrackerCustomizer</code> methods on itself.
+	 * @since 1.1
+	 */
+	public ServiceTracker(final BundleContext context, final Filter filter,
+			final ServiceTrackerCustomizer customizer) {
+		this.context = context;
+		this.trackReference = null;
+		this.trackClass = null;
+		final Version frameworkVersion = (Version) AccessController
+				.doPrivileged(new PrivilegedAction<Version>() {
+					public Version run() {
+						String version = context
+								.getProperty(Constants.FRAMEWORK_VERSION);
+						return (version == null) ? Version.emptyVersion
+								: new Version(version);
+					}
+				});
+		final boolean endMatchSupported = (frameworkVersion
+				.compareTo(endMatchVersion) >= 0);
+		this.listenerFilter = endMatchSupported ? filter.toString() : null;
+		this.filter = filter;
+		this.customizer = (customizer == null) ? this : customizer;
+		if ((context == null) || (filter == null)) {
+			/*
+			 * we throw a NPE here to be consistent with the other constructors
+			 */
+			throw new NullPointerException();
+		}
+	}
+
+	/**
+	 * Open this <code>ServiceTracker</code> and begin tracking services.
+	 * 
+	 * <p>
+	 * This implementation calls <code>open(false)</code>.
+	 * 
+	 * @throws java.lang.IllegalStateException If the <code>BundleContext</code>
+	 *         with which this <code>ServiceTracker</code> was created is no
+	 *         longer valid.
+	 * @see #open(boolean)
+	 */
+	public void open() {
+		open(false);
+	}
+
+	/**
+	 * Open this <code>ServiceTracker</code> and begin tracking services.
+	 * 
+	 * <p>
+	 * Services which match the search criteria specified when this
+	 * <code>ServiceTracker</code> was created are now tracked by this
+	 * <code>ServiceTracker</code>.
+	 * 
+	 * @param trackAllServices If <code>true</code>, then this
+	 *        <code>ServiceTracker</code> will track all matching services
+	 *        regardless of class loader accessibility. If <code>false</code>,
+	 *        then this <code>ServiceTracker</code> will only track matching
+	 *        services which are class loader accessible to the bundle whose
+	 *        <code>BundleContext</code> is used by this
+	 *        <code>ServiceTracker</code>.
+	 * @throws java.lang.IllegalStateException If the <code>BundleContext</code>
+	 *         with which this <code>ServiceTracker</code> was created is no
+	 *         longer valid.
+	 * @since 1.3
+	 */
+    public void open(boolean trackAllServices) {
+        open(trackAllServices, false);
+    }
+	
+    /**
+     * Open this <code>ServiceTracker</code> and begin tracking services.
+     * 
+     * <p>
+     * Services which match the search criteria specified when this
+     * <code>ServiceTracker</code> was created are now tracked by this
+     * <code>ServiceTracker</code>.
+     * 
+     * @param trackAllServices If <code>true</code>, then this
+     *        <code>ServiceTracker</code> will track all matching services
+     *        regardless of class loader accessibility. If <code>false</code>,
+     *        then this <code>ServiceTracker</code> will only track matching
+     *        services which are class loader accessible to the bundle whose
+     *        <code>BundleContext</code> is used by this
+     *        <code>ServiceTracker</code>.
+     * @param trackAllAspects If <code>true</code> then this
+     *        <code>ServiceTracker</code> will track all aspects regardless
+     *        of their rank. If <code>false</code> only the highest ranked
+     *        aspects (or the original service if there are no aspects) will
+     *        be tracked. The latter is the default.
+     * @throws java.lang.IllegalStateException If the <code>BundleContext</code>
+     *         with which this <code>ServiceTracker</code> was created is no
+     *         longer valid.
+     */
+	public void open(boolean trackAllServices, boolean trackAllAspects) {
+		if (debug) {
+			System.out.println("[ServiceTracker] " + debugKey + " T" + Thread.currentThread().getId() + " open");
+		}
+		final Tracked t;
+		synchronized (this) {
+			if (tracked != null) {
+				return;
+			}
+			if (DEBUG) {
+				System.out.println("ServiceTracker.open: " + filter); 
+			}
+			m_trackAllAspects = trackAllAspects;
+			t = trackAllServices ? new AllTracked() : new Tracked();
+			synchronized (t) {
+				try {
+					context.addServiceListener(t, listenerFilter);
+					ServiceReference[] references = null;
+					if (trackClass != null) {
+						references = getInitialReferences(trackAllServices,
+								trackClass, null);
+					}
+					else {
+						if (trackReference != null) {
+							if (trackReference.getBundle() != null) {
+								references = new ServiceReference[] {trackReference};
+							}
+						}
+						else { /* user supplied filter */
+							references = getInitialReferences(trackAllServices,
+									null,
+									(listenerFilter != null) ? listenerFilter
+											: filter.toString());
+						}
+					}
+					/* set tracked with the initial references */
+					t.setInitial(references);
+					
+					// only actually schedules the actions for execution within this synchronized block,
+					// but do the actual execution afterwards.
+					t.trackInitial(); 
+
+				}
+				catch (InvalidSyntaxException e) {
+					throw new RuntimeException(
+							"unexpected InvalidSyntaxException: "
+									+ e.getMessage(), e); 
+				}
+			}
+			tracked = t;
+		}
+		/* Call tracked outside of synchronized region */
+		// just trigger the executor
+		t.getExecutor().execute();
+	}
+
+	/**
+	 * Returns the list of initial <code>ServiceReference</code>s that will be
+	 * tracked by this <code>ServiceTracker</code>.
+	 * 
+	 * @param trackAllServices If <code>true</code>, use
+	 *        <code>getAllServiceReferences</code>.
+	 * @param className The class name with which the service was registered, or
+	 *        <code>null</code> for all services.
+	 * @param filterString The filter criteria or <code>null</code> for all
+	 *        services.
+	 * @return The list of initial <code>ServiceReference</code>s.
+	 * @throws InvalidSyntaxException If the specified filterString has an
+	 *         invalid syntax.
+	 */
+	private ServiceReference[] getInitialReferences(boolean trackAllServices,
+			String className, String filterString)
+			throws InvalidSyntaxException {
+		if (trackAllServices) {
+			return context.getAllServiceReferences(className, filterString);
+		}
+		return context.getServiceReferences(className, filterString);
+	}
+
+	/**
+	 * Close this <code>ServiceTracker</code>.
+	 * 
+	 * <p>
+	 * This method should be called when this <code>ServiceTracker</code> should
+	 * end the tracking of services.
+	 * 
+	 * <p>
+	 * This implementation calls {@link #getServiceReferences()} to get the list
+	 * of tracked services to remove.
+	 */
+	public void close() {
+		final Tracked outgoing;
+		final ServiceReference[] references;
+		synchronized (this) {
+			outgoing = tracked;
+			if (outgoing == null) {
+				return;
+			}
+			if (DEBUG) {
+				System.out.println("ServiceTracker.close: " + filter); 
+			}
+			outgoing.close();
+			references = getServiceReferences();
+			tracked = null;
+			try {
+				context.removeServiceListener(outgoing);
+			}
+			catch (IllegalStateException e) {
+				/* In case the context was stopped. */
+			}
+		}
+		modified(); /* clear the cache */
+		synchronized (outgoing) {
+			outgoing.notifyAll(); /* wake up any waiters */
+		}
+		if (references != null) {
+			for (int i = 0; i < references.length; i++) {
+				outgoing.untrack(references[i], null).execute();
+			}
+		}
+		if (DEBUG) {
+			if ((cachedReference == null) && (cachedService == null)) {
+				System.out
+						.println("ServiceTracker.close[cached cleared]: "
+						+ filter); 
+			}
+		}
+	}
+
+	/**
+	 * Default implementation of the
+	 * <code>ServiceTrackerCustomizer.addingService</code> method.
+	 * 
+	 * <p>
+	 * This method is only called when this <code>ServiceTracker</code> has been
+	 * constructed with a <code>null ServiceTrackerCustomizer</code> argument.
+	 * 
+	 * <p>
+	 * This implementation returns the result of calling <code>getService</code>
+	 * on the <code>BundleContext</code> with which this
+	 * <code>ServiceTracker</code> was created passing the specified
+	 * <code>ServiceReference</code>.
+	 * <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
+	 * {@link #removedService(ServiceReference, Object) removedService} to unget
+	 * the service.
+	 * 
+	 * @param reference The reference to the service being added to this
+	 *        <code>ServiceTracker</code>.
+	 * @return The service object to be tracked for the service added to this
+	 *         <code>ServiceTracker</code>.
+	 * @see ServiceTrackerCustomizer#addingService(ServiceReference)
+	 */
+	public Object addingService(ServiceReference reference) {
+		return context.getService(reference);
+	}
+	
+	public void addedService(ServiceReference reference, Object service) {
+		/* do nothing */
+	}
+
+	/**
+	 * Default implementation of the
+	 * <code>ServiceTrackerCustomizer.modifiedService</code> method.
+	 * 
+	 * <p>
+	 * This method is only called when this <code>ServiceTracker</code> has been
+	 * constructed with a <code>null ServiceTrackerCustomizer</code> argument.
+	 * 
+	 * <p>
+	 * This implementation does nothing.
+	 * 
+	 * @param reference The reference to modified service.
+	 * @param service The service object for the modified service.
+	 * @see ServiceTrackerCustomizer#modifiedService(ServiceReference, Object)
+	 */
+	public void modifiedService(ServiceReference reference, Object service) {
+		/* do nothing */
+	}
+
+	/**
+	 * Default implementation of the
+	 * <code>ServiceTrackerCustomizer.removedService</code> method.
+	 * 
+	 * <p>
+	 * This method is only called when this <code>ServiceTracker</code> has been
+	 * constructed with a <code>null ServiceTrackerCustomizer</code> argument.
+	 * 
+	 * <p>
+	 * This implementation calls <code>ungetService</code>, on the
+	 * <code>BundleContext</code> with which this <code>ServiceTracker</code>
+	 * was created, passing the specified <code>ServiceReference</code>.
+	 * <p>
+	 * This method can be overridden in a subclass. If the default
+	 * implementation of {@link #addingService(ServiceReference) addingService}
+	 * method was used, this method must unget the service.
+	 * 
+	 * @param reference The reference to removed service.
+	 * @param service The service object for the removed service.
+	 * @see ServiceTrackerCustomizer#removedService(ServiceReference, Object)
+	 */
+	public void removedService(ServiceReference reference, Object service) {
+		context.ungetService(reference);
+	}
+
+	/**
+	 * Wait for at least one service to be tracked by this
+	 * <code>ServiceTracker</code>. This method will also return when this
+	 * <code>ServiceTracker</code> is closed.
+	 * 
+	 * <p>
+	 * It is strongly recommended that <code>waitForService</code> is not used
+	 * during the calling of the <code>BundleActivator</code> methods.
+	 * <code>BundleActivator</code> methods are expected to complete in a short
+	 * period of time.
+	 * 
+	 * <p>
+	 * This implementation calls {@link #getService()} to determine if a service
+	 * is being tracked.
+	 * 
+	 * @param timeout The time interval in milliseconds to wait. If zero, the
+	 *        method will wait indefinitely.
+	 * @return Returns the result of {@link #getService()}.
+	 * @throws InterruptedException If another thread has interrupted the
+	 *         current thread.
+	 * @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) {
+			final Tracked t = tracked();
+			if (t == null) { /* if ServiceTracker is not open */
+				return null;
+			}
+			synchronized (t) {
+				if (t.size() == 0) {
+					t.wait(timeout);
+				}
+			}
+			object = getService(); 
+			if (timeout > 0) {
+				return object;
+			}
+		}
+		return object;
+	}
+
+	/**
+	 * Return an array of <code>ServiceReference</code>s for all services being
+	 * tracked by this <code>ServiceTracker</code>.
+	 * 
+	 * @return Array of <code>ServiceReference</code>s or <code>null</code> if
+	 *         no services are being tracked.
+	 */
+	public ServiceReference[] getServiceReferences() {
+		final Tracked t = tracked();
+		if (t == null) { /* if ServiceTracker is not open */
+			return null;
+		}
+		synchronized (t) {
+			int length = t.size();
+			if (length == 0) {
+				return null;
+			}
+			return (ServiceReference[]) t
+					.getTracked(new ServiceReference[length]);
+		}
+	}
+	
+	/**
+	 * Returns a boolean indicating whether this <code>ServiceTracker</code> is tracking any services.
+	 * 
+	 * @return true if services are being tracked, false if no services are being tracked.
+	 */
+	public boolean hasReference() {
+		if (cachedReference != null) {
+			return true;
+		}
+		final Tracked t = tracked();
+		if (t == null) { /* if ServiceTracker is not open */
+			return false;
+		}
+		synchronized (t) {
+			int length = t.size();
+			return length > 0;
+		}
+	}
+
+	/**
+	 * Returns a <code>ServiceReference</code> for one of the services being
+	 * tracked by this <code>ServiceTracker</code>.
+	 * 
+	 * <p>
+	 * If multiple services are being tracked, the service with the highest
+	 * ranking (as specified in its <code>service.ranking</code> property) is
+	 * returned. If there is a tie in ranking, the service with the lowest
+	 * service ID (as specified in its <code>service.id</code> property); that
+	 * is, the service that was registered first is returned. This is the same
+	 * algorithm used by <code>BundleContext.getServiceReference</code>.
+	 * 
+	 * <p>
+	 * This implementation calls {@link #getServiceReferences()} to get the list
+	 * of references for the tracked services.
+	 * 
+	 * @return A <code>ServiceReference</code> or <code>null</code> if no
+	 *         services are being tracked.
+	 * @since 1.1
+	 */
+	public ServiceReference getServiceReference() {
+		ServiceReference reference = cachedReference;
+		if (reference != null) {
+			if (DEBUG) {
+				System.out
+						.println("ServiceTracker.getServiceReference[cached]: "
+								+ filter); 
+			}
+			return reference;
+		}
+		if (DEBUG) {
+			System.out.println("ServiceTracker.getServiceReference: " + filter); 
+		}
+		ServiceReference[] references = getServiceReferences(); 
+		int length = (references == null) ? 0 : references.length;
+		if (length == 0) { /* if no service is being tracked */
+			return null;
+		}
+		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 cachedReference = references[index];
+	}
+
+	/**
+	 * Returns the service object for the specified
+	 * <code>ServiceReference</code> if the specified referenced service is
+	 * being tracked by this <code>ServiceTracker</code>.
+	 * 
+	 * @param reference The reference to the desired service.
+	 * @return A service object or <code>null</code> if the service referenced
+	 *         by the specified <code>ServiceReference</code> is not being
+	 *         tracked.
+	 */
+	public Object getService(ServiceReference reference) {
+		final Tracked t = tracked();
+		if (t == null) { /* if ServiceTracker is not open */
+			return null;
+		}
+		synchronized (t) {
+			return t.getCustomizedObject(reference);
+		}
+	}
+
+	/**
+	 * Return an array of service objects for all services being tracked by this
+	 * <code>ServiceTracker</code>.
+	 * 
+	 * <p>
+	 * This implementation calls {@link #getServiceReferences()} to get the list
+	 * of references for the tracked services and then calls
+	 * {@link #getService(ServiceReference)} for each reference to get the
+	 * tracked service object.
+	 * 
+	 * @return An array of service objects or <code>null</code> if no services
+	 *         are being tracked.
+	 */
+	public Object[] getServices() {
+		final Tracked t = tracked();
+		if (t == null) { /* if ServiceTracker is not open */
+			return null;
+		}
+		synchronized (t) {
+			ServiceReference[] references = getServiceReferences(); 
+			int length = (references == null) ? 0 : references.length;
+			if (length == 0) {
+				return null;
+			}
+			Object[] objects = new Object[length];
+			for (int i = 0; i < length; i++) {
+				objects[i] = getService(references[i]); 
+			}
+			return objects;
+		}
+	}
+
+	/**
+	 * Returns a service object for one of the services being tracked by this
+	 * <code>ServiceTracker</code>.
+	 * 
+	 * <p>
+	 * If any services are being tracked, this implementation returns the result
+	 * of calling <code>getService(getServiceReference())</code>.
+	 * 
+	 * @return A service object or <code>null</code> if no services are being
+	 *         tracked.
+	 */
+	public Object getService() {
+		Object service = cachedService;
+		if (service != null) {
+			if (DEBUG) {
+				System.out
+						.println("ServiceTracker.getService[cached]: "
+						+ filter); 
+			}
+			return service;
+		}
+		if (DEBUG) {
+			System.out.println("ServiceTracker.getService: " + filter); 
+		}
+		ServiceReference reference = getServiceReference(); 
+		if (reference == null) {
+			return null;
+		}
+		return cachedService = getService(reference); 
+	}
+
+	/**
+	 * Remove a service from this <code>ServiceTracker</code>.
+	 * 
+	 * The specified service will be removed from this
+	 * <code>ServiceTracker</code>. If the specified service was being tracked
+	 * then the <code>ServiceTrackerCustomizer.removedService</code> method will
+	 * be called for that service.
+	 * 
+	 * @param reference The reference to the service to be removed.
+	 */
+	public void remove(ServiceReference reference) {
+		final Tracked t = tracked();
+		if (t == null) { /* if ServiceTracker is not open */
+			return;
+		}
+		t.untrack(reference, null).execute();
+	}
+
+	/**
+	 * Return the number of services being tracked by this
+	 * <code>ServiceTracker</code>.
+	 * 
+	 * @return The number of services being tracked.
+	 */
+	public int size() {
+		final Tracked t = tracked();
+		if (t == null) { /* if ServiceTracker is not open */
+			return 0;
+		}
+		synchronized (t) {
+			return t.size();
+		}
+	}
+
+	/**
+	 * Returns the tracking count for this <code>ServiceTracker</code>.
+	 * 
+	 * The tracking count is initialized to 0 when this
+	 * <code>ServiceTracker</code> is opened. Every time a service is added,
+	 * modified or removed from this <code>ServiceTracker</code>, the tracking
+	 * count is incremented.
+	 * 
+	 * <p>
+	 * The tracking count can be used to determine if this
+	 * <code>ServiceTracker</code> has added, modified 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, modified or removed from this <code>ServiceTracker</code>
+	 * since the previous tracking count was collected.
+	 * 
+	 * @since 1.2
+	 * @return The tracking count for this <code>ServiceTracker</code> or -1 if
+	 *         this <code>ServiceTracker</code> is not open.
+	 */
+	public int getTrackingCount() {
+		final Tracked t = tracked();
+		if (t == null) { /* if ServiceTracker is not open */
+			return -1;
+		}
+		synchronized (t) {
+			return t.getTrackingCount();
+		}
+	}
+
+	/**
+	 * Called by the Tracked object whenever the set of tracked services is
+	 * modified. Clears the cache.
+	 */
+	/*
+	 * This method must not be synchronized since it is called by Tracked while
+	 * Tracked is synchronized. We don't want synchronization interactions
+	 * between the listener thread and the user thread.
+	 */
+	void modified() {
+		cachedReference = null; /* clear cached value */
+		cachedService = null; /* clear cached value */
+		if (DEBUG) {
+			System.out.println("ServiceTracker.modified: " + filter); 
+		}
+	}
+
+	/**
+	 * Inner class which subclasses AbstractTracked. This class is the
+	 * <code>ServiceListener</code> object for the tracker.
+	 * 
+	 * @ThreadSafe
+	 */
+	class Tracked extends AbstractTracked implements ServiceListener {
+	    /**
+	     * A list of services that are currently hidden because there is an aspect available with a higher ranking.
+	     * @GuardedBy this
+	     */
+	    private final Map<Long, TreeSet<ServiceReference>> m_highestTrackedCache = new HashMap<>();
+	    private final Map<Long, TreeSet<ServiceReference>> m_highestHiddenCache = new HashMap<>();
+        
+        private ServiceReference highestTrackedCache(long serviceId) {
+            Long sid = Long.valueOf(serviceId);
+            synchronized (this) {
+            	TreeSet<ServiceReference> services = m_highestTrackedCache.get(sid);
+            	if (services != null && services.size() > 0) {
+            		ServiceReference result = (ServiceReference) services.last();
+            		return result;
+            	}
+			}
+            return null;
+        }
+        
+        private void addHighestTrackedCache(ServiceReference reference) {
+            Long serviceId = ServiceUtil.getServiceIdObject(reference);
+            synchronized (this) {
+            	TreeSet<ServiceReference> services = m_highestTrackedCache.get(serviceId);
+            	if (services == null) {
+            		services = new TreeSet<ServiceReference>();
+            		m_highestTrackedCache.put(serviceId, services);
+            	}
+            	services.add(reference);
+			}
+        }
+        
+        private void removeHighestTrackedCache(ServiceReference reference) {
+            Long serviceId = ServiceUtil.getServiceIdObject(reference);
+            synchronized (this) {
+            	TreeSet<ServiceReference> services = m_highestTrackedCache.get(serviceId);
+            	if (services != null) {
+            		services.remove(reference);
+            	}
+			}
+        }
+        
+        private void clearHighestTrackedCache() {
+        	synchronized (this) {
+        		m_highestTrackedCache.clear();
+			}
+        }
+        
+        private ServiceReference highestHiddenCache(long serviceId) {
+            Long sid = Long.valueOf(serviceId);
+            synchronized (this) {
+            	TreeSet<ServiceReference> services = m_highestHiddenCache.get(sid);
+	            if (services != null && services.size() > 0) {
+	                ServiceReference result = (ServiceReference) services.last();
+	                return result;
+	            }
+            }
+            return null;
+        }
+        
+        private void addHighestHiddenCache(ServiceReference reference) {
+            Long serviceId = ServiceUtil.getServiceIdObject(reference);
+            synchronized (this) {
+            	TreeSet<ServiceReference> services = m_highestHiddenCache.get(serviceId);
+            	if (services == null) {
+            		services = new TreeSet<ServiceReference>();
+            		m_highestHiddenCache.put(serviceId, services);
+            	}
+            	services.add(reference);
+			}
+        }
+        
+        private void removeHighestHiddenCache(ServiceReference reference) {
+            Long serviceId = ServiceUtil.getServiceIdObject(reference);
+            synchronized (this) {
+            	TreeSet<ServiceReference> services = m_highestHiddenCache.get(serviceId);
+            	if (services != null) {
+            		services.remove(reference);
+            	}
+			}
+        }
+
+        /**
+         * Hide a service reference, placing it in the list of hidden services.
+         * 
+         * @param ref the service reference to add to the hidden list
+         */
+        private void hide(ServiceReference ref) {
+            addHighestHiddenCache(ref);
+        }
+        
+        /**
+         * Unhide a service reference, removing it from the list of hidden services.
+         * 
+         * @param ref the service reference to remove from the hidden list
+         */
+        private void unhide(ServiceReference ref) {
+            removeHighestHiddenCache(ref);
+        }
+	    
+		/**
+		 * Tracked constructor.
+		 */
+		Tracked() {
+			super();
+			setTracked(new HashMapCache<Object, Object>());
+		}
+		
+		void setInitial(Object[] list) {
+		    if (list == null) {
+		        return;
+		    }
+		    if (m_trackAllAspects) {
+		    	// not hiding aspects
+		    	super.setInitial(list);
+		    } else { 
+			    Map<Long, RankedService> highestRankedServiceMap = new HashMap<>();
+			    for (int i = 0; i < list.length; i++) {
+			    	ServiceReference sr = (ServiceReference) list[i];
+			    	if (sr != null) {
+				    	Long serviceId = ServiceUtil.getServiceIdAsLong(sr);
+				    	int ranking = ServiceUtil.getRanking(sr);
+				    	
+				    	RankedService rs = (RankedService) highestRankedServiceMap.get(serviceId);
+				    	if (rs == null) {
+				    	    // the service did not exist yet in our map
+				    	    highestRankedServiceMap.put(serviceId, new RankedService(ranking, sr));
+				    	}
+				    	else if (ranking > rs.getRanking()) {
+	                        // the service replaces a lower ranked one
+				    	    hide(rs.getServiceReference());
+				    	    rs.update(ranking, sr);
+				    	}
+				    	else {
+	                        // the service does NOT replace a lower ranked one
+				    	    hide(sr);
+				    	}
+			    	}
+			    }
+			    if (highestRankedServiceMap.size() > 0) {
+			        Object[] result = new Object[highestRankedServiceMap.size()];
+			        int index = 0;
+			        for(Iterator<Entry<Long, RankedService>> it = highestRankedServiceMap.entrySet().iterator(); it.hasNext(); ) {
+			        	Entry<Long, RankedService> entry = it.next();
+			        	result[index] = ((RankedService)entry.getValue()).getServiceReference();
+			        	index++;
+			        }
+			        super.setInitial(result);	    	
+			    }
+		    }
+		}
+
+		/**
+		 * <code>ServiceListener</code> method for the
+		 * <code>ServiceTracker</code> class. This method must NOT be
+		 * synchronized to avoid deadlock potential.
+		 * 
+		 * @param event <code>ServiceEvent</code> object from the framework.
+		 */
+		public void serviceChanged(final ServiceEvent event) {
+			if (m_trackAllAspects) {
+				serviceChangedIncludeAspects(event);
+			}
+			else {
+				serviceChangedHideAspects(event);
+			}
+		}
+		
+        public void serviceChangedIncludeAspects(final ServiceEvent event) {
+            /*
+             * Check if we had a delayed call (which could happen when we
+             * close).
+             */
+            if (closed) {
+                return;
+            }
+            final ServiceReference reference = event.getServiceReference();
+			if (debug) {
+				System.out.println("[ServiceTracker] " + debugKey + " T" + Thread.currentThread().getId() + " [serviceChangedIncludeAspects] " + reference.getProperty("service.ranking"));
+			}            
+            if (DEBUG) {
+                System.out
+                        .println("ServiceTracker.Tracked.serviceChanged["
+                        + event.getType() + "]: " + reference);  
+            }
+
+            switch (event.getType()) {
+                case ServiceEvent.REGISTERED :
+                case ServiceEvent.MODIFIED :
+                    if (listenerFilter != null) { // service listener added with
+                        // filter
+                        track(reference, event).execute();
+                        /*
+                         * If the customizer throws an unchecked exception, it
+                         * is safe to let it propagate
+                         */
+                    }
+                    else { // service listener added without filter
+                        if (filter.match(reference)) {
+                            track(reference, event).execute();
+                            /*
+                             * If the customizer throws an unchecked exception,
+                             * it is safe to let it propagate
+                             */
+                        }
+                        else {
+                            untrack(reference, event).execute();
+                            /*
+                             * If the customizer throws an unchecked exception,
+                             * it is safe to let it propagate
+                             */
+                        }
+                    }
+                    break;
+                case 8 /* ServiceEvent.MODIFIED_ENDMATCH */ :
+                case ServiceEvent.UNREGISTERING :
+                    untrack(reference, event).execute();
+                    /*
+                     * If the customizer throws an unchecked exception, it is
+                     * safe to let it propagate
+                     */
+                    break;
+            }
+        }
+        
+        private boolean isModifiedEndmatchSupported() {
+        	return listenerFilter != null;
+        }
+        
+        private AtomicInteger step = new AtomicInteger();
+		
+		public void serviceChangedHideAspects(final ServiceEvent event) {
+			int n = step.getAndIncrement();
+			/*
+			 * Check if we had a delayed call (which could happen when we
+			 * close).
+			 */
+			if (closed) {
+				return;
+			}
+			final ServiceReference reference = event.getServiceReference();
+			if (DEBUG) {
+				System.out
+						.println(n + " ServiceTracker.Tracked.serviceChanged["
+						+ event.getType() + "]: " + reference);  
+			}
+
+			long sid = ServiceUtil.getServiceId(reference);
+			AbstractCustomizerActionSet actionSet = null;
+			synchronized(this) {
+				switch (event.getType()) {
+					case ServiceEvent.REGISTERED :
+					case ServiceEvent.MODIFIED :
+					    ServiceReference higherRankedReference = null;
+					    ServiceReference lowerRankedReference = null;
+					    ServiceReference highestTrackedReference = highestTrackedCache(sid);
+					    if (highestTrackedReference != null) {
+					        int ranking = ServiceUtil.getRanking(reference);
+					        int highestTrackedRanking = ServiceUtil.getRanking(highestTrackedReference);
+					        if (ranking > highestTrackedRanking) {
+					            // found a higher ranked one!
+					            if (DEBUG) {
+					                System.out.println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: Found a higher ranked aspect: " + ServiceUtil.toString(reference) + " vs " + ServiceUtil.toString(highestTrackedReference));
+					            }
+					            higherRankedReference = highestTrackedReference;
+					        }
+					        else if (ranking < highestTrackedRanking) {
+					            // found lower ranked one!
+	                            if (DEBUG) {
+	                                System.out.println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: Found a lower ranked aspect: " + ServiceUtil.toString(reference) + " vs " + ServiceUtil.toString(highestTrackedReference));
+	                            }
+					            lowerRankedReference = highestTrackedReference;
+					        }
+					    }
+						if (isModifiedEndmatchSupported()) { // either registered or modified
+							actionSet = registerOrUpdate(event, reference, higherRankedReference, lowerRankedReference);
+						}
+						else { // service listener added without filter
+							if (filter.match(reference)) {
+		                        actionSet = registerOrUpdate(event, reference, higherRankedReference, lowerRankedReference);
+							}
+							else {
+			                    actionSet = unregister(event, reference, sid);
+							}
+						}
+						break;
+	                case 8 /* ServiceEvent.MODIFIED_ENDMATCH */ : // handle as unregister
+					case ServiceEvent.UNREGISTERING :
+						actionSet = unregister(event, reference, sid);
+						/*
+						 * If the customizer throws an unchecked exception, it is
+						 * safe to let it propagate
+						 */
+						break;
+				}
+				// schedule the actionset for execution. We'll use a serial executor to prevent the actions to
+				// be performed out of order.
+				final AbstractCustomizerActionSet commandActionSet = actionSet;
+				if (commandActionSet != null) {
+                    getExecutor().schedule(new Runnable() {
+
+                        @Override
+                        public void run() {
+                            commandActionSet.execute();
+                        }
+
+                    });
+				}
+			}
+			getExecutor().execute();
+		}
+		
+		private AbstractCustomizerActionSet registerOrUpdate(final ServiceEvent event,
+				final ServiceReference reference, ServiceReference higher,
+				ServiceReference lower) {
+			if (debug) {
+//				System.out.println("[ServiceTracker] " + debugKey + " T" + Thread.currentThread().getId() + " [registerOrUpdate] lower: " + lower + ", higher: " + higher);
+			}
+			AbstractCustomizerActionSet actionSet = null;
+			if (lower != null) {
+			    hide(reference);
+			}
+			else {
+				actionSet = track(reference, event);
+		        if (higher != null) {
+	                actionSet.appendActionSet(untrack(higher, null));
+	                hide(higher);
+			    }
+			}
+			/*
+			 * If the customizer throws an unchecked exception, it
+			 * is safe to let it propagate
+			 */
+			return actionSet;
+		}
+
+		private AbstractCustomizerActionSet unregister(final ServiceEvent event,
+				final ServiceReference reference, long sid) {
+			if (debug) {
+				System.out.println("[ServiceTracker] " + debugKey + " T" + Thread.currentThread().getId() + " [unregister] " + reference.getProperty("service.ranking"));
+			}
+			AbstractCustomizerActionSet actionSet = null;
+			ServiceReference ht = highestTrackedCache(sid);
+			if (reference.equals(ht)) {
+		        ServiceReference hh = highestHiddenCache(sid);
+		        
+		        if (hh != null) {
+		            unhide(hh);
+		            actionSet = track(hh, null);
+		        }
+		        if (actionSet == null) {
+		        	actionSet = untrack(reference, event);
+		        } else {
+		        	actionSet.appendActionSet(untrack(reference, event));
+		        }
+			}
+			else {
+			    unhide(reference);
+			}
+			return actionSet;
+		}
+		
+		
+
+		/**
+		 * Increment the tracking count and tell the tracker there was a
+		 * modification.
+		 * 
+		 * @GuardedBy this
+		 */
+		void modified() {
+			super.modified(); /* increment the modification count */
+			ServiceTracker.this.modified();
+		}
+
+		/**
+		 * Call the specific customizer adding method. This method must not be
+		 * called while synchronized on this object.
+		 * 
+		 * @param item Item to be tracked.
+		 * @param related Action related object.
+		 * @return Customized object for the tracked item or <code>null</code>
+		 *         if the item is not to be tracked.
+		 */
+		Object customizerAdding(final Object item,
+				final Object related) {
+			return customizer.addingService((ServiceReference) item);
+		}
+		
+		void customizerAdded(final Object item, final Object related, final Object object) {
+			customizer.addedService((ServiceReference) item, object);
+		}
+
+		/**
+		 * Call the specific customizer modified method. This method must not be
+		 * called while synchronized on this object.
+		 * 
+		 * @param item Tracked item.
+		 * @param related Action related object.
+		 * @param object Customized object for the tracked item.
+		 */
+		void customizerModified(final Object item,
+				final Object related, final Object object) {
+			customizer.modifiedService((ServiceReference) item, object);
+		}
+
+		/**
+		 * Call the specific customizer removed method. This method must not be
+		 * called while synchronized on this object.
+		 * 
+		 * @param item Tracked item.
+		 * @param related Action related object.
+		 * @param object Customized object for the tracked item.
+		 */
+		void customizerRemoved(final Object item,
+				final Object related, final Object object) {
+			customizer.removedService((ServiceReference) item, object);
+		}
+		
+		class HashMapCache<K, V> extends LinkedHashMap<K, V> {
+
+			private static final long serialVersionUID = 1627005136730183946L;
+
+			public V put(K key, V value) {
+		        addHighestTrackedCache((ServiceReference) key);
+		        return super.put(key, value);
+		    }
+
+		    public void putAll(Map<? extends K, ? extends V> m) {
+		        Iterator<? extends K> i = m.keySet().iterator();
+		        while (i.hasNext()) {
+		            addHighestTrackedCache((ServiceReference) i.next());
+		        }
+		        super.putAll(m);
+		    }
+
+		    public V remove(Object key) {
+		        removeHighestTrackedCache((ServiceReference) key);
+		        return super.remove(key);
+		    }
+
+		    public void clear() {
+		        clearHighestTrackedCache();
+		        super.clear();
+		    }
+		}
+
+		@Override
+		AbstractCustomizerActionSet createCustomizerActionSet() {
+			// This actions set deliberately postpones invocation of the customizer methods to be able to combine added and removed
+			// into a single swap call.
+			return new AbstractCustomizerActionSet() {
+				
+				@Override
+				public void addCustomizerAdded(Object item, Object related,
+						Object object) {
+					if (debug) {
+//						System.out.println("[ServiceTracker] " + debugKey + " T" + Thread.currentThread().getId() + " addCustomizerAdded " + object);
+					}
+					super.addCustomizerAdded(item, related, object);
+				}
+				
+				@Override
+				public void addCustomizerModified(Object item, Object related,
+						Object object) {
+					if (debug) {
+//						System.out.println("[ServiceTracker] " + debugKey + " T" + Thread.currentThread().getId() + " addCustomizerModified " + object);
+					}					
+					super.addCustomizerModified(item, related, object);
+				}
+				
+				@Override
+				public void addCustomizerRemoved(Object item, Object related,
+						Object object) {
+					if (debug) {
+//						System.out.println("[ServiceTracker] " + debugKey + " T" + Thread.currentThread().getId() + " addCustomizerRemoved " + object);
+					}					
+					super.addCustomizerRemoved(item, related, object);
+				}
+				
+				@Override
+				void execute() {
+					// inspect the actions and check whether we should perform a swap
+					List<CustomizerAction> actions = getActions();
+					if (actions.size() > 2) {
+						throw new IllegalStateException("Unexpected action count: " + actions.size());
+					}
+					if (actions.size() == 2 && actions.get(0).getType() == Type.ADDED && actions.get(1).getType() == Type.REMOVED) {
+						// ignore related
+						// item = ServiceReference
+						// object = service
+						debug("swapped");
+						if (debug) {
+							System.out.println("[ServiceTracker] " + debugKey + " T" + Thread.currentThread().getId() + " swapping " + actions.get(1).getObject() + " with " + actions.get(0).getObject());
+						}
+						customizer.swappedService((ServiceReference)actions.get(1).getItem(), actions.get(1).getObject(), (ServiceReference)actions.get(0).getItem(), actions.get(0).getObject());
+					} else {
+						// just sequentially call the customizer methods
+						for (CustomizerAction action : getActions()) {
+							try {
+								switch (action.getType()) {
+									case ADDED: 
+										debug(Thread.currentThread().getId() + " added");
+										if (debug) {
+											System.out.println("[ServiceTracker] " + debugKey + " T" + Thread.currentThread().getId() + " adding " + action.getObject());
+										}
+										customizerAdded(action.getItem(), action.getRelated(), action.getObject());
+										break;
+									case MODIFIED:
+										debug("modified");
+										customizerModified(action.getItem(), action.getRelated(), action.getObject());
+										break;
+									case REMOVED:
+										debug("removed");
+										if (debug) {
+											System.out.println("[ServiceTracker] " + debugKey + " T" + Thread.currentThread().getId() + " removing " + action.getObject());
+										}
+										customizerRemoved(action.getItem(), action.getRelated(), action.getObject());
+								}
+							} catch (Exception e) {
+								// just continue. log messages will be printed elsewhere.
+							}
+						}
+					}
+				}
+			};
+		}
+
+	}
+	
+	private void debug(String message) {
+		if (customizer.toString().equals("ServiceDependency[interface dm.it.AspectRaceTest$S (&(!(org.apache.felix.dependencymanager.aspect=*))(id=1))]")) {
+//			System.out.println(message);
+		}
+	}
+
+	/**
+	 * Subclass of Tracked which implements the AllServiceListener interface.
+	 * This class is used by the ServiceTracker if open is called with true.
+	 * 
+	 * @since 1.3
+	 * @ThreadSafe
+	 */
+	class AllTracked extends Tracked implements AllServiceListener {
+		/**
+		 * AllTracked constructor.
+		 */
+		AllTracked() {
+			super();
+            setTracked(new HashMapCache<Object, Object>());
+		}
+	}
+	
+	/**
+	 * Holds a ranking and a service reference that can be updated if necessary.
+	 */
+	private static final class RankedService {
+		private int m_ranking;
+		private ServiceReference m_serviceReference;
+		
+		public RankedService(int ranking, ServiceReference serviceReference) {
+			m_ranking = ranking;
+			m_serviceReference = serviceReference;
+		}
+		
+        public void update(int ranking, ServiceReference serviceReference) {
+            m_ranking = ranking;
+            m_serviceReference = serviceReference;
+        }
+		
+		public int getRanking() {
+			return m_ranking;
+		}
+		
+		public ServiceReference getServiceReference() {
+			return m_serviceReference;
+		}
+	}
+
+	@Override
+	public void swappedService(ServiceReference reference, Object service,
+			ServiceReference newReference, Object newService) {
+		
+	}
+
+	// Package private, used for unit testing Tracked
+	Tracked getTracked() {
+		return tracked;
+	}
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/ServiceTrackerCustomizer.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/ServiceTrackerCustomizer.java
new file mode 100644
index 0000000..ab3abd2
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/ServiceTrackerCustomizer.java
@@ -0,0 +1,115 @@
+package org.apache.felix.dm.tracker;
+/*
+ * Copyright (c) OSGi Alliance (2000, 2008). All Rights Reserved.
+ * 
+ * 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.
+ */
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * The <code>ServiceTrackerCustomizer</code> interface allows a
+ * <code>ServiceTracker</code> to customize the service objects that are
+ * tracked. A <code>ServiceTrackerCustomizer</code> is called when a service is
+ * being added to a <code>ServiceTracker</code>. The
+ * <code>ServiceTrackerCustomizer</code> can then return an object for the
+ * tracked service. A <code>ServiceTrackerCustomizer</code> is also called when
+ * a tracked service is modified or has been removed from a
+ * <code>ServiceTracker</code>.
+ * 
+ * <p>
+ * The methods in this interface may be called as the result of a
+ * <code>ServiceEvent</code> being received by a <code>ServiceTracker</code>.
+ * Since <code>ServiceEvent</code>s are synchronously delivered by the
+ * Framework, it is highly recommended that implementations of these methods do
+ * not register (<code>BundleContext.registerService</code>), modify (
+ * <code>ServiceRegistration.setProperties</code>) or unregister (
+ * <code>ServiceRegistration.unregister</code>) a service while being
+ * synchronized on any object.
+ * 
+ * <p>
+ * The <code>ServiceTracker</code> class is thread-safe. It does not call a
+ * <code>ServiceTrackerCustomizer</code> while holding any locks.
+ * <code>ServiceTrackerCustomizer</code> implementations must also be
+ * thread-safe.
+ * 
+ * @ThreadSafe
+ * @version $Revision: 5874 $
+ */
+public interface ServiceTrackerCustomizer {
+	/**
+	 * A service is being added to the <code>ServiceTracker</code>.
+	 * 
+	 * <p>
+	 * This method is called before a service which matched the search
+	 * parameters of the <code>ServiceTracker</code> is added to the
+	 * <code>ServiceTracker</code>. This method should return the service object
+	 * to be tracked for the specified <code>ServiceReference</code>. The
+	 * returned service object is stored in the <code>ServiceTracker</code> and
+	 * is available from the <code>getService</code> and
+	 * <code>getServices</code> methods.
+	 * 
+	 * @param reference The reference to the service being added to the
+	 *        <code>ServiceTracker</code>.
+	 * @return The service object to be tracked for the specified referenced
+	 *         service or <code>null</code> if the specified referenced service
+	 *         should not be tracked.
+	 */
+	public Object addingService(ServiceReference reference);
+
+	/** marrs: A service has been added to the ServiceTracker. */
+	public void addedService(ServiceReference reference, Object service);
+
+	/**
+	 * A service tracked by the <code>ServiceTracker</code> has been modified.
+	 * 
+	 * <p>
+	 * This method is called when a service being tracked by the
+	 * <code>ServiceTracker</code> has had it properties modified.
+	 * 
+	 * @param reference The reference to the service that has been modified.
+	 * @param service The service object for the specified referenced service.
+	 */
+	public void modifiedService(ServiceReference reference, Object service);
+	
+	/**
+	 * A service tracked by the <code>ServiceTracker</code> has an aspect service 
+	 * added or removed for a tracked service.
+	 * 
+	 * <p>
+	 * This method is called when an aspect service has been either added or removed 
+	 * for a tracked service. This method will only be called when there's a new 
+	 * highest ranked service as result of adding or removal of the aspect service.
+	 * In this case the previously highest ranked service is 'swapped' for the new
+	 * highest ranked service ensuring the client always gets the highest ranked
+	 * aspect. 
+	 * 
+	 * @param reference The reference for the old highest ranked service.
+	 * @param service The service object for the old highest ranked service.
+	 * @param newReference The reference to the new highest ranked service.
+	 * @param newService The service object for the new highest ranked service.
+	 */
+	public void swappedService(ServiceReference reference, Object service, ServiceReference newReference, Object newService);
+
+	/**
+	 * A service tracked by the <code>ServiceTracker</code> has been removed.
+	 * 
+	 * <p>
+	 * This method is called after a service is no longer being tracked by the
+	 * <code>ServiceTracker</code>.
+	 * 
+	 * @param reference The reference to the service that has been removed.
+	 * @param service The service object for the specified referenced service.
+	 */
+	public void removedService(ServiceReference reference, Object service);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/packageinfo b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/packageinfo
new file mode 100644
index 0000000..bd33369
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/tracker/packageinfo
@@ -0,0 +1 @@
+version 4.0.0
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager/test/.gitignore b/dependencymanager/org.apache.felix.dependencymanager/test/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/test/.gitignore
diff --git a/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/tracker/TrackedTest.java b/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/tracker/TrackedTest.java
new file mode 100644
index 0000000..9b71939
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/tracker/TrackedTest.java
@@ -0,0 +1,316 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.tracker;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.tracker.ServiceTracker;
+import org.apache.felix.dm.tracker.ServiceTrackerCustomizer;
+import org.apache.felix.dm.tracker.ServiceTracker.Tracked;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class TrackedTest {
+
+	@Test
+	public void testSetInitialHideAspects() {
+		System.out.println("testSetInitialHideAspects");
+		TestCustomizer customizer = new TestCustomizer();
+		
+		ServiceTracker tracker = new TestTracker(customizer);
+		tracker.open();
+		Tracked tracked = tracker.getTracked();
+
+		Object[] initialReferences = new Object[] {
+				createServiceReference(1L),
+				createServiceReference(2L, 1L, 10),
+				createServiceReference(3L),
+				createServiceReference(4L, 1L, 5),
+				createServiceReference(5L, 3L, 5),
+		};
+		tracked.setInitial(initialReferences);
+		tracked.trackInitial();
+		tracked.getExecutor().execute();
+		assertArrayEquals(new Long[] { 2L, 5L }, customizer.getServiceReferenceIds());
+	}
+	
+	@Test
+	public void testUnHideAspect() {
+		System.out.println("testUnhideAspect");
+		TestCustomizer customizer = new TestCustomizer();
+		
+		ServiceTracker tracker = new TestTracker(customizer);
+		tracker.open();
+		Tracked tracked = tracker.getTracked();
+		
+		ServiceReference[] initialReferences = new ServiceReference[] {
+				createServiceReference(1L),
+				createServiceReference(2L, 1L, 10),
+				createServiceReference(3L),
+				createServiceReference(4L, 1L, 5),
+				createServiceReference(5L, 3L, 5),
+		};
+		tracked.setInitial(initialReferences);
+		tracked.trackInitial();
+		tracked.getExecutor().execute();
+		assertArrayEquals(new Long[] { 2L, 5L }, customizer.getServiceReferenceIds());
+		
+		// create a service event that unregisters service with id 2, we would expect it to be swapped with 4.
+		ServiceEvent event = new ServiceEvent(ServiceEvent.UNREGISTERING, initialReferences[1]);
+		tracked.serviceChanged(event);
+		assertArrayEquals(new Long[] { 5L, 4L }, customizer.getServiceReferenceIds());
+		// create a service event that unregisters service with id 4, we would expect it to be swapped with 1.
+		event = new ServiceEvent(ServiceEvent.UNREGISTERING, initialReferences[3]);
+		tracked.serviceChanged(event);
+		assertArrayEquals(new Long[] { 5L, 1L }, customizer.getServiceReferenceIds());
+	}	
+	
+	@Test
+	public void testHideAspect() {
+		System.out.println("testHideAspect");
+		TestCustomizer customizer = new TestCustomizer();
+		
+		ServiceTracker tracker = new TestTracker(customizer);
+		tracker.open();
+		Tracked tracked = tracker.getTracked();
+		
+		ServiceReference[] initialReferences = new ServiceReference[] {
+				createServiceReference(1L),
+				createServiceReference(2L, 1L, 10),
+				createServiceReference(3L),
+				createServiceReference(4L, 1L, 5),
+				createServiceReference(5L, 3L, 5),
+		};
+		tracked.setInitial(initialReferences);
+		tracked.trackInitial();
+		tracked.getExecutor().execute();
+		assertArrayEquals(new Long[] { 2L, 5L }, customizer.getServiceReferenceIds());
+		
+		// create a service event that registers another but lower ranked aspect for service with id 1. 
+		ServiceReference newReference = createServiceReference(6L, 1L, 8);
+		ServiceEvent event = new ServiceEvent(ServiceEvent.REGISTERED, newReference);
+		tracked.serviceChanged(event);
+		assertArrayEquals(new Long[] { 2L, 5L }, customizer.getServiceReferenceIds());
+		
+		// create a service event that unregisters service with id 2, we would expect it to be swapped with 6.
+		event = new ServiceEvent(ServiceEvent.UNREGISTERING, initialReferences[1]);
+		tracked.serviceChanged(event);
+		assertArrayEquals(new Long[] { 5L, 6L }, customizer.getServiceReferenceIds());
+		
+		// create a service event that unregisters service with id 6, we would expect it to be swapped with 4.
+		event = new ServiceEvent(ServiceEvent.UNREGISTERING, newReference);
+		tracked.serviceChanged(event);
+		assertArrayEquals(new Long[] { 5L, 4L }, customizer.getServiceReferenceIds());	
+		
+		// create a service event that registers a higher ranked aspect for service with id 1.
+		ServiceReference higherRankedReference = createServiceReference(7L, 1L, 15);
+		ServiceEvent addHigherRankedEvent = new ServiceEvent(ServiceEvent.REGISTERED, higherRankedReference);
+		tracked.serviceChanged(addHigherRankedEvent);
+		assertArrayEquals(new Long[] { 5L, 7L }, customizer.getServiceReferenceIds());	
+	}		
+	
+	@Test
+	public void testSetInitialTrackAspects() {
+		System.out.println("testSetInitialTrackAspects");
+		TestCustomizer customizer = new TestCustomizer();
+		
+		ServiceTracker tracker = new TestTracker(customizer);
+		tracker.open(false, true);
+		Tracked tracked = tracker.getTracked();
+		
+		Object[] initialReferences = new Object[] {
+				createServiceReference(1L),
+				createServiceReference(2L, 1L, 10),
+				createServiceReference(3L, 1L, 5)
+		};
+		tracked.setInitial(initialReferences);
+		tracked.trackInitial();
+		tracked.getExecutor().execute();
+		assertArrayEquals(new Long[] { 1L, 2L, 3L }, customizer.getServiceReferenceIds());
+	}	
+	
+	private static BundleContext createBundleContext() {
+		BundleContext context = mock(BundleContext.class);
+		when(context.getProperty(Constants.FRAMEWORK_VERSION)).thenReturn(null);
+		return context;
+	}
+	
+	private ServiceReference createServiceReference(Long serviceId) {
+		return createServiceReference(serviceId, null, null);
+	}
+
+	private ServiceReference createServiceReference(Long serviceId, Long aspectId, Integer ranking) {
+		return new TestServiceReference(serviceId, aspectId, ranking);
+	}
+
+	class TestTracker extends ServiceTracker {
+
+		public TestTracker(ServiceTrackerCustomizer customizer) {
+			super(createBundleContext(), "(objectClass=*)", customizer);
+		}
+		
+	}
+	
+	class TestCustomizer implements ServiceTrackerCustomizer {
+		
+		List<ServiceReference> serviceReferences = new ArrayList<>();
+
+		@Override
+		public Object addingService(ServiceReference reference) {
+			System.out.println("adding service: " + reference);
+			return new Object();
+		}
+
+		@Override
+		public void addedService(ServiceReference reference, Object service) {
+			System.out.println("added service: " + reference);
+			serviceReferences.add(reference);
+		}
+
+		@Override
+		public void modifiedService(ServiceReference reference, Object service) {
+			System.out.println("modified service: " + reference);
+		}
+
+		@Override
+		public void swappedService(ServiceReference reference, Object service,
+				ServiceReference newReference, Object newService) {
+			System.out.println("swapped service: " + reference);
+			serviceReferences.remove(reference);
+			serviceReferences.add(newReference);
+		}
+
+		@Override
+		public void removedService(ServiceReference reference, Object service) {
+			System.out.println("removed service: " + reference);
+			serviceReferences.remove(reference);
+		}
+		
+		public Long[] getServiceReferenceIds() {
+			Long[] ids = new Long[serviceReferences.size()];
+			for (int i = 0; i < serviceReferences.size(); i++) {
+				ids[i] = (Long) serviceReferences.get(i).getProperty(Constants.SERVICE_ID);
+			}
+			return ids;
+		}
+		
+	}
+	
+	class TestServiceReference implements ServiceReference {
+		
+		Properties props = new Properties();
+
+		public TestServiceReference(Long serviceId, Long aspectId,
+				Integer ranking) {
+			props.put(Constants.SERVICE_ID, serviceId);
+			if (aspectId != null) {
+				props.put(DependencyManager.ASPECT, aspectId);
+			}
+			if (ranking != null) {
+				props.put(Constants.SERVICE_RANKING, ranking);
+			}
+		}
+
+		@Override
+		public Object getProperty(String key) {
+			return props.get(key);
+		}
+
+		@Override
+		public String[] getPropertyKeys() {
+			return props.keySet().toArray(new String[]{});
+		}
+
+		@Override
+		public Bundle getBundle() {
+			return null;
+		}
+
+		@Override
+		public Bundle[] getUsingBundles() {
+			return null;
+		}
+
+		@Override
+		public boolean isAssignableTo(Bundle bundle, String className) {
+			return false;
+		}
+
+		@Override
+		public int compareTo(Object reference) // Kindly borrowed from the Apache Felix ServiceRegistrationImpl.ServiceReferenceImpl
+        {
+            ServiceReference other = (ServiceReference) reference;
+
+            Long id = (Long) getProperty(Constants.SERVICE_ID);
+            Long otherId = (Long) other.getProperty(Constants.SERVICE_ID);
+
+            if (id.equals(otherId))
+            {
+                return 0; // same service
+            }
+
+            Object rankObj = getProperty(Constants.SERVICE_RANKING);
+            Object otherRankObj = other.getProperty(Constants.SERVICE_RANKING);
+
+            // If no rank, then spec says it defaults to zero.
+            rankObj = (rankObj == null) ? new Integer(0) : rankObj;
+            otherRankObj = (otherRankObj == null) ? new Integer(0) : otherRankObj;
+
+            // If rank is not Integer, then spec says it defaults to zero.
+            Integer rank = (rankObj instanceof Integer)
+                ? (Integer) rankObj : new Integer(0);
+            Integer otherRank = (otherRankObj instanceof Integer)
+                ? (Integer) otherRankObj : new Integer(0);
+
+            // Sort by rank in ascending order.
+            if (rank.compareTo(otherRank) < 0)
+            {
+                return -1; // lower rank
+            }
+            else if (rank.compareTo(otherRank) > 0)
+            {
+                return 1; // higher rank
+            }
+
+            // If ranks are equal, then sort by service id in descending order.
+            return (id.compareTo(otherId) < 0) ? 1 : -1;
+        }
+
+		@Override
+		public String toString() {
+			return "TestServiceReference [props=" + props + "]";
+		}
+
+	}
+	
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/test/test/ComponentTest.java b/dependencymanager/org.apache.felix.dependencymanager/test/test/ComponentTest.java
new file mode 100644
index 0000000..9a79f70
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/test/test/ComponentTest.java
@@ -0,0 +1,699 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package test;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentState;
+import org.apache.felix.dm.ComponentStateListener;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.context.AbstractDependency;
+import org.apache.felix.dm.impl.ComponentImpl;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings("unused")
+public class ComponentTest {
+	static class MyComponent {
+		public MyComponent() {
+		}
+	}
+	
+	@Test
+	public void createStartAndStopComponent() {
+		ComponentImpl c = new ComponentImpl();
+		c.setImplementation(MyComponent.class);
+		Assert.assertEquals("should not be available until started", false, c.isAvailable());
+		c.start();
+		Assert.assertEquals("should be available", true, c.isAvailable());
+		c.stop();
+		Assert.assertEquals("should no longer be available when stopped", false, c.isAvailable());
+	}
+	
+	@Test
+	public void testInitCallbackOfComponent() {
+		final Ensure e = new Ensure();
+		ComponentImpl c = new ComponentImpl();
+		c.setImplementation(new Object() {
+			void init() {
+				e.step(2);
+			}
+            void start() {
+				e.step(3);
+			}
+			void stop() {
+				e.step(5);
+			}
+			void destroy() {
+				e.step(6);
+			}
+		});
+		e.step(1);
+		c.start();
+		e.step(4);
+		c.stop();
+		e.step(7);
+	}
+
+	@Test
+	public void testAddDependencyFromInitCallback() {
+		final Ensure e = new Ensure();
+		final SimpleServiceDependency d = new SimpleServiceDependency();
+		d.setRequired(true);
+		ComponentImpl c = new ComponentImpl();
+		c.setImplementation(new Object() {
+			void init(Component c) {
+				e.step(2);
+				c.add(d);
+			}
+			void start() {
+				e.step(4);
+			}
+			void stop() {
+				e.step(6);
+			}
+			void destroy() {
+				e.step(7);
+			}
+		});
+		e.step(1);
+		c.start();
+		e.step(3);
+		d.add(new EventImpl()); // NPE?!
+		e.step(5);
+		d.remove(new EventImpl());
+		c.stop();
+		e.step(8);
+	}
+	
+    @Test
+    public void testAddAvailableDependencyFromInitCallback() {
+        final Ensure e = new Ensure();
+        final SimpleServiceDependency d = new SimpleServiceDependency();
+        d.setRequired(true);
+        final SimpleServiceDependency d2 = new SimpleServiceDependency();
+        d2.setRequired(true);
+        ComponentImpl c = new ComponentImpl();
+        c.setImplementation(new Object() {
+            void init(Component c) {
+                System.out.println("init");
+                e.step(2);
+                c.add(d);
+                d.add(new EventImpl());
+                c.add(d2);
+            }
+            void start() {
+                System.out.println("start");
+                e.step();
+            }
+            void stop() {
+                System.out.println("stop");
+                e.step();
+            }
+            void destroy() {
+                System.out.println("destroy");
+                e.step(9);
+            }
+        });
+        e.step(1);
+        c.start();
+        e.step(5);
+        d2.add(new EventImpl());
+        e.step(7);
+        d.remove(new EventImpl());
+        c.stop();
+        e.step(10);
+    }
+    
+	@Test
+	public void testAtomicallyAddMultipleDependenciesFromInitCallback() {
+		final Ensure e = new Ensure();
+		final SimpleServiceDependency d = new SimpleServiceDependency();
+		d.setRequired(true);
+
+		final SimpleServiceDependency d2 = new SimpleServiceDependency();
+		d2.setRequired(true);
+
+		ComponentImpl c = new ComponentImpl();
+		c.setImplementation(new Object() {
+			void init(Component c) {
+				System.out.println("init");
+				e.step(2);
+				c.add(d, d2); 
+				d.add(new EventImpl()); // won't trigger start because d2 is not yet available
+			}
+			void start() {
+				System.out.println("start");
+				e.step(4);
+			}
+			void stop() {
+				System.out.println("stop");
+				e.step(6);
+			}
+			void destroy() {
+				System.out.println("destroy");
+				e.step(7);
+			}
+		});
+		e.step(1);
+		c.start();
+		e.step(3);
+		d2.add(new EventImpl());
+		e.step(5);
+		d.remove(new EventImpl());
+		c.stop();
+		e.step(8);
+	}
+
+	@Test
+	public void createComponentAddDependencyAndStartComponent() {
+		ComponentImpl c = new ComponentImpl();
+		c.setImplementation(MyComponent.class);
+		SimpleServiceDependency d = new SimpleServiceDependency();
+		d.setRequired(true);
+		c.add(d);
+		c.start();
+		Assert.assertEquals("should not be available when started because of missing dependency", false, c.isAvailable());
+		c.stop();
+		c.remove(d);
+	}
+	
+	@Test
+	public void createComponentStartItAndAddDependency() {
+		ComponentImpl c = new ComponentImpl();
+		c.setImplementation(MyComponent.class);
+		SimpleServiceDependency d = new SimpleServiceDependency();
+		d.setRequired(true);
+		c.start();
+		Assert.assertEquals("should be available when started", true, c.isAvailable());
+		c.add(d);
+		Assert.assertEquals("dependency should not be available", false, d.isAvailable());
+		Assert.assertEquals("Component should not be available", false, c.isAvailable());
+		c.remove(d);
+		c.stop();
+	}
+	
+	@Test
+	public void createComponentStartItAddDependencyAndMakeDependencyAvailable() {
+		ComponentImpl c = new ComponentImpl();
+		c.setImplementation(MyComponent.class);
+		SimpleServiceDependency d = new SimpleServiceDependency();
+		d.setRequired(true);
+		c.start();
+		c.add(d);
+		Assert.assertEquals("Component should not be available: it is started but the dependency is not available", false, c.isAvailable());
+		d.add(new EventImpl());
+		Assert.assertEquals("dependency is available, component should be too", true, c.isAvailable());
+		d.remove(new EventImpl());
+		Assert.assertEquals("dependency is no longer available, component should not be either", false, c.isAvailable());
+		c.remove(d);
+		Assert.assertEquals("dependency is removed, component should be available again", true, c.isAvailable());
+		c.stop();
+		Assert.assertEquals("Component is stopped, should be unavailable now", false, c.isAvailable());
+	}
+	
+	@Test
+	public void createComponentStartItAddDependencyAndListenerMakeDependencyAvailableAndUnavailableImmediately() {
+		ComponentImpl c = new ComponentImpl();
+		c.setImplementation(MyComponent.class);
+		final SimpleServiceDependency d = new SimpleServiceDependency();
+		d.setRequired(true);
+		ComponentStateListener l = new ComponentStateListener() {
+			@Override
+			public void changed(Component c, ComponentState state) {
+				// make the dependency unavailable
+				d.remove(new EventImpl());
+			}
+		};
+		c.start();
+		c.add(d);
+		// we add a listener here which immediately triggers an 'external event' that
+		// makes the dependency unavailable again as soon as it's invoked
+		c.add(l);
+		Assert.assertEquals("Component unavailable, dependency unavailable", false, c.isAvailable());
+		// so even though we make the dependency available here, before our call returns it
+		// is made unavailable again
+		d.add(new EventImpl());
+		Assert.assertEquals("Component *still* unavailable, because the listener immediately makes the dependency unavailable", false, c.isAvailable());
+		c.remove(l);
+		Assert.assertEquals("listener removed, component still unavailable", false, c.isAvailable());
+		c.remove(d);
+		Assert.assertEquals("dependency removed, component available", true, c.isAvailable());
+		c.stop();
+		Assert.assertEquals("Component stopped, should be unavailable", false, c.isAvailable());
+	}
+	
+	@Test
+	public void createComponentAddTwoDependenciesMakeBothAvailableAndUnavailable() {
+		ComponentImpl c = new ComponentImpl();
+		c.setImplementation(MyComponent.class);
+		SimpleServiceDependency d1 = new SimpleServiceDependency();
+		d1.setRequired(true);
+		SimpleServiceDependency d2 = new SimpleServiceDependency();
+		d2.setRequired(true);
+		c.start();
+		c.add(d1);
+		c.add(d2);
+		Assert.assertEquals("Component should be unavailable, both dependencies are too", false, c.isAvailable());
+		d1.add(new EventImpl());
+		Assert.assertEquals("one dependency available, component should still be unavailable", false, c.isAvailable());
+		d2.add(new EventImpl());
+		Assert.assertEquals("both dependencies available, component should be available", true, c.isAvailable());
+		d1.remove(new EventImpl());
+		Assert.assertEquals("one dependency unavailable again, component should be unavailable too", false, c.isAvailable());
+		d2.remove(new EventImpl());
+		Assert.assertEquals("both dependencies unavailable, component should be too", false, c.isAvailable());
+		c.remove(d2);
+		Assert.assertEquals("removed one dependency, still unavailable", false, c.isAvailable());
+		c.remove(d1);
+		Assert.assertEquals("removed the other dependency, component should be available now", true, c.isAvailable());
+		c.stop();
+		Assert.assertEquals("Component stopped, should be unavailable again", false, c.isAvailable());
+	}
+
+	@Test
+	public void createComponentAddDependencyMakeAvailableAndUnavailableWithCallbacks() {
+		final Ensure e = new Ensure();
+		ComponentImpl c = new ComponentImpl();
+		c.setImplementation(new Object() {
+            public void add() {
+				e.step(1);
+			}
+            public void remove() {
+				e.step(3);
+			}
+		});
+		SimpleServiceDependency d1 = new SimpleServiceDependency();
+		d1.setCallbacks("add", "remove");
+		d1.setRequired(true);
+		// add the dependency to the component
+		c.add(d1);
+		// start the component
+		c.start();
+		// make the dependency available, we expect the add callback
+		// to be invoked here
+		d1.add(new EventImpl());
+		e.step(2);
+		// remove the dependency, should trigger the remove callback
+		d1.remove(new EventImpl());
+		e.step(4);
+		c.stop();
+		c.remove(d1);
+		Assert.assertEquals("Component stopped, should be unavailable again", false, c.isAvailable());
+	}
+	
+	@Test
+	public void createAndStartComponentAddDependencyMakeAvailableAndUnavailableWithCallbacks() {
+		final Ensure e = new Ensure();
+		ComponentImpl c = new ComponentImpl();
+		c.setImplementation(new Object() {
+			public void add() {
+				e.step(1);
+			}
+			public void remove() {
+				e.step(3);
+			}
+		});
+		SimpleServiceDependency d1 = new SimpleServiceDependency();
+		d1.setCallbacks("add", "remove");
+		d1.setRequired(true);
+		// start the ComponentImpl (it should become available)
+		c.start();
+		// add the dependency (it should become unavailable)
+		c.add(d1);
+		// make the dependency available, which should invoke the
+		// add callback
+		d1.add(new EventImpl());
+		e.step(2);
+		// make the dependency unavailable, should trigger the
+		// remove callback
+		d1.remove(new EventImpl());
+		e.step(4);
+		c.remove(d1);
+		c.stop();
+		Assert.assertEquals("Component stopped, should be unavailable again", false, c.isAvailable());
+	}
+
+	@Test
+	public void createComponentAddTwoDependenciesMakeBothAvailableAndUnavailableWithCallbacks() {
+		final Ensure e = new Ensure();
+		ComponentImpl c = new ComponentImpl();
+		c.setImplementation(new Object() {
+			public void add() {
+				e.step();
+			}
+			public void remove() {
+				e.step();
+			}
+		});
+		SimpleServiceDependency d1 = new SimpleServiceDependency();
+		d1.setCallbacks("add", "remove");
+		d1.setRequired(true);
+		SimpleServiceDependency d2 = new SimpleServiceDependency();
+		d2.setCallbacks("add", "remove");
+		d2.setRequired(true);
+		// start the component, which should become active because there are no
+		// dependencies yet
+		c.start();
+		// now add the dependencies, making the ComponentImpl unavailable
+		c.add(d1);
+		c.add(d2);
+		// make the first dependency available, should have no effect on the
+		// component
+		d1.add(new EventImpl());
+		e.step(1);
+		// second dependency available, now all the add callbacks should be
+		// invoked
+		d2.add(new EventImpl());
+		e.step(4);
+		// remove the first dependency, triggering the remove callbacks
+		d1.remove(new EventImpl());
+		e.step(7);
+		// remove the second dependency, should not trigger more callbacks
+		d2.remove(new EventImpl());
+		e.step(8);
+		c.remove(d2);
+		c.remove(d1);
+		c.stop();
+		// still, no more callbacks should have been invoked
+		e.step(9);
+		Assert.assertEquals("Component stopped, should be unavailable again", false, c.isAvailable());
+	}
+	
+	@Test
+	public void createAndStartComponentAddTwoDependenciesMakeBothAvailableAndUnavailableWithCallbacks() {
+		final Ensure e = new Ensure();
+		ComponentImpl c = new ComponentImpl();
+		c.setImplementation(new Object() {
+			public void add() {
+				e.step();
+			}
+			public void remove() {
+				e.step();
+			}
+		});
+		// start the component, it should become available
+		c.start();
+		SimpleServiceDependency d1 = new SimpleServiceDependency();
+		d1.setCallbacks("add", "remove");
+		d1.setRequired(true);
+		SimpleServiceDependency d2 = new SimpleServiceDependency();
+		d2.setCallbacks("add", "remove");
+		d2.setRequired(true);
+		// add the first dependency, ComponentImpl should be unavailable
+		c.add(d1);
+		c.add(d2);
+		// make first dependency available, ComponentImpl should still be unavailable
+		d1.add(new EventImpl());
+		e.step(1);
+		// make second dependency available, ComponentImpl available, callbacks should
+		// be invoked
+		d2.add(new EventImpl());
+		e.step(4);
+		// remove the first dependency, callbacks should be invoked
+		d1.remove(new EventImpl());
+		e.step(7);
+		// remove second dependency, no callbacks should be invoked
+		d2.remove(new EventImpl());
+		e.step(8);
+		c.remove(d2);
+		c.remove(d1);
+		c.stop();
+		e.step(9);
+		Assert.assertEquals("Component stopped, should be unavailable again", false, c.isAvailable());
+	}
+
+	@Test
+	public void createAndStartComponentAddTwoDependenciesWithMultipleServicesWithCallbacks() {
+		final Ensure e = new Ensure();
+		ComponentImpl c = new ComponentImpl();
+		c.setImplementation(new Object() {
+			public void add() {
+				e.step();
+			}
+			public void remove() {
+				e.step();
+			}
+		});
+		// start component
+		c.start();
+		SimpleServiceDependency d1 = new SimpleServiceDependency();
+		d1.setCallbacks("add", "remove");
+		d1.setRequired(true);
+		SimpleServiceDependency d2 = new SimpleServiceDependency();
+		d2.setCallbacks("add", "remove");
+		d2.setRequired(true);
+		c.add(d1);
+		c.add(d2);
+		// add three instances to first dependency, no callbacks should
+		// be triggered
+		d1.add(new EventImpl(1));
+		d1.add(new EventImpl(2));
+		d1.add(new EventImpl(3));
+		e.step(1);
+		// add two instances to the second dependency, callbacks should
+		// be invoked (4x)
+		d2.add(new EventImpl(1));
+		e.step(6);
+		// add another dependency, triggering another callback
+		d2.add(new EventImpl(2));
+		e.step(8);
+		// remove first dependency (all three of them) which makes the 
+		// ComponentImpl unavailable so it should trigger calling remove for
+		// all of them (so 5x)
+		d1.remove(new EventImpl(1));
+		d1.remove(new EventImpl(2));
+		d1.remove(new EventImpl(3));
+		e.step(14);
+		// remove second dependency, should not trigger further callbacks
+		d2.remove(new EventImpl(1));
+		d2.remove(new EventImpl(2));
+		e.step(15);
+		c.remove(d2);
+		c.remove(d1);
+		c.stop();
+		e.step(16);
+		Assert.assertEquals("Component stopped, should be unavailable again", false, c.isAvailable());
+	}
+	
+	@Test
+	public void createComponentAddDependencyMakeAvailableChangeAndUnavailableWithCallbacks() {
+		final Ensure e = new Ensure();
+		ComponentImpl c = new ComponentImpl();
+		c.setImplementation(new Object() {
+			public void add() {
+				e.step(1);
+			}
+			public void change() {
+				e.step(3);
+			}
+			public void remove() {
+				e.step(5);
+			}
+		});
+		SimpleServiceDependency d1 = new SimpleServiceDependency();
+		d1.setCallbacks("add", "change", "remove");
+		d1.setRequired(true);
+		// add the dependency to the component
+		c.add(d1);
+		// start the component
+		c.start();
+		// make the dependency available, we expect the add callback
+		// to be invoked here
+		d1.add(new EventImpl());
+		e.step(2);
+		// change the dependency
+		d1.change(new EventImpl());
+		e.step(4);
+		// remove the dependency, should trigger the remove callback
+		d1.remove(new EventImpl());
+		e.step(6);
+		c.stop();
+		c.remove(d1);
+		Assert.assertEquals("Component stopped, should be unavailable again", false, c.isAvailable());
+	}
+
+	@Test
+	public void createComponentWithOptionalDependency() {
+		final Ensure e = new Ensure();
+		ComponentImpl c = new ComponentImpl();
+		c.setImplementation(new Object() {
+			public void add() {
+				e.step(1);
+			}
+			public void change() {
+				e.step(3);
+			}
+			public void remove() {
+				e.step(5);
+			}
+		});
+		SimpleServiceDependency d1 = new SimpleServiceDependency();
+		d1.setCallbacks("add", "change", "remove");
+		d1.setRequired(false);
+		// add the dependency to the component
+		c.add(d1);
+		// start the component
+		c.start();
+		Assert.assertEquals("Component started with an optional dependency, should be available", true, c.isAvailable());
+		// make the dependency available, we expect the add callback
+		// to be invoked here
+		d1.add(new EventImpl());
+		e.step(2);
+		Assert.assertEquals("Component started with an optional dependency, should be available", true, c.isAvailable());
+		// change the dependency
+		d1.change(new EventImpl());
+		e.step(4);
+		Assert.assertEquals("Component started with an optional dependency, should be available", true, c.isAvailable());
+		// remove the dependency, should trigger the remove callback
+		d1.remove(new EventImpl());
+		Assert.assertEquals("Component started with an optional dependency, should be available", true, c.isAvailable());
+		e.step(6);
+		c.stop();
+		c.remove(d1);
+		Assert.assertEquals("Component stopped, should be unavailable again", false, c.isAvailable());
+	}
+
+	@Test
+	public void createComponentWithOptionalAndRequiredDependency() {
+		final Ensure e = new Ensure();
+		ComponentImpl c = new ComponentImpl();
+		c.setImplementation(new Object() {
+			public void add() {
+				e.step();
+			}
+			public void remove() {
+				e.step();
+			}
+		});
+		SimpleServiceDependency d1 = new SimpleServiceDependency();
+		d1.setCallbacks("add", "remove");
+		d1.setRequired(false);
+		SimpleServiceDependency d2 = new SimpleServiceDependency();
+		d2.setCallbacks("add", "remove");
+		d2.setRequired(true);
+		// add the dependencies to the component
+		c.add(d1);
+		c.add(d2);
+		// start the component
+		c.start();
+		Assert.assertEquals("Component started with a required and optional dependency, should not be available", false, c.isAvailable());
+		// make the optional dependency available
+		d1.add(new EventImpl());
+		e.step(1);
+		Assert.assertEquals("Component should not be available", false, c.isAvailable());
+		// make the required dependency available
+		d2.add(new EventImpl());
+		e.step(4);
+		Assert.assertEquals("Component should be available", true, c.isAvailable());
+		// remove the optional dependency
+		d1.remove(new EventImpl());
+		e.step(6);
+		Assert.assertEquals("Component should be available", true, c.isAvailable());
+		// remove the required dependency
+		d1.remove(new EventImpl());
+		e.step(8);
+		Assert.assertEquals("Component should be available", true, c.isAvailable());
+		c.stop();
+		c.remove(d1);
+		Assert.assertEquals("Component stopped, should be unavailable again", false, c.isAvailable());
+	}
+	
+	@Test
+	public void createComponentAddAvailableDependencyRemoveDependencyCheckStopCalledBeforeUnbind() {
+		final Ensure e = new Ensure();
+		ComponentImpl c = new ComponentImpl();
+		c.setImplementation(new Object() {
+			void add() {
+				e.step(1);
+			}
+			void start() {
+				e.step(2);
+			}
+			void stop() {
+				e.step(4);
+			}
+			void remove() {
+				e.step(5);
+			}
+		});
+		SimpleServiceDependency d = new SimpleServiceDependency();
+		d.setCallbacks("add", "remove");
+		d.setRequired(true);
+		// add the dependency to the component
+		c.add(d);
+		// start the component
+		c.start();
+		// make the dependency available, we expect the add callback
+		// to be invoked here, then start is called.
+		d.add(new EventImpl());
+		e.step(3);
+		// remove the dependency, should trigger the stop, then remove callback
+		d.remove(new EventImpl());
+		e.step(6);
+		c.stop();
+		c.remove(d);
+		Assert.assertEquals("Component stopped, should be unavailable", false, c.isAvailable());
+	}
+	
+	@Test
+	public void createDependenciesWithCallbackInstance() {
+		final Ensure e = new Ensure();
+		ComponentImpl c = new ComponentImpl();
+		c.setImplementation(new Object() {
+			void start() {
+				e.step(2);
+			}
+			
+			void stop() {
+				e.step(4);
+			}
+		});
+		
+		Object callbackInstance = new Object() {
+			void add() {
+				e.step(1);
+			}
+			
+			void remove() {
+				e.step(5);
+			}
+		};		
+		
+		SimpleServiceDependency d = new SimpleServiceDependency();
+		d.setCallbacks(callbackInstance, "add", "remove");
+		d.setRequired(true);
+		// add the dependency to the component
+		c.add(d);
+		// start the component
+		c.start();
+		// make the dependency available, we expect the add callback
+		// to be invoked here, then start is called.
+		d.add(new EventImpl());
+		e.step(3);
+		// remove the dependency, should trigger the stop, then remove callback
+		d.remove(new EventImpl());
+		e.step(6);
+		c.stop();
+		c.remove(d);
+		Assert.assertEquals("Component stopped, should be unavailable", false, c.isAvailable());
+	}
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/test/test/ConcurrencyTest.java b/dependencymanager/org.apache.felix.dependencymanager/test/test/ConcurrencyTest.java
new file mode 100644
index 0000000..7f999bc
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/test/test/ConcurrencyTest.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package test;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentState;
+import org.apache.felix.dm.ComponentStateListener;
+import org.apache.felix.dm.impl.ComponentImpl;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ConcurrencyTest {
+	
+	/**
+	 * Ensure actions from another thread than the current thread executing in the SerialExecutor are being
+	 * scheduled (added to the queue) rather than being executed immediately.  
+	 */
+	@Test
+	public void createComponentAddDependencyAndListenerAndAddAnotherDependencyInAParallelThread() {
+		final Semaphore s = new Semaphore(0);
+		final ComponentImpl c = new ComponentImpl();
+		final SimpleServiceDependency d = new SimpleServiceDependency();
+		d.setRequired(true);
+		final SimpleServiceDependency d2 = new SimpleServiceDependency();
+		d2.setRequired(true);
+		final Thread t = new Thread() {
+			public void run() {
+				c.add(d2);
+				s.release();
+			}
+		};
+		ComponentStateListener l = new ComponentStateListener() {
+			@Override
+			public void changed(Component component, ComponentState state) {
+				try {
+				    c.remove(this);
+				    // launch a second thread interacting with our ComponentImpl and block this thread until the
+				    // second thread finished its interaction with our component. We want to ensure the work of 
+				    // the second thread is scheduled after our current job in the serial executor and does not
+				    // get executed immediately.
+				    t.start();
+				    s.acquire();
+				    Assert.assertEquals("dependency count should be 1", 1, c.getDependencies().size());
+				}
+				catch (InterruptedException e) {
+					e.printStackTrace();
+				}
+			}
+		};
+		
+		c.setImplementation(new Object()); // If not, we may see NullPointers when invoking lifecycle callbacks
+		c.start();
+		c.add(d);
+		c.add(l);
+		Assert.assertEquals("component should not be available", false, c.isAvailable());
+		d.add(new EventImpl()); // sets dependency d to available and triggers our ComponentStateListener
+		
+		// due to the dependency added by the second thread in the serial executor we still expect our component
+		// to be unavailable. This work was scheduled in the serial executor and will be executed by the current
+		// thread after it finished handling the job for handling the changed() method.
+		Assert.assertEquals("component should not be available", false, c.isAvailable());
+		c.remove(l);
+		Assert.assertEquals("component should not be available", false, c.isAvailable());
+		c.remove(d);
+		Assert.assertEquals("component should not be available", false, c.isAvailable());
+		c.remove(d2);
+		Assert.assertEquals("component should be available", true, c.isAvailable());
+		c.stop();
+		Assert.assertEquals("component should not be available", false, c.isAvailable());
+	}
+
+	@Test
+	public void createComponentAddAndRemoveDependenciesInParallelThreads() throws Exception {
+		final ComponentImpl c = new ComponentImpl();
+		c.setImplementation(new Object()); // If not, we may see NullPointers when invoking lifecycle callbacks
+		ExecutorService e = Executors.newFixedThreadPool(16); 
+		c.start();
+		for (int i = 0; i < 1000; i++) {
+			e.execute(new Runnable() {
+				@Override
+				public void run() {
+				    SimpleServiceDependency d = new SimpleServiceDependency();
+					d.setRequired(true);
+					c.add(d);
+//					d.changed(new EventImpl(true));
+//					d.changed(new EventImpl(false));
+					c.remove(d);
+				}});
+		}
+		e.shutdown();
+		e.awaitTermination(10, TimeUnit.SECONDS);
+//		Assert.assertEquals("component should not be available", false, c.isAvailable());
+		c.stop();
+		Assert.assertEquals("component should not be available", false, c.isAvailable());
+	}
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/test/test/ConfigurationTest.java b/dependencymanager/org.apache.felix.dependencymanager/test/test/ConfigurationTest.java
new file mode 100644
index 0000000..bf58bcf
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/test/test/ConfigurationTest.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package test;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.impl.ComponentImpl;
+import org.apache.felix.dm.impl.ConfigurationDependencyImpl;
+import org.junit.Test;
+import org.osgi.service.cm.ConfigurationException;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "unused"})
+public class ConfigurationTest extends TestBase {
+    @Test
+    public void testConfigurationFailure() throws Throwable {
+        final Ensure e = new Ensure();
+
+        // Create our configuration dependency
+        final ConfigurationDependencyImpl conf = new ConfigurationDependencyImpl();
+
+        // Create another required dependency
+        final SimpleServiceDependency requiredDependency = new SimpleServiceDependency();
+        requiredDependency.setRequired(true);
+        requiredDependency.setCallbacks("addDep", null);
+
+        // Create our component, which will fail when handling configuration update
+        ComponentImpl c = new ComponentImpl();
+
+        c.setImplementation(new Object() {
+            volatile Dictionary m_conf;
+
+            public void updated(Dictionary conf) {
+                debug("updated: conf=%s", conf);
+                m_conf = conf;
+                if ("invalid".equals(conf.get("conf"))) {
+                    // We refuse the first configuration. 
+                    debug("refusing configuration");
+                    e.step(1);
+                    // Set our acceptUpdate flag to true, so next update will be successful
+                    throw new RuntimeException("update failed (expected)");
+                }
+                else {
+                    debug("accepting configuration");
+                    e.step(2);
+                }
+            }
+
+            public void addDep() {
+                if ("invalid".equals(m_conf.get("conf"))) {
+                    e.throwable(new Exception("addDep should not be called"));
+                }
+                e.step(3);
+                debug("addDep");
+            }
+
+            void init(Component c) {
+                if ("invalid".equals(m_conf.get("conf"))) {
+                    e.throwable(new Exception("init should not be called"));
+                }
+                e.step(4);
+                debug("init");
+            }
+
+            void start() {
+                if ("invalid".equals(m_conf.get("conf"))) {
+                    e.throwable(new Exception("start should not be called"));
+                }
+                e.step(5);
+                debug("start");
+            }
+        });
+
+        // Add the dependencies
+        c.add(conf);
+        c.add(requiredDependency);
+
+        // Start our component ("requiredDependency" is not yet available, so we'll stay in WAITING_FOR_REQUIRED state).
+        c.start();
+        
+        // Enabled "requiredDependency"
+        requiredDependency.add(new EventImpl());
+
+        // Now, act as the configuration admin service and inject a wrong dependency
+        try {
+            Hashtable props = new Hashtable();
+            props.put("conf", "invalid");
+            conf.updated(props);
+        }
+        catch (ConfigurationException err) {
+            warn("got expected configuration error");
+        }
+        e.waitForStep(1, 5000);
+        e.ensure();
+        
+        // Now, inject another valid configuration
+        try {
+            Hashtable props = new Hashtable();
+            props.put("conf", "valid");
+            conf.updated(props);
+        }
+        catch (ConfigurationException err) {
+            warn("got expected configuration error");
+        }
+        
+        // This time, our component should be started properly.
+        e.waitForStep(5, 5000);
+        e.ensure();
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/test/test/Ensure.java b/dependencymanager/org.apache.felix.dependencymanager/test/test/Ensure.java
new file mode 100644
index 0000000..4d2378f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/test/test/Ensure.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package test;
+
+import java.io.PrintStream;
+
+import org.junit.Assert;
+
+/**
+ * Helper class to make sure that steps in a test happen in the correct order. Instantiate
+ * this class and subsequently invoke <code>step(nr)</code> with steps starting at 1. You
+ * can also have threads wait until you arrive at a certain step.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Ensure {
+    private final boolean DEBUG;
+    private static long INSTANCE = 0;
+    private static final int RESOLUTION = 100;
+    private static PrintStream STREAM = System.out;
+    int step = 0;
+    private Throwable m_throwable;
+	private boolean previousStepFailed;
+    
+    public Ensure() {
+        this(true);
+    }
+    
+    public Ensure(boolean debug) {
+        DEBUG = debug;
+        if (DEBUG) {
+            INSTANCE++;
+        }
+    }
+
+    public void setStream(PrintStream output) {
+        STREAM = output;
+    }
+    
+    /**
+     * Mark this point as step <code>nr</code>.
+     * 
+     * @param nr the step we are in
+     */
+    public synchronized void step(int nr) {
+    	if (previousStepFailed) {
+    		throw new RuntimeException("can not enter into step " + nr + " (some previous steps failed)");
+    	}
+        step++;
+        try {
+        	Assert.assertEquals(nr, step);
+        } catch (Throwable e) {
+        	previousStepFailed = true;
+        	throw e;
+        }
+        if (DEBUG) {
+            String info = getLineInfo(3);
+            STREAM.println("[Ensure " + INSTANCE + "] step " + step + " [" + currentThread() + "] " + info);
+        }
+        notifyAll();
+    }
+
+    private String getLineInfo(int depth) {
+        StackTraceElement[] trace = Thread.currentThread().getStackTrace();
+        String info = trace[depth].getClassName() + "." + trace[depth].getMethodName() + ":" + trace[depth].getLineNumber();
+        return info;
+    }
+    
+    /**
+     * Mark this point as the next step.
+     */
+    public synchronized void step() {
+    	if (previousStepFailed) {
+    		throw new RuntimeException("can not enter into step " + (step+1) + " (some previous steps failed)");
+    	}
+        step++;
+        if (DEBUG) {
+            String info = getLineInfo(3);
+            STREAM.println("[Ensure " + INSTANCE + "] next step " + step + " [" + currentThread() + "] " + info);
+        }
+        notifyAll();
+    }
+
+    /**
+     * Wait until we arrive at least at step <code>nr</code> in the process, or fail if that
+     * takes more than <code>timeout</code> milliseconds. If you invoke wait on a thread,
+     * you are effectively assuming some other thread will invoke the <code>step(nr)</code>
+     * method.
+     * 
+     * @param nr the step to wait for
+     * @param timeout the number of milliseconds to wait
+     */
+    public synchronized void waitForStep(int nr, int timeout) {
+        final int initialTimeout = timeout;
+        if (DEBUG) {
+            String info = getLineInfo(3);
+            STREAM.println("[Ensure " + INSTANCE + "] waiting for step " + nr + " [" + currentThread() + "] " + info);
+        }
+        while (step < nr && timeout > 0) {
+            try {
+                wait(RESOLUTION);
+                timeout -= RESOLUTION;
+            }
+            catch (InterruptedException e) {}
+        }
+        if (step < nr) {
+            throw new IllegalStateException("Timed out waiting for " + initialTimeout + " ms for step " + nr + ", we are still at step " + step);
+        }
+        if (DEBUG) {
+            String info = getLineInfo(3);
+            STREAM.println("[Ensure " + INSTANCE + "] arrived at step " + nr + " [" + currentThread() + "] " + info);
+        }
+    }
+    
+    private String currentThread() {
+        Thread thread = Thread.currentThread();
+        return thread.getId() + " " + thread.getName();
+    }
+    
+    public static Runnable createRunnableStep(final Ensure ensure, final int nr) {
+        return new Runnable() { public void run() { ensure.step(nr); }};
+    }
+    
+    public synchronized void steps(Steps steps) {
+        steps.next(this);
+    }
+    
+    /** 
+     * Helper class for naming a list of step numbers. If used with the steps(Steps) method
+     * you can define at which steps in time this point should be passed. That means you can
+     * check methods that will get invoked multiple times during a test.
+     */
+    public static class Steps {
+        private final int[] m_steps;
+        private int m_stepIndex;
+
+        /** 
+         * Create a list of steps and initialize the step counter to zero.
+         */
+        public Steps(int... steps) {
+            m_steps = steps;
+            m_stepIndex = 0;
+        }
+
+        /**
+         * Ensure we're at the right step. Will throw an index out of bounds exception if we enter this step more often than defined.
+         */
+        public void next(Ensure ensure) {
+            ensure.step(m_steps[m_stepIndex++]);
+        }
+    }
+
+    /**
+     * Saves a thrown exception that occurred in a different thread. You can only save one exception
+     * at a time this way.
+     */
+    public synchronized void throwable(Throwable throwable) {
+        m_throwable = throwable;
+    }
+
+    /**
+     * Throws a <code>Throwable</code> if one occurred in a different thread and that thread saved it
+     * using the <code>throwable()</code> method.
+     */
+    public synchronized void ensure() throws Throwable {
+        if (m_throwable != null) {
+            throw m_throwable;
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/test/test/EventImpl.java b/dependencymanager/org.apache.felix.dependencymanager/test/test/EventImpl.java
new file mode 100644
index 0000000..28c5455
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/test/test/EventImpl.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package test;
+
+import org.apache.felix.dm.context.Event;
+
+/** in real life, this event might contain a service reference and service instance
+ * or something similar
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class EventImpl extends Event { // the actual event object (a Service, a Bundle, a Configuration, etc ...)
+	private final int m_id;
+
+	public EventImpl() {
+		this(1);
+	}
+	/** By constructing events with different IDs, we can simulate different unique instances. */
+	public EventImpl(int id) {
+		this (id, null);
+	}
+	
+	public EventImpl(int id, Object event) {
+	    super(event);
+	    m_id = id;
+	}
+	
+
+	@Override
+	public int hashCode() {
+		return m_id;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		// an instanceof check here is not "strong" enough with subclasses overriding the
+		// equals: we need to be sure that a.equals(b) == b.equals(a) at all times
+		if (obj != null && obj.getClass().equals(EventImpl.class)) {
+			return ((EventImpl) obj).m_id == m_id;
+		}
+		return false;
+	}
+	
+    @Override
+    public int compareTo(Event o) {
+        EventImpl a = this, b = (EventImpl) o;
+        if (a.m_id < b.m_id) {
+            return -1;
+        } else if (a.m_id == b.m_id){
+            return 0;
+        } else {
+            return 1;
+        }
+    }            
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/test/test/SerialExecutorTest.java b/dependencymanager/org.apache.felix.dependencymanager/test/test/SerialExecutorTest.java
new file mode 100644
index 0000000..feb04c4
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/test/test/SerialExecutorTest.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package test;
+
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.felix.dm.Logger;
+import org.apache.felix.dm.impl.SerialExecutor;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Validates SerialExecutor used by DM implementation.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class SerialExecutorTest extends TestBase {
+    final Random m_rnd = new Random();
+    final int TESTS = 100000;
+    
+    @Test
+    public void testSerialExecutor() {
+        info("Testing serial executor");
+        int cores = Math.max(10, Runtime.getRuntime().availableProcessors());
+        ExecutorService threadPool = null;
+
+        try {
+            threadPool = Executors.newFixedThreadPool(cores);
+            final SerialExecutor serial = new SerialExecutor(new Logger(null));
+
+            long timeStamp = System.currentTimeMillis();
+            for (int i = 0; i < TESTS; i++) {
+                final CountDownLatch latch = new CountDownLatch(cores * 2 /* each task reexecutes itself one time */);
+                final SerialTask task = new SerialTask(serial, latch);
+                for (int j = 0; j < cores; j ++) {
+                    threadPool.execute(new Runnable() {
+                        public void run() {
+                            serial.execute(task);
+                        }
+                    });
+                }
+                Assert.assertTrue("Test " + i + " did not terminate timely", latch.await(20000, TimeUnit.MILLISECONDS));
+            }
+            long now = System.currentTimeMillis();
+            System.out.println("Performed " + TESTS + " tests in " + (now - timeStamp) + " ms.");
+            timeStamp = now;
+        }
+        catch (Throwable t) {
+            t.printStackTrace();
+            Assert.fail("Test failed: " + t.getMessage());
+        }
+        finally {
+        	if (threadPool != null) {
+        		shutdown(threadPool);
+        	}
+        }
+    }
+
+    void shutdown(ExecutorService exec) {
+        exec.shutdown();
+        try {
+            exec.awaitTermination(5, TimeUnit.SECONDS);
+        }
+        catch (InterruptedException e) {
+        }
+    }
+
+    class SerialTask implements Runnable {
+        final AtomicReference<Thread> m_executingThread = new AtomicReference<Thread>();
+        final CountDownLatch m_latch;
+        private boolean m_firstExecution;
+        private final SerialExecutor m_exec;          
+        
+        SerialTask(SerialExecutor exec, CountDownLatch latch) {
+            m_latch = latch;
+            m_exec = exec;
+            m_firstExecution = true;
+        }
+        
+        public void run() {
+            Thread self = Thread.currentThread();
+            if (m_firstExecution) {
+                // The first time we are executed, the previous executing thread stored in our m_executingThread should be null
+                if (!m_executingThread.compareAndSet(null, self)) {
+                    System.out.println("detected concurrent call to SerialTask: currThread=" + self
+                        + ", other executing thread=" + m_executingThread);
+                    return;
+                }
+            } else {
+                // The second time we are executed, the previous executing thread stored in our m_executingThread should be 
+                // the current running thread.
+                if (m_executingThread.get() != self) {
+                    System.out.println("expect to execute reentrant tasks in same thread, but current thread=" + self
+                        + ", while expected is " + m_executingThread);
+                    return;
+                }
+            }
+                        
+            if (m_firstExecution) {
+                m_firstExecution = false;
+                m_exec.execute(this); // Our run method must be called immediately
+            } else {
+                if (! m_executingThread.compareAndSet(self, null)) {
+                    System.out.println("detected concurrent call to SerialTask: currThread=" + self
+                        + ", other executing thread=" + m_executingThread);
+                    return;                    
+                }
+                m_firstExecution = true;
+            }
+            
+            m_latch.countDown();
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/test/test/ServiceRaceTest.java b/dependencymanager/org.apache.felix.dependencymanager/test/test/ServiceRaceTest.java
new file mode 100644
index 0000000..90873e7
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/test/test/ServiceRaceTest.java
@@ -0,0 +1,255 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package test;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentState;
+import org.apache.felix.dm.ComponentStateListener;
+import org.apache.felix.dm.context.Event;
+import org.apache.felix.dm.impl.ComponentImpl;
+import org.apache.felix.dm.impl.ConfigurationDependencyImpl;
+import org.junit.Assert;
+import org.junit.Test;
+import org.osgi.service.cm.ConfigurationException;
+
+/**
+ * This test class simulates a client having many dependencies being registered/unregistered concurrently.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class ServiceRaceTest extends TestBase {
+    final static int STEP_WAIT = 5000;
+    final static int DEPENDENCIES = 10;
+    final static int LOOPS = 10000;
+
+    // Executor used to bind/unbind service dependencies.
+    ExecutorService m_threadpool;
+    // Timestamp used to log the time consumed to execute 100 tests.
+    long m_timeStamp;
+
+    /**
+     * Creates many service dependencies, and activate/deactivate them concurrently.  
+     */
+    @Test
+    public void createParallelComponentRegistgrationUnregistration() {
+        info("Starting createParallelComponentRegistgrationUnregistration test");
+        int cores = Math.max(16, Runtime.getRuntime().availableProcessors());
+        info("using " + cores + " cores.");
+
+        m_threadpool = Executors.newFixedThreadPool(Math.max(cores, DEPENDENCIES + 3 /* start/stop/configure */));
+
+        try {
+            m_timeStamp = System.currentTimeMillis();
+            for (int loop = 0; loop < LOOPS; loop++) {
+                doTest(loop);
+            }
+        }
+        catch (Throwable t) {
+            warn("got unexpected exception", t);
+        }
+        finally {
+            shutdown(m_threadpool);
+        }
+    }
+
+    void shutdown(ExecutorService exec) {
+        exec.shutdown();
+        try {
+            exec.awaitTermination(5, TimeUnit.SECONDS);
+        }
+        catch (InterruptedException e) {
+        }
+    }
+
+    void doTest(int loop) throws Throwable {
+        debug("loop#%d -------------------------", loop);
+
+        final Ensure step = new Ensure(false);
+
+        // Create one client component, which depends on many service dependencies
+        final ComponentImpl client = new ComponentImpl();
+        final Client theClient = new Client(step);
+        client.setImplementation(theClient);
+
+        // Create client service dependencies
+        final SimpleServiceDependency[] dependencies = new SimpleServiceDependency[DEPENDENCIES];
+        for (int i = 0; i < DEPENDENCIES; i++) {
+            dependencies[i] = new SimpleServiceDependency();
+            dependencies[i].setRequired(true);
+            dependencies[i].setCallbacks("add", "remove");
+            client.add(dependencies[i]);
+        }
+        final ConfigurationDependencyImpl confDependency = new ConfigurationDependencyImpl();
+        confDependency.setPid("mypid");
+        client.add(confDependency);
+
+        // Create Configuration (concurrently).
+        // We have to simulate the configuration update, using a component state listener, which will
+        // trigger an update thread, but only once the component is started.
+        final ComponentStateListener listener = new ComponentStateListener() {
+            private volatile Dictionary m_conf;
+
+            public void changed(Component c, ComponentState state) {
+                if (state == ComponentState.WAITING_FOR_REQUIRED && m_conf == null) {
+                    m_conf = new Hashtable();
+                    m_conf.put("foo", "bar");
+                    m_threadpool.execute(new Runnable() {
+                        public void run() {
+                            try {
+                                confDependency.updated(m_conf);
+                            }
+                            catch (ConfigurationException e) {
+                                warn("configuration failed", e);
+                            }
+                        }
+                    });
+                }
+            }
+        };
+        client.add(listener);
+
+
+        // Start the client (concurrently)
+        m_threadpool.execute(new Runnable() {
+            public void run() {
+                client.start();
+                
+                // Activate the client service dependencies concurrently.
+                // We *must* do this after having started the component (in a reality, the dependencies can be 
+                // injected only one the tracker has been opened ...
+                for (int i = 0; i < DEPENDENCIES; i++) {
+                    final SimpleServiceDependency dep = dependencies[i];
+                    final Event added = new EventImpl(i);
+                    m_threadpool.execute(new Runnable() {
+                        public void run() {
+                            dep.add(added);
+                        }
+                    });
+                }
+
+            }
+        });
+
+        // Ensure that client has been started.
+        int expectedStep = 1 /* conf */ + DEPENDENCIES + 1 /* start */;
+        step.waitForStep(expectedStep, STEP_WAIT);
+        Assert.assertEquals(DEPENDENCIES, theClient.getDependencies());
+        Assert.assertNotNull(theClient.getConfiguration());
+        client.remove(listener);
+
+        // Stop the client and all dependencies concurrently.
+        for (int i = 0; i < DEPENDENCIES; i++) {
+            final SimpleServiceDependency dep = dependencies[i];
+            final Event removed = new EventImpl(i);
+            m_threadpool.execute(new Runnable() {
+                public void run() {
+                    dep.remove(removed);
+                }
+            });
+        }
+        m_threadpool.execute(new Runnable() {
+            public void run() {
+                client.stop();
+            }
+        });
+        m_threadpool.execute(new Runnable() {
+            public void run() {
+                try {
+                    confDependency.updated(null);
+                }
+                catch (ConfigurationException e) {
+                    warn("error while unconfiguring", e);
+                }
+            }
+        });
+
+        // Ensure that client has been stopped, then destroyed, then unbound from all dependencies
+        expectedStep += 2; // stop/destroy
+        expectedStep += DEPENDENCIES; // removed all dependencies
+        expectedStep += 1; // removed configuration
+        step.waitForStep(expectedStep, STEP_WAIT);
+        step.ensure();
+        Assert.assertEquals(0, theClient.getDependencies());
+        Assert.assertNull(theClient.getConfiguration());
+
+        debug("finished one test loop");
+        if ((loop + 1) % 100 == 0) {
+            long duration = System.currentTimeMillis() - m_timeStamp;
+            warn("Performed 100 tests (total=%d) in %d ms.", (loop + 1), duration);
+            m_timeStamp = System.currentTimeMillis();
+        }
+    }
+
+    public class Client {
+        final Ensure m_step;
+        int m_dependencies;
+        volatile Dictionary m_conf;
+        
+        public Client(Ensure step) {
+            m_step = step;
+        }
+
+        public void updated(Dictionary conf) throws ConfigurationException {
+            m_conf = conf;
+            if (conf != null) {
+                Assert.assertEquals("bar", conf.get("foo"));
+                m_step.step(1);
+            } else {
+                m_step.step();
+            }
+        }
+        
+        synchronized void add() {
+            m_step.step();
+            m_dependencies++;
+        }
+        
+        synchronized void remove() {
+            m_step.step();
+            m_dependencies--;
+        }
+                
+        void start() {
+            m_step.step((DEPENDENCIES + 1) /* deps + conf */ + 1 /* start */);
+        }
+
+        void stop() {
+            m_step.step((DEPENDENCIES + 1) /* deps + conf */ + 1 /* start */ + 1 /* stop */);
+        }
+        
+        void destroy() {
+            m_step.step((DEPENDENCIES + 1) /* deps + conf */ + 1 /* start */ + 1 /* stop */  + 1 /* destroy */);
+        }
+        
+        synchronized int getDependencies() {
+            return m_dependencies;
+        }
+        
+        Dictionary getConfiguration() {
+            return m_conf;
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/test/test/SimpleServiceDependency.java b/dependencymanager/org.apache.felix.dependencymanager/test/test/SimpleServiceDependency.java
new file mode 100644
index 0000000..323fffc
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/test/test/SimpleServiceDependency.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package test;
+
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.context.AbstractDependency;
+import org.apache.felix.dm.context.DependencyContext;
+import org.apache.felix.dm.context.Event;
+import org.apache.felix.dm.context.EventType;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class SimpleServiceDependency extends AbstractDependency<Dependency> {
+    @Override
+    public String getType() {
+        return "SimpleServiceDependency";
+    }
+
+    @Override
+    public String getSimpleName() {
+        return "SimpleServiceDependency";
+    }
+
+    @Override
+    public DependencyContext createCopy() {
+        return new SimpleServiceDependency();
+    }
+    
+    @Override
+    public void invokeCallback(EventType type, Event ... e) {
+        switch (type) {
+        case ADDED:
+            if (m_add != null) {
+                invoke (m_add, e[0], getInstances());
+            }
+            break;
+        case CHANGED:
+            if (m_change != null) {
+                invoke (m_change, e[0], getInstances());
+            }
+            break;
+        case REMOVED:
+            if (m_remove != null) {
+                invoke (m_remove, e[0], getInstances());
+            }
+            break;
+        default:
+            break;
+        }
+    }
+
+    public void invoke(String method, Event e, Object[] instances) {
+        // specific for this type of dependency
+        m_component.invokeCallbackMethod(instances, method, new Class[][] { {} }, new Object[][] { {} });
+    }
+
+    public void add(final Event e) {
+        m_component.handleEvent(this, EventType.ADDED, e);
+    }
+    
+    public void change(final Event e) {
+        m_component.handleEvent(this, EventType.CHANGED, e);
+    }
+
+    public void remove(final Event e) {
+        m_component.handleEvent(this, EventType.REMOVED, e);
+    }
+    
+    public void swap(final Event event, final Event newEvent) {
+        m_component.handleEvent(this, EventType.SWAPPED, event, newEvent);
+    }
+
+    @Override
+    public Class<?> getAutoConfigType() {
+        return null; // we don't support auto config mode.
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/test/test/TestBase.java b/dependencymanager/org.apache.felix.dependencymanager/test/test/TestBase.java
new file mode 100644
index 0000000..948a596
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/test/test/TestBase.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package test;
+
+import static java.lang.System.out;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Base class for all tests.
+ * For now, this class provides logging support.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class TestBase {
+    final int WARN = 1;
+    final int INFO = 2;
+    final int DEBUG = 3;
+    
+    // Set the enabled log level.
+    final int m_level = WARN;
+    
+    @SuppressWarnings("unused")
+    void debug(String format, Object ... params) {
+        if (m_level >= DEBUG) {
+            out.println(Thread.currentThread().getName() + " - " + String.format(format, params));
+        }
+    }
+    
+    void warn(String format, Object ... params) {
+        warn(format, null, params);
+    }
+    
+    @SuppressWarnings("unused")
+    void info(String format, Object ... params) {
+        if (m_level >= INFO) {
+            out.println(Thread.currentThread().getName() + " - " + String.format(format, params));
+        }
+    }
+
+    void warn(String format, Throwable t, Object ... params) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(Thread.currentThread().getName()).append(" - ").append(String.format(format, params));
+        if (t != null) {
+            StringWriter buffer = new StringWriter();
+            PrintWriter pw = new PrintWriter(buffer);
+            t.printStackTrace(pw);
+            sb.append(System.getProperty("line.separator"));
+            sb.append(buffer.toString());
+        }
+        System.out.println(sb.toString());
+    }
+}