You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by dj...@apache.org on 2010/07/13 01:52:52 UTC

svn commit: r963535 - in /incubator/aries/trunk/jndi: ./ jndi-url/ jndi-url/src/main/java/org/apache/aries/jndi/services/ jndi-url/src/main/java/org/apache/aries/jndi/url/ jndi-url/src/test/java/org/apache/aries/jndi/url/

Author: djencks
Date: Mon Jul 12 23:52:51 2010
New Revision: 963535

URL: http://svn.apache.org/viewvc?rev=963535&view=rev
Log:
ARIES-356 Make jndi proxy creation more flexible with cglib, and make proxy creation optional in the aries: namespace

Modified:
    incubator/aries/trunk/jndi/jndi-url/pom.xml
    incubator/aries/trunk/jndi/jndi-url/src/main/java/org/apache/aries/jndi/services/ServiceHelper.java
    incubator/aries/trunk/jndi/jndi-url/src/main/java/org/apache/aries/jndi/url/ServiceRegistryContext.java
    incubator/aries/trunk/jndi/jndi-url/src/main/java/org/apache/aries/jndi/url/ServiceRegistryListContext.java
    incubator/aries/trunk/jndi/jndi-url/src/test/java/org/apache/aries/jndi/url/ServiceRegistryContextTest.java
    incubator/aries/trunk/jndi/pom.xml

Modified: incubator/aries/trunk/jndi/jndi-url/pom.xml
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jndi/jndi-url/pom.xml?rev=963535&r1=963534&r2=963535&view=diff
==============================================================================
--- incubator/aries/trunk/jndi/jndi-url/pom.xml (original)
+++ incubator/aries/trunk/jndi/jndi-url/pom.xml Mon Jul 12 23:52:51 2010
@@ -59,6 +59,11 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.cglib</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
             <groupId>org.apache.aries.jndi</groupId>
             <artifactId>org.apache.aries.jndi.core</artifactId>
             <scope>test</scope>

Modified: incubator/aries/trunk/jndi/jndi-url/src/main/java/org/apache/aries/jndi/services/ServiceHelper.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jndi/jndi-url/src/main/java/org/apache/aries/jndi/services/ServiceHelper.java?rev=963535&r1=963534&r2=963535&view=diff
==============================================================================
--- incubator/aries/trunk/jndi/jndi-url/src/main/java/org/apache/aries/jndi/services/ServiceHelper.java (original)
+++ incubator/aries/trunk/jndi/jndi-url/src/main/java/org/apache/aries/jndi/services/ServiceHelper.java Mon Jul 12 23:52:51 2010
@@ -22,20 +22,26 @@ import java.lang.ref.WeakReference;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.lang.reflect.Proxy;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
 import javax.naming.NamingException;
 
+import net.sf.cglib.proxy.Dispatcher;
+import net.sf.cglib.proxy.Enhancer;
 import org.apache.aries.jndi.url.OsgiName;
 import org.apache.aries.util.BundleToClassLoaderAdapter;
 import org.osgi.framework.Bundle;
@@ -70,6 +76,7 @@ public final class ServiceHelper
     /** The cache to purge */
     private final ConcurrentMap<ServiceKey, WeakReference<Object>> cache;
 
+
     public CacheClearoutListener(ConcurrentMap<ServiceKey, WeakReference<Object>> pc)
     {
       cache = pc;
@@ -153,7 +160,7 @@ public final class ServiceHelper
     }
   }
 
-  private static class JNDIServiceDamper implements InvocationHandler
+  private static class JNDIServiceDamper implements Callable<Object>
   {
     private BundleContext ctx;
     private ServicePair pair;
@@ -171,8 +178,7 @@ public final class ServiceHelper
       dynamic = d;
     }
 
-    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
-    {
+    public Object call() throws NamingException {
       if (pair == null || pair.ref.getBundle() == null) {
         if (dynamic) {
           pair = findService(ctx, interfaceName, filter);
@@ -184,12 +190,7 @@ public final class ServiceHelper
       if (pair == null) {
         throw new ServiceException(interfaceName, ServiceException.UNREGISTERED);
       }
-
-      try {
-        return method.invoke(pair.service, args);
-      } catch (InvocationTargetException ite) {
-        throw ite.getTargetException();
-      }
+      return pair.service;
     }
   }
 
@@ -201,11 +202,12 @@ public final class ServiceHelper
 
   /** A cache of proxies returned to the client */
   private static final ConcurrentMap<ServiceKey, WeakReference<Object>> proxyCache = new ConcurrentHashMap<ServiceKey, WeakReference<Object>>();
-  private static final CacheClearoutListener cacheClearoutListener = new CacheClearoutListener(
-      proxyCache);
+  private static final CacheClearoutListener cacheClearoutListener = new CacheClearoutListener(proxyCache);
+
+  private static ProxyFactory proxyFactory;
 
   public static Object getService(BundleContext ctx, OsgiName lookupName, String id,
-      boolean dynamicRebind, Map<String, Object> env) throws NamingException
+                                  boolean dynamicRebind, Map<String, Object> env, boolean requireProxy) throws NamingException
   {
     Object result = null;
 
@@ -239,14 +241,14 @@ public final class ServiceHelper
     }
 
     if (pair != null) {
-      result = proxy(interfaceName, filter, dynamicRebind, ctx, pair);
+      result = proxy(interfaceName, filter, dynamicRebind, ctx, pair, requireProxy);
     }
 
     return result;
   }
 
   private static Object proxy(final String interface1, final String filter, final boolean rebind,
-      final BundleContext ctx, final ServicePair pair)
+                              final BundleContext ctx, final ServicePair pair, final boolean requireProxy)
   {
     Object result = null;
     Bundle owningBundle = ctx.getBundle();
@@ -266,7 +268,7 @@ public final class ServiceHelper
       result = AccessController.doPrivileged(new PrivilegedAction<Object>() {
         public Object run()
         {
-          return proxyPriviledged(interface1, filter, rebind, ctx, pair);
+          return proxyPriviledged(interface1, filter, rebind, ctx, pair, requireProxy);
         }
       });
 
@@ -280,8 +282,7 @@ public final class ServiceHelper
     return result;
   }
 
-  private static Object proxyPriviledged(String interface1, String filter, boolean dynamicRebind,
-      BundleContext ctx, ServicePair pair)
+  private static Object proxyPriviledged(String interface1, String filter, boolean dynamicRebind, BundleContext ctx, ServicePair pair, boolean requireProxy)
   {
     String[] interfaces = null;
     if (interface1 != null) {
@@ -317,14 +318,37 @@ public final class ServiceHelper
     Bundle serviceProviderBundle = pair.ref.getBundle();
     Bundle owningBundle = ctx.getBundle();
 
+    ProxyFactory proxyFactory = getProxyFactory();
+
     for (String interfaceName : interfaces) {
       try {
         Class<?> potentialClass = serviceProviderBundle.loadClass(interfaceName);
-
+        if (Modifier.isFinal(potentialClass.getModifiers())) {
+          if (requireProxy) {
+            continue;
+          }
+          return pair.service;
+        }
+        if (!potentialClass.isInterface()) {
+          if (requireProxy && !proxyFactory.proxiesClasses()) {
+            continue;
+          }
+          try {
+            potentialClass.getConstructor(new Class[0]);
+          } catch (NoSuchMethodException e) {
+            if (requireProxy) {
+              continue;
+            }
+            return pair.service;
+          }
+        }
         if (pair.ref.isAssignableTo(owningBundle, interfaceName)) {
           clazz.add(potentialClass);
         }
       } catch (ClassNotFoundException e) {
+        if (!requireProxy) {
+          return pair.service;
+        }
       }
     }
 
@@ -332,15 +356,22 @@ public final class ServiceHelper
       throw new IllegalArgumentException(Arrays.asList(interfaces).toString());
     }
 
-    InvocationHandler ih = new JNDIServiceDamper(ctx, interface1, filter, pair, dynamicRebind);
+    Callable<Object> ih = new JNDIServiceDamper(ctx, interface1, filter, pair, dynamicRebind);
 
     // The ClassLoader needs to be able to load the service interface
     // classes so it needs to be
     // wrapping the service provider bundle. The class is actually defined
     // on this adapter.
 
-    return Proxy.newProxyInstance(new BundleToClassLoaderAdapter(serviceProviderBundle), clazz
-        .toArray(new Class<?>[clazz.size()]), ih);
+    try {
+      return proxyFactory.createProxy(new BundleToClassLoaderAdapter(serviceProviderBundle), clazz
+          .toArray(new Class<?>[clazz.size()]), ih);
+    } catch (IllegalArgumentException e) {
+      if (requireProxy) {
+        throw e;
+      }
+      return pair.service;
+    }
   }
 
   private static ServicePair findService(BundleContext ctx, String interface1, String filter)
@@ -422,10 +453,106 @@ public final class ServiceHelper
       pair.ref = ref;
       pair.service = service;
 
-      result = proxy(null, null, false, ctx, pair);
+      result = proxy(null, null, false, ctx, pair, true);
     }
 
     return result;
   }
 
+  protected static synchronized ProxyFactory getProxyFactory() {
+    if (proxyFactory == null) {
+      try {
+        // Try load load a cglib class (to make sure it's actually available
+        // then create the cglib factory
+        ServiceHelper.class.getClassLoader().loadClass("net.sf.cglib.proxy.Enhancer");
+        proxyFactory = new CgLibProxyFactory();
+      } catch (Throwable t) {
+        proxyFactory = new JdkProxyFactory();
+      }
+    }
+    return proxyFactory;
+  }
+
+  private static Class[] getInterfaces(Class[] classes) {
+    Set<Class> interfaces = new HashSet<Class>();
+    for (Class clazz : classes) {
+      if (clazz.isInterface()) {
+        interfaces.add(clazz);
+      }
+    }
+    return toClassArray(interfaces);
+  }
+
+  private static Class[] toClassArray(Set<Class> classes) {
+    return classes.toArray(new Class[classes.size()]);
+  }
+
+  public static interface ProxyFactory {
+
+    Object createProxy(ClassLoader classLoader, Class[] classes, Callable<Object> dispatcher);
+
+    boolean proxiesClasses();
+  }
+
+  public static class JdkProxyFactory implements ProxyFactory {
+
+    public Object createProxy(final ClassLoader classLoader, final Class[] classes, final Callable<Object> dispatcher) {
+      return Proxy.newProxyInstance(classLoader, getInterfaces(classes), new InvocationHandler() {
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+          try {
+            return method.invoke(dispatcher.call(), args);
+          } catch (InvocationTargetException ite) {
+            throw ite.getTargetException();
+          }
+        }
+      });
+    }
+
+    public boolean proxiesClasses() {
+      return false;
+    }
+
+  }
+
+  public static class CgLibProxyFactory implements ProxyFactory {
+
+    public Object createProxy(final ClassLoader classLoader, final Class[] classes, final Callable<Object> dispatcher) {
+      Enhancer e = new Enhancer();
+      e.setClassLoader(classLoader);
+      e.setSuperclass(getTargetClass(classes));
+      e.setInterfaces(getInterfaces(classes));
+      e.setInterceptDuringConstruction(false);
+      e.setCallback(new Dispatcher() {
+        public Object loadObject() throws Exception {
+          return dispatcher.call();
+        }
+      });
+      e.setUseFactory(false);
+      return e.create();
+    }
+
+    public boolean proxiesClasses() {
+      return true;
+    }
+
+    protected Class<?> getTargetClass(Class<?>[] interfaceNames) {
+      // Only allow class proxying if specifically asked to
+      Class<?> root = Object.class;
+      for (Class<?> clazz : interfaceNames) {
+        if (!clazz.isInterface()) {
+          if (root.isAssignableFrom(clazz)) {
+            root = clazz;
+          } else if (clazz.isAssignableFrom(root)) {
+            //nothing to do, root is correct
+          } else {
+            throw new IllegalArgumentException("Classes " + root.getClass().getName() + " and " + clazz.getName() + " are not in the same hierarchy");
+          }
+        }
+      }
+      return root;
+    }
+
+  }
+
+
 }

Modified: incubator/aries/trunk/jndi/jndi-url/src/main/java/org/apache/aries/jndi/url/ServiceRegistryContext.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jndi/jndi-url/src/main/java/org/apache/aries/jndi/url/ServiceRegistryContext.java?rev=963535&r1=963534&r2=963535&view=diff
==============================================================================
--- incubator/aries/trunk/jndi/jndi-url/src/main/java/org/apache/aries/jndi/url/ServiceRegistryContext.java (original)
+++ incubator/aries/trunk/jndi/jndi-url/src/main/java/org/apache/aries/jndi/url/ServiceRegistryContext.java Mon Jul 12 23:52:51 2010
@@ -96,7 +96,7 @@ public class ServiceRegistryContext exte
                 }
             } else if ((OsgiName.SERVICE_PATH.equals(pathFragment) && OsgiName.OSGI_SCHEME.equals(schemeName))
                        || (OsgiName.SERVICES_PATH.equals(pathFragment) && OsgiName.ARIES_SCHEME.equals(schemeName))) {
-                result = ServiceHelper.getService(callerContext, validName, null, true, env);
+                result = ServiceHelper.getService(callerContext, validName, null, true, env, OsgiName.OSGI_SCHEME.equals(schemeName));
             } else if (OsgiName.SERVICE_LIST_PATH.equals(pathFragment)) {
                 result = new ServiceRegistryListContext(callerContext, env, validName);
             } else {

Modified: incubator/aries/trunk/jndi/jndi-url/src/main/java/org/apache/aries/jndi/url/ServiceRegistryListContext.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jndi/jndi-url/src/main/java/org/apache/aries/jndi/url/ServiceRegistryListContext.java?rev=963535&r1=963534&r2=963535&view=diff
==============================================================================
--- incubator/aries/trunk/jndi/jndi-url/src/main/java/org/apache/aries/jndi/url/ServiceRegistryListContext.java (original)
+++ incubator/aries/trunk/jndi/jndi-url/src/main/java/org/apache/aries/jndi/url/ServiceRegistryListContext.java Mon Jul 12 23:52:51 2010
@@ -169,7 +169,7 @@ public class ServiceRegistryListContext 
   {
     Object result = null;
     
-    result = ServiceHelper.getService(callerContext, parentName, name, false, env);
+    result = ServiceHelper.getService(callerContext, parentName, name, false, env, true);
     
     if (result == null) {
       throw new NameNotFoundException(name.toString());

Modified: incubator/aries/trunk/jndi/jndi-url/src/test/java/org/apache/aries/jndi/url/ServiceRegistryContextTest.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jndi/jndi-url/src/test/java/org/apache/aries/jndi/url/ServiceRegistryContextTest.java?rev=963535&r1=963534&r2=963535&view=diff
==============================================================================
--- incubator/aries/trunk/jndi/jndi-url/src/test/java/org/apache/aries/jndi/url/ServiceRegistryContextTest.java (original)
+++ incubator/aries/trunk/jndi/jndi-url/src/test/java/org/apache/aries/jndi/url/ServiceRegistryContextTest.java Mon Jul 12 23:52:51 2010
@@ -476,7 +476,7 @@ public class ServiceRegistryContextTest
     Binding bnd = ne.nextElement();
     
     assertEquals(String.valueOf(reg.getReference().getProperty(Constants.SERVICE_ID)), bnd.getName());
-    assertTrue("Class name not correct. Was: " + bnd.getClassName(), bnd.getClassName().contains("Proxy"));
+    assertTrue("Class name not correct. Was: " + bnd.getClassName(), bnd.getClassName().contains("Proxy") || bnd.getClassName().contains("EnhancerByCGLIB"));
     
     Runnable r = (Runnable) bnd.getObject();
     
@@ -492,7 +492,7 @@ public class ServiceRegistryContextTest
     bnd = ne.nextElement();
     
     assertEquals(String.valueOf(reg2.getReference().getProperty(Constants.SERVICE_ID)), bnd.getName());
-    assertTrue("Class name not correct. Was: " + bnd.getClassName(), bnd.getClassName().contains("Proxy"));
+    assertTrue("Class name not correct. Was: " + bnd.getClassName(), bnd.getClassName().contains("Proxy") || bnd.getClassName().contains("EnhancerByCGLIB"));
     
     r = (Runnable) bnd.getObject();
     

Modified: incubator/aries/trunk/jndi/pom.xml
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/jndi/pom.xml?rev=963535&r1=963534&r2=963535&view=diff
==============================================================================
--- incubator/aries/trunk/jndi/pom.xml (original)
+++ incubator/aries/trunk/jndi/pom.xml Mon Jul 12 23:52:51 2010
@@ -75,6 +75,13 @@
             	<artifactId>org.apache.aries.util</artifactId>
             	<version>0.2-incubating-SNAPSHOT</version>
             </dependency>
+            <!-- Use an OSGi enabled cglib version, so that BND can find the version information
+                 and we can use it in integration tests -->
+            <dependency>
+                <groupId>org.apache.servicemix.bundles</groupId>
+                <artifactId>org.apache.servicemix.bundles.cglib</artifactId>
+                <version>2.1_3_4</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>