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/.gitignore b/dependencymanager/.gitignore
new file mode 100644
index 0000000..78442f4
--- /dev/null
+++ b/dependencymanager/.gitignore
@@ -0,0 +1,3 @@
+/.gradle/
+/reports/
+/generated/
diff --git a/dependencymanager/README b/dependencymanager/README
new file mode 100644
index 0000000..6cd3557
--- /dev/null
+++ b/dependencymanager/README
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+Welcome to Apache Felix Dependency Manager
+==========================================
+
+Apache Felix Dependency Manager is a versatile java API, allowing to declaratively
+register, acquire, and manage dynamic OSGi services.
+
+Please refer to release/resources/src/README
+
+Building and testing Apache Felix Dependency Manager
+====================================================
+
+The build instructions can be found from release/resources/src/README.src
+
+Getting Started
+===============
+
+To start using Apache Felix Dependency Manager, please go to our website and read the
+getting started guide for users:
+
+ http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager.html
+
+Many examples are also available from the dependency manager examples, in the org.apache.felix.dependencymanager.samples module
+See org.apache.felix.dependencymanager.samples/README.samples
+
+Many thanks for using Apache Felix Dependency Manager.
+
+The Felix Team
diff --git a/dependencymanager/build.gradle b/dependencymanager/build.gradle
new file mode 100644
index 0000000..db485ef
--- /dev/null
+++ b/dependencymanager/build.gradle
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+/*
+ * Master Gradle build script
+ *
+ * Depends on bndWorkspace and bndURI properties set by settings.gradle.
+ */
+
+/* Add bnd as a script dependency */
+buildscript {
+ dependencies {
+ classpath files(bndURI)
+ }
+}
+
+/* Configure the subprojects */
+subprojects {
+ def bndProject = bndWorkspace.getProject(name)
+ if (bndProject != null) {
+ plugins.apply 'biz.aQute.bnd'
+ }
+}
+
diff --git a/dependencymanager/cnf/.classpath b/dependencymanager/cnf/.classpath
new file mode 100644
index 0000000..fb50116
--- /dev/null
+++ b/dependencymanager/cnf/.classpath
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/dependencymanager/cnf/.gitignore b/dependencymanager/cnf/.gitignore
new file mode 100644
index 0000000..120f0c4
--- /dev/null
+++ b/dependencymanager/cnf/.gitignore
@@ -0,0 +1,3 @@
+/bin/
+/generated/
+/cache/
diff --git a/dependencymanager/cnf/.project b/dependencymanager/cnf/.project
new file mode 100644
index 0000000..0b71642
--- /dev/null
+++ b/dependencymanager/cnf/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>cnf</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/dependencymanager/cnf/build.bnd b/dependencymanager/cnf/build.bnd
new file mode 100644
index 0000000..4dabd28
--- /dev/null
+++ b/dependencymanager/cnf/build.bnd
@@ -0,0 +1,73 @@
+#
+# 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.
+#
+
+########################
+## BND BUILD SETTINGS ##
+########################
+
+
+## Global defaults are loaded from the bnd library (as shown below), place your
+## specific settings here. Additional settings are inherited from ext/*.bnd and
+## they will be overridden by anything you specify in this file.
+
+## General Options
+#project.dependson: ${p-dependson;:}
+#project.bootclasspath: ${p-bootclasspath;:}
+#project.buildpath: ${p-buildpath;:}
+#project.sourcepath: ${p-sourcepath;:}
+#project.allsourcepath: ${p-allsourcepath;:}
+#project.output: ${p-output}
+#project.testpath: ${p-testpath;:}
+
+#-verbose: false
+#project: ${basedir}
+#src: src
+#bin: bin
+#testsrc: test
+#testbin: bin_test
+#target-dir: generated
+#target: ${project}/${target-dir}
+#build: ${workspace}/cnf
+#p: ${basename;${project}}
+#project.name: ${p}
+#plugin-dir: ${build}/plugins
+
+## Java Compiler Options
+#java: java
+#javac: javac
+javac.source: 1.7
+javac.target: 1.7
+#javac.debug: on
+
+## Bnd Options
+-sources: false
+#-sourcepath: ${project}/src
+
+
+## Properties from ext/*.bnd can be referenced in order to extend them. For
+## example, to add one additional plugin to the list defined in
+## ext/repositories.bnd:
+# -plugin: ${ext.repositories.-plugin}, org.example.MyPlugin
+
+
+## To enable baselining, uncomment the following lines:
+-baseline: *
+
+## If you use git, you might want to uncomment the following lines:
+# Git-Descriptor: ${system-allow-fail;git describe --dirty --always}
+# Git-SHA: ${system-allow-fail;git rev-list -1 HEAD}
+# -diffignore: Git-Descriptor,Git-SHA
diff --git a/dependencymanager/cnf/buildrepo/README.txt b/dependencymanager/cnf/buildrepo/README.txt
new file mode 100644
index 0000000..8bb02f1
--- /dev/null
+++ b/dependencymanager/cnf/buildrepo/README.txt
@@ -0,0 +1,6 @@
+WARNING
+=======
+
+This directory contains JAR file dependencies that are intended ONLY FOR BUILT-TIME usage.
+None are intended to be deployed as bundles into a running OSGi Framework, and indeed they may cause
+unexpected errors if they are used at runtime.
diff --git a/dependencymanager/cnf/buildrepo/biz.aQute.junit/biz.aQute.junit-latest.jar b/dependencymanager/cnf/buildrepo/biz.aQute.junit/biz.aQute.junit-latest.jar
new file mode 100644
index 0000000..3d0d214
--- /dev/null
+++ b/dependencymanager/cnf/buildrepo/biz.aQute.junit/biz.aQute.junit-latest.jar
Binary files differ
diff --git a/dependencymanager/cnf/buildrepo/biz.aQute.launcher/biz.aQute.launcher-latest.jar b/dependencymanager/cnf/buildrepo/biz.aQute.launcher/biz.aQute.launcher-latest.jar
new file mode 100644
index 0000000..efc751c
--- /dev/null
+++ b/dependencymanager/cnf/buildrepo/biz.aQute.launcher/biz.aQute.launcher-latest.jar
Binary files differ
diff --git a/dependencymanager/cnf/buildrepo/hamcrest-core/hamcrest-core-1.3.0.jar b/dependencymanager/cnf/buildrepo/hamcrest-core/hamcrest-core-1.3.0.jar
new file mode 100644
index 0000000..9d5fe16
--- /dev/null
+++ b/dependencymanager/cnf/buildrepo/hamcrest-core/hamcrest-core-1.3.0.jar
Binary files differ
diff --git a/dependencymanager/cnf/buildrepo/junit.osgi/junit.osgi-3.8.2.jar b/dependencymanager/cnf/buildrepo/junit.osgi/junit.osgi-3.8.2.jar
new file mode 100644
index 0000000..1126b95
--- /dev/null
+++ b/dependencymanager/cnf/buildrepo/junit.osgi/junit.osgi-3.8.2.jar
Binary files differ
diff --git a/dependencymanager/cnf/buildrepo/junit/junit-4.11.0.jar b/dependencymanager/cnf/buildrepo/junit/junit-4.11.0.jar
new file mode 100644
index 0000000..aaf7444
--- /dev/null
+++ b/dependencymanager/cnf/buildrepo/junit/junit-4.11.0.jar
Binary files differ
diff --git a/dependencymanager/cnf/buildrepo/osgi.core/osgi.core-4.2.0.jar b/dependencymanager/cnf/buildrepo/osgi.core/osgi.core-4.2.0.jar
new file mode 100644
index 0000000..9ed943f
--- /dev/null
+++ b/dependencymanager/cnf/buildrepo/osgi.core/osgi.core-4.2.0.jar
Binary files differ
diff --git a/dependencymanager/cnf/ext/junit.bnd b/dependencymanager/cnf/ext/junit.bnd
new file mode 100644
index 0000000..1a42d1a
--- /dev/null
+++ b/dependencymanager/cnf/ext/junit.bnd
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+junit:\
+ junit;version=latest,\
+ hamcrest-core;version=latest
diff --git a/dependencymanager/cnf/ext/pluginpaths.bnd b/dependencymanager/cnf/ext/pluginpaths.bnd
new file mode 100644
index 0000000..9c12f2e
--- /dev/null
+++ b/dependencymanager/cnf/ext/pluginpaths.bnd
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+-pluginpath:\
+ ${plugin-dir}/biz.aQute.repository/biz.aQute.repository.jar
\ No newline at end of file
diff --git a/dependencymanager/cnf/ext/repositories.bnd b/dependencymanager/cnf/ext/repositories.bnd
new file mode 100644
index 0000000..9a3aab7
--- /dev/null
+++ b/dependencymanager/cnf/ext/repositories.bnd
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+-plugin:\
+ aQute.bnd.deployer.repository.LocalIndexedRepo; name=Release; local=${workspace}/cnf/releaserepo;pretty=true,\
+ aQute.bnd.deployer.repository.LocalIndexedRepo; name=Local; local=${workspace}/cnf/localrepo;pretty=true,\
+ aQute.bnd.deployer.repository.FixedIndexedRepo; name=Bndtools Hub; locations=https://raw.githubusercontent.com/bndtools/bundle-hub/master/index.xml.gz,\
+ aQute.lib.deployer.FileRepo; name=Build; location=${workspace}/cnf/buildrepo;latest=false
+
+-releaserepo: Release
diff --git a/dependencymanager/cnf/gradle/biz.aQute.bnd.gradle.jar b/dependencymanager/cnf/gradle/biz.aQute.bnd.gradle.jar
new file mode 100644
index 0000000..0a25453
--- /dev/null
+++ b/dependencymanager/cnf/gradle/biz.aQute.bnd.gradle.jar
Binary files differ
diff --git a/dependencymanager/cnf/localrepo/de.twentyeleven.skysail.org.json-osgi/de.twentyeleven.skysail.org.json-osgi-20080701.0.0.jar b/dependencymanager/cnf/localrepo/de.twentyeleven.skysail.org.json-osgi/de.twentyeleven.skysail.org.json-osgi-20080701.0.0.jar
new file mode 100644
index 0000000..d82686f
--- /dev/null
+++ b/dependencymanager/cnf/localrepo/de.twentyeleven.skysail.org.json-osgi/de.twentyeleven.skysail.org.json-osgi-20080701.0.0.jar
Binary files differ
diff --git a/dependencymanager/cnf/localrepo/index.xml b/dependencymanager/cnf/localrepo/index.xml
new file mode 100644
index 0000000..72a68d9
--- /dev/null
+++ b/dependencymanager/cnf/localrepo/index.xml
@@ -0,0 +1,300 @@
+<?xml version="1.0" encoding="utf-8"?>
+<repository increment="1425156877273" name="Local" xmlns="http://www.osgi.org/xmlns/repository/v1.0.0">
+ <resource>
+ <capability namespace="osgi.identity">
+ <attribute name="osgi.identity" value="de.twentyeleven.skysail.org.json-osgi"/>
+ <attribute name="type" value="osgi.bundle"/>
+ <attribute name="version" type="Version" value="20080701.0.0"/>
+ </capability>
+ <capability namespace="osgi.content">
+ <attribute name="osgi.content" value="7d299abb0f4f0330c746dc456a767f1b02b3675ed24a2d4b9d6e7e7b99e7e223"/>
+ <attribute name="url" value="de.twentyeleven.skysail.org.json-osgi/de.twentyeleven.skysail.org.json-osgi-20080701.0.0.jar"/>
+ <attribute name="size" type="Long" value="38491"/>
+ <attribute name="mime" value="application/vnd.osgi.bundle"/>
+ </capability>
+ <capability namespace="osgi.wiring.bundle">
+ <attribute name="osgi.wiring.bundle" value="de.twentyeleven.skysail.org.json-osgi"/>
+ <attribute name="bundle-version" type="Version" value="20080701.0.0"/>
+ </capability>
+ <capability namespace="osgi.wiring.host">
+ <attribute name="osgi.wiring.host" value="de.twentyeleven.skysail.org.json-osgi"/>
+ <attribute name="bundle-version" type="Version" value="20080701.0.0"/>
+ </capability>
+ <capability namespace="osgi.wiring.package">
+ <attribute name="osgi.wiring.package" value="org.json"/>
+ <attribute name="version" type="Version" value="20080701.0.0"/>
+ <attribute name="bundle-symbolic-name" value="de.twentyeleven.skysail.org.json-osgi"/>
+ <attribute name="bundle-version" type="Version" value="20080701.0.0"/>
+ </capability>
+ </resource>
+ <resource>
+ <capability namespace="osgi.identity">
+ <attribute name="osgi.identity" value="org.apache.felix.configadmin"/>
+ <attribute name="type" value="osgi.bundle"/>
+ <attribute name="version" type="Version" value="1.8.1.SNAPSHOT"/>
+ </capability>
+ <capability namespace="osgi.content">
+ <attribute name="osgi.content" value="a55e3603a99d81086996622e03ed3cac324b5de469db681acef99ebcb0ab89c7"/>
+ <attribute name="url" value="org.apache.felix.configadmin/org.apache.felix.configadmin-1.8.1.jar"/>
+ <attribute name="size" type="Long" value="120333"/>
+ <attribute name="mime" value="application/vnd.osgi.bundle"/>
+ </capability>
+ <capability namespace="osgi.wiring.bundle">
+ <attribute name="osgi.wiring.bundle" value="org.apache.felix.configadmin"/>
+ <attribute name="bundle-version" type="Version" value="1.8.1.SNAPSHOT"/>
+ </capability>
+ <capability namespace="osgi.wiring.host">
+ <attribute name="osgi.wiring.host" value="org.apache.felix.configadmin"/>
+ <attribute name="bundle-version" type="Version" value="1.8.1.SNAPSHOT"/>
+ </capability>
+ <capability namespace="osgi.wiring.package">
+ <attribute name="osgi.wiring.package" value="org.apache.felix.cm"/>
+ <attribute name="version" type="Version" value="1.0.0"/>
+ <attribute name="bundle-symbolic-name" value="org.apache.felix.configadmin"/>
+ <attribute name="bundle-version" type="Version" value="1.8.1.SNAPSHOT"/>
+ </capability>
+ <capability namespace="osgi.wiring.package">
+ <attribute name="osgi.wiring.package" value="org.apache.felix.cm.file"/>
+ <attribute name="version" type="Version" value="1.0.0"/>
+ <attribute name="bundle-symbolic-name" value="org.apache.felix.configadmin"/>
+ <attribute name="bundle-version" type="Version" value="1.8.1.SNAPSHOT"/>
+ <directive name="uses" value="org.apache.felix.cm,org.osgi.framework"/>
+ </capability>
+ <capability namespace="osgi.wiring.package">
+ <attribute name="osgi.wiring.package" value="org.osgi.service.cm"/>
+ <attribute name="version" type="Version" value="1.5.0"/>
+ <attribute name="bundle-symbolic-name" value="org.apache.felix.configadmin"/>
+ <attribute name="bundle-version" type="Version" value="1.8.1.SNAPSHOT"/>
+ <directive name="uses" value="org.osgi.framework"/>
+ </capability>
+ <capability namespace="osgi.service">
+ <attribute name="objectClass" value="org.osgi.service.cm.ConfigurationAdmin"/>
+ <attribute name="service.description" value="Configuration Admin Service Specification 1.5 Implementation"/>
+ <attribute name="service.pid" value="org.osgi.service.cm.ConfigurationAdmin"/>
+ <attribute name="service.vendor" value="Apache Software Foundation"/>
+ <directive name="effective" value="active"/>
+ </capability>
+ <capability namespace="osgi.service">
+ <attribute name="objectClass" value="org.apache.felix.cm.PersistenceManager"/>
+ <attribute name="service.description" value="Platform Filesystem Persistence Manager"/>
+ <attribute name="service.pid" value="org.apache.felix.cm.file.FilePersistenceManager"/>
+ <attribute name="service.vendor" value="Apache Software Foundation"/>
+ <directive name="effective" value="active"/>
+ </capability>
+ <capability namespace="osgi.service">
+ <attribute name="objectClass" value="org.osgi.service.cm.ConfigurationAdmin"/>
+ <directive name="uses" value="org.osgi.service.cm"/>
+ <directive name="effective" value="active"/>
+ </capability>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.apache.felix.cm)(version>=1.0.0)(!(version>=1.1.0)))"/>
+ </requirement>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.apache.felix.cm.file)(version>=1.0.0)(!(version>=1.1.0)))"/>
+ </requirement>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.osgi.framework)(version>=1.4.0)(!(version>=2.0.0)))"/>
+ </requirement>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.osgi.service.cm)(version>=1.5.0)(!(version>=1.6.0)))"/>
+ </requirement>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.osgi.service.log)(version>=1.3.0))"/>
+ <directive name="resolution" value="optional"/>
+ </requirement>
+ <requirement namespace="osgi.service">
+ <directive name="filter" value="(objectClass=org.osgi.service.log.LogService)"/>
+ <directive name="effective" value="active"/>
+ <directive name="resolution" value="optional"/>
+ </requirement>
+ </resource>
+ <resource>
+ <capability namespace="osgi.identity">
+ <attribute name="osgi.identity" value="org.apache.felix.eventadmin"/>
+ <attribute name="type" value="osgi.bundle"/>
+ <attribute name="version" type="Version" value="1.4.3.SNAPSHOT"/>
+ </capability>
+ <capability namespace="osgi.content">
+ <attribute name="osgi.content" value="b1186e46be9268a5e96689292e01cfc1a008e1af6f7b6a8be44580f13b2a65ad"/>
+ <attribute name="url" value="org.apache.felix.eventadmin/org.apache.felix.eventadmin-1.4.3.jar"/>
+ <attribute name="size" type="Long" value="94092"/>
+ <attribute name="mime" value="application/vnd.osgi.bundle"/>
+ </capability>
+ <capability namespace="osgi.wiring.bundle">
+ <attribute name="osgi.wiring.bundle" value="org.apache.felix.eventadmin"/>
+ <attribute name="bundle-version" type="Version" value="1.4.3.SNAPSHOT"/>
+ </capability>
+ <capability namespace="osgi.wiring.host">
+ <attribute name="osgi.wiring.host" value="org.apache.felix.eventadmin"/>
+ <attribute name="bundle-version" type="Version" value="1.4.3.SNAPSHOT"/>
+ </capability>
+ <capability namespace="osgi.wiring.package">
+ <attribute name="osgi.wiring.package" value="org.osgi.service.event"/>
+ <attribute name="version" type="Version" value="1.3.0"/>
+ <attribute name="bundle-symbolic-name" value="org.apache.felix.eventadmin"/>
+ <attribute name="bundle-version" type="Version" value="1.4.3.SNAPSHOT"/>
+ <directive name="uses" value="org.osgi.framework"/>
+ </capability>
+ <capability namespace="osgi.service">
+ <attribute name="objectClass" value="org.osgi.service.event.EventAdmin"/>
+ <directive name="effective" value="active"/>
+ </capability>
+ <capability namespace="osgi.service">
+ <attribute name="objectClass" value="org.osgi.service.event.EventAdmin"/>
+ <directive name="uses" value="org.osgi.service.event"/>
+ <directive name="effective" value="active"/>
+ </capability>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.osgi.framework)(version>=1.3.0)(!(version>=2.0.0)))"/>
+ </requirement>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.osgi.service.cm)(version>=1.2.0)(!(version>=2.0.0)))"/>
+ <directive name="resolution" value="optional"/>
+ </requirement>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.osgi.service.metatype)(version>=1.1.0)(!(version>=2.0.0)))"/>
+ <directive name="resolution" value="optional"/>
+ </requirement>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.osgi.service.log)(version>=1.3.0)(!(version>=2.0.0)))"/>
+ <directive name="resolution" value="optional"/>
+ </requirement>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.osgi.service.event)(version>=1.3.0)(!(version>=2.0.0)))"/>
+ </requirement>
+ <requirement namespace="osgi.service">
+ <directive name="filter" value="(objectClass=org.osgi.service.event.EventHandler)"/>
+ <directive name="effective" value="active"/>
+ <directive name="resolution" value="optional"/>
+ </requirement>
+ <requirement namespace="osgi.service">
+ <directive name="filter" value="(objectClass=org.osgi.service.log.LogService)"/>
+ <directive name="effective" value="active"/>
+ <directive name="resolution" value="optional"/>
+ </requirement>
+ <requirement namespace="osgi.service">
+ <directive name="filter" value="(objectClass=org.osgi.service.log.LogReaderService)"/>
+ <directive name="effective" value="active"/>
+ <directive name="resolution" value="optional"/>
+ </requirement>
+ <requirement namespace="osgi.ee">
+ <directive name="filter" value="(&(osgi.ee=JavaSE)(version=1.6))"/>
+ </requirement>
+ </resource>
+ <resource>
+ <capability namespace="osgi.identity">
+ <attribute name="osgi.identity" value="org.apache.felix.http.api"/>
+ <attribute name="type" value="osgi.bundle"/>
+ <attribute name="version" type="Version" value="2.3.0"/>
+ </capability>
+ <capability namespace="osgi.content">
+ <attribute name="osgi.content" value="526c8b0727b9ff5a05051d98ac59b5e99f0f7924613ca7a099b8f441cac2f8ae"/>
+ <attribute name="url" value="org.apache.felix.http.api/org.apache.felix.http.api-2.3.0.jar"/>
+ <attribute name="size" type="Long" value="11104"/>
+ <attribute name="mime" value="application/vnd.osgi.bundle"/>
+ </capability>
+ <capability namespace="osgi.wiring.bundle">
+ <attribute name="osgi.wiring.bundle" value="org.apache.felix.http.api"/>
+ <attribute name="bundle-version" type="Version" value="2.3.0"/>
+ </capability>
+ <capability namespace="osgi.wiring.host">
+ <attribute name="osgi.wiring.host" value="org.apache.felix.http.api"/>
+ <attribute name="bundle-version" type="Version" value="2.3.0"/>
+ </capability>
+ <capability namespace="osgi.wiring.package">
+ <attribute name="osgi.wiring.package" value="org.osgi.service.http"/>
+ <attribute name="version" type="Version" value="1.2.0"/>
+ <attribute name="bundle-symbolic-name" value="org.apache.felix.http.api"/>
+ <attribute name="bundle-version" type="Version" value="2.3.0"/>
+ <directive name="uses" value="javax.servlet.http,javax.servlet"/>
+ </capability>
+ <capability namespace="osgi.wiring.package">
+ <attribute name="osgi.wiring.package" value="org.apache.felix.http.api"/>
+ <attribute name="version" type="Version" value="2.0.4"/>
+ <attribute name="bundle-symbolic-name" value="org.apache.felix.http.api"/>
+ <attribute name="bundle-version" type="Version" value="2.3.0"/>
+ <directive name="uses" value="javax.servlet,org.osgi.service.http"/>
+ </capability>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=javax.servlet)(version>=2.3.0)(!(version>=4.0.0)))"/>
+ </requirement>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=javax.servlet.http)(version>=2.3.0)(!(version>=4.0.0)))"/>
+ </requirement>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.osgi.service.http)(version>=1.2.0)(!(version>=2.0.0)))"/>
+ </requirement>
+ </resource>
+ <resource>
+ <capability namespace="osgi.identity">
+ <attribute name="osgi.identity" value="org.apache.felix.http.servlet-api"/>
+ <attribute name="type" value="osgi.bundle"/>
+ <attribute name="version" type="Version" value="1.0.0"/>
+ </capability>
+ <capability namespace="osgi.content">
+ <attribute name="osgi.content" value="13fa48abf6a63300e4645383647ec2e6fb69a89541b1baa8aa0620843e5eecdc"/>
+ <attribute name="url" value="org.apache.felix.http.servlet-api/org.apache.felix.http.servlet-api-1.0.0.jar"/>
+ <attribute name="size" type="Long" value="69412"/>
+ <attribute name="mime" value="application/vnd.osgi.bundle"/>
+ </capability>
+ <capability namespace="osgi.wiring.bundle">
+ <attribute name="osgi.wiring.bundle" value="org.apache.felix.http.servlet-api"/>
+ <attribute name="bundle-version" type="Version" value="1.0.0"/>
+ </capability>
+ <capability namespace="osgi.wiring.host">
+ <attribute name="osgi.wiring.host" value="org.apache.felix.http.servlet-api"/>
+ <attribute name="bundle-version" type="Version" value="1.0.0"/>
+ </capability>
+ <capability namespace="osgi.wiring.package">
+ <attribute name="osgi.wiring.package" value="javax.servlet"/>
+ <attribute name="version" type="Version" value="2.6.0"/>
+ <attribute name="bundle-symbolic-name" value="org.apache.felix.http.servlet-api"/>
+ <attribute name="bundle-version" type="Version" value="1.0.0"/>
+ <directive name="uses" value="javax.servlet.annotation,javax.servlet.descriptor"/>
+ </capability>
+ <capability namespace="osgi.wiring.package">
+ <attribute name="osgi.wiring.package" value="javax.servlet.annotation"/>
+ <attribute name="version" type="Version" value="2.6.0"/>
+ <attribute name="bundle-symbolic-name" value="org.apache.felix.http.servlet-api"/>
+ <attribute name="bundle-version" type="Version" value="1.0.0"/>
+ <directive name="uses" value="javax.servlet"/>
+ </capability>
+ <capability namespace="osgi.wiring.package">
+ <attribute name="osgi.wiring.package" value="javax.servlet.descriptor"/>
+ <attribute name="version" type="Version" value="2.6.0"/>
+ <attribute name="bundle-symbolic-name" value="org.apache.felix.http.servlet-api"/>
+ <attribute name="bundle-version" type="Version" value="1.0.0"/>
+ </capability>
+ <capability namespace="osgi.wiring.package">
+ <attribute name="osgi.wiring.package" value="javax.servlet.http"/>
+ <attribute name="version" type="Version" value="2.6.0"/>
+ <attribute name="bundle-symbolic-name" value="org.apache.felix.http.servlet-api"/>
+ <attribute name="bundle-version" type="Version" value="1.0.0"/>
+ <directive name="uses" value="javax.servlet"/>
+ </capability>
+ <capability namespace="osgi.wiring.package">
+ <attribute name="osgi.wiring.package" value="javax.servlet"/>
+ <attribute name="version" type="Version" value="3.0.0"/>
+ <attribute name="bundle-symbolic-name" value="org.apache.felix.http.servlet-api"/>
+ <attribute name="bundle-version" type="Version" value="1.0.0"/>
+ </capability>
+ <capability namespace="osgi.wiring.package">
+ <attribute name="osgi.wiring.package" value="javax.servlet.annotation"/>
+ <attribute name="version" type="Version" value="3.0.0"/>
+ <attribute name="bundle-symbolic-name" value="org.apache.felix.http.servlet-api"/>
+ <attribute name="bundle-version" type="Version" value="1.0.0"/>
+ </capability>
+ <capability namespace="osgi.wiring.package">
+ <attribute name="osgi.wiring.package" value="javax.servlet.descriptor"/>
+ <attribute name="version" type="Version" value="3.0.0"/>
+ <attribute name="bundle-symbolic-name" value="org.apache.felix.http.servlet-api"/>
+ <attribute name="bundle-version" type="Version" value="1.0.0"/>
+ </capability>
+ <capability namespace="osgi.wiring.package">
+ <attribute name="osgi.wiring.package" value="javax.servlet.http"/>
+ <attribute name="version" type="Version" value="3.0.0"/>
+ <attribute name="bundle-symbolic-name" value="org.apache.felix.http.servlet-api"/>
+ <attribute name="bundle-version" type="Version" value="1.0.0"/>
+ </capability>
+ </resource>
+</repository>
\ No newline at end of file
diff --git a/dependencymanager/cnf/localrepo/index.xml.sha b/dependencymanager/cnf/localrepo/index.xml.sha
new file mode 100644
index 0000000..1308154
--- /dev/null
+++ b/dependencymanager/cnf/localrepo/index.xml.sha
@@ -0,0 +1 @@
+6985dc81e95848d75021052d7dd392b0f3eae258fdc1ed883c10f6b2be40555b
\ No newline at end of file
diff --git a/dependencymanager/cnf/localrepo/org.apache.felix.configadmin/org.apache.felix.configadmin-1.8.1.jar b/dependencymanager/cnf/localrepo/org.apache.felix.configadmin/org.apache.felix.configadmin-1.8.1.jar
new file mode 100644
index 0000000..109463d
--- /dev/null
+++ b/dependencymanager/cnf/localrepo/org.apache.felix.configadmin/org.apache.felix.configadmin-1.8.1.jar
Binary files differ
diff --git a/dependencymanager/cnf/localrepo/org.apache.felix.eventadmin/org.apache.felix.eventadmin-1.4.3.jar b/dependencymanager/cnf/localrepo/org.apache.felix.eventadmin/org.apache.felix.eventadmin-1.4.3.jar
new file mode 100644
index 0000000..6d651c7
--- /dev/null
+++ b/dependencymanager/cnf/localrepo/org.apache.felix.eventadmin/org.apache.felix.eventadmin-1.4.3.jar
Binary files differ
diff --git a/dependencymanager/cnf/localrepo/org.apache.felix.http.api/org.apache.felix.http.api-2.3.0.jar b/dependencymanager/cnf/localrepo/org.apache.felix.http.api/org.apache.felix.http.api-2.3.0.jar
new file mode 100644
index 0000000..d9df5d7
--- /dev/null
+++ b/dependencymanager/cnf/localrepo/org.apache.felix.http.api/org.apache.felix.http.api-2.3.0.jar
Binary files differ
diff --git a/dependencymanager/cnf/localrepo/org.apache.felix.http.servlet-api/org.apache.felix.http.servlet-api-1.0.0.jar b/dependencymanager/cnf/localrepo/org.apache.felix.http.servlet-api/org.apache.felix.http.servlet-api-1.0.0.jar
new file mode 100644
index 0000000..49841e1
--- /dev/null
+++ b/dependencymanager/cnf/localrepo/org.apache.felix.http.servlet-api/org.apache.felix.http.servlet-api-1.0.0.jar
Binary files differ
diff --git a/dependencymanager/cnf/plugins/biz.aQute.repository/biz.aQute.repository.jar b/dependencymanager/cnf/plugins/biz.aQute.repository/biz.aQute.repository.jar
new file mode 100644
index 0000000..1b6e664
--- /dev/null
+++ b/dependencymanager/cnf/plugins/biz.aQute.repository/biz.aQute.repository.jar
Binary files differ
diff --git a/dependencymanager/cnf/releaserepo/index.xml b/dependencymanager/cnf/releaserepo/index.xml
new file mode 100644
index 0000000..9a86204
--- /dev/null
+++ b/dependencymanager/cnf/releaserepo/index.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="utf-8"?>
+<repository increment="1425156877252" name="Release" xmlns="http://www.osgi.org/xmlns/repository/v1.0.0">
+ <resource>
+ <capability namespace="osgi.identity">
+ <attribute name="osgi.identity" value="org.apache.felix.dependencymanager.runtime"/>
+ <attribute name="type" value="osgi.bundle"/>
+ <attribute name="version" type="Version" value="3.2.0"/>
+ </capability>
+ <capability namespace="osgi.content">
+ <attribute name="osgi.content" value="ca2ae8e0ea60a77153ec79ea540e28f01c6fd558d389d3813440ab82c942d6ec"/>
+ <attribute name="url" value="org.apache.felix.dependencymanager.runtime/org.apache.felix.dependencymanager.runtime-3.2.0.jar"/>
+ <attribute name="size" type="Long" value="104762"/>
+ <attribute name="mime" value="application/vnd.osgi.bundle"/>
+ </capability>
+ <capability namespace="osgi.wiring.bundle">
+ <attribute name="osgi.wiring.bundle" value="org.apache.felix.dependencymanager.runtime"/>
+ <attribute name="bundle-version" type="Version" value="3.2.0"/>
+ </capability>
+ <capability namespace="osgi.wiring.host">
+ <attribute name="osgi.wiring.host" value="org.apache.felix.dependencymanager.runtime"/>
+ <attribute name="bundle-version" type="Version" value="3.2.0"/>
+ </capability>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.apache.felix.dm)(version>=3.1.0)(!(version>=4.0.0)))"/>
+ </requirement>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.osgi.framework)(version>=1.5.0)(!(version>=2.0.0)))"/>
+ </requirement>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.osgi.service.log)(version>=1.3.0)(!(version>=2.0.0)))"/>
+ </requirement>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.osgi.service.packageadmin)(version>=1.2.0)(!(version>=2.0.0)))"/>
+ </requirement>
+ </resource>
+ <resource>
+ <capability namespace="osgi.identity">
+ <attribute name="osgi.identity" value="org.apache.felix.dependencymanager.shell"/>
+ <attribute name="type" value="osgi.bundle"/>
+ <attribute name="version" type="Version" value="3.2.0"/>
+ </capability>
+ <capability namespace="osgi.content">
+ <attribute name="osgi.content" value="8abdc97de5abe96aac15c2090d5544ddf7ccc06440d7a20f2daccee2efbf340d"/>
+ <attribute name="url" value="org.apache.felix.dependencymanager.shell/org.apache.felix.dependencymanager.shell-3.2.0.jar"/>
+ <attribute name="size" type="Long" value="21257"/>
+ <attribute name="mime" value="application/vnd.osgi.bundle"/>
+ </capability>
+ <capability namespace="osgi.wiring.bundle">
+ <attribute name="osgi.wiring.bundle" value="org.apache.felix.dependencymanager.shell"/>
+ <attribute name="bundle-version" type="Version" value="3.2.0"/>
+ </capability>
+ <capability namespace="osgi.wiring.host">
+ <attribute name="osgi.wiring.host" value="org.apache.felix.dependencymanager.shell"/>
+ <attribute name="bundle-version" type="Version" value="3.2.0"/>
+ </capability>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.apache.felix.dm)(version>=3.1.0)(!(version>=4.0.0)))"/>
+ </requirement>
+ <requirement namespace="osgi.wiring.package">
+ <attribute name="status" value="provisional"/>
+ <directive name="filter" value="(&(osgi.wiring.package=org.apache.felix.service.command)(version>=0.10.0)(!(version>=1.0.0)))"/>
+ </requirement>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.osgi.framework)(version>=1.5.0)(!(version>=2.0.0)))"/>
+ </requirement>
+ </resource>
+ <resource>
+ <capability namespace="osgi.identity">
+ <attribute name="osgi.identity" value="org.apache.felix.dependencymanager"/>
+ <attribute name="type" value="osgi.bundle"/>
+ <attribute name="version" type="Version" value="3.2.0"/>
+ </capability>
+ <capability namespace="osgi.content">
+ <attribute name="osgi.content" value="31cc27a67c457face02ababd9901dfc19bd80c01ce22f1bab25be9c7c948f265"/>
+ <attribute name="url" value="org.apache.felix.dependencymanager/org.apache.felix.dependencymanager-3.2.0.jar"/>
+ <attribute name="size" type="Long" value="193078"/>
+ <attribute name="mime" value="application/vnd.osgi.bundle"/>
+ </capability>
+ <capability namespace="osgi.wiring.bundle">
+ <attribute name="osgi.wiring.bundle" value="org.apache.felix.dependencymanager"/>
+ <attribute name="bundle-version" type="Version" value="3.2.0"/>
+ </capability>
+ <capability namespace="osgi.wiring.host">
+ <attribute name="osgi.wiring.host" value="org.apache.felix.dependencymanager"/>
+ <attribute name="bundle-version" type="Version" value="3.2.0"/>
+ </capability>
+ <capability namespace="osgi.wiring.package">
+ <attribute name="osgi.wiring.package" value="org.apache.felix.dm"/>
+ <attribute name="version" type="Version" value="3.1.0"/>
+ <attribute name="bundle-symbolic-name" value="org.apache.felix.dependencymanager"/>
+ <attribute name="bundle-version" type="Version" value="3.2.0"/>
+ <directive name="uses" value="org.osgi.framework"/>
+ </capability>
+ <capability namespace="osgi.wiring.package">
+ <attribute name="osgi.wiring.package" value="org.apache.felix.dm.tracker"/>
+ <attribute name="version" type="Version" value="3.1.0"/>
+ <attribute name="bundle-symbolic-name" value="org.apache.felix.dependencymanager"/>
+ <attribute name="bundle-version" type="Version" value="3.2.0"/>
+ <directive name="uses" value="org.osgi.framework,org.apache.felix.dm"/>
+ </capability>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.apache.felix.dm.tracker)(version>=3.1.0)(!(version>=3.2.0)))"/>
+ </requirement>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.osgi.framework)(version>=1.5.0)(!(version>=2.0.0)))"/>
+ </requirement>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.osgi.service.cm)(version>=1.3.0)(!(version>=2.0.0)))"/>
+ </requirement>
+ <requirement namespace="osgi.wiring.package">
+ <directive name="filter" value="(&(osgi.wiring.package=org.osgi.service.metatype)(version>=1.1.0)(!(version>=2.0.0)))"/>
+ </requirement>
+ </resource>
+</repository>
\ No newline at end of file
diff --git a/dependencymanager/cnf/releaserepo/index.xml.sha b/dependencymanager/cnf/releaserepo/index.xml.sha
new file mode 100644
index 0000000..9321c6e
--- /dev/null
+++ b/dependencymanager/cnf/releaserepo/index.xml.sha
@@ -0,0 +1 @@
+52327447e0d3fc0d7d29dd203c14473caad9388425a7d7dff83adfcb0d814765
\ No newline at end of file
diff --git a/dependencymanager/cnf/releaserepo/org.apache.felix.dependencymanager.runtime/org.apache.felix.dependencymanager.runtime-3.2.0.jar b/dependencymanager/cnf/releaserepo/org.apache.felix.dependencymanager.runtime/org.apache.felix.dependencymanager.runtime-3.2.0.jar
new file mode 100644
index 0000000..b9148fe
--- /dev/null
+++ b/dependencymanager/cnf/releaserepo/org.apache.felix.dependencymanager.runtime/org.apache.felix.dependencymanager.runtime-3.2.0.jar
Binary files differ
diff --git a/dependencymanager/cnf/releaserepo/org.apache.felix.dependencymanager.shell/org.apache.felix.dependencymanager.shell-3.2.0.jar b/dependencymanager/cnf/releaserepo/org.apache.felix.dependencymanager.shell/org.apache.felix.dependencymanager.shell-3.2.0.jar
new file mode 100644
index 0000000..90f5dd0
--- /dev/null
+++ b/dependencymanager/cnf/releaserepo/org.apache.felix.dependencymanager.shell/org.apache.felix.dependencymanager.shell-3.2.0.jar
Binary files differ
diff --git a/dependencymanager/cnf/releaserepo/org.apache.felix.dependencymanager/org.apache.felix.dependencymanager-3.2.0.jar b/dependencymanager/cnf/releaserepo/org.apache.felix.dependencymanager/org.apache.felix.dependencymanager-3.2.0.jar
new file mode 100644
index 0000000..36f36d9
--- /dev/null
+++ b/dependencymanager/cnf/releaserepo/org.apache.felix.dependencymanager/org.apache.felix.dependencymanager-3.2.0.jar
Binary files differ
diff --git a/dependencymanager/cnf/src/.gitignore b/dependencymanager/cnf/src/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dependencymanager/cnf/src/.gitignore
diff --git a/dependencymanager/docs/.project b/dependencymanager/docs/.project
new file mode 100644
index 0000000..e248a4f
--- /dev/null
+++ b/dependencymanager/docs/.project
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>docs</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ </buildSpec>
+ <natures>
+ </natures>
+</projectDescription>
diff --git a/dependencymanager/docs/A_Glance_At_DependencyManager.odp b/dependencymanager/docs/A_Glance_At_DependencyManager.odp
new file mode 100644
index 0000000..7221e7a
--- /dev/null
+++ b/dependencymanager/docs/A_Glance_At_DependencyManager.odp
Binary files differ
diff --git a/dependencymanager/docs/migrating.mdtext b/dependencymanager/docs/migrating.mdtext
new file mode 100644
index 0000000..306f91e
--- /dev/null
+++ b/dependencymanager/docs/migrating.mdtext
@@ -0,0 +1,14 @@
+# Migrating from earlier versions
+
+DependencyManager 4.0 has some API changes that need to be taken into account when migrating from DependencyManager 3.
+
+* A dependency can no longer be shared accross components.
+* You no longer have to call setInstanceBound() when adding a dependency from within the init() method of a component. Therefore the setInstanceBound() method has been removed from all Dependency interfaces.
+* in the Dependency interface, the following method have been removed: isInstanceBound, invokeAdded, invokeRemoved, createCopy.
+* In the Component interface, the "Object Component.getService()" method has been replaced by the "<T> T getInstance()" method.
+* In the Component interface, the "void addStateListener(ComponentStateListener listener) method" has been replaced by the "add(ComponentStateListener listener)" method.
+* In the Component interface, the "start", "stop", "getDependencies" methods have been removed.
+* In the Component interface and in the DependencyManager class, the createTemporalServiceDependency() method is now taking a timeout parameter: createTemporalServiceDependency(long timeout).
+* The ComponentStateListener interface has changed: it is now providing a single "changed(Component c, ComponentState state)" method.
+* The DependencyManager 4 Shell commands are no longer available for framework specific shell implementations, and support the gogo shell only.
+* The TemporalServiceDependency interface has been removed.
diff --git a/dependencymanager/docs/shell.mdtext b/dependencymanager/docs/shell.mdtext
new file mode 100644
index 0000000..f2bf6c7
--- /dev/null
+++ b/dependencymanager/docs/shell.mdtext
@@ -0,0 +1,102 @@
+# Introduction
+
+The shell bundle for the dependency manager extends the gogo shell with one new command called "dm". This command can be used to get insight in the actual components and services in a running OSGi framework.
+
+Typing help ```help dm``` in the gogo shell gives an overview of the available command options.
+
+```
+dm - List dependency manager components
+ scope: dependencymanager
+ flags:
+ compact, cp Displays components using a compact form
+ nodeps, nd Hides component dependencies
+ notavail, na Only displays unavailable components
+ stats, stat, st Displays components statistics
+ wtf Detects where are the root failures
+ options:
+ bundleIds, bid, bi, b <List of bundle ids or bundle symbolic names to display (comma separated)> [optional]
+ componentIds, cid, ci <List of component identifiers to display (comma separated)> [optional]
+ components, c <Regex(s) used to filter on component implementation class names (comma separated), can be negated using "!" prefix> [optional]
+ services, s <OSGi filter used to filter some service properties> [optional]
+ top <Max number of top components to display (0=all)> This command displays components callbacks (init/start) times> [optional]
+ parameters:
+ CommandSession
+```
+
+
+# Usage examples
+Below are some examples for typical usage of the dependency manager shell commands. The examples are based on a simple component model with a dashboard which has a required dependency on four probes (temperature, humidity, radiation, pressure). The radiation probe requires a Sensor service but this sensor is not available.
+
+__List all dependency manager components__
+
+```dm```
+
+Sample output
+
+```
+[9] dm.demo
+ [6] dm.demo.Probe(type=radiation) unregistered
+ dm.demo.Sensor service required unavailable
+ [7] dm.demo.Probe(type=humidity) registered
+ [9] dm.demo.impl.Dashboard unregistered
+ dm.demo.Probe (type=temperature) service required available
+ dm.demo.Probe (type=radiation) service required unavailable
+ dm.demo.Probe (type=humidity) service required available
+ dm.demo.Probe (type=pressure) service required available
+ [5] dm.demo.Probe(type=temperature) registered
+ [8] dm.demo.Probe(type=pressure) registered
+```
+All components are listed including the dependencies and the availability of these dependencies. The top level element is the bundle and below are the components registered with that bundle's bundle context. The lowest level is that of the component's dependencies.
+
+```
+[bundleid] bundle
+ [component id] component interfaces (service properties)
+ dependency <availability>
+```
+
+The following flags can be used to tailor the output.
+
+```compact, cp``` shortens package names and dependencies and therefore gives a more compressed output.
+
+```nodeps, nd``` omits the dependencies from the output.
+
+```notavail, na``` filters out all components that are registered wich results in the output only containing those components that are in the unregistered state due to one or more unsatisfied required dependencies. This is the command option most used when using the dependency manager shell commands.
+
+Sample output for ```dm na```:
+
+```
+[9] dm.demo
+ [14] dm.demo.impl.Dashboard unregistered
+ dm.demo.Probe (type=radiation) service required unavailable
+ [11] dm.demo.Probe(type=radiation) unregistered
+ dm.demo.Sensor service required unavailable
+```
+
+The flags can be used in conjunction with the other command options.
+
+__Find all components for a given classname__
+
+```dm c .*ProbeImpl```
+
+dm c or components finds all components for which the classname of the implementation matches the regular expression.
+
+__Find all services matching a service filter__
+
+```dm s "(type=temperature)"```
+
+dm s allows finding components based on the service properties of their registered services in the service registry using a standard OSGi service filter.
+
+__Find out why components are not registered__
+
+```dm wtf```
+
+Sample output
+
+```
+2 missing dependencies found.
+-----------------------------
+The following service(s) are missing:
+ * dm.demo.Sensor is not found in the service registry
+```
+
+wtf gives the root cause for components not being registered and therefore their services not being available. In a typical application components have dependencies on services implemented by components that have dependencies on services etcetera. This transitivity means that an entire chain of components could be unregistered due to a (few) root dependencies not being satisified. wtf is about discovering those dependencies.
diff --git a/dependencymanager/docs/whatsnew.mdtext b/dependencymanager/docs/whatsnew.mdtext
new file mode 100644
index 0000000..425a31b
--- /dev/null
+++ b/dependencymanager/docs/whatsnew.mdtext
@@ -0,0 +1,22 @@
+# What's new in DependencyManager 4.0
+
+DependencyManager 4.0 has been significantly reworked to improve support for concurrency. The following principles form the basis of the new concurrency model in DM4.
+
+ * 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.
+
+DependencyManager 4 now also supports parallel execution of component wiring.
+
+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 information.
+
+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/gradle.properties b/dependencymanager/gradle.properties
new file mode 100644
index 0000000..d0b7672
--- /dev/null
+++ b/dependencymanager/gradle.properties
@@ -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.
+ */
+
+# http(s) proxy settings
+
+#systemProp.http.proxyHost=
+#systemProp.http.proxyPort=
+#systemProp.http.proxyUser=
+#systemProp.http.proxyPassword=
+#systemProp.http.nonProxyHosts=*.nonproxyrepos.com|localhost
+#systemProp.https.proxyHost=
+#systemProp.https.proxyPort=
+#systemProp.https.proxyUser=
+#systemProp.https.proxyPassword=
+#systemProp.https.nonProxyHosts=*.nonproxyrepos.com|localhost
+
+# cnf project name
+bnd_cnf=cnf
+
+# bnd_jar can also be a URL.
+bnd_jar=cnf/gradle/biz.aQute.bnd.gradle.jar
+
+# bnd_build can be set to the name of a "master" project whose dependencies will seed the set of projects to build.
+bnd_build=
+
+# Default gradle task to build
+bnd_defaultTask=build
+
+# This should be false. It only needs to be true in rare cases.
+bnd_preCompileRefresh=false
diff --git a/dependencymanager/gradle/wrapper/gradle-wrapper.jar b/dependencymanager/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..3d0dee6
--- /dev/null
+++ b/dependencymanager/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/dependencymanager/gradle/wrapper/gradle-wrapper.properties b/dependencymanager/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..1b2a875
--- /dev/null
+++ b/dependencymanager/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Jan 15 16:04:00 CET 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-bin.zip
diff --git a/dependencymanager/gradlew b/dependencymanager/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/dependencymanager/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/.classpath b/dependencymanager/org.apache.felix.dependencymanager.annotation/.classpath
new file mode 100644
index 0000000..f89ae43
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/.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.annotation/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.annotation/.gitignore
new file mode 100644
index 0000000..90dde36
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/.gitignore
@@ -0,0 +1,3 @@
+/bin/
+/bin_test/
+/generated/
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/.project b/dependencymanager/org.apache.felix.dependencymanager.annotation/.project
new file mode 100644
index 0000000..35b34ca
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.apache.felix.dependencymanager.annotation</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.annotation/.settings/org.eclipse.jdt.core.prefs b/dependencymanager/org.apache.felix.dependencymanager.annotation/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..7341ab1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/.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.annotation/bnd.bnd b/dependencymanager/org.apache.felix.dependencymanager.annotation/bnd.bnd
new file mode 100644
index 0000000..392161c
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/bnd.bnd
@@ -0,0 +1,37 @@
+#
+# 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.
+#
+Bundle-Version:4.0.0
+-buildpath: \
+ osgi.core;version=4.2,\
+ de.twentyeleven.skysail.org.json-osgi;version=20080701.0,\
+ biz.aQute.bndlib;version=2.4
+Private-Package: \
+ org.apache.felix.dm.annotation.plugin.bnd, \
+ org.json.*
+Export-Package: \
+ org.apache.felix.dm.annotation.api
+Include-Resource: META-INF/=resources/LICENSE,\
+ META-INF/=resources/NOTICE,\
+ META-INF/=resources/DEPENDENCIES,\
+ META-INF/=resources/changelog.txt
+
+Bundle-Name: Apache Felix Dependency Manager Annotations
+Bundle-Description: Annotations for Apache Felix Dependency Manager
+Bundle-Category: osgi
+Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+Bundle-Vendor: The Apache Software Foundation
+Bundle-DocURL: http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager/apache-felix-dependency-manager-using-annotations.html
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/resources/DEPENDENCIES b/dependencymanager/org.apache.felix.dependencymanager.annotation/resources/DEPENDENCIES
new file mode 100644
index 0000000..ba8e14b
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/resources/DEPENDENCIES
@@ -0,0 +1,29 @@
+Apache Felix Dependency Manager Annotation
+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 from http://www.json.org.
+Copyright (c) 2002 JSON.org
+Licensed under the JSON License
+
+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.
+
+This product uses software developed by Peter Kriens
+(http://www.aqute.biz/Code/Bnd)
+Copyright 2006-2014 aQute, All rights reserved
+Licensed under the Apache License 2.0.
+
+III. Overall License Summary
+
+- Apache License 2.0
+- JSON License
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/resources/LICENSE b/dependencymanager/org.apache.felix.dependencymanager.annotation/resources/LICENSE
new file mode 100644
index 0000000..eedfc8a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/resources/LICENSE
@@ -0,0 +1,228 @@
+
+ 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.
+
+=========================================================================
+== JSON License ==
+=========================================================================
+
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/resources/NOTICE b/dependencymanager/org.apache.felix.dependencymanager.annotation/resources/NOTICE
new file mode 100644
index 0000000..2581fb6
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/resources/NOTICE
@@ -0,0 +1,10 @@
+Apache Felix Dependency Manager Annotation
+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 from http://www.json.org.
+Copyright (c) 2002 JSON.org
+Licensed under the JSON License
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/resources/changelog.txt b/dependencymanager/org.apache.felix.dependencymanager.annotation/resources/changelog.txt
new file mode 100644
index 0000000..0e49f0a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/resources/changelog.txt
@@ -0,0 +1,8 @@
+Release 4.0.0:
+-------------
+
+FELIX-4805: Deprecate DM annotation metatypes
+FELIX-4777: Dynamic initialization time configuration of @ConfigurationDependency
+FELIX-4676: Add Provide-Capability for DependencyManager Runtime bundle
+FELIX-4600: Cherrypicking of propagated properties
+FELIX-4684: Replace DependencyManager Runtime "factorySet" by a cleaner API
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/.gitignore
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/AdapterService.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/AdapterService.java
new file mode 100644
index 0000000..bf64c19
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/AdapterService.java
@@ -0,0 +1,123 @@
+/*
+ * 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.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates an Adapater service. Adapters, like {@link AspectService}, are used to "extend"
+ * existing services, and can publish different services based on the existing one.
+ * An example would be implementing a management interface for an existing service, etc ....
+ * <p>When you annotate an adapter class with the <code>@AdapterService</code> annotation, it will be applied
+ * to any service that matches the implemented interface and filter. The adapter will be registered
+ * with the specified interface and existing properties from the original service plus any extra
+ * properties you supply here. If you declare the original service as a member it will be injected.
+ *
+ * <h3>Usage Examples</h3>
+ *
+ * <p> Here, the AdapterService is registered into the OSGI registry each time an AdapteeService
+ * is found from the registry. The AdapterImpl class adapts the AdapteeService to the AdapterService.
+ * The AdapterService will also have a service property (param=value), and will also include eventual
+ * service properties found from the AdapteeService:<p>
+ * <blockquote>
+ * <pre>
+ *
+ * @AdapterService(adapteeService = AdapteeService.class, properties={@Property(name="param", value="value")})
+ * class AdapterImpl implements AdapterService {
+ * // The service we are adapting (injected by reflection)
+ * protected AdapteeService adaptee;
+ *
+ * public void doWork() {
+ * adaptee.mehod1();
+ * adaptee.method2();
+ * }
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.TYPE)
+public @interface AdapterService
+{
+ /**
+ * Sets the adapter service interface(s). By default, the directly implemented interface(s) is (are) used.
+ */
+ Class<?>[] provides() default {};
+
+ /**
+ * Sets some additional properties to use with the adapter service registration. By default,
+ * the adapter will inherit all adaptee service properties.
+ */
+ Property[] properties() default {};
+
+ /**
+ * Sets the adaptee service interface this adapter is applying to.
+ */
+ Class<?> adapteeService();
+
+ /**
+ * Sets the filter condition to use with the adapted service interface.
+ */
+ String adapteeFilter() default "";
+
+ /**
+ * Sets the static method used to create the adapter service implementation instance.
+ * By default, the default constructor of the annotated class is used.
+ */
+ String factoryMethod() default "";
+
+ /**
+ * Sets the field name where to inject the original service. By default, the original service is injected
+ * in any attributes in the aspect implementation that are of the same type as the aspect interface.
+ */
+ String field() default "";
+
+ /**
+ * The callback method to be invoked when the original service is available. This attribute can't be mixed with
+ * the field attribute.
+ */
+ String added() default "";
+
+ /**
+ * The callback method to be invoked when the original service properties have changed. When this attribute is used,
+ * then the added attribute must also be used.
+ */
+ String changed() default "";
+
+ /**
+ * name of the callback method to invoke on swap.
+ */
+ String swap() default "";
+
+ /**
+ * The callback method to invoke when the service is lost. When this attribute is used, then the added attribute
+ * must also be used.
+ */
+ String removed() default "";
+
+ /**
+ * Specifies if adaptee service properties should be propagated to the adapter service.
+ */
+ boolean propagate() default true;
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/AspectService.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/AspectService.java
new file mode 100644
index 0000000..600ba85
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/AspectService.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 org.apache.felix.dm.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates an Aspect service. Aspects allow you to define an interceptor, or chain of interceptors
+ * for a service (to add features like caching or logging, etc ...). The dependency manager intercepts
+ * the original service, and allows you to execute some code before invoking the original service ...
+ * The aspect will be applied to any service that matches the specified interface and filter and
+ * 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.<p>
+ *
+ * <h3>Usage Examples</h3>
+ *
+ * <p> Here, the AspectService is registered into the OSGI registry each time an InterceptedService
+ * is found from the registry. The AspectService class intercepts the InterceptedService, and decorates
+ * its "doWork()" method. This aspect uses a rank with value "10", meaning that it will intercept some
+ * other eventual aspects with lower ranks. The Aspect also uses a service property (param=value), and
+ * include eventual service properties found from the InterceptedService:<p>
+ * <blockquote>
+ * <pre>
+ *
+ * @AspectService(ranking=10), properties={@Property(name="param", value="value")})
+ * class AspectService implements InterceptedService {
+ * // The service we are intercepting (injected by reflection)
+ * protected InterceptedService intercepted;
+ *
+ * public void doWork() {
+ * intercepted.doWork();
+ * }
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.TYPE)
+public @interface AspectService
+{
+ /**
+ * Sets the service interface to apply the aspect to. By default, the directly implemented interface is used.
+ */
+ Class<?> service() default Object.class;
+
+ /**
+ * Sets the filter condition to use with the service interface this aspect is applying to.
+ */
+ String filter() default "";
+
+ /**
+ * Sets Additional properties to use with the aspect service registration
+ */
+ Property[] properties() default {};
+
+ /**
+ * Sets the ranking of this aspect. Since aspects are chained, the ranking defines the order in which they are chained.
+ * Chain ranking is implemented as a service ranking so service lookups automatically retrieve the top of the
+ * chain.
+ */
+ int ranking();
+
+ /**
+ * Sets the field name where to inject the original service. By default, the original service is injected
+ * in any attributes in the aspect implementation that are of the same type as the aspect interface.
+ */
+ String field() default "";
+
+ /**
+ * The callback method to be invoked when the original service is available. This attribute can't be mixed with
+ * the field attribute.
+ */
+ String added() default "";
+
+ /**
+ * The callback method to be invoked when the original service properties have changed. When this attribute is used,
+ * then the added attribute must also be used.
+ */
+ String changed() default "";
+
+ /**
+ * The callback method to invoke when the service is lost. When this attribute is used, then the added attribute
+ * must also be used.
+ */
+ String removed() default "";
+
+ /**
+ * name of the callback method to invoke on swap.
+ */
+ String swap() default "";
+
+ /**
+ * Sets the static method used to create the AspectService implementation instance. The
+ * default constructor of the annotated class is used. The factoryMethod can be used to provide a specific
+ * aspect implements, like a DynamicProxy.
+ */
+ String factoryMethod() default "";
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/BundleAdapterService.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/BundleAdapterService.java
new file mode 100644
index 0000000..7c0d5e9
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/BundleAdapterService.java
@@ -0,0 +1,96 @@
+/*
+ * 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.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * Annotates a bundle adapter service class. Bundle adapters are similar to {@link AdapterService},
+ * but instead of adapting a service, they adapt a bundle with a certain set of states (STARTED|INSTALLED|...),
+ * and provide a service on top of it. <p>
+ * The bundle adapter will be applied to any bundle that matches the specified bundle state mask and
+ * filter conditions, which may match some of the bundle OSGi manifest headers. For each matching
+ * bundle an adapter will be created based on the adapter implementation class. The adapter will be
+ * registered with the specified interface and with service properties found from the original bundle
+ * OSGi manifest headers plus any extra properties you supply here.
+ * If you declare the original bundle as a member it will be injected.
+ *
+ * <h3>Usage Examples</h3>
+ *
+ * <p> In the following example, a "VideoPlayer" Service is registered into the OSGi registry each time
+ * an active bundle containing a "Video-Path" manifest header is detected:
+ * <p>
+ * <blockquote>
+ * <pre>
+ * @BundleAdapterService(filter = "(Video-Path=*)", stateMask = Bundle.ACTIVE, propagate=true)
+ * public class VideoPlayerImpl implements VideoPlayer {
+ * Bundle bundle; // Injected by reflection
+ *
+ * void play() {
+ * URL mpegFile = bundle.getEntry(bundle.getHeaders().get("Video-Path"));
+ * // play the video provided by the bundle ...
+ * }
+ *
+ * void stop() {}
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public @Retention(RetentionPolicy.CLASS)
+@Target(ElementType.TYPE)
+@interface BundleAdapterService
+{
+ /**
+ * The interface(s) to use when registering adapters. By default, the interface(s) directly implemented
+ * by the annotated class is (are) used.
+ */
+ Class<?>[] provides() default {};
+
+ /**
+ * Additional properties to use with the service registration
+ */
+ Property[] properties() default {};
+
+ /**
+ * The filter used to match a given bundle.
+ */
+ String filter();
+
+ /**
+ * the bundle state mask to apply
+ */
+ int stateMask() default Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE;
+
+ /**
+ * Specifies if manifest headers from the bundle should be propagated to the service properties.
+ */
+ boolean propagate() default true;
+
+ /**
+ * Sets the static method used to create the BundleAdapterService implementation instance.
+ */
+ String factoryMethod() default "";
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/BundleDependency.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/BundleDependency.java
new file mode 100644
index 0000000..87d8204
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/BundleDependency.java
@@ -0,0 +1,107 @@
+/*
+ * 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.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * Annotates a class or method for a bundle dependency. A bundle dependency allows you to
+ * depend on a bundle in a certain set of states (INSTALLED|RESOLVED|STARTED|...), as
+ * indicated by a state mask. You can also use a filter condition that is matched against
+ * all manifest entries. When applied on a class field, optional unavailable dependencies
+ * are injected with a NullObject.
+ *
+ * <h3>Usage Examples</h3>
+ *
+ * <p> In the following example, the "SCR" Component allows to track
+ * all bundles containing a specific "Service-Component" OSGi header, in order to load
+ * and manage all Declarative Service components specified in the SCR xml documents referenced by the header:
+ * <p>
+ * <blockquote>
+ * <pre>
+ * @Component
+ * public class SCR {
+ * @BundleDependency(required = false,
+ * removed = "unloadServiceComponents",
+ * filter = "(Service-Component=*)"
+ * stateMask = Bundle.ACTIVE)
+ * void loadServiceComponents(Bundle b) {
+ * String descriptorPaths = (String) b.getHeaders().get("Service-Component");
+ * // load all service component specified in the XML descriptorPaths files ...
+ * }
+ *
+ * void unloadServiceComponents(Bundle b) {
+ * // unload all service component we loaded from our "loadServiceComponents" method.
+ * }
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.METHOD)
+public @interface BundleDependency
+{
+ /**
+ * Returns the callback method to be invoked when the service have changed.
+ */
+ String changed() default "";
+
+ /**
+ * Returns the callback method to invoke when the service is lost.
+ */
+ String removed() default "";
+
+ /**
+ * Returns whether the dependency is required or not.
+ */
+ boolean required() default true;
+
+ /**
+ * Returns the filter dependency
+ */
+ String filter() default "";
+
+ /**
+ * Returns the bundle state mask
+ */
+ int stateMask() default Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE;
+
+ /**
+ * Specifies if the manifest headers from the bundle should be propagated to
+ * the service properties.
+ */
+ boolean propagate() default false;
+
+ /**
+ * The name used when dynamically configuring this dependency from the init method.
+ * Specifying this attribute allows to dynamically configure the dependency
+ * <code>filter</code> and <code>required</code> flag from the Service's init method.
+ * All unnamed dependencies will be injected before the init() method; so from the init() method, you can
+ * then pick up whatever information needed from already injected (unnamed) dependencies, and configure dynamically
+ * your named dependencies, which will then be calculated once the init() method returns.
+ */
+ String name() default "";
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Component.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Component.java
new file mode 100644
index 0000000..0aa07ed
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Component.java
@@ -0,0 +1,222 @@
+/*
+ * 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.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates an OSGi Component class with its dependencies. Components are the main building
+ * blocks for OSGi applications. They can publish themselves as a service, and/or they can have
+ * dependencies. These dependencies will influence their life cycle as component will only be
+ * activated when all required dependencies are available.
+ * By default, all directly implemented interfaces are registered into the OSGi registry,
+ * and the component is instantiated automatically, when the component bundle is started and
+ * when the component dependencies are available. If you need to take control of when and how
+ * much component instances must be created, then you can use the <code>factoryName</code>
+ * annotation attribute.<p>
+ * If a <code>factoryName</code> attribute is set, the component is not started automatically
+ * during bundle startup, and a <code>org.apache.felix.dm.runtime.api.ComponentFactory</code>
+ * object is registered into the OSGi registry on behalf of the component. This ComponentFactory
+ * can then be used by another component in order to instantiate multiple instances of the component
+ * (DM ComponentFactory are really similar to DS ComponentFactory).
+ *
+ * <h3>Usage Examples</h3>
+ *
+ * <p> Here is a sample showing a X component, which depends on a configuration dependency:<p>
+ * <blockquote>
+ *
+ * <pre>
+ * /**
+ * * This component will be activated once the bundle is started and when all required dependencies
+ * * are available.
+ * */
+ * @Component
+ * class X implements Z {
+ * @ConfigurationDependency(pid="MyPid")
+ * void configure(Dictionary conf) {
+ * // Configure or reconfigure our component.
+ * }
+ *
+ * @Start
+ * void start() {
+ * // Our component is starting and is about to be registered in the OSGi registry as a Z service.
+ * }
+ *
+ * public void doService() {
+ * // ...
+ * }
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * Here is a sample showing how a Y component may dynamically instantiate several X component instances,
+ * using the {@link #factoryName()} attribute:<p>
+ * <blockquote>
+ *
+ * <pre>
+ * /**
+ * * All component instances will be created/updated/removed by the "Y" component
+ * */
+ * @Component(factoryName="MyComponentFactory", factoryConfigure="configure")
+ * class X implements Z {
+ * void configure(Dictionary conf) {
+ * // Configure or reconfigure our component. The conf is provided by the factory,
+ * // and all public properties (which don't start with a dot) are propagated with the
+ * // service properties specified in the properties annotation attribute.
+ * }
+ *
+ * @ServiceDependency
+ * void bindOtherService(OtherService other) {
+ * // store this require dependency
+ * }
+ *
+ * @Start
+ * void start() {
+ * // Our component is starting and is about to be registered in the OSGi registry as a Z service.
+ * }
+ *
+ * public void doService() {
+ * // ...
+ * }
+ * }
+ *
+ * import import org.apache.felix.dm.runtime.api.ComponentFactory;
+ *
+ * /**
+ * * This class will instantiate some X component instances
+ * */
+ * @Component
+ * class Y {
+ * @ServiceDependency(filter="(" + Component.FACTORY_NAME + "=MyComponentFactory)")
+ * ComponentFactory _XFactory;
+ *
+ * @Start
+ * void start() {
+ * // Instantiate a X component instance
+ * Dictionary instance1Conf = new Hashtable() {{ put("foo", "bar1"); }};
+ * ComponentInstance instance1 = _XFactory.newInstance(instance1Conf);
+ *
+ * // Instantiate another X component instance
+ * Dictionary instance2Conf = new Hashtable() {{ put("foo2", "bar2"); }};
+ * ComponentInstance instance2 = _XFactory.newInstance(instance2Conf);
+ *
+ * // Update the first X component instance
+ * instance1Conf = new Hashtable() {{ put("foo", "bar1 modified"); }};
+ * instance1.update(instance1Conf);
+ *
+ * // Instantiate a third X instance, by explicitly providing the implementation object
+ * Dictionary instance3Conf = new Hashtable() {{ put(Component.FACTORY_INSTANCE, new X()); }};
+ * ComponentInstance instance3 = _XFactory.newInstance(instance3Conf);
+ *
+ * // Destroy x1/x2/x3 components
+ * instance1.dispose();
+ * instance2.dispose();
+ * instance3.dispose();
+ * }
+ * }
+ * </pre>
+ *
+ * </blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.TYPE)
+public @interface Component
+{
+ /**
+ * Service property name used to match a given Component Factory.
+ * @see #factoryName() for more information about factory sets.
+ */
+ final static String FACTORY_NAME = "dm.factory.name";
+
+ /**
+ * Key used when providing an implementation when using a Component Factory .
+ * @see #factoryName()
+ */
+ final static String FACTORY_INSTANCE = "dm.factory.instance";
+
+ /**
+ * Sets list of provided interfaces. By default, the directly implemented interfaces are provided.
+ */
+ Class<?>[] provides() default {};
+
+ /**
+ * Sets list of provided service properties.
+ */
+ Property[] properties() default {};
+
+ /**
+ * Returns the name of the <code>Factory Set</code> used to dynamically instantiate this component.
+ * When you set this attribute, a <code>java.util.Set<java.lang.Dictionary></code> OSGi Service will
+ * be provided with a <code>dm.factory.name</code> service property matching your specified <code>factorySet</code> attribute.
+ * This Set will be provided once the component bundle is started, even if required dependencies are not available, and the
+ * Set will be unregistered from the OSGi registry once the component bundle is stopped or being updated.<p>
+ * So, basically, another component may then be injected with this set in order to dynamically instantiate some component instances:
+ * <ul>
+ * <li> Each time a new Dictionary is added into the Set, then a new instance of the annotated component will be instantiated.</li>
+ * <li> Each time an existing Dictionary is re-added into the Set, then the corresponding component instance will be updated.</li>
+ * <li> Each time an existing Dictionary is removed from the Set, then the corresponding component instance will be destroyed.</li>
+ * </ul>
+ *
+ * <p>The dictionary registered in the Set will be provided to the created component instance using a callback method that you can
+ * optionally specify in the {@link Component#factoryConfigure()} attribute. Each public properties from that dictionary
+ * (which don't start with a dot) will be propagated along with the annotated component service properties.
+ *
+ * <p>Optionally, the dictionary registered into the factory set may provide an implementation instance for the component to be created,
+ * using the {@value #FACTORY_INSTANCE} key.
+ *
+ * @deprecated use {@link #factoryName()} instead of a factorySet.
+ */
+ String factorySet() default "";
+
+ /**
+ * Returns the name of the <code>ComponentFactory</code> used to dynamically instantiate this component.
+ * When you set this attribute, a <code>org.apache.felix.dm.runtime.api.ComponentFactory</code> OSGi Service will
+ * be provided with a <code>dm.factory.name</code> service property matching your specified <code>factoryName</code> attribute.
+ *
+ * The ComponentFactory will be provided once the component bundle is started, even if required dependencies are not available, and the
+ * ComponentFactory will be unregistered from the OSGi registry once the component bundle is stopped or being updated.<p>
+ * So, another component may then be injected with this ComponentFactory in order to dynamically instantiate some component instances:
+ *
+ * <p>The dictionary passed to the ComponentFactory.newInstance method will be provided to the created component instance using a callback
+ * method that you can optionally specify in the {@link Component#factoryConfigure()} attribute. Each public properties from that dictionary
+ * (which don't start with a dot) will be propagated along with the annotated component service properties.
+ *
+ * <p>Optionally, the dictionary registered into the factory set may provide an implementation instance for the component to be created,
+ * using a "dm.runtime.factory.instance" key.
+ */
+ String factoryName() default "";
+
+ /**
+ * Sets "configure" callback method name to be called with the factory configuration. This attribute only makes sense if the
+ * {@link #factoryName()} attribute is used. If specified, then this attribute references a callback method, which is called
+ * for providing the configuration supplied by the factory that instantiated this component. The current component service properties will be
+ * also updated with all public properties (which don't start with a dot).
+ */
+ String factoryConfigure() default "";
+
+ /**
+ * Sets the static method used to create the components implementation instance.
+ */
+ String factoryMethod() default "";
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Composition.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Composition.java
new file mode 100644
index 0000000..b62ac70
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Composition.java
@@ -0,0 +1,81 @@
+/*
+ * 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.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a method returning the list of objects which are part of a Component implementation.
+ * When implementing complex Components, you often need to use more than one object instances.
+ * Moreover, several of these instances might want to have dependencies injected, as well as lifecycle
+ * callbacks invoked, like the methods annotated with {@link Init}, {@link Start}, {@link Stop},
+ * {@link Destroy} annotations. In such cases you can tell the dependency manager which instances to
+ * consider, by annotating a method in your Component, returning a list of objects which are part
+ * of the implementation.
+ * <p>
+ * This annotation may be applied on a method which is part of class annotated with either a {@link Component},
+ * {@link AspectService}, {@link AdapterService}, {@link FactoryConfigurationAdapterService} or
+ * {@link ResourceAdapterService} annotation.
+ *
+ * <h3>Usage Examples</h3>
+ *
+ * <p> Here, the "MyComponent" component is composed of the Helper class, which is also injected with
+ * service dependencies. The lifecycle callbacks are also invoked in the Helper (if the Helper defines
+ * them):<p>
+ * <blockquote>
+ * <pre>
+ *
+ * class Helper {
+ * LogService logService; // Injected
+ * void start() {} // lifecycle callback
+ * void bind(OtherService otherService) {} // injected
+ * }
+ *
+ * @Component
+ * class MyComponent {
+ * // Helper which will also be injected with our service dependencies
+ * private Helper helper = new Helper();
+ *
+ * @Composition
+ * Object[] getComposition() {
+ * return new Object[] { this, helper };
+ * }
+ *
+ * @ServiceDependency
+ * private LogService logService; // Helper.logService will be also be injected, if defined.
+ *
+ * @Start
+ * void start() {} // the Helper.start() method will also be called, if defined
+ *
+ * @ServiceDependency
+ * void bind(OtherService otherService) {} // the Helper.bind() method will also be called, if defined
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.METHOD)
+public @interface Composition
+{
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ConfigurationDependency.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ConfigurationDependency.java
new file mode 100644
index 0000000..30ebf46
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ConfigurationDependency.java
@@ -0,0 +1,176 @@
+/*
+ * 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.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+/**
+ * Annotates a method for injecting a Configuration Dependency. A configuration dependency
+ * is always required, and allows you to depend on the availability of a valid configuration
+ * for your component. This dependency requires the OSGi Configuration Admin Service.
+ *
+ * <h3>Usage Examples</h3>
+ *
+ * <p> In the following example, the "Printer" component depends on a configuration
+ * whose PID name is "sample.PrinterConfiguration". This service will initialize
+ * its ip/port number from the provided configuration.
+ * <p> First, we define the configuration metadata, using standard bndtools metatatype annotations
+ * (see http://www.aqute.biz/Bnd/MetaType):
+ *
+ * <blockquote>
+ * <pre>
+ * package sample;
+ * import aQute.bnd.annotation.metatype.Meta.AD;
+ * import aQute.bnd.annotation.metatype.Meta.OCD;
+ *
+ * @OCD(description = "Declare here the Printer Configuration.")
+ * public interface PrinterConfiguration {
+ * @AD(description = "Enter the printer ip address")
+ * String ipAddress();
+ *
+ * @AD(description = "Enter the printer address port number.")
+ * int portNumber();
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * Next, we define our Printer service which depends on the PrinterConfiguration:
+ *
+ * <blockquote>
+ * <pre>
+ * package sample;
+ * import aQute.bnd.annotation.metatype.*;
+ *
+ * @Component
+ * public class Printer {
+ * @ConfigurationDependency(pidClass = PrinterConfiguration.class) // Will use pid "sample.PrinterConfiguration"
+ * void updated(Dictionary props) {
+ * // load configuration from the provided dictionary, or throw an exception of any configuration error.
+ * PrinterConfig cnf = Configurable.createConfigurable(PrinterConfig.class, props);
+ * String ip = cnf.ipAddress();
+ * int port = cnf.portNumber();
+ * ...
+ * }
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.METHOD)
+public @interface ConfigurationDependency
+{
+ /**
+ * Returns the pid for a given service (by default, the pid is the service class name).
+ * @return the pid for a given service (default = Service class name)
+ */
+ String pid() default "";
+
+ /**
+ * Returns the pid from a class name. The full class name will be used as the configuration PID.
+ * You can use this method when you use an interface annoted with standard bndtols metatype annotations.
+ * (see http://www.aqute.biz/Bnd/MetaType).
+ */
+ Class<?> pidClass() default Object.class;
+
+ /**
+ * Returns true if the configuration properties must be published along with the service.
+ * Any additional service properties specified directly are merged with these.
+ * @return true if configuration must be published along with the service, false if not.
+ */
+ boolean propagate() default false;
+
+ /**
+ * The name for this configuration dependency. When you give a name a dependency, it won't be evaluated
+ * immediately, but after the component's init method has been called, and from the init method, you can then return
+ * a map in order to dynamically configure the configuration dependency (the map has to contain a "pid" and/or "propagate"
+ * flag, prefixed with the dependency name). Then the dependency will be evaluated after the component init method, and will
+ * be injected before the start method.
+ *
+ * <p> Usage example of a Configuration dependency whose pid and propagate flag is configured dynamically from init method:
+ *
+ * <blockquote><pre>
+ * /**
+ * * A Service that dynamically defines an extra dynamic configuration dependency from its init method.
+ * */
+ * @Component
+ * class X {
+ * private Dictionary m_config;
+ *
+ * // Inject initial Configuration (injected before any other required dependencies)
+ * @ConfigurationDependency
+ * void componentConfiguration(Dictionary config) {
+ * // you must throw an exception if the configuration is not valid
+ * m_config = config;
+ * }
+ *
+ * /**
+ * * All unnamed dependencies are injected: we can now configure our dynamic configuration whose dependency name is "global".
+ * */
+ * @Init
+ * Map init() {
+ * return new HashMap() {{
+ * put("global.pid", m_config.get("globalConfig.pid"));
+ * put("global.propagate", m_config.get("globalConfig.propagate"));
+ * }};
+ * }
+ *
+ * // Injected after init, and dynamically configured by the init method.
+ * @ConfigurationDependency(name="global")
+ * void globalConfiguration(Dictionary globalConfig) {
+ * // you must throw an exception if the configuration is not valid
+ * }
+ *
+ * /**
+ * * All dependencies are injected and our service is now ready to be published.
+ * */
+ * @Start
+ * void start() {
+ * }
+ * }
+ * </pre></blockquote>
+ */
+ String name() default "";
+
+ /**
+ * 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.
+ * @deprecated use standard bndtools metatype annotations instead (see http://www.aqute.biz/Bnd/MetaType)
+ */
+ String heading() default "";
+
+ /**
+ * 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.
+ * @deprecated use standard bndtools metatype annotations instead (see http://www.aqute.biz/Bnd/MetaType)
+ */
+ String description() default "";
+
+ /**
+ * The list of properties types used to expose properties in web console.
+ * @return The list of properties types used to expose properties in web console.
+ * @deprecated use standard bndtools metatype annotations instead (see http://www.aqute.biz/Bnd/MetaType)
+ */
+ PropertyMetaData[] metadata() default {};
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Destroy.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Destroy.java
new file mode 100644
index 0000000..4934c89a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Destroy.java
@@ -0,0 +1,53 @@
+/*
+ * 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.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a method which is invoked when the component is destroyed.
+ * The method is called when the component's bundle is stopped, or when one of its
+ * required dependency is lost (unless the dependency has been defined as an "instance bound"
+ * dependency using the Dependency Manager API).
+ * </ul>
+ *
+ * <h3>Usage Examples</h3>
+ * <blockquote>
+ * <pre>
+ * @Component
+ * class MyComponent {
+ * @ServiceDependency
+ * private LogService logService; // Required dependency over the log service.
+ *
+ * @Destroy
+ * void destroyed() {} // called if bundle is stopped or if we have lost some required dependencies.
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.METHOD)
+public @interface Destroy
+{
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/FactoryConfigurationAdapterService.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/FactoryConfigurationAdapterService.java
new file mode 100644
index 0000000..44fc281
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/FactoryConfigurationAdapterService.java
@@ -0,0 +1,150 @@
+/*
+ * 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.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+/**
+ * Annotates a class that acts as a Factory Configuration Adapter Service. For each new <code>Config Admin</code>
+ * factory configuration matching the specified factoryPid, an instance of this service will be created.
+ * 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. <p>
+ *
+ * <h3>Usage Examples</h3>
+ * Here, a "Dictionary" service instance is created for each existing "sample.DictionaryConfiguration" factory pids.
+ *
+ * First, we declare our factory configuration metadata using standard bndtools metatatype annotations
+ * (see http://www.aqute.biz/Bnd/MetaType):
+ *
+ * <blockquote>
+ * <pre>
+ * package sample;
+ * import java.util.List;
+ * import aQute.bnd.annotation.metatype.Meta.AD;
+ * import aQute.bnd.annotation.metatype.Meta.OCD;
+ *
+ * @OCD(factory = true, description = "Declare here some Dictionary instances.")
+ * public interface DictionaryConfiguration {
+ * @AD(description = "Describes the dictionary language.", deflt = "en")
+ * String lang();
+ *
+ * @AD(description = "Declare here the list of words supported by this dictionary.")
+ * List<String> words();
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * And here is the Dictionary service:
+ *
+ * <blockquote>
+ * <pre>
+ * import java.util.List;
+ * import aQute.bnd.annotation.metatype.Configurable;
+ *
+ * @FactoryConfigurationAdapterService(factoryPidClass=DictionaryConfiguration.class)
+ * public class DictionaryImpl implements DictionaryService {
+ * protected void updated(Dictionary<String, ?> props) {
+ * // load configuration from the provided dictionary, or throw an exception of any configuration error.
+ * DictionaryConfiguration cnf = Configurable.createConfigurable(DictionaryConfiguration.class, props);
+ *
+ * m_lang = config.lang();
+ * m_words.clear();
+ * for (String word : conf.words()) {
+ * m_words.add(word);
+ * }
+ * }
+ * ...
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.TYPE)
+public @interface FactoryConfigurationAdapterService
+{
+ /**
+ * The interface(s) to use when registering adapters. By default, directly implemented
+ * interfaces will be registered in the OSGi registry.
+ */
+ Class<?>[] provides() default {};
+
+ /**
+ * Adapter Service properties. Notice that public factory configuration is also registered in service properties,
+ * (only if propagate is true). Public factory configuration properties are those which don't starts with a dot (".").
+ */
+ Property[] properties() default {};
+
+ /**
+ * Returns the factory pid whose configurations will instantiate the annotated service class. (By default, the pid is the
+ * service class name).
+ */
+ String factoryPid() default "";
+
+ /**
+ * Returns the factory pid from a class name. The full class name will be used as the configuration PID.
+ * You can use this method when you use an interface annoted with standard bndtols metatype annotations.
+ * (see http://www.aqute.biz/Bnd/MetaType).
+ */
+ Class<?> factoryPidClass() default Object.class;
+
+ /**
+ * The Update method to invoke (defaulting to "updated"), when a factory configuration is created or updated
+ */
+ String updated() default "updated";
+
+ /**
+ * Returns true if the configuration properties must be published along with the service.
+ * Any additional service properties specified directly are merged with these.
+ * @return true if configuration must be published along with the service, false if not.
+ */
+ boolean propagate() default false;
+
+ /**
+ * 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.
+ * @deprecated use standard bndtools metatype annotations instead (see http://www.aqute.biz/Bnd/MetaType)
+ */
+ String heading() default "";
+
+ /**
+ * 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.
+ * @deprecated use standard bndtools metatype annotations instead (see http://www.aqute.biz/Bnd/MetaType)
+ */
+ String description() default "";
+
+ /**
+ * The list of properties types used to expose properties in web console.
+ * @return The list of properties types used to expose properties in web console.
+ * @deprecated use standard bndtools metatype annotations instead (see http://www.aqute.biz/Bnd/MetaType)
+ */
+ PropertyMetaData[] metadata() default {};
+
+ /**
+ * Sets the static method used to create the adapter instance.
+ */
+ String factoryMethod() default "";
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Init.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Init.java
new file mode 100644
index 0000000..c18cd67
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Init.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.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a method which will be invoked when the Service is initializing.
+ * All required dependencies are already injected before the annotated method is called, and
+ * optional dependencies on class fields are injected with NullObjects if the optional
+ * dependencies are not currently available.<p>
+ *
+ * If some dependencies are declared using a <b>named</b> @{@link ServiceDependency} annotation,
+ * then the annotated method may optionally return a Map used to dynamically configure such
+ * dependencies (Please refer to @{@link ServiceDependency#name()} attribute for more
+ * information about this feature).<p>
+ *
+ * After the init method returns, the component is then invoked in the method annotated with
+ * @{@link Start}, in order to notify that the component is about to be registered into the OSGi
+ * registry (if this one provides a service). However, you can take control of when the service is registered,
+ * using the @{@link LifecycleController} annotation).
+ *
+ * <h3>Usage Examples</h3>
+ * Here, the "VideoPlayer" init method is called after the "log" dependency is injected.
+ * <blockquote>
+ * <pre>
+ *
+ * @Component
+ * public class VideoPlayer {
+ * @ServiceDependency
+ * LogService log;
+ *
+ * @Init
+ * void init() {} // initialize our service (the "log" dependency is already injected).
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.METHOD)
+public @interface Init
+{
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Inject.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Inject.java
new file mode 100644
index 0000000..e97c627
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Inject.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Inject classes in a component instance field.
+ * The following injections are currently performed, depending on the type of the
+ * field this annotation is applied on:
+ * <ul>
+ * <li>BundleContext: the bundle context of the bundle
+ * <li>DependencyManager: the dependency manager instance
+ * <li>Component: the component instance of the dependency manager
+ * </ul>
+ *
+ * <p>
+ * <h3>Usage Examples</h3>
+ * <blockquote>
+ *
+ * <pre>
+ * @Component
+ * class X implements Z {
+ * @Inject
+ * BundleContext bundleContext;
+ *
+ * @Inject
+ * Component component;
+ *
+ * @Inject
+ * DependencyManager manager;
+ *
+ * OtherService otherService;
+ *
+ * @Init
+ * void init() {
+ * System.out.println("Bundle Context: " + bundleContext);
+ * System.out.println("Manager: " + manager);
+ *
+ * // Use DM API for defining an extra service dependency
+ * componnent.add(manager.createServiceDependency()
+ * .setService(OtherService.class)
+ * .setRequired(true)
+ * .setInstanceBound(true));
+ * }
+ *
+ * @Start
+ * void start() {
+ * System.out.println("OtherService: " + otherService);
+ * }
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.FIELD)
+public @interface Inject
+{
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/LifecycleController.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/LifecycleController.java
new file mode 100644
index 0000000..afd9295
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/LifecycleController.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Injects a <code>Runnable</code> object in a Service for starting/stopping it programatically.
+ * By default, a Service is implicitly started when the service's bundle is started and when
+ * all required dependencies are satisfied. However, it is sometimes required to programatically
+ * take control of when the service is started or stopped. In this case, the injected <code>Runnable</code>
+ * can be invoked in order to start/register (or stop/unregister) a Service at any time. When this annotation
+ * is used, then the Service on which this annotation is applied is not activated by default, and you have to
+ * call the injected Runnable yourself.
+ * <p>
+ * <h3>Usage Examples</h3>
+ * <blockquote>
+ *
+ * <pre>
+ * /**
+ * * This Service will be registered programatically into the OSGi registry, using the LifecycleController annotation.
+ * */
+ * @Service
+ * class X implements Z {
+ * @LifecycleController
+ * Runnable starter
+ *
+ * @LifecycleController(start=false)
+ * Runnable stopper
+ *
+ * @Init
+ * void init() {
+ * // At this point, all required dependencies are there, but we'll activate our service in 2 seconds ...
+ * Thread t = new Thread() {
+ * public void run() {
+ * sleep(2000);
+ * // start our "Z" service (our "start" method will be called, juste before service registration
+ * starter.run();
+ *
+ * sleep(2000);
+ * // now, stop/unregister the "Z" service (we'll then be called in our stop() method
+ * stopper.run();
+ * }
+ * };
+ * t.start();
+ * }
+ *
+ * @Start
+ * public void start() {
+ * // This method will be called after we invoke our starter Runnable, and our service will be
+ * // published after our method returns, as in normal case.
+ * }
+
+ * @Stop
+ * public void stop() {
+ * // This method will be called after we invoke our "stop" Runnable, and our service will be
+ * // unregistered before our method is invoked, as in normal case. Notice that the service won't
+ * // be destroyed here, and the "starter" runnable can be re-invoked later.
+ * }
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.FIELD)
+public @interface LifecycleController
+{
+ /**
+ * Specifies the action to be performed when the Injected runnable is invoked. By default, the
+ * Runnable will fire a Service Component activation, when invoked. If you specify this attribute
+ * to false, then the Service Component will be stopped, when the runnable is invoked.
+ */
+ public boolean start() default true;
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Property.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Property.java
new file mode 100644
index 0000000..f760ee9
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Property.java
@@ -0,0 +1,146 @@
+/*
+ * 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.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation used to describe a property key-value(s) pair. It is used for example when
+ * declaring {@link Component#properties()} attribute.<p>
+ *
+ * Property value(s) type is String by default, and the type is scalar if the value is single-valued,
+ * or an array if the value is multi-valued.
+ *
+ * Eight primitive types are supported:<p>
+ * <ul>
+ * <li> String (default type)
+ * <li> Long
+ * <li> Double
+ * <li> Float
+ * <li> Integer
+ * <li> Byte
+ * <li> Boolean
+ * <li> Short
+ * </ul>
+ *
+ * You can specify the type of a property either using a combination of <code>value</code> and <code>type</code> attributes,
+ * or using one of the <code>longValue/doubleValue/floatValue/intValue/byteValue/charValue/booleanValue/shortValue</code> attributes.
+ *
+ * Notice that you can also specify service properties dynamically by returning a Map from a method
+ * annotated with {@link Start}.
+ *
+ * <p>
+ * <h3>Usage Examples</h3>
+ * <blockquote>
+ *
+ * <pre>
+ * @Component(properties={
+ * @Property(name="p1", value="v")}) // String value type (scalar)
+ * @Property(name="p2", value={"s1", "s2")}) // Array of Strings
+ * @Property(name="service.ranking", intValue=10) // Integer value type (scalar)
+ * @Property(name="p3", intValue={1,2}) // Array of Integers
+ * @Property(name="p3", value={"1"), type=Long.class}) // Long value (scalar)
+ * class ServiceImpl implements Service {
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target( { ElementType.ANNOTATION_TYPE })
+public @interface Property
+{
+ /**
+ * Returns the property name.
+ * @return this property name
+ */
+ String name();
+
+ /**
+ * Returns the property value(s). The property value(s) is (are)
+ * parsed using the <code>valueOf</code> method of the class specified in the #type attribute
+ * (which is <code>String</code> by default). When the property value is single-value, then
+ * the value type is scalar (not an array). If the property value is multi-valued, then the value type
+ * is an array of the type specified in the {@link #type()} attribute (String by default).
+ *
+ * @return this property value(s).
+ */
+ String[] value() default {};
+
+ /**
+ * Specifies how the {@link #value()} or {@link #values()} attributes are parsed.
+ * @return the property value type (String by default) used to parse {@link #value()} or {@link #values()}
+ * attribtues
+ */
+ Class<?> type() default String.class;
+
+ /**
+ * A Long value or an array of Long values.
+ */
+ long[] longValue() default {};
+
+ /**
+ * A Double value or an array of Double values.
+ */
+ double[] doubleValue() default {};
+
+ /**
+ * A Float value or an array of Float values.
+ */
+ float[] floatValue() default {};
+
+ /**
+ * An Integer value or an array of Integer values.
+ */
+ int[] intValue() default {};
+
+ /**
+ * A Byte value or an array of Byte values.
+ */
+ byte[] byteValue() default {};
+
+ /**
+ * A Character value or an array of Character values.
+ */
+ char[] charValue() default {};
+
+ /**
+ * A Boolean value or an array of Boolean values.
+ */
+ boolean[] booleanValue() default {};
+
+ /**
+ * A Short value or an array of Short values.
+ */
+ short[] shortValue() default {};
+
+ /**
+ * Returns an array of property values.
+ * The property value are parsed using the <code>valueOf</code> method of the class specified in the #type attribute
+ * (which is <code>String</code> by default).
+ *
+ * @return an array of property values.
+ * @deprecated use {@link #value()} attribute.
+ */
+ String[] values() default {};
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/PropertyMetaData.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/PropertyMetaData.java
new file mode 100644
index 0000000..8e7d724
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/PropertyMetaData.java
@@ -0,0 +1,121 @@
+/*
+ * 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.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+/**
+ * This annotation describes the data types of a configuration Property.
+ * It can be used by other annotations which require meta type support.
+ * For now, the following annotations are using <code>PropertyMetaData</code:
+ * <ul>
+ * <li>{@link ConfigurationDependency}: This dependency allows to define a
+ * dependency over a <code>Configuration Admin</code> configuration dictionaries, whose
+ * metadata can be described using <code>PropertyMetaData</code> annotation.
+ * <li>{@link FactoryConfigurationAdapterService}: This service adapter allows
+ * to dynamically create Services on behalf of <code>Factory Configuration Admin</code>
+ * configuration dictionaries, whose metadata can be described using this <code>PropertyMetaData</code> annotation.
+ * </ul>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.ANNOTATION_TYPE)
+public @interface PropertyMetaData
+{
+ /**
+ * The label used to display the property. Example: "Log Level".
+ * @return The label used to display the property
+ */
+ String heading();
+
+ /**
+ * The key of a ConfigurationAdmin property. Example: "printer.logLevel"
+ * @return The Configuration Admin property name
+ */
+ String id();
+
+ /**
+ * Return 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>
+ */
+ Class<?> type() default String.class;
+
+ /**
+ * Return 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.
+ */
+ String[] defaults() default {};
+
+ /**
+ * 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 The localized description of the definition.
+ */
+ 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>
+ */
+ int cardinality() default 0;
+
+ /**
+ * Tells if this property is required or not.
+ */
+ boolean required() default true;
+
+ /**
+ * Return a list of valid option labels for this property. The purpose of this method is to allow menus with localized labels.
+ * It is associated with the {@link #optionValues()} attribute. The labels returned here are ordered in the same way as the
+ * {@link #optionValues()} attribute values.
+ * @return the list of valid option labels for this property.
+ */
+ String[] optionLabels() default {};
+
+ /**
+ * Return a list of option values that this property can take. This list must be in the same sequence as the {@link #optionLabels()}
+ * attribute.
+ */
+ String[] optionValues() default {};
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Registered.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Registered.java
new file mode 100644
index 0000000..5c34855
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Registered.java
@@ -0,0 +1,58 @@
+/*
+ * 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.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation can be used to be notified when a component is registered. At this point, the
+ * component has been registered into the OSGI registry (if it provides some services).
+ * When a service is registered, the ServiceRegistration used to register the service is
+ * also passed to the method (if it takes a ServiceRegistration as parameter).
+ *
+ * <p>
+ * <h3>Usage Examples</h3>
+ * <blockquote>
+ *
+ * <pre>
+ * @Component
+ * class X implements Z {
+ * @Start
+ * void start() {
+ * // Our Z Service is about to be registered into the OSGi registry.
+ * }
+ *
+ * @Registered
+ * void registered(ServiceRegistration sr) {
+ * // At this point, our service has been registered into the registry.
+ * }
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.METHOD)
+public @interface Registered
+{
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ResourceAdapterService.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ResourceAdapterService.java
new file mode 100644
index 0000000..bfa388b
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ResourceAdapterService.java
@@ -0,0 +1,158 @@
+/*
+ * 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.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a class as a Resource adapter service. Resource adapters are things that
+ * adapt a resource instead of a service, and provide an adapter service on top of this resource.
+ * Resources are an abstraction that is introduced by the dependency manager, represented as a URL.
+ * They can be implemented to serve resources embedded in bundles, somewhere on a file system or in
+ * an http content repository server, or database.<p>
+ * The adapter will be applied to any resource that matches the specified filter condition, which can
+ * match some part of the resource URL (with "path", "protocol", "port", or "host" filters).
+ * 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 with any extra service properties
+ * you supply here. Moreover, the following service properties will be propagated from the resource URL:
+ *
+ * <ul><li> "host": this property exposes the host part of the resource URL
+ * <li>"path": the resource URL path
+ * <li>"protocol": the resource URL protocol
+ * <li>"port": the resource URL port
+ * </ul>
+ *
+ * <h3>Usage Examples</h3>
+ * Here, the "VideoPlayer" service provides a video service on top of any movie resources, with service
+ * properties "host"/"port"/"protocol"/"path" extracted from the resource URL:
+ * <blockquote>
+ * <pre>
+ *
+ * @ResourceAdapterService(filter = "(&(path=/videos/*.mkv)(host=localhost))", propagate = true)
+ * public class VideoPlayerImpl implements VideoPlayer {
+ * // Injected by reflection
+ * URL resource;
+ *
+ * void play() {} // play video referenced by this.resource
+ * void stop() {} // stop playing the video
+ * void transcode() {} // ...
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * And here is an example of a VideoProvider, which provides some videos using a web URL.
+ * Notice that Resource providers need to depend on the DependencyManager API:
+ *
+ * <blockquote>
+ * <pre>
+ * import java.net.MalformedURLException;
+ * import java.net.URL;
+ * import java.util.HashMap;
+ * import java.util.Map;
+ *
+ * import org.apache.felix.dm.ResourceHandler;
+ * import org.apache.felix.dm.ResourceUtil;
+ * import org.apache.felix.dm.annotation.api.Component;
+ * import org.apache.felix.dm.annotation.api.Init;
+ * import org.apache.felix.dm.annotation.api.ServiceDependency;
+ * import org.osgi.framework.BundleContext;
+ * import org.osgi.framework.Filter;
+ * import org.osgi.framework.InvalidSyntaxException;
+ *
+ * @Component
+ * public class VideoProvider
+ * {
+ * // Injected by reflection
+ * private volatile BundleContext context;
+ * // List of known resource handlers
+ * private Map<ResourceHandler, Filter> m_handlers = new HashMap<ResourceHandler, Filter>();
+ * // List of known video resources
+ * private URL[] m_videos;
+ *
+ * @Init
+ * void init() throws MalformedURLException
+ * {
+ * m_videos = new URL[] {
+ * new URL("http://localhost:8080/videos/video1.mkv"),
+ * new URL("http://localhost:8080/videos/video2.mkv"),
+ * };
+ * }
+ *
+ * // Track resource handlers
+ * @ServiceDependency(required = false)
+ * public void add(Map<String, String> serviceProperties, ResourceHandler handler) throws InvalidSyntaxException
+ * {
+ * String filterString = serviceProperties.get("filter");
+ * filterString = (filterString != null) ? filterString : "(path=*)";
+ * Filter filter = context.createFilter(filterString);
+ * synchronized (this)
+ * {
+ * m_handlers.put(handler, filter);
+ * }
+ * for (URL video : m_videos)
+ * {
+ * if (filter.match(ResourceUtil.createProperties(video)))
+ * {
+ * handler.added(video);
+ * }
+ * }
+ * }
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.TYPE)
+public @interface ResourceAdapterService
+{
+ /**
+ * The interface(s) to use when registering adapters
+ */
+ Class<?>[] provides() default {};
+
+ /**
+ * Additional properties to use with the adapter service registration
+ */
+ Property[] properties() default {};
+
+ /**
+ * The filter condition to use with the resource.
+ */
+ String filter();
+
+ /**
+ * <code>true</code> if properties from the resource should be propagated to the service properties.
+ */
+ boolean propagate() default false;
+
+ /**
+ * The callback method to be invoked when the Resource has changed.
+ */
+ String changed() default "";
+
+ /**
+ * Sets the static method used to create the AdapterService implementation instance.
+ */
+ String factoryMethod() default "";
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ResourceDependency.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ResourceDependency.java
new file mode 100644
index 0000000..ebeac18
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ResourceDependency.java
@@ -0,0 +1,155 @@
+/*
+ * 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.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a method of field as a Resource Dependency. A resource dependency allows you to
+ * depend on a resource. Resources are an abstraction that is introduced by the dependency manager, represented as a URL.
+ * They can be implemented to serve resources embedded in bundles, somewhere on a file system or in
+ * an http content repository server, or database. <p> A resource is a URL and you can use a filter condition based on
+ * protocol, host, port, and path.
+ *
+ * <h3>Usage Examples</h3>
+ * Here, the "VideoPlayer" component plays any provided MKV video resources
+ * <blockquote>
+ * <pre>
+ *
+ * @Component
+ * public class VideoPlayer {
+ * @ResourceDependency(required=false, filter="(path=/videos/*.mkv)")
+ * void playResource(URL video) { ... }
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * And here is an example of a VideoProvider, which provides some videos using a web URL.
+ * Notice that Resource providers need to depend on the DependencyManager API:
+ *
+ * <blockquote>
+ * <pre>
+ * import java.net.MalformedURLException;
+ * import java.net.URL;
+ * import java.util.HashMap;
+ * import java.util.Map;
+ *
+ * import org.apache.felix.dm.ResourceHandler;
+ * import org.apache.felix.dm.ResourceUtil;
+ * import org.apache.felix.dm.annotation.api.Component;
+ * import org.apache.felix.dm.annotation.api.Init;
+ * import org.apache.felix.dm.annotation.api.ServiceDependency;
+ * import org.osgi.framework.BundleContext;
+ * import org.osgi.framework.Filter;
+ * import org.osgi.framework.InvalidSyntaxException;
+ *
+ * @Component
+ * public class VideoProvider
+ * {
+ * // Injected by reflection
+ * private volatile BundleContext context;
+ * // List of known resource handlers
+ * private Map<ResourceHandler, Filter> m_handlers = new HashMap<ResourceHandler, Filter>();
+ * // List of known video resources
+ * private URL[] m_videos;
+ *
+ * @Init
+ * void init() throws MalformedURLException
+ * {
+ * m_videos = new URL[] {
+ * new URL("http://localhost:8080/videos/video1.mkv"),
+ * new URL("http://localhost:8080/videos/video2.mkv"),
+ * };
+ * }
+ *
+ * // Track resource handlers
+ * @ServiceDependency(required = false)
+ * public void add(Map<String, String> serviceProperties, ResourceHandler handler) throws InvalidSyntaxException
+ * {
+ * String filterString = serviceProperties.get("filter");
+ * filterString = (filterString != null) ? filterString : "(path=*)";
+ * Filter filter = context.createFilter(filterString);
+ * synchronized (this)
+ * {
+ * m_handlers.put(handler, filter);
+ * }
+ * for (URL video : m_videos)
+ * {
+ * if (filter.match(ResourceUtil.createProperties(video)))
+ * {
+ * handler.added(video);
+ * }
+ * }
+ * }
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.METHOD, ElementType.FIELD})
+public @interface ResourceDependency
+{
+ /**
+ * Returns the callback method to be invoked when the service is available. This attribute is only meaningful when
+ * the annotation is applied on a class field.
+ */
+ String added() default "";
+
+ /**
+ * Returns the callback method to be invoked when the service properties have changed.
+ */
+ String changed() default "";
+
+ /**
+ * Returns the callback method to invoke when the service is lost.
+ */
+ String removed() default "";
+
+ /**
+ * Returns whether the Service dependency is required or not.
+ */
+ boolean required() default true;
+
+ /**
+ * Returns the Service dependency OSGi filter.
+ */
+ String filter() default "";
+
+ /**
+ * Specifies if the resource URL properties must be propagated. If set to true, then the URL properties
+ * ("protocol"/"host"/"port"/"path") will be propagated to the service properties of the component which
+ * is using this dependency.
+ */
+ boolean propagate() default false;
+
+ /**
+ * The name used when dynamically configuring this dependency from the init method.
+ * Specifying this attribute allows to dynamically configure the dependency
+ * <code>filter</code> and <code>required</code> flag from the Service's init method.
+ * All unnamed dependencies will be injected before the init() method; so from the init() method, you can
+ * then pick up whatever information needed from already injected (unnamed) dependencies, and configure dynamically
+ * your named dependencies, which will then be calculated once the init() method returns.
+ */
+ String name() default "";
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ServiceDependency.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ServiceDependency.java
new file mode 100644
index 0000000..f935992
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ServiceDependency.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 org.apache.felix.dm.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a method or a field for injecting a Service Dependency. When applied on a class
+ * field, optional unavailable dependencies are injected with a NullObject.
+ *
+ * <h3>Usage Examples</h3>
+ * Here, the MyComponent component is injected with a dependency over a "MyDependency" service
+ *
+ * <blockquote><pre>
+ * @Component
+ * class MyComponent {
+ * @ServiceDependency(timeout=15000)
+ * MyDependency dependency;
+ * </pre></blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.METHOD, ElementType.FIELD})
+public @interface ServiceDependency
+{
+ /**
+ * The type if the service this dependency is applying on. By default, the method parameter
+ * (or the class field) is used as the type.
+ */
+ Class<?> service() default Object.class;
+
+ /**
+ * The Service dependency OSGi filter.
+ */
+ String filter() default "";
+
+ /**
+ * The class for the default implementation, if the dependency is not available.
+ */
+ Class<?> defaultImpl() default Object.class;
+
+ /**
+ * Whether the Service dependency is required or not.
+ */
+ boolean required() default true;
+
+ /**
+ * The callback method to be invoked when the service is available. This attribute is only meaningful when
+ * the annotation is applied on a class field.
+ */
+ String added() default "";
+
+ /**
+ * The callback method to be invoked when the service properties have changed.
+ */
+ String changed() default "";
+
+ /**
+ * The callback method to invoke when the service is lost.
+ */
+ String removed() default "";
+
+ /**
+ * The max time in millis to wait for the dependency availability.
+ * Specifying a positive number allow to block the caller thread between service updates. Only
+ * useful for required stateless dependencies that can be replaced transparently.
+ * A Dynamic Proxy is used to wrap the actual service dependency (which must be an interface).
+ * When the dependency goes away, an attempt is made to replace it with another one which satisfies
+ * the service dependency criteria. If no service replacement is available, then any method invocation
+ * (through the dynamic proxy) will block during a configurable timeout. On timeout, an unchecked
+ * <code>IllegalStateException</code> exception is raised (but the service is not deactivated).<p>
+ * Notice that the changed/removed callbacks are not used when the timeout parameter is > -1.
+ * <p>
+ *
+ * -1 means no timeout at all (default). 0 means that invocation on a missing service will fail
+ * immediately. A positive number represents the max timeout in millis to wait for the service availability.
+ *
+ * <p> Sample Code:<p>
+ * <blockquote><pre>
+ * @Component
+ * class MyServer implements Runnable {
+ * @ServiceDependency(timeout=15000)
+ * MyDependency dependency;.
+ *
+ * @Start
+ * void start() {
+ * (new Thread(this)).start();
+ * }
+ *
+ * public void run() {
+ * try {
+ * dependency.doWork();
+ * } catch (IllegalStateException e) {
+ * t.printStackTrace();
+ * }
+ * }
+ * </pre></blockquote>
+ */
+ long timeout() default -1;
+
+ /**
+ * The name used when dynamically configuring this dependency from the init method.
+ * Specifying this attribute allows to dynamically configure the dependency
+ * <code>filter</code> and <code>required</code> flag from the Service's init method.
+ * All unnamed dependencies will be injected before the init() method; so from the init() method, you can
+ * then pick up whatever information needed from already injected (unnamed) dependencies, and configure dynamically
+ * your named dependencies, which will then be calculated once the init() method returns.
+ *
+ * <p> Usage example of a Service whose dependency filter is configured from ConfigAdmin:
+ *
+ * <blockquote><pre>
+ * /**
+ * * A Service whose service dependency "otherService" filter is configured from ConfigAdmin
+ * */
+ * @Service
+ * class X {
+ * private Dictionary m_config;
+ *
+ * /**
+ * * Initialize our service from config ... and store the config for later usage (from our init method)
+ * */
+ * @ConfigurationDependency(pid="MyPid")
+ * void configure(Dictionary conf) {
+ * m_config = config;
+ * }
+ *
+ * /**
+ * * All unnamed dependencies are injected: we can now configure other named
+ * * dependencies, using the already injected configuration.
+ * * The returned Map will be used to configure our "otherService" Dependency.
+ * */
+ * @Init
+ * Map init() {
+ * return new HashMap() {{
+ * put("otherService.filter", m_config.get("filter"));
+ * put("otherService.required", m_config.get("required"));
+ * }};
+ * }
+ *
+ * /**
+ * * This named dependency filter/required flag will be configured by our init method (see above).
+ * */
+ * @ServiceDependency(name="otherService")
+ * void bindOtherService(OtherService other) {
+ * }
+ *
+ * /**
+ * * All dependencies are injected and our service is now ready to be published.
+ * * Notice that you can also use the publisher service attribute if you need
+ * * to take control on service exposition.
+ * */
+ * @Start
+ * void start() {
+ * }
+ * }
+ * </pre></blockquote>
+ */
+ String name() default "";
+
+ /**
+ * Returns true if the dependency service properties must be published along with the service.
+ * Any additional service properties specified directly are merged with these.
+ * @return true if dependency service properties must be published along with the service, false if not.
+ */
+ boolean propagate() default false;
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Start.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Start.java
new file mode 100644
index 0000000..da61b70
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Start.java
@@ -0,0 +1,63 @@
+/*
+ * 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.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a method which will be invoked when the component is started.
+ * The annotated method is invoked juste before registering the service into the OSGi registry
+ * (if the service provides an interface). Notice that the start method may optionally return
+ * a Map which will be propagated to the provided service properties.<p>
+ * Service activation/deactivation can be programatically controlled using {@link LifecycleController}.
+ *
+ * <p>
+ * <h3>Usage Examples</h3>
+ * <blockquote>
+ *
+ * <pre>
+ * @Component(properties={@Property(name="foo", value="bar")})
+ * class X implements Z {
+ * @ServiceDependency
+ * OtherService m_dependency;
+ *
+ * @Start
+ * Map start() {
+ * // Our Z Service is ready (all required dependencies have been satisfied), and is about to be
+ * // registered into the OSGi registry. We return here an optional Map containing some extra-properties
+ * // which will be appended to the properties supplied in the Component annotation.
+ * return new HashMap() {{
+ * put("foo2", "bar2");
+ * put(Constants.SERVICE_RANKING, Integer.valueOf(10));
+ * }};
+ * }
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.METHOD)
+public @interface Start
+{
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Stop.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Stop.java
new file mode 100644
index 0000000..672b7ea
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Stop.java
@@ -0,0 +1,54 @@
+/*
+ * 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.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a method which is invoked when the Service is being unregistered from the
+ * OSGi registry.
+ * The method is called when the component's bundle is stopped, or when one of its
+ * required dependency is lost, or when a {@link LifecycleController} is programatically used to
+ * stop a service.
+ * </ul>
+ *
+ * <h3>Usage Examples</h3>
+ * <blockquote>
+ * <pre>
+ * @Component
+ * class MyComponent implements MyService {
+ * @ServiceDependency
+ * private LogService logService; // Required dependency over the log service.
+ *
+ * @Stop
+ * void stop() {} // We are unregistering from the OSGi registry.
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.METHOD)
+public @interface Stop
+{
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Unregistered.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Unregistered.java
new file mode 100644
index 0000000..c59a435
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Unregistered.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation can be used to be notified when a component is unregistered from the registry.
+ * At this point, the component has been unregistered from the OSGI registry (if it provides some services).
+ * The method must not take any parameters.
+ *
+ * <p>
+ * <h3>Usage Examples</h3>
+ * <blockquote>
+ *
+ * <pre>
+ * @Component
+ * class X implements Z {
+ * @Stop
+ * void stop(ServiceRegistration sr) {
+ * // Our service must stop because it is about to be unregistered from the registry.
+ * }
+ *
+ * @Unregistered
+ * void unregistered() {
+ * // At this point, our service has been unregistered from the OSGi registry
+ * }
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.METHOD)
+public @interface Unregistered
+{
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/packageinfo b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/packageinfo
new file mode 100644
index 0000000..a4f1546
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/packageinfo
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
new file mode 100644
index 0000000..403a58e
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
@@ -0,0 +1,1304 @@
+/*
+ * 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.annotation.plugin.bnd;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.felix.dm.annotation.api.AdapterService;
+import org.apache.felix.dm.annotation.api.AspectService;
+import org.apache.felix.dm.annotation.api.BundleAdapterService;
+import org.apache.felix.dm.annotation.api.BundleDependency;
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Composition;
+import org.apache.felix.dm.annotation.api.ConfigurationDependency;
+import org.apache.felix.dm.annotation.api.Destroy;
+import org.apache.felix.dm.annotation.api.FactoryConfigurationAdapterService;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.Inject;
+import org.apache.felix.dm.annotation.api.LifecycleController;
+import org.apache.felix.dm.annotation.api.Registered;
+import org.apache.felix.dm.annotation.api.ResourceAdapterService;
+import org.apache.felix.dm.annotation.api.ResourceDependency;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.dm.annotation.api.Unregistered;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.osgi.framework.Bundle;
+
+import aQute.bnd.osgi.Annotation;
+import aQute.bnd.osgi.ClassDataCollector;
+import aQute.bnd.osgi.Clazz;
+import aQute.bnd.osgi.Descriptors.TypeRef;
+import aQute.bnd.osgi.Verifier;
+
+/**
+ * This is the scanner which does all the annotation parsing on a given class.
+ * To start the parsing, just invoke the parseClassFileWithCollector and finish methods.
+ * Once parsed, the corresponding component descriptors can be built using the "writeTo" method.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AnnotationCollector extends ClassDataCollector
+{
+ private final static String A_INIT = Init.class.getName();
+ private final static String A_START = Start.class.getName();
+ private final static String A_STOP = Stop.class.getName();
+ private final static String A_DESTROY = Destroy.class.getName();
+ private final static String A_COMPOSITION = Composition.class.getName();
+ private final static String A_LIFCLE_CTRL = LifecycleController.class.getName();
+
+ private final static String A_COMPONENT = Component.class.getName();
+ private final static String A_SERVICE_DEP = ServiceDependency.class.getName();
+ private final static String A_CONFIGURATION_DEPENDENCY = ConfigurationDependency.class.getName();
+ private final static String A_BUNDLE_DEPENDENCY = BundleDependency.class.getName();
+ private final static String A_RESOURCE_DEPENDENCY = ResourceDependency.class.getName();
+ private final static String A_ASPECT_SERVICE = AspectService.class.getName();
+ private final static String A_ADAPTER_SERVICE = AdapterService.class.getName();
+ private final static String A_BUNDLE_ADAPTER_SERVICE = BundleAdapterService.class.getName();
+ private final static String A_RESOURCE_ADAPTER_SERVICE = ResourceAdapterService.class.getName();
+ private final static String A_FACTORYCONFIG_ADAPTER_SERVICE = FactoryConfigurationAdapterService.class.getName();
+ private final static String A_INJECT = Inject.class.getName();
+ private final static String A_REGISTERED = Registered.class.getName();
+ private final static String A_UNREGISTERED = Unregistered.class.getName();
+
+ private Logger m_logger;
+ private String m_className;
+ private String[] m_interfaces;
+ private boolean m_isField;
+ private String m_field;
+ private String m_method;
+ private String m_descriptor;
+ private Set<String> m_dependencyNames = new HashSet<String>();
+ private List<EntryWriter> m_writers = new ArrayList<EntryWriter>(); // Last elem is either Service or AspectService
+ private MetaType m_metaType;
+ private String m_startMethod;
+ private String m_stopMethod;
+ private String m_initMethod;
+ private String m_destroyMethod;
+ private String m_compositionMethod;
+ private String m_starter;
+ private String m_stopper;
+ private Set<String> m_importService = new HashSet<String>();
+ private Set<String> m_exportService = new HashSet<String>();
+ private String m_bundleContextField;
+ private String m_dependencyManagerField;
+ private String m_componentField;
+ private String m_registeredMethod;
+ private String m_unregisteredMethod;
+
+ /**
+ * This class represents a DependencyManager component descriptor entry.
+ * (Service, a ServiceDependency ... see EntryType enum).
+ */
+
+ /**
+ * Makes a new Collector for parsing a given class.
+ * @param reporter the object used to report logs.
+ */
+ public AnnotationCollector(Logger reporter, MetaType metaType)
+ {
+ m_logger = reporter;
+ m_metaType = metaType;
+ }
+
+ /**
+ * Parses the name of the class.
+ * @param access the class access
+ * @param name the class name (package are "/" separated).
+ */
+ @Override
+ public void classBegin(int access, TypeRef name)
+ {
+ m_className = name.getFQN();
+ m_logger.debug("class name: %s", m_className);
+ }
+
+ /**
+ * Parses the implemented interfaces ("/" separated).
+ */
+ @Override
+ public void implementsInterfaces(TypeRef[] interfaces)
+ {
+ List<String> result = new ArrayList<String>();
+ for (int i = 0; i < interfaces.length; i++)
+ {
+ if (!interfaces[i].getBinary().equals("scala/ScalaObject"))
+ {
+ result.add(interfaces[i].getFQN());
+ }
+ }
+
+ m_interfaces = result.toArray(new String[result.size()]);
+ m_logger.debug("implements: %s", Arrays.toString(m_interfaces));
+ }
+
+ /**
+ * Parses a method. Always invoked BEFORE eventual method annotation.
+ */
+ @Override
+ public void method(Clazz.MethodDef method)
+ {
+ m_logger.debug("Parsed method %s, descriptor=%s", method.getName(), method.getDescriptor());
+ m_isField = false;
+ m_method = method.getName();
+ m_descriptor = method.getDescriptor().toString();
+ }
+
+ /**
+ * Parses a field. Always invoked BEFORE eventual field annotation
+ */
+ @Override
+ public void field(Clazz.FieldDef field)
+ {
+ m_logger.debug("Parsed field %s, descriptor=%s", field.getName(), field.getDescriptor());
+ m_isField = true;
+ m_field = field.getName();
+ m_descriptor = field.getDescriptor().toString();
+ }
+
+ /**
+ * An annotation has been parsed. Always invoked AFTER the "method"/"field"/"classBegin" callbacks.
+ */
+ @Override
+ public void annotation(Annotation annotation)
+ {
+ m_logger.debug("Parsing annotation: %s", annotation.getName());
+
+ if (annotation.getName().getFQN().equals(A_COMPONENT))
+ {
+ parseComponentAnnotation(annotation);
+ }
+ else if (annotation.getName().getFQN().equals(A_ASPECT_SERVICE))
+ {
+ parseAspectService(annotation);
+ }
+ else if (annotation.getName().getFQN().equals(A_ADAPTER_SERVICE))
+ {
+ parseAdapterService(annotation);
+ }
+ else if (annotation.getName().getFQN().equals(A_BUNDLE_ADAPTER_SERVICE))
+ {
+ parseBundleAdapterService(annotation);
+ }
+ else if (annotation.getName().getFQN().equals(A_RESOURCE_ADAPTER_SERVICE))
+ {
+ parseResourceAdapterService(annotation);
+ }
+ else if (annotation.getName().getFQN().equals(A_FACTORYCONFIG_ADAPTER_SERVICE))
+ {
+ parseFactoryConfigurationAdapterService(annotation);
+ }
+ else if (annotation.getName().getFQN().equals(A_INIT))
+ {
+ m_initMethod = m_method;
+ }
+ else if (annotation.getName().getFQN().equals(A_START))
+ {
+ m_startMethod = m_method;
+ }
+ else if (annotation.getName().getFQN().equals(A_REGISTERED))
+ {
+ m_registeredMethod = m_method;
+ }
+ else if (annotation.getName().getFQN().equals(A_STOP))
+ {
+ m_stopMethod = m_method;
+ }
+ else if (annotation.getName().getFQN().equals(A_UNREGISTERED))
+ {
+ m_unregisteredMethod = m_method;
+ }
+ else if (annotation.getName().getFQN().equals(A_DESTROY))
+ {
+ m_destroyMethod = m_method;
+ }
+ else if (annotation.getName().getFQN().equals(A_COMPOSITION))
+ {
+ Patterns.parseMethod(m_method, m_descriptor, Patterns.COMPOSITION);
+ m_compositionMethod = m_method;
+ } else if (annotation.getName().getFQN().equals(A_LIFCLE_CTRL))
+ {
+ parseLifecycleAnnotation(annotation);
+ }
+ else if (annotation.getName().getFQN().equals(A_SERVICE_DEP))
+ {
+ parseServiceDependencyAnnotation(annotation);
+ }
+ else if (annotation.getName().getFQN().equals(A_CONFIGURATION_DEPENDENCY))
+ {
+ parseConfigurationDependencyAnnotation(annotation);
+ }
+ else if (annotation.getName().getFQN().equals(A_BUNDLE_DEPENDENCY))
+ {
+ parseBundleDependencyAnnotation(annotation);
+ }
+ else if (annotation.getName().getFQN().equals(A_RESOURCE_DEPENDENCY))
+ {
+ parseRersourceDependencyAnnotation(annotation);
+ }
+ else if (annotation.getName().getFQN().equals(A_INJECT))
+ {
+ parseInject(annotation);
+ }
+ }
+
+ /**
+ * Finishes up the class parsing. This method must be called once the parseClassFileWithCollector method has returned.
+ * @return true if some annotations have been parsed, false if not.
+ */
+ public boolean finish()
+ {
+ if (m_writers.size() == 0)
+ {
+ m_logger.info("No components found for class " + m_className);
+ return false;
+ }
+
+ // We must have at least a Service annotation.
+ checkServiceDeclared(EntryType.Component, EntryType.AspectService, EntryType.AdapterService,
+ EntryType.BundleAdapterService,
+ EntryType.ResourceAdapterService, EntryType.FactoryConfigurationAdapterService);
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("Parsed annotation for class ");
+ sb.append(m_className);
+ for (int i = m_writers.size() - 1; i >= 0; i--)
+ {
+ sb.append("\n\t").append(m_writers.get(i).toString());
+ }
+ m_logger.info(sb.toString());
+ return true;
+ }
+
+ /**
+ * Writes the generated component descriptor in the given print writer.
+ * The first line must be the service (@Service or AspectService).
+ * @param pw the writer where the component descriptor will be written.
+ */
+ public void writeTo(PrintWriter pw)
+ {
+ // The last element our our m_writers list contains either the Service, or the AspectService descriptor.
+ for (int i = m_writers.size() - 1; i >= 0; i--)
+ {
+ pw.println(m_writers.get(i).toString());
+ }
+ }
+
+ /**
+ * Returns list of all imported services. Imported services are deduced from every
+ * @ServiceDependency annotations.
+ * @return the list of imported services, or null
+ */
+ public Set<String> getImportService()
+ {
+ return m_importService;
+ }
+
+ /**
+ * Returns list of all exported services. Imported services are deduced from every
+ * annotations which provides a service (@Component, etc ...)
+ * @return the list of exported services, or null
+ */
+ public Set<String> getExportService()
+ {
+ return m_exportService;
+ }
+
+ private void parseComponentAnnotation(Annotation annotation)
+ {
+ EntryWriter writer = new EntryWriter(EntryType.Component);
+ m_writers.add(writer);
+
+ // Register previously parsed annotations common to services (Init/Start/...)
+ addCommonServiceParams(writer);
+
+ // impl attribute
+ writer.put(EntryParam.impl, m_className);
+
+ // properties attribute
+ parseProperties(annotation, EntryParam.properties, writer);
+
+ // provides attribute.
+ if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
+ {
+ // no service provided: check if @Registered/@Unregistered annotation are used
+ // and raise an error if true.
+ checkRegisteredUnregisteredNotPresent();
+ }
+
+ // factorySet attribute (deprecated, replaced by factoryName)
+ String factorySetName = writer.putString(annotation, EntryParam.factorySet, null);
+ if (factorySetName != null)
+ {
+ // When a component defines a factorySet, it means that a java.util.Set will
+ // be provided into the OSGi registry, in order to let anoter component add
+ // some component instance configurations into it.
+ // So, we have to indicate that the Set is provided as a service, in the Export-Serviec
+ // header.
+ m_exportService.add("java.util.Set");
+ }
+
+ // factoryName attribute
+ String factoryName = writer.putString(annotation, EntryParam.factoryName, null);
+ if (factoryName != null)
+ {
+ // When a component defines a factoryName, it means that a ComponentFactory will
+ // be provided into the OSGi registry, in order to let another component create some component instances.
+ // So, we have to indicate that the ComponentFactory is provided as a service, in the Export-Serviec
+ // header.
+ m_exportService.add("org.apache.felix.dependencymanager.runtime.api.ComponentFactory");
+ }
+
+ // factoryConfigure attribute
+ writer.putString(annotation, EntryParam.factoryConfigure, null);
+
+ // factoryMethod attribute
+ writer.putString(annotation, EntryParam.factoryMethod, null);
+ }
+
+ private void addCommonServiceParams(EntryWriter writer)
+ {
+ if (m_initMethod != null)
+ {
+ writer.put(EntryParam.init, m_initMethod);
+ }
+
+ if (m_startMethod != null)
+ {
+ writer.put(EntryParam.start, m_startMethod);
+ }
+
+ if (m_registeredMethod != null)
+ {
+ writer.put(EntryParam.registered, m_registeredMethod);
+ }
+
+ if (m_stopMethod != null)
+ {
+ writer.put(EntryParam.stop, m_stopMethod);
+ }
+
+ if (m_unregisteredMethod != null)
+ {
+ writer.put(EntryParam.unregistered, m_unregisteredMethod);
+ }
+
+ if (m_destroyMethod != null)
+ {
+ writer.put(EntryParam.destroy, m_destroyMethod);
+ }
+
+ if (m_compositionMethod != null)
+ {
+ writer.put(EntryParam.composition, m_compositionMethod);
+ }
+
+ if (m_starter != null)
+ {
+ writer.put(EntryParam.starter, m_starter);
+ }
+
+ if (m_stopper != null)
+ {
+ writer.put(EntryParam.stopper, m_stopper);
+ if (m_starter == null)
+ {
+ throw new IllegalArgumentException("Can't use a @LifecycleController annotation for stopping a service without declaring a " +
+ "@LifecycleController that starts the component in class " + m_className);
+ }
+ }
+
+ if (m_bundleContextField != null)
+ {
+ writer.put(EntryParam.bundleContextField, m_bundleContextField);
+ }
+
+ if (m_dependencyManagerField != null)
+ {
+ writer.put(EntryParam.dependencyManagerField, m_dependencyManagerField);
+ }
+
+ if (m_componentField != null)
+ {
+ writer.put(EntryParam.componentField, m_componentField);
+ }
+ }
+
+ /**
+ * Parses a ServiceDependency Annotation.
+ * @param annotation the ServiceDependency Annotation.
+ */
+ private void parseServiceDependencyAnnotation(Annotation annotation)
+ {
+ EntryWriter writer = new EntryWriter(EntryType.ServiceDependency);
+ m_writers.add(writer);
+
+ // service attribute
+ String service = annotation.get(EntryParam.service.toString());
+ if (service != null)
+ {
+ service = Patterns.parseClass(service, Patterns.CLASS, 1);
+ }
+ else
+ {
+ if (m_isField)
+ {
+ service = Patterns.parseClass(m_descriptor, Patterns.CLASS, 1);
+ }
+ else
+ {
+ service = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS, 2);
+ }
+ }
+ writer.put(EntryParam.service, service);
+
+ // Store this service in list of imported services.
+ m_importService.add(service);
+
+ // autoConfig attribute
+ if (m_isField)
+ {
+ writer.put(EntryParam.autoConfig, m_field);
+ }
+
+ // filter attribute
+ String filter = annotation.get(EntryParam.filter.toString());
+ if (filter != null)
+ {
+ Verifier.verifyFilter(filter, 0);
+ writer.put(EntryParam.filter, filter);
+ }
+
+ // defaultImpl attribute
+ writer.putClass(annotation, EntryParam.defaultImpl, null);
+
+ // added callback
+ writer.putString(annotation, EntryParam.added, (!m_isField) ? m_method : null);
+
+ // timeout parameter
+ writer.putString(annotation, EntryParam.timeout, null);
+ Long t = (Long) annotation.get(EntryParam.timeout.toString());
+ if (t != null && t.longValue() < -1)
+ {
+ throw new IllegalArgumentException("Invalid timeout value " + t + " in ServiceDependency annotation from class " + m_className);
+ }
+
+ // required attribute (not valid if parsing a temporal service dependency)
+ writer.putString(annotation, EntryParam.required, null);
+
+ // changed callback
+ writer.putString(annotation, EntryParam.changed, null);
+
+ // removed callback
+ writer.putString(annotation, EntryParam.removed, null);
+
+ // name attribute
+ parseDependencyName(writer, annotation);
+
+ // propagate attribute
+ writer.putString(annotation, EntryParam.propagate, null);
+ }
+
+ /**
+ * Parses a ConfigurationDependency annotation.
+ * @param annotation the ConfigurationDependency annotation.
+ */
+ private void parseConfigurationDependencyAnnotation(Annotation annotation)
+ {
+ EntryWriter writer = new EntryWriter(EntryType.ConfigurationDependency);
+ m_writers.add(writer);
+
+ // pid attribute (can be specified using the pid attribute, or using the classPid attribute)
+ String pid = annotation.get(EntryParam.pidClass.toString());
+ if (pid != null)
+ {
+ pid = Patterns.parseClass(pid, Patterns.CLASS, 1);
+ } else {
+ pid = get(annotation, EntryParam.pid.toString(), m_className);
+ }
+ writer.put(EntryParam.pid, pid);
+
+ // the method on which the annotation is applied
+ writer.put(EntryParam.updated, m_method);
+
+ // propagate attribute
+ writer.putString(annotation, EntryParam.propagate, null);
+
+ // Property Meta Types
+ parseMetaTypes(annotation, pid, false);
+
+ // name attribute
+ parseDependencyName(writer, annotation);
+ }
+
+ /**
+ * Parses an AspectService annotation.
+ * @param annotation
+ */
+ private void parseAspectService(Annotation annotation)
+ {
+ EntryWriter writer = new EntryWriter(EntryType.AspectService);
+ m_writers.add(writer);
+
+ // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
+ addCommonServiceParams(writer);
+
+ // Parse service filter
+ String filter = annotation.get(EntryParam.filter.toString());
+ if (filter != null)
+ {
+ Verifier.verifyFilter(filter, 0);
+ writer.put(EntryParam.filter, filter);
+ }
+
+ // Parse service aspect ranking
+ Integer ranking = annotation.get(EntryParam.ranking.toString());
+ writer.put(EntryParam.ranking, ranking.toString());
+
+ // Generate Aspect Implementation
+ writer.put(EntryParam.impl, m_className);
+
+ // Parse Aspect properties.
+ parseProperties(annotation, EntryParam.properties, writer);
+
+ // Parse field/added/changed/removed attributes
+ parseAspectOrAdapterCallbackMethods(annotation, writer);
+
+ // Parse service interface this aspect is applying to
+ Object service = annotation.get(EntryParam.service.toString());
+ if (service == null)
+ {
+ if (m_interfaces == null)
+ {
+ throw new IllegalStateException("Invalid AspectService annotation: " +
+ "the service attribute has not been set and the class " + m_className
+ + " does not implement any interfaces");
+ }
+ if (m_interfaces.length != 1)
+ {
+ throw new IllegalStateException("Invalid AspectService annotation: " +
+ "the service attribute has not been set and the class " + m_className
+ + " implements more than one interface");
+ }
+
+ writer.put(EntryParam.service, m_interfaces[0]);
+ }
+ else
+ {
+ writer.putClass(annotation, EntryParam.service, null);
+ }
+
+ // Parse factoryMethod attribute
+ writer.putString(annotation, EntryParam.factoryMethod, null);
+ }
+
+ private void parseAspectOrAdapterCallbackMethods(Annotation annotation, EntryWriter writer)
+ {
+ String field = annotation.get(EntryParam.field.toString());
+ String added = annotation.get(EntryParam.added.toString());
+ String changed = annotation.get(EntryParam.changed.toString());
+ String removed = annotation.get(EntryParam.removed.toString());
+ String swap = annotation.get(EntryParam.swap.toString());
+
+ // "field" and "added/changed/removed/swap" attributes can't be mixed
+ if (field != null && (added != null || changed != null || removed != null || swap != null))
+ {
+ throw new IllegalStateException("Annotation " + annotation + "can't applied on " + m_className
+ + " can't mix \"field\" attribute with \"added/changed/removed\" attributes");
+ }
+
+ // Parse aspect impl field where to inject the original service.
+ writer.putString(annotation, EntryParam.field, null);
+
+ // Parse aspect impl callback methods.
+ writer.putString(annotation, EntryParam.added, null);
+ writer.putString(annotation, EntryParam.changed, null);
+ writer.putString(annotation, EntryParam.removed, null);
+ writer.putString(annotation, EntryParam.swap, null);
+ }
+
+ /**
+ * Parses an AspectService annotation.
+ * @param annotation
+ */
+ private void parseAdapterService(Annotation annotation)
+ {
+ EntryWriter writer = new EntryWriter(EntryType.AdapterService);
+ m_writers.add(writer);
+
+ // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
+ addCommonServiceParams(writer);
+
+ // Generate Adapter Implementation
+ writer.put(EntryParam.impl, m_className);
+
+ // Parse adaptee filter
+ String adapteeFilter = annotation.get(EntryParam.adapteeFilter.toString());
+ if (adapteeFilter != null)
+ {
+ Verifier.verifyFilter(adapteeFilter, 0);
+ writer.put(EntryParam.adapteeFilter, adapteeFilter);
+ }
+
+ // Parse the mandatory adapted service interface.
+ writer.putClass(annotation, EntryParam.adapteeService, null);
+
+ // Parse Adapter properties.
+ parseProperties(annotation, EntryParam.properties, writer);
+
+ // Parse the provided adapter service (use directly implemented interface by default).
+ if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
+ {
+ checkRegisteredUnregisteredNotPresent();
+ }
+
+ // Parse factoryMethod attribute
+ writer.putString(annotation, EntryParam.factoryMethod, null);
+
+ // Parse propagate flag.
+ // Parse factoryMethod attribute
+ writer.putString(annotation, EntryParam.propagate, null);
+
+ // Parse field/added/changed/removed attributes
+ parseAspectOrAdapterCallbackMethods(annotation, writer);
+ }
+
+ /**
+ * Parses a BundleAdapterService annotation.
+ * @param annotation
+ */
+ private void parseBundleAdapterService(Annotation annotation)
+ {
+ EntryWriter writer = new EntryWriter(EntryType.BundleAdapterService);
+ m_writers.add(writer);
+
+ // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
+ addCommonServiceParams(writer);
+
+ // Generate Adapter Implementation
+ writer.put(EntryParam.impl, m_className);
+
+ // Parse bundle filter
+ String filter = annotation.get(EntryParam.filter.toString());
+ if (filter != null)
+ {
+ Verifier.verifyFilter(filter, 0);
+ writer.put(EntryParam.filter, filter);
+ }
+
+ // Parse stateMask attribute
+ writer.putString(annotation, EntryParam.stateMask, Integer.valueOf(
+ Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE).toString());
+
+ // Parse Adapter properties.
+ parseProperties(annotation, EntryParam.properties, writer);
+
+ // Parse the optional adapter service (use directly implemented interface by default).
+ if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
+ {
+ checkRegisteredUnregisteredNotPresent();
+ }
+
+ // Parse propagate attribute
+ writer.putString(annotation, EntryParam.propagate, Boolean.FALSE.toString());
+
+ // Parse factoryMethod attribute
+ writer.putString(annotation, EntryParam.factoryMethod, null);
+ }
+
+ /**
+ * Parses a BundleAdapterService annotation.
+ * @param annotation
+ */
+ private void parseResourceAdapterService(Annotation annotation)
+ {
+ EntryWriter writer = new EntryWriter(EntryType.ResourceAdapterService);
+ m_writers.add(writer);
+
+ // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
+ addCommonServiceParams(writer);
+
+ // Generate Adapter Implementation
+ writer.put(EntryParam.impl, m_className);
+
+ // Parse resource filter
+ String filter = annotation.get(EntryParam.filter.toString());
+ if (filter != null)
+ {
+ Verifier.verifyFilter(filter, 0);
+ writer.put(EntryParam.filter, filter);
+ }
+
+ // Parse Adapter properties.
+ parseProperties(annotation, EntryParam.properties, writer);
+
+ // Parse the provided adapter service (use directly implemented interface by default).
+ if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
+ {
+ checkRegisteredUnregisteredNotPresent();
+ }
+
+ // Parse propagate attribute
+ writer.putString(annotation, EntryParam.propagate, Boolean.FALSE.toString());
+
+ // Parse changed attribute
+ writer.putString(annotation, EntryParam.changed, null);
+ }
+
+ /**
+ * Parses a Factory Configuration Adapter annotation.
+ * @param annotation
+ */
+ private void parseFactoryConfigurationAdapterService(Annotation annotation)
+ {
+ EntryWriter writer = new EntryWriter(EntryType.FactoryConfigurationAdapterService);
+ m_writers.add(writer);
+
+ // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
+ addCommonServiceParams(writer);
+
+ // Generate Adapter Implementation
+ writer.put(EntryParam.impl, m_className);
+
+ // factory pid attribute (can be specified using the factoryPid attribute, or using the factoryPidClass attribute)
+ String factoryPid = annotation.get(EntryParam.factoryPidClass.toString());
+ if (factoryPid != null)
+ {
+ factoryPid = Patterns.parseClass(factoryPid, Patterns.CLASS, 1);
+ } else {
+ factoryPid = get(annotation, EntryParam.factoryPid.toString(), m_className);
+ }
+ writer.put(EntryParam.factoryPid, factoryPid);
+
+ // Parse updated callback
+ writer.putString(annotation, EntryParam.updated, "updated");
+
+ // propagate attribute
+ writer.putString(annotation, EntryParam.propagate, Boolean.FALSE.toString());
+
+ // Parse the provided adapter service (use directly implemented interface by default).
+ if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
+ {
+ checkRegisteredUnregisteredNotPresent();
+ }
+
+ // Parse Adapter properties.
+ parseProperties(annotation, EntryParam.properties, writer);
+
+ // Parse optional meta types for configuration description.
+ parseMetaTypes(annotation, factoryPid, true);
+
+ // Parse factoryMethod attribute
+ writer.putString(annotation, EntryParam.factoryMethod, null);
+ }
+
+ private void parseBundleDependencyAnnotation(Annotation annotation)
+ {
+ EntryWriter writer = new EntryWriter(EntryType.BundleDependency);
+ m_writers.add(writer);
+
+ String filter = annotation.get(EntryParam.filter.toString());
+ if (filter != null)
+ {
+ Verifier.verifyFilter(filter, 0);
+ writer.put(EntryParam.filter, filter);
+ }
+
+ writer.putString(annotation, EntryParam.added, m_method);
+ writer.putString(annotation, EntryParam.changed, null);
+ writer.putString(annotation, EntryParam.removed, null);
+ writer.putString(annotation, EntryParam.required, null);
+ writer.putString(annotation, EntryParam.stateMask, null);
+ writer.putString(annotation, EntryParam.propagate, null);
+ parseDependencyName(writer, annotation);
+ }
+
+ private void parseRersourceDependencyAnnotation(Annotation annotation)
+ {
+ EntryWriter writer = new EntryWriter(EntryType.ResourceDependency);
+ m_writers.add(writer);
+
+ String filter = annotation.get(EntryParam.filter.toString());
+ if (filter != null)
+ {
+ Verifier.verifyFilter(filter, 0);
+ writer.put(EntryParam.filter, filter);
+ }
+
+ if (m_isField)
+ {
+ writer.put(EntryParam.autoConfig, m_field);
+ }
+
+ writer.putString(annotation, EntryParam.added, (!m_isField) ? m_method : null);
+ writer.putString(annotation, EntryParam.changed, null);
+ writer.putString(annotation, EntryParam.removed, null);
+ writer.putString(annotation, EntryParam.required, null);
+ writer.putString(annotation, EntryParam.propagate, null);
+ writer.putString(annotation, EntryParam.factoryMethod, null);
+ parseDependencyName(writer, annotation);
+ }
+
+ /**
+ * Parse the name of a given dependency.
+ * @param writer The writer where to write the dependency name
+ * @param annotation the dependency to be parsed
+ */
+ private void parseDependencyName(EntryWriter writer, Annotation annotation)
+ {
+ String name = annotation.get(EntryParam.name.toString());
+ if (name != null)
+ {
+ if(! m_dependencyNames.add(name))
+ {
+ throw new IllegalArgumentException("Duplicate dependency name " + name + " in Dependency " + annotation + " from class " + m_className);
+ }
+ writer.put(EntryParam.name, name);
+ }
+ }
+
+ private void parseLifecycleAnnotation(Annotation annotation)
+ {
+ Patterns.parseField(m_field, m_descriptor, Patterns.RUNNABLE);
+ if ("true".equals(get(annotation,EntryParam.start.name(), "true")))
+ {
+ if (m_starter != null) {
+ throw new IllegalStateException("Lifecycle annotation already defined on field " +
+ m_starter + " in class " + m_className);
+ }
+ m_starter = m_field;
+ } else {
+ if (m_stopper != null) {
+ throw new IllegalStateException("Lifecycle annotation already defined on field " +
+ m_stopper + " in class " + m_className);
+ }
+ m_stopper = m_field;
+ }
+ }
+
+ /**
+ * Parse optional meta types annotation attributes
+ * @param annotation
+ */
+ private void parseMetaTypes(Annotation annotation, String pid, boolean factory)
+ {
+ if (annotation.get("metadata") != null)
+ {
+ String propertiesHeading = annotation.get("heading");
+ String propertiesDesc = annotation.get("description");
+
+ MetaType.OCD ocd = new MetaType.OCD(pid, propertiesHeading, propertiesDesc);
+ for (Object p: (Object[]) annotation.get("metadata"))
+ {
+ Annotation property = (Annotation) p;
+ String heading = property.get("heading");
+ String id = property.get("id");
+ String type = (String) property.get("type");
+ type = (type != null) ? Patterns.parseClass(type, Patterns.CLASS, 1) : null;
+ Object[] defaults = (Object[]) property.get("defaults");
+ String description = property.get("description");
+ Integer cardinality = property.get("cardinality");
+ Boolean required = property.get("required");
+
+ MetaType.AD ad = new MetaType.AD(id, type, defaults, heading, description,
+ cardinality, required);
+
+ Object[] optionLabels = property.get("optionLabels");
+ Object[] optionValues = property.get("optionValues");
+
+ if (optionLabels == null
+ && optionValues != null
+ ||
+ optionLabels != null
+ && optionValues == null
+ ||
+ (optionLabels != null && optionValues != null && optionLabels.length != optionValues.length))
+ {
+ throw new IllegalArgumentException("invalid option labels/values specified for property "
+ + id +
+ " in PropertyMetadata annotation from class " + m_className);
+ }
+
+ if (optionValues != null)
+ {
+ for (int i = 0; i < optionValues.length; i++)
+ {
+ ad.add(new MetaType.Option(optionValues[i].toString(), optionLabels[i].toString()));
+ }
+ }
+
+ ocd.add(ad);
+ }
+
+ m_metaType.add(ocd);
+ MetaType.Designate designate = new MetaType.Designate(pid, factory);
+ m_metaType.add(designate);
+ m_logger.info("Parsed MetaType Properties from class " + m_className);
+ }
+ }
+
+ /**
+ * Parses a Property annotation (which represents a list of key-value pair).
+ * The properties are encoded using the following json form:
+ *
+ * properties ::= key-value-pair*
+ * key-value-pair ::= key value
+ * value ::= String | String[] | value-type
+ * value-type ::= jsonObject with value-type-info
+ * value-type-info ::= "type"=primitive java type
+ * "value"=String|String[]
+ *
+ * Exemple:
+ *
+ * "properties" : {
+ * "string-param" : "string-value",
+ * "string-array-param" : ["str1", "str2],
+ * "long-param" : {"type":"java.lang.Long", "value":"1"}}
+ * "long-array-param" : {"type":"java.lang.Long", "value":["1"]}}
+ * }
+ * }
+ *
+ * @param annotation the annotation where the Param annotation is defined
+ * @param attribute the attribute name which is of Param type
+ * @param writer the object where the parsed attributes are written
+ */
+ private void parseProperties(Annotation annotation, EntryParam attribute, EntryWriter writer)
+ {
+ try
+ {
+ Object[] parameters = annotation.get(attribute.toString());
+ if (parameters != null)
+ {
+ JSONObject properties = new JSONObject();
+ for (Object p : parameters)
+ {
+ Annotation a = (Annotation) p;
+ String name = (String) a.get("name");
+
+ String type = a.get("type");
+ Class<?> classType;
+ try
+ {
+ classType = (type == null) ? String.class : Class.forName(Patterns.parseClass(type,
+ Patterns.CLASS, 1));
+ }
+ catch (ClassNotFoundException e)
+ {
+ // Theorically impossible
+ throw new IllegalArgumentException("Invalid Property type " + type
+ + " from annotation " + annotation + " in class " + m_className);
+ }
+
+ Object[] values;
+
+ if ((values = a.get("value")) != null)
+ {
+ values = checkPropertyType(name, classType, values);
+ addProperty(properties, name, values, classType);
+ }
+ else if ((values = a.get("values")) != null)
+ { // deprecated
+ values = checkPropertyType(name, classType, values);
+ addProperty(properties, name, values, classType);
+ }
+ else if ((values = a.get("longValue")) != null)
+ {
+ addProperty(properties, name, values, Long.class);
+ }
+ else if ((values = a.get("doubleValue")) != null)
+ {
+ addProperty(properties, name, values, Double.class);
+ }
+ else if ((values = a.get("floatValue")) != null)
+ {
+ addProperty(properties, name, values, Float.class);
+ }
+ else if ((values = a.get("intValue")) != null)
+ {
+ addProperty(properties, name, values, Integer.class);
+ }
+ else if ((values = a.get("byteValue")) != null)
+ {
+ addProperty(properties, name, values, Byte.class);
+ }
+ else if ((values = a.get("charValue")) != null)
+ {
+ addProperty(properties, name, values, Character.class);
+ }
+ else if ((values = a.get("booleanValue")) != null)
+ {
+ addProperty(properties, name, values, Boolean.class);
+ }
+ else if ((values = a.get("shortValue")) != null)
+ {
+ addProperty(properties, name, values, Short.class);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Missing Property value from annotation "
+ + annotation + " in class " + m_className);
+ }
+ }
+ writer.putJsonObject(attribute, properties);
+ }
+ }
+ catch (JSONException e)
+ {
+ throw new IllegalArgumentException("UNexpected exception while parsing Property from annotation "
+ + annotation + " in class " + m_className, e);
+ }
+ }
+
+ /**
+ * Checks if a property contains values that are compatible with a give primitive type.
+ *
+ * @param name the property name
+ * @param values the values for the property name
+ * @param type the primitive type.
+ * @return the same property values, possibly modified if the type is 'Character' (the strings are converted to their character integer values).
+ */
+ private Object[] checkPropertyType(String name, Class<?> type, Object ... values)
+ {
+ if (type.equals(String.class))
+ {
+ return values;
+ } else if (type.equals(Long.class)) {
+ for (Object value : values) {
+ try {
+ Long.valueOf(value.toString());
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+ + " does not contain a valid Long value (" + value.toString() + ")");
+ }
+ }
+ } else if (type.equals(Double.class)) {
+ for (Object value : values) {
+ try {
+ Double.valueOf(value.toString());
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+ + " does not contain a valid Double value (" + value.toString() + ")");
+ }
+ }
+ } else if (type.equals(Float.class)) {
+ for (Object value : values) {
+ try {
+ Float.valueOf(value.toString());
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+ + " does not contain a valid Float value (" + value.toString() + ")");
+ }
+ }
+ } else if (type.equals(Integer.class)) {
+ for (Object value : values) {
+ try {
+ Integer.valueOf(value.toString());
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+ + " does not contain a valid Integer value (" + value.toString() + ")");
+ }
+ }
+ } else if (type.equals(Byte.class)) {
+ for (Object value : values) {
+ try {
+ Byte.valueOf(value.toString());
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+ + " does not contain a valid Byte value (" + value.toString() + ")");
+ }
+ }
+ } else if (type.equals(Character.class)) {
+ for (int i = 0; i < values.length; i++)
+ {
+ try
+ {
+ // If the string is already an integer, don't modify it
+ Integer.valueOf(values[i].toString());
+ }
+ catch (NumberFormatException e)
+ {
+ // Converter the character string to its corresponding integer code.
+ if (values[i].toString().length() != 1)
+ {
+ throw new IllegalArgumentException("Property \"" + name + "\" in class "
+ + m_className + " does not contain a valid Character value (" + values[i] + ")");
+ }
+ try
+ {
+ values[i] = Integer.valueOf(values[i].toString().charAt(0));
+ }
+ catch (NumberFormatException e2)
+ {
+ throw new IllegalArgumentException("Property \"" + name + "\" in class "
+ + m_className + " does not contain a valid Character value (" + values[i].toString()
+ + ")");
+ }
+ }
+ }
+ } else if (type.equals(Boolean.class)) {
+ for (Object value : values) {
+ if (! value.toString().equalsIgnoreCase("false") && ! value.toString().equalsIgnoreCase("true")) {
+ throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+ + " does not contain a valid Boolean value (" + value.toString() + ")");
+ }
+ }
+ } else if (type.equals(Short.class)) {
+ for (Object value : values) {
+ try {
+ Short.valueOf(value.toString());
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+ + " does not contain a valid Short value (" + value.toString() + ")");
+ }
+ }
+ }
+
+ return values;
+ }
+
+ /**
+ * Adds a key/value(s) pair in a properties map
+ * @param properties the target properties map
+ * @param name the property name
+ * @param value the property value(s)
+ * @param type the property type
+ * @throws JSONException
+ */
+ private void addProperty(JSONObject props, String name, Object value, Class<?> type) throws JSONException {
+ if (value.getClass().isArray())
+ {
+ Object[] array = (Object[]) value;
+ if (array.length == 1)
+ {
+ value = array[0];
+ }
+ }
+
+ if (type.equals(String.class))
+ {
+ props.put(name, value.getClass().isArray() ? new JSONArray(value) : value);
+ }
+ else
+ {
+ JSONObject jsonValue = new JSONObject();
+ jsonValue.put("type", type.getName());
+ jsonValue.put("value", value.getClass().isArray() ? new JSONArray(value) : value);
+ props.put(name, jsonValue);
+ }
+ }
+
+ /**
+ * Parse Inject annotation, used to inject some special classes in some fields
+ * (BundleContext/DependencyManager etc ...)
+ * @param annotation the Inject annotation
+ */
+ private void parseInject(Annotation annotation)
+ {
+ if (Patterns.BUNDLE_CONTEXT.matcher(m_descriptor).matches())
+ {
+ m_bundleContextField = m_field;
+ }
+ else if (Patterns.DEPENDENCY_MANAGER.matcher(m_descriptor).matches())
+ {
+ m_dependencyManagerField = m_field;
+ }
+ else if (Patterns.COMPONENT.matcher(m_descriptor).matches())
+ {
+ m_componentField = m_field;
+ }
+ else
+ {
+ throw new IllegalArgumentException("@Inject annotation can't be applied on the field \"" + m_field
+ + "\" in class " + m_className);
+ }
+ }
+
+ /**
+ * Checks if the class is annotated with some given annotations. Notice that the Service
+ * is always parsed at end of parsing, so, we have to check the last element of our m_writers
+ * List.
+ * @return true if one of the provided annotations has been found from the parsed class.
+ */
+ private void checkServiceDeclared(EntryType... types)
+ {
+ boolean ok = false;
+ if (m_writers.size() > 0)
+ {
+ for (EntryType type: types)
+ {
+ if (m_writers.get(m_writers.size() - 1).getEntryType() == type)
+ {
+ ok = true;
+ break;
+ }
+ }
+ }
+
+ if (!ok)
+ {
+ throw new IllegalStateException(
+ ": the class must be annotated with either one of the following types: "
+ + Arrays.toString(types));
+ }
+ }
+
+ /**
+ * This method checks if the @Registered and/or @Unregistered annotations have been defined
+ * while they should not, because the component does not provide a service.
+ */
+ private void checkRegisteredUnregisteredNotPresent()
+ {
+ if (m_registeredMethod != null)
+ {
+ throw new IllegalStateException("@Registered annotation can't be used on a Component " +
+ " which does not provide a service (class=" + m_className + ")");
+
+ }
+
+ if (m_unregisteredMethod != null)
+ {
+ throw new IllegalStateException("@Unregistered annotation can't be used on a Component " +
+ " which does not provide a service (class=" + m_className + ")");
+
+ }
+ }
+
+ /**
+ * Get an annotation attribute, and return a default value if its not present.
+ * @param <T> the type of the variable which is assigned to the return value of this method.
+ * @param annotation The annotation we are parsing
+ * @param name the attribute name to get from the annotation
+ * @param defaultValue the default value to return if the attribute is not found in the annotation
+ * @return the annotation attribute value, or the defaultValue if not found
+ */
+ @SuppressWarnings("unchecked")
+ private <T> T get(Annotation annotation, String name, T defaultValue)
+ {
+ T value = (T) annotation.get(name);
+ return value != null ? value : defaultValue;
+ }
+}
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationPlugin.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationPlugin.java
new file mode 100644
index 0000000..4139ec9
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationPlugin.java
@@ -0,0 +1,180 @@
+/*
+ * 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.annotation.plugin.bnd;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Map;
+import java.util.Set;
+
+import aQute.bnd.osgi.Analyzer;
+import aQute.bnd.osgi.Resource;
+import aQute.bnd.service.AnalyzerPlugin;
+import aQute.bnd.service.Plugin;
+import aQute.service.reporter.Reporter;
+
+/**
+ * This class is a BND plugin. It scans the target bundle and look for DependencyManager annotations.
+ * It can be directly used when using ant and can be referenced inside the ".bnd" descriptor, using
+ * the "-plugin" parameter.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AnnotationPlugin implements AnalyzerPlugin, Plugin {
+ private static final String IMPORT_SERVICE = "Import-Service";
+ private static final String EXPORT_SERVICE = "Export-Service";
+ private static final String REQUIRE_CAPABILITY = "Require-Capability";
+
+ private static final String LOGLEVEL = "log";
+ private static final String BUILD_IMPEXT = "build-import-export-service";
+ private static final String ADD_REQUIRE_CAPABILITY = "add-require-capability";
+ private static final String DM_RUNTIME_CAPABILITY = "osgi.extender; filter:=\"(&(osgi.extender=org.apache.felix.dependencymanager.runtime)(version>=4.0.0))\"";
+ private BndLogger m_logger;
+ private Reporter m_reporter;
+ private boolean m_buildImportExportService;
+ private boolean m_addRequireCapability;
+ private Map<String, String> m_properties;
+
+ public void setReporter(Reporter reporter) {
+ m_reporter = reporter;
+ }
+
+ public void setProperties(Map<String, String> map) {
+ m_properties = map;
+ }
+
+ /**
+ * This plugin is called after analysis of the JAR but before manifest
+ * generation. When some DM annotations are found, the plugin will add the corresponding
+ * DM component descriptors under META-INF/ directory. It will also set the
+ * "DependencyManager-Component" manifest header (which references the descriptor paths).
+ *
+ * @param analyzer the object that is used to retrieve classes containing DM annotations.
+ * @return true if the classpath has been modified so that the bundle classpath must be reanalyzed
+ * @throws Exception on any errors.
+ */
+ public boolean analyzeJar(Analyzer analyzer) throws Exception {
+ m_logger = new BndLogger(m_reporter, analyzer.getBsn());
+
+ try {
+ init(analyzer);
+
+ // We'll do the actual parsing using a DescriptorGenerator object.
+ DescriptorGenerator generator = new DescriptorGenerator(analyzer, m_logger);
+
+ if (generator.execute()) {
+ // We have parsed some annotations: set the OSGi "DependencyManager-Component" header in the target bundle.
+ analyzer.setProperty("DependencyManager-Component", generator.getDescriptorPaths());
+
+ if (m_addRequireCapability) {
+ // Add our Require-Capability header
+ buildRequireCapability(analyzer);
+ }
+
+ // Possibly set the Import-Service/Export-Service header
+ if (m_buildImportExportService) {
+ // Don't override Import-Service header, if it is found from the bnd directives.
+ if (analyzer.getProperty(IMPORT_SERVICE) == null) {
+ buildImportExportService(analyzer, IMPORT_SERVICE, generator.getImportService());
+ }
+
+ // Don't override Export-Service header, if already defined
+ if (analyzer.getProperty(EXPORT_SERVICE) == null) {
+ buildImportExportService(analyzer, EXPORT_SERVICE, generator.getExportService());
+ }
+ }
+
+ // And insert the generated descriptors into the target bundle.
+ Map<String, Resource> resources = generator.getDescriptors();
+ for (Map.Entry<String, Resource> entry : resources.entrySet()) {
+ analyzer.getJar().putResource(entry.getKey(), entry.getValue());
+ }
+
+ // Insert the metatype resource, if any.
+ Resource metaType = generator.getMetaTypeResource();
+ if (metaType != null) {
+ analyzer.getJar().putResource("OSGI-INF/metatype/metatype.xml", metaType);
+ }
+ }
+ }
+
+ catch (Throwable t) {
+ m_logger.error(parse(t));
+ }
+
+ finally {
+ m_logger.close();
+ }
+
+ return false; // do not reanalyze bundle classpath because our plugin has not changed it.
+ }
+
+ private void init(Analyzer analyzer) {
+ m_logger.setLevel(parseOption(m_properties, LOGLEVEL, BndLogger.Level.Warn.toString()));
+ m_buildImportExportService = parseOption(m_properties, BUILD_IMPEXT, false);
+ m_addRequireCapability = parseOption(m_properties, ADD_REQUIRE_CAPABILITY, true);
+ analyzer.setExceptions(true);
+ m_logger.info("Initialized Bnd DependencyManager plugin: buildImportExport=%b", m_buildImportExportService);
+ }
+
+ private String parseOption(Map<String, String> opts, String name, String def) {
+ String value = opts.get(name);
+ return value == null ? def : value;
+ }
+
+ private boolean parseOption(Map<String, String> opts, String name, boolean def) {
+ String value = opts.get(name);
+ return value == null ? def : Boolean.valueOf(value);
+ }
+
+ private void buildImportExportService(Analyzer analyzer, String header, Set<String> services) {
+ m_logger.info("building %s header with the following services: %s", header, services);
+ if (services.size() > 0) {
+ StringBuilder sb = new StringBuilder();
+ for (String service : services) {
+ sb.append(service);
+ sb.append(",");
+ }
+ sb.setLength(sb.length() - 1); // skip last comma
+ analyzer.setProperty(header, sb.toString());
+ }
+ }
+
+ private void buildRequireCapability(Analyzer analyzer) {
+ String requireCapability = analyzer.getProperty(REQUIRE_CAPABILITY);
+ if (requireCapability == null) {
+ analyzer.setProperty(REQUIRE_CAPABILITY, DM_RUNTIME_CAPABILITY);
+ } else {
+ StringBuilder sb = new StringBuilder(requireCapability).append(",").append(DM_RUNTIME_CAPABILITY);
+ analyzer.setProperty(REQUIRE_CAPABILITY, sb.toString());
+ }
+ }
+
+ /**
+ * Parse an exception into a string.
+ * @param e The exception to parse
+ * @return the parsed exception
+ */
+ private static String parse(Throwable e) {
+ StringWriter buffer = new StringWriter();
+ PrintWriter pw = new PrintWriter(buffer);
+ e.printStackTrace(pw);
+ return (buffer.toString());
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/BndLogger.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/BndLogger.java
new file mode 100644
index 0000000..5ca89d9
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/BndLogger.java
@@ -0,0 +1,199 @@
+/*
+ * 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.annotation.plugin.bnd;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import aQute.service.reporter.Reporter;
+
+/**
+ * Clas used to log messages into the bnd logger.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class BndLogger extends Logger {
+ private final Reporter m_reporter;
+
+ /**
+ * Writer to log file, in tmp dir/dmplugin-BSN.log
+ */
+ private final PrintWriter logWriter;
+
+ /**
+ * DateFormat used when logging.
+ */
+ private final static SimpleDateFormat dateFormat = new SimpleDateFormat("E yyyy.MM.dd hh:mm:ss.S");
+
+ /**
+ * Enabled log level, which can be configured in bnd plugin declaration.
+ */
+ private Level logEnabled = Level.Warn;
+
+ /**
+ * Creates a new bnd Log implementaiton
+ *
+ * @param reporter
+ * the bnd logger
+ * @param logLevel
+ * @param bsn
+ */
+ public BndLogger(Reporter reporter, String bsn) {
+ m_reporter = reporter;
+ File logFilePath = new File(System.getProperty("java.io.tmpdir") + File.separator + "dmplugin"
+ + File.separator + bsn + ".log");
+ new File(logFilePath.getParent()).mkdirs();
+
+ PrintWriter writer = null;
+ try {
+ writer = new PrintWriter(new FileWriter(logFilePath, false));
+ }
+ catch (IOException e) {
+ reporter.exception(e, "Could not create scrplugin log file: %s", logFilePath);
+ writer = null;
+ }
+ this.logWriter = writer;
+ }
+
+ /**
+ * Close log file.
+ */
+ public void close() {
+ if (logWriter != null) {
+ logWriter.close();
+ }
+ }
+
+ /**
+ * Sets the enabled log level.
+ *
+ * @param level
+ * the enabled level ("Error", "Warn", "Info", or "Debug")
+ */
+ public void setLevel(String level) {
+ try {
+ level = Character.toUpperCase(level.charAt(0))
+ + level.substring(1).toLowerCase();
+ this.logEnabled = Level.valueOf(level);
+ } catch (IllegalArgumentException e) {
+ this.logEnabled = Level.Warn;
+ warn("Bnd scrplugin logger initialized with invalid log level: "
+ + level);
+ }
+ }
+
+ // Reporter
+
+ public boolean isDebugEnabled() {
+ return logEnabled.ordinal() >= Level.Debug.ordinal();
+ }
+
+ public void debug(String content, Object ... args) {
+ if (isDebugEnabled()) {
+ m_reporter.trace(content, args);
+ logDebug(String.format(content, args), null);
+ }
+ }
+
+ public boolean isInfoEnabled() {
+ return logEnabled.ordinal() >= Level.Info.ordinal();
+ }
+
+ public void info(String content, Object ... args) {
+ if (isInfoEnabled()) {
+ m_reporter.trace(content, args);
+ logInfo(String.format(content, args), null);
+ }
+ }
+
+ public boolean isWarnEnabled() {
+ return logEnabled.ordinal() >= Level.Warn.ordinal();
+ }
+
+ public void warn(String content, Object ... args) {
+ if (isWarnEnabled()) {
+ m_reporter.warning(content, args);
+ logWarn(String.format(content, args), null);
+ }
+ }
+
+ public void warn(String content, Throwable err, Object ... args) {
+ if (isWarnEnabled()) {
+ m_reporter.warning(content, args);
+ logWarn(String.format(content, args), err);
+ }
+ }
+
+ public boolean isErrorEnabled() {
+ return logEnabled.ordinal() >= Level.Error.ordinal();
+ }
+
+ public void error(String content, Object ... args) {
+ m_reporter.error(content, args);
+ logErr(String.format(content, args), null);
+ }
+
+ public void error(String content, Throwable err, Object ... args) {
+ m_reporter.error(content, args);
+ logErr(String.format(content, args), err);
+ }
+
+ private void logErr(String msg, Throwable t) {
+ log(Level.Error, msg, t);
+ }
+
+ private void logWarn(String msg, Throwable t) {
+ log(Level.Warn, msg, t);
+ }
+
+ private void logInfo(String msg, Throwable t) {
+ log(Level.Info, msg, t);
+ }
+
+ private void logDebug(String msg, Throwable t) {
+ log(Level.Debug, msg, t);
+ }
+
+ private void log(Level level, String msg, Throwable t) {
+ if (logWriter != null) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(dateFormat.format(new Date()));
+ sb.append(" - ");
+ sb.append(level);
+ sb.append(": ");
+ sb.append(msg);
+ if (t != null) {
+ sb.append(" - ").append(toString(t));
+ }
+ logWriter.println(sb.toString());
+ }
+ }
+
+ private static String toString(Throwable e) {
+ StringWriter buffer = new StringWriter();
+ PrintWriter pw = new PrintWriter(buffer);
+ e.printStackTrace(pw);
+ return (buffer.toString());
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/DescriptorGenerator.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/DescriptorGenerator.java
new file mode 100644
index 0000000..e4f3650
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/DescriptorGenerator.java
@@ -0,0 +1,216 @@
+/*
+ * 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.annotation.plugin.bnd;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import aQute.bnd.osgi.Analyzer;
+import aQute.bnd.osgi.Clazz;
+import aQute.bnd.osgi.EmbeddedResource;
+import aQute.bnd.osgi.Resource;
+import aQute.bnd.osgi.Clazz.QUERY;
+
+/**
+ * This helper parses all classes which contain DM annotations, and generates the corresponding component descriptors.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DescriptorGenerator
+{
+ /**
+ * This is the bnd analyzer used to lookup classes containing DM annotations.
+ */
+ private Analyzer m_analyzer;
+
+ /**
+ * This is the generated Dependency Manager descriptors. The hashtable key is the path
+ * to a descriptor. The value is a bnd Resource object which contains the content of a
+ * descriptor.
+ */
+ Map<String, Resource> m_resources = new HashMap<String, Resource>();
+
+ /**
+ * This is the generated MetaType XML descriptor, if any Properties/Property annotations have been found.
+ */
+ private Resource m_metaTypeResource;
+
+ /**
+ * Object used to collect logs.
+ */
+ private final Logger m_logger;
+
+ /**
+ * List of imported services found from every ServiceDependency annotations.
+ */
+ private Set<String> m_importService = new HashSet<String>();
+
+ /**
+ * List of exported services found from every service providing components.
+ */
+ private Set<String> m_exportService = new HashSet<String>();
+
+ /**
+ * Creates a new descriptor generator.
+ * @param analyzer The bnd analyzer used to lookup classes containing DM annotations.
+ * @param debug
+ */
+ public DescriptorGenerator(Analyzer analyzer, Logger logger)
+ {
+ m_analyzer = analyzer;
+ m_logger = logger;
+ }
+
+ /**
+ * Starts the scanning.
+ * @return true if some annotations were successfully parsed, false if not. corresponding generated
+ * descriptors can then be retrieved by invoking the getDescriptors/getDescriptorPaths methods.
+ */
+ public boolean execute() throws Exception
+ {
+ boolean annotationsFound = false;
+ // Try to locate any classes in the wildcarded universe
+ // that are annotated with the DependencyManager "Service" annotations.
+ Collection<Clazz> expanded = m_analyzer.getClasses("",
+ // Parse everything
+ QUERY.NAMED.toString(), "*");
+
+ // Create the object which will collect Config Admin MetaTypes.
+ MetaType metaType = new MetaType();
+
+ for (Clazz c : expanded)
+ {
+ // Let's parse all annotations from that class !
+ AnnotationCollector reader = new AnnotationCollector(m_logger, metaType);
+ c.parseClassFileWithCollector(reader);
+ if (reader.finish())
+ {
+ // And store the generated component descriptors in our resource list.
+ String name = c.getFQN();
+ Resource resource = createComponentResource(reader);
+ m_resources.put("META-INF/dependencymanager/" + name, resource);
+ annotationsFound = true;
+
+ m_importService.addAll(reader.getImportService());
+ m_exportService.addAll(reader.getExportService());
+ }
+ }
+
+ // If some Meta Types have been parsed, then creates the corresponding resource file.
+ if (metaType.getSize() > 0)
+ {
+ m_metaTypeResource = createMetaTypeResource(metaType);
+ }
+ return annotationsFound;
+ }
+
+ /**
+ * Returns the path of the descriptor.
+ * @return the path of the generated descriptors.
+ */
+ public String getDescriptorPaths()
+ {
+ StringBuilder descriptorPaths = new StringBuilder();
+ String del = "";
+ for (Map.Entry<String, Resource> entry : m_resources.entrySet())
+ {
+ descriptorPaths.append(del);
+ descriptorPaths.append(entry.getKey());
+ del = ",";
+ }
+ return descriptorPaths.toString();
+ }
+
+ /**
+ * Returns the list of the generated descriptors.
+ * @return the list of the generated descriptors.
+ */
+ public Map<String, Resource> getDescriptors()
+ {
+ return m_resources;
+ }
+
+ /**
+ * Returns the MetaType resource.
+ */
+ public Resource getMetaTypeResource() {
+ return m_metaTypeResource;
+ }
+
+ /**
+ * Returns set of all imported services. Imported services are deduced from every
+ * @ServiceDependency annotations.
+ * @return the list of imported services
+ */
+ public Set<String> getImportService()
+ {
+ return m_importService;
+ }
+
+ /**
+ * Returns set of all exported services. Imported services are deduced from every
+ * annotations which provides a service (@Component, etc ...)
+ * @return the list of exported services
+ */
+ public Set<String> getExportService()
+ {
+ return m_exportService;
+ }
+
+ /**
+ * Creates a bnd resource that contains the generated dm descriptor.
+ * @param collector
+ * @return
+ * @throws IOException
+ */
+ private Resource createComponentResource(AnnotationCollector collector) throws IOException
+ {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ PrintWriter pw = new PrintWriter(new OutputStreamWriter(out, "UTF-8"));
+ collector.writeTo(pw);
+ pw.close();
+ byte[] data = out.toByteArray();
+ out.close();
+ return new EmbeddedResource(data, 0);
+ }
+
+ /**
+ * Creates a bnd resource that contains the generated metatype descriptor.
+ * @param metaType the Object that has collected all meta type informations.
+ * @return the meta type resource
+ * @throws IOException on any errors
+ */
+ private Resource createMetaTypeResource(MetaType metaType) throws IOException
+ {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ PrintWriter pw = new PrintWriter(new OutputStreamWriter(out, "UTF-8"));
+ metaType.writeTo(pw);
+ pw.close();
+ byte[] data = out.toByteArray();
+ out.close();
+ return new EmbeddedResource(data, 0);
+ }
+}
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryParam.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryParam.java
new file mode 100644
index 0000000..41a0b5d
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryParam.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.annotation.plugin.bnd;
+
+/**
+ * The type of parameters which can be found in a component descriptor.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public enum EntryParam
+{
+ init,
+ start,
+ stop,
+ destroy,
+ impl,
+ provides,
+ properties,
+ composition,
+ service,
+ filter,
+ defaultImpl,
+ required,
+ added,
+ changed,
+ removed,
+ swap,
+ autoConfig,
+ pid,
+ pidClass,
+ factoryPid,
+ factoryPidClass,
+ propagate,
+ updated,
+ timeout,
+ adapteeService,
+ adapteeFilter,
+ stateMask,
+ ranking,
+ factorySet,
+ factoryName,
+ factoryConfigure,
+ factoryMethod,
+ field,
+ name,
+ starter,
+ stopper,
+ bundleContextField,
+ dependencyManagerField,
+ componentField,
+ registered,
+ unregistered
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryType.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryType.java
new file mode 100644
index 0000000..274505b
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryType.java
@@ -0,0 +1,38 @@
+/*
+ * 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.annotation.plugin.bnd;
+
+/**
+ * The type of each entry (lines) stored in a component descriptor.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public enum EntryType
+{
+ Component,
+ AspectService,
+ AdapterService,
+ BundleAdapterService,
+ ResourceAdapterService,
+ FactoryConfigurationAdapterService,
+ ServiceDependency,
+ ConfigurationDependency,
+ BundleDependency,
+ ResourceDependency,
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryWriter.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryWriter.java
new file mode 100644
index 0000000..047aedd
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryWriter.java
@@ -0,0 +1,252 @@
+/*
+ * 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.annotation.plugin.bnd;
+
+import java.util.Arrays;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import aQute.bnd.osgi.Annotation;
+
+/**
+ * This class encodes a component descriptor entry line, using json.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class EntryWriter
+{
+ // Every descriptor entries contains a type parameter for identifying the kind of entry
+ private final static String TYPE = "type";
+
+ /** All parameters as stored in a json object */
+ private JSONObject m_json;
+
+ /** The entry type */
+ private EntryType m_type;
+
+ /**
+ * Makes a new component descriptor entry.
+ */
+ public EntryWriter(EntryType type)
+ {
+ m_type = type;
+ m_json = new JSONObject();
+ try
+ {
+ m_json.put("type", type.toString());
+ }
+ catch (JSONException e)
+ {
+ throw new RuntimeException("could not initialize json object", e);
+ }
+ }
+
+ /**
+ * Returns this entry type.
+ */
+ EntryType getEntryType()
+ {
+ return m_type;
+ }
+
+ /**
+ * Returns a string representation for the given component descriptor entry.
+ */
+ @Override
+ public String toString()
+ {
+ return m_json.toString();
+ }
+
+ /**
+ * Put a String parameter in this descritor entry.
+ */
+ public void put(EntryParam param, String value)
+ {
+ checkType(param.toString());
+ try
+ {
+ m_json.put(param.toString(), value);
+ }
+ catch (JSONException e)
+ {
+ throw new IllegalArgumentException("could not add param " + param + ":" + value, e);
+ }
+ }
+
+ /**
+ * Put a String[] parameter in this descriptor entry.
+ */
+ public void put(EntryParam param, String[] array)
+ {
+ checkType(param.toString());
+ try
+ {
+ m_json.put(param.toString(), new JSONArray(Arrays.asList(array)));
+ }
+ catch (JSONException e)
+ {
+ throw new IllegalArgumentException("could not add param " + param + ":"
+ + Arrays.toString(array), e);
+ }
+ }
+
+ /**
+ * Puts a json object.
+ * @throws JSONException
+ */
+ public void putJsonObject(EntryParam param, JSONObject jsonObject) throws JSONException
+ {
+ m_json.put(param.toString(), jsonObject);
+ }
+
+ /**
+ * Get a String attribute value from an annotation and write it into this descriptor entry.
+ */
+ public String putString(Annotation annotation, EntryParam param, String def)
+ {
+ checkType(param.toString());
+ Object value = annotation.get(param.toString());
+ if (value == null && def != null)
+ {
+ value = def;
+ }
+ if (value != null)
+ {
+ put(param, value.toString());
+ }
+ return value == null ? null : value.toString();
+ }
+
+ /**
+ * Get a String array attribute value from an annotation and write it into this descriptor entry.
+ */
+ public void putStringArray(Annotation annotation, EntryParam param, String[] def)
+ {
+ checkType(param.toString());
+ Object value = annotation.get(param.toString());
+ if (value == null && def != null)
+ {
+ value = def;
+ }
+ if (value != null)
+ {
+ for (Object v: ((Object[]) value))
+ {
+ try
+ {
+ m_json.append(param.toString(), v.toString());
+ }
+ catch (JSONException e)
+ {
+ throw new IllegalArgumentException("Could not add param " + param + ":"
+ + value.toString(), e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get a class attribute value from an annotation and write it into this descriptor entry.
+ */
+ public void putClass(Annotation annotation, EntryParam param, Object def)
+ {
+ checkType(param.toString());
+
+ Pattern pattern = Patterns.CLASS;
+ Object value = annotation.get(param.toString());
+ if (value == null && def != null)
+ {
+ value = def;
+ pattern = null;
+ }
+ if (value != null)
+ {
+ if (pattern != null)
+ {
+ value = Patterns.parseClass(value.toString(), pattern, 1);
+ }
+ put(param, value.toString());
+ }
+ }
+
+ /**
+ * Get a class array attribute value from an annotation and write it into this descriptor entry.
+ * Also collect classes found from the array into a given Set.
+ * @return the class array size.
+ */
+ public int putClassArray(Annotation annotation, EntryParam param, Object def, Set<String> collect)
+ {
+ checkType(param.toString());
+
+ Pattern pattern = Patterns.CLASS;
+ Object value = annotation.get(param.toString());
+ if (value == null && def != null)
+ {
+ value = def;
+ pattern = null;
+ }
+ if (value != null)
+ {
+ if (!(value instanceof Object[]))
+ {
+ throw new IllegalArgumentException("annotation parameter " + param
+ + " has not a class array type");
+ }
+
+ for (Object v: ((Object[]) value))
+ {
+ if (pattern != null)
+ {
+ v = Patterns.parseClass(v.toString(), pattern, 1);
+ }
+ try
+ {
+ m_json.append(param.toString(), v.toString());
+ collect.add(v.toString());
+ }
+ catch (JSONException e)
+ {
+ throw new IllegalArgumentException("Could not add param " + param + ":"
+ + value.toString(), e);
+ }
+ }
+
+ return ((Object[]) value).length;
+ }
+
+ return 0;
+ }
+
+ /**
+ * Check if the written key is not equals to "type" ("type" is an internal attribute we are using
+ * in order to identify a kind of descriptor entry (Service, ServiceDependency, etc ...).
+ */
+ private void checkType(String key)
+ {
+ if (TYPE.equals(key))
+ {
+ throw new IllegalArgumentException("\"" + TYPE + "\" parameter can't be overriden");
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/Logger.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/Logger.java
new file mode 100644
index 0000000..bd16bf1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/Logger.java
@@ -0,0 +1,40 @@
+/*
+ * 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.annotation.plugin.bnd;
+
+/**
+ * Base class for our logger. Under Maven, we log into the Maven logger. Under bnd, we log into the Bnd logger.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class Logger
+{
+ /**
+ * Log Levels.
+ */
+ enum Level {
+ Error, Warn, Info, Debug
+ }
+
+ public abstract void error(String msg, Object ... args);
+ public abstract void error(String msg, Throwable err, Object ... args);
+ public abstract void warn(String msg , Object ... args);
+ public abstract void info(String msg , Object ... args);
+ public abstract void debug(String msg, Object ... args);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/MetaType.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/MetaType.java
new file mode 100644
index 0000000..b934306
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/MetaType.java
@@ -0,0 +1,326 @@
+/*
+ * 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.annotation.plugin.bnd;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper class used to generate an XML representation of a MetaType data structure.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class MetaType
+{
+ /**
+ * The list of Object Class Definitions used to group the attributes of a given
+ * set of properties.
+ */
+ private List<OCD> m_ocdList = new ArrayList<OCD>();
+
+ /**
+ * The list of Designate elements.
+ */
+ private List<Designate> m_designateList = new ArrayList<Designate>();
+
+ /**
+ * The default localization directory.
+ */
+ private final static String LOCALIZATION = "OSGI-INF/metatype/metatype";
+
+ /**
+ * Adds an Object Class Definition into this meta type.
+ * @param ocd the Object Class Definition.
+ */
+ public void add(OCD ocd)
+ {
+ m_ocdList.add(ocd);
+ }
+
+ /**
+ * Adds a Designate element, which maps a PID to an OCD.
+ * @param designate the Designate element.
+ */
+ public void add(Designate designate)
+ {
+ m_designateList.add(designate);
+ }
+
+ /**
+ * Returns the number of OCD contained in this meta type.
+ * @return the number of OCD contained in this meta type.
+ */
+ public int getSize()
+ {
+ return m_ocdList.size();
+ }
+
+ /**
+ * Generates an XML representation of this metatype.
+ * @param pw a PrintWriter where the XML is written
+ */
+ public void writeTo(PrintWriter pw)
+ {
+ pw.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ pw.println("<metatype:MetaData xmlns:metatype=\"http://www.osgi.org/xmlns/metatype/v1.0.0\" localization=\""
+ + LOCALIZATION + "\">");
+ for (OCD ocd : m_ocdList)
+ {
+ ocd.writeTo(pw);
+ }
+ for (Designate designate : m_designateList)
+ {
+ designate.writeTo(pw);
+ }
+ pw.println("</metatype:MetaData>");
+ }
+
+ private static void writeAttribute(String name, Object value, PrintWriter pw)
+ {
+ if (value != null)
+ {
+ pw.print(" " + name + "=" + "\"" + value.toString() + "\"");
+ }
+ }
+
+ /**
+ * An Object Class Definition, which contains a set of Attributes properies.
+ */
+ public static class OCD
+ {
+ String m_id;
+ String m_name;
+ String m_description;
+ List<AD> m_attributes = new ArrayList<AD>();
+
+ OCD(String pid, String name, String desc)
+ {
+ this.m_id = pid;
+ this.m_name = name;
+ this.m_description = desc;
+ }
+
+ public void add(AD ad)
+ {
+ m_attributes.add(ad);
+ }
+
+ public void writeTo(PrintWriter pw)
+ {
+ pw.print(" <OCD");
+ writeAttribute("id", m_id, pw);
+ writeAttribute("name", m_name, pw);
+ writeAttribute("description", m_description, pw);
+ if (m_attributes.size() == 0)
+ {
+ pw.println("/>");
+ }
+ else
+ {
+ pw.println(">");
+ for (AD ad : m_attributes)
+ {
+ ad.writeTo(pw);
+ }
+ pw.println(" </OCD>");
+ }
+ }
+ }
+
+ /**
+ * An Attribute Definition, which describes a given Properties
+ */
+ @SuppressWarnings("serial")
+ public static class AD
+ {
+ String m_id;
+ String m_type;
+ String m_defaults;
+ String m_name;
+ String m_description;
+ Integer m_cardinality;
+ Boolean m_required;
+ List<Option> m_options = new ArrayList<Option>();
+
+ private final static Map<String, String> _allowedTypes = new HashMap<String, String>()
+ {
+ {
+ put(String.class.getName(), "String");
+ put(Long.class.getName(), "Long");
+ put(Integer.class.getName(), "Integer");
+ put(Character.class.getName(), "Char");
+ put(Byte.class.getName(), "Byte");
+ put(Double.class.getName(), "Double");
+ put(Float.class.getName(), "Float");
+ put(Boolean.class.getName(), "Boolean");
+ }
+ };
+
+ public AD(String id, String type, Object[] defaults, String name, String desc, Integer cardinality, Boolean required)
+ {
+ this.m_id = id;
+ this.m_type = (type == null) ? "String" : getType(type);
+ this.m_name = name;
+ this.m_description = desc;
+ this.m_cardinality = cardinality;
+ this.m_required = required;
+
+ if (defaults != null)
+ {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < defaults.length; i++)
+ {
+ sb.append(defaults[i].toString());
+ if (i < defaults.length - 1)
+ {
+ sb.append(",");
+ }
+ }
+ this.m_defaults = sb.toString();
+
+ // Check if the number of default values is consistent with the cardinality.
+ if (cardinality != null)
+ {
+ int max = (cardinality.intValue() == 0) ? 1 : Math.abs(cardinality.intValue());
+ if (defaults.length > max)
+ {
+ throw new IllegalArgumentException("number of default values ("
+ + defaults.length + ") is inconsistent with cardinality ("
+ + cardinality + ")");
+ }
+ }
+ }
+ }
+
+ public void writeTo(PrintWriter pw)
+ {
+ pw.print(" <AD");
+ writeAttribute("id", m_id, pw);
+ writeAttribute("type", m_type, pw);
+ writeAttribute("default", m_defaults, pw);
+ writeAttribute("name", m_name, pw);
+ writeAttribute("description", m_description, pw);
+ writeAttribute("cardinality", m_cardinality, pw);
+ if (m_options.size() == 0)
+ {
+ pw.println("/>");
+ }
+ else
+ {
+ pw.println(">");
+ for (Option option : m_options)
+ {
+ option.writeTo(pw);
+ }
+ pw.println(" </AD>");
+ }
+ }
+
+ private String getType(String t)
+ {
+ String result = _allowedTypes.get(t);
+ if (result == null)
+ {
+ throw new IllegalArgumentException("Invalid Property type: " + m_type);
+ }
+ return result;
+ }
+
+ public void add(Option option)
+ {
+ m_options.add(option);
+ }
+ }
+
+ /**
+ * An Option datastructure, which can be associated with an Attribute.
+ */
+ public static class Option
+ {
+ String m_value;
+ String m_label;
+
+ Option(String value, String label)
+ {
+ this.m_value = value;
+ this.m_label = label;
+ }
+
+ public void writeTo(PrintWriter pw)
+ {
+ pw.print(" <Option");
+ writeAttribute("value", m_value, pw);
+ writeAttribute("label", m_label, pw);
+ pw.println("/>");
+ }
+ }
+
+ /**
+ * A Designate element, which maps a PID to a given Object Class Definition.
+ */
+ public static class Designate
+ {
+ String m_pid;
+ boolean m_factory;
+ OBject m_object;
+
+ public Designate(String pid, boolean factory)
+ {
+ this.m_pid = pid;
+ this.m_factory = factory;
+ this.m_object = new OBject(pid);
+ }
+
+ public void writeTo(PrintWriter pw)
+ {
+ pw.print(" <Designate");
+ writeAttribute("pid", m_pid, pw);
+ if (m_factory)
+ {
+ writeAttribute("factoryPid", m_pid, pw);
+ }
+ pw.println(">");
+ m_object.writeTo(pw);
+ pw.println(" </Designate>");
+ }
+ }
+
+ /**
+ * A definition of an instance.
+ */
+ public static class OBject
+ {
+ String m_ocdref;
+
+ OBject(String ocdref)
+ {
+ this.m_ocdref = ocdref;
+ }
+
+ public void writeTo(PrintWriter pw)
+ {
+ pw.print(" <Object");
+ writeAttribute("ocdref", m_ocdref, pw);
+ pw.println("/>");
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/Patterns.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/Patterns.java
new file mode 100644
index 0000000..4def6b9
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/Patterns.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.annotation.plugin.bnd;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Class containings pattern matching helper methods.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Patterns
+{
+ // Pattern used to check if a method is void and does not take any params
+ public final static Pattern VOID = Pattern.compile("\\(\\)V");
+
+ // Pattern used to check if a method returns an array of Objects
+ public final static Pattern COMPOSITION = Pattern.compile("\\(\\)\\[Ljava/lang/Object;");
+
+ // Pattern used to parse the class parameter from the bind methods ("bind(Type)" or "bind(Map, Type)" or "bind(BundleContext, Type)"
+ public final static Pattern BIND_CLASS = Pattern.compile("\\((L[^;]+;)?L([^;]+);\\)V");
+
+ // Pattern used to parse classes from class descriptors;
+ public final static Pattern CLASS = Pattern.compile("L([^;]+);");
+
+ // Pattern used to parse the field on which a Publisher annotation may be applied on
+ public final static Pattern RUNNABLE = Pattern.compile("Ljava/lang/Runnable;");
+
+ // Pattern used to parse a field whose type is BundleContext
+ public final static Pattern BUNDLE_CONTEXT = Pattern.compile("Lorg/osgi/framework/BundleContext;");
+
+ // Pattern used to parse a field whose type is DependencyManager
+ public final static Pattern DEPENDENCY_MANAGER = Pattern.compile("Lorg.apache.felix.dm.DependencyManager;");
+
+ // Pattern used to parse a field whose type is Component
+ public final static Pattern COMPONENT = Pattern.compile("Lorg.apache.felix.dm.Component;");
+
+ /**
+ * Parses a class.
+ * @param clazz the class to be parsed (the package is "/" separated).
+ * @param pattern the pattern used to match the class.
+ * @param group the pattern group index where the class can be retrieved.
+ * @return the parsed class.
+ */
+ public static String parseClass(String clazz, Pattern pattern, int group)
+ {
+ Matcher matcher = pattern.matcher(clazz);
+ if (matcher.matches())
+ {
+ return matcher.group(group).replace("/", ".");
+ }
+ else
+ {
+ throw new IllegalArgumentException("Invalid class descriptor: " + clazz);
+ }
+ }
+
+ /**
+ * Checks if a method descriptor matches a given pattern.
+ * @param the method whose signature descriptor is checked
+ * @param pattern the pattern used to check the method signature descriptor
+ * @throws IllegalArgumentException if the method signature descriptor does not match the given pattern.
+ */
+ public static void parseMethod(String method, String descriptor, Pattern pattern)
+ {
+ Matcher matcher = pattern.matcher(descriptor);
+ if (!matcher.matches())
+ {
+ throw new IllegalArgumentException("Invalid method " + method + ", wrong signature: "
+ + descriptor);
+ }
+ }
+
+ /**
+ * Checks if a field descriptor matches a given pattern.
+ * @param field the field whose type descriptor is checked
+ * @param descriptor the field descriptor to be checked
+ * @param pattern the pattern to use
+ * @throws IllegalArgumentException if the method signature descriptor does not match the given pattern.
+ */
+ public static void parseField(String field, String descriptor, Pattern pattern) {
+ Matcher matcher = pattern.matcher(descriptor);
+ if (!matcher.matches())
+ {
+ throw new IllegalArgumentException("Invalid field " + field + ", wrong signature: "
+ + descriptor);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/test/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.annotation/test/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/test/.gitignore
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/.classpath b/dependencymanager/org.apache.felix.dependencymanager.benchmark/.classpath
new file mode 100644
index 0000000..57c70f3
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/.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.8"/>
+ <classpathentry kind="con" path="aQute.bnd.classpath.container"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.benchmark/.gitignore
new file mode 100644
index 0000000..90dde36
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/.gitignore
@@ -0,0 +1,3 @@
+/bin/
+/bin_test/
+/generated/
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/.project b/dependencymanager/org.apache.felix.dependencymanager.benchmark/.project
new file mode 100644
index 0000000..e601a91
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.apache.felix.dependencymanager.benchmark</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.benchmark/.settings/org.eclipse.jdt.core.prefs b/dependencymanager/org.apache.felix.dependencymanager.benchmark/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..a698e59
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,12 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.8
+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.8
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/README b/dependencymanager/org.apache.felix.dependencymanager.benchmark/README
new file mode 100644
index 0000000..ff032fe
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/README
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ */
+
+Installation:
+============
+
+- see toplevel README on how to import dependencymanager into bndtools
+
+How to launch the stress test under bndtools:
+============================================
+
+The stress test performs two kind of tests on DM and parallel DM.
+
+1) first kind of tests: starts/stops several times each tested bundle (DM/Parallel DM). When
+started, the test bundle is expected to register/unregister several services. And no processing is
+done at all in each component start methods).
+
+2) second kind of tests: same as before, but some processing is done in each component start methods.
+
+To launch the stress test under BndTools, click on the noindex.bndrun file of the
+"org.apache.felix.dm.benchmark" project, then click on "Run", then in "Run OSGi".
+
+You should see something like that in the eclipse console:
+
+>> --------------------------------------------------------------------------------------------------------------
+g! Starting benchmarks (each tested bundle will add/remove 630 components during bundle activation).
+
+ [Starting benchmarks with no processing done in components start methods]
+
+Benchmarking bundle: org.apache.felix.dependencymanager.benchmark.dependencymanager ....................
+-> results in nanos: [189,130,687 | 205,730,144 | 312,092,102 | 357,470,857 | 871,419,487]
+
+Benchmarking bundle: org.apache.felix.dependencymanager.benchmark.dependencymanager.parallel ....................
+-> results in nanos: [85,158,366 | 103,439,337 | 122,633,515 | 157,082,407 | 284,332,202]
+
+ [Starting benchmarks with processing done in components start methods]
+
+Benchmarking bundle: org.apache.felix.dependencymanager.benchmark.dependencymanager .....
+-> results in nanos: [2,748,431,149 | 2,750,475,610 | 2,756,254,193 | 2,772,447,115 | 2,774,345,245]
+
+Benchmarking bundle: org.apache.felix.dependencymanager.benchmark.dependencymanager.parallel .....
+-> results in nanos: [687,259,058 | 696,725,568 | 700,220,615 | 704,310,739 | 740,325,481]
+-----------------------------------------------------------------------------------------------------------------
+
+You can also possibly run the same test using optimized DM filter indices.
+To do so, run "index.bndrun"
+but using DM filter indices has a CPU cost and are useful if you have many service dependencies.
+To test filter indices, first increase the number of components created/removed during bundle
+startup. To do so, edit the Artist.java and change the "Artists" when is by default set to 30, and set it to 300.
+
+You should then observe some significant performance improvements:
+
+for example, with Artist.ARTISTS=300, you should observe the following:
+
+noindex.bndrun (no filter indices used):
+
+>> --------------------------------------------------------------------------------------------------------------
+ g! Starting benchmarks (each tested bundle will add/remove 6300 components during bundle activation).
+
+ [Starting benchmarks with no processing done in components start methods]
+
+Benchmarking bundle: org.apache.felix.dependencymanager.benchmark.dependencymanager .....
+-> results in nanos: [17,436,869,644 | 17,525,534,346 | 18,080,624,001 | 18,246,597,908 | 20,715,696,669]
+
+Benchmarking bundle: org.apache.felix.dependencymanager.benchmark.dependencymanager.parallel .....
+-> results in nanos: [9,660,520,501 | 9,810,057,488 | 9,870,295,166 | 10,014,334,906 | 10,628,193,815]
+
+ [Starting benchmarks with processing done in components start methods]
+
+Benchmarking bundle: org.apache.felix.dependencymanager.benchmark.dependencymanager .....
+-> results in nanos: [42,700,651,438 | 43,207,156,615 | 43,653,372,523 | 43,869,438,994 | 44,715,701,457]
+
+Benchmarking bundle: org.apache.felix.dependencymanager.benchmark.dependencymanager.parallel .....
+-> results in nanos: [15,021,876,153 | 15,091,340,552 | 15,202,305,936 | 15,248,728,826 | 15,398,221,492]
+-----------------------------------------------------------------------------------------------------------------
+
+and with index.bndrun (using DM filter indices):
+
+>> --------------------------------------------------------------------------------------------------------------
+g! Starting benchmarks (each tested bundle will add/remove 6300 components during bundle activation).
+
+ [Starting benchmarks with no processing done in components start methods]
+
+Benchmarking bundle: org.apache.felix.dependencymanager.benchmark.dependencymanager .....
+-> results in nanos: [3,142,869,517 | 3,564,970,695 | 4,023,603,870 | 6,206,640,362 | 6,918,113,818]
+
+Benchmarking bundle: org.apache.felix.dependencymanager.benchmark.dependencymanager.parallel .....
+-> results in nanos: [2,868,554,914 | 2,873,491,201 | 2,897,439,973 | 2,913,317,331 | 3,890,123,728]
+
+ [Starting benchmarks with processing done in components start methods]
+
+Benchmarking bundle: org.apache.felix.dependencymanager.benchmark.dependencymanager .....
+-> results in nanos: [28,515,623,505 | 28,558,774,886 | 28,661,315,061 | 28,808,682,302 | 28,915,519,208]
+
+Benchmarking bundle: org.apache.felix.dependencymanager.benchmark.dependencymanager.parallel .....
+-> results in nanos: [7,702,400,991 | 7,749,145,806 | 7,760,650,323 | 7,832,386,237 | 7,854,739,136]
+-----------------------------------------------------------------------------------------------------------------
+
+
+How to interpret results:
+========================
+
+for each tested bundle, the time spent is displayed in nanos.
+for example:
+
+ -> results in nanos: [85,158,366 | 103,439,337 | 122,633,515 | 157,082,407 | 284,332,202]
+
+Here is how to interpret the results: when testing a bundle, the benchmark controller starts/stops
+it many times, then the elapsed time used to start the bundle, activate/deactivate all services, and
+stop the bundle is recorded in a list. Then this list is sorted: the first entry is the fastest
+execution time, the last entry is the slowest. the middle one is the average. We display the first
+entry (fastest), the entry at 1/4 of the list, the middle of the list, the entry at 3/4 of the list,
+and the last entry (slowest time).
+
+We don't do an average, because usually, when running benchmark, some measurements don't reflect
+reality, especially, when there is a full GC or when the JVM is warming up. (we actually do the same
+as in Java Chronicle: https://github.com/peter-lawrey/Java-Chronicle).
+
+Stress test scenario description
+--------------------------------
+
+For sake of simplicity, a simple scenario domain is used (actually, this example domain has been
+inspired from the "Java8 Lambdas" book, O'reilly): We have the following services:
+
+"Artist" service: An Artist is an individual or group of musicians, who creates some "Albums". One
+Artist service depends on several Album services.
+
+"Album" service: is a single release of musics, comprising several music Tracks. One Album depends
+on several Track services.
+
+"Track" service: A piece of music.
+
+The scenario is implemented in the following bundles
+
+- org.apache.felix.dm.benchmark.scenario: defines the interfaces.
+- org.apache.felix.dm.benchmark.scenario.impl: defines the basic implementations for the services.
+
+The Scenario Controller (see
+org.apache.felix.dm.benchmark.scenario/org.apache.felix.dm.benchmark.scenario.impl) is in charge of
+starting/stopping many times some specific bundles (DM, Parallel DM).
+
+By default, when a tested bundle is started, it will create several Artists (see Artists.ARTISTS
+constant). each Artist depends on several Albums (see Artists.ALBUMS constant), and each Album
+depends on several music Tracks (see Artists.TRACKS constants).
+
+Test bundles (DM, parallel DM)
+==============================
+
+- org.apache.felix.dm.benchmark.dependencymanager:
+It contains a simple activator, which creates the various services using dependency manager API.
+
+- org.apache.felix.dm.benchmark.dependencymanager.parallel:
+same as above, but using parallel dependency manager where components dependency management and
+components activation processing is performed concurrently, using a fixed thread pool.
+
+The org.apache.felix.dm.benchmark.controller bundle, when started, first stops all tested bundles.
+Then for each one, it performs the following test (multiple times):
+
+- start the tested bundle
+- wait for all expected services to be registered (Artists/Albums/Tracks)
+- stop the tested bundle.
+- wait for all expected services to be unregistered
+
+All the elapsed time (nanoseconds) used to execute each iteration is then recorded in a list.
+When enough iterations are done, the list is sorted (that is : the first entry in the list
+corresponds to the fastest execution time, and the last entry corresponds to the slowest execution
+time). Then, we display some meaningful entries in the list (like the first entry, the entry in the
+middle of the list (average), and the last entry (slowest).
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/bnd.bnd b/dependencymanager/org.apache.felix.dependencymanager.benchmark/bnd.bnd
new file mode 100644
index 0000000..58f280a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/bnd.bnd
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+javac.source: 1.8
+javac.target: 1.8
+Bundle-Version: 1.0.0
+-buildpath: \
+ org.apache.felix.dependencymanager;version=latest,\
+ osgi.core;version=4.2,\
+ osgi.cmpn;version=4.2
+
+-sub: \
+ *.bnd
+Export-Package: \
+ org.apache.felix.dm.benchmark.scenario.impl
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/controller.bnd b/dependencymanager/org.apache.felix.dependencymanager.benchmark/controller.bnd
new file mode 100644
index 0000000..d4fcced
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/controller.bnd
@@ -0,0 +1,21 @@
+#
+# 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.
+#
+Private-Package: \
+ org.apache.felix.dm.benchmark.controller.impl
+Bundle-Activator: org.apache.felix.dm.benchmark.controller.impl.Activator
+Export-Package: \
+ org.apache.felix.dm.benchmark.controller
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/dependencymanager.bnd b/dependencymanager/org.apache.felix.dependencymanager.benchmark/dependencymanager.bnd
new file mode 100644
index 0000000..8e12db1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/dependencymanager.bnd
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+Private-Package: \
+ org.apache.felix.dm.benchmark.dependencymanager
+Bundle-Activator: org.apache.felix.dm.benchmark.dependencymanager.Activator
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/dependencymanager.parallel.bnd b/dependencymanager/org.apache.felix.dependencymanager.benchmark/dependencymanager.parallel.bnd
new file mode 100644
index 0000000..b6ce3b2
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/dependencymanager.parallel.bnd
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+Bundle-Activator: org.apache.felix.dm.benchmark.dependencymanager.ParallelActivator
+Private-Package: \
+ org.apache.felix.dm.benchmark.dependencymanager
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/index.bndrun b/dependencymanager/org.apache.felix.dependencymanager.benchmark/index.bndrun
new file mode 100644
index 0000000..a05e08e
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/index.bndrun
@@ -0,0 +1,40 @@
+#
+# 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.
+#
+-runbundles: \
+ org.apache.felix.dependencymanager;version=latest,\
+ org.apache.felix.dependencymanager.shell;version=latest,\
+ org.apache.felix.metatype;version=1.0.10,\
+ org.apache.felix.log;version=1.0.1,\
+ org.apache.felix.gogo.command;version=0.12.0,\
+ org.apache.felix.gogo.shell;version=0.10.0,\
+ org.apache.felix.gogo.runtime;version=0.10.0,\
+ org.apache.felix.configadmin;version=1.8.0,\
+ org.apache.felix.dependencymanager.benchmark.scenario,\
+ org.apache.felix.dependencymanager.benchmark.dependencymanager,\
+ org.apache.felix.dependencymanager.benchmark.dependencymanager.parallel,\
+ org.apache.felix.dependencymanager.benchmark.controller
+
+-runfw: org.apache.felix.framework;version='[4.4.0,4.4.0]'
+
+-runproperties: \
+ ds.loglevel=warn,\
+ org.osgi.framework.bootdelegation='sun.*,com.sun.*,org.netbeans.*',\
+ org.apache.felix.dependencymanager.filterindex=objectClass,id
+-runvm: -server -Xmx1024m -Xms1024m
+-runee: JavaSE-1.8
+javac.source: 1.8
+javac.target: 1.8
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/noindex.bndrun b/dependencymanager/org.apache.felix.dependencymanager.benchmark/noindex.bndrun
new file mode 100644
index 0000000..480a954
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/noindex.bndrun
@@ -0,0 +1,39 @@
+#
+# 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.
+#
+-runbundles: \
+ org.apache.felix.dependencymanager;version=latest,\
+ org.apache.felix.dependencymanager.shell;version=latest,\
+ org.apache.felix.metatype;version=1.0.10,\
+ org.apache.felix.log;version=1.0.1,\
+ org.apache.felix.gogo.command;version=0.12.0,\
+ org.apache.felix.gogo.shell;version=0.10.0,\
+ org.apache.felix.gogo.runtime;version=0.10.0,\
+ org.apache.felix.configadmin;version=1.8.0,\
+ org.apache.felix.dependencymanager.benchmark.scenario,\
+ org.apache.felix.dependencymanager.benchmark.dependencymanager,\
+ org.apache.felix.dependencymanager.benchmark.dependencymanager.parallel,\
+ org.apache.felix.dependencymanager.benchmark.controller
+
+-runfw: org.apache.felix.framework;version='[4.4.0,4.4.0]'
+
+-runproperties: \
+ ds.loglevel=warn,\
+ org.osgi.framework.bootdelegation='sun.*,com.sun.*,org.netbeans.*'
+-runvm: -server -Xmx1024m -Xms1024m
+-runee: JavaSE-1.8
+javac.source: 1.8
+javac.target: 1.8
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/scenario.bnd b/dependencymanager/org.apache.felix.dependencymanager.benchmark/scenario.bnd
new file mode 100644
index 0000000..8ede3fc
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/scenario.bnd
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+Export-Package: \
+ org.apache.felix.dm.benchmark.scenario.impl,\
+ org.apache.felix.dm.benchmark.scenario
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/.gitignore
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/controller/ScenarioController.java b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/controller/ScenarioController.java
new file mode 100644
index 0000000..bca5cf5
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/controller/ScenarioController.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.benchmark.controller;
+
+import org.apache.felix.dm.benchmark.scenario.Album;
+import org.apache.felix.dm.benchmark.scenario.Artist;
+import org.apache.felix.dm.benchmark.scenario.Track;
+
+/**
+ * This service is injected in each scenario bundle. All scenario bundle components must depend on this
+ * service, and must invoke the xxAdded() method once the component is fully initialized, and
+ * the xxRemoved() method when the component is stopped.
+ * This benchmark expect scenario bundles to register some "Artists" components. Each "Artist" component is
+ * then expected to depend on many "Albums", and each "Album" then depends on many music "Tracks".
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ScenarioController {
+ /**
+ * An Artist is added (service is started)
+ */
+ void artistAdded(Artist artist);
+
+ /**
+ * An Artist is removed (service is stopped)
+ */
+ void artistRemoved(Artist artist);
+
+ /**
+ * An Album is added (service is started)
+ */
+ void albumAdded(Album artist);
+
+ /**
+ * An Album is removed (service is stopped)
+ */
+ void albumRemoved(Album artist);
+
+ /**
+ * A Music Track is added (service is started)
+ */
+ void trackAdded(Track artist);
+
+ /**
+ * A Music Track is removed (service is stopped)
+ */
+ void trackRemoved(Track artist);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/controller/impl/Activator.java b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/controller/impl/Activator.java
new file mode 100644
index 0000000..d2e4f9a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/controller/impl/Activator.java
@@ -0,0 +1,36 @@
+/*
+ * 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.benchmark.controller.impl;
+
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+
+/**
+ * This activator triggers the scenario controller thread, which will do some microbenchmarks for a given
+ * set of scenario bundles. The controller thread is fired only once the framework is started.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext context, DependencyManager m) throws Exception {
+ m.add(createComponent().setImplementation(ScenarioControllerImpl.class));
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/controller/impl/ScenarioControllerImpl.java b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/controller/impl/ScenarioControllerImpl.java
new file mode 100644
index 0000000..a384e8a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/controller/impl/ScenarioControllerImpl.java
@@ -0,0 +1,299 @@
+/*
+ * 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.benchmark.controller.impl;
+
+import static java.lang.System.out;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toList;
+import static org.apache.felix.dm.benchmark.scenario.Artist.ALBUMS;
+import static org.apache.felix.dm.benchmark.scenario.Artist.ARTISTS;
+import static org.apache.felix.dm.benchmark.scenario.Artist.TRACKS;
+import static org.apache.felix.dm.benchmark.scenario.Helper.debug;
+
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.stream.LongStream;
+import java.util.stream.Stream;
+
+import org.apache.felix.dm.benchmark.controller.ScenarioController;
+import org.apache.felix.dm.benchmark.scenario.Album;
+import org.apache.felix.dm.benchmark.scenario.Artist;
+import org.apache.felix.dm.benchmark.scenario.Helper;
+import org.apache.felix.dm.benchmark.scenario.Track;
+import org.apache.felix.dm.benchmark.scenario.Unchecked;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The controller which perform microbenchmarks on some scenario bundles.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ScenarioControllerImpl implements Runnable, ScenarioController {
+ /**
+ * List of bundles to be executed by the benchmark.
+ */
+ final List<String> TESTS = Arrays.asList(
+ "org.apache.felix.dependencymanager.benchmark.dependencymanager",
+ "org.apache.felix.dependencymanager.benchmark.dependencymanager.parallel"
+ );
+
+ /**
+ * Our injected bundle context, used to lookup the bundles to benchmark.
+ */
+ private volatile BundleContext m_bctx;
+
+ /**
+ * Latches used to detect when expected services are registered, or unregistered.
+ */
+ private volatile CountDownLatch m_startLatch, m_stopLatch;
+
+ /**
+ * When a components is called in its start or stop method, we'll perform some processing if the following
+ * attribute is true.
+ */
+ private volatile boolean m_doProcessingInStartStop;
+
+ /**
+ * Our component is starting: we'll first stop all bundles participating in the benchmark, then we'll
+ * fire a thread, and from that thread we'll iterate on all bundles in order to do a benchmark on each.
+ * (we'll call start/stop N times, and will display the elapsed times for each bundle).
+ */
+ void start() {
+ new Thread(this).start();
+ }
+
+ void stop() {
+ }
+
+ @Override
+ public void run() {
+ // wait a bit in order to let the gogo banner be displayed before we start the bench.
+ Unchecked.run(() -> Thread.sleep(500));
+
+ out.println("Starting benchmarks (each tested bundle will add/remove " + (ARTISTS + (ARTISTS * (ALBUMS + (ALBUMS * TRACKS))))
+ + " components during bundle activation).");
+
+ // Stop all tested bundles.
+ forEachScenarioBundle(TESTS, Unchecked.consumer(bundle -> {
+ debug(() -> "Stopping bundle " + bundle.getSymbolicName());
+ bundle.stop();
+ }));
+
+ // Register our controller service
+ m_bctx.registerService(ScenarioController.class.getName(), this, null);
+
+ // Start/stop several times the tested bundles. (no processing done in components start methods).
+ m_doProcessingInStartStop = false;
+ out.println("\n\t[Starting benchmarks with no processing done in components start methods]");
+ startStopScenarioBundles(TESTS, 50);
+
+ // Start/stop several times the tested bundles (processing is done in components start methods).
+ m_doProcessingInStartStop = true;
+ out.println("\n\t[Starting benchmarks with processing done in components start methods]");
+ startStopScenarioBundles(TESTS, 5);
+ }
+
+ @Override
+ public void artistAdded(Artist artist) {
+ int size = artist.getAlbums().size();
+ if (size != Artist.ALBUMS) {
+ throw new IllegalStateException("Artist has not created expected number of albums:" + size);
+ }
+ artist.play();
+ componentAdded();
+ Helper.debug(() -> "Artist added : " + artist);
+ }
+
+ @Override
+ public void artistRemoved(Artist artist) {
+ componentRemoved();
+ Helper.debug(() -> "Artist removed : " + artist);
+ }
+
+ @Override
+ public void albumAdded(Album album) {
+ int size = album.getMusicTracks().size();
+ if (size != Artist.TRACKS) {
+ throw new IllegalStateException("Album does not contain expected number of music tracks:" + size);
+ }
+ componentAdded();
+ Helper.debug(() -> "Album added : " + album);
+ }
+
+ @Override
+ public void albumRemoved(Album album) {
+ componentRemoved();
+ Helper.debug(() -> "Album removed : " + album);
+ }
+
+ @Override
+ public void trackAdded(Track track) {
+ componentAdded();
+ Helper.debug(() -> "Track added : " + track);
+ }
+
+ @Override
+ public void trackRemoved(Track track) {
+ componentRemoved();
+ Helper.debug(() -> "Track removed : " + track);
+ }
+
+ // ------------------- Private methods -----------------------------------------------------
+
+ private void startStopScenarioBundles(List<String> tests, int iterations) {
+ forEachScenarioBundle(tests, bundle -> {
+ out.print("\nBenchmarking bundle: " + bundle.getSymbolicName() + " ");
+ List<Long> sortedResults = LongStream.range(0, iterations)
+ .peek(i -> out.print("."))
+ .map(n -> durationOf(() -> startAndStop(bundle)))
+ .sorted().boxed().collect(toList());
+ out.println();
+ displaySortedResults(sortedResults);
+ Unchecked.run(() -> Thread.sleep(500));
+ });
+ }
+
+ /**
+ * Displays meaningful values in the sorted results (first=fastest, midle=average, last entry=slowest)
+ * @param sortedResults
+ */
+ private void displaySortedResults(List<Long> sortedResults) {
+ // We don't display an average of the duration times; Instead, we sort the results,
+ // and we display the significant results (the first entry is the fastest, the middle entry is the
+ // average, the last entry is the slowest ...)
+ out.printf("-> results in nanos: [%s]%n",
+ Stream.of(0f, 24.99f, 49.99f, 74.99f, 99.99f)
+ .mapToInt(perc -> (int) (perc * sortedResults.size() / 100))
+ .mapToObj(sortedResults::get)
+ .map(this::formatNano)
+ .collect(joining(" | ")));
+ }
+
+ /**
+ * Displays a nanosecond value using thousands separator.
+ * Example: 1000000 -> 1,000,000
+ */
+ private String formatNano(Long nanoseconds) {
+ DecimalFormat formatter = (DecimalFormat) NumberFormat.getInstance(Locale.US);
+ DecimalFormatSymbols symbols = formatter.getDecimalFormatSymbols();
+ symbols.setGroupingSeparator(',');
+ return formatter.format(nanoseconds);
+ }
+
+ private void componentAdded() {
+ doProcessing();
+ m_startLatch.countDown();
+ }
+
+ private void componentRemoved() {
+ //doProcessing();
+ m_stopLatch.countDown();
+ }
+
+ private void doProcessing() {
+ if (m_doProcessingInStartStop) {
+ long duration = TimeUnit.MILLISECONDS.toNanos(ThreadLocalRandom.current().nextLong(5));
+ long t1 = System.nanoTime();
+ while (System.nanoTime() - t1 < duration)
+ ;
+ }
+ }
+
+ /**
+ * Maps a function to all bundles participating in the benchmark.
+ */
+ private void forEachScenarioBundle(List<String> tests, Consumer<Bundle> consumer) {
+ tests.stream().forEach(test -> {
+ Optional<Bundle> bundle = Stream.of(m_bctx.getBundles()).filter(b -> b.getSymbolicName().equals(test)).findFirst();
+ bundle.ifPresent(b -> {
+ consumer.accept(b);
+ });
+ });
+ }
+
+ /**
+ * This function does this:
+ *
+ * 1) start a bundle, and register the ScenarioController service (this will trigger all components activation)
+ * 2) wait for all expected components to be fully started
+ * 3) stop the bundle and wait for all expected components to be fully stopped
+ *
+ * @param b the benchmarked scenario bundle
+ */
+ void startAndStop(Bundle b) {
+ try {
+ initLatches();
+
+ debug(() -> "starting bundle " + b.getSymbolicName());
+ b.start();
+
+ if (! m_startLatch.await(60, TimeUnit.SECONDS)) {
+ out.println("Could not start components timely: current start latch=" + m_startLatch.getCount() + ", stop latch=" + m_stopLatch.getCount());
+ Unchecked.run(() -> Thread.sleep(Integer.MAX_VALUE));
+ }
+
+ debug(() -> "stopping bundle " + b.getSymbolicName());
+ b.stop();
+
+ // Wait for all component deactivations
+ if (! m_stopLatch.await(60, TimeUnit.SECONDS)) {
+ out.println("Could not stop components timely: current start latch=" + m_startLatch.getCount() + ", stop latch=" + m_stopLatch.getCount());
+ Unchecked.run(() -> Thread.sleep(Integer.MAX_VALUE));
+ }
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+
+ /**
+ * Initialize the latches used to track when all scenario bundle components are started or stopped.
+ */
+ private void initLatches() {
+ m_startLatch = new CountDownLatch(ARTISTS
+ + (ARTISTS * (ALBUMS + (ALBUMS * TRACKS))));
+
+ m_stopLatch = new CountDownLatch(ARTISTS
+ + (ARTISTS * (ALBUMS + (ALBUMS * TRACKS))));
+ }
+
+ /**
+ * Returns the time consumed by the given runnable, ²ch is executed by this method.
+ */
+ private long durationOf(Runnable scenario) {
+ long start = System.nanoTime();
+ long end = 0;
+ try {
+ scenario.run();
+ end = System.nanoTime();
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ return (end - start);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/controller/packageinfo b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/controller/packageinfo
new file mode 100644
index 0000000..a4f1546
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/controller/packageinfo
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/dependencymanager/Activator.java b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/dependencymanager/Activator.java
new file mode 100644
index 0000000..740fcf2
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/dependencymanager/Activator.java
@@ -0,0 +1,39 @@
+/*
+ * 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.benchmark.dependencymanager;
+
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.benchmark.controller.ScenarioController;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Activator for a scenario based on Dependency Manager 4.0
+ * We'll create many Artists, each one is depending on many Albums, and each Album depends on many Tracks.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext context, DependencyManager dm) throws Exception {
+ dm.add(createComponent()
+ .setImplementation(Benchmark.class)
+ .add(createServiceDependency().setService(ScenarioController.class).setRequired(true)));
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/dependencymanager/Benchmark.java b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/dependencymanager/Benchmark.java
new file mode 100644
index 0000000..10384fb
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/dependencymanager/Benchmark.java
@@ -0,0 +1,108 @@
+/*
+ * 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.benchmark.dependencymanager;
+
+import static org.apache.felix.dm.benchmark.scenario.Artist.ALBUMS;
+import static org.apache.felix.dm.benchmark.scenario.Artist.ARTISTS;
+import static org.apache.felix.dm.benchmark.scenario.Artist.TRACKS;
+
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.benchmark.controller.ScenarioController;
+import org.apache.felix.dm.benchmark.scenario.Album;
+import org.apache.felix.dm.benchmark.scenario.Artist;
+import org.apache.felix.dm.benchmark.scenario.Helper;
+import org.apache.felix.dm.benchmark.scenario.Track;
+import org.apache.felix.dm.benchmark.scenario.impl.AlbumImpl;
+import org.apache.felix.dm.benchmark.scenario.impl.ArtistImpl;
+import org.apache.felix.dm.benchmark.scenario.impl.TrackImpl;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Benchmark {
+ volatile DependencyManager m_dm;
+ volatile ScenarioController m_controller;
+ final List<Component> m_components = new ArrayList<>();
+
+ /**
+ * Initialize our Artists, Albums/Tracks, possibly using a parallel dependency manager.
+ */
+ @SuppressWarnings("unused")
+ private void start() {
+ Helper.debug(() -> "Benchmark.start");
+
+ IntStream.range(0, ARTISTS)
+ .mapToObj(i -> createArtists(m_dm)).peek(m_components::add)
+ .flatMap(artist -> createAlbums(m_dm, artist)).peek(m_components::add)
+ .flatMap(album -> createTracks(m_dm, album)).forEach(m_components::add);
+
+ m_components.stream().forEach(m_dm::add);
+ }
+
+ @SuppressWarnings("unused")
+ private void stop() {
+ m_components.forEach(m_dm::remove);
+ }
+
+ private Component createArtists(DependencyManager dm) {
+ Component artist = dm.createComponent()
+ .setInterface(Artist.class.getName(), null)
+ .setImplementation(new ArtistImpl(m_controller));
+ return artist;
+ }
+
+ private Stream<Component> createAlbums(DependencyManager dm, Component artist) {
+ return IntStream.range(0, ALBUMS).mapToObj(i -> {
+ long id = Helper.generateId();
+ String filter = "(id=" + id + ")";
+ artist.add(dm.createServiceDependency()
+ .setService(Album.class, filter).setRequired(true).setCallbacks("addAlbum", null));
+
+ Hashtable<String, Object> props = new Hashtable<>();
+ props.put("id", String.valueOf(id));
+ Component c = dm.createComponent()
+ .setInterface(Album.class.getName(), props)
+ .setImplementation(new AlbumImpl(m_controller));
+ return c;
+ });
+ }
+
+ private Stream<Component> createTracks(DependencyManager dm, Component album) {
+ return IntStream.range(0, TRACKS).mapToObj(i -> {
+ long id = Helper.generateId();
+ String f = "(id=" + String.valueOf(id) + ")";
+ album.add(dm.createServiceDependency()
+ .setService(Track.class, f).setRequired(true).setCallbacks("addTrack", null));
+
+ Hashtable<String, Object> p = new Hashtable<>();
+ p.put("id", String.valueOf(id));
+ Component c = dm.createComponent()
+ .setInterface(Track.class.getName(), p)
+ .setImplementation(new TrackImpl(m_controller));
+ return c;
+ });
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/dependencymanager/ParallelActivator.java b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/dependencymanager/ParallelActivator.java
new file mode 100644
index 0000000..cdcb6b3
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/dependencymanager/ParallelActivator.java
@@ -0,0 +1,44 @@
+/*
+ * 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.benchmark.dependencymanager;
+
+import java.util.concurrent.Executor;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentExecutorFactory;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.benchmark.scenario.Helper;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Parallel version of our default Activator.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ParallelActivator extends Activator {
+ public void init(BundleContext context, DependencyManager mgr) throws Exception {
+ context.registerService(ComponentExecutorFactory.class.getName(), new ComponentExecutorFactory() {
+ @Override
+ public Executor getExecutorFor(Component component) {
+ return Helper.getThreadPool(); // Return our thread pool shared for all components
+ }
+ }, null);
+ super.init(context, mgr);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/Album.java b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/Album.java
new file mode 100644
index 0000000..1d6ac0c
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/Album.java
@@ -0,0 +1,38 @@
+/*
+ * 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.benchmark.scenario;
+
+import java.util.List;
+
+/**
+ * A single release of musics, comprising some music tracks.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface Album {
+ /**
+ * Returns the music tracks this Album is comprising.
+ */
+ List<Track> getMusicTracks();
+
+ /**
+ * Play all tracks from all albums.
+ */
+ void play();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/Artist.java b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/Artist.java
new file mode 100644
index 0000000..eb78702
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/Artist.java
@@ -0,0 +1,56 @@
+/*
+ * 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.benchmark.scenario;
+
+import java.util.List;
+
+/**
+ * An individual who creates musical Albums
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface Artist {
+ /**
+ * When a scenario bundles starts, it creates the following number of Artists (service)
+ * (you have to regenerate the SCR xml descriptor if you modify this, see README)
+ */
+ public final int ARTISTS = 30;
+
+ /**
+ * Each Artist creates the following number of musical Albums.
+ * (you have to regenerate the SCR xml descriptor if you modify this, see README)
+ */
+ public final int ALBUMS = 5;
+
+ /**
+ * Each Album contains the following number of musical Tracks.
+ * (you have to regenerate the SCR xml descriptor if you modify this, see README)
+ */
+ public final int TRACKS = 3;
+
+ /**
+ * Returns the Albums that this Artist has created
+ */
+ List<Album> getAlbums();
+
+ /**
+ * Play all tracks from all albums (this test method invocation time between services).
+ */
+ void play();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/Helper.java b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/Helper.java
new file mode 100644
index 0000000..9ada693
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/Helper.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.benchmark.scenario;
+
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+
+/**
+ * Helper class containing misc functions, and constants.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Helper {
+ /**
+ * Activate this flag for debugging.
+ */
+ private final static boolean DEBUG = false;
+
+ /**
+ * Generator used to create unique identifiers.
+ */
+ private final static AtomicLong m_idGenerator = new AtomicLong();
+
+ /**
+ * Threadpool which can be optionally used by parallel scenarios.
+ */
+ private final static int CORES = Runtime.getRuntime().availableProcessors();
+ private final static ForkJoinPool TPOOL = new ForkJoinPool(CORES);
+
+ /**
+ * Get the threadpool, possibly needed by some scenario supporting parallel mode
+ */
+ public static ForkJoinPool getThreadPool() {
+ return TPOOL;
+ }
+
+ /**
+ * Display some debug messages.
+ */
+ public static void debug(Supplier<String> message) {
+ if (DEBUG) {
+ System.out.println(message.get());
+ }
+ }
+
+ /**
+ * Generates a unique id.
+ */
+ public static long generateId() {
+ return m_idGenerator.incrementAndGet();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/Track.java b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/Track.java
new file mode 100644
index 0000000..0e4d414
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/Track.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.benchmark.scenario;
+
+/**
+ * A piece of music
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface Track {
+ /**
+ * Play this single piece of music.
+ */
+ void play();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/Unchecked.java b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/Unchecked.java
new file mode 100644
index 0000000..c9849d9
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/Unchecked.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.benchmark.scenario;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * Helper functions used to work around the java.util.function.* functions, which don't support
+ * methods throwing a checked exception.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Unchecked {
+ /**
+ * Same functional interface as java.util.function.Consumer, except that the accept method may throw an exception.
+ */
+ @FunctionalInterface
+ public static interface CheckedConsumer<T> {
+ public void accept(T t) throws Exception;
+ }
+
+ /**
+ * Same interface as Runnable, except that the run method may throw an exception.
+ */
+ @FunctionalInterface
+ public static interface CheckedRunnable {
+ public void run() throws Exception;
+ }
+
+ /**
+ * Same interface as Function, except that the accept method may throw an exception.
+ */
+ @FunctionalInterface
+ public static interface CheckedFunction<T,U> {
+ public U apply(T t) throws Exception;
+ }
+
+ /**
+ * Wraps a Consumer whose accept method may throw an exception behind a regular java.util.function.Consumer
+ */
+ public static <T> Consumer<T> consumer(CheckedConsumer<T> c) {
+ return (t) -> {
+ try {
+ c.accept(t);
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ catch (Throwable err) {
+ throw err;
+ }
+ };
+ }
+
+ /**
+ * Wraps a Consumer whose accept method may throw an exception behind a regular java.util.function.Consumer
+ */
+ public static <T,U> Function<T, U> func(CheckedFunction<T, U> f) {
+ return (t) -> {
+ try {
+ return f.apply(t);
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ catch (Throwable err) {
+ throw err;
+ }
+ };
+ }
+
+ /**
+ * Runs a runnable which may throw an exception without having to catch it.
+ */
+ public static void run(CheckedRunnable r) {
+ try {
+ r.run();
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ catch (Throwable err) {
+ throw err;
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/impl/AlbumImpl.java b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/impl/AlbumImpl.java
new file mode 100644
index 0000000..1de8bb7
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/impl/AlbumImpl.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.benchmark.scenario.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.felix.dm.benchmark.controller.ScenarioController;
+import org.apache.felix.dm.benchmark.scenario.Album;
+import org.apache.felix.dm.benchmark.scenario.Track;
+
+/**
+ * An album comprising several music tracks.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AlbumImpl implements Album {
+ final List<Track> m_musicTracks = new ArrayList<>();
+ final ScenarioController m_controller;
+
+ public AlbumImpl(ScenarioController controller) {
+ m_controller = controller;
+ }
+
+ void addTrack(Track dep) {
+ m_musicTracks.add(dep);
+ }
+
+ void start() {
+ m_controller.albumAdded(this);
+ }
+
+ void stop() {
+ m_controller.albumRemoved(this);
+ }
+
+ @Override
+ public List<Track> getMusicTracks() {
+ return m_musicTracks;
+ }
+
+ @Override
+ public void play() {
+ for (Track track : m_musicTracks) {
+ track.play();
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/impl/ArtistImpl.java b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/impl/ArtistImpl.java
new file mode 100644
index 0000000..2bfbc28
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/impl/ArtistImpl.java
@@ -0,0 +1,63 @@
+/*
+ * 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.benchmark.scenario.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.felix.dm.benchmark.controller.ScenarioController;
+import org.apache.felix.dm.benchmark.scenario.Album;
+import org.apache.felix.dm.benchmark.scenario.Artist;
+
+/**
+ * One artist who depends on multiple Albums.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ArtistImpl implements Artist {
+ final List<Album> m_albums = new ArrayList<>();
+ final ScenarioController m_controller;
+
+ public ArtistImpl(ScenarioController controller) {
+ m_controller = controller;
+ }
+
+ void addAlbum(Album dep) {
+ m_albums.add(dep);
+ }
+
+ void start() {
+ m_controller.artistAdded(this);
+ }
+
+ void stop() {
+ m_controller.artistRemoved(this);
+ }
+
+ @Override
+ public List<Album> getAlbums() {
+ return m_albums;
+ }
+
+ public void play() {
+ for (Album album : m_albums) {
+ album.play();
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/impl/TrackImpl.java b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/impl/TrackImpl.java
new file mode 100644
index 0000000..3115b3b
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/impl/TrackImpl.java
@@ -0,0 +1,47 @@
+/*
+ * 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.benchmark.scenario.impl;
+
+import org.apache.felix.dm.benchmark.controller.ScenarioController;
+import org.apache.felix.dm.benchmark.scenario.Track;
+
+/**
+ * One single music.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class TrackImpl implements Track {
+ final ScenarioController m_controller;
+
+ public TrackImpl(ScenarioController controller) {
+ m_controller = controller;
+ }
+
+ void start() {
+ m_controller.trackAdded(this);
+ }
+
+ void stop() {
+ m_controller.trackRemoved(this);
+ }
+
+ @Override
+ public void play() {
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/impl/packageinfo b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/impl/packageinfo
new file mode 100644
index 0000000..a4f1546
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/impl/packageinfo
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/packageinfo b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/packageinfo
new file mode 100644
index 0000000..a4f1546
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/scenario/packageinfo
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/test/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.benchmark/test/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/test/.gitignore
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/.classpath b/dependencymanager/org.apache.felix.dependencymanager.itest/.classpath
new file mode 100644
index 0000000..f89ae43
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/.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.itest/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.itest/.gitignore
new file mode 100644
index 0000000..90dde36
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/.gitignore
@@ -0,0 +1,3 @@
+/bin/
+/bin_test/
+/generated/
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/.project b/dependencymanager/org.apache.felix.dependencymanager.itest/.project
new file mode 100644
index 0000000..9aaf1ea
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.apache.felix.dependencymanager.itest</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.itest/.settings/org.eclipse.jdt.core.prefs b/dependencymanager/org.apache.felix.dependencymanager.itest/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..7341ab1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/.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.itest/api.bnd b/dependencymanager/org.apache.felix.dependencymanager.itest/api.bnd
new file mode 100644
index 0000000..6e0cfca
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/api.bnd
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+Private-Package: \
+ org.apache.felix.dm.itest.api
+Export-Package: \
+ org.apache.felix.dm.itest.util
+Bundle-Name: Apache Felix Dependency Manager integration tests
+Bundle-Description: Integration tests for Apache Felix Dependency Manager
+Bundle-Category: test
+Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+Bundle-Vendor: The Apache Software Foundation
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/bnd.bnd b/dependencymanager/org.apache.felix.dependencymanager.itest/bnd.bnd
new file mode 100644
index 0000000..4ffc8c7
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/bnd.bnd
@@ -0,0 +1,39 @@
+#
+# 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.
+#
+-runbundles: \
+ org.apache.felix.metatype;version=1.0.4,\
+ org.apache.felix.gogo.runtime;version=0.10.0,\
+ org.apache.felix.log;version=1.0.1,\
+ org.apache.felix.configadmin;version=1.8.1.SNAPSHOT,\
+ org.apache.felix.dependencymanager;version=latest,\
+ org.apache.felix.dependencymanager.shell;version=latest
+-runee: JavaSE-1.7
+-runvm: -ea
+-runfw: org.apache.felix.framework
+-buildpath: \
+ osgi.core;version=4.2,\
+ osgi.cmpn;version=4.2,\
+ org.apache.felix.gogo.runtime;version=0.10,\
+ org.apache.felix.dependencymanager;version=latest,\
+ org.apache.felix.dependencymanager.shell;version=latest,\
+ ${junit}
+-runsystempackages: \
+ sun.reflect
+-sub: \
+ *.bnd
+Test-Cases: \
+ ${classes;CONCRETE;EXTENDS;junit.framework.TestCase}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/bundle.bnd b/dependencymanager/org.apache.felix.dependencymanager.itest/bundle.bnd
new file mode 100644
index 0000000..c2f69fa
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/bundle.bnd
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+Export-Package: \
+ org.apache.felix.dm.itest.bundle
+Bundle-Activator: org.apache.felix.dm.itest.bundle.Activator
+Bundle-Name: Apache Felix Dependency Manager integration test bundle
+Bundle-Description: Internal test bundle used by Apache Felix Dependency Manager integration \
+ tests
+Bundle-Category: test
+Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+Bundle-Vendor: The Apache Software Foundation
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AbstractServiceDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AbstractServiceDependencyTest.java
new file mode 100644
index 0000000..fb0bbe0
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AbstractServiceDependencyTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.itest.api;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AbstractServiceDependencyTest extends TestBase {
+ public void testAbstractClassDependency() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ Component sp = m.createComponent()
+ .setInterface(ServiceAbstract.class.getName(), null)
+ .setImplementation(new ServiceProvider(e))
+ ;
+ Component sc = m.createComponent()
+ .setImplementation(new ServiceConsumer(e))
+ .add(m.createServiceDependency()
+ .setService(ServiceAbstract.class)
+ .setRequired(true)
+ .setCallbacks("bind", "unbind")
+ );
+ m.add(sp);
+ m.add(sc);
+ m.remove(sp);
+ // ensure we executed all steps inside the component instance
+ e.step(8);
+ m.clear();
+ }
+
+ static abstract class ServiceAbstract {
+ public abstract void invoke();
+ }
+
+ static class ServiceProvider extends ServiceAbstract {
+ private final Ensure m_ensure;
+ public ServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void start() {
+ m_ensure.step(1);
+ }
+
+ public void invoke() {
+ m_ensure.step(4);
+ }
+
+ public void stop() {
+ m_ensure.step(7);
+ }
+ }
+
+ static class ServiceConsumer {
+ private volatile ServiceAbstract m_service;
+ private final Ensure m_ensure;
+
+ public ServiceConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void bind(ServiceAbstract service) {
+ m_ensure.step(2);
+ m_service = service;
+ }
+
+ public void start() {
+ m_ensure.step(3);
+ m_service.invoke();
+ }
+
+ public void stop() {
+ m_ensure.step(5);
+ }
+
+ public void unbind(ServiceAbstract service) {
+ System.out.println("UNBINDDDDDDDDDDDDDDDDDDDDDDDDDDD");
+ Assert.assertEquals(m_service, service);
+ m_ensure.step(6);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterAndConsumerTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterAndConsumerTest.java
new file mode 100644
index 0000000..93949ee
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterAndConsumerTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterAndConsumerTest extends TestBase {
+
+ public void testServiceWithAdapterAndConsumer() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+
+ Component provider = m.createComponent()
+ .setInterface(OriginalService.class.getName(), null)
+ .setImplementation(new ServiceProvider(e));
+
+ Component consumer = m.createComponent()
+ .setImplementation(new ServiceConsumer(e))
+ .add(m.createServiceDependency()
+ .setService(AdaptedService.class)
+ .setRequired(true)
+ );
+
+ Component adapter = m.createAdapterService(OriginalService.class, null)
+ .setInterface(AdaptedService.class.getName(), null)
+ .setImplementation(ServiceAdapter.class);
+
+ // add the provider and the adapter
+ m.add(provider);
+ m.add(adapter);
+ // add a consumer that will invoke the adapter
+ // which will in turn invoke the original provider
+ m.add(consumer);
+ // now validate that both have been invoked in the right order
+ e.waitForStep(2, 5000);
+ // remove the provider again
+ m.remove(provider);
+ // ensure that the consumer is stopped
+ e.waitForStep(3, 5000);
+ // remove adapter and consumer
+ m.remove(adapter);
+ m.remove(consumer);
+ }
+
+ static interface OriginalService {
+ public void invoke();
+ }
+
+ static interface AdaptedService {
+ public void invoke();
+ }
+
+ static class ServiceProvider implements OriginalService {
+ private final Ensure m_ensure;
+ public ServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+ public void invoke() {
+ m_ensure.step(2);
+ }
+ }
+
+ public static class ServiceAdapter implements AdaptedService {
+ private volatile OriginalService m_originalService;
+
+ public void start() { System.out.println("start"); }
+ public void stop() { System.out.println("stop"); }
+ public void invoke() {
+ m_originalService.invoke();
+ }
+ }
+
+ static class ServiceConsumer {
+ private volatile AdaptedService m_service;
+ private final Ensure m_ensure;
+
+ public ServiceConsumer(Ensure e) {
+ m_ensure = e;
+ }
+ public void start() {
+ m_ensure.step(1);
+ m_service.invoke();
+ }
+ public void stop() {
+ m_ensure.step(3);
+ }
+ }
+}
+
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithCallbackInstanceTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithCallbackInstanceTest.java
new file mode 100644
index 0000000..a7073f5
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithCallbackInstanceTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.itest.api;
+
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterWithCallbackInstanceTest extends TestBase {
+
+ public void testServiceWithAdapterAndConsumer() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+
+ ServiceProvider serviceProvider = new ServiceProvider(e);
+ Component provider = m.createComponent()
+ .setInterface(OriginalService.class.getName(), null)
+ .setImplementation(serviceProvider);
+
+ Component consumer = m.createComponent()
+ .setImplementation(new ServiceConsumer(e))
+ .add(m.createServiceDependency()
+ .setService(AdaptedService.class)
+ .setRequired(true)
+ );
+
+ ServiceAdapterCallbackInstance callbackInstance = new ServiceAdapterCallbackInstance(e);
+ Component adapter = m.createAdapterService(OriginalService.class, null, "m_originalService",
+ callbackInstance, "set", "changed","unset", null, true)
+ .setInterface(AdaptedService.class.getName(), null)
+ .setImplementation(new ServiceAdapter(e));
+
+ // add the provider and the adapter
+ m.add(provider);
+ m.add(adapter);
+ // Checks if the callbackInstances is called, and if the adapter start method is called
+ e.waitForStep(2, 5000);
+
+ // add a consumer that will invoke the adapter
+ // which will in turn invoke the original provider
+ m.add(consumer);
+ // now validate that both have been invoked in the right order
+ e.waitForStep(4, 5000);
+
+ // change the service properties of the provider, and check that the adapter callback instance is changed.
+ serviceProvider.changeServiceProperties();
+ e.waitForStep(5, 5000);
+
+ // remove the provider
+ m.remove(provider);
+ // ensure that the consumer is stopped, the adapter callback is called in its unset method, and the adapter is stopped.
+ e.waitForStep(8, 5000);
+ // remove adapter and consumer
+ m.remove(adapter);
+ m.remove(consumer);
+ }
+
+ static interface OriginalService {
+ public void invoke();
+ }
+
+ static interface AdaptedService {
+ public void invoke();
+ }
+
+ static class ServiceProvider implements OriginalService {
+ private final Ensure m_ensure;
+ private volatile ServiceRegistration m_registration; // auto injected when started.
+ public ServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+ public void changeServiceProperties() {
+ Hashtable<String, String> props = new Hashtable<>();
+ props.put("foo", "bar");
+ m_registration.setProperties(props);
+ }
+ public void invoke() {
+ m_ensure.step(4);
+ }
+ }
+
+ public static class ServiceAdapter implements AdaptedService {
+ private volatile OriginalService m_originalService;
+ private final Ensure m_ensure;
+
+ public ServiceAdapter(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void start() { m_ensure.step(2); }
+ public void stop() { m_ensure.step(7); }
+ public void invoke() {
+ m_originalService.invoke();
+ }
+ }
+
+ public static class ServiceAdapterCallbackInstance {
+ private final Ensure m_ensure;
+ public ServiceAdapterCallbackInstance(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void set(OriginalService m_originalService) {
+ m_ensure.step(1);
+ }
+
+ public void changed(Map<String, String> props, OriginalService m_originalService) {
+ Assert.assertEquals("bar", props.get("foo"));
+ m_ensure.step(5);
+ }
+
+ public void unset(Map<String, String> props, OriginalService m_originalService) {
+ m_ensure.step(8);
+ }
+ }
+
+ static class ServiceConsumer {
+ private volatile AdaptedService m_service;
+ private final Ensure m_ensure;
+
+ public ServiceConsumer(Ensure e) {
+ m_ensure = e;
+ }
+ public void start() {
+ m_ensure.step(3);
+ m_service.invoke();
+ }
+ public void stop() {
+ m_ensure.step(6);
+ }
+ }
+}
+
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithConfigurationAndMetaType.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithConfigurationAndMetaType.java
new file mode 100644
index 0000000..68eedd6
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithConfigurationAndMetaType.java
@@ -0,0 +1,183 @@
+/*
+ * 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.itest.api;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.metatype.AttributeDefinition;
+import org.osgi.service.metatype.MetaTypeInformation;
+import org.osgi.service.metatype.MetaTypeService;
+import org.osgi.service.metatype.ObjectClassDefinition;
+
+/**
+ * Tests an Adapter which adapts A To B interface.
+ * And the Adapter also depends on a Configuration Dependency with MetaType support.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterWithConfigurationAndMetaType extends TestBase {
+ final static String PID = "AdapterWithConfigurationAndMetaType";
+ final static String PID_HEADING = "English Dictionary";
+ final static String PID_DESC = "Configuration for the EnglishDictionary Service";
+ final static String WORDS_HEADING = "English words";
+ final static String WORDS_DESC = "Declare here some valid English words";
+ final static String WORDS_PROPERTY = "words";
+
+ public void testAdapterWithConfigurationDependencyAndMetaType() {
+ DependencyManager m = getDM();
+ Ensure e = new Ensure();
+
+ m.add(m.createAdapterService(A.class, null)
+ .setInterface(B.class.getName(), null)
+ .setImplementation(new BImpl(e))
+ .add(m.createConfigurationDependency()
+ .setPid(PID)
+ .setHeading(PID_HEADING)
+ .setDescription(PID_DESC)
+ .add(m.createPropertyMetaData()
+ .setCardinality(Integer.MAX_VALUE)
+ .setType(String.class)
+ .setHeading(WORDS_HEADING)
+ .setDescription(WORDS_DESC)
+ .setDefaults(new String[] {"hello", "world"})
+ .setId(WORDS_PROPERTY))));
+
+ m.add(m.createComponent()
+ .setInterface(A.class.getName(), null)
+ .setImplementation(new AImpl()));
+
+ Component configurator = m.createComponent()
+ .setImplementation(new Configurator(e))
+ .add(m.createServiceDependency().setService(ConfigurationAdmin.class).setRequired(true))
+ .add(m.createServiceDependency().setService(MetaTypeService.class).setRequired(true));
+ m.add(configurator);
+
+ // Ensures that all components are started
+ e.waitForStep(4, 5000);
+
+ // now stop configurator, and ensure that all components have been stopped
+ m.remove(configurator);
+ e.waitForStep(7, 5000);
+ m.clear();
+ }
+
+ public interface A {
+ }
+
+ public interface B {
+ }
+
+ public class AImpl implements A {
+ }
+
+ public class Configurator {
+ volatile MetaTypeService m_metaType;
+ volatile ConfigurationAdmin m_cm;
+ volatile BundleContext m_ctx;
+ final Ensure m_ensure;
+ Configuration m_conf;
+
+ Configurator(Ensure ensure) {
+ m_ensure = ensure;
+ }
+
+ void start() {
+ m_ensure.step(1);
+ checkMetaTypeAndConfigure();
+ }
+
+ void stop() {
+ m_ensure.step(5);
+ if (m_conf != null) {
+ try {
+ m_ensure.step(6);
+ m_conf.delete();
+ }
+ catch (IOException e) {
+ m_ensure.throwable(e);
+ }
+ }
+ }
+
+ void checkMetaTypeAndConfigure() {
+ MetaTypeInformation info = m_metaType.getMetaTypeInformation(m_ctx.getBundle());
+ Assert.assertNotNull(info);
+ Assert.assertEquals(PID, info.getPids()[0]);
+ ObjectClassDefinition ocd = info.getObjectClassDefinition(PID, null);
+ Assert.assertNotNull(ocd);
+ Assert.assertEquals(PID_HEADING, ocd.getName());
+ Assert.assertEquals(PID_DESC, ocd.getDescription());
+ AttributeDefinition[] defs = ocd.getAttributeDefinitions(ObjectClassDefinition.ALL);
+ Assert.assertNotNull(defs);
+ Assert.assertEquals(1, defs.length);
+ Assert.assertEquals(WORDS_HEADING, defs[0].getName());
+ Assert.assertEquals(WORDS_DESC, defs[0].getDescription());
+ Assert.assertEquals(WORDS_PROPERTY, defs[0].getID());
+ m_ensure.step(2);
+
+ try {
+ m_conf = m_cm.getConfiguration(PID, null);
+ Hashtable<String, String> props = new Hashtable<>();
+ props.put("foo", "bar");
+ m_conf.update(props);
+ } catch (Throwable t) {
+ m_ensure.throwable(t);
+ }
+ }
+ }
+
+ public class BImpl implements B, A {
+ final Ensure m_ensure;
+ volatile A m_a;
+ Dictionary<String, String> m_conf;
+
+ public BImpl(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void updated(Dictionary<String, String> conf) {
+ if (conf != null) {
+ m_ensure.step(3);
+ m_conf = conf;
+ }
+ }
+
+ public void start() {
+ Assert.assertNotNull(m_a);
+ Assert.assertNotNull(m_conf);
+ Assert.assertEquals("bar", m_conf.get("foo"));
+ m_ensure.step(4);
+ }
+
+ public void stop() {
+ m_ensure.step(7);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithExtraDependenciesTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithExtraDependenciesTest.java
new file mode 100644
index 0000000..a0c6fb1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithExtraDependenciesTest.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.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterWithExtraDependenciesTest extends TestBase {
+ public void testAdapterWithExtraDependenciesAndCallbacks() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+
+ // create a service adapter that adapts to services S1 and has an optional dependency on services S2
+ Component sa = m.createAdapterService(S1.class, null)
+ .setImplementation(SA.class)
+ .add(m.createServiceDependency().setService(S2.class).setCallbacks("add", "remove"));
+ m.add(sa);
+
+ // create a service S1, which triggers the creation of the first adapter instance (A1)
+ Component s1 = m.createComponent().setInterface(S1.class.getName(), null).setImplementation(new S1Impl());
+ m.add(s1);
+
+ // create a service S2, which will be added to A1
+ Component s2 = m.createComponent().setInterface(S2.class.getName(), null).setImplementation(new S2Impl(e));
+ m.add(s2);
+
+ // create a second service S1, which triggers the creation of the second adapter instance (A2)
+ Component s1b = m.createComponent().setInterface(S1.class.getName(), null).setImplementation(new S1Impl());
+ m.add(s1b);
+
+ // observe that S2 is also added to A2
+ e.waitForStep(2, 5000);
+
+ // remove S2 again
+ m.remove(s2);
+
+ // make sure both adapters have their "remove" callbacks invoked
+ e.waitForStep(4, 5000);
+
+ m.remove(s1);
+ m.remove(sa);
+ m.clear();
+ }
+
+ static interface S1 {
+ }
+ static interface S2 {
+ public void invoke();
+ }
+ static class S1Impl implements S1 {
+ }
+ static class S2Impl implements S2 {
+
+ private final Ensure m_e;
+
+ public S2Impl(Ensure e) {
+ m_e = e;
+ }
+
+ public void invoke() {
+ m_e.step();
+ }
+ }
+
+ public static class SA {
+ volatile S2 s2;
+
+ public SA() {
+ System.out.println("Adapter created");
+ }
+ public void init() {
+ System.out.println("Adapter init " + s2);
+ }
+ public void add(S2 s) {
+ System.out.println("adding " + s);
+ s.invoke();
+ }
+ public void remove(S2 s) {
+ System.out.println("removing " + s);
+ s.invoke();
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithInstanceBoundDependencyParallelTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithInstanceBoundDependencyParallelTest.java
new file mode 100644
index 0000000..4fe3edd
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithInstanceBoundDependencyParallelTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.itest.api;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterWithInstanceBoundDependencyParallelTest extends AdapterWithInstanceBoundDependencyTest {
+ public AdapterWithInstanceBoundDependencyParallelTest() {
+ setParallel();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithInstanceBoundDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithInstanceBoundDependencyTest.java
new file mode 100644
index 0000000..77645a3
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithInstanceBoundDependencyTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterWithInstanceBoundDependencyTest extends TestBase {
+ public void testInstanceBoundDependency() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ Component sp = m.createComponent()
+ .setInterface(ServiceInterface.class.getName(), null)
+ .setImplementation(new ServiceProvider(e));
+ Component sp2 = m.createComponent()
+ .setInterface(ServiceInterface2.class.getName(), null)
+ .setImplementation(new ServiceProvider2(e));
+ Component sc = m.createComponent()
+ .setImplementation(new ServiceConsumer(e))
+ .add(m.createServiceDependency()
+ .setService(ServiceInterface3.class)
+ .setRequired(true));
+ Component sa = m.createAdapterService(ServiceInterface.class, null)
+ .setInterface(ServiceInterface3.class.getName(), null)
+ .setImplementation(new ServiceAdapter(e));
+ m.add(sc);
+ m.add(sp);
+ m.add(sp2);
+ m.add(sa);
+ e.waitForStep(5, 15000);
+ // cleanup
+ m.remove(sa);
+ m.remove(sp2);
+ m.remove(sp);
+ m.remove(sc);
+ m.clear();
+ e.waitForStep(9, 5000); // make sure all components are stopped
+ }
+
+ static interface ServiceInterface {
+ public void invoke();
+ }
+
+ static interface ServiceInterface2 {
+ public void invoke();
+ }
+
+ static interface ServiceInterface3 {
+ public void invoke();
+ }
+
+ static class ServiceProvider2 implements ServiceInterface2 {
+ private final Ensure m_ensure;
+
+ public ServiceProvider2(Ensure ensure) {
+ m_ensure = ensure;
+ }
+
+ public void invoke() {
+ m_ensure.step(4);
+ }
+
+ public void stop() {
+ m_ensure.step();
+ }
+ }
+
+ static class ServiceProvider implements ServiceInterface {
+ private final Ensure m_ensure;
+ public ServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+ public void invoke() {
+ m_ensure.step(5);
+ }
+ public void stop() {
+ m_ensure.step();
+ }
+ }
+
+ static class ServiceAdapter implements ServiceInterface3 {
+ private Ensure m_ensure;
+ private volatile ServiceInterface m_originalService;
+ private volatile ServiceInterface2 m_injectedService;
+ private volatile Component m_service;
+ private volatile DependencyManager m_manager;
+
+ public ServiceAdapter(Ensure e) {
+ m_ensure = e;
+ }
+ public void init() {
+ m_ensure.step(1);
+ m_service.add(m_manager.createServiceDependency().setRequired(true).setService(ServiceInterface2.class));
+ }
+ public void start() {
+ m_ensure.step(2);
+ }
+ public void invoke() {
+ m_ensure.step(3);
+ m_injectedService.invoke();
+ m_originalService.invoke();
+ }
+
+ public void stop() {
+ m_ensure.step();
+ }
+ }
+
+ static class ServiceConsumer implements Runnable {
+ volatile ServiceInterface3 m_service;
+ final Ensure m_ensure;
+
+ ServiceConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void init() {
+ Thread t = new Thread(this);
+ t.start();
+ }
+
+ public void run() {
+ m_service.invoke();
+ }
+ public void stop() {
+ m_ensure.step();
+ }
+ }
+}
+
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithModifiedInstanceBoundDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithModifiedInstanceBoundDependencyTest.java
new file mode 100644
index 0000000..46ecf74
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithModifiedInstanceBoundDependencyTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.itest.api;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * Test for FELIX-4334 issue.
+ *
+ * Three components: A, B and C
+ *
+ * - A provided with property foo=bar
+ * - B adapts A, B has no filters on A, and B.init() method adds an instance bound required dependency to C.
+ * - C depends on A(foo=bar)
+ * - Now someone modifies the service properties of A: foo=bar2
+ * - As a result of that, C becomes unavailable and is unbound from B.
+ * - Since B has an instance bound required dependency to C: B should not be destroyed: it should be called in B.stop(), B.remove(C), B.change(A, "foo=bar2))
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class AdapterWithModifiedInstanceBoundDependencyTest extends TestBase {
+ public static interface A {
+ }
+
+ static class AImpl implements A {
+ final Ensure m_e;
+ AImpl(Ensure e) {
+ m_e = e;
+ }
+ }
+
+ public static interface C {
+ }
+
+ static class CImpl implements C {
+ volatile A m_a;
+ }
+
+ public static interface B {
+ }
+
+ static class BImpl implements B {
+ final Ensure m_e;
+ volatile A m_a;
+ volatile C m_c;
+
+ BImpl(Ensure e) {
+ m_e = e;
+ }
+
+ public void add(A a) {
+ m_e.step(1);
+ }
+
+ void init(Component c) {
+ m_e.step(2);
+ DependencyManager dm = c.getDependencyManager();
+ c.add(dm.createServiceDependency().setService(C.class).setRequired(true).setCallbacks("add", "remove"));
+ }
+
+ public void add(C c) {
+ m_e.step(3);
+ }
+
+ public void start() {
+ m_e.step(4);
+ }
+
+ public void stop() { // C becomes unsatisfied when A properties are changed to foo=bar2
+ m_e.step(5);
+ }
+
+ public void remove(C c) {
+ m_e.step(6);
+ }
+
+ public void change(Map properties, A a) {
+ Assert.assertEquals("bar2", properties.get("foo"));
+ m_e.step(7);
+ }
+
+ public void destroy() {
+ m_e.step(8);
+ }
+
+ public void remove(A a) {
+ m_e.step(9);
+ }
+ }
+
+ public void testAdapterWithChangedInstanceBoundDependency() {
+ DependencyManager m = getDM();
+ Ensure e = new Ensure();
+
+ Dictionary props = new Hashtable();
+ props.put("foo", "bar");
+ Component a = m.createComponent()
+ .setImplementation(new AImpl(e))
+ .setInterface(A.class.getName(), props);
+
+ Component b = m.createAdapterService(A.class, null, "add", "change", "remove")
+ .setInterface(B.class.getName(), null)
+ .setImplementation(new BImpl(e));
+
+ Component c = m.createComponent()
+ .setImplementation(new CImpl())
+ .setInterface(C.class.getName(), null)
+ .add(m.createServiceDependency().setService(A.class, "(foo=bar)").setRequired(true));
+
+ m.add(a);
+ m.add(c);
+ m.add(b);
+
+ e.waitForStep(4, 5000);
+
+ System.out.println("changing A props ...");
+ props = new Hashtable();
+ props.put("foo", "bar2");
+ a.setServiceProperties(props);
+
+ e.waitForStep(7, 5000);
+
+ m.remove(c);
+ m.remove(a);
+ m.remove(b);
+
+ e.waitForStep(9, 5000);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithPropagationTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithPropagationTest.java
new file mode 100644
index 0000000..29ed79e
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithPropagationTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.itest.api;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * Checks if a service adapter propagates its service properties, if
+ * the adapted service properties are changed:
+ *
+ * S1Impl provides S
+ * S1Adapter adapts S1Impl(S) to S2
+ * S3 depends on S2
+ *
+ * So, when S1Impl service properties are changed, S1Adapter shall propagate the changed properties to S3.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class AdapterWithPropagationTest extends TestBase {
+ public static interface S1 {}
+
+ static class S1Impl implements S1 {
+ private Ensure m_ensure;
+ public S1Impl(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void start() {
+ m_ensure.step(1);
+ }
+ }
+
+ public static interface S2 {}
+
+ static class S1Adapter implements S2 {
+ private Ensure m_ensure;
+ public S1Adapter(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void add(Map properties, S1 s1) {
+ Assert.assertTrue("v1".equals(properties.get("p1")));
+ Assert.assertTrue("v2overriden".equals(properties.get("p2")));
+ m_ensure.step(2);
+ }
+
+ public void change(Map properties, S1 s1) {
+ Assert.assertTrue("v1modified".equals(properties.get("p1")));
+ Assert.assertTrue("v2overriden".equals(properties.get("p2")));
+ m_ensure.step(4);
+ }
+ }
+
+ static class S3 {
+ private final Ensure m_ensure;
+
+ public S3(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void add(Map properties, S2 s2) {
+ Assert.assertTrue("v1".equals(properties.get("p1")));
+ Assert.assertTrue("v2".equals(properties.get("p2"))); // s1 should not override adapter service properties
+ m_ensure.step(3);
+ }
+
+ public void change(Map properties, S2 s2) {
+ Assert.assertTrue("v1modified".equals(properties.get("p1")));
+ Assert.assertTrue("v2".equals(properties.get("p2"))); // s1 should not override adapter service properties
+ m_ensure.step(5);
+ }
+ }
+
+ public void testAdapterWithPropagation() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+
+ Dictionary s1Properties = new Hashtable();
+ s1Properties.put("p1", "v1");
+ s1Properties.put("p2", "v2overriden"); // should not override adapter
+ Component s1 = m.createComponent()
+ .setImplementation(new S1Impl(e))
+ .setInterface(S1.class.getName(), s1Properties);
+
+ Dictionary s1AdapterProperties = new Hashtable();
+ s1AdapterProperties.put("p2", "v2");
+ Component s1Adapter = m.createAdapterService(S1.class, null, "add", "change", null)
+ .setInterface(S2.class.getName(), s1AdapterProperties)
+ .setImplementation(new S1Adapter(e));
+
+ Component s3 = m.createComponent()
+ .setImplementation(new S3(e))
+ .add(m.createServiceDependency()
+ .setService(S2.class)
+ .setRequired(true)
+ .setCallbacks("add", "change", null));
+
+
+ m.add(s1);
+ m.add(s1Adapter);
+ m.add(s3);
+
+ e.waitForStep(3, 5000);
+
+ s1Properties = new Hashtable();
+ s1Properties.put("p1", "v1modified");
+ s1Properties.put("p2", "v2overriden");
+ s1.setServiceProperties(s1Properties);
+
+ e.waitForStep(5, 5000);
+
+ m.clear();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithoutPropagationTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithoutPropagationTest.java
new file mode 100644
index 0000000..d50bbd3
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithoutPropagationTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.itest.api;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.junit.Assert;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterWithoutPropagationTest extends TestBase {
+
+ public void testAdapterNoPropagate() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+
+ // The provider has a "foo=bar" property
+ Hashtable<String, String> props = new Hashtable<>();
+ props.put("foo", "bar");
+ ServiceProvider serviceProvider = new ServiceProvider(e);
+ Component provider = m.createComponent()
+ .setInterface(OriginalService.class.getName(), props).setImplementation(serviceProvider);
+
+ // The Adapter will see the "foo=bar" property from the adaptee
+ Component adapter = m.createAdapterService(OriginalService.class, null, null,
+ null, "set", "change", null, null, false)
+ .setInterface(AdaptedService.class.getName(), null)
+ .setImplementation(new ServiceAdapter(e));
+
+ // The consumer depends on the AdaptedService, but won't see foo=bar property from the adaptee
+ Component consumer = m.createComponent()
+ .setImplementation(new ServiceConsumer(e))
+ .add(m.createServiceDependency()
+ .setService(AdaptedService.class)
+ .setRequired(true)
+ .setCallbacks("set", "change", null)
+ );
+
+ // add the provider and the adapter
+ m.add(provider);
+ m.add(adapter);
+ // Checks if the adapter has been started and has seen the adaptee properties
+ e.waitForStep(1, 5000);
+
+ // add a consumer that must not see the adaptee service properties
+ m.add(consumer);
+ e.waitForStep(2, 5000);
+
+ // change the service properties of the provider, and check that the adapter callback instance is caled.
+ serviceProvider.changeServiceProperties();
+ e.waitForStep(3, 5000);
+
+ // cleanup
+ m.clear();
+ }
+
+ static interface OriginalService {
+ }
+
+ static interface AdaptedService {
+ }
+
+ static class ServiceProvider implements OriginalService {
+ private volatile ServiceRegistration m_registration; // auto injected when started.
+ public ServiceProvider(Ensure e) {
+ }
+ public void changeServiceProperties() {
+ Hashtable<String, String> props = new Hashtable<>();
+ props.put("foo", "bar2");
+ m_registration.setProperties(props);
+ }
+ }
+
+ public static class ServiceAdapter implements AdaptedService {
+ private final Ensure m_ensure;
+
+ public ServiceAdapter(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void set(OriginalService adaptee, Dictionary<String, String> props) {
+ Assert.assertEquals("bar", props.get("foo"));
+ m_ensure.step(1);
+ }
+
+ void change(OriginalService adapted, Dictionary<String, String> props) {
+ Assert.assertEquals("bar2", props.get("foo"));
+ m_ensure.step(3);
+ }
+ }
+
+ static class ServiceConsumer {
+ @SuppressWarnings("unused")
+ private volatile AdaptedService m_service;
+ private final Ensure m_ensure;
+
+ public ServiceConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ void set(AdaptedService adapted, Dictionary<String, String> props) {
+ Assert.assertNull(props.get("foo"));
+ m_ensure.step(2);
+ }
+
+ void change(AdaptedService adapted, Dictionary<String, String> props) {
+ Assert.assertNull(props.get("foo"));
+ Assert.fail("Change callback should not be called");
+ }
+ }
+}
+
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectBaseTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectBaseTest.java
new file mode 100644
index 0000000..e4d2d73
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectBaseTest.java
@@ -0,0 +1,227 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.itest.api;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Properties;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.ServiceUtil;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "unused"})
+public class AspectBaseTest extends TestBase {
+
+ public void testSingleAspect() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+
+ // create a service provider and consumer
+ ServiceProvider p = new ServiceProvider(e, "a");
+ ServiceConsumer c = new ServiceConsumer(e);
+ Hashtable<String, Object> props = new Hashtable<>();
+ props.put("name", "a");
+ Component sp = m.createComponent()
+ .setInterface(ServiceInterface.class.getName(), props)
+ .setImplementation(p);
+ Component sc = m.createComponent()
+ .setImplementation(c)
+ .add(m.createServiceDependency()
+ .setService(ServiceInterface.class)
+ .setRequired(true)
+ .setCallbacks("add", "remove")
+ .setAutoConfig("m_service")
+ );
+ Component sa = m.createAspectService(ServiceInterface.class, null, 20, null)
+ .setImplementation(ServiceAspect.class);
+ m.add(sc);
+ m.add(sp);
+ // after the provider was added, the consumer's add should have been invoked once
+ e.waitForStep(1, 2000);
+ Assert.assertEquals("a", c.invoke());
+ m.add(sa);
+ // after the aspect was added, the consumer should get and add for the aspect and a remove
+ // for the original service
+ e.waitForStep(3, 2000);
+ Assert.assertEquals("aa", c.invoke());
+ m.remove(sa);
+ // removing the aspect again should give a remove and add
+ e.waitForStep(5, 2000);
+ Assert.assertEquals("a", c.invoke());
+ m.remove(sp);
+ // finally removing the original service should give a remove
+ e.waitForStep(6, 2000);
+ m.remove(sc);
+ e.step(7);
+ }
+
+ @SuppressWarnings("serial")
+ public void testSingleAspectThatAlreadyExisted() {
+ DependencyManager m = new DependencyManager(context);
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+
+ // create a service provider and consumer
+ ServiceProvider p = new ServiceProvider(e, "a");
+ ServiceConsumer c = new ServiceConsumer(e);
+ Component sp = m.createComponent().setImplementation(p).setInterface(ServiceInterface.class.getName(), new Hashtable() {{ put("name", "a"); }});
+ Component sc = m.createComponent().setImplementation(c).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true).setCallbacks("add", "remove").setAutoConfig("m_service"));
+ Component sa = m.createAspectService(ServiceInterface.class, null, 20, null).setImplementation(ServiceAspect.class);
+ // we first add the aspect
+ m.add(sa);
+ // then the service provider
+ m.add(sp);
+ // finally the consumer
+ m.add(sc);
+
+ Assert.assertEquals("aa", c.invoke());
+
+ // now the consumer's added should be invoked once, as the aspect is already available and should
+ // directly hide the original service
+ e.waitForStep(1, 2000);
+ e.step(2);
+
+ m.remove(sa);
+ // after removing the aspect, the consumer should get the original service back, so
+ // remove and add will be invoked
+ e.waitForStep(4, 2000);
+
+ Assert.assertEquals("a", c.invoke());
+
+ m.remove(sp);
+ // after removing the original service, the consumer's remove should be called once
+ e.waitForStep(5, 2000);
+
+ m.remove(sc);
+ e.step(6);
+ }
+
+ @SuppressWarnings("serial")
+ public void testMultipleAspects() {
+ DependencyManager m = new DependencyManager(context);
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+
+ // create service providers and consumers
+ ServiceConsumer c = new ServiceConsumer(e);
+ Component sp = m.createComponent().setImplementation(new ServiceProvider(e, "a")).setInterface(ServiceInterface.class.getName(), new Hashtable() {{ put("name", "a"); }});
+ Component sp2 = m.createComponent().setImplementation(new ServiceProvider(e, "b")).setInterface(ServiceInterface.class.getName(), new Hashtable() {{ put("name", "b"); }});
+ Component sc = m.createComponent().setImplementation(c).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true).setCallbacks("add", "remove"));
+ Component sa = m.createAspectService(ServiceInterface.class, null, 20, null).setImplementation(ServiceAspect.class);
+ Component sa2 = m.createAspectService(ServiceInterface.class, null, 10, null).setImplementation(ServiceAspect.class);
+ m.add(sp);
+ m.add(sp2);
+ m.add(sa);
+ m.add(sa2);
+ m.add(sc);
+ // the consumer will monitor progress, it should get it's add invoked twice, once for every
+ // (highest) aspect
+ e.waitForStep(2, 2000);
+ e.step(3);
+
+ // now invoke all services the consumer collected
+ List<String> list = c.invokeAll();
+ // and make sure both of them are correctly invoked
+ Assert.assertTrue(list.size() == 2);
+ Assert.assertTrue(list.contains("aaa"));
+ Assert.assertTrue(list.contains("bbb"));
+
+ m.remove(sc);
+ // removing the consumer now should get its removed method invoked twice
+ e.waitForStep(5, 2000);
+ e.step(6);
+ m.remove(sa2);
+ m.remove(sa);
+ m.remove(sp2);
+ m.remove(sp);
+ e.step(7);
+ }
+
+ public static interface ServiceInterface {
+ public String invoke(String input);
+ }
+
+ public static class ServiceProvider implements ServiceInterface {
+ private final Ensure m_ensure;
+ private final String m_name;
+ public ServiceProvider(Ensure e, String name) {
+ m_ensure = e;
+ m_name = name;
+ }
+ public String invoke(String input) {
+ return input + m_name;
+ }
+ }
+
+ public static class ServiceAspect implements ServiceInterface {
+ private volatile ServiceInterface m_originalService;
+ private volatile ServiceRegistration m_registration;
+
+ public String invoke(String input) {
+ String result = m_originalService.invoke(input);
+ String property = (String) m_registration.getReference().getProperty("name");
+ return result + property;
+ }
+ }
+
+ public static class ServiceConsumer {
+ private final Ensure m_ensure;
+ private volatile ServiceInterface m_service;
+ private List<ServiceInterface> m_services = new ArrayList<ServiceInterface>();
+
+ public ServiceConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void add(ServiceReference ref, ServiceInterface si) {
+ System.out.println("add: " + ServiceUtil.toString(ref));
+ m_services.add(si);
+ m_ensure.step();
+ }
+
+ public void remove(ServiceReference ref, ServiceInterface si) {
+ System.out.println("rem: " + ServiceUtil.toString(ref));
+ m_services.remove(si);
+ m_ensure.step();
+ }
+
+ public String invoke() {
+ return m_service.invoke("");
+ }
+
+ public List<String> invokeAll() {
+ List<String> results = new ArrayList<String>();
+ for (ServiceInterface si : m_services) {
+ results.add(si.invoke(""));
+ }
+ return results;
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectChainTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectChainTest.java
new file mode 100644
index 0000000..4d524fd
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectChainTest.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.itest.api;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectChainTest extends TestBase {
+
+ public void testBuildAspectChain() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ Component sp = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+ Component sc = m.createComponent().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true));
+ Component sa2 = m.createAspectService(ServiceInterface.class, null, 20, null).setImplementation(new ServiceAspect(e, 3));
+ Component sa3 = m.createAspectService(ServiceInterface.class, null, 30, null).setImplementation(new ServiceAspect(e, 2));
+ Component sa1 = m.createAspectService(ServiceInterface.class, null, 10, null).setImplementation(new ServiceAspect(e, 4));
+ m.add(sc);
+
+ m.add(sp);
+ m.add(sa2);
+ m.add(sa3);
+ m.add(sa1);
+ e.step();
+ e.waitForStep(5, 5000);
+
+ m.remove(sa3);
+ m.remove(sa2);
+ m.remove(sa1);
+ m.remove(sp);
+
+ m.remove(sc);
+ }
+
+ static interface ServiceInterface {
+ public void invoke(Runnable run);
+ }
+
+ static class ServiceProvider implements ServiceInterface {
+ @SuppressWarnings("unused")
+ private final Ensure m_ensure;
+ public ServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+ public void invoke(Runnable run) {
+ run.run();
+ }
+ }
+
+ static class ServiceAspect implements ServiceInterface {
+ private final Ensure m_ensure;
+ private volatile ServiceInterface m_parentService;
+ private final int m_step;
+
+ public ServiceAspect(Ensure e, int step) {
+ m_ensure = e;
+ m_step = step;
+ }
+ public void start() {
+ }
+
+ public void invoke(Runnable run) {
+ m_ensure.step(m_step);
+ m_parentService.invoke(run);
+ }
+
+ public void stop() {
+ }
+ }
+
+ static class ServiceConsumer implements Runnable {
+ private volatile ServiceInterface m_service;
+ private final Ensure m_ensure;
+
+ public ServiceConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void init() {
+ Thread t = new Thread(this);
+ t.start();
+ }
+
+ public void run() {
+ m_ensure.waitForStep(1, 2000);
+ m_service.invoke(Ensure.createRunnableStep(m_ensure, 5));
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectDynamicsTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectDynamicsTest.java
new file mode 100644
index 0000000..7177edb
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectDynamicsTest.java
@@ -0,0 +1,194 @@
+/*
+ * 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.itest.api;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectDynamicsTest extends TestBase {
+
+ public void testDynamicallyAddAndRemoveAspect() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ Ensure aspectStopEnsure = new Ensure();
+ // create a service provider and consumer
+ Component provider = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+ Component provider2 = m.createComponent().setImplementation(new ServiceProvider2(e)).setInterface(ServiceInterface2.class.getName(), null);
+ Component consumer = m.createComponent().setImplementation(new ServiceConsumer(e))
+ .add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true).setCallbacks("add", null, null, "swap"));
+ Component aspect = m.createAspectService(ServiceInterface.class, null, 1, null).setImplementation(new ServiceAspect(e, aspectStopEnsure));
+
+ m.add(consumer);
+ m.add(provider);
+ // the consumer should invoke the provider here, and when done, arrive at step 3
+ // finally wait for step 6 before continuing
+ e.waitForStep(3, 15000);
+
+ m.add(aspect);
+ // after adding the aspect, we wait for its init to be invoked, arriving at
+ // step 4 after an instance bound dependency was added (on a service provided by
+ // provider 2)
+ e.waitForStep(4, 15000);
+
+ m.add(provider2);
+
+ // after adding provider 2, we should now see the client being swapped, so
+ // we wait for step 5 to happen
+ e.waitForStep(5, 15000);
+
+ // now we continue with step 6, which will trigger the next part of the consumer's
+ // run method to be executed
+ e.step(6);
+
+ // invoking step 7, 8 and 9 when invoking the aspect which in turn invokes the
+ // dependency and the original service, so we wait for that to finish here, which
+ // is after step 10 has been reached (the client will now wait for step 12)
+ e.waitForStep(10, 15000);
+
+ m.remove(aspect);
+ aspectStopEnsure.waitForStep(1, 15000);
+ // removing the aspect should trigger step 11 (in the swap method of the consumer)
+ e.waitForStep(11, 15000);
+
+ // step 12 triggers the client to continue
+ e.step(12);
+
+ // wait for step 13, the final invocation of the provided service (without aspect)
+ e.waitForStep(13, 15000);
+
+ // clean up
+ m.remove(provider2);
+ m.remove(provider);
+ m.remove(consumer);
+ e.waitForStep(16, 15000);
+ m.clear();
+ }
+
+ static interface ServiceInterface {
+ public void invoke(Runnable run);
+ }
+
+ static interface ServiceInterface2 {
+ public void invoke();
+ }
+
+ static class ServiceProvider2 implements ServiceInterface2 {
+ private final Ensure m_ensure;
+
+ public ServiceProvider2(Ensure ensure) {
+ m_ensure = ensure;
+ }
+
+ public void invoke() {
+ m_ensure.step(9);
+ }
+ public void stop() {
+ m_ensure.step();
+ }
+ }
+
+ static class ServiceProvider implements ServiceInterface {
+ private final Ensure m_ensure;
+ public ServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+ public void invoke(Runnable run) {
+ run.run();
+ }
+ public void stop() {
+ m_ensure.step();
+ }
+ }
+
+ static class ServiceAspect implements ServiceInterface {
+ private final Ensure m_ensure;
+ private volatile ServiceInterface m_originalService;
+ private volatile ServiceInterface2 m_injectedService;
+ private volatile Component m_service;
+ private volatile DependencyManager m_manager;
+ private final Ensure m_stopEnsure;
+
+ public ServiceAspect(Ensure e, Ensure stopEnsure) {
+ m_ensure = e;
+ m_stopEnsure = stopEnsure;
+ }
+ public void init() {
+ m_service.add(m_manager.createServiceDependency()
+ .setService(ServiceInterface2.class)
+ .setRequired(true)
+ );
+ m_ensure.step(4);
+ }
+
+ public void invoke(Runnable run) {
+ m_ensure.step(7);
+ m_originalService.invoke(run);
+ m_injectedService.invoke();
+ }
+
+ public void stop() {
+ m_stopEnsure.step(1);
+ }
+ }
+
+ static class ServiceConsumer implements Runnable {
+ private volatile ServiceInterface m_service;
+ private final Ensure m_ensure;
+ private final Ensure.Steps m_swapSteps = new Ensure.Steps(5, 11);
+
+ public ServiceConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ void add(ServiceInterface service) {
+ m_service = service;
+ }
+
+ void swap(ServiceInterface oldService, ServiceInterface newService) {
+ System.out.println("swap: old=" + oldService + ", new=" + newService);
+ m_ensure.steps(m_swapSteps);
+ m_service = newService;
+ }
+
+ public void init() {
+ Thread t = new Thread(this);
+ t.start();
+ }
+
+ public void run() {
+ m_ensure.step(1);
+ m_service.invoke(Ensure.createRunnableStep(m_ensure, 2));
+ m_ensure.step(3);
+ m_ensure.waitForStep(6, 15000);
+ m_service.invoke(Ensure.createRunnableStep(m_ensure, 8));
+ m_ensure.step(10);
+ m_ensure.waitForStep(12, 15000);
+ m_service.invoke(Ensure.createRunnableStep(m_ensure, 13));
+ }
+
+ public void stop() {
+ m_ensure.step();
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectRaceParallelTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectRaceParallelTest.java
new file mode 100644
index 0000000..1559a75
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectRaceParallelTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.itest.api;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectRaceParallelTest extends AspectRaceTest {
+ public AspectRaceParallelTest() {
+ setParallel();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectRaceTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectRaceTest.java
new file mode 100644
index 0000000..e8f6e0d
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectRaceTest.java
@@ -0,0 +1,324 @@
+/*
+ * 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.itest.api;
+
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * This class validates that some aspect aware services are correctly managed and ordered when components and aspects are
+ * registered concurrently.
+ *
+ * By default, this class uses a custom threadpool, but a subclass may override this class and call "setParallel()" method, in
+ * this case we won't use any threadpool, since calling setParallel() method means we are using a parallel Dependency Manager.
+ *
+ * @see AspectRaceParallelTest
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectRaceTest extends TestBase {
+ final static int SERVICES = 3;
+ final static int ASPECTS_PER_SERVICE = 10;
+ final static int ITERATIONS = 1000;
+ final AtomicInteger m_IDGenerator = new AtomicInteger();
+ ExecutorService m_threadpool;
+
+ public void testConcurrentAspects() {
+ try {
+ warn("starting aspect race test");
+ initThreadPool(); // only if setParallel() has not been called (only if a parallel DM is not used).
+
+ for (int loop = 1; loop <= ITERATIONS; loop++) {
+ // Perform concurrent injections of "S" service and S aspects into the Controller component;
+ debug("Iteration: " + loop);
+
+ // Use a helper class to wait for components to be started/stopped.
+ int count = 1 /* for controller */ + SERVICES + (SERVICES * ASPECTS_PER_SERVICE);
+ ComponentTracker tracker = new ComponentTracker(count, count);
+
+ // Create the components (controller / services / aspects)
+ Controller controller = new Controller();
+ Factory f = new Factory();
+ f.createComponents(controller, tracker);
+
+ // Activate the components asynchronously
+ f.registerComponents();
+
+ // Wait for the components to be started (using the tracker)
+ if (!tracker.awaitStarted(5000)) {
+ throw new IllegalStateException("Could not start components timely.");
+ }
+
+ // Check aspect chains consistency.
+ controller.checkConsistency();
+
+ // unregister all services and aspects.
+ f.unregisterComponents();
+
+ // use component tracker to wait for all components to be stopped.
+ if (!tracker.awaitStopped(5000)) {
+ throw new IllegalStateException("Could not stop components timely.");
+ }
+
+ if ((loop) % 50 == 0) {
+ warn("Performed " + loop + " tests.");
+ }
+
+ if (super.errorsLogged()) {
+ throw new IllegalStateException("Race test interrupted (some error occured, see previous logs)");
+ }
+ }
+ }
+
+ catch (Throwable t) {
+ error("Test failed", t);
+ Assert.fail("Test failed: " + t.getMessage());
+ } finally {
+ m_dm.clear();
+ shutdownThreadPool();
+ }
+ }
+
+ private void initThreadPool() {
+ // Create a threadpool only if setParallel() method has not been called.
+ if (! m_parallel) {
+ int cores = Math.max(16, Runtime.getRuntime().availableProcessors());
+ m_threadpool = Executors.newFixedThreadPool(cores);
+ }
+ }
+
+ void shutdownThreadPool() {
+ if (! m_parallel && m_threadpool != null) {
+ m_threadpool.shutdown();
+ try {
+ m_threadpool.awaitTermination(60, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ public interface S {
+ void invoke(Ensure e);
+
+ int getRank();
+ }
+
+ public static class SImpl implements S {
+
+ SImpl() {
+ }
+
+ public void invoke(Ensure e) {
+ e.step(1);
+ }
+
+ public String toString() {
+ return "SImpl";
+ }
+
+ @Override
+ public int getRank() {
+ return Integer.MIN_VALUE;
+ }
+ }
+
+ public class SAspect implements S {
+ volatile S m_next;
+ final int m_rank;
+ volatile Component m_component;
+
+ SAspect(int rank) {
+ m_rank = rank;
+ }
+
+ public synchronized void added(S s) {
+ debug("aspect.added: this rank=%d, next rank=%d", getRank(), s.getRank());
+ m_next = s;
+ }
+
+ public synchronized void swap(S oldS, S newS) {
+ debug("aspect.swap: this rank=%d, old rank=%d, next rank=%d", getRank(), oldS.getRank(), newS.getRank());
+ m_next = newS;
+ }
+
+ public synchronized void removed(S s) {
+ debug("aspect.remove: this rank=%d, removed rank=%d", getRank(), s.getRank());
+ m_next = null;
+ }
+
+ public synchronized void invoke(Ensure e) {
+ debug("aspect.invoke: this rank=%d, next rank=%d", this.getRank(), m_next.getRank());
+ Assert.assertTrue(m_rank > m_next.getRank());
+ m_next.invoke(e);
+ }
+
+ public String toString() {
+ return "[Aspect/rank=" + m_rank + "], next="
+ + ((m_next != null) ? m_next : "null");
+ }
+
+ @Override
+ public int getRank() {
+ return m_rank;
+ }
+ }
+
+ class Factory {
+ int m_serviceId;
+ Component m_controller;
+ final ConcurrentLinkedQueue<Component> m_services = new ConcurrentLinkedQueue<Component>();
+ final ConcurrentLinkedQueue<Component> m_aspects = new ConcurrentLinkedQueue<Component>();
+
+ private void createComponents(Controller controller, ComponentTracker tracker) {
+ // create the controller
+ int controllerID = m_IDGenerator.incrementAndGet();
+ m_controller = m_dm.createComponent()
+ .setImplementation(controller)
+ .setComposition("getComposition")
+ .add(tracker);
+ for (int i = 0; i < SERVICES; i ++) {
+ m_controller.add(m_dm.createServiceDependency()
+ .setService(S.class, "(controller.id=" + controllerID + ")")
+ .setCallbacks("bind", null, "unbind", "swap")
+ .setRequired(true));
+ }
+
+ // create the services
+ for (int i = 1; i <= SERVICES; i++) {
+ int aspectId = m_IDGenerator.incrementAndGet();
+ Component s = m_dm.createComponent();
+ Hashtable<String, String> props = new Hashtable<String, String>();
+ props.put("controller.id", String.valueOf(controllerID));
+ props.put("aspect.id", String.valueOf(aspectId));
+ s.setInterface(S.class.getName(), props)
+ .setImplementation(new SImpl());
+ s.add(tracker);
+ m_services.add(s);
+
+ // create the aspects for that service
+ for (int j = 1; j <= ASPECTS_PER_SERVICE; j++) {
+ final int rank = j;
+ SAspect sa = new SAspect(rank);
+ Component a =
+ m_dm.createAspectService(S.class, "(aspect.id=" + aspectId + ")", rank, "added", null, "removed", "swap")
+ .setImplementation(sa);
+ a.add(tracker);
+ m_aspects.add(a);
+ }
+ }
+ }
+
+ public void registerComponents() {
+ // If setParallel() has been called (we are using a parallel dependency manager), then no needs to use a custom thread pool.
+ if (m_parallel) { // using a parallel DM.
+ for (final Component s : m_services) {
+ m_dm.add(s);
+ }
+ m_dm.add(m_controller);
+ for (final Component a : m_aspects) {
+ m_dm.add(a);
+ }
+ } else {
+ for (final Component s : m_services) {
+ m_threadpool.execute(new Runnable() {
+ public void run() {
+ m_dm.add(s);
+ }
+ });
+ }
+ m_threadpool.execute(new Runnable() {
+ public void run() {
+ m_dm.add(m_controller);
+ }
+ });
+ for (final Component a : m_aspects) {
+ m_threadpool.execute(new Runnable() {
+ public void run() {
+ m_dm.add(a);
+ }
+ });
+ }
+ }
+ }
+
+ public void unregisterComponents() throws InterruptedException, InvalidSyntaxException {
+ m_dm.remove(m_controller);
+ for (final Component s : m_services) {
+ m_dm.remove(s);
+ }
+ for (final Component a : m_aspects) {
+ m_dm.remove(a);
+ }
+ }
+ }
+
+ public class Controller {
+ final Composition m_compo = new Composition();
+ final HashSet<S> m_services = new HashSet<S>();
+
+ Object[] getComposition() {
+ return new Object[] { this, m_compo };
+ }
+
+ synchronized void bind(ServiceReference sr, Object service) {
+ debug("controller.bind: %s", service);
+ S s = (S) service;
+ m_services.add(s);
+ debug("bind: service count after bind: %d", m_services.size());
+ }
+
+ synchronized void swap(S previous, S current) {
+ debug("controller.swap: previous=%s, current=%s", previous, current);
+ if (!m_services.remove(previous)) {
+ debug("swap: unknow previous service: " + previous);
+ }
+ m_services.add(current);
+ debug("controller.swap: service count after swap: %d", m_services.size());
+ }
+
+ synchronized void unbind(S a) {
+ debug("unbind " + a);
+ m_services.remove(a);
+ }
+
+ synchronized void checkConsistency() {
+ debug("service count: %d", m_services.size());
+ for (S s : m_services) {
+ info("checking service: %s", s);
+ Ensure ensure = new Ensure(false);
+ s.invoke(ensure);
+ }
+ }
+ }
+
+ public static class Composition {
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectServiceDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectServiceDependencyTest.java
new file mode 100644
index 0000000..365ee31
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectServiceDependencyTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectServiceDependencyTest extends TestBase {
+ public void testServiceRegistrationAndConsumption() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ Component sp = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+ Component sc = m.createComponent().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency()
+ .setService(ServiceInterface.class)
+ .setCallbacks("add", "remove")
+ .setRequired(true));
+ Component asp = m.createAspectService(ServiceInterface.class, null, 100)
+ .setImplementation(ServiceProviderAspect.class);
+ m.add(sp);
+ m.add(sc);
+ m.add(asp);
+ m.remove(asp);
+ m.remove(sc);
+ m.remove(sp);
+
+ // ensure we executed all steps inside the component instance
+ e.step(8);
+ }
+
+ static interface ServiceInterface {
+ public void invoke(String caller);
+ }
+
+ static class ServiceProvider implements ServiceInterface {
+ private final Ensure m_ensure;
+ public ServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+ public void invoke(String caller) {
+ if (caller.equals("consumer.init")) {
+ m_ensure.step(3);
+ } else if (caller.equals("aspect.consumer.add")) {
+ m_ensure.step(5);
+ }
+ }
+ }
+
+ static class ServiceProviderAspect implements ServiceInterface {
+ private volatile ServiceInterface m_service;
+
+ public ServiceProviderAspect() {
+ }
+
+ @Override
+ public void invoke(String caller) {
+ m_service.invoke("aspect." + caller);
+ }
+ }
+
+ static class ServiceConsumer {
+ private volatile ServiceInterface m_service;
+ private final Ensure m_ensure;
+ private int addCount = 0;
+
+ public ServiceConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void init() {
+ m_ensure.step(2);
+ m_service.invoke("consumer.init");
+ }
+
+ public void destroy() {
+ m_ensure.step(7);
+ }
+
+ public void add(ServiceInterface service) {
+ m_service = service;
+ switch (addCount) {
+ case 0: m_ensure.step(1);
+ break;
+ case 1: m_ensure.step(4);
+ // aspect had been added
+ m_service.invoke("consumer.add");
+ break;
+ case 2: m_ensure.step(6);
+ break;
+ default:
+ }
+ addCount ++;
+ }
+ public void remove(ServiceInterface service) {
+ m_service = null;
+ }
+ }
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectServiceDependencyWithSwapCallbackTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectServiceDependencyWithSwapCallbackTest.java
new file mode 100644
index 0000000..03ce7c1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectServiceDependencyWithSwapCallbackTest.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.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectServiceDependencyWithSwapCallbackTest extends TestBase {
+ public void testServiceRegistrationAndConsumption() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ Component sp = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+ Component sc = m.createComponent().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency()
+ .setService(ServiceInterface.class)
+ .setCallbacks("add", null, "remove", "swap")
+ .setRequired(true));
+ Component asp = m.createAspectService(ServiceInterface.class, null, 100)
+ .setImplementation(ServiceProviderAspect.class);
+ m.add(sp);
+ m.add(sc);
+ m.add(asp);
+ m.remove(asp);
+ m.remove(sc);
+ m.remove(sp);
+
+ // ensure we executed all steps inside the component instance
+ e.step(7);
+ }
+
+ static interface ServiceInterface {
+ public void invoke(String caller);
+ }
+
+ static class ServiceProvider implements ServiceInterface {
+ private final Ensure m_ensure;
+ public ServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+ public void invoke(String caller) {
+ if (caller.equals("consumer.init")) {
+ m_ensure.step(3);
+ } else if (caller.equals("aspect.consumer.add")) {
+ m_ensure.step(5);
+ }
+ }
+ }
+
+ static class ServiceProviderAspect implements ServiceInterface {
+ private volatile ServiceInterface m_service;
+
+ public ServiceProviderAspect() {
+ }
+
+ @Override
+ public void invoke(String caller) {
+ m_service.invoke("aspect." + caller);
+ }
+ }
+
+ static class ServiceConsumer {
+ private volatile ServiceInterface m_service;
+ private final Ensure m_ensure;
+ private int swapCount = 0;
+
+ public ServiceConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void init() {
+ m_ensure.step(2);
+ m_service.invoke("consumer.init");
+ }
+
+ public void destroy() {
+ m_ensure.step(6);
+ }
+
+ public void add(ServiceInterface service) {
+ m_service = service;
+ m_ensure.step(1);
+ }
+
+ public void remove(ServiceInterface service) {
+ m_service = null;
+ }
+
+ public void swap(ServiceInterface previous, ServiceInterface current) {
+ switch (swapCount) {
+ case 0: m_ensure.step(4);
+ break;
+ case 1: m_ensure.step(5);
+ break;
+ default:
+ }
+ m_service = current;
+ swapCount ++;
+ }
+ }
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWhiteboardTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWhiteboardTest.java
new file mode 100644
index 0000000..4e4181d
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWhiteboardTest.java
@@ -0,0 +1,201 @@
+/*
+ * 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.itest.api;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.ServiceUtil;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"rawtypes", "unchecked", "unused"})
+public class AspectWhiteboardTest extends TestBase {
+
+ public void testWhiteboardConsumer() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create service providers and consumer
+ Component sp1 = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+ Component sp2 = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+ ServiceConsumer sci = new ServiceConsumer(e);
+ Component sc = m.createComponent().setImplementation(sci).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(false).setCallbacks("add", "remove"));
+ Component sa2 = m.createAspectService(ServiceInterface.class, null, 20, null).setImplementation(new ServiceAspect(e, 3));
+ Component sa1 = m.createAspectService(ServiceInterface.class, null, 10, null).setImplementation(new ServiceAspect(e, 4));
+
+ // start with a service consumer
+ System.out.println("Adding consumer");
+ m.add(sc);
+
+ // then add two providers, so the consumer will see two services
+ System.out.println("Adding 2 providers");
+ m.add(sp1);
+ m.add(sp2);
+
+ // make sure consumer sees both services
+ Assert.assertEquals(2, sci.services());
+
+ // add an aspect with ranking 20
+ System.out.println("Adding aspect with rank 20");
+ m.add(sa2);
+
+ // make sure the consumer sees the two new aspects and no longer sees the two original services
+ Assert.assertEquals(2, sci.services());
+ Assert.assertEquals(20, sci.highestRanking());
+ Assert.assertEquals(20, sci.lowestRanking());
+
+ // add an aspect with ranking 10
+ System.out.println("Adding aspect with rank 10");
+ m.add(sa1);
+
+ // make sure the consumer still sees the two aspects with ranking 20
+ Assert.assertEquals(2, sci.services());
+ Assert.assertEquals(20, sci.highestRanking());
+ Assert.assertEquals(20, sci.lowestRanking());
+
+ // remove the aspect with ranking 20
+ System.out.println("Removing aspect with rank 20");
+ m.remove(sa2);
+
+ // make sure the consumer now sees the aspects with ranking 10
+ Assert.assertEquals(2, sci.services());
+ Assert.assertEquals(10, sci.highestRanking());
+ Assert.assertEquals(10, sci.lowestRanking());
+
+ // remove one of the original services
+ System.out.println("Removing 1 service");
+ m.remove(sp1);
+
+ // make sure the aspect of that service goes away
+ Assert.assertEquals(1, sci.services());
+ Assert.assertEquals(10, sci.highestRanking());
+ Assert.assertEquals(10, sci.lowestRanking());
+
+ // remove the aspect with ranking 10
+ System.out.println("Removing aspect with rank 10");
+ m.remove(sa1);
+
+ // make sure only the original service remains
+ Assert.assertEquals(1, sci.services());
+ Assert.assertEquals(0, sci.highestRanking());
+ Assert.assertEquals(0, sci.lowestRanking());
+
+ System.out.println("Done with test");
+
+ // end of test
+ m.remove(sa2);
+ m.remove(sp2);
+ m.remove(sc);
+ }
+
+ static interface ServiceInterface {
+ public void invoke(Runnable run);
+ }
+
+ static class ServiceProvider implements ServiceInterface {
+ private final Ensure m_ensure;
+ public ServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+ public void invoke(Runnable run) {
+ run.run();
+ }
+ }
+
+ static class ServiceAspect implements ServiceInterface {
+ private final Ensure m_ensure;
+ private volatile ServiceInterface m_parentService;
+ private final int m_step;
+
+ public ServiceAspect(Ensure e, int step) {
+ m_ensure = e;
+ m_step = step;
+ }
+ public void start() {
+ }
+
+ public void invoke(Runnable run) {
+ m_ensure.step(m_step);
+ m_parentService.invoke(run);
+ }
+
+ public void stop() {
+ }
+ }
+
+ static class ServiceConsumer implements Runnable {
+ private List m_services = new ArrayList();
+ private final Ensure m_ensure;
+
+ public ServiceConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void init() {
+ Thread t = new Thread(this);
+ t.start();
+ }
+
+ public void run() {
+ }
+
+ public int services() {
+ return m_services.size();
+ }
+
+ public int highestRanking() {
+ int ranking = Integer.MIN_VALUE;
+ for (int i = 0; i < m_services.size(); i++) {
+ ServiceReference ref = (ServiceReference) m_services.get(i);
+ Integer r = (Integer) ref.getProperty(Constants.SERVICE_RANKING);
+ int rank = r == null ? 0 : r.intValue();
+ ranking = Math.max(ranking, rank);
+ }
+ return ranking;
+ }
+ public int lowestRanking() {
+ int ranking = Integer.MAX_VALUE;
+ for (int i = 0; i < m_services.size(); i++) {
+ ServiceReference ref = (ServiceReference) m_services.get(i);
+ Integer r = (Integer) ref.getProperty(Constants.SERVICE_RANKING);
+ int rank = r == null ? 0 : r.intValue();
+ ranking = Math.min(ranking, rank);
+ }
+ return ranking;
+ }
+
+ public void add(ServiceReference ref, ServiceInterface svc) {
+ System.out.println("Added: " + ServiceUtil.toString(ref));
+ m_services.add(ref);
+ }
+ public void remove(ServiceReference ref, ServiceInterface svc) {
+ System.out.println("Removed: " + ServiceUtil.toString(ref));
+ m_services.remove(ref);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWithCallbacksServiceDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWithCallbacksServiceDependencyTest.java
new file mode 100644
index 0000000..c5116f2
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWithCallbacksServiceDependencyTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectWithCallbacksServiceDependencyTest extends TestBase {
+ public void testServiceRegistrationAndConsumption() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ Component sp = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+ Component sc = m.createComponent().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency()
+ .setService(ServiceInterface.class)
+ .setCallbacks("add", "remove")
+ .setRequired(true));
+ Component asp = m.createAspectService(ServiceInterface.class, null, 100, "add", null, "remove", "swap")
+ .setImplementation(ServiceProviderAspect.class);
+ m.add(sp);
+ m.add(sc);
+ m.add(asp);
+ m.remove(asp);
+ m.remove(sc);
+ m.remove(sp);
+
+ // ensure we executed all steps inside the component instance
+ e.step(8);
+ }
+
+ static interface ServiceInterface {
+ public void invoke(String caller);
+ }
+
+ static class ServiceProvider implements ServiceInterface {
+ private final Ensure m_ensure;
+ public ServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+ public void invoke(String caller) {
+ if (caller.equals("consumer.init")) {
+ m_ensure.step(3);
+ } else if (caller.equals("aspect.consumer.add")) {
+ m_ensure.step(5);
+ }
+ }
+ }
+
+ static class ServiceProviderAspect implements ServiceInterface {
+ private volatile ServiceInterface m_service;
+
+ public ServiceProviderAspect() {
+ }
+
+ @Override
+ public void invoke(String caller) {
+ m_service.invoke("aspect." + caller);
+ }
+
+ public void add(ServiceInterface service) {
+ m_service = service;
+ }
+
+ public void remove(ServiceInterface service) {
+ m_service = null;
+ }
+
+ public void swap(ServiceInterface previous, ServiceInterface current) {
+ m_service = current;
+ }
+ }
+
+ static class ServiceConsumer {
+ private volatile ServiceInterface m_service;
+ private final Ensure m_ensure;
+ private int addCount = 0;
+
+ public ServiceConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void init() {
+ m_ensure.step(2);
+ m_service.invoke("consumer.init");
+ }
+
+ public void destroy() {
+ m_ensure.step(7);
+ }
+
+ public void add(ServiceInterface service) {
+ m_service = service;
+ switch (addCount) {
+ case 0: m_ensure.step(1);
+ break;
+ case 1: m_ensure.step(4);
+ // aspect had been added
+ m_service.invoke("consumer.add");
+ break;
+ case 2: m_ensure.step(6);
+ break;
+ default:
+ }
+ addCount ++;
+ }
+ public void remove(ServiceInterface service) {
+ m_service = null;
+ }
+ }
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWithPropagationTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWithPropagationTest.java
new file mode 100644
index 0000000..f90c264
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWithPropagationTest.java
@@ -0,0 +1,763 @@
+/*
+ * 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.itest.api;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.ServiceUtil;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Test for aspects with service properties propagations.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"rawtypes", "unchecked", "unused"})
+public class AspectWithPropagationTest extends TestBase {
+ private final static int ASPECTS = 3;
+ private final Set<Integer> _randoms = new HashSet<Integer>();
+ private final Random _rnd = new Random();
+ private static Ensure m_invokeStep;
+ private static Ensure m_changeStep;
+
+ /**
+ * This test does the following:
+ *
+ * - Create S service with property "p=s"
+ * - Create SA (aspect of S) with property "p=aspect"
+ * - Create Client, depending on S (actually, on SA).
+ * - Client should see SA with properties p=aspect
+ * - Change S service property with "p=smodified": the Client should be changed with SA(p=aspect)
+ * - Change aspect service property with "p=aspectmodified": The client should be changed with SA(p=aspectmodified)
+ */
+ public void testAspectsWithPropagationNotOverriding() {
+ System.out.println("----------- Running testAspectsWithPropagationNotOverriding ...");
+ DependencyManager m = getDM();
+ m_invokeStep = new Ensure();
+
+ // Create our original "S" service.
+ S s = new S() {
+ public void invoke() {
+ }
+ };
+ Dictionary props = new Hashtable();
+ props.put("p", "s");
+ Component sComp = m.createComponent()
+ .setImplementation(s)
+ .setInterface(S.class.getName(), props);
+
+ // Create SA (aspect of S)
+ S sa = new S() {
+ volatile S m_s;
+ public void invoke() {
+ }
+ };
+ Component saComp = m.createAspectService(S.class, null, 1).setImplementation(sa);
+ props = new Hashtable();
+ props.put("p", "aspect");
+ saComp.setServiceProperties(props);
+
+ // Create client depending on S
+ Object client = new Object() {
+ int m_changeCount;
+ void add(Map props, S s) {
+ Assert.assertEquals("aspect", props.get("p"));
+ m_invokeStep.step(1);
+ }
+
+ void change(Map props, S s) {
+ switch (++m_changeCount) {
+ case 1:
+ Assert.assertEquals("aspect", props.get("p"));
+ m_invokeStep.step(2);
+ break;
+ case 2:
+ Assert.assertEquals("aspectmodified", props.get("p"));
+ m_invokeStep.step(3);
+ }
+ }
+ };
+ Component clientComp = m.createComponent()
+ .add(m.createServiceDependency()
+ .setService(S.class)
+ .setRequired(true)
+ .setCallbacks("add", "change", null))
+ .setImplementation(client);
+
+ // Add components in dependency manager
+ m.add(sComp);
+ m.add(saComp);
+ m.add(clientComp);
+
+ // client should have been added with SA aspect
+ m_invokeStep.waitForStep(1, 5000);
+
+ // now change s "p=s" to "p=smodified": client should not see it
+ props = new Hashtable();
+ props.put("p", "smodified");
+ sComp.setServiceProperties(props);
+ m_invokeStep.waitForStep(2, 5000);
+
+ // now change sa aspect "p=aspect" to "p=aspectmodified": client should see it
+ props = new Hashtable();
+ props.put("p", "aspectmodified");
+ saComp.setServiceProperties(props);
+ m_invokeStep.waitForStep(3, 5000);
+
+ // remove components
+ m.remove(clientComp);
+ m.remove(saComp);
+ m.remove(sComp);
+ }
+
+ /**
+ * This test does the following:
+ *
+ * - Create S service
+ * - Create some S Aspects
+ * - Create a Client, depending on S (actually, on the top-level S aspect)
+ * - Client has a "change" callback in order to track S service properties modifications.
+ * - First, invoke Client.invoke(): all S aspects, and finally original S service must be invoked orderly.
+ * - Modify S original service properties, and check if all aspects, and the client has been orderly called in their "change" callback.
+ * - Modify the First lowest ranked aspect (rank=1), and check if all aspects, and client have been orderly called in their "change" callback.
+ */
+ public void testAspectsWithPropagation() {
+ System.out.println("----------- Running testAspectsWithPropagation ...");
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ m_invokeStep = new Ensure();
+
+ // Create our original "S" service.
+ Dictionary props = new Hashtable();
+ props.put("foo", "bar");
+ Component s = m.createComponent()
+ .setImplementation(new SImpl())
+ .setInterface(S.class.getName(), props);
+
+ // Create an aspect aware client, depending on "S" service.
+ Client clientImpl;
+ Component client = m.createComponent()
+ .setImplementation((clientImpl = new Client()))
+ .add(m.createServiceDependency()
+ .setService(S.class)
+ .setRequired(true)
+ .setCallbacks("add", "change", "remove", "swap"));
+
+ // Create some "S" aspects
+ Component[] aspects = new Component[ASPECTS];
+ for (int rank = 1; rank <= ASPECTS; rank ++) {
+ aspects[rank-1] = m.createAspectService(S.class, null, rank, "add", "change", "remove", "swap")
+ .setImplementation(new A("A" + rank, rank));
+ props = new Hashtable();
+ props.put("a" + rank, "v" + rank);
+ aspects[rank-1].setServiceProperties(props);
+ }
+
+ // Register client
+ m.add(client);
+
+ // Randomly register aspects and original service
+ boolean originalServiceAdded = false;
+ for (int i = 0; i < ASPECTS; i ++) {
+ int index = getRandomAspect();
+ m.add(aspects[index]);
+ if (! originalServiceAdded && _rnd.nextBoolean()) {
+ m.add(s);
+ originalServiceAdded = true;
+ }
+ }
+ if (! originalServiceAdded) {
+ m.add(s);
+ }
+
+ // All set, check if client has inherited from top level aspect properties + original service properties
+ Map check = new HashMap();
+ check.put("foo", "bar");
+ for (int i = 1; i < (ASPECTS - 1); i ++) {
+ check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+ }
+ check.put("a" + ASPECTS, "v" + ASPECTS);
+ checkServiceProperties(check, clientImpl.getServiceProperties());
+
+ // Now invoke client, which orderly calls all aspects in the chain, and finally the original service "S".
+ System.out.println("-------------------------- Invoking client.");
+ clientImpl.invoke();
+ m_invokeStep.waitForStep(ASPECTS+1, 5000);
+
+ // Now, change original service "S" properties: this will orderly trigger "change" callbacks on aspects, and on client.
+ System.out.println("-------------------------- Modifying original service properties.");
+ m_changeStep = new Ensure();
+ props = new Hashtable();
+ props.put("foo", "barModified");
+ s.setServiceProperties(props);
+
+ // Check if aspects and client have been orderly called in their "changed" callback
+ m_changeStep.waitForStep(ASPECTS+1, 5000);
+
+ // Check if modified "foo" original service property has been propagated
+ check = new HashMap();
+ check.put("foo", "barModified");
+ for (int i = 1; i < (ASPECTS - 1); i ++) {
+ check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+ }
+ check.put("a" + ASPECTS, "v" + ASPECTS); // we only see top-level aspect service properties
+ checkServiceProperties(check, clientImpl.getServiceProperties());
+
+ // Now, change the top-level ranked aspect: it must propagate to all upper aspects, as well as to the client
+ System.out.println("-------------------------- Modifying top-level aspect service properties.");
+
+ m_changeStep = new Ensure();
+ for (int i = 1; i <= ASPECTS; i ++) {
+ m_changeStep.step(i); // only client has to be changed.
+ }
+ props = new Hashtable();
+ props.put("a" + ASPECTS, "v" + ASPECTS + "-Modified");
+ aspects[ASPECTS-1].setServiceProperties(props); // That triggers change callbacks for upper aspects (with rank >= 2)
+ m_changeStep.waitForStep(ASPECTS+1, 5000); // check if client have been changed.
+
+ // Check if top level aspect service properties have been propagated up to the client.
+ check = new HashMap();
+ check.put("foo", "barModified");
+ for (int i = 1; i < (ASPECTS - 1); i ++) {
+ check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+ }
+ check.put("a" + ASPECTS, "v" + ASPECTS + "-Modified");
+ checkServiceProperties(check, clientImpl.getServiceProperties());
+
+ // Clear all components.
+ m_changeStep = null;
+ m.clear();
+ }
+
+ /**
+ * This test does the following:
+ *
+ * - Create S service
+ * - Create some S Aspects without any callbacks (add/change/remove/swap)
+ * - Create a Client, depending on S (actually, on the top-level S aspect)
+ * - Client has a "change" callack in order to track S service properties modifications.
+ * - First, invoke Client.invoke(): all S aspects, and finally original S service must be invoked orderly.
+ * - Modify S original service properties, and check if the client has been called in its "change" callback.
+ */
+ public void testAspectsWithPropagationAndNoCallbacks() {
+ System.out.println("----------- Running testAspectsWithPropagation ...");
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ m_invokeStep = new Ensure();
+
+ // Create our original "S" service.
+ Dictionary props = new Hashtable();
+ props.put("foo", "bar");
+ Component s = m.createComponent()
+ .setImplementation(new SImpl())
+ .setInterface(S.class.getName(), props);
+
+ // Create an aspect aware client, depending on "S" service.
+ Client clientImpl;
+ Component client = m.createComponent()
+ .setImplementation((clientImpl = new Client()))
+ .add(m.createServiceDependency()
+ .setService(S.class)
+ .setRequired(true)
+ .setCallbacks("add", "change", "remove"));
+
+ // Create some "S" aspects
+ Component[] aspects = new Component[ASPECTS];
+ for (int rank = 1; rank <= ASPECTS; rank ++) {
+ aspects[rank-1] = m.createAspectService(S.class, null, rank)
+ .setImplementation(new A("A" + rank, rank));
+ props = new Hashtable();
+ props.put("a" + rank, "v" + rank);
+ aspects[rank-1].setServiceProperties(props);
+ }
+
+ // Register client
+ m.add(client);
+
+ // Randomly register aspects and original service
+ boolean originalServiceAdded = false;
+ for (int i = 0; i < ASPECTS; i ++) {
+ int index = getRandomAspect();
+ m.add(aspects[index]);
+ if (! originalServiceAdded && _rnd.nextBoolean()) {
+ m.add(s);
+ originalServiceAdded = true;
+ }
+ }
+ if (! originalServiceAdded) {
+ m.add(s);
+ }
+
+ // All set, check if client has inherited from top level aspect properties + original service properties
+ Map check = new HashMap();
+ check.put("foo", "bar");
+ for (int i = 1; i < (ASPECTS - 1); i ++) {
+ check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+ }
+ check.put("a" + ASPECTS, "v" + ASPECTS);
+ checkServiceProperties(check, clientImpl.getServiceProperties());
+
+ // Now invoke client, which orderly calls all aspects in the chain, and finally the original service "S".
+ System.out.println("-------------------------- Invoking client.");
+ clientImpl.invoke();
+ m_invokeStep.waitForStep(ASPECTS+1, 5000);
+
+ // Now, change original service "S" properties: this will orderly trigger "change" callbacks on aspects, and on client.
+ System.out.println("-------------------------- Modifying original service properties.");
+ m_changeStep = new Ensure();
+ for (int i = 1; i <= ASPECTS; i ++) {
+ m_changeStep.step(i); // skip aspects, which have no "change" callbacks.
+ }
+ props = new Hashtable();
+ props.put("foo", "barModified");
+ s.setServiceProperties(props);
+
+ // Check if aspects and client have been orderly called in their "changed" callback
+ m_changeStep.waitForStep(ASPECTS+1, 5000);
+
+ // Check if modified "foo" original service property has been propagated
+ check = new HashMap();
+ check.put("foo", "barModified");
+ for (int i = 1; i < (ASPECTS - 1); i ++) {
+ check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+ }
+ check.put("a" + ASPECTS, "v" + ASPECTS); // we only see top-level aspect service properties
+ checkServiceProperties(check, clientImpl.getServiceProperties());
+
+ // Clear all components.
+ m_changeStep = null;
+ m.clear();
+ }
+
+ /**
+ * This test does the following:
+ *
+ * - Create S service
+ * - Create some S Aspects
+ * - Create S2 Adapter, which adapts S to S2
+ * - Create Client2, which depends on S2. Client2 listens to S2 property change events.
+ * - Now, invoke Client2.invoke(): all S aspects, and finally original S service must be invoked orderly.
+ * - Modify S original service properties, and check if all aspects, S2 Adapter, and Client2 have been orderly called in their "change" callback.
+ */
+ public void testAdapterWithAspectsAndPropagation() {
+ System.out.println("----------- Running testAdapterWithAspectsAndPropagation ...");
+
+ DependencyManager m = getDM();
+ m_invokeStep = new Ensure();
+
+ // Create our original "S" service.
+ Dictionary props = new Hashtable();
+ props.put("foo", "bar");
+ Component s = m.createComponent()
+ .setImplementation(new SImpl())
+ .setInterface(S.class.getName(), props);
+
+ // Create some "S" aspects
+ Component[] aspects = new Component[ASPECTS];
+ for (int rank = 1; rank <= ASPECTS; rank ++) {
+ aspects[rank-1] = m.createAspectService(S.class, null, rank, "add", "change", "remove", "swap")
+ .setImplementation(new A("A" + rank, rank));
+ props = new Hashtable();
+ props.put("a" + rank, "v" + rank);
+ aspects[rank-1].setServiceProperties(props);
+ }
+
+ // Create S2 adapter (which adapts S1 to S2 interface)
+ Component adapter = m.createAdapterService(S.class, null, "add", "change", "remove", "swap")
+ .setInterface(S2.class.getName(), null)
+ .setImplementation(new S2Impl());
+
+ // Create Client2, which depends on "S2" service.
+ Client2 client2Impl;
+ Component client2 = m.createComponent()
+ .setImplementation((client2Impl = new Client2()))
+ .add(m.createServiceDependency()
+ .setService(S2.class)
+ .setRequired(true)
+ .setCallbacks("add", "change", null));
+
+ // Register client2
+ m.add(client2);
+
+ // Register S2 adapter
+ m.add(adapter);
+
+ // Randomly register aspects, original service
+ boolean originalServiceAdded = false;
+ for (int i = 0; i < ASPECTS; i ++) {
+ int index = getRandomAspect();
+ m.add(aspects[index]);
+ if (! originalServiceAdded && _rnd.nextBoolean()) {
+ m.add(s);
+ originalServiceAdded = true;
+ }
+ }
+ if (! originalServiceAdded) {
+ m.add(s);
+ }
+
+ // Now invoke client2, which orderly calls all S1 aspects, then S1Impl, and finally S2 service
+ System.out.println("-------------------------- Invoking client2.");
+ client2Impl.invoke2();
+ m_invokeStep.waitForStep(ASPECTS+2, 5000);
+
+ // Now, change original service "S" properties: this will orderly trigger "change" callbacks on aspects, S2Impl, and Client2.
+ System.out.println("-------------------------- Modifying original service properties.");
+ m_changeStep = new Ensure();
+ props = new Hashtable();
+ props.put("foo", "barModified");
+ s.setServiceProperties(props);
+
+ // Check if aspects and Client2 have been orderly called in their "changed" callback
+ m_changeStep.waitForStep(ASPECTS+2, 5000);
+
+ // Check if modified "foo" original service property has been propagated to Client2
+ Map check = new HashMap();
+ check.put("foo", "barModified");
+ for (int i = 1; i < (ASPECTS - 1); i ++) {
+ check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+ }
+ check.put("a" + ASPECTS, "v" + ASPECTS);
+ checkServiceProperties(check, client2Impl.getServiceProperties());
+
+ // Clear all components.
+ m_changeStep = null;
+ m.clear();
+ }
+
+ /**
+ * This test does the following:
+ *
+ * - Create S service
+ * - Create some S Aspects without any callbacks (add/change/remove)
+ * - Create S2 Adapter, which adapts S to S2 (but does not have any add/change/remove callbacks)
+ * - Create Client2, which depends on S2. Client2 listens to S2 property change events.
+ * - Now, invoke Client2.invoke(): all S aspects, and finally original S service must be invoked orderly.
+ * - Modify S original service properties, and check if all aspects, S2 Adapter, and Client2 have been orderly called in their "change" callback.
+ */
+ public void testAdapterWithAspectsAndPropagationNoCallbacks() {
+ System.out.println("----------- Running testAdapterWithAspectsAndPropagationNoCallbacks ...");
+
+ DependencyManager m = getDM();
+ m_invokeStep = new Ensure();
+
+ // Create our original "S" service.
+ Dictionary props = new Hashtable();
+ props.put("foo", "bar");
+ Component s = m.createComponent()
+ .setImplementation(new SImpl())
+ .setInterface(S.class.getName(), props);
+
+ // Create some "S" aspects
+ Component[] aspects = new Component[ASPECTS];
+ for (int rank = 1; rank <= ASPECTS; rank ++) {
+ aspects[rank-1] = m.createAspectService(S.class, null, rank)
+ .setImplementation(new A("A" + rank, rank));
+ props = new Hashtable();
+ props.put("a" + rank, "v" + rank);
+ aspects[rank-1].setServiceProperties(props);
+ }
+
+ // Create S2 adapter (which adapts S1 to S2 interface)
+ Component adapter = m.createAdapterService(S.class, null)
+ .setInterface(S2.class.getName(), null)
+ .setImplementation(new S2Impl());
+
+ // Create Client2, which depends on "S2" service.
+ Client2 client2Impl;
+ Component client2 = m.createComponent()
+ .setImplementation((client2Impl = new Client2()))
+ .add(m.createServiceDependency()
+ .setService(S2.class)
+ .setRequired(true)
+ .setCallbacks("add", "change", null));
+
+ // Register client2
+ m.add(client2);
+
+ // Register S2 adapter
+ m.add(adapter);
+
+ // Randomly register aspects, original service
+ boolean originalServiceAdded = false;
+ for (int i = 0; i < ASPECTS; i ++) {
+ int index = getRandomAspect();
+ m.add(aspects[index]);
+ if (! originalServiceAdded && _rnd.nextBoolean()) {
+ m.add(s);
+ originalServiceAdded = true;
+ }
+ }
+ if (! originalServiceAdded) {
+ m.add(s);
+ }
+
+ // Now invoke client2, which orderly calls all S1 aspects, then S1Impl, and finally S2 service
+ System.out.println("-------------------------- Invoking client2.");
+ client2Impl.invoke2();
+ m_invokeStep.waitForStep(ASPECTS+2, 5000);
+
+ // Now, change original service "S" properties: this will orderly trigger "change" callbacks on aspects, S2Impl, and Client2.
+ System.out.println("-------------------------- Modifying original service properties.");
+ m_changeStep = new Ensure();
+ for (int i = 1; i <= ASPECTS+1; i ++) {
+ m_changeStep.step(i); // skip all aspects and the adapter
+ }
+ props = new Hashtable();
+ props.put("foo", "barModified");
+ s.setServiceProperties(props);
+
+ // Check if Client2 has been called in its "changed" callback
+ m_changeStep.waitForStep(ASPECTS+2, 5000);
+
+ // Check if modified "foo" original service property has been propagated to Client2
+ Map check = new HashMap();
+ check.put("foo", "barModified");
+ for (int i = 1; i < (ASPECTS - 1); i ++) {
+ check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+ }
+ check.put("a" + ASPECTS, "v" + ASPECTS);
+ checkServiceProperties(check, client2Impl.getServiceProperties());
+
+ // Clear all components.
+ m_changeStep = null;
+ m.clear();
+ }
+
+ private void checkServiceProperties(Map<?, ?> check, Dictionary properties) {
+ for (Object key : check.keySet()) {
+ Object val = check.get(key);
+ if (val == null) {
+ Assert.assertNull(properties.get(key));
+ } else {
+ Assert.assertEquals(val, properties.get(key));
+ }
+ }
+ }
+
+ private int getRandomAspect() {
+ int index = 0;
+ do {
+ index = _rnd.nextInt(ASPECTS);
+ } while (_randoms.contains(new Integer(index)));
+ _randoms.add(new Integer(index));
+ return index;
+ }
+
+ // S Service
+ public static interface S {
+ public void invoke();
+ }
+
+ // S ServiceImpl
+ static class SImpl implements S {
+ public SImpl() {
+ }
+
+ public String toString() {
+ return "S";
+ }
+
+ public void invoke() {
+ m_invokeStep.step(ASPECTS+1);
+ }
+ }
+
+ // S Aspect
+ static class A implements S {
+ private final String m_name;
+ private volatile ServiceRegistration m_registration;
+ private volatile S m_next;
+ private final int m_rank;
+
+ public A(String name, int rank) {
+ m_name = name;
+ m_rank = rank;
+ }
+
+ public String toString() {
+ return m_name;
+ }
+
+ public void invoke() {
+ int rank = ServiceUtil.getRanking(m_registration.getReference());
+ m_invokeStep.step(ASPECTS - rank + 1);
+ m_next.invoke();
+ }
+
+ public void add(ServiceReference ref, S s) {
+ System.out.println("+++ A" + m_rank + ".add:" + s + "/" + ServiceUtil.toString(ref));
+ m_next = s;
+ }
+
+ public void swap(ServiceReference oldSRef, S oldS, ServiceReference newSRef, S newS) {
+ System.out.println("+++ A" + m_rank + ".swap: new=" + newS + ", props=" + ServiceUtil.toString(newSRef));
+ Assert.assertTrue(m_next == oldS);
+ m_next = newS;
+ }
+
+ public void change(ServiceReference props, S s) {
+ System.out.println("+++ A" + m_rank + ".change: s=" + s + ", props=" + ServiceUtil.toString(props));
+ if (m_changeStep != null) {
+ int rank = ServiceUtil.getRanking(m_registration.getReference());
+ m_changeStep.step(rank);
+ }
+ }
+
+ public void remove(ServiceReference props, S s) {
+ System.out.println("+++ A" + m_rank + ".remove: " + s + ", props=" + ServiceUtil.toString(props));
+ }
+ }
+
+ // Aspect aware client, depending of "S" service aspects.
+ static class Client {
+ private volatile S m_s;
+ private volatile ServiceReference m_sRef;
+
+ public Client() {
+ }
+
+ public Dictionary getServiceProperties() {
+ Dictionary props = new Hashtable();
+ for (String key : m_sRef.getPropertyKeys()) {
+ props.put(key, m_sRef.getProperty(key));
+ }
+ return props;
+ }
+
+ public void invoke() {
+ m_s.invoke();
+ }
+
+ public String toString() {
+ return "Client";
+ }
+
+ public void add(ServiceReference ref, S s) {
+ System.out.println("+++ Client.add: " + s + "/" + ServiceUtil.toString(ref));
+ m_s = s;
+ m_sRef = ref;
+ }
+
+ public void swap(ServiceReference oldSRef, S oldS, ServiceReference newSRef, S newS) {
+ System.out.println("+++ Client.swap: m_s = " + m_s + ", old=" + oldS + ", oldProps=" + ServiceUtil.toString(oldSRef) + ", new=" + newS + ", props=" + ServiceUtil.toString(newSRef));
+ Assert.assertTrue(m_s == oldS);
+ m_s = newS;
+ m_sRef = newSRef;
+ }
+
+ public void change(ServiceReference properties, S s) {
+ System.out.println("+++ Client.change: s=" + s + ", props=" + ServiceUtil.toString(properties));
+ if (m_changeStep != null) {
+ m_changeStep.step(ASPECTS+1);
+ }
+ }
+
+ public void remove(ServiceReference props, S s) {
+ System.out.println("+++ Client.remove: " + s + ", props=" + ServiceUtil.toString(props));
+ }
+ }
+
+ // S2 Service
+ public static interface S2 {
+ public void invoke2();
+ }
+
+ // S2 impl, which adapts S1 interface to S2 interface
+ static class S2Impl implements S2 {
+ private volatile S m_s; // we shall see top-level aspect on S service
+
+ public void add(ServiceReference ref, S s) {
+ System.out.println("+++ S2Impl.add: " + s + "/" + ServiceUtil.toString(ref));
+ m_s = s;
+ }
+
+ public void swap(ServiceReference oldSRef, S oldS, ServiceReference newSRef, S newS) {
+ System.out.println("+++ S2Impl.swap: new=" + newS + ", props=" + ServiceUtil.toString(newSRef));
+ m_s = newS;
+ }
+
+ public void change(ServiceReference properties, S s) {
+ System.out.println("+++ S2Impl.change: s=" + s + ", props=" + ServiceUtil.toString(properties));
+ if (m_changeStep != null) {
+ m_changeStep.step(ASPECTS+1);
+ }
+ }
+
+ public void remove(ServiceReference props, S s) {
+ System.out.println("+++ S2Impl.remove: " + s + ", props=" + ServiceUtil.toString(props));
+ }
+
+ public void invoke2() {
+ m_s.invoke();
+ m_invokeStep.step(ASPECTS + 2); // All aspects, and S1Impl have been invoked
+ }
+
+ public String toString() {
+ return "S2";
+ }
+ }
+
+ // Client2 depending on S2.
+ static class Client2 {
+ private volatile S2 m_s2;
+ private volatile ServiceReference m_s2Ref;
+
+ public Dictionary getServiceProperties() {
+ Dictionary props = new Hashtable();
+ for (String key : m_s2Ref.getPropertyKeys()) {
+ props.put(key, m_s2Ref.getProperty(key));
+ }
+ return props;
+ }
+
+ public void invoke2() {
+ m_s2.invoke2();
+ }
+
+ public String toString() {
+ return "Client2";
+ }
+
+ public void add(ServiceReference ref, S2 s2) {
+ System.out.println("+++ Client2.add: " + s2 + "/" + ServiceUtil.toString(ref));
+ m_s2 = s2;
+ m_s2Ref = ref;
+ }
+
+ public void change(ServiceReference props, S2 s2) {
+ System.out.println("+++ Client2.change: s2=" + s2 + ", props=" + ServiceUtil.toString(props));
+ if (m_changeStep != null) {
+ m_changeStep.step(ASPECTS + 2); // S1Impl, all aspects, and S2 adapters have been changed before us.
+ }
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AutoConfigTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AutoConfigTest.java
new file mode 100644
index 0000000..0e2ca8f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AutoConfigTest.java
@@ -0,0 +1,251 @@
+/*
+ * 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.itest.api;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.Constants;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class AutoConfigTest extends TestBase {
+ private final Ensure m_ensure = new Ensure();
+
+ public void testField() throws Exception {
+ final DependencyManager dm = getDM();
+ // Create a consumer, depending on some providers (autoconfig field).
+ ConsumeWithProviderField consumer = new ConsumeWithProviderField();
+ Component c = createConsumer(dm, consumer);
+ // Create two providers
+ Component p1 = createProvider(dm, 10, new Provider() {
+ public String toString() { return "provider1"; }
+ public void run() { m_ensure.step(); }
+ });
+ Component p2 = createProvider(dm, 20, new Provider() {
+ public String toString() { return "provider2"; }
+ public void run() { m_ensure.step(); }
+ });
+
+ // add the two providers
+ dm.add(p2);
+ dm.add(p1);
+ // add the consumer, which should have been injected with provider2 (highest rank)
+ dm.add(c);
+ m_ensure.waitForStep(1, 5000);
+ // remove the provider2, the consumer should now be injected with provider1
+ dm.remove(p2);
+ Assert.assertNotNull(consumer.getProvider());
+ Assert.assertEquals("provider1", consumer.getProvider().toString());
+ // remove the provider1, the consumer should have been stopped
+ dm.remove(p1);
+ m_ensure.waitForStep(2, 5000);
+ dm.clear();
+ }
+
+ public void testIterableField() throws Exception {
+ final DependencyManager dm = getDM();
+ ConsumerWithIterableField consumer = new ConsumerWithIterableField();
+ Component c = createConsumer(dm, consumer);
+ Component p1 = createProvider(dm, 10, new Provider() {
+ public void run() { m_ensure.step(); }
+ public String toString() { return "provider1"; }
+ });
+ Component p2 = createProvider(dm, 20, new Provider() {
+ public void run() { m_ensure.step();}
+ public String toString() { return "provider2"; }
+ });
+
+ dm.add(p2);
+ dm.add(p1);
+ dm.add(c);
+ // the consumer should have been injected with all providers.
+ m_ensure.waitForStep(3, 5000);
+
+ // check if all providers are there
+ Assert.assertNotNull(consumer.getProvider("provider1"));
+ Assert.assertNotNull(consumer.getProvider("provider2"));
+
+ // remove provider1
+ dm.remove(p1);
+
+ // check if provider1 has been removed and if provider2 is still there
+ Assert.assertNull(consumer.getProvider("provider1"));
+ Assert.assertNotNull(consumer.getProvider("provider2"));
+
+ // remove provider2, the consumer should be stopped
+ dm.remove(p2);
+ m_ensure.waitForStep(4, 5000);
+ dm.clear();
+ }
+
+ public void testMapField() throws Exception {
+ final DependencyManager dm = getDM();
+ ConsumerWithMapField consumer = new ConsumerWithMapField();
+ Component c = createConsumer(dm, consumer);
+ Component p1 = createProvider(dm, 10, new Provider() {
+ public void run() { m_ensure.step(); }
+ public String toString() { return "provider1"; }
+ });
+ Component p2 = createProvider(dm, 20, new Provider() {
+ public void run() { m_ensure.step();}
+ public String toString() { return "provider2"; }
+ });
+
+ dm.add(p2);
+ dm.add(p1);
+ dm.add(c);
+ // the consumer should have been injected with all providers.
+ m_ensure.waitForStep(3, 5000);
+
+ // check if all providers are there
+ Assert.assertNotNull(consumer.getProvider("provider1"));
+ Assert.assertNotNull(consumer.getProvider("provider2"));
+
+ // remove provider1
+ dm.remove(p1);
+
+ // check if provider1 has been removed and if provider2 is still there
+ Assert.assertNull(consumer.getProvider("provider1"));
+ Assert.assertNotNull(consumer.getProvider("provider2"));
+
+ // remove provider2, the consumer should be stopped
+ dm.remove(p2);
+ m_ensure.waitForStep(4, 5000);
+ dm.clear();
+ }
+
+ private Component createProvider(DependencyManager dm, int rank, Provider provider) {
+ Hashtable props = new Hashtable();
+ props.put(Constants.SERVICE_RANKING, new Integer(rank));
+ return dm.createComponent()
+ .setImplementation(provider)
+ .setInterface(Provider.class.getName(), props);
+ }
+
+ private Component createConsumer(DependencyManager dm, Object consumer) {
+ return dm.createComponent()
+ .setImplementation(consumer)
+ .add(dm.createServiceDependency().setService(Provider.class).setRequired(true));
+ }
+
+ public static interface Provider extends Runnable {
+ }
+
+ public class ConsumeWithProviderField {
+ volatile Provider m_provider;
+
+ void start() {
+ Assert.assertNotNull(m_provider);
+ Assert.assertEquals("provider2", m_provider.toString());
+ m_ensure.step(1);
+ }
+
+ public Provider getProvider() {
+ return m_provider;
+ }
+
+ void stop() {
+ m_ensure.step(2);
+ }
+ }
+
+ public class ConsumerWithIterableField {
+ final Iterable<Provider> m_providers = new ConcurrentLinkedQueue<>();
+
+ void start() {
+ Assert.assertNotNull(m_providers);
+ int found = 0;
+ for (Provider provider : m_providers) {
+ provider.run();
+ found ++;
+ }
+ Assert.assertTrue(found == 2);
+ m_ensure.step(3);
+ }
+
+ public Provider getProvider(String name) {
+ System.out.println("getProvider(" + name + ") : proviers=" + m_providers);
+ for (Provider provider : m_providers) {
+ if (provider.toString().equals(name)) {
+ return provider;
+ }
+ }
+ return null;
+ }
+
+ void stop() {
+ m_ensure.step(4);
+ }
+ }
+
+ public class ConsumerWithMapField {
+ final Map<Provider, Dictionary> m_providers = new ConcurrentHashMap<>();
+
+ void start() {
+ Assert.assertNotNull(m_providers);
+ System.out.println("ConsumerMap.start: injected providers=" + m_providers);
+ Assert.assertTrue(m_providers.size() == 2);
+ for (Map.Entry<Provider, Dictionary> e : m_providers.entrySet()) {
+ Provider provider = e.getKey();
+ Dictionary props = e.getValue();
+
+ provider.run();
+ if (provider.toString().equals("provider1")) {
+ Assert.assertEquals(props.get(Constants.SERVICE_RANKING), 10);
+ } else if (provider.toString().equals("provider2")) {
+ Assert.assertEquals(props.get(Constants.SERVICE_RANKING), 20);
+ } else {
+ Assert.fail("Did not find any properties for provider " + provider);
+ }
+ }
+
+ m_ensure.step(3);
+ }
+
+ public Provider getProvider(String name) {
+ System.out.println("getProvider(" + name + ") : providers=" + m_providers);
+ for (Provider provider : m_providers.keySet()) {
+ if (provider.toString().equals(name)) {
+ return provider;
+ }
+ }
+ return null;
+ }
+
+ Map<Provider, Dictionary> getProviders() {
+ return m_providers;
+ }
+
+ void stop() {
+ m_ensure.step(4);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/BundleAdapterTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/BundleAdapterTest.java
new file mode 100644
index 0000000..9cd074a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/BundleAdapterTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.itest.api;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.Bundle;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class BundleAdapterTest extends TestBase {
+ public void testBundleAdapter() {
+ DependencyManager m = getDM();
+ // create a bundle adapter service (one is created for each bundle)
+ Component adapter = m.createBundleAdapterService(Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE, null, false)
+ .setImplementation(BundleAdapter.class)
+ .setInterface(BundleAdapter.class.getName(), null);
+
+ // create a service provider and consumer
+ Consumer c = new Consumer();
+ Component consumer = m.createComponent().setImplementation(c)
+ .add(m.createServiceDependency().setService(BundleAdapter.class).setCallbacks("add", "remove"));
+
+ // add the bundle adapter
+ m.add(adapter);
+ // add the service consumer
+ m.add(consumer);
+ // check if at least one bundle was found
+ c.check();
+ // remove the consumer again
+ m.remove(consumer);
+ // check if all bundles were removed correctly
+ c.doubleCheck();
+ // remove the bundle adapter
+ m.remove(adapter);
+ }
+
+ public static class BundleAdapter {
+ volatile Bundle m_bundle;
+
+ Bundle getBundle() {
+ return m_bundle;
+ }
+ }
+
+ static class Consumer {
+ private volatile int m_count = 0;
+
+ public void add(BundleAdapter ba) {
+ Bundle b = ba.getBundle();
+ System.out.println("Consumer.add(" + b.getSymbolicName() + ")");
+ Assert.assertNotNull("bundle instance must not be null", b);
+ m_count++;
+ }
+
+ public void check() {
+ Assert.assertTrue("we should have found at least one bundle", m_count > 0);
+ }
+
+ public void remove(BundleAdapter ba) {
+ Bundle b = ba.getBundle();
+ System.out.println("Consumer.remove(" + b.getSymbolicName() + ")");
+ m_count--;
+ }
+
+ public void doubleCheck() {
+ Assert.assertEquals("all bundles we found should have been removed again", 0, m_count);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/BundleDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/BundleDependencyTest.java
new file mode 100644
index 0000000..f59d9b0
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/BundleDependencyTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.itest.api;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.Bundle;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class BundleDependencyTest extends TestBase {
+ private final static String BSN = "org.apache.felix.metatype";
+
+ public void testBundleDependencies() {
+ DependencyManager m = getDM();
+ // create a service provider and consumer
+ Consumer c = new Consumer();
+ Component consumer = m.createComponent().setImplementation(c).add(m.createBundleDependency().setCallbacks("add", "remove"));
+ // add the service consumer
+ m.add(consumer);
+ // check if at least one bundle was found
+ c.check();
+ // remove the consumer again
+ m.remove(consumer);
+ // check if all bundles were removed correctly
+ c.doubleCheck();
+
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ Component consumerWithFilter = m.createComponent().setImplementation(new FilteredConsumer(e)).add(m.createBundleDependency().setFilter("(Bundle-SymbolicName=" + BSN + ")").setCallbacks("add", "remove"));
+ // add a consumer with a filter
+ m.add(consumerWithFilter);
+ e.step(2);
+ // remove the consumer again
+ m.remove(consumerWithFilter);
+ e.step(4);
+ }
+
+ public void testRequiredBundleDependency() {
+ DependencyManager m = getDM();
+
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ Component consumerWithFilter = m.createComponent()
+ .setImplementation(new FilteredConsumerRequired(e))
+ .add(m.createBundleDependency()
+ .setRequired(true)
+ .setFilter("(Bundle-SymbolicName=" + BSN + ")")
+ .setCallbacks("add", "remove")
+ );
+ // add a consumer with a filter
+ m.add(consumerWithFilter);
+ e.waitForStep(1, 5000);
+ // remove the consumer again
+ m.remove(consumerWithFilter);
+ e.waitForStep(2, 5000);
+ }
+
+ static class Consumer {
+ private volatile int m_count = 0;
+
+ public void add(Bundle b) {
+ System.out.println("Consumer.add(" + b.getSymbolicName() + ")");
+ Assert.assertNotNull("bundle instance must not be null", b);
+ m_count++;
+ }
+
+ public void check() {
+ Assert.assertTrue("we should have found at least one bundle", m_count > 0);
+ }
+
+ public void remove(Bundle b) {
+ System.out.println("Consumer.remove(" + b.getSymbolicName() + ")");
+ m_count--;
+ }
+
+ public void doubleCheck() {
+ Assert.assertEquals("all bundles we found should have been removed again", 0, m_count);
+ }
+ }
+
+ static class FilteredConsumer {
+ private final Ensure m_ensure;
+
+ public FilteredConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void add(Bundle b) {
+ m_ensure.step(1);
+ }
+
+ public void remove(Bundle b) {
+ m_ensure.step(3);
+ }
+ }
+
+ static class FilteredConsumerRequired {
+ private final Ensure m_ensure;
+
+ public FilteredConsumerRequired(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void add(Bundle b) {
+ System.out.println("Bundle is " + b);
+// Assert.assertNotNull(b);
+ if (b.getSymbolicName().equals(BSN)) {
+ m_ensure.step(1);
+ }
+ }
+
+ public void remove(Bundle b) {
+ Assert.assertNotNull(b);
+ if (b.getSymbolicName().equals(BSN)) {
+ m_ensure.step(2);
+ }
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ComponentTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ComponentTest.java
new file mode 100644
index 0000000..bb4ca74
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ComponentTest.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.itest.api;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class ComponentTest extends TestBase {
+ private final Ensure m_ensure = new Ensure();
+
+ public void testSimple() throws Exception {
+ final DependencyManager dm = getDM();
+ Component consumer = dm.createComponent();
+ consumer
+ .setImplementation(new Consumer())
+ .add(dm.createServiceDependency()
+ .setService(Provider.class, "(name=provider2)")
+ .setRequired(true)
+ .setCallbacks("add", "remove"))
+ .add(dm.createServiceDependency()
+ .setService(Provider.class, "(name=provider1)")
+ .setRequired(true)
+ .setAutoConfig("m_autoConfiguredProvider"));
+
+ Dictionary props = new Hashtable();
+ props.put("name", "provider1");
+ Component provider1 = dm.createComponent()
+ .setImplementation(new Provider() { public String toString() { return "provider1";}})
+ .setInterface(Provider.class.getName(), props);
+ props = new Hashtable();
+ props.put("name", "provider2");
+ Component provider2 = dm.createComponent()
+ .setImplementation(new Provider() { public String toString() { return "provider2";}})
+ .setInterface(Provider.class.getName(), props);
+ dm.add(provider1);
+ dm.add(provider2);
+ dm.add(consumer);
+ m_ensure.waitForStep(2, 5000);
+ dm.remove(provider1);
+ dm.remove(provider2);
+ m_ensure.waitForStep(5, 5000);
+ dm.clear();
+ }
+
+ public static interface Provider {
+ }
+
+ public class Consumer {
+ Provider m_provider;
+ Provider m_autoConfiguredProvider;
+
+ void add(Map props, Provider provider) {
+ Assert.assertNotNull(provider);
+ Assert.assertEquals("provider2", props.get("name"));
+ m_provider = provider;
+ m_ensure.step(1);
+ }
+
+ void start() {
+ Assert.assertNotNull(m_autoConfiguredProvider);
+ Assert.assertEquals("provider1", m_autoConfiguredProvider.toString());
+ m_ensure.step(2);
+ }
+
+ void stop() {
+ m_ensure.step(3);
+ }
+
+ void destroy() {
+ m_ensure.step(4);
+ }
+
+ void remove(Provider provider) {
+ Assert.assertEquals(m_provider, provider);
+ m_ensure.step(5);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ComponentTracker.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ComponentTracker.java
new file mode 100644
index 0000000..27d0b37
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ComponentTracker.java
@@ -0,0 +1,66 @@
+/*
+ * 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.itest.api;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentState;
+import org.apache.felix.dm.ComponentStateListener;
+
+/**
+ * Helper class used to wait for a group of components to be started and stopped.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ComponentTracker implements ComponentStateListener {
+
+ private final CountDownLatch m_startLatch;
+ private final CountDownLatch m_stopLatch;
+
+ public ComponentTracker(int startCount, int stopCount) {
+ m_startLatch = new CountDownLatch(startCount);
+ m_stopLatch = new CountDownLatch(stopCount);
+ }
+
+ @Override
+ public void changed(Component c, ComponentState state) {
+ switch (state) {
+ case TRACKING_OPTIONAL:
+ m_startLatch.countDown();
+ break;
+
+ case INACTIVE:
+ m_stopLatch.countDown();
+ break;
+
+ default:
+ }
+ }
+
+ public boolean awaitStarted(long millis) throws InterruptedException {
+ return m_startLatch.await(millis, TimeUnit.MILLISECONDS);
+ }
+
+ public boolean awaitStopped(long millis) throws InterruptedException {
+ return m_stopLatch.await(millis, TimeUnit.MILLISECONDS);
+ }
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/CompositionTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/CompositionTest.java
new file mode 100644
index 0000000..bfdcdd1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/CompositionTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class CompositionTest extends TestBase {
+ public void testComposition() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ Component sp = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+ Component sc = m.createComponent().setImplementation(new ServiceConsumer(e))
+ .setComposition("getComposition")
+ .add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true).setCallbacks("add", null));
+ m.add(sp);
+ m.add(sc);
+ // ensure we executed all steps inside the component instance
+ e.step(6);
+ m.clear();
+ }
+
+ static interface ServiceInterface {
+ public void invoke();
+ }
+
+ static class ServiceProvider implements ServiceInterface {
+ private final Ensure m_ensure;
+ public ServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+ public void invoke() {
+ m_ensure.step(4);
+ }
+ }
+
+ static class ServiceConsumer {
+ private final Ensure m_ensure;
+ private ServiceConsumerComposite m_composite;
+ @SuppressWarnings("unused")
+ private ServiceInterface m_service;
+
+ public ServiceConsumer(Ensure e) {
+ m_ensure = e;
+ m_composite = new ServiceConsumerComposite(m_ensure);
+ }
+
+ public Object[] getComposition() {
+ return new Object[] { this, m_composite };
+ }
+
+ void add(ServiceInterface service) {
+ m_ensure.step(1);
+ m_service = service; // This method seems to not being called anymore
+ }
+
+ void start() {
+ m_composite.invoke();
+ m_ensure.step(5);
+ }
+ }
+
+ static class ServiceConsumerComposite {
+ ServiceInterface m_service;
+ private Ensure m_ensure;
+
+ ServiceConsumerComposite(Ensure ensure)
+ {
+ m_ensure = ensure;
+ }
+
+ void add(ServiceInterface service) {
+
+ m_ensure.step(2);
+ m_service = service;
+ }
+
+ void invoke()
+ {
+ m_ensure.step(3);
+ m_service.invoke();
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ConfigurationDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ConfigurationDependencyTest.java
new file mode 100644
index 0000000..d3a6aba
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ConfigurationDependencyTest.java
@@ -0,0 +1,209 @@
+/*
+ * 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.itest.api;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Properties;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class ConfigurationDependencyTest extends TestBase {
+ final static String PID = "ConfigurationDependencyTest.pid";
+
+ public void testComponentWithRequiredConfigurationAndServicePropertyPropagation() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ Component s1 = m.createComponent().setImplementation(new ConfigurationConsumer(e)).setInterface(Runnable.class.getName(), null).add(m.createConfigurationDependency().setPid(PID).setPropagate(true));
+ Component s2 = m.createComponent().setImplementation(new ConfigurationCreator(e)).add(m.createServiceDependency().setService(ConfigurationAdmin.class).setRequired(true));
+ Component s3 = m.createComponent().setImplementation(new ConfiguredServiceConsumer(e)).add(m.createServiceDependency().setService(Runnable.class, ("(testkey=testvalue)")).setRequired(true));
+ m.add(s1);
+ m.add(s2);
+ m.add(s3);
+ e.waitForStep(4, 50000000);
+ m.remove(s1);
+ m.remove(s2);
+ m.remove(s3);
+ // ensure we executed all steps inside the component instance
+ e.step(6);
+ }
+
+ public void testComponentWithRequiredConfigurationAndCallbackInstanceAndServicePropertyPropagation() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ ConfigurationConsumerCallbackInstance callbackInstance = new ConfigurationConsumerCallbackInstance(e);
+ Component s1 = m.createComponent().setImplementation(new ConfigurationConsumerWithCallbackInstance(e))
+ .setInterface(Runnable.class.getName(), null)
+ .add(m.createConfigurationDependency().setPid(PID).setPropagate(true).setCallback(callbackInstance, "updateConfiguration"));
+ Component s2 = m.createComponent().setImplementation(new ConfigurationCreator(e))
+ .add(m.createServiceDependency().setService(ConfigurationAdmin.class).setRequired(true));
+ Component s3 = m.createComponent().setImplementation(new ConfiguredServiceConsumer(e))
+ .add(m.createServiceDependency().setService(Runnable.class, ("(testkey=testvalue)")).setRequired(true));
+ m.add(s1);
+ m.add(s2);
+ m.add(s3);
+ e.waitForStep(4, 5000);
+ m.remove(s1);
+ m.remove(s2);
+ m.remove(s3);
+ // ensure we executed all steps inside the component instance
+ e.step(6);
+ }
+
+ public void testFELIX2987() {
+ // mimics testComponentWithRequiredConfigurationAndServicePropertyPropagation
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ Component s1 = m.createComponent().setImplementation(new ConfigurationConsumer2(e)).setInterface(Runnable.class.getName(), null).add(m.createConfigurationDependency().setPid(PID).setPropagate(true));
+ Component s2 = m.createComponent().setImplementation(new ConfigurationCreator(e)).add(m.createServiceDependency().setService(ConfigurationAdmin.class).setRequired(true));
+ Component s3 = m.createComponent().setImplementation(new ConfiguredServiceConsumer(e)).add(m.createServiceDependency().setService(Runnable.class, ("(testkey=testvalue)")).setRequired(true));
+ m.add(s1);
+ m.add(s2);
+ m.add(s3);
+ e.waitForStep(4, 5000);
+ m.remove(s1);
+ m.remove(s2);
+ m.remove(s3);
+ // ensure we executed all steps inside the component instance
+ e.step(6);
+ }
+
+
+ class ConfigurationCreator {
+ private volatile ConfigurationAdmin m_ca;
+ private final Ensure m_ensure;
+ Configuration m_conf;
+
+ public ConfigurationCreator(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void init() {
+ try {
+ warn("ConfigurationCreator.init");
+ Assert.assertNotNull(m_ca);
+ m_ensure.step(1);
+ m_conf = m_ca.getConfiguration(PID, null);
+ Hashtable props = new Properties();
+ props.put("testkey", "testvalue");
+ m_conf.update(props);
+ }
+ catch (IOException e) {
+ Assert.fail("Could not create configuration: " + e.getMessage());
+ }
+ }
+
+ public void destroy() throws IOException {
+ warn("ConfigurationCreator.destroy");
+ m_conf.delete();
+ m_ensure.step();
+ }
+ }
+
+ static class ConfigurationConsumer2 extends ConfigurationConsumer {
+ public ConfigurationConsumer2(Ensure e) {
+ super(e);
+ }
+ }
+
+ static class ConfigurationConsumer implements ManagedService, Runnable {
+ private final Ensure m_ensure;
+
+ public ConfigurationConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void updated(Dictionary props) throws ConfigurationException {
+ if (props != null) {
+ m_ensure.step(2);
+ if (!"testvalue".equals(props.get("testkey"))) {
+ Assert.fail("Could not find the configured property.");
+ }
+ }
+ }
+
+ public void run() {
+ m_ensure.step(4);
+ }
+ }
+
+ static class ConfigurationConsumerCallbackInstance {
+ private final Ensure m_ensure;
+
+ public ConfigurationConsumerCallbackInstance(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void updateConfiguration(Dictionary props) throws Exception {
+ if (props != null) {
+ m_ensure.step(2);
+ if (!"testvalue".equals(props.get("testkey"))) {
+ Assert.fail("Could not find the configured property.");
+ }
+ }
+ }
+ }
+
+ static class ConfigurationConsumerWithCallbackInstance implements Runnable {
+ private final Ensure m_ensure;
+
+ public ConfigurationConsumerWithCallbackInstance(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void run() {
+ m_ensure.step(4);
+ }
+ }
+
+ static class ConfiguredServiceConsumer {
+ private final Ensure m_ensure;
+ private volatile Runnable m_runnable;
+
+ public ConfiguredServiceConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void init() {
+ m_ensure.step(3);
+ m_runnable.run();
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/DynamicProxyAspectTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/DynamicProxyAspectTest.java
new file mode 100644
index 0000000..b5e5938
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/DynamicProxyAspectTest.java
@@ -0,0 +1,199 @@
+/**
+ * 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.itest.api;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"rawtypes"})
+public class DynamicProxyAspectTest extends TestBase {
+ public void testImplementGenericAspectWithDynamicProxy() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+
+ // create two service providers, each providing a different service interface
+ Component sp1 = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+ Component sp2 = m.createComponent().setImplementation(new ServiceProvider2(e)).setInterface(ServiceInterface2.class.getName(), null);
+
+ // create a dynamic proxy based aspect and hook it up to both services
+ Component a1 = m.createAspectService(ServiceInterface.class, null, 10, "m_service")
+ .setFactory(new Factory(e, ServiceInterface.class, "ServiceInterfaceProxy"), "create");
+ Component a2 = m.createAspectService(ServiceInterface2.class, null, 10, "m_service")
+ .setFactory(new Factory(e, ServiceInterface2.class, "ServiceInterfaceProxy2"), "create");
+
+ // create a client that invokes a method on boths services, validate that it goes
+ // through the proxy twice
+ Component sc = m.createComponent()
+ .setImplementation(new ServiceConsumer(e))
+ .add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true))
+ .add(m.createServiceDependency().setService(ServiceInterface2.class).setRequired(true))
+ ;
+
+ // register both producers, validate that both services are started
+ m.add(sp1);
+ e.waitForStep(1, 2000);
+ m.add(sp2);
+ e.waitForStep(2, 2000);
+
+ // add both aspects, and validate that both instances have been created
+ m.add(a1);
+ m.add(a2);
+ e.waitForStep(4, 4000);
+
+ // add the client, which will automatically invoke both services
+ m.add(sc);
+
+ // wait until both services have been invoked
+ e.waitForStep(6, 4000);
+
+ // make sure the proxy has been called twice
+ Assert.assertEquals("Proxy should have been invoked this many times.", 2, DynamicProxyHandler.getCounter());
+
+ m.remove(sc);
+ m.remove(a2);
+ m.remove(a1);
+ m.remove(sp2);
+ m.remove(sp1);
+ m.remove(a2);
+ m.remove(a1);
+
+ try {
+ Thread.sleep(2000);
+ }
+ catch (InterruptedException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+ }
+
+ static interface ServiceInterface {
+ public void invoke(Runnable run);
+ }
+
+ static interface ServiceInterface2 {
+ public void invoke(Runnable run);
+ }
+
+ static class ServiceProvider implements ServiceInterface {
+ private final Ensure m_ensure;
+ public ServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+ public void start() {
+ m_ensure.step(1);
+ }
+ public void invoke(Runnable run) {
+ run.run();
+ }
+ }
+
+ static class ServiceProvider2 implements ServiceInterface2 {
+ private final Ensure m_ensure;
+ public ServiceProvider2(Ensure ensure) {
+ m_ensure = ensure;
+ }
+ public void start() {
+ m_ensure.step(2);
+ }
+ public void invoke(Runnable run) {
+ run.run();
+ }
+ }
+
+ static class ServiceConsumer implements Runnable {
+ private volatile ServiceInterface m_service;
+ private volatile ServiceInterface2 m_service2;
+ private final Ensure m_ensure;
+
+ public ServiceConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void init() {
+ Thread t = new Thread(this);
+ t.start();
+ }
+
+ public void run() {
+ m_service.invoke(Ensure.createRunnableStep(m_ensure, 5));
+ m_service2.invoke(Ensure.createRunnableStep(m_ensure, 6));
+ }
+ }
+
+ static class DynamicProxyHandler implements InvocationHandler {
+ public volatile Object m_service; // ISSUE, we cannot inject into "Object" at the moment
+ private final String m_label;
+ private static volatile int m_counter = 0;
+
+ public DynamicProxyHandler(String label) {
+ m_label = label;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ System.out.println("IIIIIIINVOKE--------------------------" + method.getName());
+ if (m_service == null) {
+ Assert.fail("No service was injected into dynamic proxy handler " + m_label);
+ }
+ Method m = m_service.getClass().getMethod(method.getName(), method.getParameterTypes());
+ if (m == null) {
+ Assert.fail("No method " + method.getName() + " was found in instance " + m_service + " in dynamic proxy handler " + m_label);
+ }
+ if (method.getName().equals("invoke")) {
+ // only count methods called 'invoke' because those are actually the ones
+ // both interfaces implement (and the dynamic proxy might be invoked for
+ // other methods, such as toString() as well)
+ m_counter++;
+ }
+ return m.invoke(m_service, args);
+ }
+
+ public static int getCounter() {
+ return m_counter;
+ }
+ }
+
+ static class Factory {
+ private final String m_label;
+ private Class m_class;
+ private final Ensure m_ensure;
+
+ public Factory(Ensure ensure, Class clazz, String label) {
+ m_ensure = ensure;
+ m_class = clazz;
+ m_label = label;
+ }
+
+ public Object create() {
+ m_ensure.step();
+ return Proxy.newProxyInstance(m_class.getClassLoader(), new Class[] { m_class }, new DynamicProxyHandler(m_label));
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2078_ServiceDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2078_ServiceDependencyTest.java
new file mode 100644
index 0000000..030bd0b
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2078_ServiceDependencyTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX2078_ServiceDependencyTest extends TestBase {
+ public void testRequiredServiceRegistrationAndConsumption() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ Component sp = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+ Component sp2 = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+ Component sc = m.createComponent().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true).setCallbacks("add", "remove"));
+ m.add(sp);
+ m.add(sp2);
+ System.out.println("adding client");
+ m.add(sc);
+ System.out.println("waiting");
+ // wait until both services have been added to our consumer
+ e.waitForStep(2, 5000);
+ m.remove(sc);
+ m.remove(sp2);
+ m.remove(sp);
+ // ensure we executed all steps inside the component instance
+ }
+
+ public void testOptionalServiceRegistrationAndConsumption() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ Component sp = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+ Component sp2 = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+ Component sc = m.createComponent().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(false).setCallbacks("add", "remove"));
+ m.add(sp);
+ m.add(sp2);
+ m.add(sc);
+ // wait until both services have been added to our consumer
+ e.waitForStep(2, 5000);
+ m.remove(sc);
+ m.remove(sp2);
+ m.remove(sp);
+ // ensure we executed all steps inside the component instance
+ }
+
+ static interface ServiceInterface {
+ public void invoke();
+ }
+
+ static class ServiceProvider implements ServiceInterface {
+ public ServiceProvider(Ensure e) {
+ }
+ public void invoke() {
+ }
+ }
+
+ static class ServiceConsumer {
+ private final Ensure m_ensure;
+
+ public ServiceConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void add(ServiceInterface i) {
+ System.out.println("add " + i);
+ m_ensure.step();
+ }
+
+ public void remove(ServiceInterface i) {
+
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2344_ExtraDependencyWithAutoConfigTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2344_ExtraDependencyWithAutoConfigTest.java
new file mode 100644
index 0000000..b87f7d1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2344_ExtraDependencyWithAutoConfigTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.itest.api;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings("unused")
+public class FELIX2344_ExtraDependencyWithAutoConfigTest extends TestBase {
+ /**
+ * Test if an auto config extra dependency is injected in the expected order.
+ */
+ public void testExtraDependencyWithAutoConfig() {
+ DependencyManager m = getDM();
+ // Helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // Create a service provider
+ Component sp = m.createComponent().setInterface(ProviderInterface.class.getName(), null).setImplementation(ProviderImpl.class);
+ // Create a service consumer with a required/autoconfig dependency over the service provider.
+ Client c1;
+ Component sc1 = m.createComponent().setImplementation((c1 = new Client(e, true, 1)));
+ // Create a second service consumer with an optional/autoconfig dependency over the service provider.
+ Client c2;
+ Component sc2 = m.createComponent().setImplementation(c2 = new Client(e, false, 3));
+
+ // Add service provider and consumer sc1 (required dependency over provider)
+ m.add(sc1);
+ m.add(sp);
+ e.waitForStep(2, 5000);
+
+ // Remove provider and consumer
+ m.remove(sc1);
+ m.remove(sp);
+
+ // Add consumer sc2 (optional dependency over provider)
+ m.add(sc2);
+ e.waitForStep(4, 5000);
+ m.clear();
+ }
+
+ public interface ProviderInterface {
+ public boolean action();
+ }
+
+ public static class ProviderImpl implements ProviderInterface {
+ public boolean action()
+ {
+ return true;
+ }
+ }
+
+ // This client is not using callbacks, but instead, it uses auto config.
+ public static class Client {
+ volatile ProviderInterface m_provider;
+ private Ensure m_ensure;
+ private final boolean m_required;
+ private final int m_startStep;
+
+ public Client(Ensure e, boolean required, int startStep) {
+ m_ensure = e;
+ m_required = required;
+ m_startStep = startStep;
+ }
+
+ public void init(Component s) {
+ DependencyManager dm = s.getDependencyManager();
+ m_ensure.step(m_startStep);
+ s.add(dm.createServiceDependency()
+ .setService(ProviderInterface.class)
+ .setRequired(m_required)
+ .setAutoConfig("m_provider"));
+ }
+
+ public void start() {
+ // if required dependency: we must have been injected with the service provider
+ // else, we have been injected with a null object.
+ Assert.assertNotNull("provider has not been injected", m_provider);
+ Assert.assertEquals(m_required, m_provider.action()); // action returns false if null object
+ m_ensure.step();
+ }
+ }
+}
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2344_ExtraDependencyWithCallbackTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2344_ExtraDependencyWithCallbackTest.java
new file mode 100644
index 0000000..a28c8ab
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2344_ExtraDependencyWithCallbackTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.itest.api;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * Tests for extra dependencies which are declared from service's init method.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX2344_ExtraDependencyWithCallbackTest extends TestBase {
+ /**
+ * Checks if an extra optional/required dependency is properly injected into a consumer, using callbacks.
+ */
+ public void testExtraDependencyWithCallback() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service consumer and provider
+ Component sp = m.createComponent().setInterface(ProviderInterface.class.getName(), null).setImplementation(ProviderImpl.class);
+ Component sc = m.createComponent().setImplementation(new Client(e, false, 1));
+ Component sc2 = m.createComponent().setImplementation(new Client(e, true, 5));
+ Component sc3 = m.createComponent().setImplementation(new Client(e, true, 9));
+
+ // add the provider first, then add the consumer which initially will have no dependencies
+ // but via the init() method an optional dependency with a callback method will be added
+ m.add(sp);
+ m.add(sc);
+ // remove the consumer again
+ m.remove(sc);
+ e.waitForStep(4, 5000);
+
+ // next up, add a second consumer, identical to the first, but with a required dependency
+ // with a callback method which will be added in the init() method
+ m.add(sc2);
+ // remove the consumer again
+ m.remove(sc2);
+ e.waitForStep(8, 5000);
+
+ // now remove the provider, add a third consumer, identical to the second, and after the
+ // consumer has started, add the provider again
+ m.remove(sp);
+ m.add(sc3);
+ m.add(sp);
+ e.waitForStep(12, 5000);
+ m.clear();
+ }
+
+ public interface ProviderInterface {
+ }
+
+ public static class ProviderImpl implements ProviderInterface {
+ }
+
+ public static class Client {
+ ProviderInterface m_provider;
+ private Ensure m_ensure;
+ private final boolean m_required;
+ private final int m_startStep;
+
+ public Client(Ensure e, boolean required, int startStep) {
+ m_ensure = e;
+ m_required = required;
+ m_startStep = startStep;
+ }
+
+ public void init(Component s) {
+ DependencyManager dm = s.getDependencyManager();
+ m_ensure.step(m_startStep);
+ s.add(dm.createServiceDependency()
+ .setService(ProviderInterface.class)
+ .setRequired(m_required)
+ .setCallbacks("bind", null));
+ }
+
+ // called before start() for required dependency, or after start for optional dependency
+ void bind(ProviderInterface provider) {
+ System.out.println("bind");
+ m_ensure.step(m_required ? m_startStep + 1 : m_startStep + 3);
+ m_provider = provider;
+ }
+
+ public void start() {
+ System.out.println("start");
+ m_ensure.step(m_required ? m_startStep + 2: m_startStep + 1);
+ if (m_required) {
+ Assert.assertNotNull("Dependendency should have been injected", m_provider);
+ }
+ m_ensure.step(m_required ? m_startStep + 3: m_startStep + 2);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2348_ResourceAdapterTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2348_ResourceAdapterTest.java
new file mode 100644
index 0000000..fbf592d
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2348_ResourceAdapterTest.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.itest.api;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URL;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.junit.Assert;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX2348_ResourceAdapterTest extends TestBase {
+ public void testBasicResourceAdapter() throws Exception {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ m.add(m.createResourceAdapterService("(&(path=/path/to/*.txt)(host=localhost))", false, null, "changed")
+ .setImplementation(new ResourceAdapter(e)));
+ URL resourceURL = new URL("file://localhost/path/to/file1.txt");
+ m.add(m.createComponent().setImplementation(new ResourceProvider(context, resourceURL))
+ .add(m.createServiceDependency().setService(ResourceHandler.class).setCallbacks("add", "remove")));
+ e.waitForStep(3, 5000);
+ m.clear();
+ }
+
+ static class ResourceAdapter {
+ protected URL m_resource; // injected by reflection.
+ private Ensure m_ensure;
+
+ ResourceAdapter(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void start() {
+ m_ensure.step(1);
+ Assert.assertNotNull("resource not injected", m_resource);
+ m_ensure.step(2);
+ try {
+ m_resource.openStream();
+ }
+ catch (FileNotFoundException e) {
+ m_ensure.step(3);
+ }
+ catch (IOException e) {
+ Assert.fail("We should not have gotten this exception.");
+ }
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2369_ExtraDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2369_ExtraDependencyTest.java
new file mode 100644
index 0000000..bb1ff01
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2369_ExtraDependencyTest.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.itest.api;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ServiceDependency;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * This testcase verify that a Service is not started if one of its extra required dependencies
+ * is unavailable.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX2369_ExtraDependencyTest extends TestBase
+{
+ public void testExtraDependencies() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service consumer and provider
+ Component sp1 = m.createComponent().setInterface(MyService1.class.getName(), null).setImplementation(new MyService1Impl());
+ Component sc = m.createComponent().setImplementation(new MyClient(e, 1));
+
+ // provides the MyService1 service (but not the MyService2, which is required by MyClient).
+ m.add(sp1);
+ // add MyClient (it should not be invoked in its start() method because MyService2 is not there
+ m.add(sc);
+ // remove MyClient (it should not be invoked in its stop() method because it should not be active, since MyService2 is not there.
+ m.remove(sc);
+ e.waitForStep(2, 5000);
+ m.clear();
+ }
+
+ public interface MyService1 {
+ }
+
+ public interface MyService2 {
+ }
+
+ public static class MyService1Impl implements MyService1 {
+ }
+
+ public static class MyService2Impl implements MyService2 {
+ }
+
+ // This client is not using callbacks, but instead, it uses auto config.
+ public static class MyClient {
+ MyService1 m_myService2; // required/unavailable
+ private Ensure m_ensure;
+ private final int m_startStep;
+
+ public MyClient(Ensure e, int startStep) {
+ m_ensure = e;
+ m_startStep = startStep;
+ }
+
+ public void init(Component s) {
+ DependencyManager dm = s.getDependencyManager();
+ m_ensure.step(m_startStep);
+ ServiceDependency d1 =
+ dm.createServiceDependency() // this dependency is available at this point
+ .setService(MyService1.class)
+ .setRequired(false)
+ .setCallbacks("bind", null);
+ ServiceDependency d2 =
+ dm.createServiceDependency() // not available: we should not be started
+ .setService(MyService2.class)
+ .setRequired(true)
+ .setAutoConfig("m_myService2");
+
+ s.add(d1, d2); // atomically add these two dependencies
+ }
+
+ public void start() {
+ Assert.fail("start should not be called since MyService2 is unavailable");
+ }
+
+ void bind(MyService1 s1) { // optional/available
+ System.out.println("bound MyService1");
+ }
+
+ public void stop() {
+ Assert.fail("stop should not be called since we should not be active at this point");
+ }
+
+ public void destroy() {
+ m_ensure.step(m_startStep+1);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2696_ConfigurationAndServiceDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2696_ConfigurationAndServiceDependencyTest.java
new file mode 100644
index 0000000..6f2ea65
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2696_ConfigurationAndServiceDependencyTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.itest.api;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings("rawtypes")
+public class FELIX2696_ConfigurationAndServiceDependencyTest extends TestBase {
+ final static String PID = "FELIX2696_ConfigurationAndServiceDependencyTest.pid";
+
+ public void testComponentWithRequiredConfigurationAndServicePropertyPropagation() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ Component s1 = m.createComponent()
+ .setImplementation(new ConfigurationConsumer(e))
+ .add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true))
+ .add(m.createConfigurationDependency().setPid(PID));
+ Component s2 = m.createComponent()
+ .setImplementation(new ConfigurationCreator(e))
+ .add(m.createServiceDependency().setService(ConfigurationAdmin.class).setRequired(true));
+ Component s3 = m.createComponent()
+ .setInterface(ServiceInterface.class.getName(), null)
+ .setImplementation(DependentServiceProvider.class);
+ m.add(s1);
+ m.add(s2);
+ m.add(s3);
+ e.waitForStep(2, 5000);
+ warn("removing s3");
+ m.remove(s3);
+ warn("readding s3");
+ m.add(s3);
+ // after adding the required dependency again, the issue in FELIX-2696 means that the
+ // updated() method is not invoked for the new instance, and init() is, so our step
+ // count will only go up to 3 (not 4) causing this test to fail
+ e.waitForStep(4, 5000);
+ m.remove(s3);
+ m.remove(s2);
+ m.remove(s1);
+ e.waitForStep(5, 5000);
+ }
+
+ public static class ConfigurationCreator {
+ private volatile ConfigurationAdmin m_ca;
+ private final Ensure m_ensure;
+ Configuration m_conf;
+
+ public ConfigurationCreator(Ensure e) {
+ m_ensure = e;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void init() {
+ try {
+ m_conf = m_ca.getConfiguration(PID, null);
+ Hashtable props = new Hashtable();
+ props.put("testkey", "testvalue");
+ m_conf.update(props);
+ }
+ catch (IOException e) {
+ Assert.fail("Could not create configuration: " + e.getMessage());
+ }
+ }
+
+ public void destroy() throws IOException {
+ m_conf.delete();
+ m_ensure.step(5);
+ }
+ }
+
+ public class ConfigurationConsumer implements ManagedService {
+ private final Ensure m_ensure;
+
+ public ConfigurationConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void updated(Dictionary props) throws ConfigurationException {
+ warn("Consumer: updated %s", props);
+ if (props != null) {
+ if (!"testvalue".equals(props.get("testkey"))) {
+ Assert.fail("Could not find the configured property.");
+ }
+ m_ensure.step();
+ }
+ }
+
+ public void init() {
+ warn("Consumer: init");
+ m_ensure.step();
+ }
+ }
+
+ public static interface ServiceInterface {
+ }
+
+ public static class DependentServiceProvider implements ServiceInterface {
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2875_ServiceDependencyWithoutServiceNameTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2875_ServiceDependencyWithoutServiceNameTest.java
new file mode 100644
index 0000000..3cc2a90
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2875_ServiceDependencyWithoutServiceNameTest.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.itest.api;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX2875_ServiceDependencyWithoutServiceNameTest extends TestBase {
+ Ensure m_e;
+
+ public void testServiceDependencyWithoutName() {
+ m_e = new Ensure();
+ DependencyManager dm = getDM();
+ Component consumer = dm.createComponent()
+ .setImplementation(new ConsumeServiceDependencyWithoutName())
+ .add(dm.createServiceDependency()
+ .setService("(provider=*)").setRequired(true)
+ .setCallbacks("add", null))
+ .add(dm.createServiceDependency()
+ .setService("(|(provider=provider1)(provider=provider2))").setRequired(true)
+ .setAutoConfig("m_providers"));
+
+ Hashtable<String, String> props = new Hashtable<>();
+ props.put("provider", "provider1");
+ Component provider1 = dm.createComponent()
+ .setImplementation(new Provider1())
+ .setInterface(Provider.class.getName(), props);
+
+ props = new Hashtable<>();
+ props.put("provider", "provider2");
+ Component provider2 = dm.createComponent()
+ .setImplementation(new Provider2())
+ .setInterface(Provider.class.getName(), props);
+
+ dm.add(provider1);
+ dm.add(provider2);
+ dm.add(consumer);
+ m_e.waitForStep(5, 5000);
+ dm.clear();
+ }
+
+ private class ConsumeServiceDependencyWithoutName {
+ volatile Map<Object, Dictionary<String, String>> m_providers; // autoconfig
+
+ @SuppressWarnings("unused")
+ void add(Map<String, Object> props, Object service) {
+ if ("provider1".equals(props.get("provider"))) {
+ m_e.step();
+ } else if ("provider2".equals(props.get("provider"))) {
+ m_e.step();
+ }
+ }
+
+ @SuppressWarnings("unused")
+ void start() {
+ // check if all providers have been injected in our autoconfig field.
+ for (Map.Entry<Object, Dictionary<String, String>> e : m_providers.entrySet()) {
+ if ("provider1".equals(e.getValue().get("provider"))) {
+ m_e.step();
+ } else if ("provider2".equals(e.getValue().get("provider"))) {
+ m_e.step();
+ }
+ }
+ m_e.step(5);
+ }
+ }
+
+ public interface Provider {
+ }
+
+ public class Provider1 implements Provider {
+ }
+
+ public class Provider2 implements Provider {
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2955_ShellCommandTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2955_ShellCommandTest.java
new file mode 100644
index 0000000..4ee902a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2955_ShellCommandTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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.itest.api;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.osgi.framework.Bundle;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX2955_ShellCommandTest extends TestBase {
+ private long m_myBundleId;
+ private Bundle m_testBundle;
+
+ public void testShellCommands() throws Throwable {
+ try {
+ m_myBundleId = context.getBundle().getBundleId();
+ for (Bundle b : context.getBundles()) {
+ if (b.getSymbolicName().equals("org.apache.felix.dependencymanager.itest.bundle")) {
+ m_testBundle = b;
+ b.stop();
+ break;
+ }
+ }
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+
+ Component shellClient = m.createComponent();
+ Component missing = m.createComponent();
+
+ long shellClientId = shellClient.getComponentDeclaration().getId();
+ long missingId = missing.getComponentDeclaration().getId();
+ shellClient.setImplementation(new ShellClient(e, shellClientId, missingId))
+ .add(m.createServiceDependency()
+ .setService(CommandProcessor.class)
+ .setRequired(true));
+
+ m.add(shellClient);
+ e.waitForStep(3, 5000);
+ // now create a component with a missing dependency
+ missing.setImplementation(new Object() { public String toString() { return "Object"; }})
+ .add(m.createServiceDependency()
+ .setService(Missing.class) // Warning: don't use Object, or Runnable, which are already registered by bndtools ?
+ .setRequired(true));
+
+ m.add(missing);
+ e.step(4);
+ e.waitForStep(5, 5000);
+ m.remove(missing);
+ // now start/stop our test bundle, which publishes a service that uses the dependency manager
+ m_testBundle.start();
+ m_testBundle.stop();
+ e.step(6);
+ e.waitForStep(7, 5000);
+ e.ensure();
+ m.remove(shellClient);
+ m_testBundle.start(); // restart the runtime bundle
+ m.clear();
+ }
+
+ catch (Throwable t) {
+ error("test failed", t);
+ }
+ }
+
+ public class ShellClient {
+ volatile CommandProcessor m_commandProcessor;
+ private final Ensure m_ensure;
+ private final long m_shellClientId;
+ private final long m_missingId;
+
+ public ShellClient(Ensure e, long shellClientId, long missingId) {
+ m_ensure = e;
+ m_shellClientId = shellClientId;
+ m_missingId = missingId;
+ }
+
+ public void start() throws InterruptedException {
+ Thread t = new Thread("Shell Client") {
+ public void run() {
+ String bsn = context.getBundle().getSymbolicName();
+ m_ensure.step(1);
+ execute("dm bid " + m_myBundleId,
+ "[" + m_myBundleId + "] " + bsn + "\n" +
+ " [" + m_shellClientId + "] ShellClient registered\n" +
+ " org.apache.felix.service.command.CommandProcessor service required available\n",
+ "");
+
+ m_ensure.step(2);
+ // see if there's anything that's not available
+ execute("dm notavail bid " + m_myBundleId,
+ "",
+ "");
+ m_ensure.step(3);
+ // check again, now there should be something missing
+ m_ensure.waitForStep(4, 5000);
+ execute("dm notavail bid " + m_myBundleId,
+ "[" + m_myBundleId + "] " + bsn + "\n" +
+ " [" + m_missingId + "] Object unregistered\n" +
+ " " + Missing.class.getName() + " service required unavailable\n",
+ "");
+ m_ensure.step(5);
+ m_ensure.waitForStep(6, 5000);
+ // this next step actually triggers the bug in FELIX-2955
+ execute("dm notavail bid " + m_myBundleId,
+ "",
+ "");
+ m_ensure.step(7);
+ }
+ };
+ t.start();
+ }
+
+ @Override
+ public String toString() {
+ return "ShellClient";
+ }
+
+ public void execute(String command, String expectedOutput, String expectedError) {
+ try {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ ByteArrayOutputStream error = new ByteArrayOutputStream();
+ CommandSession session = m_commandProcessor.createSession(System.in, new PrintStream(output), new PrintStream(error));
+ session.execute(command);
+
+ String out = output.toString();
+ Assert.assertEquals(expectedOutput, out.toString());
+ Assert.assertEquals(expectedError, error.toString());
+ }
+ catch (Throwable throwable) {
+ m_ensure.throwable(throwable);
+ }
+ }
+ }
+
+ public static class Missing {
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX3008_FilterIndexStartupTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX3008_FilterIndexStartupTest.java
new file mode 100644
index 0000000..f5b3f41
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX3008_FilterIndexStartupTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.itest.api;
+//import static org.ops4j.pax.exam.CoreOptions.waitForFrameworkStartupFor;
+//import static org.ops4j.pax.exam.container.def.PaxRunnerOptions.vmOption;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.Bundle;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX3008_FilterIndexStartupTest extends TestBase {
+ public void testNormalStart() throws Exception {
+ System.setProperty("org.apache.felix.dependencymanager.filterindex", "objectClass");
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a provider
+ Provider provider = new Provider();
+ // activate it
+ Component p = m.createComponent()
+ .setInterface(Service.class.getName(), null)
+ .setImplementation(provider);
+
+ Consumer consumer = new Consumer(e);
+ Component c = m.createComponent()
+ .setImplementation(consumer)
+ .add(m.createServiceDependency()
+ .setService(Service.class)
+ .setRequired(true)
+ );
+
+ m.add(p);
+ m.add(c);
+ e.waitForStep(1, 5000);
+ m.remove(p);
+ e.waitForStep(2, 5000);
+ m.remove(c);
+
+ Assert.assertEquals("Dependency manager bundle should be active.", Bundle.ACTIVE, context.getBundle().getState());
+ }
+
+ public static class Consumer {
+ volatile Service m_service;
+ private final Ensure m_ensure;
+
+ public Consumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void start() {
+ System.out.println("start");
+ m_ensure.step(1);
+ }
+
+ public void stop() {
+ System.out.println("stop");
+ m_ensure.step(2);
+ }
+ }
+
+ public static interface Service {
+ }
+
+ public static class Provider implements Service {
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX3057_EmptyServiceReferenceArray.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX3057_EmptyServiceReferenceArray.java
new file mode 100644
index 0000000..07ce09b
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX3057_EmptyServiceReferenceArray.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.itest.api;
+//import static org.ops4j.pax.exam.CoreOptions.waitForFrameworkStartupFor;
+//import static org.ops4j.pax.exam.container.def.PaxRunnerOptions.vmOption;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX3057_EmptyServiceReferenceArray extends TestBase {
+ public void testWithoutIndex() throws Exception {
+ executeTest(context);
+ }
+
+ public void testWithIndex() throws Exception {
+ System.setProperty(DependencyManager.SERVICEREGISTRY_CACHE_INDICES, "objectClass");
+ executeTest(context);
+ }
+
+ private void executeTest(BundleContext context) throws InvalidSyntaxException {
+ DependencyManager m = getDM();
+ Assert.assertNull("Looking up a non-existing service should return null.", m.getBundleContext().getServiceReferences(Service.class.getName(), "(objectClass=*)"));
+ Assert.assertNull("Looking up a non-existing service should return null.", m.getBundleContext().getAllServiceReferences(Service.class.getName(), "(objectClass=*)"));
+ Assert.assertNull("Looking up a non-existing service should return null.", m.getBundleContext().getServiceReference(Service.class.getName()));
+ }
+
+ /** Dummy interface for lookup. */
+ public static interface Service {}
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX3337_UpdatedConfigurationDependencyWithPropagationTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX3337_UpdatedConfigurationDependencyWithPropagationTest.java
new file mode 100644
index 0000000..368acb8
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX3337_UpdatedConfigurationDependencyWithPropagationTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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.itest.api;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Properties;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+
+
+/**
+ * This test validates the following scenario:
+ * - Service S1 depends on a ConfigurationDependency with propagate = true
+ * - Service S2 depends on S1 (and has access to the S1 configuration using the S1 service
+ * properties (because the ConfigurationDependency is propagated)
+ * - then the S1 PID is updated from ConfigAdmin
+ * - S1 is then called in its updated callback
+ * - S2 is called in its "change" callback.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class FELIX3337_UpdatedConfigurationDependencyWithPropagationTest extends TestBase {
+ /*
+ * This Pojo creates the configuration pid "test".
+ */
+ static class ConfigurationCreator {
+ private volatile ConfigurationAdmin m_ca;
+ org.osgi.service.cm.Configuration m_conf;
+
+ public void init() {
+ try {
+ m_conf = m_ca.getConfiguration("test", null);
+ Hashtable props = new Properties();
+ props.put("testkey", "testvalue");
+ m_conf.update(props);
+ }
+ catch (IOException e) {
+ Assert.fail("Could not create configuration: " + e.getMessage());
+ }
+ }
+
+ public void update() {
+ try {
+ Hashtable props = new Properties();
+ props.put("testkey", "testvalue");
+ props.put("testkey2", "testvalue2");
+ m_conf.update(props);
+ } catch (IOException e) {
+ Assert.fail("Could not update the configured property: " + e.toString());
+ }
+ }
+ }
+
+ static class S1 implements ManagedService {
+ private Ensure m_ensure;
+ private boolean m_initialized;
+
+ public S1(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void updated(Dictionary props) throws ConfigurationException {
+ if (props != null) {
+ if (!m_initialized) {
+ m_ensure.step(1);
+ m_initialized = true;
+ } else {
+ // we are updated
+ m_ensure.step(3);
+ }
+ }
+ }
+ }
+
+ static class S2 {
+ private final Ensure m_ensure;
+
+ public S2(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void add(S1 s1) {
+ m_ensure.step(2);
+ }
+
+ public void change(S1 runnable) {
+ m_ensure.step(4);
+ }
+ }
+
+ public void testComponentWithRequiredUpdatedConfigurationAndServicePropertyPropagation() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ ConfigurationCreator confCreator = new ConfigurationCreator();
+ Component s1 = m.createComponent()
+ .setImplementation(new S1(e))
+ .setInterface(S1.class.getName(), null)
+ .add(m.createConfigurationDependency()
+ .setPid("test")
+ .setPropagate(true));
+ Component s2 = m.createComponent()
+ .setImplementation(new S2(e))
+ .add(m.createServiceDependency()
+ .setService(S1.class, ("(testkey=testvalue)"))
+ .setRequired(true)
+ .setCallbacks("add", "change", null));
+ Component s3 = m.createComponent()
+ .setImplementation(confCreator)
+ .add(m.createServiceDependency()
+ .setService(ConfigurationAdmin.class)
+ .setRequired(true));
+
+ m.add(s1);
+ m.add(s2);
+ m.add(s3);
+ e.waitForStep(2, 5000);
+ confCreator.update();
+ e.waitForStep(4, 5000);
+ m.remove(s1);
+ m.remove(s2);
+ m.remove(s3);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX4158_DependencyDeclarationTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX4158_DependencyDeclarationTest.java
new file mode 100644
index 0000000..11edb49
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX4158_DependencyDeclarationTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.itest.api;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentDeclaration;
+import org.apache.felix.dm.ComponentDependencyDeclaration;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.Bundle;
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX4158_DependencyDeclarationTest extends TestBase {
+ public void testServiceDependencyDeclaration() {
+ DependencyManager m = getDM();
+ Component c = m.createComponent()
+ .setImplementation(new Object())
+ .add(m.createServiceDependency().setService(LogService.class, "(foo=bar)"));
+
+ ComponentDeclaration cd = c.getComponentDeclaration();
+ ComponentDependencyDeclaration[] cdds = cd.getComponentDependencies();
+ Assert.assertNotNull(cdds);
+ Assert.assertNotNull(cdds.length == 1);
+ Assert.assertEquals(cdds[0].getName(), "org.osgi.service.log.LogService (foo=bar)");
+ Assert.assertEquals(cdds[0].getSimpleName(), "org.osgi.service.log.LogService");
+ Assert.assertNotNull(cdds[0].getFilter());
+ Assert.assertEquals(cdds[0].getFilter(), "(foo=bar)");
+ m.clear();
+ }
+
+ public void testConfigurationDependencyDeclaration() {
+ DependencyManager m = getDM();
+ Component c = m.createComponent()
+ .setImplementation(new Object())
+ .add(m.createConfigurationDependency().setPid("foo"));
+
+ ComponentDeclaration cd = c.getComponentDeclaration();
+ ComponentDependencyDeclaration[] cdds = cd.getComponentDependencies();
+ Assert.assertNotNull(cdds);
+ Assert.assertNotNull(cdds.length == 1);
+ Assert.assertEquals(cdds[0].getName(), "foo");
+ Assert.assertEquals(cdds[0].getSimpleName(), "foo");
+ Assert.assertNull(cdds[0].getFilter());
+ m.clear();
+ }
+
+ public void testResourceDependencyDeclaration() throws MalformedURLException {
+ DependencyManager m = getDM();
+ Component c = m.createComponent()
+ .setImplementation(new Object())
+ .add(m.createResourceDependency()
+ .setResource(new URL("file://localhost/path/to/file1.txt")));
+
+ ComponentDeclaration cd = c.getComponentDeclaration();
+ ComponentDependencyDeclaration[] cdds = cd.getComponentDependencies();
+ Assert.assertNotNull(cdds);
+ Assert.assertNotNull(cdds.length == 1);
+ Assert.assertEquals(cdds[0].getName(), "file://localhost/path/to/file1.txt");
+ Assert.assertNotNull(cdds[0].getSimpleName());
+ Assert.assertEquals(cdds[0].getSimpleName(), "file://localhost/path/to/file1.txt");
+ Assert.assertNull(cdds[0].getFilter());
+ m.clear();
+ }
+
+ public void testResourceDependencyDeclarationWithFilter() {
+ DependencyManager m = getDM();
+ Component c = m.createComponent()
+ .setImplementation(new Object())
+ .add(m.createResourceDependency()
+ .setFilter("(&(path=/path/to/*.txt)(host=localhost))"));
+
+ ComponentDeclaration cd = c.getComponentDeclaration();
+ ComponentDependencyDeclaration[] cdds = cd.getComponentDependencies();
+ Assert.assertNotNull(cdds);
+ Assert.assertNotNull(cdds.length == 1);
+ Assert.assertEquals(cdds[0].getName(), ("(&(path=/path/to/*.txt)(host=localhost))"));
+ Assert.assertNull(cdds[0].getSimpleName());
+ Assert.assertNotNull(cdds[0].getFilter());
+ Assert.assertEquals(cdds[0].getFilter(), "(&(path=/path/to/*.txt)(host=localhost))");
+ m.clear();
+ }
+
+ public void testBundleDependencyDeclaration() throws MalformedURLException {
+ DependencyManager m = getDM();
+ Component c = m.createComponent()
+ .setImplementation(new Object())
+ .add(m.createBundleDependency());
+
+ ComponentDeclaration cd = c.getComponentDeclaration();
+ ComponentDependencyDeclaration[] cdds = cd.getComponentDependencies();
+ Assert.assertNotNull(cdds);
+ Assert.assertNotNull(cdds.length == 1);
+ Assert.assertEquals(cdds[0].getName(), "active installed resolved");
+ Assert.assertNotNull(cdds[0].getSimpleName());
+ Assert.assertEquals(cdds[0].getSimpleName(), "active installed resolved");
+ Assert.assertNull(cdds[0].getFilter());
+ m.clear();
+ }
+
+ public void testBundleDependencyDeclarationWithMask() throws MalformedURLException {
+ DependencyManager m = getDM();
+ Component c = m.createComponent()
+ .setImplementation(new Object())
+ .add(m.createBundleDependency()
+ .setStateMask( Bundle.ACTIVE | Bundle.RESOLVED));
+
+ ComponentDeclaration cd = c.getComponentDeclaration();
+ ComponentDependencyDeclaration[] cdds = cd.getComponentDependencies();
+ Assert.assertNotNull(cdds);
+ Assert.assertNotNull(cdds.length == 1);
+ Assert.assertEquals(cdds[0].getName(), "active resolved");
+ Assert.assertNotNull(cdds[0].getSimpleName());
+ Assert.assertEquals(cdds[0].getSimpleName(), "active resolved");
+ Assert.assertNull(cdds[0].getFilter());
+ m.clear();
+ }
+
+ public void testBundleDependencyDeclarationWithFilter() throws MalformedURLException {
+ DependencyManager m = getDM();
+ Component c = m.createComponent()
+ .setImplementation(new Object())
+ .add(m.createBundleDependency()
+ .setStateMask( Bundle.ACTIVE )
+ .setFilter("(DependencyManager-Component=*)"));
+
+ ComponentDeclaration cd = c.getComponentDeclaration();
+ ComponentDependencyDeclaration[] cdds = cd.getComponentDependencies();
+ Assert.assertNotNull(cdds);
+ Assert.assertNotNull(cdds.length == 1);
+ Assert.assertEquals(cdds[0].getName(), "active (DependencyManager-Component=*)");
+ Assert.assertNotNull(cdds[0].getSimpleName());
+ Assert.assertEquals(cdds[0].getSimpleName(), "active");
+ Assert.assertNotNull(cdds[0].getFilter());
+ Assert.assertEquals(cdds[0].getFilter(), "(DependencyManager-Component=*)");
+ m.clear();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX4361_ConcurrentComponentListingTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX4361_ConcurrentComponentListingTest.java
new file mode 100644
index 0000000..0752c3f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX4361_ConcurrentComponentListingTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.itest.api;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.TestBase;
+
+
+/**
+ * Test for FELIX-4361 The DependencyManager.getComponents method failed in a concurrent situation on iterating the
+ * result of the method.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX4361_ConcurrentComponentListingTest extends TestBase {
+
+ @SuppressWarnings("rawtypes")
+ public void testConcurrentGetComponentsManipulation() {
+ DependencyManager dm = getDM();
+ dm.add(dm.createComponent().setImplementation(Object.class));
+ Iterator iterator = dm.getComponents().iterator();
+ dm.add(dm.createComponent().setImplementation(Object.class));
+ iterator.next();
+ dm.clear();
+ }
+
+ public void testConcurrentGetComponentsMultipleThreads() {
+ final DependencyManager m = getDM();
+ final AtomicInteger errors = new AtomicInteger(0);
+ final AtomicInteger componentsAdded = new AtomicInteger(0);
+ final int max = 10000;
+ final AtomicBoolean isRunning = new AtomicBoolean(true);
+
+ ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1);
+ Runnable readTask = new Runnable() {
+ @SuppressWarnings({ "rawtypes", "unused" })
+ public void run() {
+ while (isRunning.get()) {
+ try {
+ List components = m.getComponents();
+ for (Object component : components) {
+ // Just iterating the components should check for concurrent modifications
+ }
+ }
+ catch (Exception ex) {
+ errors.addAndGet(1);
+ ex.printStackTrace();
+ }
+ }
+ }
+ };
+
+ Callable<Boolean> modifyTask = new Callable<Boolean>() {
+ public Boolean call() throws Exception {
+ try {
+ m.add(m.createComponent().setImplementation(Object.class));
+ componentsAdded.addAndGet(1);
+ return true;
+ }
+ catch (Exception ex) {
+ return false;
+ }
+ }
+ };
+
+ executorService.submit(readTask);
+ for (int i = 0; i < max; i++) {
+ executorService.submit(modifyTask);
+ }
+ isRunning.set(false);
+ executorService.shutdown();
+
+ try {
+ executorService.awaitTermination(30, TimeUnit.SECONDS);
+ }
+ catch (InterruptedException e) {
+ }
+ Assert.assertEquals(0, errors.get());
+ Assert.assertEquals(max, componentsAdded.get());
+ m.clear();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX_4168AdapterWithDynamicallyAddedDependencies.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX_4168AdapterWithDynamicallyAddedDependencies.java
new file mode 100644
index 0000000..cffdcf9
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX_4168AdapterWithDynamicallyAddedDependencies.java
@@ -0,0 +1,110 @@
+/**
+ * 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.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX_4168AdapterWithDynamicallyAddedDependencies extends TestBase {
+ public void testAdapterWithExtraDependenciesAndCallbacks() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+
+ // create a service S2, which will be added to A1 (needs to be available)
+ Component s2 = m.createComponent().setInterface(S2.class.getName(), null).setImplementation(new S2Impl(e));
+ m.add(s2);
+
+ // create a service adapter that adapts to services S1 and has an optional dependency on services S2
+ Component sa = m.createAdapterService(S1.class, null).setImplementation(SA.class);
+ m.add(sa);
+
+ // create a service S1, which triggers the creation of the first adapter instance (A1)
+ Component s1 = m.createComponent().setInterface(S1.class.getName(), null).setImplementation(new S1Impl());
+ m.add(s1);
+
+ // create a second service S1, which triggers the creation of the second adapter instance (A2)
+ Component s1b = m.createComponent().setInterface(S1.class.getName(), null).setImplementation(new S1Impl());
+ m.add(s1b);
+
+ // observe that S2 is also added to A2
+ e.waitForStep(2, 5000);
+
+ // remove S2 again
+ m.remove(s2);
+
+ // make sure both adapters have their "remove" callbacks invoked
+ e.waitForStep(4, 5000);
+ m.clear();
+ }
+
+ static interface S1 {
+ }
+
+ static interface S2 {
+ public void invoke();
+ }
+
+ static class S1Impl implements S1 {
+ }
+
+ static class S2Impl implements S2 {
+
+ private final Ensure m_e;
+
+ public S2Impl(Ensure e) {
+ m_e = e;
+ }
+
+ public void invoke() {
+ m_e.step();
+ }
+ }
+
+ public static class SA {
+ volatile S2 s2;
+ volatile Component component;
+ volatile DependencyManager manager;
+
+ public SA() {
+ System.out.println("Adapter created");
+ }
+
+ public void init() {
+ System.out.println("Adapter init " + s2);
+ component.add(manager.createServiceDependency()
+ .setService(S2.class).setCallbacks("add", "remove").setRequired(true));
+ }
+
+ public void add(S2 s) {
+ System.out.println("adding " + s);
+ s.invoke();
+ }
+
+ public void remove(S2 s) {
+ System.out.println("removing " + s);
+ s.invoke();
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FactoryConfigurationAdapterTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FactoryConfigurationAdapterTest.java
new file mode 100644
index 0000000..315b3b8
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FactoryConfigurationAdapterTest.java
@@ -0,0 +1,224 @@
+/*
+ * 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.itest.api;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class FactoryConfigurationAdapterTest extends TestBase
+{
+ private static Ensure m_ensure;
+
+ public void testFactoryConfigurationAdapter() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ m_ensure = new Ensure();
+
+ // Create a Configuration instance, which will create/update/remove a configuration for factoryPid "MyFactoryPid"
+ ConfigurationCreator configurator = new ConfigurationCreator("MyFactoryPid", "key", "value1");
+ Component s1 = m.createComponent()
+ .setImplementation(configurator)
+ .add(m.createServiceDependency()
+ .setService(ConfigurationAdmin.class)
+ .setRequired(true));
+
+ // Create an Adapter that will be instantiated, once the configuration is created.
+ // This Adapter provides an AdapterService, and depends on an AdapterExtraDependency service.
+ Component s2 = m.createFactoryConfigurationAdapterService("MyFactoryPid", "updated", true /* propagate CM settings */)
+ .setInterface(AdapterService.class.getName(), new Hashtable() {{ put("foo", "bar"); }})
+ .setImplementation(Adapter.class);
+
+ s2.add(m.createServiceDependency()
+ .setService(AdapterExtraDependency.class)
+ .setRequired(true)
+ .setAutoConfig(true));
+
+ // Create extra adapter service dependency upon which our adapter depends on.
+ Component s3 = m.createComponent()
+ .setImplementation(new AdapterExtraDependency())
+ .setInterface(AdapterExtraDependency.class.getName(), null);
+
+ // Create an AdapterService Consumer
+ Component s4 = m.createComponent()
+ .setImplementation(AdapterServiceConsumer.class)
+ .add(m.createServiceDependency()
+ .setService(AdapterService.class)
+ .setRequired(true)
+ .setCallbacks("bind", "change", "remove"));
+
+ // Start services
+ m.add(s1);
+ m.add(s2);
+ m.add(s3);
+ m.add(s4);
+
+ // Wait for step 8: the AdapterService consumer has been injected with the AdapterService, and has called the doService method.
+ m_ensure.waitForStep(8, 10000);
+
+ // Modify configuration.
+ configurator.update("key", "value2");
+
+ // Wait for step 13: the AdapterService has been updated, and the AdapterService consumer has seen the change
+ m_ensure.waitForStep(13, 10000);
+
+ // Remove the configuration
+ m.remove(s1); // The stop method will remove the configuration
+ m_ensure.waitForStep(16, 10000);
+ m.clear();
+ }
+
+ public static class ConfigurationCreator {
+ private volatile ConfigurationAdmin m_ca;
+ private String m_key;
+ private String m_value;
+ private org.osgi.service.cm.Configuration m_conf;
+ private String m_factoryPid;
+
+ public ConfigurationCreator(String factoryPid, String key, String value) {
+ m_factoryPid = factoryPid;
+ m_key = key;
+ m_value = value;
+ }
+
+ public void start() {
+ try {
+ m_ensure.step(1);
+ m_conf = m_ca.createFactoryConfiguration(m_factoryPid, null);
+ Hashtable props = new Hashtable();
+ props.put(m_key, m_value);
+ m_conf.update(props);
+ }
+ catch (IOException e) {
+ Assert.fail("Could not create configuration: " + e.getMessage());
+ }
+ }
+
+ public void update(String key, String val) {
+ Hashtable props = new Hashtable();
+ props.put(key, val);
+ try {
+ m_conf.update(props);
+ }
+ catch (IOException e) {
+ Assert.fail("Could not update configuration: " + e.getMessage());
+ }
+ }
+
+ public void stop() {
+ try
+ {
+ m_conf.delete();
+ }
+ catch (IOException e)
+ {
+ Assert.fail("Could not remove configuration: " + e.toString());
+ }
+ }
+ }
+
+ public interface AdapterService {
+ public void doService();
+ }
+
+ public static class AdapterExtraDependency {
+ }
+
+ public static class Adapter implements AdapterService {
+ volatile AdapterExtraDependency m_extraDependency; // extra dependency.
+ private int updateCount;
+
+ void updated(Dictionary settings) {
+ updateCount ++;
+ if (updateCount == 1) {
+ m_ensure.step(2);
+ Assert.assertEquals(true, "value1".equals(settings.get("key")));
+ m_ensure.step(3);
+ } else if (updateCount == 2) {
+ m_ensure.step(9);
+ Assert.assertEquals(true, "value2".equals(settings.get("key")));
+ m_ensure.step(10);
+ } else {
+ Assert.fail("wrong call to updated method: count=" + updateCount);
+ }
+ }
+
+ public void doService() {
+ m_ensure.step(8);
+ }
+
+ public void start() {
+ m_ensure.step(4);
+ Assert.assertNotNull(m_extraDependency);
+ m_ensure.step(5);
+ }
+
+ public void stop() {
+ m_ensure.step(16);
+ }
+ }
+
+ public static class AdapterServiceConsumer {
+ private AdapterService m_adapterService;
+ private Map m_adapterServiceProperties;
+
+ void bind(Map serviceProperties, AdapterService adapterService) {
+ m_ensure.step(6);
+ m_adapterService = adapterService;
+ m_adapterServiceProperties = serviceProperties;
+ }
+
+ void change(Map serviceProperties, AdapterService adapterService) {
+ m_ensure.step(11);
+ Assert.assertEquals(true, "value2".equals(m_adapterServiceProperties.get("key")));
+ m_ensure.step(12);
+ Assert.assertEquals(true, "bar".equals(m_adapterServiceProperties.get("foo")));
+ m_ensure.step(13);
+ }
+
+ public void start() {
+ m_ensure.step(7);
+ Assert.assertNotNull(m_adapterService);
+ Assert.assertEquals(true, "value1".equals(m_adapterServiceProperties.get("key")));
+ Assert.assertEquals(true, "bar".equals(m_adapterServiceProperties.get("foo")));
+ m_adapterService.doService();
+ }
+
+ public void stop() {
+ m_ensure.step(14);
+ }
+
+ void remove(AdapterService adapterService) {
+ m_ensure.step(15);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FilterIndexResourceAdapterTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FilterIndexResourceAdapterTest.java
new file mode 100644
index 0000000..56437d9
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FilterIndexResourceAdapterTest.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.itest.api;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.junit.Assert;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FilterIndexResourceAdapterTest extends TestBase {
+ public void testBasicResourceAdapter() throws Exception {
+ System.setProperty("org.apache.felix.dependencymanager.filterindex", "objectClass");
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a resource provider
+ ResourceProvider provider = new ResourceProvider(context, new URL("file://localhost/path/to/file1.txt"));
+ // activate it
+ m.add(m.createComponent().setImplementation(provider).add(m.createServiceDependency().setService(ResourceHandler.class).setCallbacks("add", "remove")));
+ // create a resource adapter for our single resource
+ // note that we can provide an actual implementation instance here because there will be only one
+ // adapter, normally you'd want to specify a Class here
+ m.add(m.createResourceAdapterService("(&(path=/path/to/*.txt)(host=localhost))", false, null, "changed")
+ .setImplementation(new ResourceAdapter(e)));
+ // wait until the single resource is available
+ e.waitForStep(3, 5000);
+ // trigger a 'change' in our resource
+ provider.change();
+ // wait until the changed callback is invoked
+ e.waitForStep(4, 5000);
+ m.clear();
+ }
+
+ static class ResourceAdapter {
+ protected URL m_resource; // injected by reflection.
+ private Ensure m_ensure;
+
+ ResourceAdapter(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void start() {
+ m_ensure.step(1);
+ Assert.assertNotNull("resource not injected", m_resource);
+ m_ensure.step(2);
+ try {
+ @SuppressWarnings("unused")
+ InputStream in = m_resource.openStream();
+ }
+ catch (FileNotFoundException e) {
+ m_ensure.step(3);
+ }
+ catch (IOException e) {
+ Assert.fail("We should not have gotten this exception.");
+ }
+ }
+
+ public void changed() {
+ m_ensure.step(4);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/InstanceBoundDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/InstanceBoundDependencyTest.java
new file mode 100644
index 0000000..988588d
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/InstanceBoundDependencyTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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.itest.api;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * This test does some injection tests on components being in INSTANTIATED_AND_WAITING_FOR_REQUIRED state.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class InstanceBoundDependencyTest extends TestBase {
+ Ensure m_e;
+
+ public void testServiceInjection() {
+ DependencyManager m = getDM();
+ m_e = new Ensure();
+
+ // Create a "C" component: it depends on some S1 services, and on some S2 instance-bound services (declared from C.init() method)
+ C cimpl = new C();
+ Component c = m.createComponent().setImplementation(cimpl);
+ c.add(m.createServiceDependency().setService(S1.class).setRequired(true).setCallbacks("addS1", "changeS1", "removeS1").setAutoConfig(true));
+ m.add(c);
+
+ // Add S1 (s1_1): C.add(S1 s1) is called, then init() is called where a dependency is declared on S2
+ Hashtable s1_1_props = new Hashtable();
+ s1_1_props.put("name", "s1_1");
+ s1_1_props.put(Constants.SERVICE_RANKING, new Integer(10));
+ S1Impl s1_1_impl = new S1Impl();
+ Component s1_1 = m.createComponent().setImplementation(s1_1_impl).setInterface(S1.class.getName(), s1_1_props);
+ m.add(s1_1);
+ m_e.waitForStep(1, 5000); // wait until C.init called
+ ServiceReference ref = cimpl.getS1("s1_1");
+ Assert.assertNotNull(ref);
+ Assert.assertNotNull(cimpl.getS1());
+ Assert.assertEquals(s1_1_impl, cimpl.getS1());
+
+ // At this point, MyComponent is in INSTANTIATED_AND_WAITING_FOR_REQUIRED state.
+ // add now add another higher ranked S1 (s1_2) instance. C.add(s1_2) method should be called (the S1 dependency
+ // is not instance bound), and m_s1 autoconfig field should be updated.
+ Hashtable s1_2_props = new Hashtable();
+ s1_2_props.put(Constants.SERVICE_RANKING, new Integer(20));
+ s1_2_props.put("name", "s1_2");
+ S1Impl s1_2_impl = new S1Impl();
+ Component s1_2 = m.createComponent().setImplementation(s1_2_impl).setInterface(S1.class.getName(), s1_2_props);
+ m.add(s1_2);
+ ref = cimpl.getS1("s1_2");
+ Assert.assertNotNull(ref);
+ Assert.assertNotNull(cimpl.getS1());
+ Assert.assertEquals(s1_2_impl, cimpl.getS1()); // must return s1_2 with ranking = 20
+
+ // Now, change the s1_1 service properties: C.changed(s1_1) should be called, and C.m_s1AutoConfig should be updated
+ s1_1_props.put(Constants.SERVICE_RANKING, new Integer(30));
+ s1_1.setServiceProperties(s1_1_props);
+ ref = cimpl.getS1("s1_1");
+ Assert.assertNotNull(ref);
+ Assert.assertEquals(new Integer(30), ref.getProperty(Constants.SERVICE_RANKING));
+ Assert.assertNotNull(cimpl.getS1());
+ Assert.assertEquals(s1_1_impl, cimpl.getS1());
+
+ // Now, remove the s1_1: C.remove(s1_1) should be called, and C.m_s1AutoConfig should be updated
+ m.remove(s1_1);
+ ref = cimpl.getS1("s1_1");
+ Assert.assertNull(cimpl.getS1("s1_1"));
+ Assert.assertNotNull(cimpl.getS1());
+ Assert.assertEquals(s1_2_impl, cimpl.getS1());
+ m.clear();
+ }
+
+ // C component depends on some S1 required services
+ public interface S1 {
+ }
+
+ public class S1Impl implements S1 {
+ }
+
+ public interface S2 {
+ }
+
+ public class S2Impl implements S2 {
+ }
+
+ // Our "C" component: it depends on S1 (required) and S2 (required/instance bound)
+ class C {
+ final Map<String, ServiceReference> m_s1Map = new HashMap();
+ final Map<String, ServiceReference> m_s2Map = new HashMap();
+ volatile S1 m_s1; // auto configured
+
+ S1 getS1() {
+ return m_s1;
+ }
+
+ void addS1(ServiceReference s1) {
+ m_s1Map.put((String) s1.getProperty("name"), s1);
+ }
+
+ void changeS1(ServiceReference s1) {
+ m_s1Map.put((String) s1.getProperty("name"), s1);
+ }
+
+ void removeS1(ServiceReference s1) {
+ m_s1Map.remove((String) s1.getProperty("name"));
+ }
+
+ void addS2(ServiceReference s2) {
+ m_s2Map.put((String) s2.getProperty("name"), s2);
+ }
+
+ void changeS2(ServiceReference s2) {
+ m_s2Map.put((String) s2.getProperty("name"), s2);
+ }
+
+ void removeS2(ServiceReference s2) {
+ m_s2Map.remove((String) s2.getProperty("name"));
+ }
+
+ ServiceReference getS1(String name) {
+ return m_s1Map.get(name);
+ }
+
+ ServiceReference getS2(String name) {
+ return m_s2Map.get(name);
+ }
+
+ void init(Component c) {
+ DependencyManager m = c.getDependencyManager();
+ c.add(m.createServiceDependency().setService(S2.class).setRequired(true).setCallbacks("addS2", "changeS2", "removeS2"));
+ m_e.step(1);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ModifiedBundleDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ModifiedBundleDependencyTest.java
new file mode 100644
index 0000000..76d681c
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ModifiedBundleDependencyTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.itest.api;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+
+/**
+ * Test for FELIX-4334 issue.
+ *
+ * Two components: A, B
+ *
+ * - A provided.
+ * - B has a bundle dependency on the dependency manager shell bundle, which is currently stopped.
+ * - B has an instance bound dependency on A.
+ * - Now unregister A.
+ * - As a result of that, B becomes unavailable and is unbound from A. But B is not destroyed, because A dependency
+ * is "instance bound". So B is still bound to the bundle dependency.
+ * - Now, someone starts the dependency manager shell bundle: B then shall be called in its "changed" callback.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ModifiedBundleDependencyTest extends TestBase {
+ public static interface A {
+ }
+
+ static class AImpl implements A {
+ }
+
+ public static interface B {
+ }
+
+ static class BImpl implements B {
+ final Ensure m_e;
+
+ BImpl(Ensure e) {
+ m_e = e;
+ }
+
+ public void add(Bundle dmTest) {
+ m_e.step(1);
+ }
+
+ void init(Component c) {
+ m_e.step(2);
+ DependencyManager dm = c.getDependencyManager();
+ c.add(dm.createServiceDependency().setService(A.class).setRequired(true).setCallbacks("add", "remove"));
+ }
+
+ public void add(A a) {
+ m_e.step(3);
+ }
+
+ public void start() {
+ m_e.step(4);
+ }
+
+ public void stop() {
+ m_e.step(5);
+ }
+
+ public void remove(A a) {
+ m_e.step(6);
+ }
+
+ public void change(Bundle dmTest) { // called two times: one for STARTING, one for STARTED
+ m_e.step();
+ }
+
+ public void destroy() {
+ m_e.step(9);
+ }
+
+ public void remove(Bundle dmTest) {
+ m_e.step(10);
+ }
+ }
+
+ public void testAdapterWithChangedInstanceBoundDependency() {
+ DependencyManager m = getDM();
+ Ensure e = new Ensure();
+
+ Component a = m.createComponent()
+ .setImplementation(new AImpl())
+ .setInterface(A.class.getName(), null);
+
+ Component b = m.createComponent()
+ .setInterface(B.class.getName(), null)
+ .setImplementation(new BImpl(e))
+ .add(m.createBundleDependency()
+ .setFilter("(Bundle-SymbolicName=org.apache.felix.metatype)")
+ .setStateMask(Bundle.INSTALLED|Bundle.ACTIVE|Bundle.RESOLVED|Bundle.STARTING)
+ .setRequired(true)
+ .setCallbacks("add", "change", "remove"));
+
+ Bundle dmtest = getBundle("org.apache.felix.metatype");
+ try {
+ dmtest.stop();
+ } catch (BundleException e1) {
+ Assert.fail("could not find metatype bundle");
+ }
+
+ m.add(a);
+ m.add(b);
+
+ e.waitForStep(4, 5000);
+ m.remove(a); // B will loose A and will enter into "waiting for required (instantiated)" state.
+ System.out.println("Starting metatype bundle ...");
+ try {
+ dmtest.start();
+ } catch (BundleException e1) {
+ Assert.fail("could not start metatype bundle");
+ }
+ e.waitForStep(7, 5000);
+ m.remove(b);
+ e.waitForStep(10, 5000);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleExtraDependenciesTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleExtraDependenciesTest.java
new file mode 100644
index 0000000..32f7855
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleExtraDependenciesTest.java
@@ -0,0 +1,222 @@
+/*
+ * 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.itest.api;
+
+import java.util.Hashtable;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class MultipleExtraDependenciesTest extends TestBase {
+ /**
+ * Check that list of extra dependencies (defined from init method) are handled properly.
+ * The extra dependencies are added using a List object (Component.add(List)).
+ * A component c1 will define two extra dependencies over *available* c4/c5 services.
+ */
+ public void testWithTwoAvailableExtraDependency() {
+ DependencyManager m = getDM();
+ // Helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ Component c1 = m.createComponent()
+ .setInterface(Service1.class.getName(), null)
+ .setImplementation(new MyComponent1(e))
+ .add(m.createServiceDependency()
+ .setService(Service2.class)
+ .setRequired(true)
+ .setAutoConfig("m_service2"));
+
+ Component c2 = m.createComponent()
+ .setImplementation(new MyComponent2(e))
+ .add(m.createServiceDependency()
+ .setService(Service1.class)
+ .setRequired(false)
+ .setAutoConfig(false)
+ .setCallbacks("added", null, null));
+
+ Component c3 = m.createComponent()
+ .setInterface(Service2.class.getName(), null)
+ .setImplementation(Service2Impl.class);
+
+ Hashtable h = new Hashtable();
+ h.put("type", "xx");
+ Component c4 = m.createComponent()
+ .setInterface(Service3.class.getName(), h)
+ .setImplementation(Service3Impl1.class);
+
+ h = new Hashtable();
+ h.put("type", "yy");
+ Component c5 = m.createComponent()
+ .setInterface(Service3.class.getName(), h)
+ .setImplementation(Service3Impl2.class);
+
+
+ System.out.println("\n+++ Adding c2 / MyComponent2");
+ m.add(c2);
+ System.out.println("\n+++ Adding c3 / Service2");
+ m.add(c3);
+ System.out.println("\n+++ Adding c4 / Service3(xx)");
+ m.add(c4);
+ System.out.println("\n+++ Adding c5 / Service3(yy)");
+ m.add(c5);
+ System.out.println("\n+++ Adding c1 / MyComponent1");
+ // c1 have declared two extra dependency on Service3 (xx/yy).
+ // both extra dependencies are available, so the c1 component should be started immediately.
+ m.add(c1);
+ e.waitForStep(3, 3000);
+ m.clear();
+ }
+
+ /**
+ * Check that list of extra dependencies (defined from init method) are handled properly.
+ * The extra dependencies are added using a List object (Component.add(List)).
+ * A component c1 will define two extra dependencies over c4/c5. At the point c1.init()
+ * is adding the two extra dependencies from its init method, c4 is available, but not c5.
+ * So, c1 is not yet activated.
+ * Then c5 is added, and it triggers the c1 activation ...
+ */
+ public void testWithOneAvailableExtraDependency() {
+ DependencyManager m = getDM();
+ // Helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ Component c1 = m.createComponent()
+ .setInterface(Service1.class.getName(), null)
+ .setImplementation(new MyComponent1(e))
+ .add(m.createServiceDependency()
+ .setService(Service2.class)
+ .setRequired(true)
+ .setAutoConfig("m_service2"));
+
+ Component c2 = m.createComponent()
+ .setImplementation(new MyComponent2(e))
+ .add(m.createServiceDependency()
+ .setService(Service1.class)
+ .setRequired(false)
+ .setAutoConfig(false)
+ .setCallbacks("added", null, null));
+
+ Component c3 = m.createComponent()
+ .setInterface(Service2.class.getName(), null)
+ .setImplementation(Service2Impl.class);
+
+ Hashtable h = new Hashtable();
+ h.put("type", "xx");
+ Component c4 = m.createComponent()
+ .setInterface(Service3.class.getName(), h)
+ .setImplementation(Service3Impl1.class);
+
+ h = new Hashtable();
+ h.put("type", "yy");
+ Component c5 = m.createComponent()
+ .setInterface(Service3.class.getName(), h)
+ .setImplementation(Service3Impl2.class);
+
+
+ System.out.println("\n+++ Adding c2 / MyComponent2");
+ m.add(c2);
+ System.out.println("\n+++ Adding c3 / Service2");
+ m.add(c3);
+ System.out.println("\n+++ Adding c4 / Service3(xx)");
+ m.add(c4);
+ System.out.println("\n+++ Adding c1 / MyComponent1");
+ m.add(c1);
+
+ // c1 have declared two extra dependency on Service3 (xx/yy).
+ // So, because we have not yet added c5 (yy), c1 should not be started currently.
+ // But, now, we'll add c5 (Service3/yy) and c1 should then be started ...
+ System.out.println("\n+++ Adding c5 / Service3(yy)");
+ m.add(c5);
+ e.waitForStep(3, 3000);
+ m.clear();
+ }
+
+
+ public interface Service1 {}
+ public interface Service2 {}
+ public interface Service3 {}
+
+ public static class Service2Impl implements Service2 {}
+ public static class Service3Impl1 implements Service3 {}
+ public static class Service3Impl2 implements Service3 {}
+
+ public static class MyComponent1 implements Service1 {
+ Service2 m_service2;
+ Service3 m_service3_xx;
+ Service3 m_service3_yy;
+ Ensure m_ensure;
+
+ public MyComponent1(Ensure e) {
+ m_ensure = e;
+ }
+
+ void init(Component c) {
+ m_ensure.step(1);
+ DependencyManager dm = c.getDependencyManager();
+ // Service3/xx currently available
+ Dependency d1 =
+ dm.createServiceDependency()
+ .setService(Service3.class, "(type=xx)")
+ .setRequired(true)
+ .setAutoConfig("m_service3_xx");
+
+ // Service3/yy not yet available
+ Dependency d2 =
+ dm.createServiceDependency()
+ .setService(Service3.class, "(type=yy)")
+ .setRequired(true)
+ .setAutoConfig("m_service3_yy");
+ c.add(d1, d2);
+ }
+
+ void start() {
+ System.out.println("MyComponent1.start");
+ Assert.assertNotNull(m_service2);
+ Assert.assertNotNull(m_service3_xx);
+ Assert.assertNotNull(m_service3_yy);
+ m_ensure.step(2);
+ }
+ }
+
+ public static class MyComponent2 {
+ Ensure m_ensure;
+
+ public MyComponent2(Ensure e) {
+ m_ensure = e;
+ }
+
+ void added(Service1 s1) {
+ System.out.println("MyComponent2.bind(" + s1 + ")");
+ Assert.assertNotNull(s1);
+ m_ensure.step(3);
+ }
+
+ void start() {
+ System.out.println("MyComponent2.start");
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleExtraDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleExtraDependencyTest.java
new file mode 100644
index 0000000..9816b0f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleExtraDependencyTest.java
@@ -0,0 +1,226 @@
+/*
+ * 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.itest.api;
+
+import java.util.Hashtable;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * Test which validates multi-dependencies combination.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class MultipleExtraDependencyTest extends TestBase {
+ public void testMultipleExtraDependencies()
+ {
+ DependencyManager m = getDM();
+ Ensure e = new Ensure();
+
+ Component sp2 = m.createComponent()
+ .setImplementation(ServiceProvider2.class).setInterface(ServiceProvider2.class.getName(), null)
+ .add(m.createServiceDependency()
+ .setService(Runnable.class, "(foo=bar)")
+ .setRequired(false)
+ .setAutoConfig("m_runnable"))
+ .add(m.createServiceDependency()
+ .setService(Sequencer.class)
+ .setRequired(true)
+ .setCallbacks("bind", null))
+ .setCallbacks(null, "start", "stop", null)
+ .setComposition("getComposition");
+
+ Component sp = m.createComponent()
+ .setImplementation(ServiceProvider.class)
+ .setInterface(ServiceInterface.class.getName(),
+ new Hashtable() {{ put("foo", "bar"); }})
+ .add(m.createServiceDependency()
+ .setService(Sequencer.class)
+ .setRequired(true)
+ .setAutoConfig("m_sequencer"))
+ .add(m.createServiceDependency()
+ .setService(ServiceProvider2.class)
+ .setRequired(true)
+ .setCallbacks("bind", "unbind"))
+ .setCallbacks(null, "start", "stop", null);
+
+ Component sc = m.createComponent()
+ .setImplementation(ServiceConsumer.class)
+ .add(m.createServiceDependency()
+ .setService(Sequencer.class)
+ .setRequired(true)
+ .setAutoConfig("m_sequencer"))
+ .add(m.createServiceDependency()
+ .setService(ServiceInterface.class, "(foo=bar)")
+ .setRequired(true)
+ .setAutoConfig("m_service"))
+ .setCallbacks(null, "start", "stop", null);
+
+ Component sequencer =
+ m.createComponent().setImplementation(new SequencerImpl(e))
+ .setInterface(Sequencer.class.getName(), null);
+ m.add(sp2);
+ m.add(sp);
+ m.add(sc);
+ m.add(sequencer);
+
+ // Check if ServiceProvider component have been initialized orderly
+ e.waitForStep(7, 5000);
+
+ // Stop the test.annotation bundle
+ m.remove(sequencer);
+ m.remove(sp);
+ m.remove(sp2);
+ m.remove(sc);
+
+ // And check if ServiceProvider2 has been deactivated orderly
+ e.waitForStep(11, 5000);
+ }
+
+ public interface Sequencer
+ {
+ void step();
+ void step(int step);
+ void waitForStep(int step, int timeout);
+ }
+
+ public static class SequencerImpl implements Sequencer {
+ Ensure m_ensure;
+
+ public SequencerImpl(Ensure e)
+ {
+ m_ensure = e;
+ }
+
+ public void step()
+ {
+ m_ensure.step();
+ }
+
+ public void step(int step)
+ {
+ m_ensure.step(step);
+ }
+
+ public void waitForStep(int step, int timeout)
+ {
+ m_ensure.waitForStep(step, timeout);
+ }
+ }
+
+ public interface ServiceInterface
+ {
+ public void doService();
+ }
+
+ public static class ServiceConsumer
+ {
+ volatile Sequencer m_sequencer;
+ volatile ServiceInterface m_service;
+
+ void start()
+ {
+ m_sequencer.step(6);
+ m_service.doService();
+ }
+
+ void stop()
+ {
+ m_sequencer.step(8);
+ }
+ }
+
+ public static class ServiceProvider implements ServiceInterface
+ {
+ Sequencer m_sequencer;
+ ServiceProvider2 m_serviceProvider2;
+
+ void bind(ServiceProvider2 provider2)
+ {
+ m_serviceProvider2 = provider2;
+ }
+
+ void start()
+ {
+ m_serviceProvider2.step(4);
+ m_sequencer.step(5);
+ }
+
+ void stop()
+ {
+ m_sequencer.step(9);
+ }
+
+ void unbind(ServiceProvider2 provider2)
+ {
+ m_sequencer.step(10);
+ }
+
+ public void doService()
+ {
+ m_sequencer.step(7);
+ }
+ }
+
+ public static class ServiceProvider2
+ {
+ Composite m_composite = new Composite();
+ Sequencer m_sequencer;
+ Runnable m_runnable;
+
+ void bind(Sequencer seq)
+ {
+ m_sequencer = seq;
+ m_sequencer.step(1);
+ }
+
+ void start()
+ {
+ m_sequencer.step(3);
+ m_runnable.run(); // NullObject
+ }
+
+ public void step(int step) // called by ServiceProvider.start() method
+ {
+ m_sequencer.step(step);
+ }
+
+ void stop()
+ {
+ m_sequencer.step(11);
+ }
+
+ Object[] getComposition()
+ {
+ return new Object[] { this, m_composite };
+ }
+ }
+
+ public static class Composite
+ {
+ void bind(Sequencer seq)
+ {
+ seq.step(2);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleExtraDependencyTest2.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleExtraDependencyTest2.java
new file mode 100644
index 0000000..9889f3a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleExtraDependencyTest2.java
@@ -0,0 +1,257 @@
+/*
+ * 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.itest.api;
+
+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.ServiceDependency;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * Tests for extra dependencies which are declared from service's init method.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class MultipleExtraDependencyTest2 extends TestBase {
+ public void testMultipleExtraDependencies()
+ {
+ DependencyManager m = getDM();
+ Ensure e = new Ensure();
+
+ Component sp2 = m.createComponent()
+ .setImplementation(ServiceProvider2.class).setInterface(ServiceProvider2.class.getName(), null)
+ .setCallbacks("init", "start", "stop", null)
+ .setComposition("getComposition");
+
+ Component sp = m.createComponent()
+ .setImplementation(ServiceProvider.class)
+ .setInterface(ServiceInterface.class.getName(), new Hashtable() {{ put("foo", "bar"); }})
+ .setCallbacks("init", "start", "stop", null);
+
+ Component sc = m.createComponent()
+ .setImplementation(ServiceConsumer.class)
+ .setCallbacks("init", "start", "stop", null);
+
+ // Provide the Sequencer service to the MultipleAnnotationsTest class.
+ Component sequencer =
+ m.createComponent().setImplementation(new SequencerImpl(e))
+ .setInterface(Sequencer.class.getName(), null);
+ m.add(sp2);
+ m.add(sp);
+ m.add(sc);
+ m.add(sequencer);
+
+ // Check if the test.annotation components have been initialized orderly
+ e.waitForStep(7, 10000);
+
+ // Stop the test.annotation bundle
+ m.remove(sequencer);
+ m.remove(sp);
+ m.remove(sp2);
+ m.remove(sc);
+
+// m.remove(sp2);
+// m.remove(sc);
+// m.remove(sp);
+// m.remove(sequencer);
+
+
+
+ // And check if the test.annotation bundle has been deactivated orderly
+ e.waitForStep(11, 10000);
+ m.clear();
+ }
+
+ public interface Sequencer
+ {
+ void step();
+ void step(int step);
+ void waitForStep(int step, int timeout);
+ }
+
+ public static class SequencerImpl implements Sequencer {
+ final Ensure m_ensure;
+
+ public SequencerImpl(Ensure e)
+ {
+ m_ensure = e;
+ }
+
+ public void step()
+ {
+ m_ensure.step();
+ }
+
+ public void step(int step)
+ {
+ m_ensure.step(step);
+ }
+
+ public void waitForStep(int step, int timeout)
+ {
+ m_ensure.waitForStep(step, timeout);
+ }
+ }
+
+ public interface ServiceInterface
+ {
+ public void doService();
+ }
+
+ public static class ServiceConsumer {
+ volatile Sequencer m_sequencer;
+ volatile ServiceInterface m_service;
+ volatile Dependency m_d1, m_d2;
+
+ public void init(Component s) {
+ DependencyManager m = s.getDependencyManager();
+ m_d1 = m.createServiceDependency()
+ .setService(Sequencer.class)
+ .setRequired(true)
+ .setAutoConfig("m_sequencer");
+ m_d2 = m.createServiceDependency()
+ .setService(ServiceInterface.class, "(foo=bar)")
+ .setRequired(true)
+ .setAutoConfig("m_service");
+ s.add(m_d1, m_d2);
+ }
+
+ void start() {
+ m_sequencer.step(6);
+ m_service.doService();
+ }
+
+ void stop() {
+ m_sequencer.step(8);
+ }
+ }
+
+ public static class ServiceProvider implements ServiceInterface
+ {
+ volatile Sequencer m_sequencer;
+ volatile ServiceProvider2 m_serviceProvider2;
+ volatile ServiceDependency m_d1, m_d2;
+
+ public void init(Component c)
+ {
+ DependencyManager m = c.getDependencyManager();
+ m_d1 = m.createServiceDependency()
+ .setService(Sequencer.class)
+ .setRequired(true)
+ .setAutoConfig("m_sequencer");
+ m_d2 = m.createServiceDependency()
+ .setService(ServiceProvider2.class)
+ .setRequired(true)
+ .setCallbacks("bind", "unbind");
+ c.add(m_d1, m_d2);
+ }
+
+ void bind(ServiceProvider2 provider2)
+ {
+ m_serviceProvider2 = provider2;
+ }
+
+ void start()
+ {
+ m_serviceProvider2.step(4);
+ m_sequencer.step(5);
+ }
+
+ void stop()
+ {
+ m_sequencer.step(9);
+ }
+
+ void unbind(ServiceProvider2 provider2)
+ {
+ m_sequencer.step(10);
+ }
+
+ public void doService()
+ {
+ m_sequencer.step(7);
+ }
+ }
+
+ public static class ServiceProvider2
+ {
+ final Composite m_composite = new Composite();
+ volatile Sequencer m_sequencer;
+ volatile Runnable m_runnable;
+ volatile ServiceDependency m_d1, m_d2;
+
+ public void init(Component c)
+ {
+ System.out.println("ServiceProvider2.init");
+ DependencyManager m = c.getDependencyManager();
+
+ m_d1 = m.createServiceDependency()
+ .setService(Runnable.class, "(foo=bar)")
+ .setRequired(false)
+ .setAutoConfig("m_runnable");
+ m_d2 = m.createServiceDependency()
+ .setService(Sequencer.class)
+ .setRequired(true)
+ .setCallbacks("bind", null);
+ c.add(m_d1, m_d2);
+ }
+
+ void bind(Sequencer seq)
+ {
+ System.out.println("ServiceProvider2.bind(" + seq + ")");
+ m_sequencer = seq;
+ m_sequencer.step(1);
+ }
+
+ void start()
+ {
+ System.out.println("ServiceProvider2.start: m_runnable=" + m_runnable + ", m_sequencer = " + m_sequencer);
+ m_sequencer.step(3);
+ m_runnable.run(); // NullObject
+ }
+
+ public void step(int step) // called by ServiceProvider.start() method
+ {
+ m_sequencer.step(step);
+ }
+
+ void stop()
+ {
+ m_sequencer.step(11);
+ }
+
+ Object[] getComposition()
+ {
+ return new Object[] { this, m_composite };
+ }
+ }
+
+ public static class Composite
+ {
+ void bind(Sequencer seq)
+ {
+ seq.step(2);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleServiceDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleServiceDependencyTest.java
new file mode 100644
index 0000000..36874ee
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleServiceDependencyTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.itest.api;
+
+import java.util.Hashtable;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.Constants;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class MultipleServiceDependencyTest extends TestBase {
+ public void testMultipleServiceRegistrationAndConsumption() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ Component provider = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+ Component providerWithHighRank = m.createComponent().setImplementation(new ServiceProvider2(e)).setInterface(ServiceInterface.class.getName(), new Hashtable() {{ put(Constants.SERVICE_RANKING, Integer.valueOf(5)); }});
+ Component consumer = m.createComponent().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true));
+ m.add(provider);
+ m.add(providerWithHighRank);
+ m.add(consumer);
+ e.waitForStep(3, 5000);
+ m.remove(providerWithHighRank);
+ e.step(4);
+ e.waitForStep(5, 5000);
+ m.remove(provider);
+ m.remove(consumer);
+ e.waitForStep(6, 5000);
+ }
+
+ public void testReplacementAutoConfig() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ Component provider = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+ Component provider2 = m.createComponent().setImplementation(new ServiceProvider2(e)).setInterface(ServiceInterface.class.getName(), null);
+ Component consumer = m.createComponent().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true));
+ m.add(provider2);
+ m.add(consumer);
+ e.waitForStep(3, 5000);
+ m.add(provider);
+ m.remove(provider2);
+ e.step(4);
+ e.waitForStep(5, 5000);
+ m.remove(provider);
+ m.remove(consumer);
+ e.waitForStep(6, 5000);
+ }
+
+ public void testReplacementCallbacks() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ Component provider = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+ Component provider2 = m.createComponent().setImplementation(new ServiceProvider2(e)).setInterface(ServiceInterface.class.getName(), null);
+ Component consumer = m.createComponent().setImplementation(new ServiceConsumer(e))
+ .add(m.createServiceDependency()
+ .setService(ServiceInterface.class)
+ .setRequired(true)
+ .setCallbacks("add", "remove"));
+ m.add(provider2);
+ m.add(consumer);
+ e.waitForStep(3, 15000);
+ m.add(provider);
+ m.remove(provider2);
+ e.step(4);
+ e.waitForStep(5, 15000);
+ m.remove(provider);
+ m.remove(consumer);
+ e.waitForStep(6, 15000);
+ }
+
+ static interface ServiceInterface {
+ public void invoke();
+ }
+
+ static class ServiceProvider implements ServiceInterface {
+ private final Ensure m_ensure;
+ public ServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+ public void invoke() {
+ m_ensure.step(5);
+ }
+ }
+
+ static class ServiceProvider2 implements ServiceInterface {
+ private final Ensure m_ensure;
+ public ServiceProvider2(Ensure e) {
+ m_ensure = e;
+ }
+ public void invoke() {
+ m_ensure.step(2);
+ }
+ }
+
+ static class ServiceConsumer implements Runnable {
+ private volatile ServiceInterface m_service;
+ private final Ensure m_ensure;
+
+ @SuppressWarnings("unused")
+ private void add(ServiceInterface service) { m_service = service; }
+
+ @SuppressWarnings("unused")
+ private void remove(ServiceInterface service) { if (m_service == service) { m_service = null; }}
+ public ServiceConsumer(Ensure e) { m_ensure = e; }
+
+ public void start() {
+ Thread t = new Thread(this);
+ t.start();
+ }
+
+ public void run() {
+ m_ensure.step(1);
+ m_service.invoke();
+ m_ensure.step(3);
+ m_ensure.waitForStep(4, 15000);
+ m_service.invoke();
+ }
+
+ public void stop() {
+ m_ensure.step(6);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/RemovedDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/RemovedDependencyTest.java
new file mode 100644
index 0000000..5d1af03
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/RemovedDependencyTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.itest.api;
+
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Properties;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * One consumer, Three providers. The Consumer has two required dependency on provider1, provider2, and one
+ * instance-bound required dependency on provider3.
+ * When the three providers are there, the consumer is started.
+ *
+ * This test asserts the following correct behaviors:
+ * - when we remove the dependency on provider2, then the consumer is not stopped.
+ * - when we remove the (instance-bound) dependency on provider3, then the consumer os not stopped.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "unused"})
+public class RemovedDependencyTest extends TestBase {
+ public void testRemoveDependencyAndConsumerMustRemainStarted() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // Create two providers
+ Hashtable props = new Hashtable();
+ props.put("name", "provider1");
+ Component sp = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), props);
+ props = new Properties();
+ props.put("name", "provider2");
+ Component sp2 = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), props);
+ props = new Properties();
+ props.put("name", "provider3");
+ Component sp3 = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), props);
+
+ // Create the consumer, and start it
+ Dependency d1 = m.createServiceDependency().setService(ServiceInterface.class, "(name=provider1)").setRequired(true).setCallbacks("add", "remove");
+ Dependency d2 = m.createServiceDependency().setService(ServiceInterface.class, "(name=provider2)").setRequired(true).setCallbacks("add", "remove");
+ Dependency d3 = m.createServiceDependency().setService(ServiceInterface.class, "(name=provider3)").setRequired(true).setCallbacks("add", "remove");
+
+ ServiceConsumer consumer = new ServiceConsumer(e, d3);
+ Component sc = m.createComponent().setImplementation(consumer).add(d1, d2);
+
+ // Add the first two providers and the consumer
+ m.add(sp);
+ m.add(sp2);
+ m.add(sp3);
+ m.add(sc);
+
+ // Check if consumer has been bound to the three providers
+ e.waitForStep(3, 5000);
+ Assert.assertEquals(3, consumer.getProvidersCount());
+ Assert.assertNotNull(consumer.getProvider("provider1"));
+ Assert.assertNotNull(consumer.getProvider("provider2"));
+ Assert.assertNotNull(consumer.getProvider("provider3"));
+
+ // Now remove the provider2, and check if the consumer is still alive
+ sc.remove(d2);
+ Assert.assertFalse(consumer.isStopped());
+ Assert.assertEquals(2, consumer.getProvidersCount());
+ Assert.assertNotNull(consumer.getProvider("provider1"));
+ Assert.assertNull(consumer.getProvider("provider2"));
+ Assert.assertNotNull(consumer.getProvider("provider3"));
+
+ // Now remove the provider3 (the consumer has an instance bound dependency on it), and check if the consumer is still alive
+ sc.remove(d3);
+ Assert.assertFalse(consumer.isStopped());
+ Assert.assertEquals(1, consumer.getProvidersCount());
+ Assert.assertNotNull(consumer.getProvider("provider1"));
+ Assert.assertNull(consumer.getProvider("provider2"));
+ Assert.assertNull(consumer.getProvider("provider3"));
+
+ m.clear();
+ }
+
+ static interface ServiceInterface {
+ public void invoke();
+ }
+
+ class ServiceProvider implements ServiceInterface {
+ final Ensure m_ensure;
+
+ public ServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+ public void invoke() {
+ m_ensure.step();
+ }
+ }
+
+ class ServiceConsumer {
+ private final Ensure m_ensure;
+ private final List<ServiceReference> m_providers = new ArrayList<>();
+ private BundleContext m_bc;
+ private boolean m_stopped;
+ private final Dependency m_dependency3;
+
+ public ServiceConsumer(Ensure e, Dependency dependency3) {
+ m_ensure = e;
+ m_dependency3 = dependency3;
+ }
+
+ public void add(ServiceReference ref) {
+ debug("ServiceConsumer.add(%s)", ref);
+ m_providers.add(ref);
+ ServiceInterface s = (ServiceInterface) m_bc.getService(ref);
+ s.invoke();
+ }
+
+ public void remove(ServiceReference ref) {
+ debug("ServiceConsumer.remove(%s)", ref);
+ m_providers.remove(ref);
+ debug("ServiceConsumer: current providers list=%s", m_providers);
+ }
+
+ public void init(Component c) {
+ c.add(m_dependency3);
+ }
+
+ public int getProvidersCount() {
+ return m_providers.size();
+ }
+
+ public ServiceInterface getProvider(String name) {
+ for (ServiceReference ref : m_providers) {
+ Object n = ref.getProperty("name");
+ if (n.equals(name)) {
+ return (ServiceInterface) m_bc.getService(ref);
+ }
+ }
+ return null;
+ }
+
+ public void stop() {
+ m_stopped = true;
+ }
+
+ public boolean isStopped() {
+ return m_stopped;
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceAdapterDependencyAddAndRemoveTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceAdapterDependencyAddAndRemoveTest.java
new file mode 100644
index 0000000..6d844d1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceAdapterDependencyAddAndRemoveTest.java
@@ -0,0 +1,167 @@
+/*
+ * 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.itest.api;
+
+import java.net.URL;
+
+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.DependencyManager;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResourceAdapterDependencyAddAndRemoveTest extends TestBase {
+ public void testBasicResourceAdapter() throws Exception {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+
+ // create and add a service provider
+ m.add(m.createComponent()
+ .setInterface(ServiceInterface.class.getName(), null)
+ .setImplementation(new ServiceProvider(e)));
+
+ // create and add a resource provider
+ ResourceProvider provider = new ResourceProvider(context, new URL("file://localhost/path/to/file1.txt"));
+ m.add(m.createComponent()
+ .setImplementation(provider)
+ .add(m.createServiceDependency()
+ .setService(ResourceHandler.class)
+ .setCallbacks("add", "remove"))
+ );
+
+ // create a resource adapter for our single resource
+ // note that we can provide an actual implementation instance here because there will be only one
+ // adapter, normally you'd want to specify a Class here
+ // also, create a callback instance which will be used for both callbacks on resource changes and
+ // life cycle callbacks on the adapters themselves
+
+ Dependency d = m.createServiceDependency()
+ .setService(ServiceInterface.class)
+ .setRequired(true);
+ CallbackInstance callbackInstance = new CallbackInstance(e, d);
+ Component component = m.createResourceAdapterService("(&(path=/path/to/*.txt)(host=localhost))", false, callbackInstance, "changed")
+ .setImplementation(new ResourceAdapter(e))
+ .setCallbacks(callbackInstance, "init", "start", "stop", "destroy");
+
+ // add the resource adapter
+ m.add(component);
+
+ // wait until the single resource is available (the adapter has been started)
+ e.waitForStep(1, 5000);
+ // trigger a 'change' in our resource
+ provider.change();
+ // wait until the changed callback is invoked
+ e.waitForStep(2, 5000);
+ // and has completed (ensuring no "extra" steps are invoked in the mean time)
+ e.waitForStep(3, 5000);
+
+ // remove the resource adapter again
+ // add a component state listener, in order to track resource adapter destruction
+ component.add(new ComponentStateListenerImpl(e));
+ m.remove(component);
+
+ // wait for the stopped callback in the state listener
+ e.waitForStep(4, 5000);
+ m.clear();
+ }
+
+ static class ResourceAdapter {
+ protected URL m_resource; // injected by reflection.
+
+ ResourceAdapter(Ensure e) {
+ }
+ }
+
+ static interface ServiceInterface {
+ public void invoke();
+ }
+
+ static class ServiceProvider implements ServiceInterface {
+ public ServiceProvider(Ensure e) {
+ }
+ public void invoke() {
+ }
+ }
+
+ class CallbackInstance {
+ private final Dependency m_dependency;
+ private final Ensure m_ensure;
+
+
+ public CallbackInstance(Ensure e, Dependency d) {
+ m_ensure = e;
+ m_dependency = d;
+ }
+
+ void init(Component c) {
+ debug("CallbackInstance.init");
+ c.add(m_dependency);
+ }
+
+ void start() {
+ debug("CallbackInstance.start");
+ m_ensure.step(1);
+ }
+
+ void stop() {
+ debug("CallbackInstance.stop");
+ }
+
+ void destroy() {
+ debug("CallbackInstance.destroy");
+ }
+
+ void changed(Component component) {
+ m_ensure.step(2);
+ Dependency oldDependency = m_dependency;
+ // and add a new dependency
+ component.add(component.getDependencyManager().createServiceDependency().setService(ServiceInterface.class).setRequired(true));
+ // remove the old dependency
+ component.remove(oldDependency);
+ debug("CallbackInstance.changed the dependencies");
+ m_ensure.step(3);
+ }
+ }
+
+ class ComponentStateListenerImpl implements ComponentStateListener {
+
+ private final Ensure m_ensure;
+
+ public ComponentStateListenerImpl(Ensure e) {
+ this.m_ensure = e;
+ }
+
+ public void changed(Component c, ComponentState state) {
+ debug("ComponentStateListenerImpl.changed: state=%s", state);
+ switch (state) {
+ case INACTIVE:
+ System.out.println("stopped");
+ m_ensure.step(4);
+ default:
+ }
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceAdapterDependencyAddAndRemoveTest2.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceAdapterDependencyAddAndRemoveTest2.java
new file mode 100644
index 0000000..09d7dd1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceAdapterDependencyAddAndRemoveTest2.java
@@ -0,0 +1,172 @@
+/*
+ * 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.itest.api;
+
+import java.net.URL;
+import java.util.Hashtable;
+
+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.DependencyManager;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResourceAdapterDependencyAddAndRemoveTest2 extends TestBase {
+ public void testBasicResourceAdapter() throws Exception {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a resource provider
+ ResourceProvider provider = new ResourceProvider(context, new URL("file://localhost/path/to/file1.txt"));
+ // activate it
+ Hashtable<String, String> props = new Hashtable<String, String>();
+ props.put("id", "1");
+ m.add(m.createComponent()
+ .setInterface(ServiceInterface.class.getName(), props)
+ .setImplementation(new ServiceProvider(e))
+ );
+
+ props = new Hashtable<String, String>();
+ props.put("id", "2");
+ m.add(m.createComponent()
+ .setInterface(ServiceInterface.class.getName(), props)
+ .setImplementation(new ServiceProvider(e))
+ );
+
+ m.add(m.createComponent()
+ .setImplementation(provider)
+ .add(m.createServiceDependency()
+ .setService(ResourceHandler.class)
+ .setCallbacks("add", "remove")
+ )
+ );
+
+ // create a resource adapter for our single resource
+ // note that we can provide an actual implementation instance here because there will be only one
+ // adapter, normally you'd want to specify a Class here
+ Dependency d = m.createServiceDependency().setService(ServiceInterface.class, "(id=1)").setRequired(true);
+ ResourceAdapter service = new ResourceAdapter(e, d);
+
+ CallbackInstance callbackInstance = new CallbackInstance(e, d);
+ Component component = m.createResourceAdapterService("(&(path=/path/to/*.txt)(host=localhost))", false, callbackInstance, "changed")
+ .setImplementation(service)
+ .setCallbacks(callbackInstance, "init", "start", "stop", "destroy");
+ component.add(new ComponentStateListenerImpl(e));
+ m.add(component);
+ // wait until the single resource is available
+ e.waitForStep(1, 5000);
+ // trigger a 'change' in our resource
+ provider.change();
+ // wait until the changed callback is invoked
+ e.waitForStep(2, 5000);
+
+ System.out.println("Done!");
+ m.clear();
+ }
+
+ static class ResourceAdapter {
+ protected URL m_resource; // injected by reflection.
+ final Dependency m_dependency;
+
+ ResourceAdapter(Ensure e, Dependency d) {
+ m_dependency = d;
+ }
+ }
+
+ static interface ServiceInterface {
+ public void invoke();
+ }
+
+ static class ServiceProvider implements ServiceInterface {
+ public ServiceProvider(Ensure e) {
+ }
+ public void invoke() {
+ }
+ }
+
+ static class CallbackInstance {
+ private final Ensure m_ensure;
+ private final Dependency m_dependency;
+
+ public CallbackInstance(Ensure e, Dependency d) {
+ m_ensure = e;
+ m_dependency = d;
+ }
+
+ void init(Component c) {
+ c.add(m_dependency);
+ System.out.println("init");
+ m_ensure.step(1);
+ }
+
+ void start() {
+ System.out.println("start");
+ }
+
+ void stop() {
+ System.out.println("stop");
+ }
+
+ void destroy() {
+ System.out.println("destroy");
+ }
+
+ void changed(Component component) {
+ m_ensure.step(2);
+ System.out.println("Changing the dependencies");
+ Dependency oldDependency = m_dependency;
+
+ // and add a new dependency
+ component.add(component.getDependencyManager().createServiceDependency().setService(ServiceInterface.class, "(id=2)").setRequired(true));
+ // remove the old dependency
+ component.remove(oldDependency);
+ System.out.println("Changed the dependencies");
+ }
+ }
+
+ static class ComponentStateListenerImpl implements ComponentStateListener {
+ public ComponentStateListenerImpl(Ensure e) {
+ }
+
+ @Override
+ public void changed(Component c, ComponentState state) {
+ switch (state) {
+ case INACTIVE:
+ System.out.println("INACTIVE");
+ break;
+ case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
+ System.out.println("INSTANTIATED_AND_WAITING_FOR_REQUIRED");
+ break;
+ case WAITING_FOR_REQUIRED:
+ System.out.println("WAITING_FOR_REQUIRED");
+ break;
+ case TRACKING_OPTIONAL:
+ System.out.println("TRACKING_OPTIONAL");
+ break;
+
+ }
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceAdapterTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceAdapterTest.java
new file mode 100644
index 0000000..ad25a54
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceAdapterTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.itest.api;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URL;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.junit.Assert;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResourceAdapterTest extends TestBase {
+ public void testBasicResourceAdapter() throws Exception {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a resource provider
+ ResourceProvider provider = new ResourceProvider(context, new URL("file://localhost/path/to/file1.txt"));
+ // activate it
+ m.add(m.createComponent().setImplementation(provider).add(m.createServiceDependency().setService(ResourceHandler.class).setCallbacks("add", "remove")));
+ // create a resource adapter for our single resource
+ // note that we can provide an actual implementation instance here because there will be only one
+ // adapter, normally you'd want to specify a Class here
+ m.add(m.createResourceAdapterService("(&(path=/path/to/*.txt)(host=localhost))", false, null, "changed")
+ .setImplementation(new ResourceAdapter(e)));
+ // wait until the single resource is available
+ e.waitForStep(3, 5000);
+ // trigger a 'change' in our resource
+ provider.change();
+ // wait until the changed callback is invoked
+ e.waitForStep(4, 5000);
+ m.clear();
+ }
+
+ static class ResourceAdapter {
+ protected URL m_resource; // injected by reflection.
+ private Ensure m_ensure;
+
+ ResourceAdapter(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void start() {
+ m_ensure.step(1);
+ Assert.assertNotNull("resource not injected", m_resource);
+ m_ensure.step(2);
+ try {
+ m_resource.openStream();
+ }
+ catch (FileNotFoundException e) {
+ m_ensure.step(3);
+ }
+ catch (IOException e) {
+ Assert.fail("We should not have gotten this exception.");
+ }
+ }
+
+ public void changed() {
+ m_ensure.step(4);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceDependencyTest.java
new file mode 100644
index 0000000..9595a9f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceDependencyTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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.itest.api;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.junit.Assert;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResourceDependencyTest extends TestBase {
+ public void testResourceDependency() throws MalformedURLException {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ ResourceConsumer c = new ResourceConsumer(e);
+ Component consumer = m.createComponent()
+ .setImplementation(c)
+ .add(m.createResourceDependency()
+ .setFilter("(&(path=/path/to/*.txt)(host=localhost))")
+ .setCallbacks("add", "change", "remove"));
+ Component dynamicProxyConsumer = m.createComponent()
+ .setFactory(new ResourceConsumerFactory(e), "create")
+ .add(m.createResourceDependency()
+ .setFilter("(path=*.doc)")
+ .setCallbacks("add", null));
+ ResourceProvider provider = new ResourceProvider(context,
+ new URL("file://localhost/path/to/file1.txt"),
+ new URL("file://localhost/path/to/file2.txt"),
+ new URL("file://localhost/path/to/file3.doc"));
+ Component resourceProvider = m.createComponent()
+ .setImplementation(provider)
+ .add(m.createServiceDependency()
+ .setService(ResourceHandler.class)
+ .setCallbacks("add", "remove"));
+
+ // first add the consumer
+ m.add(consumer);
+ // then the resource provider, which will provide 3 resources,
+ // 2 of which match the consumers filter conditions
+ m.add(resourceProvider);
+ // make sure our consumer invoked openStream() on both resources,
+ // increasing the step counter to 2
+ e.step(3);
+
+ // now add another consumer, that matches only one resource, and uses
+ // a dynamic proxy as its implementation
+ m.add(dynamicProxyConsumer);
+ // ensure the resource was injected properly
+ e.waitForStep(4, 5000);
+
+ // now change a resource and see if it gets propagated to the consumer
+ provider.change(0);
+
+ // wait for change callback
+ e.waitForStep(5, 5000);
+ e.step(6);
+
+ // cleanup
+ m.remove(dynamicProxyConsumer);
+ m.remove(resourceProvider);
+ m.remove(consumer);
+
+ // validate that all consumed resources are "unconsumed" again
+ c.ensure();
+ m.clear();
+ }
+
+ class ResourceConsumer {
+ private volatile int m_counter;
+ private Ensure m_ensure;
+
+ public ResourceConsumer(Ensure ensure) {
+ m_ensure = ensure;
+ }
+
+ public void add(URL resource) {
+ debug("ResourceConsumer.add(%s)", resource);
+ m_counter++;
+ m_ensure.step();
+ }
+ public void change(URL resource) {
+ m_ensure.step();
+ }
+ public void remove(URL resource) {
+ debug("ResourceConsumer.remove(%s)", resource);
+ m_counter--;
+ }
+ public void ensure() {
+ Assert.assertTrue("all resources should have been added and removed at this point, but " + m_counter + " are remaining", m_counter == 0);
+ }
+ }
+
+
+ class ResourceConsumerFactory {
+ private final Ensure m_ensure;
+ public ResourceConsumerFactory(Ensure ensure) {
+ m_ensure = ensure;
+ }
+ public Object create() {
+ ResourceConsumer resourceConsumer = new ResourceConsumer(m_ensure);
+ // create a dynamic proxy for the ResourceProvider
+ return Proxy.newProxyInstance(resourceConsumer.getClass().getClassLoader(), resourceConsumer.getClass().getInterfaces(), new DynamicProxyHandler(resourceConsumer, m_ensure));
+ }
+ }
+
+ static class DynamicProxyHandler implements InvocationHandler {
+ Ensure m_ensure;
+ ResourceConsumer resourceConsumer = null;
+
+ public DynamicProxyHandler(ResourceConsumer resourceConsumer, Ensure ensure) {
+ this.resourceConsumer = resourceConsumer;
+ m_ensure = ensure;
+ }
+
+ public void add(URL resource) {
+ m_ensure.step(4);
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ return method.invoke(resourceConsumer, args);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceProvider.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceProvider.java
new file mode 100644
index 0000000..7e0e46b
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceProvider.java
@@ -0,0 +1,121 @@
+/*
+ * 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.itest.api;
+
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.ResourceUtil;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+class ResourceProvider {
+ final URL[] m_resources;
+ final BundleContext m_context;
+ final Map<ResourceHandler, Filter> m_handlers = new HashMap<>();
+
+ ResourceProvider(BundleContext ctx, URL ... resources) {
+ m_context = ctx;
+ m_resources = resources;
+ }
+
+ public void change() {
+ for (int i = 0; i < m_resources.length; i++) {
+ change(i);
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void change(int resourceIndex) {
+ Map<ResourceHandler, Filter> handlers = new HashMap<>();
+ synchronized (m_handlers) {
+ handlers.putAll(m_handlers);
+ }
+ for (Map.Entry<ResourceHandler, Filter> e : handlers.entrySet()) {
+ ResourceHandler handler = e.getKey();
+ Filter filter = e.getValue();
+ if (filter == null || filter.match(ResourceUtil.createProperties(m_resources[resourceIndex]))) {
+ handler.changed(m_resources[resourceIndex]);
+ }
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void add(ServiceReference ref, ResourceHandler handler) {
+ String filterString = (String) ref.getProperty("filter");
+ Filter filter = null;
+ if (filterString != null) {
+ try {
+ filter = m_context.createFilter(filterString);
+ }
+ catch (InvalidSyntaxException e) {
+ Assert.fail("Could not create filter for resource handler: " + e);
+ return;
+ }
+ }
+ for (int i = 0; i < m_resources.length; i++) {
+ if (filter == null || filter.match(ResourceUtil.createProperties(m_resources[i]))) {
+ synchronized (m_handlers) {
+ m_handlers.put(handler, filter);
+ }
+ handler.added(m_resources[i]);
+ }
+ }
+ }
+
+ public void remove(ServiceReference ref, ResourceHandler handler) {
+ Filter filter;
+ synchronized (m_handlers) {
+ filter = (Filter) m_handlers.remove(handler);
+ }
+ if (filter != null) {
+ removeResources(handler, filter);
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private void removeResources(ResourceHandler handler, Filter filter) {
+ for (int i = 0; i < m_resources.length; i++) {
+ if (filter == null || filter.match(ResourceUtil.createProperties(m_resources[i]))) {
+ handler.removed(m_resources[i]);
+ }
+ }
+ }
+
+ public void destroy() {
+ Map<ResourceHandler, Filter> handlers = new HashMap<>();
+ synchronized (m_handlers) {
+ handlers.putAll(m_handlers);
+ }
+
+ for (Map.Entry<ResourceHandler, Filter> e : handlers.entrySet()) {
+ ResourceHandler handler = e.getKey();
+ Filter filter = e.getValue();
+ removeResources(handler, filter);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyCallbackSignaturesTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyCallbackSignaturesTest.java
new file mode 100644
index 0000000..e39f830
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyCallbackSignaturesTest.java
@@ -0,0 +1,294 @@
+/*
+ * 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.itest.api;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings("unused")
+public class ServiceDependencyCallbackSignaturesTest extends TestBase {
+ volatile Ensure m_ensure;
+
+ /**
+ * Tests if all possible dependency callbacks signatures supported by ServiceDependency.
+ */
+ public void testDependencyCallbackSignatures() {
+ DependencyManager m = getDM();
+ m_ensure = new Ensure();
+ Hashtable<String, String> props = new Hashtable<>();
+ props.put("foo", "bar");
+ Component provider = m.createComponent()
+ .setImplementation(new ProviderImpl()).setInterface(Provider.class.getName(), props);
+
+ declareConsumer(m, new Object() {
+ void bind(Component c, ServiceReference ref, Provider provider) {
+ Assert.assertNotNull(c);
+ Assert.assertNotNull(ref);
+ Assert.assertNotNull(provider);
+ Assert.assertEquals("bar", ref.getProperty("foo"));
+ Assert.assertEquals(context.getService(ref), provider);
+ m_ensure.step();
+ }
+ void change(Component c, ServiceReference ref, Provider provider) {
+ Assert.assertNotNull(c);
+ Assert.assertNotNull(ref);
+ Assert.assertNotNull(provider);
+ Assert.assertEquals("zoo", ref.getProperty("foo"));
+ Assert.assertEquals(context.getService(ref), provider);
+ m_ensure.step();
+ }
+ });
+ declareConsumer(m, new Object() {
+ void bind(Component c, ServiceReference ref, Object provider) {
+ Assert.assertNotNull(c);
+ Assert.assertNotNull(ref);
+ Assert.assertEquals("bar", ref.getProperty("foo"));
+ Assert.assertNotNull(provider);
+ Assert.assertEquals(context.getService(ref), provider);
+ m_ensure.step();
+ }
+ void change(Component c, ServiceReference ref, Object provider) {
+ Assert.assertNotNull(c);
+ Assert.assertNotNull(ref);
+ Assert.assertEquals("zoo", ref.getProperty("foo"));
+ Assert.assertNotNull(provider);
+ Assert.assertEquals(context.getService(ref), provider);
+ m_ensure.step();
+ }
+ });
+ declareConsumer(m, new Object() {
+ void bind(Component c, ServiceReference ref) {
+ Assert.assertNotNull(c);
+ Assert.assertNotNull(ref);
+ Assert.assertEquals("bar", ref.getProperty("foo"));
+ Assert.assertNotNull(context.getService(ref));
+ Assert.assertEquals(context.getService(ref).getClass(), ProviderImpl.class);
+ m_ensure.step();
+ }
+ void change(Component c, ServiceReference ref) {
+ Assert.assertNotNull(c);
+ Assert.assertNotNull(ref);
+ Assert.assertEquals("zoo", ref.getProperty("foo"));
+ Assert.assertNotNull(context.getService(ref));
+ Assert.assertEquals(context.getService(ref).getClass(), ProviderImpl.class);
+ m_ensure.step();
+ }
+ });
+ declareConsumer(m, new Object() {
+ void bind(Component c, Provider provider) {
+ Assert.assertNotNull(c);
+ Assert.assertNotNull(provider);
+ m_ensure.step();
+ }
+ void change(Component c, Provider provider) {
+ Assert.assertNotNull(c);
+ Assert.assertNotNull(provider);
+ m_ensure.step();
+ }
+ });
+ declareConsumer(m, new Object() {
+ void bind(Component c, Object provider) {
+ Assert.assertNotNull(c);
+ Assert.assertNotNull(provider);
+ Assert.assertEquals(provider.getClass(), ProviderImpl.class);
+ m_ensure.step();
+ }
+ void change(Component c, Object provider) {
+ Assert.assertNotNull(c);
+ Assert.assertNotNull(provider);
+ Assert.assertEquals(provider.getClass(), ProviderImpl.class);
+ m_ensure.step();
+ }
+ });
+ declareConsumer(m, new Object() {
+ void bind(Component c) {
+ Assert.assertNotNull(c);
+ m_ensure.step();
+ }
+ void change(Component c) {
+ Assert.assertNotNull(c);
+ m_ensure.step();
+ }
+ });
+ declareConsumer(m, new Object() {
+ void bind(Component c, Map<String, String> props, Provider provider) {
+ Assert.assertNotNull(c);
+ Assert.assertNotNull(props);
+ Assert.assertNotNull(provider);
+ Assert.assertEquals("bar", props.get("foo"));
+ Assert.assertEquals(provider.getClass(), ProviderImpl.class);
+ m_ensure.step();
+ }
+ void change(Component c, Map<String, String> props, Provider provider) {
+ Assert.assertNotNull(c);
+ Assert.assertNotNull(props);
+ Assert.assertNotNull(provider);
+ Assert.assertEquals("zoo", props.get("foo"));
+ Assert.assertEquals(provider.getClass(), ProviderImpl.class);
+ m_ensure.step();
+ }
+ });
+ declareConsumer(m, new Object() {
+ void bind(ServiceReference ref, Provider provider) {
+ Assert.assertNotNull(ref);
+ Assert.assertNotNull(provider);
+ Assert.assertEquals("bar", ref.getProperty("foo"));
+ m_ensure.step();
+ }
+ void change(ServiceReference ref, Provider provider) {
+ Assert.assertNotNull(ref);
+ Assert.assertNotNull(provider);
+ Assert.assertEquals("zoo", ref.getProperty("foo"));
+ m_ensure.step();
+ }
+ });
+ declareConsumer(m, new Object() {
+ void bind(ServiceReference ref, Object provider) {
+ Assert.assertNotNull(ref);
+ Assert.assertNotNull(provider);
+ Assert.assertEquals("bar", ref.getProperty("foo"));
+ Assert.assertEquals(provider.getClass(), ProviderImpl.class);
+ m_ensure.step();
+ }
+ void change(ServiceReference ref, Object provider) {
+ Assert.assertNotNull(ref);
+ Assert.assertNotNull(provider);
+ Assert.assertEquals("zoo", ref.getProperty("foo"));
+ Assert.assertEquals(provider.getClass(), ProviderImpl.class);
+ m_ensure.step();
+ }
+ });
+ declareConsumer(m, new Object() {
+ void bind(ServiceReference ref) {
+ Assert.assertNotNull(ref);
+ Assert.assertEquals("bar", ref.getProperty("foo"));
+ m_ensure.step();
+ }
+ void change(ServiceReference ref) {
+ Assert.assertNotNull(ref);
+ Assert.assertEquals("zoo", ref.getProperty("foo"));
+ m_ensure.step();
+ }
+ });
+ declareConsumer(m, new Object() {
+ void bind(Provider provider) {
+ Assert.assertNotNull(provider);
+ m_ensure.step();
+ }
+ void change(Provider provider) {
+ Assert.assertNotNull(provider);
+ m_ensure.step();
+ }
+ });
+ declareConsumer(m, new Object() {
+ void bind(Provider provider, Map<?, ?> props) {
+ Assert.assertNotNull(provider);
+ Assert.assertEquals("bar", props.get("foo"));
+ m_ensure.step();
+ }
+ void change(Provider provider, Map<?, ?> props) {
+ Assert.assertNotNull(provider);
+ Assert.assertEquals("zoo", props.get("foo"));
+ m_ensure.step();
+ }
+ });
+ declareConsumer(m, new Object() {
+ void bind(Map<?, ?> props, Provider provider) {
+ Assert.assertNotNull(provider);
+ Assert.assertEquals("bar", props.get("foo"));
+ m_ensure.step();
+ }
+ void change(Map<?, ?> props, Provider provider) {
+ Assert.assertNotNull(provider);
+ Assert.assertEquals("zoo", props.get("foo"));
+ m_ensure.step();
+ }
+ });
+ declareConsumer(m, new Object() {
+ void bind(Provider provider, Dictionary<?, ?> props) {
+ Assert.assertNotNull(provider);
+ Assert.assertEquals("bar", props.get("foo"));
+ m_ensure.step();
+ }
+ void change(Provider provider, Dictionary<?, ?> props) {
+ Assert.assertNotNull(provider);
+ Assert.assertEquals("zoo", props.get("foo"));
+ m_ensure.step();
+ }
+ });
+ declareConsumer(m, new Object() {
+ void bind(Dictionary<?, ?> props, Provider provider) {
+ Assert.assertNotNull(provider);
+ Assert.assertEquals("bar", props.get("foo"));
+ m_ensure.step();
+ }
+ void change(Dictionary<?, ?> props, Provider provider) {
+ Assert.assertNotNull(provider);
+ Assert.assertEquals("zoo", props.get("foo"));
+ m_ensure.step();
+ }
+ });
+ declareConsumer(m, new Object() {
+ void bind(Object provider) {
+ Assert.assertNotNull(provider);
+ Assert.assertEquals(provider.getClass(), ProviderImpl.class);
+ m_ensure.step();
+ }
+ void change(Object provider) {
+ bind(provider);
+ }
+ });
+
+ m.add(provider);
+ m_ensure.waitForStep(16, 5000);
+
+ props = new Hashtable<>();
+ props.put("foo", "zoo");
+ provider.setServiceProperties(props);
+ m_ensure.waitForStep(32, 5000);
+
+ m.remove(provider);
+ m_ensure.waitForStep(48, 5000);
+ }
+
+ private void declareConsumer(DependencyManager m, Object consumerImpl) {
+ Component consumer = m.createComponent().setImplementation(consumerImpl)
+ .add(m.createServiceDependency().setService(Provider.class).setCallbacks("bind", "change", "change").setRequired(true));
+ m.add(consumer);
+ }
+
+ public static interface Provider {
+ }
+
+ public static class ProviderImpl implements Provider {
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyInjectionTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyInjectionTest.java
new file mode 100644
index 0000000..47581ff
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyInjectionTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.itest.api;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceDependencyInjectionTest extends TestBase {
+ public void testServiceInjection() {
+ DependencyManager m = getDM();
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ ServiceProvider provider = new ServiceProvider(e);
+ Component sp = m.createComponent().setImplementation(provider).setInterface(ServiceInterface2.class.getName(), null);
+ Component sc = m.createComponent().setImplementation(new ServiceConsumer())
+ .add(m.createServiceDependency().setService(ServiceInterface2.class).setRequired(true));
+ Component sc2 = m.createComponent() // all dependencies are optional
+ .setImplementation(new ServiceConsumerNamedInjection(false, false))
+ .add(m.createServiceDependency().setService(ServiceInterface2.class).setRequired(false).setAutoConfig("m_service"))
+ .add(m.createServiceDependency().setService(ServiceInterface2.class).setRequired(false).setAutoConfig("m_service2"))
+ .add(m.createServiceDependency().setService(ServiceInterface2.class).setRequired(false).setAutoConfig("m_service3"))
+ ;
+ Component sc3 = m.createComponent() // second dependency is required, first and third are optional
+ .setImplementation(new ServiceConsumerNamedInjection(false, false))
+ .add(m.createServiceDependency().setService(ServiceInterface2.class).setRequired(false).setAutoConfig("m_service"))
+ .add(m.createServiceDependency().setService(ServiceInterface2.class).setRequired(true).setAutoConfig("m_service2"))
+ .add(m.createServiceDependency().setService(ServiceInterface2.class).setRequired(false).setAutoConfig("m_service3"))
+ ;
+ Component sc4 = m.createComponent()
+ .setImplementation(new ServiceConsumerNamedInjection(true, false));
+ Component sc5 = m.createComponent()
+ .setImplementation(new ServiceConsumerNamedInjection(true, true));
+ m.add(sp);
+ m.add(sc);
+ m.remove(sc);
+ m.add(sc2);
+ m.remove(sc2);
+ m.add(sc3);
+ m.remove(sc4);
+ m.add(sc4);
+ m.remove(sc4);
+ m.add(sc5);
+ m.remove(sc5);
+ m.remove(sp);
+ e.waitForStep(11, 5000);
+ m.clear();
+ }
+
+ static interface ServiceInterface {
+ public void invoke();
+ }
+
+ static interface ServiceInterface2 extends ServiceInterface {
+ public void invoke2();
+ }
+
+ static class ServiceProvider implements ServiceInterface2 {
+ private final Ensure m_ensure;
+ private Ensure.Steps m_invokeSteps = new Ensure.Steps(4, 5, 7, 8, 10, 11, 13, 14);
+ private Ensure.Steps m_invoke2Steps = new Ensure.Steps(1, 2, 3, 6, 9, 12);
+
+ public ServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void invoke() {
+ System.out.println("invoke");
+ m_ensure.steps(m_invokeSteps);
+ }
+
+ public void invoke2() {
+ System.out.println("invoke2");
+ m_ensure.steps(m_invoke2Steps);
+ }
+ }
+
+ static class ServiceConsumer {
+ private volatile ServiceInterface2 m_service;
+ private volatile ServiceInterface2 m_service2;
+
+ public void init() {
+ // invoke the second method of the interface via both injected members, to ensure
+ // neither of them is a null object (or null)
+ m_service.invoke2();
+ m_service2.invoke2();
+ Assert.assertEquals("Both members should have been injected with the same service.", m_service, m_service2);
+ }
+ }
+
+ class ServiceConsumerNamedInjection {
+ private volatile ServiceInterface2 m_service;
+ private volatile ServiceInterface m_service2;
+ private volatile Object m_service3;
+ private final boolean m_secondDependencyRequired;
+ private final boolean m_instanceBound;
+
+ ServiceConsumerNamedInjection(boolean instanceBound, boolean withSecondRequired) {
+ m_secondDependencyRequired = withSecondRequired;
+ m_instanceBound = instanceBound;
+ }
+
+ public void init(Component c) {
+ if (m_instanceBound) {
+ DependencyManager m = c.getDependencyManager();
+ c.add(m.createServiceDependency().setService(ServiceInterface2.class).setRequired(false).setAutoConfig("m_service"),
+ m.createServiceDependency().setService(ServiceInterface2.class).setRequired(m_secondDependencyRequired).setAutoConfig("m_service2"),
+ m.createServiceDependency().setService(ServiceInterface2.class).setRequired(false).setAutoConfig("m_service3"));
+ } else {
+ check();
+ }
+ }
+
+ public void start() {
+ if (m_instanceBound) {
+ check();
+ }
+ }
+
+ public void check() {
+ warn("ServiceConsumerNamedInjectionInstanceBound: m_service=%s, m_service2=%s, m_service3=%s", m_service, m_service2, m_service3);
+ // invoke the second method
+ m_service.invoke2();
+ // invoke the first method (twice)
+ m_service2.invoke();
+ ((ServiceInterface) m_service3).invoke();
+ Assert.assertNotNull("Should have been injected", m_service);
+ Assert.assertNotNull("Should have been injected", m_service2);
+ Assert.assertNotNull("Should have been injected", m_service3);
+ Assert.assertEquals("Members should have been injected with the same service.", m_service, m_service2);
+ Assert.assertEquals("Members should have been injected with the same service.", m_service, m_service3);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyPropagateTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyPropagateTest.java
new file mode 100644
index 0000000..2efb898
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyPropagateTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.itest.api;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Validates ServiceDependency service properties propagation.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class ServiceDependencyPropagateTest extends TestBase {
+ /**
+ * Checks that a ServiceDependency propagates the dependency service properties to the provided service properties.
+ */
+ public void testServiceDependencyPropagate() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ Component c1 = m.createComponent()
+ .setImplementation(new C1(e))
+ .add(m.createServiceDependency().setService(C2.class).setRequired(true).setCallbacks("bind", null));
+
+ Component c2 = m.createComponent()
+ .setInterface(C2.class.getName(), new Hashtable() {{ put("foo", "bar"); }})
+ .setImplementation(new C2())
+ .add(m.createServiceDependency().setService(C3.class).setRequired(true).setPropagate(true));
+
+ Component c3 = m.createComponent()
+ .setInterface(C3.class.getName(), new Hashtable() {{ put("foo2", "bar2"); put("foo", "overriden");}})
+ .setImplementation(new C3());
+
+ m.add(c1);
+ m.add(c2);
+ m.add(c3);
+
+ e.waitForStep(3, 10000);
+
+ m.remove(c3);
+ m.remove(c2);
+ m.remove(c1);
+ m.clear();
+ }
+
+ /**
+ * Checks that a ServiceDependency propagates the dependency service properties to the provided service properties,
+ * using a callback method.
+ */
+ public void testServiceDependencyPropagateCallback() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ Component c1 = m.createComponent()
+ .setImplementation(new C1(e))
+ .add(m.createServiceDependency().setService(C2.class).setRequired(true).setCallbacks("bind", null));
+
+ C2 c2Impl = new C2();
+ Component c2 = m.createComponent()
+ .setInterface(C2.class.getName(), new Hashtable() {{ put("foo", "bar"); }})
+ .setImplementation(c2Impl)
+ .add(m.createServiceDependency().setService(C3.class).setRequired(true).setPropagate(c2Impl, "getServiceProperties"));
+
+ Component c3 = m.createComponent()
+ .setInterface(C3.class.getName(), null)
+ .setImplementation(new C3());
+
+ m.add(c1);
+ m.add(c2);
+ m.add(c3);
+
+ e.waitForStep(3, 10000);
+ m.clear();
+ }
+
+ public static class C1 {
+ private Map m_props;
+ private Ensure m_ensure;
+
+ C1(Ensure ensure) {
+ m_ensure = ensure;
+ }
+
+ void bind(Map props, C2 c2) {
+ m_props = props;
+ }
+
+ void start() {
+ m_ensure.step(1);
+ if ("bar".equals(m_props.get("foo"))) { // "foo=overriden" from C2 should not override our own "foo" property
+ m_ensure.step(2);
+ }
+ if ("bar2".equals(m_props.get("foo2"))) {
+ m_ensure.step(3);
+ }
+ }
+ }
+
+ public static class C2 {
+ C3 m_c3;
+
+ public Dictionary getServiceProperties(ServiceReference ref) {
+ return new Hashtable() {{ put("foo2", "bar2"); put("foo", "overriden"); }};
+ }
+ }
+
+ public static class C3 {
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyTest.java
new file mode 100644
index 0000000..ae0b2ee
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyTest.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceDependencyTest extends TestBase {
+ public void testServiceRegistrationAndConsumption() {
+ DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ Component sp = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+ Component sc = m.createComponent().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true));
+ Component sc2 = m.createComponent().setImplementation(new ServiceConsumerCallbacks(e)).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(false).setCallbacks("add", "remove"));
+ m.add(sp);
+ m.add(sc);
+ m.remove(sc);
+ m.add(sc2);
+ m.remove(sp);
+ m.remove(sc2);
+ // ensure we executed all steps inside the component instance
+ e.step(6);
+ }
+
+ static interface ServiceInterface {
+ public void invoke();
+ }
+
+ static class ServiceProvider implements ServiceInterface {
+ private final Ensure m_ensure;
+ public ServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+ public void invoke() {
+ m_ensure.step(2);
+ }
+ }
+
+ static class ServiceConsumer {
+ private volatile ServiceInterface m_service;
+ private final Ensure m_ensure;
+
+ public ServiceConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void init() {
+ m_ensure.step(1);
+ m_service.invoke();
+ }
+
+ public void destroy() {
+ m_ensure.step(3);
+ }
+ }
+
+ static class ServiceConsumerCallbacks {
+ private final Ensure m_ensure;
+
+ public ServiceConsumerCallbacks(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void add(ServiceInterface service) {
+ m_ensure.step(4);
+ }
+ public void remove(ServiceInterface service) {
+ m_ensure.step(5);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyThroughCallbackInstanceTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyThroughCallbackInstanceTest.java
new file mode 100644
index 0000000..e376e29
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyThroughCallbackInstanceTest.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.itest.api;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.BundleContext;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceDependencyThroughCallbackInstanceTest extends TestBase {
+ public void testServiceWithCallbacksAndOneDependency() {
+ invokeTest(context, 1);
+ }
+
+ public void testServiceWithCallbacksAndThreeDependencies() {
+ invokeTest(context, 3);
+ }
+
+ private void invokeTest(BundleContext context, int numberOfServices) {
+ DependencyManager m = getDM();
+ // create a number of services
+ for (int i = 0; i < numberOfServices; i++) {
+ final int num = i;
+ m.add(m.createComponent()
+ .setInterface(Service.class.getName(), null)
+ .setImplementation(new Service() {
+ public String toString() {
+ return "A" + num;
+ }
+ }
+ )
+ );
+ }
+
+ // create a service with dependency which will be invoked on a callback instance
+ CallbackInstance instance = new CallbackInstance();
+ m.add(m.createComponent()
+ .setImplementation(new SimpleService() {})
+ .add(m.createServiceDependency()
+ .setService(Service.class)
+ .setCallbacks(instance, "added", "removed")
+ .setRequired(true)
+ )
+ );
+
+ Assert.assertEquals(numberOfServices, instance.getCount());
+ m.clear();
+ }
+
+ public static interface Service {
+ }
+
+ public static interface SimpleService {
+ }
+
+ public static class CallbackInstance {
+ int m_count = 0;
+
+ void added(Service service) {
+ System.out.println("added " + service);
+ m_count++;
+ }
+
+ void removed(Service service) {
+ }
+
+ int getCount() {
+ return m_count;
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceParallelTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceParallelTest.java
new file mode 100644
index 0000000..ba97fbe
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceParallelTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.itest.api;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceRaceParallelTest extends ServiceRaceTest {
+ public ServiceRaceParallelTest() {
+ setParallel(); // Configure DM to use a threadpool
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceTest.java
new file mode 100644
index 0000000..bcc6b17
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceTest.java
@@ -0,0 +1,306 @@
+/*
+ * 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.itest.api;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ConfigurationDependency;
+import org.apache.felix.dm.ServiceDependency;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+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 {
+ volatile ConfigurationAdmin m_cm;
+ final static int STEP_WAIT = 5000;
+ final static int DEPENDENCIES = 10;
+ final static int LOOPS = 3000;
+ final Ensure m_done = new Ensure(true);
+
+ // Executor used to bind/unbind service dependencies.
+ ExecutorService m_threadpool;
+
+ // Timestamp used to log the time consumed to execute 100 tests.
+ long m_timeStamp;
+
+ public interface Dep {
+ }
+
+ public class DepImpl implements Dep {
+ }
+
+ /**
+ * Creates many service dependencies, and activate/deactivate them concurrently.
+ */
+ public void testCreateParallelComponentRegistgrationUnregistration() {
+ m_dm.add(m_dm.createComponent()
+ .setImplementation(this)
+ .setCallbacks(null, "start", null, null)
+ .add(m_dm.createServiceDependency().setService(ConfigurationAdmin.class).setRequired(true)));
+ m_done.waitForStep(1, 60000);
+ m_dm.clear();
+ Assert.assertFalse(super.errorsLogged());
+ }
+
+ void start() {
+ new Thread(new Runnable() {
+ public void run() {
+ doStart();
+ }}).start();
+ }
+
+ void doStart() {
+ info("Starting createParallelComponentRegistgrationUnregistration test");
+ initThreadPool(); // only if setParallel() has not been called (only if a parallel DM is not used).
+
+ try {
+ m_timeStamp = System.currentTimeMillis();
+ for (int loop = 0; loop < LOOPS; loop++) {
+ doTest(loop);
+ }
+ }
+ catch (Throwable t) {
+ error("got unexpected exception", t);
+ }
+ finally {
+ shutdownThreadPool();
+ m_done.step(1);
+ }
+ }
+
+ private void initThreadPool() {
+ if (! m_parallel) {
+ // We are not using a parallel DM, so we create a custom threadpool in order to add components concurrently.
+ int cores = Math.max(16, Runtime.getRuntime().availableProcessors());
+ info("using " + cores + " cores.");
+ m_threadpool = Executors.newFixedThreadPool(Math.max(cores, DEPENDENCIES + 3 /* start/stop/configure */));
+ }
+ }
+
+ void shutdownThreadPool() {
+ if (! m_parallel && m_threadpool != null) {
+ m_threadpool.shutdown();
+ try {
+ m_threadpool.awaitTermination(60, 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 Component client = m_dm.createComponent();
+ final Client clientImpl = new Client(step);
+ client.setImplementation(clientImpl);
+
+ // Create client service dependencies
+ final ServiceDependency[] dependencies = new ServiceDependency[DEPENDENCIES];
+ for (int i = 0; i < DEPENDENCIES; i++) {
+ final String filter = "(id=loop" + loop + "." + i + ")";
+ dependencies[i] = m_dm.createServiceDependency().setService(Dep.class, filter)
+ .setRequired(true)
+ .setCallbacks("add", "remove");
+ client.add(dependencies[i]);
+ }
+ String pid = "pid." + loop;
+ final ConfigurationDependency confDependency = m_dm.createConfigurationDependency().setPid(pid);
+ client.add(confDependency);
+
+ // Create Configuration (concurrently).
+ final Configuration conf = m_cm.getConfiguration(pid, null);
+ final Hashtable props = new Hashtable();
+ props.put("foo", "bar");
+ schedule(new Runnable() {
+ public void run() {
+ try {
+ conf.update(props);
+ }
+ catch (IOException e) {
+ error("update failed", e);
+ }
+ }
+ });
+
+ // Activate the client service dependencies concurrently.
+ List<Component> deps = new ArrayList();
+ for (int i = 0; i < DEPENDENCIES; i++) {
+ Hashtable h = new Hashtable();
+ h.put("id", "loop" + loop + "." + i);
+ final Component s = m_dm.createComponent()
+ .setInterface(Dep.class.getName(), h)
+ .setImplementation(new DepImpl());
+ deps.add(s);
+ schedule(new Runnable() {
+ public void run() {
+ m_dm.add(s);
+ }
+ });
+ }
+
+ // Start the client (concurrently)
+ schedule(new Runnable() {
+ public void run() {
+ m_dm.add(client);
+ }
+ });
+
+ // Ensure that client has been started.
+ int expectedStep = 1 /* conf */ + DEPENDENCIES + 1 /* start */;
+ step.waitForStep(expectedStep, STEP_WAIT);
+ Assert.assertEquals(DEPENDENCIES, clientImpl.getDependencies());
+ Assert.assertNotNull(clientImpl.getConfiguration());
+
+ // Stop all dependencies concurrently.
+ for (Component dep : deps) {
+ final Component dependency = dep;
+ schedule(new Runnable() {
+ public void run() {
+ m_dm.remove(dependency);
+ }
+ });
+ }
+
+ // Stop client concurrently.
+ schedule(new Runnable() {
+ public void run() {
+ m_dm.remove(client);
+ }
+ });
+
+ // Remove configuration (asynchronously)
+ schedule(new Runnable() {
+ public void run() {
+ try {
+ conf.delete();
+ }
+ catch (IOException 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, clientImpl.getDependencies());
+ Assert.assertNull(clientImpl.getConfiguration());
+
+ if (super.errorsLogged()) {
+ throw new IllegalStateException("Race test interrupted (some error occured, see previous logs)");
+ }
+
+ 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();
+ }
+ }
+
+ private void schedule(Runnable task) {
+ if (! m_parallel) {
+ // not using parallel DM, so use our custom threadpool.
+ m_threadpool.execute(task);
+ } else {
+ task.run();
+ }
+ }
+
+ public class Client {
+ final Ensure m_step;
+ volatile int m_dependencies;
+ volatile Dictionary m_conf;
+
+ public Client(Ensure step) {
+ m_step = step;
+ }
+
+ public void updated(Dictionary conf) throws ConfigurationException {
+ m_conf = conf;
+ try {
+ if (conf != null) {
+ Assert.assertEquals("bar", conf.get("foo"));
+ m_step.step(1);
+ } else {
+ m_step.step();
+ }
+ } catch (Throwable t) {
+ m_step.throwable(t);
+ }
+ }
+
+ void add(Dep d) {
+ Assert.assertNotNull(d);
+ m_step.step();
+ m_dependencies ++;
+ }
+
+ void remove(Dep d) {
+ Assert.assertNotNull(d);
+ 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 */);
+ }
+
+ int getDependencies() {
+ return m_dependencies;
+ }
+
+ Dictionary getConfiguration() {
+ return m_conf;
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceTrackerTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceTrackerTest.java
new file mode 100644
index 0000000..d9076b4
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceTrackerTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.itest.api;
+
+import java.util.Hashtable;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.ServiceUtil;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.tracker.ServiceTracker;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class ServiceTrackerTest extends TestBase {
+ public void testPlainServiceTracker() {
+ ServiceTracker st = new ServiceTracker(context, ServiceInterface.class.getName(), null);
+ st.open();
+ ServiceRegistration sr = context.registerService(ServiceInterface.class.getName(), new ServiceProvider(), null);
+ Assert.assertEquals("There should be one service that matches the tracker", 1, st.getServices().length);
+ sr.unregister();
+ Assert.assertNull("There should be no service that matches the tracker", st.getServices());
+ st.close();
+ }
+
+ public void testAspectServiceTracker() {
+ ServiceTracker st = new ServiceTracker(context, ServiceInterface.class.getName(), null);
+ st.open();
+
+ ServiceRegistration sr = context.registerService(ServiceInterface.class.getName(), new ServiceProvider(), null);
+ Assert.assertEquals("There should be one service that matches the tracker", 1, st.getServices().length);
+
+ final long sid = ServiceUtil.getServiceId(sr.getReference());
+ ServiceRegistration asr = context.registerService(ServiceInterface.class.getName(), new ServiceProvider(),
+ new Hashtable() {{ put(DependencyManager.ASPECT, sid); put(Constants.SERVICE_RANKING, 10); }});
+ Assert.assertEquals("There should be one service that matches the tracker", 1, st.getServices().length);
+ Assert.assertEquals("Service ranking should be 10", Integer.valueOf(10), (Integer) st.getServiceReference().getProperty(Constants.SERVICE_RANKING));
+
+ ServiceRegistration asr2 = context.registerService(ServiceInterface.class.getName(), new ServiceProvider(),
+ new Hashtable() {{ put(DependencyManager.ASPECT, sid); put(Constants.SERVICE_RANKING, 20); }});
+ Assert.assertEquals("There should be one service that matches the tracker", 1, st.getServices().length);
+ Assert.assertEquals("Service ranking should be 20", Integer.valueOf(20), (Integer) st.getServiceReference().getProperty(Constants.SERVICE_RANKING));
+
+ asr.unregister();
+ Assert.assertEquals("There should be one service that matches the tracker", 1, st.getServices().length);
+ Assert.assertEquals("Service ranking should be 20", Integer.valueOf(20), (Integer) st.getServiceReference().getProperty(Constants.SERVICE_RANKING));
+
+ asr2.unregister();
+ Assert.assertEquals("There should be one service that matches the tracker", 1, st.getServices().length);
+ Assert.assertNull("Service should not have a ranking", st.getServiceReference().getProperty(Constants.SERVICE_RANKING));
+
+ sr.unregister();
+ Assert.assertNull("There should be no service that matches the tracker", st.getServices());
+
+ st.close();
+ }
+
+ public void testExistingAspectServiceTracker() {
+ ServiceTracker st = new ServiceTracker(context, ServiceInterface.class.getName(), null);
+ ServiceRegistration sr = context.registerService(ServiceInterface.class.getName(), new ServiceProvider(), null);
+ final long sid = ServiceUtil.getServiceId(sr.getReference());
+ ServiceRegistration asr = context.registerService(ServiceInterface.class.getName(), new ServiceProvider(),
+ new Hashtable() {{ put(DependencyManager.ASPECT, sid); put(Constants.SERVICE_RANKING, 10); }});
+ ServiceRegistration asr2 = context.registerService(ServiceInterface.class.getName(), new ServiceProvider(),
+ new Hashtable() {{ put(DependencyManager.ASPECT, sid); put(Constants.SERVICE_RANKING, 20); }});
+
+ st.open();
+ Assert.assertEquals("There should be one service that matches the tracker", 1, st.getServices().length);
+ Assert.assertEquals("Service ranking should be 20", Integer.valueOf(20), (Integer) st.getServiceReference().getProperty(Constants.SERVICE_RANKING));
+
+ asr2.unregister();
+ Assert.assertEquals("There should be one service that matches the tracker", 1, st.getServices().length);
+ Assert.assertEquals("Service ranking should be 10", Integer.valueOf(10), (Integer) st.getServiceReference().getProperty(Constants.SERVICE_RANKING));
+
+ asr.unregister();
+ Assert.assertEquals("There should be one service that matches the tracker", 1, st.getServices().length);
+ Assert.assertNull("Service should not have a ranking", st.getServiceReference().getProperty(Constants.SERVICE_RANKING));
+
+ sr.unregister();
+ Assert.assertNull("There should be no service that matches the tracker", st.getServices());
+
+ st.close();
+ }
+
+ static interface ServiceInterface {
+ public void invoke();
+ }
+
+ static class ServiceProvider implements ServiceInterface {
+ public void invoke() {
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceUpdateTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceUpdateTest.java
new file mode 100644
index 0000000..6bc1522
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceUpdateTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.itest.api;
+
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceUpdateTest extends TestBase {
+ public void testServiceUpdate() throws Exception {
+ final DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a resource provider
+ ResourceProvider provider = new ResourceProvider(context, new URL("file://localhost/path/to/file1.txt"));
+ // activate it
+ m.add(m.createComponent()
+ .setImplementation(new ServiceProvider(e))
+ .add(m.createServiceDependency()
+ .setService(ServiceInterface.class)
+ .setRequired(true)
+ .setCallbacks("add", "change", "remove")
+ )
+ );
+
+ m.add(m.createComponent()
+ .setImplementation(provider)
+ .add(m.createServiceDependency()
+ .setService(ResourceHandler.class)
+ .setCallbacks("add", "remove")
+ )
+ );
+
+ // create a resource adapter for our single resource
+ // note that we can provide an actual implementation instance here because there will be only one
+ // adapter, normally you'd want to specify a Class here
+ CallbackInstance callbackInstance = new CallbackInstance(e);
+ Hashtable<String, String> serviceProps = new Hashtable<String, String>();
+ serviceProps.put("number", "1");
+ Component component = m.createResourceAdapterService("(&(path=/path/to/*.txt)(host=localhost))", false, callbackInstance, "changed")
+ .setImplementation(new ResourceAdapter(e))
+ .setInterface(ServiceInterface.class.getName(), serviceProps)
+ .setCallbacks(callbackInstance, "init", "start", "stop", "destroy");
+ m.add(component);
+ // wait until the single resource is available
+ e.waitForStep(1, 5000);
+ // wait until the component gets the dependency injected
+ e.waitForStep(2, 5000);
+ // trigger a 'change' in our resource
+ provider.change();
+ // wait until the changed callback is invoked
+ e.waitForStep(3, 5000);
+ // wait until the changed event arrived at the component
+ e.waitForStep(4, 5000);
+ System.out.println("Done!");
+ m.clear();
+ }
+
+ static class ResourceAdapter implements ServiceInterface {
+ protected URL m_resource; // injected by reflection.
+
+ ResourceAdapter(Ensure e) {
+ }
+
+ public void invoke() {
+ // TODO Auto-generated method stub
+
+ }
+ }
+
+ static interface ServiceInterface {
+ public void invoke();
+ }
+
+ static class ServiceProvider {
+ private final Ensure m_ensure;
+ public ServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+ void add(ServiceInterface i) {
+ m_ensure.step(2);
+ }
+ void change(ServiceInterface i) {
+ System.out.println("Change...");
+ m_ensure.step(4);
+ }
+ void remove(ServiceInterface i) {
+ System.out.println("Remove...");
+ }
+ }
+
+ static class CallbackInstance {
+ private final Ensure m_ensure;
+ public CallbackInstance(Ensure e) {
+ m_ensure = e;
+ }
+
+ void init() {
+ System.out.println("init");
+ }
+ void start() {
+ System.out.println("start");
+ m_ensure.step(1);
+ }
+ void stop() {
+ System.out.println("stop");
+ }
+ void destroy() {
+ System.out.println("destroy");
+ }
+ void changed(Component component) {
+ System.out.println("resource changed");
+ m_ensure.step(3);
+
+ Properties newProps = new Properties();
+ // update the component's service properties
+ Dictionary<String, String> dict = component.getServiceProperties();
+ Enumeration<String> e = dict.keys();
+ while (e.hasMoreElements()) {
+ String key = e.nextElement();
+ String value = dict.get(key);
+ newProps.setProperty(key, value);
+ }
+ newProps.setProperty("new-property", "2");
+ component.getServiceRegistration().setProperties(newProps);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/TemporalServiceDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/TemporalServiceDependencyTest.java
new file mode 100644
index 0000000..97b1565
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/TemporalServiceDependencyTest.java
@@ -0,0 +1,206 @@
+/*
+ * 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.itest.api;
+
+import java.util.Hashtable;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ServiceDependency;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class TemporalServiceDependencyTest extends TestBase {
+ public void testServiceConsumptionAndIntermittentAvailability() {
+ final DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ TemporalServiceProvider provider = new TemporalServiceProvider(e);
+ Component sp = m.createComponent().setImplementation(provider).setInterface(TemporalServiceInterface.class.getName(), null);
+ TemporalServiceProvider2 provider2 = new TemporalServiceProvider2(e);
+ Component sp2 = m.createComponent().setImplementation(provider2).setInterface(TemporalServiceInterface.class.getName(), null);
+ TemporalServiceConsumer consumer = new TemporalServiceConsumer(e);
+ Component sc = m.createComponent().setImplementation(consumer)
+ .add(m.createTemporalServiceDependency(10000).setService(TemporalServiceInterface.class).setRequired(true));
+ // add the service consumer
+ m.add(sc);
+ // now add the first provider
+ m.add(sp);
+ e.waitForStep(2, 5000);
+ // and remove it again (this should not affect the consumer yet)
+ m.remove(sp);
+ // now add the second provider
+ m.add(sp2);
+ e.step(3);
+ e.waitForStep(4, 5000);
+ // and remove it again
+ m.remove(sp2);
+ // finally remove the consumer
+ m.remove(sc);
+ // ensure we executed all steps inside the component instance
+ e.step(6);
+ m.clear();
+ }
+
+ public void testServiceConsumptionWithCallbackAndIntermittentAvailability() {
+ final DependencyManager m = getDM();
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ TemporalServiceProvider provider = new TemporalServiceProvider(e);
+ Component sp = m.createComponent().setImplementation(provider).setInterface(TemporalServiceInterface.class.getName(), null);
+ TemporalServiceProvider2 provider2 = new TemporalServiceProvider2(e);
+ Component sp2 = m.createComponent().setImplementation(provider2).setInterface(TemporalServiceInterface.class.getName(), null);
+ TemporalServiceConsumerWithCallback consumer = new TemporalServiceConsumerWithCallback(e);
+ ServiceDependency temporalDep = m.createTemporalServiceDependency(10000).setService(TemporalServiceInterface.class).setRequired(true).setCallbacks("add", "remove");
+ Component sc = m.createComponent().setImplementation(consumer).add(temporalDep);
+
+ // add the service consumer
+ m.add(sc);
+ // now add the first provider
+ m.add(sp);
+ e.waitForStep(2, 5000);
+ // and remove it again (this should not affect the consumer yet)
+ m.remove(sp);
+ // now add the second provider
+ m.add(sp2);
+ e.step(3);
+ e.waitForStep(4, 5000);
+ // and remove it again
+ m.remove(sp2);
+ // finally remove the consumer
+ m.remove(sc);
+ // Wait for the consumer.remove callback
+ e.waitForStep(6, 5000);
+ // ensure we executed all steps inside the component instance
+ e.step(7);
+ m.clear();
+ }
+
+ public void testFelix4602_PropagateServiceInvocationException() {
+ final DependencyManager m = getDM();
+ final Ensure ensure = new Ensure();
+ Runnable provider = new Runnable() {
+ public void run() {
+ throw new UncheckedException();
+ }
+ };
+ Hashtable props = new Hashtable();
+ props.put("target", getClass().getSimpleName());
+ Component providerComp = m.createComponent()
+ .setInterface(Runnable.class.getName(), props)
+ .setImplementation(provider);
+
+ Object consumer = new Object() {
+ volatile Runnable m_provider;
+ @SuppressWarnings("unused")
+ void start() {
+ try {
+ ensure.step(1);
+ m_provider.run();
+ } catch (UncheckedException e) {
+ ensure.step(2);
+ }
+ }
+ };
+ Component consumerComp = m.createComponent()
+ .setImplementation(consumer)
+ .add(m.createTemporalServiceDependency(5000)
+ .setService(Runnable.class, "(target=" + getClass().getSimpleName() + ")")
+ .setRequired(true));
+ m.add(consumerComp);
+ m.add(providerComp);
+ ensure.waitForStep(2, 5000);
+ m.clear();
+ }
+
+ static class UncheckedException extends RuntimeException {
+ }
+
+ static interface TemporalServiceInterface {
+ public void invoke();
+ }
+
+ static class TemporalServiceProvider implements TemporalServiceInterface {
+ private final Ensure m_ensure;
+ public TemporalServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+ public void invoke() {
+ m_ensure.step(2);
+ }
+ }
+
+ static class TemporalServiceProvider2 implements TemporalServiceInterface {
+ protected final Ensure m_ensure;
+ public TemporalServiceProvider2(Ensure e) {
+ m_ensure = e;
+ }
+ public void invoke() {
+ m_ensure.step(4);
+ }
+ }
+
+ static class TemporalServiceConsumer implements Runnable {
+ protected volatile TemporalServiceInterface m_service;
+ protected final Ensure m_ensure;
+
+ public TemporalServiceConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void init() {
+ m_ensure.step(1);
+ Thread t = new Thread(this);
+ t.start();
+ }
+
+ public void run() {
+ m_service.invoke();
+ m_ensure.waitForStep(3, 15000);
+ m_service.invoke();
+ }
+
+ public void destroy() {
+ m_ensure.step(5);
+ }
+ }
+
+ static class TemporalServiceConsumerWithCallback extends TemporalServiceConsumer {
+ public TemporalServiceConsumerWithCallback(Ensure e) {
+ super(e);
+ }
+
+ public void add(TemporalServiceInterface service) {
+ m_service = service;
+ }
+
+ public void remove(TemporalServiceInterface service) {
+ Assert.assertTrue(m_service == service);
+ m_ensure.step(6);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/bundle/Activator.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/bundle/Activator.java
new file mode 100644
index 0000000..a550e30
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/bundle/Activator.java
@@ -0,0 +1,38 @@
+/*
+ * 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.itest.bundle;
+
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Activator extends DependencyActivatorBase {
+
+ @Override
+ public void init(BundleContext ctx, DependencyManager m)
+ throws Exception {
+ m.add(createComponent()
+ .setImplementation(TestComponent.class)
+ .setInterface(TestService.class.getName(), null));
+ }
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/bundle/TestComponent.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/bundle/TestComponent.java
new file mode 100644
index 0000000..2ff626f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/bundle/TestComponent.java
@@ -0,0 +1,28 @@
+/*
+ * 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.itest.bundle;
+
+/**
+ * Test Component used by the FELIX2955_ShellCommandTest test.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class TestComponent implements TestService {
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/bundle/TestService.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/bundle/TestService.java
new file mode 100644
index 0000000..6393075
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/bundle/TestService.java
@@ -0,0 +1,26 @@
+/*
+ * 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.itest.bundle;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface TestService {
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/Ensure.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/Ensure.java
new file mode 100644
index 0000000..9c34741
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/Ensure.java
@@ -0,0 +1,174 @@
+/*
+ * 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.itest.util;
+
+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;
+
+ 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) {
+ step++;
+ Assert.assertEquals(nr, step);
+ 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() {
+ 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.itest/src/org/apache/felix/dm/itest/util/ServiceUtil.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/ServiceUtil.java
new file mode 100644
index 0000000..628189c
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/ServiceUtil.java
@@ -0,0 +1,176 @@
+/*
+ * 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.itest.util;
+
+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 (copied from dependency manager implementation).
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceUtil {
+ /**
+ * 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();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/TestBase.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/TestBase.java
new file mode 100644
index 0000000..4cedfb6
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/TestBase.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.itest.util;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Hashtable;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Assert;
+import junit.framework.TestCase;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentExecutorFactory;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.log.LogService;
+
+/**
+ * Base class for all integration tests.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class TestBase extends TestCase implements LogService, FrameworkListener {
+ // Default OSGI log service level.
+ protected final static int LOG_LEVEL = LogService.LOG_WARNING;
+
+ // optional thread pool used by parallel dependency managers
+ private volatile ExecutorService m_threadPool;
+
+ // flag used to check if the threadpool must be used for a given test.
+ protected volatile boolean m_parallel;
+
+ // Flag used to check if some errors have been logged during the execution of a given test.
+ private volatile boolean m_errorsLogged;
+
+ // We implement OSGI log service.
+ protected ServiceRegistration logService;
+
+ // Our bundle context
+ protected BundleContext context;
+
+ // Our dependency manager used to create test components.
+ protected volatile DependencyManager m_dm;
+
+ // The Registration for the DM threadpool.
+ private ServiceRegistration m_componentExecutorFactoryReg;
+
+ public TestBase() {
+ }
+
+ protected void setParallel() {
+ m_parallel = true;
+ }
+
+ public void setUp() throws Exception {
+ warn("Setting up test " + getClass().getName());
+ context = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
+ Hashtable<String, Object> props = new Hashtable<>();
+ props.put(Constants.SERVICE_RANKING, new Integer(Integer.MAX_VALUE));
+ logService = context.registerService(LogService.class.getName(), this, props);
+ context.addFrameworkListener(this);
+ m_dm = new DependencyManager(context);
+ if (m_parallel) {
+ warn("Using threadpool ...");
+ m_threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
+ m_componentExecutorFactoryReg = context.registerService(ComponentExecutorFactory.class.getName(),
+ new ComponentExecutorFactory() {
+ @Override
+ public Executor getExecutorFor(Component component) {
+ return m_threadPool;
+ }
+ },
+ null);
+ }
+ }
+
+ public void tearDown() throws Exception {
+ warn("Tearing down test " + getClass().getName());
+ logService.unregister();
+ context.removeFrameworkListener(this);
+ clearComponents();
+ if (m_parallel && m_componentExecutorFactoryReg != null) {
+ m_componentExecutorFactoryReg.unregister();
+ m_threadPool.shutdown();
+ try {
+ m_threadPool.awaitTermination(60, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ }
+ }
+ Assert.assertFalse(errorsLogged());
+ }
+
+ protected DependencyManager getDM() {
+ return m_dm;
+ }
+
+ protected void clearComponents() throws InterruptedException {
+ m_dm.clear();
+ warn("All component cleared.");
+ }
+
+ /**
+ * Creates and provides an Ensure object with a name service property into the OSGi service registry.
+ */
+ protected ServiceRegistration register(Ensure e, String name) {
+ Hashtable<String, String> props = new Hashtable<String, String>();
+ props.put("name", name);
+ return context.registerService(Ensure.class.getName(), e, props);
+ }
+
+ /**
+ * Helper method used to stop a given bundle.
+ *
+ * @param symbolicName
+ * the symbolic name of the bundle to be stopped.
+ */
+ protected void stopBundle(String symbolicName) {
+ // Stop the test.annotation bundle
+ boolean found = false;
+ for (Bundle b : context.getBundles()) {
+ if (b.getSymbolicName().equals(symbolicName)) {
+ try {
+ found = true;
+ b.stop();
+ } catch (BundleException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ if (!found) {
+ throw new IllegalStateException("bundle " + symbolicName + " not found");
+ }
+ }
+
+ /**
+ * Helper method used to start a given bundle.
+ *
+ * @param symbolicName
+ * the symbolic name of the bundle to be started.
+ */
+ protected void startBundle(String symbolicName) {
+ // Stop the test.annotation bundle
+ boolean found = false;
+ for (Bundle b : context.getBundles()) {
+ if (b.getSymbolicName().equals(symbolicName)) {
+ try {
+ found = true;
+ b.start();
+ } catch (BundleException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ if (!found) {
+ throw new IllegalStateException("bundle " + symbolicName + " not found");
+ }
+ }
+
+ /**
+ * Helper method used to get a given bundle.
+ *
+ * @param symbolicName
+ * the symbolic name of the bundle to get.
+ */
+ protected Bundle getBundle(String symbolicName) {
+ for (Bundle b : context.getBundles()) {
+ if (b.getSymbolicName().equals(symbolicName)) {
+ return b;
+ }
+ }
+ throw new IllegalStateException("bundle " + symbolicName + " not found");
+ }
+
+ /**
+ * Suspend the current thread for a while.
+ *
+ * @param n
+ * the number of milliseconds to wait for.
+ */
+ protected void sleep(int ms) {
+ try {
+ Thread.sleep(ms);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ public void log(int level, String message) {
+ checkError(level, null);
+ if (LOG_LEVEL >= level) {
+ System.out.println(getLevel(level) + " - " + Thread.currentThread().getName() + " : " + message);
+ }
+ }
+
+ public void log(int level, String message, Throwable exception) {
+ checkError(level, exception);
+ if (LOG_LEVEL >= level) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(getLevel(level) + " - " + Thread.currentThread().getName() + " : ");
+ sb.append(message);
+ parse(sb, exception);
+ System.out.println(sb.toString());
+ }
+ }
+
+ public void log(ServiceReference sr, int level, String message) {
+ checkError(level, null);
+ if (LOG_LEVEL >= level) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(getLevel(level) + " - " + Thread.currentThread().getName() + " : ");
+ sb.append(message);
+ System.out.println(sb.toString());
+ }
+ }
+
+ public void log(ServiceReference sr, int level, String message, Throwable exception) {
+ checkError(level, exception);
+ if (LOG_LEVEL >= level) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(getLevel(level) + " - " + Thread.currentThread().getName() + " : ");
+ sb.append(message);
+ parse(sb, exception);
+ System.out.println(sb.toString());
+ }
+ }
+
+ protected boolean errorsLogged() {
+ return m_errorsLogged;
+ }
+
+ private void parse(StringBuilder sb, Throwable t) {
+ if (t != null) {
+ sb.append(" - ");
+ StringWriter buffer = new StringWriter();
+ PrintWriter pw = new PrintWriter(buffer);
+ t.printStackTrace(pw);
+ sb.append(buffer.toString());
+ m_errorsLogged = true;
+ }
+ }
+
+ private String getLevel(int level) {
+ switch (level) {
+ case LogService.LOG_DEBUG :
+ return "DEBUG";
+ case LogService.LOG_ERROR :
+ return "ERROR";
+ case LogService.LOG_INFO :
+ return "INFO";
+ case LogService.LOG_WARNING :
+ return "WARN";
+ default :
+ return "";
+ }
+ }
+
+ private void checkError(int level, Throwable exception) {
+ if (level <= LOG_ERROR) {
+ m_errorsLogged = true;
+ }
+ if (exception != null) {
+ m_errorsLogged = true;
+ }
+ }
+
+ public void frameworkEvent(FrameworkEvent event) {
+ int eventType = event.getType();
+ String msg = getFrameworkEventMessage(eventType);
+ int level = (eventType == FrameworkEvent.ERROR) ? LOG_ERROR : LOG_WARNING;
+ if (msg != null) {
+ log(level, msg, event.getThrowable());
+ } else {
+ log(level, "Unknown fwk event: " + event);
+ }
+ }
+
+ private String getFrameworkEventMessage(int event) {
+ switch (event) {
+ case FrameworkEvent.ERROR :
+ return "FrameworkEvent: ERROR";
+ case FrameworkEvent.INFO :
+ return "FrameworkEvent INFO";
+ case FrameworkEvent.PACKAGES_REFRESHED :
+ return "FrameworkEvent: PACKAGE REFRESHED";
+ case FrameworkEvent.STARTED :
+ return "FrameworkEvent: STARTED";
+ case FrameworkEvent.STARTLEVEL_CHANGED :
+ return "FrameworkEvent: STARTLEVEL CHANGED";
+ case FrameworkEvent.WARNING :
+ return "FrameworkEvent: WARNING";
+ default :
+ return null;
+ }
+ }
+
+ protected void warn(String msg, Object ... params) {
+ if (LOG_LEVEL >= LogService.LOG_WARNING) {
+ log(LogService.LOG_WARNING, params.length > 0 ? String.format(msg, params) : msg);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ protected void info(String msg, Object ... params) {
+ if (LOG_LEVEL >= LogService.LOG_INFO) {
+ log(LogService.LOG_INFO, params.length > 0 ? String.format(msg, params) : msg);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ protected void debug(String msg, Object ... params) {
+ if (LOG_LEVEL >= LogService.LOG_DEBUG) {
+ log(LogService.LOG_DEBUG, params.length > 0 ? String.format(msg, params) : msg);
+ }
+ }
+
+ protected void error(String msg, Object ... params) {
+ log(LogService.LOG_ERROR, params.length > 0 ? String.format(msg, params) : msg);
+ }
+
+ protected void error(String msg, Throwable err, Object ... params) {
+ log(LogService.LOG_ERROR, params.length > 0 ? String.format(msg, params) : msg, err);
+ }
+
+ protected void error(Throwable err) {
+ log(LogService.LOG_ERROR, "error", err);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/packageinfo b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/packageinfo
new file mode 100644
index 0000000..e252556
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/packageinfo
@@ -0,0 +1 @@
+version 1.0.0
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/test/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.itest/test/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/test/.gitignore
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/.classpath b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/.classpath
new file mode 100644
index 0000000..f89ae43
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/.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.runtime.itest/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/.gitignore
new file mode 100644
index 0000000..90dde36
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/.gitignore
@@ -0,0 +1,3 @@
+/bin/
+/bin_test/
+/generated/
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/.project b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/.project
new file mode 100644
index 0000000..919fddb
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.apache.felix.dependencymanager.runtime.itest</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.runtime.itest/.settings/org.eclipse.jdt.core.prefs b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..7341ab1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/.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.runtime.itest/bnd.bnd b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/bnd.bnd
new file mode 100644
index 0000000..38e5cce
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/bnd.bnd
@@ -0,0 +1,50 @@
+#
+# 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,\
+ ${junit},\
+ org.apache.felix.dependencymanager.annotation;version=latest,\
+ org.apache.felix.dependencymanager;version=latest,\
+ org.apache.felix.dependencymanager.runtime;version=latest,\
+ org.apache.felix.dependencymanager.itest.api;version=latest
+-runbundles: \
+ org.apache.felix.configadmin;version=1.8.1.SNAPSHOT,\
+ org.apache.felix.metatype;version=1.0.4,\
+ org.apache.felix.gogo.runtime;version=0.10.0,\
+ org.apache.felix.log;version=1.0.1,\
+ org.apache.felix.dependencymanager;version=latest,\
+ org.apache.felix.dependencymanager.runtime;version=latest,\
+ org.apache.felix.dependencymanager.shell;version=latest
+-runee: JavaSE-1.7
+-runfw: org.apache.felix.framework;version='[4.4.0,4.4.0]'
+-runsystempackages: \
+ sun.reflect
+-runvm:-ea
+Private-Package: \
+ org.apache.felix.dm.runtime.itest.tests,\
+ org.apache.felix.dm.runtime.itest.components,\
+ org.apache.felix.dm.itest.util
+-plugin: org.apache.felix.dm.annotation.plugin.bnd.AnnotationPlugin;log=debug;\
+ path:=${workspace}/org.apache.felix.dependencymanager.annotation/generated/org.apache.felix.dependencymanager.annotation.jar
+Test-Cases: \
+ ${classes;CONCRETE;EXTENDS;junit.framework.TestCase}
+Bundle-Name: Apache Felix Dependency Manager Runtime integration tests
+Bundle-Description: Integration tests for Apache Felix Dependency Manager Runtime
+Bundle-Category: test
+Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+Bundle-Vendor: The Apache Software Foundation
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/AdapterAnnotation.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/AdapterAnnotation.java
new file mode 100644
index 0000000..35df1d1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/AdapterAnnotation.java
@@ -0,0 +1,200 @@
+/*
+ * 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.runtime.itest.components;
+
+import java.util.Map;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.annotation.api.AdapterService;
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Inject;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.osgi.framework.BundleContext;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterAnnotation {
+ public interface S1 {
+ public void run();
+ }
+
+ public interface S2 {
+ public void run2();
+ }
+
+ public interface S3 {
+ public void run3();
+ }
+
+ @Component
+ public static class S3Consumer {
+ private volatile Map<String, String> m_serviceProperties;
+ private volatile S3 m_s3;
+
+ @ServiceDependency
+ void bind(Map<String, String> serviceProperties, S3 s3) {
+ m_serviceProperties = serviceProperties;
+ m_s3 = s3;
+ }
+
+ @Start
+ void start() {
+ // The adapter service must inherit from adaptee service properties ...
+ if ("value1".equals(m_serviceProperties.get("param1")) // adaptee properties
+ && "true".equals(m_serviceProperties.get("adapter"))) // adapter properties
+ {
+ m_s3.run3();
+ }
+ }
+ }
+
+ @AdapterService(adapteeService = S1.class, properties = { @Property(name = "adapter", value = "true") })
+ public static class S1ToS3AdapterAutoConfig implements S3 {
+ public static final String ENSURE = "AdapterAnnotation.autoConfig";
+
+ // This is the adapted service
+ protected volatile S1 m_s1;
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ protected volatile Ensure m_sequencer;
+
+ // Check auto config injections
+ @Inject
+ volatile BundleContext m_bc;
+ BundleContext m_bcNotInjected;
+
+ @Inject
+ volatile DependencyManager m_dm;
+ DependencyManager m_dmNotInjected;
+
+ @Inject
+ volatile org.apache.felix.dm.Component m_component;
+ org.apache.felix.dm.Component m_componentNotInjected;
+
+ public void run3() {
+ checkInjectedFields();
+ m_s1.run();
+ m_sequencer.step(3);
+ }
+
+ private void checkInjectedFields() {
+ if (m_bc == null) {
+ m_sequencer.throwable(new Exception("Bundle Context not injected"));
+ return;
+ }
+ if (m_bcNotInjected != null) {
+ m_sequencer.throwable(new Exception("Bundle Context must not be injected"));
+ return;
+ }
+
+ if (m_dm == null) {
+ m_sequencer.throwable(new Exception("DependencyManager not injected"));
+ return;
+ }
+ if (m_dmNotInjected != null) {
+ m_sequencer.throwable(new Exception("DependencyManager must not be injected"));
+ return;
+ }
+
+ if (m_component == null) {
+ m_sequencer.throwable(new Exception("Component not injected"));
+ return;
+ }
+ if (m_componentNotInjected != null) {
+ m_sequencer.throwable(new Exception("Component must not be injected"));
+ return;
+ }
+ }
+ }
+
+ @AdapterService(adapteeService = S1.class, properties = { @Property(name = "adapter", value = "true") }, field = "m_s1")
+ public static class S1ToS3AdapterAutoConfigField implements S3 {
+ public final static String ENSURE = "AdapterAnnotation.autoConfig.field";
+ // This is the adapted service
+ protected volatile S1 m_s1;
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ protected volatile Ensure m_sequencer;
+
+ public void run3() {
+ m_s1.run();
+ m_sequencer.step(3);
+ }
+ }
+
+ @AdapterService(adapteeService = S1.class, properties = { @Property(name = "adapter", value = "true") }, added = "bind", removed = "removed")
+ public static class S1ToS3AdapterCallback implements S3 {
+ public final static String ENSURE = "AdapterAnnotation.callback";
+ // This is the adapted service
+ protected Object m_s1;
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ protected Ensure m_sequencer;
+
+ void bind(S1 s1) {
+ m_s1 = s1;
+ }
+
+ public void run3() {
+ ((S1) m_s1).run();
+ }
+
+ @Stop
+ void stop() {
+ m_sequencer.step(3);
+ }
+
+ void removed(S1 s1) {
+ m_sequencer.step(4);
+ }
+ }
+
+ @Component(properties = { @Property(name = "param1", value = "value1") })
+ public static class S1Impl implements S1 {
+ public final static String ENSURE = "AdapterAnnotation.S1Impl";
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ protected Ensure m_sequencer;
+
+ @ServiceDependency
+ protected S2 m_s2;
+
+ public void run() {
+ m_sequencer.step(1);
+ m_s2.run2();
+ }
+ }
+
+ @Component
+ public static class S2Impl implements S2 {
+ public final static String ENSURE = "AdapterAnnotation.S2Impl";
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ protected Ensure m_sequencer;
+
+ public void run2() {
+ m_sequencer.step(2);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/AdapterServiceTestWithPublisher.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/AdapterServiceTestWithPublisher.java
new file mode 100644
index 0000000..42fadd6
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/AdapterServiceTestWithPublisher.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.runtime.itest.components;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.felix.dm.annotation.api.AdapterService;
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.LifecycleController;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.itest.util.Ensure;
+
+/**
+ * Test an AdapterService which provides its interface using a @ServiceLifecycle.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class AdapterServiceTestWithPublisher {
+ public static final String ENSURE = "AdapterServiceTestWithPublisher";
+
+ public interface Provider {
+ }
+
+ public interface Provider2 {
+ }
+
+ @Component
+ public static class Consumer {
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @ServiceDependency(required = false, removed = "unbind")
+ void bind(Map properties, Provider2 provider) {
+ m_sequencer.step(1);
+ // check ProviderImpl properties
+ if ("bar".equals(properties.get("foo"))) {
+ m_sequencer.step(2);
+ }
+ // check extra ProviderImpl properties (returned by start method)
+ if ("bar2".equals(properties.get("foo2"))) {
+ m_sequencer.step(3);
+ }
+ // check Provider2Impl properties
+ if ("bar3".equals(properties.get("foo3"))) {
+ m_sequencer.step(4);
+ }
+ // check extra Provider2Impl properties (returned by start method)
+ if ("bar4".equals(properties.get("foo4"))) {
+ m_sequencer.step(5);
+ }
+
+ }
+
+ void unbind(Provider2 provider) {
+ m_sequencer.step(6);
+ }
+ }
+
+ @Component(properties = {@Property(name = "foo", value = "bar")})
+ public static class ProviderImpl implements Provider {
+ @Start
+ Map start() {
+ // Add some extra service properties ... they will be appended to the one we have defined
+ // in the @Service annotation.
+ return new HashMap() {
+ {
+ put("foo2", "bar2");
+ }
+ };
+ }
+ }
+
+ @AdapterService(properties = {@Property(name = "foo3", value = "bar3")}, adapteeService = Provider.class)
+ public static class Provider2Impl implements Provider2 {
+ @LifecycleController
+ volatile Runnable m_publisher; // injected and used to register our service
+
+ @LifecycleController(start = false)
+ volatile Runnable m_unpublisher; // injected and used to unregister our service
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @Init
+ void init() {
+ // register service in 1 second
+ Utils.schedule(m_publisher, 1000);
+ // unregister the service in 2 seconds
+ Utils.schedule(m_unpublisher, 2000);
+ }
+
+ @Start
+ Map start() {
+ // Add some extra service properties ... they will be appended to the one we have defined
+ // in the @Service annotation.
+ return new HashMap() {
+ {
+ put("foo4", "bar4");
+ }
+ };
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/AspectAnnotation.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/AspectAnnotation.java
new file mode 100644
index 0000000..108debe
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/AspectAnnotation.java
@@ -0,0 +1,237 @@
+/*
+ * 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.runtime.itest.components;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.annotation.api.AspectService;
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Destroy;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.Inject;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectAnnotation {
+ public interface ServiceInterface {
+ public void invoke(Runnable run);
+ }
+
+ @Component
+ public static class ServiceProvider implements ServiceInterface {
+ public final static String ENSURE = "AspectAnnotation.ServiceProvider";
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ protected volatile Ensure m_sequencer;
+ // Injected by reflection.
+ protected volatile ServiceRegistration m_sr;
+
+ @Init
+ void init() {
+ System.out.println("ServiceProvider.init");
+ }
+
+ @Destroy
+ void destroy() {
+ System.out.println("ServiceProvider.destroy");
+ }
+
+ public void invoke(Runnable run) {
+ run.run();
+ m_sequencer.step(6);
+ }
+ }
+
+ @AspectService(ranking = 20)
+ public static class ServiceAspect2 implements ServiceInterface {
+ public final static String ENSURE = "AspectAnnotation.ServiceAspect2";
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ protected volatile Ensure m_sequencer;
+ // Injected by reflection.
+ private volatile ServiceInterface m_parentService;
+
+ // Check auto config injections
+ @Inject
+ volatile BundleContext m_bc;
+ BundleContext m_bcNotInjected;
+
+ @Inject
+ volatile DependencyManager m_dm;
+ DependencyManager m_dmNotInjected;
+
+ @Inject
+ volatile org.apache.felix.dm.Component m_component;
+ org.apache.felix.dm.Component m_componentNotInjected;
+
+ @Init
+ void init() {
+ System.out.println("ServiceAspect2.init");
+ }
+
+ @Destroy
+ void destroy() {
+ System.out.println("ServiceAspect2.destroy");
+ }
+
+ public void invoke(Runnable run) {
+ checkInjectedFields();
+ m_sequencer.step(3);
+ m_parentService.invoke(run);
+ }
+
+ private void checkInjectedFields() {
+ if (m_bc == null) {
+ m_sequencer.throwable(new Exception("Bundle Context not injected"));
+ return;
+ }
+ if (m_bcNotInjected != null) {
+ m_sequencer.throwable(new Exception("Bundle Context must not be injected"));
+ return;
+ }
+
+ if (m_dm == null) {
+ m_sequencer.throwable(new Exception("DependencyManager not injected"));
+ return;
+ }
+ if (m_dmNotInjected != null) {
+ m_sequencer.throwable(new Exception("DependencyManager must not be injected"));
+ return;
+ }
+
+ if (m_component == null) {
+ m_sequencer.throwable(new Exception("Component not injected"));
+ return;
+ }
+ if (m_componentNotInjected != null) {
+ m_sequencer.throwable(new Exception("Component must not be injected"));
+ return;
+ }
+ }
+ }
+
+ @AspectService(ranking = 30, added = "add")
+ public static class ServiceAspect3 implements ServiceInterface {
+ public final static String ENSURE = "AspectAnnotation.ServiceAspect3";
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ protected volatile Ensure m_sequencer;
+ // Injected using add callback.
+ private volatile ServiceInterface m_parentService;
+
+ @Init
+ void init() {
+ System.out.println("ServiceAspect3.init");
+ }
+
+ @Destroy
+ void destroy() {
+ System.out.println("ServiceAspect3.destroy");
+ }
+
+ void add(ServiceInterface si) {
+ m_parentService = si;
+ }
+
+ public void invoke(Runnable run) {
+ m_sequencer.step(2);
+ m_parentService.invoke(run);
+ }
+ }
+
+ @AspectService(ranking = 10, added = "added", removed = "removed")
+ public static class ServiceAspect1 implements ServiceInterface {
+ public final static String ENSURE = "AspectAnnotation.ServiceAspect1";
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ protected volatile Ensure m_sequencer;
+ // Injected by reflection.
+ private volatile ServiceInterface m_parentService;
+
+ @Init
+ void init() {
+ System.out.println("ServiceAspect1.init");
+ }
+
+ @Destroy
+ void destroy() {
+ System.out.println("ServiceAspect1.destroy");
+ }
+
+ void added(ServiceInterface si) {
+ m_parentService = si;
+ }
+
+ @Stop
+ void stop() {
+ m_sequencer.step(7);
+ }
+
+ void removed(ServiceInterface si) {
+ m_sequencer.step(8);
+ }
+
+ public void invoke(Runnable run) {
+ m_sequencer.step(4);
+ m_parentService.invoke(run);
+ }
+ }
+
+ @Component
+ public static class ServiceConsumer implements Runnable {
+ public final static String ENSURE = "AspectAnnotation.ServiceConsumer";
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ protected volatile Ensure m_sequencer;
+
+ @ServiceDependency
+ private volatile ServiceInterface m_service;
+
+ private Thread m_thread;
+
+ @Init
+ public void init() {
+ m_thread = new Thread(this, "ServiceConsumer");
+ m_thread.start();
+ }
+
+ public void run() {
+ m_sequencer.waitForStep(1, 2000);
+ m_service.invoke(new Runnable() {
+ public void run() {
+ m_sequencer.step(5);
+ }
+ });
+ }
+
+ @Destroy
+ void destroy() {
+ m_thread.interrupt();
+ try {
+ m_thread.join();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/AspectLifecycleAnnotation.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/AspectLifecycleAnnotation.java
new file mode 100644
index 0000000..841ba87
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/AspectLifecycleAnnotation.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.runtime.itest.components;
+
+import org.apache.felix.dm.annotation.api.AspectService;
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Destroy;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.dm.itest.util.Ensure;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectLifecycleAnnotation {
+ public interface ServiceInterface {
+ public void run();
+ }
+
+ /**
+ * Tests an aspect service, and ensure that its lifecycle methods are properly invoked (init/start/stop/destroy)
+ */
+ @Component
+ public static class AspectLifecycleTest {
+ @ServiceDependency
+ void bind(ServiceInterface service) {
+ service.run();
+ }
+ }
+
+ @Component
+ public static class ServiceProvider implements ServiceInterface {
+ public final static String ENSURE = "AspectLifecycleAnnotation.ServiceProvider";
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ protected volatile Ensure m_sequencer;
+
+ public void run() {
+ m_sequencer.step();
+ }
+ }
+
+ @AspectService(ranking = 10)
+ public static class ServiceProviderAspect implements ServiceInterface {
+ public final static String ENSURE = "AspectLifecycleAnnotation.ServiceProviderAspect";
+
+ protected volatile boolean m_initCalled;
+ protected volatile Ensure m_sequencer;
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ protected void bind(Ensure sequencer) {
+ m_sequencer = sequencer;
+ m_sequencer.step(2);
+ }
+
+ @Init
+ void init() {
+ m_initCalled = true;
+ }
+
+ @Start
+ void start() {
+ if (!m_initCalled) {
+ throw new IllegalStateException("start method called, but init method was not called");
+ }
+ m_sequencer.step(3);
+ }
+
+ public void run() {
+ m_sequencer.step(4);
+ }
+
+ @Stop()
+ void stop() {
+ // At this point, the AspectLifecycleTest class has been rebound to the original ServiceProvider.
+ m_sequencer.step(6);
+ }
+
+ @Destroy
+ void destroy() {
+ m_sequencer.step(7);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/AspectLifecycleWithDynamicProxyAnnotation.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/AspectLifecycleWithDynamicProxyAnnotation.java
new file mode 100644
index 0000000..9164ab8
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/AspectLifecycleWithDynamicProxyAnnotation.java
@@ -0,0 +1,119 @@
+/*
+* 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.runtime.itest.components;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.apache.felix.dm.annotation.api.AspectService;
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Destroy;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.dm.itest.util.Ensure;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectLifecycleWithDynamicProxyAnnotation {
+ public interface ServiceInterface {
+ public void run();
+ }
+
+ /**
+ * Tests an aspect service, and ensure that its lifecycle methods are properly invoked (init/start/stop/destroy)
+ */
+ @Component
+ public static class AspectLifecycleTest {
+ @ServiceDependency
+ void bind(ServiceInterface service) {
+ service.run();
+ }
+ }
+
+ @Component
+ public static class ServiceProvider implements ServiceInterface {
+ public final static String ENSURE = "AspectLifecycleWithDynamicProxyAnnotation.ServiceProvider";
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ protected volatile Ensure m_sequencer;
+
+ public void run() {
+ m_sequencer.step();
+ }
+ }
+
+ @AspectService(ranking = 10, service = ServiceInterface.class, factoryMethod = "create")
+ public static class ServiceProviderAspect implements InvocationHandler {
+ public final static String ENSURE = "AspectLifecycleWithDynamicProxyAnnotation.ServiceProviderAspect";
+
+ protected volatile boolean m_initCalled;
+ protected volatile Ensure m_sequencer;
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ protected void bind(Ensure sequencer) {
+ m_sequencer = sequencer;
+ m_sequencer.step(2);
+ }
+
+ public static Object create() {
+ return Proxy.newProxyInstance(ServiceProviderAspect.class.getClassLoader(),
+ new Class[]{ServiceInterface.class}, new ServiceProviderAspect());
+ }
+
+ @Init
+ void init() {
+ m_initCalled = true;
+ }
+
+ @Start
+ void start() {
+ if (!m_initCalled) {
+ throw new IllegalStateException("start method called, but init method was not called");
+ }
+ m_sequencer.step(3);
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if (method.getName().equals("toString")) {
+ return "ServiceProviderAspect@" + System.identityHashCode(this);
+ }
+
+ if (!method.getName().equals("run")) {
+ throw new IllegalStateException("wrong invoked method: " + method);
+ }
+ m_sequencer.step(4);
+ return null;
+ }
+
+ @Stop()
+ void stop() {
+ // At this point, the AspectLifecycleTest class has been rebound to the original ServiceProvider.
+ m_sequencer.step(6);
+ }
+
+ @Destroy
+ void destroy() {
+ m_sequencer.step(7);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/BundleAdapterServiceTestWithPublisher.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/BundleAdapterServiceTestWithPublisher.java
new file mode 100644
index 0000000..0f0ef04
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/BundleAdapterServiceTestWithPublisher.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.runtime.itest.components;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.felix.dm.annotation.api.BundleAdapterService;
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.LifecycleController;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.osgi.framework.Bundle;
+
+/**
+ * Test a BundleAdapterService which provides its interface using a @ServiceLifecycle.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class BundleAdapterServiceTestWithPublisher {
+ public static final String ENSURE = "BundleAdapterServiceTestWithPublisher";
+
+ public interface Provider {
+ }
+
+ @Component
+ public static class Consumer {
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @ServiceDependency(required = false, removed = "unbind")
+ void bind(Map properties, Provider provider) {
+ m_sequencer.step(1);
+ // check ProviderImpl properties
+ if ("bar".equals(properties.get("foo"))) {
+ m_sequencer.step(2);
+ }
+ // check extra ProviderImpl properties (returned by start method)
+ if ("bar2".equals(properties.get("foo2"))) {
+ m_sequencer.step(3);
+ }
+ // check properties propagated from bundle
+ if (Utils.DM_BSN.equals(properties.get("Bundle-SymbolicName"))) {
+ m_sequencer.step(4);
+ } else {
+ System.out.println("Consumer.bind: properties=" + properties);
+ }
+ }
+
+ void unbind(Provider provider) {
+ m_sequencer.step(5);
+ }
+ }
+
+ @BundleAdapterService(properties = {@Property(name = "foo", value = "bar")}, filter = "(Bundle-SymbolicName=" + Utils.DM_BSN + ")", stateMask = Bundle.INSTALLED
+ | Bundle.RESOLVED | Bundle.ACTIVE, propagate = true)
+ public static class ProviderImpl implements Provider {
+ @LifecycleController
+ volatile Runnable m_publisher; // injected and used to register our service
+
+ @LifecycleController(start = false)
+ volatile Runnable m_unpublisher; // injected and used to unregister our service
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @Init
+ void init() {
+ // register service in 1 second
+ Utils.schedule(m_publisher, 1000);
+ // unregister the service in 2 seconds
+ Utils.schedule(m_unpublisher, 2000);
+ }
+
+ @Start
+ Map start() {
+ // Add some extra service properties ... they will be appended to the one we have defined
+ // in the @Service annotation.
+ return new HashMap() {
+ {
+ put("foo2", "bar2");
+ }
+ };
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/BundleDependencyAnnotation.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/BundleDependencyAnnotation.java
new file mode 100644
index 0000000..a9f0d65
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/BundleDependencyAnnotation.java
@@ -0,0 +1,163 @@
+/*
+* 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.runtime.itest.components;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.annotation.api.BundleAdapterService;
+import org.apache.felix.dm.annotation.api.BundleDependency;
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Inject;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class BundleDependencyAnnotation {
+ public static final String ENSURE_CONSUMER = "BundleDependencyAnnotation.consumer";
+ public static final String ENSURE_ADAPTER = "BundleDependencyAnnotation.adapter";
+ public static final String METATYPE_BSN = "org.apache.felix.metatype";
+
+ public interface ServiceInterface extends Runnable {
+ }
+
+ /**
+ * Simple Consumer which has a BundleDependency dependency.
+ */
+ @Component
+ public static class Consumer {
+ @ServiceDependency(filter = "(name=" + ENSURE_CONSUMER + ")")
+ private volatile Ensure m_sequencer;
+
+ @BundleDependency(required = true, removed = "removed", filter = "(Bundle-SymbolicName=" + METATYPE_BSN + ")", stateMask = Bundle.ACTIVE)
+ public void add(Bundle b) {
+ if (b != null && b.getSymbolicName().equals(METATYPE_BSN)) {
+ m_sequencer.step(1);
+ }
+ }
+
+ @Start
+ public void start() {
+ m_sequencer.step(2);
+ }
+
+ @Stop
+ public void stop() {
+ m_sequencer.step(3);
+ }
+
+ protected void removed(Bundle b) {
+ if (b != null && b.getSymbolicName().equals(METATYPE_BSN)) {
+ m_sequencer.step(4);
+ }
+ }
+ }
+
+ /**
+ * ServiceInterface Consumer.
+ */
+ @Component
+ public static class ServiceConsumer {
+ @ServiceDependency(filter = "(name=" + ENSURE_ADAPTER + ")")
+ volatile Ensure m_sequencer;
+
+ @ServiceDependency
+ volatile ServiceInterface m_service;
+
+ @Start
+ void start() {
+ m_sequencer.step(2);
+ m_service.run();
+ }
+ }
+
+ /**
+ * A BundleAdapter test, which adapts the dependency manager bundle to the ServiceInterface service.
+ */
+ @BundleAdapterService(filter = "(Bundle-SymbolicName=" + METATYPE_BSN + ")", stateMask = Bundle.INSTALLED
+ | Bundle.RESOLVED | Bundle.ACTIVE, propagate = true, properties = {@Property(name = "foo", value = "bar")})
+ public static class ServiceProvider implements ServiceInterface {
+ // Adapted bundle (injected by reflection).
+ protected volatile Bundle m_bundle;
+
+ // Our Sequencer required dependency
+ @ServiceDependency(filter = "(name=" + ENSURE_ADAPTER + ")")
+ volatile Ensure m_sequencer;
+
+ // Check auto config injections
+ @Inject
+ volatile BundleContext m_bc;
+ BundleContext m_bcNotInjected;
+
+ @Inject
+ volatile DependencyManager m_dm;
+ DependencyManager m_dmNotInjected;
+
+ @Inject
+ volatile org.apache.felix.dm.Component m_component;
+ org.apache.felix.dm.Component m_componentNotInjected;
+
+ @Start
+ void start() {
+ checkInjectedFields();
+ m_sequencer.step(1);
+ }
+
+ public void run() {
+ if (m_bundle == null || !m_bundle.getSymbolicName().equals(METATYPE_BSN)) {
+ throw new IllegalStateException("ServiceProvider did not get proper bundle: " + m_bundle);
+ }
+ m_sequencer.step(3);
+ }
+
+ private void checkInjectedFields() {
+ if (m_bc == null) {
+ m_sequencer.throwable(new Exception("Bundle Context not injected"));
+ return;
+ }
+ if (m_bcNotInjected != null) {
+ m_sequencer.throwable(new Exception("Bundle Context must not be injected"));
+ return;
+ }
+
+ if (m_dm == null) {
+ m_sequencer.throwable(new Exception("DependencyManager not injected"));
+ return;
+ }
+ if (m_dmNotInjected != null) {
+ m_sequencer.throwable(new Exception("DependencyManager must not be injected"));
+ return;
+ }
+
+ if (m_component == null) {
+ m_sequencer.throwable(new Exception("Component not injected"));
+ return;
+ }
+ if (m_componentNotInjected != null) {
+ m_sequencer.throwable(new Exception("Component must not be injected"));
+ return;
+ }
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ComponentFactoryAnnotation.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ComponentFactoryAnnotation.java
new file mode 100644
index 0000000..f8102e8
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ComponentFactoryAnnotation.java
@@ -0,0 +1,182 @@
+/*
+* 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.runtime.itest.components;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.dm.itest.util.Ensure;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class ComponentFactoryAnnotation {
+ public final static String FACTORY = "ComponentFactoryAnnotation.Factory";
+ public final static String ENSURE = "ComponentFactoryAnnotation.Ensure";
+
+ public interface MyServiceInterface {
+ public void added(String instanceId);
+
+ public void changed(String modified);
+
+ public void removed();
+ }
+
+ @Component(properties = @Property(name = "foo", value = "bar"))
+ public static class ExtraDependency1 implements Runnable {
+ public void run() {
+ }
+ }
+
+ @Component(properties = @Property(name = "foo", value = "bar2"))
+ public static class ExtraDependency2 implements Runnable {
+ public void run() {
+ System.out.println("ExtraDependency2.run()");
+ }
+ }
+
+ /**
+ * This service is instantiated using a "factory set" from the
+ * ServiceFactoryAnnotationTest class.
+ *
+ * @see org.apache.felix.dm.test.annotation.ServiceFactoryAnnotationTest
+ */
+ @Component(factoryName = FACTORY, factoryConfigure = "configure", properties = {@Property(name = "foo", value = "bar")})
+ public static class MyService implements MyServiceInterface {
+ /**
+ * The configuration provided by MyServiceFactory
+ */
+ volatile Dictionary m_configuration;
+
+ /**
+ * Our sequencer.
+ */
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ /**
+ * An extra dependency (we'll dynamically configure the filter from our
+ * init() method).
+ */
+ @ServiceDependency(name = "extra")
+ Runnable m_extra;
+
+ /**
+ * This is the first method called: we are provided with the
+ * MyServiceFactory configuration.
+ */
+ public void configure(Dictionary<?, ?> configuration) {
+ if (m_configuration == null) {
+ m_configuration = configuration;
+ } else {
+ m_sequencer.step(5);
+ m_configuration = configuration;
+ }
+ }
+
+ /**
+ * Initialize our Service: we'll dynamically configure our dependency whose
+ * name is "extra".
+ */
+ @SuppressWarnings("serial")
+ @Init
+ Map init() {
+ return new HashMap() {
+ {
+ put("extra.filter", "(foo=bar2)");
+ put("extra.required", "true");
+ }
+ };
+ }
+
+ /**
+ * our Service is starting: at this point, all required dependencies have
+ * been injected.
+ */
+ @Start
+ public void start() {
+ Assert.assertNotNull("Extra dependency not injected", m_extra);
+ m_extra.run();
+ m_sequencer.step(2);
+ }
+
+ /**
+ * Our service is stopping.
+ */
+ @Stop
+ public void stop() {
+ m_sequencer.step(10);
+ }
+
+ public void added(String instanceId) {
+ if (instanceId.equals(m_configuration.get("instance.id"))) {
+ m_sequencer.step(4);
+ }
+ }
+
+ public void changed(String modified) {
+ if (modified.equals(m_configuration.get("instance.modified"))) {
+ m_sequencer.step(7);
+ }
+ }
+
+ public void removed() {
+ m_sequencer.step(9);
+ }
+ }
+
+ @Component
+ public static class MyServiceClient {
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @Start
+ void start() {
+ m_sequencer.step(1);
+ }
+
+ @ServiceDependency(required = false, changed = "update", removed = "removed")
+ void bind(Map serviceProperties, MyServiceInterface service) {
+ m_sequencer.step(3);
+ Assert.assertEquals("bar", serviceProperties.get("foo"));
+ Assert.assertNull(serviceProperties.get(".private.param"));
+ service.added((String) serviceProperties.get("instance.id"));
+ }
+
+ void update(Map serviceProperties, MyServiceInterface service) {
+ m_sequencer.step(6);
+ service.changed((String) serviceProperties.get("instance.modified"));
+ }
+
+ void removed(MyServiceInterface service) {
+ m_sequencer.step(8);
+ service.removed();
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ComponentFactoryServiceTestWthPublisher.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ComponentFactoryServiceTestWthPublisher.java
new file mode 100644
index 0000000..bc8de14
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ComponentFactoryServiceTestWthPublisher.java
@@ -0,0 +1,115 @@
+/*
+ * 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.runtime.itest.components;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.LifecycleController;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.runtime.api.ComponentFactory;
+
+/**
+ * A Service instantiated from a DM ComponentFactory, and which registers/unregisters its service,
+ * using the @ServiceLifecycle annotation.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class ComponentFactoryServiceTestWthPublisher {
+ public final static String FACTORY = "ComponentFactoryServiceTestWthPublisher.FACTORYSET";
+ public final static String ENSURE = "ComponentFactoryServiceTestWthPublisher";
+
+ public interface Provider {
+ }
+
+ @Component
+ public static class Consumer {
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @ServiceDependency(required = false, removed = "unbind")
+ void bind(Map properties, Provider provider) {
+ System.out.println("BIND: " + provider + ", map=" + properties);
+ m_sequencer.step(1);
+ if ("bar".equals(properties.get("foo"))) {
+ m_sequencer.step(2);
+ }
+ if ("bar2".equals(properties.get("foo2"))) {
+ m_sequencer.step(3);
+ }
+ if ("bar3".equals(properties.get("foo3"))) {
+ m_sequencer.step(4);
+ }
+ }
+
+ void unbind(Provider provider) {
+ m_sequencer.step(5);
+ }
+ }
+
+ @Component(factoryName = FACTORY, properties = {@Property(name = "foo", value = "bar")})
+ public static class ProviderImpl implements Provider {
+ @LifecycleController
+ volatile Runnable m_publisher; // injected and used to register our service
+
+ @LifecycleController(start = false)
+ volatile Runnable m_unpublisher; // injected and used to unregister our service
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @Init
+ void init() {
+ // register service in 1 second
+ Utils.schedule(m_publisher, 1000);
+ // unregister the service in 2 seconds
+ Utils.schedule(m_unpublisher, 2000);
+ }
+
+ @Start
+ Map start() {
+ // At this point, our service properties are the one specified in our @Service annotation + the one specified by our Factory.
+ // We also append an extra service property here:
+ return new HashMap() {
+ {
+ put("foo3", "bar3");
+ }
+ };
+ }
+ }
+
+ @Component
+ public static class ProviderImplFactory {
+ @ServiceDependency(filter = "(" + Component.FACTORY_NAME + "=" + FACTORY + ")")
+ void bind(ComponentFactory providerImplFactory) {
+ providerImplFactory.newInstance(new Hashtable() {
+ {
+ put("foo2", "bar2");
+ }
+ });
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/CompositeAnnotations.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/CompositeAnnotations.java
new file mode 100644
index 0000000..6657839
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/CompositeAnnotations.java
@@ -0,0 +1,194 @@
+/*
+ * 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.runtime.itest.components;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Composition;
+import org.apache.felix.dm.annotation.api.Destroy;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.Registered;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class CompositeAnnotations {
+ public interface C1Service {
+ }
+
+ public interface Dependency extends Runnable {
+ }
+
+ /**
+ * This service is also composed of the Component object.
+ */
+ @Component
+ public static class C1 implements C1Service {
+ public final static String ENSURE = "CompositeAnnotations.C1";
+
+ /* We are composed of this object, which will also be injected with our dependencies */
+ private final C2 m_c2 = new C2();
+
+ /* This dependency filter will be configured from our init method */
+ @ServiceDependency(name = "D")
+ public volatile Dependency m_runnable;
+
+ /* Object used to check that methods are called in the proper sequence */
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ private volatile Ensure m_sequencer;
+
+ /**
+ * Dynamically configure our "D" dependency, using a dependency customization map
+ */
+ @Init
+ Map<String, String> init() {
+ m_sequencer.step(1);
+ // Configure a filter for our dependency whose name is "D"
+ Map<String, String> customization = new HashMap<String, String>();
+ customization.put("D.filter", "(foo=bar2)");
+ customization.put("D.required", "true");
+ return customization;
+ }
+
+ /**
+ * Return the list of object our service is composed of
+ */
+ @Composition
+ Object[] getComposition() {
+ return new Object[]{this, m_c2};
+ }
+
+ /**
+ * Our Service is starting, and our Composites will also be
+ */
+ @Start
+ void start() {
+ m_sequencer.step(3);
+ m_runnable.run(); /* step 4 */
+ // Our Component.start() method should be called once this method returns.
+ }
+
+ /**
+ * Our provided service has been registered into the OSGi service registry.
+ */
+ @Registered
+ void registered(ServiceRegistration sr) {
+ m_sequencer.step(7);
+ }
+
+ /**
+ * Our Service is stopping, and our Composites will also be
+ */
+ @Stop
+ void stop() {
+ m_sequencer.step(9);
+ // Our Component.stop() method should be called once this method returns.
+ }
+
+ /**
+ * Our Service is destroying, and our Composites will also be.
+ */
+ @Destroy
+ void destroy() {
+ m_sequencer.step(11);
+ // Our Component.destroy() method should be called once this method returns.
+ }
+ }
+
+ /**
+ * The CompositeService is also made up of this Class.
+ */
+ public static class C2 {
+ // Injected dependency (from CompositeService)
+ private volatile Ensure m_sequencer;
+
+ // Injected dependency (from CompositeService)
+ public volatile Dependency m_runnable;
+
+ // lifecycle callback (same method as the one from CompositeService)
+ void init() {
+ m_sequencer.step(2);
+ }
+
+ // lifecycle callback (same method as the one from CompositeService)
+ void start() {
+ m_sequencer.step(5);
+ m_runnable.run(); /* step 6 */
+ }
+
+ void registered(ServiceRegistration sr) {
+ if (sr == null) {
+ m_sequencer.throwable(new Exception("null service registration"));
+ }
+ m_sequencer.step(8);
+ }
+
+ // lifecycle callback (same method as the one from CompositeService)
+ void stop() {
+ m_sequencer.step(10);
+ }
+
+ // lifecycle callback (same method as the one from CompositeService)
+ void destroy() {
+ m_sequencer.step(12);
+ }
+ }
+
+ @Component(properties = @Property(name = "foo", value = "bar1"))
+ public static class Dependency1 implements Dependency {
+ public final static String ENSURE = "CompositeAnnotations.Dependency1";
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @Start
+ void start() {
+ System.out.println("Dependency1.start");
+ }
+
+ public void run() {
+ m_sequencer.step(Integer.MAX_VALUE); // Makes the test fail.
+ }
+ }
+
+ @Component(properties = @Property(name = "foo", value = "bar2"))
+ public static class Dependency2 implements Dependency {
+ public final static String ENSURE = "CompositeAnnotations.Dependency2";
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @Start
+ void start() {
+ System.out.println("Dependency2.start");
+ }
+
+ public void run() {
+ m_sequencer.step();
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ConfigurationDependencyAnnotation.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ConfigurationDependencyAnnotation.java
new file mode 100644
index 0000000..7ed926c
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ConfigurationDependencyAnnotation.java
@@ -0,0 +1,118 @@
+/*
+ * 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.runtime.itest.components;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.ConfigurationDependency;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.junit.Assert;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ConfigurationDependencyAnnotation {
+ @Component
+ public static class ConfigurableComponent {
+ public final static String ENSURE = "ConfigurableComponent";
+
+ volatile Ensure m_ensure;
+ volatile Dictionary<String, String> m_conf;
+
+ @ConfigurationDependency
+ void updated(Dictionary<String, String> conf) {
+ m_conf = conf;
+ }
+
+ @ServiceDependency(filter="(name=" + ENSURE + ")")
+ void bind(Ensure ensure) {
+ m_ensure = ensure;
+ Assert.assertNotNull(m_conf);
+ Assert.assertEquals("bar", m_conf.get("foo"));
+ m_ensure.step(1);
+ }
+
+ @Start
+ void start() {
+ m_ensure.step(2);
+ }
+
+ @Stop
+ void stop() {
+ m_ensure.step(3);
+ }
+ }
+
+ @Component
+ public static class ConfigurableComponentWithDynamicExtraConfiguration {
+ public final static String ENSURE = "ConfigurableComponentWithDynamicExtraConfiguration";
+
+ volatile Ensure m_ensure;
+ volatile Dictionary<String, String> m_conf;
+
+ @ConfigurationDependency
+ void updated(Dictionary<String, String> conf) {
+ m_conf = conf;
+ }
+
+ @ServiceDependency(filter="(name=" + ENSURE + ")")
+ void bind(Ensure ensure) {
+ m_ensure = ensure;
+ Assert.assertNotNull(m_conf);
+ Assert.assertEquals("bar", m_conf.get("foo"));
+ m_ensure.step(1);
+ }
+
+ @Init
+ Map<String, String> init() {
+ Assert.assertNotNull(m_conf);
+ String dynamicPid = m_conf.get("dynamicPid");
+ Assert.assertNotNull(dynamicPid);
+ Map<String, String> map = new HashMap<>();
+ map.put("dynamicConfig.pid", m_conf.get("dynamicPid"));
+ m_ensure.step(2);
+ return map;
+ }
+
+ @ConfigurationDependency(name="dynamicConfig")
+ void extraConfiguration(Dictionary<String, String> dynamicConf) {
+ if (dynamicConf != null) {
+ Assert.assertEquals("bar2", dynamicConf.get("foo2"));
+ m_ensure.step(3);
+ }
+ }
+
+ @Start
+ void start() {
+ m_ensure.step(4);
+ }
+
+ @Stop
+ void stop() {
+ m_ensure.step(5);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ExtraAdapterServiceProperties.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ExtraAdapterServiceProperties.java
new file mode 100644
index 0000000..897728e
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ExtraAdapterServiceProperties.java
@@ -0,0 +1,93 @@
+/*
+ * 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.runtime.itest.components;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.felix.dm.annotation.api.AdapterService;
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.itest.util.Ensure;
+
+/**
+ * This test validates that an adapter Service may specify some extra service properties
+ * from it's start callback.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"rawtypes", "serial"})
+public class ExtraAdapterServiceProperties {
+ public final static String ENSURE = "ExtraAdapterServiceProperties";
+
+ public interface Provider {
+ }
+
+ public interface Provider2 {
+ }
+
+ @Component(properties = {@Property(name = "foo", value = "bar")})
+ public static class ProviderImpl implements Provider {
+ }
+
+ @AdapterService(provides = Provider2.class, properties = {@Property(name = "foo2", value = "bar2")}, adapteeService = Provider.class)
+ public static class Provider2Impl implements Provider2 {
+ protected Provider m_adaptee;
+
+ @Start
+ Map<String, String> start() {
+ return new HashMap<String, String>() {
+ {
+ put("foo3", "bar3");
+ }
+ };
+ }
+ }
+
+ @Component
+ public static class Consumer {
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ private volatile Map m_properties;
+
+ @ServiceDependency
+ void bind(Map properties, Provider2 provider2) {
+ m_properties = properties;
+ }
+
+ @Start
+ void start() {
+ System.out.println("provider2 service properties: " + m_properties);
+ if ("bar".equals(m_properties.get("foo"))) {
+ m_sequencer.step(1);
+ }
+
+ if ("bar2".equals(m_properties.get("foo2"))) {
+ m_sequencer.step(2);
+ }
+
+ if ("bar3".equals(m_properties.get("foo3"))) {
+ m_sequencer.step(3);
+ }
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ExtraComponentFactoryServiceProperties.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ExtraComponentFactoryServiceProperties.java
new file mode 100644
index 0000000..d593ba6
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ExtraComponentFactoryServiceProperties.java
@@ -0,0 +1,98 @@
+/*
+ * 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.runtime.itest.components;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.runtime.api.ComponentFactory;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class ExtraComponentFactoryServiceProperties {
+ public final static String FACTORYNAME = "ExtraComponentFactoryServiceProperties.FACTORYSET";
+ public final static String ENSURE = "ExtraComponentFactoryServiceProperties";
+
+ public interface Provider {
+ }
+
+ @Component(properties = {@Property(name = "foo", value = "bar")}, factoryName = FACTORYNAME)
+ public static class ProviderImpl implements Provider {
+ @Start
+ Map<String, String> start() {
+ return new HashMap<String, String>() {
+ {
+ put("foo2", "bar2");
+ }
+ };
+ }
+ }
+
+ @Component
+ public static class ProviderImplFactory {
+ @ServiceDependency(filter = "(" + Component.FACTORY_NAME + "=" + FACTORYNAME + ")")
+ volatile ComponentFactory m_factory;
+
+ @Start
+ void start() {
+ m_factory.newInstance(new Hashtable() {
+ {
+ put("foo3", "bar3");
+ }
+ });
+ }
+ }
+
+ @Component
+ public static class Consumer {
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ private volatile Map m_properties;
+
+ @ServiceDependency
+ void bindProvider(Map properties, Provider m_provider) {
+ m_properties = properties;
+ }
+
+ @Start
+ void start() {
+ System.out.println("provider service properties: " + m_properties);
+ if ("bar".equals(m_properties.get("foo"))) {
+ m_sequencer.step(1);
+ }
+
+ if ("bar2".equals(m_properties.get("foo2"))) {
+ m_sequencer.step(2);
+ }
+
+ if ("bar3".equals(m_properties.get("foo3"))) {
+ m_sequencer.step(3);
+ }
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ExtraFactoryServiceProperties.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ExtraFactoryServiceProperties.java
new file mode 100644
index 0000000..432f092
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ExtraFactoryServiceProperties.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.runtime.itest.components;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.itest.util.Ensure;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class ExtraFactoryServiceProperties {
+ public final static String FACTORYSET = "ExtraFactoryServiceProperties.FACTORYSET";
+ public final static String ENSURE = "ExtraFactoryServiceProperties";
+
+ public interface Provider {
+ }
+
+ @Component(properties = {@Property(name = "foo", value = "bar")}, factorySet = FACTORYSET)
+ public static class ProviderImpl implements Provider {
+ @Start
+ Map<String, String> start() {
+ return new HashMap<String, String>() {
+ {
+ put("foo2", "bar2");
+ }
+ };
+ }
+ }
+
+ @Component
+ public static class ProviderImplFactory {
+ @ServiceDependency(filter = "(" + Component.FACTORY_NAME + "=" + FACTORYSET + ")")
+ volatile Set<Dictionary> m_factory;
+
+ @Start
+ void start() {
+ m_factory.add(new Hashtable() {
+ {
+ put("foo3", "bar3");
+ }
+ });
+ }
+ }
+
+ @Component
+ public static class Consumer {
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ private volatile Map m_properties;
+
+ @ServiceDependency
+ void bindProvider(Map properties, Provider m_provider) {
+ m_properties = properties;
+ }
+
+ @Start
+ void start() {
+ System.out.println("provider service properties: " + m_properties);
+ if ("bar".equals(m_properties.get("foo"))) {
+ m_sequencer.step(1);
+ }
+
+ if ("bar2".equals(m_properties.get("foo2"))) {
+ m_sequencer.step(2);
+ }
+
+ if ("bar3".equals(m_properties.get("foo3"))) {
+ m_sequencer.step(3);
+ }
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ExtraServiceProperties.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ExtraServiceProperties.java
new file mode 100644
index 0000000..0e29208
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ExtraServiceProperties.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.runtime.itest.components;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.itest.util.Ensure;
+
+/**
+ * This test validates that a basic Service may specify some extra service properties
+ * from it's start callback.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"rawtypes", "serial"})
+public class ExtraServiceProperties {
+ public final static String ENSURE = "ExtraServiceProperties";
+
+ public interface Provider {
+ }
+
+ @Component(properties = {@Property(name = "foo", value = "bar")})
+ public static class ProviderImpl implements Provider {
+ @Start
+ Map<String, String> start() {
+ return new HashMap<String, String>() {
+ {
+ put("foo2", "bar2");
+ }
+ };
+ }
+ }
+
+ @Component
+ public static class Consumer {
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ private volatile Map m_properties;
+
+ @ServiceDependency
+ void bindProvider(Map properties, Provider m_provider) {
+ m_properties = properties;
+ }
+
+ @Start
+ void start() {
+ System.out.println("provider service properties: " + m_properties);
+ if ("bar".equals(m_properties.get("foo"))) {
+ m_sequencer.step(1);
+ }
+
+ if ("bar2".equals(m_properties.get("foo2"))) {
+ m_sequencer.step(2);
+ }
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/FactoryConfigurationAdapterAnnotation.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/FactoryConfigurationAdapterAnnotation.java
new file mode 100644
index 0000000..96514fd
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/FactoryConfigurationAdapterAnnotation.java
@@ -0,0 +1,160 @@
+/*
+* 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.runtime.itest.components;
+
+import java.util.Dictionary;
+import java.util.Map;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.FactoryConfigurationAdapterService;
+import org.apache.felix.dm.annotation.api.Inject;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.osgi.framework.BundleContext;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"rawtypes"})
+public class FactoryConfigurationAdapterAnnotation {
+ public interface ServiceInterface {
+ }
+
+ @Component
+ public static class ServiceClient {
+ @ServiceDependency(filter="(name=" + ServiceProvider.ENSURE + ")")
+ private volatile Ensure m_sequencer;
+
+ @ServiceDependency(changed = "changeServiceProvider", removed="removedServiceProvider")
+ void addServiceProvider(Map props, ServiceInterface si) {
+ // props should contain foo=bar, foo2=bar2
+ if (!"bar".equals(props.get("foo"))) {
+ throw new IllegalArgumentException("configuration does not contain foo=bar: " + props);
+ }
+ if (!"bar2".equals(props.get("foo2"))) {
+ throw new IllegalArgumentException("configuration does not contain foo2=bar2: " + props);
+ }
+ m_sequencer.step(2);
+ }
+
+ void changeServiceProvider(Map props, ServiceInterface si) {
+ System.out.println("ServiceClient: changeServiceProvider");
+ // props should contain foo=bar, foo2=bar2_modified
+ if (!"bar".equals(props.get("foo"))) {
+ throw new IllegalArgumentException("configuration does not contain foo=bar: " + props);
+ }
+ if (!"bar2_modified".equals(props.get("foo2"))) {
+ throw new IllegalArgumentException("configuration does not contain foo2=bar2: " + props);
+ }
+
+ m_sequencer.step(4);
+ }
+
+ void removedServiceProvider(ServiceInterface si) {
+ m_sequencer.step(5);
+ }
+ }
+
+ /**
+ * This service is instantiated when a factory configuration is created from ConfigAdmin
+ */
+ @FactoryConfigurationAdapterService(factoryPid = "FactoryPidTest", properties = {@Property(name = "foo", value = "bar")}, propagate = true)
+ public static class ServiceProvider implements ServiceInterface {
+ public final static String ENSURE = "FactoryConfigurationAdapterAnnotation.ServiceProvider";
+
+ @ServiceDependency(filter="(name=" + ENSURE + ")")
+ private volatile Ensure m_sequencer;
+
+ private volatile boolean m_started;
+
+ // Check auto config injections
+ @Inject
+ volatile BundleContext m_bc;
+ BundleContext m_bcNotInjected;
+
+ @Inject
+ volatile DependencyManager m_dm;
+ DependencyManager m_dmNotInjected;
+
+ @Inject
+ volatile org.apache.felix.dm.Component m_component;
+ org.apache.felix.dm.Component m_componentNotInjected;
+
+ // Either initial config, or an updated config
+ protected void updated(Dictionary conf) {
+ if (m_started) {
+ // conf should contain foo2=bar2_modified
+ if (!"bar2_modified".equals(conf.get("foo2"))) {
+ m_sequencer.throwable(new Exception("configuration does not contain foo=bar"));
+ }
+ m_sequencer.step(3);
+ } else {
+ // conf should contain foo2=bar2
+ if (!"bar2".equals(conf.get("foo2"))) {
+ throw new IllegalArgumentException("configuration does not contain foo2=bar2: " + conf);
+ }
+ }
+ }
+
+ @Start
+ void start() {
+ checkInjectedFields();
+ m_started = true;
+ m_sequencer.step(1);
+ }
+
+ @Stop
+ void stop() {
+ m_sequencer.step(6);
+ }
+
+ private void checkInjectedFields() {
+ if (m_bc == null) {
+ m_sequencer.throwable(new Exception("Bundle Context not injected"));
+ return;
+ }
+ if (m_bcNotInjected != null) {
+ m_sequencer.throwable(new Exception("Bundle Context must not be injected"));
+ return;
+ }
+
+ if (m_dm == null) {
+ m_sequencer.throwable(new Exception("DependencyManager not injected"));
+ return;
+ }
+ if (m_dmNotInjected != null) {
+ m_sequencer.throwable(new Exception("DependencyManager must not be injected"));
+ return;
+ }
+
+ if (m_component == null) {
+ m_sequencer.throwable(new Exception("Component not injected"));
+ return;
+ }
+ if (m_componentNotInjected != null) {
+ m_sequencer.throwable(new Exception("Component must not be injected"));
+ return;
+ }
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/FactoryConfigurationAdapterServiceTestWithPublisher.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/FactoryConfigurationAdapterServiceTestWithPublisher.java
new file mode 100644
index 0000000..22ebf27
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/FactoryConfigurationAdapterServiceTestWithPublisher.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.runtime.itest.components;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.FactoryConfigurationAdapterService;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.LifecycleController;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * Test a FactoryConfigurationAdapterService which provides its interface using a @ServiceLifecycle.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class FactoryConfigurationAdapterServiceTestWithPublisher {
+ public final static String PID="FactoryConfigurationAdapterServiceTestWithPublisher.PID";
+ public final static String ENSURE = "FactoryConfigurationAdapterServiceTestWithPublisher";
+
+ public interface Provider {
+ }
+
+ @Component
+ public static class Consumer {
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @ServiceDependency(required = false, removed = "unbind")
+ void bind(Map properties, Provider provider) {
+ m_sequencer.step(1);
+ // check ProviderImpl properties
+ if ("bar".equals(properties.get("foo"))) {
+ m_sequencer.step(2);
+ }
+ // check extra ProviderImpl properties (returned by start method)
+ if ("bar2".equals(properties.get("foo2"))) {
+ m_sequencer.step(3);
+ }
+ // check Factory Configuration properties
+ if ("bar3".equals(properties.get("foo3"))) {
+ m_sequencer.step(4);
+ }
+ }
+
+ void unbind(Provider provider) {
+ m_sequencer.step(5);
+ }
+ }
+
+ @Component
+ public static class Configurator {
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ volatile Configuration m_conf;
+
+ @ServiceDependency
+ volatile ConfigurationAdmin m_cm;
+
+ @Start
+ void start() throws IOException {
+ m_conf = m_cm.createFactoryConfiguration(PID, null);
+ m_conf.update(new Hashtable() {
+ {
+ put("foo3", "bar3");
+ }
+ });
+ }
+
+ @Stop
+ void stop() throws IOException {
+ m_conf.delete();
+ }
+ }
+
+ @FactoryConfigurationAdapterService(propagate = true, properties = {@Property(name = "foo", value = "bar")}, factoryPid = PID, updated = "updated")
+ public static class ProviderImpl implements Provider {
+ @LifecycleController
+ volatile Runnable m_publisher; // injected and used to register our service
+
+ @LifecycleController(start = false)
+ volatile Runnable m_unpublisher; // injected and used to unregister our service
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ void updated(Dictionary conf) {
+ }
+
+ @Init
+ void init() {
+ // register service in 1 second
+ Utils.schedule(m_publisher, 1000);
+ // unregister the service in 2 seconds
+ Utils.schedule(m_unpublisher, 2000);
+ }
+
+ @Start
+ Map start() {
+ // Add some extra service properties ... they will be appended to the one we have defined
+ // in the @Service annotation.
+ return new HashMap() {
+ {
+ put("foo2", "bar2");
+ }
+ };
+ }
+
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/FactoryServiceTestWthPublisher.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/FactoryServiceTestWthPublisher.java
new file mode 100644
index 0000000..af34c98
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/FactoryServiceTestWthPublisher.java
@@ -0,0 +1,116 @@
+/*
+ * 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.runtime.itest.components;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.LifecycleController;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.itest.util.Ensure;
+
+/**
+ * A Service instantiated from a FactorySet, and which registers/unregisters its service,
+ * using the @ServiceLifecycle annotation.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class FactoryServiceTestWthPublisher {
+ public final static String FACTORY_SET = "FactoryServiceTestWthPublisher.FACTORYSET";
+ public final static String ENSURE = "FactoryServiceTestWthPublisher";
+
+ public interface Provider {
+ }
+
+ @Component
+ public static class Consumer {
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @ServiceDependency(required = false, removed = "unbind")
+ void bind(Map properties, Provider provider) {
+ System.out.println("BIND: " + provider + ", map=" + properties);
+ m_sequencer.step(1);
+ if ("bar".equals(properties.get("foo"))) {
+ m_sequencer.step(2);
+ }
+ if ("bar2".equals(properties.get("foo2"))) {
+ m_sequencer.step(3);
+ }
+ if ("bar3".equals(properties.get("foo3"))) {
+ m_sequencer.step(4);
+ }
+ }
+
+ void unbind(Provider provider) {
+ m_sequencer.step(5);
+ }
+ }
+
+ @Component(factorySet = FACTORY_SET, properties = {@Property(name = "foo", value = "bar")})
+ public static class ProviderImpl implements Provider {
+ @LifecycleController
+ volatile Runnable m_publisher; // injected and used to register our service
+
+ @LifecycleController(start = false)
+ volatile Runnable m_unpublisher; // injected and used to unregister our service
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @Init
+ void init() {
+ // register service in 1 second
+ Utils.schedule(m_publisher, 1000);
+ // unregister the service in 2 seconds
+ Utils.schedule(m_unpublisher, 2000);
+ }
+
+ @Start
+ Map start() {
+ // At this point, our service properties are the one specified in our @Service annotation + the one specified by our Factory.
+ // We also append an extra service property here:
+ return new HashMap() {
+ {
+ put("foo3", "bar3");
+ }
+ };
+ }
+ }
+
+ @Component
+ public static class ProviderImplFactory {
+ @ServiceDependency(filter = "(" + Component.FACTORY_NAME + "=" + FACTORY_SET + ")")
+ void bind(Set<Dictionary> m_providerImplFactory) {
+ m_providerImplFactory.add(new Hashtable() {
+ {
+ put("foo2", "bar2");
+ }
+ });
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/Felix4050.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/Felix4050.java
new file mode 100644
index 0000000..0f22b1e
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/Felix4050.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.runtime.itest.components;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Destroy;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.Inject;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.osgi.framework.BundleContext;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Felix4050 {
+ public final static String ENSURE = "Felix4050";
+
+ @Component(provides = {A.class})
+ public static class A {
+
+ }
+
+ public interface B {
+ void run();
+ }
+
+ @Component(properties = {@Property(name = "type", value = "b1")})
+ public static class B1 implements B {
+ public void run() {
+ }
+ }
+
+ @Component(provides = {})
+ public static class B2 implements B {
+ @Inject
+ volatile BundleContext _ctx;
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @Start
+ void start() {
+ Thread t = new Thread(new Runnable() {
+ public void run() {
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException e) {
+ }
+ System.out.println("Registering B2");
+ Properties props = new Properties();
+ props.put("type", "b2");
+ _ctx.registerService(B.class.getName(), B2.this, props);
+
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ }
+ }
+ });
+ t.start();
+ }
+
+ public void run() {
+ m_sequencer.step(3);
+ }
+ }
+
+ @Component
+ public static class S {
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @Inject
+ volatile org.apache.felix.dm.Component _component;
+
+ volatile A m_a;
+ volatile B m_b;
+
+ void bind(A a) {
+ System.out.println("bind(A): " + a);
+ m_a = a;
+ }
+
+ @ServiceDependency(name = "B")
+ void bind(B b) {
+ System.out.println("bind(B): " + b);
+ m_b = b;
+ }
+
+ @Init
+ Map<?,?> init() {
+ m_sequencer.step(1);
+ _component.add(_component.getDependencyManager().createServiceDependency()
+ .setService(A.class).setRequired(true)
+ .setCallbacks("bind", null));
+ Map<String, String> props = new HashMap<>();
+ props.put("B.required", "true");
+ props.put("B.filter", "(type=b2)");
+ return props;
+ }
+
+ @Start
+ void start() {
+ if (m_a == null) {
+ throw new RuntimeException("A not injected");
+ }
+ if (m_b == null) {
+ throw new RuntimeException("B not injected");
+ }
+ m_sequencer.step(2);
+ m_b.run(); // step(3)
+ }
+
+ @Stop
+ void stop() {
+ m_sequencer.step(4);
+ }
+
+ @Destroy
+ void destroy() {
+ m_sequencer.step(5);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/Felix4357.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/Felix4357.java
new file mode 100644
index 0000000..f2948ea
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/Felix4357.java
@@ -0,0 +1,141 @@
+/*
+ * 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.runtime.itest.components;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.Registered;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Checks support of primitive types for @Property annotation.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Component(properties={
+ @Property(name="v1", value="s"),
+ @Property(name="v2", value={"s1", "s2"}),
+ @Property(name="v3", values={"s1", "s2"}),
+
+ @Property(name="v4", value="1", type=Long.class),
+ @Property(name="v5", longValue=1),
+ @Property(name="v6", longValue={1, 2}),
+
+ @Property(name="v7", value="1", type=Double.class),
+ @Property(name="v8", doubleValue=1),
+ @Property(name="v9", doubleValue={1, 2}),
+
+ @Property(name="v10", value="1", type=Float.class),
+ @Property(name="v11", floatValue=1),
+ @Property(name="v12", floatValue={1, 2}),
+
+ @Property(name="v13", value="1", type=Integer.class),
+ @Property(name="v14", intValue=1),
+ @Property(name="v15", intValue={1, 2}),
+
+ @Property(name="v16", value="65", type=Byte.class),
+ @Property(name="v17", byteValue=65),
+ @Property(name="v18", byteValue={65, 66}),
+
+ @Property(name="v19", value="A", type=Character.class),
+ @Property(name="v20", charValue='A'),
+ @Property(name="v21", charValue={'A', 'B'}),
+
+ @Property(name="v22", value="true", type=Boolean.class),
+ @Property(name="v23", booleanValue=true),
+ @Property(name="v24", booleanValue={true, false}),
+
+ @Property(name="v25", value="1", type=Short.class),
+ @Property(name="v26", shortValue=1),
+ @Property(name="v27", shortValue={1, 2}),
+
+ @Property(name="v28", value="65", type=Character.class),
+ @Property(name="v29", charValue=65),
+ @Property(name="v30", charValue={65, 66}),
+ },
+ provides=Felix4357.class
+)
+public class Felix4357 {
+ public final static String ENSURE = "Felix4357";
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_ensure;
+
+ @Registered
+ void registered(ServiceRegistration sr) {
+ ServiceReference ref = sr.getReference();
+ assertEquals(ref, "v1", "s", 1);
+ assertArrayEquals(ref, "v2", new String[] {"s1", "s2"}, 2);
+ assertArrayEquals(ref, "v3", new String[] {"s1", "s2"}, 3);
+ assertEquals(ref, "v4", new Long(1), 4);
+ assertEquals(ref, "v5", new Long(1), 5);
+ assertArrayEquals(ref, "v6", new Long[] { 1L, 2L } , 6);
+ assertEquals(ref, "v7", new Double(1), 7);
+ assertEquals(ref, "v8", new Double(1), 8);
+ assertArrayEquals(ref, "v9", new Double[] { 1.0, 2.0 } , 9);
+ assertEquals(ref, "v10", new Float(1), 10);
+ assertEquals(ref, "v11", new Float(1), 11);
+ assertArrayEquals(ref, "v12", new Float[] { 1.f, 2.f } , 12);
+ assertEquals(ref, "v13", new Integer(1), 13);
+ assertEquals(ref, "v14", new Integer(1), 14);
+ assertArrayEquals(ref, "v15", new Integer[] { 1, 2 } , 15);
+ assertEquals(ref, "v16", Byte.valueOf("65"), 16);
+ assertEquals(ref, "v17", Byte.valueOf("65"), 17);
+ assertArrayEquals(ref, "v18", new Byte[] { Byte.valueOf("65"), Byte.valueOf("66") } , 18);
+ assertEquals(ref, "v19", Character.valueOf('A'), 19);
+ assertEquals(ref, "v20", Character.valueOf('A'), 20);
+ assertArrayEquals(ref, "v21", new Character[] { 'A', 'B' } , 21);
+ assertEquals(ref, "v22", Boolean.valueOf(true), 22);
+ assertEquals(ref, "v23", Boolean.valueOf(true), 23);
+ assertArrayEquals(ref, "v24", new Boolean[] { true, false } , 24);
+ assertEquals(ref, "v25", Short.valueOf((short) 1), 25);
+ assertEquals(ref, "v26", Short.valueOf((short) 1), 26);
+ assertArrayEquals(ref, "v27", new Short[] { 1, 2 } , 27);
+ assertEquals(ref, "v28", Character.valueOf('A'), 28);
+ assertEquals(ref, "v29", Character.valueOf('A'), 29);
+ assertArrayEquals(ref, "v30", new Character[] { 'A', 'B' } , 30);
+ }
+
+ void assertEquals(ServiceReference ref, String property, Object expected, int step) {
+ Object value = ref.getProperty(property);
+ Assert.assertNotNull(value);
+ Assert.assertEquals(value.getClass(), expected.getClass());
+ Assert.assertEquals(value, expected);
+ m_ensure.step(step);
+ }
+
+ void assertArrayEquals(ServiceReference ref, String property, Object[] expected, int step) {
+ Object values = ref.getProperty(property);
+ Assert.assertNotNull(values);
+ Assert.assertTrue(values.getClass().isArray());
+ Assert.assertEquals(values.getClass(), expected.getClass());
+ Object[] array = (Object[]) values;
+ Assert.assertEquals(array.length, expected.length);
+ for (int i = 0; i < array.length; i ++) {
+ Assert.assertEquals(array[i].getClass(), expected[i].getClass());
+ Assert.assertEquals(array[i], expected[i]);
+ }
+ m_ensure.step(step);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/MultipleAnnotations.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/MultipleAnnotations.java
new file mode 100644
index 0000000..42a3e00
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/MultipleAnnotations.java
@@ -0,0 +1,136 @@
+/*
+* 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.runtime.itest.components;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Composition;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.dm.itest.util.Ensure;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class MultipleAnnotations {
+ public final static String ENSURE = "MultipleAnnotations";
+
+ public static class Composite {
+ void bind(Ensure seq) {
+ seq.step(2);
+ }
+ }
+
+ @Component
+ public static class ServiceConsumer {
+ @ServiceDependency(filter="(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @ServiceDependency(filter = "(foo=bar)")
+ volatile ServiceInterface m_service;
+
+ @Start
+ void start() {
+ m_sequencer.step(6);
+ m_service.doService();
+ }
+
+ @Stop
+ void stop() {
+ m_sequencer.step(8);
+ }
+ }
+
+ public interface ServiceInterface {
+ public void doService();
+ }
+
+ @Component(properties = {@Property(name = "foo", value = "bar")})
+ public static class ServiceProvider implements ServiceInterface {
+ @ServiceDependency(filter="(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ volatile ServiceProvider2 m_serviceProvider2;
+
+ @ServiceDependency(removed = "unbind")
+ void bind(ServiceProvider2 provider2) {
+ m_serviceProvider2 = provider2;
+ }
+
+ @Start
+ void start() {
+ m_serviceProvider2.step(4);
+ m_sequencer.step(5);
+ }
+
+ @Stop
+ void stop() {
+ m_sequencer.step(9);
+ }
+
+ void unbind(ServiceProvider2 provider2) {
+ m_sequencer.step(10);
+ }
+
+ public void doService() {
+ m_sequencer.step(7);
+ }
+ }
+
+ @Component(provides = {ServiceProvider2.class}, factoryMethod = "create")
+ public static class ServiceProvider2 {
+ final Composite m_composite = new Composite();
+ volatile Ensure m_sequencer;
+
+ static ServiceProvider2 create() {
+ return new ServiceProvider2();
+ }
+
+ @ServiceDependency(required = false, filter = "(foo=bar)") // NullObject
+ volatile Runnable m_runnable;
+
+ @ServiceDependency(service = Ensure.class, filter="(name=" + ENSURE + ")")
+ void bind(Ensure seq) {
+ m_sequencer = seq;
+ m_sequencer.step(1);
+ }
+
+ @Start
+ void start() {
+ m_sequencer.step(3);
+ m_runnable.run(); // NullObject
+ }
+
+ public void step(int step) { // called by ServiceProvider.start() method
+ m_sequencer.step(step);
+ }
+
+ @Stop
+ void stop() {
+ m_sequencer.step(11);
+ }
+
+ @Composition
+ Object[] getComposition() {
+ return new Object[]{this, m_composite};
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/PropagateAnnotation.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/PropagateAnnotation.java
new file mode 100644
index 0000000..10dc934
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/PropagateAnnotation.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.runtime.itest.components;
+
+import java.util.Map;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.itest.util.Ensure;
+
+/**
+ * Verifies ServiceDependencyservice properties propagation.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"rawtypes"})
+public class PropagateAnnotation {
+ public final static String ENSURE = "PropagateAnnotation";
+
+ @Component
+ public static class Consumer {
+ private volatile Map m_producerProps;
+
+ @ServiceDependency
+ void bind(Map props, Producer producer) {
+ m_producerProps = props;
+ }
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @Start
+ void start() {
+ m_sequencer.step(1);
+ if ("bar".equals(m_producerProps.get("foo"))) {
+ m_sequencer.step(2);
+ }
+ if ("bar2".equals(m_producerProps.get("foo2"))) {
+ m_sequencer.step(3);
+ }
+ }
+ }
+
+ @Component(provides = {Producer.class}, properties = {@Property(name = "foo", value = "bar")})
+ public static class Producer {
+ @ServiceDependency(propagate = true)
+ volatile Producer2 m_producer;
+ }
+
+ @Component(provides = {Producer2.class}, properties = {@Property(name = "foo2", value = "bar2")})
+ public static class Producer2 {
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ResourceAdapterServiceTestWithPublisher.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ResourceAdapterServiceTestWithPublisher.java
new file mode 100644
index 0000000..5b77a33
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ResourceAdapterServiceTestWithPublisher.java
@@ -0,0 +1,197 @@
+/*
+ * 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.runtime.itest.components;
+
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.ResourceUtil;
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Destroy;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.Inject;
+import org.apache.felix.dm.annotation.api.LifecycleController;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ResourceAdapterService;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * Test a ResourceAdapterService which provides its interface using a @ServiceLifecycle.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"deprecation", "unchecked", "rawtypes", "serial"})
+public class ResourceAdapterServiceTestWithPublisher {
+ public static final String ENSURE = "ResourceAdapterServiceTestWithPublisher";
+
+ public interface Provider {
+ }
+
+ @Component
+ public static class Consumer {
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @ServiceDependency(required = false, removed = "unbind")
+ void bind(Map properties, Provider provider) {
+ m_sequencer.step(1);
+ // check ProviderImpl properties
+ if ("bar".equals(properties.get("foo"))) {
+ m_sequencer.step(2);
+ }
+ // check extra ProviderImpl properties (returned by start method)
+ if ("bar2".equals(properties.get("foo2"))) {
+ m_sequencer.step(3);
+ }
+ // check properties propagated by the resource adapter
+ if ("/path/to/test1.txt".equals(properties.get(ResourceHandler.PATH))) {
+ m_sequencer.step(4);
+ }
+ if ("localhost".equals(properties.get(ResourceHandler.HOST))) {
+ m_sequencer.step(5);
+ }
+ }
+
+ void unbind(Provider provider) {
+ m_sequencer.step(6);
+ }
+ }
+
+ @Component
+ public static class ResourceProvider {
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @Inject
+ private volatile BundleContext m_context;
+ private final Map m_handlers = new HashMap();
+ private URL[] m_resources;
+
+ public ResourceProvider() throws Exception {
+ m_resources = new URL[]{new URL("file://localhost/path/to/test1.txt"),
+ new URL("file://localhost/path/to/test2.txt"), new URL("file://localhost/path/to/README.doc")};
+ }
+
+ /**
+ * Handles a new Resource consumer
+ * @param serviceProperties
+ * @param handler
+ */
+ @ServiceDependency(removed = "remove", required = false)
+ public void add(Map serviceProperties, ResourceHandler handler) {
+ String filterString = (String) serviceProperties.get("filter");
+ Filter filter = null;
+ if (filterString != null) {
+ try {
+ filter = m_context.createFilter(filterString);
+ } catch (InvalidSyntaxException e) {
+ Assert.fail("Could not create filter for resource handler: " + e);
+ return;
+ }
+ }
+ synchronized (m_handlers) {
+ m_handlers.put(handler, filter);
+ }
+ for (int i = 0; i < m_resources.length; i++) {
+ if (filter == null || filter.match(ResourceUtil.createProperties(m_resources[i]))) {
+ handler.added(m_resources[i]);
+ }
+ }
+ }
+
+ /**
+ * Remove a Resource consumer.bar
+ * @param handler
+ */
+ public void remove(ResourceHandler handler) {
+ Filter filter;
+ synchronized (m_handlers) {
+ filter = (Filter) m_handlers.remove(handler);
+ }
+ removeResources(handler, filter);
+ }
+
+ private void removeResources(ResourceHandler handler, Filter filter) {
+ for (int i = 0; i < m_resources.length; i++) {
+ if (filter == null || filter.match(ResourceUtil.createProperties(m_resources[i]))) {
+ handler.removed(m_resources[i]);
+ }
+ }
+ }
+
+ /**
+ * Our component is being destroyed: notify all our registered Resource consumers that we don't
+ * provide our Resources anymore.
+ */
+ @Destroy
+ public void destroy() {
+ Entry[] handlers;
+ synchronized (m_handlers) {
+ handlers = (Entry[]) m_handlers.entrySet().toArray(new Entry[m_handlers.size()]);
+ }
+ for (int i = 0; i < handlers.length; i++) {
+ removeResources((ResourceHandler) handlers[i].getKey(), (Filter) handlers[i].getValue());
+ }
+ }
+ }
+
+ @ResourceAdapterService(filter = "(&(path=/path/to/test1.txt)(host=localhost))", properties = {@Property(name = "foo", value = "bar")}, propagate = true)
+ public static class ProviderImpl implements Provider {
+ @LifecycleController
+ volatile Runnable m_publisher; // injected and used to register our service
+
+ @LifecycleController(start = false)
+ volatile Runnable m_unpublisher; // injected and used to unregister our service
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ // Injected by reflection
+ volatile URL m_resource;
+
+ @Init
+ void init() {
+ // register service in 1 second
+ Utils.schedule(m_publisher, 1000);
+ // unregister the service in 2 seconds
+ Utils.schedule(m_unpublisher, 2000);
+ }
+
+ @Start
+ Map start() {
+ // Add some extra service properties ... they will be appended to the one we have defined
+ // in the @Service annotation.
+ return new HashMap() {
+ {
+ put("foo2", "bar2");
+ }
+ };
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ResourceAnnotation.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ResourceAnnotation.java
new file mode 100644
index 0000000..b87347b
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ResourceAnnotation.java
@@ -0,0 +1,294 @@
+/*
+* 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.runtime.itest.components;
+
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.ResourceUtil;
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Destroy;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.Inject;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ResourceAdapterService;
+import org.apache.felix.dm.annotation.api.ResourceDependency;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"rawtypes"})
+public class ResourceAnnotation {
+ public final static String ENSURE_RESOURCE = "ResourceAnnotation.resource";
+ public final static String ENSURE_FIELD = "ResourceAnnotation.field";
+ public final static String ENSURE_ADAPTER = "ResourceAnnotation.adapter";
+ public final static String ENSURE_PROVIDER = "ResourceAnnotation.provider";
+
+ /**
+ * A Service provided the ServiceProvider, which is a ResourceAdapter.
+ */
+ public interface ServiceInterface extends Runnable {
+ }
+
+ /**
+ * A Component which has a resource dependency.
+ */
+ @Component
+ public static class ResourceConsumer {
+ @ServiceDependency(required = true, filter = "(name=" + ENSURE_RESOURCE + ")")
+ volatile Ensure m_sequencer;
+
+ private volatile int m_resourcesSeen;
+
+ @Start
+ void start() {
+ System.out.println("ResourceConsumer.start: sequencer=" + m_sequencer);
+ }
+
+ @ResourceDependency(required = false, filter = "(&(path=/path/to/*.txt)(host=localhost))")
+ public void add(URL resource) {
+ System.out.println("ResourceConsumer.add: resource=" + resource + ", m_sequencer=" + m_sequencer);
+ if (match(resource, "file://localhost/path/to/test1.txt")) {
+ m_resourcesSeen++;
+ return;
+ }
+
+ if (match(resource, "file://localhost/path/to/test2.txt")) {
+ m_resourcesSeen++;
+ return;
+ }
+
+ Assert.fail("Got unexpected resource: " + resource);
+ }
+
+ private boolean match(URL resource, String url) {
+ return url.equals(resource.toString());
+ }
+
+ @Stop
+ void stop() {
+ System.out.println("ResourceConsumer.stop: m_sequencer=" + m_sequencer);
+ Assert.assertEquals(2, m_resourcesSeen);
+ m_sequencer.step(1);
+ }
+ }
+
+ /**
+ * A Component which as a resource dependency, using a class field.
+ */
+ @Component
+ public static class ResourceConsumerField {
+ @ServiceDependency(required = true, filter = "(name=" + ENSURE_FIELD + ")")
+ volatile Ensure m_sequencer;
+
+ @ResourceDependency(filter = "(&(path=*/test1.txt)(host=localhost))")
+ URL m_resource;
+
+ @Init
+ void init() {
+ if (m_resource != null) {
+ Assert.assertTrue("file://localhost/path/to/test1.txt".equals(m_resource.toString()));
+ m_sequencer.step(1);
+ }
+ }
+ }
+
+ /**
+ * Provides some simple resources.
+ */
+ @Component
+ public static class ResourceProvider {
+ @ServiceDependency(required = true, filter = "(name=" + ENSURE_PROVIDER + ")")
+ volatile Ensure m_sequencer;
+
+ @Inject
+ private volatile BundleContext m_context;
+ private final Map m_handlers = new HashMap();
+ private final URL[] m_resources;
+
+ public ResourceProvider() throws Exception {
+ m_resources = new URL[]{
+ new URL("file://localhost/path/to/test1.txt"),
+ new URL("file://localhost/path/to/test2.txt"),
+ new URL("file://localhost/path/to/README.doc")};
+ }
+
+ /**
+ * Handles a new Resource consumer
+ * @param serviceProperties
+ * @param handler
+ */
+ @SuppressWarnings("unchecked")
+ @ServiceDependency(removed = "remove", required = false)
+ public void add(Map serviceProperties, ResourceHandler handler) {
+ System.out.println("ResourceProvider.addResourceHandler " + handler);
+ String filterString = (String) serviceProperties.get("filter");
+ Filter filter = null;
+ if (filterString != null) {
+ try {
+ filter = m_context.createFilter(filterString);
+ } catch (InvalidSyntaxException e) {
+ Assert.fail("Could not create filter for resource handler: " + e);
+ return;
+ }
+ }
+ synchronized (m_handlers) {
+ m_handlers.put(handler, filter);
+ }
+ for (int i = 0; i < m_resources.length; i++) {
+ if (filter == null || filter.match(ResourceUtil.createProperties(m_resources[i]))) {
+ System.out.println("ResourceProvider: calling handled.added(" + m_resources[i] + ")");
+ handler.added(m_resources[i], null);
+ }
+ }
+ }
+
+ /**
+ * Remove a Resource consumer.
+ * @param handler
+ */
+ public void remove(ResourceHandler handler) {
+ System.out.println("ResourceProvider.removeResourceHandler " + handler);
+
+ Filter filter;
+ synchronized (m_handlers) {
+ filter = (Filter) m_handlers.remove(handler);
+ }
+ removeResources(handler, filter);
+ }
+
+ private void removeResources(ResourceHandler handler, Filter filter) {
+ for (int i = 0; i < m_resources.length; i++) {
+ if (filter == null || filter.match(ResourceUtil.createProperties(m_resources[i]))) {
+ handler.removed(m_resources[i], null);
+ }
+ }
+ }
+
+ /**
+ * Our component is being destroyed: notify all our registered Resource consumers that we don't
+ * provide our Resources anymore.
+ */
+ @SuppressWarnings("unchecked")
+ @Destroy
+ public void destroy() {
+ Entry[] handlers;
+ synchronized (m_handlers) {
+ handlers = (Entry[]) m_handlers.entrySet().toArray(new Entry[m_handlers.size()]);
+ }
+ for (int i = 0; i < handlers.length; i++) {
+ removeResources((ResourceHandler) handlers[i].getKey(), (Filter) handlers[i].getValue());
+ }
+ }
+ }
+
+ /**
+ * Our ServiceInterface provider, which service is activated by a ResourceAdapter.
+ */
+ @ResourceAdapterService(filter = "(&(path=/path/to/test1.txt)(host=localhost))", properties = {@Property(name = "foo", value = "bar")}, propagate = true)
+ public static class ServiceProvider implements ServiceInterface {
+ // Injected by reflection
+ URL m_resource;
+
+ @ServiceDependency(filter = "(name=" + ENSURE_ADAPTER + ")")
+ Ensure m_sequencer;
+
+ // Check auto config injections
+ @Inject
+ BundleContext m_bc;
+ BundleContext m_bcNotInjected;
+
+ @Inject
+ DependencyManager m_dm;
+ DependencyManager m_dmNotInjected;
+
+ @Inject
+ org.apache.felix.dm.Component m_component;
+ org.apache.felix.dm.Component m_componentNotInjected;
+
+ public void run() {
+ checkInjectedFields();
+ Assert.assertNotNull("Resource has not been injected in the adapter", m_resource);
+ Assert.assertEquals("ServiceProvider did not get expected resource", "file://localhost/path/to/test1.txt",
+ m_resource.toString());
+ m_sequencer.step(2);
+ }
+
+ private void checkInjectedFields() {
+ if (m_bc == null) {
+ m_sequencer.throwable(new Exception("Bundle Context not injected"));
+ return;
+ }
+ if (m_bcNotInjected != null) {
+ m_sequencer.throwable(new Exception("Bundle Context must not be injected"));
+ return;
+ }
+
+ if (m_dm == null) {
+ m_sequencer.throwable(new Exception("DependencyManager not injected"));
+ return;
+ }
+ if (m_dmNotInjected != null) {
+ m_sequencer.throwable(new Exception("DependencyManager must not be injected"));
+ return;
+ }
+
+ if (m_component == null) {
+ m_sequencer.throwable(new Exception("Component not injected"));
+ return;
+ }
+ if (m_componentNotInjected != null) {
+ m_sequencer.throwable(new Exception("Component must not be injected"));
+ return;
+ }
+ }
+ }
+
+ /**
+ * A Component with a dependency over the ServiceInterface, which is actually provided
+ * by a ResourceAdapter.
+ */
+ @Component
+ public static class ServiceConsumer {
+ @ServiceDependency
+ ServiceInterface m_serviceInterface;
+
+ @ServiceDependency(filter = "(name=" + ENSURE_ADAPTER + ")")
+ Ensure m_sequencer;
+
+ @Start
+ void start() {
+ m_sequencer.step(1);
+ m_serviceInterface.run();
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ServiceFactoryAnnotation.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ServiceFactoryAnnotation.java
new file mode 100644
index 0000000..0bb2676
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ServiceFactoryAnnotation.java
@@ -0,0 +1,181 @@
+/*
+* 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.runtime.itest.components;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.dm.itest.util.Ensure;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class ServiceFactoryAnnotation {
+ public final static String FACTORY = "ServiceFactoryAnnotation.Factory";
+ public final static String ENSURE = "ServiceFactoryAnnotation.Ensure";
+
+ public interface MyServiceInterface {
+ public void added(String instanceId);
+
+ public void changed(String modified);
+
+ public void removed();
+ }
+
+ @Component(properties = @Property(name = "foo", value = "bar"))
+ public static class ExtraDependency1 implements Runnable {
+ public void run() {
+ }
+ }
+
+ @Component(properties = @Property(name = "foo", value = "bar2"))
+ public static class ExtraDependency2 implements Runnable {
+ public void run() {
+ System.out.println("ExtraDependency2.run()");
+ }
+ }
+
+ /**
+ * This service is instantiated using a "factory set" from the
+ * ServiceFactoryAnnotationTest class.
+ *
+ * @see org.apache.felix.dm.test.annotation.ServiceFactoryAnnotationTest
+ */
+ @Component(factorySet = FACTORY, factoryConfigure = "configure", properties = {@Property(name = "foo", value = "bar")})
+ public static class MyService implements MyServiceInterface {
+ /**
+ * The configuration provided by MyServiceFactory
+ */
+ volatile Dictionary m_configuration;
+
+ /**
+ * Our sequencer.
+ */
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ /**
+ * An extra dependency (we'll dynamically configure the filter from our
+ * init() method).
+ */
+ @ServiceDependency(name = "extra")
+ Runnable m_extra;
+
+ /**
+ * This is the first method called: we are provided with the
+ * MyServiceFactory configuration.
+ */
+ public void configure(Dictionary<?, ?> configuration) {
+ if (m_configuration == null) {
+ m_configuration = configuration;
+ } else {
+ m_sequencer.step(5);
+ }
+ }
+
+ /**
+ * Initialize our Service: we'll dynamically configure our dependency whose
+ * name is "extra".
+ */
+ @SuppressWarnings("serial")
+ @Init
+ Map init() {
+ return new HashMap() {
+ {
+ put("extra.filter", "(foo=bar2)");
+ put("extra.required", "true");
+ }
+ };
+ }
+
+ /**
+ * our Service is starting: at this point, all required dependencies have
+ * been injected.
+ */
+ @Start
+ public void start() {
+ Assert.assertNotNull("Extra dependency not injected", m_extra);
+ m_extra.run();
+ m_sequencer.step(2);
+ }
+
+ /**
+ * Our service is stopping.
+ */
+ @Stop
+ public void stop() {
+ m_sequencer.step(10);
+ }
+
+ public void added(String instanceId) {
+ if (instanceId.equals(m_configuration.get("instance.id"))) {
+ m_sequencer.step(4);
+ }
+ }
+
+ public void changed(String modified) {
+ if (modified.equals(m_configuration.get("instance.modified"))) {
+ m_sequencer.step(7);
+ }
+ }
+
+ public void removed() {
+ m_sequencer.step(9);
+ }
+ }
+
+ @Component
+ public static class MyServiceClient {
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @Start
+ void start() {
+ m_sequencer.step(1);
+ }
+
+ @ServiceDependency(required = false, changed = "update", removed = "removed")
+ void bind(Map serviceProperties, MyServiceInterface service) {
+ m_sequencer.step(3);
+ Assert.assertEquals("bar", serviceProperties.get("foo"));
+ Assert.assertNull(serviceProperties.get(".private.param"));
+ service.added((String) serviceProperties.get("instance.id"));
+ }
+
+ void update(Map serviceProperties, MyServiceInterface service) {
+ m_sequencer.step(6);
+ service.changed((String) serviceProperties.get("instance.modified"));
+ }
+
+ void removed(MyServiceInterface service) {
+ m_sequencer.step(8);
+ service.removed();
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ServiceTestWthPublisher.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ServiceTestWthPublisher.java
new file mode 100644
index 0000000..bbeae4e
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/ServiceTestWthPublisher.java
@@ -0,0 +1,96 @@
+/*
+ * 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.runtime.itest.components;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.LifecycleController;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.itest.util.Ensure;
+
+/**
+ * A Service that just registers/unregisters its service, using the @ServiceLifecycle annotation.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class ServiceTestWthPublisher {
+ public final static String ENSURE = "ServiceTestWthPublisher";
+
+ public interface Provider {
+ }
+
+ @Component
+ public static class Consumer {
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @ServiceDependency(required = false, removed = "unbind")
+ void bind(Map properties, Provider provider) {
+ m_sequencer.step(1);
+ if ("bar".equals(properties.get("foo"))) {
+ m_sequencer.step(2);
+ }
+ if ("bar2".equals(properties.get("foo2"))) {
+ m_sequencer.step(3);
+ }
+ }
+
+ void unbind(Provider provider) {
+ m_sequencer.step(4);
+ }
+ }
+
+ @Component(properties = {@Property(name = "foo", value = "bar")})
+ public static class ProviderImpl implements Provider {
+ @LifecycleController
+ volatile Runnable m_publisher; // injected and used to register our service
+
+ @LifecycleController(start = false)
+ volatile Runnable m_unpublisher; // injected and used to unregister our service
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @Init
+ void init() {
+ // register service in 1 second
+ Utils.schedule(m_publisher, 1000);
+ // unregister the service in 2 seconds
+ Utils.schedule(m_unpublisher, 2000);
+ }
+
+ @SuppressWarnings("serial")
+ @Start
+ Map start() {
+ // Add some extra service properties ... they will be appended to the one we have defined
+ // in the @Service annotation.
+ return new HashMap() {
+ {
+ put("foo2", "bar2");
+ }
+ };
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/SimpleAnnotations.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/SimpleAnnotations.java
new file mode 100644
index 0000000..7bc7ad6
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/SimpleAnnotations.java
@@ -0,0 +1,183 @@
+/*
+ * 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.runtime.itest.components;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Destroy;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.Inject;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.Registered;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.dm.annotation.api.Unregistered;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class SimpleAnnotations {
+ /**
+ * Provides a <code>Runnable</code> service, which is required by the
+ * {@link Consumer} class.
+ */
+ @Component(properties = {@Property(name = "foo", value = "bar"), @Property(name="type", value="SimpleAnnotations")})
+ public static class Producer implements Runnable {
+ public final static String ENSURE = "SimpleAnnotations.Producer";
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure _ensure;
+
+ @ServiceDependency
+ volatile LogService _logService;
+
+ @Inject
+ volatile BundleContext _ctx;
+
+ @Init
+ protected void init() {
+ _logService.log(LogService.LOG_INFO, "producer.init");
+ // Our component is initializing (at this point: all required
+ // dependencies are injected).
+ _ensure.step(1);
+ }
+
+ @Start
+ protected void start() {
+ // We are about to be registered in the OSGi registry.
+ _ensure.step(2);
+ }
+
+ @Registered
+ protected void registered(ServiceRegistration sr) {
+ _logService.log(LogService.LOG_INFO, "Registered");
+ if (sr == null) {
+ _ensure.throwable(new Exception("ServiceRegistration is null"));
+ }
+ if (!"bar".equals(sr.getReference().getProperty("foo"))) {
+ _ensure.throwable(new Exception("Invalid Service Properties"));
+ }
+ _ensure.step(3);
+ }
+
+ public void run() {
+ _ensure.step(5);
+ }
+
+ @Stop
+ protected void stop() {
+ // We are about to be unregistered from the OSGi registry, and we
+ // must stop.
+ _ensure.step(8);
+ }
+
+ @Unregistered
+ protected void stopped() {
+ // We are unregistered from the OSGi registry.
+ _ensure.step(9);
+ }
+
+ @Destroy
+ public void destroy() {
+ // Our component is shutting down.
+ _ensure.step(10);
+ }
+ }
+
+ /**
+ * Consumes a service which is provided by the {@link Producer} class.
+ */
+ @Component
+ public static class Consumer {
+ public final static String ENSURE = "SimpleAnnotations.Consumer";
+
+ @ServiceDependency
+ volatile LogService _logService;
+
+ @ServiceDependency(filter="(type=SimpleAnnotations)")
+ volatile Runnable _runnable;
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure _ensure;
+
+ @Inject
+ volatile BundleContext _bc;
+ BundleContext _bcNotInjected;
+
+ @Inject
+ volatile DependencyManager _dm;
+ DependencyManager _dmNotInjected;
+
+ @Inject
+ volatile org.apache.felix.dm.Component _component;
+ org.apache.felix.dm.Component _componentNotInjected;
+
+ @Start
+ protected void start() {
+ _logService.log(LogService.LOG_INFO, "Consumer.START: ");
+ checkInjectedFields();
+ _ensure.step(4);
+ _runnable.run();
+ }
+
+ private void checkInjectedFields() {
+ if (_bc == null) {
+ _ensure.throwable(new Exception("Bundle Context not injected"));
+ return;
+ }
+ if (_bcNotInjected != null) {
+ _ensure.throwable(new Exception("Bundle Context must not be injected"));
+ return;
+ }
+
+ if (_dm == null) {
+ _ensure.throwable(new Exception("DependencyManager not injected"));
+ return;
+ }
+ if (_dmNotInjected != null) {
+ _ensure.throwable(new Exception("DependencyManager must not be injected"));
+ return;
+ }
+
+ if (_component == null) {
+ _ensure.throwable(new Exception("Component not injected"));
+ return;
+ }
+ if (_componentNotInjected != null) {
+ _ensure.throwable(new Exception("Component must not be injected"));
+ return;
+ }
+ }
+
+ @Stop
+ protected void stop() {
+ _ensure.step(6);
+ }
+
+ @Destroy
+ void destroy() {
+ _ensure.step(7);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/TemporalAnnotations.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/TemporalAnnotations.java
new file mode 100644
index 0000000..55cbf1c
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/TemporalAnnotations.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.runtime.itest.components;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.dm.itest.util.Ensure;
+
+/**
+ * Service using an annotated Temporal Service dependency.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Component(provides = {})
+public class TemporalAnnotations implements Runnable {
+ public final static String ENSURE = "TemporalAnnotations";
+ Thread m_thread;
+
+ @ServiceDependency(filter = "(name=" + ENSURE + ")")
+ volatile Ensure m_sequencer;
+
+ @ServiceDependency(timeout = 1000L, filter = "(test=temporal)")
+ volatile Runnable m_service;
+
+ @Start
+ protected void start() {
+ m_thread = new Thread(this);
+ m_thread.start();
+ }
+
+ @Stop
+ protected void stop() {
+ m_thread.interrupt();
+ try {
+ m_thread.join();
+ } catch (InterruptedException e) {
+ }
+ }
+
+ public void run() {
+ m_service.run();
+ m_sequencer.waitForStep(2, 15000);
+ m_service.run(); // we should block here
+ m_sequencer.waitForStep(4, 15000);
+ try {
+ m_service.run(); // should raise IllegalStateException
+ } catch (IllegalStateException e) {
+ m_sequencer.step(5);
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/Utils.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/Utils.java
new file mode 100644
index 0000000..a409e91
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/Utils.java
@@ -0,0 +1,41 @@
+/*
+ * 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.runtime.itest.components;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Utils {
+ public static final String DM_BSN = "org.apache.felix.dependencymanager";
+
+ public static void schedule(final Runnable task, final long n) {
+ Thread t = new Thread() {
+ public void run() {
+ try {
+ sleep(n);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ task.run();
+ }
+ };
+ t.start();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/packageinfo b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/packageinfo
new file mode 100644
index 0000000..a4f1546
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/packageinfo
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AdapterAnnotationParallelTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AdapterAnnotationParallelTest.java
new file mode 100644
index 0000000..8065dbe
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AdapterAnnotationParallelTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.runtime.itest.tests;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterAnnotationParallelTest extends AdapterAnnotationTest {
+ public AdapterAnnotationParallelTest() {
+ setParallel();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AdapterAnnotationTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AdapterAnnotationTest.java
new file mode 100644
index 0000000..4b4464a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AdapterAnnotationTest.java
@@ -0,0 +1,81 @@
+/*
+* 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.runtime.itest.tests;
+
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.runtime.itest.components.AdapterAnnotation.S1Impl;
+import org.apache.felix.dm.runtime.itest.components.AdapterAnnotation.S1ToS3AdapterAutoConfig;
+import org.apache.felix.dm.runtime.itest.components.AdapterAnnotation.S1ToS3AdapterAutoConfigField;
+import org.apache.felix.dm.runtime.itest.components.AdapterAnnotation.S1ToS3AdapterCallback;
+import org.apache.felix.dm.runtime.itest.components.AdapterAnnotation.S2Impl;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Use case: Verify Aspect Annotations usage.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterAnnotationTest extends TestBase {
+ /**
+ * Check if an adapter gets injected with its adaptee using default auto config mode.
+ * @throws Throwable
+ */
+ public void testAnnotatedAdapterAutoConfig() throws Throwable {
+ Ensure e = new Ensure();
+ ServiceRegistration sr1 = register(e, S1ToS3AdapterAutoConfig.ENSURE);
+ ServiceRegistration sr2 = register(e, S1Impl.ENSURE);
+ ServiceRegistration sr3 = register(e, S2Impl.ENSURE);
+ e.waitForStep(3, 10000);
+ e.ensure();
+ sr1.unregister();
+ sr2.unregister();
+ sr3.unregister();
+ }
+
+ /**
+ * Check if an adapter gets injected with its adaptee in a named class field.
+ */
+ public void testAnnotatedAdapterAutoConfigField() throws Throwable {
+ Ensure e = new Ensure();
+ ServiceRegistration sr1 = register(e, S1ToS3AdapterAutoConfigField.ENSURE);
+ ServiceRegistration sr2 = register(e, S1Impl.ENSURE);
+ ServiceRegistration sr3 = register(e, S2Impl.ENSURE);
+ e.waitForStep(3, 10000);
+ e.ensure();
+ sr1.unregister();
+ sr2.unregister();
+ sr3.unregister();
+ }
+
+ /**
+ * Check if an adapter gets injected with its adaptee in a callback method.
+ */
+ public void testAnnotatedAdapterCallback() {
+ Ensure e = new Ensure();
+ ServiceRegistration sr1 = register(e, S1ToS3AdapterCallback.ENSURE);
+ ServiceRegistration sr2 = register(e, S1Impl.ENSURE);
+ ServiceRegistration sr3 = register(e, S2Impl.ENSURE);
+ e.waitForStep(2, 10000);
+ sr1.unregister();
+ e.waitForStep(4, 10000);
+ sr2.unregister();
+ sr3.unregister();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AspectAnnotationTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AspectAnnotationTest.java
new file mode 100644
index 0000000..041b784
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AspectAnnotationTest.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.runtime.itest.tests;
+
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.runtime.itest.components.AspectAnnotation.ServiceAspect1;
+import org.apache.felix.dm.runtime.itest.components.AspectAnnotation.ServiceAspect2;
+import org.apache.felix.dm.runtime.itest.components.AspectAnnotation.ServiceAspect3;
+import org.apache.felix.dm.runtime.itest.components.AspectAnnotation.ServiceConsumer;
+import org.apache.felix.dm.runtime.itest.components.AspectAnnotation.ServiceProvider;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Use case: Verify Aspect Annotations usage.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectAnnotationTest extends TestBase {
+
+ public void testAspectChain() throws Throwable {
+ Ensure e = new Ensure();
+ // Activate service consumer
+ ServiceRegistration scSequencer = register(e, ServiceConsumer.ENSURE);
+ // Activate service provider
+ ServiceRegistration spSequencer = register(e, ServiceProvider.ENSURE);
+ // Activate service aspect 2
+ ServiceRegistration sa2Sequencer = register(e, ServiceAspect2.ENSURE);
+ // Activate service aspect 3
+ ServiceRegistration sa3Sequencer = register(e, ServiceAspect3.ENSURE);
+ // Activate service aspect 1
+ ServiceRegistration sa1Sequencer = register(e, ServiceAspect1.ENSURE);
+
+ e.step();
+ e.waitForStep(6, 10000);
+
+ // Deactivate service provider
+ spSequencer.unregister();
+ // Make sure that service aspect 1 has been called in ts removed and stop callbacks
+ e.waitForStep(8, 10000);
+ e.ensure();
+
+ scSequencer.unregister();
+ sa1Sequencer.unregister();
+ sa2Sequencer.unregister();
+ sa3Sequencer.unregister();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AspectLifecycleAnnotationParallelTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AspectLifecycleAnnotationParallelTest.java
new file mode 100644
index 0000000..1d14852
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AspectLifecycleAnnotationParallelTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.runtime.itest.tests;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectLifecycleAnnotationParallelTest extends AspectLifecycleAnnotationTest {
+ public AspectLifecycleAnnotationParallelTest() {
+ setParallel();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AspectLifecycleAnnotationTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AspectLifecycleAnnotationTest.java
new file mode 100644
index 0000000..3509886
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AspectLifecycleAnnotationTest.java
@@ -0,0 +1,51 @@
+/*
+* 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.runtime.itest.tests;
+
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.runtime.itest.components.AspectLifecycleAnnotation.ServiceProvider;
+import org.apache.felix.dm.runtime.itest.components.AspectLifecycleAnnotation.ServiceProviderAspect;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Use case: Tests an aspect service, and ensure that its lifecycle methods are properly invoked
+ * (init/start/stop/destroy methods).
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectLifecycleAnnotationTest extends TestBase {
+
+ public void testAnnotatedAspect() {
+ Ensure e = new Ensure();
+ // Provide the Sequencer server to the ServiceProvider service
+ ServiceRegistration sr1 = register(e, ServiceProvider.ENSURE);
+ // Check if the ServiceProvider has been injected in the AspectTest service.
+ e.waitForStep(1, 10000);
+ // Provide the Sequencer server to the ServiceProviderAspect service
+ ServiceRegistration sr2 = register(e, ServiceProviderAspect.ENSURE);
+ // Check if the AspectTest has been injected with the aspect
+ e.waitForStep(3, 10000);
+ // Stop the ServiceProviderAspect service.
+ sr2.unregister();
+ // And check if the aspect has been called in its stop/destroy methods.
+ e.waitForStep(7, 10000);
+ sr1.unregister();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AspectLifecycleWithDynamicProxyAnnotationParalleTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AspectLifecycleWithDynamicProxyAnnotationParalleTest.java
new file mode 100644
index 0000000..df553db
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AspectLifecycleWithDynamicProxyAnnotationParalleTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.runtime.itest.tests;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectLifecycleWithDynamicProxyAnnotationParalleTest extends AspectLifecycleWithDynamicProxyAnnotationTest{
+ public AspectLifecycleWithDynamicProxyAnnotationParalleTest() {
+ setParallel();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AspectLifecycleWithDynamicProxyAnnotationParallelTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AspectLifecycleWithDynamicProxyAnnotationParallelTest.java
new file mode 100644
index 0000000..7e4e189
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AspectLifecycleWithDynamicProxyAnnotationParallelTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.runtime.itest.tests;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectLifecycleWithDynamicProxyAnnotationParallelTest extends AspectLifecycleWithDynamicProxyAnnotationTest {
+ public AspectLifecycleWithDynamicProxyAnnotationParallelTest() {
+ setParallel();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AspectLifecycleWithDynamicProxyAnnotationTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AspectLifecycleWithDynamicProxyAnnotationTest.java
new file mode 100644
index 0000000..239be17
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/AspectLifecycleWithDynamicProxyAnnotationTest.java
@@ -0,0 +1,50 @@
+/*
+* 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.runtime.itest.tests;
+
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.runtime.itest.components.AspectLifecycleWithDynamicProxyAnnotation.ServiceProvider;
+import org.apache.felix.dm.runtime.itest.components.AspectLifecycleWithDynamicProxyAnnotation.ServiceProviderAspect;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Use case: Tests an aspect service implemented as a dynamic proxy, and ensure that its lifecycle methods are properly invoked
+ * (init/start/stop/destroy methods).
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectLifecycleWithDynamicProxyAnnotationTest extends TestBase {
+ public void testAnnotatedAspect() {
+ Ensure e = new Ensure();
+ // Provide the Sequencer server to the ServiceProvider service
+ ServiceRegistration sr1 = register(e, ServiceProvider.ENSURE);
+ // Check if the ServiceProvider has been injected in the AspectTest service.
+ e.waitForStep(1, 10000);
+ // Provide the Sequencer server to the ServiceProviderAspect service
+ ServiceRegistration sr2 = register(e, ServiceProviderAspect.ENSURE);
+ // Check if the AspectTest has been injected with the aspect
+ e.waitForStep(3, 10000);
+ // Remove the ServiceProviderAspect service
+ sr2.unregister();
+ // And check if the aspect has been called in its stop/destroy methods.
+ e.waitForStep(7, 10000);
+ sr1.unregister();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/BundleDependencyAnnotationParallelTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/BundleDependencyAnnotationParallelTest.java
new file mode 100644
index 0000000..8008784
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/BundleDependencyAnnotationParallelTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.runtime.itest.tests;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class BundleDependencyAnnotationParallelTest extends BundleDependencyAnnotationTest {
+ public BundleDependencyAnnotationParallelTest() {
+ setParallel();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/BundleDependencyAnnotationTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/BundleDependencyAnnotationTest.java
new file mode 100644
index 0000000..e5967be
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/BundleDependencyAnnotationTest.java
@@ -0,0 +1,59 @@
+/*
+* 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.runtime.itest.tests;
+
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.runtime.itest.components.BundleDependencyAnnotation;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Use case: Verify Bundle Dependency annotations usage.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class BundleDependencyAnnotationTest extends TestBase {
+
+ /**
+ * Tests a simple Consumer, which has a BundleDependency over the dependency manager bundle.
+ * TODO: this test is not currently working.
+ */
+ public void testBundleDependencyAnnotation() {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, BundleDependencyAnnotation.ENSURE_CONSUMER);
+ e.waitForStep(2, 10000);
+ stopBundle(BundleDependencyAnnotation.METATYPE_BSN);
+ e.waitForStep(4, 10000);
+ sr.unregister();
+ startBundle(BundleDependencyAnnotation.METATYPE_BSN);
+ }
+
+ /**
+ * Tests a Bundle Adapter, which adapts the dependency manager bundle to a "ServiceInterface" service.
+ * @throws Throwable
+ */
+ public void testBundleAdapterServiceAnnotation() throws Throwable {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, BundleDependencyAnnotation.ENSURE_ADAPTER);
+ e.waitForStep(3, 10000);
+ e.ensure();
+ sr.unregister();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/ComponentFactoryAnnotationTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/ComponentFactoryAnnotationTest.java
new file mode 100644
index 0000000..48a2863
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/ComponentFactoryAnnotationTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.runtime.itest.tests;
+
+import java.util.Hashtable;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.runtime.api.ComponentFactory;
+import org.apache.felix.dm.runtime.api.ComponentInstance;
+import org.apache.felix.dm.runtime.itest.components.ComponentFactoryAnnotation;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class ComponentFactoryAnnotationTest extends TestBase {
+
+ private final Ensure m_ensure = new Ensure();
+
+ public void testServiceFactory() {
+ ServiceRegistration sr = register(m_ensure, ComponentFactoryAnnotation.ENSURE);
+
+ DependencyManager m = new DependencyManager(context);
+ // Wait for the factory.
+ m.add(m.createComponent()
+ .setImplementation(this)
+ .add(m.createServiceDependency()
+ .setService(ComponentFactory.class,
+ "(" + Component.FACTORY_NAME + "=" + ComponentFactoryAnnotation.FACTORY + ")")
+ .setRequired(true).setCallbacks("bindFactory", null)));
+
+ // Check if the test.annotation components have been initialized orderly
+ m_ensure.waitForStep(10, 5000);
+ m.clear();
+ sr.unregister();
+ }
+
+ void bindFactory(ComponentFactory factory) {
+ // create a service instance with this configuration
+ Hashtable conf = new Hashtable();
+ conf.put("instance.id", "instance");
+ conf.put(".private.param", "private");
+ ComponentInstance instance = factory.newInstance(conf);
+ m_ensure.waitForStep(4, 5000);
+
+ // update the service instance
+ conf = new Hashtable();
+ conf.put("instance.id", "instance");
+ conf.put(".private.param", "private");
+ conf.put("instance.modified", "true");
+ instance.update(conf);
+ m_ensure.waitForStep(7, 5000);
+
+ // remove instance
+ instance.dispose();
+ m_ensure.waitForStep(10, 5000);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/CompositeAnnotationsParallelTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/CompositeAnnotationsParallelTest.java
new file mode 100644
index 0000000..8e5167f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/CompositeAnnotationsParallelTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.runtime.itest.tests;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class CompositeAnnotationsParallelTest extends CompositeAnnotationsTest {
+ public CompositeAnnotationsParallelTest() {
+ setParallel();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/CompositeAnnotationsTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/CompositeAnnotationsTest.java
new file mode 100644
index 0000000..9c2d54c
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/CompositeAnnotationsTest.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.runtime.itest.tests;
+
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.runtime.itest.components.CompositeAnnotations.C1;
+import org.apache.felix.dm.runtime.itest.components.CompositeAnnotations.Dependency1;
+import org.apache.felix.dm.runtime.itest.components.CompositeAnnotations.Dependency2;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Use case: Verify Composite annotated services.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class CompositeAnnotationsTest extends TestBase {
+
+ public void testComposite() {
+ Ensure e = new Ensure();
+ ServiceRegistration sr1 = register(e, C1.ENSURE);
+ ServiceRegistration sr2 = register(e, Dependency1.ENSURE);
+ ServiceRegistration sr3 = register(e, Dependency2.ENSURE);
+ e.waitForStep(4, 10000);
+ sr3.unregister();
+ sr2.unregister();
+ sr1.unregister();
+ e.waitForStep(12, 10000);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/ConfigurationDependencyAnnotationTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/ConfigurationDependencyAnnotationTest.java
new file mode 100644
index 0000000..0330654
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/ConfigurationDependencyAnnotationTest.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.runtime.itest.tests;
+
+import java.io.IOException;
+import java.util.Hashtable;
+
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.runtime.itest.components.ConfigurationDependencyAnnotation.ConfigurableComponent;
+import org.apache.felix.dm.runtime.itest.components.ConfigurationDependencyAnnotation.ConfigurableComponentWithDynamicExtraConfiguration;
+import org.junit.Assert;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ConfigurationDependencyAnnotationTest extends TestBase {
+
+ private final static int MAXWAIT = 5000;
+
+
+ /**
+ * Tests the ConfigurationDependency annotation.
+ */
+ @SuppressWarnings({ "serial", "rawtypes", "unchecked" })
+ public void testConfigurationDependencyAnnotation() throws Throwable {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, ConfigurableComponent.ENSURE);
+ ConfigurationAdmin cm = (ConfigurationAdmin) context.getService(context.getServiceReference(ConfigurationAdmin.class.getName()));
+ try {
+ org.osgi.service.cm.Configuration cf = cm.getConfiguration(ConfigurableComponent.class.getName(), null);
+ cf.update(new Hashtable() {
+ {
+ put("foo", "bar");
+ }
+ });
+ e.waitForStep(1, MAXWAIT);
+ cf.delete();
+ e.waitForStep(3, MAXWAIT);
+ e.ensure();
+ sr.unregister();
+ } catch (IOException err) {
+ err.printStackTrace();
+ Assert.fail("can't create factory configuration");
+ }
+ }
+
+ /**
+ * Tests a Component two ConfigurationDependency (the second one is "instance bound"
+ * and its pid is declared from the init method).
+ */
+ @SuppressWarnings({ "serial", "rawtypes", "unchecked" })
+ public void testConfigurationDependencyWithAnotherExtraDynamicConfigurationAnnotation() throws Throwable {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, ConfigurableComponentWithDynamicExtraConfiguration.ENSURE);
+ ConfigurationAdmin cm = (ConfigurationAdmin) context.getService(context.getServiceReference(ConfigurationAdmin.class.getName()));
+ try {
+ org.osgi.service.cm.Configuration cf = cm.getConfiguration(ConfigurableComponentWithDynamicExtraConfiguration.class.getName(), null);
+ cf.update(new Hashtable() {
+ {
+ put("foo", "bar");
+ put("dynamicPid", "dynamicPid"); // Pid of the second Configuration Dependency.
+ }
+ });
+ org.osgi.service.cm.Configuration extraCf = cm.getConfiguration("dynamicPid", null);
+ extraCf.update(new Hashtable() {
+ {
+ put("foo2", "bar2");
+ }
+ });
+
+ e.waitForStep(4, MAXWAIT);
+ cf.delete();
+ extraCf.delete();
+ e.waitForStep(5, MAXWAIT);
+ e.ensure();
+ sr.unregister();
+ } catch (IOException err) {
+ err.printStackTrace();
+ Assert.fail("can't create factory configuration");
+ }
+ }
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/ExtraServicePropertiesParallelTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/ExtraServicePropertiesParallelTest.java
new file mode 100644
index 0000000..7e523e8
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/ExtraServicePropertiesParallelTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.runtime.itest.tests;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ExtraServicePropertiesParallelTest extends ExtraServicePropertiesTest {
+ public ExtraServicePropertiesParallelTest() {
+ setParallel();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/ExtraServicePropertiesTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/ExtraServicePropertiesTest.java
new file mode 100644
index 0000000..849aa14
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/ExtraServicePropertiesTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.runtime.itest.tests;
+
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.runtime.itest.components.ExtraAdapterServiceProperties;
+import org.apache.felix.dm.runtime.itest.components.ExtraComponentFactoryServiceProperties;
+import org.apache.felix.dm.runtime.itest.components.ExtraFactoryServiceProperties;
+import org.apache.felix.dm.runtime.itest.components.ExtraServiceProperties;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Use case: Verify the a Service may provide its service properties dynamically from its start method.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ExtraServicePropertiesTest extends TestBase {
+
+ /**
+ * Tests if a Service can provide its service properties from its start method.
+ */
+ public void testExtraServiceProperties() {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, ExtraServiceProperties.ENSURE);
+ e.waitForStep(2, 10000);
+ sr.unregister();
+ }
+
+ /**
+ * Tests if a Service instantiated by a Factory set can provide its service properties from its start method.
+ */
+ public void testExtraFactoryServiceProperties() {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, ExtraFactoryServiceProperties.ENSURE);
+ e.waitForStep(3, 10000);
+ sr.unregister();
+ }
+
+ /**
+ * Tests if a Service instantiated by a DM ComponentFactory can provide its service properties from its start method.
+ */
+ public void testExtraComponentFactoryServiceProperties() {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, ExtraComponentFactoryServiceProperties.ENSURE);
+ e.waitForStep(3, 10000);
+ sr.unregister();
+ }
+
+ /**
+ * Tests if an AdapterService can provide its service properties from its start method.
+ */
+ public void testExtraAdapterServiceProperties() {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, ExtraAdapterServiceProperties.ENSURE);
+ e.waitForStep(3, 10000);
+ sr.unregister();
+ }
+
+ /**
+ * Tests if an AspectService can provide its service properties from its start method.
+ */
+ // TODO
+ public void testExtraAspectServiceProperties() {
+// Ensure e = new Ensure();
+// ServiceRegistration sr = register(e, ExtraAspectServiceProperties.ENSURE);
+// e.waitForStep(3, 10000);
+// sr.unregister();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/FactoryConfigurationAdapterAnnotationParallelTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/FactoryConfigurationAdapterAnnotationParallelTest.java
new file mode 100644
index 0000000..1675260
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/FactoryConfigurationAdapterAnnotationParallelTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.runtime.itest.tests;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FactoryConfigurationAdapterAnnotationParallelTest extends FactoryConfigurationAdapterAnnotationTest {
+ public FactoryConfigurationAdapterAnnotationParallelTest() {
+ setParallel();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/FactoryConfigurationAdapterAnnotationTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/FactoryConfigurationAdapterAnnotationTest.java
new file mode 100644
index 0000000..8e181f5
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/FactoryConfigurationAdapterAnnotationTest.java
@@ -0,0 +1,78 @@
+/*
+* 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.runtime.itest.tests;
+
+import java.io.IOException;
+import java.util.Hashtable;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.runtime.itest.components.FactoryConfigurationAdapterAnnotation.ServiceProvider;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * Use case: Verify that an annotated Configuration Factory Adapter Service is properly created when a factory configuration
+ * is created from Config Admin.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class FactoryConfigurationAdapterAnnotationTest extends TestBase {
+
+ private final static int MAXWAIT = 5000;
+
+ @SuppressWarnings("serial")
+ public void testFactoryConfigurationAdapterAnnotation() throws Throwable {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, ServiceProvider.ENSURE);
+ ConfigurationAdmin cm = (ConfigurationAdmin) context.getService(context.getServiceReference(ConfigurationAdmin.class.getName()));
+ try {
+ // Create a factory configuration in order to instantiate the ServiceProvider
+ org.osgi.service.cm.Configuration cf = cm.createFactoryConfiguration("FactoryPidTest", null);
+ cf.update(new Hashtable() {
+ {
+ put("foo2", "bar2");
+ }
+ });
+ // Wait for the ServiceProvider activation.
+ e.waitForStep(2, MAXWAIT);
+ // Update conf
+ cf.update(new Hashtable() {
+ {
+ put("foo2", "bar2_modified");
+ }
+ });
+ // Wait for effective update
+ e.waitForStep(4, MAXWAIT);
+ // Remove configuration.
+ cf.delete();
+ // Check if ServiceProvider has been stopped.
+ e.waitForStep(6, MAXWAIT);
+ e.ensure();
+ sr.unregister();
+ }
+ catch (IOException err) {
+ err.printStackTrace();
+ Assert.fail("can't create factory configuration");
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/Felix4050Test.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/Felix4050Test.java
new file mode 100644
index 0000000..bd2729d
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/Felix4050Test.java
@@ -0,0 +1,44 @@
+/*
+ * 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.runtime.itest.tests;
+
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.runtime.itest.components.Felix4050;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Test for FELIX-4050 issue: It validates that component state calculation does not mess up
+ * when an @Init method adds an available dependency using the API, and also returns a Map for
+ * configuring a named dependency.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Felix4050Test extends TestBase {
+ public void testFelix4050() {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, Felix4050.ENSURE);
+ // wait for S to be started
+ e.waitForStep(3, 10000);
+ // remove our sequencer: this will stop S
+ sr.unregister();
+ // ensure that S is stopped and destroyed
+ e.waitForStep(5, 10000);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/Felix4357ParallelTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/Felix4357ParallelTest.java
new file mode 100644
index 0000000..9ddbdb9
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/Felix4357ParallelTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.runtime.itest.tests;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Felix4357ParallelTest extends Felix4357Test {
+ public Felix4357ParallelTest() {
+ setParallel();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/Felix4357Test.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/Felix4357Test.java
new file mode 100644
index 0000000..9cf415d
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/Felix4357Test.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.runtime.itest.tests;
+
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.runtime.itest.components.Felix4357;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Test for FELIX-4357 issue: It validates the types of some service component properties
+ * defined with @Property annotation.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Felix4357Test extends TestBase {
+
+ public void testPropertiesWithTypes() {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, Felix4357.ENSURE);
+ // wait for S to be started
+ e.waitForStep(30, 10000);
+ // remove our sequencer: this will stop S
+ sr.unregister();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/MultipleAnnotationsParallelTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/MultipleAnnotationsParallelTest.java
new file mode 100644
index 0000000..ffb4fce
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/MultipleAnnotationsParallelTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.runtime.itest.tests;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class MultipleAnnotationsParallelTest extends MultipleAnnotationsTest {
+ public MultipleAnnotationsParallelTest() {
+ setParallel();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/MultipleAnnotationsTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/MultipleAnnotationsTest.java
new file mode 100644
index 0000000..f3b302a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/MultipleAnnotationsTest.java
@@ -0,0 +1,40 @@
+/*
+* 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.runtime.itest.tests;
+
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.runtime.itest.components.MultipleAnnotations;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Use case: Verify complex Annotation usage.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class MultipleAnnotationsTest extends TestBase {
+
+ public void testMultipleAnnotations() {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, MultipleAnnotations.ENSURE);
+ e.waitForStep(7, 10000);
+ sr.unregister();
+ e.waitForStep(11, 10000);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/PropagateAnnotationParallelTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/PropagateAnnotationParallelTest.java
new file mode 100644
index 0000000..c250ddd
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/PropagateAnnotationParallelTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.runtime.itest.tests;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class PropagateAnnotationParallelTest extends PropagateAnnotationTest {
+ public PropagateAnnotationParallelTest() {
+ setParallel();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/PropagateAnnotationTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/PropagateAnnotationTest.java
new file mode 100644
index 0000000..89cab59
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/PropagateAnnotationTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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.runtime.itest.tests;
+
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.runtime.itest.components.PropagateAnnotation;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Use case: Verify that dependency "propagate" option is properly propagating properties to provided service.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class PropagateAnnotationTest extends TestBase {
+
+ public void testServiceDependencyPropagate() {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, PropagateAnnotation.ENSURE);
+ e.waitForStep(3, 10000);
+ sr.unregister();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/PublisherAnnotationTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/PublisherAnnotationTest.java
new file mode 100644
index 0000000..2466937
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/PublisherAnnotationTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.runtime.itest.tests;
+
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.runtime.itest.components.AdapterServiceTestWithPublisher;
+import org.apache.felix.dm.runtime.itest.components.BundleAdapterServiceTestWithPublisher;
+import org.apache.felix.dm.runtime.itest.components.ComponentFactoryServiceTestWthPublisher;
+import org.apache.felix.dm.runtime.itest.components.FactoryConfigurationAdapterServiceTestWithPublisher;
+import org.apache.felix.dm.runtime.itest.components.FactoryServiceTestWthPublisher;
+import org.apache.felix.dm.runtime.itest.components.ResourceAdapterServiceTestWithPublisher;
+import org.apache.felix.dm.runtime.itest.components.ServiceTestWthPublisher;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class PublisherAnnotationTest extends TestBase {
+
+ /**
+ * A Service that just registers/unregisters its service, using the @ServiceLifecycle annotation.
+ */
+ public void testServiceWithPublisher() {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, ServiceTestWthPublisher.ENSURE);
+ e.waitForStep(4, 10000);
+ sr.unregister();
+ }
+
+ /**
+ * A Service instantiated from a FactorySet, and which registers/unregisters its service,
+ * using the @ServiceLifecycle annotation.
+ */
+ public void testFactoryServiceWithPublisher() {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, FactoryServiceTestWthPublisher.ENSURE);
+ e.waitForStep(5, 10000);
+ sr.unregister();
+ }
+
+ /**
+ * A Service instantiated from a DM ComponentFactory, and which registers/unregisters its service,
+ * using the @ServiceLifecycle annotation.
+ */
+ public void testComponentFactoryServiceWithPublisher() {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, ComponentFactoryServiceTestWthPublisher.ENSURE);
+ e.waitForStep(5, 10000);
+ sr.unregister();
+ }
+
+ /**
+ * Test an AdapterService which provides its interface using a @ServiceLifecycle.
+ */
+ public void testAdapterServiceWithPublisher() {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, AdapterServiceTestWithPublisher.ENSURE);
+ e.waitForStep(6, 10000);
+ sr.unregister();
+ }
+
+ /**
+ * Test a BundleAdapterService which provides its interface using a @ServiceLifecycle.
+ */
+ public void testBundleAdapterServiceWithPublisher() {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, BundleAdapterServiceTestWithPublisher.ENSURE);
+ e.waitForStep(5, 10000);
+ sr.unregister();
+ }
+
+ /**
+ * Test a ResourceAdapterService which provides its interface using a @ServiceLifecycle.
+ */
+ public void TestResourceAdapterServiceWithPublisher() {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, ResourceAdapterServiceTestWithPublisher.ENSURE);
+ e.waitForStep(5, 10000);
+ sr.unregister();
+ }
+
+ /**
+ * Test a FactoryConfigurationAdapterService which provides its interface using a @ServiceLifecycle.
+ */
+ public void testFactoryAdapterServiceWithPublisher() {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, FactoryConfigurationAdapterServiceTestWithPublisher.ENSURE);
+ e.waitForStep(5, 10000);
+ sr.unregister();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/ResourceAnnotationTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/ResourceAnnotationTest.java
new file mode 100644
index 0000000..a08033c
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/ResourceAnnotationTest.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.runtime.itest.tests;
+
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.runtime.itest.components.ResourceAnnotation;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Use case: Verify Bundle Dependency annotations usage.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResourceAnnotationTest extends TestBase {
+
+ /**
+ * Tests a simple ResourceConsumer
+ * @param context
+ */
+ public void testResourceAnnotation() {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, ResourceAnnotation.ENSURE_RESOURCE);
+ ServiceRegistration sr2 = register(e, ResourceAnnotation.ENSURE_PROVIDER);
+ sr.unregister();
+ sr2.unregister();
+ e.waitForStep(1, 10000);
+ }
+
+ /**
+ * Tests a simple ResourceConsumer using a class field for resource injection
+ */
+ public void testResourceAnnotationAutoConfig() {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, ResourceAnnotation.ENSURE_FIELD);
+ ServiceRegistration sr2 = register(e, ResourceAnnotation.ENSURE_PROVIDER);
+ sr.unregister();
+ sr2.unregister();
+ e.waitForStep(1, 10000);
+ }
+
+ /**
+ * Tests a ResourceAdapter
+ * @param context
+ */
+ public void testResourceAdapterAnnotation() throws Throwable {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, ResourceAnnotation.ENSURE_ADAPTER);
+ ServiceRegistration sr2 = register(e, ResourceAnnotation.ENSURE_PROVIDER);
+ sr.unregister();
+ sr2.unregister();
+ e.waitForStep(2, 10000);
+ e.ensure();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/ServiceFactoryAnnotationTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/ServiceFactoryAnnotationTest.java
new file mode 100644
index 0000000..0104016
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/ServiceFactoryAnnotationTest.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.runtime.itest.tests;
+
+import java.util.Hashtable;
+import java.util.Set;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.runtime.itest.components.ServiceFactoryAnnotation;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class ServiceFactoryAnnotationTest extends TestBase {
+
+ private final Ensure m_ensure = new Ensure();
+
+ public void testServiceFactory() {
+ ServiceRegistration sr = register(m_ensure, ServiceFactoryAnnotation.ENSURE);
+
+ DependencyManager m = new DependencyManager(context);
+ // Wait for the factory.
+ m.add(m.createComponent()
+ .setImplementation(this)
+ .add(m.createServiceDependency()
+ .setService(Set.class,
+ "(" + Component.FACTORY_NAME + "=" + ServiceFactoryAnnotation.FACTORY + ")")
+ .setRequired(true).setCallbacks("bindFactory", null)));
+
+ // Check if the test.annotation components have been initialized orderly
+ m_ensure.waitForStep(10, 5000);
+ m.clear();
+ sr.unregister();
+ }
+
+ void bindFactory(Set factory) {
+ // create a service instance with this configuration
+ Hashtable conf = new Hashtable();
+ conf.put("instance.id", "instance");
+ conf.put(".private.param", "private");
+ Assert.assertTrue(factory.add(conf));
+ m_ensure.waitForStep(4, 5000);
+
+ // update the service instance
+ conf.put("instance.modified", "true");
+ Assert.assertFalse(factory.add(conf));
+ m_ensure.waitForStep(7, 5000);
+
+ // remove instance
+ Assert.assertTrue(factory.remove(conf));
+ Assert.assertFalse(factory.remove(conf));
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/SimpleAnnotationsParallelTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/SimpleAnnotationsParallelTest.java
new file mode 100644
index 0000000..75cfa3e
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/SimpleAnnotationsParallelTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.runtime.itest.tests;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class SimpleAnnotationsParallelTest extends SimpleAnnotationsTest {
+ public SimpleAnnotationsParallelTest() {
+ setParallel();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/SimpleAnnotationsTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/SimpleAnnotationsTest.java
new file mode 100644
index 0000000..768a814
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/SimpleAnnotationsTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.runtime.itest.tests;
+
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.runtime.itest.components.SimpleAnnotations.Consumer;
+import org.apache.felix.dm.runtime.itest.components.SimpleAnnotations.Producer;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Use case: Ensure that a Provider can be injected into a Consumer, using simple DM annotations.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class SimpleAnnotationsTest extends TestBase {
+
+ public void testSimpleAnnotations() throws Throwable {
+ Ensure e = new Ensure();
+ ServiceRegistration er = register(e, Producer.ENSURE);
+ e.waitForStep(3, 10000); // Producer registered
+ ServiceRegistration er2 = register(e, Consumer.ENSURE);
+
+ er2.unregister(); // stop consumer
+ er.unregister(); // stop provider
+
+ // And check if components have been deactivated orderly.
+ e.waitForStep(10, 10000);
+ e.ensure();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/TemporalAnnotationsParallelTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/TemporalAnnotationsParallelTest.java
new file mode 100644
index 0000000..e4bd4ba
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/TemporalAnnotationsParallelTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.runtime.itest.tests;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class TemporalAnnotationsParallelTest extends TemporalAnnotationsTest {
+ public TemporalAnnotationsParallelTest() {
+ setParallel();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/TemporalAnnotationsTest.java b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/TemporalAnnotationsTest.java
new file mode 100644
index 0000000..ac50edf
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/TemporalAnnotationsTest.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.runtime.itest.tests;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.runtime.itest.components.TemporalAnnotations;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Use case: Verify Temporal Service dependency Annotations usage.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class TemporalAnnotationsTest extends TestBase {
+
+ public void testTemporalServiceDependency() {
+ Ensure ensure = new Ensure();
+ ServiceRegistration ensureReg = register(ensure, TemporalAnnotations.ENSURE);
+ Dictionary props = new Hashtable() {
+ {
+ put("test", "temporal");
+ }
+ };
+ Runnable r = Ensure.createRunnableStep(ensure, 1);
+ ServiceRegistration sr = context.registerService(Runnable.class.getName(), r, props);
+ ensure.waitForStep(1, 15000);
+ System.out.println("unregistering R");
+ sr.unregister();
+ ensure.step(2);
+ sleep(500);
+ r = Ensure.createRunnableStep(ensure, 3);
+ sr = context.registerService(Runnable.class.getName(), r, props);
+ ensure.waitForStep(3, 15000);
+ sr.unregister();
+ ensure.step(4);
+ sleep(1500);
+ ensure.waitForStep(5, 15000);
+ ensureReg.unregister();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/test/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/test/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/test/.gitignore
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/.classpath b/dependencymanager/org.apache.felix.dependencymanager.runtime/.classpath
new file mode 100644
index 0000000..f89ae43
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/.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.runtime/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.runtime/.gitignore
new file mode 100644
index 0000000..90dde36
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/.gitignore
@@ -0,0 +1,3 @@
+/bin/
+/bin_test/
+/generated/
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/.project b/dependencymanager/org.apache.felix.dependencymanager.runtime/.project
new file mode 100644
index 0000000..56b71cc
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.apache.felix.dependencymanager.runtime</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.runtime/.settings/org.eclipse.jdt.core.prefs b/dependencymanager/org.apache.felix.dependencymanager.runtime/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..7341ab1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/.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.runtime/bnd.bnd b/dependencymanager/org.apache.felix.dependencymanager.runtime/bnd.bnd
new file mode 100644
index 0000000..087aae0
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/bnd.bnd
@@ -0,0 +1,44 @@
+#
+# 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.
+#
+Bundle-Version: 4.0.0
+-buildpath: \
+ osgi.core;version=4.2,\
+ osgi.cmpn;version=4.2,\
+ org.apache.felix.dependencymanager;version=latest,\
+ de.twentyeleven.skysail.org.json-osgi;version=20080701.0
+Bundle-Activator:org.apache.felix.dm.runtime.Activator
+Private-Package: \
+ org.apache.felix.dm.runtime,\
+ org.json
+Export-Package: \
+ org.apache.felix.dm.runtime.api
+Provide-Capability: osgi.extender; osgi.extender="org.apache.felix.dependencymanager.runtime";\
+ uses:="org.apache.felix.dm";version:Version="4.0.0"
+Include-Resource: META-INF/=resources/LICENSE,\
+ META-INF/=resources/NOTICE,\
+ META-INF/=resources/DEPENDENCIES,\
+ META-INF/=resources/changelog.txt
+
+Bundle-Name: Apache Felix Dependency Manager Runtime
+Bundle-Description: Loads Apache Felix Dependency Manager component descriptors from active \
+ annoted bundles
+Bundle-Category: osgi
+Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+Bundle-Vendor: The Apache Software Foundation
+Bundle-ContactAddress: http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager.html
+# Work around used to make sure DM api is imported using proper version range (can be removed with bndtools 3)
+Import-Package: org.apache.felix.dm;org.apache.felix.dm.context;version="[4, 5)", *
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/DEPENDENCIES b/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/DEPENDENCIES
new file mode 100644
index 0000000..3fdced9
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/DEPENDENCIES
@@ -0,0 +1,24 @@
+Apache Felix Dependency Manager Runtime
+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 from http://www.json.org.
+Copyright (c) 2002 JSON.org
+Licensed under the JSON License
+
+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
+- JSON License
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/LICENSE b/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/LICENSE
new file mode 100644
index 0000000..eedfc8a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/LICENSE
@@ -0,0 +1,228 @@
+
+ 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.
+
+=========================================================================
+== JSON License ==
+=========================================================================
+
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/NOTICE b/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/NOTICE
new file mode 100644
index 0000000..5fa750e
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/NOTICE
@@ -0,0 +1,10 @@
+Apache Felix Dependency Manager Runtime
+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 from http://www.json.org.
+Copyright (c) 2002 JSON.org
+Licensed under the JSON License
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/changelog.txt b/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/changelog.txt
new file mode 100644
index 0000000..3c75cff
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/changelog.txt
@@ -0,0 +1,8 @@
+Release 4.0.0:
+-------------
+
+FELIX-4777: Dynamic initialization time configuration of @ConfigurationDependency
+FELIX-4676: Add Provide-Capability for DependencyManager Runtime bundle
+FELIX-4600: Cherrypicking of propagated properties
+FELIX-4684: Replace DependencyManager Runtime "factorySet" by a cleaner API
+FELIX-4709: Incorrect Named Dependencies are binded to the Service Instance
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/.gitignore
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/AbstractBuilder.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/AbstractBuilder.java
new file mode 100644
index 0000000..2e58f1c
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/AbstractBuilder.java
@@ -0,0 +1,191 @@
+/*
+ * 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.runtime;
+
+import java.util.List;
+
+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.DependencyManager;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Base class for all kind of DM component builders (for Component, Aspect, Adapters ...).
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class AbstractBuilder
+{
+ /**
+ * Returns the service component type.
+ */
+ abstract String getType();
+
+ /**
+ * Builds the service component.
+ * @param serviceMetaData the service component metadata parsed from the descriptor file.
+ * @param serviceDependencies the service component dependencies metadata parsed from the descriptor file.
+ */
+ abstract void build(MetaData serviceMetaData, List<MetaData> serviceDependencies, Bundle b, DependencyManager dm)
+ throws Exception;
+
+ /**
+ * Sets common Service parameters, if provided from our Component descriptor
+ */
+ protected void setCommonServiceParams(Component c, MetaData srvMeta) throws Exception
+ {
+ // Set auto configured component fields.
+ DependencyManager dm = c.getDependencyManager();
+ boolean autoConfigureComponents = "true".equals(dm.getBundleContext().getProperty(
+ Activator.CONF_ENABLE_AUTOCONFIG));
+
+ if (!autoConfigureComponents)
+ {
+ c.setAutoConfig(BundleContext.class, Boolean.FALSE);
+ c.setAutoConfig(ServiceRegistration.class, Boolean.FALSE);
+ c.setAutoConfig(DependencyManager.class, Boolean.FALSE);
+ c.setAutoConfig(Component.class, Boolean.FALSE);
+ }
+
+ // See if BundleContext must be auto configured.
+ String bundleContextField = srvMeta.getString(Params.bundleContextField, null);
+ if (bundleContextField != null)
+ {
+ c.setAutoConfig(BundleContext.class, bundleContextField);
+ }
+
+ // See if DependencyManager must be auto configured.
+ String dependencyManagerField = srvMeta.getString(Params.dependencyManagerField, null);
+ if (dependencyManagerField != null)
+ {
+ c.setAutoConfig(DependencyManager.class, dependencyManagerField);
+ }
+
+ // See if Component must be auto configured.
+ String componentField = srvMeta.getString(Params.componentField, null);
+ if (componentField != null)
+ {
+ c.setAutoConfig(Component.class, componentField);
+ }
+
+ // Now, if the component has a @Started annotation, then add our component state listener,
+ // which will callback the corresponding annotated method, once the component is started.
+ String registered = srvMeta.getString(Params.registered, null);
+ String unregistered = srvMeta.getString(Params.unregistered, null);
+
+ if (registered != null || unregistered != null)
+ {
+ c.add(new RegistrationListener(registered, unregistered));
+ }
+ }
+
+ /**
+ * Registers all unnamed dependencies into a given service. Named dependencies are
+ * handled differently, and are managed by the ServiceLifecycleHandler class.
+ * @throws Exception
+ */
+ protected static void addUnamedDependencies(Bundle b, DependencyManager dm, Component s, MetaData srvMeta,
+ List<MetaData> depsMeta) throws Exception
+ {
+ for (MetaData dependency : depsMeta)
+ {
+ String name = dependency.getString(Params.name, null);
+ if (name == null)
+ {
+ DependencyBuilder depBuilder = new DependencyBuilder(dependency);
+ Log.instance().info("adding dependency %s into service %s", dependency, srvMeta);
+ Dependency d = depBuilder.build(b, dm);
+ s.add(d);
+ }
+ }
+ }
+
+ static class RegistrationListener implements ComponentStateListener
+ {
+ private final String m_registered;
+ private final String m_unregistered;
+ private volatile boolean m_isRegistered;
+
+ RegistrationListener(String registered, String unregistered)
+ {
+ m_registered = registered;
+ m_unregistered = unregistered;
+ }
+
+ public void changed(Component c, ComponentState state)
+ {
+ boolean wasRegistered = m_isRegistered;
+ switch (state)
+ {
+ case TRACKING_OPTIONAL:
+ // Started
+ m_isRegistered = true;
+ if (!wasRegistered && m_registered != null)
+ {
+ // The component has registered a service: notify all component instances
+ Object[] componentInstances = c.getInstances();
+ for (Object instance : componentInstances)
+ {
+ try
+ {
+ Class<?>[][] signatures = new Class<?>[][] { { ServiceRegistration.class }, {} };
+ Object[][] params = new Object[][] { { c.getServiceRegistration() }, {} };
+ InvocationUtil.invokeCallbackMethod(instance, m_registered, signatures, params);
+ }
+ catch (Throwable t)
+ {
+ Log.instance().error("Exception caught while invoking method %s on component %s", t,
+ m_registered, instance);
+ }
+ }
+ }
+ break;
+
+ case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
+ // Stopped
+ m_isRegistered = false;
+ if (wasRegistered && m_unregistered != null)
+ {
+ Object[] componentInstances = c.getInstances();
+ for (Object instance : componentInstances)
+ {
+ try
+ {
+ InvocationUtil.invokeCallbackMethod(instance, m_unregistered, new Class[][] { {} },
+ new Object[][] { {} });
+ }
+ catch (Throwable t)
+ {
+ Log.instance().error("Exception caught while invoking method %s on component %s", t,
+ m_registered, instance);
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Activator.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Activator.java
new file mode 100644
index 0000000..91d702a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Activator.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+/*
+ * This is the Activator for our DependencyManager Component Runtime.
+ * Here, we'll track started/stopped bundles which have some DependencyManager
+ * descriptors (META-INF/dependencymanager/*.dm). Such descriptors are generated
+ * by the Bnd plugin which parses DependencyManager annotations at compile time.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Activator extends DependencyActivatorBase
+{
+ /**
+ * Name of bundle context property telling if log service is required or not.
+ * (default = false)
+ */
+ final static String CONF_LOG = "org.apache.felix.dependencymanager.runtime.log";
+
+ /**
+ * Name of bundle context property telling if Components must be auto configured
+ * with BundleContext/ServiceRegistration etc .. (default = false)
+ */
+ final static String CONF_ENABLE_AUTOCONFIG = "org.apache.felix.dependencymanager.runtime.autoconfig";
+
+ /**
+ * Initialize our DependencyManager Runtime service.
+ *
+ * We depend on the OSGi LogService, and we track all started bundles which do have a
+ * "DependencyManager-Component" Manifest header.
+ * If the "dm.runtime.log=true" or "dm.runtime.packageAdmin=true" parameter is configured in the Felix config.properties
+ * then we'll use a required/temporal service dependency over the respective service.
+ * These temporal dependencies avoid us to be restarted if the respective service is temporarily
+ * unavailable (that is: when the service is updating).
+ * if the "dm.runtime.log" or "dm.runtime.packageAdmin" is not configured or it it is set to false, then we'll use
+ * an optional dependency over the respective service, in order to use a NullObject in case
+ * the service is not available.
+ */
+ @Override
+ public void init(BundleContext context, DependencyManager dm) throws Exception
+ {
+ Component component = createComponent()
+ .setImplementation(DependencyManagerRuntime.class)
+ .setComposition("getComposition")
+ .add(createBundleDependency()
+ .setRequired(false)
+ .setStateMask(Bundle.ACTIVE)
+ .setFilter("(DependencyManager-Component=*)")
+ .setCallbacks("bundleStarted", "bundleStopped"))
+ .add(createServiceDependency()
+ .setRequired(true)
+ .setService(PackageAdmin.class))
+ .add(createServiceDependency()
+ .setRequired("true".equalsIgnoreCase(context.getProperty(CONF_LOG)))
+ .setService(LogService.class));
+
+ dm.add(component);
+ }
+
+ /**
+ * Our bundle is stopping: shutdown our Dependency Manager Runtime service.
+ */
+ @Override
+ public void destroy(BundleContext context, DependencyManager dm) throws Exception
+ {
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/AdapterServiceBuilder.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/AdapterServiceBuilder.java
new file mode 100644
index 0000000..6acd109
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/AdapterServiceBuilder.java
@@ -0,0 +1,104 @@
+/*
+ * 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.runtime;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+
+/**
+ * Builded called when the JSON parser find an adapter service descriptor.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterServiceBuilder extends AbstractBuilder
+{
+ /** The type attribute specified in the JSON descriptor */
+ private final static String TYPE = "AdapterService";
+
+ @Override
+ public String getType()
+ {
+ return TYPE;
+ }
+
+ @Override
+ public void build(MetaData srvMeta, List<MetaData> depsMeta, Bundle b, DependencyManager dm)
+ throws Exception
+ {
+ Class<?> adapterImplClass = b.loadClass(srvMeta.getString(Params.impl));
+ String[] provides = srvMeta.getStrings(Params.provides, null);
+ Dictionary<String, Object> adapterProperties = srvMeta.getDictionary(Params.properties, null);
+ Class<?> adapteeService = b.loadClass(srvMeta.getString(Params.adapteeService));
+ String adapteeFilter = srvMeta.getString(Params.adapteeFilter, null);
+ String field = srvMeta.getString(Params.field, null);
+ String added = srvMeta.getString(Params.added, null);
+ String changed = srvMeta.getString(Params.changed, null);
+ String removed = srvMeta.getString(Params.removed, null);
+ String swap = srvMeta.getString(Params.swap, null);
+ boolean propagate = "true".equals(srvMeta.getString(Params.propagate, "true"));
+
+ if (field != null && (added != null || changed != null || removed != null || swap != null))
+ {
+ throw new IllegalArgumentException("autoconfig field " + field + " can't be defined with both added/changed/removed/swap calllbacks");
+ }
+
+ Component c;
+
+ if (field != null)
+ {
+ c = dm.createAdapterService(adapteeService, adapteeFilter, field, null, null, null, null, null, propagate);
+ }
+ else
+ {
+ if (added != null || changed != null || removed != null || swap != null)
+ {
+ c = dm.createAdapterService(adapteeService, adapteeFilter, null, null, added, changed, removed, swap, propagate);
+
+ }
+ else
+ {
+ c = dm.createAdapterService(adapteeService, adapteeFilter, null, null, null, null, null, null, propagate);
+ }
+ }
+
+ setCommonServiceParams(c, srvMeta);
+ c.setInterface(provides, adapterProperties);
+
+ String factoryMethod = srvMeta.getString(Params.factoryMethod, null);
+ if (factoryMethod == null)
+ {
+ c.setImplementation(adapterImplClass);
+ }
+ else
+ {
+ c.setFactory(adapterImplClass, factoryMethod);
+ }
+ c.setComposition(srvMeta.getString(Params.composition, null));
+ ServiceLifecycleHandler lfcleHandler = new ServiceLifecycleHandler(c, b, dm, srvMeta, depsMeta);
+ // The dependencies will be plugged by our lifecycle handler.
+ c.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
+ // Adds dependencies (except named dependencies, which are managed by the lifecycle handler).
+ addUnamedDependencies(b, dm, c, srvMeta, depsMeta);
+ dm.add(c);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/AspectServiceBuilder.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/AspectServiceBuilder.java
new file mode 100644
index 0000000..46273ef
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/AspectServiceBuilder.java
@@ -0,0 +1,110 @@
+/*
+ * 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.runtime;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+
+/**
+ * Class used to build an aspect service using metadata found from DependencyManager runtime
+ * meta-inf descriptor.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectServiceBuilder extends AbstractBuilder
+{
+ private final static String TYPE = "AspectService";
+
+ @Override
+ public String getType()
+ {
+ return TYPE;
+ }
+
+ @Override
+ public void build(MetaData srvMeta, List<MetaData> depsMeta, Bundle b, DependencyManager dm)
+ throws Exception
+ {
+ Log.instance().info("AspectServiceBuilder: building aspect service: %s with dependencies %s",
+ srvMeta,
+ depsMeta);
+
+ Class<?> serviceInterface = b.loadClass(srvMeta.getString(Params.service));
+ String serviceFilter = srvMeta.getString(Params.filter, null);
+ Dictionary<String, Object> aspectProperties = srvMeta.getDictionary(Params.properties, null);
+ int ranking = srvMeta.getInt(Params.ranking, 1);
+ String implClassName = srvMeta.getString(Params.impl);
+ Object implClass = b.loadClass(implClassName);
+ String field = srvMeta.getString(Params.field, null);
+ String added = srvMeta.getString(Params.added, null);
+ String changed = srvMeta.getString(Params.changed, null);
+ String removed = srvMeta.getString(Params.removed, null);
+ String swap = srvMeta.getString(Params.swap, null);
+
+ if (field != null && (added != null || changed != null || removed != null || swap != null))
+ {
+ throw new IllegalArgumentException("autoconfig field " + field + " can't be defined with both added/changed/removed/swap calllbacks");
+ }
+
+ Component c;
+ if (field != null)
+ {
+ c = dm.createAspectService(serviceInterface, serviceFilter, ranking, field)
+ .setServiceProperties(aspectProperties);
+ }
+ else
+ {
+ if (added != null || changed != null || removed != null || swap != null)
+ {
+ c = dm.createAspectService(serviceInterface, serviceFilter, ranking, added, changed, removed, swap)
+ .setServiceProperties(aspectProperties);
+ }
+ else
+ {
+ c = dm.createAspectService(serviceInterface, serviceFilter, ranking)
+ .setServiceProperties(aspectProperties);
+ }
+
+ }
+
+ setCommonServiceParams(c, srvMeta);
+ String factoryMethod = srvMeta.getString(Params.factoryMethod, null);
+ if (factoryMethod == null)
+ {
+ c.setImplementation(implClass);
+ }
+ else
+ {
+ c.setFactory(implClass, factoryMethod);
+ }
+
+ c.setComposition(srvMeta.getString(Params.composition, null));
+ ServiceLifecycleHandler lfcleHandler = new ServiceLifecycleHandler(c, b, dm, srvMeta, depsMeta);
+ // The dependencies will be plugged by our lifecycle handler.
+ c.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
+ // Adds dependencies (except named dependencies, which are managed by the lifecycle
+ // handler).
+ addUnamedDependencies(b, dm, c, srvMeta, depsMeta);
+ dm.add(c);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/BundleAdapterServiceBuilder.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/BundleAdapterServiceBuilder.java
new file mode 100644
index 0000000..94f8e62
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/BundleAdapterServiceBuilder.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+
+/**
+ * Class used to build a bundle adapter service using metadata found from DependencyManager runtime
+ * meta-inf descriptor.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class BundleAdapterServiceBuilder extends AbstractBuilder
+{
+ private final static String TYPE = "BundleAdapterService";
+
+ @Override
+ public String getType()
+ {
+ return TYPE;
+ }
+
+ @Override
+ public void build(MetaData srvMeta, List<MetaData> depsMeta, Bundle b, DependencyManager dm)
+ throws Exception
+ {
+ int stateMask = srvMeta.getInt(Params.stateMask, Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE);
+ String filter = srvMeta.getString(Params.filter, null);
+ Class<?> adapterImplClass = b.loadClass(srvMeta.getString(Params.impl));
+ String[] provides = srvMeta.getStrings(Params.provides, null);
+ Dictionary<String, Object> properties = srvMeta.getDictionary(Params.properties, null);
+ boolean propagate = "true".equals(srvMeta.getString(Params.propagate, "false"));
+ Component c = dm.createBundleAdapterService(stateMask, filter, propagate);
+ c.setInterface(provides, properties);
+ String factoryMethod = srvMeta.getString(Params.factoryMethod, null);
+ if (factoryMethod == null)
+ {
+ c.setImplementation(adapterImplClass);
+ }
+ else
+ {
+ c.setFactory(adapterImplClass, factoryMethod);
+ }
+
+ setCommonServiceParams(c, srvMeta);
+ c.setComposition(srvMeta.getString(Params.composition, null));
+ ServiceLifecycleHandler lfcleHandler = new ServiceLifecycleHandler(c, b, dm, srvMeta, depsMeta);
+ // The dependencies will be plugged by our lifecycle handler.
+ c.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
+ // Adds dependencies (except named dependencies, which are managed by the lifecycle handler).
+ addUnamedDependencies(b, dm, c, srvMeta, depsMeta);
+ dm.add(c);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ComponentBuilder.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ComponentBuilder.java
new file mode 100644
index 0000000..b9ec8be
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ComponentBuilder.java
@@ -0,0 +1,127 @@
+/*
+ * 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.runtime;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.runtime.api.ComponentFactory;
+import org.osgi.framework.Bundle;
+
+/**
+ * Builds a DependencyManager Component.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ComponentBuilder extends AbstractBuilder
+{
+ private final static String TYPE = "Component";
+ public final static String FACTORY_NAME = "dm.factory.name";
+ public final static String FACTORY_INSTANCE = "dm.factory.instance";
+
+ @Override
+ public String getType()
+ {
+ return TYPE;
+ }
+
+ @Override
+ public void build(MetaData srvMeta, List<MetaData> depsMeta, Bundle b, DependencyManager dm)
+ throws Exception
+ {
+ Component c = dm.createComponent();
+ String factory = srvMeta.getString(Params.factorySet, null);
+ String factoryName = srvMeta.getString(Params.factoryName, null);
+
+ // Setup Component auto config fields
+ setCommonServiceParams(c, srvMeta);
+
+ // Check if we must provide a Component factory set (deprecated), or a ComponentFactory.
+ if (factory == null && factoryName == null)
+ {
+ Log.instance().info("ComponentBuilder: building service %s with dependencies %s",
+ srvMeta,
+ depsMeta);
+
+ String impl = srvMeta.getString(Params.impl);
+ String composition = srvMeta.getString(Params.composition, null);
+ String factoryMethod = srvMeta.getString(Params.factoryMethod, null);
+ if (factoryMethod == null)
+ {
+ c.setImplementation(b.loadClass(impl));
+ }
+ else
+ {
+ c.setFactory(b.loadClass(impl), factoryMethod);
+ }
+ c.setComposition(composition);
+
+ // Adds dependencies (except named dependencies, which are managed by the lifecycle
+ // handler).
+ addUnamedDependencies(b, dm, c, srvMeta, depsMeta);
+ // Creates a ServiceHandler, which will filter all service lifecycle callbacks.
+ ServiceLifecycleHandler lfcleHandler =
+ new ServiceLifecycleHandler(c, b, dm, srvMeta, depsMeta);
+ c.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
+
+ // Set the provided services
+ Dictionary<String, Object> properties = srvMeta.getDictionary(Params.properties, null);
+ String[] services = srvMeta.getStrings(Params.provides, null);
+ c.setInterface(services, properties);
+ }
+ else if (factory != null) /* deprecated */
+ {
+ Log.instance()
+ .info("ComponentBuilder: providing factory set for service %s with dependencies %s",
+ srvMeta,
+ depsMeta);
+
+ // We don't instantiate the service, but instead we provide a Set in the registry.
+ // This Set will act as a factory and another component may registers some
+ // service configurations into it in order to fire some service instantiations.
+ FactorySet factorySet = new FactorySet(b, srvMeta, depsMeta);
+ c.setImplementation(factorySet);
+ c.setCallbacks(null, "start", "stop", null);
+ Hashtable<String, String> props = new Hashtable<String, String>();
+ props.put(ComponentBuilder.FACTORY_NAME, factory);
+ c.setInterface(Set.class.getName(), props);
+ }
+ else if (factoryName != null) {
+ Log.instance()
+ .info("ComponentBuilder: providing component factory for service %s with dependencies %s",
+ srvMeta,
+ depsMeta);
+
+ // We don't instantiate the service, but instead we provide a ComponentFactory in the registry.
+ // (similar to DS ComponentFactory).
+ ComponentFactoryImpl compFactory = new ComponentFactoryImpl(b, srvMeta, depsMeta);
+ c.setImplementation(compFactory);
+ c.setCallbacks(null, "start", "stop", null);
+ Hashtable<String, String> props = new Hashtable<String, String>();
+ props.put(ComponentBuilder.FACTORY_NAME, factoryName);
+ c.setInterface(ComponentFactory.class.getName(), props);
+ }
+
+ dm.add(c);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ComponentFactoryImpl.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ComponentFactoryImpl.java
new file mode 100644
index 0000000..3a15ded
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ComponentFactoryImpl.java
@@ -0,0 +1,121 @@
+/*
+ * 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.runtime;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.runtime.api.ComponentException;
+import org.apache.felix.dm.runtime.api.ComponentFactory;
+import org.apache.felix.dm.runtime.api.ComponentInstance;
+import org.osgi.framework.Bundle;
+
+/**
+ * This class implements a DM Component factory service.
+ * When a <code>Component</annotation> contains a <code>factoryName</code> attribute, this class is provided
+ * into the OSGi registry with a <code>org.apache.felix.dependencymanager.factory.name</code> service property.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ComponentFactoryImpl implements ComponentFactory {
+ /**
+ * The list of Dependencies which are applied in the Service.
+ */
+ private MetaData m_srvMeta;
+
+ /**
+ * The list of Dependencies which are applied in the Service.
+ */
+ private List<MetaData> m_depsMeta;
+
+ /**
+ * The DependencyManager which is used to create Service instances.
+ */
+ private DependencyManager m_dm;
+
+ /**
+ * Flag used to check if our service is Active.
+ */
+ private volatile boolean m_active;
+
+ /**
+ * The bundle containing the Service annotated with the factory attribute.
+ */
+ private final Bundle m_bundle;
+
+ /**
+ * Sole constructor.
+ * @param b the bundle containing the Service annotated with the factory attribute
+ * @param srvMeta the component service metadata
+ * @param depsMeta teh component dependencies metadata
+ */
+ public ComponentFactoryImpl(Bundle b, MetaData srvMeta, List<MetaData> depsMeta) {
+ m_bundle = b;
+ m_srvMeta = srvMeta;
+ m_depsMeta = depsMeta;
+ }
+
+ /**
+ * Our Service is starting.
+ */
+ public void start(Component c) {
+ m_active = true;
+ m_dm = c.getDependencyManager();
+ }
+
+ /**
+ * Our Service is stopping.
+ */
+ public void stop() {
+ m_active = false;
+ }
+
+ /**
+ * Create or Update a Service.
+ */
+ public ComponentInstance newInstance(Dictionary<String, ?> conf) {
+ // Check parameter validity
+ if (conf == null) {
+ throw new NullPointerException("configuration parameter can't be null");
+ }
+
+ // Check if our service is running.
+ checkServiceAvailable();
+
+ try {
+ ComponentInstanceImpl instance = new ComponentInstanceImpl(m_dm,m_bundle, m_srvMeta, m_depsMeta, conf);
+ return instance;
+
+ } catch (Throwable t) {
+ Log.instance().error("ServiceFactory: could not instantiate service %s", t, m_srvMeta);
+ throw new ComponentException("could not instantiate factory component", t);
+ }
+ }
+
+ /**
+ * Checks if our Service is available (we are not stopped").
+ */
+ private void checkServiceAvailable() {
+ if (!m_active) {
+ throw new IllegalStateException("Service not available");
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ComponentInstanceImpl.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ComponentInstanceImpl.java
new file mode 100644
index 0000000..90c0b1f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ComponentInstanceImpl.java
@@ -0,0 +1,207 @@
+/*
+ * 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.runtime;
+
+import java.lang.reflect.Method;
+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.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.runtime.api.ComponentInstance;
+import org.osgi.framework.Bundle;
+
+/**
+ * Implementation for our DM Runtime ComponentInstance.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ComponentInstanceImpl implements ComponentInstance {
+ /**
+ * The list of Dependencies which are applied in the Service.
+ */
+ private final MetaData m_srvMeta;
+
+ /**
+ * The list of Dependencies which are applied in the Service.
+ */
+ private final List<MetaData> m_depsMeta;
+
+ /**
+ * The DependencyManager which is used to create Service instances.
+ */
+ private final DependencyManager m_dm;
+
+ /**
+ * The bundle containing the Service annotated with the factory attribute.
+ */
+ private final Bundle m_bundle;
+
+ /**
+ * The component
+ */
+ private final Object m_impl;
+
+ /**
+ * The DM Component used to define the component
+ */
+ private final Component m_component;
+
+ public ComponentInstanceImpl(DependencyManager dm, Bundle b, MetaData srvMeta, List<MetaData> depsMeta, Dictionary<String, ?> conf) throws Exception
+ {
+ m_bundle = b;
+ m_dm = dm;
+ m_srvMeta = srvMeta;
+ m_depsMeta = depsMeta;
+ m_component = m_dm.createComponent();
+
+ Class<?> implClass = m_bundle.loadClass(m_srvMeta.getString(Params.impl));
+ Object impl = conf.get(ComponentBuilder.FACTORY_INSTANCE);
+ if (impl == null) {
+ String factoryMethod = m_srvMeta.getString(Params.factoryMethod, null);
+ if (factoryMethod == null) {
+ impl = implClass.newInstance();
+ } else {
+ Method m = implClass.getDeclaredMethod(factoryMethod);
+ m.setAccessible(true);
+ impl = m.invoke(null);
+ }
+ }
+ m_impl = impl;
+
+ // Invoke "configure" callback
+ String configure = m_srvMeta.getString(Params.factoryConfigure, null);
+
+ if (configure != null) {
+ invokeConfigure(impl, configure, conf);
+ }
+
+ // Create Service
+ m_component.setImplementation(impl);
+ String[] provides = m_srvMeta.getStrings(Params.provides, null);
+ if (provides != null) {
+ // Merge service properties with the configuration provided by the factory.
+ Dictionary<String, ?> serviceProperties = m_srvMeta.getDictionary(Params.properties, null);
+ serviceProperties = mergeSettings(serviceProperties, conf);
+ m_component.setInterface(provides, serviceProperties);
+ }
+
+ m_component.setComposition(m_srvMeta.getString(Params.composition, null));
+ ServiceLifecycleHandler lfcleHandler = new ServiceLifecycleHandler(m_component, m_bundle, m_dm, m_srvMeta, m_depsMeta);
+ // The dependencies will be plugged by our lifecycle handler.
+ m_component.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
+
+ // Adds dependencies (except named dependencies, which are managed by the lifecycle handler).
+ for (MetaData dependency : m_depsMeta) {
+ String name = dependency.getString(Params.name, null);
+ if (name == null) {
+ DependencyBuilder depBuilder = new DependencyBuilder(dependency);
+ Log.instance().info("ServiceLifecycleHandler.init: adding dependency %s into service %s", dependency,
+ m_srvMeta);
+ Dependency d = depBuilder.build(m_bundle, m_dm);
+ m_component.add(d);
+ }
+ }
+
+ // Register the Service instance, and keep track of it.
+ Log.instance().info("ServiceFactory: created service %s", m_srvMeta);
+ m_dm.add(m_component);
+ }
+
+ @Override
+ public void dispose() {
+ m_dm.remove(m_component);
+ }
+
+ @Override
+ public void update(Dictionary<String, ?> conf) {
+ // Reconfigure an already existing Service.
+ String configure = m_srvMeta.getString(Params.factoryConfigure, null);
+ if (configure != null) {
+ Log.instance().info("ServiceFactory: updating service %s", m_impl);
+ invokeConfigure(m_impl, configure, conf);
+ }
+
+ // Update service properties
+ String[] provides = m_srvMeta.getStrings(Params.provides, null);
+ if (provides != null) {
+ Dictionary<String, ?> serviceProperties = m_srvMeta.getDictionary(Params.properties, null);
+ serviceProperties = mergeSettings(serviceProperties, conf);
+ m_component.setServiceProperties(serviceProperties);
+ }
+ }
+
+ /**
+ * Invokes the configure callback method on the service instance implemenatation.
+ * @param impl
+ * @param configure
+ * @param config
+ */
+ private void invokeConfigure(Object impl, String configure, Dictionary<String, ?> config) {
+ try {
+ InvocationUtil.invokeCallbackMethod(impl, configure, new Class[][] { { Dictionary.class } },
+ new Object[][] { { config } });
+ }
+
+ catch (Throwable t) {
+ if (t instanceof RuntimeException) {
+ throw (RuntimeException) t;
+ } else {
+ throw new RuntimeException("Could not invoke method " + configure + " on object " + impl, t);
+ }
+ }
+ }
+
+ /**
+ * Merge factory configuration settings with the service properties. The private factory configuration
+ * settings are ignored. A factory configuration property is private if its name starts with a dot (".").
+ *
+ * @param serviceProperties
+ * @param factoryConfiguration
+ * @return
+ */
+ private Dictionary<String, Object> mergeSettings(Dictionary<String, ?> serviceProperties,
+ Dictionary<String, ?> factoryConfiguration)
+ {
+ Dictionary<String, Object> props = new Hashtable<>();
+
+ if (serviceProperties != null) {
+ Enumeration<String> keys = serviceProperties.keys();
+ while (keys.hasMoreElements()) {
+ String key = keys.nextElement();
+ Object val = serviceProperties.get(key);
+ props.put(key, val);
+ }
+ }
+
+ Enumeration<String> keys = factoryConfiguration.keys();
+ while (keys.hasMoreElements()) {
+ String key = keys.nextElement();
+ if (!key.toString().startsWith(".")) {
+ // public properties are propagated
+ Object val = factoryConfiguration.get(key);
+ props.put(key, val);
+ }
+ }
+ return props;
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DependencyBuilder.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DependencyBuilder.java
new file mode 100644
index 0000000..e25d441
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DependencyBuilder.java
@@ -0,0 +1,237 @@
+/*
+ * 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.runtime;
+
+import org.apache.felix.dm.BundleDependency;
+import org.apache.felix.dm.ConfigurationDependency;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ResourceDependency;
+import org.apache.felix.dm.ServiceDependency;
+import org.osgi.framework.Bundle;
+
+/**
+ * Class used to build a concrete dependency from meta data.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DependencyBuilder
+{
+ public enum DependencyType
+ {
+ ServiceDependency,
+ TemporalServiceDependency,
+ ConfigurationDependency,
+ BundleDependency,
+ ResourceDependency
+ }
+
+ private MetaData m_metaData;
+
+ public DependencyBuilder(MetaData dependencyMetaData)
+ {
+ m_metaData = dependencyMetaData;
+ }
+
+ public Dependency build(Bundle b, DependencyManager dm)
+ throws Exception
+ {
+ Dependency dp = null;
+ DependencyType type;
+
+ try
+ {
+ type = DependencyType.valueOf(m_metaData.getString(Params.type));
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new IllegalArgumentException("no \"type\" parameter found from metaData: " + m_metaData);
+ }
+
+ switch (type)
+ {
+ case ServiceDependency:
+ dp = createServiceDependency(b, dm);
+ break;
+
+ case ConfigurationDependency:
+ dp = createConfigurationDependency(dm);
+ break;
+
+ case BundleDependency:
+ dp = createBundleDependency(dm);
+ break;
+
+ case ResourceDependency:
+ dp = createResourceDependency(dm);
+ break;
+
+ default:
+ throw new IllegalArgumentException("Can't build service dependency: " + type);
+ }
+ return dp;
+ }
+
+ private Dependency createServiceDependency(Bundle b, DependencyManager dm)
+ throws ClassNotFoundException
+ {
+ String service = m_metaData.getString(Params.service);
+ Class<?> serviceClass = b.loadClass(service);
+ String serviceFilter = m_metaData.getString(Params.filter, null);
+ String defaultServiceImpl = m_metaData.getString(Params.defaultImpl, null);
+ Class<?> defaultServiceImplClass =
+ (defaultServiceImpl != null) ? b.loadClass(defaultServiceImpl) : null;
+ String added = m_metaData.getString(Params.added, null);
+ long timeout = m_metaData.getLong(Params.timeout, -1L);
+ String changed = timeout != -1 ? null : m_metaData.getString(Params.changed, null);
+ String removed = timeout != -1 ? null : m_metaData.getString(Params.removed, null);
+ String autoConfigField = m_metaData.getString(Params.autoConfig, null);
+ boolean required = "true".equals(m_metaData.getString(Params.required, "true"));
+ boolean propagate = "true".equals(m_metaData.getString(Params.propagate, "false"));
+
+ Dependency dp = createServiceDependency(dm, serviceClass,
+ serviceFilter, defaultServiceImplClass, added, changed,
+ removed, autoConfigField, timeout, required, propagate);
+ return dp;
+ }
+
+ private Dependency createServiceDependency(DependencyManager dm, Class<?> serviceClass,
+ String serviceFilter, Class<?> defaultServiceImplClass, String added,
+ String changed, String removed, String autoConfigField, long timeout, boolean required,
+ boolean propagate)
+ {
+ ServiceDependency sd = timeout != -1 ? dm.createTemporalServiceDependency(timeout)
+ : dm.createServiceDependency();
+ sd.setService(serviceClass, serviceFilter);
+ if (defaultServiceImplClass != null)
+ {
+ sd.setDefaultImplementation(defaultServiceImplClass);
+ }
+ sd.setCallbacks(added, changed, removed);
+ if (autoConfigField != null)
+ {
+ sd.setAutoConfig(autoConfigField);
+ }
+ if (timeout == -1)
+ {
+ sd.setRequired(required);
+ }
+
+ sd.setPropagate(propagate);
+ return sd;
+ }
+
+ private Dependency createConfigurationDependency(DependencyManager dm)
+ {
+ String pid = m_metaData.getString(Params.pid);
+ boolean propagate = "true".equals(m_metaData.getString(Params.propagate, "false"));
+ String callback = m_metaData.getString(Params.updated, "updated");
+ Dependency dp = createConfigurationDependency(dm, pid, callback, propagate);
+ return dp;
+ }
+
+ private Dependency createConfigurationDependency(DependencyManager dm, String pid, String callback,
+ boolean propagate)
+ {
+ if (pid == null)
+ {
+ throw new IllegalArgumentException(
+ "pid attribute not provided in ConfigurationDependency declaration");
+ }
+ ConfigurationDependency cd = dm.createConfigurationDependency();
+ cd.setPid(pid);
+ cd.setCallback(callback);
+ cd.setPropagate(propagate);
+ return cd;
+ }
+
+ /**
+ * Creates a BundleDependency that we parsed from a component descriptor entry.
+ * @param b
+ * @param dm
+ * @param parser
+ * @return
+ */
+ private Dependency createBundleDependency(DependencyManager dm)
+ {
+ String added = m_metaData.getString(Params.added, null);
+ String changed = m_metaData.getString(Params.changed, null);
+ String removed = m_metaData.getString(Params.removed, null);
+ boolean required = "true".equals(m_metaData.getString(Params.required, "true"));
+ String filter = m_metaData.getString(Params.filter, null);
+ int stateMask = m_metaData.getInt(Params.stateMask, -1);
+ boolean propagate = "true".equals(m_metaData.getString(Params.propagate, "false"));
+
+ Dependency dp = createBundleDependency(dm, added, changed, removed, required, propagate, filter,
+ stateMask);
+ return dp;
+ }
+
+ private Dependency createBundleDependency(DependencyManager dm, String added, String changed,
+ String removed, boolean required, boolean propagate, String filter, int stateMask)
+ {
+ BundleDependency bd = dm.createBundleDependency();
+ bd.setCallbacks(added, changed, removed);
+ bd.setRequired(required);
+ bd.setPropagate(propagate);
+ if (filter != null)
+ {
+ bd.setFilter(filter);
+ }
+ if (stateMask != -1)
+ {
+ bd.setStateMask(stateMask);
+ }
+ return bd;
+ }
+
+ private Dependency createResourceDependency(DependencyManager dm)
+ {
+ String added = m_metaData.getString(Params.added, null);
+ String changed = m_metaData.getString(Params.changed, null);
+ String removed = m_metaData.getString(Params.removed, null);
+ String filter = m_metaData.getString(Params.filter, null);
+ boolean required = "true".equals(m_metaData.getString(Params.required, "true"));
+ boolean propagate = "true".equals(m_metaData.getString(Params.propagate, "false"));
+ String autoConfigField = m_metaData.getString(Params.autoConfig, null);
+
+ Dependency dp = createResourceDependency(dm, added, changed, removed, required, filter,
+ propagate, autoConfigField);
+ return dp;
+ }
+
+ private Dependency createResourceDependency(DependencyManager dm, String added,
+ String changed, String removed, boolean required, String filter, boolean propagate,
+ String autoConfigField)
+ {
+ ResourceDependency rd = dm.createResourceDependency();
+ rd.setCallbacks(added, changed, removed);
+ rd.setRequired(required);
+ if (filter != null)
+ {
+ rd.setFilter(filter);
+ }
+ if (autoConfigField != null)
+ {
+ rd.setAutoConfig(autoConfigField);
+ }
+ rd.setPropagate(propagate);
+ return rd;
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DependencyManagerRuntime.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DependencyManagerRuntime.java
new file mode 100644
index 0000000..b6994f8
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DependencyManagerRuntime.java
@@ -0,0 +1,227 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+/**
+ * This class parses service descriptors generated by the annotation bnd processor.
+ * The descriptors are located under META-INF/dependencymanager directory. Such files are actually
+ * referenced by a specific "DependendencyManager-Component" manifest header.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DependencyManagerRuntime
+{
+ /**
+ * Map between bundles and their corresponding DependencyManager objects used to create bundle's components.
+ * Notice that we can safely use this map without synchronization because we are relying on the new DM4 thread
+ * model which serialize all component events safely.
+ */
+ private final Map<Bundle, DependencyManager> m_managers = new HashMap<Bundle, DependencyManager>();
+
+ /**
+ * Parser used to scan component descriptors defined in bundles meta data.
+ */
+ private final DescriptorParser m_parser;
+
+ /**
+ * We use the PackageAdmin service to allow support for annotations in fragment bundles.
+ */
+ private volatile PackageAdmin m_packageAdmin;
+
+ /**
+ * Our constructor. We'll initialize here our DM component builders.
+ */
+ public DependencyManagerRuntime()
+ {
+ // Instantiates our descriptor parser, and register our service builders into it.
+ m_parser = new DescriptorParser();
+ m_parser.addBuilder(new ComponentBuilder());
+ m_parser.addBuilder(new AspectServiceBuilder());
+ m_parser.addBuilder(new AdapterServiceBuilder());
+ m_parser.addBuilder(new BundleAdapterServiceBuilder());
+ m_parser.addBuilder(new FactoryConfigurationAdapterServiceBuilder());
+ m_parser.addBuilder(new ResourceAdapterServiceBuilder());
+ }
+
+ /**
+ * Return our Object Composition (the Activator will inject dependencies into it)
+ */
+ protected Object[] getComposition()
+ {
+ return new Object[] { this, Log.instance() };
+ }
+
+ /**
+ * Starts our Service (at this point, we have been injected with our bundle context, as well
+ * as with our log service. We'll listen to bundle start/stop events (we implement the
+ * SynchronousBundleListener interface).
+ */
+ protected void start()
+ {
+ Log.instance().info("Starting Dependency Manager annotation runtime");
+ }
+
+ /**
+ * Stops our service. We'll stop all activated DependencyManager services.
+ */
+ protected void stop()
+ {
+ Log.instance().info("Runtime: stopping services");
+ for (DependencyManager dm : m_managers.values())
+ {
+ List<Component> services = new ArrayList<Component>(dm.getComponents());
+ for (Component service : services)
+ {
+ dm.remove(service);
+ }
+ }
+
+ m_managers.clear();
+ }
+
+ /**
+ * Load the DM descriptors from the started bundle. We also check possible fragments
+ * attached to the bundle, which might also contain some DM descriptors.
+ * @param bundle the started bundle which contains a DependencyManager-Component header
+ */
+ protected void bundleStarted(Bundle bundle)
+ {
+ Log.instance().info("Scanning started bundle %s", bundle.getSymbolicName());
+ List<URL> descriptorURLs = new ArrayList<URL>();
+ collectDescriptors(bundle, descriptorURLs);
+ Bundle[] fragments = m_packageAdmin.getFragments(bundle);
+ if (fragments != null)
+ {
+ for (Bundle fragment : fragments)
+ {
+ collectDescriptors(fragment, descriptorURLs);
+ }
+ }
+ for (URL descriptorURL : descriptorURLs)
+ {
+ loadDescriptor(bundle, descriptorURL);
+ }
+ }
+
+ /**
+ * Unregisters all services for a stopping bundle.
+ * @param b
+ */
+ protected void bundleStopped(Bundle b)
+ {
+ Log.instance().info("Runtime: Removing services from stopping bundle: %s", b.getSymbolicName());
+ DependencyManager dm = m_managers.remove(b);
+ if (dm != null)
+ {
+ List<Component> services = new ArrayList<Component>(dm.getComponents());
+ for (Component service : services)
+ {
+ Log.instance().info("Runtime: Removing service: %s", service);
+ dm.remove(service);
+ }
+ }
+ }
+
+ /**
+ * Collect all descriptors found from a given bundle, including its possible attached fragments.
+ * @param bundle a started bundle containing some DM descriptors
+ * @param out the list of descriptors' URLS found from the started bundle, as well as from possibly
+ * attached fragments.
+ */
+ private void collectDescriptors(Bundle bundle, List<URL> out) {
+ String descriptorPaths = (String) bundle.getHeaders().get("DependencyManager-Component");
+ if (descriptorPaths == null)
+ {
+ return;
+ }
+
+ for (String descriptorPath : descriptorPaths.split(","))
+ {
+ URL descriptorURL = bundle.getEntry(descriptorPath);
+ if (descriptorURL == null)
+ {
+ Log.instance()
+ .error("Runtime: " + "DependencyManager component descriptor not found: %s",
+ descriptorPath);
+ continue;
+ }
+ out.add(descriptorURL);
+ }
+ }
+
+ /**
+ * Load a DependencyManager component descriptor from a given bundle.
+ * @param b
+ * @param descriptorURL
+ */
+ private void loadDescriptor(Bundle b, URL descriptorURL)
+ {
+ Log.instance().debug("Parsing descriptor %s from bundle %s", descriptorURL, b.getSymbolicName());
+
+ BufferedReader in = null;
+ try
+ {
+ in = new BufferedReader(new InputStreamReader(descriptorURL.openStream()));
+ DependencyManager dm = m_managers.get(b);
+ if (dm == null)
+ {
+ dm = new DependencyManager(b.getBundleContext());
+ m_managers.put(b, dm);
+ }
+
+ m_parser.parse(in, b, dm);
+ }
+
+ catch (Throwable t)
+ {
+ Log.instance().error("Runtime: Error while parsing descriptor %s from bundle %s",
+ t,
+ descriptorURL,
+ b.getSymbolicName());
+ }
+
+ finally
+ {
+ if (in != null)
+ {
+ try
+ {
+ in.close();
+ }
+ catch (IOException ignored)
+ {
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DescriptorParser.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DescriptorParser.java
new file mode 100644
index 0000000..2c162b1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DescriptorParser.java
@@ -0,0 +1,83 @@
+/*
+ * 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.runtime;
+
+import java.io.BufferedReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.dm.DependencyManager;
+import org.json.JSONObject;
+import org.osgi.framework.Bundle;
+
+/**
+ * This class parses files generated in META-INF/*.dm by the DependencyManager bnd plugin.
+ * Each descriptor contains a JSON definition of a Service, along with its corresponding
+ * dependencies.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DescriptorParser
+{
+ private Map<String, AbstractBuilder> m_builders = new HashMap<String, AbstractBuilder>();
+
+ public void addBuilder(AbstractBuilder sb)
+ {
+ m_builders.put(sb.getType(), sb);
+ }
+
+ public void parse(BufferedReader reader, Bundle b, DependencyManager dm) throws Exception
+ {
+ String line;
+
+ // The first line is a Service Component (a Service, an Aspect Service, etc ...)
+ line = reader.readLine();
+ Log.instance().debug("DescriptorParser: parsing service %s", line);
+ JSONObject json = new JSONObject(line);
+ JSONMetaData serviceMetaData = new JSONMetaData(json);
+
+ String type = (String) json.get("type");
+ if (type == null)
+ {
+ throw new IllegalArgumentException("Invalid descriptor"
+ + ": no \"type\" parameter found in first line");
+ }
+
+ AbstractBuilder builder = m_builders.get(type);
+ if (builder == null)
+ {
+ throw new IllegalArgumentException("Invalid descriptor"
+ + ": invalid \"type\" parameter found in first line");
+ }
+
+ // Parse the rest of the lines (dependencies)
+ List<MetaData> serviceDependencies = new ArrayList<MetaData>();
+ while ((line = reader.readLine()) != null)
+ {
+ Log.instance().debug("Parsing dependency %s", line);
+ JSONObject dep = new JSONObject(line);
+ serviceDependencies.add(new JSONMetaData(dep));
+ }
+
+ // and Invoke the builder
+ builder.build(serviceMetaData, serviceDependencies, b, dm);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/FactoryConfigurationAdapterServiceBuilder.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/FactoryConfigurationAdapterServiceBuilder.java
new file mode 100644
index 0000000..0f4eb09
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/FactoryConfigurationAdapterServiceBuilder.java
@@ -0,0 +1,74 @@
+/*
+ * 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.runtime;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+
+/**
+ * Class used to build a factory configuration adapter service using metadata found from DependencyManager runtime
+ * meta-inf descriptor.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FactoryConfigurationAdapterServiceBuilder extends AbstractBuilder
+{
+ private final static String TYPE = "FactoryConfigurationAdapterService";
+
+ @Override
+ public String getType()
+ {
+ return TYPE;
+ }
+
+ @Override
+ public void build(MetaData srvMeta, List<MetaData> depsMeta, Bundle b, DependencyManager dm)
+ throws Exception
+ {
+ Class<?> implClass = b.loadClass(srvMeta.getString(Params.impl));
+ String factoryPid = srvMeta.getString(Params.factoryPid);
+ String updated = srvMeta.getString(Params.updated);
+ String[] provides = srvMeta.getStrings(Params.provides, null);
+ Dictionary<String, Object> properties = srvMeta.getDictionary(Params.properties, null);
+ boolean propagate = "true".equals(srvMeta.getString(Params.propagate, "false"));
+ Component c = dm.createFactoryConfigurationAdapterService(factoryPid, updated, propagate);
+ c.setInterface(provides, properties);
+ String factoryMethod = srvMeta.getString(Params.factoryMethod, null);
+ if (factoryMethod == null)
+ {
+ c.setImplementation(implClass);
+ }
+ else
+ {
+ c.setFactory(implClass, factoryMethod);
+ }
+ setCommonServiceParams(c, srvMeta);
+ c.setComposition(srvMeta.getString(Params.composition, null));
+ ServiceLifecycleHandler lfcleHandler = new ServiceLifecycleHandler(c, b, dm, srvMeta, depsMeta);
+ // The dependencies will be plugged by our lifecycle handler.
+ c.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
+ // Adds dependencies (except named dependencies, which are managed by the lifecycle handler).
+ addUnamedDependencies(b, dm, c, srvMeta, depsMeta);
+ dm.add(c);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/FactorySet.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/FactorySet.java
new file mode 100644
index 0000000..6d042f3
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/FactorySet.java
@@ -0,0 +1,528 @@
+/*
+ * 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.runtime;
+
+import java.lang.reflect.Method;
+import java.util.AbstractSet;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+
+/**
+ * This class implements a <code>java.util.Set</code> which acts as a service factory.
+ * When a <code>Service</annotation> contains a <code>factory</code> attribute, this class is provided
+ * into the OSGi registry with a <code>dm.factory.name</code> service property. So, another factory component
+ * may be injected with this Set. And each time a Dictionary configuration is registered in the Set,
+ * then a new Service instance will be instantiated, and will be provided with the Dictionary passed to the
+ * Service instance.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings( { "unchecked", "rawtypes"})
+public class FactorySet extends AbstractSet<Dictionary>
+{
+ /**
+ * When a Dictionary is registered in a factory Set, we use this special
+ * property key, whose value may provide the instance to use when
+ * creating a service.
+ */
+ private final static String DM_FACTORY_INSTANCE = "dm.factory.instance";
+
+ /**
+ * The actual Service instance that is allocated for each dictionaries added in this Set.
+ */
+ private volatile Object m_impl;
+
+ /**
+ * The Service provided in the OSGi registry.
+ */
+ private final String[] m_provide;
+
+ /**
+ * The properties to be provided by the Service.
+ */
+ private final Dictionary m_serviceProperties;
+
+ /**
+ * The configure Service callback used to pass configuration added in this Set.
+ */
+ private final String m_configure;
+
+ /**
+ * The map between our Dictionaries and corresponding Service instances.
+ * This map is modified from our serial executor, or from the add method.
+ */
+ private final ConcurrentHashMap<ServiceKey, Object> m_services = new ConcurrentHashMap<ServiceKey, Object>();
+
+ /**
+ * The list of Dependencies which are applied in the Service.
+ */
+ private final MetaData m_srvMeta;
+
+ /**
+ * The list of Dependencies which are applied in the Service.
+ */
+ private final List<MetaData> m_depsMeta;
+
+ /**
+ * The DependencyManager which is used to create Service instances.
+ */
+ private volatile DependencyManager m_dm;
+
+ /**
+ * This class is used to serialize concurrent method calls, and allow to leave our methods unsynchronized.
+ * This is required because some of our methods may invoke some Service callbacks, which must be called outside
+ * synchronized block (in order to avoid potential dead locks).
+ */
+ private final SerialExecutor m_serialExecutor = new SerialExecutor();
+
+ /**
+ * Flag used to check if our service is Active.
+ */
+ private volatile boolean m_active;
+
+ /**
+ * The bundle containing the Service annotated with the factory attribute.
+ */
+ private final Bundle m_bundle;
+
+ /**
+ * Flag used to check if a service is being created
+ */
+ private final static Object SERVICE_CREATING = new Object();
+
+ /**
+ * This class wraps <tt>Dictionary</tt>, allowing to store the dictionary into a Map, using
+ * reference-equality in place of object-equality when getting the Dictionary from the Map.
+ */
+ private static class ServiceKey
+ {
+ private Dictionary m_dictionary;
+
+ public ServiceKey(Dictionary dictionary)
+ {
+ m_dictionary = dictionary;
+ }
+
+ Dictionary getDictionary()
+ {
+ return m_dictionary;
+ }
+
+ @Override
+ public boolean equals(Object that)
+ {
+ return that instanceof ServiceKey ? (((ServiceKey) that).getDictionary() == m_dictionary)
+ : false;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return System.identityHashCode(m_dictionary);
+ }
+
+ @Override
+ public String toString()
+ {
+ return Dictionary.class.getName() + "@" + System.identityHashCode(m_dictionary);
+ }
+ }
+
+ /**
+ * Sole constructor.
+ * @param b the bundle containing the Service annotated with the factory attribute
+ * @param impl The Service implementation class
+ * @param serviceProperties The Service properties
+ * @param provides The Services provided by this Service
+ * @param factoryConfigure The configure callback invoked in order to pass configurations added in this Set.
+ */
+ public FactorySet(Bundle b, MetaData srvMeta, List<MetaData> depsMeta)
+ {
+ m_serviceProperties = srvMeta.getDictionary(Params.properties, null);
+ m_provide = srvMeta.getStrings(Params.provides, null);
+ m_configure = srvMeta.getString(Params.factoryConfigure, null);
+ m_bundle = b;
+ m_srvMeta = srvMeta;
+ m_depsMeta = depsMeta;
+ }
+
+ /**
+ * Our Service is starting.
+ */
+ public void start(Component c)
+ {
+ m_active = true;
+ m_dm = c.getDependencyManager();
+ }
+
+ /**
+ * Our Service is stopping: we have to remove all Service instances that we have created.
+ */
+ public void stop()
+ {
+ try
+ {
+ clear();
+ }
+ finally
+ {
+ m_active = false;
+ }
+ }
+
+ /**
+ * Create or Update a Service.
+ */
+ @Override
+ @SuppressWarnings({ "synthetic-access" })
+ public boolean add(final Dictionary configuration)
+ {
+ // Check parameter validity
+ if (configuration == null)
+ {
+ throw new NullPointerException("configuration parameter can't be null");
+ }
+
+ // Check if our service is running.
+ checkServiceAvailable();
+
+ // Check if service is being created
+ ServiceKey serviceKey = new ServiceKey(configuration);
+ boolean creating = m_services.putIfAbsent(serviceKey, SERVICE_CREATING) == null;
+
+ // Create or Update the Service.
+ m_serialExecutor.enqueue(new Runnable()
+ {
+ public void run()
+ {
+ doAdd(configuration);
+ }
+ });
+
+ m_serialExecutor.execute();
+ return creating;
+ }
+
+ /**
+ * Another Service wants to remove an existing Service.
+ * This method is not synchronized but uses a SerialExecutor for serializing concurrent method call.
+ * (This avoid potential dead locks, especially when Service callback methods are invoked).
+ */
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public boolean remove(final Object configuration)
+ {
+ // Check parameter validity.
+ if (configuration == null)
+ {
+ throw new NullPointerException("configuration parameter can't be null");
+ }
+ if (!(configuration instanceof Dictionary))
+ {
+ throw new IllegalArgumentException("configuration must be an instance of a Dictionary");
+ }
+
+ // Check if our service is active.
+ checkServiceAvailable();
+
+ // Check if service is created (or creating)
+ boolean found = m_services.containsKey(new ServiceKey((Dictionary) configuration));
+ if (found)
+ {
+ // Create or Update the Service.
+ m_serialExecutor.enqueue(new Runnable()
+ {
+ public void run()
+ {
+ doRemove((Dictionary) configuration);
+ }
+ });
+ m_serialExecutor.execute();
+ }
+ return found;
+ }
+
+ /**
+ * Another Service wants to remove all existing Services.
+ * This method is not synchronized but uses a SerialExecutor for serializing concurrent method call.
+ * (This avoid potential dead locks, especially when Service callback methods are invoked).
+ */
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public void clear()
+ {
+ if (!m_active)
+ {
+ return;
+ }
+
+ // Make sure add/update/clear events are handled in FIFO order (serially).
+ m_serialExecutor.enqueue(new Runnable()
+ {
+ public void run()
+ {
+ doClear();
+ }
+ });
+ m_serialExecutor.execute();
+ }
+
+ /**
+ * returns the list of active Service instances configurations.
+ */
+ @Override
+ public Iterator<Dictionary> iterator()
+ {
+ throw new UnsupportedOperationException(
+ "iterator method is not supported by DependencyManager Set's service factories");
+ }
+
+ @Override
+ public String toString()
+ {
+ return FactorySet.class.getName() + "(" + m_services.size() + " active instances)";
+ }
+
+ /**
+ * Returns the number of active Service instances.
+ */
+ @Override
+ public int size()
+ {
+ if (!m_active)
+ {
+ return 0;
+ }
+ return m_services.size();
+ }
+
+ /**
+ * Checks if our Service is available (we are not stopped").
+ */
+ private void checkServiceAvailable()
+ {
+ if (!m_active)
+ {
+ throw new IllegalStateException("Service not available");
+ }
+ }
+
+ /**
+ * Add or create a new Service instance, given its configuration. This method is invoked by the
+ * SerialExecutor, hence it's thread safe and we'll invoke Service's callbacks without being
+ * synchronized (hence this will avoid potential dead locks).
+ */
+ private void doAdd(Dictionary configuration)
+ {
+ // Check if the service exists.
+ ServiceKey serviceKey = new ServiceKey(configuration);
+ Object service = m_services.get(serviceKey);
+
+ if (service == null || service == SERVICE_CREATING)
+ {
+ try
+ {
+ // Create the Service / impl, unless it is explicitly provided from the
+ // configuration (using the specific key "dm.factory.instance").
+ Component s = m_dm.createComponent();
+ Class<?> implClass = m_bundle.loadClass(m_srvMeta.getString(Params.impl));
+ Object impl = configuration.get(DM_FACTORY_INSTANCE);
+ if (impl == null)
+ {
+ String factoryMethod = m_srvMeta.getString(Params.factoryMethod, null);
+ if (factoryMethod == null)
+ {
+ m_impl = implClass.newInstance();
+ }
+ else
+ {
+ Method m = implClass.getDeclaredMethod(factoryMethod);
+ m.setAccessible(true);
+ m_impl = m.invoke(null);
+ }
+ }
+ else
+ {
+ m_impl = impl;
+ }
+
+ // Invoke "configure" callback
+ if (m_configure != null)
+ {
+ invokeConfigure(m_impl, m_configure, configuration);
+ }
+
+ // Create Service
+ s.setImplementation(m_impl);
+ if (m_provide != null)
+ {
+ // Merge service properties with the configuration provided by the factory.
+ Dictionary serviceProperties = mergeSettings(m_serviceProperties, configuration);
+ s.setInterface(m_provide, serviceProperties);
+ }
+
+ s.setComposition(m_srvMeta.getString(Params.composition, null));
+ ServiceLifecycleHandler lfcleHandler = new ServiceLifecycleHandler(s, m_bundle, m_dm,
+ m_srvMeta, m_depsMeta);
+ // The dependencies will be plugged by our lifecycle handler.
+ s.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
+
+ // Adds dependencies (except named dependencies, which are managed by the lifecycle handler).
+ for (MetaData dependency: m_depsMeta)
+ {
+ String name = dependency.getString(Params.name, null);
+ if (name == null)
+ {
+ DependencyBuilder depBuilder = new DependencyBuilder(dependency);
+ Log.instance().info("ServiceLifecycleHandler.init: adding dependency %s into service %s",
+ dependency, m_srvMeta);
+ Dependency d = depBuilder.build(m_bundle, m_dm);
+ s.add(d);
+ }
+ }
+
+ // Register the Service instance, and keep track of it.
+ Log.instance().info("ServiceFactory: created service %s", m_srvMeta);
+ m_dm.add(s);
+ m_services.put(serviceKey, s);
+ }
+ catch (Throwable t)
+ {
+ // Make sure the SERVICE_CREATING flag is also removed
+ m_services.remove(serviceKey);
+ Log.instance().error("ServiceFactory: could not instantiate service %s",
+ t, m_srvMeta);
+ }
+ }
+ else
+ {
+ // Reconfigure an already existing Service.
+ if (m_configure != null)
+ {
+ Log.instance().info("ServiceFactory: updating service %s", m_impl);
+ invokeConfigure(m_impl, m_configure, configuration);
+ }
+
+ // Update service properties
+ if (m_provide != null)
+ {
+ Dictionary settings = mergeSettings(m_serviceProperties, configuration);
+ ((Component) service).setServiceProperties(settings);
+ }
+ }
+ }
+
+ private void doRemove(Dictionary configuraton)
+ {
+ Log.instance().info("ServiceFactory: removing service %s", m_srvMeta);
+ ServiceKey serviceKey = new ServiceKey(configuraton);
+ Object service = m_services.remove(serviceKey);
+ if (service != null && service != SERVICE_CREATING)
+ {
+ m_dm.remove((Component) service);
+ }
+ }
+
+ private void doClear()
+ {
+ for (Object service : m_services.values())
+ {
+ if (service instanceof Component)
+ {
+ m_dm.remove((Component) service);
+ }
+ }
+ m_services.clear();
+ }
+
+ /**
+ * Merge factory configuration settings with the service properties. The private factory configuration
+ * settings are ignored. A factory configuration property is private if its name starts with a dot (".").
+ *
+ * @param serviceProperties
+ * @param factoryConfiguration
+ * @return
+ */
+ private Dictionary mergeSettings(Dictionary serviceProperties, Dictionary factoryConfiguration)
+ {
+ Dictionary props = new Hashtable();
+
+ if (serviceProperties != null)
+ {
+ Enumeration keys = serviceProperties.keys();
+ while (keys.hasMoreElements())
+ {
+ Object key = keys.nextElement();
+ Object val = serviceProperties.get(key);
+ props.put(key, val);
+ }
+ }
+
+ Enumeration keys = factoryConfiguration.keys();
+ while (keys.hasMoreElements())
+ {
+ Object key = keys.nextElement();
+ if (!key.toString().startsWith("."))
+ {
+ // public properties are propagated
+ Object val = factoryConfiguration.get(key);
+ props.put(key, val);
+ }
+ }
+ return props;
+ }
+
+ /**
+ * Invokes the configure callback method on the service instance implemenatation.
+ * @param impl
+ * @param configure
+ * @param config
+ */
+ private void invokeConfigure(Object impl, String configure, Dictionary config)
+ {
+ try
+ {
+ InvocationUtil.invokeCallbackMethod(impl, configure,
+ new Class[][] { { Dictionary.class } },
+ new Object[][] { { config } });
+ }
+
+ catch (Throwable t)
+ {
+ if (t instanceof RuntimeException)
+ {
+ throw (RuntimeException) t;
+ }
+ else
+ {
+ throw new RuntimeException("Could not invoke method " + configure
+ + " on object " + impl, t);
+ }
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/InvocationUtil.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/InvocationUtil.java
new file mode 100644
index 0000000..fb4456b
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/InvocationUtil.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Java reflexion utility methods.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class InvocationUtil
+{
+ public static Object invokeCallbackMethod(Object instance,
+ String methodName,
+ Class<?>[][] signatures,
+ Object[][] parameters)
+ throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException,
+ InvocationTargetException
+ {
+ Class<?> currentClazz = instance.getClass();
+ while (currentClazz != null)
+ {
+ try
+ {
+ return invokeMethod(instance, currentClazz, methodName, signatures, parameters, false);
+ }
+ catch (NoSuchMethodException nsme)
+ {
+ // ignore
+ }
+ currentClazz = currentClazz.getSuperclass();
+ }
+ throw new NoSuchMethodException(methodName);
+ }
+
+ public static Object invokeMethod(Object object,
+ Class<?> clazz,
+ String name,
+ Class<?>[][] signatures,
+ Object[][] parameters,
+ boolean isSuper)
+ throws NoSuchMethodException, InvocationTargetException, IllegalArgumentException,
+ IllegalAccessException
+ {
+ if (object == null)
+ {
+ throw new IllegalArgumentException("Instance cannot be null");
+ }
+ if (clazz == null)
+ {
+ throw new IllegalArgumentException("Class cannot be null");
+ }
+
+ // If we're talking to a proxy here, dig one level deeper to expose the
+ // underlying invocation handler ...
+
+ if (Proxy.isProxyClass(clazz))
+ {
+ object = Proxy.getInvocationHandler(object);
+ clazz = object.getClass();
+ }
+
+ for (int i = 0; i < signatures.length; i++)
+ {
+ Class<?>[] signature = signatures[i];
+ try
+ {
+ final Method m = clazz.getDeclaredMethod(name, signature);
+ if (!(isSuper && Modifier.isPrivate(m.getModifiers())))
+ {
+ AccessController.doPrivileged(new PrivilegedAction<Object>()
+ {
+ public Object run()
+ {
+ m.setAccessible(true);
+ return null;
+ }
+ });
+ return m.invoke(object, parameters[i]);
+ }
+ }
+ catch (NoSuchMethodException e)
+ {
+ // ignore this and keep looking
+ }
+ }
+ throw new NoSuchMethodException(name);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/JSONMetaData.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/JSONMetaData.java
new file mode 100644
index 0000000..0548394
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/JSONMetaData.java
@@ -0,0 +1,482 @@
+/*
+ * 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.runtime;
+
+import java.lang.reflect.Array;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Thsi class represents the parsed data found from meta-inf dependencymanager descriptors.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class JSONMetaData implements MetaData, Cloneable
+{
+ /**
+ * The parsed Dependency or Service metadata. The map value is either a String, a String[],
+ * or a Dictionary, whose values are String or String[].
+ */
+ private HashMap<String, Object> m_metadata = new HashMap<String, Object>();
+
+ /**
+ * Decodes Json metadata for either a Service or a Dependency descriptor entry.
+ * The JSON object has the following form:
+ *
+ * entry ::= String | String[] | dictionary
+ * dictionary ::= key-value-pair*
+ * key-value-pair ::= key value
+ * value ::= String | String[] | value-type
+ * value-type ::= jsonObject with value-type-info
+ * value-type-info ::= "type"=primitive java type
+ * "value"=String|String[]
+ *
+ * Exemple:
+ *
+ * {"string-param" : "string-value",
+ * "string-array-param" : ["string1", "string2"],
+ * "properties" : {
+ * "string-param" : "string-value",
+ * "string-array-param" : ["str1", "str2],
+ * "long-param" : {"type":"java.lang.Long", "value":"1"}}
+ * "long-array-param" : {"type":"java.lang.Long", "value":["1"]}}
+ * }
+ * }
+ *
+ * @param jso the JSON object that corresponds to a dependency manager descriptor entry line.
+ * @throws JSONException
+ */
+ @SuppressWarnings("unchecked")
+ public JSONMetaData(JSONObject jso) throws JSONException
+ {
+ // Decode json object into our internal map.
+ Iterator<String> it = jso.keys();
+ while (it.hasNext())
+ {
+ String key = it.next();
+ Object value = jso.get(key);
+ if (value instanceof String)
+ {
+ m_metadata.put(key, value);
+ }
+ else if (value instanceof JSONArray)
+ {
+ m_metadata.put(key, decodeStringArray((JSONArray) value));
+ }
+ else if (value instanceof JSONObject)
+ {
+ m_metadata.put(key, parseProperties((JSONObject) value));
+ }
+ }
+ }
+
+ private Hashtable<String, Object> parseProperties(JSONObject properties) throws JSONException {
+ Hashtable<String, Object> parsedProps = new Hashtable<String, Object>();
+ @SuppressWarnings("unchecked")
+ Iterator<String> it = properties.keys();
+ while (it.hasNext())
+ {
+ String key = it.next();
+ Object value = properties.get(key);
+ if (value instanceof String)
+ {
+ // This property type is a simple string
+ parsedProps.put(key, value);
+ }
+ else if (value instanceof JSONArray)
+ {
+ // This property type is a simple string array
+ parsedProps.put(key, decodeStringArray((JSONArray) value));
+ }
+ else if (value instanceof JSONObject)
+ {
+ // This property type is a typed value, encoded as a JSONObject with two keys: "type"/"value"
+ JSONObject json = ((JSONObject) value);
+ String type = json.getString("type");
+ Object typeValue = json.get("value");
+
+ if (type == null)
+ {
+ throw new JSONException("missing type attribute in json metadata for key " + key);
+ }
+ if (typeValue == null)
+ {
+ throw new JSONException("missing type value attribute in json metadata for key " + key);
+ }
+
+ Class<?> typeClass;
+ try
+ {
+ typeClass = Class.forName(type);
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new JSONException("invalid type attribute (" + type + ") in json metadata for key "
+ + key);
+ }
+
+ if (typeValue instanceof JSONArray)
+ {
+ parsedProps.put(key, toPrimitiveTypeArray(typeClass, (JSONArray) typeValue));
+ }
+ else
+ {
+ parsedProps.put(key, toPrimitiveType(typeClass, typeValue.toString()));
+ }
+ }
+ }
+ return parsedProps;
+ }
+
+ private Object toPrimitiveType(Class<?> type, String value) throws JSONException {
+ if (type.equals(String.class))
+ {
+ return value;
+ }
+ else if (type.equals(Long.class))
+ {
+ return Long.parseLong(value);
+ }
+ else if (type.equals(Double.class))
+ {
+ return Double.valueOf(value);
+ }
+ else if (type.equals(Float.class))
+ {
+ return Float.valueOf(value);
+ }
+ else if (type.equals(Integer.class))
+ {
+ return Integer.valueOf(value);
+ }
+ else if (type.equals(Byte.class))
+ {
+ return Byte.valueOf(value);
+ }
+ else if (type.equals(Character.class))
+ {
+ return Character.valueOf((char) Integer.parseInt(value));
+ }
+ else if (type.equals(Boolean.class))
+ {
+ return Boolean.valueOf(value);
+ }
+ else if (type.equals(Short.class))
+ {
+ return Short.valueOf(value);
+ }
+ else
+ {
+ throw new JSONException("invalid type (" + type + ") attribute in json metadata");
+ }
+ }
+
+ private Object toPrimitiveTypeArray(Class<?> type, JSONArray array) throws JSONException {
+ int len = array.length();
+ Object result = Array.newInstance(type, len);
+
+ if (type.equals(String.class))
+ {
+ for (int i = 0; i < len; i ++) {
+ Array.set(result, i, array.getString(i));
+ }
+ }
+ else if (type.equals(Long.class))
+ {
+ for (int i = 0; i < len; i ++) {
+ Array.set(result, i, Long.valueOf(array.getString(i)));
+ }
+ }
+ else if (type.equals(Double.class))
+ {
+ for (int i = 0; i < len; i ++) {
+ Array.set(result, i, Double.valueOf(array.getString(i)));
+ }
+ }
+ else if (type.equals(Float.class))
+ {
+ for (int i = 0; i < len; i ++) {
+ Array.set(result, i, Float.valueOf(array.getString(i)));
+ }
+ }
+ else if (type.equals(Integer.class))
+ {
+ for (int i = 0; i < len; i ++) {
+ Array.set(result, i, Integer.valueOf(array.getString(i)));
+ }
+ }
+ else if (type.equals(Byte.class))
+ {
+ for (int i = 0; i < len; i ++) {
+ Array.set(result, i, Byte.valueOf(array.getString(i)));
+ }
+ }
+ else if (type.equals(Character.class))
+ {
+ for (int i = 0; i < len; i ++) {
+ Array.set(result, i, Character.valueOf((char) Integer.parseInt(array.getString(i))));
+ }
+ }
+ else if (type.equals(Boolean.class))
+ {
+ for (int i = 0; i < len; i ++) {
+ Array.set(result, i, Boolean.valueOf(array.getString(i)));
+ }
+ }
+ else if (type.equals(Short.class))
+ {
+ for (int i = 0; i < len; i ++) {
+ Array.set(result, i, Short.valueOf(array.getString(i)));
+ }
+ }
+ else
+ {
+ throw new JSONException("invalid type (" + type + ") attribute in json metadata");
+ }
+ return result;
+ }
+
+ /**
+ * Close this class instance to another one.
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object clone() throws CloneNotSupportedException
+ {
+ JSONMetaData clone = (JSONMetaData) super.clone();
+ clone.m_metadata = (HashMap<String, Object>) m_metadata.clone();
+ return clone;
+ }
+
+ public String getString(Params key)
+ {
+ Object value = m_metadata.get(key.toString());
+ if (value == null)
+ {
+ throw new IllegalArgumentException("Parameter " + key + " not found");
+ }
+ return value.toString();
+ }
+
+ public String getString(Params key, String def)
+ {
+ try
+ {
+ return getString(key);
+ }
+ catch (IllegalArgumentException e)
+ {
+ return def;
+ }
+ }
+
+ public int getInt(Params key)
+ {
+ Object value = m_metadata.get(key.toString());
+ if (value != null)
+ {
+ try
+ {
+ if (value instanceof Integer) {
+ return ((Integer) value).intValue();
+ }
+ return Integer.parseInt(value.toString());
+ }
+ catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("parameter " + key
+ + " is not an int value: "
+ + value);
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("missing " + key
+ + " parameter from annotation");
+ }
+ }
+
+ public int getInt(Params key, int def)
+ {
+ Object value = m_metadata.get(key.toString());
+ if (value != null)
+ {
+ try
+ {
+ if (value instanceof Integer) {
+ return ((Integer) value).intValue();
+ }
+ return Integer.parseInt(value.toString());
+ }
+ catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("parameter " + key
+ + " is not an int value: "
+ + value);
+ }
+ }
+ else
+ {
+ return def;
+ }
+ }
+
+ public long getLong(Params key)
+ {
+ Object value = m_metadata.get(key.toString());
+ if (value != null)
+ {
+ try
+ {
+ if (value instanceof Long) {
+ return ((Long) value).longValue();
+ }
+ return Long.parseLong(value.toString());
+ }
+ catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("parameter " + key
+ + " is not a long value: "
+ + value);
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("missing " + key
+ + " parameter from annotation");
+ }
+ }
+
+ public long getLong(Params key, long def)
+ {
+ Object value = m_metadata.get(key.toString());
+ if (value != null)
+ {
+ try
+ {
+ if (value instanceof Long) {
+ return (Long) value;
+ }
+ return Long.parseLong(value.toString());
+ }
+ catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("parameter " + key
+ + " is not a long value: "
+ + value);
+ }
+ }
+ else
+ {
+ return def;
+ }
+ }
+
+ public String[] getStrings(Params key)
+ {
+ Object array = m_metadata.get(key.toString());
+ if (array == null)
+ {
+ throw new IllegalArgumentException("Parameter " + key + " not found");
+ }
+
+ if (!(array instanceof String[]))
+ {
+ throw new IllegalArgumentException("Parameter " + key + " is not a String[] (" + array.getClass()
+ + ")");
+ }
+ return (String[]) array;
+ }
+
+ public String[] getStrings(Params key, String[] def)
+ {
+ try
+ {
+ return getStrings(key);
+ }
+ catch (IllegalArgumentException t)
+ {
+ return def;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public Dictionary<String, Object> getDictionary(Params key,
+ Dictionary<String, Object> def)
+ {
+ Object dictionary = m_metadata.get(key.toString());
+ if (dictionary == null)
+ {
+ return def;
+ }
+
+ if (!(dictionary instanceof Dictionary<?, ?>))
+ {
+ throw new IllegalArgumentException("Parameter " + key + " is not a Dictionary ("
+ + dictionary.getClass() + ")");
+ }
+
+ return (Dictionary<String, Object>) dictionary;
+ }
+
+ @Override
+ public String toString()
+ {
+ return m_metadata.toString();
+ }
+
+ public void setDictionary(Params key, Dictionary<String, Object> dictionary)
+ {
+ m_metadata.put(key.toString(), dictionary);
+ }
+
+ public void setString(Params key, String value)
+ {
+ m_metadata.put(key.toString(), value);
+ }
+
+ public void setStrings(Params key, String[] values)
+ {
+ m_metadata.put(key.toString(), values);
+ }
+
+ /**
+ * Decodes a JSONArray into a String array (all JSON array values are supposed to be strings).
+ */
+ private String[] decodeStringArray(JSONArray array) throws JSONException
+ {
+ String[] arr = new String[array.length()];
+ for (int i = 0; i < array.length(); i++)
+ {
+ Object value = array.get(i);
+ if (!(value instanceof String))
+ {
+ throw new IllegalArgumentException("JSON array is not an array of Strings: " + array);
+ }
+ arr[i] = value.toString();
+ }
+ return arr;
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Log.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Log.java
new file mode 100644
index 0000000..f23ba0e
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Log.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import org.osgi.service.log.LogService;
+
+/**
+ * This class logs some formattable strings into the OSGi Log Service.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Log
+{
+ /** The wrap log service which is actually used (Injected by Activator) */
+ private volatile LogService m_logService;
+
+ /** Our sole instance */
+ private static Log m_instance = new Log();
+
+ public static Log instance()
+ {
+ return m_instance;
+ }
+
+ public void error(String format, Object ... args)
+ {
+ m_logService.log(LogService.LOG_ERROR, String.format(format, args));
+ }
+
+ public void error(String format, Throwable t, Object ... args)
+ {
+ m_logService.log(LogService.LOG_ERROR, String.format(format, args), t);
+ }
+
+ public void warn(String format, Object ... args)
+ {
+ m_logService.log(LogService.LOG_WARNING, String.format(format, args));
+ }
+
+ public void warn(String format, Throwable t, Object ... args)
+ {
+ m_logService.log(LogService.LOG_WARNING, String.format(format, args), t);
+ }
+
+ public void info(String format, Object ... args)
+ {
+ m_logService.log(LogService.LOG_INFO, String.format(format, args));
+ }
+
+ public void info(String format, Throwable t, Object ... args)
+ {
+ m_logService.log(LogService.LOG_INFO, String.format(format, args), t);
+ }
+
+ public void debug(String format, Object ... args)
+ {
+ m_logService.log(LogService.LOG_DEBUG, String.format(format, args));
+ }
+
+ public void debug(String format, Throwable t, Object ... args)
+ {
+ m_logService.log(LogService.LOG_DEBUG, String.format(format, args), t);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/MetaData.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/MetaData.java
new file mode 100644
index 0000000..435f958
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/MetaData.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 org.apache.felix.dm.runtime;
+
+import java.util.Dictionary;
+
+/**
+ * This class represents the meta data parsed from a descriptor entry (json) line.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface MetaData extends Cloneable
+{
+ /**
+ * Returns a String descriptor entry parameter value.
+ */
+ String getString(Params key);
+
+ /**
+ * Returns a String descriptor entry parameter value.
+ */
+ String getString(Params key, String def);
+
+ /**
+ * Returns a String descriptor entry parameter value.
+ */
+ int getInt(Params key);
+
+ /**
+ * Returns a String descriptor entry parameter value.
+ */
+ int getInt(Params key, int def);
+
+ /**
+ * Returns a String descriptor entry parameter value.
+ */
+ long getLong(Params key);
+
+ /**
+ * Returns a String descriptor entry parameter value.
+ */
+ long getLong(Params key, long def);
+
+ /**
+ * Returns a String array descriptor entry parameter value.
+ */
+ String[] getStrings(Params key);
+
+ /**
+ * Returns a String array descriptor entry parameter value.
+ */
+ String[] getStrings(Params key, String[] def);
+
+ /**
+ * Returns a descriptor entry value which is a complex value.
+ */
+ Dictionary<String, Object> getDictionary(Params key, Dictionary<String, Object> def);
+
+ /**
+ * Modifies a key Sring value
+ */
+ void setString(Params key, String value);
+
+ /**
+ * Modifies a String[] value.
+ */
+ void setStrings(Params key, String[] values);
+
+ /**
+ * Modifies a String[] value.
+ */
+ void setDictionary(Params key, Dictionary<String, Object> dictionary);
+
+ /**
+ * Clone this MetaData object.
+ */
+ Object clone() throws CloneNotSupportedException;
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Params.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Params.java
new file mode 100644
index 0000000..37f251f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Params.java
@@ -0,0 +1,68 @@
+/*
+ * 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.runtime;
+
+/**
+ * List of descriptor parameters.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public enum Params
+{
+ type,
+ init,
+ start,
+ stop,
+ destroy,
+ impl,
+ provides,
+ properties,
+ composition,
+ service,
+ filter,
+ defaultImpl,
+ required,
+ added,
+ changed,
+ removed,
+ swap,
+ autoConfig,
+ pid,
+ propagate,
+ updated,
+ timeout,
+ adapteeService,
+ adapteeFilter,
+ stateMask,
+ ranking,
+ factoryPid,
+ factorySet,
+ factoryName,
+ factoryConfigure,
+ factoryMethod,
+ name,
+ field,
+ starter,
+ stopper,
+ bundleContextField,
+ dependencyManagerField,
+ componentField,
+ registered,
+ unregistered
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ResourceAdapterServiceBuilder.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ResourceAdapterServiceBuilder.java
new file mode 100644
index 0000000..7174396
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ResourceAdapterServiceBuilder.java
@@ -0,0 +1,74 @@
+/*
+ * 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.runtime;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+
+/**
+ * Class used to build a resource adapter service using metadata found from DependencyManager runtime
+ * meta-inf descriptor.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResourceAdapterServiceBuilder extends AbstractBuilder
+{
+ private final static String TYPE = "ResourceAdapterService";
+
+ @Override
+ public String getType()
+ {
+ return TYPE;
+ }
+
+ @Override
+ public void build(MetaData srvMeta, List<MetaData> depsMeta, Bundle b, DependencyManager dm)
+ throws Exception
+ {
+ String filter = srvMeta.getString(Params.filter, null);
+ Class<?> implClass = b.loadClass(srvMeta.getString(Params.impl));
+ String[] provides = srvMeta.getStrings(Params.provides, null);
+ Dictionary<String, Object> properties = srvMeta.getDictionary(Params.properties, null);
+ boolean propagate = "true".equals(srvMeta.getString(Params.propagate, "false"));
+ String changed = srvMeta.getString(Params.changed, null /* no change callback if not specified explicitly */);
+ Component c = dm.createResourceAdapterService(filter, propagate, null, changed);
+ c.setInterface(provides, properties);
+ String factoryMethod = srvMeta.getString(Params.factoryMethod, null);
+ if (factoryMethod == null)
+ {
+ c.setImplementation(implClass);
+ }
+ else
+ {
+ c.setFactory(implClass, factoryMethod);
+ }
+ setCommonServiceParams(c, srvMeta);
+ c.setComposition(srvMeta.getString(Params.composition, null));
+ ServiceLifecycleHandler lfcleHandler = new ServiceLifecycleHandler(c, b, dm, srvMeta, depsMeta);
+ // The dependencies will be plugged by our lifecycle handler.
+ c.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
+ // Adds dependencies (except named dependencies, which are managed by the lifecycle handler).
+ addUnamedDependencies(b, dm, c, srvMeta, depsMeta);
+ dm.add(c);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/SerialExecutor.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/SerialExecutor.java
new file mode 100644
index 0000000..b7dae8f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/SerialExecutor.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+
+/**
+ * Allows you to enqueue tasks from multiple threads and then execute
+ * them on one thread sequentially. It assumes more than one thread will
+ * try to execute the tasks and it will make an effort to pick the first
+ * task that comes along whilst making sure subsequent tasks return
+ * without waiting.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public final class SerialExecutor {
+ private static final Runnable DUMMY_RUNNABLE = new Runnable() { public void run() {} };
+ private final LinkedList<Runnable> m_workQueue = new LinkedList<>();
+ private Runnable m_active;
+
+ /**
+ * Enqueue a new task for later execution. This method is
+ * thread-safe, so multiple threads can contribute tasks.
+ *
+ * @param runnable the runnable containing the actual task
+ */
+ public synchronized void enqueue(final Runnable runnable) {
+ m_workQueue.addLast(new Runnable() {
+ public void run() {
+ try {
+ runnable.run();
+ }
+ finally {
+ scheduleNext();
+ }
+ }
+ });
+ }
+
+ /**
+ * Execute any pending tasks. This method is thread safe,
+ * so multiple threads can try to execute the pending
+ * tasks, but only the first will be used to actually do
+ * so. Other threads will return immediately.
+ */
+ public void execute() {
+ Runnable active;
+ synchronized (this) {
+ active = m_active;
+ // for now just put some non-null value in there so we can never
+ // get a race condition when two threads enter this section after
+ // one another (causing sheduleNext() to be invoked twice below)
+ m_active = DUMMY_RUNNABLE;
+ }
+ if (active == null) {
+ scheduleNext();
+ }
+ }
+
+ private void scheduleNext() {
+ Runnable active;
+ synchronized (this) {
+ try {
+ m_active = (Runnable) m_workQueue.removeFirst();
+ }
+ catch (NoSuchElementException e) {
+ m_active = null;
+ }
+ active = m_active;
+ }
+ if (active != null) {
+ active.run();
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ServiceLifecycleHandler.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ServiceLifecycleHandler.java
new file mode 100644
index 0000000..4058988
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ServiceLifecycleHandler.java
@@ -0,0 +1,456 @@
+/*
+ * 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.runtime;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+
+/**
+ * Caution: this class *MUST* be immutable, because it may be shared between Aspects/Adapters
+ * and concrete Aspect/Adapter instance.
+ *
+ * This class allows Services to configure dynamically their dependency filters from their init() method.
+ * Basically, this class acts as a service implementation lifecycle handler. When we detect that the Service is
+ * called in its init() method, and if init() returns a Map, then the Map is assumed to contain
+ * dependency filters, which will be applied to all named dependencies. The Map optionally returned by
+ * Service's init method may contain the following keys:
+ * <ul>
+ * <li>name.filter: the value must be a valid OSGi filter, and the "name" prefix must match a ServiceDependency
+ * name attribute</li>
+ * <li>name.required: the value is a boolean ("true"|"false") and the "name" prefix must match a
+ * ServiceDependency name attribute</li>
+ * </ul>
+ *
+ * <p>Dependencies which provide a name attribute will be activated after the init method returns. Other
+ * dependencies are injected before the init method.
+ *
+ * <p>Example of a Service whose dependency filter is configured from ConfigAdmin:
+ *
+ * <blockquote><pre>
+ * /**
+ * * A Service whose service dependency filter/require attribute may be configured from ConfigAdmin
+ * */
+ * @Service
+ * class X {
+ * private Dictionary m_config;
+ *
+ * @ConfigurationDependency(pid="MyPid")
+ * void configure(Dictionary conf) {
+ * // Initialize our service from config ...
+ *
+ * // And store the config for later usage (from our init method)
+ * m_config = config;
+ * }
+ *
+ * @ServiceDependency(name="dependency1")
+ * void bindOtherService(OtherService other) {
+ * // the filter and required flag will be configured from our init method.
+ * }
+ *
+ * // The returned Map will be used to configure our "dependency1" Dependency.
+ * @Init
+ * Map init() {
+ * return new HashMap() {{
+ * put("dependency1.filter", m_config.get("filter"));
+ * put("dependency1.required", m_config.get("required"));
+ * }};
+ * }
+ * }
+ * </pre></blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceLifecycleHandler
+{
+ private final String m_init;
+ private final String m_start;
+ private final String m_stop;
+ private final String m_destroy;
+ private final MetaData m_srvMeta;
+ private final List<MetaData> m_depsMeta;
+ private final Bundle m_bundle;
+ private final static Object SYNC = new Object();
+
+ /**
+ * Makes a new ServiceLifecycleHandler object. This objects allows to decorate the "init" service callback, in
+ * order to see if "init" callback returns a dependency customization map.
+ *
+ * @param srv The Service for the annotated class
+ * @param srvBundle the Service bundle
+ * @param dm The DependencyManager that was used to create the service
+ * @param srvMeta The Service MetaData
+ * @param depMeta The Dependencies MetaData
+ */
+ public ServiceLifecycleHandler(Component srv, Bundle srvBundle, DependencyManager dm,
+ MetaData srvMeta, List<MetaData> depMeta)
+ {
+ m_srvMeta = srvMeta;
+ m_init = srvMeta.getString(Params.init, null);
+ m_start = srvMeta.getString(Params.start, null);
+ m_stop = srvMeta.getString(Params.stop, null);
+ m_destroy = srvMeta.getString(Params.destroy, null);
+ m_bundle = srvBundle;
+ m_depsMeta = depMeta;
+ }
+
+ /**
+ * Handles an "init" lifecycle service callback. We just catch the "init" method, and callback
+ * the actual Service' init method, to see if a dependency customization map is returned.
+ * We also check if a Lifecycle Controller is used. In this case, we add a hidden custom dependency,
+ * allowing to take control of when the component is actually started/stopped.
+ * We also handle an edge case described in FELIX-4050, where component state calculation
+ * may mess up if some dependencies are added using the API from the init method.
+ *
+ * @param c The Annotated Component
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public void init(Component c)
+ throws Exception
+ {
+ Object serviceInstance = c.getInstances()[0];
+ DependencyManager dm = c.getDependencyManager();
+
+ // Check if a lifecycle controller is defined for this service. If true, then
+ // We'll use the ToggleServiceDependency in order to manually activate/deactivate
+ // the component ...
+ String starter = m_srvMeta.getString(Params.starter, null);
+ String stopper = m_srvMeta.getString(Params.stopper, null);
+
+ List<Dependency> instanceBoundDeps = new ArrayList<>();
+
+ if (starter != null)
+ {
+ // We'll inject two runnables: one that will start or service, when invoked, and the other
+ // that will stop our service, when invoked. We'll use a shared atomic boolean in order to
+ // synchronize both runnables.
+ Log.instance().debug("Setting up a lifecycle controller for service %s", serviceInstance);
+ String componentName = serviceInstance.getClass().getName();
+ // Create a toggle service, used to start/stop our service.
+ ToggleServiceDependency toggle = new ToggleServiceDependency();
+ AtomicBoolean startFlag = new AtomicBoolean(false);
+ // Add the toggle to the service.
+ instanceBoundDeps.add(toggle);
+ // Inject the runnable that will start our service, when invoked.
+ setField(serviceInstance, starter, Runnable.class, new ComponentStarter(componentName, toggle, startFlag));
+ if (stopper != null) {
+ // Inject the runnable that will stop our service, when invoked.
+ setField(serviceInstance, stopper, Runnable.class, new ComponentStopper(componentName, toggle, startFlag));
+ }
+ }
+
+ // Before invoking an optional init method, we have to handle an edge case (FELIX-4050), where
+ // init may add dependencies using the API and also return a map for configuring some
+ // named dependencies. We have to add a hidden toggle dependency in the component, which we'll
+ // active *after* the init method is called, and possibly *after* named dependencies are configured.
+
+ ToggleServiceDependency initToggle = null;
+ if (m_init != null)
+ {
+ initToggle = new ToggleServiceDependency();
+ c.add(initToggle);
+ }
+
+ // Invoke component and all composites init methods, and for each one, check if a dependency
+ // customization map is returned by the method. This map will be used to configure
+ // some dependency filters (or required flag).
+
+ Map<String, String> customization = new HashMap<String, String>();
+ Object[] composites = c.getInstances();
+ for (Object composite: composites)
+ {
+ Object o = invokeMethod(composite, m_init, dm, c);
+ if (o != null && Map.class.isAssignableFrom(o.getClass()))
+ {
+ customization.putAll((Map) o);
+ }
+ }
+
+ Log.instance().debug("ServiceLifecycleHandler.init: invoked init method from service %s " +
+ ", returned map: %s", serviceInstance, customization);
+
+ // Apply name dependency filters possibly returned by the init() method.
+
+ for (MetaData dependency: m_depsMeta)
+ {
+ // Check if this dependency has a name, and if we find the name from the
+ // customization map, then apply filters and required flag from the map into it.
+ // Also parse optional pid/propagate flags for named Configuration dependencies
+
+ String name = dependency.getString(Params.name, null);
+ if (name != null)
+ {
+ String filter = customization.get(name + ".filter");
+ String required = customization.get(name + ".required");
+ String pid = customization.get(name + ".pid");
+ String propagate = customization.get(name + ".propagate");
+
+ if (filter != null || required != null || pid != null || propagate != null)
+ {
+ dependency = (MetaData) dependency.clone();
+ if (filter != null)
+ {
+ dependency.setString(Params.filter, filter);
+ }
+ if (required != null)
+ {
+ dependency.setString(Params.required, required);
+ }
+ if (pid != null)
+ {
+ dependency.setString(Params.pid, pid);
+ }
+ if (propagate != null)
+ {
+ dependency.setString(Params.propagate, propagate);
+ }
+ }
+
+ DependencyBuilder depBuilder = new DependencyBuilder(dependency);
+ Log.instance().info("ServiceLifecycleHandler.init: adding dependency %s into service %s",
+ dependency, m_srvMeta);
+ Dependency d = depBuilder.build(m_bundle, dm);
+ instanceBoundDeps.add(d);
+ }
+ }
+
+ // Add all extra dependencies in one shot, in order to calculate state changes for all dependencies at a time.
+ if (instanceBoundDeps.size() > 0)
+ {
+ Log.instance().info("ServiceLifecycleHandler.init: adding extra/named dependencies %s",
+ instanceBoundDeps);
+ c.add(instanceBoundDeps.toArray(new Dependency[instanceBoundDeps.size()]));
+ }
+
+ // init method fully handled, and all possible named dependencies have been configured. Now, activate the
+ // hidden toggle, and then remove it from the component, because we don't need it anymore.
+ if (initToggle != null)
+ {
+ c.remove(initToggle);
+ }
+ }
+
+ /**
+ * Handles the Service's start lifecycle callback. We just invoke the service "start" service callback on
+ * the service instance, as well as on all eventual service composites.
+ * We take care to check if a start callback returns a Map, which is meant to contain
+ * some additional properties which must be appended to existing service properties.
+ * Such extra properties takes precedence over existing service properties.
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public void start(Component service)
+ throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+ {
+ // Check if some extra service properties are returned by start method.
+
+ DependencyManager dm = service.getDependencyManager();
+ Map<String, String> extraProperties = new HashMap<String, String>();
+ Object[] composites = service.getInstances();
+ for (Object composite: composites)
+ {
+ Object o = invokeMethod(composite, m_start, dm, service);
+ if (o != null && Map.class.isAssignableFrom(o.getClass()))
+ {
+ extraProperties.putAll((Map) o);
+ }
+ }
+
+ if (extraProperties.size() > 0)
+ {
+ // Store extra properties returned by start callbacks into existing service properties
+ Dictionary existingProperties = service.getServiceProperties();
+ if (existingProperties != null)
+ {
+ Hashtable props = new Hashtable();
+ Enumeration e = existingProperties.keys();
+ while (e.hasMoreElements())
+ {
+ Object key = e.nextElement();
+ props.put(key, existingProperties.get(key));
+ }
+ props.putAll(extraProperties);
+ service.setServiceProperties(props);
+ }
+ else
+ {
+ service.setServiceProperties(new Hashtable(extraProperties));
+ }
+ }
+ }
+
+ /**
+ * Handles the Service's stop lifecycle callback. We just invoke the service "stop" callback on
+ * the service instance, as well as on all eventual service composites.
+ */
+ public void stop(Component service)
+ throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+ {
+ callbackComposites(service, m_stop);
+ }
+
+ /**
+ * Handles the Service's destroy lifecycle callback. We just invoke the service "destroy" callback on
+ * the service instance, as well as on all eventual service composites.
+ */
+ public void destroy(Component service)
+ throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+ {
+ callbackComposites(service, m_destroy);
+ }
+
+ /**
+ * Invoke a callback on all Service compositions.
+ */
+ private void callbackComposites(Component service, String callback)
+ throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+ {
+ Object[] composites = service.getInstances();
+ for (Object composite: composites)
+ {
+ invokeMethod(composite, callback, service.getDependencyManager(), service);
+ }
+ }
+
+ /**
+ * Invoke a callback on an Object instance.
+ */
+ private Object invokeMethod(Object serviceInstance, String method, DependencyManager dm, Component c)
+ throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+ {
+ if (method != null)
+ {
+ try
+ {
+ return InvocationUtil.invokeCallbackMethod(serviceInstance, method,
+ new Class[][] { { Component.class }, {} },
+ new Object[][] { { c }, {} }
+ );
+ }
+
+ catch (NoSuchMethodException e)
+ {
+ // ignore this
+ }
+
+ // Other exception will be thrown up to the ServiceImpl.invokeCallbackMethod(), which is
+ // currently invoking our method. So, no need to log something here, since the invokeCallbackMethod
+ // method is already logging any thrown exception.
+ }
+ return null;
+ }
+
+ /**
+ * Sets a field of an object by reflexion.
+ */
+ private void setField(Object instance, String fieldName, Class<?> fieldClass, Object fieldValue)
+ {
+ Object serviceInstance = instance;
+ Class<?> serviceClazz = serviceInstance.getClass();
+ if (Proxy.isProxyClass(serviceClazz))
+ {
+ serviceInstance = Proxy.getInvocationHandler(serviceInstance);
+ serviceClazz = serviceInstance.getClass();
+ }
+ while (serviceClazz != null)
+ {
+ Field[] fields = serviceClazz.getDeclaredFields();
+ for (int j = 0; j < fields.length; j++)
+ {
+ Field field = fields[j];
+ Class<?> type = field.getType();
+ if (field.getName().equals(fieldName) && type.isAssignableFrom(fieldClass))
+ {
+ try
+ {
+ field.setAccessible(true);
+ // synchronized makes sure the field is actually written to immediately
+ synchronized (SYNC)
+ {
+ field.set(serviceInstance, fieldValue);
+ }
+ }
+ catch (Throwable e)
+ {
+ throw new RuntimeException("Could not set field " + field, e);
+ }
+ }
+ }
+ serviceClazz = serviceClazz.getSuperclass();
+ }
+ }
+
+ private static class ComponentStarter implements Runnable {
+ private final String m_componentName;
+ private final ToggleServiceDependency m_toggle;
+ private final AtomicBoolean m_startFlag;
+
+ public ComponentStarter(String name, ToggleServiceDependency toggle, AtomicBoolean startFlag)
+ {
+ m_componentName = name;
+ m_toggle = toggle;
+ m_startFlag = startFlag;
+ }
+
+ @SuppressWarnings("synthetic-access")
+ public void run()
+ {
+ if (m_startFlag.compareAndSet(false, true)) {
+ Log.instance().debug("Lifecycle controller is activating the component %s",
+ m_componentName);
+ m_toggle.activate(true);
+ }
+ }
+ }
+
+ private static class ComponentStopper implements Runnable {
+ private final Object m_componentName;
+ private final ToggleServiceDependency m_toggle;
+ private final AtomicBoolean m_startFlag;
+
+ public ComponentStopper(String componentName, ToggleServiceDependency toggle, AtomicBoolean startFlag)
+ {
+ m_componentName = componentName;
+ m_toggle = toggle;
+ m_startFlag = startFlag;
+ }
+
+ @SuppressWarnings("synthetic-access")
+ public void run()
+ {
+ if (m_startFlag.compareAndSet(true, false)) {
+ Log.instance().debug("Lifecycle controller is deactivating the component %s",
+ m_componentName);
+ m_toggle.activate(false);
+ }
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ToggleServiceDependency.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ToggleServiceDependency.java
new file mode 100644
index 0000000..43adcd3
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ToggleServiceDependency.java
@@ -0,0 +1,66 @@
+/*
+ * 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.runtime;
+
+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;
+
+/**
+ * This is a custom DependencyManager Dependency, allowing to take control of
+ * when the dependency is available or not. It's used in the context of the
+ * LifecycleController class, in order to activate/deactivate a Component on
+ * demand.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ToggleServiceDependency extends AbstractDependency<ToggleServiceDependency> {
+ public ToggleServiceDependency() {
+ super.setRequired(true);
+ }
+
+ public ToggleServiceDependency(ToggleServiceDependency prototype) {
+ super(prototype);
+ }
+
+ @Override
+ public DependencyContext createCopy() {
+ return new ToggleServiceDependency(this);
+ }
+
+ public void activate(boolean active) {
+ m_component.handleEvent(this, active ? EventType.ADDED : EventType.REMOVED, new Event(active));
+ }
+
+ @Override
+ public String getSimpleName() {
+ return "" + isAvailable();
+ }
+
+ @Override
+ public String getType() {
+ return "toggle";
+ }
+
+ @Override
+ public Class<?> getAutoConfigType() {
+ return null; // we don't support auto config mode.
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/ComponentException.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/ComponentException.java
new file mode 100644
index 0000000..1a05f6f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/ComponentException.java
@@ -0,0 +1,32 @@
+/*
+ * 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.runtime.api;
+
+/**
+ * Exception thrown when a Component can't be instantiated using a {@link ComponentFactory#newInstance(java.util.Dictionary)}
+ * service.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings("serial")
+public class ComponentException extends RuntimeException {
+ public ComponentException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/ComponentFactory.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/ComponentFactory.java
new file mode 100644
index 0000000..26e42d2
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/ComponentFactory.java
@@ -0,0 +1,39 @@
+/*
+ * 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.runtime.api;
+
+import java.util.Dictionary;
+
+/**
+ * When a Component is annotated with a DM "Component" annotation with a "factoryName" attribute, a corresponding
+ * ComponentFactory is registered in the OSGi service registry with a @link {@link ComponentFactory#FACTORY_NAME}
+ * servie property with the Component "factoryName" value.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ComponentFactory {
+ /**
+ * Instantiates a Component instance. Any properties starts with a "." are considered as private. Other properties will be
+ * published as the component instance service properties (if the component provides a services).
+ * @param conf the properties passed to the component "configure" method which is specified with the "configure" attribute
+ * of the @Component annotation.
+ * @return the component instance.
+ */
+ ComponentInstance newInstance(Dictionary<String, ?> conf);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/ComponentInstance.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/ComponentInstance.java
new file mode 100644
index 0000000..49ca071
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/ComponentInstance.java
@@ -0,0 +1,39 @@
+/*
+ * 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.runtime.api;
+
+import java.util.Dictionary;
+
+/**
+ * A Component instance created using a {@link ComponentFactory} service
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ComponentInstance {
+ /**
+ * Destroy the component instance.
+ */
+ void dispose();
+
+ /**
+ * Updates the component instance.
+ * @param conf the properties used to update the component.
+ */
+ void update(Dictionary<String, ?> conf);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/packageinfo b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/packageinfo
new file mode 100644
index 0000000..a4f1546
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/packageinfo
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/test/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.runtime/test/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/test/.gitignore
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/.classpath b/dependencymanager/org.apache.felix.dependencymanager.samples/.classpath
new file mode 100644
index 0000000..f89ae43
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/.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.samples/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.samples/.gitignore
new file mode 100644
index 0000000..90dde36
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/.gitignore
@@ -0,0 +1,3 @@
+/bin/
+/bin_test/
+/generated/
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/.project b/dependencymanager/org.apache.felix.dependencymanager.samples/.project
new file mode 100644
index 0000000..cd3ff99
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.apache.felix.dependencymanager.samples</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.samples/.settings/org.eclipse.jdt.core.prefs b/dependencymanager/org.apache.felix.dependencymanager.samples/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..7341ab1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/.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.samples/README.samples b/dependencymanager/org.apache.felix.dependencymanager.samples/README.samples
new file mode 100644
index 0000000..5ca10a6
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/README.samples
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+This sub-project contains some examples using the Dependency Manager Api and annotations.
+
+To execute the samples under bndtools, click on the "org.apache.felix.dependencymanager.samples" project,
+then Run as "Bnd OSGi Run Launcher".
+
+Each samples displays some logs using the OSGi log service.
+Just type:
+
+ log info
+
+To see a log for a given sample (for example org.apache.felix.dependencymanager.samples.device.api), just type:
+
+ log info|grep org.apache.felix.dependencymanager.samples.device.api
+
+For more informations on each sample, please refer to each README files in the sample source directories:
+
+ ./src/org/apache/felix/dependencymanager/samples/*/README
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/bnd.bnd b/dependencymanager/org.apache.felix.dependencymanager.samples/bnd.bnd
new file mode 100644
index 0000000..580f25f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/bnd.bnd
@@ -0,0 +1,59 @@
+#
+# 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.
+#
+Bundle-Version: 1.0.0
+-buildpath: \
+ org.apache.felix.dependencymanager;version=latest,\
+ osgi.core;version=4.2,\
+ osgi.cmpn;version=4.2,\
+ org.apache.felix.gogo.runtime;version=latest,\
+ org.apache.felix.dependencymanager.runtime;version=latest,\
+ org.apache.felix.dependencymanager.annotation;version=latest,\
+ biz.aQute.bnd.annotation
+-runfw: org.apache.felix.framework;version='[4.4.0,4.4.0]'
+-runee: JavaSE-1.7
+-runbundles: \
+ org.apache.felix.dependencymanager;version=latest,\
+ org.apache.felix.metatype;version=1.0.4,\
+ org.apache.felix.log;version=1.0.1,\
+ org.apache.felix.gogo.command;version=0.14.0,\
+ org.apache.felix.gogo.runtime;version=0.12.0,\
+ org.apache.felix.gogo.shell;version=0.10.0,\
+ org.apache.felix.dependencymanager.shell;version=latest,\
+ org.apache.felix.dependencymanager.runtime;version=latest,\
+ org.apache.felix.configadmin;version=1.8.0,\
+ org.apache.felix.eventadmin;version=1.4.3,\
+ biz.aQute.bndlib;version=2.3.0,\
+ org.apache.felix.webconsole;version=4.2.2,\
+ org.apache.felix.http.api;version=2.3.0,\
+ org.apache.felix.http.servlet-api;version=1.0.0,\
+ org.apache.felix.http.jetty;version=2.3.0,\
+ org.apache.felix.dependencymanager.samples.hello.api;version=latest,\
+ org.apache.felix.dependencymanager.samples.tpool;version=latest,\
+ org.apache.felix.dependencymanager.samples.conf;version=latest,\
+ org.apache.felix.dependencymanager.samples.device.api;version=latest
+-runproperties: \
+ org.apache.felix.dependencymanager.parallel='!org.apache.felix.dependencymanager.samples.tpool, *',\
+ org.apache.felix.dependencymanager.runtime.log=warn,\
+ org.apache.felix.dependencymanager.loglevel=2,\
+ org.apache.felix.log.maxSize=100000,\
+ org.apache.felix.log.storeDebug=true
+-plugin: org.apache.felix.dm.annotation.plugin.bnd.AnnotationPlugin;log=debug;\
+ path:=${workspace}/org.apache.felix.dependencymanager.annotation/generated/org.apache.felix.dependencymanager.annotation.jar
+
+-sub: \
+ *.bnd
+-metatype: *
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/composite.bnd b/dependencymanager/org.apache.felix.dependencymanager.samples/composite.bnd
new file mode 100644
index 0000000..9ce386f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/composite.bnd
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+Private-Package: \
+ org.apache.felix.dependencymanager.samples.composite
+Bundle-Activator: org.apache.felix.dependencymanager.samples.composite.Activator
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/compositefactory.bnd b/dependencymanager/org.apache.felix.dependencymanager.samples/compositefactory.bnd
new file mode 100644
index 0000000..e02104f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/compositefactory.bnd
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+Private-Package: \
+ org.apache.felix.dependencymanager.samples.compositefactory
+Bundle-Activator: org.apache.felix.dependencymanager.samples.compositefactory.Activator
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/conf.bnd b/dependencymanager/org.apache.felix.dependencymanager.samples/conf.bnd
new file mode 100644
index 0000000..b4cc885
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/conf.bnd
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+Private-Package: \
+ org.apache.felix.dependencymanager.samples.conf
+Bundle-Activator: org.apache.felix.dependencymanager.samples.conf.Activator
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/customdep.bnd b/dependencymanager/org.apache.felix.dependencymanager.samples/customdep.bnd
new file mode 100644
index 0000000..9a1206a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/customdep.bnd
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+Private-Package: \
+ org.apache.felix.dependencymanager.samples.customdep
+Bundle-Activator: org.apache.felix.dependencymanager.samples.customdep.Activator
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/device.annot.bnd b/dependencymanager/org.apache.felix.dependencymanager.samples/device.annot.bnd
new file mode 100644
index 0000000..39f5265
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/device.annot.bnd
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+Private-Package: \
+ org.apache.felix.dependencymanager.samples.device.annot
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/device.api.bnd b/dependencymanager/org.apache.felix.dependencymanager.samples/device.api.bnd
new file mode 100644
index 0000000..3f937ef
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/device.api.bnd
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+Private-Package: \
+ org.apache.felix.dependencymanager.samples.device.api
+Bundle-Activator: org.apache.felix.dependencymanager.samples.device.api.Activator
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/dictionary.annot.bnd b/dependencymanager/org.apache.felix.dependencymanager.samples/dictionary.annot.bnd
new file mode 100644
index 0000000..ebec7bb
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/dictionary.annot.bnd
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+Private-Package: \
+ org.apache.felix.dependencymanager.samples.dictionary.annot
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/dictionary.api.bnd b/dependencymanager/org.apache.felix.dependencymanager.samples/dictionary.api.bnd
new file mode 100644
index 0000000..6e0b0c7
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/dictionary.api.bnd
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+Private-Package: \
+ org.apache.felix.dependencymanager.samples.dictionary.api
+Bundle-Activator: org.apache.felix.dependencymanager.samples.dictionary.api.Activator
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/dynamicdep.annot.bnd b/dependencymanager/org.apache.felix.dependencymanager.samples/dynamicdep.annot.bnd
new file mode 100644
index 0000000..4865f28
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/dynamicdep.annot.bnd
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+Private-Package: \
+ org.apache.felix.dependencymanager.samples.dynamicdep.annot
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/dynamicdep.api.bnd b/dependencymanager/org.apache.felix.dependencymanager.samples/dynamicdep.api.bnd
new file mode 100644
index 0000000..b0471c8
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/dynamicdep.api.bnd
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+Bundle-Activator: org.apache.felix.dependencymanager.samples.dynamicdep.api.Activator
+Private-Package: \
+ org.apache.felix.dependencymanager.samples.dynamicdep.api
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/hello.annot.bnd b/dependencymanager/org.apache.felix.dependencymanager.samples/hello.annot.bnd
new file mode 100644
index 0000000..0bd078c
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/hello.annot.bnd
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+Private-Package: \
+ org.apache.felix.dependencymanager.samples.hello.annot
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/hello.api.bnd b/dependencymanager/org.apache.felix.dependencymanager.samples/hello.api.bnd
new file mode 100644
index 0000000..a7fa47c
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/hello.api.bnd
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+Private-Package: \
+ org.apache.felix.dependencymanager.samples.hello.api
+Bundle-Activator: org.apache.felix.dependencymanager.samples.hello.api.Activator
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.samples/src/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/.gitignore
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/composite/Activator.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/composite/Activator.java
new file mode 100644
index 0000000..e49c878
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/composite/Activator.java
@@ -0,0 +1,38 @@
+/*
+ * 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.dependencymanager.samples.composite;
+
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext ctx, DependencyManager m) throws Exception {
+ m.add(createComponent()
+ .setImplementation(ProviderImpl.class)
+ .setComposition("getComposition")
+ .add(createConfigurationDependency().setPid(ProviderImpl.class.getName()))
+ .add(createServiceDependency().setService(LogService.class).setRequired(true)));
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/composite/Provider.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/composite/Provider.java
new file mode 100644
index 0000000..96d917c
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/composite/Provider.java
@@ -0,0 +1,25 @@
+/*
+ * 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.dependencymanager.samples.composite;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface Provider {
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/composite/ProviderImpl.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/composite/ProviderImpl.java
new file mode 100644
index 0000000..a693e00
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/composite/ProviderImpl.java
@@ -0,0 +1,51 @@
+/*
+ * 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.dependencymanager.samples.composite;
+
+import java.util.Dictionary;
+
+import org.osgi.service.log.LogService;
+
+/**
+ * This is the main implementation for our "Provider" service.
+ * This service is using a composition of two participants, which are used to provide the service
+ * (ProviderParticipant1, and ProviderParticipant2).
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ProviderImpl implements Provider {
+ private final ProviderParticipant1 m_participant1 = new ProviderParticipant1();
+ private final ProviderParticipant2 m_participant2 = new ProviderParticipant2();
+ private volatile LogService m_log;
+ private Dictionary<String, String> m_conf;
+
+ public void updated(Dictionary<String, String> conf) throws Exception {
+ // validate configuration and throw an exception if the properties are invalid
+ m_conf = conf;
+ }
+
+ Object[] getComposition() {
+ return new Object[] { this, m_participant1, m_participant2 };
+ }
+
+ void start() {
+ m_log.log(LogService.LOG_INFO, "ProviderImpl.start(): participants=" + m_participant1 + "," + m_participant2
+ + ", conf=" + m_conf);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/composite/ProviderParticipant1.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/composite/ProviderParticipant1.java
new file mode 100644
index 0000000..c6d76f3
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/composite/ProviderParticipant1.java
@@ -0,0 +1,32 @@
+/*
+ * 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.dependencymanager.samples.composite;
+
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ProviderParticipant1 {
+ private volatile LogService m_log; // Injected
+
+ void start() {
+ m_log.log(LogService.LOG_INFO, "ProviderParticipant1.start()");
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/composite/ProviderParticipant2.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/composite/ProviderParticipant2.java
new file mode 100644
index 0000000..efa5b4e
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/composite/ProviderParticipant2.java
@@ -0,0 +1,32 @@
+/*
+ * 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.dependencymanager.samples.composite;
+
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ProviderParticipant2 {
+ private volatile LogService m_log; // Injected
+
+ void start() {
+ m_log.log(LogService.LOG_INFO, "ProviderParticipant2.start()");
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/composite/README b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/composite/README
new file mode 100644
index 0000000..11ad910
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/composite/README
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+This sample is an example usage of DM composite components. A composite component is implemented
+using a composition of multiple object instances, which are used to implement a given complex
+service. Here, we define a "Provider" service, which is implemented by three object instances:
+ProviderImpl, ProviderParticipant1, ProviderParticipant2.
+
+Dependencies are injected in all objects being part of the composition.
+
+To see logs, type this command under the gogo shell:
+
+g! log info|grep compositefactory
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/Activator.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/Activator.java
new file mode 100644
index 0000000..f3ac03e
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/Activator.java
@@ -0,0 +1,41 @@
+/*
+ * 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.dependencymanager.samples.compositefactory;
+
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext ctx, DependencyManager m) throws Exception {
+ CompositionManager compositionMngr = new CompositionManager();
+ m.add(createComponent()
+ .setFactory(compositionMngr, "create")
+ .setComposition(compositionMngr, "getComposition")
+ .add(createConfigurationDependency()
+ .setPid(CompositionManager.class.getName())
+ .setCallback(compositionMngr, "updated"))
+ .add(createServiceDependency().setService(LogService.class).setRequired(true)));
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/CompositionManager.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/CompositionManager.java
new file mode 100644
index 0000000..fb222ea
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/CompositionManager.java
@@ -0,0 +1,58 @@
+/*
+ * 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.dependencymanager.samples.compositefactory;
+
+import java.util.Dictionary;
+
+/**
+ * Pojo used to create all the objects composition used to implements the "Provider" Service.
+ * The manager is using a Configuration injected by Config Admin, in order to configure the
+ * various objects being part of the "Provider" service implementation.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class CompositionManager {
+ private ProviderParticipant1 m_participant1;
+ private ProviderParticipant2 m_participant2;
+ private ProviderImpl m_providerImpl;
+ @SuppressWarnings("unused")
+ private Dictionary<String, String> m_conf;
+
+ public void updated(Dictionary<String, String> conf) throws Exception {
+ // validate configuration and throw an exception if the properties are invalid
+ m_conf = conf;
+ }
+
+ /**
+ * Builds the composition of objects used to implement the "Provider" service.
+ * The Configuration injected by Config Admin will be used to configure the components
+ * @return The "main" object providing the "Provider" service.
+ */
+ Object create() {
+ // Here, we can instantiate our object composition and configure them using the injected Configuration ...
+ m_participant1 = new ProviderParticipant1(); // possibly configure this object using our configuration
+ m_participant2 = new ProviderParticipant2(); // possibly configure this object using our configuration
+ m_providerImpl = new ProviderImpl(m_participant1, m_participant2);
+ return m_providerImpl; // Main object implementing the Provider service
+ }
+
+ Object[] getComposition() {
+ return new Object[] { m_providerImpl, m_participant1, m_participant2 };
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/Provider.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/Provider.java
new file mode 100644
index 0000000..7b13596
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/Provider.java
@@ -0,0 +1,26 @@
+/*
+ * 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.dependencymanager.samples.compositefactory;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface Provider {
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/ProviderImpl.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/ProviderImpl.java
new file mode 100644
index 0000000..e88f653
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/ProviderImpl.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.dependencymanager.samples.compositefactory;
+
+import org.osgi.service.log.LogService;
+
+/**
+ * This is the main implementation for our "Provider" service.
+ * This service is using a composition of two participants, which are used to provide the service
+ * (ProviderParticipant1, and ProviderParticipant2).
+ *
+ * This class is instantiated by the CompositionManager class.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ProviderImpl implements Provider {
+ private final ProviderParticipant1 m_participant1;
+ private final ProviderParticipant2 m_participant2;
+
+ private volatile LogService m_log; // Injected
+
+ ProviderImpl(ProviderParticipant1 participant1, ProviderParticipant2 participant2) {
+ m_participant1 = participant1;
+ m_participant2 = participant2;
+ }
+
+ void start() {
+ m_log.log(LogService.LOG_INFO, "ProviderImpl.start(): participants=" + m_participant1 + "," + m_participant2);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/ProviderParticipant1.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/ProviderParticipant1.java
new file mode 100644
index 0000000..8397b13
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/ProviderParticipant1.java
@@ -0,0 +1,32 @@
+/*
+ * 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.dependencymanager.samples.compositefactory;
+
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ProviderParticipant1 {
+ private volatile LogService m_log; // Injected
+
+ void start() {
+ m_log.log(LogService.LOG_INFO, "ProviderParticipant1.start()");
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/ProviderParticipant2.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/ProviderParticipant2.java
new file mode 100644
index 0000000..61a9d45
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/ProviderParticipant2.java
@@ -0,0 +1,32 @@
+/*
+ * 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.dependencymanager.samples.compositefactory;
+
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ProviderParticipant2 {
+ private volatile LogService m_log; // Injected
+
+ void start() {
+ m_log.log(LogService.LOG_INFO, "ProviderParticipant2.start()");
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/README b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/README
new file mode 100644
index 0000000..5d886bb
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/compositefactory/README
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+This Activator is an example usage of DM composite components. A composite component is implemented
+using a composition of multiple object instances, which are used to implement a given service.
+
+The sample also uses a Factory approach in order to instantiate the composition of objects: A
+"CompositionManager" is first injected with a Configuration that can be possibly be used to create
+and configure all the composites.
+
+Dependencies are injected in all objects in the composition.
+
+To see logs, type this command under the gogo shell:
+
+g! log info|grep compositefactory
+
+
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/conf/Activator.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/conf/Activator.java
new file mode 100644
index 0000000..6498201
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/conf/Activator.java
@@ -0,0 +1,38 @@
+/*
+ * 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.dependencymanager.samples.conf;
+
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext context, DependencyManager dm) throws Exception {
+ dm.add(createComponent()
+ .setImplementation(Configurator.class)
+ .add(createServiceDependency().setService(LogService.class).setRequired(true))
+ .add(createServiceDependency().setService(ConfigurationAdmin.class).setRequired(true)));
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/conf/Configurator.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/conf/Configurator.java
new file mode 100644
index 0000000..151fba4
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/conf/Configurator.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.dependencymanager.samples.conf;
+
+import java.io.IOException;
+import java.util.Hashtable;
+
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.log.LogService;
+
+/**
+ * Configurator class used to inject configuration into Configuration Admin Service.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Configurator {
+ private volatile ConfigurationAdmin m_ca;
+ volatile Configuration m_serviceConsumerConf;
+ volatile Configuration m_serviceConsumerAnnotConf;
+ volatile LogService m_log;
+
+ public void start() {
+ try {
+ System.out.println("Configuring sample components ... please consult log service messages for each sample you want to play with.");
+ System.out.println("For example: \"log info|grep org.apache.felix.dependencymanager.samples.device.api\"");
+ // Provide configuration to the hello.ServiceConsumer component
+ m_serviceConsumerConf = m_ca.getConfiguration("org.apache.felix.dependencymanager.samples.hello.api.ServiceConsumer", null);
+ Hashtable<String, String> props = new Hashtable<>();
+ props.put("key", "value");
+ m_serviceConsumerConf.update(props);
+
+ // Provide configuration to the hello.annot.ServiceConsumer component
+ m_serviceConsumerAnnotConf = m_ca.getConfiguration("org.apache.felix.dependencymanager.samples.hello.annot.ServiceConsumer", null);
+ props = new Hashtable<>();
+ props.put("key", "value");
+ m_serviceConsumerAnnotConf.update(props);
+
+ // Provide configuration to the composite component
+ m_serviceConsumerAnnotConf = m_ca.getConfiguration("org.apache.felix.dependencymanager.samples.composite.ProviderImpl", null);
+ props = new Hashtable<>();
+ props.put("key", "value");
+ m_serviceConsumerAnnotConf.update(props);
+
+ // Provide configuration to the compositefactory component
+ m_serviceConsumerAnnotConf = m_ca.getConfiguration("org.apache.felix.dependencymanager.samples.compositefactory.CompositionManager", null);
+ props = new Hashtable<>();
+ props.put("key", "value");
+ m_serviceConsumerAnnotConf.update(props);
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void destroy() throws IOException {
+ m_serviceConsumerConf.delete();
+ m_serviceConsumerAnnotConf.delete();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/conf/README b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/conf/README
new file mode 100644
index 0000000..b51f853
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/conf/README
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+This sample defines a component that is used to inject configuration into the Configuration Admin
+service, in order to configure other components declared in the various samples.
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/customdep/Activator.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/customdep/Activator.java
new file mode 100644
index 0000000..3a6607f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/customdep/Activator.java
@@ -0,0 +1,41 @@
+/*
+ * 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.dependencymanager.samples.customdep;
+
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Activator extends DependencyActivatorBase {
+ PathDependency createPathDependency(String path) {
+ return new PathDependencyImpl(path);
+ }
+
+ @Override
+ public void init(BundleContext context, DependencyManager m) throws Exception {
+ m.add(createComponent()
+ .setImplementation(PathTracker.class)
+ .add(createServiceDependency().setService(LogService.class).setRequired(true))
+ .add(createPathDependency("/tmp").setCallbacks("add", "remove").setRequired(true)));
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/customdep/PathDependency.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/customdep/PathDependency.java
new file mode 100644
index 0000000..f05fe79
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/customdep/PathDependency.java
@@ -0,0 +1,33 @@
+/*
+ * 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.dependencymanager.samples.customdep;
+
+import org.apache.felix.dm.Dependency;
+
+/**
+ * A custom Dependency Manager Path Dependency that can track a path directory.
+ * When a file is added or removed from the path dir, then the component is called
+ * in the corresponding add/remove callback.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface PathDependency extends Dependency {
+ PathDependency setRequired(boolean required);
+ PathDependency setCallbacks(String add, String remove);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/customdep/PathDependencyImpl.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/customdep/PathDependencyImpl.java
new file mode 100644
index 0000000..2b5edb4
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/customdep/PathDependencyImpl.java
@@ -0,0 +1,179 @@
+/*
+ * 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.dependencymanager.samples.customdep;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardWatchEventKinds;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchEvent.Kind;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.util.List;
+
+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;
+
+/**
+ * This is our own "path" Dependency Manager Dependency, which can track the presence of files in a given path dir.
+ * Every DM custom dependency must implement the DependencyContext interface, but we extends the AbstractDependency
+ * which already implements most of the DependencyContext methods.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class PathDependencyImpl extends AbstractDependency<PathDependencyImpl> implements PathDependency, Runnable {
+ private final String m_path;
+ private volatile Thread m_thread;
+
+ /**
+ * Creates a new custom DM "path" dependency.
+ * @param path the directory to watch for
+ */
+ public PathDependencyImpl(String path) {
+ super.setRequired(true);
+ m_path = path;
+ }
+
+ /**
+ * Create a new PathDependency from an existing prototype.
+ * @param prototype the existing PathDependency.
+ */
+ public PathDependencyImpl(PathDependencyImpl prototype) {
+ super(prototype);
+ m_path = prototype.m_path;
+ }
+
+ // ---------- DependencyContext interface ----------
+
+ @Override
+ public DependencyContext createCopy() {
+ return new PathDependencyImpl(this);
+ }
+
+ @Override
+ public Class<?> getAutoConfigType() {
+ return null; // we don't support auto config mode
+ }
+
+ @Override
+ public void start() {
+ m_thread = new Thread(this);
+ m_thread.start();
+ super.start();
+ }
+
+ @Override
+ public void stop() {
+ m_thread.interrupt();
+ super.stop();
+ }
+
+ @Override
+ public void invokeCallback(EventType type, Event ...events) {
+ switch (type) {
+ case ADDED:
+ if (m_add != null) {
+ invoke(m_add, events[0], getInstances());
+ }
+ break;
+ case REMOVED:
+ if (m_remove != null) {
+ invoke(m_remove, events[0], getInstances());
+ }
+ break;
+ default:
+ // We don't support other kind of callbacks.
+ break;
+ }
+ }
+
+ // ---------- ComponentDependencyDeclaration interface -----------
+
+ /**
+ * Returns the name of this dependency (a generic name with optional info separated by spaces).
+ * The DM Shell will use this method when displaying the dependency
+ **/
+ @Override
+ public String getSimpleName() {
+ return m_path;
+ }
+
+ /**
+ * Returns the name of the type of this dependency. Used by the DM shell when displaying the dependency.
+ **/
+ @Override
+ public String getType() {
+ return "path";
+ }
+
+ // ---------- other methods -----------
+
+ /**
+ * Our start method fires a thread and this is our run method, which is watching for a given directory path
+ */
+ public void run() {
+ Path myDir = Paths.get(m_path);
+
+ try {
+ WatchService watcher = myDir.getFileSystem().newWatchService();
+ myDir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY,
+ StandardWatchEventKinds.ENTRY_DELETE);
+ while (! Thread.currentThread().isInterrupted()) {
+ WatchKey watckKey = watcher.take();
+
+ List<WatchEvent<?>> events = watckKey.pollEvents();
+
+ for (@SuppressWarnings("rawtypes") WatchEvent event : events) {
+ final Kind<?> kind = event.kind();
+ if (StandardWatchEventKinds.OVERFLOW == kind) {
+ continue;
+ }
+ if (StandardWatchEventKinds.ENTRY_CREATE == kind) {
+ // Notify the component implementation context that a file has been created.
+ // Later, the component will call our invokeAdd method in order to inject the file
+ // in the component instance
+ m_component.handleEvent(this, EventType.ADDED, new Event(event.context().toString()));
+ } else if (StandardWatchEventKinds.ENTRY_DELETE == kind) {
+ // Notify the component implementation context that a file has been removed.
+ // Later, the component will call our invokeRemove method in order to call our component "remove" callback
+ m_component.handleEvent(this, EventType.REMOVED, new Event(event.context().toString()));
+ }
+ }
+
+ watckKey.reset();
+ }
+ } catch (Throwable e) {
+ m_component.getLogger().err("path dependency exception", e);
+ }
+ }
+
+ /**
+ * Invoke either the "add" or "remove" callback of the component instance(s).
+ */
+ private void invoke(String method, Event e, Object[] instances) {
+ // specific for this type of dependency
+ m_component.invokeCallbackMethod(instances, method,
+ new Class[][] { {String.class},
+ {}},
+ new Object[][] { { e.getEvent() },
+ {}});
+ }
+}
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/customdep/PathTracker.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/customdep/PathTracker.java
new file mode 100644
index 0000000..bd6c7b7
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/customdep/PathTracker.java
@@ -0,0 +1,41 @@
+/*
+ * 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.dependencymanager.samples.customdep;
+
+import org.osgi.service.log.LogService;
+
+public class PathTracker {
+ volatile LogService logService;
+
+ void start() {
+ logService.log(LogService.LOG_INFO, "PathTracker.start");
+ }
+
+ void stop() {
+ logService.log(LogService.LOG_INFO, "PathTracker.stop");
+ }
+
+ void add(String path) {
+ logService.log(LogService.LOG_INFO, "PathTracker.add: " + path);
+ }
+
+ void remove(String path) {
+ logService.log(LogService.LOG_INFO, "PathTracker.remove: " + path);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/customdep/README b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/customdep/README
new file mode 100644
index 0000000..7be7d6e
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/customdep/README
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+This directory contains an example of a custom dependency (PathDependency).
+This dependency tracks all created files in /tmp and injects them in the PathTracker.add(String path) method ...
+
+The PathDependendency is a low level DM example, but shows how to create any custom dependencies.
+
+To test this sample, start the samples under bndtools, then type:
+
+ g! log info|grep customdep
+
+Then create a "test" file under /tmp:
+
+ $ echo "test" > /tmp/test
+
+Then redisplay logs (your Tracker component has normally been injected with the added file and has been started):
+
+ g! log info|grep customdep
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/Device.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/Device.java
new file mode 100644
index 0000000..b08062a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/Device.java
@@ -0,0 +1,26 @@
+/*
+ * 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.dependencymanager.samples.device.annot;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface Device {
+ int getDeviceId();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceAccess.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceAccess.java
new file mode 100644
index 0000000..2f10133
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceAccess.java
@@ -0,0 +1,30 @@
+/*
+ * 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.dependencymanager.samples.device.annot;
+
+/**
+ * Provides unified access to a pair of Device/DeviceParameter services having the same device ID.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface DeviceAccess {
+ Device getDevice();
+
+ DeviceParameter getDeviceParameter();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceAccessConsumer.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceAccessConsumer.java
new file mode 100644
index 0000000..0da433a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceAccessConsumer.java
@@ -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.
+ */
+package org.apache.felix.dependencymanager.samples.device.annot;
+
+import java.util.Map;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Component
+public class DeviceAccessConsumer {
+ @ServiceDependency
+ volatile LogService log;
+
+ // Injected afer all required dependencies have been injected (including our logger)
+ @ServiceDependency(required=false)
+ void add(Map<String, Object> props, DeviceAccess deviceAccess) {
+ log.log(LogService.LOG_INFO, "Handling device access: id=" + props.get("device.id")
+ + "\n\t device=" + deviceAccess.getDevice()
+ + "\n\t device parameter=" + deviceAccess.getDeviceParameter()
+ + "\n\t device access properties=" + props);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceAccessImpl.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceAccessImpl.java
new file mode 100644
index 0000000..669f306
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceAccessImpl.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager.samples.device.annot;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.apache.felix.dm.annotation.api.AdapterService;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@AdapterService(adapteeService = Device.class)
+public class DeviceAccessImpl implements DeviceAccess {
+ volatile Device device;
+
+ @ServiceDependency(name = "deviceparam")
+ volatile DeviceParameter deviceParameter;
+
+ @ServiceDependency
+ volatile LogService log;
+
+ @Init
+ Map<String, String> init() {
+ log.log(LogService.LOG_INFO, "DeviceAccessImpl.init: device id=" + device.getDeviceId());
+ // Dynamically configure our "deviceparam" dependency, using the already injected device service.
+ Map<String, String> filters = new HashMap<>();
+ filters.put("deviceparam.filter", "(device.id=" + device.getDeviceId() + ")");
+ filters.put("deviceparam.required", "true");
+ return filters;
+ }
+
+ @Start
+ Map<?, ?> start() {
+ log.log(LogService.LOG_INFO, "DeviceAccessImpl.start");
+ // Dynamically add a service property, using the device.id
+ Map<String, Object> props = new Hashtable<>();
+ props.put("device.access.id", device.getDeviceId());
+ return props;
+ }
+
+ @Override
+ public Device getDevice() {
+ return device;
+ }
+
+ @Override
+ public DeviceParameter getDeviceParameter() {
+ return deviceParameter;
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceAndParameterFactory.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceAndParameterFactory.java
new file mode 100644
index 0000000..2d77dd1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceAndParameterFactory.java
@@ -0,0 +1,65 @@
+/*
+ * 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.dependencymanager.samples.device.annot;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.runtime.api.ComponentFactory;
+import org.osgi.service.log.LogService;
+
+/**
+ * Component used to instantiate Device and DeviceParameter services, using DM component factory annotation.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Component
+public class DeviceAndParameterFactory {
+ @ServiceDependency(filter = "(" + Component.FACTORY_NAME + "=Device)")
+ volatile ComponentFactory m_deviceFactory;
+
+ @ServiceDependency(filter = "(" + Component.FACTORY_NAME + "=DeviceParameter)")
+ volatile ComponentFactory m_deviceParameterFactory;
+
+ @ServiceDependency
+ volatile LogService log;
+
+ @Start
+ public void start() {
+ log.log(LogService.LOG_INFO, "DeviceAndParameterFactory.start");
+ for (int i = 0; i < 2; i++) {
+ createDeviceAndParameter(i);
+ }
+ }
+
+ private void createDeviceAndParameter(int id) {
+ log.log(LogService.LOG_INFO, "DeviceAndParameterFactory: creating Device/DeviceParameter with id=" + id);
+
+ Dictionary<String, Object> device = new Hashtable<>();
+ device.put("device.id", new Integer(id));
+ m_deviceFactory.newInstance(device);
+
+ Dictionary<String, Object> param = new Hashtable<>();
+ param.put("device.id", new Integer(id));
+ m_deviceParameterFactory.newInstance(param);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceImpl.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceImpl.java
new file mode 100644
index 0000000..834043f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceImpl.java
@@ -0,0 +1,40 @@
+/*
+ * 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.dependencymanager.samples.device.annot;
+
+import java.util.Dictionary;
+
+import org.apache.felix.dm.annotation.api.Component;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Component(factoryName = "Device", factoryConfigure = "configure")
+public class DeviceImpl implements Device {
+ int id;
+
+ void configure(Dictionary<String, Object> configuration) {
+ this.id = (Integer) configuration.get("device.id");
+ }
+
+ @Override
+ public int getDeviceId() {
+ return id;
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceParameter.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceParameter.java
new file mode 100644
index 0000000..81f2705
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceParameter.java
@@ -0,0 +1,26 @@
+/*
+ * 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.dependencymanager.samples.device.annot;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface DeviceParameter {
+ int getDeviceId();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceParameterImpl.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceParameterImpl.java
new file mode 100644
index 0000000..07f4b4b
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/DeviceParameterImpl.java
@@ -0,0 +1,40 @@
+/*
+ * 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.dependencymanager.samples.device.annot;
+
+import java.util.Dictionary;
+
+import org.apache.felix.dm.annotation.api.Component;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Component(factoryName = "DeviceParameter", factoryConfigure = "configure")
+public class DeviceParameterImpl implements DeviceParameter {
+ int id;
+
+ void configure(Dictionary<String, Object> configuration) {
+ this.id = (Integer) configuration.get("device.id");
+ }
+
+ @Override
+ public int getDeviceId() {
+ return id;
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/README b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/README
new file mode 100644
index 0000000..63b8436
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/annot/README
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+This is an example showing a Dependency Manager "Adapter" in action, using DM annotations. Two kinds
+of services are registered in the registry: some Device, and some DeviceParameter services. For each
+Device (having a given id), there is also a corresponding "DeviceParameter" service, having the same
+id.
+
+Then a "DeviceAccessImpl" adapter service is defined: it is used to "adapt" the "Device" service to
+a "DeviceAccess" service, which provides the union of each pair of Device/DeviceParameter having the
+same device.id . The adapter also dynamically propagate the service properties of the adapted Device
+service.
+
+So see logs, just type this command under gogo shell:
+
+g! log info|grep device.annot
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/Activator.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/Activator.java
new file mode 100644
index 0000000..7dbc6f1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/Activator.java
@@ -0,0 +1,58 @@
+/*
+ * 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.dependencymanager.samples.device.api;
+
+import java.util.Hashtable;
+
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext context, DependencyManager dm) throws Exception {
+ createDeviceAndParameter(dm, 1);
+ createDeviceAndParameter(dm, 2);
+
+ dm.add(createAdapterService(Device.class, null)
+ .setImplementation(DeviceAccessImpl.class)
+ .setInterface(DeviceAccess.class.getName(), null));
+
+ dm.add(createComponent()
+ .setImplementation(DeviceAccessConsumer.class)
+ .add(createServiceDependency().setService(LogService.class).setRequired(true))
+ .add(createServiceDependency().setService(DeviceAccess.class).setRequired(true).setCallbacks("add", null)));
+ }
+
+ private void createDeviceAndParameter(DependencyManager dm, int id) {
+ Hashtable<String, Object> props = new Hashtable<>();
+ props.put("device.id", id);
+ dm.add(createComponent()
+ .setImplementation(new DeviceImpl(id)).setInterface(Device.class.getName(), props));
+
+ props = new Hashtable<>();
+ props.put("device.id", id);
+ dm.add(createComponent()
+ .setImplementation(new DeviceParameterImpl(id)).setInterface(DeviceParameter.class.getName(), props));
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/Device.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/Device.java
new file mode 100644
index 0000000..0903ff5
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/Device.java
@@ -0,0 +1,26 @@
+/*
+ * 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.dependencymanager.samples.device.api;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface Device {
+ int getDeviceId();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/DeviceAccess.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/DeviceAccess.java
new file mode 100644
index 0000000..fd52061
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/DeviceAccess.java
@@ -0,0 +1,30 @@
+/*
+ * 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.dependencymanager.samples.device.api;
+
+/**
+ * Provides unified access to a pair of Device/DeviceParameter services having the same device ID.
+
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface DeviceAccess {
+ Device getDevice();
+
+ DeviceParameter getDeviceParameter();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/DeviceAccessConsumer.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/DeviceAccessConsumer.java
new file mode 100644
index 0000000..ce3d400
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/DeviceAccessConsumer.java
@@ -0,0 +1,37 @@
+/*
+ * 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.dependencymanager.samples.device.api;
+
+import java.util.Map;
+
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DeviceAccessConsumer {
+ volatile LogService log;
+
+ void add(Map<String, Object> props, DeviceAccess deviceAccess) {
+ log.log(LogService.LOG_INFO, "DeviceAccessConsumer: Handling device access: id=" + props.get("device.id")
+ + "\n\t device=" + deviceAccess.getDevice()
+ + "\n\t device parameter=" + deviceAccess.getDeviceParameter()
+ + "\n\t device access properties=" + props);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/DeviceAccessImpl.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/DeviceAccessImpl.java
new file mode 100644
index 0000000..29b4b2a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/DeviceAccessImpl.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager.samples.device.api;
+
+import java.util.Hashtable;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DeviceAccessImpl implements DeviceAccess {
+ volatile Device device;
+ volatile DeviceParameter deviceParameter;
+
+ void init(Component c) {
+ // Dynamically add an extra dependency on a DeviceParameter.
+ DependencyManager dm = c.getDependencyManager();
+ c.add(dm.createServiceDependency().setService(DeviceParameter.class, "(device.id=" + device.getDeviceId() + ")").setRequired(
+ true));
+ }
+
+ void start(Component c) {
+ // Our service is starting: before being registered in the OSGi service registry,
+ // add here a service property, using the device.id.
+ Hashtable<String, Object> props = new Hashtable<>();
+ props.put("device.access.id", device.getDeviceId());
+ c.setServiceProperties(props);
+ }
+
+ @Override
+ public Device getDevice() {
+ return device;
+ }
+
+ @Override
+ public DeviceParameter getDeviceParameter() {
+ return deviceParameter;
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/DeviceImpl.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/DeviceImpl.java
new file mode 100644
index 0000000..969fba3
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/DeviceImpl.java
@@ -0,0 +1,35 @@
+/*
+ * 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.dependencymanager.samples.device.api;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DeviceImpl implements Device {
+ final int id;
+
+ public DeviceImpl(int id) {
+ this.id = id;
+ }
+
+ @Override
+ public int getDeviceId() {
+ return id;
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/DeviceParameter.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/DeviceParameter.java
new file mode 100644
index 0000000..0f8a39e
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/DeviceParameter.java
@@ -0,0 +1,26 @@
+/*
+ * 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.dependencymanager.samples.device.api;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface DeviceParameter {
+ int getDeviceId();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/DeviceParameterImpl.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/DeviceParameterImpl.java
new file mode 100644
index 0000000..f6da3af
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/DeviceParameterImpl.java
@@ -0,0 +1,35 @@
+/*
+ * 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.dependencymanager.samples.device.api;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DeviceParameterImpl implements DeviceParameter {
+ final int id;
+
+ public DeviceParameterImpl(int id) {
+ this.id = id;
+ }
+
+ @Override
+ public int getDeviceId() {
+ return id;
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/README b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/README
new file mode 100644
index 0000000..d013c28
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/device/api/README
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+This is an example showing a Dependency Manager "Adapter" in action. Two kinds of services are
+registered in the registry: some Device, and some DeviceParameter services. For each Device (having
+a given id), there is also a corresponding "DeviceParameter" service, having the same id.
+
+Then a "DeviceAccessImpl" adapter service is defined: it is used to "adapt" the "Device" service to
+a "DeviceAccess" service, which provides the union of each pair of Device/DeviceParameter having the
+same device.id . The adapter also dynamically propagate the service properties of the adapted Device
+service.
+
+So see logs, just type this command under gogo shell:
+
+g! log info|grep device.api
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/DictionaryAspect.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/DictionaryAspect.java
new file mode 100644
index 0000000..77c3f49
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/DictionaryAspect.java
@@ -0,0 +1,93 @@
+/*
+ * 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.dependencymanager.samples.dictionary.annot;
+
+import java.util.Dictionary;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.felix.dm.annotation.api.AspectService;
+import org.apache.felix.dm.annotation.api.ConfigurationDependency;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.osgi.service.log.LogService;
+
+import aQute.bnd.annotation.metatype.Configurable;
+
+/**
+ * This aspect applies to the English DictionaryService, and allows to decorate it with some
+ * custom English words, which are configurable from WebConsole.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@AspectService(ranking = 10, filter = "(lang=en)")
+public class DictionaryAspect implements DictionaryService {
+ /**
+ * This is the service this aspect is applying to.
+ */
+ private volatile DictionaryService m_originalDictionary;
+
+ /**
+ * We store all configured words in a thread-safe data structure, because ConfigAdmin may
+ * invoke our updated method at any time.
+ */
+ private CopyOnWriteArrayList<String> m_words = new CopyOnWriteArrayList<String>();
+
+ /**
+ * We'll use the OSGi log service for logging. If no log service is available, then we'll
+ * use a NullObject.
+ */
+ @ServiceDependency(required = false)
+ private LogService m_log;
+
+ /**
+ * Defines a configuration dependency for retrieving our english custom words (by default,
+ * our PID is our full class name).
+ */
+ @ConfigurationDependency(pidClass = DictionaryAspectConfiguration.class, propagate = false)
+ protected void updated(Dictionary<String, ?> config) {
+ if (config != null) {
+ // We use the bnd "Configurable" helper in order to get an implementation for our DictionaryConfiguration interface.
+ DictionaryConfiguration cnf = Configurable.createConfigurable(
+ DictionaryConfiguration.class, config);
+ m_words.clear();
+ for (String word : cnf.words()) {
+ m_words.add(word);
+ }
+ }
+ }
+
+ /**
+ * Our Aspect Service is starting and is about to be registered in the OSGi regsitry.
+ */
+ @Start
+ protected void start() {
+ m_log.log(LogService.LOG_INFO, "Starting aspect Dictionary with words: " + m_words
+ + "; original dictionary service=" + m_originalDictionary);
+ }
+
+ /**
+ * Checks if a word is found from our custom word list. if not, delegate to the decorated
+ * dictionary.
+ */
+ public boolean checkWord(String word) {
+ m_log.log(LogService.LOG_INFO, "DictionaryAspect: checking word " + word + " (original dictionary="
+ + m_originalDictionary + ")");
+ if (m_words.contains(word)) {
+ return true;
+ }
+ return m_originalDictionary.checkWord(word);
+ }
+
+ public String toString() {
+ return "DictionaryAspect: words=" + m_words + "; original dictionary=" + m_originalDictionary;
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/DictionaryAspectConfiguration.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/DictionaryAspectConfiguration.java
new file mode 100644
index 0000000..5bfc6c0
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/DictionaryAspectConfiguration.java
@@ -0,0 +1,37 @@
+/*
+ * 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.dependencymanager.samples.dictionary.annot;
+
+import java.util.List;
+
+import aQute.bnd.annotation.metatype.Meta.AD;
+import aQute.bnd.annotation.metatype.Meta.OCD;
+
+/**
+ * This interface describes the configuration for our DictionaryAspect component. We are using the bnd metatype
+ * annotations, allowing to configure our Dictionary Services from web console.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@OCD(name="Spell Checker Aspect Dictionary (annotation)",
+ description = "Declare here the list of english words to be added into the default english dictionary")
+public interface DictionaryAspectConfiguration {
+ @AD(description = "Dictionary aspect words")
+ List<String> words();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/DictionaryConfiguration.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/DictionaryConfiguration.java
new file mode 100644
index 0000000..b853826
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/DictionaryConfiguration.java
@@ -0,0 +1,41 @@
+/*
+ * 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.dependencymanager.samples.dictionary.annot;
+
+import java.util.List;
+
+import aQute.bnd.annotation.metatype.Meta.AD;
+import aQute.bnd.annotation.metatype.Meta.OCD;
+
+/**
+ * This interface describes the configuration for our DictionaryImpl component. We are using the bnd metatype
+ * annotations, allowing to configure our Dictionary Services from web console.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@OCD(name="Spell Checker Dictionary (annotation)",
+ factory = true,
+ description = "Declare here some Dictionary instances, allowing to instantiates some DictionaryService services for a given dictionary language")
+public interface DictionaryConfiguration {
+ @AD(description = "Describes the dictionary language", deflt = "en")
+ String lang();
+
+ @AD(description = "Declare here the list of words supported by this dictionary. This properties starts with a Dot and won't be propagated with Dictionary OSGi service properties")
+ List<String> words();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/DictionaryImpl.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/DictionaryImpl.java
new file mode 100644
index 0000000..995b9ea
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/DictionaryImpl.java
@@ -0,0 +1,98 @@
+/*
+ * 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.dependencymanager.samples.dictionary.annot;
+
+import java.util.Dictionary;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.felix.dm.annotation.api.FactoryConfigurationAdapterService;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.osgi.service.log.LogService;
+
+import aQute.bnd.annotation.metatype.Configurable;
+
+/**
+ * A Dictionary Service. This service uses a FactoryConfigurationAdapterService annotation,
+ * allowing to instantiate this service from webconsole. This annotation will actually register
+ * a ManagedServiceFactory in the registry. The Configuration metatype informations is described using the
+ * bnd metatype information (see the DictionaryConfiguration interface).
+ *
+ * You must configure at least one Dictionary from web console, since the SpellCheck won't start if no Dictionary
+ * Service is available.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@FactoryConfigurationAdapterService(factoryPidClass = DictionaryConfiguration.class, propagate = true, updated = "updated")
+public class DictionaryImpl implements DictionaryService {
+ /**
+ * We store all configured words in a thread-safe data structure, because ConfigAdmin
+ * may invoke our updated method at any time.
+ */
+ private CopyOnWriteArrayList<String> m_words = new CopyOnWriteArrayList<String>();
+
+ /**
+ * We'll use the OSGi log service for logging. If no log service is available, then we'll use a NullObject.
+ */
+ @ServiceDependency(required = false)
+ private LogService m_log;
+
+ /**
+ * Our Dictionary language.
+ */
+ private String m_lang;
+
+ /**
+ * Our service will be initialized from ConfigAdmin.
+ * @param config The configuration where we'll lookup our words list (key=".words").
+ */
+ protected void updated(Dictionary<String, ?> config) {
+ if (config != null) {
+ // We use the bnd "Configurable" helper in order to get an implementation for our DictionaryConfiguration interface.
+ DictionaryConfiguration cnf = Configurable.createConfigurable(DictionaryConfiguration.class, config);
+
+ m_lang = cnf.lang();
+ m_words.clear();
+ for (String word : cnf.words()) {
+ m_words.add(word);
+ }
+ }
+ }
+
+ /**
+ * A new Dictionary Service is starting (because a new factory configuration has been created
+ * from webconsole).
+ */
+ @Start
+ protected void start() {
+ m_log.log(LogService.LOG_INFO, "Starting Dictionary Service with language: " + m_lang);
+ }
+
+ /**
+ * Check if a word exists if the list of words we have been configured from ConfigAdmin/WebConsole.
+ */
+ public boolean checkWord(String word) {
+ return m_words.contains(word);
+ }
+
+ @Override
+ public String toString() {
+ return "Dictionary: language=" + m_lang + ", words=" + m_words;
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/DictionaryService.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/DictionaryService.java
new file mode 100644
index 0000000..ea498ec
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/DictionaryService.java
@@ -0,0 +1,35 @@
+/*
+ * 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.dependencymanager.samples.dictionary.annot;
+
+/**
+ * A simple service interface that defines a dictionary service. A dictionary
+ * service simply verifies the existence of a word.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface DictionaryService {
+ /**
+ * Check for the existence of a word.
+ *
+ * @param word the word to be checked.
+ * @return true if the word is in the dictionary, false otherwise.
+ */
+ public boolean checkWord(String word);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/README b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/README
new file mode 100644
index 0000000..d622672
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/README
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+This sample shows a "SpellChecker" application (using DM Annotations) which provides a
+"dictionary.annotation:spellcheck" GOGO shell command. The GOGO "dictionary.annotation:spellcheck" command accepts a
+string as parameter, which is checked for proper existence. The SpellChecker class has a
+required/multiple (1..N) dependency over every available "DictionaryService" services, which are
+internally used by the SpellChecker command, when checking word existence.
+
+A DictionaryService is defined using a FactoryConfigurationAdapterService , allowing to instantiate
+many "DictionaryService" instances when some configurations are added to the
+"Spell Checker Dictionary (annotation)" factory pid from web console.
+The factory pid configuration metatypes are defined using the bnd "metatype" annotations
+(see DictionaryConfiguration.java).
+
+The DictionaryService is decorated with a DictionaryAspect, which you can instantiate by adding a
+configuration to the "Spell Checker Aspect Dictionary (annotation)" factory pid from web console. The
+aspect configuration metatype is also declared using the bnd metatype annotations (see
+DictionaryAspectConfiguration.java).
+
+Before running this sample, go to webconsole, and add some words in the Spell Checker Configuration (annotation) factory PID, and
+in the Spell Checker Aspect Dictionary (annotation) PID.
+
+Then go to gogo shell, and type dm help. You will normally see the dictionary.annotation:spellcheck command.
+Type dictionary.annotation:spellcheck <some words configured either in the spell checker annotation configuration, or in
+the spell checker annotation aspect configuration, and the dictionary will check for proper word existance in the configuration.
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/SpellChecker.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/SpellChecker.java
new file mode 100644
index 0000000..c420ad8
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/annot/SpellChecker.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.dependencymanager.samples.dictionary.annot;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.Descriptor;
+import org.osgi.service.log.LogService;
+
+/**
+ * Felix "spellcheck" Gogo Shell Command. This command allows to check if some given words are valid or not.
+ * This command will be activated only if (at least) one DictionaryService has been injected.
+ * To create a Dictionary Service, you have to go the the web console and define a "Dictionary Services" factory
+ * configuration instance, which will fire an instantiation of the corresponding dictionary service.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Component(provides = { SpellChecker.class }, properties = {
+ @Property(name = CommandProcessor.COMMAND_SCOPE, value = "dictionary.annotation"),
+ @Property(name = CommandProcessor.COMMAND_FUNCTION, value = "spellcheck" ) })
+public class SpellChecker {
+ /**
+ * We'll use the OSGi log service for logging. If no log service is available, then we'll use a NullObject.
+ */
+ @ServiceDependency(required = false)
+ private volatile LogService m_log;
+
+ /**
+ * We'll store all Dictionaries in a concurrent list, in order to avoid method synchronization.
+ */
+ @ServiceDependency(service = DictionaryService.class)
+ private final Iterable<DictionaryService> m_dictionaries = new ConcurrentLinkedQueue<>();
+
+ /**
+ * Lifecycle method callback, used to check if our service has been activated.
+ */
+ @Start
+ protected void start() {
+ m_log.log(LogService.LOG_WARNING, "Spell Checker started");
+ }
+
+ /**
+ * Lifecycle method callback, used to check if our service has been activated.
+ */
+ @Stop
+ protected void stop() {
+ m_log.log(LogService.LOG_WARNING, "Spell Checker stopped");
+ }
+
+ // --- Gogo Shell command
+
+ @Descriptor("checks if word is found from an available dictionary")
+ public void spellcheck(@Descriptor("the word to check") String word) {
+ m_log.log(LogService.LOG_INFO, "Checking spelling of word \"" + word + "\" using the following dictionaries: "
+ + m_dictionaries);
+
+ for (DictionaryService dictionary : m_dictionaries) {
+ if (dictionary.checkWord(word)) {
+ System.out.println("word " + word + " is correct");
+ return;
+ }
+ }
+ System.err.println("word " + word + " is incorrect");
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/Activator.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/Activator.java
new file mode 100644
index 0000000..011da46
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/Activator.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager.samples.dictionary.api;
+
+import java.util.Hashtable;
+
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.service.command.CommandProcessor;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext context, DependencyManager dm) throws Exception {
+ // Create the factory configuration for our DictionaryImpl service.
+ dm.add(createFactoryConfigurationAdapterService(DictionaryConfiguration.class.getName(), "updated", true)
+ .setInterface(DictionaryService.class.getName(), null)
+ .setImplementation(DictionaryImpl.class)
+ .add(createServiceDependency().setService(LogService.class))); // NullObject
+
+ // Create the Dictionary Aspect
+ dm.add(createAspectService(DictionaryService.class, "(lang=en)", 10)
+ .setImplementation(DictionaryAspect.class)
+ .add(createConfigurationDependency().setPid(DictionaryAspectConfiguration.class.getName()))
+ .add(createServiceDependency().setService(LogService.class))); // NullObject
+
+ // Create the SpellChecker component
+ Hashtable<String, Object> props = new Hashtable<>();
+ props.put(CommandProcessor.COMMAND_SCOPE, "dictionary");
+ props.put(CommandProcessor.COMMAND_FUNCTION, new String[] { "spellcheck" });
+ dm.add(createComponent()
+ .setImplementation(SpellChecker.class)
+ .setInterface(SpellChecker.class.getName(), props)
+ .add(createServiceDependency().setService(DictionaryService.class).setRequired(true))
+ .add(createServiceDependency().setService(LogService.class))); // NullObject
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryAspect.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryAspect.java
new file mode 100644
index 0000000..7ae8a76
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryAspect.java
@@ -0,0 +1,84 @@
+/*
+ * 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.dependencymanager.samples.dictionary.api;
+
+import java.util.Dictionary;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.osgi.service.log.LogService;
+
+import aQute.bnd.annotation.metatype.Configurable;
+
+/**
+ * This aspect applies to the English DictionaryService, and allows to decorate it with some
+ * custom English words, which are configurable from WebConsole.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DictionaryAspect implements DictionaryService {
+ /**
+ * This is the service this aspect is applying to.
+ */
+ private volatile DictionaryService m_originalDictionary;
+
+ /**
+ * We store all configured words in a thread-safe data structure, because ConfigAdmin may
+ * invoke our updated method at any time.
+ */
+ private CopyOnWriteArrayList<String> m_words = new CopyOnWriteArrayList<String>();
+
+ /**
+ * We'll use the OSGi log service for logging. If no log service is available, then we'll
+ * use a NullObject.
+ */
+ private LogService m_log;
+
+ /**
+ * Defines a configuration dependency for retrieving our english custom words (by default,
+ * our PID is our full class name).
+ */
+ protected void updated(Dictionary<String, ?> config) {
+ if (config != null) {
+ // We use the bnd "Configurable" helper in order to get an implementation for our DictionaryConfiguration interface.
+ DictionaryConfiguration cnf = Configurable.createConfigurable(DictionaryConfiguration.class, config);
+ m_words.clear();
+ for (String word : cnf.words()) {
+ m_words.add(word);
+ }
+ }
+ }
+
+ /**
+ * Our Aspect Service is starting and is about to be registered in the OSGi regsitry.
+ */
+ protected void start() {
+ m_log.log(LogService.LOG_INFO, "Starting aspect Dictionary with words: " + m_words
+ + "; original dictionary service=" + m_originalDictionary);
+ }
+
+ /**
+ * Checks if a word is found from our custom word list. if not, delegate to the decorated
+ * dictionary.
+ */
+ public boolean checkWord(String word) {
+ m_log.log(LogService.LOG_INFO, "DictionaryAspect: checking word " + word + " (original dictionary="
+ + m_originalDictionary + ")");
+ if (m_words.contains(word)) {
+ return true;
+ }
+ return m_originalDictionary.checkWord(word);
+ }
+
+ public String toString() {
+ return "DictionaryAspect: words=" + m_words + "; original dictionary=" + m_originalDictionary;
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryAspectConfiguration.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryAspectConfiguration.java
new file mode 100644
index 0000000..c7a2a9b
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryAspectConfiguration.java
@@ -0,0 +1,37 @@
+/*
+ * 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.dependencymanager.samples.dictionary.api;
+
+import java.util.List;
+
+import aQute.bnd.annotation.metatype.Meta.AD;
+import aQute.bnd.annotation.metatype.Meta.OCD;
+
+/**
+ * This interface describes the configuration for our DictionaryAspect component. We are using the bnd metatype
+ * annotations, allowing to configure our Dictionary Services from web console.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@OCD(name="Spell Checker Aspect Dictionary (api)",
+ description = "Declare here the list of english words to be added into the default english dictionary")
+public interface DictionaryAspectConfiguration {
+ @AD(description = "Dictionary aspect words")
+ List<String> words();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryConfiguration.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryConfiguration.java
new file mode 100644
index 0000000..a022c80
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryConfiguration.java
@@ -0,0 +1,41 @@
+/*
+ * 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.dependencymanager.samples.dictionary.api;
+
+import java.util.List;
+
+import aQute.bnd.annotation.metatype.Meta.AD;
+import aQute.bnd.annotation.metatype.Meta.OCD;
+
+/**
+ * This interface describes the configuration for our DictionaryImpl component. We are using the bnd metatype
+ * annotations, allowing to configure our Dictionary Services from web console.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@OCD(name="Spell Checker Dictionary (api)",
+ factory = true,
+ description = "Declare here some Dictionary instances, allowing to instantiates some DictionaryService services for a given dictionary language")
+public interface DictionaryConfiguration {
+ @AD(description = "Describes the dictionary language", deflt = "en")
+ String lang();
+
+ @AD(description = "Declare here the list of words supported by this dictionary.")
+ List<String> words();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryImpl.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryImpl.java
new file mode 100644
index 0000000..4b361e1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryImpl.java
@@ -0,0 +1,96 @@
+/*
+ * 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.dependencymanager.samples.dictionary.api;
+
+import java.util.Dictionary;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.osgi.service.log.LogService;
+
+import aQute.bnd.annotation.metatype.Configurable;
+
+/**
+ * A Dictionary Service, instantiated from webconsole, when you add some configurations instances to the
+ * DictionaryConfiguration factory pid. The Configuration metatype informations is described using the
+ * bnd metatype information (see the DictionaryConfiguration interface).
+ *
+ * You must configure at least one Dictionary from web console, since the SpellCheck won't start if no Dictionary
+ * Service is available.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DictionaryImpl implements DictionaryService {
+ /**
+ * The key of our config admin dictionary values.
+ */
+ final static String WORDS = "words";
+
+ /**
+ * We store all configured words in a thread-safe data structure, because ConfigAdmin
+ * may invoke our updated method at any time.
+ */
+ private CopyOnWriteArrayList<String> m_words = new CopyOnWriteArrayList<String>();
+
+ /**
+ * We'll use the OSGi log service for logging. If no log service is available, then we'll use a NullObject.
+ */
+ private LogService m_log;
+
+ /**
+ * Our Dictionary language.
+ */
+ private String m_lang;
+
+ /**
+ * Our service will be initialized from ConfigAdmin.
+ * @param config The configuration where we'll lookup our words list (key=".words").
+ */
+ protected void updated(Dictionary<String, ?> config) {
+ if (config != null) {
+ // We use the bnd "Configurable" helper in order to get an implementation for our DictionaryConfiguration interface.
+ DictionaryConfiguration cnf = Configurable.createConfigurable(DictionaryConfiguration.class, config);
+
+ m_lang = cnf.lang();
+ m_words.clear();
+ for (String word : cnf.words()) {
+ m_words.add(word);
+ }
+ }
+ }
+
+ /**
+ * A new Dictionary Service is starting (because a new factory configuration has been created
+ * from webconsole).
+ */
+ protected void start() {
+ m_log.log(LogService.LOG_INFO, "Starting Dictionary Service with language: " + m_lang);
+ }
+
+ /**
+ * Check if a word exists if the list of words we have been configured from ConfigAdmin/WebConsole.
+ */
+ public boolean checkWord(String word) {
+ return m_words.contains(word);
+ }
+
+ @Override
+ public String toString() {
+ return "Dictionary: language=" + m_lang + ", words=" + m_words;
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryService.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryService.java
new file mode 100644
index 0000000..cb60f35
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryService.java
@@ -0,0 +1,35 @@
+/*
+ * 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.dependencymanager.samples.dictionary.api;
+
+/**
+ * A simple service interface that defines a dictionary service. A dictionary
+ * service simply verifies the existence of a word.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface DictionaryService {
+ /**
+ * Check for the existence of a word.
+ *
+ * @param word the word to be checked.
+ * @return true if the word is in the dictionary, false otherwise.
+ */
+ public boolean checkWord(String word);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/README b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/README
new file mode 100644
index 0000000..a8a70fb
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/README
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+This sample shows a "SpellChecker" application (using DM API) which provides a
+"dictionary:spellcheck" GOGO shell command. The GOGO "dictionary:spellcheck" command accepts a
+string as parameter, which is checked for proper existence. The SpellChecker class has a
+required/multiple (1..N) dependency over every available "DictionaryService" services, which are
+internally used by the SpellChecker command, when checking word existence.
+
+A DictionaryService is defined using a FactoryConfigurationAdapterService , allowing to instantiate
+many "DictionaryService" instances when some configurations are added to the
+"Spell Checker Configuration (api)" factory pid from web
+console. The factory pid configuration metatypes are defined using the bnd "metatype" annotations
+(see DictionaryConfiguration.java).
+
+The DictionaryService is decorated with a DictionaryAspect, which you can instantiate by adding a
+configuration to the "Spell Checker Aspect Dictionary (api)" pid from web console. The
+aspect configuration metatype is also declared using the bnd metatype annotations (see
+DictionaryAspectConfiguration.java).
+
+Before running this sample, go to webconsole, and add some words in the Spell Checker Configuration (api) factory PID, and
+in the Spell Checker Aspect Dictionary (api) PID.
+
+Then go to gogo shell, and type dm help. You will normally see the dictionary:spellcheck command.
+Type dictionary:spellcheck <some words configured either in the spell checker configuration, or in the spell checker aspect configuration,
+and the dictionary will check for proper word existance in the configuration.
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/SpellChecker.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/SpellChecker.java
new file mode 100644
index 0000000..89c13bb
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/SpellChecker.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager.samples.dictionary.api;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import org.apache.felix.service.command.Descriptor;
+import org.osgi.service.log.LogService;
+
+/**
+ * Felix "spellcheck" Gogo Shell Command. This command allows to check if some given words are valid or not.
+ * This command will be activated only if (at least) one DictionaryService has been injected.
+ * To create a Dictionary Service, you have to go the the web console and add a configuration in the
+ * "Dictionary Configuration" factory pid.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class SpellChecker {
+ /**
+ * We'll use the OSGi log service for logging. If no log service is available, then we'll use a NullObject.
+ */
+ private volatile LogService m_log;
+
+ /**
+ * We'll store all Dictionaries in a concurrent list, in order to avoid method synchronization.
+ * (Auto-Injected from Activator, at any time).
+ */
+ private final Iterable<DictionaryService> m_dictionaries = new ConcurrentLinkedQueue<>();
+
+ /**
+ * Lifecycle method callback, used to check if our service has been activated.
+ */
+ protected void start() {
+ m_log.log(LogService.LOG_WARNING, "Spell Checker started");
+ }
+
+ /**
+ * Lifecycle method callback, used to check if our service has been activated.
+ */
+ protected void stop() {
+ m_log.log(LogService.LOG_WARNING, "Spell Checker stopped");
+ }
+
+ // --- Gogo Shell command
+
+ @Descriptor("checks if word is found from an available dictionary")
+ public void spellcheck(@Descriptor("the word to check") String word) {
+ m_log.log(LogService.LOG_INFO, "Checking spelling of word \"" + word + "\" using the following dictionaries: "
+ + m_dictionaries);
+
+ for (DictionaryService dictionary : m_dictionaries) {
+ if (dictionary.checkWord(word)) {
+ System.out.println("word " + word + " is correct");
+ return;
+ }
+ }
+ System.err.println("word " + word + " is incorrect");
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/annot/DynamicDependency.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/annot/DynamicDependency.java
new file mode 100644
index 0000000..d9a201b
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/annot/DynamicDependency.java
@@ -0,0 +1,97 @@
+/*
+ * 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.dependencymanager.samples.dynamicdep.annot;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.ConfigurationDependency;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.log.LogService;
+
+import aQute.bnd.annotation.metatype.Configurable;
+
+/**
+ * This Component depends on the following services declared from the Activator:
+ * - LogService
+ * - Configuration with PID="org.apache.felix.dependencymanager.samples.dynamicdep.api.DynamicDependencyConfiguration"
+ *
+ * We the define a dynamic dependency on a Storage Service from our init method and we configure the dependency filter and
+ * required from using the injected configuration in our updated method.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Component
+public class DynamicDependency {
+ @ServiceDependency
+ volatile EventAdmin eventAdmin;
+
+ @ServiceDependency
+ volatile LogService log;
+
+ @ServiceDependency(name="storage")
+ volatile Storage storage; // dependency defined dynamically from our init() method
+
+ private String storageType; // type of Storage to depend on (we get that from configadmin)
+ private boolean storageRequired; // is our Storage dependency required or not (we get that from configadmin)
+
+ /**
+ * This is the first callback: we are injected with our configuration.
+ */
+ @ConfigurationDependency(pidClass=DynamicDependencyConfiguration.class)
+ public void updated(Dictionary<String, Object> properties) throws ConfigurationException {
+ // We use the bnd "Configurable" helper in order to get an implementation for our DictionaryConfiguration interface.
+ if (properties != null) {
+ DynamicDependencyConfiguration cnf = Configurable.createConfigurable(DynamicDependencyConfiguration.class, properties);
+ storageType = cnf.storageType();
+ storageRequired = cnf.storageRequired();
+ }
+ }
+
+ /**
+ * The configuration has been injected and also other required dependencies defined from the Activator.
+ * Now, define some dynamic dependencies (here we use the configuration injected from our updated method in
+ * order to configure the filter and required flag for the "Storage" dependency).
+ */
+ @Init
+ Map<String, String> init() {
+ log.log(LogService.LOG_WARNING, "init: storage type=" + storageType + ", storageRequired=" + storageRequired);
+ Map<String, String> props = new HashMap<>();
+ props.put("storage.required", Boolean.toString(storageRequired));
+ props.put("storage.filter", "(type=" + storageType + ")");
+ return props;
+ }
+
+ /**
+ * All dependencies injected, including dynamic dependencies defined from init method.
+ */
+ @Start
+ void start() {
+ log.log(LogService.LOG_WARNING, "start");
+ // Use storage to load/store some key-value pairs ...
+ storage.store("gabu", "zo");
+ }
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/annot/DynamicDependencyConfiguration.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/annot/DynamicDependencyConfiguration.java
new file mode 100644
index 0000000..7c07742
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/annot/DynamicDependencyConfiguration.java
@@ -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.
+ */
+package org.apache.felix.dependencymanager.samples.dynamicdep.annot;
+
+import aQute.bnd.annotation.metatype.Meta.AD;
+import aQute.bnd.annotation.metatype.Meta.OCD;
+
+/**
+ * This interface describes the configuration for our DynamicDependencyComponent component. We are using the bnd metatype
+ * annotations, allowing to configure our component from web console.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@OCD(name = "Dynamic Dependency Configuration (annotation)",
+ description = "Declare here the configuration for the DynamicDependency component.")
+public interface DynamicDependencyConfiguration {
+
+ @AD(description = "Enter the storage type to use",
+ deflt = "mapdb",
+ optionLabels = { "Map DB Storage implementation", "File Storage implementation" },
+ optionValues = { "mapdb", "file" })
+ String storageType();
+
+ @AD(description = "Specifies here is the storage dependency is required or not (if false, a null object will be used)", deflt = "true")
+ boolean storageRequired();
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/annot/FileStorage.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/annot/FileStorage.java
new file mode 100644
index 0000000..4216201
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/annot/FileStorage.java
@@ -0,0 +1,47 @@
+/*
+ * 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.dependencymanager.samples.dynamicdep.annot;
+
+import java.io.Serializable;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Component(properties={@Property(name="type", value="file")})
+public class FileStorage implements Storage {
+ @ServiceDependency
+ volatile LogService log; // injected
+
+ @Override
+ public void store(String key, Serializable data) {
+ log.log(LogService.LOG_WARNING, "FileStorage.store(" + key + "," + data + ")");
+ }
+
+ @Override
+ public Serializable get(String key) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/annot/MapDBStorage.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/annot/MapDBStorage.java
new file mode 100644
index 0000000..d6dad98
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/annot/MapDBStorage.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.dependencymanager.samples.dynamicdep.annot;
+
+import java.io.Serializable;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Component(properties={@Property(name="type", value="mapdb")})
+public class MapDBStorage implements Storage {
+ @ServiceDependency
+ volatile LogService log; // injected
+
+ @Override
+ public void store(String key, Serializable data) {
+ log.log(LogService.LOG_WARNING, "MapDBStorage.store(" + key + "," + data + ")");
+ }
+
+ @Override
+ public Serializable get(String key) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/annot/README b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/annot/README
new file mode 100644
index 0000000..ec48466
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/annot/README
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+This sample shows how to define a dynamic dependency using annotations.
+
+When you declare a Component:
+
+- the configuration (if any) is first injected (updated callback).
+- then all required dependencies are injected, except "named" dependencies whose required flag and filter can be configured
+dynamically from the init method.
+- then the init method (annotated with @Init) is invoked; And from there you are then able return a Map that will be used
+to configure the required flag and the filter of all named dependencies.
+- then the start callback (annotated with @Start) is invoked when all required dependencies are injected, including named
+dependencies that have been configured from the init method.
+
+In this sample, the "DynamicDependency" Components configures in its "init" method the dependency having a "storage" name.
+the dependency "required" flag and filter string are loaded from a Configuration PID
+(see the "Dynamic Dependency Configuration (annotation)" PID, from webconsole), which is defined using
+Bnd MetaType Annotations.
+So, you have configure the "Dynamic Dependency Configuration (annotation)" PID from web console before playing with this sample; then
+just type "log info|grep dynamicdep.annot" under the gogo shell.
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/annot/Storage.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/annot/Storage.java
new file mode 100644
index 0000000..8164d36
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/annot/Storage.java
@@ -0,0 +1,29 @@
+/*
+ * 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.dependencymanager.samples.dynamicdep.annot;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface Storage {
+ Serializable get(String key);
+ void store(String key, Serializable data);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/Activator.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/Activator.java
new file mode 100644
index 0000000..7821363
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/Activator.java
@@ -0,0 +1,55 @@
+/*
+ * 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.dependencymanager.samples.dynamicdep.api;
+
+import java.util.Properties;
+
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Activator extends DependencyActivatorBase {
+
+ @Override
+ public void init(BundleContext bc, DependencyManager dm)throws Exception {
+ Properties props = new Properties();
+ props.put("type", "mapdb");
+ dm.add(createComponent()
+ .setImplementation(MapDBStorage.class).setInterface(Storage.class.getName(), props)
+ .add(createServiceDependency().setService(LogService.class).setRequired(true)));
+
+ props = new Properties();
+ props.put("type", "file");
+ dm.add(createComponent()
+ .setImplementation(FileStorage.class).setInterface(Storage.class.getName(), props)
+ .add(createServiceDependency().setService(LogService.class).setRequired(true)));
+
+ dm.add(createComponent()
+ .setImplementation(DynamicDependency.class)
+ .add(createServiceDependency().setService(LogService.class).setRequired(true))
+ .add(createConfigurationDependency().setPid(DynamicDependencyConfiguration.class.getName()))
+ .add(createServiceDependency().setService(EventAdmin.class).setRequired(true)));
+ }
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/DynamicDependency.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/DynamicDependency.java
new file mode 100644
index 0000000..f2de627
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/DynamicDependency.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.dependencymanager.samples.dynamicdep.api;
+
+import java.util.Dictionary;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.log.LogService;
+
+import aQute.bnd.annotation.metatype.Configurable;
+
+/**
+ * This Component depends on the following services declared from the Activator:
+ * - LogService
+ * - Configuration with PID="org.apache.felix.dependencymanager.samples.dynamicdep.api.DynamicDependencyConfiguration"
+ *
+ * We the define a dynamic dependency on a Storage Service from our init method and we configure the dependency filter and
+ * required from using the injected configuration in our updated method.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DynamicDependency {
+
+ volatile EventAdmin eventAdmin; // injected and defined in Activator
+ volatile LogService log; // injected and defined in Activator
+ volatile Storage storage; // dependency defined dynamically from our init() method
+ private String storageType; // type of Storage to depend on (we get that from configadmin)
+ private boolean storageRequired; // is our Storage dependency required or not (we get that from configadmin)
+
+ /**
+ * This is the first callback: we are injected with our configuration.
+ */
+ public void updated(Dictionary<String, Object> properties) throws ConfigurationException {
+ // We use the bnd "Configurable" helper in order to get an implementation for our DictionaryConfiguration interface.
+ if (properties != null) {
+ DynamicDependencyConfiguration cnf = Configurable.createConfigurable(DynamicDependencyConfiguration.class, properties);
+ storageType = cnf.storageType();
+ storageRequired = cnf.storageRequired();
+ }
+ }
+
+ /**
+ * The configuration has been injected and also other required dependencies defined from the Activator.
+ * Now, define some dynamic dependencies (here we use the configuration injected from our updated method in
+ * order to configure the filter and required flag for the "Storage" dependency).
+ */
+ public void init(Component c) {
+ log.log(LogService.LOG_WARNING, "init: storage type=" + storageType + ", storageRequired=" + storageRequired);
+ DependencyManager dm = c.getDependencyManager();
+ // all dynamic dependencies must be declared atomically in the Component.add(...) method, which accepts varargs.
+ c.add(dm.createServiceDependency().setService(Storage.class, "(type=" + storageType + ")").setRequired(storageRequired));
+ }
+
+ /**
+ * All dependencies injected, including dynamic dependencies defined from init method.
+ */
+ void start() {
+ log.log(LogService.LOG_WARNING, "start");
+ // Use storage to load/store some key-value pairs ...
+ storage.store("gabu", "zo");
+ }
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/DynamicDependencyConfiguration.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/DynamicDependencyConfiguration.java
new file mode 100644
index 0000000..03fa3d9
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/DynamicDependencyConfiguration.java
@@ -0,0 +1,41 @@
+/*
+ * 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.dependencymanager.samples.dynamicdep.api;
+
+import aQute.bnd.annotation.metatype.Meta.AD;
+import aQute.bnd.annotation.metatype.Meta.OCD;
+
+/**
+ * This interface describes the configuration for our DynamicDependencyComponent component. We are using the bnd metatype
+ * annotations, allowing to configure our component from web console.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@OCD(name = "Dynamic Dependency Configuration (api)",
+ description = "Declare here the configuration for the DynamicDependency component.")
+public interface DynamicDependencyConfiguration {
+ @AD(description = "Enter the storage type to use",
+ deflt = "mapdb",
+ optionLabels= {"Map DB Storage implementation", "File Storage implementation"},
+ optionValues={"mapdb", "file"})
+ String storageType();
+
+ @AD(description = "Specifies here is the storage dependency is required or not (if false, a null object will be used)", deflt = "true")
+ boolean storageRequired();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/FileStorage.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/FileStorage.java
new file mode 100644
index 0000000..5a3e233
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/FileStorage.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.dependencymanager.samples.dynamicdep.api;
+
+import java.io.Serializable;
+
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FileStorage implements Storage {
+ volatile LogService log; // injected
+
+ @Override
+ public void store(String key, Serializable data) {
+ log.log(LogService.LOG_WARNING, "FileStorage.store(" + key + "," + data + ")");
+ }
+
+ @Override
+ public Serializable get(String key) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/MapDBStorage.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/MapDBStorage.java
new file mode 100644
index 0000000..2a3d4b9
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/MapDBStorage.java
@@ -0,0 +1,41 @@
+/*
+ * 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.dependencymanager.samples.dynamicdep.api;
+
+import java.io.Serializable;
+
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class MapDBStorage implements Storage {
+ volatile LogService log; // injected
+
+ @Override
+ public void store(String key, Serializable data) {
+ log.log(LogService.LOG_WARNING, "MapDBStorage.store(" + key + "," + data + ")");
+ }
+
+ @Override
+ public Serializable get(String key) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/README b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/README
new file mode 100644
index 0000000..4de67ed
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/README
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+This sample shows how to define a dynamic dependency from a Component's init method.
+
+When you declare a Component:
+
+- the configuration (if any) is first injected (updated callback).
+- then all required dependencies are injected.
+- then the init(Component c) method is invoked; And from there you are then able to add dynamic dependencies using any previously
+injected services (either configuration injected in update method, or other injected services declared from the Activator).
+- then the start callback is invoked when all required dependencies declared from the init method are injected.
+
+In this sample, the "DynamicDependency" Components defines in its "init" method a dynamic dependency on a Storage service.
+But it first loads the "storage type" and "storage required" dependency informations from a Configuration PID
+(see the "Dynamic Dependency Configuration (api)" PID from webconsole), which is defined using Bnd MetaType Annotations.
+So, you have configure the "Dynamic Dependency Configuration (api)" PID from web console before playing with this sample; then
+just type "log info|grep dynamicdep.api" under the gogo shell.
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/Storage.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/Storage.java
new file mode 100644
index 0000000..8391cdc
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dynamicdep/api/Storage.java
@@ -0,0 +1,29 @@
+/*
+ * 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.dependencymanager.samples.dynamicdep.api;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface Storage {
+ Serializable get(String key);
+ void store(String key, Serializable data);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/README b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/README
new file mode 100644
index 0000000..c575e65
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/README
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+This sample provides an example with one service consumer and a service provider, both declared
+using DM Annotations. The ServiceConsumer is also depending on a configuration pid (see
+org.apache.felix.dependencymanager.samples.conf.Configurator).
+To see logs, just type this under gogo shell:
+
+g! log info|grep hello.annot
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/ServiceConsumer.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/ServiceConsumer.java
new file mode 100644
index 0000000..cee1d93
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/ServiceConsumer.java
@@ -0,0 +1,54 @@
+/*
+ * 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.dependencymanager.samples.hello.annot;
+
+import java.util.Dictionary;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.ConfigurationDependency;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.osgi.service.log.LogService;
+
+/**
+ * Our service consumer. We depend on a ServiceProvider, and on a configuration.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Component
+public class ServiceConsumer {
+ @ServiceDependency
+ volatile ServiceProvider service;
+
+ @ServiceDependency
+ volatile LogService log;
+
+ Dictionary<?, ?> conf;
+
+ @ConfigurationDependency
+ protected void update(Dictionary<?, ?> conf) {
+ this.conf = conf;
+ }
+
+ @Start
+ public void start() {
+ log.log(LogService.LOG_INFO, "ServiceConsumer.start: calling service.hello() ...");
+ this.service.hello();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/ServiceProvider.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/ServiceProvider.java
new file mode 100644
index 0000000..40d81b2
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/ServiceProvider.java
@@ -0,0 +1,28 @@
+/*
+ * 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.dependencymanager.samples.hello.annot;
+
+/**
+ * The interface for our service provider.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ServiceProvider {
+ public void hello();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/ServiceProviderImpl.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/ServiceProviderImpl.java
new file mode 100644
index 0000000..c131db3
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/ServiceProviderImpl.java
@@ -0,0 +1,39 @@
+/*
+ * 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.dependencymanager.samples.hello.annot;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.osgi.service.log.LogService;
+
+/**
+ * The implementation for our service provider.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Component
+public class ServiceProviderImpl implements ServiceProvider {
+ @ServiceDependency
+ volatile LogService log;
+
+ @Override
+ public void hello() {
+ log.log(LogService.LOG_INFO, "ServiceProviderImpl.hello");
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/Activator.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/Activator.java
new file mode 100644
index 0000000..8dbec58
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/Activator.java
@@ -0,0 +1,44 @@
+/*
+ * 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.dependencymanager.samples.hello.api;
+
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext ctx, DependencyManager dm) throws Exception {
+ dm.add(createComponent()
+ .setImplementation(ServiceProviderImpl.class)
+ .add(createServiceDependency().setService(LogService.class).setRequired(true))
+ .setInterface(ServiceProvider.class.getName(), null));
+
+ dm.add(createComponent()
+ .setImplementation(ServiceConsumer.class)
+ .add(createServiceDependency().setService(LogService.class).setRequired(true))
+ .add(createConfigurationDependency()
+ .setPid(ServiceConsumer.class.getName()).setCallback("updated"))
+ .add(createServiceDependency().setService(ServiceProvider.class).setRequired(true)));
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/README b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/README
new file mode 100644
index 0000000..ff16694
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/README
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+This sample provides a DM Activator declaring one service consumer and a service provider. The
+ServiceConsumer is also depending on a configuration pid (see org.apache.felix.dependencymanager.samples.conf.Configurator).
+To see logs, just type this under gogo shell:
+
+g! log info|grep hello.annot
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/ServiceConsumer.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/ServiceConsumer.java
new file mode 100644
index 0000000..e287667
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/ServiceConsumer.java
@@ -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.
+ */
+package org.apache.felix.dependencymanager.samples.hello.api;
+
+import java.util.Dictionary;
+
+import org.osgi.service.log.LogService;
+
+/**
+ * Our service consumer. We depend on a ServiceProvider, and on a configuration.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceConsumer {
+ volatile ServiceProvider service;
+ volatile LogService log;
+ Dictionary<?, ?> conf;
+
+ protected void update(Dictionary<?, ?> conf) {
+ this.conf = conf;
+ }
+
+ public void start() {
+ log.log(LogService.LOG_INFO, "ServiceConsumer.start: calling service.hello()");
+ this.service.hello();
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/ServiceProvider.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/ServiceProvider.java
new file mode 100644
index 0000000..e8d5191
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/ServiceProvider.java
@@ -0,0 +1,28 @@
+/*
+ * 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.dependencymanager.samples.hello.api;
+
+/**
+ * The interface for our service provider.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ServiceProvider {
+ public void hello();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/ServiceProviderImpl.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/ServiceProviderImpl.java
new file mode 100644
index 0000000..4da110c
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/ServiceProviderImpl.java
@@ -0,0 +1,35 @@
+/*
+ * 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.dependencymanager.samples.hello.api;
+
+import org.osgi.service.log.LogService;
+
+/**
+ * The implementation for our service provider.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceProviderImpl implements ServiceProvider {
+ volatile LogService log;
+
+ @Override
+ public void hello() {
+ log.log(LogService.LOG_INFO, "ServiceProviderImpl.hello");
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/tpool/Activator.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/tpool/Activator.java
new file mode 100644
index 0000000..d71fa00
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/tpool/Activator.java
@@ -0,0 +1,38 @@
+/*
+ * 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.dependencymanager.samples.tpool;
+
+import org.apache.felix.dm.ComponentExecutorFactory;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+
+/**
+ * See README file describing this Activator.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext context, DependencyManager mgr) throws Exception {
+ mgr.add(createComponent()
+ .setInterface(ComponentExecutorFactory.class.getName(), null)
+ .setImplementation(ComponentExecutorFactoryImpl.class));
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/tpool/ComponentExecutorFactoryImpl.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/tpool/ComponentExecutorFactoryImpl.java
new file mode 100644
index 0000000..63fb402
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/tpool/ComponentExecutorFactoryImpl.java
@@ -0,0 +1,38 @@
+/*
+ * 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.dependencymanager.samples.tpool;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentExecutorFactory;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ComponentExecutorFactoryImpl implements ComponentExecutorFactory {
+ final static int SIZE = Runtime.getRuntime().availableProcessors();
+ final static Executor m_threadPool = Executors.newFixedThreadPool(SIZE);
+
+ @Override
+ public Executor getExecutorFor(Component component) {
+ return m_threadPool;
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/tpool/README b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/tpool/README
new file mode 100644
index 0000000..b75404e
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/tpool/README
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+The Activator you will find in this bundle registers a ComponentExecutorFactory in the OSGi service
+registry to enable parallelism. DependencyManager core will use the Executor returned by the
+ComponentExecutorFactory in order to handle components dependencies/lifecycle callbacks
+concurrently.
+
+Important note: since we are using the DM API to declare our threadpool, we have to disable
+parallelism for our "org.apache.felix.dependencymanager.samples.tpool.ThreadPool" component.
+To do so, we define the following OSGi service property (see the bnd.bnd configuration file):
+
+->
+
+org.apache.felix.dependencymanager.parallelism=!org.apache.felix.dependencymanager.samples.tpool,*
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/test/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.samples/test/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/test/.gitignore
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/tpool.bnd b/dependencymanager/org.apache.felix.dependencymanager.samples/tpool.bnd
new file mode 100644
index 0000000..819d205
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/tpool.bnd
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+Private-Package: \
+ org.apache.felix.dependencymanager.samples.tpool
+Bundle-Activator: org.apache.felix.dependencymanager.samples.tpool.Activator
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.shell/.classpath b/dependencymanager/org.apache.felix.dependencymanager.shell/.classpath
new file mode 100644
index 0000000..f89ae43
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.shell/.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.shell/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.shell/.gitignore
new file mode 100644
index 0000000..90dde36
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.shell/.gitignore
@@ -0,0 +1,3 @@
+/bin/
+/bin_test/
+/generated/
diff --git a/dependencymanager/org.apache.felix.dependencymanager.shell/.project b/dependencymanager/org.apache.felix.dependencymanager.shell/.project
new file mode 100644
index 0000000..bb43ab1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.shell/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.apache.felix.dependencymanager.shell</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.shell/.settings/org.eclipse.jdt.core.prefs b/dependencymanager/org.apache.felix.dependencymanager.shell/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..7341ab1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.shell/.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.shell/bnd.bnd b/dependencymanager/org.apache.felix.dependencymanager.shell/bnd.bnd
new file mode 100644
index 0000000..6d93dba
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.shell/bnd.bnd
@@ -0,0 +1,38 @@
+#
+# 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.apache.felix.gogo.runtime;version=0.10,\
+ org.mockito.mockito-all;version=1.9,\
+ org.apache.felix.dependencymanager;version=latest,\
+ ${junit}
+Private-Package: \
+ org.apache.felix.dm.shell
+Bundle-Activator:org.apache.felix.dm.shell.Activator
+Bundle-Version: 4.0.0
+Include-Resource: META-INF/=resources/LICENSE,\
+ META-INF/=resources/NOTICE,\
+ META-INF/=resources/DEPENDENCIES,\
+ META-INF/=resources/changelog.txt
+Bundle-Name: Apache Felix Dependency Manager Shell
+Bundle-Description: Gogo Shell commands for Apache Felix Dependency Manager
+Bundle-Category: osgi
+Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+Bundle-Vendor: The Apache Software Foundation
+# Work around used to make sure DM api is imported using proper version range (can be removed with bndtools 3)
+Import-Package: org.apache.felix.dm;version="[4.0, 5)", *
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.shell/resources/DEPENDENCIES b/dependencymanager/org.apache.felix.dependencymanager.shell/resources/DEPENDENCIES
new file mode 100644
index 0000000..983ccc7
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.shell/resources/DEPENDENCIES
@@ -0,0 +1,22 @@
+Apache Felix Dependency Manager Shell
+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
+
+n/a
+
+II. Used Third-Party Software
+
+This product uses software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2009).
+Licensed under the Apache License 2.0.
+
+III. Overall License Summary
+
+- Apache License 2.0
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager.shell/resources/LICENSE b/dependencymanager/org.apache.felix.dependencymanager.shell/resources/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.shell/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.shell/resources/NOTICE b/dependencymanager/org.apache.felix.dependencymanager.shell/resources/NOTICE
new file mode 100644
index 0000000..a3949e3
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.shell/resources/NOTICE
@@ -0,0 +1,7 @@
+Apache Felix Dependency Manager Shell
+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.
diff --git a/dependencymanager/org.apache.felix.dependencymanager.shell/resources/changelog.txt b/dependencymanager/org.apache.felix.dependencymanager.shell/resources/changelog.txt
new file mode 100644
index 0000000..302f4da
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.shell/resources/changelog.txt
@@ -0,0 +1,5 @@
+Release 4.0.0:
+-------------
+
+FELIX-4667: "top" command for the Dependency Manager Shell
+FELIX-4683: Allow to configure the DependencyManager shell scope
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.shell/src/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.shell/src/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.shell/src/.gitignore
diff --git a/dependencymanager/org.apache.felix.dependencymanager.shell/src/org/apache/felix/dm/shell/Activator.java b/dependencymanager/org.apache.felix.dependencymanager.shell/src/org/apache/felix/dm/shell/Activator.java
new file mode 100644
index 0000000..d16305f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.shell/src/org/apache/felix/dm/shell/Activator.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.shell;
+
+import java.util.Hashtable;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Bundle activator for the dependency manager shell command.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Activator implements BundleActivator {
+ /**
+ * You can configure the DM commands scope, by specifying this property in the bundle context.
+ */
+ private final static String SCOPE = "org.apache.felix.dependencymanager.shell.scope";
+
+ /**
+ * Default gogo shell "scope" used.
+ */
+ private final static String DEFAULT_SCOPE = "dependencymanager";
+
+ public void start(BundleContext context) throws Exception {
+ String scope = context.getProperty(SCOPE);
+ if (scope == null) {
+ scope = DEFAULT_SCOPE;
+ }
+
+ Hashtable<String, Object> props = new Hashtable<>();
+ props.put(org.apache.felix.service.command.CommandProcessor.COMMAND_SCOPE, scope);
+ props.put(org.apache.felix.service.command.CommandProcessor.COMMAND_FUNCTION,
+ new String[] { "dm" });
+ context.registerService(DMCommand.class.getName(), new DMCommand(context), props);
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.shell/src/org/apache/felix/dm/shell/ComponentId.java b/dependencymanager/org.apache.felix.dependencymanager.shell/src/org/apache/felix/dm/shell/ComponentId.java
new file mode 100644
index 0000000..8d49ea7
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.shell/src/org/apache/felix/dm/shell/ComponentId.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.shell;
+
+/**
+ * Unique identification of a component based on its name, type and bundle name.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ComponentId implements Comparable<ComponentId> {
+ private final String name;
+ private final String type;
+ private final String bundleName;
+
+ public ComponentId(String name, String type, String bundleName) {
+ super();
+ this.name = name;
+ this.type = type;
+ this.bundleName = bundleName;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getBundleName() {
+ return bundleName;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((bundleName == null) ? 0 : bundleName.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ComponentId other = (ComponentId) obj;
+ if (bundleName == null) {
+ if (other.bundleName != null)
+ return false;
+ }
+ else if (!bundleName.equals(other.bundleName))
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ }
+ else if (!name.equals(other.name))
+ return false;
+ if (type == null) {
+ if (other.type != null)
+ return false;
+ }
+ else if (!type.equals(other.type))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "ComponentId [name=" + name + ", type=" + type + ", bundleName=" + bundleName + "]";
+ }
+
+ public int compareTo(ComponentId o) {
+ // TODO it is common to have compareTo use the same fields that equals does
+ // if not for a good reason, document this
+ return name.compareTo(o.name);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.shell/src/org/apache/felix/dm/shell/DMCommand.java b/dependencymanager/org.apache.felix.dependencymanager.shell/src/org/apache/felix/dm/shell/DMCommand.java
new file mode 100644
index 0000000..e4171cb
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.shell/src/org/apache/felix/dm/shell/DMCommand.java
@@ -0,0 +1,784 @@
+/*
+ * 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.shell;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentDeclaration;
+import org.apache.felix.dm.ComponentDependencyDeclaration;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.felix.service.command.Descriptor;
+import org.apache.felix.service.command.Parameter;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * Shell command for showing all services and dependencies that are managed
+ * by the dependency manager.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Descriptor("Commands used to dump all existing Dependency Manager components")
+public class DMCommand {
+ /**
+ * Bundle context used to create OSGi filters.
+ */
+ private final BundleContext m_context;
+
+ /**
+ * Sorter used to sort components.
+ */
+ private static final DependencyManagerSorter SORTER = new DependencyManagerSorter();
+
+ /**
+ * Constant used by the wtf command, when listing missing services.
+ */
+ private static final String SERVICE = "service";
+
+ /**
+ * Constant used by the wtf command, when listing missing configurations.
+ */
+ private static final String CONFIGURATION = "configuration";
+
+ /**
+ * Name of a specific gogo shell variable, which may be used to configure "compact" mode.
+ * Example: g! dependencymanager.compact=true
+ */
+ private final static String ENV_COMPACT = "dependencymanager.compact";
+
+ /**
+ * Name of a specific gogo shell variable, which may be used to configure an OSGi filter, normally
+ * passed to the "dm services" option. It is used to display only some service providing components
+ * matching the given filter. The filter can contain an "objectClass" option.
+ * Example:
+ * g! dependencymanager.services="(protocol=http)"
+ * g! dependencymanager.services="(&(objectClass=foo.Bar)(protocol=http))"
+ */
+ private final static String ENV_SERVICES = "dependencymanager.services";
+
+ /**
+ * Name of a specific gogo shell variable, which may be used to configure a filter on the
+ * component implementation class name.
+ * The value of this shell variable may contain multiple regex (space separated), and each regex can
+ * be negated using "!".
+ * Example: g! dependencymanager.components="foo.bar.* ga.bu.zo.*"
+ */
+ private final static String ENV_COMPONENTS = "dependencymanager.components";
+
+ /**
+ * Constructor.
+ */
+ public DMCommand(BundleContext context) {
+ m_context = context;
+ }
+
+ /**
+ * Dependency Manager "dm" command. We use gogo annotations, in order to automate documentation,
+ * and also to automatically manage optional flags/options and parameters ordering.
+ *
+ * @param session the gogo command session, used to get some variables declared in the shell
+ * This parameter is automatically passed by the gogo runtime.
+ * @param nodeps false means that dependencies are not displayed
+ * @param compact true means informations are displayed in a compact format. This parameter can also be
+ * set using the "dependencymanager.compact" gogo shell variable.
+ * @param notavail only unregistered components / unavailable dependencies are displayed
+ * @param stats true means some statistics are displayed
+ * @param services an osgi filter used to filter on some given osgi service properties. This parameter can also be
+ * set using the "dependencymanager.services" gogo shell variable.
+ * @param components a regular expression to match either component implementation class names. This parameter can also be
+ * set using the "dependencymanager.components" gogo shell variable.
+ * @param componentIds only components matching one of the specified components ids are displayed
+ * @param bundleIds a list of bundle ids or symbolic names, used to filter on some given bundles
+ */
+ @Descriptor("List dependency manager components")
+ public void dm(
+ CommandSession session,
+
+ @Descriptor("Hides component dependencies")
+ @Parameter(names = {"nodeps", "nd"}, presentValue = "true", absentValue = "false")
+ boolean nodeps,
+
+ @Descriptor("Displays components using a compact form")
+ @Parameter(names = {"compact", "cp"}, presentValue = "true", absentValue = "")
+ String compact,
+
+ @Descriptor("Only displays unavailable components")
+ @Parameter(names = {"notavail", "na"}, presentValue = "true", absentValue = "false")
+ boolean notavail,
+
+ @Descriptor("Detects where are the root failures")
+ @Parameter(names = {"wtf"}, presentValue = "true", absentValue = "false")
+ boolean wtf,
+
+ @Descriptor("Displays components statistics")
+ @Parameter(names = {"stats", "stat", "st"}, presentValue = "true", absentValue = "false")
+ boolean stats,
+
+ @Descriptor("<OSGi filter used to filter some service properties>")
+ @Parameter(names = {"services", "s"}, absentValue = "")
+ String services,
+
+ @Descriptor("<Regex(s) used to filter on component implementation class names (comma separated), can be negated using \"!\" prefix>")
+ @Parameter(names = {"components", "c"}, absentValue = "")
+ String components,
+
+ @Descriptor("<List of component identifiers to display (comma separated)>")
+ @Parameter(names = {"componentIds", "cid", "ci"}, absentValue = "")
+ String componentIds,
+
+ @Descriptor("<List of bundle ids or bundle symbolic names to display (comma separated)>")
+ @Parameter(names = {"bundleIds", "bid", "bi", "b"}, absentValue = "")
+ String bundleIds,
+
+ @Descriptor("<Max number of top components to display (0=all)> This command displays components callbacks (init/start) times>")
+ @Parameter(names = {"top"}, absentValue = "-1")
+ int top) throws Throwable
+ {
+
+ boolean comp = Boolean.parseBoolean(getParam(session, ENV_COMPACT, compact));
+ services = getParam(session, ENV_SERVICES, services);
+ String[] componentsRegex = getParams(session, ENV_COMPONENTS, components);
+ ArrayList<String> bids = new ArrayList<String>(); // list of bundle ids or bundle symbolic names
+ ArrayList<Long> cids = new ArrayList<Long>(); // list of component ids
+
+ // Parse and check componentIds option
+ StringTokenizer tok = new StringTokenizer(componentIds, ", ");
+ while (tok.hasMoreTokens()) {
+ try {
+ cids.add(Long.parseLong(tok.nextToken()));
+ } catch (NumberFormatException e) {
+ System.out.println("Invalid value for componentIds option");
+ return;
+ }
+ }
+
+ // Parse services filter
+ Filter servicesFilter = null;
+ try {
+ if (services != null) {
+ servicesFilter = m_context.createFilter(services);
+ }
+ } catch (InvalidSyntaxException e) {
+ System.out.println("Invalid services OSGi filter: " + services);
+ e.printStackTrace(System.err);
+ return;
+ }
+
+ // Parse and check bundleIds option
+ tok = new StringTokenizer(bundleIds, ", ");
+ while (tok.hasMoreTokens()) {
+ bids.add(tok.nextToken());
+ }
+
+ if (top != -1) {
+ showTopComponents(top);
+ return;
+ }
+
+ if (wtf) {
+ wtf();
+ return;
+ }
+
+ // lookup all dependency manager service components
+ List<DependencyManager> managers = DependencyManager.getDependencyManagers();
+ Collections.sort(managers, SORTER);
+ Iterator<DependencyManager> iterator = managers.iterator();
+ long numberOfComponents = 0;
+ long numberOfDependencies = 0;
+ long lastBundleId = -1;
+ while (iterator.hasNext()) {
+ DependencyManager manager = iterator.next();
+ List<Component> complist = manager.getComponents();
+ Iterator<Component> componentIterator = complist.iterator();
+ while (componentIterator.hasNext()) {
+ Component component = componentIterator.next();
+ ComponentDeclaration sc = component.getComponentDeclaration();
+ String name = sc.getName();
+ // check if this component is enabled or disabled.
+ if (!mayDisplay(component, servicesFilter, componentsRegex, cids)) {
+ continue;
+ }
+ int state = sc.getState();
+ Bundle bundle = sc.getBundleContext().getBundle();
+ if (matchBundle(bundle, bids)) {
+ long bundleId = bundle.getBundleId();
+ if (notavail) {
+ if (sc.getState() != ComponentDeclaration.STATE_UNREGISTERED) {
+ continue;
+ }
+ }
+
+ numberOfComponents++;
+ if (lastBundleId != bundleId) {
+ lastBundleId = bundleId;
+ if (comp) {
+ System.out.println("[" + bundleId + "] " + compactName(bundle.getSymbolicName()));
+ } else {
+ System.out.println("[" + bundleId + "] " + bundle.getSymbolicName());
+ }
+ }
+ if (comp) {
+ System.out.print(" [" + sc.getId() + "] " + compactName(name) + " "
+ + compactState(ComponentDeclaration.STATE_NAMES[state]));
+ } else {
+ System.out.println(" [" + sc.getId() + "] " + name + " "
+ + ComponentDeclaration.STATE_NAMES[state]);
+ }
+ if (!nodeps) {
+ ComponentDependencyDeclaration[] dependencies = sc.getComponentDependencies();
+ if (dependencies != null && dependencies.length > 0) {
+ numberOfDependencies += dependencies.length;
+ if (comp) {
+ System.out.print('(');
+ }
+ for (int j = 0; j < dependencies.length; j++) {
+ ComponentDependencyDeclaration dep = dependencies[j];
+ if (notavail && !isUnavailable(dep)) {
+ continue;
+ }
+ String depName = dep.getName();
+ String depType = dep.getType();
+ int depState = dep.getState();
+
+ if (comp) {
+ if (j > 0) {
+ System.out.print(' ');
+ }
+ System.out.print(compactName(depName) + " " + compactState(depType) + " "
+ + compactState(ComponentDependencyDeclaration.STATE_NAMES[depState]));
+ } else {
+ System.out.println(" " + depName + " " + depType + " "
+ + ComponentDependencyDeclaration.STATE_NAMES[depState]);
+ }
+ }
+ if (comp) {
+ System.out.print(')');
+ }
+ }
+ }
+ if (comp) {
+ System.out.println();
+ }
+ }
+ }
+ }
+
+ if (stats) {
+ System.out.println("Statistics:");
+ System.out.println(" - Dependency managers: " + managers.size());
+ System.out.println(" - Components: " + numberOfComponents);
+ if (!nodeps) {
+ System.out.println(" - Dependencies: " + numberOfDependencies);
+ }
+ }
+ }
+
+ /**
+ * Displays components callbacks (init/start/stop/destroy) elapsed time.
+ * The components are sorted (the most time consuming components are displayed first).
+ * @param max the max number of components to display (0 means all components)
+ */
+ private void showTopComponents(int max) {
+ List<Component> components = new ArrayList<>();
+ for (DependencyManager manager : DependencyManager.getDependencyManagers()) {
+ components.addAll(manager.getComponents());
+ }
+ Collections.sort(components, new Comparator<Component>() {
+ @Override
+ public int compare(Component c1, Component c2) {
+ Map<String, Long> c1Times = c1.getComponentDeclaration().getCallbacksTime();
+ Map<String, Long> c2Times = c2.getComponentDeclaration().getCallbacksTime();
+ Long c1Start = c1Times.get("start");
+ Long c2Start = c2Times.get("start");
+ if (c1Start != null) {
+ if (c2Start != null) {
+ return c1Start > c2Start ? 1 : -1;
+ } else {
+ return 1;
+ }
+ } else {
+ if (c2Start != null) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ }
+ });
+
+ Collections.reverse(components);
+
+ System.out.printf("%-100s %10s %10s%n%n", "Top components (sorted by start duration time)", "[init time]", "[start time]");
+
+ if (components.size() > 0) {
+ System.out.println();
+
+ max = max == 0 ? components.size() : Math.min(components.size(), max);
+ for (int i = 0 ; i < components.size() && i < max; i++) {
+ ComponentDeclaration decl = components.get(i).getComponentDeclaration();
+ System.out.printf("%-100s %10d %10d%n", decl.getClassName(),
+ decl.getCallbacksTime().get("init"), decl.getCallbacksTime().get("start"));
+ }
+ }
+ }
+
+ private boolean isUnavailable(ComponentDependencyDeclaration dep) {
+ switch (dep.getState()) {
+ case ComponentDependencyDeclaration.STATE_UNAVAILABLE_OPTIONAL:
+ case ComponentDependencyDeclaration.STATE_UNAVAILABLE_REQUIRED:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private boolean matchBundle(Bundle bundle, List<String> ids) {
+ if (ids.size() == 0) {
+ return true;
+ }
+
+ for (int i = 0; i < ids.size(); i ++) {
+ String id = ids.get(i);
+ try {
+ Long longId = Long.valueOf(id);
+ if (longId == bundle.getBundleId()) {
+ return true;
+ }
+ } catch (NumberFormatException e) {
+ // must match symbolic name
+ if (id.equals(bundle.getSymbolicName())) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the value of a command arg parameter, or from the gogo shell if the parameter is not passed to
+ * the command.
+ */
+ private String getParam(CommandSession session, String param, String value) {
+ if (value != null && value.length() > 0) {
+ return value;
+ }
+ Object shellParamValue = session.get(param);
+ return shellParamValue != null ? shellParamValue.toString() : null;
+ }
+
+ /**
+ * Returns the value of a command arg parameter, or from the gogo shell if the parameter is not passed to
+ * the command. The parameter value is meant to be a list of values separated by a blank or a comma.
+ * The values are split and returned as an array.
+ */
+ private String[] getParams(CommandSession session, String name, String value) {
+ String values = null;
+ if (value == null || value.length() == 0) {
+ value = (String) session.get(name);
+ if (value != null) {
+ values = value;
+ }
+ } else {
+ values = value;
+ }
+ if (values == null) {
+ return new String[0];
+ }
+ return values.trim().split(", ");
+ }
+
+ /**
+ * Checks if a component can be displayed. We make a logical OR between the three following conditions:
+ *
+ * - the component service properties are matching a given service filter ("services" option)
+ * - the component implementation class name is matching some regex ("components" option)
+ * - the component declaration name is matching some regex ("names" option)
+ *
+ * If some component ids are provided, then the component must also match one of them.
+ */
+ private boolean mayDisplay(Component component, Filter servicesFilter, String[] components, List<Long> componentIds) {
+ // Check component id
+ if (componentIds.size() > 0) {
+ long componentId = ((ComponentDeclaration) component).getId();
+ if (componentIds.indexOf(componentId) == -1) {
+ return false;
+ }
+ }
+
+ if (servicesFilter == null && components.length == 0) {
+ return true;
+ }
+
+ // Check component service properties
+ boolean servicesMatches = servicesMatches(component, servicesFilter);
+
+ // Check components regexs, which may match component implementation class name
+ boolean componentsMatches = componentMatches(((ComponentDeclaration) component).getClassName(), components);
+
+ // Logical OR between service properties match and component service/impl match.
+ return servicesMatches || componentsMatches;
+ }
+
+ /**
+ * Checks if a given filter is matching some service properties possibly provided by a component
+ */
+ private boolean servicesMatches(Component component, Filter servicesFilter) {
+ boolean match = false;
+ if (servicesFilter != null) {
+ String[] services = ((ComponentDeclaration) component).getServices();
+ if (services != null) {
+ Dictionary<String, Object> properties = component.getServiceProperties();
+ if (properties == null) {
+ properties = new Hashtable<String, Object>();
+ }
+ if (properties.get(Constants.OBJECTCLASS) == null) {
+ properties.put(Constants.OBJECTCLASS, services);
+ }
+ match = servicesFilter.match(properties);
+ }
+ }
+ return match;
+ }
+
+ /**
+ * Checks if the component implementation class name (or some possible provided services) are matching
+ * some regular expressions.
+ */
+ private boolean componentMatches(String description, String[] names) {
+ for (int i = 0; i < names.length; i ++) {
+ String name = names[i];
+ boolean not = false;
+ if (name.startsWith("!")) {
+ name = name.substring(1);
+ not = true;
+ }
+ boolean match = false;
+
+ if (description.matches(name)) {
+ match = true;
+ }
+
+ if (not) {
+ match = !match;
+ }
+
+ if (match) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Compact names that look like state strings. State strings consist of
+ * one or more words. Each word will be shortened to the first letter,
+ * all letters concatenated and uppercased.
+ */
+ private String compactState(String input) {
+ StringBuffer output = new StringBuffer();
+ StringTokenizer st = new StringTokenizer(input);
+ while (st.hasMoreTokens()) {
+ output.append(st.nextToken().toUpperCase().charAt(0));
+ }
+ return output.toString();
+ }
+
+ /**
+ * Compacts names that look like fully qualified class names. All packages
+ * will be shortened to the first letter, except for the last one. So
+ * something like "org.apache.felix.MyClass" will become "o.a.f.MyClass".
+ */
+ private String compactName(String input) {
+ StringBuffer output = new StringBuffer();
+ int lastIndex = 0;
+ for (int i = 0; i < input.length(); i++) {
+ char c = input.charAt(i);
+ switch (c) {
+ case '.' :
+ output.append(input.charAt(lastIndex));
+ output.append('.');
+ lastIndex = i + 1;
+ break;
+ case ' ' :
+ case ',' :
+ if (lastIndex < i) {
+ output.append(input.substring(lastIndex, i));
+ }
+ output.append(c);
+ lastIndex = i + 1;
+ break;
+ default:
+ }
+ }
+ if (lastIndex < input.length()) {
+ output.append(input.substring(lastIndex));
+ }
+ return output.toString();
+ }
+
+ public void wtf() {
+ List<ComponentDeclaration> downComponents = getComponentsThatAreUnregistered();
+ if (downComponents.isEmpty()) {
+ System.out.println("No missing dependencies found.");
+ }
+ else {
+ String message = downComponents.size() + " missing dependencies found.";
+ System.out.println(message);
+ System.out.println("----------------------------------------------------".substring(0, message.length()));
+ }
+ listResolvedBundles();
+ listInstalledBundles();
+ Set<ComponentId> downComponentsRoot = getTheRootCouses(downComponents);
+ listAllMissingConfigurations(downComponentsRoot);
+ listAllMissingServices(downComponents, downComponentsRoot);
+ }
+
+ private Set<ComponentId> getTheRootCouses(List<ComponentDeclaration> downComponents) {
+ Set<ComponentId> downComponentsRoot = new TreeSet<ComponentId>();
+ for (ComponentDeclaration c : downComponents) {
+ List<ComponentId> root = getRoot(downComponents, c, new ArrayList<ComponentId>());
+ downComponentsRoot.addAll(root);
+ }
+ return downComponentsRoot;
+ }
+
+ private List<ComponentDeclaration> getComponentsThatAreUnregistered() {
+ List<DependencyManager> dependencyManagers = DependencyManager.getDependencyManagers();
+ List<ComponentDeclaration> unregisteredComponents = new ArrayList<ComponentDeclaration>();
+ for (DependencyManager dm : dependencyManagers) {
+ List<Component> components = dm.getComponents();
+ // create a list of all components that are unregistered
+ for (Component c : components) {
+ ComponentDeclaration cd = c.getComponentDeclaration();
+ if (cd.getState() == ComponentDeclaration.STATE_UNREGISTERED) {
+ unregisteredComponents.add(cd);
+ }
+ }
+ }
+ return unregisteredComponents;
+ }
+
+ private void listResolvedBundles() {
+ boolean areResolved = false;
+ for (Bundle b : m_context.getBundles()) {
+ if (b.getState() == Bundle.RESOLVED && !isFragment(b)) {
+ areResolved = true;
+ break;
+ }
+ }
+ if (areResolved) {
+ System.out.println("Please note that the following bundles are in the RESOLVED state:");
+ for (Bundle b : m_context.getBundles()) {
+ if (b.getState() == Bundle.RESOLVED && !isFragment(b)) {
+ System.out.println(" * [" + b.getBundleId() + "] " + b.getSymbolicName());
+ }
+ }
+ }
+ }
+
+ private void listInstalledBundles() {
+ boolean areResolved = false;
+ for (Bundle b : m_context.getBundles()) {
+ if (b.getState() == Bundle.INSTALLED) {
+ areResolved = true;
+ break;
+ }
+ }
+ if (areResolved) {
+ System.out.println("Please note that the following bundles are in the INSTALLED state:");
+ for (Bundle b : m_context.getBundles()) {
+ if (b.getState() == Bundle.INSTALLED) {
+ System.out.println(" * [" + b.getBundleId() + "] " + b.getSymbolicName());
+ }
+ }
+ }
+ }
+
+ private boolean isFragment(Bundle b) {
+ @SuppressWarnings("unchecked")
+ Dictionary<String, String> headers = b.getHeaders();
+ return headers.get("Fragment-Host") != null;
+ }
+
+ private void listAllMissingConfigurations(Set<ComponentId> unregisteredComponentsRoot) {
+ if (hasMissingType(unregisteredComponentsRoot, CONFIGURATION)) {
+ System.out.println("The following configuration(s) are missing: ");
+ for (ComponentId s : unregisteredComponentsRoot) {
+ if (CONFIGURATION.equals(s.getType())) {
+ System.out.println(" * " + s.getName() + " for bundle " + s.getBundleName());
+ }
+ }
+ }
+ }
+
+ private void listAllMissingServices(List<ComponentDeclaration> downComponents, Set<ComponentId> unregisteredComponentsRoot) {
+ if (hasMissingType(unregisteredComponentsRoot, SERVICE)) {
+ System.out.println("The following service(s) are missing: ");
+ for (ComponentId s : unregisteredComponentsRoot) {
+ if (SERVICE.equals(s.getType())) {
+ System.out.print(" * " + s.getName());
+ ComponentDeclaration component = getComponentDeclaration(s.getName(), downComponents);
+ if (component == null) {
+ System.out.println(" is not found in the service registry");
+ } else {
+ ComponentDependencyDeclaration[] componentDependencies = component.getComponentDependencies();
+ System.out.println(" and needs:");
+ for (ComponentDependencyDeclaration cdd : componentDependencies) {
+ if (cdd.getState() == ComponentDependencyDeclaration.STATE_UNAVAILABLE_REQUIRED) {
+ System.out.println(cdd.getName());
+ }
+ }
+ System.out.println(" to work");
+ }
+ }
+ }
+ }
+ }
+
+ private boolean hasMissingType(Set<ComponentId> downComponentsRoot, String type) {
+ for (ComponentId s : downComponentsRoot) {
+ if (type.equals(s.getType())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private List<ComponentId> getRoot(List<ComponentDeclaration> downComponents, ComponentDeclaration c, List<ComponentId> backTrace) {
+ ComponentDependencyDeclaration[] componentDependencies = c.getComponentDependencies();
+ int unregisteredDeps = 0;
+ List<ComponentId> result = new ArrayList<ComponentId>();
+ for (ComponentDependencyDeclaration cdd : componentDependencies) {
+ if (cdd.getState() == ComponentDependencyDeclaration.STATE_UNAVAILABLE_REQUIRED) {
+ unregisteredDeps++;
+ // Detect missing configuration dependency
+ if (CONFIGURATION.equals(cdd.getType())) {
+ String bsn = c.getBundleContext().getBundle().getSymbolicName();
+ result.add(new ComponentId(cdd.getName(), cdd.getType(), bsn));
+ continue;
+ }
+
+ // Detect if the missing dependency is a root cause failure
+ ComponentDeclaration component = getComponentDeclaration(cdd.getName(), downComponents);
+ if (component == null) {
+ result.add(new ComponentId(cdd.getName(), cdd.getType(), null));
+ continue;
+ }
+ // Detect circular dependency
+ ComponentId componentId = new ComponentId(cdd.getName(), cdd.getType(), null);
+ if (backTrace.contains(componentId)) {
+ // We already got this one so it's a circular dependency
+ System.out.print("Circular dependency found:\n *");
+ for (ComponentId cid : backTrace) {
+ System.out.print(" -> " + cid.getName() + " ");
+ }
+ System.out.println(" -> " + componentId.getName());
+ result.add(new ComponentId(c.getName(), SERVICE, c.getBundleContext().getBundle().getSymbolicName()));
+ continue;
+ }
+ backTrace.add(componentId);
+ return getRoot(downComponents, component, backTrace);
+ }
+ }
+ if (unregisteredDeps > 0 && result.isEmpty()) {
+ result.add(new ComponentId(c.getName(), SERVICE, c.getBundleContext().getBundle().getSymbolicName()));
+ }
+ return result;
+ }
+
+ private ComponentDeclaration getComponentDeclaration(final String fullName, List<ComponentDeclaration> list) {
+ String simpleName = getSimpleName(fullName);
+ Properties props = parseProperties(fullName);
+ for (ComponentDeclaration c : list) {
+ String serviceNames = c.getName();
+ int cuttOff = serviceNames.indexOf("(");
+ if (cuttOff != -1) {
+ serviceNames = serviceNames.substring(0, cuttOff).trim();
+ }
+ for (String serviceName : serviceNames.split(",")) {
+ if (simpleName.equals(serviceName.trim()) && doPropertiesMatch(props, parseProperties(c.getName()))) {
+ return c;
+ }
+ }
+ }
+ return null;
+ }
+
+ private boolean doPropertiesMatch(Properties need, Properties provide) {
+ for (Entry<Object, Object> entry : need.entrySet()) {
+ Object prop = provide.get(entry.getKey());
+ if (prop == null || !prop.equals(entry.getValue())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private String getSimpleName(String name) {
+ int cuttOff = name.indexOf("(");
+ if (cuttOff != -1) {
+ return name.substring(0, cuttOff).trim();
+ }
+ return name.trim();
+ }
+
+ private Properties parseProperties(String name) {
+ Properties result = new Properties();
+ int cuttOff = name.indexOf("(");
+ if (cuttOff != -1) {
+ String propsText = name.substring(cuttOff + 1, name.indexOf(")"));
+ String[] split = propsText.split(",");
+ for (String prop : split) {
+ String[] kv = prop.split("=");
+ if (kv.length == 2) {
+ result.put(kv[0], kv[1]);
+ }
+ }
+ }
+ return result;
+ }
+
+ public static class DependencyManagerSorter implements Comparator<DependencyManager> {
+ public int compare(DependencyManager dm1, DependencyManager dm2) {
+ long id1 = dm1.getBundleContext().getBundle().getBundleId();
+ long id2 = dm2.getBundleContext().getBundle().getBundleId();
+ return id1 > id2 ? 1 : -1;
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.shell/test/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.shell/test/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.shell/test/.gitignore
diff --git a/dependencymanager/org.apache.felix.dependencymanager.shell/test/test/DMCommandTest.java b/dependencymanager/org.apache.felix.dependencymanager.shell/test/test/DMCommandTest.java
new file mode 100644
index 0000000..18d0f0e
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.shell/test/test/DMCommandTest.java
@@ -0,0 +1,306 @@
+/*
+ * 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.util.Properties;
+
+import javax.crypto.Cipher;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.shell.DMCommand;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DMCommandTest {
+ /** System output just used to debug **/
+ private final static PrintStream OUT =
+ new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.out), 128));
+
+ /** Setup a ByteArrayOutputStream to capture the system out printlines */
+ private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
+ private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();
+
+ private DependencyManager dm;
+ @Spy @InjectMocks private DMCommand dme;
+ @Mock private BundleContext m_bundleContext;
+
+ @Before
+ public void setUp() throws Exception {
+ m_bundleContext = mock(BundleContext.class);
+ Bundle bundle = mock(Bundle.class);
+ when(m_bundleContext.getBundle()).thenReturn(bundle);
+ System.setOut(new PrintStream(outContent));
+ System.setErr(new PrintStream(errContent));
+ dm = new DependencyManager(m_bundleContext);
+ dme = new DMCommand(m_bundleContext);
+ DependencyManager.getDependencyManagers().add(dm);
+ }
+
+ @After
+ public void cleanUp() {
+ System.setOut(null);
+ System.setErr(null);
+ DependencyManager.getDependencyManagers().remove(dm);
+ }
+
+ @Test
+ public void testWithoutAnyDependcyManagersShouldNotCrash() {
+ OUT.println("testWithoutAnyDependcyManagersShouldNotCrash");
+ setupEmptyBundles();
+
+ dme.wtf();
+ assertEquals("No missing dependencies found.\n", outContent.toString());
+ }
+
+ @Test
+ public void testASingleComponentShouldNotRegisterAsFailure() {
+ OUT.println("testASingleComponentShouldNotRegisterAsFailure");
+ setupEmptyBundles();
+
+ dm.add(dm.createComponent()
+ .setImplementation(Object.class)
+ .setInterface(Object.class.getName(), null)
+ );
+ dme.wtf();
+ assertEquals("No missing dependencies found.\n", outContent.toString());
+ }
+
+ @Test
+ public void testComponentThatDependsOnAOtheComponentShouldRegisterAsFailure() {
+ OUT.println("testComponentThatDependsOnAOtheComponentShouldRegisterAsFailure");
+ setupEmptyBundles();
+ DependencyManager dm = new DependencyManager(m_bundleContext);
+ DependencyManager.getDependencyManagers().add(dm);
+
+ Component component = dm.createComponent()
+ .setImplementation(Object.class)
+ .setInterface(Object.class.getName(), null)
+ .add(dm.createServiceDependency().setService(Math.class).setRequired(true));
+ dm.add(component);
+
+ dme.wtf();
+ String output = outContent.toString();
+ assertTrue(output.contains("1 missing"));
+ assertTrue(output.contains("java.lang.Math"));
+
+ // remove the mess
+ dm.remove(component);
+ }
+
+ @Test
+ public void testComponentThatHaveCycliclyDependencyOnAOtheComponentShouldRegisterAsFailure() {
+ OUT.println("testComponentThatHaveCycliclyDependencyOnAOtheComponentShouldRegisterAsFailure");
+ setupEmptyBundles();
+ DependencyManager dm = new DependencyManager(m_bundleContext);
+ DependencyManager.getDependencyManagers().add(dm);
+
+ Component component1 = dm.createComponent()
+ .setImplementation(Cipher.class)
+ .setInterface(Cipher.class.getName(), null)
+ .add(dm.createServiceDependency().setService(Math.class).setRequired(true));
+ dm.add(component1);
+
+ Component component2 = dm.createComponent()
+ .setImplementation(Math.class)
+ .setInterface(Math.class.getName(), null)
+ .add(dm.createServiceDependency().setService(Cipher.class).setRequired(true));
+ dm.add(component2);
+
+ dme.wtf();
+ String output = outContent.toString();
+ assertTrue(output.contains("Circular dependency found:"));
+ assertTrue(output.contains("-> java.lang.Math -> javax.crypto.Cipher -> java.lang.Math"));
+
+ // remove the mess
+ dm.remove(component1);
+ dm.remove(component2);
+ }
+
+ @Test
+ public void testCanFindRootFailure() {
+ OUT.println("testCanFindRootFailure");
+ setupEmptyBundles();
+
+ Component component1 = dm.createComponent()
+ .setImplementation(Object.class)
+ .setInterface(Object.class.getName(), null)
+ .add(dm.createServiceDependency().setService(Math.class).setRequired(true));
+ dm.add(component1);
+
+ Component component2 = dm.createComponent()
+ .setImplementation(Math.class)
+ .setInterface(Math.class.getName(), null)
+ .add(dm.createServiceDependency().setService(String.class).setRequired(true));
+ dm.add(component2);
+
+ dme.wtf();
+ String output = outContent.toString();
+ assertTrue(output.contains("2 missing"));
+ assertTrue(output.contains("java.lang.String"));
+
+ // remove the mess
+ dm.remove(component1);
+ dm.remove(component2);
+ }
+
+ @Test
+ public void testCanFindRootFailureWithSecondair() {
+ OUT.println("testCanFindRootFailureWithSecondair");
+ setupEmptyBundles();
+
+ Component component1 = dm.createComponent()
+ .setImplementation(Object.class)
+ .setInterface(Object.class.getName(), null)
+ .add(dm.createServiceDependency().setService(Math.class).setRequired(true));
+ dm.add(component1);
+
+ Component component2 = dm.createComponent()
+ .setImplementation(Math.class)
+ .setInterface(Math.class.getName(), null)
+ .add(dm.createServiceDependency().setService(Float.class).setRequired(true));
+ dm.add(component2);
+
+ Component component3 = dm.createComponent()
+ .setImplementation(Object.class)
+ .setInterface(new String[] {Object.class.getName(), Float.class.getName()}, null)
+ .add(dm.createServiceDependency().setService(String.class).setRequired(true));
+ dm.add(component3);
+
+ dme.wtf();
+ String output = outContent.toString();
+ assertTrue(output.contains("3 missing"));
+ assertTrue(output.contains("java.lang.String"));
+ assertFalse(output.contains("java.lang.Float"));
+
+ // remove the mess
+ dm.remove(component1);
+ dm.remove(component2);
+ dm.remove(component3);
+ }
+
+ @Test
+ public void testCanFindRootFailureWithTwoFailures() {
+ OUT.println("testCanFindRootFailureWithTwoFailures");
+ setupEmptyBundles();
+
+ Component component1 = dm.createComponent()
+ .setImplementation(Object.class)
+ .setInterface(Object.class.getName(), null)
+ .add(dm.createServiceDependency().setService(Math.class).setRequired(true))
+ .add(dm.createServiceDependency().setService(Long.class).setRequired(true));
+ dm.add(component1);
+
+
+ dme.wtf();
+ String output = outContent.toString();
+ assertTrue(output.contains("1 missing"));
+ assertTrue(output.contains("java.lang.Math"));
+ assertTrue(output.contains("java.lang.Long"));
+
+ // remove the mess
+ dm.remove(component1);
+ }
+
+ @Test
+ public void testInstalledBundleListing() {
+ OUT.println("testInstalledBundleListing");
+ Bundle bundle1 = mock(Bundle.class);
+ when(bundle1.getState()).thenReturn(Bundle.INSTALLED);
+ when(bundle1.getSymbolicName()).thenReturn("BadBundle");
+
+ setupBundles(bundle1);
+
+ dme.wtf();
+ String output = outContent.toString();
+ assertTrue(output.contains("following bundles are in the INSTALLED"));
+ assertTrue(output.contains("[0] BadBundle"));
+ // Will print null if it gets bundle 2, that should not happen
+ assertFalse(output.contains("null"));
+ }
+
+ @Test
+ public void testResolvedBundleListing() {
+ OUT.println("testResolvedBundleListing");
+ Bundle bundle1 = mock(Bundle.class);
+ when(bundle1.getState()).thenReturn(Bundle.RESOLVED);
+ when(bundle1.getSymbolicName()).thenReturn("BadBundle");
+ Properties headers = new Properties();
+ when(bundle1.getHeaders()).thenReturn(headers);
+
+ setupBundles(bundle1);
+
+ dme.wtf();
+ String output = outContent.toString();
+ assertTrue(output.contains("following bundles are in the RESOLVED"));
+ assertTrue(output.contains("[0] BadBundle"));
+ assertFalse(output.contains("null"));
+ }
+
+ @Test
+ public void testResolvedBundleListingButNoFragements() {
+ OUT.println("testResolvedBundleListingButNoFragements");
+ Bundle bundle1 = mock(Bundle.class);
+ when(bundle1.getState()).thenReturn(Bundle.RESOLVED);
+ when(bundle1.getSymbolicName()).thenReturn("BadBundle");
+ Properties headers = new Properties();
+ headers.put("Fragment-Host", "some value");
+ when(bundle1.getHeaders()).thenReturn(headers);
+ setupBundles(bundle1);
+
+ dme.wtf();
+ String output = outContent.toString();
+ assertFalse(output.contains("following bundles are in the RESOLVED"));
+ // Will print null if it gets bundle 2, that should not happen
+ assertFalse(output.contains("null"));
+ }
+
+ private void setupBundles( Bundle bundle1) {
+ Bundle bundle2 = mock(Bundle.class);
+ when(bundle2.getState()).thenReturn(Bundle.ACTIVE);
+
+ when(m_bundleContext.getBundles()).thenReturn(new Bundle[] { bundle1, bundle2});
+ }
+
+ /** Sets up the bundle context without any bundles */
+ private void setupEmptyBundles() {
+ when(m_bundleContext.getBundles()).thenReturn(new Bundle[] {});
+ }
+
+}
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"));
+ * }
+ *
+ * @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<T></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<K,V></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<DictionaryService> dictionaries = new ConcurrentLinkedQueue<>();
+ *
+ * Or
+ *
+ * // will be injected by DM automatically and can be traversed any time to inspect all currently available dependencies.
+ * volatile Iterable<DictionaryService> 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<DictionaryService, Dictionary> dictionaries = new ConcurrentLinkedQueue<>();
+ *
+ * or
+ *
+ * // will be injected by DM automatically and can be traversed to inspect currently available dependencies
+ * volatile Map<DictionaryService, Dictionary> 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());
+ }
+}
diff --git a/dependencymanager/release/.classpath b/dependencymanager/release/.classpath
new file mode 100644
index 0000000..f89ae43
--- /dev/null
+++ b/dependencymanager/release/.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/release/.gitignore b/dependencymanager/release/.gitignore
new file mode 100644
index 0000000..90dde36
--- /dev/null
+++ b/dependencymanager/release/.gitignore
@@ -0,0 +1,3 @@
+/bin/
+/bin_test/
+/generated/
diff --git a/dependencymanager/release/.project b/dependencymanager/release/.project
new file mode 100644
index 0000000..46fcd83
--- /dev/null
+++ b/dependencymanager/release/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>release</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/release/.settings/org.eclipse.jdt.core.prefs b/dependencymanager/release/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..7341ab1
--- /dev/null
+++ b/dependencymanager/release/.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/release/README.release b/dependencymanager/release/README.release
new file mode 100644
index 0000000..9c7a158
--- /dev/null
+++ b/dependencymanager/release/README.release
@@ -0,0 +1,112 @@
+
+
+
+Apache Felix Dependency Manager Release Guide
+This document describes how to do a source release. It is based on the Release FAQ [1]
+
+Prerequisites
+=============
+
+To create a release you must:
+
+* Have Subversion installed on your system;
+* Have gpg installed on your system;
+* Have a public key added to the keys file, and committed to [2]
+* If you are using an http proxy, configure the following:
+
+ export GRADLE_OPTS="-Dhttps.proxyHost=www.somehost.org -Dhttps.proxyPort=8080"
+
+Before you can start staging a release candidate, you must:
+
+* make sure there are no dependencies on snapshots/unreleased versions;
+* create a tagged version of the sources in preparation of the release candidate.
+
+Creating a tagged version of the sources can be done directly through svn (replace r<n> by the actual release number, like "r1"):
+
+svn copy https://svn.apache.org/repos/asf/felix/trunk/dependencymanager https://svn.apache.org/repos/asf/felix/releases/org.apache.felix.dependencymanager-r<n> -m "Release of Apache Felix Dependency Manager r<n>"
+
+Staging a release candidate
+===========================
+
+Staging a release starts by checking out a tagged version of the sources (replace r<n> by the actual release number, like "r1"):
+
+svn co https://svn.apache.org/repos/asf/felix/releases/org.apache.felix.dependencymanager-r<n>
+
+The next step is to build/test the software and create the release/staging/ directory (where the source/jars will be packaged):
+(replace r<n> by the actual release number, like "r1")
+
+Use a Java7 JDK
+
+$ cd org.apache.felix.dependencymanager-r<n>
+$ ./gradlew rat
+$ ./gradlew org.apache.felix.dependencymanager.annotation:jar
+$ ./gradlew jar
+$ ./gradlew test
+$ ./gradlew check
+
+Then Edit the ./release/build.gradle script, increment the "ext.dmRelease" property, and create the staging:
+
+$ ./gradlew makeStaging (this will create ./release/staging/ directory)
+
+Then you sign archives by invoking the following task:
+
+$ ./gradlew signStaging
+
+You can upload the archives and the signatures to our development area, which we use to stage this release candidate. This development area can be found at
+https://dist.apache.org/repos/dist/dev/felix and adding files to it can be done using "svnpubsub" which is taken care of by the following target:
+
+$ ./gradlew commitToStaging
+
+Voting on the release
+=====================
+
+Start a vote on the dev@felix.apache.org list, for example (be sure to replace r<n> with the correct release number, like "r1"):
+
+>>>
+To: "Felix Developers List" <dev@felix.apache.org>
+Subject: [VOTE] Release of Apache Felix Dependency Manager release r<n>
+
+Hi,
+
+We solved N issues in this release:
+http://issues.apache.org/jira/...
+
+There are still some outstanding issues:
+http://issues.apache.org/jira/...
+
+Staging repository:
+https://dist.apache.org/repos/dist/dev/felix/apache-felix-dependencymanager-r<n>/
+
+You can use this UNIX script to download the release and verify the signatures:
+http://svn.apache.org/repos/asf/felix/trunk/dependencymanager/release/check_staged_release.sh
+
+Usage:
+sh check_staged_release.sh r<n> /tmp/felix-staging
+
+This script, unlike the original Felix check_stage_release.sh, will download staging from https://dist.apache.org/repos/dist/dev/felix instead of
+http://repository.apache.org/content/repositories.
+
+
+Please vote to approve this release:
+
+[ ] +1 Approve the release
+[ ] -1 Veto the release (please provide specific comments)
+
+This vote will be open for 72 hours.
+<<<
+
+Promoting the release:
+=====================
+
+Move the artifacts from the development area to the final release location at
+https://dist.apache.org/repos/dist/release/felix by invoking the following target:
+
+$ ./gradlew promoteToRelease
+
+Cancelling the release
+======================
+
+$ ./gradlew deleteFromStaging
+
+[1] http://www.apache.org/dev/release.html
+[2] http://www.apache.org/dist/felix/KEYS
diff --git a/dependencymanager/release/bnd.bnd b/dependencymanager/release/bnd.bnd
new file mode 100644
index 0000000..ee1f36d
--- /dev/null
+++ b/dependencymanager/release/bnd.bnd
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+-nobundles
+-dependson \
+ org.apache.felix.dependencymanager,\
+ org.apache.felix.dependencymanager.shell,\
+ org.apache.felix.dependencymanager.annotation,\
+ org.apache.felix.dependencymanager.runtime
+
diff --git a/dependencymanager/release/build.gradle b/dependencymanager/release/build.gradle
new file mode 100644
index 0000000..ea12b1d
--- /dev/null
+++ b/dependencymanager/release/build.gradle
@@ -0,0 +1,197 @@
+/*
+ * 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.
+ */
+
+/**
+ * Gradle script used to perform DM releases (really similar to Apache ACE build.xml)
+ */
+import aQute.bnd.build.Workspace
+
+// Our release number, which has to be monotonically incremented each time we make a new release.
+ext.dmRelease = "r1"
+
+// Our Apache svn Staging repo
+ext.svnStagingPath = "https://dist.apache.org/repos/dist/dev/felix"
+
+// Our Apache svn Release repo
+ext.svnReleasePath = "https://dist.apache.org/repos/dist/release/felix"
+
+apply plugin: 'java'
+apply from: file("rat.gradle")
+
+// Add bnd as a build dependency
+buildscript {
+ dependencies {
+ classpath files('cnf/gradle/biz.aQute.bnd.gradle.jar')
+ }
+}
+
+// Configure RAT plugin to ignore some files
+rat {
+ excludes = [
+ 'rat-report.xml',
+ '**/.git/**',
+ '**/.gradle/**',
+ '**/.project',
+ '**/.settings/**',
+ '**/*.iml',
+ '**/*.iws',
+ '**/*.ipr',
+ '**/.classpath',
+ 'cnf/**',
+ 'gradle/wrapper/**',
+ 'release/**',
+ 'gradlew',
+ 'README',
+ '**/DEPENDENCIES',
+ '**/README',
+ '**/.gitignore',
+ '**/generated/**',
+ 'doc/**',
+ '**/packageinfo',
+ '**/*.txt',
+ 'docs/**',
+ '.metadata/**'
+ ]
+}
+
+// Setup the workspace
+Workspace workspace
+workspace = Workspace.getWorkspace(".")
+
+task makeStaging << {
+ description = 'Packages the source and binary distributions.'
+
+ // Package source and source bin dependencies distributions.
+ logger.lifecycle(" Packaging source distributions.")
+ def topdir="org.apache.felix.dependencymanager-" + dmRelease
+ ant.zip(destfile: "staging/"+topdir+'-src.zip') {
+ zipfileset(dir: '../cnf', prefix: topdir+"-src/cnf", includes: ".project,.classpath,src/**,*.bnd,ext/**")
+ zipfileset(dir: '..', prefix: topdir+"-src", includes: '*.gradle,*.properties')
+ zipfileset(dir: 'resources/src', prefix: topdir+"-src", includes: '*')
+ new File('.').eachFile {
+ if(new File(it, 'bnd.bnd').exists()) {
+ def bndProject = workspace.getProject(it.name)
+ if (! bndProject.isNoBundles() && ! bndProject.getName().endsWith(".benchmark")) {
+ zipfileset(dir: "../${bndProject.name}", prefix: topdir+"-src/${bndProject.name}",
+ includes: "*.gradle,.project,.classpath,.settings/**,src/**,test/**,*.bnd,*.bndrun,run-*/conf/**,resources/**,README*")
+ }
+ }
+ }
+ }
+
+ // Package binary dependencies, needed to build the source distributions.
+ logger.lifecycle(" Packaging binary dependencies.")
+ ant.zip(destfile: "staging/"+topdir+"-deps.zip") {
+ ant.zipfileset(dir: '..', prefix: topdir+"-src", includes: 'gradlew')
+ ant.zipfileset(dir: '../gradle', prefix: topdir+"-src/gradle", includes: '*')
+ ant.zipfileset(dir: 'resources/deps', prefix: topdir+"-src", includes: '*')
+ ant.zipfileset(dir: '../cnf', prefix: topdir+"-src/cnf",
+ includes: 'buildrepo/**,localrepo/**,releaserepo/**,plugins/**,gradle/**')
+ }
+
+ // Package binaries as a simple collection of bundles. We use same license files as for src distrib.
+ logger.lifecycle(" Packaging binary distribution.")
+ ant.zip(destfile: "staging/"+topdir+"-bin.zip") {
+ ant.mappedresources() {
+ // don't include itests, samples, and benchmark in the convenience binary release.
+ ant.fileset(dir: '..', includes: '*/generated/*.jar', excludes: '*.itest/generated/*.jar,*.benchmark/generated/*.jar,*.samples/generated/*.jar')
+ ant.chainedmapper() {
+ ant.flattenmapper()
+ ant.globmapper(from: '*', to: topdir+'-bin/*')
+ }
+ }
+ ant.mappedresources() {
+ ant.fileset(dir: 'resources/bin', includes: '*')
+ ant.chainedmapper() {
+ ant.flattenmapper()
+ ant.globmapper(from: '*', to: topdir+'-bin/*')
+ }
+ }
+ }
+}
+
+// Sign staging directory
+task signStaging << {
+ description = 'Signs the local staging distribution.'
+ fileTree("staging").visit { FileVisitDetails details ->
+ logger.lifecycle(" Signing " + details.file.path)
+ ant.exec(executable: 'gpg', dir: 'staging') {
+ ant.arg(line: '--armor')
+ ant.arg(line: '--output')
+ ant.arg(line: details.file.name + ".asc")
+ ant.arg(line: "--detach-sig")
+ ant.arg(line: details.file.name)
+ }
+
+ ant.exec(executable: 'gpg', dir: 'staging', output: "staging/" + details.file.name + ".md5") {
+ ant.arg(line: '--print-md')
+ ant.arg(line: 'MD5')
+ ant.arg(line: details.file.name)
+ }
+
+ ant.exec(executable: 'gpg', dir: 'staging', output: "staging/" + details.file.name + ".sha") {
+ ant.arg(line: '--print-md')
+ ant.arg(line: 'SHA512')
+ ant.arg(line: details.file.name)
+ }
+ }
+}
+
+
+// Moves the source and binary distributions to staging.
+task commitToStaging << {
+ description = 'Commits the local staging to the Apache svn staging repository.'
+ getProject().exec {
+ commandLine 'svn',
+ 'import', 'staging', svnStagingPath + "/org.apache.felix.dependencymanager-" + dmRelease + "/",
+ '-m', "Staging Apache Felix Dependency Manager release " + dmRelease + "."
+ }
+}
+
+// Promotes the staged distributions to release
+task promoteToRelease << {
+ description = 'Moves the staging repository to the Apache release repository.'
+ getProject().exec {
+ commandLine 'svn',
+ 'move', svnStagingPath+"/org.apache.felix.dependencymanager-" + dmRelease , svnReleasePath,
+ '-m', "Releasing Apache Felix Dependency Manager release " + dmRelease + "."
+ }
+}
+
+// Removes the staged distributions from staging
+task deleteFromStaging << {
+ description = 'Cancels the staged distribution from the Apache staging repository.'
+ getProject().exec {
+ commandLine 'svn',
+ 'delete', svnStagingPath+"/org.apache.felix.dependencymanager-" + dmRelease + "/",
+ "-m", "Removing Apache Felix Dependency Manager release " + dmRelease + " from staging."
+ }
+}
+
+// Clean staging directory
+task clean(overwrite: true) << {
+ new File("release/staging").deleteDir()
+ new File("rat-report.xml").delete()
+}
+
+// Only clean the staging directory
+task cleanStaging << {
+ description = 'Clean the local staging directory.'
+ new File("release/staging").deleteDir()
+}
diff --git a/dependencymanager/release/check_staged_release.sh b/dependencymanager/release/check_staged_release.sh
new file mode 100755
index 0000000..9e30d77
--- /dev/null
+++ b/dependencymanager/release/check_staged_release.sh
@@ -0,0 +1,119 @@
+#!/bin/bash
+#
+# 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.
+# This script verifies the signatures and checksums of a release.
+#
+# This script can be used to check the signatures and checksums of staged
+# Apache Felix Dependency Manager release using gpg.
+# Usage:
+#
+# check_staged_dependencymanager.sh <version> [<temp-dir>]
+#
+# Where:
+# <version> represents the staged release version, e.g., 2.0.0;
+# <temp-dir> represents the location where the release artifacts
+# should be stored, defaults to /tmp/felix-staging if
+# omitted.
+
+
+version=${1}
+tmpDir=${2:-/tmp/felix-staging}
+
+if [ ! -d "${tmpDir}" ]; then
+ mkdir "${tmpDir}"
+fi
+
+if [ -z "${version}" -o ! -d "${tmpDir}" ]; then
+ echo "Usage: check_staged_dependencymanager.sh <release-version> [temp-directory]"
+ exit
+fi
+
+checkSig() {
+ sigFile="$1.asc"
+ if [ ! -f $sigFile ]; then
+ echo "$sigFile is missing!!!"
+ exit 1
+ fi
+
+ gpg --verify $sigFile 2>/dev/null >/dev/null
+ if [ "$?" = "0" ]; then echo "OK"; else echo "BAD!!!"; fi
+}
+
+checkSum() {
+ archive=$1
+ sumFile=$2
+ alg=$3
+ if [ ! -f $sumFile ]; then
+ echo "$sumFile is missing!!!"
+ exit 1
+ fi
+
+ orig=`cat $sumFile | sed 's/.*: *//' | tr -d ' \t\n\r'`
+ actual=`gpg --print-md $alg $archive | sed 's/.*: *//' | tr -d ' \t\n\r'`
+ if [ "$orig" = "$actual" ]; then echo "OK"; else echo "BAD!!!"; fi
+}
+
+KEYS_URL="http://www.apache.org/dist/felix/KEYS"
+REL_URL="https://dist.apache.org/repos/dist/dev/felix/org.apache.felix.dependencymanager-${version}/"
+PWD=`pwd`
+
+echo "################################################################################"
+echo " IMPORTING KEYS "
+echo "################################################################################"
+if [ ! -e "${tmpDir}/KEYS" ]; then
+ wget --no-check-certificate -P "${tmpDir}" $KEYS_URL
+fi
+gpg --import "${tmpDir}/KEYS"
+
+if [ ! -e "${tmpDir}/org.apache.felix.dependencymanager-${version}" ]
+then
+ echo "################################################################################"
+ echo " DOWNLOAD STAGED REPOSITORY "
+ echo "################################################################################"
+
+ wget \
+ -e "robots=off" --wait 1 -r -np "--reject=html,txt" "--follow-tags=" \
+ -P "${tmpDir}/org.apache.felix.dependencymanager-${version}" -nH "--cut-dirs=5" --ignore-length --no-check-certificate \
+ $REL_URL
+else
+ echo "################################################################################"
+ echo " USING EXISTING STAGED REPOSITORY "
+ echo "################################################################################"
+ echo "${tmpDir}/org.apache.felix.dependencymanager-${version}"
+fi
+
+echo "################################################################################"
+echo " CHECK SIGNATURES AND DIGESTS "
+echo "################################################################################"
+
+cd ${tmpDir}/org.apache.felix.dependencymanager-${version}
+for f in `find . -type f | grep -v '\.\(asc\|sha\?\|md5\)$'`; do
+ echo "checking $f"
+
+ echo -e " ASC: \c"
+ checkSig $f
+ echo -e " MD5: \c"
+ checkSum $f "$f.md5" MD5
+ echo -e " SHA: \c"
+ checkSum $f "$f.sha" SHA512
+ echo ""
+done
+
+cd $PWD
+echo "################################################################################"
+
diff --git a/dependencymanager/release/rat.gradle b/dependencymanager/release/rat.gradle
new file mode 100644
index 0000000..a15d798
--- /dev/null
+++ b/dependencymanager/release/rat.gradle
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.Task
+import org.gradle.api.internal.project.IsolatedAntBuilder
+
+apply plugin: RatPlugin
+
+class RatTask extends DefaultTask {
+ @Input
+ List<String> excludes
+
+ def reportPath = '.'
+ def xmlReport = reportPath + '/rat-report.xml'
+
+ def generateXmlReport(File reportDir) {
+ def antBuilder = services.get(IsolatedAntBuilder)
+ def ratClasspath = project.configurations.rat
+ antBuilder.withClasspath(ratClasspath).execute {
+ ant.taskdef(resource: 'org/apache/rat/anttasks/antlib.xml')
+ ant.report(format: 'xml', reportFile: xmlReport) {
+ fileset(dir: ".") {
+ patternset {
+ excludes.each {
+ exclude(name: it)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ def printUnknownFiles() {
+ def ratXml = new XmlParser().parse(xmlReport)
+ ratXml.resource.each { resource ->
+ if (resource.'license-approval'.@name[0] == "false") {
+ println('Unknown license: ' + resource.@name)
+ }
+ }
+ }
+
+ @TaskAction
+ def rat() {
+ File reportDir = new File(reportPath)
+ if (!reportDir.exists()) {
+ reportDir.mkdirs()
+ }
+ generateXmlReport(reportDir)
+ printUnknownFiles()
+ }
+}
+
+class RatPlugin implements Plugin<Project> {
+ void apply(Project project) {
+ configureDependencies(project)
+ project.plugins.apply(JavaBasePlugin);
+ Task ratTask = project.task("rat",
+ type: RatTask,
+ group: 'Build',
+ description: 'Runs Apache Rat checks.')
+ project.tasks[JavaBasePlugin.CHECK_TASK_NAME].dependsOn ratTask
+ }
+
+ void configureDependencies(final Project project) {
+ project.configurations {
+ rat
+ }
+ project.repositories {
+ mavenCentral()
+ }
+ project.dependencies {
+ rat 'org.apache.rat:apache-rat-tasks:0.8'
+ }
+ }
+}
diff --git a/dependencymanager/release/resources/bin/LICENSE b/dependencymanager/release/resources/bin/LICENSE
new file mode 100644
index 0000000..eedfc8a
--- /dev/null
+++ b/dependencymanager/release/resources/bin/LICENSE
@@ -0,0 +1,228 @@
+
+ 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.
+
+=========================================================================
+== JSON License ==
+=========================================================================
+
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/dependencymanager/release/resources/bin/NOTICE b/dependencymanager/release/resources/bin/NOTICE
new file mode 100644
index 0000000..9a34d63
--- /dev/null
+++ b/dependencymanager/release/resources/bin/NOTICE
@@ -0,0 +1,15 @@
+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.
+
+This product includes software from http://www.json.org.
+Copyright (c) 2002 JSON.org
+Licensed under the JSON License
diff --git a/dependencymanager/release/resources/bin/README.bin b/dependencymanager/release/resources/bin/README.bin
new file mode 100644
index 0000000..a80184c
--- /dev/null
+++ b/dependencymanager/release/resources/bin/README.bin
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+Welcome to Apache Felix Dependency Manager
+==========================================
+
+Apache Felix Dependency Manager is a versatile java API, allowing to declaratively
+register, acquire, and manage dynamic OSGi services.
+
+Getting Started
+===============
+
+To start using Apache Felix Dependency Manager, please go to our website and read the
+getting started guide for users:
+
+ http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager.html
+
+You can also find examples from the following location:
+
+ http://svn.apache.org/repos/asf/felix/trunk/examples/dependencymanager-samples
+
+Many thanks for using Apache Felix Dependency Manager.
+
+The Felix Team
diff --git a/dependencymanager/release/resources/deps/LICENSE b/dependencymanager/release/resources/deps/LICENSE
new file mode 100644
index 0000000..3892ce5
--- /dev/null
+++ b/dependencymanager/release/resources/deps/LICENSE
@@ -0,0 +1,367 @@
+
+ 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.
+
+=========================================================================
+== License for JSON ==
+=========================================================================
+
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+=========================================================================
+== License for Junit ==
+=========================================================================
+
+Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and
+b) in the case of each subsequent Contributor:
+i) changes to the Program, and
+ii) additions to the Program;
+where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
+b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.
+c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.
+d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+b) its license agreement:
+i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
+ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;
+iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and
+iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+b) a copy of this Agreement must be included with each copy of the Program.
+Contributors may not remove or alter any copyright notices contained within the Program.
+
+Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED 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. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.
+
+
+
+Junit includes LICENCES.txt which is reproduced below:
+
+BSD License
+
+Copyright (c) 2000-2006, www.hamcrest.org
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of
+conditions and the following disclaimer. Redistributions in binary form must reproduce
+the above copyright notice, this list of conditions and the following disclaimer in
+the documentation and/or other materials provided with the distribution.
+
+Neither the name of Hamcrest nor the names of its contributors may be used to endorse
+or promote products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+=========================================================================
+== License for Hamcrest ==
+=========================================================================
+
+BSD License
+
+Copyright (c) 2000-2006, www.hamcrest.org
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of
+conditions and the following disclaimer. Redistributions in binary form must reproduce
+the above copyright notice, this list of conditions and the following disclaimer in
+the documentation and/or other materials provided with the distribution.
+
+Neither the name of Hamcrest nor the names of its contributors may be used to endorse
+or promote products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
diff --git a/dependencymanager/release/resources/deps/NOTICE b/dependencymanager/release/resources/deps/NOTICE
new file mode 100644
index 0000000..4cffa7a
--- /dev/null
+++ b/dependencymanager/release/resources/deps/NOTICE
@@ -0,0 +1,27 @@
+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)
+Copyright (c) 2000 Gatespace AB. All Rights Reserved.
+Licensed under the Apache License 2.0.
+
+This product includes software from http://www.json.org.
+Copyright (c) 2002 JSON.org
+Licensed under the JSON License
+
+This product includes software from (http://junit.org),
+Licensed under the Eclipse Public License - v 1.0.
+
+This product includes software from http://bndtools.org
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+Hamcrest (http://www.hamcrest.org)
+Copyright (c) 2000-2006, www.hamcrest.org
+Licensed under BSD License
diff --git a/dependencymanager/release/resources/deps/README.deps b/dependencymanager/release/resources/deps/README.deps
new file mode 100644
index 0000000..a6255e0
--- /dev/null
+++ b/dependencymanager/release/resources/deps/README.deps
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+Welcome to Apache Felix Dependency Manager
+==========================================
+
+Apache Felix Dependency Manager is a versatile java API, allowing to declaratively
+register, acquire, and manage dynamic OSGi services.
+
+Before compiling a released version of Dependency Manager, you have to unfold the org.apache.felix.dependencymanager-r<n>-deps.zip on the same
+directory where you have unfolded the org.apache.felix.dependencymanager-r<n>-src.zip archive.
+
diff --git a/dependencymanager/release/resources/src/LICENSE b/dependencymanager/release/resources/src/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/dependencymanager/release/resources/src/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/release/resources/src/NOTICE b/dependencymanager/release/resources/src/NOTICE
new file mode 100644
index 0000000..fb5d7af
--- /dev/null
+++ b/dependencymanager/release/resources/src/NOTICE
@@ -0,0 +1,11 @@
+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/release/resources/src/README.src b/dependencymanager/release/resources/src/README.src
new file mode 100644
index 0000000..b253672
--- /dev/null
+++ b/dependencymanager/release/resources/src/README.src
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+Welcome to Apache Felix Dependency Manager
+==========================================
+
+Apache Felix Dependency Manager is a versatile java API, allowing to declaratively
+register, acquire, and manage dynamic OSGi services.
+
+Building and testing Apache Felix Dependency Manager
+====================================================
+
+** Compilation Using gradle:
+
+- If necessary, configure your https proxy settings:
+
+ export GRADLE_OPTS="-Dhttps.proxyHost=www.somehost.org -Dhttps.proxyPort=8080"
+
+- If you are building a released org.apache.felix.dependencymanager-r<n>-src.zip artifact, then
+ you also have to unfold the org.apache.felix.dependencymanager-r<n>-deps.zip on the same directory where you have unfolded the source archive.
+ (no need to do this if you have checked out from the felix-trunk).
+
+- Install java7.
+
+- Compile Dependendency Manager annotations bndtools plugin:
+
+$ ./gradlew org.apache.felix.dependencymanager.annotation:jar
+
+- Compile all other bundles:
+
+$ ./gradlew jar
+
+- run junit tests:
+
+$ ./gradlew test
+
+- run integration tests:
+
+$ ./gradlew check
+
+** Compilation Using Eclipse:
+
+- Install either Eclipse Kepler SR2 or Eclipse Luna and configure two JREs for both java7 and java8:
+
+* go to Windows -> Preferences -> Java -> Installed JREs
+* Then add two JREs: one for java7, and the other for java8.
+* Declare the java7 JRE as the default one.
+
+Java8 is only used to build and run the org.apache.felix.dependencymanager.benchmark module, which is used to perform
+DM performance tests within eclipse bndtools.
+
+- Install BndTools 2.4.1, and a subversion plugin for eclipse.
+- Open BndTools perspective
+- Import Dependency Manager into Eclipse, and compile everything
+- if it's the first time you import the project into eclipse, it may happen that some modules that requires the
+Dependency Manager Annotations bnd plugin don't compile: It's a know issue. To work around, restart eclipse and
+rebuild every modules.
+- Click on org.apache.felix.dependencymanager project and run it as "JUnit test".
+- Click on org.apache.felix.dependencymanager.shell and run it as "JUnit test"
+- Click on org.apache.felix.dependencymanager.itest and run it as "Bnd OSGi Test Launcer (Junit)".
+- Click on org.apache.felix.dependencymanager.runtime.itest and run it as ""Bnd OSGi Test Launcer (Junit)".
+
+Getting Started
+===============
+
+To start using Apache Felix Dependency Manager, please go to our website and read the
+getting started guide for users:
+
+ http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager.html
+
+Many examples are also available from the dependency manager samples module.
+See ./org.apache.felix.dependencymanager.samples/README.samples
+
+Many thanks for using Apache Felix Dependency Manager.
+
+The Felix Team
diff --git a/dependencymanager/release/src/.gitignore b/dependencymanager/release/src/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dependencymanager/release/src/.gitignore
diff --git a/dependencymanager/release/test/.gitignore b/dependencymanager/release/test/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dependencymanager/release/test/.gitignore
diff --git a/dependencymanager/settings.gradle b/dependencymanager/settings.gradle
new file mode 100644
index 0000000..69007e8
--- /dev/null
+++ b/dependencymanager/settings.gradle
@@ -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.
+ */
+
+/*
+ * Master Gradle initialization script
+ *
+ * Depends on bnd_* values from gradle.properties.
+ */
+
+import aQute.bnd.build.Workspace
+
+/* Add bnd as a script dependency */
+buildscript {
+ dependencies {
+ def bndURI = rootDir.toURI().resolve(bnd_jar)
+ if (bndURI.scheme != 'file') {
+ /* If not a local file, copy to a local file in cnf/cache */
+ def cnfCache = mkdir("${rootDir}/${bnd_cnf}/cache")
+ def bndJarFile = new File(cnfCache, 'biz.aQute.bnd.gradle.jar')
+ if (!bndJarFile.exists()) {
+ println "Downloading ${bndURI} to ${bndJarFile} ..."
+ bndURI.toURL().withInputStream { is ->
+ bndJarFile.withOutputStream { os ->
+ def bos = new BufferedOutputStream( os )
+ bos << is
+ }
+ }
+ }
+ bndURI = bndJarFile.toURI()
+ }
+ classpath files(bndURI)
+
+ /* After the rootProject is created, pass URI to projects */
+ gradle.rootProject { rootProject ->
+ rootProject.ext.bndURI = bndURI
+ }
+ }
+}
+
+/* Initialize the bnd workspace */
+def workspace = Workspace.getWorkspace(rootDir, bnd_cnf)
+if (workspace == null) {
+ throw new GradleException("Unable to load workspace ${rootDir}/${bnd_cnf}")
+}
+
+/* Add cnf project to the graph */
+include bnd_cnf
+
+/* Start with the declared build project name */
+def defaultProjectName = bnd_build
+
+/* If in a subproject, use the subproject name */
+for (def currentDir = startParameter.currentDir; currentDir != rootDir; currentDir = currentDir.parentFile) {
+ defaultProjectName = currentDir.name
+}
+
+/* Build a set of project names we need to include from the specified tasks */
+def projectNames = startParameter.taskNames.collect { taskName ->
+ def elements = taskName.split(':')
+ switch (elements.length) {
+ case 1:
+ return defaultProjectName
+ case 2:
+ return elements[0].empty ? bnd_build : elements[0]
+ default:
+ return elements[0].empty ? elements[1] : elements[0]
+ }
+}.toSet()
+
+/* Include the default project name if in a subproject or no tasks specified */
+if ((startParameter.currentDir != rootDir) || projectNames.empty) {
+ projectNames += defaultProjectName
+}
+
+/* If bnd_build used but declared empty, add all non-private folders of rootDir */
+if (projectNames.remove('')) {
+ rootDir.eachDir {
+ def projectName = it.name
+ if (!projectName.startsWith('.')) {
+ projectNames += projectName
+ }
+ }
+}
+
+/* Add each project and its dependencies to the graph */
+projectNames.each { projectName ->
+ // Don't build the org.apache.felix.dependencymanager.benchmark, which requires java8 (build the benchmark bundle only makes sense within eclipse
+ if (! projectName.equals("org.apache.felix.dependencymanager.benchmark")) {
+ include projectName
+ def project = getBndProject(workspace, projectName)
+ project?.dependson.each {
+ include it.name
+ }
+ }
+}
+
+/* Get the bnd project for the specified project name */
+def getBndProject(Workspace workspace, String projectName) {
+ def project = workspace.getProject(projectName)
+ if (project == null) {
+ return null
+ }
+ project.prepare()
+ if (project.isValid()) {
+ return project
+ }
+
+ project.getInfo(workspace, "${rootDir} :")
+ def errorCount = 0
+ project.warnings.each {
+ println "Warning: ${it}"
+ }
+ project.errors.each {
+ println "Error : ${it}"
+ errorCount++
+ }
+ if (!project.isOk()) {
+ def str = 'even though no errors were reported'
+ if (errorCount == 1) {
+ str = 'one error was reported'
+ } else if (errorCount > 1) {
+ str = "${errorCount} errors were reported"
+ }
+ throw new GradleException("Project ${rootDir}/${projectName} is invalid, ${str}")
+ }
+ throw new GradleException("Project ${rootDir}/${projectName} is not a valid bnd project")
+}
+
+/* After the rootProject is created, set up some properties. */
+gradle.rootProject { rootProject ->
+ rootProject.ext.bndWorkspace = workspace
+ rootProject.ext.cnf = rootProject.project(bnd_cnf)
+}