moved new dm4 from sandbox to trunk.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1663056 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/org.apache.felix.dependencymanager.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