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.runtime/.classpath b/dependencymanager/org.apache.felix.dependencymanager.runtime/.classpath
new file mode 100644
index 0000000..f89ae43
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" output="bin" path="src"/>
+ <classpathentry kind="src" output="bin_test" path="test"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
+ <classpathentry kind="con" path="aQute.bnd.classpath.container"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.runtime/.gitignore
new file mode 100644
index 0000000..90dde36
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/.gitignore
@@ -0,0 +1,3 @@
+/bin/
+/bin_test/
+/generated/
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/.project b/dependencymanager/org.apache.felix.dependencymanager.runtime/.project
new file mode 100644
index 0000000..56b71cc
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.apache.felix.dependencymanager.runtime</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>bndtools.core.bndbuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>bndtools.core.bndnature</nature>
+ </natures>
+</projectDescription>
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/.settings/org.eclipse.jdt.core.prefs b/dependencymanager/org.apache.felix.dependencymanager.runtime/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..7341ab1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,11 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.7
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/bnd.bnd b/dependencymanager/org.apache.felix.dependencymanager.runtime/bnd.bnd
new file mode 100644
index 0000000..087aae0
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/bnd.bnd
@@ -0,0 +1,44 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+Bundle-Version: 4.0.0
+-buildpath: \
+ osgi.core;version=4.2,\
+ osgi.cmpn;version=4.2,\
+ org.apache.felix.dependencymanager;version=latest,\
+ de.twentyeleven.skysail.org.json-osgi;version=20080701.0
+Bundle-Activator:org.apache.felix.dm.runtime.Activator
+Private-Package: \
+ org.apache.felix.dm.runtime,\
+ org.json
+Export-Package: \
+ org.apache.felix.dm.runtime.api
+Provide-Capability: osgi.extender; osgi.extender="org.apache.felix.dependencymanager.runtime";\
+ uses:="org.apache.felix.dm";version:Version="4.0.0"
+Include-Resource: META-INF/=resources/LICENSE,\
+ META-INF/=resources/NOTICE,\
+ META-INF/=resources/DEPENDENCIES,\
+ META-INF/=resources/changelog.txt
+
+Bundle-Name: Apache Felix Dependency Manager Runtime
+Bundle-Description: Loads Apache Felix Dependency Manager component descriptors from active \
+ annoted bundles
+Bundle-Category: osgi
+Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+Bundle-Vendor: The Apache Software Foundation
+Bundle-ContactAddress: http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager.html
+# Work around used to make sure DM api is imported using proper version range (can be removed with bndtools 3)
+Import-Package: org.apache.felix.dm;org.apache.felix.dm.context;version="[4, 5)", *
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/DEPENDENCIES b/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/DEPENDENCIES
new file mode 100644
index 0000000..3fdced9
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/DEPENDENCIES
@@ -0,0 +1,24 @@
+Apache Felix Dependency Manager Runtime
+Copyright 2011-2015 The Apache Software Foundation
+
+This software was developed at the Apache Software Foundation
+(http://www.apache.org) and may have dependencies on other
+Apache software licensed under Apache License 2.0.
+
+I. Included Third-Party Software
+
+This product includes software from http://www.json.org.
+Copyright (c) 2002 JSON.org
+Licensed under the JSON License
+
+II. Used Third-Party Software
+
+This product uses software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2014).
+Licensed under the Apache License 2.0.
+
+III. Overall License Summary
+
+- Apache License 2.0
+- JSON License
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/LICENSE b/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/LICENSE
new file mode 100644
index 0000000..eedfc8a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/LICENSE
@@ -0,0 +1,228 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+=========================================================================
+== JSON License ==
+=========================================================================
+
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/NOTICE b/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/NOTICE
new file mode 100644
index 0000000..5fa750e
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/NOTICE
@@ -0,0 +1,10 @@
+Apache Felix Dependency Manager Runtime
+Copyright 2011-2015 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software from http://www.json.org.
+Copyright (c) 2002 JSON.org
+Licensed under the JSON License
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/changelog.txt b/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/changelog.txt
new file mode 100644
index 0000000..3c75cff
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/resources/changelog.txt
@@ -0,0 +1,8 @@
+Release 4.0.0:
+-------------
+
+FELIX-4777: Dynamic initialization time configuration of @ConfigurationDependency
+FELIX-4676: Add Provide-Capability for DependencyManager Runtime bundle
+FELIX-4600: Cherrypicking of propagated properties
+FELIX-4684: Replace DependencyManager Runtime "factorySet" by a cleaner API
+FELIX-4709: Incorrect Named Dependencies are binded to the Service Instance
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/.gitignore
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/AbstractBuilder.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/AbstractBuilder.java
new file mode 100644
index 0000000..2e58f1c
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/AbstractBuilder.java
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.util.List;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentState;
+import org.apache.felix.dm.ComponentStateListener;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Base class for all kind of DM component builders (for Component, Aspect, Adapters ...).
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class AbstractBuilder
+{
+ /**
+ * Returns the service component type.
+ */
+ abstract String getType();
+
+ /**
+ * Builds the service component.
+ * @param serviceMetaData the service component metadata parsed from the descriptor file.
+ * @param serviceDependencies the service component dependencies metadata parsed from the descriptor file.
+ */
+ abstract void build(MetaData serviceMetaData, List<MetaData> serviceDependencies, Bundle b, DependencyManager dm)
+ throws Exception;
+
+ /**
+ * Sets common Service parameters, if provided from our Component descriptor
+ */
+ protected void setCommonServiceParams(Component c, MetaData srvMeta) throws Exception
+ {
+ // Set auto configured component fields.
+ DependencyManager dm = c.getDependencyManager();
+ boolean autoConfigureComponents = "true".equals(dm.getBundleContext().getProperty(
+ Activator.CONF_ENABLE_AUTOCONFIG));
+
+ if (!autoConfigureComponents)
+ {
+ c.setAutoConfig(BundleContext.class, Boolean.FALSE);
+ c.setAutoConfig(ServiceRegistration.class, Boolean.FALSE);
+ c.setAutoConfig(DependencyManager.class, Boolean.FALSE);
+ c.setAutoConfig(Component.class, Boolean.FALSE);
+ }
+
+ // See if BundleContext must be auto configured.
+ String bundleContextField = srvMeta.getString(Params.bundleContextField, null);
+ if (bundleContextField != null)
+ {
+ c.setAutoConfig(BundleContext.class, bundleContextField);
+ }
+
+ // See if DependencyManager must be auto configured.
+ String dependencyManagerField = srvMeta.getString(Params.dependencyManagerField, null);
+ if (dependencyManagerField != null)
+ {
+ c.setAutoConfig(DependencyManager.class, dependencyManagerField);
+ }
+
+ // See if Component must be auto configured.
+ String componentField = srvMeta.getString(Params.componentField, null);
+ if (componentField != null)
+ {
+ c.setAutoConfig(Component.class, componentField);
+ }
+
+ // Now, if the component has a @Started annotation, then add our component state listener,
+ // which will callback the corresponding annotated method, once the component is started.
+ String registered = srvMeta.getString(Params.registered, null);
+ String unregistered = srvMeta.getString(Params.unregistered, null);
+
+ if (registered != null || unregistered != null)
+ {
+ c.add(new RegistrationListener(registered, unregistered));
+ }
+ }
+
+ /**
+ * Registers all unnamed dependencies into a given service. Named dependencies are
+ * handled differently, and are managed by the ServiceLifecycleHandler class.
+ * @throws Exception
+ */
+ protected static void addUnamedDependencies(Bundle b, DependencyManager dm, Component s, MetaData srvMeta,
+ List<MetaData> depsMeta) throws Exception
+ {
+ for (MetaData dependency : depsMeta)
+ {
+ String name = dependency.getString(Params.name, null);
+ if (name == null)
+ {
+ DependencyBuilder depBuilder = new DependencyBuilder(dependency);
+ Log.instance().info("adding dependency %s into service %s", dependency, srvMeta);
+ Dependency d = depBuilder.build(b, dm);
+ s.add(d);
+ }
+ }
+ }
+
+ static class RegistrationListener implements ComponentStateListener
+ {
+ private final String m_registered;
+ private final String m_unregistered;
+ private volatile boolean m_isRegistered;
+
+ RegistrationListener(String registered, String unregistered)
+ {
+ m_registered = registered;
+ m_unregistered = unregistered;
+ }
+
+ public void changed(Component c, ComponentState state)
+ {
+ boolean wasRegistered = m_isRegistered;
+ switch (state)
+ {
+ case TRACKING_OPTIONAL:
+ // Started
+ m_isRegistered = true;
+ if (!wasRegistered && m_registered != null)
+ {
+ // The component has registered a service: notify all component instances
+ Object[] componentInstances = c.getInstances();
+ for (Object instance : componentInstances)
+ {
+ try
+ {
+ Class<?>[][] signatures = new Class<?>[][] { { ServiceRegistration.class }, {} };
+ Object[][] params = new Object[][] { { c.getServiceRegistration() }, {} };
+ InvocationUtil.invokeCallbackMethod(instance, m_registered, signatures, params);
+ }
+ catch (Throwable t)
+ {
+ Log.instance().error("Exception caught while invoking method %s on component %s", t,
+ m_registered, instance);
+ }
+ }
+ }
+ break;
+
+ case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
+ // Stopped
+ m_isRegistered = false;
+ if (wasRegistered && m_unregistered != null)
+ {
+ Object[] componentInstances = c.getInstances();
+ for (Object instance : componentInstances)
+ {
+ try
+ {
+ InvocationUtil.invokeCallbackMethod(instance, m_unregistered, new Class[][] { {} },
+ new Object[][] { {} });
+ }
+ catch (Throwable t)
+ {
+ Log.instance().error("Exception caught while invoking method %s on component %s", t,
+ m_registered, instance);
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Activator.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Activator.java
new file mode 100644
index 0000000..91d702a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Activator.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+/*
+ * This is the Activator for our DependencyManager Component Runtime.
+ * Here, we'll track started/stopped bundles which have some DependencyManager
+ * descriptors (META-INF/dependencymanager/*.dm). Such descriptors are generated
+ * by the Bnd plugin which parses DependencyManager annotations at compile time.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Activator extends DependencyActivatorBase
+{
+ /**
+ * Name of bundle context property telling if log service is required or not.
+ * (default = false)
+ */
+ final static String CONF_LOG = "org.apache.felix.dependencymanager.runtime.log";
+
+ /**
+ * Name of bundle context property telling if Components must be auto configured
+ * with BundleContext/ServiceRegistration etc .. (default = false)
+ */
+ final static String CONF_ENABLE_AUTOCONFIG = "org.apache.felix.dependencymanager.runtime.autoconfig";
+
+ /**
+ * Initialize our DependencyManager Runtime service.
+ *
+ * We depend on the OSGi LogService, and we track all started bundles which do have a
+ * "DependencyManager-Component" Manifest header.
+ * If the "dm.runtime.log=true" or "dm.runtime.packageAdmin=true" parameter is configured in the Felix config.properties
+ * then we'll use a required/temporal service dependency over the respective service.
+ * These temporal dependencies avoid us to be restarted if the respective service is temporarily
+ * unavailable (that is: when the service is updating).
+ * if the "dm.runtime.log" or "dm.runtime.packageAdmin" is not configured or it it is set to false, then we'll use
+ * an optional dependency over the respective service, in order to use a NullObject in case
+ * the service is not available.
+ */
+ @Override
+ public void init(BundleContext context, DependencyManager dm) throws Exception
+ {
+ Component component = createComponent()
+ .setImplementation(DependencyManagerRuntime.class)
+ .setComposition("getComposition")
+ .add(createBundleDependency()
+ .setRequired(false)
+ .setStateMask(Bundle.ACTIVE)
+ .setFilter("(DependencyManager-Component=*)")
+ .setCallbacks("bundleStarted", "bundleStopped"))
+ .add(createServiceDependency()
+ .setRequired(true)
+ .setService(PackageAdmin.class))
+ .add(createServiceDependency()
+ .setRequired("true".equalsIgnoreCase(context.getProperty(CONF_LOG)))
+ .setService(LogService.class));
+
+ dm.add(component);
+ }
+
+ /**
+ * Our bundle is stopping: shutdown our Dependency Manager Runtime service.
+ */
+ @Override
+ public void destroy(BundleContext context, DependencyManager dm) throws Exception
+ {
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/AdapterServiceBuilder.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/AdapterServiceBuilder.java
new file mode 100644
index 0000000..6acd109
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/AdapterServiceBuilder.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+
+/**
+ * Builded called when the JSON parser find an adapter service descriptor.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterServiceBuilder extends AbstractBuilder
+{
+ /** The type attribute specified in the JSON descriptor */
+ private final static String TYPE = "AdapterService";
+
+ @Override
+ public String getType()
+ {
+ return TYPE;
+ }
+
+ @Override
+ public void build(MetaData srvMeta, List<MetaData> depsMeta, Bundle b, DependencyManager dm)
+ throws Exception
+ {
+ Class<?> adapterImplClass = b.loadClass(srvMeta.getString(Params.impl));
+ String[] provides = srvMeta.getStrings(Params.provides, null);
+ Dictionary<String, Object> adapterProperties = srvMeta.getDictionary(Params.properties, null);
+ Class<?> adapteeService = b.loadClass(srvMeta.getString(Params.adapteeService));
+ String adapteeFilter = srvMeta.getString(Params.adapteeFilter, null);
+ String field = srvMeta.getString(Params.field, null);
+ String added = srvMeta.getString(Params.added, null);
+ String changed = srvMeta.getString(Params.changed, null);
+ String removed = srvMeta.getString(Params.removed, null);
+ String swap = srvMeta.getString(Params.swap, null);
+ boolean propagate = "true".equals(srvMeta.getString(Params.propagate, "true"));
+
+ if (field != null && (added != null || changed != null || removed != null || swap != null))
+ {
+ throw new IllegalArgumentException("autoconfig field " + field + " can't be defined with both added/changed/removed/swap calllbacks");
+ }
+
+ Component c;
+
+ if (field != null)
+ {
+ c = dm.createAdapterService(adapteeService, adapteeFilter, field, null, null, null, null, null, propagate);
+ }
+ else
+ {
+ if (added != null || changed != null || removed != null || swap != null)
+ {
+ c = dm.createAdapterService(adapteeService, adapteeFilter, null, null, added, changed, removed, swap, propagate);
+
+ }
+ else
+ {
+ c = dm.createAdapterService(adapteeService, adapteeFilter, null, null, null, null, null, null, propagate);
+ }
+ }
+
+ setCommonServiceParams(c, srvMeta);
+ c.setInterface(provides, adapterProperties);
+
+ String factoryMethod = srvMeta.getString(Params.factoryMethod, null);
+ if (factoryMethod == null)
+ {
+ c.setImplementation(adapterImplClass);
+ }
+ else
+ {
+ c.setFactory(adapterImplClass, factoryMethod);
+ }
+ c.setComposition(srvMeta.getString(Params.composition, null));
+ ServiceLifecycleHandler lfcleHandler = new ServiceLifecycleHandler(c, b, dm, srvMeta, depsMeta);
+ // The dependencies will be plugged by our lifecycle handler.
+ c.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
+ // Adds dependencies (except named dependencies, which are managed by the lifecycle handler).
+ addUnamedDependencies(b, dm, c, srvMeta, depsMeta);
+ dm.add(c);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/AspectServiceBuilder.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/AspectServiceBuilder.java
new file mode 100644
index 0000000..46273ef
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/AspectServiceBuilder.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+
+/**
+ * Class used to build an aspect service using metadata found from DependencyManager runtime
+ * meta-inf descriptor.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectServiceBuilder extends AbstractBuilder
+{
+ private final static String TYPE = "AspectService";
+
+ @Override
+ public String getType()
+ {
+ return TYPE;
+ }
+
+ @Override
+ public void build(MetaData srvMeta, List<MetaData> depsMeta, Bundle b, DependencyManager dm)
+ throws Exception
+ {
+ Log.instance().info("AspectServiceBuilder: building aspect service: %s with dependencies %s",
+ srvMeta,
+ depsMeta);
+
+ Class<?> serviceInterface = b.loadClass(srvMeta.getString(Params.service));
+ String serviceFilter = srvMeta.getString(Params.filter, null);
+ Dictionary<String, Object> aspectProperties = srvMeta.getDictionary(Params.properties, null);
+ int ranking = srvMeta.getInt(Params.ranking, 1);
+ String implClassName = srvMeta.getString(Params.impl);
+ Object implClass = b.loadClass(implClassName);
+ String field = srvMeta.getString(Params.field, null);
+ String added = srvMeta.getString(Params.added, null);
+ String changed = srvMeta.getString(Params.changed, null);
+ String removed = srvMeta.getString(Params.removed, null);
+ String swap = srvMeta.getString(Params.swap, null);
+
+ if (field != null && (added != null || changed != null || removed != null || swap != null))
+ {
+ throw new IllegalArgumentException("autoconfig field " + field + " can't be defined with both added/changed/removed/swap calllbacks");
+ }
+
+ Component c;
+ if (field != null)
+ {
+ c = dm.createAspectService(serviceInterface, serviceFilter, ranking, field)
+ .setServiceProperties(aspectProperties);
+ }
+ else
+ {
+ if (added != null || changed != null || removed != null || swap != null)
+ {
+ c = dm.createAspectService(serviceInterface, serviceFilter, ranking, added, changed, removed, swap)
+ .setServiceProperties(aspectProperties);
+ }
+ else
+ {
+ c = dm.createAspectService(serviceInterface, serviceFilter, ranking)
+ .setServiceProperties(aspectProperties);
+ }
+
+ }
+
+ setCommonServiceParams(c, srvMeta);
+ String factoryMethod = srvMeta.getString(Params.factoryMethod, null);
+ if (factoryMethod == null)
+ {
+ c.setImplementation(implClass);
+ }
+ else
+ {
+ c.setFactory(implClass, factoryMethod);
+ }
+
+ c.setComposition(srvMeta.getString(Params.composition, null));
+ ServiceLifecycleHandler lfcleHandler = new ServiceLifecycleHandler(c, b, dm, srvMeta, depsMeta);
+ // The dependencies will be plugged by our lifecycle handler.
+ c.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
+ // Adds dependencies (except named dependencies, which are managed by the lifecycle
+ // handler).
+ addUnamedDependencies(b, dm, c, srvMeta, depsMeta);
+ dm.add(c);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/BundleAdapterServiceBuilder.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/BundleAdapterServiceBuilder.java
new file mode 100644
index 0000000..94f8e62
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/BundleAdapterServiceBuilder.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+
+/**
+ * Class used to build a bundle adapter service using metadata found from DependencyManager runtime
+ * meta-inf descriptor.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class BundleAdapterServiceBuilder extends AbstractBuilder
+{
+ private final static String TYPE = "BundleAdapterService";
+
+ @Override
+ public String getType()
+ {
+ return TYPE;
+ }
+
+ @Override
+ public void build(MetaData srvMeta, List<MetaData> depsMeta, Bundle b, DependencyManager dm)
+ throws Exception
+ {
+ int stateMask = srvMeta.getInt(Params.stateMask, Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE);
+ String filter = srvMeta.getString(Params.filter, null);
+ Class<?> adapterImplClass = b.loadClass(srvMeta.getString(Params.impl));
+ String[] provides = srvMeta.getStrings(Params.provides, null);
+ Dictionary<String, Object> properties = srvMeta.getDictionary(Params.properties, null);
+ boolean propagate = "true".equals(srvMeta.getString(Params.propagate, "false"));
+ Component c = dm.createBundleAdapterService(stateMask, filter, propagate);
+ c.setInterface(provides, properties);
+ String factoryMethod = srvMeta.getString(Params.factoryMethod, null);
+ if (factoryMethod == null)
+ {
+ c.setImplementation(adapterImplClass);
+ }
+ else
+ {
+ c.setFactory(adapterImplClass, factoryMethod);
+ }
+
+ setCommonServiceParams(c, srvMeta);
+ c.setComposition(srvMeta.getString(Params.composition, null));
+ ServiceLifecycleHandler lfcleHandler = new ServiceLifecycleHandler(c, b, dm, srvMeta, depsMeta);
+ // The dependencies will be plugged by our lifecycle handler.
+ c.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
+ // Adds dependencies (except named dependencies, which are managed by the lifecycle handler).
+ addUnamedDependencies(b, dm, c, srvMeta, depsMeta);
+ dm.add(c);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ComponentBuilder.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ComponentBuilder.java
new file mode 100644
index 0000000..b9ec8be
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ComponentBuilder.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.runtime.api.ComponentFactory;
+import org.osgi.framework.Bundle;
+
+/**
+ * Builds a DependencyManager Component.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ComponentBuilder extends AbstractBuilder
+{
+ private final static String TYPE = "Component";
+ public final static String FACTORY_NAME = "dm.factory.name";
+ public final static String FACTORY_INSTANCE = "dm.factory.instance";
+
+ @Override
+ public String getType()
+ {
+ return TYPE;
+ }
+
+ @Override
+ public void build(MetaData srvMeta, List<MetaData> depsMeta, Bundle b, DependencyManager dm)
+ throws Exception
+ {
+ Component c = dm.createComponent();
+ String factory = srvMeta.getString(Params.factorySet, null);
+ String factoryName = srvMeta.getString(Params.factoryName, null);
+
+ // Setup Component auto config fields
+ setCommonServiceParams(c, srvMeta);
+
+ // Check if we must provide a Component factory set (deprecated), or a ComponentFactory.
+ if (factory == null && factoryName == null)
+ {
+ Log.instance().info("ComponentBuilder: building service %s with dependencies %s",
+ srvMeta,
+ depsMeta);
+
+ String impl = srvMeta.getString(Params.impl);
+ String composition = srvMeta.getString(Params.composition, null);
+ String factoryMethod = srvMeta.getString(Params.factoryMethod, null);
+ if (factoryMethod == null)
+ {
+ c.setImplementation(b.loadClass(impl));
+ }
+ else
+ {
+ c.setFactory(b.loadClass(impl), factoryMethod);
+ }
+ c.setComposition(composition);
+
+ // Adds dependencies (except named dependencies, which are managed by the lifecycle
+ // handler).
+ addUnamedDependencies(b, dm, c, srvMeta, depsMeta);
+ // Creates a ServiceHandler, which will filter all service lifecycle callbacks.
+ ServiceLifecycleHandler lfcleHandler =
+ new ServiceLifecycleHandler(c, b, dm, srvMeta, depsMeta);
+ c.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
+
+ // Set the provided services
+ Dictionary<String, Object> properties = srvMeta.getDictionary(Params.properties, null);
+ String[] services = srvMeta.getStrings(Params.provides, null);
+ c.setInterface(services, properties);
+ }
+ else if (factory != null) /* deprecated */
+ {
+ Log.instance()
+ .info("ComponentBuilder: providing factory set for service %s with dependencies %s",
+ srvMeta,
+ depsMeta);
+
+ // We don't instantiate the service, but instead we provide a Set in the registry.
+ // This Set will act as a factory and another component may registers some
+ // service configurations into it in order to fire some service instantiations.
+ FactorySet factorySet = new FactorySet(b, srvMeta, depsMeta);
+ c.setImplementation(factorySet);
+ c.setCallbacks(null, "start", "stop", null);
+ Hashtable<String, String> props = new Hashtable<String, String>();
+ props.put(ComponentBuilder.FACTORY_NAME, factory);
+ c.setInterface(Set.class.getName(), props);
+ }
+ else if (factoryName != null) {
+ Log.instance()
+ .info("ComponentBuilder: providing component factory for service %s with dependencies %s",
+ srvMeta,
+ depsMeta);
+
+ // We don't instantiate the service, but instead we provide a ComponentFactory in the registry.
+ // (similar to DS ComponentFactory).
+ ComponentFactoryImpl compFactory = new ComponentFactoryImpl(b, srvMeta, depsMeta);
+ c.setImplementation(compFactory);
+ c.setCallbacks(null, "start", "stop", null);
+ Hashtable<String, String> props = new Hashtable<String, String>();
+ props.put(ComponentBuilder.FACTORY_NAME, factoryName);
+ c.setInterface(ComponentFactory.class.getName(), props);
+ }
+
+ dm.add(c);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ComponentFactoryImpl.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ComponentFactoryImpl.java
new file mode 100644
index 0000000..3a15ded
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ComponentFactoryImpl.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.runtime.api.ComponentException;
+import org.apache.felix.dm.runtime.api.ComponentFactory;
+import org.apache.felix.dm.runtime.api.ComponentInstance;
+import org.osgi.framework.Bundle;
+
+/**
+ * This class implements a DM Component factory service.
+ * When a <code>Component</annotation> contains a <code>factoryName</code> attribute, this class is provided
+ * into the OSGi registry with a <code>org.apache.felix.dependencymanager.factory.name</code> service property.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ComponentFactoryImpl implements ComponentFactory {
+ /**
+ * The list of Dependencies which are applied in the Service.
+ */
+ private MetaData m_srvMeta;
+
+ /**
+ * The list of Dependencies which are applied in the Service.
+ */
+ private List<MetaData> m_depsMeta;
+
+ /**
+ * The DependencyManager which is used to create Service instances.
+ */
+ private DependencyManager m_dm;
+
+ /**
+ * Flag used to check if our service is Active.
+ */
+ private volatile boolean m_active;
+
+ /**
+ * The bundle containing the Service annotated with the factory attribute.
+ */
+ private final Bundle m_bundle;
+
+ /**
+ * Sole constructor.
+ * @param b the bundle containing the Service annotated with the factory attribute
+ * @param srvMeta the component service metadata
+ * @param depsMeta teh component dependencies metadata
+ */
+ public ComponentFactoryImpl(Bundle b, MetaData srvMeta, List<MetaData> depsMeta) {
+ m_bundle = b;
+ m_srvMeta = srvMeta;
+ m_depsMeta = depsMeta;
+ }
+
+ /**
+ * Our Service is starting.
+ */
+ public void start(Component c) {
+ m_active = true;
+ m_dm = c.getDependencyManager();
+ }
+
+ /**
+ * Our Service is stopping.
+ */
+ public void stop() {
+ m_active = false;
+ }
+
+ /**
+ * Create or Update a Service.
+ */
+ public ComponentInstance newInstance(Dictionary<String, ?> conf) {
+ // Check parameter validity
+ if (conf == null) {
+ throw new NullPointerException("configuration parameter can't be null");
+ }
+
+ // Check if our service is running.
+ checkServiceAvailable();
+
+ try {
+ ComponentInstanceImpl instance = new ComponentInstanceImpl(m_dm,m_bundle, m_srvMeta, m_depsMeta, conf);
+ return instance;
+
+ } catch (Throwable t) {
+ Log.instance().error("ServiceFactory: could not instantiate service %s", t, m_srvMeta);
+ throw new ComponentException("could not instantiate factory component", t);
+ }
+ }
+
+ /**
+ * Checks if our Service is available (we are not stopped").
+ */
+ private void checkServiceAvailable() {
+ if (!m_active) {
+ throw new IllegalStateException("Service not available");
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ComponentInstanceImpl.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ComponentInstanceImpl.java
new file mode 100644
index 0000000..90c0b1f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ComponentInstanceImpl.java
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.lang.reflect.Method;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.runtime.api.ComponentInstance;
+import org.osgi.framework.Bundle;
+
+/**
+ * Implementation for our DM Runtime ComponentInstance.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ComponentInstanceImpl implements ComponentInstance {
+ /**
+ * The list of Dependencies which are applied in the Service.
+ */
+ private final MetaData m_srvMeta;
+
+ /**
+ * The list of Dependencies which are applied in the Service.
+ */
+ private final List<MetaData> m_depsMeta;
+
+ /**
+ * The DependencyManager which is used to create Service instances.
+ */
+ private final DependencyManager m_dm;
+
+ /**
+ * The bundle containing the Service annotated with the factory attribute.
+ */
+ private final Bundle m_bundle;
+
+ /**
+ * The component
+ */
+ private final Object m_impl;
+
+ /**
+ * The DM Component used to define the component
+ */
+ private final Component m_component;
+
+ public ComponentInstanceImpl(DependencyManager dm, Bundle b, MetaData srvMeta, List<MetaData> depsMeta, Dictionary<String, ?> conf) throws Exception
+ {
+ m_bundle = b;
+ m_dm = dm;
+ m_srvMeta = srvMeta;
+ m_depsMeta = depsMeta;
+ m_component = m_dm.createComponent();
+
+ Class<?> implClass = m_bundle.loadClass(m_srvMeta.getString(Params.impl));
+ Object impl = conf.get(ComponentBuilder.FACTORY_INSTANCE);
+ if (impl == null) {
+ String factoryMethod = m_srvMeta.getString(Params.factoryMethod, null);
+ if (factoryMethod == null) {
+ impl = implClass.newInstance();
+ } else {
+ Method m = implClass.getDeclaredMethod(factoryMethod);
+ m.setAccessible(true);
+ impl = m.invoke(null);
+ }
+ }
+ m_impl = impl;
+
+ // Invoke "configure" callback
+ String configure = m_srvMeta.getString(Params.factoryConfigure, null);
+
+ if (configure != null) {
+ invokeConfigure(impl, configure, conf);
+ }
+
+ // Create Service
+ m_component.setImplementation(impl);
+ String[] provides = m_srvMeta.getStrings(Params.provides, null);
+ if (provides != null) {
+ // Merge service properties with the configuration provided by the factory.
+ Dictionary<String, ?> serviceProperties = m_srvMeta.getDictionary(Params.properties, null);
+ serviceProperties = mergeSettings(serviceProperties, conf);
+ m_component.setInterface(provides, serviceProperties);
+ }
+
+ m_component.setComposition(m_srvMeta.getString(Params.composition, null));
+ ServiceLifecycleHandler lfcleHandler = new ServiceLifecycleHandler(m_component, m_bundle, m_dm, m_srvMeta, m_depsMeta);
+ // The dependencies will be plugged by our lifecycle handler.
+ m_component.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
+
+ // Adds dependencies (except named dependencies, which are managed by the lifecycle handler).
+ for (MetaData dependency : m_depsMeta) {
+ String name = dependency.getString(Params.name, null);
+ if (name == null) {
+ DependencyBuilder depBuilder = new DependencyBuilder(dependency);
+ Log.instance().info("ServiceLifecycleHandler.init: adding dependency %s into service %s", dependency,
+ m_srvMeta);
+ Dependency d = depBuilder.build(m_bundle, m_dm);
+ m_component.add(d);
+ }
+ }
+
+ // Register the Service instance, and keep track of it.
+ Log.instance().info("ServiceFactory: created service %s", m_srvMeta);
+ m_dm.add(m_component);
+ }
+
+ @Override
+ public void dispose() {
+ m_dm.remove(m_component);
+ }
+
+ @Override
+ public void update(Dictionary<String, ?> conf) {
+ // Reconfigure an already existing Service.
+ String configure = m_srvMeta.getString(Params.factoryConfigure, null);
+ if (configure != null) {
+ Log.instance().info("ServiceFactory: updating service %s", m_impl);
+ invokeConfigure(m_impl, configure, conf);
+ }
+
+ // Update service properties
+ String[] provides = m_srvMeta.getStrings(Params.provides, null);
+ if (provides != null) {
+ Dictionary<String, ?> serviceProperties = m_srvMeta.getDictionary(Params.properties, null);
+ serviceProperties = mergeSettings(serviceProperties, conf);
+ m_component.setServiceProperties(serviceProperties);
+ }
+ }
+
+ /**
+ * Invokes the configure callback method on the service instance implemenatation.
+ * @param impl
+ * @param configure
+ * @param config
+ */
+ private void invokeConfigure(Object impl, String configure, Dictionary<String, ?> config) {
+ try {
+ InvocationUtil.invokeCallbackMethod(impl, configure, new Class[][] { { Dictionary.class } },
+ new Object[][] { { config } });
+ }
+
+ catch (Throwable t) {
+ if (t instanceof RuntimeException) {
+ throw (RuntimeException) t;
+ } else {
+ throw new RuntimeException("Could not invoke method " + configure + " on object " + impl, t);
+ }
+ }
+ }
+
+ /**
+ * Merge factory configuration settings with the service properties. The private factory configuration
+ * settings are ignored. A factory configuration property is private if its name starts with a dot (".").
+ *
+ * @param serviceProperties
+ * @param factoryConfiguration
+ * @return
+ */
+ private Dictionary<String, Object> mergeSettings(Dictionary<String, ?> serviceProperties,
+ Dictionary<String, ?> factoryConfiguration)
+ {
+ Dictionary<String, Object> props = new Hashtable<>();
+
+ if (serviceProperties != null) {
+ Enumeration<String> keys = serviceProperties.keys();
+ while (keys.hasMoreElements()) {
+ String key = keys.nextElement();
+ Object val = serviceProperties.get(key);
+ props.put(key, val);
+ }
+ }
+
+ Enumeration<String> keys = factoryConfiguration.keys();
+ while (keys.hasMoreElements()) {
+ String key = keys.nextElement();
+ if (!key.toString().startsWith(".")) {
+ // public properties are propagated
+ Object val = factoryConfiguration.get(key);
+ props.put(key, val);
+ }
+ }
+ return props;
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DependencyBuilder.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DependencyBuilder.java
new file mode 100644
index 0000000..e25d441
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DependencyBuilder.java
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import org.apache.felix.dm.BundleDependency;
+import org.apache.felix.dm.ConfigurationDependency;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ResourceDependency;
+import org.apache.felix.dm.ServiceDependency;
+import org.osgi.framework.Bundle;
+
+/**
+ * Class used to build a concrete dependency from meta data.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DependencyBuilder
+{
+ public enum DependencyType
+ {
+ ServiceDependency,
+ TemporalServiceDependency,
+ ConfigurationDependency,
+ BundleDependency,
+ ResourceDependency
+ }
+
+ private MetaData m_metaData;
+
+ public DependencyBuilder(MetaData dependencyMetaData)
+ {
+ m_metaData = dependencyMetaData;
+ }
+
+ public Dependency build(Bundle b, DependencyManager dm)
+ throws Exception
+ {
+ Dependency dp = null;
+ DependencyType type;
+
+ try
+ {
+ type = DependencyType.valueOf(m_metaData.getString(Params.type));
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new IllegalArgumentException("no \"type\" parameter found from metaData: " + m_metaData);
+ }
+
+ switch (type)
+ {
+ case ServiceDependency:
+ dp = createServiceDependency(b, dm);
+ break;
+
+ case ConfigurationDependency:
+ dp = createConfigurationDependency(dm);
+ break;
+
+ case BundleDependency:
+ dp = createBundleDependency(dm);
+ break;
+
+ case ResourceDependency:
+ dp = createResourceDependency(dm);
+ break;
+
+ default:
+ throw new IllegalArgumentException("Can't build service dependency: " + type);
+ }
+ return dp;
+ }
+
+ private Dependency createServiceDependency(Bundle b, DependencyManager dm)
+ throws ClassNotFoundException
+ {
+ String service = m_metaData.getString(Params.service);
+ Class<?> serviceClass = b.loadClass(service);
+ String serviceFilter = m_metaData.getString(Params.filter, null);
+ String defaultServiceImpl = m_metaData.getString(Params.defaultImpl, null);
+ Class<?> defaultServiceImplClass =
+ (defaultServiceImpl != null) ? b.loadClass(defaultServiceImpl) : null;
+ String added = m_metaData.getString(Params.added, null);
+ long timeout = m_metaData.getLong(Params.timeout, -1L);
+ String changed = timeout != -1 ? null : m_metaData.getString(Params.changed, null);
+ String removed = timeout != -1 ? null : m_metaData.getString(Params.removed, null);
+ String autoConfigField = m_metaData.getString(Params.autoConfig, null);
+ boolean required = "true".equals(m_metaData.getString(Params.required, "true"));
+ boolean propagate = "true".equals(m_metaData.getString(Params.propagate, "false"));
+
+ Dependency dp = createServiceDependency(dm, serviceClass,
+ serviceFilter, defaultServiceImplClass, added, changed,
+ removed, autoConfigField, timeout, required, propagate);
+ return dp;
+ }
+
+ private Dependency createServiceDependency(DependencyManager dm, Class<?> serviceClass,
+ String serviceFilter, Class<?> defaultServiceImplClass, String added,
+ String changed, String removed, String autoConfigField, long timeout, boolean required,
+ boolean propagate)
+ {
+ ServiceDependency sd = timeout != -1 ? dm.createTemporalServiceDependency(timeout)
+ : dm.createServiceDependency();
+ sd.setService(serviceClass, serviceFilter);
+ if (defaultServiceImplClass != null)
+ {
+ sd.setDefaultImplementation(defaultServiceImplClass);
+ }
+ sd.setCallbacks(added, changed, removed);
+ if (autoConfigField != null)
+ {
+ sd.setAutoConfig(autoConfigField);
+ }
+ if (timeout == -1)
+ {
+ sd.setRequired(required);
+ }
+
+ sd.setPropagate(propagate);
+ return sd;
+ }
+
+ private Dependency createConfigurationDependency(DependencyManager dm)
+ {
+ String pid = m_metaData.getString(Params.pid);
+ boolean propagate = "true".equals(m_metaData.getString(Params.propagate, "false"));
+ String callback = m_metaData.getString(Params.updated, "updated");
+ Dependency dp = createConfigurationDependency(dm, pid, callback, propagate);
+ return dp;
+ }
+
+ private Dependency createConfigurationDependency(DependencyManager dm, String pid, String callback,
+ boolean propagate)
+ {
+ if (pid == null)
+ {
+ throw new IllegalArgumentException(
+ "pid attribute not provided in ConfigurationDependency declaration");
+ }
+ ConfigurationDependency cd = dm.createConfigurationDependency();
+ cd.setPid(pid);
+ cd.setCallback(callback);
+ cd.setPropagate(propagate);
+ return cd;
+ }
+
+ /**
+ * Creates a BundleDependency that we parsed from a component descriptor entry.
+ * @param b
+ * @param dm
+ * @param parser
+ * @return
+ */
+ private Dependency createBundleDependency(DependencyManager dm)
+ {
+ String added = m_metaData.getString(Params.added, null);
+ String changed = m_metaData.getString(Params.changed, null);
+ String removed = m_metaData.getString(Params.removed, null);
+ boolean required = "true".equals(m_metaData.getString(Params.required, "true"));
+ String filter = m_metaData.getString(Params.filter, null);
+ int stateMask = m_metaData.getInt(Params.stateMask, -1);
+ boolean propagate = "true".equals(m_metaData.getString(Params.propagate, "false"));
+
+ Dependency dp = createBundleDependency(dm, added, changed, removed, required, propagate, filter,
+ stateMask);
+ return dp;
+ }
+
+ private Dependency createBundleDependency(DependencyManager dm, String added, String changed,
+ String removed, boolean required, boolean propagate, String filter, int stateMask)
+ {
+ BundleDependency bd = dm.createBundleDependency();
+ bd.setCallbacks(added, changed, removed);
+ bd.setRequired(required);
+ bd.setPropagate(propagate);
+ if (filter != null)
+ {
+ bd.setFilter(filter);
+ }
+ if (stateMask != -1)
+ {
+ bd.setStateMask(stateMask);
+ }
+ return bd;
+ }
+
+ private Dependency createResourceDependency(DependencyManager dm)
+ {
+ String added = m_metaData.getString(Params.added, null);
+ String changed = m_metaData.getString(Params.changed, null);
+ String removed = m_metaData.getString(Params.removed, null);
+ String filter = m_metaData.getString(Params.filter, null);
+ boolean required = "true".equals(m_metaData.getString(Params.required, "true"));
+ boolean propagate = "true".equals(m_metaData.getString(Params.propagate, "false"));
+ String autoConfigField = m_metaData.getString(Params.autoConfig, null);
+
+ Dependency dp = createResourceDependency(dm, added, changed, removed, required, filter,
+ propagate, autoConfigField);
+ return dp;
+ }
+
+ private Dependency createResourceDependency(DependencyManager dm, String added,
+ String changed, String removed, boolean required, String filter, boolean propagate,
+ String autoConfigField)
+ {
+ ResourceDependency rd = dm.createResourceDependency();
+ rd.setCallbacks(added, changed, removed);
+ rd.setRequired(required);
+ if (filter != null)
+ {
+ rd.setFilter(filter);
+ }
+ if (autoConfigField != null)
+ {
+ rd.setAutoConfig(autoConfigField);
+ }
+ rd.setPropagate(propagate);
+ return rd;
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DependencyManagerRuntime.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DependencyManagerRuntime.java
new file mode 100644
index 0000000..b6994f8
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DependencyManagerRuntime.java
@@ -0,0 +1,227 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+/**
+ * This class parses service descriptors generated by the annotation bnd processor.
+ * The descriptors are located under META-INF/dependencymanager directory. Such files are actually
+ * referenced by a specific "DependendencyManager-Component" manifest header.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DependencyManagerRuntime
+{
+ /**
+ * Map between bundles and their corresponding DependencyManager objects used to create bundle's components.
+ * Notice that we can safely use this map without synchronization because we are relying on the new DM4 thread
+ * model which serialize all component events safely.
+ */
+ private final Map<Bundle, DependencyManager> m_managers = new HashMap<Bundle, DependencyManager>();
+
+ /**
+ * Parser used to scan component descriptors defined in bundles meta data.
+ */
+ private final DescriptorParser m_parser;
+
+ /**
+ * We use the PackageAdmin service to allow support for annotations in fragment bundles.
+ */
+ private volatile PackageAdmin m_packageAdmin;
+
+ /**
+ * Our constructor. We'll initialize here our DM component builders.
+ */
+ public DependencyManagerRuntime()
+ {
+ // Instantiates our descriptor parser, and register our service builders into it.
+ m_parser = new DescriptorParser();
+ m_parser.addBuilder(new ComponentBuilder());
+ m_parser.addBuilder(new AspectServiceBuilder());
+ m_parser.addBuilder(new AdapterServiceBuilder());
+ m_parser.addBuilder(new BundleAdapterServiceBuilder());
+ m_parser.addBuilder(new FactoryConfigurationAdapterServiceBuilder());
+ m_parser.addBuilder(new ResourceAdapterServiceBuilder());
+ }
+
+ /**
+ * Return our Object Composition (the Activator will inject dependencies into it)
+ */
+ protected Object[] getComposition()
+ {
+ return new Object[] { this, Log.instance() };
+ }
+
+ /**
+ * Starts our Service (at this point, we have been injected with our bundle context, as well
+ * as with our log service. We'll listen to bundle start/stop events (we implement the
+ * SynchronousBundleListener interface).
+ */
+ protected void start()
+ {
+ Log.instance().info("Starting Dependency Manager annotation runtime");
+ }
+
+ /**
+ * Stops our service. We'll stop all activated DependencyManager services.
+ */
+ protected void stop()
+ {
+ Log.instance().info("Runtime: stopping services");
+ for (DependencyManager dm : m_managers.values())
+ {
+ List<Component> services = new ArrayList<Component>(dm.getComponents());
+ for (Component service : services)
+ {
+ dm.remove(service);
+ }
+ }
+
+ m_managers.clear();
+ }
+
+ /**
+ * Load the DM descriptors from the started bundle. We also check possible fragments
+ * attached to the bundle, which might also contain some DM descriptors.
+ * @param bundle the started bundle which contains a DependencyManager-Component header
+ */
+ protected void bundleStarted(Bundle bundle)
+ {
+ Log.instance().info("Scanning started bundle %s", bundle.getSymbolicName());
+ List<URL> descriptorURLs = new ArrayList<URL>();
+ collectDescriptors(bundle, descriptorURLs);
+ Bundle[] fragments = m_packageAdmin.getFragments(bundle);
+ if (fragments != null)
+ {
+ for (Bundle fragment : fragments)
+ {
+ collectDescriptors(fragment, descriptorURLs);
+ }
+ }
+ for (URL descriptorURL : descriptorURLs)
+ {
+ loadDescriptor(bundle, descriptorURL);
+ }
+ }
+
+ /**
+ * Unregisters all services for a stopping bundle.
+ * @param b
+ */
+ protected void bundleStopped(Bundle b)
+ {
+ Log.instance().info("Runtime: Removing services from stopping bundle: %s", b.getSymbolicName());
+ DependencyManager dm = m_managers.remove(b);
+ if (dm != null)
+ {
+ List<Component> services = new ArrayList<Component>(dm.getComponents());
+ for (Component service : services)
+ {
+ Log.instance().info("Runtime: Removing service: %s", service);
+ dm.remove(service);
+ }
+ }
+ }
+
+ /**
+ * Collect all descriptors found from a given bundle, including its possible attached fragments.
+ * @param bundle a started bundle containing some DM descriptors
+ * @param out the list of descriptors' URLS found from the started bundle, as well as from possibly
+ * attached fragments.
+ */
+ private void collectDescriptors(Bundle bundle, List<URL> out) {
+ String descriptorPaths = (String) bundle.getHeaders().get("DependencyManager-Component");
+ if (descriptorPaths == null)
+ {
+ return;
+ }
+
+ for (String descriptorPath : descriptorPaths.split(","))
+ {
+ URL descriptorURL = bundle.getEntry(descriptorPath);
+ if (descriptorURL == null)
+ {
+ Log.instance()
+ .error("Runtime: " + "DependencyManager component descriptor not found: %s",
+ descriptorPath);
+ continue;
+ }
+ out.add(descriptorURL);
+ }
+ }
+
+ /**
+ * Load a DependencyManager component descriptor from a given bundle.
+ * @param b
+ * @param descriptorURL
+ */
+ private void loadDescriptor(Bundle b, URL descriptorURL)
+ {
+ Log.instance().debug("Parsing descriptor %s from bundle %s", descriptorURL, b.getSymbolicName());
+
+ BufferedReader in = null;
+ try
+ {
+ in = new BufferedReader(new InputStreamReader(descriptorURL.openStream()));
+ DependencyManager dm = m_managers.get(b);
+ if (dm == null)
+ {
+ dm = new DependencyManager(b.getBundleContext());
+ m_managers.put(b, dm);
+ }
+
+ m_parser.parse(in, b, dm);
+ }
+
+ catch (Throwable t)
+ {
+ Log.instance().error("Runtime: Error while parsing descriptor %s from bundle %s",
+ t,
+ descriptorURL,
+ b.getSymbolicName());
+ }
+
+ finally
+ {
+ if (in != null)
+ {
+ try
+ {
+ in.close();
+ }
+ catch (IOException ignored)
+ {
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DescriptorParser.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DescriptorParser.java
new file mode 100644
index 0000000..2c162b1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DescriptorParser.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.io.BufferedReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.dm.DependencyManager;
+import org.json.JSONObject;
+import org.osgi.framework.Bundle;
+
+/**
+ * This class parses files generated in META-INF/*.dm by the DependencyManager bnd plugin.
+ * Each descriptor contains a JSON definition of a Service, along with its corresponding
+ * dependencies.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DescriptorParser
+{
+ private Map<String, AbstractBuilder> m_builders = new HashMap<String, AbstractBuilder>();
+
+ public void addBuilder(AbstractBuilder sb)
+ {
+ m_builders.put(sb.getType(), sb);
+ }
+
+ public void parse(BufferedReader reader, Bundle b, DependencyManager dm) throws Exception
+ {
+ String line;
+
+ // The first line is a Service Component (a Service, an Aspect Service, etc ...)
+ line = reader.readLine();
+ Log.instance().debug("DescriptorParser: parsing service %s", line);
+ JSONObject json = new JSONObject(line);
+ JSONMetaData serviceMetaData = new JSONMetaData(json);
+
+ String type = (String) json.get("type");
+ if (type == null)
+ {
+ throw new IllegalArgumentException("Invalid descriptor"
+ + ": no \"type\" parameter found in first line");
+ }
+
+ AbstractBuilder builder = m_builders.get(type);
+ if (builder == null)
+ {
+ throw new IllegalArgumentException("Invalid descriptor"
+ + ": invalid \"type\" parameter found in first line");
+ }
+
+ // Parse the rest of the lines (dependencies)
+ List<MetaData> serviceDependencies = new ArrayList<MetaData>();
+ while ((line = reader.readLine()) != null)
+ {
+ Log.instance().debug("Parsing dependency %s", line);
+ JSONObject dep = new JSONObject(line);
+ serviceDependencies.add(new JSONMetaData(dep));
+ }
+
+ // and Invoke the builder
+ builder.build(serviceMetaData, serviceDependencies, b, dm);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/FactoryConfigurationAdapterServiceBuilder.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/FactoryConfigurationAdapterServiceBuilder.java
new file mode 100644
index 0000000..0f4eb09
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/FactoryConfigurationAdapterServiceBuilder.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+
+/**
+ * Class used to build a factory configuration adapter service using metadata found from DependencyManager runtime
+ * meta-inf descriptor.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FactoryConfigurationAdapterServiceBuilder extends AbstractBuilder
+{
+ private final static String TYPE = "FactoryConfigurationAdapterService";
+
+ @Override
+ public String getType()
+ {
+ return TYPE;
+ }
+
+ @Override
+ public void build(MetaData srvMeta, List<MetaData> depsMeta, Bundle b, DependencyManager dm)
+ throws Exception
+ {
+ Class<?> implClass = b.loadClass(srvMeta.getString(Params.impl));
+ String factoryPid = srvMeta.getString(Params.factoryPid);
+ String updated = srvMeta.getString(Params.updated);
+ String[] provides = srvMeta.getStrings(Params.provides, null);
+ Dictionary<String, Object> properties = srvMeta.getDictionary(Params.properties, null);
+ boolean propagate = "true".equals(srvMeta.getString(Params.propagate, "false"));
+ Component c = dm.createFactoryConfigurationAdapterService(factoryPid, updated, propagate);
+ c.setInterface(provides, properties);
+ String factoryMethod = srvMeta.getString(Params.factoryMethod, null);
+ if (factoryMethod == null)
+ {
+ c.setImplementation(implClass);
+ }
+ else
+ {
+ c.setFactory(implClass, factoryMethod);
+ }
+ setCommonServiceParams(c, srvMeta);
+ c.setComposition(srvMeta.getString(Params.composition, null));
+ ServiceLifecycleHandler lfcleHandler = new ServiceLifecycleHandler(c, b, dm, srvMeta, depsMeta);
+ // The dependencies will be plugged by our lifecycle handler.
+ c.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
+ // Adds dependencies (except named dependencies, which are managed by the lifecycle handler).
+ addUnamedDependencies(b, dm, c, srvMeta, depsMeta);
+ dm.add(c);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/FactorySet.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/FactorySet.java
new file mode 100644
index 0000000..6d042f3
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/FactorySet.java
@@ -0,0 +1,528 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.lang.reflect.Method;
+import java.util.AbstractSet;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+
+/**
+ * This class implements a <code>java.util.Set</code> which acts as a service factory.
+ * When a <code>Service</annotation> contains a <code>factory</code> attribute, this class is provided
+ * into the OSGi registry with a <code>dm.factory.name</code> service property. So, another factory component
+ * may be injected with this Set. And each time a Dictionary configuration is registered in the Set,
+ * then a new Service instance will be instantiated, and will be provided with the Dictionary passed to the
+ * Service instance.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings( { "unchecked", "rawtypes"})
+public class FactorySet extends AbstractSet<Dictionary>
+{
+ /**
+ * When a Dictionary is registered in a factory Set, we use this special
+ * property key, whose value may provide the instance to use when
+ * creating a service.
+ */
+ private final static String DM_FACTORY_INSTANCE = "dm.factory.instance";
+
+ /**
+ * The actual Service instance that is allocated for each dictionaries added in this Set.
+ */
+ private volatile Object m_impl;
+
+ /**
+ * The Service provided in the OSGi registry.
+ */
+ private final String[] m_provide;
+
+ /**
+ * The properties to be provided by the Service.
+ */
+ private final Dictionary m_serviceProperties;
+
+ /**
+ * The configure Service callback used to pass configuration added in this Set.
+ */
+ private final String m_configure;
+
+ /**
+ * The map between our Dictionaries and corresponding Service instances.
+ * This map is modified from our serial executor, or from the add method.
+ */
+ private final ConcurrentHashMap<ServiceKey, Object> m_services = new ConcurrentHashMap<ServiceKey, Object>();
+
+ /**
+ * The list of Dependencies which are applied in the Service.
+ */
+ private final MetaData m_srvMeta;
+
+ /**
+ * The list of Dependencies which are applied in the Service.
+ */
+ private final List<MetaData> m_depsMeta;
+
+ /**
+ * The DependencyManager which is used to create Service instances.
+ */
+ private volatile DependencyManager m_dm;
+
+ /**
+ * This class is used to serialize concurrent method calls, and allow to leave our methods unsynchronized.
+ * This is required because some of our methods may invoke some Service callbacks, which must be called outside
+ * synchronized block (in order to avoid potential dead locks).
+ */
+ private final SerialExecutor m_serialExecutor = new SerialExecutor();
+
+ /**
+ * Flag used to check if our service is Active.
+ */
+ private volatile boolean m_active;
+
+ /**
+ * The bundle containing the Service annotated with the factory attribute.
+ */
+ private final Bundle m_bundle;
+
+ /**
+ * Flag used to check if a service is being created
+ */
+ private final static Object SERVICE_CREATING = new Object();
+
+ /**
+ * This class wraps <tt>Dictionary</tt>, allowing to store the dictionary into a Map, using
+ * reference-equality in place of object-equality when getting the Dictionary from the Map.
+ */
+ private static class ServiceKey
+ {
+ private Dictionary m_dictionary;
+
+ public ServiceKey(Dictionary dictionary)
+ {
+ m_dictionary = dictionary;
+ }
+
+ Dictionary getDictionary()
+ {
+ return m_dictionary;
+ }
+
+ @Override
+ public boolean equals(Object that)
+ {
+ return that instanceof ServiceKey ? (((ServiceKey) that).getDictionary() == m_dictionary)
+ : false;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return System.identityHashCode(m_dictionary);
+ }
+
+ @Override
+ public String toString()
+ {
+ return Dictionary.class.getName() + "@" + System.identityHashCode(m_dictionary);
+ }
+ }
+
+ /**
+ * Sole constructor.
+ * @param b the bundle containing the Service annotated with the factory attribute
+ * @param impl The Service implementation class
+ * @param serviceProperties The Service properties
+ * @param provides The Services provided by this Service
+ * @param factoryConfigure The configure callback invoked in order to pass configurations added in this Set.
+ */
+ public FactorySet(Bundle b, MetaData srvMeta, List<MetaData> depsMeta)
+ {
+ m_serviceProperties = srvMeta.getDictionary(Params.properties, null);
+ m_provide = srvMeta.getStrings(Params.provides, null);
+ m_configure = srvMeta.getString(Params.factoryConfigure, null);
+ m_bundle = b;
+ m_srvMeta = srvMeta;
+ m_depsMeta = depsMeta;
+ }
+
+ /**
+ * Our Service is starting.
+ */
+ public void start(Component c)
+ {
+ m_active = true;
+ m_dm = c.getDependencyManager();
+ }
+
+ /**
+ * Our Service is stopping: we have to remove all Service instances that we have created.
+ */
+ public void stop()
+ {
+ try
+ {
+ clear();
+ }
+ finally
+ {
+ m_active = false;
+ }
+ }
+
+ /**
+ * Create or Update a Service.
+ */
+ @Override
+ @SuppressWarnings({ "synthetic-access" })
+ public boolean add(final Dictionary configuration)
+ {
+ // Check parameter validity
+ if (configuration == null)
+ {
+ throw new NullPointerException("configuration parameter can't be null");
+ }
+
+ // Check if our service is running.
+ checkServiceAvailable();
+
+ // Check if service is being created
+ ServiceKey serviceKey = new ServiceKey(configuration);
+ boolean creating = m_services.putIfAbsent(serviceKey, SERVICE_CREATING) == null;
+
+ // Create or Update the Service.
+ m_serialExecutor.enqueue(new Runnable()
+ {
+ public void run()
+ {
+ doAdd(configuration);
+ }
+ });
+
+ m_serialExecutor.execute();
+ return creating;
+ }
+
+ /**
+ * Another Service wants to remove an existing Service.
+ * This method is not synchronized but uses a SerialExecutor for serializing concurrent method call.
+ * (This avoid potential dead locks, especially when Service callback methods are invoked).
+ */
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public boolean remove(final Object configuration)
+ {
+ // Check parameter validity.
+ if (configuration == null)
+ {
+ throw new NullPointerException("configuration parameter can't be null");
+ }
+ if (!(configuration instanceof Dictionary))
+ {
+ throw new IllegalArgumentException("configuration must be an instance of a Dictionary");
+ }
+
+ // Check if our service is active.
+ checkServiceAvailable();
+
+ // Check if service is created (or creating)
+ boolean found = m_services.containsKey(new ServiceKey((Dictionary) configuration));
+ if (found)
+ {
+ // Create or Update the Service.
+ m_serialExecutor.enqueue(new Runnable()
+ {
+ public void run()
+ {
+ doRemove((Dictionary) configuration);
+ }
+ });
+ m_serialExecutor.execute();
+ }
+ return found;
+ }
+
+ /**
+ * Another Service wants to remove all existing Services.
+ * This method is not synchronized but uses a SerialExecutor for serializing concurrent method call.
+ * (This avoid potential dead locks, especially when Service callback methods are invoked).
+ */
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public void clear()
+ {
+ if (!m_active)
+ {
+ return;
+ }
+
+ // Make sure add/update/clear events are handled in FIFO order (serially).
+ m_serialExecutor.enqueue(new Runnable()
+ {
+ public void run()
+ {
+ doClear();
+ }
+ });
+ m_serialExecutor.execute();
+ }
+
+ /**
+ * returns the list of active Service instances configurations.
+ */
+ @Override
+ public Iterator<Dictionary> iterator()
+ {
+ throw new UnsupportedOperationException(
+ "iterator method is not supported by DependencyManager Set's service factories");
+ }
+
+ @Override
+ public String toString()
+ {
+ return FactorySet.class.getName() + "(" + m_services.size() + " active instances)";
+ }
+
+ /**
+ * Returns the number of active Service instances.
+ */
+ @Override
+ public int size()
+ {
+ if (!m_active)
+ {
+ return 0;
+ }
+ return m_services.size();
+ }
+
+ /**
+ * Checks if our Service is available (we are not stopped").
+ */
+ private void checkServiceAvailable()
+ {
+ if (!m_active)
+ {
+ throw new IllegalStateException("Service not available");
+ }
+ }
+
+ /**
+ * Add or create a new Service instance, given its configuration. This method is invoked by the
+ * SerialExecutor, hence it's thread safe and we'll invoke Service's callbacks without being
+ * synchronized (hence this will avoid potential dead locks).
+ */
+ private void doAdd(Dictionary configuration)
+ {
+ // Check if the service exists.
+ ServiceKey serviceKey = new ServiceKey(configuration);
+ Object service = m_services.get(serviceKey);
+
+ if (service == null || service == SERVICE_CREATING)
+ {
+ try
+ {
+ // Create the Service / impl, unless it is explicitly provided from the
+ // configuration (using the specific key "dm.factory.instance").
+ Component s = m_dm.createComponent();
+ Class<?> implClass = m_bundle.loadClass(m_srvMeta.getString(Params.impl));
+ Object impl = configuration.get(DM_FACTORY_INSTANCE);
+ if (impl == null)
+ {
+ String factoryMethod = m_srvMeta.getString(Params.factoryMethod, null);
+ if (factoryMethod == null)
+ {
+ m_impl = implClass.newInstance();
+ }
+ else
+ {
+ Method m = implClass.getDeclaredMethod(factoryMethod);
+ m.setAccessible(true);
+ m_impl = m.invoke(null);
+ }
+ }
+ else
+ {
+ m_impl = impl;
+ }
+
+ // Invoke "configure" callback
+ if (m_configure != null)
+ {
+ invokeConfigure(m_impl, m_configure, configuration);
+ }
+
+ // Create Service
+ s.setImplementation(m_impl);
+ if (m_provide != null)
+ {
+ // Merge service properties with the configuration provided by the factory.
+ Dictionary serviceProperties = mergeSettings(m_serviceProperties, configuration);
+ s.setInterface(m_provide, serviceProperties);
+ }
+
+ s.setComposition(m_srvMeta.getString(Params.composition, null));
+ ServiceLifecycleHandler lfcleHandler = new ServiceLifecycleHandler(s, m_bundle, m_dm,
+ m_srvMeta, m_depsMeta);
+ // The dependencies will be plugged by our lifecycle handler.
+ s.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
+
+ // Adds dependencies (except named dependencies, which are managed by the lifecycle handler).
+ for (MetaData dependency: m_depsMeta)
+ {
+ String name = dependency.getString(Params.name, null);
+ if (name == null)
+ {
+ DependencyBuilder depBuilder = new DependencyBuilder(dependency);
+ Log.instance().info("ServiceLifecycleHandler.init: adding dependency %s into service %s",
+ dependency, m_srvMeta);
+ Dependency d = depBuilder.build(m_bundle, m_dm);
+ s.add(d);
+ }
+ }
+
+ // Register the Service instance, and keep track of it.
+ Log.instance().info("ServiceFactory: created service %s", m_srvMeta);
+ m_dm.add(s);
+ m_services.put(serviceKey, s);
+ }
+ catch (Throwable t)
+ {
+ // Make sure the SERVICE_CREATING flag is also removed
+ m_services.remove(serviceKey);
+ Log.instance().error("ServiceFactory: could not instantiate service %s",
+ t, m_srvMeta);
+ }
+ }
+ else
+ {
+ // Reconfigure an already existing Service.
+ if (m_configure != null)
+ {
+ Log.instance().info("ServiceFactory: updating service %s", m_impl);
+ invokeConfigure(m_impl, m_configure, configuration);
+ }
+
+ // Update service properties
+ if (m_provide != null)
+ {
+ Dictionary settings = mergeSettings(m_serviceProperties, configuration);
+ ((Component) service).setServiceProperties(settings);
+ }
+ }
+ }
+
+ private void doRemove(Dictionary configuraton)
+ {
+ Log.instance().info("ServiceFactory: removing service %s", m_srvMeta);
+ ServiceKey serviceKey = new ServiceKey(configuraton);
+ Object service = m_services.remove(serviceKey);
+ if (service != null && service != SERVICE_CREATING)
+ {
+ m_dm.remove((Component) service);
+ }
+ }
+
+ private void doClear()
+ {
+ for (Object service : m_services.values())
+ {
+ if (service instanceof Component)
+ {
+ m_dm.remove((Component) service);
+ }
+ }
+ m_services.clear();
+ }
+
+ /**
+ * Merge factory configuration settings with the service properties. The private factory configuration
+ * settings are ignored. A factory configuration property is private if its name starts with a dot (".").
+ *
+ * @param serviceProperties
+ * @param factoryConfiguration
+ * @return
+ */
+ private Dictionary mergeSettings(Dictionary serviceProperties, Dictionary factoryConfiguration)
+ {
+ Dictionary props = new Hashtable();
+
+ if (serviceProperties != null)
+ {
+ Enumeration keys = serviceProperties.keys();
+ while (keys.hasMoreElements())
+ {
+ Object key = keys.nextElement();
+ Object val = serviceProperties.get(key);
+ props.put(key, val);
+ }
+ }
+
+ Enumeration keys = factoryConfiguration.keys();
+ while (keys.hasMoreElements())
+ {
+ Object key = keys.nextElement();
+ if (!key.toString().startsWith("."))
+ {
+ // public properties are propagated
+ Object val = factoryConfiguration.get(key);
+ props.put(key, val);
+ }
+ }
+ return props;
+ }
+
+ /**
+ * Invokes the configure callback method on the service instance implemenatation.
+ * @param impl
+ * @param configure
+ * @param config
+ */
+ private void invokeConfigure(Object impl, String configure, Dictionary config)
+ {
+ try
+ {
+ InvocationUtil.invokeCallbackMethod(impl, configure,
+ new Class[][] { { Dictionary.class } },
+ new Object[][] { { config } });
+ }
+
+ catch (Throwable t)
+ {
+ if (t instanceof RuntimeException)
+ {
+ throw (RuntimeException) t;
+ }
+ else
+ {
+ throw new RuntimeException("Could not invoke method " + configure
+ + " on object " + impl, t);
+ }
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/InvocationUtil.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/InvocationUtil.java
new file mode 100644
index 0000000..fb4456b
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/InvocationUtil.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Java reflexion utility methods.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class InvocationUtil
+{
+ public static Object invokeCallbackMethod(Object instance,
+ String methodName,
+ Class<?>[][] signatures,
+ Object[][] parameters)
+ throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException,
+ InvocationTargetException
+ {
+ Class<?> currentClazz = instance.getClass();
+ while (currentClazz != null)
+ {
+ try
+ {
+ return invokeMethod(instance, currentClazz, methodName, signatures, parameters, false);
+ }
+ catch (NoSuchMethodException nsme)
+ {
+ // ignore
+ }
+ currentClazz = currentClazz.getSuperclass();
+ }
+ throw new NoSuchMethodException(methodName);
+ }
+
+ public static Object invokeMethod(Object object,
+ Class<?> clazz,
+ String name,
+ Class<?>[][] signatures,
+ Object[][] parameters,
+ boolean isSuper)
+ throws NoSuchMethodException, InvocationTargetException, IllegalArgumentException,
+ IllegalAccessException
+ {
+ if (object == null)
+ {
+ throw new IllegalArgumentException("Instance cannot be null");
+ }
+ if (clazz == null)
+ {
+ throw new IllegalArgumentException("Class cannot be null");
+ }
+
+ // If we're talking to a proxy here, dig one level deeper to expose the
+ // underlying invocation handler ...
+
+ if (Proxy.isProxyClass(clazz))
+ {
+ object = Proxy.getInvocationHandler(object);
+ clazz = object.getClass();
+ }
+
+ for (int i = 0; i < signatures.length; i++)
+ {
+ Class<?>[] signature = signatures[i];
+ try
+ {
+ final Method m = clazz.getDeclaredMethod(name, signature);
+ if (!(isSuper && Modifier.isPrivate(m.getModifiers())))
+ {
+ AccessController.doPrivileged(new PrivilegedAction<Object>()
+ {
+ public Object run()
+ {
+ m.setAccessible(true);
+ return null;
+ }
+ });
+ return m.invoke(object, parameters[i]);
+ }
+ }
+ catch (NoSuchMethodException e)
+ {
+ // ignore this and keep looking
+ }
+ }
+ throw new NoSuchMethodException(name);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/JSONMetaData.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/JSONMetaData.java
new file mode 100644
index 0000000..0548394
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/JSONMetaData.java
@@ -0,0 +1,482 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.lang.reflect.Array;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Thsi class represents the parsed data found from meta-inf dependencymanager descriptors.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class JSONMetaData implements MetaData, Cloneable
+{
+ /**
+ * The parsed Dependency or Service metadata. The map value is either a String, a String[],
+ * or a Dictionary, whose values are String or String[].
+ */
+ private HashMap<String, Object> m_metadata = new HashMap<String, Object>();
+
+ /**
+ * Decodes Json metadata for either a Service or a Dependency descriptor entry.
+ * The JSON object has the following form:
+ *
+ * entry ::= String | String[] | dictionary
+ * dictionary ::= key-value-pair*
+ * key-value-pair ::= key value
+ * value ::= String | String[] | value-type
+ * value-type ::= jsonObject with value-type-info
+ * value-type-info ::= "type"=primitive java type
+ * "value"=String|String[]
+ *
+ * Exemple:
+ *
+ * {"string-param" : "string-value",
+ * "string-array-param" : ["string1", "string2"],
+ * "properties" : {
+ * "string-param" : "string-value",
+ * "string-array-param" : ["str1", "str2],
+ * "long-param" : {"type":"java.lang.Long", "value":"1"}}
+ * "long-array-param" : {"type":"java.lang.Long", "value":["1"]}}
+ * }
+ * }
+ *
+ * @param jso the JSON object that corresponds to a dependency manager descriptor entry line.
+ * @throws JSONException
+ */
+ @SuppressWarnings("unchecked")
+ public JSONMetaData(JSONObject jso) throws JSONException
+ {
+ // Decode json object into our internal map.
+ Iterator<String> it = jso.keys();
+ while (it.hasNext())
+ {
+ String key = it.next();
+ Object value = jso.get(key);
+ if (value instanceof String)
+ {
+ m_metadata.put(key, value);
+ }
+ else if (value instanceof JSONArray)
+ {
+ m_metadata.put(key, decodeStringArray((JSONArray) value));
+ }
+ else if (value instanceof JSONObject)
+ {
+ m_metadata.put(key, parseProperties((JSONObject) value));
+ }
+ }
+ }
+
+ private Hashtable<String, Object> parseProperties(JSONObject properties) throws JSONException {
+ Hashtable<String, Object> parsedProps = new Hashtable<String, Object>();
+ @SuppressWarnings("unchecked")
+ Iterator<String> it = properties.keys();
+ while (it.hasNext())
+ {
+ String key = it.next();
+ Object value = properties.get(key);
+ if (value instanceof String)
+ {
+ // This property type is a simple string
+ parsedProps.put(key, value);
+ }
+ else if (value instanceof JSONArray)
+ {
+ // This property type is a simple string array
+ parsedProps.put(key, decodeStringArray((JSONArray) value));
+ }
+ else if (value instanceof JSONObject)
+ {
+ // This property type is a typed value, encoded as a JSONObject with two keys: "type"/"value"
+ JSONObject json = ((JSONObject) value);
+ String type = json.getString("type");
+ Object typeValue = json.get("value");
+
+ if (type == null)
+ {
+ throw new JSONException("missing type attribute in json metadata for key " + key);
+ }
+ if (typeValue == null)
+ {
+ throw new JSONException("missing type value attribute in json metadata for key " + key);
+ }
+
+ Class<?> typeClass;
+ try
+ {
+ typeClass = Class.forName(type);
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new JSONException("invalid type attribute (" + type + ") in json metadata for key "
+ + key);
+ }
+
+ if (typeValue instanceof JSONArray)
+ {
+ parsedProps.put(key, toPrimitiveTypeArray(typeClass, (JSONArray) typeValue));
+ }
+ else
+ {
+ parsedProps.put(key, toPrimitiveType(typeClass, typeValue.toString()));
+ }
+ }
+ }
+ return parsedProps;
+ }
+
+ private Object toPrimitiveType(Class<?> type, String value) throws JSONException {
+ if (type.equals(String.class))
+ {
+ return value;
+ }
+ else if (type.equals(Long.class))
+ {
+ return Long.parseLong(value);
+ }
+ else if (type.equals(Double.class))
+ {
+ return Double.valueOf(value);
+ }
+ else if (type.equals(Float.class))
+ {
+ return Float.valueOf(value);
+ }
+ else if (type.equals(Integer.class))
+ {
+ return Integer.valueOf(value);
+ }
+ else if (type.equals(Byte.class))
+ {
+ return Byte.valueOf(value);
+ }
+ else if (type.equals(Character.class))
+ {
+ return Character.valueOf((char) Integer.parseInt(value));
+ }
+ else if (type.equals(Boolean.class))
+ {
+ return Boolean.valueOf(value);
+ }
+ else if (type.equals(Short.class))
+ {
+ return Short.valueOf(value);
+ }
+ else
+ {
+ throw new JSONException("invalid type (" + type + ") attribute in json metadata");
+ }
+ }
+
+ private Object toPrimitiveTypeArray(Class<?> type, JSONArray array) throws JSONException {
+ int len = array.length();
+ Object result = Array.newInstance(type, len);
+
+ if (type.equals(String.class))
+ {
+ for (int i = 0; i < len; i ++) {
+ Array.set(result, i, array.getString(i));
+ }
+ }
+ else if (type.equals(Long.class))
+ {
+ for (int i = 0; i < len; i ++) {
+ Array.set(result, i, Long.valueOf(array.getString(i)));
+ }
+ }
+ else if (type.equals(Double.class))
+ {
+ for (int i = 0; i < len; i ++) {
+ Array.set(result, i, Double.valueOf(array.getString(i)));
+ }
+ }
+ else if (type.equals(Float.class))
+ {
+ for (int i = 0; i < len; i ++) {
+ Array.set(result, i, Float.valueOf(array.getString(i)));
+ }
+ }
+ else if (type.equals(Integer.class))
+ {
+ for (int i = 0; i < len; i ++) {
+ Array.set(result, i, Integer.valueOf(array.getString(i)));
+ }
+ }
+ else if (type.equals(Byte.class))
+ {
+ for (int i = 0; i < len; i ++) {
+ Array.set(result, i, Byte.valueOf(array.getString(i)));
+ }
+ }
+ else if (type.equals(Character.class))
+ {
+ for (int i = 0; i < len; i ++) {
+ Array.set(result, i, Character.valueOf((char) Integer.parseInt(array.getString(i))));
+ }
+ }
+ else if (type.equals(Boolean.class))
+ {
+ for (int i = 0; i < len; i ++) {
+ Array.set(result, i, Boolean.valueOf(array.getString(i)));
+ }
+ }
+ else if (type.equals(Short.class))
+ {
+ for (int i = 0; i < len; i ++) {
+ Array.set(result, i, Short.valueOf(array.getString(i)));
+ }
+ }
+ else
+ {
+ throw new JSONException("invalid type (" + type + ") attribute in json metadata");
+ }
+ return result;
+ }
+
+ /**
+ * Close this class instance to another one.
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object clone() throws CloneNotSupportedException
+ {
+ JSONMetaData clone = (JSONMetaData) super.clone();
+ clone.m_metadata = (HashMap<String, Object>) m_metadata.clone();
+ return clone;
+ }
+
+ public String getString(Params key)
+ {
+ Object value = m_metadata.get(key.toString());
+ if (value == null)
+ {
+ throw new IllegalArgumentException("Parameter " + key + " not found");
+ }
+ return value.toString();
+ }
+
+ public String getString(Params key, String def)
+ {
+ try
+ {
+ return getString(key);
+ }
+ catch (IllegalArgumentException e)
+ {
+ return def;
+ }
+ }
+
+ public int getInt(Params key)
+ {
+ Object value = m_metadata.get(key.toString());
+ if (value != null)
+ {
+ try
+ {
+ if (value instanceof Integer) {
+ return ((Integer) value).intValue();
+ }
+ return Integer.parseInt(value.toString());
+ }
+ catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("parameter " + key
+ + " is not an int value: "
+ + value);
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("missing " + key
+ + " parameter from annotation");
+ }
+ }
+
+ public int getInt(Params key, int def)
+ {
+ Object value = m_metadata.get(key.toString());
+ if (value != null)
+ {
+ try
+ {
+ if (value instanceof Integer) {
+ return ((Integer) value).intValue();
+ }
+ return Integer.parseInt(value.toString());
+ }
+ catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("parameter " + key
+ + " is not an int value: "
+ + value);
+ }
+ }
+ else
+ {
+ return def;
+ }
+ }
+
+ public long getLong(Params key)
+ {
+ Object value = m_metadata.get(key.toString());
+ if (value != null)
+ {
+ try
+ {
+ if (value instanceof Long) {
+ return ((Long) value).longValue();
+ }
+ return Long.parseLong(value.toString());
+ }
+ catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("parameter " + key
+ + " is not a long value: "
+ + value);
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("missing " + key
+ + " parameter from annotation");
+ }
+ }
+
+ public long getLong(Params key, long def)
+ {
+ Object value = m_metadata.get(key.toString());
+ if (value != null)
+ {
+ try
+ {
+ if (value instanceof Long) {
+ return (Long) value;
+ }
+ return Long.parseLong(value.toString());
+ }
+ catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("parameter " + key
+ + " is not a long value: "
+ + value);
+ }
+ }
+ else
+ {
+ return def;
+ }
+ }
+
+ public String[] getStrings(Params key)
+ {
+ Object array = m_metadata.get(key.toString());
+ if (array == null)
+ {
+ throw new IllegalArgumentException("Parameter " + key + " not found");
+ }
+
+ if (!(array instanceof String[]))
+ {
+ throw new IllegalArgumentException("Parameter " + key + " is not a String[] (" + array.getClass()
+ + ")");
+ }
+ return (String[]) array;
+ }
+
+ public String[] getStrings(Params key, String[] def)
+ {
+ try
+ {
+ return getStrings(key);
+ }
+ catch (IllegalArgumentException t)
+ {
+ return def;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public Dictionary<String, Object> getDictionary(Params key,
+ Dictionary<String, Object> def)
+ {
+ Object dictionary = m_metadata.get(key.toString());
+ if (dictionary == null)
+ {
+ return def;
+ }
+
+ if (!(dictionary instanceof Dictionary<?, ?>))
+ {
+ throw new IllegalArgumentException("Parameter " + key + " is not a Dictionary ("
+ + dictionary.getClass() + ")");
+ }
+
+ return (Dictionary<String, Object>) dictionary;
+ }
+
+ @Override
+ public String toString()
+ {
+ return m_metadata.toString();
+ }
+
+ public void setDictionary(Params key, Dictionary<String, Object> dictionary)
+ {
+ m_metadata.put(key.toString(), dictionary);
+ }
+
+ public void setString(Params key, String value)
+ {
+ m_metadata.put(key.toString(), value);
+ }
+
+ public void setStrings(Params key, String[] values)
+ {
+ m_metadata.put(key.toString(), values);
+ }
+
+ /**
+ * Decodes a JSONArray into a String array (all JSON array values are supposed to be strings).
+ */
+ private String[] decodeStringArray(JSONArray array) throws JSONException
+ {
+ String[] arr = new String[array.length()];
+ for (int i = 0; i < array.length(); i++)
+ {
+ Object value = array.get(i);
+ if (!(value instanceof String))
+ {
+ throw new IllegalArgumentException("JSON array is not an array of Strings: " + array);
+ }
+ arr[i] = value.toString();
+ }
+ return arr;
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Log.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Log.java
new file mode 100644
index 0000000..f23ba0e
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Log.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import org.osgi.service.log.LogService;
+
+/**
+ * This class logs some formattable strings into the OSGi Log Service.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Log
+{
+ /** The wrap log service which is actually used (Injected by Activator) */
+ private volatile LogService m_logService;
+
+ /** Our sole instance */
+ private static Log m_instance = new Log();
+
+ public static Log instance()
+ {
+ return m_instance;
+ }
+
+ public void error(String format, Object ... args)
+ {
+ m_logService.log(LogService.LOG_ERROR, String.format(format, args));
+ }
+
+ public void error(String format, Throwable t, Object ... args)
+ {
+ m_logService.log(LogService.LOG_ERROR, String.format(format, args), t);
+ }
+
+ public void warn(String format, Object ... args)
+ {
+ m_logService.log(LogService.LOG_WARNING, String.format(format, args));
+ }
+
+ public void warn(String format, Throwable t, Object ... args)
+ {
+ m_logService.log(LogService.LOG_WARNING, String.format(format, args), t);
+ }
+
+ public void info(String format, Object ... args)
+ {
+ m_logService.log(LogService.LOG_INFO, String.format(format, args));
+ }
+
+ public void info(String format, Throwable t, Object ... args)
+ {
+ m_logService.log(LogService.LOG_INFO, String.format(format, args), t);
+ }
+
+ public void debug(String format, Object ... args)
+ {
+ m_logService.log(LogService.LOG_DEBUG, String.format(format, args));
+ }
+
+ public void debug(String format, Throwable t, Object ... args)
+ {
+ m_logService.log(LogService.LOG_DEBUG, String.format(format, args), t);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/MetaData.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/MetaData.java
new file mode 100644
index 0000000..435f958
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/MetaData.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.util.Dictionary;
+
+/**
+ * This class represents the meta data parsed from a descriptor entry (json) line.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface MetaData extends Cloneable
+{
+ /**
+ * Returns a String descriptor entry parameter value.
+ */
+ String getString(Params key);
+
+ /**
+ * Returns a String descriptor entry parameter value.
+ */
+ String getString(Params key, String def);
+
+ /**
+ * Returns a String descriptor entry parameter value.
+ */
+ int getInt(Params key);
+
+ /**
+ * Returns a String descriptor entry parameter value.
+ */
+ int getInt(Params key, int def);
+
+ /**
+ * Returns a String descriptor entry parameter value.
+ */
+ long getLong(Params key);
+
+ /**
+ * Returns a String descriptor entry parameter value.
+ */
+ long getLong(Params key, long def);
+
+ /**
+ * Returns a String array descriptor entry parameter value.
+ */
+ String[] getStrings(Params key);
+
+ /**
+ * Returns a String array descriptor entry parameter value.
+ */
+ String[] getStrings(Params key, String[] def);
+
+ /**
+ * Returns a descriptor entry value which is a complex value.
+ */
+ Dictionary<String, Object> getDictionary(Params key, Dictionary<String, Object> def);
+
+ /**
+ * Modifies a key Sring value
+ */
+ void setString(Params key, String value);
+
+ /**
+ * Modifies a String[] value.
+ */
+ void setStrings(Params key, String[] values);
+
+ /**
+ * Modifies a String[] value.
+ */
+ void setDictionary(Params key, Dictionary<String, Object> dictionary);
+
+ /**
+ * Clone this MetaData object.
+ */
+ Object clone() throws CloneNotSupportedException;
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Params.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Params.java
new file mode 100644
index 0000000..37f251f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Params.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+/**
+ * List of descriptor parameters.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public enum Params
+{
+ type,
+ init,
+ start,
+ stop,
+ destroy,
+ impl,
+ provides,
+ properties,
+ composition,
+ service,
+ filter,
+ defaultImpl,
+ required,
+ added,
+ changed,
+ removed,
+ swap,
+ autoConfig,
+ pid,
+ propagate,
+ updated,
+ timeout,
+ adapteeService,
+ adapteeFilter,
+ stateMask,
+ ranking,
+ factoryPid,
+ factorySet,
+ factoryName,
+ factoryConfigure,
+ factoryMethod,
+ name,
+ field,
+ starter,
+ stopper,
+ bundleContextField,
+ dependencyManagerField,
+ componentField,
+ registered,
+ unregistered
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ResourceAdapterServiceBuilder.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ResourceAdapterServiceBuilder.java
new file mode 100644
index 0000000..7174396
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ResourceAdapterServiceBuilder.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+
+/**
+ * Class used to build a resource adapter service using metadata found from DependencyManager runtime
+ * meta-inf descriptor.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResourceAdapterServiceBuilder extends AbstractBuilder
+{
+ private final static String TYPE = "ResourceAdapterService";
+
+ @Override
+ public String getType()
+ {
+ return TYPE;
+ }
+
+ @Override
+ public void build(MetaData srvMeta, List<MetaData> depsMeta, Bundle b, DependencyManager dm)
+ throws Exception
+ {
+ String filter = srvMeta.getString(Params.filter, null);
+ Class<?> implClass = b.loadClass(srvMeta.getString(Params.impl));
+ String[] provides = srvMeta.getStrings(Params.provides, null);
+ Dictionary<String, Object> properties = srvMeta.getDictionary(Params.properties, null);
+ boolean propagate = "true".equals(srvMeta.getString(Params.propagate, "false"));
+ String changed = srvMeta.getString(Params.changed, null /* no change callback if not specified explicitly */);
+ Component c = dm.createResourceAdapterService(filter, propagate, null, changed);
+ c.setInterface(provides, properties);
+ String factoryMethod = srvMeta.getString(Params.factoryMethod, null);
+ if (factoryMethod == null)
+ {
+ c.setImplementation(implClass);
+ }
+ else
+ {
+ c.setFactory(implClass, factoryMethod);
+ }
+ setCommonServiceParams(c, srvMeta);
+ c.setComposition(srvMeta.getString(Params.composition, null));
+ ServiceLifecycleHandler lfcleHandler = new ServiceLifecycleHandler(c, b, dm, srvMeta, depsMeta);
+ // The dependencies will be plugged by our lifecycle handler.
+ c.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
+ // Adds dependencies (except named dependencies, which are managed by the lifecycle handler).
+ addUnamedDependencies(b, dm, c, srvMeta, depsMeta);
+ dm.add(c);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/SerialExecutor.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/SerialExecutor.java
new file mode 100644
index 0000000..b7dae8f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/SerialExecutor.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+
+/**
+ * Allows you to enqueue tasks from multiple threads and then execute
+ * them on one thread sequentially. It assumes more than one thread will
+ * try to execute the tasks and it will make an effort to pick the first
+ * task that comes along whilst making sure subsequent tasks return
+ * without waiting.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public final class SerialExecutor {
+ private static final Runnable DUMMY_RUNNABLE = new Runnable() { public void run() {} };
+ private final LinkedList<Runnable> m_workQueue = new LinkedList<>();
+ private Runnable m_active;
+
+ /**
+ * Enqueue a new task for later execution. This method is
+ * thread-safe, so multiple threads can contribute tasks.
+ *
+ * @param runnable the runnable containing the actual task
+ */
+ public synchronized void enqueue(final Runnable runnable) {
+ m_workQueue.addLast(new Runnable() {
+ public void run() {
+ try {
+ runnable.run();
+ }
+ finally {
+ scheduleNext();
+ }
+ }
+ });
+ }
+
+ /**
+ * Execute any pending tasks. This method is thread safe,
+ * so multiple threads can try to execute the pending
+ * tasks, but only the first will be used to actually do
+ * so. Other threads will return immediately.
+ */
+ public void execute() {
+ Runnable active;
+ synchronized (this) {
+ active = m_active;
+ // for now just put some non-null value in there so we can never
+ // get a race condition when two threads enter this section after
+ // one another (causing sheduleNext() to be invoked twice below)
+ m_active = DUMMY_RUNNABLE;
+ }
+ if (active == null) {
+ scheduleNext();
+ }
+ }
+
+ private void scheduleNext() {
+ Runnable active;
+ synchronized (this) {
+ try {
+ m_active = (Runnable) m_workQueue.removeFirst();
+ }
+ catch (NoSuchElementException e) {
+ m_active = null;
+ }
+ active = m_active;
+ }
+ if (active != null) {
+ active.run();
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ServiceLifecycleHandler.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ServiceLifecycleHandler.java
new file mode 100644
index 0000000..4058988
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ServiceLifecycleHandler.java
@@ -0,0 +1,456 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+
+/**
+ * Caution: this class *MUST* be immutable, because it may be shared between Aspects/Adapters
+ * and concrete Aspect/Adapter instance.
+ *
+ * This class allows Services to configure dynamically their dependency filters from their init() method.
+ * Basically, this class acts as a service implementation lifecycle handler. When we detect that the Service is
+ * called in its init() method, and if init() returns a Map, then the Map is assumed to contain
+ * dependency filters, which will be applied to all named dependencies. The Map optionally returned by
+ * Service's init method may contain the following keys:
+ * <ul>
+ * <li>name.filter: the value must be a valid OSGi filter, and the "name" prefix must match a ServiceDependency
+ * name attribute</li>
+ * <li>name.required: the value is a boolean ("true"|"false") and the "name" prefix must match a
+ * ServiceDependency name attribute</li>
+ * </ul>
+ *
+ * <p>Dependencies which provide a name attribute will be activated after the init method returns. Other
+ * dependencies are injected before the init method.
+ *
+ * <p>Example of a Service whose dependency filter is configured from ConfigAdmin:
+ *
+ * <blockquote><pre>
+ * /**
+ * * A Service whose service dependency filter/require attribute may be configured from ConfigAdmin
+ * */
+ * @Service
+ * class X {
+ * private Dictionary m_config;
+ *
+ * @ConfigurationDependency(pid="MyPid")
+ * void configure(Dictionary conf) {
+ * // Initialize our service from config ...
+ *
+ * // And store the config for later usage (from our init method)
+ * m_config = config;
+ * }
+ *
+ * @ServiceDependency(name="dependency1")
+ * void bindOtherService(OtherService other) {
+ * // the filter and required flag will be configured from our init method.
+ * }
+ *
+ * // The returned Map will be used to configure our "dependency1" Dependency.
+ * @Init
+ * Map init() {
+ * return new HashMap() {{
+ * put("dependency1.filter", m_config.get("filter"));
+ * put("dependency1.required", m_config.get("required"));
+ * }};
+ * }
+ * }
+ * </pre></blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceLifecycleHandler
+{
+ private final String m_init;
+ private final String m_start;
+ private final String m_stop;
+ private final String m_destroy;
+ private final MetaData m_srvMeta;
+ private final List<MetaData> m_depsMeta;
+ private final Bundle m_bundle;
+ private final static Object SYNC = new Object();
+
+ /**
+ * Makes a new ServiceLifecycleHandler object. This objects allows to decorate the "init" service callback, in
+ * order to see if "init" callback returns a dependency customization map.
+ *
+ * @param srv The Service for the annotated class
+ * @param srvBundle the Service bundle
+ * @param dm The DependencyManager that was used to create the service
+ * @param srvMeta The Service MetaData
+ * @param depMeta The Dependencies MetaData
+ */
+ public ServiceLifecycleHandler(Component srv, Bundle srvBundle, DependencyManager dm,
+ MetaData srvMeta, List<MetaData> depMeta)
+ {
+ m_srvMeta = srvMeta;
+ m_init = srvMeta.getString(Params.init, null);
+ m_start = srvMeta.getString(Params.start, null);
+ m_stop = srvMeta.getString(Params.stop, null);
+ m_destroy = srvMeta.getString(Params.destroy, null);
+ m_bundle = srvBundle;
+ m_depsMeta = depMeta;
+ }
+
+ /**
+ * Handles an "init" lifecycle service callback. We just catch the "init" method, and callback
+ * the actual Service' init method, to see if a dependency customization map is returned.
+ * We also check if a Lifecycle Controller is used. In this case, we add a hidden custom dependency,
+ * allowing to take control of when the component is actually started/stopped.
+ * We also handle an edge case described in FELIX-4050, where component state calculation
+ * may mess up if some dependencies are added using the API from the init method.
+ *
+ * @param c The Annotated Component
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public void init(Component c)
+ throws Exception
+ {
+ Object serviceInstance = c.getInstances()[0];
+ DependencyManager dm = c.getDependencyManager();
+
+ // Check if a lifecycle controller is defined for this service. If true, then
+ // We'll use the ToggleServiceDependency in order to manually activate/deactivate
+ // the component ...
+ String starter = m_srvMeta.getString(Params.starter, null);
+ String stopper = m_srvMeta.getString(Params.stopper, null);
+
+ List<Dependency> instanceBoundDeps = new ArrayList<>();
+
+ if (starter != null)
+ {
+ // We'll inject two runnables: one that will start or service, when invoked, and the other
+ // that will stop our service, when invoked. We'll use a shared atomic boolean in order to
+ // synchronize both runnables.
+ Log.instance().debug("Setting up a lifecycle controller for service %s", serviceInstance);
+ String componentName = serviceInstance.getClass().getName();
+ // Create a toggle service, used to start/stop our service.
+ ToggleServiceDependency toggle = new ToggleServiceDependency();
+ AtomicBoolean startFlag = new AtomicBoolean(false);
+ // Add the toggle to the service.
+ instanceBoundDeps.add(toggle);
+ // Inject the runnable that will start our service, when invoked.
+ setField(serviceInstance, starter, Runnable.class, new ComponentStarter(componentName, toggle, startFlag));
+ if (stopper != null) {
+ // Inject the runnable that will stop our service, when invoked.
+ setField(serviceInstance, stopper, Runnable.class, new ComponentStopper(componentName, toggle, startFlag));
+ }
+ }
+
+ // Before invoking an optional init method, we have to handle an edge case (FELIX-4050), where
+ // init may add dependencies using the API and also return a map for configuring some
+ // named dependencies. We have to add a hidden toggle dependency in the component, which we'll
+ // active *after* the init method is called, and possibly *after* named dependencies are configured.
+
+ ToggleServiceDependency initToggle = null;
+ if (m_init != null)
+ {
+ initToggle = new ToggleServiceDependency();
+ c.add(initToggle);
+ }
+
+ // Invoke component and all composites init methods, and for each one, check if a dependency
+ // customization map is returned by the method. This map will be used to configure
+ // some dependency filters (or required flag).
+
+ Map<String, String> customization = new HashMap<String, String>();
+ Object[] composites = c.getInstances();
+ for (Object composite: composites)
+ {
+ Object o = invokeMethod(composite, m_init, dm, c);
+ if (o != null && Map.class.isAssignableFrom(o.getClass()))
+ {
+ customization.putAll((Map) o);
+ }
+ }
+
+ Log.instance().debug("ServiceLifecycleHandler.init: invoked init method from service %s " +
+ ", returned map: %s", serviceInstance, customization);
+
+ // Apply name dependency filters possibly returned by the init() method.
+
+ for (MetaData dependency: m_depsMeta)
+ {
+ // Check if this dependency has a name, and if we find the name from the
+ // customization map, then apply filters and required flag from the map into it.
+ // Also parse optional pid/propagate flags for named Configuration dependencies
+
+ String name = dependency.getString(Params.name, null);
+ if (name != null)
+ {
+ String filter = customization.get(name + ".filter");
+ String required = customization.get(name + ".required");
+ String pid = customization.get(name + ".pid");
+ String propagate = customization.get(name + ".propagate");
+
+ if (filter != null || required != null || pid != null || propagate != null)
+ {
+ dependency = (MetaData) dependency.clone();
+ if (filter != null)
+ {
+ dependency.setString(Params.filter, filter);
+ }
+ if (required != null)
+ {
+ dependency.setString(Params.required, required);
+ }
+ if (pid != null)
+ {
+ dependency.setString(Params.pid, pid);
+ }
+ if (propagate != null)
+ {
+ dependency.setString(Params.propagate, propagate);
+ }
+ }
+
+ DependencyBuilder depBuilder = new DependencyBuilder(dependency);
+ Log.instance().info("ServiceLifecycleHandler.init: adding dependency %s into service %s",
+ dependency, m_srvMeta);
+ Dependency d = depBuilder.build(m_bundle, dm);
+ instanceBoundDeps.add(d);
+ }
+ }
+
+ // Add all extra dependencies in one shot, in order to calculate state changes for all dependencies at a time.
+ if (instanceBoundDeps.size() > 0)
+ {
+ Log.instance().info("ServiceLifecycleHandler.init: adding extra/named dependencies %s",
+ instanceBoundDeps);
+ c.add(instanceBoundDeps.toArray(new Dependency[instanceBoundDeps.size()]));
+ }
+
+ // init method fully handled, and all possible named dependencies have been configured. Now, activate the
+ // hidden toggle, and then remove it from the component, because we don't need it anymore.
+ if (initToggle != null)
+ {
+ c.remove(initToggle);
+ }
+ }
+
+ /**
+ * Handles the Service's start lifecycle callback. We just invoke the service "start" service callback on
+ * the service instance, as well as on all eventual service composites.
+ * We take care to check if a start callback returns a Map, which is meant to contain
+ * some additional properties which must be appended to existing service properties.
+ * Such extra properties takes precedence over existing service properties.
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public void start(Component service)
+ throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+ {
+ // Check if some extra service properties are returned by start method.
+
+ DependencyManager dm = service.getDependencyManager();
+ Map<String, String> extraProperties = new HashMap<String, String>();
+ Object[] composites = service.getInstances();
+ for (Object composite: composites)
+ {
+ Object o = invokeMethod(composite, m_start, dm, service);
+ if (o != null && Map.class.isAssignableFrom(o.getClass()))
+ {
+ extraProperties.putAll((Map) o);
+ }
+ }
+
+ if (extraProperties.size() > 0)
+ {
+ // Store extra properties returned by start callbacks into existing service properties
+ Dictionary existingProperties = service.getServiceProperties();
+ if (existingProperties != null)
+ {
+ Hashtable props = new Hashtable();
+ Enumeration e = existingProperties.keys();
+ while (e.hasMoreElements())
+ {
+ Object key = e.nextElement();
+ props.put(key, existingProperties.get(key));
+ }
+ props.putAll(extraProperties);
+ service.setServiceProperties(props);
+ }
+ else
+ {
+ service.setServiceProperties(new Hashtable(extraProperties));
+ }
+ }
+ }
+
+ /**
+ * Handles the Service's stop lifecycle callback. We just invoke the service "stop" callback on
+ * the service instance, as well as on all eventual service composites.
+ */
+ public void stop(Component service)
+ throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+ {
+ callbackComposites(service, m_stop);
+ }
+
+ /**
+ * Handles the Service's destroy lifecycle callback. We just invoke the service "destroy" callback on
+ * the service instance, as well as on all eventual service composites.
+ */
+ public void destroy(Component service)
+ throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+ {
+ callbackComposites(service, m_destroy);
+ }
+
+ /**
+ * Invoke a callback on all Service compositions.
+ */
+ private void callbackComposites(Component service, String callback)
+ throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+ {
+ Object[] composites = service.getInstances();
+ for (Object composite: composites)
+ {
+ invokeMethod(composite, callback, service.getDependencyManager(), service);
+ }
+ }
+
+ /**
+ * Invoke a callback on an Object instance.
+ */
+ private Object invokeMethod(Object serviceInstance, String method, DependencyManager dm, Component c)
+ throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+ {
+ if (method != null)
+ {
+ try
+ {
+ return InvocationUtil.invokeCallbackMethod(serviceInstance, method,
+ new Class[][] { { Component.class }, {} },
+ new Object[][] { { c }, {} }
+ );
+ }
+
+ catch (NoSuchMethodException e)
+ {
+ // ignore this
+ }
+
+ // Other exception will be thrown up to the ServiceImpl.invokeCallbackMethod(), which is
+ // currently invoking our method. So, no need to log something here, since the invokeCallbackMethod
+ // method is already logging any thrown exception.
+ }
+ return null;
+ }
+
+ /**
+ * Sets a field of an object by reflexion.
+ */
+ private void setField(Object instance, String fieldName, Class<?> fieldClass, Object fieldValue)
+ {
+ Object serviceInstance = instance;
+ Class<?> serviceClazz = serviceInstance.getClass();
+ if (Proxy.isProxyClass(serviceClazz))
+ {
+ serviceInstance = Proxy.getInvocationHandler(serviceInstance);
+ serviceClazz = serviceInstance.getClass();
+ }
+ while (serviceClazz != null)
+ {
+ Field[] fields = serviceClazz.getDeclaredFields();
+ for (int j = 0; j < fields.length; j++)
+ {
+ Field field = fields[j];
+ Class<?> type = field.getType();
+ if (field.getName().equals(fieldName) && type.isAssignableFrom(fieldClass))
+ {
+ try
+ {
+ field.setAccessible(true);
+ // synchronized makes sure the field is actually written to immediately
+ synchronized (SYNC)
+ {
+ field.set(serviceInstance, fieldValue);
+ }
+ }
+ catch (Throwable e)
+ {
+ throw new RuntimeException("Could not set field " + field, e);
+ }
+ }
+ }
+ serviceClazz = serviceClazz.getSuperclass();
+ }
+ }
+
+ private static class ComponentStarter implements Runnable {
+ private final String m_componentName;
+ private final ToggleServiceDependency m_toggle;
+ private final AtomicBoolean m_startFlag;
+
+ public ComponentStarter(String name, ToggleServiceDependency toggle, AtomicBoolean startFlag)
+ {
+ m_componentName = name;
+ m_toggle = toggle;
+ m_startFlag = startFlag;
+ }
+
+ @SuppressWarnings("synthetic-access")
+ public void run()
+ {
+ if (m_startFlag.compareAndSet(false, true)) {
+ Log.instance().debug("Lifecycle controller is activating the component %s",
+ m_componentName);
+ m_toggle.activate(true);
+ }
+ }
+ }
+
+ private static class ComponentStopper implements Runnable {
+ private final Object m_componentName;
+ private final ToggleServiceDependency m_toggle;
+ private final AtomicBoolean m_startFlag;
+
+ public ComponentStopper(String componentName, ToggleServiceDependency toggle, AtomicBoolean startFlag)
+ {
+ m_componentName = componentName;
+ m_toggle = toggle;
+ m_startFlag = startFlag;
+ }
+
+ @SuppressWarnings("synthetic-access")
+ public void run()
+ {
+ if (m_startFlag.compareAndSet(true, false)) {
+ Log.instance().debug("Lifecycle controller is deactivating the component %s",
+ m_componentName);
+ m_toggle.activate(false);
+ }
+ }
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ToggleServiceDependency.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ToggleServiceDependency.java
new file mode 100644
index 0000000..43adcd3
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/ToggleServiceDependency.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import org.apache.felix.dm.context.AbstractDependency;
+import org.apache.felix.dm.context.DependencyContext;
+import org.apache.felix.dm.context.Event;
+import org.apache.felix.dm.context.EventType;
+
+/**
+ * This is a custom DependencyManager Dependency, allowing to take control of
+ * when the dependency is available or not. It's used in the context of the
+ * LifecycleController class, in order to activate/deactivate a Component on
+ * demand.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ToggleServiceDependency extends AbstractDependency<ToggleServiceDependency> {
+ public ToggleServiceDependency() {
+ super.setRequired(true);
+ }
+
+ public ToggleServiceDependency(ToggleServiceDependency prototype) {
+ super(prototype);
+ }
+
+ @Override
+ public DependencyContext createCopy() {
+ return new ToggleServiceDependency(this);
+ }
+
+ public void activate(boolean active) {
+ m_component.handleEvent(this, active ? EventType.ADDED : EventType.REMOVED, new Event(active));
+ }
+
+ @Override
+ public String getSimpleName() {
+ return "" + isAvailable();
+ }
+
+ @Override
+ public String getType() {
+ return "toggle";
+ }
+
+ @Override
+ public Class<?> getAutoConfigType() {
+ return null; // we don't support auto config mode.
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/ComponentException.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/ComponentException.java
new file mode 100644
index 0000000..1a05f6f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/ComponentException.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime.api;
+
+/**
+ * Exception thrown when a Component can't be instantiated using a {@link ComponentFactory#newInstance(java.util.Dictionary)}
+ * service.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings("serial")
+public class ComponentException extends RuntimeException {
+ public ComponentException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/ComponentFactory.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/ComponentFactory.java
new file mode 100644
index 0000000..26e42d2
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/ComponentFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime.api;
+
+import java.util.Dictionary;
+
+/**
+ * When a Component is annotated with a DM "Component" annotation with a "factoryName" attribute, a corresponding
+ * ComponentFactory is registered in the OSGi service registry with a @link {@link ComponentFactory#FACTORY_NAME}
+ * servie property with the Component "factoryName" value.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ComponentFactory {
+ /**
+ * Instantiates a Component instance. Any properties starts with a "." are considered as private. Other properties will be
+ * published as the component instance service properties (if the component provides a services).
+ * @param conf the properties passed to the component "configure" method which is specified with the "configure" attribute
+ * of the @Component annotation.
+ * @return the component instance.
+ */
+ ComponentInstance newInstance(Dictionary<String, ?> conf);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/ComponentInstance.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/ComponentInstance.java
new file mode 100644
index 0000000..49ca071
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/ComponentInstance.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime.api;
+
+import java.util.Dictionary;
+
+/**
+ * A Component instance created using a {@link ComponentFactory} service
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ComponentInstance {
+ /**
+ * Destroy the component instance.
+ */
+ void dispose();
+
+ /**
+ * Updates the component instance.
+ * @param conf the properties used to update the component.
+ */
+ void update(Dictionary<String, ?> conf);
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/packageinfo b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/packageinfo
new file mode 100644
index 0000000..a4f1546
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/api/packageinfo
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/test/.gitignore b/dependencymanager/org.apache.felix.dependencymanager.runtime/test/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/test/.gitignore