Introduce RouterFunction visitor
This commit introduces a visitor for router functions (RouterFunctions.Visitor), allowing to iterate over all the components that make up a router function. This commit also introduces a ToStringVisitor, which creates a nicely formatted string for use with toString(). Issue: SPR-15711, SPR-15711
This commit is contained in:
		
							parent
							
								
									7b6f1d1b58
								
							
						
					
					
						commit
						2841ef5d05
					
				| 
						 | 
				
			
			@ -155,4 +155,8 @@ class PathResourceLookupFunction implements Function<ServerRequest, Mono<Resourc
 | 
			
		|||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public String toString() {
 | 
			
		||||
		return String.format("%s -> %s", this.pattern, this.location);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,8 +47,7 @@ public interface RouterFunction<T extends ServerResponse> {
 | 
			
		|||
	 * @see #andOther(RouterFunction)
 | 
			
		||||
	 */
 | 
			
		||||
	default RouterFunction<T> and(RouterFunction<T> other) {
 | 
			
		||||
		return request -> this.route(request)
 | 
			
		||||
				.switchIfEmpty(Mono.defer(() -> other.route(request)));
 | 
			
		||||
		return new RouterFunctions.SameComposedRouterFunction<>(this, other);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -61,10 +60,7 @@ public interface RouterFunction<T extends ServerResponse> {
 | 
			
		|||
	 * @see #and(RouterFunction)
 | 
			
		||||
	 */
 | 
			
		||||
	default RouterFunction<?> andOther(RouterFunction<?> other) {
 | 
			
		||||
		return request -> this.route(request)
 | 
			
		||||
				.map(RouterFunctions::cast)
 | 
			
		||||
				.switchIfEmpty(
 | 
			
		||||
						Mono.defer(() -> other.route(request).map(RouterFunctions::cast)));
 | 
			
		||||
		return new RouterFunctions.DifferentComposedRouterFunction(this, other);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -105,7 +101,18 @@ public interface RouterFunction<T extends ServerResponse> {
 | 
			
		|||
	 * @return the filtered routing function
 | 
			
		||||
	 */
 | 
			
		||||
	default <S extends ServerResponse> RouterFunction<S> filter(HandlerFilterFunction<T, S> filterFunction) {
 | 
			
		||||
		return request -> this.route(request).map(filterFunction::apply);
 | 
			
		||||
		return new RouterFunctions.FilteredRouterFunction<>(this, filterFunction);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Accept the given visitor. Default implementation calls
 | 
			
		||||
	 * {@link RouterFunctions.Visitor#unknown(RouterFunction)}; composed {@code RouterFunction}
 | 
			
		||||
	 * implementations are expected to call {@code accept} for all components that make up this
 | 
			
		||||
	 * router function
 | 
			
		||||
	 * @param visitor the visitor to accept
 | 
			
		||||
	 */
 | 
			
		||||
	default void accept(RouterFunctions.Visitor visitor) {
 | 
			
		||||
		visitor.unknown(this);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -150,7 +150,7 @@ public abstract class RouterFunctions {
 | 
			
		|||
	 */
 | 
			
		||||
	public static RouterFunction<ServerResponse> resources(Function<ServerRequest, Mono<Resource>> lookupFunction) {
 | 
			
		||||
		Assert.notNull(lookupFunction, "'lookupFunction' must not be null");
 | 
			
		||||
		return request -> lookupFunction.apply(request).map(ResourceHandlerFunction::new);
 | 
			
		||||
		return new ResourcesRouterFunction(lookupFunction);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -259,8 +259,138 @@ public abstract class RouterFunctions {
 | 
			
		|||
		return (HandlerFunction<T>) handlerFunction;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static class DefaultRouterFunction<T extends ServerResponse>
 | 
			
		||||
			implements RouterFunction<T> {
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Receives notifications from the logical structure of router functions.
 | 
			
		||||
	 */
 | 
			
		||||
	public interface Visitor {
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Receive notification of the beginning of a nested router function.
 | 
			
		||||
		 * @param predicate the predicate that applies to the nested router functions
 | 
			
		||||
		 * @see RouterFunctions#nest(RequestPredicate, RouterFunction)
 | 
			
		||||
		 */
 | 
			
		||||
		void startNested(RequestPredicate predicate);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Receive notification of the end of a nested router function.
 | 
			
		||||
		 * @param predicate the predicate that applies to the nested router functions
 | 
			
		||||
		 * @see RouterFunctions#nest(RequestPredicate, RouterFunction)
 | 
			
		||||
		 */
 | 
			
		||||
		void endNested(RequestPredicate predicate);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Receive notification of a standard predicated route to a handler function.
 | 
			
		||||
		 * @param predicate the predicate that applies to the handler function
 | 
			
		||||
		 * @param handlerFunction the handler function.
 | 
			
		||||
		 * @see RouterFunctions#route(RequestPredicate, HandlerFunction)
 | 
			
		||||
		 */
 | 
			
		||||
		void route(RequestPredicate predicate, HandlerFunction<?> handlerFunction);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Receive notification of a resource router function.
 | 
			
		||||
		 * @param lookupFunction the lookup function for the resources
 | 
			
		||||
		 * @see RouterFunctions#resources(Function)
 | 
			
		||||
		 */
 | 
			
		||||
		void resources(Function<ServerRequest, Mono<Resource>> lookupFunction);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Receive notification of an unknown router function. This method is called for router
 | 
			
		||||
		 * functions that were not created via the various {@link RouterFunctions} methods.
 | 
			
		||||
		 * @param routerFunction the router function
 | 
			
		||||
		 */
 | 
			
		||||
		void unknown(RouterFunction<?> routerFunction);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	private static abstract class AbstractRouterFunction<T extends ServerResponse> implements RouterFunction<T> {
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public String toString() {
 | 
			
		||||
			ToStringVisitor visitor = new ToStringVisitor();
 | 
			
		||||
			accept(visitor);
 | 
			
		||||
			return visitor.toString();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	final static class SameComposedRouterFunction<T extends ServerResponse> extends AbstractRouterFunction<T> {
 | 
			
		||||
 | 
			
		||||
		private final RouterFunction<T> first;
 | 
			
		||||
 | 
			
		||||
		private final RouterFunction<T> second;
 | 
			
		||||
 | 
			
		||||
		public SameComposedRouterFunction(RouterFunction<T> first, RouterFunction<T> second) {
 | 
			
		||||
			this.first = first;
 | 
			
		||||
			this.second = second;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public Mono<HandlerFunction<T>> route(ServerRequest request) {
 | 
			
		||||
			return this.first.route(request)
 | 
			
		||||
					.switchIfEmpty(Mono.defer(() -> this.second.route(request)));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public void accept(Visitor visitor) {
 | 
			
		||||
			this.first.accept(visitor);
 | 
			
		||||
			this.second.accept(visitor);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	final static class DifferentComposedRouterFunction extends AbstractRouterFunction<ServerResponse> {
 | 
			
		||||
 | 
			
		||||
		private final RouterFunction<?> first;
 | 
			
		||||
 | 
			
		||||
		private final RouterFunction<?> second;
 | 
			
		||||
 | 
			
		||||
		public DifferentComposedRouterFunction(RouterFunction<?> first, RouterFunction<?> second) {
 | 
			
		||||
			this.first = first;
 | 
			
		||||
			this.second = second;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public Mono<HandlerFunction<ServerResponse>> route(ServerRequest request) {
 | 
			
		||||
			return this.first.route(request)
 | 
			
		||||
					.map(RouterFunctions::cast)
 | 
			
		||||
					.switchIfEmpty(Mono.defer(() -> this.second.route(request).map(RouterFunctions::cast)));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public void accept(Visitor visitor) {
 | 
			
		||||
			this.first.accept(visitor);
 | 
			
		||||
			this.second.accept(visitor);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	final static class FilteredRouterFunction<T extends ServerResponse, S extends ServerResponse>
 | 
			
		||||
			implements RouterFunction<S> {
 | 
			
		||||
 | 
			
		||||
		private final RouterFunction<T> routerFunction;
 | 
			
		||||
 | 
			
		||||
		private final HandlerFilterFunction<T, S> filterFunction;
 | 
			
		||||
 | 
			
		||||
		public FilteredRouterFunction(
 | 
			
		||||
				RouterFunction<T> routerFunction,
 | 
			
		||||
				HandlerFilterFunction<T, S> filterFunction) {
 | 
			
		||||
			this.routerFunction = routerFunction;
 | 
			
		||||
			this.filterFunction = filterFunction;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public Mono<HandlerFunction<S>> route(ServerRequest request) {
 | 
			
		||||
			return this.routerFunction.route(request).map(this.filterFunction::apply);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public void accept(Visitor visitor) {
 | 
			
		||||
			this.routerFunction.accept(visitor);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static final class DefaultRouterFunction<T extends ServerResponse>
 | 
			
		||||
			extends AbstractRouterFunction<T> {
 | 
			
		||||
 | 
			
		||||
		private final RequestPredicate predicate;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -287,13 +417,14 @@ public abstract class RouterFunctions {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public String toString() {
 | 
			
		||||
			return String.format("%s -> %s", this.predicate, this.handlerFunction);
 | 
			
		||||
		}
 | 
			
		||||
		public void accept(Visitor visitor) {
 | 
			
		||||
			visitor.route(this.predicate, this.handlerFunction);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	private static class DefaultNestedRouterFunction<T extends ServerResponse>
 | 
			
		||||
			implements RouterFunction<T> {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static final class DefaultNestedRouterFunction<T extends ServerResponse>
 | 
			
		||||
			extends AbstractRouterFunction<T> {
 | 
			
		||||
 | 
			
		||||
		private final RequestPredicate predicate;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -322,12 +453,33 @@ public abstract class RouterFunctions {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public String toString() {
 | 
			
		||||
			return String.format("%s -> %s", this.predicate, this.routerFunction);
 | 
			
		||||
		public void accept(Visitor visitor) {
 | 
			
		||||
			visitor.startNested(this.predicate);
 | 
			
		||||
			this.routerFunction.accept(visitor);
 | 
			
		||||
			visitor.endNested(this.predicate);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static class ResourcesRouterFunction extends  AbstractRouterFunction<ServerResponse> {
 | 
			
		||||
 | 
			
		||||
		private final Function<ServerRequest, Mono<Resource>> lookupFunction;
 | 
			
		||||
 | 
			
		||||
		public ResourcesRouterFunction(Function<ServerRequest, Mono<Resource>> lookupFunction) {
 | 
			
		||||
			this.lookupFunction = lookupFunction;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public Mono<HandlerFunction<ServerResponse>> route(ServerRequest request) {
 | 
			
		||||
			return this.lookupFunction.apply(request).map(ResourceHandlerFunction::new);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public void accept(Visitor visitor) {
 | 
			
		||||
			visitor.resources(this.lookupFunction);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static class HandlerStrategiesResponseContext implements ServerResponse.Context {
 | 
			
		||||
 | 
			
		||||
		private final HandlerStrategies strategies;
 | 
			
		||||
| 
						 | 
				
			
			@ -346,4 +498,5 @@ public abstract class RouterFunctions {
 | 
			
		|||
			return this.strategies.viewResolvers();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,91 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2002-2017 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.web.reactive.function.server;
 | 
			
		||||
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
import reactor.core.publisher.Mono;
 | 
			
		||||
 | 
			
		||||
import org.springframework.core.io.Resource;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Implementation of {@link RouterFunctions.Visitor} that creates a
 | 
			
		||||
 * @author Arjen Poutsma
 | 
			
		||||
 * @since 5.0
 | 
			
		||||
 */
 | 
			
		||||
class ToStringVisitor implements RouterFunctions.Visitor {
 | 
			
		||||
 | 
			
		||||
	private static final String NEW_LINE = System.getProperty("line.separator", "\\n");
 | 
			
		||||
 | 
			
		||||
	private final StringBuilder builder = new StringBuilder();
 | 
			
		||||
 | 
			
		||||
	private int indent = 0;
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void startNested(RequestPredicate predicate) {
 | 
			
		||||
		indent();
 | 
			
		||||
		this.builder.append(predicate);
 | 
			
		||||
		this.builder.append(" => {");
 | 
			
		||||
		this.builder.append(NEW_LINE);
 | 
			
		||||
		this.indent++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void endNested(RequestPredicate predicate) {
 | 
			
		||||
		this.indent--;
 | 
			
		||||
		indent();
 | 
			
		||||
		this.builder.append('}');
 | 
			
		||||
		this.builder.append(NEW_LINE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void route(RequestPredicate predicate, HandlerFunction<?> handlerFunction) {
 | 
			
		||||
		indent();
 | 
			
		||||
		this.builder.append(predicate);
 | 
			
		||||
		this.builder.append(" -> ");
 | 
			
		||||
		this.builder.append(handlerFunction);
 | 
			
		||||
		this.builder.append(NEW_LINE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void resources(Function<ServerRequest, Mono<Resource>> lookupFunction) {
 | 
			
		||||
		indent();
 | 
			
		||||
		this.builder.append(lookupFunction);
 | 
			
		||||
		this.builder.append(NEW_LINE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void unknown(RouterFunction<?> routerFunction) {
 | 
			
		||||
		indent();
 | 
			
		||||
		this.builder.append(routerFunction);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void indent() {
 | 
			
		||||
		for (int i=0; i < this.indent; i++) {
 | 
			
		||||
			this.builder.append(' ');
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public String toString() {
 | 
			
		||||
		String result = this.builder.toString();
 | 
			
		||||
		if (result.endsWith(NEW_LINE)) {
 | 
			
		||||
			result = result.substring(0, result.length() - NEW_LINE.length());
 | 
			
		||||
		}
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -102,8 +102,8 @@ public class RouterFunctionMapping extends AbstractHandlerMapping implements Ini
 | 
			
		|||
 | 
			
		||||
		List<RouterFunction<?>> routerFunctions = routerFunctions();
 | 
			
		||||
		if (!CollectionUtils.isEmpty(routerFunctions) && logger.isInfoEnabled()) {
 | 
			
		||||
			routerFunctions.forEach(routerFunction1 -> {
 | 
			
		||||
				logger.info("Mapped " + routerFunction1);
 | 
			
		||||
			routerFunctions.forEach(routerFunction -> {
 | 
			
		||||
				logger.info("Mapped " + routerFunction);
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
		this.routerFunction = routerFunctions.stream()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue