Completed support for using dynamic proxies as generic aspects. Added a test to validate this behaviour.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@946137 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyActivatorBase.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyActivatorBase.java
index 736972c..2685e87 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyActivatorBase.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyActivatorBase.java
@@ -163,6 +163,9 @@
public Service createAspectService(Class serviceInterface, String serviceFilter, int ranking, Object factory, String factoryCreateMethod, Dictionary properties) {
return m_manager.createAspectService(serviceInterface, serviceFilter, ranking, factory, factoryCreateMethod, properties);
}
+ public Service createAspectService(Class serviceInterface, String serviceFilter, int ranking, Object factory, String factoryCreateMethod, String attributeName, Dictionary properties) {
+ return m_manager.createAspectService(serviceInterface, serviceFilter, ranking, factory, factoryCreateMethod, attributeName, properties);
+ }
public Service createAdapterService(Class serviceInterface, String serviceFilter, String adapterInterface, Object adapterImplementation, Dictionary adapterProperties) {
return m_manager.createAdapterService(serviceInterface, serviceFilter, adapterInterface, adapterImplementation, adapterProperties);
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java
index 36f86b8..c704526 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java
@@ -33,9 +33,9 @@
import org.apache.felix.dm.impl.AdapterImpl;
import org.apache.felix.dm.impl.AspectImpl;
import org.apache.felix.dm.impl.BundleAdapterImpl;
+import org.apache.felix.dm.impl.FactoryConfigurationAdapterImpl;
import org.apache.felix.dm.impl.FactoryConfigurationAdapterMetaTypeImpl;
import org.apache.felix.dm.impl.Logger;
-import org.apache.felix.dm.impl.FactoryConfigurationAdapterImpl;
import org.apache.felix.dm.impl.ResourceAdapterImpl;
import org.apache.felix.dm.impl.ServiceImpl;
import org.apache.felix.dm.impl.dependencies.BundleDependencyImpl;
@@ -201,6 +201,15 @@
.setCallbacks("added", "removed")
);
}
+ public Service createAspectService(Class serviceInterface, String serviceFilter, int ranking, Object factory, String factoryCreateMethod, String attributeName, Dictionary aspectProperties) {
+ return createService()
+ .setImplementation(new AspectImpl(serviceInterface, serviceFilter, ranking, factory, factoryCreateMethod, attributeName, aspectProperties))
+ .add(createServiceDependency()
+ .setService(serviceInterface, createAspectFilter(serviceFilter))
+ .setAutoConfig(false)
+ .setCallbacks("added", "removed")
+ );
+ }
private String createAspectFilter(String filter) {
// we only want to match services which are not themselves aspects
if (filter == null || filter.length() == 0) {
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectImpl.java
index 230886d..72c1436 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectImpl.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectImpl.java
@@ -37,6 +37,7 @@
private final Object m_factory;
private final String m_factoryCreateMethod;
private final int m_ranking;
+ private final String m_attributeName;
public AspectImpl(Class serviceInterface, String serviceFilter, int ranking, Object aspectImplementation, Dictionary properties) {
m_serviceInterface = serviceInterface;
@@ -46,6 +47,7 @@
m_factory = null;
m_factoryCreateMethod = null;
m_ranking = ranking;
+ m_attributeName = null;
}
public AspectImpl(Class serviceInterface, String serviceFilter, int ranking, Object factory, String factoryCreateMethod, Dictionary properties) {
@@ -56,6 +58,18 @@
m_aspectProperties = properties;
m_aspectImplementation = null;
m_ranking = ranking;
+ m_attributeName = null;
+ }
+
+ public AspectImpl(Class serviceInterface, String serviceFilter, int ranking, Object factory, String factoryCreateMethod, String attributeName, Dictionary properties) {
+ m_serviceInterface = serviceInterface;
+ m_serviceFilter = serviceFilter;
+ m_factory = factory;
+ m_factoryCreateMethod = factoryCreateMethod;
+ m_attributeName = attributeName;
+ m_aspectProperties = properties;
+ m_aspectImplementation = null;
+ m_ranking = ranking;
}
public Service createService(Object[] properties) {
@@ -80,13 +94,25 @@
List dependencies = m_service.getDependencies();
dependencies.remove(0);
if (m_aspectImplementation == null) {
- return m_manager.createService()
- .setInterface(m_serviceInterface.getName(), props)
- .setFactory(m_factory, m_factoryCreateMethod)
- .add(dependencies)
- .add(m_manager.createServiceDependency()
- .setService(m_serviceInterface, createAspectFilter(m_serviceFilter))
- .setRequired(true));
+ if (m_attributeName == null) {
+ return m_manager.createService()
+ .setInterface(m_serviceInterface.getName(), props)
+ .setFactory(m_factory, m_factoryCreateMethod)
+ .add(dependencies)
+ .add(m_manager.createServiceDependency()
+ .setService(m_serviceInterface, createAspectFilter(m_serviceFilter))
+ .setRequired(true));
+ }
+ else {
+ return m_manager.createService()
+ .setInterface(m_serviceInterface.getName(), props)
+ .setFactory(m_factory, m_factoryCreateMethod)
+ .add(dependencies)
+ .add(m_manager.createServiceDependency()
+ .setService(m_serviceInterface, createAspectFilter(m_serviceFilter))
+ .setAutoConfig(m_attributeName)
+ .setRequired(true));
+ }
}
else {
return m_manager.createService()
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/InvocationUtil.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/InvocationUtil.java
index c2b6208..b2e70c2 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/InvocationUtil.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/InvocationUtil.java
@@ -20,7 +20,7 @@
throw new NoSuchMethodException(methodName);
}
- public static void invokeMethod(Object object, Class clazz, String name, Class[][] signatures, Object[][] parameters, boolean isSuper) throws NoSuchMethodException, InvocationTargetException, IllegalArgumentException, IllegalAccessException {
+ public static Object invokeMethod(Object object, Class clazz, String name, Class[][] signatures, Object[][] parameters, boolean isSuper) throws NoSuchMethodException, InvocationTargetException, IllegalArgumentException, IllegalAccessException {
if (object == null) {
throw new IllegalArgumentException("Instance cannot be null");
}
@@ -34,8 +34,7 @@
m = clazz.getDeclaredMethod(name, signature);
if (!(isSuper && Modifier.isPrivate(m.getModifiers()))) {
m.setAccessible(true);
- m.invoke(object, parameters[i]);
- return;
+ return m.invoke(object, parameters[i]);
}
}
catch (NoSuchMethodException e) {
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/ServiceImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/ServiceImpl.java
index 9a0d33e..36381c4 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/ServiceImpl.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/ServiceImpl.java
@@ -684,8 +684,10 @@
}
else {
try {
- Method m = factory.getClass().getDeclaredMethod(m_instanceFactoryCreateMethod, null);
- m_serviceInstance = m.invoke(factory, null);
+// Method m = factory.getClass().getDeclaredMethod(m_instanceFactoryCreateMethod, null);
+// m_serviceInstance = m.invoke(factory, null);
+//
+ m_serviceInstance = InvocationUtil.invokeMethod(factory, factory.getClass(), m_instanceFactoryCreateMethod, new Class[][] {{}}, new Object[][] {{}}, false);
}
catch (Exception e) {
m_logger.log(Logger.LOG_ERROR, "Could not create service instance using factory " + factory + " method " + m_instanceFactoryCreateMethod + ".", e);
diff --git a/dependencymanager/test/src/test/java/org/apache/felix/dm/test/DynamicProxyAspectTest.java b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/DynamicProxyAspectTest.java
new file mode 100644
index 0000000..6728239
--- /dev/null
+++ b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/DynamicProxyAspectTest.java
@@ -0,0 +1,191 @@
+/*
+ * 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.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import junit.framework.Assert;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.service.Service;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.Configuration;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.framework.BundleContext;
+
+@RunWith(JUnit4TestRunner.class)
+public class DynamicProxyAspectTest 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 testImplementGenericAspectWithDynamicProxy(BundleContext context) {
+ DependencyManager m = new DependencyManager(context);
+ // helper class that ensures certain steps get executed in sequence
+ Ensure e = new Ensure();
+
+ // create two service providers, each providing a different service interface
+ Service sp1 = m.createService().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+ Service sp2 = m.createService().setImplementation(new ServiceProvider2(e)).setInterface(ServiceInterface2.class.getName(), null);
+
+ // create a dynamic proxy based aspect and hook it up to both services
+ Service a1 = m.createAspectService(ServiceInterface.class, null, 10, new Factory(e, ServiceInterface.class, "ServiceInterfaceProxy"), "create", "m_service", null);
+ Service a2 = m.createAspectService(ServiceInterface2.class, null, 10, new Factory(e, ServiceInterface2.class, "ServiceInterfaceProxy2"), "create", "m_service", null);
+
+ // create a client that invokes a method on boths services, validate that it goes
+ // through the proxy twice
+ Service sc = m.createService()
+ .setImplementation(new ServiceConsumer(e))
+ .add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true))
+ .add(m.createServiceDependency().setService(ServiceInterface2.class).setRequired(true))
+ ;
+
+ // register both producers, validate that both services are started
+ m.add(sp1);
+ e.waitForStep(1, 2000);
+ m.add(sp2);
+ e.waitForStep(2, 2000);
+
+ // add both aspects, and validate that both instances have been created
+ m.add(a1);
+ m.add(a2);
+ e.waitForStep(4, 4000);
+
+ // add the client, which will automatically invoke both services
+ m.add(sc);
+
+ // wait until both services have been invoked
+ e.waitForStep(6, 4000);
+
+ // make sure the proxy has been called twice
+ Assert.assertEquals("Proxy should have been invoked this many times.", 2, DynamicProxyHandler.getCounter());
+ }
+
+ static interface ServiceInterface {
+ public void invoke(Runnable run);
+ }
+
+ static interface ServiceInterface2 {
+ public void invoke(Runnable run);
+ }
+
+ static class ServiceProvider implements ServiceInterface {
+ private final Ensure m_ensure;
+ public ServiceProvider(Ensure e) {
+ m_ensure = e;
+ }
+ public void start() {
+ m_ensure.step(1);
+ }
+ public void invoke(Runnable run) {
+ run.run();
+ }
+ }
+
+ static class ServiceProvider2 implements ServiceInterface2 {
+ private final Ensure m_ensure;
+ public ServiceProvider2(Ensure ensure) {
+ m_ensure = ensure;
+ }
+ public void start() {
+ m_ensure.step(2);
+ }
+ public void invoke(Runnable run) {
+ run.run();
+ }
+ }
+
+ static class ServiceConsumer implements Runnable {
+ private volatile ServiceInterface m_service;
+ private volatile ServiceInterface2 m_service2;
+ 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_service.invoke(Ensure.createRunnableStep(m_ensure, 5));
+ m_service2.invoke(Ensure.createRunnableStep(m_ensure, 6));
+ }
+ }
+
+ static class DynamicProxyHandler implements InvocationHandler {
+ public volatile Object m_service; // ISSUE, we cannot inject into "Object" at the moment
+ private final String m_label;
+ private static volatile int m_counter = 0;
+
+ public DynamicProxyHandler(String label) {
+ m_label = label;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if (m_service == null) {
+ Assert.fail("No service was injected into dynamic proxy handler " + m_label);
+ }
+ Method m = m_service.getClass().getMethod(method.getName(), method.getParameterTypes());
+ if (m == null) {
+ Assert.fail("No method " + method.getName() + " was found in instance " + m_service + " in dynamic proxy handler " + m_label);
+ }
+ m_counter++;
+ return m.invoke(m_service, args);
+ }
+
+ public static int getCounter() {
+ return m_counter;
+ }
+ }
+
+ static class Factory {
+ private final String m_label;
+ private Class m_class;
+ private final Ensure m_ensure;
+
+ public Factory(Ensure ensure, Class clazz, String label) {
+ m_ensure = ensure;
+ m_class = clazz;
+ m_label = label;
+ }
+
+ public Object create() {
+ m_ensure.step();
+ return Proxy.newProxyInstance(m_class.getClassLoader(), new Class[] { m_class }, new DynamicProxyHandler(m_label));
+ }
+ }
+}