001/*
002 * Copyright 2002-2019 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.reactive;
018
019import java.util.ArrayList;
020import java.util.Collections;
021import java.util.List;
022import java.util.Map;
023
024import reactor.core.publisher.Flux;
025import reactor.core.publisher.Mono;
026
027import org.springframework.beans.factory.BeanFactoryUtils;
028import org.springframework.context.ApplicationContext;
029import org.springframework.context.ApplicationContextAware;
030import org.springframework.core.annotation.AnnotationAwareOrderComparator;
031import org.springframework.http.HttpStatus;
032import org.springframework.lang.Nullable;
033import org.springframework.web.server.ResponseStatusException;
034import org.springframework.web.server.ServerWebExchange;
035import org.springframework.web.server.WebHandler;
036import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
037
038/**
039 * Central dispatcher for HTTP request handlers/controllers. Dispatches to
040 * registered handlers for processing a request, providing convenient mapping
041 * facilities.
042 *
043 * <p>{@code DispatcherHandler} discovers the delegate components it needs from
044 * Spring configuration. It detects the following in the application context:
045 * <ul>
046 * <li>{@link HandlerMapping} -- map requests to handler objects
047 * <li>{@link HandlerAdapter} -- for using any handler interface
048 * <li>{@link HandlerResultHandler} -- process handler return values
049 * </ul>
050 *
051 * <p>{@code DispatcherHandler} is also designed to be a Spring bean itself and
052 * implements {@link ApplicationContextAware} for access to the context it runs
053 * in. If {@code DispatcherHandler} is declared with the bean name "webHandler"
054 * it is discovered by {@link WebHttpHandlerBuilder#applicationContext} which
055 * creates a processing chain together with {@code WebFilter},
056 * {@code WebExceptionHandler} and others.
057 *
058 * <p>A {@code DispatcherHandler} bean declaration is included in
059 * {@link org.springframework.web.reactive.config.EnableWebFlux @EnableWebFlux}
060 * configuration.
061 *
062 * @author Rossen Stoyanchev
063 * @author Sebastien Deleuze
064 * @author Juergen Hoeller
065 * @since 5.0
066 * @see WebHttpHandlerBuilder#applicationContext(ApplicationContext)
067 */
068public class DispatcherHandler implements WebHandler, ApplicationContextAware {
069
070        @Nullable
071        private List<HandlerMapping> handlerMappings;
072
073        @Nullable
074        private List<HandlerAdapter> handlerAdapters;
075
076        @Nullable
077        private List<HandlerResultHandler> resultHandlers;
078
079
080        /**
081         * Create a new {@code DispatcherHandler} which needs to be configured with
082         * an {@link ApplicationContext} through {@link #setApplicationContext}.
083         */
084        public DispatcherHandler() {
085        }
086
087        /**
088         * Create a new {@code DispatcherHandler} for the given {@link ApplicationContext}.
089         * @param applicationContext the application context to find the handler beans in
090         */
091        public DispatcherHandler(ApplicationContext applicationContext) {
092                initStrategies(applicationContext);
093        }
094
095
096        /**
097         * Return all {@link HandlerMapping} beans detected by type in the
098         * {@link #setApplicationContext injected context} and also
099         * {@link AnnotationAwareOrderComparator#sort(List) sorted}.
100         * <p><strong>Note:</strong> This method may return {@code null} if invoked
101         * prior to {@link #setApplicationContext(ApplicationContext)}.
102         * @return immutable list with the configured mappings or {@code null}
103         */
104        @Nullable
105        public final List<HandlerMapping> getHandlerMappings() {
106                return this.handlerMappings;
107        }
108
109        @Override
110        public void setApplicationContext(ApplicationContext applicationContext) {
111                initStrategies(applicationContext);
112        }
113
114
115        protected void initStrategies(ApplicationContext context) {
116                Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
117                                context, HandlerMapping.class, true, false);
118
119                ArrayList<HandlerMapping> mappings = new ArrayList<>(mappingBeans.values());
120                AnnotationAwareOrderComparator.sort(mappings);
121                this.handlerMappings = Collections.unmodifiableList(mappings);
122
123                Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
124                                context, HandlerAdapter.class, true, false);
125
126                this.handlerAdapters = new ArrayList<>(adapterBeans.values());
127                AnnotationAwareOrderComparator.sort(this.handlerAdapters);
128
129                Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
130                                context, HandlerResultHandler.class, true, false);
131
132                this.resultHandlers = new ArrayList<>(beans.values());
133                AnnotationAwareOrderComparator.sort(this.resultHandlers);
134        }
135
136
137        @Override
138        public Mono<Void> handle(ServerWebExchange exchange) {
139                if (this.handlerMappings == null) {
140                        return createNotFoundError();
141                }
142                return Flux.fromIterable(this.handlerMappings)
143                                .concatMap(mapping -> mapping.getHandler(exchange))
144                                .next()
145                                .switchIfEmpty(createNotFoundError())
146                                .flatMap(handler -> invokeHandler(exchange, handler))
147                                .flatMap(result -> handleResult(exchange, result));
148        }
149
150        private <R> Mono<R> createNotFoundError() {
151                return Mono.defer(() -> {
152                        Exception ex = new ResponseStatusException(HttpStatus.NOT_FOUND, "No matching handler");
153                        return Mono.error(ex);
154                });
155        }
156
157        private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
158                if (this.handlerAdapters != null) {
159                        for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
160                                if (handlerAdapter.supports(handler)) {
161                                        return handlerAdapter.handle(exchange, handler);
162                                }
163                        }
164                }
165                return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
166        }
167
168        private Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
169                return getResultHandler(result).handleResult(exchange, result)
170                                .checkpoint("Handler " + result.getHandler() + " [DispatcherHandler]")
171                                .onErrorResume(ex ->
172                                                result.applyExceptionHandler(ex).flatMap(exResult -> {
173                                                        String text = "Exception handler " + exResult.getHandler() +
174                                                                        ", error=\"" + ex.getMessage() + "\" [DispatcherHandler]";
175                                                        return getResultHandler(exResult).handleResult(exchange, exResult).checkpoint(text);
176                                                }));
177        }
178
179        private HandlerResultHandler getResultHandler(HandlerResult handlerResult) {
180                if (this.resultHandlers != null) {
181                        for (HandlerResultHandler resultHandler : this.resultHandlers) {
182                                if (resultHandler.supports(handlerResult)) {
183                                        return resultHandler;
184                                }
185                        }
186                }
187                throw new IllegalStateException("No HandlerResultHandler for " + handlerResult.getReturnValue());
188        }
189
190}