Extended the test suite for aspects, added a resource adapter test.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@987474 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/test/src/test/java/org/apache/felix/dm/test/AspectBaseTest.java b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/AspectBaseTest.java
new file mode 100644
index 0000000..8f27ff5
--- /dev/null
+++ b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/AspectBaseTest.java
@@ -0,0 +1,230 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.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.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import junit.framework.Assert;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.Service;
+import org.apache.felix.dm.ServiceUtil;
+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.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+@RunWith(JUnit4TestRunner.class)
+public class AspectBaseTest extends Base {
+ @Configuration
+ public static Option[] configuration() {
+ return options(
+// vmOption( "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005" ),
+// waitForFrameworkStartupFor(Long.MAX_VALUE),
+ provision(
+ mavenBundle().groupId("org.osgi").artifactId("org.osgi.compendium").version("4.1.0"),
+ mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.dependencymanager").versionAsInProject()
+ )
+ );
+ }
+
+ @Test
+ public void testSingleAspect(BundleContext context) {
+ DependencyManager m = new DependencyManager(context);
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+
+ // create a service provider and consumer
+ ServiceProvider p = new ServiceProvider(e, "a");
+ ServiceConsumer c = new ServiceConsumer(e);
+ Service sp = m.createService().setImplementation(p).setInterface(ServiceInterface.class.getName(), new Properties() {{ put("name", "a"); }});
+ Service sc = m.createService().setImplementation(c).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true).setCallbacks("add", "remove").setAutoConfig(true));
+ Service sa = m.createAspectService(ServiceInterface.class, null, 20, null).setImplementation(ServiceAspect.class);
+ m.add(sc);
+ m.add(sp);
+ // after the provider was added, the consumer's add should have been invoked once
+ e.waitForStep(1, 2000);
+ Assert.assertEquals("a", c.invoke());
+ m.add(sa);
+ // after the aspect was added, the consumer should get and add for the aspect and a remove
+ // for the original service
+ e.waitForStep(3, 2000);
+ Assert.assertEquals("aa", c.invoke());
+ m.remove(sa);
+ // removing the aspect again should give a remove and add
+ e.waitForStep(5, 2000);
+ Assert.assertEquals("a", c.invoke());
+ m.remove(sp);
+ // finally removing the original service should give a remove
+ e.waitForStep(6, 2000);
+ m.remove(sc);
+ e.step(7);
+ }
+
+ @Test
+ public void testSingleAspectThatAlreadyExisted(BundleContext context) {
+ DependencyManager m = new DependencyManager(context);
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+
+ // create a service provider and consumer
+ ServiceProvider p = new ServiceProvider(e, "a");
+ ServiceConsumer c = new ServiceConsumer(e);
+ Service sp = m.createService().setImplementation(p).setInterface(ServiceInterface.class.getName(), new Properties() {{ put("name", "a"); }});
+ Service sc = m.createService().setImplementation(c).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true).setCallbacks("add", "remove").setAutoConfig(true));
+ Service sa = m.createAspectService(ServiceInterface.class, null, 20, null).setImplementation(ServiceAspect.class);
+ // we first add the aspect
+ m.add(sa);
+ // then the service provider
+ m.add(sp);
+ // finally the consumer
+ m.add(sc);
+
+ Assert.assertEquals("aa", c.invoke());
+
+ // now the consumer's added should be invoked once, as the aspect is already available and should
+ // directly hide the original service
+ e.waitForStep(1, 2000);
+ e.step(2);
+
+ m.remove(sa);
+ // after removing the aspect, the consumer should get the original service back, so
+ // remove and add will be invoked
+ e.waitForStep(4, 2000);
+
+ m.remove(sp);
+ // after removing the original service, the consumer's remove should be called once
+ e.waitForStep(5, 2000);
+
+ m.remove(sc);
+ e.step(6);
+ }
+
+ @Test
+ public void testMultipleAspects(BundleContext context) {
+ DependencyManager m = new DependencyManager(context);
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+
+ // create service providers and consumers
+ ServiceConsumer c = new ServiceConsumer(e);
+ Service sp = m.createService().setImplementation(new ServiceProvider(e, "a")).setInterface(ServiceInterface.class.getName(), new Properties() {{ put("name", "a"); }});
+ Service sp2 = m.createService().setImplementation(new ServiceProvider(e, "b")).setInterface(ServiceInterface.class.getName(), new Properties() {{ put("name", "b"); }});
+ Service sc = m.createService().setImplementation(c).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true).setCallbacks("add", "remove"));
+ Service sa = m.createAspectService(ServiceInterface.class, null, 20, null).setImplementation(ServiceAspect.class);
+ Service sa2 = m.createAspectService(ServiceInterface.class, null, 10, null).setImplementation(ServiceAspect.class);
+ m.add(sp);
+ m.add(sp2);
+ m.add(sa);
+ m.add(sa2);
+ m.add(sc);
+ // the consumer will monitor progress, it should get it's add invoked twice, once for every
+ // (highest) aspect
+ e.waitForStep(2, 2000);
+ e.step(3);
+
+ // now invoke all services the consumer collected
+ List<String> list = c.invokeAll();
+ // and make sure both of them are correctly invoked
+ Assert.assertTrue(list.size() == 2);
+ Assert.assertTrue(list.contains("aaa"));
+ Assert.assertTrue(list.contains("bbb"));
+
+ m.remove(sc);
+ // removing the consumer now should get its removed method invoked twice
+ e.waitForStep(5, 2000);
+ e.step(6);
+ m.remove(sa2);
+ m.remove(sa);
+ m.remove(sp2);
+ m.remove(sp);
+ e.step(7);
+ }
+
+ public static interface ServiceInterface {
+ public String invoke(String input);
+ }
+
+ public static class ServiceProvider implements ServiceInterface {
+ private final Ensure m_ensure;
+ private final String m_name;
+ public ServiceProvider(Ensure e, String name) {
+ m_ensure = e;
+ m_name = name;
+ }
+ public String invoke(String input) {
+ return input + m_name;
+ }
+ }
+
+ public static class ServiceAspect implements ServiceInterface {
+ private volatile ServiceInterface m_originalService;
+ private volatile ServiceRegistration m_registration;
+
+ public String invoke(String input) {
+ String result = m_originalService.invoke(input);
+ String property = (String) m_registration.getReference().getProperty("name");
+ return result + property;
+ }
+ }
+
+ public static class ServiceConsumer {
+ private final Ensure m_ensure;
+ private volatile ServiceInterface m_service;
+ private List<ServiceInterface> m_services = new ArrayList<ServiceInterface>();
+
+ public ServiceConsumer(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void add(ServiceReference ref, ServiceInterface si) {
+ System.out.println("add: " + ServiceUtil.toString(ref));
+ m_services.add(si);
+ m_ensure.step();
+ }
+
+ public void remove(ServiceReference ref, ServiceInterface si) {
+ System.out.println("rem: " + ServiceUtil.toString(ref));
+ m_services.remove(si);
+ m_ensure.step();
+ }
+
+ public String invoke() {
+ return m_service.invoke("");
+ }
+
+ public List<String> invokeAll() {
+ List<String> results = new ArrayList<String>();
+ for (ServiceInterface si : m_services) {
+ results.add(si.invoke(""));
+ }
+ return results;
+ }
+ }
+}
diff --git a/dependencymanager/test/src/test/java/org/apache/felix/dm/test/AspectTest.java b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/AspectDynamicsTest.java
similarity index 98%
rename from dependencymanager/test/src/test/java/org/apache/felix/dm/test/AspectTest.java
rename to dependencymanager/test/src/test/java/org/apache/felix/dm/test/AspectDynamicsTest.java
index 5cfd398..bba5788 100644
--- a/dependencymanager/test/src/test/java/org/apache/felix/dm/test/AspectTest.java
+++ b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/AspectDynamicsTest.java
@@ -32,7 +32,7 @@
import org.osgi.framework.BundleContext;
@RunWith(JUnit4TestRunner.class)
-public class AspectTest extends Base {
+public class AspectDynamicsTest extends Base {
@Configuration
public static Option[] configuration() {
return options(
diff --git a/dependencymanager/test/src/test/java/org/apache/felix/dm/test/ResourceAdapterTest.java b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/ResourceAdapterTest.java
new file mode 100644
index 0000000..5b35732
--- /dev/null
+++ b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/ResourceAdapterTest.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.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.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import junit.framework.Assert;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.ResourceUtil;
+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;
+
+@RunWith(JUnit4TestRunner.class)
+public class ResourceAdapterTest extends Base {
+ @Configuration
+ public static Option[] configuration() {
+ return options(
+ provision(
+ mavenBundle().groupId("org.osgi").artifactId("org.osgi.compendium").version("4.1.0"),
+ mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.dependencymanager").versionAsInProject()
+ )
+ );
+ }
+
+ @Test
+ public void testBasicResourceAdapter(BundleContext context) throws Exception {
+ DependencyManager m = new DependencyManager(context);
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+ // create a resource provider
+ ResourceProvider provider = new ResourceProvider(e);
+ // activate it
+ m.add(m.createService().setImplementation(provider).add(m.createServiceDependency().setService(ResourceHandler.class).setCallbacks("add", "remove")));
+ // create a resource adapter for our single resource
+ // note that we can provide an actual implementation instance here because there will be only one
+ // adapter, normally you'd want to specify a Class here
+ m.add(m.createResourceAdapterService("(&(path=/path/to/*.txt)(host=localhost))", false, null, "changed")
+ .setImplementation(new ResourceAdapter(e)));
+ // wait until the single resource is available
+ e.waitForStep(3, 5000);
+ // trigger a 'change' in our resource
+ provider.change();
+ // wait until the changed callback is invoked
+ e.waitForStep(4, 5000);
+ }
+
+ static class ResourceAdapter {
+ protected URL m_resource; // injected by reflection.
+ private Ensure m_ensure;
+
+ ResourceAdapter(Ensure e) {
+ m_ensure = e;
+ }
+
+ public void start() {
+ m_ensure.step(1);
+ Assert.assertNotNull("resource not injected", m_resource);
+ m_ensure.step(2);
+ try {
+ InputStream in = m_resource.openStream();
+ }
+ catch (FileNotFoundException e) {
+ m_ensure.step(3);
+ }
+ catch (IOException e) {
+ Assert.fail("We should not have gotten this exception.");
+ }
+ }
+
+ public void changed() {
+ m_ensure.step(4);
+ }
+ }
+
+ static class ResourceProvider {
+ private volatile BundleContext m_context;
+ private final Ensure m_ensure;
+ private final Map m_handlers = new HashMap();
+ private URL[] m_resources;
+
+ public ResourceProvider(Ensure ensure) throws MalformedURLException {
+ m_ensure = ensure;
+ m_resources = new URL[] {
+ new URL("file://localhost/path/to/file1.txt")
+ };
+ }
+
+ public void change() {
+ ResourceHandler[] handlers;
+ synchronized (m_handlers) {
+ handlers = (ResourceHandler[]) m_handlers.keySet().toArray(new ResourceHandler[m_handlers.size()]);
+ }
+ for (int i = 0; i < m_resources.length; i++) {
+ for (int j = 0; j < handlers.length; j++) {
+ ResourceHandler handler = handlers[j];
+ handler.changed(m_resources[i]);
+ }
+ }
+ }
+
+ public void add(ServiceReference ref, ResourceHandler handler) {
+ String filterString = (String) ref.getProperty("filter");
+ Filter filter = null;
+ if (filterString != null) {
+ try {
+ filter = m_context.createFilter(filterString);
+ }
+ catch (InvalidSyntaxException e) {
+ 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 == null || filter.match(ResourceUtil.createProperties(m_resources[i]))) {
+ handler.added(m_resources[i]);
+ }
+ }
+ }
+
+ public void remove(ServiceReference ref, ResourceHandler handler) {
+ 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 == null || filter.match(ResourceUtil.createProperties(m_resources[i]))) {
+ handler.removed(m_resources[i]);
+ }
+ }
+ }
+
+ 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());
+ }
+ }
+}