diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/DelegatingHandlerMapping.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/DelegatingHandlerMapping.java index cea73c78f..568e16b5a 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/DelegatingHandlerMapping.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/DelegatingHandlerMapping.java @@ -16,6 +16,8 @@ package org.springframework.data.rest.webmvc.support; import lombok.Getter; +import lombok.NonNull; +import lombok.Value; import java.util.List; @@ -28,6 +30,8 @@ import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.UnsatisfiedServletRequestParameterException; import org.springframework.web.servlet.HandlerExecutionChain; import org.springframework.web.servlet.HandlerMapping; +import org.springframework.web.servlet.handler.MatchableHandlerMapping; +import org.springframework.web.servlet.handler.RequestMatchResult; /** * A {@link HandlerMapping} that considers a {@link List} of delegates. It will keep on traversing the delegates in case @@ -36,7 +40,7 @@ import org.springframework.web.servlet.HandlerMapping; * @author Oliver Gierke * @soundtrack Benny Greb - Stabila (Moving Parts) */ -public class DelegatingHandlerMapping implements HandlerMapping, Ordered { +public class DelegatingHandlerMapping implements HandlerMapping, Ordered, MatchableHandlerMapping { private final @Getter List delegates; @@ -67,32 +71,81 @@ public class DelegatingHandlerMapping implements HandlerMapping, Ordered { */ @Override public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { + return HandlerSelectionResult.from(request, delegates).resultOrException(); + } - Exception exception = null; + /* + * (non-Javadoc) + * @see org.springframework.web.servlet.handler.MatchableHandlerMapping#match(javax.servlet.http.HttpServletRequest, java.lang.String) + */ + @Override + public RequestMatchResult match(HttpServletRequest request, String pattern) { - for (HandlerMapping delegate : delegates) { + try { + return HandlerSelectionResult.from(request, delegates).match(pattern); + } catch (Exception o_O) { + return null; + } + } - try { + @Value + private static class HandlerSelectionResult { - HandlerExecutionChain result = delegate.getHandler(request); + @NonNull HttpServletRequest request; + HandlerMapping mapping; + HandlerExecutionChain result; + Exception ignoredException; - if (result != null) { - return result; + public static HandlerSelectionResult from(HttpServletRequest request, Iterable delegates) + throws Exception { + + Exception ignoredException = null; + + for (HandlerMapping delegate : delegates) { + + try { + + HandlerExecutionChain result = delegate.getHandler(request); + + if (result != null) { + return HandlerSelectionResult.forResult(request, delegate, result); + } + + } catch (HttpMediaTypeNotAcceptableException o_O) { + ignoredException = o_O; + } catch (HttpRequestMethodNotSupportedException o_O) { + ignoredException = o_O; + } catch (UnsatisfiedServletRequestParameterException o_O) { + ignoredException = o_O; } - - } catch (HttpMediaTypeNotAcceptableException o_O) { - exception = o_O; - } catch (HttpRequestMethodNotSupportedException o_O) { - exception = o_O; - } catch (UnsatisfiedServletRequestParameterException o_O) { - exception = o_O; } + + return HandlerSelectionResult.withoutResult(request, ignoredException); } - if (exception != null) { - throw exception; + private static HandlerSelectionResult forResult(HttpServletRequest request, HandlerMapping delegate, + HandlerExecutionChain result) { + return new HandlerSelectionResult(request, delegate, result, null); } - return null; + private static HandlerSelectionResult withoutResult(HttpServletRequest request, Exception exception) { + return new HandlerSelectionResult(request, null, null, exception); + } + + public HandlerExecutionChain resultOrException() throws Exception { + + if (ignoredException != null) { + throw ignoredException; + } + + return result; + } + + public RequestMatchResult match(String pattern) { + + return MatchableHandlerMapping.class.isInstance(mapping) // + ? ((MatchableHandlerMapping) mapping).match(request, pattern) // + : null; + } } } diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/support/DelegatingHandlerMappingUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/support/DelegatingHandlerMappingUnitTests.java index e47b69d84..c38ff0e08 100755 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/support/DelegatingHandlerMappingUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/support/DelegatingHandlerMappingUnitTests.java @@ -17,6 +17,7 @@ package org.springframework.data.rest.webmvc.support; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.util.Arrays; @@ -25,12 +26,15 @@ import javax.servlet.http.HttpServletRequest; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Answers; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.UnsatisfiedServletRequestParameterException; import org.springframework.web.servlet.HandlerMapping; +import org.springframework.web.servlet.handler.MatchableHandlerMapping; +import org.springframework.web.servlet.handler.RequestMatchResult; /** * Unit tests for {@link DelegatingHandlerMapping}. @@ -38,7 +42,7 @@ import org.springframework.web.servlet.HandlerMapping; * @author Oliver Gierke * @soundtrack Benny Greb - Stabila (Moving Parts) */ -@RunWith(MockitoJUnitRunner.class) +@RunWith(MockitoJUnitRunner.Silent.class) public class DelegatingHandlerMappingUnitTests { @Mock HandlerMapping first, second; @@ -54,6 +58,24 @@ public class DelegatingHandlerMappingUnitTests { assertHandlerTriedButExceptionThrown(mapping, UnsatisfiedServletRequestParameterException.class); } + @Test // DATAREST-1193 + public void exposesMatchabilityOfSelectedMapping() { + + // Given: + // A matching mapping that doesn't get selected + MatchableHandlerMapping third = mock(MatchableHandlerMapping.class); + doReturn(mock(RequestMatchResult.class)).when(third).match(any(), any()); + + // A matching mapping that gets selected + RequestMatchResult result = mock(RequestMatchResult.class); + MatchableHandlerMapping fourth = mock(MatchableHandlerMapping.class, Answers.RETURNS_MOCKS); + doReturn(result).when(fourth).match(any(), any()); + + DelegatingHandlerMapping mapping = new DelegatingHandlerMapping(Arrays.asList(first, second, third, fourth)); + + assertThat(mapping.match(request, "somePattern")).isEqualTo(result); + } + private final void assertHandlerTriedButExceptionThrown(HandlerMapping mapping, Class type) throws Exception {