From daccb0db6ddfaaefd4d2a2ebd663cb5e886daf83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Sun, 6 Apr 2025 12:44:02 +0200 Subject: [PATCH] Allow SmartEndpointInterceptor to be added as regular interceptor Previously, only SmartEndpointInterceptor instances declared as bean were considered by AbstractEndpointMapping. Given that it extends from EndpointInterceptor, this meant that adding them using the regular setEndpointInterceptor mean they were not considered as such, that is added irrespective of their `shouldIntercept` contract. This commit considers both list as holding SmartEndpointInterceptor instances. As a result, these can now be added using regular hook points, such as WsConfigurer. Closes gh-1130 --- .../mapping/AbstractEndpointMapping.java | 40 +++++++++----- .../endpoint/mapping/EndpointMappingTest.java | 53 +++++++++++++++++++ 2 files changed, 79 insertions(+), 14 deletions(-) diff --git a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractEndpointMapping.java b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractEndpointMapping.java index ab712d80..ff005c88 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractEndpointMapping.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractEndpointMapping.java @@ -112,6 +112,26 @@ public abstract class AbstractEndpointMapping extends ApplicationObjectSupport i */ @Override public final EndpointInvocationChain getEndpoint(MessageContext messageContext) throws Exception { + Object endpoint = resoleEndpoint(messageContext); + if (endpoint == null) { + return null; + } + List interceptors = new ArrayList<>(); + if (this.interceptors != null) { + interceptors.addAll(Arrays.stream(this.interceptors) + .filter(interceptor -> shouldIntercept(interceptor, messageContext, endpoint)) + .toList()); + } + if (this.smartInterceptors != null) { + interceptors.addAll(Arrays.stream(this.smartInterceptors) + .filter(interceptor -> shouldIntercept(interceptor, messageContext, endpoint)) + .toList()); + } + return createEndpointInvocationChain(messageContext, endpoint, + interceptors.toArray(new EndpointInterceptor[0])); + } + + private Object resoleEndpoint(MessageContext messageContext) throws Exception { Object endpoint = getEndpointInternal(messageContext); if (endpoint == null) { endpoint = this.defaultEndpoint; @@ -125,22 +145,14 @@ public abstract class AbstractEndpointMapping extends ApplicationObjectSupport i return null; } } + return endpoint; + } - List interceptors = new ArrayList<>(); - if (this.interceptors != null) { - interceptors.addAll(Arrays.asList(this.interceptors)); + private boolean shouldIntercept(EndpointInterceptor interceptor, MessageContext messageContext, Object endpoint) { + if (interceptor instanceof SmartEndpointInterceptor smartEndpointInterceptor) { + return smartEndpointInterceptor.shouldIntercept(messageContext, endpoint); } - - if (this.smartInterceptors != null) { - for (SmartEndpointInterceptor smartInterceptor : this.smartInterceptors) { - if (smartInterceptor.shouldIntercept(messageContext, endpoint)) { - interceptors.add(smartInterceptor); - } - } - } - - return createEndpointInvocationChain(messageContext, endpoint, - interceptors.toArray(new EndpointInterceptor[0])); + return true; } /** diff --git a/spring-ws-core/src/test/java/org/springframework/ws/server/endpoint/mapping/EndpointMappingTest.java b/spring-ws-core/src/test/java/org/springframework/ws/server/endpoint/mapping/EndpointMappingTest.java index e92e1974..38cc1da7 100644 --- a/spring-ws-core/src/test/java/org/springframework/ws/server/endpoint/mapping/EndpointMappingTest.java +++ b/spring-ws-core/src/test/java/org/springframework/ws/server/endpoint/mapping/EndpointMappingTest.java @@ -25,10 +25,14 @@ import org.springframework.ws.context.DefaultMessageContext; import org.springframework.ws.context.MessageContext; import org.springframework.ws.server.EndpointInterceptor; import org.springframework.ws.server.EndpointInvocationChain; +import org.springframework.ws.server.SmartEndpointInterceptor; import org.springframework.ws.server.endpoint.interceptor.DelegatingSmartEndpointInterceptor; import org.springframework.ws.server.endpoint.interceptor.EndpointInterceptorAdapter; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; /** * Test case for {@link AbstractEndpointMapping}. @@ -124,6 +128,55 @@ public class EndpointMappingTest { assertThat(result.getInterceptors()[1]).isInstanceOf(MySmartEndpointInterceptor.class); } + @Test + void smartEndpointInterceptorAddedOnlyIfNecessary() throws Exception { + StaticApplicationContext applicationContext = new StaticApplicationContext(); + Object endpoint = new Object(); + SmartEndpointInterceptor firstInterceptor = mock(SmartEndpointInterceptor.class); + given(firstInterceptor.shouldIntercept(this.messageContext, endpoint)).willReturn(false); + applicationContext.registerBean("first", SmartEndpointInterceptor.class, () -> firstInterceptor); + SmartEndpointInterceptor secondInterceptor = mock(SmartEndpointInterceptor.class); + given(secondInterceptor.shouldIntercept(this.messageContext, endpoint)).willReturn(true); + applicationContext.registerBean("second", SmartEndpointInterceptor.class, () -> secondInterceptor); + + AbstractEndpointMapping mapping = new AbstractEndpointMapping() { + @Override + protected Object getEndpointInternal(MessageContext givenRequest) { + assertThat(givenRequest).isEqualTo(EndpointMappingTest.this.messageContext); + return endpoint; + } + }; + mapping.setApplicationContext(applicationContext); + EndpointInvocationChain result = mapping.getEndpoint(this.messageContext); + assertThat(result).isNotNull(); + assertThat(result.getInterceptors()).singleElement().isSameAs(secondInterceptor); + verify(firstInterceptor).shouldIntercept(this.messageContext, endpoint); + verify(secondInterceptor).shouldIntercept(this.messageContext, endpoint); + } + + @Test + void smartEndpointInterceptorSetAsInterceptorAreHandled() throws Exception { + Object endpoint = new Object(); + SmartEndpointInterceptor firstInterceptor = mock(SmartEndpointInterceptor.class); + given(firstInterceptor.shouldIntercept(this.messageContext, endpoint)).willReturn(false); + SmartEndpointInterceptor secondInterceptor = mock(SmartEndpointInterceptor.class); + given(secondInterceptor.shouldIntercept(this.messageContext, endpoint)).willReturn(true); + + AbstractEndpointMapping mapping = new AbstractEndpointMapping() { + @Override + protected Object getEndpointInternal(MessageContext givenRequest) { + assertThat(givenRequest).isEqualTo(EndpointMappingTest.this.messageContext); + return endpoint; + } + }; + mapping.setInterceptors(new EndpointInterceptor[] { firstInterceptor, secondInterceptor }); + EndpointInvocationChain result = mapping.getEndpoint(this.messageContext); + assertThat(result).isNotNull(); + assertThat(result.getInterceptors()).singleElement().isSameAs(secondInterceptor); + verify(firstInterceptor).shouldIntercept(this.messageContext, endpoint); + verify(secondInterceptor).shouldIntercept(this.messageContext, endpoint); + } + @Test public void endpointBeanName() throws Exception {