Fix FELIX-1426
Provide a way to also inject dynamic proxy for scalar dependencies.
The ipojo.dependency.proxy.type property allows to set which kind of proxy is supported. By default, it used smart (bytecode) proxies. However some VM will not support such mechanisms. That's why, this property set to 'dynamic-proxy' allows injecting Java dynamic proxy (slower). This does not affect aggregate dependencies.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@894012 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java
index b023592..1b258b7 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java
@@ -20,6 +20,7 @@
 
 import java.lang.reflect.Array;
 import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
@@ -112,12 +113,12 @@
      * Immutable once set.
      */
     private String m_id;
-    
+
     /**
      * Do we have to inject proxy?
      */
     private boolean m_isProxy;
-    
+
     /**
      * Proxy Object.
      */
@@ -151,7 +152,7 @@
         } else {
             m_usage = null;
         }
-        
+
         m_supportNullable = nullable;
         m_di = defaultImplem;
 
@@ -313,7 +314,7 @@
             }
         }
     }
-    
+
 
     /**
      * Call 'modify' method with the service reference in parameter (if accepted).
@@ -335,7 +336,7 @@
      * Start the dependency.
      */
     public void start() {
-        
+
         if (isOptional() && !isAggregate()) {
             if (m_di == null) {
                 // If nullable are supported, create the nullable object.
@@ -372,13 +373,19 @@
                 }
             }
         }
-        
+
         if (m_isProxy) {
             if (isAggregate()) {
                 m_proxyObject = new ServiceCollection(this);
             } else {
-                ProxyFactory proxyFactory = new ProxyFactory(this.getClass().getClassLoader());
-                m_proxyObject = proxyFactory.getProxy(getSpecification(), this);
+                String type = getHandler().getInstanceManager().getContext().getProperty(DependencyHandler.PROXY_TYPE_PROPERTY);
+                if (type == null || type.equals(DependencyHandler.SMART_PROXY)) {
+                    SmartProxyFactory proxyFactory = new SmartProxyFactory(this.getClass().getClassLoader());
+                    m_proxyObject = proxyFactory.getProxy(getSpecification(), this);
+                } else {
+                    DynamicProxyFactory proxyFactory = new DynamicProxyFactory();
+                    m_proxyObject = proxyFactory.getProxy(getSpecification());
+                }
             }
         }
 
@@ -426,7 +433,7 @@
         callBindMethod(reference);
         //The method is only called when a new service arrives, or when the used one is replaced.
     }
-    
+
     /**
      * An already injected service is modified.
      * @param reference : the modified service reference.
@@ -487,7 +494,7 @@
             return Arrays.asList(refs);
         }
     }
-    
+
     /**
      * Called by the proxy to get  service objects to delegate a method.
      * On aggregate dependencies, it returns a list.
@@ -499,7 +506,7 @@
         if (! m_isProxy) {
             throw new IllegalStateException("The dependency is not a proxied dependency");
         }
-        
+
         Usage usage = (Usage) m_usage.get();
         if (usage.m_stack == 0) { // uninitialized usage.
             if (usage.m_componentStack > 0) {
@@ -534,7 +541,7 @@
                             objs.add(getService(ref));
                         }
                         return objs;
-                    } 
+                    }
                 } else { // Scalar dependency.
                     ServiceReference ref = getServiceReference();
                     if (ref != null) {
@@ -542,7 +549,7 @@
                     } else {
                         // No service available.
                         // TODO Decide what we have to do.
-                        throw new RuntimeException("Service " + getSpecification() + " unavailable"); 
+                        throw new RuntimeException("Service " + getSpecification() + " unavailable");
                     }
                 }
             }
@@ -562,7 +569,7 @@
             } else {
                 return usage.m_object;
             }
-            
+
         }
     }
 
@@ -576,7 +583,7 @@
      * @see org.apache.felix.ipojo.FieldInterceptor#onGet(java.lang.Object, java.lang.String, java.lang.Object)
      */
     public Object onGet(Object pojo, String fieldName, Object value) {
-        
+
         // Initialize the thread local object is not already touched.
         Usage usage = (Usage) m_usage.get();
         if (usage.m_stack == 0) { // uninitialized usage.
@@ -756,7 +763,7 @@
     public boolean isProxy() {
         return m_isProxy;
     }
-    
+
     public void setProxy(boolean proxy) {
         m_isProxy = proxy;
     }
@@ -770,14 +777,14 @@
         setAggregate(true);
         m_type = type;
     }
-    
+
     /**
-     * Creates proxy object for proxied scalar dependencies.
+     * Creates smart proxy object for proxied scalar dependencies.
      */
-    private class ProxyFactory extends ClassLoader {
-        
+    private class SmartProxyFactory extends ClassLoader {
+
         /**
-         * Handler classloader, used to load the temporal dependency class. 
+         * Handler classloader, used to load the temporal dependency class.
          */
         private ClassLoader m_handlerCL;
 
@@ -785,11 +792,11 @@
          * Creates the proxy classloader.
          * @param parent the handler classloader.
          */
-        public ProxyFactory(ClassLoader parent) {
+        public SmartProxyFactory(ClassLoader parent) {
             super(getHandler().getInstanceManager().getFactory().getBundleClassLoader());
             m_handlerCL = parent;
         }
-        
+
         /**
          * Loads a proxy class generated for the given (interface) class.
          * @param clazz the service specification to proxy
@@ -799,19 +806,19 @@
             byte[] clz = ProxyGenerator.dumpProxy(clazz); // Generate the proxy.
             return defineClass(clazz.getName() + "$$Proxy", clz, 0, clz.length);
         }
-        
+
         /**
          * Create a proxy object for the given specification. The proxy
-         * uses the given dependency to get the service object.  
+         * uses the given dependency to get the service object.
          * @param spec the service specification (interface)
-         * @param dep the temporal dependency used to get the service
+         * @param dep the dependency used to get the service
          * @return the proxy object.
          */
         public Object getProxy(Class spec, Dependency dep) {
             try {
                 Class clazz = getProxyClass(getSpecification());
                 Constructor constructor = clazz.getConstructor(
-                        new Class[]{clazz.getClassLoader().loadClass(Dependency.class.getName())});                                               
+                        new Class[]{clazz.getClassLoader().loadClass(Dependency.class.getName())});
                 return constructor.newInstance(new Object[] {dep});
             } catch (Throwable e) {
                 m_handler.error("Cannot create the proxy object", e);
@@ -819,7 +826,7 @@
                 return null;
             }
         }
-        
+
         /**
          * Loads the given class.
          * This class use the classloader of the specification class
@@ -837,7 +844,85 @@
             }
         }
     }
-    
-    
+
+    /**
+     * Creates java dynamic proxy object for proxied scalar dependencies.
+     */
+    private class DynamicProxyFactory implements InvocationHandler {
+
+        /**
+         * HashCode method.
+         */
+        private Method m_hashCodeMethod;
+        
+        /**
+         * Equals method.
+         */
+        private Method m_equalsMethod;
+        
+        /**
+         * toStirng method. 
+         */
+        private Method m_toStringMethod;
+        
+        /**
+         * Creates a DynamicProxyFactory.
+         */
+        public DynamicProxyFactory() {
+            try {
+                m_hashCodeMethod = Object.class.getMethod("hashCode", null);
+                m_equalsMethod = Object.class
+                    .getMethod("equals", new Class[] { Object.class });
+                m_toStringMethod = Object.class.getMethod("toString", null);
+            } catch (NoSuchMethodException e) {
+                throw new NoSuchMethodError(e.getMessage());
+            }
+        }
+        
+        /**
+         * Creates a proxy object for the given specification. The proxy
+         * uses the given dependency to get the service object.
+         * @param spec the service specification (interface)
+         * @return the proxy object.
+         */
+        public Object getProxy(Class spec) {
+            return java.lang.reflect.Proxy.newProxyInstance(
+                    getHandler().getInstanceManager().getClazz().getClassLoader(),
+                    new Class[] {spec},
+                    this);
+        }
+
+        /**
+         * Invocation Handler delegating invocation on the
+         * service object.
+         * @param proxy the proxy object
+         * @param method the method
+         * @param args the arguments
+         * @return a proxy object.
+         * @throws Exception if the invocation throws an exception
+         * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
+         */
+        public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
+            Object svc = getService();
+            Class declaringClass = method.getDeclaringClass();
+            if (declaringClass == Object.class) {
+                if (method.equals(m_hashCodeMethod)) {
+                    return new Integer(this.hashCode());
+                } else if (method.equals(m_equalsMethod)) {
+                    return proxy == args[0] ? Boolean.TRUE : Boolean.FALSE;
+                } else if (method.equals(m_toStringMethod)) {
+                    return this.toString();
+                } else {
+                    throw new InternalError(
+                            "Unexpected Object method dispatched: " + method);
+                }
+            }
+            
+            return method.invoke(svc, args);
+        }
+
+    }
+
+
 
 }
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java
index cdcee18..81ab766 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java
@@ -54,6 +54,21 @@
     public static final String PROXY_SETTINGS_PROPERTY = "ipojo.proxy";
     
     /**
+     * Proxy type property.
+     */
+    public static final String PROXY_TYPE_PROPERTY = "ipojo.proxy.type";
+    
+    /**
+     * Proxy type value: smart.
+     */
+    public static final String SMART_PROXY = "smart";
+    
+    /**
+     * Proxy type value: dynamic-proxy.
+     */
+    public static final String DYNAMIC_PROXY = "dynamic-proxy";
+    
+    /**
      * Proxy settings value: enabled.
      */
     public static final String PROXY_ENABLED = "enabled";
diff --git a/ipojo/tests/core/service-dependency/src/main/java/org/apache/felix/ipojo/test/scenarios/service/dependency/ProxyTest.java b/ipojo/tests/core/service-dependency/src/main/java/org/apache/felix/ipojo/test/scenarios/service/dependency/ProxyTest.java
index 5e9af89..8ecbca6 100644
--- a/ipojo/tests/core/service-dependency/src/main/java/org/apache/felix/ipojo/test/scenarios/service/dependency/ProxyTest.java
+++ b/ipojo/tests/core/service-dependency/src/main/java/org/apache/felix/ipojo/test/scenarios/service/dependency/ProxyTest.java
@@ -192,5 +192,42 @@
         System.setProperty(DependencyHandler.PROXY_SETTINGS_PROPERTY, DependencyHandler.PROXY_ENABLED);
 
     }
+    
+    public void testDynamicProxy() throws UnacceptableConfiguration, MissingHandlerException, ConfigurationException {
+        // Dynamic proxy
+        System.setProperty(DependencyHandler.PROXY_TYPE_PROPERTY, DependencyHandler.DYNAMIC_PROXY);
+        Properties prov = new Properties();
+        prov.put("instance.name","FooProvider1-Proxy");
+        ComponentInstance fooProvider1 = Utils.getFactoryByName(getContext(), "FooProviderType-1").createComponentInstance(prov);
+        
+        
+        Properties i1 = new Properties();
+        i1.put("instance.name","Delegator");
+        ComponentInstance instance1 = Utils.getFactoryByName(getContext(), 
+                "org.apache.felix.ipojo.test.scenarios.service.dependency.proxy.CheckServiceDelegator").createComponentInstance(i1);
+        
+        
+        ServiceReference ref = Utils.getServiceReferenceByName(context, CheckService.class.getName(), instance1.getInstanceName());
+        assertNotNull(ref);
+        CheckService cs = (CheckService) context.getService(ref);
+        
+        Properties props = cs.getProps();
+        FooService helper = (FooService) props.get("helper.fs");
+        assertNotNull(helper);
+        assertFalse(helper.toString().contains("$$Proxy")); // Dynamic proxy.
+        assertTrue(helper.toString().contains("DynamicProxyFactory"));
+        assertTrue(helper.hashCode() > 0);
+        
+        assertTrue(helper.equals(helper));
+        assertFalse(helper.equals(i1)); // This is a quite stupid test...
+
+        assertTrue(cs.check());
+        
+        fooProvider1.dispose();
+        instance1.dispose();
+        System.setProperty(DependencyHandler.PROXY_TYPE_PROPERTY, DependencyHandler.SMART_PROXY);
+
+    }
+
 
 }