DATAREST-1193 - DelegatingHandlerMapping is now a MatchableHandlerMapping.

DelegatingHandlerMapping now implements MatchableHandlerMapping by selecting the a delegate based on the already implemented algorithm and then invoking the ….match(…) method in case the selected one is a MatchableHandlerMapping in turn.

Refactored the internals a bit to be able to reuse the selection algorithm from both ….getHandler(…) and ….match(…).
This commit is contained in:
Oliver Gierke
2018-02-19 18:00:59 +01:00
parent 4dd66ba782
commit 622cf07720
2 changed files with 93 additions and 18 deletions

View File

@@ -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<HandlerMapping> 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<HandlerMapping> 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;
}
}
}

View File

@@ -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<? extends Exception> type)
throws Exception {