You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@camel.apache.org by "Alex Paransky (JIRA)" <ji...@apache.org> on 2015/10/22 19:17:27 UTC

[jira] [Updated] (CAMEL-9243) Invocation of Bean fails when Bean extends and abstract which implements the actual method

     [ https://issues.apache.org/jira/browse/CAMEL-9243?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Alex Paransky updated CAMEL-9243:
---------------------------------
    Description: 
The issue described here does NOT exist in 2.15.2 and only manifests in 2.15.3.

With the following definition of a Bean:

{code}
    public interface MyBaseInterface {
        @Handler
        String hello(@Body String hi);
    }

    public abstract static class MyAbstractBean implements MyBaseInterface {
        public String hello(@Body String hi) {
            return "Hello " + hi;
        }
        public String doCompute(String input) {
            fail("Should not invoke me");
            return null;
        }
    }

    public static class MyConcreteBean extends MyAbstractBean {
    }

{code}

The following test case will fail to invoke the proper method:

{code}
public class BeanHandlerMethodTest extends ContextTestSupport {

    public void testInterfaceBeanMethod() throws Exception {
        BeanInfo info = new BeanInfo(context, MyConcreteBean.class);

        Exchange exchange = new DefaultExchange(context);
        MyNoDummyBean pojo = new MyNoDummyBean();
        MethodInvocation mi = info.createInvocation(pojo, exchange);
        assertNotNull(mi);
        assertEquals("hello", mi.getMethod().getName());
    }
{code}

The issue is how BeanInfo.introspect determines which methods are available to be invoked.

At line 344, if the class is public, the interface methods are added to the list:

{code}
        if (Modifier.isPublic(clazz.getModifiers())) {
            // add additional interface methods
            List<Method> extraMethods = getInterfaceMethods(clazz);
            for (Method target : extraMethods) {
                for (Method source : methods) {
                    if (ObjectHelper.isOverridingMethod(source, target, false)) {
                        overrides.add(target);
                    }
                }
            }
            // remove all the overrides methods
            extraMethods.removeAll(overrides);
            methods.addAll(extraMethods);
        }
{code}

However, all the methods from the interface are "abstract".  Later, when the real implementation is encountered as the code crawls up the tree, the abstract method is not replaced:

Line 390:

{code}
        MethodInfo existingMethodInfo = overridesExistingMethod(methodInfo);
        if (existingMethodInfo != null) {
            LOG.trace("This method is already overridden in a subclass, so the method from the sub class is preferred: {}", existingMethodInfo);
            return existingMethodInfo;
        }
{code}

Finally, during the invocation, the following was added as part of 2.15.3 release:

Line 561:

{code}
        removeAllAbstractMethods(localOperationsWithBody);
        removeAllAbstractMethods(localOperationsWithNoBody);
        removeAllAbstractMethods(localOperationsWithCustomAnnotation);
        removeAllAbstractMethods(localOperationsWithHandlerAnnotation);
{code}

As a result, the abstract method is removed and not invoked.

I think the fix should be to see if the existingMethodInfo references an "abstract' method and if it does and methodInfo does not, replace the existingMethodInfo with methodInfo in the collection.

This would preserve the preferences implied with the rest of the code while properly replacing the abstract method with their proper implementations.


  was:
With the following definition of a Bean:

{code}
    public interface MyBaseInterface {
        @Handler
        String hello(@Body String hi);
    }

    public abstract static class MyAbstractBean implements MyBaseInterface {
        public String hello(@Body String hi) {
            return "Hello " + hi;
        }
        public String doCompute(String input) {
            fail("Should not invoke me");
            return null;
        }
    }

    public static class MyConcreteBean extends MyAbstractBean {
    }

{code}

The following test case will fail to invoke the proper method:

{code}
public class BeanHandlerMethodTest extends ContextTestSupport {

    public void testInterfaceBeanMethod() throws Exception {
        BeanInfo info = new BeanInfo(context, MyConcreteBean.class);

        Exchange exchange = new DefaultExchange(context);
        MyNoDummyBean pojo = new MyNoDummyBean();
        MethodInvocation mi = info.createInvocation(pojo, exchange);
        assertNotNull(mi);
        assertEquals("hello", mi.getMethod().getName());
    }
{code}

The issue is how BeanInfo.introspect determines which methods are available to be invoked.

At line 344, if the class is public, the interface methods are added to the list:

{code}
        if (Modifier.isPublic(clazz.getModifiers())) {
            // add additional interface methods
            List<Method> extraMethods = getInterfaceMethods(clazz);
            for (Method target : extraMethods) {
                for (Method source : methods) {
                    if (ObjectHelper.isOverridingMethod(source, target, false)) {
                        overrides.add(target);
                    }
                }
            }
            // remove all the overrides methods
            extraMethods.removeAll(overrides);
            methods.addAll(extraMethods);
        }
{code}

However, all the methods from the interface are "abstract".  Later, when the real implementation is encountered as the code crawls up the tree, the abstract method is not replaced:

Line 390:

{code}
        MethodInfo existingMethodInfo = overridesExistingMethod(methodInfo);
        if (existingMethodInfo != null) {
            LOG.trace("This method is already overridden in a subclass, so the method from the sub class is preferred: {}", existingMethodInfo);
            return existingMethodInfo;
        }
{code}

Finally, during the invocation, the following was added as part of 2.15.3 release:

Line 561:

{code}
        removeAllAbstractMethods(localOperationsWithBody);
        removeAllAbstractMethods(localOperationsWithNoBody);
        removeAllAbstractMethods(localOperationsWithCustomAnnotation);
        removeAllAbstractMethods(localOperationsWithHandlerAnnotation);
{code}

As a result, the abstract method is removed and not invoked.

I think the fix should be to see if the existingMethodInfo references an "abstract' method and if it does and methodInfo does not, replace the existingMethodInfo with methodInfo in the collection.

This would preserve the preferences implied with the rest of the code while properly replacing the abstract method with their proper implementations.



> Invocation of Bean fails when Bean extends and abstract which implements the actual method
> ------------------------------------------------------------------------------------------
>
>                 Key: CAMEL-9243
>                 URL: https://issues.apache.org/jira/browse/CAMEL-9243
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-core
>    Affects Versions: 2.15.3
>            Reporter: Alex Paransky
>
> The issue described here does NOT exist in 2.15.2 and only manifests in 2.15.3.
> With the following definition of a Bean:
> {code}
>     public interface MyBaseInterface {
>         @Handler
>         String hello(@Body String hi);
>     }
>     public abstract static class MyAbstractBean implements MyBaseInterface {
>         public String hello(@Body String hi) {
>             return "Hello " + hi;
>         }
>         public String doCompute(String input) {
>             fail("Should not invoke me");
>             return null;
>         }
>     }
>     public static class MyConcreteBean extends MyAbstractBean {
>     }
> {code}
> The following test case will fail to invoke the proper method:
> {code}
> public class BeanHandlerMethodTest extends ContextTestSupport {
>     public void testInterfaceBeanMethod() throws Exception {
>         BeanInfo info = new BeanInfo(context, MyConcreteBean.class);
>         Exchange exchange = new DefaultExchange(context);
>         MyNoDummyBean pojo = new MyNoDummyBean();
>         MethodInvocation mi = info.createInvocation(pojo, exchange);
>         assertNotNull(mi);
>         assertEquals("hello", mi.getMethod().getName());
>     }
> {code}
> The issue is how BeanInfo.introspect determines which methods are available to be invoked.
> At line 344, if the class is public, the interface methods are added to the list:
> {code}
>         if (Modifier.isPublic(clazz.getModifiers())) {
>             // add additional interface methods
>             List<Method> extraMethods = getInterfaceMethods(clazz);
>             for (Method target : extraMethods) {
>                 for (Method source : methods) {
>                     if (ObjectHelper.isOverridingMethod(source, target, false)) {
>                         overrides.add(target);
>                     }
>                 }
>             }
>             // remove all the overrides methods
>             extraMethods.removeAll(overrides);
>             methods.addAll(extraMethods);
>         }
> {code}
> However, all the methods from the interface are "abstract".  Later, when the real implementation is encountered as the code crawls up the tree, the abstract method is not replaced:
> Line 390:
> {code}
>         MethodInfo existingMethodInfo = overridesExistingMethod(methodInfo);
>         if (existingMethodInfo != null) {
>             LOG.trace("This method is already overridden in a subclass, so the method from the sub class is preferred: {}", existingMethodInfo);
>             return existingMethodInfo;
>         }
> {code}
> Finally, during the invocation, the following was added as part of 2.15.3 release:
> Line 561:
> {code}
>         removeAllAbstractMethods(localOperationsWithBody);
>         removeAllAbstractMethods(localOperationsWithNoBody);
>         removeAllAbstractMethods(localOperationsWithCustomAnnotation);
>         removeAllAbstractMethods(localOperationsWithHandlerAnnotation);
> {code}
> As a result, the abstract method is removed and not invoked.
> I think the fix should be to see if the existingMethodInfo references an "abstract' method and if it does and methodInfo does not, replace the existingMethodInfo with methodInfo in the collection.
> This would preserve the preferences implied with the rest of the code while properly replacing the abstract method with their proper implementations.



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)