You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by th...@apache.org on 2019/02/24 03:11:16 UTC

[tapestry-5] 02/02: TAP5-2582: Service creation for Hibernate Session results in

This is an automated email from the ASF dual-hosted git repository.

thiagohp pushed a commit to branch 5.4.x
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git

commit b8f4a672fe5a5a6709bb5ec7cd89c0c7ff0cf0be
Author: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
AuthorDate: Sun Feb 24 00:10:42 2019 -0300

    TAP5-2582: Service creation for Hibernate Session results in
    
    ClassFormatError: Duplicate method name&signature
---
 .../internal/services/PlasticProxyFactoryImpl.java | 14 ++-
 .../ioc/services/PlasticProxyFactory.java          | 15 ++++
 .../internal/plastic/PlasticClassImpl.java         | 99 ++++++++++++++++++++--
 .../org/apache/tapestry5/plastic/PlasticClass.java | 11 +++
 .../apache/tapestry5/plastic/PlasticManager.java   | 56 +++++++++++-
 .../apache/tapestry5/ioc/internal/ModuleImpl.java  |  7 +-
 .../specs/AspectInterceptorBuilderImplSpec.groovy  |  2 +-
 .../groovy/ioc/specs/GeneralIntegrationSpec.groovy | 24 ++++++
 .../tapestry5/ioc/internal/AdviceModule.java       |  4 +
 9 files changed, 214 insertions(+), 18 deletions(-)

diff --git a/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/PlasticProxyFactoryImpl.java b/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/PlasticProxyFactoryImpl.java
index dc5397c..f1b3f67 100644
--- a/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/PlasticProxyFactoryImpl.java
+++ b/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/PlasticProxyFactoryImpl.java
@@ -65,16 +65,24 @@ public class PlasticProxyFactoryImpl implements PlasticProxyFactory
     @Override
     public <T> ClassInstantiator<T> createProxy(Class<T> interfaceType, Class<? extends T> implementationType, PlasticClassTransformer callback)
     {
-        return manager.createProxy(interfaceType, implementationType, callback);
+        return createProxy(interfaceType, implementationType, callback, true);
     }
-    
+
+    @Override
+    public <T> ClassInstantiator<T> createProxy(Class<T> interfaceType,
+            Class<? extends T> implementationType,
+            PlasticClassTransformer callback,
+            boolean introduceInterface) {
+        return manager.createProxy(interfaceType, implementationType, callback, introduceInterface);
+    }
+
+
     @Override
     public <T> ClassInstantiator<T> createProxy(Class<T> interfaceType, PlasticClassTransformer callback)
     {
         return manager.createProxy(interfaceType, callback);
     }
     
-    
     @Override
     public <T> PlasticClassTransformation<T> createProxyTransformation(Class<T> interfaceType,
             Class<? extends T> implementationType)
diff --git a/commons/src/main/java/org/apache/tapestry5/ioc/services/PlasticProxyFactory.java b/commons/src/main/java/org/apache/tapestry5/ioc/services/PlasticProxyFactory.java
index 75e93e4..99bcc55 100644
--- a/commons/src/main/java/org/apache/tapestry5/ioc/services/PlasticProxyFactory.java
+++ b/commons/src/main/java/org/apache/tapestry5/ioc/services/PlasticProxyFactory.java
@@ -63,6 +63,20 @@ public interface PlasticProxyFactory extends PlasticClassListenerHub
      *         configures the proxy
      * @return instantiator that can be used to create an instance of the proxy class
      */
+    @IncompatibleChange(release = "5.4.5", details = "TAP5-2528")
+    <T> ClassInstantiator<T> createProxy(Class<T> interfaceType, Class<? extends T> implementationType, PlasticClassTransformer callback, boolean introduceInterface);
+
+    /**
+     * Same as <code>createProxy(interfacetype, implementationType, callback, true)</code>
+     *
+     * @param interfaceType
+     *         interface implemented by proxy
+     * @param implementationType
+     *         a class that implements the interfaceType. It can be null.
+     * @param callback
+     *         configures the proxy
+     * @return instantiator that can be used to create an instance of the proxy class
+     */
     @IncompatibleChange(release = "5.4", details = "TAP5-2029")
     <T> ClassInstantiator<T> createProxy(Class<T> interfaceType, Class<? extends T> implementationType, PlasticClassTransformer callback);
 
@@ -156,4 +170,5 @@ public interface PlasticProxyFactory extends PlasticClassListenerHub
      * @since 5.3.3
      */
     void clearCache();
+
 }
diff --git a/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassImpl.java b/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassImpl.java
index 264825a..a7c1fc5 100644
--- a/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassImpl.java
+++ b/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassImpl.java
@@ -621,7 +621,7 @@ public class PlasticClassImpl extends Lockable implements PlasticClass, Internal
 
         introduceInterface(interfaceType);
 
-        for (Method m : interfaceType.getMethods())
+        for (Method m : getUniqueMethods(interfaceType))
         {
             introduceMethod(m).delegateTo(field);
         }
@@ -968,7 +968,6 @@ public class PlasticClassImpl extends Lockable implements PlasticClass, Internal
     public PlasticMethod introduceMethod(Method method)
     {
         check();
-
         return introduceMethod(new MethodDescription(method));
     }
 
@@ -1401,9 +1400,13 @@ public class PlasticClassImpl extends Lockable implements PlasticClass, Internal
         return new InstructionBuilderImpl(description, mn, nameCache);
     }
 
-    @Override
     public Set<PlasticMethod> introduceInterface(Class interfaceType)
     {
+        return introduceInterface(interfaceType, null);
+    }
+    
+    private Set<PlasticMethod> introduceInterface(Class interfaceType, PlasticMethod method)
+    {
         check();
 
         assert interfaceType != null;
@@ -1431,14 +1434,22 @@ public class PlasticClassImpl extends Lockable implements PlasticClass, Internal
         addClassAnnotations(interfaceClassNode);
 
         Set<PlasticMethod> introducedMethods = new HashSet<PlasticMethod>();
+        Set<Method> alreadyIntroducedMethods = new HashSet<>();
 
-        for (Method m : interfaceType.getMethods())
+        Method[] sortedMethods = interfaceType.getMethods();
+        Arrays.sort(sortedMethods, METHOD_COMPARATOR);
+        for (Method m : sortedMethods)
         {
             MethodDescription description = new MethodDescription(m);
 
-            if (!isMethodImplemented(description) && !isDefaultMethod(m))
+            if (!isMethodImplemented(description) && !isDefaultMethod(m) && !contains(alreadyIntroducedMethods, m))
             {
-                introducedMethods.add(introduceMethod(m));
+                PlasticMethod introducedMethod = introduceMethod(m);
+                introducedMethods.add(introducedMethod);
+                if (method != null) {
+                    introducedMethod.delegateTo(method);
+                }
+                alreadyIntroducedMethods.add(m);
             }
         }
 
@@ -1446,6 +1457,30 @@ public class PlasticClassImpl extends Lockable implements PlasticClass, Internal
 
         return introducedMethods;
     }
+    
+    @Override
+    public PlasticClass proxyInterface(Class interfaceType, PlasticMethod method)
+    {
+        check();
+        assert method != null;
+
+        introduceInterface(interfaceType, method);
+        
+        return this;
+    }
+
+    private boolean contains(Set<Method> alreadyIntroducedMethods, Method m) {
+        boolean contains = false;
+        for (Method method : alreadyIntroducedMethods) 
+        {
+            if (METHOD_COMPARATOR.compare(method, m) == 0)
+            {
+                contains = true;
+                break;
+            }
+        }
+        return false;
+    }
 
     @Override
     public PlasticClass addToString(final String toStringValue)
@@ -1525,6 +1560,58 @@ public class PlasticClassImpl extends Lockable implements PlasticClass, Internal
             pool.setFieldReadInstrumentation(classNode.name, fieldName, fi);
         }
     }
+    
+    final private MethodComparator METHOD_COMPARATOR = new MethodComparator();
+    
+    final private class MethodComparator implements Comparator<Method> 
+    {
+
+        @Override
+        public int compare(Method o1, Method o2) 
+        {
+            
+            int comparison = o1.getName().compareTo(o2.getName());
+            
+            if (comparison == 0) 
+            {
+                comparison = o1.getParameterTypes().length - o2.getParameterTypes().length;
+            }
+            
+            if (comparison == 0) 
+            {
+                final int count = o1.getParameterTypes().length;
+                for (int i = 0; i < count; i++) 
+                {
+                    Class p1 = o1.getParameterTypes()[i];
+                    Class p2 = o1.getParameterTypes()[i];
+                    if (!p1.equals(p2)) 
+                    {
+                        comparison = p1.getName().compareTo(p2.getName());
+                        break;
+                    }
+                }
+            }
+            return comparison;
+        }
+    }
+    
+    private List<Method> getUniqueMethods(Class interfaceType) 
+    {
+        final List<Method> unique = new ArrayList<>(Arrays.asList(interfaceType.getMethods()));
+        Collections.sort(unique, METHOD_COMPARATOR);
+        Method last = null;
+        Iterator<Method> iterator = unique.iterator();
+        while (iterator.hasNext()) 
+        {
+            Method m = iterator.next();
+            if (last != null && METHOD_COMPARATOR.compare(m, last) == 0)
+            {
+                last = m;
+                iterator.remove();
+            }
+        }
+        return unique;
+    }
 
     @Override
     public String toString()
diff --git a/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticClass.java b/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticClass.java
index e212fe5..2168456 100644
--- a/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticClass.java
+++ b/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticClass.java
@@ -163,6 +163,17 @@ public interface PlasticClass extends AnnotationAccess
      * @return this plastic class, for further configuration
      */
     PlasticClass proxyInterface(Class interfaceType, PlasticField field);
+    
+    /**
+     * Introduces the interface, and then invokes {@link PlasticMethod#delegateTo(PlasticMethod)} on each method
+     * defined by the interface.
+     *
+     * @param interfaceType defines the interface to proxy
+     * @param method        method to delegate to
+     * @return this plastic class, for further configuration
+     * @since 5.4.4
+     */
+    PlasticClass proxyInterface(Class interfaceType, PlasticMethod method);
 
     /**
      * Conditionally adds an implementation of <code>toString()</code> to the class, but only if it is not already
diff --git a/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticManager.java b/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticManager.java
index 5f31755..74acf21 100644
--- a/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticManager.java
+++ b/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticManager.java
@@ -234,6 +234,21 @@ public class PlasticManager implements PlasticClassListenerHub
      * 
      * @param interfaceType
      *            class to extend from, which must be a class, not an interface
+     * @param callback
+     *            used to configure the new class
+     * @return the instantiator, which allows instances of the new class to be created
+     * @see #createProxyTransformation(Class, Class)
+     */
+    public <T> ClassInstantiator<T> createProxy(Class<T> interfaceType, PlasticClassTransformer callback, boolean introduceInterface)
+    {
+        return createProxy(interfaceType, null, callback, introduceInterface);
+    }
+
+    /**
+     * Creates an entirely new class. The class extends from Object and implements the provided interface.
+     * 
+     * @param interfaceType
+     *            class to extend from, which must be a class, not an interface
      * @param implementationType
      *            class that implements interfaceType. It can be null. 
      * @param callback
@@ -244,9 +259,30 @@ public class PlasticManager implements PlasticClassListenerHub
      */
     public <T> ClassInstantiator<T> createProxy(Class<T> interfaceType, Class<? extends T> implementationType, PlasticClassTransformer callback)
     {
+        return createProxy(interfaceType, implementationType, callback, true);
+    }
+    
+    /**
+     * Creates an entirely new class. The class extends from Object and implements the provided interface.
+     * 
+     * @param interfaceType
+     *            class to extend from, which must be a class, not an interface
+     * @param implementationType
+     *            class that implements interfaceType. It can be null. 
+     * @param callback
+     *            used to configure the new class
+     * @param introduceInterface
+     *            whether to introduce the interface to the Plastic class or not.
+     * @return the instantiator, which allows instances of the new class to be created
+     * @see #createProxyTransformation(Class, Class)
+     * @since 5.4.5
+     */
+    public <T> ClassInstantiator<T> createProxy(Class<T> interfaceType, Class<? extends T> implementationType, PlasticClassTransformer callback,
+            boolean introduceInterface)
+    {
         assert callback != null;
 
-        PlasticClassTransformation<T> transformation = createProxyTransformation(interfaceType, implementationType);
+        PlasticClassTransformation<T> transformation = createProxyTransformation(interfaceType, implementationType, introduceInterface);
 
         callback.transform(transformation.getPlasticClass());
 
@@ -254,6 +290,14 @@ public class PlasticManager implements PlasticClassListenerHub
     }
 
     /**
+     * Returns <code>createProxyTransformation(interfaceType, implementationType, true)</code>
+     */
+    public <T> PlasticClassTransformation<T> createProxyTransformation(Class interfaceType, Class implementationType)
+    {
+        return createProxyTransformation(interfaceType, implementationType, true);
+    }
+
+    /**
      * Creates the underlying {@link PlasticClassTransformation} for an interface proxy. This should only be
      * used in the cases where encapsulating the PlasticClass construction into a {@linkplain PlasticClassTransformer
      * callback} is not feasible (which is the case for some of the older APIs inside Tapestry IoC).
@@ -262,9 +306,12 @@ public class PlasticManager implements PlasticClassListenerHub
      *            class proxy will extend from
      * @param implementationType
      *            class that implements interfaceType. It can be null.
+     * @param introduceInterface
+     *            whether <code>result.getPlasticClass().introduceInterface(interfaceType);</code> should
+     *            be called or not.
      * @return transformation from which an instantiator may be created
      */
-    public <T> PlasticClassTransformation<T> createProxyTransformation(Class interfaceType, Class implementationType)
+    public <T> PlasticClassTransformation<T> createProxyTransformation(Class interfaceType, Class implementationType, boolean introduceInterface)
     {
         assert interfaceType != null;
 
@@ -279,7 +326,10 @@ public class PlasticManager implements PlasticClassListenerHub
         PlasticClassTransformation<T> result = 
                 pool.createTransformation("java.lang.Object", name, implementationClassName);
 
-        result.getPlasticClass().introduceInterface(interfaceType);
+        if (introduceInterface)
+        {
+            result.getPlasticClass().introduceInterface(interfaceType);
+        }
 
         return result;
     }
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ModuleImpl.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ModuleImpl.java
index 48bf24d..98a2f8c 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ModuleImpl.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ModuleImpl.java
@@ -509,10 +509,7 @@ public class ModuleImpl implements Module
                     }
                 });
 
-                for (Method m : serviceInterface.getMethods())
-                {
-                    plasticClass.introduceMethod(m).delegateTo(delegateMethod);
-                }
+                plasticClass.proxyInterface(serviceInterface, delegateMethod);
 
                 plasticClass.introduceMethod(WRITE_REPLACE).changeImplementation(new InstructionBuilderCallback()
                 {
@@ -525,7 +522,7 @@ public class ModuleImpl implements Module
 
                 plasticClass.addToString(description);
             }
-        });
+        }, false);
 
         return instantiator.newInstance();
     }
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/AspectInterceptorBuilderImplSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/AspectInterceptorBuilderImplSpec.groovy
index e543482..b614c38 100644
--- a/tapestry-ioc/src/test/groovy/ioc/specs/AspectInterceptorBuilderImplSpec.groovy
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/AspectInterceptorBuilderImplSpec.groovy
@@ -1,6 +1,6 @@
 package ioc.specs
 
-import org.apache.commons.lang.StringUtils
+import org.apache.commons.lang3.StringUtils
 import org.apache.tapestry5.ioc.internal.services.TextTransformer
 import org.apache.tapestry5.ioc.services.AspectDecorator
 import org.apache.tapestry5.plastic.MethodAdvice
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/GeneralIntegrationSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/GeneralIntegrationSpec.groovy
index 1e30463..8fda9a4 100644
--- a/tapestry-ioc/src/test/groovy/ioc/specs/GeneralIntegrationSpec.groovy
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/GeneralIntegrationSpec.groovy
@@ -1,7 +1,12 @@
 package ioc.specs
 
+import org.apache.tapestry5.ioc.ObjectLocator
+import org.apache.tapestry5.ioc.Registry
+import org.apache.tapestry5.ioc.RegistryBuilder
 import org.apache.tapestry5.ioc.internal.services.Bean
 import org.apache.tapestry5.ioc.services.PropertyAccess
+import org.hibernate.Session
+import org.hibernate.cfg.Configuration
 
 class GeneralIntegrationSpec extends AbstractSharedRegistrySpecification {
 
@@ -20,6 +25,25 @@ class GeneralIntegrationSpec extends AbstractSharedRegistrySpecification {
     b.value == 99
     pa.get(b, "value") == 99
   }
+  
+  def "Avoiding duplicated method implementation in service proxies"() {
+      when:
+      Registry registry = RegistryBuilder.buildAndStartupRegistry(TestModule.class);
+      then:
+      // Throws exception without fix.
+      Session session = registry.getService(Session.class);
+  }
 
+  public static final class TestModule 
+  {
+      public static Session buildHibernateSession(
+          ObjectLocator objectLocator
+      ) {
+          return new Configuration()
+              .configure("hibernate.cfg.xml")
+              .buildSessionFactory()
+              .openSession();
+      }
+  }
 
 }
diff --git a/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/AdviceModule.java b/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/AdviceModule.java
index 0e0a186..ea2e576 100644
--- a/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/AdviceModule.java
+++ b/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/AdviceModule.java
@@ -14,8 +14,11 @@
 package org.apache.tapestry5.ioc.internal;
 
 import org.apache.tapestry5.ioc.MethodAdviceReceiver;
+import org.apache.tapestry5.ioc.ObjectLocator;
 import org.apache.tapestry5.ioc.ServiceBinder;
 import org.apache.tapestry5.ioc.annotations.Advise;
+import org.hibernate.Session;
+import org.hibernate.cfg.Configuration;
 
 public class AdviceModule
 {
@@ -45,4 +48,5 @@ public class AdviceModule
             final MethodAdviceReceiver methodAdviceReceiver) {
         methodAdviceReceiver.adviseAllMethods(new TestAdvice());
     }
+    
 }