added support and tests for resource dependencies that are shared between services
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@887124 13f79535-47bb-0310-9956-ffa450edef68
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
index 0e024d5..3865c5e 100644
--- 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
@@ -21,12 +21,13 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
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;
@@ -44,11 +45,13 @@
private boolean m_autoConfig;
private final Logger m_logger;
private String m_autoConfigInstance;
- private DependencyService m_service;
+// private DependencyService m_service;
+ protected List m_services = new ArrayList();
private boolean m_isRequired;
private String m_resourceFilter;
private Resource m_resource;
private Resource m_trackedResource;
+ private boolean m_isStarted;
public ResourceDependency(BundleContext context, Logger logger) {
@@ -70,101 +73,115 @@
}
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);
-
+ boolean needsStarting = false;
+ synchronized (this) {
+ m_services.add(service);
+ if (!m_isStarted) {
+ m_isStarted = true;
+ needsStarting = true;
+ }
+ }
+ if (needsStarting) {
+ Properties props = new Properties();
+ props.setProperty(Resource.FILTER, m_resourceFilter);
+ m_registration = m_context.registerService(ResourceHandler.class.getName(), this, props);
+ }
}
public void stop(DependencyService service) {
- m_registration.unregister();
- m_registration = null;
+ boolean needsStopping = false;
+ synchronized (this) {
+ if (m_services.size() == 1 && m_services.contains(service)) {
+ m_isStarted = false;
+ needsStopping = true;
+ m_services.remove(service);
+ }
+ }
+ if (needsStopping) {
+ m_registration.unregister();
+ m_registration = null;
+ }
}
public void added(Resource resource) {
- System.out.println("RD ADDED " + resource);
long counter;
+ Object[] services;
synchronized (this) {
m_resourceCounter++;
counter = m_resourceCounter;
m_resource = resource; // TODO this really sucks as a way to track a single resource
+ services = m_services.toArray();
}
- 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);
+ for (int i = 0; i < services.length; i++) {
+ DependencyService ds = (DependencyService) services[i];
+ if (counter == 1) {
+ ds.dependencyAvailable(this);
+ if (!isRequired()) {
+ invokeAdded(ds, resource);
+ }
+ }
+ else {
+ ds.dependencyChanged(this);
+ invokeAdded(ds, resource);
+ }
}
}
public void changed(Resource resource) {
- invokeChanged(resource);
+ Object[] services;
+ synchronized (this) {
+ services = m_services.toArray();
+ }
+ for (int i = 0; i < services.length; i++) {
+ DependencyService ds = (DependencyService) services[i];
+ invokeChanged(ds, resource);
+ }
}
public void removed(Resource resource) {
long counter;
+ Object[] services;
synchronized (this) {
m_resourceCounter--;
counter = m_resourceCounter;
+ services = m_services.toArray();
}
- 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);
+ for (int i = 0; i < services.length; i++) {
+ DependencyService ds = (DependencyService) services[i];
+ if (counter == 0) {
+ ds.dependencyUnavailable(this);
+ if (!isRequired()) {
+ invokeRemoved(ds, resource);
+ }
+ }
+ else {
+ ds.dependencyChanged(this);
+ invokeRemoved(ds, resource);
+ }
}
}
- public void invokeAdded() {
- // TODO fixme
- //invokeAdded(m_bundleInstance);
- }
-
- public void invokeAdded(Resource serviceInstance) {
- Object[] callbackInstances = getCallbackInstances();
+ public void invokeAdded(DependencyService ds, Resource serviceInstance) {
+ Object[] callbackInstances = getCallbackInstances(ds);
if ((callbackInstances != null) && (m_callbackAdded != null)) {
- invokeCallbackMethod(callbackInstances, m_callbackAdded, serviceInstance);
+ invokeCallbackMethod(callbackInstances, m_callbackAdded, serviceInstance);
}
}
- public void invokeChanged(Resource serviceInstance) {
- Object[] callbackInstances = getCallbackInstances();
+ public void invokeChanged(DependencyService ds, Resource serviceInstance) {
+ Object[] callbackInstances = getCallbackInstances(ds);
if ((callbackInstances != null) && (m_callbackChanged != null)) {
-// if (m_reference == null) {
-// Thread.dumpStack();
-// }
- invokeCallbackMethod(callbackInstances, m_callbackChanged, serviceInstance);
+ invokeCallbackMethod(callbackInstances, m_callbackChanged, serviceInstance);
}
}
-
- public void invokeRemoved() {
- // TODO fixme
- //invokeRemoved(m_bundleInstance);
- }
-
- public void invokeRemoved(Resource serviceInstance) {
- Object[] callbackInstances = getCallbackInstances();
+ public void invokeRemoved(DependencyService ds, Resource serviceInstance) {
+ Object[] callbackInstances = getCallbackInstances(ds);
if ((callbackInstances != null) && (m_callbackRemoved != null)) {
-// if (m_reference == null) {
-// Thread.dumpStack();
-// }
- invokeCallbackMethod(callbackInstances, m_callbackRemoved, serviceInstance);
+ 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
@@ -325,15 +342,14 @@
}
return false;
}
- private synchronized Object[] getCallbackInstances() {
- Object[] callbackInstances = ((ServiceImpl) m_service).getCompositionInstances();
+
+ private synchronized Object[] getCallbackInstances(DependencyService ds) {
if (m_callbackInstance == null) {
- return callbackInstances;
+ return ds.getCompositionInstances();
}
- 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;
+ else {
+ return new Object[] { m_callbackInstance };
+ }
}
public ResourceDependency setResource(Resource resource) {
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ServiceDependency.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ServiceDependency.java
index 8ccec84..88bca3b 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ServiceDependency.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ServiceDependency.java
@@ -365,11 +365,11 @@
m_isStarted = false;
needsStopping = true;
}
+ m_services.remove(service);
}
if (needsStopping) {
m_tracker.close();
m_tracker = null;
- m_services.remove(service);
}
}
@@ -389,7 +389,10 @@
public void addedService(ServiceReference ref, Object service) {
boolean makeAvailable = makeAvailable();
- Object[] services = m_services.toArray();
+ Object[] services;
+ synchronized (this) {
+ services = m_services.toArray();
+ }
for (int i = 0; i < services.length; i++) {
DependencyService ds = (DependencyService) services[i];
if (makeAvailable) {
@@ -419,7 +422,10 @@
m_reference = ref;
m_serviceInstance = service;
- Object[] services = m_services.toArray();
+ Object[] services;
+ synchronized (this) {
+ services = m_services.toArray();
+ }
for (int i = 0; i < services.length; i++) {
DependencyService ds = (DependencyService) services[i];
ds.dependencyChanged(this);
@@ -442,7 +448,10 @@
public void removedService(ServiceReference ref, Object service) {
boolean makeUnavailable = makeUnavailable();
- Object[] services = m_services.toArray();
+ Object[] services;
+ synchronized (this) {
+ services = m_services.toArray();
+ }
for (int i = 0; i < services.length; i++) {
DependencyService ds = (DependencyService) services[i];
if (makeUnavailable) {
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/ServiceImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/ServiceImpl.java
index f11f1d3..6db3ab6 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/ServiceImpl.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/ServiceImpl.java
@@ -972,7 +972,7 @@
}
// for required dependencies, we invoke any callbacks here
if (bd.isRequired()) {
- bd.invokeAdded();
+ bd.invokeAdded(this, bd.getResource());
}
}
else if (dependency instanceof ConfigurationDependency) {
diff --git a/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/ResourceDependencyTest.java b/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/ResourceDependencyTest.java
index dc0d71d..21d32ea 100644
--- a/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/ResourceDependencyTest.java
+++ b/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/ResourceDependencyTest.java
@@ -25,7 +25,10 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Properties;
+import java.util.Map.Entry;
import junit.framework.Assert;
@@ -76,6 +79,7 @@
static class ResourceConsumer {
private volatile int m_counter;
public void add(Resource resource) {
+ System.out.println("RC:ADD " + resource);
m_counter++;
try {
resource.openStream();
@@ -85,6 +89,7 @@
}
}
public void remove(Resource resource) {
+ System.out.println("RC:REMOVE " + resource);
m_counter--;
}
public void ensure() {
@@ -95,6 +100,7 @@
static class ResourceProvider {
private volatile BundleContext m_context;
private final Ensure m_ensure;
+ private final Map m_handlers = new HashMap();
private StaticResource[] m_resources = {
new StaticResource("test1.txt", "/test", "TestRepository") {
public InputStream openStream() throws IOException {
@@ -122,32 +128,50 @@
public void add(ServiceReference ref, ResourceHandler handler) {
String filterString = (String) ref.getProperty("filter");
+ Filter filter;
try {
- Filter filter = m_context.createFilter(filterString);
- for (int i = 0; i < m_resources.length; i++) {
- if (filter.match(m_resources[i].getProperties())) {
- handler.added(m_resources[i]);
- }
- }
+ filter = m_context.createFilter(filterString);
}
catch (InvalidSyntaxException e) {
- e.printStackTrace();
+ Assert.fail("Could not create filter for resource handler: " + e);
+ return;
+ }
+ synchronized (m_handlers) {
+ m_handlers.put(handler, filter);
+ }
+ for (int i = 0; i < m_resources.length; i++) {
+ if (filter.match(m_resources[i].getProperties())) {
+ handler.added(m_resources[i]);
+ }
}
}
public void remove(ServiceReference ref, ResourceHandler handler) {
- String filterString = (String) ref.getProperty("filter");
- try {
- Filter filter = m_context.createFilter(filterString);
- for (int i = 0; i < m_resources.length; i++) {
- if (filter.match(m_resources[i].getProperties())) {
- handler.removed(m_resources[i]);
- }
+ Filter filter;
+ synchronized (m_handlers) {
+ filter = (Filter) m_handlers.remove(handler);
+ }
+ removeResources(handler, filter);
+ }
+
+ private void removeResources(ResourceHandler handler, Filter filter) {
+ for (int i = 0; i < m_resources.length; i++) {
+ if (filter.match(m_resources[i].getProperties())) {
+ handler.removed(m_resources[i]);
}
}
- catch (InvalidSyntaxException e) {
- e.printStackTrace();
+ }
+
+ public void destroy() {
+ Entry[] handlers;
+ synchronized (m_handlers) {
+ handlers = (Entry[]) m_handlers.entrySet().toArray(new Entry[m_handlers.size()]);
}
+ for (int i = 0; i < handlers.length; i++) {
+ removeResources((ResourceHandler) handlers[i].getKey(), (Filter) handlers[i].getValue());
+ }
+
+ System.out.println("DESTROY..." + m_handlers.size());
}
}
diff --git a/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/SharingDependenciesWithMultipleServicesTest.java b/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/SharingDependenciesWithMultipleServicesTest.java
index 2a6f295..b1ce30f 100644
--- a/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/SharingDependenciesWithMultipleServicesTest.java
+++ b/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/SharingDependenciesWithMultipleServicesTest.java
@@ -22,6 +22,8 @@
import static org.ops4j.pax.exam.CoreOptions.options;
import static org.ops4j.pax.exam.CoreOptions.provision;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.Dictionary;
import java.util.Properties;
@@ -29,14 +31,20 @@
import org.apache.felix.dependencymanager.Service;
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.impl.Logger;
+import org.apache.felix.dependencymanager.resources.Resource;
+import org.apache.felix.dependencymanager.resources.ResourceHandler;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.Configuration;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
@@ -118,6 +126,27 @@
m.remove(consumer1);
}
+ @Test
+ public void testShareResourceDependencyWithMultipleServices(BundleContext context) {
+ DependencyManager m = new DependencyManager(context, new Logger(context));
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ ResourceDependency dependency = m.createResourceDependency().setFilter("(" + Resource.REPOSITORY + "=TestRepository)").setRequired(true);
+ Service consumer1 = m.createService().setImplementation(new ResourceConsumer(e, 1)).add(dependency);
+ Service consumer2 = m.createService().setImplementation(new ResourceConsumer(e, 2)).add(dependency);
+ Service resourceProvider = m.createService().setImplementation(new ResourceProvider()).add(m.createServiceDependency().setService(ResourceHandler.class).setCallbacks("add", "remove"));;
+ m.add(resourceProvider);
+ m.add(consumer1);
+ e.waitForStep(1, 2000);
+ m.add(consumer2);
+ e.waitForStep(2, 2000);
+ m.remove(consumer2);
+ m.remove(consumer1);
+ m.remove(resourceProvider);
+ }
+
+
static interface ServiceInterface {
public void invoke(Runnable r);
}
@@ -199,4 +228,93 @@
m_ensure.step(m_step);
}
}
+
+ static class ResourceConsumer {
+ private final Ensure m_ensure;
+ private int m_step;
+
+ public ResourceConsumer(Ensure e, int step) {
+ m_ensure = e;
+ m_step = step;
+ }
+
+ public void start() {
+ m_ensure.step(m_step);
+ }
+ }
+
+ static class ResourceProvider {
+ private volatile BundleContext m_context;
+ private StaticResource[] m_resources = {
+ new StaticResource("test1.txt", "/test", "TestRepository"),
+ new StaticResource("test2.txt", "/test", "TestRepository")
+ };
+
+ public void add(ServiceReference ref, ResourceHandler handler) {
+ String filterString = (String) ref.getProperty("filter");
+ try {
+ Filter filter = m_context.createFilter(filterString);
+ for (int i = 0; i < m_resources.length; i++) {
+ if (filter.match(m_resources[i].getProperties())) {
+ handler.added(m_resources[i]);
+ }
+ }
+ }
+ catch (InvalidSyntaxException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void remove(ServiceReference ref, ResourceHandler handler) {
+ String filterString = (String) ref.getProperty("filter");
+ try {
+ Filter filter = m_context.createFilter(filterString);
+ for (int i = 0; i < m_resources.length; i++) {
+ if (filter.match(m_resources[i].getProperties())) {
+ handler.removed(m_resources[i]);
+ }
+ }
+ }
+ catch (InvalidSyntaxException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ static class StaticResource implements Resource {
+ private String m_name;
+ private String m_path;
+ private String m_repository;
+
+ public StaticResource(String name, String path, String repository) {
+ m_name = name;
+ m_path = path;
+ m_repository = repository;
+ }
+
+ public String getName() {
+ return m_name;
+ }
+
+ public String getPath() {
+ return m_path;
+ }
+
+ public String getRepository() {
+ return m_repository;
+ }
+
+ public Dictionary getProperties() {
+ return new Properties() {{
+ put(Resource.NAME, getName());
+ put(Resource.PATH, getPath());
+ put(Resource.REPOSITORY, getRepository());
+ }};
+ }
+
+ public InputStream openStream() throws IOException {
+ return null;
+ }
+ }
+
}