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

MethodParameter.equals is too coarse-grained for its use in HandlerMethodArgumentResolverComposite #23352

Closed
garyrussell opened this issue Jul 24, 2019 · 5 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) in: web Issues in web modules (web, webmvc, webflux, websocket) status: backported An issue that has been backported to maintenance branches type: bug A general bug
Milestone

Comments

@garyrussell
Copy link
Contributor

garyrussell commented Jul 24, 2019

	@Override
	public int hashCode() {
		return (getExecutable().hashCode() * 31 + this.parameterIndex);
	}

HandlerMethodArgumentResolverComposite uses MethodParameter as a cache key for HandlerMethodArgumentResolver.

Consider:

@FunctionalInterface
public interface GenericHandler<P> {

	Object handle(P payload, MessageHeaders headers);

}
		public static class MapHandler implements GenericHandler<Map<String, String>> {

			@Override
			public String handle(Map<String, String> mapPayload, MessageHeaders messageHeaders) {
				return "Hello " + mapPayload.get("key");
			}

		}

		public static class StringHandler implements GenericHandler<String> {

			@Override
			public String handle(String stringPayload, MessageHeaders messageHeaders) {
				return stringPayload + " World!";
			}

		}

MethodParameter.executable in both cases is the interface Method:

public abstract java.lang.Object org.springframework.integration.handler.GenericHandler.handle(java.lang.Object,org.springframework.messaging.MessageHeaders)

When invoking the second method, we get a cache hit and fail with a class cast exception because the wrong argument resolver is returned.

Spring Integration recently changed to use a shared DefaultMessageHandlerMethodFactory @Bean, and hence a shared HandlerMethodArgumentResolverComposite.

We can work around this by changing the bean scope to prototype, but I think the hashCode() and equals() should include at least the containingClass and/or parameterType field.

See spring-projects/spring-integration#3004

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jul 24, 2019
garyrussell added a commit to garyrussell/spring-integration that referenced this issue Jul 24, 2019
Fixes spring-projects#3004

Change the `DefaultMessageHandlerMethodFactory` beans to prototype scope.

See spring-projects/spring-framework#23352
@garyrussell garyrussell changed the title MethodParameter hashCode() is too general MethodParameter hashCode().equals() are too coarse-grained Jul 24, 2019
@garyrussell garyrussell changed the title MethodParameter hashCode().equals() are too coarse-grained MethodParameter hashCode()/equals() are too coarse-grained Jul 24, 2019
@sbrannen sbrannen added in: core Issues in core modules (aop, beans, core, context, expression) in: web Issues in web modules (web, webmvc, webflux, websocket) for: team-attention labels Jul 25, 2019
@philwebb philwebb self-assigned this Jul 25, 2019
@philwebb philwebb removed their assignment Jul 25, 2019
artembilan pushed a commit to spring-projects/spring-integration that referenced this issue Jul 27, 2019
Fixes #3004

Change the `DefaultMessageHandlerMethodFactory` beans to prototype scope.

See spring-projects/spring-framework#23352
@jhoeller jhoeller self-assigned this Jul 29, 2019
@jhoeller jhoeller added type: bug A general bug and removed for: team-attention status: waiting-for-triage An issue we've not yet triaged or decided on labels Jul 29, 2019
@jhoeller jhoeller added this to the 5.1.9 milestone Jul 29, 2019
philwebb added a commit to philwebb/spring-framework that referenced this issue Jul 30, 2019
Update `MethodParameter` so that the `equals()` and `hashCode()` methods
consider both the `containingClass` and the `nestingLevel`.

Closes spring-projectsgh-23352
@jhoeller
Copy link
Contributor

I've refined MethodParameter.equals to consider containingClass and nestingLevel as well, i.e. to the degree that it can differ between resolution attempts. I intend to backport this minimal change to 5.0.x and 4.3.x as well since it really is a bug, also next to DependencyDescriptor which has such an exhaustive equals implementation already (and delegates to MethodParameter.equals underneath). Beyond that, we intend to do a more extensive revision of MethodParameter for 5.2.

philwebb added a commit to philwebb/spring-framework that referenced this issue Jul 30, 2019
Update `MethodParameter` so that the `equals()` and `hashCode()` methods
consider both the `containingClass` and the `nestingLevel`.

Closes spring-projectsgh-23352
philwebb added a commit to philwebb/spring-framework that referenced this issue Jul 30, 2019
Update `MethodParameter` so that the `equals()` and `hashCode()` methods
consider both the `containingClass` and the `nestingLevel`.

Closes spring-projectsgh-23352
@jhoeller jhoeller changed the title MethodParameter hashCode()/equals() are too coarse-grained MethodParameter equals implementation is too coarse-grained for its use in HandlerMethodArgumentResolverComposite Jul 30, 2019
@jhoeller jhoeller changed the title MethodParameter equals implementation is too coarse-grained for its use in HandlerMethodArgumentResolverComposite MethodParameter.equals is too coarse-grained for its use in HandlerMethodArgumentResolverComposite Jul 30, 2019
philwebb added a commit to philwebb/spring-framework that referenced this issue Jul 30, 2019
Update `MethodParameter` so that the `equals()` and `hashCode()` methods
consider both the `containingClass` and the `nestingLevel`.

Closes spring-projectsgh-23352
pull bot pushed a commit to pierDipi/spring-framework that referenced this issue Jul 30, 2019
@jhoeller
Copy link
Contributor

jhoeller commented Aug 1, 2019

@garyrussell A minimal fix for this through a narrowed equals implementation is in all branches now. Could you give that one a try for your purposes, ideally against 5.1.9 snapshot?

@jhoeller
Copy link
Contributor

jhoeller commented Aug 1, 2019

Doing one more pass over this since the previous fix didn't take overridden getContainingClass() implementations into account...

@jhoeller jhoeller reopened this Aug 1, 2019
@garyrussell
Copy link
Contributor Author

Could you give that one a try for your purposes, ideally against 5.1.9 snapshot?

@jhoeller I already reverted the workaround on master yesterday and all is fine; thanks!

I am building our 5.1.x against 5.1.9 snapshots and don't see any problems there either (although we only had the problem on master, when we went to a shared handler factory across multiple components).

@jhoeller
Copy link
Contributor

jhoeller commented Aug 1, 2019

Cool, good to know :-) I've also taken overridden containing classes into account now, just in cases somebody uses those on MethodParameter instances that serve as a cache key.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) in: web Issues in web modules (web, webmvc, webflux, websocket) status: backported An issue that has been backported to maintenance branches type: bug A general bug
Projects
None yet
Development

No branches or pull requests

5 participants