spring-integration icon indicating copy to clipboard operation
spring-integration copied to clipboard

MessagingMethodInvokerHelper should disregard synthetic methods

Open chrylis opened this issue 3 years ago • 4 comments

In what version(s) of Spring Integration are you seeing this issue?

5.5.14

Describe the bug

I have a service object written in Groovy that I'm providing to the handle instruction on a DSL integration flow. This service method has a single public method plus some private fields and closures; the presence of the closures causes the generation of some synthetic methods to access the private fields in the top-level class. The MessagingMethodInvokerHelper throws an exception claiming ambiguous methods because it's processing the synthetics as fallbacks.

To Reproduce

Pass a class that carries synthetic methods to handle.

Expected behavior

I expect synthetic methods to be excluded. Specifically, at MessagingMethodInvokerHelper:776, the existing condition

!Modifier.isPublic(method1.getModifiers())

should be logically equivalent to

Modifier.isSynthetic(method1.getModifiers()) || !Modifier.isPublic(method1.getModifiers())

chrylis avatar Oct 11 '22 20:10 chrylis

Interesting... You know, I'm going to port Spring Integration Groovy DSL into core project soon enough: https://github.com/spring-projects/spring-integration-extensions/tree/main/spring-integration-groovy-dsl, but your catch still makes sense. I think we better look into this utility instead in org.springframework.util.ClassUtils:

	/**
	 * Determine whether the given method is declared by the user or at least pointing to
	 * a user-declared method.
	 * <p>Checks {@link Method#isSynthetic()} (for implementation methods) as well as the
	 * {@code GroovyObject} interface (for interface methods; on an implementation class,
	 * implementations of the {@code GroovyObject} methods will be marked as synthetic anyway).
	 * Note that, despite being synthetic, bridge methods ({@link Method#isBridge()}) are considered
	 * as user-level methods since they are eventually pointing to a user-declared generic method.
	 * @param method the method to check
	 * @return {@code true} if the method can be considered as user-declared; {@code false} otherwise
	 */
	public static boolean isUserLevelMethod(Method method) {

artembilan avatar Oct 11 '22 20:10 artembilan

Any chances to see such a service in Groovy, which, as you said, comes with synthetic methods?

Thanks

artembilan avatar Oct 13 '22 20:10 artembilan

Could you clarify? Are you asking for a code sample that produces the problem? I'm not at the office, but this should reproduce the error:

@CompileStatic
class MyService {
  @Autowired
  private Dependency dependency

  void perform(Command command) {
    command.list.forEach { dependency.process(it) }
    // ^ creates a `public static synthetic Dependency pfaccess$0(MyService instance)`
  }
}


...
IntegrationFlows.from(SomeInterface).handle(myService).get()

chrylis avatar Oct 13 '22 21:10 chrylis

I have just confirmed that proposed ClassUtils.isUserLevelMethod() does the trick for such a Groovy class. There are some other issues in other places which use that MessagingMethodInvokerHelper as well, but that's different story and I'll look into that in details when my PR for Groovy DSL is merged: https://github.com/spring-projects/spring-integration/pull/3914

Thank you!

artembilan avatar Oct 14 '22 14:10 artembilan