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));
+        }
+    }
+}