Preparing move.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@882947 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager.temp/LICENSE b/dependencymanager.temp/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/dependencymanager.temp/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/dependencymanager.temp/NOTICE b/dependencymanager.temp/NOTICE
new file mode 100644
index 0000000..cfc3633
--- /dev/null
+++ b/dependencymanager.temp/NOTICE
@@ -0,0 +1,26 @@
+Apache Felix Dependency Manager
+Copyright 2009 The Apache Software Foundation
+
+
+I. Included Software
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2007).
+Licensed under the Apache License 2.0.
+
+
+II. Used Software
+
+This product uses software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2007).
+Licensed under the Apache License 2.0.
+
+
+III. License Summary
+- Apache License 2.0
diff --git a/dependencymanager.temp/pom.xml b/dependencymanager.temp/pom.xml
new file mode 100644
index 0000000..9012544
--- /dev/null
+++ b/dependencymanager.temp/pom.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+ <!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to you under the Apache License, Version
+ 2.0 (the "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0 Unless required by
+ applicable law or agreed to in writing, software distributed under
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
+ OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>felix</artifactId>
+ <version>1.0.4</version>
+ <relativePath>../pom/pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <packaging>bundle</packaging>
+ <name>Apache Felix Dependency Manager</name>
+ <version>3.0.0-SNAPSHOT</version>
+ <artifactId>org.apache.felix.dependencymanager</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>1.2.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <version>1.2.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam</artifactId>
+ <version>1.2.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-container-default</artifactId>
+ <version>1.2.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-junit</artifactId>
+ <version>1.2.0</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.7</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>1.4.0</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>org.apache.felix.dependencymanager</Bundle-SymbolicName>
+ <Bundle-Name>Apache Felix Dependency Manager</Bundle-Name>
+ <Bundle-Description>A bundle that provides a run-time
+ service dependency manager.</Bundle-Description>
+ <Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
+ <Export-Package>org.apache.felix.dependencymanager</Export-Package>
+ <Import-Package>!org.apache.felix.dependencymanager,*</Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>default-testCompile</id>
+ <configuration>
+ <source>1.5</source>
+ <target>1.5</target>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>maven-paxexam-plugin</artifactId>
+ <version>1.2.0</version>
+ <executions>
+ <execution>
+ <id>generate-config</id>
+ <goals>
+ <goal>generate-depends-file</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <scm>
+ <connection>scm:svn:https://svn.apache.org/repos/asf/felix/releases/org.apache.felix.dependencymanager-3.0.0</connection>
+ <developerConnection>scm:svn:https://svn.apache.org/repos/asf/felix/releases/org.apache.felix.dependencymanager-3.0.0</developerConnection>
+ <url>scm:svn:https://svn.apache.org/repos/asf/felix/releases/org.apache.felix.dependencymanager-3.0.0</url>
+ </scm>
+</project>
diff --git a/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ConfigurationDependency.java b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ConfigurationDependency.java
new file mode 100644
index 0000000..f7ac01b
--- /dev/null
+++ b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ConfigurationDependency.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Dictionary;
+import java.util.Properties;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+
+/**
+ * Configuration dependency that can track the availability of a (valid) configuration.
+ * To use it, specify a PID for the configuration. The dependency is always required,
+ * because if it is not, it does not make sense to use the dependency manager. In that
+ * scenario, simply register your service as a <code>ManagedService(Factory)</code> and
+ * handle everything yourself. Also, only managed services are supported, not factories.
+ * There are a couple of things you need to be aware of when implementing the
+ * <code>updated(Dictionary)</code> method:
+ * <ul>
+ * <li>Make sure it throws a <code>ConfigurationException</code> when you get a
+ * configuration that is invalid. In this case, the dependency will not change:
+ * if it was not available, it will still not be. If it was available, it will
+ * remain available and implicitly assume you keep working with your old
+ * configuration.</li>
+ * <li>This method will be called before all required dependencies are available.
+ * Make sure you do not depend on these to parse your settings.</li>
+ * </ul>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ConfigurationDependency implements Dependency, ManagedService, ServiceComponentDependency {
+ private BundleContext m_context;
+ private String m_pid;
+ private ServiceRegistration m_registration;
+ private volatile Service m_service;
+ private Dictionary m_settings;
+ private boolean m_propagate;
+ private final Logger m_logger;
+ private String m_callback;
+
+ public ConfigurationDependency(BundleContext context, Logger logger) {
+ m_context = context;
+ m_logger = logger;
+ }
+
+ public synchronized boolean isAvailable() {
+ return m_settings != null;
+ }
+
+ /**
+ * Will always return <code>true</code> as optional configuration dependencies
+ * do not make sense. You might as well just implement <code>ManagedService</code>
+ * yourself in those cases.
+ */
+ public boolean isRequired() {
+ return true;
+ }
+
+ /**
+ * Returns <code>true</code> when configuration properties should be propagated
+ * as service properties.
+ */
+ public boolean isPropagated() {
+ return m_propagate;
+ }
+
+ public Dictionary getConfiguration() {
+ return m_settings;
+ }
+
+ public void start(Service service) {
+ m_service = service;
+ Properties props = new Properties();
+ props.put(Constants.SERVICE_PID, m_pid);
+ m_registration = m_context.registerService(ManagedService.class.getName(), this, props);
+ }
+
+ public void stop(Service service) {
+ m_registration.unregister();
+ m_service = null;
+ }
+
+ public Dependency setCallback(String callback) {
+ m_callback = callback;
+ return this;
+ }
+
+ public void updated(Dictionary settings) throws ConfigurationException {
+ // if non-null settings come in, we have to instantiate the service and
+ // apply these settings
+ ((ServiceImpl) m_service).initService();
+ Object service = m_service.getService();
+
+ Dictionary oldSettings = null;
+ synchronized (this) {
+ oldSettings = m_settings;
+ }
+
+ if (oldSettings == null && settings == null) {
+ // CM has started but our configuration is not still present in the CM database: ignore
+ return;
+ }
+
+ if (service != null) {
+ String callback = (m_callback == null) ? "updated" : m_callback;
+ Method m;
+ try {
+ m = service.getClass().getDeclaredMethod(callback, new Class[] { Dictionary.class });
+ m.setAccessible(true);
+ // if exception is thrown here, what does that mean for the
+ // state of this dependency? how smart do we want to be??
+ // it's okay like this, if the new settings contain errors, we
+ // remain in the state we were, assuming that any error causes
+ // the "old" configuration to stay in effect.
+ // CM will log any thrown exceptions.
+ m.invoke(service, new Object[] { settings });
+ }
+ catch (InvocationTargetException e) {
+ // The component has thrown an exception during it's callback invocation.
+ if (e.getTargetException() instanceof ConfigurationException) {
+ // the callback threw an OSGi ConfigurationException: just re-throw it.
+ throw (ConfigurationException) e.getTargetException();
+ }
+ else {
+ // wrap the callback exception into a ConfigurationException.
+ throw new ConfigurationException(null, "Service " + m_service + " with " + this.toString() + " could not be updated", e.getTargetException());
+ }
+ }
+ catch (Throwable t) {
+ // wrap any other exception as a ConfigurationException.
+ throw new ConfigurationException(null, "Service " + m_service + " with " + this.toString() + " could not be updated", t);
+ }
+ }
+ else {
+ m_logger.log(Logger.LOG_ERROR, "Service " + m_service + " with configuration dependency " + this + " could not be instantiated.");
+ return;
+ }
+
+ // If these settings did not cause a configuration exception, we determine if they have
+ // caused the dependency state to change
+ synchronized (this) {
+ m_settings = settings;
+ }
+
+ if ((oldSettings == null) && (settings != null)) {
+ m_service.dependencyAvailable(this);
+ }
+ if ((oldSettings != null) && (settings == null)) {
+ m_service.dependencyUnavailable(this);
+ }
+ if ((oldSettings != null) && (settings != null)) {
+ m_service.dependencyChanged(this);
+ }
+ }
+
+ /**
+ * Sets the <code>service.pid</code> of the configuration you
+ * are depending on.
+ */
+ public ConfigurationDependency setPid(String pid) {
+ ensureNotActive();
+ m_pid = pid;
+ return this;
+ }
+
+ /**
+ * Sets propagation of the configuration properties to the service
+ * properties. Any additional service properties specified directly
+ * are merged with these.
+ */
+ public ConfigurationDependency setPropagate(boolean propagate) {
+ ensureNotActive();
+ m_propagate = propagate;
+ return this;
+ }
+
+ private void ensureNotActive() {
+ if (m_service != null) {
+ throw new IllegalStateException("Cannot modify state while active.");
+ }
+ }
+
+ public String toString() {
+ return "ConfigurationDependency[" + m_pid + "]";
+ }
+
+ public String getName() {
+ return m_pid;
+ }
+
+ public int getState() {
+ return (isAvailable() ? 1 : 0) + (isRequired() ? 2 : 0);
+ }
+
+ public String getType() {
+ return "configuration";
+ }
+}
diff --git a/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/DefaultNullObject.java b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/DefaultNullObject.java
new file mode 100644
index 0000000..79bb240
--- /dev/null
+++ b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/DefaultNullObject.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+
+/**
+ * Default null object implementation. Uses a dynamic proxy. Null objects are used
+ * as placeholders for services that are not available.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public final class DefaultNullObject implements InvocationHandler {
+ private static final Boolean DEFAULT_BOOLEAN = Boolean.FALSE;
+ private static final Byte DEFAULT_BYTE = new Byte((byte) 0);
+ private static final Short DEFAULT_SHORT = new Short((short) 0);
+ private static final Integer DEFAULT_INT = new Integer(0);
+ private static final Long DEFAULT_LONG = new Long(0);
+ private static final Float DEFAULT_FLOAT = new Float(0.0f);
+ private static final Double DEFAULT_DOUBLE = new Double(0.0);
+
+ /**
+ * Invokes a method on this null object. The method will return a default
+ * value without doing anything.
+ */
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ Class returnType = method.getReturnType();
+ if (returnType.equals(Boolean.class) || returnType.equals(Boolean.TYPE)) {
+ return DEFAULT_BOOLEAN;
+ }
+ else if (returnType.equals(Byte.class) || returnType.equals(Byte.TYPE)) {
+ return DEFAULT_BYTE;
+ }
+ else if (returnType.equals(Short.class) || returnType.equals(Short.TYPE)) {
+ return DEFAULT_SHORT;
+ }
+ else if (returnType.equals(Integer.class) || returnType.equals(Integer.TYPE)) {
+ return DEFAULT_INT;
+ }
+ else if (returnType.equals(Long.class) || returnType.equals(Long.TYPE)) {
+ return DEFAULT_LONG;
+ }
+ else if (returnType.equals(Float.class) || returnType.equals(Float.TYPE)) {
+ return DEFAULT_FLOAT;
+ }
+ else if (returnType.equals(Double.class) || returnType.equals(Double.TYPE)) {
+ return DEFAULT_DOUBLE;
+ }
+ else {
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/Dependency.java b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/Dependency.java
new file mode 100644
index 0000000..64d241a
--- /dev/null
+++ b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/Dependency.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.dependencymanager;
+
+/**
+ * Generic dependency for a service. A dependency can be required or not.
+ * A dependency will be activated by the service it belongs to. The service
+ * will call the <code>start(Service service)</code> and
+ * <code>stop(Service service)</code> methods.
+ *
+ * After it has been started, a dependency must callback
+ * the associated service's <code>dependencyAvailable()</code> and
+ * <code>dependencyUnavailable()</code>
+ * methods. State changes of the dependency itself may only be made as long as
+ * the dependency is not 'active', meaning it is associated with a running service.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface Dependency {
+ /**
+ * Returns <code>true</code> if this a required dependency. Required dependencies
+ * are dependencies that must be available before the service can be activated.
+ *
+ * @return <code>true</code> if the dependency is required
+ */
+ public boolean isRequired();
+
+ /**
+ * Returns <code>true</code> if the dependency is available.
+ *
+ * @return <code>true</code> if the dependency is available
+ */
+ public boolean isAvailable();
+
+ /**
+ * Starts tracking the dependency. This activates some implementation
+ * specific mechanism to do the actual tracking. If the tracking discovers
+ * that the dependency becomes available, it should call
+ * <code>dependencyAvailable()</code> on the service.
+ *
+ * @param service the service that is associated with this dependency
+ */
+ public void start(Service service);
+
+ /**
+ * Stops tracking the dependency. This deactivates the tracking. If the
+ * dependency was available, the tracker should call
+ * <code>dependencyUnavaible()</code> before stopping itself to ensure
+ * that dependencies that aren't "active" are unavailable.
+ */
+ public void stop(Service service);
+}
diff --git a/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/DependencyActivatorBase.java b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/DependencyActivatorBase.java
new file mode 100644
index 0000000..2fa8b13
--- /dev/null
+++ b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/DependencyActivatorBase.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager;
+
+import java.util.List;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Base bundle activator class. Subclass this activator if you want to use dependency
+ * management in your bundle. There are two methods you should implement:
+ * <code>init()</code> and <code>destroy()</code>. Both methods take two arguments,
+ * the bundle context and the dependency manager. The dependency manager can be used
+ * to define all the dependencies.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class DependencyActivatorBase implements BundleActivator {
+ private BundleContext m_context;
+ private DependencyManager m_manager;
+ private Logger m_logger;
+
+ /**
+ * Initialize the dependency manager. Here you can add all services and their dependencies.
+ * If something goes wrong and you do not want your bundle to be started, you can throw an
+ * exception. This exception will be passed on to the <code>start()</code> method of the
+ * bundle activator, causing the bundle not to start.
+ *
+ * @param context the bundle context
+ * @param manager the dependency manager
+ * @throws Exception if the initialization fails
+ */
+ public abstract void init(BundleContext context, DependencyManager manager) throws Exception;
+
+ /**
+ * Destroy the dependency manager. Here you can remove all services and their dependencies.
+ * Actually, the base class will clean up your dependencies anyway, so most of the time you
+ * don't need to do anything here.
+ * If something goes wrong and you do not want your bundle to be stopped, you can throw an
+ * exception. This exception will be passed on to the <code>stop()</code> method of the
+ * bundle activator, causing the bundle not to stop.
+ *
+ * @param context the bundle context
+ * @param manager the dependency manager
+ * @throws Exception if the destruction fails
+ */
+ public abstract void destroy(BundleContext context, DependencyManager manager) throws Exception;
+
+ /**
+ * Start method of the bundle activator. Initializes the dependency manager
+ * and calls <code>init()</code>.
+ *
+ * @param context the bundle context
+ */
+ public void start(BundleContext context) throws Exception {
+ m_context = context;
+ m_logger = new Logger(context);
+ m_manager = new DependencyManager(context, m_logger);
+ init(m_context, m_manager);
+ }
+
+ /**
+ * Stop method of the bundle activator. Calls the <code>destroy()</code> method
+ * and cleans up all left over dependencies.
+ *
+ * @param context the bundle context
+ */
+ public void stop(BundleContext context) throws Exception {
+ destroy(m_context, m_manager);
+ cleanup(m_manager);
+ m_manager = null;
+ m_context = null;
+ }
+
+ /**
+ * Creates a new service.
+ *
+ * @return the new service
+ */
+ public Service createService() {
+ return new ServiceImpl(m_context, m_manager, m_logger);
+ }
+
+ /**
+ * Creates a new service dependency.
+ *
+ * @return the service dependency
+ */
+ public ServiceDependency createServiceDependency() {
+ return new ServiceDependency(m_context, m_logger);
+ }
+
+ /**
+ * Creates a new configuration dependency.
+ *
+ * @return the configuration dependency
+ */
+ public ConfigurationDependency createConfigurationDependency() {
+ return new ConfigurationDependency(m_context, m_logger);
+ }
+
+ /**
+ * Cleans up all services and their dependencies.
+ *
+ * @param manager the dependency manager
+ */
+ private void cleanup(DependencyManager manager) {
+ List services = manager.getServices();
+ for (int i = services.size() - 1; i >= 0; i--) {
+ Service service = (Service) services.get(i);
+ manager.remove(service);
+ // remove any state listeners that are still registered
+ if (service instanceof ServiceImpl) {
+ ServiceImpl si = (ServiceImpl) service;
+ si.removeStateListeners();
+ }
+ }
+ }
+}
diff --git a/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/DependencyManager.java b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/DependencyManager.java
new file mode 100644
index 0000000..5c99c7b
--- /dev/null
+++ b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/DependencyManager.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.osgi.framework.BundleContext;
+
+/**
+ * The dependency manager. Manages all services and their dependencies.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DependencyManager {
+ private final BundleContext m_context;
+ private final Logger m_logger;
+ private List m_services = Collections.synchronizedList(new ArrayList());
+
+ /**
+ * Creates a new dependency manager.
+ *
+ * @param context the bundle context
+ * @param logger
+ */
+ public DependencyManager(BundleContext context, Logger logger) {
+ m_context = context;
+ m_logger = logger;
+ }
+
+ /**
+ * Adds a new service to the dependency manager. After the service was added
+ * it will be started immediately.
+ *
+ * @param service the service to add
+ */
+ public void add(Service service) {
+ m_services.add(service);
+ service.start();
+ }
+
+ /**
+ * Removes a service from the dependency manager. Before the service is removed
+ * it is stopped first.
+ *
+ * @param service the service to remove
+ */
+ public void remove(Service service) {
+ service.stop();
+ m_services.remove(service);
+ }
+
+ /**
+ * Creates a new service.
+ *
+ * @return the new service
+ */
+ public Service createService() {
+ return new ServiceImpl(m_context, this, m_logger);
+ }
+
+ /**
+ * Creates a new service dependency.
+ *
+ * @return the service dependency
+ */
+ public ServiceDependency createServiceDependency() {
+ return new ServiceDependency(m_context, m_logger);
+ }
+
+ public ConfigurationDependency createConfigurationDependency() {
+ return new ConfigurationDependency(m_context, m_logger);
+ }
+
+ /**
+ * Returns a list of services.
+ *
+ * @return a list of services
+ */
+ public List getServices() {
+ return Collections.unmodifiableList(m_services);
+ }
+
+}
diff --git a/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/Logger.java b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/Logger.java
new file mode 100644
index 0000000..ab85366
--- /dev/null
+++ b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/Logger.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.dependencymanager;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * This class mimics the standard OSGi <tt>LogService</tt> interface. An
+ * instance of this class is used by the dependency manager for all logging.
+ * By default this class logs messages to standard out. The log level can be set to
+ * control the amount of logging performed, where a higher number results in
+ * more logging. A log level of zero turns off logging completely.
+ *
+ * The log levels match those specified in the OSGi Log Service.
+ * This class also tracks log services and will use the highest ranking
+ * log service, if present, as a back end instead of printing to standard
+ * out. The class uses reflection to invoking the log service's method to
+ * avoid a dependency on the log interface. This class is in many ways similar
+ * to the one used in the system bundle for that same purpose.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Logger implements ServiceListener {
+ public static final int LOG_ERROR = 1;
+ public static final int LOG_WARNING = 2;
+ public static final int LOG_INFO = 3;
+ public static final int LOG_DEBUG = 4;
+
+ private final BundleContext m_context;
+
+ private final static int LOGGER_OBJECT_IDX = 0;
+ private final static int LOGGER_METHOD_IDX = 1;
+ private ServiceReference m_logRef = null;
+ private Object[] m_logger = null;
+
+ public Logger(BundleContext context) {
+ m_context = context;
+ startListeningForLogService();
+ }
+
+ public final void log(int level, String msg) {
+ _log(null, level, msg, null);
+ }
+
+ public final void log(int level, String msg, Throwable throwable) {
+ _log(null, level, msg, throwable);
+ }
+
+ public final void log(ServiceReference sr, int level, String msg) {
+ _log(sr, level, msg, null);
+ }
+
+ public final void log(ServiceReference sr, int level, String msg, Throwable throwable) {
+ _log(sr, level, msg, throwable);
+ }
+
+ protected void doLog(ServiceReference sr, int level, String msg, Throwable throwable) {
+ String s = (sr == null) ? null : "SvcRef " + sr;
+ s = (s == null) ? msg : s + " " + msg;
+ s = (throwable == null) ? s : s + " (" + throwable + ")";
+ switch (level) {
+ case LOG_DEBUG:
+ System.out.println("DEBUG: " + s);
+ break;
+ case LOG_ERROR:
+ System.out.println("ERROR: " + s);
+ if (throwable != null) {
+ if ((throwable instanceof BundleException) && (((BundleException) throwable).getNestedException() != null)) {
+ throwable = ((BundleException) throwable).getNestedException();
+ }
+ throwable.printStackTrace();
+ }
+ break;
+ case LOG_INFO:
+ System.out.println("INFO: " + s);
+ break;
+ case LOG_WARNING:
+ System.out.println("WARNING: " + s);
+ break;
+ default:
+ System.out.println("UNKNOWN[" + level + "]: " + s);
+ }
+ }
+
+ private void _log(ServiceReference sr, int level, String msg, Throwable throwable) {
+ // Save our own copy just in case it changes. We could try to do
+ // more conservative locking here, but let's be optimistic.
+ Object[] logger = m_logger;
+ // Use the log service if available.
+ if (logger != null) {
+ _logReflectively(logger, sr, level, msg, throwable);
+ }
+ // Otherwise, default logging action.
+ else {
+ doLog(sr, level, msg, throwable);
+ }
+ }
+
+ private void _logReflectively(Object[] logger, ServiceReference sr, int level, String msg, Throwable throwable) {
+ if (logger != null) {
+ Object[] params = { sr, new Integer(level), msg, throwable };
+ try {
+ ((Method) logger[LOGGER_METHOD_IDX]).invoke(logger[LOGGER_OBJECT_IDX], params);
+ }
+ catch (InvocationTargetException ex) {
+ System.err.println("Logger: " + ex);
+ }
+ catch (IllegalAccessException ex) {
+ System.err.println("Logger: " + ex);
+ }
+ }
+ }
+
+ /**
+ * This method is called when the bundle context is set;
+ * it simply adds a service listener so that the bundle can track
+ * log services to be used as the back end of the logging mechanism. It also
+ * attempts to get an existing log service, if present, but in general
+ * there will never be a log service present since the system bundle is
+ * started before every other bundle.
+ */
+ private synchronized void startListeningForLogService() {
+ // Add a service listener for log services.
+ try {
+ m_context.addServiceListener(this, "(objectClass=org.osgi.service.log.LogService)");
+ }
+ catch (InvalidSyntaxException ex) {
+ // This will never happen since the filter is hard coded.
+ }
+ // Try to get an existing log service.
+ m_logRef = m_context.getServiceReference("org.osgi.service.log.LogService");
+ // Get the service object if available and set it in the logger.
+ if (m_logRef != null) {
+ setLogger(m_context.getService(m_logRef));
+ }
+ }
+
+ /**
+ * This method implements the callback for the ServiceListener interface.
+ * It is public as a byproduct of implementing the interface and should
+ * not be called directly. This method tracks run-time changes to log
+ * service availability. If the log service being used by the framework's
+ * logging mechanism goes away, then this will try to find an alternative.
+ * If a higher ranking log service is registered, then this will switch
+ * to the higher ranking log service.
+ */
+ public final synchronized void serviceChanged(ServiceEvent event) {
+ // If no logger is in use, then grab this one.
+ if ((event.getType() == ServiceEvent.REGISTERED) && (m_logRef == null)) {
+ m_logRef = event.getServiceReference();
+ // Get the service object and set it in the logger.
+ setLogger(m_context.getService(m_logRef));
+ }
+ // If a logger is in use, but this one has a higher ranking, then swap
+ // it for the existing logger.
+ else if ((event.getType() == ServiceEvent.REGISTERED) && (m_logRef != null)) {
+ ServiceReference ref = m_context.getServiceReference("org.osgi.service.log.LogService");
+ if (!ref.equals(m_logRef)) {
+ m_context.ungetService(m_logRef);
+ m_logRef = ref;
+ setLogger(m_context.getService(m_logRef));
+ }
+ }
+ // If the current logger is going away, release it and try to
+ // find another one.
+ else if ((event.getType() == ServiceEvent.UNREGISTERING) && m_logRef.equals(event.getServiceReference())) {
+ // Unget the service object.
+ m_context.ungetService(m_logRef);
+ // Try to get an existing log service.
+ m_logRef = m_context.getServiceReference("org.osgi.service.log.LogService");
+ // Get the service object if available and set it in the logger.
+ if (m_logRef != null) {
+ setLogger(m_context.getService(m_logRef));
+ }
+ else {
+ setLogger(null);
+ }
+ }
+ }
+
+ /**
+ * This method sets the new log service object. It also caches the method to
+ * invoke. The service object and method are stored in array to optimistically
+ * eliminate the need to locking when logging.
+ */
+ private void setLogger(Object logObj) {
+ if (logObj == null) {
+ m_logger = null;
+ }
+ else {
+ Class[] formalParams = { ServiceReference.class, Integer.TYPE, String.class, Throwable.class };
+ try {
+ Method logMethod = logObj.getClass().getMethod("log", formalParams);
+ logMethod.setAccessible(true);
+ m_logger = new Object[] { logObj, logMethod };
+ }
+ catch (NoSuchMethodException ex) {
+ System.err.println("Logger: " + ex);
+ m_logger = null;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/SerialExecutor.java b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/SerialExecutor.java
new file mode 100644
index 0000000..72063c3
--- /dev/null
+++ b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/SerialExecutor.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager;
+
+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 final LinkedList 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;
+ }
+ 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.temp/src/main/java/org/apache/felix/dependencymanager/Service.java b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/Service.java
new file mode 100644
index 0000000..dddc800
--- /dev/null
+++ b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/Service.java
@@ -0,0 +1,250 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Service interface.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface Service {
+ /**
+ * Adds a new dependency to this service.
+ *
+ * @param dependency the dependency to add
+ * @return this service
+ */
+ public Service add(Dependency dependency);
+
+ /**
+ * Removes a dependency from this service.
+ *
+ * @param dependency the dependency to remove
+ * @return this service
+ */
+ public Service remove(Dependency dependency);
+
+ /**
+ * Sets the public interface under which this service should be registered
+ * in the OSGi service registry.
+ *
+ * @param serviceName the name of the service interface
+ * @param properties the properties for this service
+ * @return this service
+ */
+ public Service setInterface(String serviceName, Dictionary properties);
+
+ /**
+ * Sets the public interfaces under which this service should be registered
+ * in the OSGi service registry.
+ *
+ * @param serviceNames the names of the service interface
+ * @param properties the properties for this service
+ * @return this service
+ */
+ public Service setInterface(String[] serviceNames, Dictionary properties);
+
+ /**
+ * Sets the implementation for this service. You can actually specify
+ * an instance you have instantiated manually, or a <code>Class</code>
+ * that will be instantiated using its default constructor when the
+ * required dependencies are resolved (effectively giving you a lazy
+ * instantiation mechanism).
+ *
+ * There are four special methods that are called when found through
+ * reflection to give you some life-cycle management options:
+ * <ol>
+ * <li><code>init()</code> is invoked right after the instance has been
+ * created, and before any dependencies are resolved, and can be used to
+ * initialize the internal state of the instance</li>
+ * <li><code>start()</code> is invoked after the required dependencies
+ * are resolved and injected, and before the service is registered</li>
+ * <li><code>stop()</code> is invoked right after the service is
+ * unregistered</li>
+ * <li><code>destroy()</code> is invoked after all dependencies are
+ * removed</li>
+ * </ol>
+ * In short, this allows you to initialize your instance before it is
+ * registered, perform some post-initialization and pre-destruction code
+ * as well as final cleanup. If a method is not defined, it simply is not
+ * called, so you can decide which one(s) you need. If you need even more
+ * fine-grained control, you can register as a service state listener too.
+ *
+ * @param implementation the implementation
+ * @return this service
+ * @see ServiceStateListener
+ */
+ public Service setImplementation(Object implementation);
+
+ /**
+ * Returns a list of dependencies.
+ *
+ * @return a list of dependencies
+ */
+ public List getDependencies();
+
+ /**
+ * Returns the service registration for this service. The method
+ * will return <code>null</code> if no service registration is
+ * available.
+ *
+ * @return the service registration
+ */
+ public ServiceRegistration getServiceRegistration();
+
+ /**
+ * Returns the service instance for this service. The method will
+ * return <code>null</code> if no service instance is available.
+ *
+ * @return the service instance
+ */
+ public Object getService();
+
+ /**
+ * Returns the service properties associated with the service.
+ *
+ * @return the properties or <code>null</code> if there are none
+ */
+ public Dictionary getServiceProperties();
+
+ /**
+ * Sets the service properties associated with the service. If the service
+ * was already registered, it will be updated.
+ *
+ * @param serviceProperties the properties
+ */
+ public void setServiceProperties(Dictionary serviceProperties);
+
+ /**
+ * Sets the names of the methods used as callbacks. These methods, when found, are
+ * invoked as part of the life-cycle management of the service implementation. The
+ * methods should not have any parameters.
+ *
+ * @param init the name of the init method
+ * @param start the name of the start method
+ * @param stop the name of the stop method
+ * @param destroy the name of the destroy method
+ * @return the service instance
+ */
+ public Service setCallbacks(String init, String start, String stop, String destroy);
+
+ // listener
+ /**
+ * Adds a service state listener to this service.
+ *
+ * @param listener the state listener
+ */
+ public void addStateListener(ServiceStateListener listener);
+
+ /**
+ * Removes a service state listener from this service.
+ *
+ * @param listener the state listener
+ */
+ public void removeStateListener(ServiceStateListener listener);
+
+ // events, must be fired when the dependency is started/active
+
+ /**
+ * Will be called when the dependency becomes available.
+ *
+ * @param dependency the dependency
+ */
+ public void dependencyAvailable(Dependency dependency);
+
+ /**
+ * Will be called when the dependency changes.
+ *
+ * @param dependency the dependency
+ */
+ public void dependencyUnavailable(Dependency dependency);
+
+ /**
+ * Will be called when the dependency becomes unavailable.
+ *
+ * @param dependency the dependency
+ */
+ public void dependencyChanged(Dependency dependency);
+
+ /**
+ * Starts the service. This activates the dependency tracking mechanism
+ * for this service.
+ */
+ public void start();
+
+ /**
+ * Stops the service. This deactivates the dependency tracking mechanism
+ * for this service.
+ */
+ public void stop();
+
+ /**
+ * Sets the factory to use to create the implementation. You can specify
+ * both the factory class and method to invoke. The method should return
+ * the implementation, and can use any method to create it. Actually, this
+ * can be used together with <code>setComposition</code> to create a
+ * composition of instances that work together to implement a service. The
+ * factory itself can also be instantiated lazily by not specifying an
+ * instance, but a <code>Class</code>.
+ *
+ * @param factory the factory instance or class
+ * @param createMethod the name of the create method
+ */
+ public Service setFactory(Object factory, String createMethod);
+
+ /**
+ * Sets the factory to use to create the implementation. You specify the
+ * method to invoke. The method should return the implementation, and can
+ * use any method to create it. Actually, this can be used together with
+ * <code>setComposition</code> to create a composition of instances that
+ * work together to implement a service.
+ * <p>
+ * Note that currently, there is no default for the factory, so please use
+ * <code>setFactory(factory, createMethod)</code> instead.
+ *
+ * @param createMethod the name of the create method
+ */
+ public Service setFactory(String createMethod);
+
+ /**
+ * Sets the instance and method to invoke to get back all instances that
+ * are part of a composition and need dependencies injected. All of them
+ * will be searched for any of the dependencies. The method that is
+ * invoked must return an <code>Object[]</code>.
+ *
+ * @param instance the instance that has the method
+ * @param getMethod the method to invoke
+ */
+ public Service setComposition(Object instance, String getMethod);
+
+ /**
+ * Sets the method to invoke on the service implementation to get back all
+ * instances that are part of a composition and need dependencies injected.
+ * All of them will be searched for any of the dependencies. The method that
+ * is invoked must return an <code>Object[]</code>.
+ *
+ * @param getMethod the method to invoke
+ */
+ public Service setComposition(String getMethod);
+}
diff --git a/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceComponent.java b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceComponent.java
new file mode 100644
index 0000000..2d70ea7
--- /dev/null
+++ b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceComponent.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager;
+
+/**
+ * Describes a service component. Service components form descriptions of services
+ * that are managed by the dependency manager. They can be used to query their state
+ * for monitoring tools. The dependency manager shell command is an example of
+ * such a tool.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ServiceComponent {
+ /** Names for the states of this component. */
+ public static final String[] STATE_NAMES = { "unregistered", "registered" };
+ /** State constant for an unregistered component. */
+ public static final int STATE_UNREGISTERED = 0;
+ /** State constant for a registered component. */
+ public static final int STATE_REGISTERED = 1;
+ /** Returns a list of dependencies associated with this service component. */
+ public ServiceComponentDependency[] getComponentDependencies();
+ /** Returns the name of this service component. */
+ public String getName();
+ /** Returns the state of this service component. */
+ public int getState();
+}
diff --git a/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceComponentDependency.java b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceComponentDependency.java
new file mode 100644
index 0000000..53c13ac
--- /dev/null
+++ b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceComponentDependency.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager;
+
+/**
+ * Describes a service component dependency. They form descriptions of dependencies
+ * that are managed by the dependency manager. They can be used to query their state
+ * for monitoring tools. The dependency manager shell command is an example of
+ * such a tool.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ServiceComponentDependency {
+ /** Names for the states of this dependency. */
+ public static final String[] STATE_NAMES = {
+ "optional unavailable",
+ "optional available",
+ "required unavailable",
+ "required available"
+ };
+ /** State constant for an unavailable, optional dependency. */
+ public static final int STATE_UNAVAILABLE_OPTIONAL = 0;
+ /** State constant for an available, optional dependency. */
+ public static final int STATE_AVAILABLE_OPTIONAL = 1;
+ /** State constant for an unavailable, required dependency. */
+ public static final int STATE_UNAVAILABLE_REQUIRED = 2;
+ /** State constant for an available, required dependency. */
+ public static final int STATE_AVAILABLE_REQUIRED = 3;
+ /** Returns the name of this dependency. */
+ public String getName();
+ /** Returns the name of the type of this dependency. */
+ public String getType();
+ /** Returns the state of this dependency. */
+ public int getState();
+}
diff --git a/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceDependency.java b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceDependency.java
new file mode 100644
index 0000000..195fa12
--- /dev/null
+++ b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceDependency.java
@@ -0,0 +1,720 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
+import java.util.AbstractMap;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Service dependency that can track an OSGi service.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceDependency implements Dependency, ServiceTrackerCustomizer, ServiceComponentDependency {
+ private boolean m_isRequired;
+ private Service m_service;
+ private volatile ServiceTracker m_tracker;
+ private BundleContext m_context;
+ private boolean m_isAvailable;
+ private volatile Class m_trackedServiceName;
+ private Object m_nullObject;
+ private volatile String m_trackedServiceFilter;
+ private volatile String m_trackedServiceFilterUnmodified;
+ private volatile ServiceReference m_trackedServiceReference;
+ private volatile boolean m_isStarted;
+ private Object m_callbackInstance;
+ private String m_callbackAdded;
+ private String m_callbackChanged;
+ private String m_callbackRemoved;
+ private boolean m_autoConfig;
+ private ServiceReference m_reference;
+ private Object m_serviceInstance;
+ private final Logger m_logger;
+ private String m_autoConfigInstance;
+ private boolean m_autoConfigInvoked;
+ private Object m_defaultImplementation;
+ private Object m_defaultImplementationInstance;
+
+ private static final Comparator COMPARATOR = new Comparator() {
+ public int getRank(ServiceReference ref) {
+ Object ranking = ref.getProperty(Constants.SERVICE_RANKING);
+ if (ranking != null && (ranking instanceof Integer)) {
+ return ((Integer) ranking).intValue();
+ }
+ return 0;
+ }
+
+ public int compare(Object a, Object b) {
+ ServiceReference ra = (ServiceReference) a, rb = (ServiceReference) b;
+ int ranka = getRank(ra);
+ int rankb = getRank(rb);
+ if (ranka < rankb) {
+ return -1;
+ }
+ else if (ranka > rankb) {
+ return 1;
+ }
+ return 0;
+ }
+ };
+
+ /**
+ * Entry to wrap service properties behind a Map.
+ */
+ private final static class ServicePropertiesMapEntry implements Map.Entry {
+ private final String m_key;
+ private Object m_value;
+
+ public ServicePropertiesMapEntry(String key, Object value) {
+ m_key = key;
+ m_value = value;
+ }
+
+ public Object getKey() {
+ return m_key;
+ }
+
+ public Object getValue() {
+ return m_value;
+ }
+
+ public String toString() {
+ return m_key + "=" + m_value;
+ }
+
+ public Object setValue(Object value) {
+ Object oldValue = m_value;
+ m_value = value;
+ return oldValue;
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof Map.Entry)) {
+ return false;
+ }
+ Map.Entry e = (Map.Entry) o;
+ return eq(m_key, e.getKey()) && eq(m_value, e.getValue());
+ }
+
+ public int hashCode() {
+ return ((m_key == null) ? 0 : m_key.hashCode()) ^ ((m_value == null) ? 0 : m_value.hashCode());
+ }
+
+ private static final boolean eq(Object o1, Object o2) {
+ return (o1 == null ? o2 == null : o1.equals(o2));
+ }
+ }
+
+ /**
+ * Wraps service properties behind a Map.
+ */
+ private final static class ServicePropertiesMap extends AbstractMap {
+ private final ServiceReference m_ref;
+
+ public ServicePropertiesMap(ServiceReference ref) {
+ m_ref = ref;
+ }
+
+ public Object get(Object key) {
+ return m_ref.getProperty(key.toString());
+ }
+
+ public int size() {
+ return m_ref.getPropertyKeys().length;
+ }
+
+ public Set entrySet() {
+ Set set = new HashSet();
+ String[] keys = m_ref.getPropertyKeys();
+ for (int i = 0; i < keys.length; i++) {
+ set.add(new ServicePropertiesMapEntry(keys[i], m_ref.getProperty(keys[i])));
+ }
+ return set;
+ }
+ }
+
+ /**
+ * Creates a new service dependency.
+ *
+ * @param context the bundle context
+ * @param logger the logger
+ */
+ public ServiceDependency(BundleContext context, Logger logger) {
+ m_context = context;
+ m_logger = logger;
+ m_autoConfig = true;
+ }
+
+ public synchronized boolean isRequired() {
+ return m_isRequired;
+ }
+
+ public synchronized boolean isAvailable() {
+ return m_isAvailable;
+ }
+
+ public synchronized boolean isAutoConfig() {
+ return m_autoConfig;
+ }
+
+ public synchronized Object getService() {
+ Object service = null;
+ if (m_isStarted) {
+ service = m_tracker.getService();
+ }
+ if (service == null) {
+ service = getDefaultImplementation();
+ if (service == null) {
+ service = getNullObject();
+ }
+ }
+ return service;
+ }
+
+ public Object lookupService() {
+ Object service = null;
+ if (m_isStarted) {
+ service = m_tracker.getService();
+ }
+ else {
+ ServiceReference[] refs = null;
+ ServiceReference ref = null;
+ if (m_trackedServiceName != null) {
+ if (m_trackedServiceFilter != null) {
+ try {
+ refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
+ if (refs != null) {
+ Arrays.sort(refs, COMPARATOR);
+ ref = refs[0];
+ }
+ }
+ catch (InvalidSyntaxException e) {
+ throw new IllegalStateException("Invalid filter definition for dependency.");
+ }
+ }
+ else if (m_trackedServiceReference != null) {
+ ref = m_trackedServiceReference;
+ }
+ else {
+ ref = m_context.getServiceReference(m_trackedServiceName.getName());
+ }
+ if (ref != null) {
+ service = m_context.getService(ref);
+ }
+ }
+ else {
+ throw new IllegalStateException("Could not lookup dependency, no service name specified.");
+ }
+ }
+ if (service == null) {
+ service = getDefaultImplementation();
+ if (service == null) {
+ service = getNullObject();
+ }
+ }
+ return service;
+ }
+
+ private Object getNullObject() {
+ if (m_nullObject == null) {
+ Class trackedServiceName;
+ synchronized (this) {
+ trackedServiceName = m_trackedServiceName;
+ }
+ try {
+ m_nullObject = Proxy.newProxyInstance(trackedServiceName.getClassLoader(), new Class[] {trackedServiceName}, new DefaultNullObject());
+ }
+ catch (Exception e) {
+ m_logger.log(Logger.LOG_ERROR, "Could not create null object for " + trackedServiceName + ".", e);
+ }
+ }
+ return m_nullObject;
+ }
+
+ private Object getDefaultImplementation() {
+ if (m_defaultImplementation != null) {
+ if (m_defaultImplementation instanceof Class) {
+ try {
+ m_defaultImplementationInstance = ((Class) m_defaultImplementation).newInstance();
+ }
+ catch (Exception e) {
+ m_logger.log(Logger.LOG_ERROR, "Could not create default implementation instance of class " + m_defaultImplementation + ".", e);
+ }
+ }
+ else {
+ m_defaultImplementationInstance = m_defaultImplementation;
+ }
+ }
+ return m_defaultImplementationInstance;
+ }
+
+ public synchronized Class getInterface() {
+ return m_trackedServiceName;
+ }
+
+ public void start(Service service) {
+ synchronized (this) {
+ if (m_isStarted) {
+ throw new IllegalStateException("Service dependency was already started." + m_trackedServiceName);
+ }
+ m_service = service;
+ if (m_trackedServiceName != null) {
+ if (m_trackedServiceFilter != null) {
+ try {
+ m_tracker = new ServiceTracker(m_context, m_context.createFilter(m_trackedServiceFilter), this);
+ }
+ catch (InvalidSyntaxException e) {
+ throw new IllegalStateException("Invalid filter definition for dependency.");
+ }
+ }
+ else if (m_trackedServiceReference != null) {
+ m_tracker = new ServiceTracker(m_context, m_trackedServiceReference, this);
+ }
+ else {
+ m_tracker = new ServiceTracker(m_context, m_trackedServiceName.getName(), this);
+ }
+ }
+ else {
+ throw new IllegalStateException("Could not create tracker for dependency, no service name specified.");
+ }
+ m_isStarted = true;
+ }
+ m_tracker.open();
+ }
+
+ public void stop(Service service) {
+ synchronized (this) {
+ if (!m_isStarted) {
+ throw new IllegalStateException("Service dependency was not started.");
+ }
+ m_isStarted = false;
+ }
+ m_tracker.close();
+ m_tracker = null;
+ }
+
+ public Object addingService(ServiceReference ref) {
+ Object service = m_context.getService(ref);
+ // first check to make sure the service is actually an instance of our service
+ if (!m_trackedServiceName.isInstance(service)) {
+ return null;
+ }
+
+ // we remember these for future reference, needed for required service callbacks
+ m_reference = ref;
+ m_serviceInstance = service;
+ return service;
+ }
+
+ public void addedService(ServiceReference ref, Object service) {
+ if (makeAvailable()) {
+ m_service.dependencyAvailable(this);
+ // try to invoke callback, if specified, but only for optional dependencies
+ // because callbacks for required dependencies are handled differently
+ if (!isRequired()) {
+ invokeAdded(ref, service);
+ }
+ }
+ else {
+ m_service.dependencyChanged(this);
+ invokeAdded(ref, service);
+ }
+ }
+
+ public void invokeAdded() {
+ invokeAdded(m_reference, m_serviceInstance);
+ }
+
+ public void invokeAdded(ServiceReference reference, Object serviceInstance) {
+ Object[] callbackInstances = getCallbackInstances();
+ if ((callbackInstances != null) && (m_callbackAdded != null)) {
+ invokeCallbackMethod(callbackInstances, m_callbackAdded, reference, serviceInstance);
+ }
+ }
+
+ public void modifiedService(ServiceReference ref, Object service) {
+ m_reference = ref;
+ m_serviceInstance = service;
+ m_service.dependencyChanged(this);
+ // only invoke the changed callback if the service itself is "active"
+ if (((ServiceImpl) m_service).isRegistered()) {
+ invokeChanged(ref, service);
+ }
+ }
+
+ public void invokeChanged(ServiceReference reference, Object serviceInstance) {
+ Object[] callbackInstances = getCallbackInstances();
+ if ((callbackInstances != null) && (m_callbackChanged != null)) {
+ invokeCallbackMethod(callbackInstances, m_callbackChanged, reference, serviceInstance);
+ }
+ }
+
+ public void removedService(ServiceReference ref, Object service) {
+ if (makeUnavailable()) {
+ m_service.dependencyUnavailable(this);
+ // try to invoke callback, if specified, but only for optional dependencies
+ // because callbacks for required dependencies are handled differently
+ if (!isRequired()) {
+ invokeRemoved(ref, service);
+ }
+ }
+ else {
+ m_service.dependencyChanged(this);
+ invokeRemoved(ref, service);
+ }
+
+ // unget what we got in addingService (see ServiceTracker 701.4.1)
+ m_context.ungetService(ref);
+ }
+
+ public void invokeRemoved() {
+ invokeRemoved(m_reference, m_serviceInstance);
+ }
+
+ public void invokeRemoved(ServiceReference reference, Object serviceInstance) {
+ Object[] callbackInstances = getCallbackInstances();
+ if ((callbackInstances != null) && (m_callbackRemoved != null)) {
+ invokeCallbackMethod(callbackInstances, m_callbackRemoved, reference, serviceInstance);
+ }
+ }
+
+ private synchronized boolean makeAvailable() {
+ if (!m_isAvailable) {
+ m_isAvailable = true;
+ return true;
+ }
+ return false;
+ }
+
+ private synchronized boolean makeUnavailable() {
+ if ((m_isAvailable) && (m_tracker.getServiceReference() == null)) {
+ m_isAvailable = false;
+ return true;
+ }
+ return false;
+ }
+
+ private synchronized Object[] getCallbackInstances() {
+ return m_callbackInstance != null ? new Object[] { m_callbackInstance } : ((ServiceImpl) m_service).getCompositionInstances();
+ }
+
+ private void invokeCallbackMethod(Object[] instances, String methodName, ServiceReference reference, Object service) {
+ for (int i = 0; i < instances.length; i++) {
+ try {
+ invokeCallbackMethod(instances[i], methodName, reference, service);
+ }
+ catch (NoSuchMethodException e) {
+ m_logger.log(Logger.LOG_DEBUG, "Method '" + methodName + "' does not exist on " + instances[i] + ". Callback skipped.");
+ }
+ }
+ }
+
+ private void invokeCallbackMethod(Object instance, String methodName, ServiceReference reference, Object service) throws NoSuchMethodException {
+ Class currentClazz = instance.getClass();
+ boolean done = false;
+ while (!done && currentClazz != null) {
+ Class trackedServiceName;
+ synchronized (this) {
+ trackedServiceName = m_trackedServiceName;
+ }
+ done = invokeMethod(instance, currentClazz, methodName,
+ new Class[][] {{ServiceReference.class, trackedServiceName}, {ServiceReference.class, Object.class}, {ServiceReference.class}, {trackedServiceName}, {Object.class}, {}, {Map.class, trackedServiceName}},
+ new Object[][] {{reference, service}, {reference, service}, {reference}, {service}, {service}, {}, {new ServicePropertiesMap(reference), service}},
+ false);
+ if (!done) {
+ currentClazz = currentClazz.getSuperclass();
+ }
+ }
+ if (!done && currentClazz == null) {
+ throw new NoSuchMethodException(methodName);
+ }
+ }
+
+ private boolean invokeMethod(Object object, Class clazz, String name, Class[][] signatures, Object[][] parameters, boolean isSuper) {
+ Method m = null;
+ for (int i = 0; i < signatures.length; i++) {
+ Class[] signature = signatures[i];
+ try {
+ m = clazz.getDeclaredMethod(name, signature);
+ if (!(isSuper && Modifier.isPrivate(m.getModifiers()))) {
+ m.setAccessible(true);
+ try {
+ m.invoke(object, parameters[i]);
+ }
+ catch (InvocationTargetException e) {
+ m_logger.log(Logger.LOG_ERROR, "Exception while invoking method " + m + ".", e);
+ }
+ // we did find and invoke the method, so we return true
+ return true;
+ }
+ }
+ catch (NoSuchMethodException e) {
+ // ignore this and keep looking
+ }
+ catch (Exception e) {
+ // could not even try to invoke the method
+ m_logger.log(Logger.LOG_ERROR, "Exception while trying to invoke method " + m + ".", e);
+ }
+ }
+ return false;
+ }
+
+ // ----- CREATION
+
+ /**
+ * Sets the name of the service that should be tracked.
+ *
+ * @param serviceName the name of the service
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setService(Class serviceName) {
+ ensureNotActive();
+ if (serviceName == null) {
+ throw new IllegalArgumentException("Service name cannot be null.");
+ }
+ m_trackedServiceName = serviceName;
+ m_trackedServiceReference = null;
+ m_trackedServiceFilter = null;
+ return this;
+ }
+
+ /**
+ * Sets the name of the service that should be tracked. You can either specify
+ * only the name, or the name and a filter. In the latter case, the filter is used
+ * to track the service and should only return services of the type that was specified
+ * in the name. To make sure of this, the filter is actually extended internally to
+ * filter on the correct name.
+ *
+ * @param serviceName the name of the service
+ * @param serviceFilter the filter condition
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setService(Class serviceName, String serviceFilter) {
+ ensureNotActive();
+ if (serviceName == null) {
+ throw new IllegalArgumentException("Service name cannot be null.");
+ }
+ m_trackedServiceName = serviceName;
+ if (serviceFilter != null) {
+ m_trackedServiceFilterUnmodified = serviceFilter;
+ m_trackedServiceFilter ="(&(" + Constants.OBJECTCLASS + "=" + serviceName.getName() + ")" + serviceFilter + ")";
+ }
+ else {
+ m_trackedServiceFilterUnmodified = null;
+ m_trackedServiceFilter = null;
+ }
+ m_trackedServiceReference = null;
+ return this;
+ }
+
+ /**
+ * Sets the name of the service that should be tracked. You can either specify
+ * only the name, or the name and a reference. In the latter case, the service reference
+ * is used to track the service and should only return services of the type that was
+ * specified in the name.
+ *
+ * @param serviceName the name of the service
+ * @param serviceReference the service reference to track
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setService(Class serviceName, ServiceReference serviceReference) {
+ ensureNotActive();
+ if (serviceName == null) {
+ throw new IllegalArgumentException("Service name cannot be null.");
+ }
+ m_trackedServiceName = serviceName;
+ m_trackedServiceReference = serviceReference;
+ m_trackedServiceFilterUnmodified = null;
+ m_trackedServiceFilter = null;
+ return this;
+ }
+
+ /**
+ * Sets the default implementation for this service dependency. You can use this to supply
+ * your own implementation that will be used instead of a Null Object when the dependency is
+ * not available. This is also convenient if the service dependency is not an interface
+ * (which would cause the Null Object creation to fail) but a class.
+ *
+ * @param implementation the instance to use or the class to instantiate if you want to lazily
+ * instantiate this implementation
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setDefaultImplementation(Object implementation) {
+ ensureNotActive();
+ m_defaultImplementation = implementation;
+ return this;
+ }
+
+ /**
+ * Sets the required flag which determines if this service is required or not.
+ *
+ * @param required the required flag
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setRequired(boolean required) {
+ ensureNotActive();
+ m_isRequired = required;
+ return this;
+ }
+
+ /**
+ * Sets auto configuration for this service. Auto configuration allows the
+ * dependency to fill in any attributes in the service implementation that
+ * are of the same type as this dependency. Default is on.
+ *
+ * @param autoConfig the value of auto config
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setAutoConfig(boolean autoConfig) {
+ ensureNotActive();
+ m_autoConfig = autoConfig;
+ m_autoConfigInvoked = true;
+ return this;
+ }
+
+ /**
+ * Sets auto configuration for this service. Auto configuration allows the
+ * dependency to fill in the attribute in the service implementation that
+ * has the same type and instance name.
+ *
+ * @param instanceName the name of attribute to auto config
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setAutoConfig(String instanceName) {
+ ensureNotActive();
+ m_autoConfig = (instanceName != null);
+ m_autoConfigInstance = instanceName;
+ m_autoConfigInvoked = true;
+ return this;
+ }
+
+ /**
+ * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+ * dependency is added or removed. When you specify callbacks, the auto configuration
+ * feature is automatically turned off, because we're assuming you don't need it in this
+ * case.
+ *
+ * @param added the method to call when a service was added
+ * @param removed the method to call when a service was removed
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setCallbacks(String added, String removed) {
+ return setCallbacks(null, added, null, removed);
+ }
+
+ /**
+ * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+ * dependency is added, changed or removed. When you specify callbacks, the auto
+ * configuration feature is automatically turned off, because we're assuming you don't
+ * need it in this case.
+ *
+ * @param added the method to call when a service was added
+ * @param changed the method to call when a service was changed
+ * @param removed the method to call when a service was removed
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setCallbacks(String added, String changed, String removed) {
+ return setCallbacks(null, added, changed, removed);
+ }
+
+ /**
+ * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+ * dependency is added or removed. They are called on the instance you provide. When you
+ * specify callbacks, the auto configuration feature is automatically turned off, because
+ * we're assuming you don't need it in this case.
+ *
+ * @param instance the instance to call the callbacks on
+ * @param added the method to call when a service was added
+ * @param removed the method to call when a service was removed
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setCallbacks(Object instance, String added, String removed) {
+ return setCallbacks(instance, added, null, removed);
+ }
+
+ /**
+ * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+ * dependency is added, changed or removed. They are called on the instance you provide. When you
+ * specify callbacks, the auto configuration feature is automatically turned off, because
+ * we're assuming you don't need it in this case.
+ *
+ * @param instance the instance to call the callbacks on
+ * @param added the method to call when a service was added
+ * @param changed the method to call when a service was changed
+ * @param removed the method to call when a service was removed
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setCallbacks(Object instance, String added, String changed, String removed) {
+ ensureNotActive();
+ // if at least one valid callback is specified, we turn off auto configuration, unless
+ // someone already explicitly invoked autoConfig
+ if ((added != null || removed != null || changed != null) && ! m_autoConfigInvoked) {
+ setAutoConfig(false);
+ }
+ m_callbackInstance = instance;
+ m_callbackAdded = added;
+ m_callbackChanged = changed;
+ m_callbackRemoved = removed;
+ return this;
+ }
+
+ private void ensureNotActive() {
+ if (m_tracker != null) {
+ throw new IllegalStateException("Cannot modify state while active.");
+ }
+ }
+
+ public synchronized String toString() {
+ return "ServiceDependency[" + m_trackedServiceName + " " + m_trackedServiceFilterUnmodified + "]";
+ }
+
+ public String getAutoConfigName() {
+ return m_autoConfigInstance;
+ }
+
+ public String getName() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(m_trackedServiceName.getName());
+ if (m_trackedServiceFilterUnmodified != null) {
+ sb.append(m_trackedServiceFilterUnmodified);
+ }
+ return sb.toString();
+ }
+
+ public int getState() {
+ return (isAvailable() ? 1 : 0) + (isRequired() ? 2 : 0);
+ }
+
+ public String getType() {
+ return "service";
+ }
+}
diff --git a/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceImpl.java b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceImpl.java
new file mode 100644
index 0000000..f5f4c90
--- /dev/null
+++ b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceImpl.java
@@ -0,0 +1,921 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+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.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Service implementation.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceImpl implements Service, ServiceComponent {
+ private static final Class[] VOID = new Class[] {};
+ private static final ServiceRegistration NULL_REGISTRATION;
+ private static final ServiceStateListener[] SERVICE_STATE_LISTENER_TYPE = new ServiceStateListener[] {};
+
+ private final BundleContext m_context;
+ private final DependencyManager m_manager;
+
+ // configuration (static)
+ private String m_callbackInit;
+ private String m_callbackStart;
+ private String m_callbackStop;
+ private String m_callbackDestroy;
+ private Object m_serviceName;
+ private Object m_implementation;
+
+ // configuration (dynamic, but does not affect state)
+ private Dictionary m_serviceProperties;
+
+ // configuration (dynamic, and affects state)
+ private ArrayList m_dependencies = new ArrayList();
+
+ // runtime state (calculated from dependencies)
+ private State m_state;
+
+ // runtime state (changes because of state changes)
+ private Object m_serviceInstance;
+ private ServiceRegistration m_registration;
+
+ // service state listeners
+ private final List m_stateListeners = new ArrayList();
+
+ // work queue
+ private final SerialExecutor m_executor = new SerialExecutor();
+
+ // instance factory
+ private Object m_instanceFactory;
+ private String m_instanceFactoryCreateMethod;
+
+ // composition manager
+ private Object m_compositionManager;
+ private String m_compositionManagerGetMethod;
+ private Object m_compositionManagerInstance;
+
+ // internal logging
+ private final Logger m_logger;
+ private ServiceRegistration m_serviceRegistration;
+ private Map m_autoConfig = new HashMap();
+ private Map m_autoConfigInstance = new HashMap();
+
+ public ServiceImpl(BundleContext context, DependencyManager manager, Logger logger) {
+ m_logger = logger;
+ m_state = new State((List) m_dependencies.clone(), false);
+ m_context = context;
+ m_manager = manager;
+ m_callbackInit = "init";
+ m_callbackStart = "start";
+ m_callbackStop = "stop";
+ m_callbackDestroy = "destroy";
+ m_implementation = null;
+ m_autoConfig.put(BundleContext.class, Boolean.TRUE);
+ m_autoConfig.put(ServiceRegistration.class, Boolean.TRUE);
+ m_autoConfig.put(DependencyManager.class, Boolean.TRUE);
+ }
+
+ private void calculateStateChanges(final State oldState, final State newState) {
+ if (oldState.isWaitingForRequired() && newState.isTrackingOptional()) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ activateService(newState);
+ }});
+ }
+ if (oldState.isTrackingOptional() && newState.isWaitingForRequired()) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ deactivateService(oldState);
+ }});
+ }
+ if (oldState.isInactive() && (newState.isTrackingOptional())) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ activateService(newState);
+ }});
+ }
+ if (oldState.isInactive() && (newState.isWaitingForRequired())) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ startTrackingRequired(newState);
+ }});
+ }
+ if ((oldState.isWaitingForRequired()) && newState.isInactive()) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ stopTrackingRequired(oldState);
+ }});
+ }
+ if ((oldState.isTrackingOptional()) && newState.isInactive()) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ deactivateService(oldState);
+ stopTrackingRequired(oldState);
+ }});
+ }
+ m_executor.execute();
+ }
+
+ public Service add(final Dependency dependency) {
+ State oldState, newState;
+ synchronized (m_dependencies) {
+ oldState = m_state;
+ m_dependencies.add(dependency);
+ }
+ if (oldState.isTrackingOptional() || (oldState.isWaitingForRequired() && dependency.isRequired())) {
+ dependency.start(this);
+ }
+ synchronized (m_dependencies) {
+ newState = new State((List) m_dependencies.clone(), !oldState.isInactive());
+ m_state = newState;
+ calculateStateChanges(oldState, newState);
+ }
+ return this;
+ }
+
+ public Service remove(Dependency dependency) {
+ State oldState, newState;
+ synchronized (m_dependencies) {
+ oldState = m_state;
+ m_dependencies.remove(dependency);
+ }
+ if (oldState.isTrackingOptional() || (oldState.isWaitingForRequired() && dependency.isRequired())) {
+ dependency.stop(this);
+ }
+ synchronized (m_dependencies) {
+ newState = new State((List) m_dependencies.clone(), !oldState.isInactive());
+ m_state = newState;
+ }
+ calculateStateChanges(oldState, newState);
+ return this;
+ }
+
+ public List getDependencies() {
+ synchronized (m_dependencies) {
+ return (List) m_dependencies.clone();
+ }
+ }
+
+ public ServiceRegistration getServiceRegistration() {
+ return m_registration;
+ }
+
+ public Object getService() {
+ return m_serviceInstance;
+ }
+
+ public void dependencyAvailable(final Dependency dependency) {
+ State oldState, newState;
+ synchronized (m_dependencies) {
+ oldState = m_state;
+ newState = new State((List) m_dependencies.clone(), !oldState.isInactive());
+ m_state = newState;
+ }
+ calculateStateChanges(oldState, newState);
+ if (newState.isTrackingOptional()) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ updateInstance(dependency);
+ }
+ });
+ m_executor.execute();
+ }
+ }
+
+ public void dependencyChanged(final Dependency dependency) {
+ State state;
+ synchronized (m_dependencies) {
+ state = m_state;
+ }
+ if (state.isTrackingOptional()) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ updateInstance(dependency);
+ }
+ });
+ m_executor.execute();
+ }
+ }
+
+ public void dependencyUnavailable(final Dependency dependency) {
+ State oldState, newState;
+ synchronized (m_dependencies) {
+ oldState = m_state;
+ newState = new State((List) m_dependencies.clone(), !oldState.isInactive());
+ m_state = newState;
+ }
+ calculateStateChanges(oldState, newState);
+ if (newState.isTrackingOptional()) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ updateInstance(dependency);
+ }
+ });
+ m_executor.execute();
+ }
+ }
+
+ public synchronized void start() {
+ m_serviceRegistration = m_context.registerService(ServiceComponent.class.getName(), this, null);
+ State oldState, newState;
+ synchronized (m_dependencies) {
+ oldState = m_state;
+ newState = new State((List) m_dependencies.clone(), true);
+ m_state = newState;
+ }
+ calculateStateChanges(oldState, newState);
+ }
+
+ public synchronized void stop() {
+ State oldState, newState;
+ synchronized (m_dependencies) {
+ oldState = m_state;
+ newState = new State((List) m_dependencies.clone(), false);
+ m_state = newState;
+ }
+ calculateStateChanges(oldState, newState);
+ m_serviceRegistration.unregister();
+ }
+
+ public synchronized Service setInterface(String serviceName, Dictionary properties) {
+ ensureNotActive();
+ m_serviceName = serviceName;
+ m_serviceProperties = properties;
+ return this;
+ }
+
+ public synchronized Service setInterface(String[] serviceName, Dictionary properties) {
+ ensureNotActive();
+ m_serviceName = serviceName;
+ m_serviceProperties = properties;
+ return this;
+ }
+
+ public synchronized Service setCallbacks(String init, String start, String stop, String destroy) {
+ ensureNotActive();
+ m_callbackInit = init;
+ m_callbackStart = start;
+ m_callbackStop = stop;
+ m_callbackDestroy = destroy;
+ return this;
+ }
+
+ public synchronized Service setImplementation(Object implementation) {
+ ensureNotActive();
+ m_implementation = implementation;
+ return this;
+ }
+
+ public synchronized Service setFactory(Object factory, String createMethod) {
+ ensureNotActive();
+ m_instanceFactory = factory;
+ m_instanceFactoryCreateMethod = createMethod;
+ return this;
+ }
+
+ public synchronized Service setFactory(String createMethod) {
+ return setFactory(null, createMethod);
+ }
+
+ public synchronized Service setComposition(Object instance, String getMethod) {
+ ensureNotActive();
+ m_compositionManager = instance;
+ m_compositionManagerGetMethod = getMethod;
+ return this;
+ }
+
+ public synchronized Service setComposition(String getMethod) {
+ return setComposition(null, getMethod);
+ }
+
+ public String toString() {
+ return "ServiceImpl[" + m_serviceName + " " + m_implementation + "]";
+ }
+
+ public synchronized Dictionary getServiceProperties() {
+ if (m_serviceProperties != null) {
+ return (Dictionary) ((Hashtable) m_serviceProperties).clone();
+ }
+ return null;
+ }
+
+ public synchronized void setServiceProperties(Dictionary serviceProperties) {
+ m_serviceProperties = serviceProperties;
+ if (isRegistered() && (m_serviceName != null)) {
+ m_registration.setProperties(calculateServiceProperties());
+ }
+ }
+
+ // service state listener methods
+ public void addStateListener(ServiceStateListener listener) {
+ synchronized (m_stateListeners) {
+ m_stateListeners.add(listener);
+ }
+ // when we register as a listener and the service is already started
+ // make sure we invoke the right callbacks so the listener knows
+ State state;
+ synchronized (m_dependencies) {
+ state = m_state;
+ }
+ if (state.isTrackingOptional()) {
+ listener.starting(this);
+ listener.started(this);
+ }
+ }
+
+ public void removeStateListener(ServiceStateListener listener) {
+ synchronized (m_stateListeners) {
+ m_stateListeners.remove(listener);
+ }
+ }
+
+ void removeStateListeners() {
+ synchronized (m_stateListeners) {
+ m_stateListeners.clear();
+ }
+ }
+
+ private void stateListenersStarting() {
+ ServiceStateListener[] list = getListeners();
+ for (int i = 0; i < list.length; i++) {
+ try {
+ list[i].starting(this);
+ }
+ catch (Throwable t) {
+ m_logger.log(Logger.LOG_ERROR, "Error invoking listener starting method.", t);
+ }
+ }
+ }
+
+ private void stateListenersStarted() {
+ ServiceStateListener[] list = getListeners();
+ for (int i = 0; i < list.length; i++) {
+ try {
+ list[i].started(this);
+ }
+ catch (Throwable t) {
+ m_logger.log(Logger.LOG_ERROR, "Error invoking listener started method.", t);
+ }
+ }
+ }
+
+ private void stateListenersStopping() {
+ ServiceStateListener[] list = getListeners();
+ for (int i = 0; i < list.length; i++) {
+ try {
+ list[i].stopping(this);
+ }
+ catch (Throwable t) {
+ m_logger.log(Logger.LOG_ERROR, "Error invoking listener stopping method.", t);
+ }
+ }
+ }
+
+ private void stateListenersStopped() {
+ ServiceStateListener[] list = getListeners();
+ for (int i = 0; i < list.length; i++) {
+ try {
+ list[i].stopped(this);
+ }
+ catch (Throwable t) {
+ m_logger.log(Logger.LOG_ERROR, "Error invoking listener stopped method.", t);
+ }
+ }
+ }
+
+ private ServiceStateListener[] getListeners() {
+ synchronized (m_stateListeners) {
+ return (ServiceStateListener[]) m_stateListeners.toArray(SERVICE_STATE_LISTENER_TYPE);
+ }
+ }
+
+ private void activateService(State state) {
+ String init, start;
+ synchronized (this) {
+ init = m_callbackInit;
+ start = m_callbackStart;
+ }
+ // service activation logic, first we initialize the service instance itself
+ // meaning it is created if necessary and the bundle context is set
+ initService();
+ // then we invoke the init callback so the service can further initialize
+ // itself
+ invoke(init);
+ // now is the time to configure the service, meaning all required
+ // dependencies will be set and any callbacks called
+ configureService(state);
+ // inform the state listeners we're starting
+ stateListenersStarting();
+ // invoke the start callback, since we're now ready to be used
+ invoke(start);
+ // start tracking optional services
+ startTrackingOptional(state);
+ // register the service in the framework's service registry
+ registerService();
+ // inform the state listeners we've started
+ stateListenersStarted();
+ }
+
+ private void deactivateService(State state) {
+ String stop, destroy;
+ synchronized (this) {
+ stop = m_callbackStop;
+ destroy = m_callbackDestroy;
+ }
+ // service deactivation logic, first inform the state listeners
+ // we're stopping
+ stateListenersStopping();
+ // then, unregister the service from the framework
+ unregisterService();
+ // stop tracking optional services
+ stopTrackingOptional(state);
+ // invoke the stop callback
+ invoke(stop);
+ // inform the state listeners we've stopped
+ stateListenersStopped();
+ // invoke the destroy callback
+ invoke(destroy);
+ // destroy the service instance
+ destroyService(state);
+ }
+
+ private void invoke(String name) {
+ if (name != null) {
+ // invoke method if it exists
+ try {
+ Class clazz = m_serviceInstance.getClass();
+ while (clazz != null) {
+ try {
+ Method method = clazz.getDeclaredMethod(name, null);
+ if (method != null) {
+ method.setAccessible(true);
+ try {
+ method.invoke(m_serviceInstance, null);
+ }
+ catch (InvocationTargetException e) {
+ m_logger.log(Logger.LOG_ERROR, "Exception while invoking method " + method + ".", e);
+ }
+ return;
+ }
+ }
+ catch (NoSuchMethodException e) {
+ // ignore this, we keep searching if the method does not exist
+ }
+ clazz = clazz.getSuperclass();
+ }
+ }
+ catch (Exception e) {
+ m_logger.log(Logger.LOG_ERROR, "Error trying to invoke method named " + name + ".", e);
+ }
+ }
+ }
+
+ private void startTrackingOptional(State state) {
+ Iterator i = state.getDependencies().iterator();
+ while (i.hasNext()) {
+ Dependency dependency = (Dependency) i.next();
+ if (!dependency.isRequired()) {
+ dependency.start(this);
+ }
+ }
+ }
+
+ private void stopTrackingOptional(State state) {
+ Iterator i = state.getDependencies().iterator();
+ while (i.hasNext()) {
+ Dependency dependency = (Dependency) i.next();
+ if (!dependency.isRequired()) {
+ dependency.stop(this);
+ }
+ }
+ }
+
+ private void startTrackingRequired(State state) {
+ Iterator i = state.getDependencies().iterator();
+ while (i.hasNext()) {
+ Dependency dependency = (Dependency) i.next();
+ if (dependency.isRequired()) {
+ dependency.start(this);
+ }
+ }
+ }
+
+ private void stopTrackingRequired(State state) {
+ Iterator i = state.getDependencies().iterator();
+ while (i.hasNext()) {
+ Dependency dependency = (Dependency) i.next();
+ if (dependency.isRequired()) {
+ dependency.stop(this);
+ }
+ }
+ }
+
+ private Object createInstance(Class clazz) throws SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException {
+ Constructor constructor = clazz.getConstructor(VOID);
+ constructor.setAccessible(true);
+ return clazz.newInstance();
+ }
+
+ void initService() {
+ if (m_serviceInstance == null) {
+ if (m_implementation instanceof Class) {
+ // instantiate
+ try {
+ m_serviceInstance = createInstance((Class) m_implementation);
+ }
+ catch (Exception e) {
+ m_logger.log(Logger.LOG_ERROR, "Could not create service instance of class " + m_implementation + ".", e);
+ }
+ }
+ else {
+ if (m_instanceFactoryCreateMethod != null) {
+ Object factory = null;
+ if (m_instanceFactory != null) {
+ if (m_instanceFactory instanceof Class) {
+ try {
+ factory = createInstance((Class) m_instanceFactory);
+ }
+ catch (Exception e) {
+ m_logger.log(Logger.LOG_ERROR, "Could not create factory instance of class " + m_instanceFactory + ".", e);
+ }
+ }
+ else {
+ factory = m_instanceFactory;
+ }
+ }
+ else {
+ // TODO review if we want to try to default to something if not specified
+ // for now the JavaDoc of setFactory(method) reflects the fact that we need
+ // to review it
+ }
+ if (factory == null) {
+ m_logger.log(Logger.LOG_ERROR, "Factory cannot be null.");
+ }
+ else {
+ try {
+ Method m = factory.getClass().getDeclaredMethod(m_instanceFactoryCreateMethod, null);
+ m_serviceInstance = m.invoke(factory, null);
+ }
+ catch (Exception e) {
+ m_logger.log(Logger.LOG_ERROR, "Could not create service instance using factory " + factory + " method " + m_instanceFactoryCreateMethod + ".", e);
+ }
+ }
+ }
+ if (m_implementation == null) {
+ m_logger.log(Logger.LOG_ERROR, "Implementation cannot be null.");
+ }
+ if (m_serviceInstance == null) {
+ m_serviceInstance = m_implementation;
+ }
+ }
+ // configure the bundle context
+ if (((Boolean) m_autoConfig.get(BundleContext.class)).booleanValue()) {
+ configureImplementation(BundleContext.class, m_context, (String) m_autoConfigInstance.get(BundleContext.class));
+ }
+ if (((Boolean) m_autoConfig.get(ServiceRegistration.class)).booleanValue()) {
+ configureImplementation(ServiceRegistration.class, NULL_REGISTRATION, (String) m_autoConfigInstance.get(ServiceRegistration.class));
+ }
+ if (((Boolean) m_autoConfig.get(DependencyManager.class)).booleanValue()) {
+ configureImplementation(DependencyManager.class, m_manager, (String) m_autoConfigInstance.get(DependencyManager.class));
+ }
+ }
+ }
+
+ public void setAutoConfig(Class clazz, boolean autoConfig) {
+ m_autoConfig.put(clazz, Boolean.valueOf(autoConfig));
+ }
+
+ public void setAutoConfig(Class clazz, String instanceName) {
+ m_autoConfig.put(clazz, Boolean.valueOf(instanceName != null));
+ m_autoConfigInstance.put(clazz, instanceName);
+ }
+
+ private void configureService(State state) {
+ // configure all services (the optional dependencies might be configured
+ // as null objects but that's what we want at this point)
+ configureServices(state);
+ }
+
+ private void destroyService(State state) {
+ unconfigureServices(state);
+ m_serviceInstance = null;
+ }
+
+ private void registerService() {
+ if (m_serviceName != null) {
+ ServiceRegistrationImpl wrapper = new ServiceRegistrationImpl();
+ m_registration = wrapper;
+ if (((Boolean) m_autoConfig.get(ServiceRegistration.class)).booleanValue()) {
+ configureImplementation(ServiceRegistration.class, m_registration, (String) m_autoConfigInstance.get(ServiceRegistration.class));
+ }
+
+ // service name can either be a string or an array of strings
+ ServiceRegistration registration;
+
+ // determine service properties
+ Dictionary properties = calculateServiceProperties();
+
+ // register the service
+ try {
+ if (m_serviceName instanceof String) {
+ registration = m_context.registerService((String) m_serviceName, m_serviceInstance, properties);
+ }
+ else {
+ registration = m_context.registerService((String[]) m_serviceName, m_serviceInstance, properties);
+ }
+ wrapper.setServiceRegistration(registration);
+ }
+ catch (IllegalArgumentException iae) {
+ m_logger.log(Logger.LOG_ERROR, "Could not register service " + m_serviceInstance, iae);
+ // set the registration to an illegal state object, which will make all invocations on this
+ // wrapper fail with an ISE (which also occurs when the SR becomes invalid)
+ wrapper.setIllegalState();
+ }
+ }
+ }
+
+ private Dictionary calculateServiceProperties() {
+ Dictionary properties = new Properties();
+ addTo(properties, m_serviceProperties);
+ for (int i = 0; i < m_dependencies.size(); i++) {
+ Dependency d = (Dependency) m_dependencies.get(i);
+ if (d instanceof ConfigurationDependency) {
+ ConfigurationDependency cd = (ConfigurationDependency) d;
+ if (cd.isPropagated()) {
+ Dictionary dict = cd.getConfiguration();
+ addTo(properties, dict);
+ }
+ }
+ }
+ if (properties.size() == 0) {
+ properties = null;
+ }
+ return properties;
+ }
+
+ private void addTo(Dictionary properties, Dictionary additional) {
+ if (properties == null) {
+ throw new IllegalArgumentException("Dictionary to add to cannot be null.");
+ }
+ if (additional != null) {
+ Enumeration e = additional.keys();
+ while (e.hasMoreElements()) {
+ Object key = e.nextElement();
+ properties.put(key, additional.get(key));
+ }
+ }
+ }
+
+ private void unregisterService() {
+ if (m_serviceName != null) {
+ m_registration.unregister();
+ configureImplementation(ServiceRegistration.class, NULL_REGISTRATION);
+ }
+ }
+
+ private void updateInstance(Dependency dependency) {
+ if (dependency instanceof ServiceDependency) {
+ ServiceDependency sd = (ServiceDependency) dependency;
+ // update the dependency in the service instance (it will use
+ // a null object if necessary)
+ if (sd.isAutoConfig()) {
+ configureImplementation(sd.getInterface(), sd.getService(), sd.getAutoConfigName());
+ }
+ }
+ else if (dependency instanceof ConfigurationDependency) {
+ ConfigurationDependency cd = (ConfigurationDependency) dependency;
+ if (cd.isPropagated()) {
+ // change service properties accordingly
+ Dictionary props = calculateServiceProperties();
+ m_registration.setProperties(props);
+ }
+ }
+ }
+
+ /**
+ * Configure a field in the service implementation. The service implementation
+ * is searched for fields that have the same type as the class that was specified
+ * and for each of these fields, the specified instance is filled in.
+ *
+ * @param clazz the class to search for
+ * @param instance the instance to fill in
+ * @param instanceName the name of the instance to fill in, or <code>null</code> if not used
+ */
+ private void configureImplementation(Class clazz, Object instance, String instanceName) {
+ Object[] instances = getCompositionInstances();
+ if (instances != null) {
+ for (int i = 0; i < instances.length; i++) {
+ Object serviceInstance = instances[i];
+ Class serviceClazz = serviceInstance.getClass();
+ while (serviceClazz != null) {
+ Field[] fields = serviceClazz.getDeclaredFields();
+ for (int j = 0; j < fields.length; j++) {
+ if (fields[j].getType().equals(clazz) && (instanceName == null || fields[j].getName().equals(instanceName))) {
+ try {
+ fields[j].setAccessible(true);
+ // synchronized makes sure the field is actually written to immediately
+ synchronized (new Object()) {
+ fields[j].set(serviceInstance, instance);
+ }
+ }
+ catch (Exception e) {
+ m_logger.log(Logger.LOG_ERROR, "Could not set field " + fields[j], e);
+ return;
+ }
+ }
+ }
+ serviceClazz = serviceClazz.getSuperclass();
+ }
+ }
+ }
+ }
+
+ public Object[] getCompositionInstances() {
+ Object[] instances = null;
+ if (m_compositionManagerGetMethod != null) {
+ if (m_compositionManager != null) {
+ m_compositionManagerInstance = m_compositionManager;
+ }
+ else {
+ m_compositionManagerInstance = m_serviceInstance;
+ }
+ if (m_compositionManagerInstance != null) {
+ try {
+ Method m = m_compositionManagerInstance.getClass().getDeclaredMethod(m_compositionManagerGetMethod, null);
+ m.setAccessible(true);
+ instances = (Object[]) m.invoke(m_compositionManagerInstance, null);
+ }
+ catch (Exception e) {
+ m_logger.log(Logger.LOG_ERROR, "Could not obtain instances from the composition manager.", e);
+ instances = new Object[] { m_serviceInstance };
+ }
+ }
+ }
+ else {
+ instances = new Object[] { m_serviceInstance };
+ }
+ return instances;
+ }
+
+ private void configureImplementation(Class clazz, Object instance) {
+ configureImplementation(clazz, instance, null);
+ }
+
+ private void configureServices(State state) {
+ Iterator i = state.getDependencies().iterator();
+ while (i.hasNext()) {
+ Dependency dependency = (Dependency) i.next();
+ if (dependency instanceof ServiceDependency) {
+ ServiceDependency sd = (ServiceDependency) dependency;
+ if (sd.isAutoConfig()) {
+ if (sd.isRequired()) {
+ configureImplementation(sd.getInterface(), sd.getService(), sd.getAutoConfigName());
+ }
+ else {
+ // for optional services, we do an "ad-hoc" lookup to inject the service if it is
+ // already available even though the tracker has not yet been started
+ configureImplementation(sd.getInterface(), sd.lookupService(), sd.getAutoConfigName());
+ }
+ }
+ // for required dependencies, we invoke any callbacks here
+ if (sd.isRequired()) {
+ sd.invokeAdded();
+ }
+ }
+ }
+ }
+
+ private void unconfigureServices(State state) {
+ Iterator i = state.getDependencies().iterator();
+ while (i.hasNext()) {
+ Dependency dependency = (Dependency) i.next();
+ if (dependency instanceof ServiceDependency) {
+ ServiceDependency sd = (ServiceDependency) dependency;
+ // for required dependencies, we invoke any callbacks here
+ if (sd.isRequired()) {
+ sd.invokeRemoved();
+ }
+ }
+ }
+ }
+
+ private void ensureNotActive() {
+ State state;
+ synchronized (m_dependencies) {
+ state = m_state;
+ }
+ if (!state.isInactive()) {
+ throw new IllegalStateException("Cannot modify state while active.");
+ }
+ }
+ boolean isRegistered() {
+ State state;
+ synchronized (m_dependencies) {
+ state = m_state;
+ }
+ return (state.isTrackingOptional());
+ }
+
+ // ServiceComponent interface
+
+ static class SCDImpl implements ServiceComponentDependency {
+ private final String m_name;
+ private final int m_state;
+ private final String m_type;
+
+ public SCDImpl(String name, int state, String type) {
+ m_name = name;
+ m_state = state;
+ m_type = type;
+ }
+
+ public String getName() {
+ return m_name;
+ }
+
+ public int getState() {
+ return m_state;
+ }
+
+ public String getType() {
+ return m_type;
+ }
+ }
+
+ public ServiceComponentDependency[] getComponentDependencies() {
+ List deps = getDependencies();
+ if (deps != null) {
+ ServiceComponentDependency[] result = new ServiceComponentDependency[deps.size()];
+ for (int i = 0; i < result.length; i++) {
+ Dependency dep = (Dependency) deps.get(i);
+ if (dep instanceof ServiceComponentDependency) {
+ result[i] = (ServiceComponentDependency) dep;
+ }
+ else {
+ result[i] = new SCDImpl(dep.toString(), (dep.isAvailable() ? 1 : 0) + (dep.isRequired() ? 2 : 0), dep.getClass().getName());
+ }
+ }
+ return result;
+ }
+ return null;
+ }
+
+ public String getName() {
+ if (m_serviceName instanceof String[]) {
+ StringBuffer sb = new StringBuffer();
+ String[] names = (String[]) m_serviceName;
+ for (int i = 0; i < names.length; i++) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append(names[i]);
+ }
+ return sb.toString();
+ }
+ else if (m_serviceName instanceof String) {
+ return m_serviceName.toString();
+ }
+ else {
+ return m_implementation.toString();
+ }
+ }
+
+ public int getState() {
+ return (isRegistered() ? 1 : 0);
+ }
+
+ static {
+ NULL_REGISTRATION = (ServiceRegistration) Proxy.newProxyInstance(ServiceImpl.class.getClassLoader(), new Class[] {ServiceRegistration.class}, new DefaultNullObject());
+ }
+}
diff --git a/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceRegistrationImpl.java b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceRegistrationImpl.java
new file mode 100644
index 0000000..0ff8473
--- /dev/null
+++ b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceRegistrationImpl.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager;
+
+import java.util.Dictionary;
+
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * A wrapper around a service registration that blocks until the
+ * service registration is available.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public final class ServiceRegistrationImpl implements ServiceRegistration {
+ public static final ServiceRegistrationImpl ILLEGAL_STATE = new ServiceRegistrationImpl();
+ private volatile ServiceRegistration m_registration;
+
+ public ServiceRegistrationImpl() {
+ m_registration = null;
+ }
+
+ public ServiceReference getReference() {
+ return ensureRegistration().getReference();
+ }
+
+ public void setProperties(Dictionary dictionary) {
+ ensureRegistration().setProperties(dictionary);
+ }
+
+ public void unregister() {
+ ensureRegistration().unregister();
+ }
+
+ public boolean equals(Object obj) {
+ return ensureRegistration().equals(obj);
+ }
+
+ public int hashCode() {
+ return ensureRegistration().hashCode();
+ }
+
+ public String toString() {
+ return ensureRegistration().toString();
+ }
+
+ private synchronized ServiceRegistration ensureRegistration() {
+ while (m_registration == null) {
+ try {
+ wait();
+ }
+ catch (InterruptedException ie) {
+ // we were interrupted so hopefully we will now have a
+ // service registration ready; if not we wait again
+ }
+ }
+ // check if we're in an illegal state and throw an exception
+ if (ILLEGAL_STATE == m_registration) {
+ throw new IllegalStateException("Service is not registered.");
+ }
+ return m_registration;
+ }
+
+ /**
+ * Sets the service registration and notifies all waiting parties.
+ */
+ void setServiceRegistration(ServiceRegistration registration) {
+ synchronized (this) {
+ m_registration = registration;
+ notifyAll();
+ }
+ }
+
+ /**
+ * Sets this wrapper to an illegal state, which will cause all threads
+ * that are waiting for this service registration to fail.
+ */
+ void setIllegalState() {
+ setServiceRegistration(ServiceRegistrationImpl.ILLEGAL_STATE);
+ }
+}
diff --git a/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceStateListener.java b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceStateListener.java
new file mode 100644
index 0000000..a655633
--- /dev/null
+++ b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceStateListener.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager;
+
+/**
+ * This interface can be used to register a service state listener. Service
+ * state listeners are called whenever a service state changes. You get notified
+ * when the service is starting, started, stopping and stopped. Each callback
+ * includes a reference to the service in question.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ServiceStateListener {
+ /**
+ * Called when the service is starting. At this point, the required
+ * dependencies have been injected, but the service has not been registered
+ * yet.
+ *
+ * @param service the service
+ */
+ public void starting(Service service);
+
+ /**
+ * Called when the service is started. At this point, the service has been
+ * registered.
+ *
+ * @param service the service
+ */
+ public void started(Service service);
+
+ /**
+ * Called when the service is stopping. At this point, the service is still
+ * registered.
+ *
+ * @param service the service
+ */
+ public void stopping(Service service);
+
+ /**
+ * Called when the service is stopped. At this point, the service has been
+ * unregistered.
+ *
+ * @param service the service
+ */
+ public void stopped(Service service);
+}
diff --git a/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceTracker.java b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceTracker.java
new file mode 100644
index 0000000..83c4b4b
--- /dev/null
+++ b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceTracker.java
@@ -0,0 +1,1153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.LinkedList;
+
+import org.osgi.framework.AllServiceListener;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * A modified <code>ServiceTracker</code> class simplifies using services
+ * from the Framework's service registry. This class is used internally
+ * by the dependency manager. It is based on the OSGi R4.1 sources, which
+ * are made available under the same ASF 2.0 license.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceTracker implements ServiceTrackerCustomizer {
+ /* set this to true to compile in debug messages */
+ static final boolean DEBUG = false;
+ /**
+ * Bundle context against which this <code>ServiceTracker</code> object is
+ * tracking.
+ */
+ protected final BundleContext context;
+ /**
+ * Filter specifying search criteria for the services to track.
+ *
+ * @since 1.1
+ */
+ protected final Filter filter;
+ /**
+ * <code>ServiceTrackerCustomizer</code> object for this tracker.
+ */
+ final ServiceTrackerCustomizer customizer;
+ /**
+ * Filter string for use when adding the ServiceListener. If this field is
+ * set, then certain optimizations can be taken since we don't have a user
+ * supplied filter.
+ */
+ final String listenerFilter;
+ /**
+ * Class name to be tracked. If this field is set, then we are tracking by
+ * class name.
+ */
+ private final String trackClass;
+ /**
+ * Reference to be tracked. If this field is set, then we are tracking a
+ * single ServiceReference.
+ */
+ private final ServiceReference trackReference;
+ /**
+ * Tracked services: <code>ServiceReference</code> object -> customized
+ * Object and <code>ServiceListener</code> object
+ */
+ private volatile Tracked tracked;
+ /**
+ * Modification count. This field is initialized to zero by open, set to -1
+ * by close and incremented by modified.
+ *
+ * This field is volatile since it is accessed by multiple threads.
+ */
+ private volatile int trackingCount = -1;
+ /**
+ * Cached ServiceReference for getServiceReference.
+ *
+ * This field is volatile since it is accessed by multiple threads.
+ */
+ private volatile ServiceReference cachedReference;
+ /**
+ * Cached service object for getService.
+ *
+ * This field is volatile since it is accessed by multiple threads.
+ */
+ private volatile Object cachedService;
+
+ /**
+ * Create a <code>ServiceTracker</code> object on the specified
+ * <code>ServiceReference</code> object.
+ *
+ * <p>
+ * The service referenced by the specified <code>ServiceReference</code>
+ * object will be tracked by this <code>ServiceTracker</code> object.
+ *
+ * @param context <code>BundleContext</code> object against which the
+ * tracking is done.
+ * @param reference <code>ServiceReference</code> object for the service
+ * to be tracked.
+ * @param customizer The customizer object to call when services are added,
+ * modified, or removed in this <code>ServiceTracker</code> object.
+ * If customizer is <code>null</code>, then this
+ * <code>ServiceTracker</code> object will be used as the
+ * <code>ServiceTrackerCustomizer</code> object and the
+ * <code>ServiceTracker</code> object will call the
+ * <code>ServiceTrackerCustomizer</code> methods on itself.
+ */
+ public ServiceTracker(BundleContext context, ServiceReference reference,
+ ServiceTrackerCustomizer customizer) {
+ this.context = context;
+ this.trackReference = reference;
+ this.trackClass = null;
+ this.customizer = (customizer == null) ? this : customizer;
+ this.listenerFilter = "(" + Constants.SERVICE_ID + "=" + reference.getProperty(Constants.SERVICE_ID).toString() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ try {
+ this.filter = context.createFilter(listenerFilter);
+ }
+ catch (InvalidSyntaxException e) { // we could only get this exception
+ // if the ServiceReference was
+ // invalid
+ throw new IllegalArgumentException(
+ "unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Create a <code>ServiceTracker</code> object on the specified class
+ * name.
+ *
+ * <p>
+ * Services registered under the specified class name will be tracked by
+ * this <code>ServiceTracker</code> object.
+ *
+ * @param context <code>BundleContext</code> object against which the
+ * tracking is done.
+ * @param clazz Class name of the services to be tracked.
+ * @param customizer The customizer object to call when services are added,
+ * modified, or removed in this <code>ServiceTracker</code> object.
+ * If customizer is <code>null</code>, then this
+ * <code>ServiceTracker</code> object will be used as the
+ * <code>ServiceTrackerCustomizer</code> object and the
+ * <code>ServiceTracker</code> object will call the
+ * <code>ServiceTrackerCustomizer</code> methods on itself.
+ */
+ public ServiceTracker(BundleContext context, String clazz,
+ ServiceTrackerCustomizer customizer) {
+ this.context = context;
+ this.trackReference = null;
+ this.trackClass = clazz;
+ this.customizer = (customizer == null) ? this : customizer;
+ this.listenerFilter = "(" + Constants.OBJECTCLASS + "=" + clazz + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ try {
+ this.filter = context.createFilter(listenerFilter);
+ }
+ catch (InvalidSyntaxException e) { // we could only get this exception
+ // if the clazz argument was
+ // malformed
+ throw new IllegalArgumentException(
+ "unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Create a <code>ServiceTracker</code> object on the specified
+ * <code>Filter</code> object.
+ *
+ * <p>
+ * Services which match the specified <code>Filter</code> object will be
+ * tracked by this <code>ServiceTracker</code> object.
+ *
+ * @param context <code>BundleContext</code> object against which the
+ * tracking is done.
+ * @param filter <code>Filter</code> object to select the services to be
+ * tracked.
+ * @param customizer The customizer object to call when services are added,
+ * modified, or removed in this <code>ServiceTracker</code> object.
+ * If customizer is null, then this <code>ServiceTracker</code>
+ * object will be used as the <code>ServiceTrackerCustomizer</code>
+ * object and the <code>ServiceTracker</code> object will call the
+ * <code>ServiceTrackerCustomizer</code> methods on itself.
+ * @since 1.1
+ */
+ public ServiceTracker(BundleContext context, Filter filter,
+ ServiceTrackerCustomizer customizer) {
+ this.context = context;
+ this.trackReference = null;
+ this.trackClass = null;
+ this.listenerFilter = null;
+ this.filter = filter;
+ this.customizer = (customizer == null) ? this : customizer;
+ if ((context == null) || (filter == null)) { // we throw a NPE here
+ // to
+ // be consistent with the
+ // other constructors
+ throw new NullPointerException();
+ }
+ }
+
+ /**
+ * Open this <code>ServiceTracker</code> object and begin tracking
+ * services.
+ *
+ * <p>
+ * This method calls <code>open(false)</code>.
+ *
+ * @throws java.lang.IllegalStateException if the <code>BundleContext</code>
+ * object with which this <code>ServiceTracker</code> object was
+ * created is no longer valid.
+ * @see #open(boolean)
+ */
+ public void open() {
+ open(false);
+ }
+
+ /**
+ * Open this <code>ServiceTracker</code> object and begin tracking
+ * services.
+ *
+ * <p>
+ * Services which match the search criteria specified when this
+ * <code>ServiceTracker</code> object was created are now tracked by this
+ * <code>ServiceTracker</code> object.
+ *
+ * @param trackAllServices If <code>true</code>, then this
+ * <code>ServiceTracker</code> will track all matching services
+ * regardless of class loader accessibility. If <code>false</code>,
+ * then this <code>ServiceTracker</code> will only track matching
+ * services which are class loader accessibile to the bundle whose
+ * <code>BundleContext</code> is used by this
+ * <code>ServiceTracker</code>.
+ * @throws java.lang.IllegalStateException if the <code>BundleContext</code>
+ * object with which this <code>ServiceTracker</code> object was
+ * created is no longer valid.
+ * @since 1.3
+ */
+ public synchronized void open(boolean trackAllServices) {
+ if (tracked != null) {
+ return;
+ }
+ if (DEBUG) {
+ System.out.println("ServiceTracker.open: " + filter); //$NON-NLS-1$
+ }
+ tracked = trackAllServices ? new AllTracked() : new Tracked();
+ trackingCount = 0;
+ synchronized (tracked) {
+ try {
+ context.addServiceListener(tracked, listenerFilter);
+ ServiceReference[] references;
+ if (listenerFilter == null) { // user supplied filter
+ references = getInitialReferences(trackAllServices, null,
+ filter.toString());
+ }
+ else { // constructor supplied filter
+ if (trackClass == null) {
+ references = new ServiceReference[] {trackReference};
+ }
+ else {
+ references = getInitialReferences(trackAllServices,
+ trackClass, null);
+ }
+ }
+
+ tracked.setInitialServices(references); // set tracked with
+ // the initial
+ // references
+ }
+ catch (InvalidSyntaxException e) {
+ throw new RuntimeException(
+ "unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$
+ }
+ }
+ /* Call tracked outside of synchronized region */
+ tracked.trackInitialServices(); // process the initial references
+ }
+
+ /**
+ * Returns the list of initial <code>ServiceReference</code> objects that
+ * will be tracked by this <code>ServiceTracker</code> object.
+ *
+ * @param trackAllServices If true, use getAllServiceReferences.
+ * @param trackClass the class name with which the service was registered,
+ * or null for all services.
+ * @param filterString the filter criteria or null for all services.
+ * @return the list of initial <code>ServiceReference</code> objects.
+ * @throws InvalidSyntaxException if the filter uses an invalid syntax.
+ */
+ private ServiceReference[] getInitialReferences(boolean trackAllServices,
+ String trackClass, String filterString)
+ throws InvalidSyntaxException {
+ if (trackAllServices) {
+ return context.getAllServiceReferences(trackClass, filterString);
+ }
+ else {
+ return context.getServiceReferences(trackClass, filterString);
+ }
+ }
+
+ /**
+ * Close this <code>ServiceTracker</code> object.
+ *
+ * <p>
+ * This method should be called when this <code>ServiceTracker</code>
+ * object should end the tracking of services.
+ */
+ public synchronized void close() {
+ if (tracked == null) {
+ return;
+ }
+ if (DEBUG) {
+ System.out.println("ServiceTracker.close: " + filter); //$NON-NLS-1$
+ }
+ tracked.close();
+ ServiceReference[] references = getServiceReferences();
+ Tracked outgoing = tracked;
+ tracked = null;
+ try {
+ context.removeServiceListener(outgoing);
+ }
+ catch (IllegalStateException e) {
+ /* In case the context was stopped. */
+ }
+ if (references != null) {
+ for (int i = 0; i < references.length; i++) {
+ outgoing.untrack(references[i]);
+ }
+ }
+ trackingCount = -1;
+ if (DEBUG) {
+ if ((cachedReference == null) && (cachedService == null)) {
+ System.out
+ .println("ServiceTracker.close[cached cleared]: " + filter); //$NON-NLS-1$
+ }
+ }
+ }
+
+ /**
+ * Default implementation of the
+ * <code>ServiceTrackerCustomizer.addingService</code> method.
+ *
+ * <p>
+ * This method is only called when this <code>ServiceTracker</code> object
+ * has been constructed with a <code>null ServiceTrackerCustomizer</code>
+ * argument.
+ *
+ * The default implementation returns the result of calling
+ * <code>getService</code>, on the <code>BundleContext</code> object
+ * with which this <code>ServiceTracker</code> object was created, passing
+ * the specified <code>ServiceReference</code> object.
+ * <p>
+ * This method can be overridden in a subclass to customize the service
+ * object to be tracked for the service being added. In that case, take care
+ * not to rely on the default implementation of removedService that will
+ * unget the service.
+ *
+ * @param reference Reference to service being added to this
+ * <code>ServiceTracker</code> object.
+ * @return The service object to be tracked for the service added to this
+ * <code>ServiceTracker</code> object.
+ * @see ServiceTrackerCustomizer
+ */
+ public Object addingService(ServiceReference reference) {
+ return context.getService(reference);
+ }
+
+ /**
+ * Default implementation of the
+ * <code>ServiceTrackerCustomizer.modifiedService</code> method.
+ *
+ * <p>
+ * This method is only called when this <code>ServiceTracker</code> object
+ * has been constructed with a <code>null ServiceTrackerCustomizer</code>
+ * argument.
+ *
+ * The default implementation does nothing.
+ *
+ * @param reference Reference to modified service.
+ * @param service The service object for the modified service.
+ * @see ServiceTrackerCustomizer
+ */
+ public void modifiedService(ServiceReference reference, Object service) {
+ }
+
+ /**
+ * Default implementation of the
+ * <code>ServiceTrackerCustomizer.removedService</code> method.
+ *
+ * <p>
+ * This method is only called when this <code>ServiceTracker</code> object
+ * has been constructed with a <code>null ServiceTrackerCustomizer</code>
+ * argument.
+ *
+ * The default implementation calls <code>ungetService</code>, on the
+ * <code>BundleContext</code> object with which this
+ * <code>ServiceTracker</code> object was created, passing the specified
+ * <code>ServiceReference</code> object.
+ * <p>
+ * This method can be overridden in a subclass. If the default
+ * implementation of <code>addingService</code> method was used, this
+ * method must unget the service.
+ *
+ * @param reference Reference to removed service.
+ * @param service The service object for the removed service.
+ * @see ServiceTrackerCustomizer
+ */
+ public void removedService(ServiceReference reference, Object service) {
+ context.ungetService(reference);
+ }
+
+ /**
+ * Wait for at least one service to be tracked by this
+ * <code>ServiceTracker</code> object.
+ * <p>
+ * It is strongly recommended that <code>waitForService</code> is not used
+ * during the calling of the <code>BundleActivator</code> methods.
+ * <code>BundleActivator</code> methods are expected to complete in a
+ * short period of time.
+ *
+ * @param timeout time interval in milliseconds to wait. If zero, the method
+ * will wait indefinately.
+ * @return Returns the result of <code>getService()</code>.
+ * @throws InterruptedException If another thread has interrupted the
+ * current thread.
+ * @throws IllegalArgumentException If the value of timeout is negative.
+ */
+ public Object waitForService(long timeout) throws InterruptedException {
+ if (timeout < 0) {
+ throw new IllegalArgumentException("timeout value is negative"); //$NON-NLS-1$
+ }
+ Object object = getService();
+ while (object == null) {
+ Tracked tracked = this.tracked; /*
+ * use local var since we are not
+ * synchronized
+ */
+ if (tracked == null) { /* if ServiceTracker is not open */
+ return null;
+ }
+ synchronized (tracked) {
+ if (tracked.size() == 0) {
+ tracked.wait(timeout);
+ }
+ }
+ object = getService();
+ if (timeout > 0) {
+ return object;
+ }
+ }
+ return object;
+ }
+
+ /**
+ * Return an array of <code>ServiceReference</code> objects for all
+ * services being tracked by this <code>ServiceTracker</code> object.
+ *
+ * @return Array of <code>ServiceReference</code> objects or
+ * <code>null</code> if no service are being tracked.
+ */
+ public ServiceReference[] getServiceReferences() {
+ Tracked tracked = this.tracked; /*
+ * use local var since we are not
+ * synchronized
+ */
+ if (tracked == null) { /* if ServiceTracker is not open */
+ return null;
+ }
+ synchronized (tracked) {
+ int length = tracked.size();
+ if (length == 0) {
+ return null;
+ }
+ ServiceReference[] references = new ServiceReference[length];
+ Enumeration keys = tracked.keys();
+ for (int i = 0; i < length; i++) {
+ references[i] = (ServiceReference) keys.nextElement();
+ }
+ return references;
+ }
+ }
+
+ /**
+ * Returns a <code>ServiceReference</code> object for one of the services
+ * being tracked by this <code>ServiceTracker</code> object.
+ *
+ * <p>
+ * If multiple services are being tracked, the service with the highest
+ * ranking (as specified in its <code>service.ranking</code> property) is
+ * returned.
+ *
+ * <p>
+ * If there is a tie in ranking, the service with the lowest service ID (as
+ * specified in its <code>service.id</code> property); that is, the
+ * service that was registered first is returned.
+ * <p>
+ * This is the same algorithm used by
+ * <code>BundleContext.getServiceReference</code>.
+ *
+ * @return <code>ServiceReference</code> object or <code>null</code> if
+ * no service is being tracked.
+ * @since 1.1
+ */
+ public ServiceReference getServiceReference() {
+ ServiceReference reference = cachedReference;
+ if (reference != null) {
+ if (DEBUG) {
+ System.out
+ .println("ServiceTracker.getServiceReference[cached]: " + filter); //$NON-NLS-1$
+ }
+ return reference;
+ }
+ if (DEBUG) {
+ System.out.println("ServiceTracker.getServiceReference: " + filter); //$NON-NLS-1$
+ }
+ ServiceReference[] references = getServiceReferences();
+ int length = (references == null) ? 0 : references.length;
+ if (length == 0) /* if no service is being tracked */
+ {
+ return null;
+ }
+ int index = 0;
+ if (length > 1) /* if more than one service, select highest ranking */
+ {
+ int rankings[] = new int[length];
+ int count = 0;
+ int maxRanking = Integer.MIN_VALUE;
+ for (int i = 0; i < length; i++) {
+ Object property = references[i]
+ .getProperty(Constants.SERVICE_RANKING);
+ int ranking = (property instanceof Integer) ? ((Integer) property)
+ .intValue()
+ : 0;
+ rankings[i] = ranking;
+ if (ranking > maxRanking) {
+ index = i;
+ maxRanking = ranking;
+ count = 1;
+ }
+ else {
+ if (ranking == maxRanking) {
+ count++;
+ }
+ }
+ }
+ if (count > 1) /* if still more than one service, select lowest id */
+ {
+ long minId = Long.MAX_VALUE;
+ for (int i = 0; i < length; i++) {
+ if (rankings[i] == maxRanking) {
+ long id = ((Long) (references[i]
+ .getProperty(Constants.SERVICE_ID)))
+ .longValue();
+ if (id < minId) {
+ index = i;
+ minId = id;
+ }
+ }
+ }
+ }
+ }
+ return cachedReference = references[index];
+ }
+
+ /**
+ * Returns the service object for the specified
+ * <code>ServiceReference</code> object if the referenced service is being
+ * tracked by this <code>ServiceTracker</code> object.
+ *
+ * @param reference Reference to the desired service.
+ * @return Service object or <code>null</code> if the service referenced
+ * by the specified <code>ServiceReference</code> object is not
+ * being tracked.
+ */
+ public Object getService(ServiceReference reference) {
+ Tracked tracked = this.tracked; /*
+ * use local var since we are not
+ * synchronized
+ */
+ if (tracked == null) { /* if ServiceTracker is not open */
+ return null;
+ }
+ synchronized (tracked) {
+ return tracked.get(reference);
+ }
+ }
+
+ /**
+ * Return an array of service objects for all services being tracked by this
+ * <code>ServiceTracker</code> object.
+ *
+ * @return Array of service objects or <code>null</code> if no service are
+ * being tracked.
+ */
+ public Object[] getServices() {
+ Tracked tracked = this.tracked; /*
+ * use local var since we are not
+ * synchronized
+ */
+ if (tracked == null) { /* if ServiceTracker is not open */
+ return null;
+ }
+ synchronized (tracked) {
+ ServiceReference[] references = getServiceReferences();
+ int length = (references == null) ? 0 : references.length;
+ if (length == 0) {
+ return null;
+ }
+ Object[] objects = new Object[length];
+ for (int i = 0; i < length; i++) {
+ objects[i] = getService(references[i]);
+ }
+ return objects;
+ }
+ }
+
+ /**
+ * Returns a service object for one of the services being tracked by this
+ * <code>ServiceTracker</code> object.
+ *
+ * <p>
+ * If any services are being tracked, this method returns the result of
+ * calling <code>getService(getServiceReference())</code>.
+ *
+ * @return Service object or <code>null</code> if no service is being
+ * tracked.
+ */
+ public Object getService() {
+ Object service = cachedService;
+ if (service != null) {
+ if (DEBUG) {
+ System.out
+ .println("ServiceTracker.getService[cached]: " + filter); //$NON-NLS-1$
+ }
+ return service;
+ }
+ if (DEBUG) {
+ System.out.println("ServiceTracker.getService: " + filter); //$NON-NLS-1$
+ }
+ ServiceReference reference = getServiceReference();
+ if (reference == null) {
+ return null;
+ }
+ return cachedService = getService(reference);
+ }
+
+ /**
+ * Remove a service from this <code>ServiceTracker</code> object.
+ *
+ * The specified service will be removed from this
+ * <code>ServiceTracker</code> object. If the specified service was being
+ * tracked then the <code>ServiceTrackerCustomizer.removedService</code>
+ * method will be called for that service.
+ *
+ * @param reference Reference to the service to be removed.
+ */
+ public void remove(ServiceReference reference) {
+ Tracked tracked = this.tracked; /*
+ * use local var since we are not
+ * synchronized
+ */
+ if (tracked == null) { /* if ServiceTracker is not open */
+ return;
+ }
+ tracked.untrack(reference);
+ }
+
+ /**
+ * Return the number of services being tracked by this
+ * <code>ServiceTracker</code> object.
+ *
+ * @return Number of services being tracked.
+ */
+ public int size() {
+ Tracked tracked = this.tracked; /*
+ * use local var since we are not
+ * synchronized
+ */
+ if (tracked == null) { /* if ServiceTracker is not open */
+ return 0;
+ }
+ return tracked.size();
+ }
+
+ /**
+ * Returns the tracking count for this <code>ServiceTracker</code> object.
+ *
+ * The tracking count is initialized to 0 when this
+ * <code>ServiceTracker</code> object is opened. Every time a service is
+ * added, modified or removed from this <code>ServiceTracker</code> object
+ * the tracking count is incremented.
+ *
+ * <p>
+ * The tracking count can be used to determine if this
+ * <code>ServiceTracker</code> object has added, modified or removed a
+ * service by comparing a tracking count value previously collected with the
+ * current tracking count value. If the value has not changed, then no
+ * service has been added, modified or removed from this
+ * <code>ServiceTracker</code> object since the previous tracking count
+ * was collected.
+ *
+ * @since 1.2
+ * @return The tracking count for this <code>ServiceTracker</code> object
+ * or -1 if this <code>ServiceTracker</code> object is not open.
+ */
+ public int getTrackingCount() {
+ return trackingCount;
+ }
+
+ /**
+ * Called by the Tracked object whenever the set of tracked services is
+ * modified. Increments the tracking count and clears the cache.
+ *
+ * @GuardedBy tracked
+ */
+ /*
+ * This method must not be synchronized since it is called by Tracked while
+ * Tracked is synchronized. We don't want synchronization interactions
+ * between the ServiceListener thread and the user thread.
+ */
+ void modified() {
+ trackingCount++; /* increment modification count */
+ cachedReference = null; /* clear cached value */
+ cachedService = null; /* clear cached value */
+ if (DEBUG) {
+ System.out.println("ServiceTracker.modified: " + filter); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Inner class to track services. If a <code>ServiceTracker</code> object
+ * is reused (closed then reopened), then a new Tracked object is used. This
+ * class is a hashtable mapping <code>ServiceReference</code> object ->
+ * customized Object. This class is the <code>ServiceListener</code>
+ * object for the tracker. This class is used to synchronize access to the
+ * tracked services. This is not a public class. It is only for use by the
+ * implementation of the <code>ServiceTracker</code> class.
+ *
+ * @ThreadSafe
+ */
+ class Tracked extends Hashtable implements ServiceListener {
+ static final long serialVersionUID = -7420065199791006079L;
+ /**
+ * List of ServiceReferences in the process of being added. This is used
+ * to deal with nesting of ServiceEvents. Since ServiceEvents are
+ * synchronously delivered, ServiceEvents can be nested. For example,
+ * when processing the adding of a service and the customizer causes the
+ * service to be unregistered, notification to the nested call to
+ * untrack that the service was unregistered can be made to the track
+ * method.
+ *
+ * Since the ArrayList implementation is not synchronized, all access to
+ * this list must be protected by the same synchronized object for
+ * thread-safety.
+ *
+ * @GuardedBy this
+ */
+ private final ArrayList adding;
+
+ /**
+ * true if the tracked object is closed.
+ *
+ * This field is volatile because it is set by one thread and read by
+ * another.
+ */
+ private volatile boolean closed;
+
+ /**
+ * Initial list of ServiceReferences for the tracker. This is used to
+ * correctly process the initial services which could become
+ * unregistered before they are tracked. This is necessary since the
+ * initial set of tracked services are not "announced" by ServiceEvents
+ * and therefore the ServiceEvent for unregistration could be delivered
+ * before we track the service.
+ *
+ * A service must not be in both the initial and adding lists at the
+ * same time. A service must be moved from the initial list to the
+ * adding list "atomically" before we begin tracking it.
+ *
+ * Since the LinkedList implementation is not synchronized, all access
+ * to this list must be protected by the same synchronized object for
+ * thread-safety.
+ *
+ * @GuardedBy this
+ */
+ private final LinkedList initial;
+
+ /**
+ * Tracked constructor.
+ */
+ protected Tracked() {
+ super();
+ closed = false;
+ adding = new ArrayList(6);
+ initial = new LinkedList();
+ }
+
+ /**
+ * Set initial list of services into tracker before ServiceEvents begin
+ * to be received.
+ *
+ * This method must be called from ServiceTracker.open while
+ * synchronized on this object in the same synchronized block as the
+ * addServiceListener call.
+ *
+ * @param references The initial list of services to be tracked.
+ * @GuardedBy this
+ */
+ protected void setInitialServices(ServiceReference[] references) {
+ if (references == null) {
+ return;
+ }
+ int size = references.length;
+ for (int i = 0; i < size; i++) {
+ if (DEBUG) {
+ System.out
+ .println("ServiceTracker.Tracked.setInitialServices: " + references[i]); //$NON-NLS-1$
+ }
+ initial.add(references[i]);
+ }
+ }
+
+ /**
+ * Track the initial list of services. This is called after
+ * ServiceEvents can begin to be received.
+ *
+ * This method must be called from ServiceTracker.open while not
+ * synchronized on this object after the addServiceListener call.
+ *
+ */
+ protected void trackInitialServices() {
+ while (true) {
+ ServiceReference reference;
+ synchronized (this) {
+ if (initial.size() == 0) {
+ /*
+ * if there are no more inital services
+ */
+ return; /* we are done */
+ }
+ /*
+ * move the first service from the initial list to the
+ * adding list within this synchronized block.
+ */
+ reference = (ServiceReference) initial.removeFirst();
+ if (this.get(reference) != null) {
+ /* if we are already tracking this service */
+ if (DEBUG) {
+ System.out
+ .println("ServiceTracker.Tracked.trackInitialServices[already tracked]: " + reference); //$NON-NLS-1$
+ }
+ continue; /* skip this service */
+ }
+ if (adding.contains(reference)) {
+ /*
+ * if this service is already in the process of being
+ * added.
+ */
+ if (DEBUG) {
+ System.out
+ .println("ServiceTracker.Tracked.trackInitialServices[already adding]: " + reference); //$NON-NLS-1$
+ }
+ continue; /* skip this service */
+ }
+ adding.add(reference);
+ }
+ if (DEBUG) {
+ System.out
+ .println("ServiceTracker.Tracked.trackInitialServices: " + reference); //$NON-NLS-1$
+ }
+ trackAdding(reference); /*
+ * Begin tracking it. We call
+ * trackAdding since we have already put
+ * the reference in the adding list.
+ */
+ }
+ }
+
+ /**
+ * Called by the owning <code>ServiceTracker</code> object when it is
+ * closed.
+ */
+ protected void close() {
+ closed = true;
+ }
+
+ /**
+ * <code>ServiceListener</code> method for the
+ * <code>ServiceTracker</code> class. This method must NOT be
+ * synchronized to avoid deadlock potential.
+ *
+ * @param event <code>ServiceEvent</code> object from the framework.
+ */
+ public void serviceChanged(ServiceEvent event) {
+ /*
+ * Check if we had a delayed call (which could happen when we
+ * close).
+ */
+ if (closed) {
+ return;
+ }
+ ServiceReference reference = event.getServiceReference();
+ if (DEBUG) {
+ System.out
+ .println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: " + reference); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ switch (event.getType()) {
+ case ServiceEvent.REGISTERED :
+ case ServiceEvent.MODIFIED :
+ if (listenerFilter != null) { // constructor supplied
+ // filter
+ track(reference);
+ /*
+ * If the customizer throws an unchecked exception, it
+ * is safe to let it propagate
+ */
+ }
+ else { // user supplied filter
+ if (filter.match(reference)) {
+ track(reference);
+ /*
+ * If the customizer throws an unchecked exception,
+ * it is safe to let it propagate
+ */
+ }
+ else {
+ untrack(reference);
+ /*
+ * If the customizer throws an unchecked exception,
+ * it is safe to let it propagate
+ */
+ }
+ }
+ break;
+ case ServiceEvent.UNREGISTERING :
+ untrack(reference);
+ /*
+ * If the customizer throws an unchecked exception, it is
+ * safe to let it propagate
+ */
+ break;
+ }
+ }
+
+ /**
+ * Begin to track the referenced service.
+ *
+ * @param reference Reference to a service to be tracked.
+ */
+ private void track(ServiceReference reference) {
+ Object object;
+ synchronized (this) {
+ object = this.get(reference);
+ }
+ if (object != null) /* we are already tracking the service */
+ {
+ if (DEBUG) {
+ System.out
+ .println("ServiceTracker.Tracked.track[modified]: " + reference); //$NON-NLS-1$
+ }
+ synchronized (this) {
+ modified(); /* increment modification count */
+ }
+ /* Call customizer outside of synchronized region */
+ customizer.modifiedService(reference, object);
+ /*
+ * If the customizer throws an unchecked exception, it is safe
+ * to let it propagate
+ */
+ return;
+ }
+ synchronized (this) {
+ if (adding.contains(reference)) { /*
+ * if this service is
+ * already in the process of
+ * being added.
+ */
+ if (DEBUG) {
+ System.out
+ .println("ServiceTracker.Tracked.track[already adding]: " + reference); //$NON-NLS-1$
+ }
+ return;
+ }
+ adding.add(reference); /* mark this service is being added */
+ }
+
+ trackAdding(reference); /*
+ * call trackAdding now that we have put the
+ * reference in the adding list
+ */
+ }
+
+ /**
+ * Common logic to add a service to the tracker used by track and
+ * trackInitialServices. The specified reference must have been placed
+ * in the adding list before calling this method.
+ *
+ * @param reference Reference to a service to be tracked.
+ */
+ private void trackAdding(ServiceReference reference) {
+ if (DEBUG) {
+ System.out
+ .println("ServiceTracker.Tracked.trackAdding: " + reference); //$NON-NLS-1$
+ }
+ Object object = null;
+ boolean becameUntracked = false;
+ /* Call customizer outside of synchronized region */
+ try {
+ object = customizer.addingService(reference);
+ /*
+ * If the customizer throws an unchecked exception, it will
+ * propagate after the finally
+ */
+ }
+ finally {
+ boolean needToCallback = false;
+ synchronized (this) {
+ if (adding.remove(reference)) { /*
+ * if the service was not
+ * untracked during the
+ * customizer callback
+ */
+ if (object != null) {
+ this.put(reference, object);
+ modified(); /* increment modification count */
+ notifyAll(); /*
+ * notify any waiters in
+ * waitForService
+ */
+ // marrs: extra callback added, will be invoked after
+ // the synchronized block
+ needToCallback = true;
+ }
+ }
+ else {
+ becameUntracked = true;
+ }
+ }
+ if (needToCallback) {
+ customizer.addedService(reference, object);
+ }
+ }
+ /*
+ * The service became untracked during the customizer callback.
+ */
+ if (becameUntracked) {
+ if (DEBUG) {
+ System.out
+ .println("ServiceTracker.Tracked.trackAdding[removed]: " + reference); //$NON-NLS-1$
+ }
+ /* Call customizer outside of synchronized region */
+ customizer.removedService(reference, object);
+ /*
+ * If the customizer throws an unchecked exception, it is safe
+ * to let it propagate
+ */
+ }
+ }
+
+ /**
+ * Discontinue tracking the referenced service.
+ *
+ * @param reference Reference to the tracked service.
+ */
+ protected void untrack(ServiceReference reference) {
+ Object object;
+ synchronized (this) {
+ if (initial.remove(reference)) { /*
+ * if this service is
+ * already in the list of
+ * initial references to
+ * process
+ */
+ if (DEBUG) {
+ System.out
+ .println("ServiceTracker.Tracked.untrack[removed from initial]: " + reference); //$NON-NLS-1$
+ }
+ return; /*
+ * we have removed it from the list and it will not
+ * be processed
+ */
+ }
+
+ if (adding.remove(reference)) { /*
+ * if the service is in the
+ * process of being added
+ */
+ if (DEBUG) {
+ System.out
+ .println("ServiceTracker.Tracked.untrack[being added]: " + reference); //$NON-NLS-1$
+ }
+ return; /*
+ * in case the service is untracked while in the
+ * process of adding
+ */
+ }
+ object = this.remove(reference); /*
+ * must remove from tracker
+ * before calling customizer
+ * callback
+ */
+ if (object == null) { /* are we actually tracking the service */
+ return;
+ }
+ modified(); /* increment modification count */
+ }
+ if (DEBUG) {
+ System.out
+ .println("ServiceTracker.Tracked.untrack[removed]: " + reference); //$NON-NLS-1$
+ }
+ /* Call customizer outside of synchronized region */
+ customizer.removedService(reference, object);
+ /*
+ * If the customizer throws an unchecked exception, it is safe to
+ * let it propagate
+ */
+ }
+ }
+
+ /**
+ * Subclass of Tracked which implements the AllServiceListener interface.
+ * This class is used by the ServiceTracker if open is called with true.
+ *
+ * @since 1.3
+ * @ThreadSafe
+ */
+ class AllTracked extends Tracked implements AllServiceListener {
+ static final long serialVersionUID = 4050764875305137716L;
+
+ /**
+ * AllTracked constructor.
+ */
+ protected AllTracked() {
+ super();
+ }
+ }
+
+ public void addedService(ServiceReference ref, Object service) {
+ // do nothing
+ }
+}
diff --git a/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceTrackerCustomizer.java b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceTrackerCustomizer.java
new file mode 100644
index 0000000..44f7590
--- /dev/null
+++ b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/ServiceTrackerCustomizer.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * A modified version of a normal service tracker customizer. This one has an
+ * extra callback "addedservice" that is invoked after the service has been added
+ * to the tracker (and therefore is accessible through the tracker API).
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ServiceTrackerCustomizer {
+ public Object addingService(ServiceReference ref);
+ public void addedService(ServiceReference ref, Object service);
+ public void modifiedService(ServiceReference ref, Object service);
+ public void removedService(ServiceReference ref, Object service);
+}
diff --git a/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/State.java b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/State.java
new file mode 100644
index 0000000..bd960b6
--- /dev/null
+++ b/dependencymanager.temp/src/main/java/org/apache/felix/dependencymanager/State.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager;
+
+import java.util.List;
+
+/**
+ * Encapsulates the current state of the dependencies of a service. A state is
+ * basically an immutable value object.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public final class State {
+ private static final String[] STATES = { "?", "inactive", "waiting for required", "tracking optional" };
+ private static final int INACTIVE = 1;
+ private static final int WAITING_FOR_REQUIRED = 2;
+ private static final int TRACKING_OPTIONAL = 3;
+ private final List m_deps;
+ private final int m_state;
+ private String m_stringValue;
+
+ /**
+ * Creates a new state instance.
+ *
+ * @param deps the dependencies that determine the state
+ * @param isActive <code>true</code> if the service is active (started)
+ */
+ public State(List deps, boolean isActive) {
+ m_deps = deps;
+ // only bother calculating dependencies if we're active
+ if (isActive) {
+ boolean allRequiredAvailable = true;
+ for (int i = 0; i < deps.size(); i++) {
+ Dependency dep = (Dependency) deps.get(i);
+ if (dep.isRequired()) {
+ if (!dep.isAvailable()) {
+ allRequiredAvailable = false;
+ }
+ }
+ }
+ if (allRequiredAvailable) {
+ m_state = TRACKING_OPTIONAL;
+ }
+ else {
+ m_state = WAITING_FOR_REQUIRED;
+ }
+ }
+ else {
+ m_state = INACTIVE;
+ }
+ }
+
+ public boolean isInactive() {
+ return m_state == INACTIVE;
+ }
+
+ public boolean isWaitingForRequired() {
+ return m_state == WAITING_FOR_REQUIRED;
+ }
+
+ public boolean isTrackingOptional() {
+ return m_state == TRACKING_OPTIONAL;
+ }
+
+ public List getDependencies() {
+ return m_deps;
+ }
+
+ public synchronized String toString() {
+ if (m_stringValue == null) {
+ // we only need to determine this once, but we do it lazily
+ StringBuffer buf = new StringBuffer();
+ buf.append("State[" + STATES[m_state] + "|");
+ List deps = m_deps;
+ for (int i = 0; i < deps.size(); i++) {
+ Dependency dep = (Dependency) deps.get(i);
+ buf.append("(" + dep + (dep.isRequired() ? " R" : " O") + (dep.isAvailable() ? " +" : " -") + ")");
+ }
+ m_stringValue = buf.toString();
+ }
+ return m_stringValue;
+ }
+}
diff --git a/dependencymanager.temp/src/test/java/org/apache/felix/dependencymanager/test/ComponentLifeCycleTest.java b/dependencymanager.temp/src/test/java/org/apache/felix/dependencymanager/test/ComponentLifeCycleTest.java
new file mode 100644
index 0000000..f836184
--- /dev/null
+++ b/dependencymanager.temp/src/test/java/org/apache/felix/dependencymanager/test/ComponentLifeCycleTest.java
@@ -0,0 +1,98 @@
+package org.apache.felix.dependencymanager.test;
+
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.provision;
+
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.apache.felix.dependencymanager.Logger;
+import org.apache.felix.dependencymanager.Service;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.Configuration;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.framework.BundleContext;
+
+@RunWith( JUnit4TestRunner.class )
+public class ComponentLifeCycleTest {
+ @Configuration
+ public static Option[] configuration() {
+ return options(
+ provision(
+ mavenBundle().groupId("org.apache.felix").artifactId("org.osgi.compendium").versionAsInProject(),
+ mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.dependencymanager").versionAsInProject()
+ )
+ );
+ }
+
+ @Test
+ public void testComponentLifeCycleCallbacks(BundleContext context) {
+ DependencyManager m = new DependencyManager(context, new Logger(context));
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a simple service component
+ Service s = m.createService().setImplementation(new ComponentInstance(e));
+ // add it, and since it has no dependencies, it should be activated immediately
+ m.add(s);
+ // remove it so it gets destroyed
+ m.remove(s);
+ // ensure we executed all steps inside the component instance
+ e.step(6);
+ }
+
+ @Test
+ public void testCustomComponentLifeCycleCallbacks(BundleContext context) {
+ DependencyManager m = new DependencyManager(context, new Logger(context));
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a simple service component
+ Service s = m.createService().setImplementation(new CustomComponentInstance(e)).setCallbacks("a", "b", "c", "d");
+ // add it, and since it has no dependencies, it should be activated immediately
+ m.add(s);
+ // remove it so it gets destroyed
+ m.remove(s);
+ // ensure we executed all steps inside the component instance
+ e.step(6);
+ }
+}
+
+class ComponentInstance {
+ private final Ensure m_ensure;
+ public ComponentInstance(Ensure e) {
+ m_ensure = e;
+ m_ensure.step(1);
+ }
+ public void init() {
+ m_ensure.step(2);
+ }
+ public void start() {
+ m_ensure.step(3);
+ }
+ public void stop() {
+ m_ensure.step(4);
+ }
+ public void destroy() {
+ m_ensure.step(5);
+ }
+}
+
+class CustomComponentInstance {
+ private final Ensure m_ensure;
+ public CustomComponentInstance(Ensure e) {
+ m_ensure = e;
+ m_ensure.step(1);
+ }
+ public void a() {
+ m_ensure.step(2);
+ }
+ public void b() {
+ m_ensure.step(3);
+ }
+ public void c() {
+ m_ensure.step(4);
+ }
+ public void d() {
+ m_ensure.step(5);
+ }
+}
diff --git a/dependencymanager.temp/src/test/java/org/apache/felix/dependencymanager/test/Ensure.java b/dependencymanager.temp/src/test/java/org/apache/felix/dependencymanager/test/Ensure.java
new file mode 100644
index 0000000..c27e246
--- /dev/null
+++ b/dependencymanager.temp/src/test/java/org/apache/felix/dependencymanager/test/Ensure.java
@@ -0,0 +1,11 @@
+package org.apache.felix.dependencymanager.test;
+
+import junit.framework.Assert;
+
+public class Ensure {
+ int step = 1;
+ public synchronized void step(int nr) {
+ Assert.assertEquals(nr, step);
+ step++;
+ }
+}
diff --git a/dependencymanager.temp/src/test/java/org/apache/felix/dependencymanager/test/ServiceDependencyTest.java b/dependencymanager.temp/src/test/java/org/apache/felix/dependencymanager/test/ServiceDependencyTest.java
new file mode 100644
index 0000000..ddd5af6
--- /dev/null
+++ b/dependencymanager.temp/src/test/java/org/apache/felix/dependencymanager/test/ServiceDependencyTest.java
@@ -0,0 +1,76 @@
+package org.apache.felix.dependencymanager.test;
+
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.provision;
+
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.apache.felix.dependencymanager.Logger;
+import org.apache.felix.dependencymanager.Service;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.Configuration;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.framework.BundleContext;
+
+@RunWith( JUnit4TestRunner.class )
+public class ServiceDependencyTest {
+ @Configuration
+ public static Option[] configuration() {
+ return options(
+ provision(
+ mavenBundle().groupId("org.apache.felix").artifactId("org.osgi.compendium").versionAsInProject(),
+ mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.dependencymanager").versionAsInProject()
+ )
+ );
+ }
+
+ @Test
+ public void testServiceRegistrationAndConsumption(BundleContext context) {
+ DependencyManager m = new DependencyManager(context, new Logger(context));
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ Service sp = m.createService().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+ Service sc = m.createService().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true));
+ m.add(sp);
+ m.add(sc);
+ m.remove(sp);
+ m.remove(sc);
+ // ensure we executed all steps inside the component instance
+ e.step(4);
+ }
+}
+
+interface ServiceInterface {
+ public void invoke();
+}
+
+class ServiceProvider implements ServiceInterface {
+ private final Ensure m_ensure;
+ public ServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+ public void invoke() {
+ m_ensure.step(2);
+ }
+}
+
+class ServiceConsumer {
+ private volatile ServiceInterface m_service;
+ private final Ensure m_ensure;
+
+ public ServiceConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void start() {
+ m_ensure.step(1);
+ m_service.invoke();
+ }
+
+ public void stop() {
+ m_ensure.step(3);
+ }
+}