Basic information
- Axon Framework version: 5.0 - 5.1
- JDK version: any
- Complete executable reproducer if available (e.g. GitHub Repo): n.A.
Steps to reproduce
1.) Create an annotated command handling component inheriting from a supertype.
2.) Implement the same @CommandHandler annotated method in both the supertype and the annotated command handling component
3.) Make the supertype command handling method private
Expected behaviour
Handler detection should throw a DuplicateCommandHandlerSubscriptionException, because the supertype method is not visible to the subtype, so it's not an override and should be recognized as unique handler.
Actual behaviour
AnnotatedHandlerInspector#initializeMessageHandlers uses java.lang.Class#getDeclaredMethods to iterate all methods and detect MessageHandlers for the inspected type.
- The order of registration is determined by the order of the method references returned by
java.lang.Class#getDeclaredMethods
- when registering a subtype that extends a supertype, after registering all discovered subtype declared methods all supertype methods will be registerd on the subtype as well (regardless of their visibility to the subtype, so also private supertype methods are registered with the subtype). This happens in a separate step after declared handler methods on the actual type are registered.
When creating an annotated message handling component, AnnotatedHandlerInspector#getUniqueHandlers is invoked. Get unique handlers takes the handlers registered for the subtype in order of registration with the AnnotatedHandlerInspector and filters them for duplicates based on the signature (name and parameter types) -> order of registration will always find the subtype method first. Due to how AnnotatedHandlerInspector#initializeMessageHandlers registers the methods declared on the actual type and only after that the supertype methods, the filtering will always prefer the subtype method and deduplicate similar supertype methods.
Thus, no DuplicateCommandHandlerSubscriptionException will be thrown.
Notes
The following table describes the inheritance scenarios for command handlers and their expected outcomes. Fixing this bug covers those cases stating reject as duplicate handler
| Base Class Method access modifier |
Subtype Class Method access modifier |
Result |
| public |
public |
subtype method overrides – no error |
| public |
protected |
invalid (weaker access privileges) |
| public |
package private* |
invalid (weaker access privileges) |
| public |
package private** |
invalid (weaker access privileges) |
| public |
private |
invalid (weaker access privileges) |
| protected |
public |
subtype method overrides – no error (same as public – public) |
| protected |
protected |
subtype method overrides – no error (same as public – public) |
| protected |
package private* |
invalid (weaker access privileges) |
| protected |
package private** |
invalid (weaker access privileges) |
| protected |
private |
invalid (weaker access privileges) |
| package private* |
public |
subtype method overrides – no error (same as public – public) |
| package private* |
protected |
subtype method overrides – no error (same as public – public) |
| package private* |
package private |
subtype method overrides – no error (same as public – public) |
| package private* |
private |
invalid (weaker access privileges) |
| package private** |
public |
reject as duplicate handler |
| package private** |
package private |
reject as duplicate handler |
| package private** |
protected |
reject as duplicate handler |
| package private** |
private |
reject as duplicate handler |
| private |
public |
reject as duplicate handler |
| private |
package private* |
reject as duplicate handler |
| private |
package private** |
reject as duplicate handler |
| private |
protected |
reject as duplicate handler |
| private |
private |
reject as duplicate handler |
* = within same package
** = in different package
Basic information
Steps to reproduce
1.) Create an annotated command handling component inheriting from a supertype.
2.) Implement the same
@CommandHandlerannotated method in both the supertype and the annotated command handling component3.) Make the supertype command handling method private
Expected behaviour
Handler detection should throw a
DuplicateCommandHandlerSubscriptionException, because the supertype method is not visible to the subtype, so it's not an override and should be recognized as unique handler.Actual behaviour
AnnotatedHandlerInspector#initializeMessageHandlersusesjava.lang.Class#getDeclaredMethodsto iterate all methods and detect MessageHandlers for the inspected type.java.lang.Class#getDeclaredMethodsWhen creating an annotated message handling component,
AnnotatedHandlerInspector#getUniqueHandlersis invoked. Get unique handlers takes the handlers registered for the subtype in order of registration with theAnnotatedHandlerInspectorand filters them for duplicates based on the signature (name and parameter types) -> order of registration will always find the subtype method first. Due to howAnnotatedHandlerInspector#initializeMessageHandlersregisters the methods declared on the actual type and only after that the supertype methods, the filtering will always prefer the subtype method and deduplicate similar supertype methods.Thus, no
DuplicateCommandHandlerSubscriptionExceptionwill be thrown.Notes
The following table describes the inheritance scenarios for command handlers and their expected outcomes. Fixing this bug covers those cases stating
reject as duplicate handler* = within same package** = in different package