moved classes to subpackages, updated ServiceTracker to 4.2 spec, added BundleTracker and BundleDependency, ResourceDependency and some preliminary resource related classes (WIP)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@884327 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/core/pom.xml b/dependencymanager/core/pom.xml
index 2309519..90b08de 100644
--- a/dependencymanager/core/pom.xml
+++ b/dependencymanager/core/pom.xml
@@ -53,7 +53,7 @@
<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,org.apache.felix.dependencymanager.management,org.apache.felix.dependencymanager.tracker</Export-Package>
+ <Export-Package>org.apache.felix.dependencymanager,org.apache.felix.dependencymanager.management,org.apache.felix.dependencymanager.tracker,org.apache.felix.dependencymanager.impl,org.apache.felix.dependencymanager.dependencies,org.apache.felix.dependencymanager.resources</Export-Package>
<Import-Package>!org.apache.felix.dependencymanager,*</Import-Package>
</instructions>
</configuration>
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/Dependency.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/Dependency.java
index 64d241a..0c2a23a 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/Dependency.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/Dependency.java
@@ -49,6 +49,14 @@
public boolean isAvailable();
/**
+ * As soon as the instance is created, keep it around, even if this dependency
+ * goes away.
+ *
+ * @return <code>true</code> if the dependency is instance bound
+ */
+ public boolean isInstanceBound();
+
+ /**
* 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
@@ -56,7 +64,7 @@
*
* @param service the service that is associated with this dependency
*/
- public void start(Service service);
+ public void start(DependencyService service);
/**
* Stops tracking the dependency. This deactivates the tracking. If the
@@ -64,5 +72,5 @@
* <code>dependencyUnavaible()</code> before stopping itself to ensure
* that dependencies that aren't "active" are unavailable.
*/
- public void stop(Service service);
+ public void stop(DependencyService service);
}
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/DependencyActivatorBase.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/DependencyActivatorBase.java
index 88ffcf7..4cc085e 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/DependencyActivatorBase.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/DependencyActivatorBase.java
@@ -20,6 +20,11 @@
import java.util.List;
+import org.apache.felix.dependencymanager.dependencies.ConfigurationDependency;
+import org.apache.felix.dependencymanager.dependencies.ServiceDependency;
+import org.apache.felix.dependencymanager.dependencies.TemporalServiceDependency;
+import org.apache.felix.dependencymanager.impl.Logger;
+import org.apache.felix.dependencymanager.impl.ServiceImpl;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
@@ -95,7 +100,7 @@
* @return the new service
*/
public Service createService() {
- return new ServiceImpl(m_context, m_manager, m_logger);
+ return m_manager.createService();
}
/**
@@ -104,7 +109,7 @@
* @return the service dependency
*/
public ServiceDependency createServiceDependency() {
- return new ServiceDependency(m_context, m_logger);
+ return m_manager.createServiceDependency();
}
/**
@@ -114,7 +119,7 @@
* @return the service dependency
*/
public TemporalServiceDependency createTemporalServiceDependency() {
- return new TemporalServiceDependency(m_context, m_logger);
+ return m_manager.createTemporalServiceDependency();
}
/**
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/DependencyManager.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/DependencyManager.java
index cb1fe44..784519c 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/DependencyManager.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/DependencyManager.java
@@ -22,6 +22,11 @@
import java.util.Collections;
import java.util.List;
+import org.apache.felix.dependencymanager.dependencies.ConfigurationDependency;
+import org.apache.felix.dependencymanager.dependencies.ServiceDependency;
+import org.apache.felix.dependencymanager.dependencies.TemporalServiceDependency;
+import org.apache.felix.dependencymanager.impl.Logger;
+import org.apache.felix.dependencymanager.impl.ServiceImpl;
import org.osgi.framework.BundleContext;
/**
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/DependencyService.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/DependencyService.java
new file mode 100644
index 0000000..ed32430
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/DependencyService.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager;
+
+public interface DependencyService {
+ /**
+ * 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);
+
+ public Object getService(); // is also defined on the Service interface
+ public void initService(); // was an implementation method
+ public boolean isRegistered(); // impl method
+ public Object[] getCompositionInstances(); // impl method
+}
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/State.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/State.java
deleted file mode 100644
index bd960b6..0000000
--- a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/State.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/core/src/main/java/org/apache/felix/dependencymanager/dependencies/BundleDependency.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/BundleDependency.java
new file mode 100644
index 0000000..8d05ab6
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/BundleDependency.java
@@ -0,0 +1,409 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.dependencies;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Dictionary;
+
+import org.apache.felix.dependencymanager.Dependency;
+import org.apache.felix.dependencymanager.DependencyService;
+import org.apache.felix.dependencymanager.impl.Logger;
+import org.apache.felix.dependencymanager.impl.ServiceImpl;
+import org.apache.felix.dependencymanager.management.ServiceComponentDependency;
+import org.apache.felix.dependencymanager.tracker.BundleTracker;
+import org.apache.felix.dependencymanager.tracker.BundleTrackerCustomizer;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+
+public class BundleDependency implements Dependency, BundleTrackerCustomizer, ServiceComponentDependency {
+ private final BundleContext m_context;
+ private final Logger m_logger;
+ private boolean m_isStarted;
+ private BundleTracker m_tracker;
+ private int m_stateMask;
+ private boolean m_isAvailable;
+ private boolean m_isRequired;
+ private DependencyService m_service;
+
+ private Object m_callbackInstance;
+ private String m_callbackAdded;
+ private String m_callbackChanged;
+ private String m_callbackRemoved;
+ private boolean m_autoConfig;
+ private Bundle m_bundleInstance;
+ private Filter m_filter;
+ private long m_bundleId = -1;
+
+
+ public BundleDependency(BundleContext context, Logger logger) {
+ m_context = context;
+ m_logger = logger;
+ m_autoConfig = true;
+ }
+
+ public boolean isAvailable() {
+ return m_isAvailable;
+ }
+
+ public boolean isRequired() {
+ return m_isRequired;
+ }
+
+ public boolean isInstanceBound() {
+ return false; // TODO for now we are never bound to the service implementation instance
+ }
+
+ public void start(DependencyService service) {
+ synchronized (this) {
+ if (m_isStarted) {
+ throw new IllegalStateException("Dependency was already started." + getName());
+ }
+ m_service = service;
+ m_tracker = new BundleTracker(m_context, m_stateMask, this);
+ m_isStarted = true;
+ }
+ m_tracker.open();
+ System.out.println("START BD " + m_tracker);
+ }
+
+ public void stop(DependencyService service) {
+ synchronized (this) {
+ if (!m_isStarted) {
+ throw new IllegalStateException("Dependency was not started.");
+ }
+ m_isStarted = false;
+ }
+ m_tracker.close();
+ m_tracker = null;
+ }
+
+ public String getName() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public int getState() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public String getType() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public Object addingBundle(Bundle bundle, BundleEvent event) {
+ System.out.println("ADDING " + bundle + " " + event);
+ // if we don't like a bundle, we could reject it here by returning null
+ if (m_bundleId >= 0 && m_bundleId != bundle.getBundleId()) {
+ return null;
+ }
+ Filter filter = m_filter;
+ if (filter != null) {
+ Dictionary headers = bundle.getHeaders();
+ if (!m_filter.match(headers)) {
+ return null;
+ }
+ }
+ return bundle;
+ }
+
+ public void addedBundle(Bundle bundle, BundleEvent event, Object object) {
+ System.out.println("ADDED " + bundle + " " + event);
+ if (makeAvailable()) {
+ m_service.dependencyAvailable(this);
+ }
+ else {
+ m_service.dependencyChanged(this);
+ }
+ // try to invoke callback, if specified, but only for optional dependencies
+ // because callbacks for required dependencies are handled differently
+ if (!isRequired()) {
+ invokeAdded(bundle);
+ }
+ }
+
+ public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) {
+ System.out.println("MODIFIED " + bundle + " " + event);
+ m_service.dependencyChanged(this);
+ // only invoke the changed callback if the service itself is "active"
+ if (((ServiceImpl) m_service).isRegistered()) {
+ invokeChanged(bundle);
+ }
+ }
+
+ public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
+ System.out.println("REMOVED " + bundle + " " + event);
+ 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(bundle);
+ }
+ }
+
+ private synchronized boolean makeAvailable() {
+ if (!m_isAvailable) {
+ m_isAvailable = true;
+ return true;
+ }
+ return false;
+ }
+
+ private synchronized boolean makeUnavailable() {
+ if ((m_isAvailable) && (m_tracker.getTrackingCount() == 0)) {
+ m_isAvailable = false;
+ return true;
+ }
+ return false;
+ }
+
+ public void invokeAdded() {
+ invokeAdded(m_bundleInstance);
+ }
+
+ public void invokeAdded(Bundle serviceInstance) {
+ Object[] callbackInstances = getCallbackInstances();
+ if ((callbackInstances != null) && (m_callbackAdded != null)) {
+ invokeCallbackMethod(callbackInstances, m_callbackAdded, serviceInstance);
+ }
+ }
+
+ public void invokeChanged(Bundle serviceInstance) {
+ Object[] callbackInstances = getCallbackInstances();
+ if ((callbackInstances != null) && (m_callbackChanged != null)) {
+// if (m_reference == null) {
+// Thread.dumpStack();
+// }
+ invokeCallbackMethod(callbackInstances, m_callbackChanged, serviceInstance);
+ }
+ }
+
+
+ public void invokeRemoved() {
+ invokeRemoved(m_bundleInstance);
+ }
+
+ public void invokeRemoved(Bundle serviceInstance) {
+ Object[] callbackInstances = getCallbackInstances();
+ if ((callbackInstances != null) && (m_callbackRemoved != null)) {
+// if (m_reference == null) {
+// Thread.dumpStack();
+// }
+ invokeCallbackMethod(callbackInstances, m_callbackRemoved, serviceInstance);
+ }
+ }
+
+
+
+
+ private void invokeCallbackMethod(Object[] instances, String methodName, Object service) {
+ for (int i = 0; i < instances.length; i++) {
+ try {
+ invokeCallbackMethod(instances[i], methodName, 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, Object service) throws NoSuchMethodException {
+ Class currentClazz = instance.getClass();
+ boolean done = false;
+ while (!done && currentClazz != null) {
+ done = invokeMethod(instance, currentClazz, methodName,
+ new Class[][] {{Bundle.class}, {Object.class}, {}},
+ new Object[][] {{service}, {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;
+ }
+
+ private synchronized Object[] getCallbackInstances() {
+ Object[] callbackInstances = ((ServiceImpl) m_service).getCompositionInstances();
+ if (m_callbackInstance == null) {
+ return callbackInstances;
+ }
+ Object[] res = new Object[callbackInstances.length + 1];
+ res[0] = m_callbackInstance; //this could also be extended to an array...?
+ System.arraycopy(callbackInstances, 0, res, 1, callbackInstances.length);
+ return res;
+ }
+ /**
+ * 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 BundleDependency 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 BundleDependency 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 BundleDependency 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 BundleDependency setCallbacks(Object instance, String added, String changed, String removed) {
+ ensureNotActive();
+ // if at least one valid callback is specified, we turn off auto configuration
+ if (added != null || removed != null || changed != null) {
+ 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 BundleDependency setAutoConfig(boolean autoConfig) {
+ ensureNotActive();
+ m_autoConfig = autoConfig;
+ return this;
+ }
+
+ public synchronized BundleDependency setRequired(boolean required) {
+ ensureNotActive();
+ m_isRequired = required;
+ return this;
+ }
+
+ public BundleDependency setBundle(Bundle bundle) {
+ m_bundleId = bundle.getBundleId();
+ return this;
+ }
+
+ public BundleDependency setFilter(String filter) throws IllegalArgumentException {
+ if (filter != null) {
+ try {
+ m_filter = m_context.createFilter(filter);
+ }
+ catch (InvalidSyntaxException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ }
+ return this;
+ }
+
+ public BundleDependency setStateMask(int mask) {
+ m_stateMask = mask;
+ return this;
+ }
+
+ public synchronized boolean isAutoConfig() {
+ return m_autoConfig;
+ }
+
+ public Bundle getBundle() {
+ Bundle[] bundles = m_tracker.getBundles();
+ if (bundles != null && bundles.length > 0) {
+ return bundles[0];
+ }
+ return null;
+ }
+}
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/ConfigurationDependency.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ConfigurationDependency.java
similarity index 91%
rename from dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/ConfigurationDependency.java
rename to dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ConfigurationDependency.java
index 3299826..8806373 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/ConfigurationDependency.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ConfigurationDependency.java
@@ -16,12 +16,16 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.dependencymanager;
+package org.apache.felix.dependencymanager.dependencies;
+
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Dictionary;
import java.util.Properties;
+import org.apache.felix.dependencymanager.Dependency;
+import org.apache.felix.dependencymanager.DependencyService;
+import org.apache.felix.dependencymanager.impl.Logger;
import org.apache.felix.dependencymanager.management.ServiceComponentDependency;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
@@ -53,7 +57,7 @@
private BundleContext m_context;
private String m_pid;
private ServiceRegistration m_registration;
- private volatile Service m_service;
+ private volatile DependencyService m_service;
private Dictionary m_settings;
private boolean m_propagate;
private final Logger m_logger;
@@ -77,6 +81,11 @@
return true;
}
+ public boolean isInstanceBound() {
+ // for now, configuration dependencies never are
+ return false;
+ }
+
/**
* Returns <code>true</code> when configuration properties should be propagated
* as service properties.
@@ -89,14 +98,14 @@
return m_settings;
}
- public void start(Service service) {
+ public void start(DependencyService 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) {
+ public void stop(DependencyService service) {
m_registration.unregister();
m_service = null;
}
@@ -109,7 +118,7 @@
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();
+ m_service.initService(); /// <<< CHANGES THE STATE, WHEN IT SHOULD NOT (YET) DO THAT (we should not use m_serviceInstance to determine the state but use a flag var instead)
Object service = m_service.getService();
Dictionary oldSettings = null;
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ResourceDependency.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ResourceDependency.java
new file mode 100644
index 0000000..0e024d5
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ResourceDependency.java
@@ -0,0 +1,362 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.dependencies;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Properties;
+
+import org.apache.felix.dependencymanager.Dependency;
+import org.apache.felix.dependencymanager.DependencyService;
+import org.apache.felix.dependencymanager.impl.Logger;
+import org.apache.felix.dependencymanager.impl.ServiceImpl;
+import org.apache.felix.dependencymanager.resources.Resource;
+import org.apache.felix.dependencymanager.resources.ResourceHandler;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+public class ResourceDependency implements Dependency, ResourceHandler {
+ private volatile BundleContext m_context;
+ private volatile ServiceRegistration m_registration;
+ private long m_resourceCounter;
+
+ private Object m_callbackInstance;
+ private String m_callbackAdded;
+ private String m_callbackChanged;
+ private String m_callbackRemoved;
+ private boolean m_autoConfig;
+ private final Logger m_logger;
+ private String m_autoConfigInstance;
+ private DependencyService m_service;
+ private boolean m_isRequired;
+ private String m_resourceFilter;
+ private Resource m_resource;
+ private Resource m_trackedResource;
+
+
+ public ResourceDependency(BundleContext context, Logger logger) {
+ m_context = context;
+ m_logger = logger;
+ m_autoConfig = true;
+ }
+
+ public synchronized boolean isAvailable() {
+ return m_resourceCounter > 0;
+ }
+
+ public boolean isRequired() {
+ return m_isRequired;
+ }
+
+ public boolean isInstanceBound() {
+ return false; // TODO for now we are never bound to the service implementation instance
+ }
+
+ public void start(DependencyService service) {
+ m_service = service;
+ Properties props = new Properties();
+ // TODO create constant for this key
+ props.setProperty("filter", m_resourceFilter);
+ m_registration = m_context.registerService(ResourceHandler.class.getName(), this, props);
+
+ }
+
+ public void stop(DependencyService service) {
+ m_registration.unregister();
+ m_registration = null;
+ }
+
+ public void added(Resource resource) {
+ System.out.println("RD ADDED " + resource);
+ long counter;
+ synchronized (this) {
+ m_resourceCounter++;
+ counter = m_resourceCounter;
+ m_resource = resource; // TODO this really sucks as a way to track a single resource
+ }
+ if (counter == 1) {
+ m_service.dependencyAvailable(this);
+ }
+ else {
+ m_service.dependencyChanged(this);
+ }
+ // try to invoke callback, if specified, but only for optional dependencies
+ // because callbacks for required dependencies are handled differently
+ if (!isRequired()) {
+ invokeAdded(resource);
+ }
+ }
+
+ public void changed(Resource resource) {
+ invokeChanged(resource);
+ }
+
+ public void removed(Resource resource) {
+ long counter;
+ synchronized (this) {
+ m_resourceCounter--;
+ counter = m_resourceCounter;
+ }
+ if (counter == 0) {
+ 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(resource);
+ }
+ }
+
+ public void invokeAdded() {
+ // TODO fixme
+ //invokeAdded(m_bundleInstance);
+ }
+
+ public void invokeAdded(Resource serviceInstance) {
+ Object[] callbackInstances = getCallbackInstances();
+ if ((callbackInstances != null) && (m_callbackAdded != null)) {
+ invokeCallbackMethod(callbackInstances, m_callbackAdded, serviceInstance);
+ }
+ }
+
+ public void invokeChanged(Resource serviceInstance) {
+ Object[] callbackInstances = getCallbackInstances();
+ if ((callbackInstances != null) && (m_callbackChanged != null)) {
+// if (m_reference == null) {
+// Thread.dumpStack();
+// }
+ invokeCallbackMethod(callbackInstances, m_callbackChanged, serviceInstance);
+ }
+ }
+
+
+ public void invokeRemoved() {
+ // TODO fixme
+ //invokeRemoved(m_bundleInstance);
+ }
+
+ public void invokeRemoved(Resource serviceInstance) {
+ Object[] callbackInstances = getCallbackInstances();
+ if ((callbackInstances != null) && (m_callbackRemoved != null)) {
+// if (m_reference == null) {
+// Thread.dumpStack();
+// }
+ invokeCallbackMethod(callbackInstances, m_callbackRemoved, serviceInstance);
+ }
+ }
+
+
+
+
+ /**
+ * 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 ResourceDependency 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 ResourceDependency 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 ResourceDependency 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 ResourceDependency setCallbacks(Object instance, String added, String changed, String removed) {
+ ensureNotActive();
+ // if at least one valid callback is specified, we turn off auto configuration
+ if (added != null || removed != null || changed != null) {
+ setAutoConfig(false);
+ }
+ m_callbackInstance = instance;
+ m_callbackAdded = added;
+ m_callbackChanged = changed;
+ m_callbackRemoved = removed;
+ return this;
+ }
+
+ private void ensureNotActive() {
+ if (m_registration != null) {
+ throw new IllegalStateException("Cannot modify state while active.");
+ }
+ }
+
+ /**
+ * 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 ResourceDependency setAutoConfig(boolean autoConfig) {
+ ensureNotActive();
+ m_autoConfig = autoConfig;
+ 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 ResourceDependency setAutoConfig(String instanceName) {
+ ensureNotActive();
+ m_autoConfig = (instanceName != null);
+ m_autoConfigInstance = instanceName;
+ return this;
+ }
+
+ private void invokeCallbackMethod(Object[] instances, String methodName, Object service) {
+ for (int i = 0; i < instances.length; i++) {
+ try {
+ invokeCallbackMethod(instances[i], methodName, 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, Object service) throws NoSuchMethodException {
+ Class currentClazz = instance.getClass();
+ boolean done = false;
+ while (!done && currentClazz != null) {
+ done = invokeMethod(instance, currentClazz, methodName,
+ new Class[][] {{Resource.class}, {Object.class}, {}},
+ new Object[][] {{service}, {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;
+ }
+ private synchronized Object[] getCallbackInstances() {
+ Object[] callbackInstances = ((ServiceImpl) m_service).getCompositionInstances();
+ if (m_callbackInstance == null) {
+ return callbackInstances;
+ }
+ Object[] res = new Object[callbackInstances.length + 1];
+ res[0] = m_callbackInstance; //this could also be extended to an array...?
+ System.arraycopy(callbackInstances, 0, res, 1, callbackInstances.length);
+ return res;
+ }
+
+ public ResourceDependency setResource(Resource resource) {
+ m_trackedResource = resource;
+ return this;
+ }
+
+ public synchronized ResourceDependency setRequired(boolean required) {
+ ensureNotActive();
+ m_isRequired = required;
+ return this;
+ }
+
+ public ResourceDependency setFilter(String resourceFilter) {
+ m_resourceFilter = resourceFilter;
+ return this;
+ }
+ public synchronized boolean isAutoConfig() {
+ return m_autoConfig;
+ }
+
+ public Resource getResource() {
+ System.out.println("Fetching resource");
+ return m_resource;
+ }
+}
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/ServiceDependency.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ServiceDependency.java
similarity index 81%
rename from dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/ServiceDependency.java
rename to dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ServiceDependency.java
index 5ba1f7d..18671ca 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/ServiceDependency.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ServiceDependency.java
@@ -16,19 +16,25 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.dependencymanager;
+package org.apache.felix.dependencymanager.dependencies;
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.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
+import org.apache.felix.dependencymanager.Dependency;
+import org.apache.felix.dependencymanager.DependencyService;
+import org.apache.felix.dependencymanager.impl.DefaultNullObject;
+import org.apache.felix.dependencymanager.impl.Logger;
import org.apache.felix.dependencymanager.management.ServiceComponentDependency;
import org.apache.felix.dependencymanager.tracker.ServiceTracker;
import org.apache.felix.dependencymanager.tracker.ServiceTrackerCustomizer;
@@ -44,7 +50,7 @@
*/
public class ServiceDependency implements Dependency, ServiceTrackerCustomizer, ServiceComponentDependency {
private boolean m_isRequired;
- protected Service m_service;
+ protected List m_services = new ArrayList();
protected volatile ServiceTracker m_tracker;
protected BundleContext m_context;
private boolean m_isAvailable;
@@ -66,6 +72,7 @@
private boolean m_autoConfigInvoked;
private Object m_defaultImplementation;
private Object m_defaultImplementationInstance;
+ private boolean m_isInstanceBound;
private static final Comparator COMPARATOR = new Comparator() {
public int getRank(ServiceReference ref) {
@@ -188,6 +195,10 @@
public synchronized boolean isAutoConfig() {
return m_autoConfig;
}
+
+ public boolean isInstanceBound() {
+ return m_isInstanceBound;
+ }
public synchronized Object getService() {
Object service = null;
@@ -246,6 +257,45 @@
}
return service;
}
+
+ // TODO lots of duplication in lookupService()
+ public ServiceReference lookupServiceReference() {
+ ServiceReference service = null;
+ if (m_isStarted) {
+ service = m_tracker.getServiceReference();
+ }
+ 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 = ref;
+ }
+ }
+ else {
+ throw new IllegalStateException("Could not lookup dependency, no service name specified.");
+ }
+ }
+ return service;
+ }
private Object getNullObject() {
if (m_nullObject == null) {
@@ -284,45 +334,52 @@
return m_trackedServiceName;
}
- public void start(Service service) {
+ public void start(DependencyService service) {
+ boolean needsStarting = false;
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);
+ m_services.add(service);
+ if (!m_isStarted) {
+ 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.");
+ }
}
- 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 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 {
- m_tracker = new ServiceTracker(m_context, m_trackedServiceName.getName(), this);
+ throw new IllegalStateException("Could not create tracker for dependency, no service name specified.");
}
+ m_isStarted = true;
+ needsStarting = true;
}
- else {
- throw new IllegalStateException("Could not create tracker for dependency, no service name specified.");
- }
- m_isStarted = true;
}
- m_tracker.open();
+ if (needsStarting) {
+ m_tracker.open();
+ }
}
- public void stop(Service service) {
+ public void stop(DependencyService service) {
+ boolean needsStopping = false;
synchronized (this) {
- if (!m_isStarted) {
- throw new IllegalStateException("Service dependency was not started.");
+ m_services.remove(service);
+ if (m_services.size() == 0) {
+ m_isStarted = false;
+ needsStopping = true;
}
- m_isStarted = false;
}
- m_tracker.close();
- m_tracker = null;
+ if (needsStopping) {
+ m_tracker.close();
+ m_tracker = null;
+ }
}
public Object addingService(ServiceReference ref) {
@@ -339,26 +396,26 @@
}
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);
+ boolean makeAvailable = makeAvailable();
+
+ Object[] services = m_services.toArray();
+ for (int i = 0; i < services.length; i++) {
+ DependencyService ds = (DependencyService) services[i];
+ if (makeAvailable) {
+ ds.dependencyAvailable(this);
+ if (!isRequired()) {
+ invokeAdded(ds, ref, service);
+ }
}
- }
- else {
- m_service.dependencyChanged(this);
- invokeAdded(ref, service);
+ else {
+ ds.dependencyChanged(this);
+ invokeAdded(ds, ref, service);
+ }
}
}
- public void invokeAdded() {
- invokeAdded(m_reference, m_serviceInstance);
- }
-
- public void invokeAdded(ServiceReference reference, Object serviceInstance) {
- Object[] callbackInstances = getCallbackInstances();
+ public void invokeAdded(DependencyService dependencyService, ServiceReference reference, Object serviceInstance) {
+ Object[] callbackInstances = getCallbackInstances(dependencyService);
if ((callbackInstances != null) && (m_callbackAdded != null)) {
invokeCallbackMethod(callbackInstances, m_callbackAdded, reference, serviceInstance);
}
@@ -367,49 +424,53 @@
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);
+
+ Object[] services = m_services.toArray();
+ for (int i = 0; i < services.length; i++) {
+ DependencyService ds = (DependencyService) services[i];
+ ds.dependencyChanged(this);
+ if (ds.isRegistered()) {
+ invokeChanged(ds, ref, service);
+ }
}
}
- public void invokeChanged(ServiceReference reference, Object serviceInstance) {
- Object[] callbackInstances = getCallbackInstances();
+ public void invokeChanged(DependencyService dependencyService, ServiceReference reference, Object serviceInstance) {
+ Object[] callbackInstances = getCallbackInstances(dependencyService);
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);
+ boolean makeUnavailable = makeUnavailable();
+
+ Object[] services = m_services.toArray();
+ for (int i = 0; i < services.length; i++) {
+ DependencyService ds = (DependencyService) services[i];
+ if (makeUnavailable) {
+ ds.dependencyUnavailable(this);
+ if (!isRequired()) {
+ invokeRemoved(ds, ref, service);
+ }
+ }
+ else {
+ ds.dependencyChanged(this);
+ invokeRemoved(ds, 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();
+ public void invokeRemoved(DependencyService dependencyService, ServiceReference reference, Object serviceInstance) {
+ Object[] callbackInstances = getCallbackInstances(dependencyService);
if ((callbackInstances != null) && (m_callbackRemoved != null)) {
invokeCallbackMethod(callbackInstances, m_callbackRemoved, reference, serviceInstance);
}
}
-
+
protected synchronized boolean makeAvailable() {
if (!m_isAvailable) {
m_isAvailable = true;
@@ -426,8 +487,13 @@
return false;
}
- private synchronized Object[] getCallbackInstances() {
- return m_callbackInstance != null ? new Object[] { m_callbackInstance } : ((ServiceImpl) m_service).getCompositionInstances();
+ private synchronized Object[] getCallbackInstances(DependencyService dependencyService) {
+ if (m_callbackInstance == null) {
+ return dependencyService.getCompositionInstances();
+ }
+ else {
+ return new Object[] { m_callbackInstance };
+ }
}
private void invokeCallbackMethod(Object[] instances, String methodName, ServiceReference reference, Object service) {
@@ -588,6 +654,11 @@
m_isRequired = required;
return this;
}
+
+ public ServiceDependency setInstanceBound(boolean isInstanceBound) {
+ m_isInstanceBound = isInstanceBound;
+ return this;
+ }
/**
* Sets auto configuration for this service. Auto configuration allows the
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/TemporalServiceDependency.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/TemporalServiceDependency.java
similarity index 91%
rename from dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/TemporalServiceDependency.java
rename to dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/TemporalServiceDependency.java
index d842503..a531e91 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/TemporalServiceDependency.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/TemporalServiceDependency.java
@@ -9,12 +9,15 @@
* or implied. See the License for the specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.dependencymanager;
+package org.apache.felix.dependencymanager.dependencies;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
+import org.apache.felix.dependencymanager.DependencyActivatorBase;
+import org.apache.felix.dependencymanager.DependencyService;
+import org.apache.felix.dependencymanager.impl.Logger;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
@@ -110,12 +113,18 @@
* The ServiceTracker calls us here in order to inform about a service arrival.
*/
public synchronized void addedService(ServiceReference ref, Object service) {
- if (makeAvailable()) {
- // So far, our dependency was not satisfied: wrap the service behind our proxy.
+ boolean makeAvailable = makeAvailable();
+ if (makeAvailable) {
m_serviceInstance = Proxy.newProxyInstance(m_trackedServiceName.getClassLoader(), new Class[] { m_trackedServiceName }, this);
- m_service.dependencyAvailable(this); // will invoke "added" callbacks, if any.
}
- else {
+ Object[] services = m_services.toArray();
+ for (int i = 0; i < services.length; i++) {
+ DependencyService ds = (DependencyService) services[i];
+ if (makeAvailable) {
+ ds.dependencyAvailable(this);
+ }
+ }
+ if (!makeAvailable) {
notifyAll();
}
}
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/DefaultNullObject.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/DefaultNullObject.java
similarity index 98%
rename from dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/DefaultNullObject.java
rename to dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/DefaultNullObject.java
index 79bb240..910f8cd 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/DefaultNullObject.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/DefaultNullObject.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.dependencymanager;
+package org.apache.felix.dependencymanager.impl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/Logger.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/Logger.java
similarity index 99%
rename from dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/Logger.java
rename to dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/Logger.java
index ab85366..4d38804 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/Logger.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/Logger.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.dependencymanager;
+package org.apache.felix.dependencymanager.impl;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/SerialExecutor.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/SerialExecutor.java
similarity index 97%
rename from dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/SerialExecutor.java
rename to dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/SerialExecutor.java
index 72063c3..addcc95 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/SerialExecutor.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/SerialExecutor.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.dependencymanager;
+package org.apache.felix.dependencymanager.impl;
import java.util.LinkedList;
import java.util.NoSuchElementException;
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/ServiceImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/ServiceImpl.java
similarity index 71%
rename from dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/ServiceImpl.java
rename to dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/ServiceImpl.java
index e61bce3..d853fcf 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/ServiceImpl.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/ServiceImpl.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.dependencymanager;
+package org.apache.felix.dependencymanager.impl;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
@@ -33,8 +33,19 @@
import java.util.Map;
import java.util.Properties;
+import org.apache.felix.dependencymanager.Dependency;
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.apache.felix.dependencymanager.DependencyService;
+import org.apache.felix.dependencymanager.Service;
+import org.apache.felix.dependencymanager.ServiceStateListener;
+import org.apache.felix.dependencymanager.dependencies.BundleDependency;
+import org.apache.felix.dependencymanager.dependencies.ConfigurationDependency;
+import org.apache.felix.dependencymanager.dependencies.ResourceDependency;
+import org.apache.felix.dependencymanager.dependencies.ServiceDependency;
import org.apache.felix.dependencymanager.management.ServiceComponent;
import org.apache.felix.dependencymanager.management.ServiceComponentDependency;
+import org.apache.felix.dependencymanager.resources.Resource;
+import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
@@ -43,11 +54,12 @@
*
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
-public class ServiceImpl implements Service, ServiceComponent {
+public class ServiceImpl implements Service, DependencyService, 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 Object SYNC = new Object();
private final BundleContext m_context;
private final DependencyManager m_manager;
@@ -71,6 +83,8 @@
// runtime state (changes because of state changes)
private Object m_serviceInstance;
private ServiceRegistration m_registration;
+ private boolean m_isBound;
+ private boolean m_isInstantiated;
// service state listeners
private final List m_stateListeners = new ArrayList();
@@ -95,7 +109,7 @@
public ServiceImpl(BundleContext context, DependencyManager manager, Logger logger) {
m_logger = logger;
- m_state = new State((List) m_dependencies.clone(), false);
+ m_state = new State((List) m_dependencies.clone(), false, false, false);
m_context = context;
m_manager = manager;
m_callbackInit = "init";
@@ -106,62 +120,134 @@
m_autoConfig.put(BundleContext.class, Boolean.TRUE);
m_autoConfig.put(ServiceRegistration.class, Boolean.TRUE);
m_autoConfig.put(DependencyManager.class, Boolean.TRUE);
+ m_autoConfig.put(Service.class, Boolean.TRUE);
}
+ private void calculateStateChanges() {
+ // see if any of the things we did caused a further change of state
+ State oldState, newState;
+ synchronized (m_dependencies) {
+ oldState = m_state;
+ newState = new State((List) m_dependencies.clone(), !oldState.isInactive(), m_isInstantiated, m_isBound);
+ m_state = newState;
+ }
+ calculateStateChanges(oldState, newState);
+ }
+
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();
+ 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.isTrackingOptional()) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ activateService(newState);
+ }});
+ }
+ if ((oldState.isWaitingForRequired()) && newState.isInactive()) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ stopTrackingRequired(oldState);
+ }});
+ }
+ if (oldState.isTrackingOptional() && newState.isWaitingForRequiredInstantiated()) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ // TODO as far as I can see there is nothing left to do here
+ }});
+ }
+ if (oldState.isTrackingOptional() && newState.isWaitingForRequired()) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ deactivateService(oldState);
+ }});
+ }
+ if (oldState.isTrackingOptional() && newState.isBound()) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ bindService(oldState);
+ }});
+ }
+ if (oldState.isTrackingOptional() && newState.isInactive()) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ deactivateService(oldState);
+ stopTrackingRequired(oldState);
+ }});
+ }
+ if (oldState.isWaitingForRequiredInstantiated() && newState.isWaitingForRequired()) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ deactivateService(oldState);
+ }});
+ }
+ if (oldState.isWaitingForRequiredInstantiated() && newState.isInactive()) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ deactivateService(oldState);
+ stopTrackingRequired(oldState);
+ }});
+ }
+ if (oldState.isWaitingForRequiredInstantiated() && newState.isBound()) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ bindService(oldState);
+ }});
+ }
+ if (oldState.isBound() && newState.isWaitingForRequiredInstantiated()) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ unbindService(oldState);
+ }});
+ }
+ if (oldState.isBound() && newState.isWaitingForRequired()) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ unbindService(oldState);
+ deactivateService(oldState);
+ }});
+ }
+ if (oldState.isBound() && newState.isInactive()) {
+ m_executor.enqueue(new Runnable() {
+ public void run() {
+ unbindService(oldState);
+ 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())) {
+ if (oldState.isAllRequiredAvailable() || (oldState.isWaitingForRequired() && dependency.isRequired())) {
dependency.start(this);
}
synchronized (m_dependencies) {
- newState = new State((List) m_dependencies.clone(), !oldState.isInactive());
+ newState = new State((List) m_dependencies.clone(), !oldState.isInactive(), m_isInstantiated, m_isBound);
m_state = newState;
- calculateStateChanges(oldState, newState);
+ }
+ calculateStateChanges(oldState, newState);
+ return this;
+ }
+
+ public Service add(List dependencies) {
+ // TODO review if this can be done more smartly
+ for (int i = 0; i < dependencies.size(); i++) {
+ add((Dependency) dependencies.get(i));
}
return this;
}
@@ -172,11 +258,11 @@
oldState = m_state;
m_dependencies.remove(dependency);
}
- if (oldState.isTrackingOptional() || (oldState.isWaitingForRequired() && dependency.isRequired())) {
+ if (oldState.isAllRequiredAvailable() || (oldState.isWaitingForRequired() && dependency.isRequired())) {
dependency.stop(this);
}
synchronized (m_dependencies) {
- newState = new State((List) m_dependencies.clone(), !oldState.isInactive());
+ newState = new State((List) m_dependencies.clone(), !oldState.isInactive(), m_isInstantiated, m_isBound);
m_state = newState;
}
calculateStateChanges(oldState, newState);
@@ -201,11 +287,11 @@
State oldState, newState;
synchronized (m_dependencies) {
oldState = m_state;
- newState = new State((List) m_dependencies.clone(), !oldState.isInactive());
+ newState = new State((List) m_dependencies.clone(), !oldState.isInactive(), m_isInstantiated, m_isBound);
m_state = newState;
}
calculateStateChanges(oldState, newState);
- if (newState.isTrackingOptional()) {
+ if (newState.isAllRequiredAvailable()) {
m_executor.enqueue(new Runnable() {
public void run() {
updateInstance(dependency);
@@ -220,7 +306,7 @@
synchronized (m_dependencies) {
state = m_state;
}
- if (state.isTrackingOptional()) {
+ if (state.isAllRequiredAvailable()) {
m_executor.enqueue(new Runnable() {
public void run() {
updateInstance(dependency);
@@ -234,11 +320,11 @@
State oldState, newState;
synchronized (m_dependencies) {
oldState = m_state;
- newState = new State((List) m_dependencies.clone(), !oldState.isInactive());
+ newState = new State((List) m_dependencies.clone(), !oldState.isInactive(), m_isInstantiated, m_isBound);
m_state = newState;
}
calculateStateChanges(oldState, newState);
- if (newState.isTrackingOptional()) {
+ if (newState.isAllRequiredAvailable()) {
m_executor.enqueue(new Runnable() {
public void run() {
updateInstance(dependency);
@@ -253,7 +339,7 @@
State oldState, newState;
synchronized (m_dependencies) {
oldState = m_state;
- newState = new State((List) m_dependencies.clone(), true);
+ newState = new State((List) m_dependencies.clone(), true, m_isInstantiated, m_isBound);
m_state = newState;
}
calculateStateChanges(oldState, newState);
@@ -263,7 +349,7 @@
State oldState, newState;
synchronized (m_dependencies) {
oldState = m_state;
- newState = new State((List) m_dependencies.clone(), false);
+ newState = new State((List) m_dependencies.clone(), false, m_isInstantiated, m_isBound);
m_state = newState;
}
calculateStateChanges(oldState, newState);
@@ -350,7 +436,7 @@
synchronized (m_dependencies) {
state = m_state;
}
- if (state.isTrackingOptional()) {
+ if (state.isAllRequiredAvailable()) {
listener.starting(this);
listener.started(this);
}
@@ -362,7 +448,7 @@
}
}
- void removeStateListeners() {
+ public void removeStateListeners() {
synchronized (m_stateListeners) {
m_stateListeners.clear();
}
@@ -422,21 +508,31 @@
}
}
- private void activateService(State state) {
- String init, start;
- synchronized (this) {
- init = m_callbackInit;
- start = m_callbackStart;
- }
+ private void activateService(State state) {
+ String init;
+ synchronized (this) {
+ init = m_callbackInit;
+ }
// 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);
+ // then we invoke the init callback so the service can further initialize
+ // itself
+ invoke(init);
+ // flag that our instance has been created
+ m_isInstantiated = true;
+ // see if any of this caused further state changes
+ calculateStateChanges();
+ }
+
+ private void bindService(State state) {
+ String start;
+ synchronized (this) {
+ start = m_callbackStart;
+ }
// inform the state listeners we're starting
stateListenersStarting();
// invoke the start callback, since we're now ready to be used
@@ -448,13 +544,12 @@
// inform the state listeners we've started
stateListenersStarted();
}
-
- private void deactivateService(State state) {
- String stop, destroy;
- synchronized (this) {
- stop = m_callbackStop;
- destroy = m_callbackDestroy;
- }
+
+ private void unbindService(State state) {
+ String stop;
+ synchronized (this) {
+ stop = m_callbackStop;
+ }
// service deactivation logic, first inform the state listeners
// we're stopping
stateListenersStopping();
@@ -466,12 +561,21 @@
invoke(stop);
// inform the state listeners we've stopped
stateListenersStopped();
+ }
+
+ private void deactivateService(State state) {
+ String destroy;
+ synchronized (this) {
+ destroy = m_callbackDestroy;
+ }
// invoke the destroy callback
invoke(destroy);
// destroy the service instance
destroyService(state);
+ // flag that our instance was destroyed
+ m_isInstantiated = false;
}
-
+
private void invoke(String name) {
if (name != null) {
// invoke method if it exists
@@ -549,7 +653,7 @@
return clazz.newInstance();
}
- void initService() {
+ public void initService() {
if (m_serviceInstance == null) {
if (m_implementation instanceof Class) {
// instantiate
@@ -611,6 +715,9 @@
if (((Boolean) m_autoConfig.get(DependencyManager.class)).booleanValue()) {
configureImplementation(DependencyManager.class, m_manager, (String) m_autoConfigInstance.get(DependencyManager.class));
}
+ if (((Boolean) m_autoConfig.get(Service.class)).booleanValue()) {
+ configureImplementation(Service.class, this, (String) m_autoConfigInstance.get(Service.class));
+ }
}
}
@@ -665,6 +772,7 @@
wrapper.setIllegalState();
}
}
+ m_isBound = true;
}
private Dictionary calculateServiceProperties() {
@@ -700,6 +808,7 @@
}
private void unregisterService() {
+ m_isBound = false;
if (m_serviceName != null) {
m_registration.unregister();
configureImplementation(ServiceRegistration.class, NULL_REGISTRATION);
@@ -718,11 +827,25 @@
else if (dependency instanceof ConfigurationDependency) {
ConfigurationDependency cd = (ConfigurationDependency) dependency;
if (cd.isPropagated()) {
- // change service properties accordingly
- Dictionary props = calculateServiceProperties();
- m_registration.setProperties(props);
+ // change service properties accordingly, but only if the service was already registered
+ if (m_registration != null) {
+ Dictionary props = calculateServiceProperties();
+ m_registration.setProperties(props);
+ }
}
}
+ else if (dependency instanceof BundleDependency) {
+ BundleDependency bd = (BundleDependency) dependency;
+ if (bd.isAutoConfig()) {
+ configureImplementation(Bundle.class, bd.getBundle()); // TODO support AutoConfigName
+ }
+ }
+ else if (dependency instanceof ResourceDependency) {
+ ResourceDependency rd = (ResourceDependency) dependency;
+ if (rd.isAutoConfig()) {
+ configureImplementation(Resource.class, rd.getResource()); // TODO support AutoConfigName
+ }
+ }
}
/**
@@ -747,7 +870,7 @@
try {
fields[j].setAccessible(true);
// synchronized makes sure the field is actually written to immediately
- synchronized (new Object()) {
+ synchronized (SYNC) {
fields[j].set(serviceInstance, instance);
}
}
@@ -764,30 +887,30 @@
}
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;
+ 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) {
@@ -812,7 +935,41 @@
}
// for required dependencies, we invoke any callbacks here
if (sd.isRequired()) {
- sd.invokeAdded();
+ sd.invokeAdded(this, sd.lookupServiceReference(), sd.lookupService());
+ }
+ }
+ else if (dependency instanceof BundleDependency) {
+ BundleDependency bd = (BundleDependency) dependency;
+ if (bd.isAutoConfig()) {
+ if (bd.isRequired()) {
+ configureImplementation(Bundle.class, bd.getBundle()); // TODO AutoConfigName support
+ }
+ 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
+ // TODO !!! configureImplementation(sd.getInterface(), sd.lookupService(), sd.getAutoConfigName());
+ }
+ }
+ // for required dependencies, we invoke any callbacks here
+ if (bd.isRequired()) {
+ bd.invokeAdded();
+ }
+ }
+ else if (dependency instanceof ResourceDependency) {
+ ResourceDependency bd = (ResourceDependency) dependency;
+ if (bd.isAutoConfig()) {
+ if (bd.isRequired()) {
+ configureImplementation(Resource.class, bd.getResource()); // TODO AutoConfigName support
+ }
+ 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
+ // TODO !!! configureImplementation(sd.getInterface(), sd.lookupService(), sd.getAutoConfigName());
+ }
+ }
+ // for required dependencies, we invoke any callbacks here
+ if (bd.isRequired()) {
+ bd.invokeAdded();
}
}
}
@@ -826,7 +983,7 @@
ServiceDependency sd = (ServiceDependency) dependency;
// for required dependencies, we invoke any callbacks here
if (sd.isRequired()) {
- sd.invokeRemoved();
+ sd.invokeRemoved(this, sd.lookupServiceReference(), sd.lookupService());
}
}
}
@@ -841,12 +998,13 @@
throw new IllegalStateException("Cannot modify state while active.");
}
}
- boolean isRegistered() {
+
+ public boolean isRegistered() {
State state;
synchronized (m_dependencies) {
state = m_state;
}
- return (state.isTrackingOptional());
+ return (state.isAllRequiredAvailable());
}
// ServiceComponent interface
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/ServiceRegistrationImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/ServiceRegistrationImpl.java
similarity index 98%
rename from dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/ServiceRegistrationImpl.java
rename to dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/ServiceRegistrationImpl.java
index 0ff8473..95087b0 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/ServiceRegistrationImpl.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/ServiceRegistrationImpl.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.dependencymanager;
+package org.apache.felix.dependencymanager.impl;
import java.util.Dictionary;
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/State.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/State.java
new file mode 100644
index 0000000..2ef448d
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/State.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager.impl;
+
+import java.util.List;
+
+import org.apache.felix.dependencymanager.Dependency;
+
+/**
+ * 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", "bound", "waiting for required (instantiated)" };
+ private static final int INACTIVE = 1;
+ private static final int WAITING_FOR_REQUIRED = 2;
+ private static final int TRACKING_OPTIONAL = 3;
+ private static final int BOUND = 4;
+ private static final int WAITING_FOR_REQUIRED_INSTANTIATED = 5;
+ 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, boolean isInstantiated, boolean isBound /* unused? */) {
+ m_deps = deps;
+ // only bother calculating dependencies if we're active
+ if (isActive) {
+ boolean allRequiredAvailable = true;
+ boolean keepInstanceAround = isInstantiated;
+ for (int i = 0; i < deps.size(); i++) {
+ Dependency dep = (Dependency) deps.get(i);
+ if (dep.isRequired()) {
+ if (!dep.isAvailable()) {
+ allRequiredAvailable = false;
+ if (!dep.isInstanceBound()) {
+ keepInstanceAround = false;
+ }
+ }
+ }
+ }
+ if (allRequiredAvailable) {
+ if (isInstantiated) {
+ m_state = BOUND;
+ }
+ else {
+ m_state = TRACKING_OPTIONAL;
+ }
+ }
+ else {
+ if (keepInstanceAround) {
+ m_state = WAITING_FOR_REQUIRED_INSTANTIATED;
+ }
+ 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 boolean isBound() {
+ return m_state == BOUND;
+ }
+
+ public boolean isAllRequiredAvailable() {
+ return isTrackingOptional() || isBound();
+ }
+
+ public boolean isWaitingForRequiredInstantiated() {
+ return m_state == WAITING_FOR_REQUIRED_INSTANTIATED;
+ }
+
+ 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/core/src/main/java/org/apache/felix/dependencymanager/resources/BundleResourceRepository.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/resources/BundleResourceRepository.java
new file mode 100644
index 0000000..3b8e6fb
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/resources/BundleResourceRepository.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager.resources;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Properties;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+public class BundleResourceRepository {
+
+ private final Bundle bundle;
+
+ public BundleResourceRepository(Bundle bundle) {
+ this.bundle = bundle;
+ }
+
+ public synchronized void addHandler(ServiceReference ref, ResourceHandler handler) {
+
+ String filter = (String) ref.getProperty("filter"); // "(&(repository=a)(path=b)(name=*.xml))"
+
+ Filter filterObject = null;
+
+ try {
+ filterObject = FrameworkUtil.createFilter(filter);
+ } catch (InvalidSyntaxException e) {
+ e.printStackTrace();
+ return;
+ }
+
+ Enumeration entries = bundle.findEntries("/", null, true);
+ while (entries.hasMoreElements()) {
+ URL entry = (URL) entries.nextElement();
+
+ Properties props = new Properties();
+ props.setProperty(Resource.REPOSITORY, bundle.getSymbolicName() + "_" + bundle.getVersion());
+ props.setProperty(Resource.PATH, entry.getPath());
+ props.setProperty(Resource.NAME, entry.getFile());
+
+ if (filterObject.match(props))
+ handler.added(new EntryResource(entry));
+
+ }
+ }
+
+ public synchronized void removeHandler(ServiceReference ref, ResourceHandler handler) {
+
+ String filter = (String) ref.getProperty("filter"); // "(&(repository=a)(path=b)(name=*.xml))"
+
+ Filter filterObject = null;
+
+ try {
+ filterObject = FrameworkUtil.createFilter(filter);
+ } catch (InvalidSyntaxException e) {
+ e.printStackTrace();
+ return;
+ }
+
+ Enumeration entries = bundle.findEntries("/", null, true);
+ while (entries.hasMoreElements()) {
+ URL entry = (URL) entries.nextElement();
+
+ Properties props = new Properties();
+ props.setProperty(Resource.REPOSITORY, bundle.getSymbolicName() + "_" + bundle.getVersion());
+ props.setProperty(Resource.PATH, entry.getPath());
+ props.setProperty(Resource.NAME, entry.getFile());
+
+ if (filterObject.match(props))
+ handler.removed(new EntryResource(entry));
+
+ }
+ }
+
+ class EntryResource implements Resource {
+
+ URL entry;
+
+ EntryResource(URL entry) {
+ this.entry = entry;
+ }
+
+ public String getName() {
+ return entry.getFile();
+ }
+
+ public String getPath() {
+ return entry.getPath();
+ }
+
+ public String getRepository() {
+
+ return bundle.getSymbolicName() + "_" + bundle.getVersion();
+ }
+
+ public InputStream openStream() throws IOException {
+ return entry.openStream();
+ }
+ }
+}
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/resources/Resource.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/resources/Resource.java
new file mode 100644
index 0000000..075f5d6
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/resources/Resource.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager.resources;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/** Interface that defines a resource. */
+public interface Resource {
+ public static final String PATH = "path";
+ public static final String NAME = "name";
+ public static final String REPOSITORY = "repository";
+
+ String getName();
+
+ String getPath();
+
+ String getRepository();
+
+ InputStream openStream() throws IOException;
+}
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/resources/ResourceHandler.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/resources/ResourceHandler.java
new file mode 100644
index 0000000..79e387e
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/resources/ResourceHandler.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager.resources;
+
+/** Service interface for anybody wanting to be notified of changes to resources. */
+public interface ResourceHandler {
+ void added(Resource resource);
+ void changed(Resource resource);
+ void removed(Resource resource);
+}
diff --git a/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/ComponentLifeCycleTest.java b/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/ComponentLifeCycleTest.java
index 38e8bb2..a97a4f3 100644
--- a/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/ComponentLifeCycleTest.java
+++ b/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/ComponentLifeCycleTest.java
@@ -23,8 +23,8 @@
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.apache.felix.dependencymanager.impl.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.Option;
diff --git a/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/ConfigurationDependencyTest.java b/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/ConfigurationDependencyTest.java
index f0d9fb6..adcfb7a 100644
--- a/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/ConfigurationDependencyTest.java
+++ b/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/ConfigurationDependencyTest.java
@@ -29,8 +29,8 @@
import junit.framework.Assert;
import org.apache.felix.dependencymanager.DependencyManager;
-import org.apache.felix.dependencymanager.Logger;
import org.apache.felix.dependencymanager.Service;
+import org.apache.felix.dependencymanager.impl.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.Option;
@@ -82,7 +82,7 @@
m_ensure = e;
}
- public void start() {
+ public void init() {
try {
m_ensure.step(1);
org.osgi.service.cm.Configuration conf = m_ca.getConfiguration("test", null);
@@ -125,7 +125,7 @@
m_ensure = e;
}
- public void start() {
+ public void init() {
m_ensure.step(3);
m_runnable.run();
}
diff --git a/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/Ensure.java b/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/Ensure.java
index 6aeba950..73e1e2a 100644
--- a/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/Ensure.java
+++ b/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/Ensure.java
@@ -50,6 +50,7 @@
* @param timeout the number of milliseconds to wait
*/
public synchronized void waitForStep(int nr, int timeout) {
+ final int initialTimeout = timeout;
while (step <= nr && timeout > 0) {
try {
wait(RESOLUTION);
@@ -58,7 +59,7 @@
catch (InterruptedException e) {}
}
if (step <= nr) {
- throw new IllegalStateException("Timed out waiting for " + timeout + " ms for step " + nr);
+ throw new IllegalStateException("Timed out waiting for " + initialTimeout + " ms for step " + nr + ", we are still at step " + step);
}
}
}
diff --git a/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/MultipleServiceDependencyTest.java b/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/MultipleServiceDependencyTest.java
index e453b15..691d551 100644
--- a/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/MultipleServiceDependencyTest.java
+++ b/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/MultipleServiceDependencyTest.java
@@ -7,8 +7,8 @@
import java.util.Properties;
import org.apache.felix.dependencymanager.DependencyManager;
-import org.apache.felix.dependencymanager.Logger;
import org.apache.felix.dependencymanager.Service;
+import org.apache.felix.dependencymanager.impl.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.Option;
@@ -82,7 +82,7 @@
m_ensure = e;
}
- public void start() {
+ public void init() {
Thread t = new Thread(this);
t.start();
}
@@ -95,7 +95,7 @@
m_service.invoke();
}
- public void stop() {
+ public void destroy() {
m_ensure.step(6);
}
}
diff --git a/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/ServiceDependencyTest.java b/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/ServiceDependencyTest.java
index 37cff66..a1533de 100644
--- a/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/ServiceDependencyTest.java
+++ b/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/ServiceDependencyTest.java
@@ -23,8 +23,8 @@
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.apache.felix.dependencymanager.impl.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.Option;
@@ -85,12 +85,12 @@
m_ensure = e;
}
- public void start() {
+ public void init() {
m_ensure.step(1);
m_service.invoke();
}
- public void stop() {
+ public void destroy() {
m_ensure.step(3);
}
}
diff --git a/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/TemporalServiceDependencyTest.java b/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/TemporalServiceDependencyTest.java
index 30bf35a..d782b1b 100644
--- a/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/TemporalServiceDependencyTest.java
+++ b/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/TemporalServiceDependencyTest.java
@@ -23,8 +23,8 @@
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.apache.felix.dependencymanager.impl.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.Option;
@@ -104,7 +104,7 @@
m_ensure = e;
}
- public void start() {
+ public void init() {
m_ensure.step(1);
Thread t = new Thread(this);
t.start();
@@ -116,7 +116,7 @@
m_service.invoke();
}
- public void stop() {
+ public void destroy() {
m_ensure.step(5);
}
}