@ -29,7 +29,6 @@ import java.util.List;
@@ -29,7 +29,6 @@ import java.util.List;
import java.util.Map ;
import java.util.Optional ;
import java.util.Set ;
import java.util.concurrent.ConcurrentHashMap ;
import java.util.function.Consumer ;
import java.util.function.Function ;
import java.util.function.Predicate ;
@ -294,19 +293,6 @@ public abstract class RequestPredicates {
@@ -294,19 +293,6 @@ public abstract class RequestPredicates {
}
}
private static Map < String , String > mergePathVariables ( Map < String , String > oldVariables ,
Map < String , String > newVariables ) {
if ( ! newVariables . isEmpty ( ) ) {
Map < String , String > mergedVariables = new LinkedHashMap < > ( oldVariables ) ;
mergedVariables . putAll ( newVariables ) ;
return mergedVariables ;
}
else {
return oldVariables ;
}
}
private static PathPattern mergePatterns ( @Nullable PathPattern oldPattern , PathPattern newPattern ) {
if ( oldPattern ! = null ) {
return oldPattern . combine ( newPattern ) ;
@ -314,7 +300,28 @@ public abstract class RequestPredicates {
@@ -314,7 +300,28 @@ public abstract class RequestPredicates {
else {
return newPattern ;
}
}
private static < K , V > Map < K , V > mergeMaps ( Map < K , V > left , Map < K , V > right ) {
if ( left . isEmpty ( ) ) {
if ( right . isEmpty ( ) ) {
return Collections . emptyMap ( ) ;
}
else {
return right ;
}
}
else {
if ( right . isEmpty ( ) ) {
return left ;
}
else {
Map < K , V > result = new LinkedHashMap < > ( left . size ( ) + right . size ( ) ) ;
result . putAll ( left ) ;
result . putAll ( right ) ;
return result ;
}
}
}
@ -451,7 +458,7 @@ public abstract class RequestPredicates {
@@ -451,7 +458,7 @@ public abstract class RequestPredicates {
Result result = testInternal ( request ) ;
boolean value = result . value ( ) ;
if ( value ) {
result . modify ( request ) ;
result . modifyAttributes ( request . attributes ( ) ) ;
}
return value ;
}
@ -469,12 +476,12 @@ public abstract class RequestPredicates {
@@ -469,12 +476,12 @@ public abstract class RequestPredicates {
private final boolean value ;
@Nullable
private final Consumer < ServerRequest > modify ;
private final Consumer < Map < String , Object > > modifyAttributes ;
private Result ( boolean value , @Nullable Consumer < ServerRequest > modify ) {
private Result ( boolean value , @Nullable Consumer < Map < String , Object > > modifyAttributes ) {
this . value = value ;
this . modify = modify ;
this . modifyAttributes = modifyAttributes ;
}
@ -482,12 +489,12 @@ public abstract class RequestPredicates {
@@ -482,12 +489,12 @@ public abstract class RequestPredicates {
return of ( value , null ) ;
}
public static Result of ( boolean value , @Nullable Consumer < ServerRequest > commit ) {
if ( commit = = null ) {
public static Result of ( boolean value , @Nullable Consumer < Map < String , Object > > modifyAttributes ) {
if ( modifyAttributes = = null ) {
return value ? TRUE : FALSE ;
}
else {
return new Result ( value , commit ) ;
return new Result ( value , modifyAttributes ) ;
}
}
@ -496,11 +503,15 @@ public abstract class RequestPredicates {
@@ -496,11 +503,15 @@ public abstract class RequestPredicates {
return this . value ;
}
public void modify ( ServerRequest request ) {
if ( this . modify ! = null ) {
this . modify . accept ( request ) ;
public void modifyAttributes ( Map < String , Object > attributes ) {
if ( this . modifyAttributes ! = null ) {
this . modifyAttributes . accept ( attributes ) ;
}
}
public boolean modifiesAttributes ( ) {
return this . modifyAttributes ! = null ;
}
}
}
@ -574,29 +585,32 @@ public abstract class RequestPredicates {
@@ -574,29 +585,32 @@ public abstract class RequestPredicates {
PathPattern . PathMatchInfo info = this . pattern . matchAndExtract ( pathContainer ) ;
traceMatch ( "Pattern" , this . pattern . getPatternString ( ) , request . path ( ) , info ! = null ) ;
if ( info ! = null ) {
return Result . of ( true , serverRequest - > mergeAttributes ( serverR equest, info . getUriVariables ( ) ) ) ;
return Result . of ( true , attributes - > modifyAttributes ( attributes , r equest, info . getUriVariables ( ) ) ) ;
}
else {
return Result . of ( false ) ;
}
}
private void mergeAttributes ( ServerRequest request , Map < String , String > variables ) {
Map < String , Object > attributes = request . attributes ( ) ;
Map < String , String > pathVariables = mergePathVariables ( request . pathVariables ( ) , variables ) ;
private void modifyAttributes ( Map < String , Object > attributes , ServerRequest request ,
Map < String , String > variables ) {
Map < String , String > pathVariables = mergeMaps ( request . pathVariables ( ) , variables ) ;
attributes . put ( RouterFunctions . URI_TEMPLATE_VARIABLES_ATTRIBUTE ,
Collections . unmodifiableMap ( pathVariables ) ) ;
PathPattern pattern = mergePatterns (
( PathPattern ) attributes . get ( RouterFunctions . MATCHING_PATTERN_ATTRIBUTE ) ,
this . pattern ) ;
attributes . put ( RouterFunctions . MATCHING_PATTERN_ATTRIBUTE , pattern ) ;
}
@Override
public Optional < ServerRequest > nest ( ServerRequest request ) {
return Optional . ofNullable ( this . pattern . matchStartOfPath ( request . requestPath ( ) . pathWithinApplication ( ) ) )
. map ( info - > new SubPath ServerRequestWrapper( request , info , this . pattern ) ) ;
. map ( info - > new NestedPathPattern ServerRequestWrapper( request , info , this . pattern ) ) ;
}
@Override
@ -860,13 +874,23 @@ public abstract class RequestPredicates {
@@ -860,13 +874,23 @@ public abstract class RequestPredicates {
if ( ! leftResult . value ( ) ) {
return leftResult ;
}
Result rightResult = this . rightModifying . testInternal ( request ) ;
// ensure that attributes (and uri variables) set in left and available in right
ServerRequest rightRequest ;
if ( leftResult . modifiesAttributes ( ) ) {
Map < String , Object > leftAttributes = new LinkedHashMap < > ( 2 ) ;
leftResult . modifyAttributes ( leftAttributes ) ;
rightRequest = new ExtendedAttributesServerRequestWrapper ( request , leftAttributes ) ;
}
else {
rightRequest = request ;
}
Result rightResult = this . rightModifying . testInternal ( rightRequest ) ;
if ( ! rightResult . value ( ) ) {
return rightResult ;
}
return Result . of ( true , serverRequest - > {
leftResult . modify ( serverRequest ) ;
rightResult . modify ( serverRequest ) ;
return Result . of ( true , attributes - > {
leftResult . modifyAttributes ( attributes ) ;
rightResult . modifyAttributes ( attributes ) ;
} ) ;
}
@ -922,7 +946,7 @@ public abstract class RequestPredicates {
@@ -922,7 +946,7 @@ public abstract class RequestPredicates {
@Override
protected Result testInternal ( ServerRequest request ) {
Result result = this . delegateModifying . testInternal ( request ) ;
return Result . of ( ! result . value ( ) , result : : modify ) ;
return Result . of ( ! result . value ( ) , result : : modifyAttributes ) ;
}
@Override
@ -1019,178 +1043,210 @@ public abstract class RequestPredicates {
@@ -1019,178 +1043,210 @@ public abstract class RequestPredicates {
}
private static class SubPathServerRequestWrapper implements ServerRequest {
private final ServerRequest request ;
private final RequestPath requestPath ;
private final Map < String , Object > attributes ;
public SubPathServerRequestWrapper ( ServerRequest request ,
PathPattern . PathRemainingMatchInfo info , PathPattern pattern ) {
this . request = request ;
this . requestPath = requestPath ( request . requestPath ( ) , info ) ;
this . attributes = mergeAttributes ( request , info . getUriVariables ( ) , pattern ) ;
}
private static RequestPath requestPath ( RequestPath original , PathPattern . PathRemainingMatchInfo info ) {
StringBuilder contextPath = new StringBuilder ( original . contextPath ( ) . value ( ) ) ;
contextPath . append ( info . getPathMatched ( ) . value ( ) ) ;
int length = contextPath . length ( ) ;
if ( length > 0 & & contextPath . charAt ( length - 1 ) = = '/' ) {
contextPath . setLength ( length - 1 ) ;
}
return original . modifyContextPath ( contextPath . toString ( ) ) ;
}
private abstract static class DelegatingServerRequest implements ServerRequest {
private static Map < String , Object > mergeAttributes ( ServerRequest request ,
Map < String , String > pathVariables , PathPattern pattern ) {
Map < String , Object > result = new ConcurrentHashMap < > ( request . attributes ( ) ) ;
private final ServerRequest delegate ;
result . put ( RouterFunctions . URI_TEMPLATE_VARIABLES_ATTRIBUTE ,
mergePathVariables ( request . pathVariables ( ) , pathVariables ) ) ;
pattern = mergePatterns (
( PathPattern ) request . attributes ( ) . get ( RouterFunctions . MATCHING_PATTERN_ATTRIBUTE ) ,
pattern ) ;
result . put ( RouterFunctions . MATCHING_PATTERN_ATTRIBUTE , pattern ) ;
return result ;
protected DelegatingServerRequest ( ServerRequest delegate ) {
Assert . notNull ( delegate , "Delegate must not be null" ) ;
this . delegate = delegate ;
}
@Override
public HttpMethod method ( ) {
return this . request . method ( ) ;
return this . delegate . method ( ) ;
}
@Override
@Deprecated
public String methodName ( ) {
return this . request . methodName ( ) ;
return this . delegate . methodName ( ) ;
}
@Override
public URI uri ( ) {
return this . request . uri ( ) ;
return this . delegate . uri ( ) ;
}
@Override
public UriBuilder uriBuilder ( ) {
return this . request . uriBuilder ( ) ;
return this . delegate . uriBuilder ( ) ;
}
@Override
public String path ( ) {
return this . delegate . path ( ) ;
}
@Override
public RequestPath requestPath ( ) {
return this . requestPath ;
return this . delegate . requestPath ( ) ;
}
@Override
public Headers headers ( ) {
return this . request . headers ( ) ;
return this . delegate . headers ( ) ;
}
@Override
public MultiValueMap < String , Cookie > cookies ( ) {
return this . request . cookies ( ) ;
return this . delegate . cookies ( ) ;
}
@Override
public Optional < InetSocketAddress > remoteAddress ( ) {
return this . request . remoteAddress ( ) ;
return this . delegate . remoteAddress ( ) ;
}
@Override
public List < HttpMessageConverter < ? > > messageConverters ( ) {
return this . request . messageConverters ( ) ;
return this . delegate . messageConverters ( ) ;
}
@Override
public < T > T body ( Class < T > bodyType ) throws ServletException , IOException {
return this . request . body ( bodyType ) ;
return this . delegate . body ( bodyType ) ;
}
@Override
public < T > T body ( ParameterizedTypeReference < T > bodyType )
throws ServletException , IOException {
return this . request . body ( bodyType ) ;
public < T > T body ( ParameterizedTypeReference < T > bodyType ) throws ServletException , IOException {
return this . delegate . body ( bodyType ) ;
}
@Override
public < T > T bind ( Class < T > bindType ) throws BindException {
return this . request . bind ( bindType ) ;
return this . delegate . bind ( bindType ) ;
}
@Override
public < T > T bind ( Class < T > bindType , Consumer < WebDataBinder > dataBinderCustomizer ) throws BindException {
return this . request . bind ( bindType , dataBinderCustomizer ) ;
}
@Override
public Optional < Object > attribute ( String name ) {
return this . request . attribute ( name ) ;
return this . delegate . bind ( bindType , dataBinderCustomizer ) ;
}
@Override
public Map < String , Object > attributes ( ) {
return this . attributes ;
}
@Override
public Optional < String > param ( String name ) {
return this . request . param ( name ) ;
return this . delegate . attributes ( ) ;
}
@Override
public MultiValueMap < String , String > params ( ) {
return this . request . params ( ) ;
return this . delegate . params ( ) ;
}
@Override
public MultiValueMap < String , Part > multipartData ( ) throws IOException , ServletException {
return this . request . multipartData ( ) ;
return this . delegate . multipartData ( ) ;
}
@Override
@SuppressWarnings ( "unchecked" )
public Map < String , String > pathVariables ( ) {
return ( Map < String , String > ) this . attributes . getOrDefault (
RouterFunctions . URI_TEMPLATE_VARIABLES_ATTRIBUTE , Collections . emptyMap ( ) ) ;
return this . delegate . pathVariables ( ) ;
}
@Override
public HttpSession session ( ) {
return this . request . session ( ) ;
return this . delegate . session ( ) ;
}
@Override
public Optional < Principal > principal ( ) {
return this . request . principal ( ) ;
return this . delegate . principal ( ) ;
}
@Override
public HttpServletRequest servletRequest ( ) {
return this . request . servletRequest ( ) ;
return this . delegate . servletRequest ( ) ;
}
@Override
public Optional < ServerResponse > checkNotModified ( Instant lastModified ) {
return this . request . checkNotModified ( lastModified ) ;
return this . delegate . checkNotModified ( lastModified ) ;
}
@Override
public Optional < ServerResponse > checkNotModified ( String etag ) {
return this . request . checkNotModified ( etag ) ;
return this . delegate . checkNotModified ( etag ) ;
}
@Override
public Optional < ServerResponse > checkNotModified ( Instant lastModified , String etag ) {
return this . request . checkNotModified ( lastModified , etag ) ;
return this . delegate . checkNotModified ( lastModified , etag ) ;
}
@Override
public String toString ( ) {
return method ( ) + " " + path ( ) ;
return this . delegate . toString ( ) ;
}
}
private static class ExtendedAttributesServerRequestWrapper extends DelegatingServerRequest {
private final Map < String , Object > attributes ;
public ExtendedAttributesServerRequestWrapper ( ServerRequest delegate , Map < String , Object > newAttributes ) {
super ( delegate ) ;
Assert . notNull ( newAttributes , "NewAttributes must not be null" ) ;
this . attributes = mergeMaps ( delegate . attributes ( ) , newAttributes ) ;
}
@Override
public Map < String , Object > attributes ( ) {
return this . attributes ;
}
@Override
@SuppressWarnings ( "unchecked" )
public Map < String , String > pathVariables ( ) {
return ( Map < String , String > ) this . attributes . getOrDefault (
RouterFunctions . URI_TEMPLATE_VARIABLES_ATTRIBUTE , Collections . emptyMap ( ) ) ;
}
}
private static class NestedPathPatternServerRequestWrapper extends ExtendedAttributesServerRequestWrapper {
private final RequestPath requestPath ;
public NestedPathPatternServerRequestWrapper ( ServerRequest request ,
PathPattern . PathRemainingMatchInfo info , PathPattern pattern ) {
super ( request , mergeAttributes ( request , info . getUriVariables ( ) , pattern ) ) ;
this . requestPath = requestPath ( request . requestPath ( ) , info ) ;
}
private static Map < String , Object > mergeAttributes ( ServerRequest request , Map < String , String > newPathVariables ,
PathPattern newPathPattern ) {
Map < String , String > oldPathVariables = request . pathVariables ( ) ;
PathPattern oldPathPattern = ( PathPattern ) request . attribute ( RouterFunctions . MATCHING_PATTERN_ATTRIBUTE )
. orElse ( null ) ;
Map < String , Object > result = new LinkedHashMap < > ( 2 ) ;
result . put ( RouterFunctions . URI_TEMPLATE_VARIABLES_ATTRIBUTE , mergeMaps ( oldPathVariables , newPathVariables ) ) ;
result . put ( RouterFunctions . MATCHING_PATTERN_ATTRIBUTE , mergePatterns ( oldPathPattern , newPathPattern ) ) ;
return result ;
}
private static RequestPath requestPath ( RequestPath original , PathPattern . PathRemainingMatchInfo info ) {
StringBuilder contextPath = new StringBuilder ( original . contextPath ( ) . value ( ) ) ;
contextPath . append ( info . getPathMatched ( ) . value ( ) ) ;
int length = contextPath . length ( ) ;
if ( length > 0 & & contextPath . charAt ( length - 1 ) = = '/' ) {
contextPath . setLength ( length - 1 ) ;
}
return original . modifyContextPath ( contextPath . toString ( ) ) ;
}
@Override
public RequestPath requestPath ( ) {
return this . requestPath ;
}
}
}