added support for adding dynamic aspects for services, plus a test that validates the basic behaviour
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@885969 13f79535-47bb-0310-9956-ffa450edef68
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 2f24f8e..782c8ad 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
@@ -18,6 +18,7 @@
*/
package org.apache.felix.dependencymanager;
+import java.util.Dictionary;
import java.util.List;
import org.apache.felix.dependencymanager.dependencies.BundleDependency;
@@ -141,6 +142,10 @@
return m_manager.createBundleDependency();
}
+ public Service createAspectService(Class serviceInterface, String serviceFilter, Object aspectImplementation, Dictionary properties) {
+ return m_manager.createAspectService(serviceInterface, serviceFilter, aspectImplementation, properties);
+ }
+
/**
* Cleans up all services and their dependencies.
*
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 26a9066..3ebfe5e 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
@@ -20,12 +20,14 @@
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Dictionary;
import java.util.List;
import org.apache.felix.dependencymanager.dependencies.BundleDependency;
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.AspectImpl;
import org.apache.felix.dependencymanager.impl.Logger;
import org.apache.felix.dependencymanager.impl.ServiceImpl;
import org.osgi.framework.BundleContext;
@@ -108,7 +110,17 @@
public BundleDependency createBundleDependency() {
return new BundleDependency(m_context, m_logger);
}
-
+
+ public Service createAspectService(Class serviceInterface, String serviceFilter, Object aspectImplementation, Dictionary properties) {
+ return createService()
+ .setImplementation(new AspectImpl(serviceInterface, serviceFilter, aspectImplementation, properties))
+ .add(createServiceDependency()
+ .setService(serviceInterface, serviceFilter)
+ .setAutoConfig(false)
+ .setCallbacks("added", "removed")
+ );
+ }
+
/**
* Returns a list of services.
*
@@ -117,5 +129,4 @@
public List getServices() {
return Collections.unmodifiableList(m_services);
}
-
}
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/Service.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/Service.java
index dddc800..cdcea6f 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/Service.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/Service.java
@@ -36,6 +36,7 @@
* @return this service
*/
public Service add(Dependency dependency);
+ public Service add(List dependencies);
/**
* Removes a dependency from this service.
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/AspectImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/AspectImpl.java
new file mode 100644
index 0000000..e5a969e
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/AspectImpl.java
@@ -0,0 +1,72 @@
+package org.apache.felix.dependencymanager.impl;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.apache.felix.dependencymanager.Service;
+import org.osgi.framework.ServiceReference;
+
+public class AspectImpl {
+ private volatile DependencyManager m_manager;
+ private volatile Service m_service;
+ private final Class m_serviceInterface;
+ private final String m_serviceFilter;
+ private final Object m_aspectImplementation;
+ private final Map m_services = new HashMap();
+ private final Dictionary m_aspectProperties;
+
+ public AspectImpl(Class serviceInterface, String serviceFilter, Object aspectImplementation, Dictionary properties) {
+ m_serviceInterface = serviceInterface;
+ m_serviceFilter = serviceFilter;
+ m_aspectImplementation = aspectImplementation;
+ m_aspectProperties = properties;
+ }
+
+ public void added(ServiceReference ref, Object service) {
+ Properties props = new Properties();
+ String[] keys = ref.getPropertyKeys();
+ for (int i = 0; i < keys.length; i++) {
+ props.put(keys[i], ref.getProperty(keys[i]));
+ }
+ Enumeration e = m_aspectProperties.keys();
+ while (e.hasMoreElements()) {
+ Object key = e.nextElement();
+ props.put(key, m_aspectProperties.get(key));
+ }
+
+ Service newService = m_manager.createService()
+ .setInterface(m_serviceInterface.getName(), props)
+ .setImplementation(m_aspectImplementation)
+ .add(m_service.getDependencies())
+ .add(m_manager.createServiceDependency()
+ .setService(m_serviceInterface, ref)
+ .setRequired(true)
+ );
+ m_services.put(ref, newService);
+ m_manager.add(newService);
+ }
+
+ public void removed(ServiceReference ref, Object service) {
+ Service newService = (Service) m_services.remove(ref);
+ if (newService == null) {
+ System.out.println("Service should not be null here, dumping stack.");
+ Thread.dumpStack();
+ }
+ else {
+ m_manager.remove(newService);
+ }
+ }
+
+ public void stop() {
+ Iterator i = m_services.values().iterator();
+ while (i.hasNext()) {
+ m_manager.remove((Service) i.next());
+ }
+ m_services.clear();
+ }
+}
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 17c5100..f11f1d3 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
@@ -904,23 +904,13 @@
}
catch (Exception e) {
m_logger.log(Logger.LOG_ERROR, "Could not obtain instances from the composition manager.", e);
- instances = new Object[] { m_serviceInstance };
+ instances = m_serviceInstance == null ? new Object[] {} : new Object[] { m_serviceInstance };
}
}
}
else {
- instances = new Object[] { m_serviceInstance };
+ instances = m_serviceInstance == null ? new Object[] {} : new Object[] { m_serviceInstance };
}
- // TODO remove this test code; there are definitely cases where some instances in this array can be
- // null, but it's not always harmful (in fact it's not possible to determine that here), this also happens
- // when you start tracking required dependencies... it's probably safe not to include these null's in the
- // array in the first place
-// for (int i = 0; i < instances.length; i++) {
-// if (instances[i] == null) {
-// System.out.println("GetCompositionInstances had a null instance at index " + i + " dumping stack:");
-// Thread.dumpStack();
-// }
-// }
return instances;
}
diff --git a/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/AspectTest.java b/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/AspectTest.java
new file mode 100644
index 0000000..b810919
--- /dev/null
+++ b/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/AspectTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.test;
+
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.provision;
+
+import java.util.Properties;
+
+import org.apache.felix.dependencymanager.DependencyManager;
+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;
+import org.ops4j.pax.exam.junit.Configuration;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+
+@RunWith(JUnit4TestRunner.class)
+public class AspectTest {
+ @Configuration
+ public static Option[] configuration() {
+ return options(
+ provision(
+ mavenBundle().groupId("org.osgi").artifactId("org.osgi.compendium").version("4.2.0"),
+ mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.dependencymanager").versionAsInProject()
+ )
+ );
+ }
+
+ @Test
+ public void testServiceRegistrationAndConsumption(BundleContext context) {
+ DependencyManager m = new DependencyManager(context, new Logger(context));
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a service provider and consumer
+ Service sp = m.createService().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+ Service sc = m.createService().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true));
+ Service sa = m.createAspectService(ServiceInterface.class, "(|(!(" + Constants.SERVICE_RANKING + "=*))(" + Constants.SERVICE_RANKING + "<=0))", new ServiceAspect(e), new Properties() {{ put(Constants.SERVICE_RANKING, Integer.valueOf(1)); }} );
+ m.add(sc);
+ m.add(sp);
+ e.waitForStep(3, 2000);
+ m.add(sa);
+ e.waitForStep(4, 2000);
+ e.step(5);
+ e.waitForStep(8, 2000);
+ m.remove(sa);
+ e.waitForStep(9, 2000);
+ e.step(10);
+ e.waitForStep(11, 2000);
+ m.remove(sp);
+ m.remove(sc);
+ }
+
+ static interface ServiceInterface {
+ public void invoke(Runnable run);
+ }
+
+ static class ServiceProvider implements ServiceInterface {
+ private final Ensure m_ensure;
+ public ServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+ public void invoke(Runnable run) {
+ run.run();
+ }
+ }
+
+ static class ServiceAspect implements ServiceInterface {
+ private final Ensure m_ensure;
+ private volatile ServiceInterface m_originalService;
+
+ public ServiceAspect(Ensure e) {
+ m_ensure = e;
+ }
+ public void start() {
+ m_ensure.step(4);
+ }
+ public void invoke(Runnable run) {
+ m_ensure.step(6);
+ m_originalService.invoke(run);
+ }
+
+ public void stop() {
+ m_ensure.step(9);
+ }
+ }
+
+ static class ServiceConsumer implements Runnable {
+ private volatile ServiceInterface m_service;
+ private final Ensure m_ensure;
+
+ public ServiceConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void init() {
+ Thread t = new Thread(this);
+ t.start();
+ }
+
+ public void run() {
+ m_ensure.step(1);
+ m_service.invoke(Ensure.createRunnableStep(m_ensure, 2));
+ m_ensure.step(3);
+ m_ensure.waitForStep(5, 2000);
+ m_service.invoke(Ensure.createRunnableStep(m_ensure, 7));
+ m_ensure.step(8);
+ m_ensure.waitForStep(10, 2000);
+ m_service.invoke(Ensure.createRunnableStep(m_ensure, 11));
+ }
+ }
+}
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 bdac0bd..d908036 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
@@ -79,4 +79,8 @@
System.out.println("[Ensure " + INSTANCE + "] arrived at step " + nr);
}
}
+
+ public static Runnable createRunnableStep(final Ensure ensure, final int nr) {
+ return new Runnable() { public void run() { ensure.step(nr); }};
+ }
}