Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: Limitations of argument type inferral from target method signature #1269

Closed
KaiStapel opened this issue Nov 18, 2020 · 9 comments · Fixed by #1275
Closed

Question: Limitations of argument type inferral from target method signature #1269

KaiStapel opened this issue Nov 18, 2020 · 9 comments · Fixed by #1275

Comments

@KaiStapel
Copy link
Contributor

This is just a question: In #390 you created the mechanism for inferring the argument type from the target method signature. You introduced the following limitation:

Methods with a single unannotated parameter or one with a @payload annotation
are considered for this feature.

What is the reasoning for limiting the feature for "single unannotated parameter" methods as opposed to simply "single parameter" methods?

I constantly run into the problem of breaking my listeners when I add a @Valid annotation to the input parameter. Then I have to analyze for hours what the problem is until I find the solution of also having to add a @Payload annotation to make it work again.

This is why I came to this question. Is this limitation really necessary? If so, why?

@KaiStapel KaiStapel changed the title Question: Argument type inferral from target method limitations Question: Limitations of argument type inferral from target method signature Nov 18, 2020
@artembilan
Copy link
Member

Would you mind to show the code produces the problem for you?

I'm not sure what drove you to use @Valid since @RabbitListener doesn't do any validation...

The reason for the limitation is really about a multi-parameter methods when we can't decide which one is intended for payload injection.

@KaiStapel
Copy link
Contributor Author

KaiStapel commented Nov 18, 2020

Env: Spring Boot 2.3.4.RELEASE with spring-boot-starter-amqp

Before (working):

  @RabbitListener(queues = "some-queue")
  public void listen(SomeDto someDto) {
    [...]
  }

Then (broken):

  @RabbitListener(queues = "some-queue")
  public void listen(@Valid SomeDto someDto) {
    [...]
  }

Finally (working again):

  @RabbitListener(queues = "some-queue")
  public void listen(@Valid @Payload SomeDto someDto) {
    [...]
  }

The @Valid annotation triggers Dto validation. I believe this is outside of @RabbitListener and has nothing to do with this question besides being an annotation that breakes the documented limitation "with a single unannotated parameter".

The reason for the limitation is really about a multi-parameter methods when we can't decide which one is intended for payload injection.

Exactly, if the reason is about multi-parameter methods, then why are you introducing the requirement for "no annotation" for single parameter methods? This is exactly my question.

@artembilan
Copy link
Member

Well, we probably didn't think about any other possible annotation on the single parameter.
That's why my question to you: why do you use that @Valid annotation since it doesn't trigger anything when this method is called by the Listener Container? How that DTO validation is triggered? Are you sure that you can confirm the validation is working for you at this point?

We indeed going to look into the solution and take away that limitation, so you still can use @Valid without @Payload.

@KaiStapel
Copy link
Contributor Author

That would be great.

I am pretty sure this annotation works, but I can only guess how exactly. Probably some AOP magic that uses the annotation to wrap the class.

There are even tutorials out there that are suggesting the double annotation. They probably all have gone through the same route:

The second link suggests that there is validation within the Spring Messaging framework: See DefaultMessageHandlerMethodFactory#setValidator()

@artembilan
Copy link
Member

Ah, right! I forgot that @RabbitListener logic is based on the DefaultMessageHandlerMethodFactory.
Thanks for nailing this concern!
Yes, I think we will revise the logic for skipping a @Payload annotation requirement when the param is single.

If you have an idea what and where to do already, feel free to contribute the fix!

@KaiStapel
Copy link
Contributor Author

@artembilan Please have a look at my proposal #1270. I did not have time to change/extend tests though. Sorry.

@garyrussell
Copy link
Contributor

FYI, we have test cases at the message handler level, but not for the type inference:

public void validatePayload(@Validated String payload) {
invocations.put("validatePayload", true);
}

@Test
public void validatePayloadValid() throws Exception {
String methodName = "validatePayload";
DefaultMessageHandlerMethodFactory customFactory = new DefaultMessageHandlerMethodFactory();
customFactory.setValidator(testValidator("invalid value"));
initializeFactory(customFactory);
Method method = getListenerMethod(methodName, String.class);
MessagingMessageListenerAdapter listener = createInstance(customFactory, method);
Channel channel = mock(Channel.class);
listener.onMessage(MessageTestUtils.createTextMessage("test"), channel); // test is a valid value
assertListenerMethodInvocation(sample, methodName);
}
@Test
public void validatePayloadInvalid() {
DefaultMessageHandlerMethodFactory customFactory = new DefaultMessageHandlerMethodFactory();
customFactory.setValidator(testValidator("invalid value"));
Method method = getListenerMethod("validatePayload", String.class);
MessagingMessageListenerAdapter listener = createInstance(customFactory, method);
Channel channel = mock(Channel.class);
assertThatThrownBy(() -> listener.onMessage(MessageTestUtils.createTextMessage("invalid value"), channel))
.isInstanceOf(ListenerExecutionFailedException.class);
}

@garyrussell
Copy link
Contributor

I found a regression caused by this #1215 while testing this; so I will take this over.

garyrussell added a commit to garyrussell/spring-amqp that referenced this issue Nov 25, 2020
Resolves spring-projects#1269

Previously, a parameter annotated with a "foreign" annoation (e.g. `@Validated`)
would not be considered as an eligible payload conversion target; it must also
have been annotated with `@Payload`.

- remove the check for zero annotations
- add a check that the method is not annotated with both `@Payload` and `@Header`
  - ignore if it does, with a warn log, to be consistent with previous behavior.
  - in a future release we might consider this to be fatal.

**cherry-pick to 2.2.x**
@garyrussell
Copy link
Contributor

Regression was a false alarm. New PR issued.

artembilan pushed a commit that referenced this issue Nov 25, 2020
Resolves #1269

Previously, a parameter annotated with a "foreign" annoation (e.g. `@Validated`)
would not be considered as an eligible payload conversion target; it must also
have been annotated with `@Payload`.

- remove the check for zero annotations
- add a check that the method is not annotated with both `@Payload` and `@Header`
  - ignore if it does, with a warn log, to be consistent with previous behavior.
  - in a future release we might consider this to be fatal.

**cherry-pick to 2.2.x**
artembilan pushed a commit that referenced this issue Nov 25, 2020
Resolves #1269

Previously, a parameter annotated with a "foreign" annoation (e.g. `@Validated`)
would not be considered as an eligible payload conversion target; it must also
have been annotated with `@Payload`.

- remove the check for zero annotations
- add a check that the method is not annotated with both `@Payload` and `@Header`
  - ignore if it does, with a warn log, to be consistent with previous behavior.
  - in a future release we might consider this to be fatal.

**cherry-pick to 2.2.x**
KaiStapel pushed a commit to KaiStapel/spring-amqp that referenced this issue Jan 5, 2021
…ated parameter type inference"

This reverts commit 040ffb2.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants