@Nullable all the way: null-safety at field level
This commits extends nullability declarations to the field level, formalizing the interaction between methods and their underlying fields and therefore avoiding any nullability mismatch. Issue: SPR-15720
This commit is contained in:
@@ -37,6 +37,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
*/
|
||||
public class BindingContext {
|
||||
|
||||
@Nullable
|
||||
private final WebBindingInitializer initializer;
|
||||
|
||||
private final Model model = new BindingAwareConcurrentModel();
|
||||
|
||||
@@ -36,12 +36,14 @@ public class HandlerResult {
|
||||
|
||||
private final Object handler;
|
||||
|
||||
@Nullable
|
||||
private final Object returnValue;
|
||||
|
||||
private final ResolvableType returnType;
|
||||
|
||||
private final BindingContext bindingContext;
|
||||
|
||||
@Nullable
|
||||
private Function<Throwable, Mono<HandlerResult>> exceptionHandler;
|
||||
|
||||
|
||||
@@ -144,7 +146,7 @@ public class HandlerResult {
|
||||
* @return the new result or the same error if there is no exception handler
|
||||
*/
|
||||
public Mono<HandlerResult> applyExceptionHandler(Throwable failure) {
|
||||
return (hasExceptionHandler() ? this.exceptionHandler.apply(failure) : Mono.error(failure));
|
||||
return (this.exceptionHandler != null ? this.exceptionHandler.apply(failure) : Mono.error(failure));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.accept;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -25,7 +26,7 @@ import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Builder for a composite {@link RequestedContentTypeResolver} that delegates
|
||||
@@ -113,9 +114,9 @@ public class RequestedContentTypeResolverBuilder {
|
||||
|
||||
private final Map<String, MediaType> mediaTypes = new HashMap<>();
|
||||
|
||||
@Nullable
|
||||
private String parameterName;
|
||||
|
||||
|
||||
/**
|
||||
* Configure a mapping between a lookup key (extracted from a query
|
||||
* parameter value) and a corresponding {@code MediaType}.
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.springframework.cache.Cache;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.resource.ResourceWebHandler;
|
||||
|
||||
@@ -40,8 +41,10 @@ public class ResourceHandlerRegistration {
|
||||
|
||||
private final List<Resource> locations = new ArrayList<>();
|
||||
|
||||
@Nullable
|
||||
private CacheControl cacheControl;
|
||||
|
||||
@Nullable
|
||||
private ResourceChainRegistration resourceChainRegistration;
|
||||
|
||||
|
||||
@@ -52,7 +55,8 @@ public class ResourceHandlerRegistration {
|
||||
* @param pathPatterns one or more resource URL path patterns
|
||||
*/
|
||||
public ResourceHandlerRegistration(ResourceLoader resourceLoader, String... pathPatterns) {
|
||||
Assert.notEmpty(pathPatterns, "At least one path pattern is required for resource handling.");
|
||||
Assert.notNull(resourceLoader, "ResourceLoader is required");
|
||||
Assert.notEmpty(pathPatterns, "At least one path pattern is required for resource handling");
|
||||
this.resourceLoader = resourceLoader;
|
||||
this.pathPatterns = pathPatterns;
|
||||
}
|
||||
@@ -74,7 +78,7 @@ public class ResourceHandlerRegistration {
|
||||
*/
|
||||
public ResourceHandlerRegistration addResourceLocations(String... resourceLocations) {
|
||||
for (String location : resourceLocations) {
|
||||
this.locations.add(resourceLoader.getResource(location));
|
||||
this.locations.add(this.resourceLoader.getResource(location));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -82,7 +86,6 @@ public class ResourceHandlerRegistration {
|
||||
/**
|
||||
* Specify the {@link CacheControl} which should be used
|
||||
* by the resource handler.
|
||||
*
|
||||
* @param cacheControl the CacheControl configuration to use
|
||||
* @return the same {@link ResourceHandlerRegistration} instance, for
|
||||
* chained method invocation
|
||||
@@ -95,11 +98,9 @@ public class ResourceHandlerRegistration {
|
||||
/**
|
||||
* Configure a chain of resource resolvers and transformers to use. This
|
||||
* can be useful, for example, to apply a version strategy to resource URLs.
|
||||
*
|
||||
* <p>If this method is not invoked, by default only a simple
|
||||
* {@code PathResourceResolver} is used in order to match URL paths to
|
||||
* resources under the configured locations.
|
||||
*
|
||||
* @param cacheResources whether to cache the result of resource resolution;
|
||||
* setting this to "true" is recommended for production (and "false" for
|
||||
* development, especially when applying a version strategy)
|
||||
@@ -114,11 +115,9 @@ public class ResourceHandlerRegistration {
|
||||
/**
|
||||
* Configure a chain of resource resolvers and transformers to use. This
|
||||
* can be useful, for example, to apply a version strategy to resource URLs.
|
||||
*
|
||||
* <p>If this method is not invoked, by default only a simple
|
||||
* {@code PathResourceResolver} is used in order to match URL paths to
|
||||
* resources under the configured locations.
|
||||
*
|
||||
* @param cacheResources whether to cache the result of resource resolution;
|
||||
* setting this to "true" is recommended for production (and "false" for
|
||||
* development, especially when applying a version strategy
|
||||
|
||||
@@ -23,9 +23,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.handler.AbstractUrlHandlerMapping;
|
||||
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.reactive.resource.ResourceWebHandler;
|
||||
@@ -53,7 +52,7 @@ import org.springframework.web.server.WebHandler;
|
||||
*/
|
||||
public class ResourceHandlerRegistry {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
private final ResourceLoader resourceLoader;
|
||||
|
||||
private final List<ResourceHandlerRegistration> registrations = new ArrayList<>();
|
||||
|
||||
@@ -61,13 +60,12 @@ public class ResourceHandlerRegistry {
|
||||
|
||||
|
||||
/**
|
||||
* Create a new resource handler registry for the given application context.
|
||||
* @param applicationContext the Spring application context
|
||||
* Create a new resource handler registry for the given resource loader
|
||||
* (typically an application context).
|
||||
* @param resourceLoader the resource loader to use
|
||||
*/
|
||||
public ResourceHandlerRegistry(ApplicationContext applicationContext) {
|
||||
|
||||
Assert.notNull(applicationContext, "ApplicationContext is required");
|
||||
this.applicationContext = applicationContext;
|
||||
public ResourceHandlerRegistry(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
|
||||
@@ -82,8 +80,7 @@ public class ResourceHandlerRegistry {
|
||||
* configure the registered resource handler
|
||||
*/
|
||||
public ResourceHandlerRegistration addResourceHandler(String... patterns) {
|
||||
ResourceHandlerRegistration registration =
|
||||
new ResourceHandlerRegistration(this.applicationContext, patterns);
|
||||
ResourceHandlerRegistration registration = new ResourceHandlerRegistration(this.resourceLoader, patterns);
|
||||
this.registrations.add(registration);
|
||||
return registration;
|
||||
}
|
||||
@@ -126,7 +123,7 @@ public class ResourceHandlerRegistry {
|
||||
try {
|
||||
handler.afterPropertiesSet();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
catch (Throwable ex) {
|
||||
throw new BeanInitializationException("Failed to init ResourceHttpRequestHandler", ex);
|
||||
}
|
||||
urlMap.put(pathPattern, handler);
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.reactive.result.view.HttpMessageWriterView;
|
||||
import org.springframework.web.reactive.result.view.UrlBasedViewResolver;
|
||||
@@ -46,17 +46,18 @@ import org.springframework.web.reactive.result.view.freemarker.FreeMarkerViewRes
|
||||
*/
|
||||
public class ViewResolverRegistry {
|
||||
|
||||
@Nullable
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
private final List<ViewResolver> viewResolvers = new ArrayList<>(4);
|
||||
|
||||
private final List<View> defaultViews = new ArrayList<>(4);
|
||||
|
||||
@Nullable
|
||||
private Integer order;
|
||||
|
||||
|
||||
public ViewResolverRegistry(ApplicationContext applicationContext) {
|
||||
Assert.notNull(applicationContext, "ApplicationContext must not be null");
|
||||
public ViewResolverRegistry(@Nullable ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@@ -67,7 +68,7 @@ public class ViewResolverRegistry {
|
||||
* adding a {@link FreeMarkerConfigurer} bean.
|
||||
*/
|
||||
public UrlBasedViewResolverRegistration freeMarker() {
|
||||
if (this.applicationContext != null && !hasBeanOfType(FreeMarkerConfigurer.class)) {
|
||||
if (!checkBeanOfType(FreeMarkerConfigurer.class)) {
|
||||
throw new BeanInitializationException("In addition to a FreeMarker view resolver " +
|
||||
"there must also be a single FreeMarkerConfig bean in this web application context " +
|
||||
"(or its parent): FreeMarkerConfigurer is the usual implementation. " +
|
||||
@@ -82,11 +83,6 @@ public class ViewResolverRegistry {
|
||||
return registration;
|
||||
}
|
||||
|
||||
protected boolean hasBeanOfType(Class<?> beanType) {
|
||||
return !ObjectUtils.isEmpty(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
|
||||
this.applicationContext, beanType, false, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a {@link ViewResolver} bean instance. This may be useful to
|
||||
* configure a 3rd party resolver implementation or as an alternative to
|
||||
@@ -126,6 +122,13 @@ public class ViewResolverRegistry {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
|
||||
private boolean checkBeanOfType(Class<?> beanType) {
|
||||
return (this.applicationContext == null ||
|
||||
!ObjectUtils.isEmpty(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
|
||||
this.applicationContext, beanType, false, false)));
|
||||
}
|
||||
|
||||
protected int getOrder() {
|
||||
return (this.order != null ? this.order : Ordered.LOWEST_PRECEDENCE);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.format.Formatter;
|
||||
import org.springframework.format.FormatterRegistry;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
@@ -58,11 +60,11 @@ import org.springframework.web.reactive.result.method.annotation.ResponseBodyRes
|
||||
import org.springframework.web.reactive.result.method.annotation.ResponseEntityResultHandler;
|
||||
import org.springframework.web.reactive.result.view.ViewResolutionResultHandler;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
import org.springframework.web.server.i18n.LocaleContextResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebExceptionHandler;
|
||||
import org.springframework.web.server.handler.ResponseStatusExceptionHandler;
|
||||
import org.springframework.web.server.i18n.AcceptHeaderLocaleContextResolver;
|
||||
import org.springframework.web.server.i18n.LocaleContextResolver;
|
||||
|
||||
/**
|
||||
* The main class for Spring WebFlux configuration.
|
||||
@@ -74,12 +76,16 @@ import org.springframework.web.server.i18n.AcceptHeaderLocaleContextResolver;
|
||||
*/
|
||||
public class WebFluxConfigurationSupport implements ApplicationContextAware {
|
||||
|
||||
@Nullable
|
||||
private Map<String, CorsConfiguration> corsConfigurations;
|
||||
|
||||
@Nullable
|
||||
private PathMatchConfigurer pathMatchConfigurer;
|
||||
|
||||
@Nullable
|
||||
private ViewResolverRegistry viewResolverRegistry;
|
||||
|
||||
@Nullable
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
|
||||
@@ -88,7 +94,8 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
protected ApplicationContext getApplicationContext() {
|
||||
@Nullable
|
||||
public final ApplicationContext getApplicationContext() {
|
||||
return this.applicationContext;
|
||||
}
|
||||
|
||||
@@ -205,7 +212,11 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware {
|
||||
*/
|
||||
@Bean
|
||||
public HandlerMapping resourceHandlerMapping() {
|
||||
ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext);
|
||||
ResourceLoader resourceLoader = this.applicationContext;
|
||||
if (resourceLoader == null) {
|
||||
resourceLoader = new DefaultResourceLoader();
|
||||
}
|
||||
ResourceHandlerRegistry registry = new ResourceHandlerRegistry(resourceLoader);
|
||||
addResourceHandlers(registry);
|
||||
|
||||
AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
|
||||
@@ -427,7 +438,7 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware {
|
||||
*/
|
||||
protected final ViewResolverRegistry getViewResolverRegistry() {
|
||||
if (this.viewResolverRegistry == null) {
|
||||
this.viewResolverRegistry = new ViewResolverRegistry(getApplicationContext());
|
||||
this.viewResolverRegistry = new ViewResolverRegistry(this.applicationContext);
|
||||
configureViewResolvers(this.viewResolverRegistry);
|
||||
}
|
||||
return this.viewResolverRegistry;
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.lang.Nullable;
|
||||
@SuppressWarnings("serial")
|
||||
public class UnsupportedMediaTypeException extends NestedRuntimeException {
|
||||
|
||||
@Nullable
|
||||
private final MediaType contentType;
|
||||
|
||||
private final List<MediaType> supportedMediaTypes;
|
||||
|
||||
@@ -63,8 +63,10 @@ class DefaultWebClient implements WebClient {
|
||||
|
||||
private final UriBuilderFactory uriBuilderFactory;
|
||||
|
||||
@Nullable
|
||||
private final HttpHeaders defaultHeaders;
|
||||
|
||||
@Nullable
|
||||
private final MultiValueMap<String, String> defaultCookies;
|
||||
|
||||
private final DefaultWebClientBuilder builder;
|
||||
@@ -170,10 +172,13 @@ class DefaultWebClient implements WebClient {
|
||||
|
||||
private final URI uri;
|
||||
|
||||
@Nullable
|
||||
private HttpHeaders headers;
|
||||
|
||||
@Nullable
|
||||
private MultiValueMap<String, String> cookies;
|
||||
|
||||
@Nullable
|
||||
private BodyInserter<?, ? super ClientHttpRequest> inserter;
|
||||
|
||||
DefaultRequestBodySpec(HttpMethod httpMethod, URI uri) {
|
||||
@@ -284,11 +289,9 @@ class DefaultWebClient implements WebClient {
|
||||
|
||||
@Override
|
||||
public Mono<ClientResponse> exchange() {
|
||||
|
||||
ClientRequest request = this.inserter != null ?
|
||||
ClientRequest request = (this.inserter != null ?
|
||||
initRequestBuilder().body(this.inserter).build() :
|
||||
initRequestBuilder().build();
|
||||
|
||||
initRequestBuilder().build());
|
||||
return exchangeFunction.exchange(request);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,24 +42,33 @@ import org.springframework.web.util.UriBuilderFactory;
|
||||
*/
|
||||
class DefaultWebClientBuilder implements WebClient.Builder {
|
||||
|
||||
@Nullable
|
||||
private String baseUrl;
|
||||
|
||||
@Nullable
|
||||
private Map<String, ?> defaultUriVariables;
|
||||
|
||||
@Nullable
|
||||
private UriBuilderFactory uriBuilderFactory;
|
||||
|
||||
@Nullable
|
||||
private HttpHeaders defaultHeaders;
|
||||
|
||||
@Nullable
|
||||
private MultiValueMap<String, String> defaultCookies;
|
||||
|
||||
@Nullable
|
||||
private List<ExchangeFilterFunction> filters;
|
||||
|
||||
@Nullable
|
||||
private ClientHttpConnector connector;
|
||||
|
||||
private ExchangeStrategies exchangeStrategies = ExchangeStrategies.withDefaults();
|
||||
|
||||
@Nullable
|
||||
private ExchangeFunction exchangeFunction;
|
||||
|
||||
|
||||
public DefaultWebClientBuilder() {
|
||||
}
|
||||
|
||||
@@ -85,6 +94,7 @@ class DefaultWebClientBuilder implements WebClient.Builder {
|
||||
this.exchangeFunction = other.exchangeFunction;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public WebClient.Builder baseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
@@ -105,9 +115,9 @@ class DefaultWebClientBuilder implements WebClient.Builder {
|
||||
|
||||
@Override
|
||||
public WebClient.Builder defaultHeader(String headerName, String... headerValues) {
|
||||
initHeaders();
|
||||
HttpHeaders headers = initHeaders();
|
||||
for (String headerValue : headerValues) {
|
||||
this.defaultHeaders.add(headerName, headerValue);
|
||||
headers.add(headerName, headerValue);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -115,37 +125,35 @@ class DefaultWebClientBuilder implements WebClient.Builder {
|
||||
@Override
|
||||
public WebClient.Builder defaultHeaders(Consumer<HttpHeaders> headersConsumer) {
|
||||
Assert.notNull(headersConsumer, "'headersConsumer' must not be null");
|
||||
initHeaders();
|
||||
headersConsumer.accept(this.defaultHeaders);
|
||||
headersConsumer.accept(initHeaders());
|
||||
return this;
|
||||
}
|
||||
|
||||
private void initHeaders() {
|
||||
private HttpHeaders initHeaders() {
|
||||
if (this.defaultHeaders == null) {
|
||||
this.defaultHeaders = new HttpHeaders();
|
||||
}
|
||||
return this.defaultHeaders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClient.Builder defaultCookie(String cookieName, String... cookieValues) {
|
||||
initCookies();
|
||||
this.defaultCookies.addAll(cookieName, Arrays.asList(cookieValues));
|
||||
initCookies().addAll(cookieName, Arrays.asList(cookieValues));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClient.Builder defaultCookies(
|
||||
Consumer<MultiValueMap<String, String>> cookiesConsumer) {
|
||||
Assert.notNull(cookiesConsumer, "'cookiesConsumer' must not be null");
|
||||
initCookies();
|
||||
cookiesConsumer.accept(this.defaultCookies);
|
||||
public WebClient.Builder defaultCookies(Consumer<MultiValueMap<String, String>> cookiesConsumer) {
|
||||
Assert.notNull(cookiesConsumer, "Cookies consumer must not be null");
|
||||
cookiesConsumer.accept(initCookies());
|
||||
return this;
|
||||
}
|
||||
|
||||
private void initCookies() {
|
||||
private MultiValueMap<String, String> initCookies() {
|
||||
if (this.defaultCookies == null) {
|
||||
this.defaultCookies = new LinkedMultiValueMap<>(4);
|
||||
}
|
||||
return this.defaultCookies;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -156,29 +164,28 @@ class DefaultWebClientBuilder implements WebClient.Builder {
|
||||
|
||||
@Override
|
||||
public WebClient.Builder filter(ExchangeFilterFunction filter) {
|
||||
Assert.notNull(filter, "'filter' must not be null");
|
||||
initFilters();
|
||||
this.filters.add(filter);
|
||||
Assert.notNull(filter, "ExchangeFilterFunction must not be null");
|
||||
initFilters().add(filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClient.Builder filters(Consumer<List<ExchangeFilterFunction>> filtersConsumer) {
|
||||
Assert.notNull(filtersConsumer, "'filtersConsumer' must not be null");
|
||||
initFilters();
|
||||
filtersConsumer.accept(this.filters);
|
||||
Assert.notNull(filtersConsumer, "Filters consumer must not be null");
|
||||
filtersConsumer.accept(initFilters());
|
||||
return this;
|
||||
}
|
||||
|
||||
private void initFilters() {
|
||||
private List<ExchangeFilterFunction> initFilters() {
|
||||
if (this.filters == null) {
|
||||
this.filters = new ArrayList<>();
|
||||
}
|
||||
return this.filters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClient.Builder exchangeStrategies(ExchangeStrategies strategies) {
|
||||
Assert.notNull(strategies, "'strategies' must not be null");
|
||||
Assert.notNull(strategies, "ExchangeStrategies must not be null");
|
||||
this.exchangeStrategies = strategies;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -392,6 +392,7 @@ public interface ServerResponse {
|
||||
Mono<ServerResponse> render(String name, Map<String, ?> model);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines the context used during the {@link #writeTo(ServerWebExchange, Context)}.
|
||||
*/
|
||||
@@ -399,13 +400,13 @@ public interface ServerResponse {
|
||||
|
||||
/**
|
||||
* Return the {@link HttpMessageWriter}s to be used for response body conversion.
|
||||
* @return the stream of message writers
|
||||
* @return the list of message writers
|
||||
*/
|
||||
List<HttpMessageWriter<?>> messageWriters();
|
||||
|
||||
/**
|
||||
* Return the {@link ViewResolver}s to be used for view name resolution.
|
||||
* @return the stream of view resolvers
|
||||
* @return the list of view resolvers
|
||||
*/
|
||||
List<ViewResolver> viewResolvers();
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
@@ -43,10 +45,13 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
*/
|
||||
public class RouterFunctionMapping extends AbstractHandlerMapping implements InitializingBean {
|
||||
|
||||
@Nullable
|
||||
private RouterFunction<?> routerFunction;
|
||||
|
||||
@Nullable
|
||||
private ServerCodecConfigurer messageCodecConfigurer;
|
||||
|
||||
|
||||
/**
|
||||
* Create an empty {@code RouterFunctionMapping}.
|
||||
* <p>If this constructor is used, this mapping will detect all {@link RouterFunction} instances
|
||||
@@ -64,6 +69,7 @@ public class RouterFunctionMapping extends AbstractHandlerMapping implements Ini
|
||||
this.routerFunction = routerFunction;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configure HTTP message readers to de-serialize the request body with.
|
||||
* <p>By default this is set to {@link ServerCodecConfigurer} with defaults.
|
||||
@@ -114,6 +120,7 @@ public class RouterFunctionMapping extends AbstractHandlerMapping implements Ini
|
||||
@Override
|
||||
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
|
||||
if (this.routerFunction != null) {
|
||||
Assert.state(this.messageCodecConfigurer != null, "No ServerCodecConfigurer set");
|
||||
ServerRequest request = ServerRequest.create(exchange, this.messageCodecConfigurer.getReaders());
|
||||
exchange.getAttributes().put(RouterFunctions.REQUEST_ATTRIBUTE, request);
|
||||
return this.routerFunction.route(request);
|
||||
@@ -123,8 +130,10 @@ public class RouterFunctionMapping extends AbstractHandlerMapping implements Ini
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class SortedRouterFunctionsContainer {
|
||||
|
||||
@Nullable
|
||||
private List<RouterFunction<?>> routerFunctions;
|
||||
|
||||
@Autowired(required = false)
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.HandlerResult;
|
||||
import org.springframework.web.reactive.HandlerResultHandler;
|
||||
@@ -38,12 +39,12 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ServerResponseResultHandler implements HandlerResultHandler, InitializingBean,
|
||||
Ordered {
|
||||
public class ServerResponseResultHandler implements HandlerResultHandler, InitializingBean, Ordered {
|
||||
|
||||
@Nullable
|
||||
private ServerCodecConfigurer messageCodecConfigurer;
|
||||
|
||||
private List<ViewResolver> viewResolvers;
|
||||
private List<ViewResolver> viewResolvers = Collections.emptyList();
|
||||
|
||||
private int order = LOWEST_PRECEDENCE;
|
||||
|
||||
@@ -79,10 +80,7 @@ public class ServerResponseResultHandler implements HandlerResultHandler, Initia
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (this.messageCodecConfigurer == null) {
|
||||
throw new IllegalArgumentException("'messageCodecConfigurer' is required");
|
||||
}
|
||||
if (this.viewResolvers == null) {
|
||||
this.viewResolvers = Collections.emptyList();
|
||||
throw new IllegalArgumentException("Property 'messageCodecConfigurer' is required");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,9 +96,9 @@ public class ServerResponseResultHandler implements HandlerResultHandler, Initia
|
||||
return response.writeTo(exchange, new ServerResponse.Context() {
|
||||
@Override
|
||||
public List<HttpMessageWriter<?>> messageWriters() {
|
||||
return messageCodecConfigurer.getWriters();
|
||||
return (messageCodecConfigurer != null ?
|
||||
messageCodecConfigurer.getWriters() : Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ViewResolver> viewResolvers() {
|
||||
return viewResolvers;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.web.reactive.handler;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -50,11 +51,9 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
||||
|
||||
private boolean lazyInitHandlers = false;
|
||||
|
||||
@Nullable
|
||||
private PathPatternRegistry<Object> patternRegistry;
|
||||
|
||||
public PathPatternRegistry<Object> getPatternRegistry() {
|
||||
return patternRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to lazily initialize handlers. Only applicable to
|
||||
@@ -76,7 +75,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
||||
* as value.
|
||||
*/
|
||||
public final Map<PathPattern, Object> getHandlerMap() {
|
||||
return this.patternRegistry.getPatternsMap();
|
||||
return (this.patternRegistry != null ? this.patternRegistry.getPatternsMap() : Collections.emptyMap());
|
||||
}
|
||||
|
||||
|
||||
@@ -103,11 +102,9 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
||||
|
||||
/**
|
||||
* Look up a handler instance for the given URL lookup path.
|
||||
*
|
||||
* <p>Supports direct matches, e.g. a registered "/test" matches "/test",
|
||||
* and various path pattern matches, e.g. a registered "/t*" matches
|
||||
* both "/test" and "/team". For details, see the PathPattern class.
|
||||
*
|
||||
* @param lookupPath URL the handler is mapped to
|
||||
* @param exchange the current exchange
|
||||
* @return the associated handler instance, or {@code null} if not found
|
||||
@@ -115,19 +112,17 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
||||
*/
|
||||
@Nullable
|
||||
protected Object lookupHandler(String lookupPath, ServerWebExchange exchange) throws Exception {
|
||||
Optional<PathMatchResult<Object>> matches = this.patternRegistry.findFirstMatch(lookupPath);
|
||||
if (matches.isPresent()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Matching patterns for request [" + lookupPath + "] are " + matches);
|
||||
if (this.patternRegistry != null) {
|
||||
Optional<PathMatchResult<Object>> matches = this.patternRegistry.findFirstMatch(lookupPath);
|
||||
if (matches.isPresent()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Matching patterns for request [" + lookupPath + "] are " + matches);
|
||||
}
|
||||
PathMatchResult<Object> bestMatch = matches.get();
|
||||
String pathWithinMapping = bestMatch.getPattern().extractPathWithinPattern(lookupPath);
|
||||
Object handler = bestMatch.getHandler();
|
||||
return handleMatch(handler, bestMatch.getPattern(), pathWithinMapping, exchange);
|
||||
}
|
||||
PathMatchResult<Object> bestMatch = matches.get();
|
||||
String pathWithinMapping = bestMatch.getPattern().extractPathWithinPattern(lookupPath);
|
||||
Object handler = bestMatch.getHandler();
|
||||
if (handler == null) {
|
||||
throw new IllegalStateException(
|
||||
"Could not find handler for best pattern match [" + bestMatch + "]");
|
||||
}
|
||||
return handleMatch(handler, bestMatch.getPattern(), pathWithinMapping, exchange);
|
||||
}
|
||||
|
||||
// No handler found...
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.web.reactive.handler;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.util.pattern.PathPattern;
|
||||
|
||||
@@ -37,8 +36,9 @@ public class PathMatchResult<T> {
|
||||
private final T handler;
|
||||
|
||||
|
||||
public PathMatchResult(PathPattern pattern, @Nullable T handler) {
|
||||
public PathMatchResult(PathPattern pattern, T handler) {
|
||||
Assert.notNull(pattern, "PathPattern must not be null");
|
||||
Assert.notNull(handler, "Handler must not be null");
|
||||
this.pattern = pattern;
|
||||
this.handler = handler;
|
||||
}
|
||||
@@ -54,7 +54,6 @@ public class PathMatchResult<T> {
|
||||
/**
|
||||
* Return the request handler associated with the {@link PathPattern}.
|
||||
*/
|
||||
@Nullable
|
||||
public T getHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
@@ -164,14 +164,13 @@ public class AppCacheManifestTransformer extends ResourceTransformerSupport {
|
||||
|
||||
private final Scanner scanner;
|
||||
|
||||
@Nullable
|
||||
private LineInfo previous;
|
||||
|
||||
|
||||
public LineGenerator(String content) {
|
||||
this.scanner = new Scanner(content);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void accept(SynchronousSink<LineInfo> sink) {
|
||||
if (this.scanner.hasNext()) {
|
||||
@@ -186,6 +185,7 @@ public class AppCacheManifestTransformer extends ResourceTransformerSupport {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class LineInfo {
|
||||
|
||||
private final String line;
|
||||
@@ -194,8 +194,7 @@ public class AppCacheManifestTransformer extends ResourceTransformerSupport {
|
||||
|
||||
private final boolean link;
|
||||
|
||||
|
||||
public LineInfo(String line, LineInfo previousLine) {
|
||||
public LineInfo(String line, @Nullable LineInfo previousLine) {
|
||||
this.line = line;
|
||||
this.cacheSection = initCacheSectionFlag(line, previousLine);
|
||||
this.link = iniLinkFlag(line, this.cacheSection);
|
||||
@@ -222,7 +221,6 @@ public class AppCacheManifestTransformer extends ResourceTransformerSupport {
|
||||
return (line.startsWith("//") || (index > 0 && !line.substring(0, index).contains("/")));
|
||||
}
|
||||
|
||||
|
||||
public String getLine() {
|
||||
return this.line;
|
||||
}
|
||||
@@ -236,13 +234,14 @@ public class AppCacheManifestTransformer extends ResourceTransformerSupport {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class LineOutput {
|
||||
|
||||
private final String line;
|
||||
|
||||
@Nullable
|
||||
private final Resource resource;
|
||||
|
||||
|
||||
public LineOutput(String line, @Nullable Resource resource) {
|
||||
this.line = line;
|
||||
this.resource = resource;
|
||||
@@ -258,6 +257,7 @@ public class AppCacheManifestTransformer extends ResourceTransformerSupport {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class LineAggregator {
|
||||
|
||||
private final StringWriter writer = new StringWriter();
|
||||
@@ -266,7 +266,6 @@ public class AppCacheManifestTransformer extends ResourceTransformerSupport {
|
||||
|
||||
private final Resource resource;
|
||||
|
||||
|
||||
public LineAggregator(Resource resource, String content) {
|
||||
this.resource = resource;
|
||||
this.baos = new ByteArrayOutputStream(content.length());
|
||||
|
||||
@@ -205,9 +205,6 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
|
||||
logger.trace("Invoking ResourceResolverChain for URL pattern \"" + result.getPattern() + "\"");
|
||||
}
|
||||
ResourceWebHandler handler = result.getHandler();
|
||||
if (handler == null) {
|
||||
throw new IllegalStateException("No handler for URL pattern \"" + result.getPattern() + "\"");
|
||||
}
|
||||
ResourceResolverChain chain = new DefaultResourceResolverChain(handler.getResourceResolvers());
|
||||
return chain.resolveUrlPath(pathWithinMapping, handler.getLocations())
|
||||
.map(resolvedPath -> {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* 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.
|
||||
@@ -20,17 +20,19 @@ import java.io.IOException;
|
||||
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* An extension of {@link ByteArrayResource}
|
||||
* that a {@link ResourceTransformer} can use to represent an original
|
||||
* resource preserving all other information except the content.
|
||||
* An extension of {@link ByteArrayResource} that a {@link ResourceTransformer}
|
||||
* can use to represent an original resource preserving all other information
|
||||
* except the content.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class TransformedResource extends ByteArrayResource {
|
||||
|
||||
@Nullable
|
||||
private final String filename;
|
||||
|
||||
private final long lastModified;
|
||||
@@ -50,6 +52,7 @@ public class TransformedResource extends ByteArrayResource {
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getFilename() {
|
||||
return this.filename;
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ import org.springframework.web.util.pattern.PathPatternParser;
|
||||
*/
|
||||
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
|
||||
|
||||
@Nullable
|
||||
private final String name;
|
||||
|
||||
private final PatternsRequestCondition patternsCondition;
|
||||
@@ -392,18 +393,25 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||
|
||||
private String[] paths;
|
||||
|
||||
@Nullable
|
||||
private RequestMethod[] methods;
|
||||
|
||||
@Nullable
|
||||
private String[] params;
|
||||
|
||||
@Nullable
|
||||
private String[] headers;
|
||||
|
||||
@Nullable
|
||||
private String[] consumes;
|
||||
|
||||
@Nullable
|
||||
private String[] produces;
|
||||
|
||||
@Nullable
|
||||
private String mappingName;
|
||||
|
||||
@Nullable
|
||||
private RequestCondition<?> customCondition;
|
||||
|
||||
private BuilderConfiguration options = new BuilderConfiguration();
|
||||
@@ -492,19 +500,21 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||
*/
|
||||
public static class BuilderConfiguration {
|
||||
|
||||
@Nullable
|
||||
private PathPatternParser patternParser;
|
||||
|
||||
@Nullable
|
||||
private RequestedContentTypeResolver contentTypeResolver;
|
||||
|
||||
public void setPatternParser(PathPatternParser patternParser) {
|
||||
this.patternParser = patternParser;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PathPatternParser getPatternParser() {
|
||||
return this.patternParser;
|
||||
}
|
||||
|
||||
public void setPatternParser(PathPatternParser patternParser) {
|
||||
this.patternParser = patternParser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ContentNegotiationManager to use for the ProducesRequestCondition.
|
||||
* <p>By default this is not set.
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.reactive.BindingContext;
|
||||
import org.springframework.web.reactive.HandlerResult;
|
||||
@@ -93,6 +94,7 @@ public class SyncInvocableHandlerMethod extends HandlerMethod {
|
||||
* @param providedArgs optional list of argument values to match by type
|
||||
* @return Mono with a {@link HandlerResult}.
|
||||
*/
|
||||
@Nullable
|
||||
public HandlerResult invokeForHandlerResult(ServerWebExchange exchange,
|
||||
BindingContext bindingContext, Object... providedArgs) {
|
||||
|
||||
|
||||
@@ -60,8 +60,10 @@ import org.springframework.web.server.ServerWebInputException;
|
||||
*/
|
||||
public abstract class AbstractNamedValueArgumentResolver extends HandlerMethodArgumentResolverSupport {
|
||||
|
||||
@Nullable
|
||||
private final ConfigurableBeanFactory configurableBeanFactory;
|
||||
|
||||
@Nullable
|
||||
private final BeanExpressionContext expressionContext;
|
||||
|
||||
private final Map<MethodParameter, NamedValueInfo> namedValueInfoCache = new ConcurrentHashMap<>(256);
|
||||
@@ -154,7 +156,7 @@ public abstract class AbstractNamedValueArgumentResolver extends HandlerMethodAr
|
||||
*/
|
||||
@Nullable
|
||||
private Object resolveStringValue(String value) {
|
||||
if (this.configurableBeanFactory == null) {
|
||||
if (this.configurableBeanFactory == null || this.expressionContext == null) {
|
||||
return value;
|
||||
}
|
||||
String placeholdersResolved = this.configurableBeanFactory.resolveEmbeddedValue(value);
|
||||
@@ -291,6 +293,7 @@ public abstract class AbstractNamedValueArgumentResolver extends HandlerMethodAr
|
||||
|
||||
private final boolean required;
|
||||
|
||||
@Nullable
|
||||
private final String defaultValue;
|
||||
|
||||
public NamedValueInfo(String name, boolean required, @Nullable String defaultValue) {
|
||||
|
||||
@@ -52,7 +52,7 @@ import org.springframework.web.reactive.result.method.InvocableHandlerMethod;
|
||||
import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver;
|
||||
import org.springframework.web.reactive.result.method.SyncInvocableHandlerMethod;
|
||||
|
||||
import static org.springframework.core.MethodIntrospector.selectMethods;
|
||||
import static org.springframework.core.MethodIntrospector.*;
|
||||
|
||||
/**
|
||||
* Package-private class to assist {@link RequestMappingHandlerAdapter} with
|
||||
@@ -218,7 +218,6 @@ class ControllerMethodResolver {
|
||||
* or in the controller of the given {@code @RequestMapping} method.
|
||||
*/
|
||||
public List<SyncInvocableHandlerMethod> getInitBinderMethods(HandlerMethod handlerMethod) {
|
||||
|
||||
List<SyncInvocableHandlerMethod> result = new ArrayList<>();
|
||||
Class<?> handlerType = handlerMethod.getBeanType();
|
||||
|
||||
@@ -251,7 +250,6 @@ class ControllerMethodResolver {
|
||||
* components or in the controller of the given {@code @RequestMapping} method.
|
||||
*/
|
||||
public List<InvocableHandlerMethod> getModelAttributeMethods(HandlerMethod handlerMethod) {
|
||||
|
||||
List<InvocableHandlerMethod> result = new ArrayList<>();
|
||||
Class<?> handlerType = handlerMethod.getBeanType();
|
||||
|
||||
@@ -331,18 +329,18 @@ class ControllerMethodResolver {
|
||||
|
||||
private final List<HandlerMethodArgumentResolver> customResolvers;
|
||||
|
||||
@Nullable
|
||||
private final List<HttpMessageReader<?>> messageReaders;
|
||||
|
||||
private final boolean modelAttributeSupported;
|
||||
|
||||
private final List<HandlerMethodArgumentResolver> result = new ArrayList<>();
|
||||
|
||||
|
||||
private ArgumentResolverRegistrar(ArgumentResolverConfigurer resolvers,
|
||||
@Nullable ServerCodecConfigurer codecs, boolean modelAttribute) {
|
||||
|
||||
this.customResolvers = resolvers.getCustomResolvers();
|
||||
this.messageReaders = codecs != null ? codecs.getReaders() : null;
|
||||
this.messageReaders = (codecs != null ? codecs.getReaders() : null);
|
||||
this.modelAttributeSupported = modelAttribute;
|
||||
}
|
||||
|
||||
@@ -379,7 +377,6 @@ class ControllerMethodResolver {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
public static Builder configurer(ArgumentResolverConfigurer configurer) {
|
||||
return new Builder(configurer);
|
||||
}
|
||||
@@ -389,12 +386,10 @@ class ControllerMethodResolver {
|
||||
|
||||
private final ArgumentResolverConfigurer resolvers;
|
||||
|
||||
|
||||
public Builder(ArgumentResolverConfigurer configurer) {
|
||||
this.resolvers = configurer;
|
||||
}
|
||||
|
||||
|
||||
public ArgumentResolverRegistrar fullSupport(ServerCodecConfigurer codecs) {
|
||||
return new ArgumentResolverRegistrar(this.resolvers, codecs, true);
|
||||
}
|
||||
@@ -407,7 +402,6 @@ class ControllerMethodResolver {
|
||||
return new ArgumentResolverRegistrar(this.resolvers, null, false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||
import org.springframework.web.bind.support.WebExchangeDataBinder;
|
||||
import org.springframework.web.reactive.BindingContext;
|
||||
import org.springframework.web.reactive.HandlerResult;
|
||||
import org.springframework.web.reactive.result.method.SyncInvocableHandlerMethod;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
@@ -71,10 +72,10 @@ class InitBinderBindingContext extends BindingContext {
|
||||
private void invokeBinderMethod(WebExchangeDataBinder dataBinder,
|
||||
ServerWebExchange exchange, SyncInvocableHandlerMethod binderMethod) {
|
||||
|
||||
Object returnValue = binderMethod.invokeForHandlerResult(exchange, this.binderMethodContext, dataBinder)
|
||||
.getReturnValue();
|
||||
HandlerResult result = binderMethod.invokeForHandlerResult(
|
||||
exchange, this.binderMethodContext, dataBinder);
|
||||
|
||||
if (returnValue != null) {
|
||||
if (result != null && result.getReturnValue() != null) {
|
||||
throw new IllegalStateException(
|
||||
"@InitBinder methods should return void: " + binderMethod);
|
||||
}
|
||||
|
||||
@@ -50,18 +50,25 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application
|
||||
private static final Log logger = LogFactory.getLog(RequestMappingHandlerAdapter.class);
|
||||
|
||||
|
||||
@Nullable
|
||||
private ServerCodecConfigurer messageCodecConfigurer;
|
||||
|
||||
@Nullable
|
||||
private WebBindingInitializer webBindingInitializer;
|
||||
|
||||
@Nullable
|
||||
private ArgumentResolverConfigurer argumentResolverConfigurer;
|
||||
|
||||
@Nullable
|
||||
private ReactiveAdapterRegistry reactiveAdapterRegistry;
|
||||
|
||||
@Nullable
|
||||
private ConfigurableApplicationContext applicationContext;
|
||||
|
||||
@Nullable
|
||||
private ControllerMethodResolver methodResolver;
|
||||
|
||||
@Nullable
|
||||
private ModelInitializer modelInitializer;
|
||||
|
||||
|
||||
@@ -76,6 +83,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application
|
||||
/**
|
||||
* Return the configurer for HTTP message readers.
|
||||
*/
|
||||
@Nullable
|
||||
public ServerCodecConfigurer getMessageCodecConfigurer() {
|
||||
return this.messageCodecConfigurer;
|
||||
}
|
||||
@@ -107,6 +115,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application
|
||||
/**
|
||||
* Return the configured resolvers for controller method arguments.
|
||||
*/
|
||||
@Nullable
|
||||
public ArgumentResolverConfigurer getArgumentResolverConfigurer() {
|
||||
return this.argumentResolverConfigurer;
|
||||
}
|
||||
@@ -123,6 +132,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application
|
||||
/**
|
||||
* Return the configured registry for adapting reactive types.
|
||||
*/
|
||||
@Nullable
|
||||
public ReactiveAdapterRegistry getReactiveAdapterRegistry() {
|
||||
return this.reactiveAdapterRegistry;
|
||||
}
|
||||
@@ -139,22 +149,17 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application
|
||||
}
|
||||
}
|
||||
|
||||
public ConfigurableApplicationContext getApplicationContext() {
|
||||
return this.applicationContext;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(this.applicationContext, "ApplicationContext is required");
|
||||
|
||||
if (this.messageCodecConfigurer == null) {
|
||||
this.messageCodecConfigurer = ServerCodecConfigurer.create();
|
||||
}
|
||||
|
||||
if (this.argumentResolverConfigurer == null) {
|
||||
this.argumentResolverConfigurer = new ArgumentResolverConfigurer();
|
||||
}
|
||||
|
||||
if (this.reactiveAdapterRegistry == null) {
|
||||
this.reactiveAdapterRegistry = new ReactiveAdapterRegistry();
|
||||
}
|
||||
@@ -173,9 +178,8 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application
|
||||
|
||||
@Override
|
||||
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
|
||||
|
||||
Assert.notNull(handler, "Expected handler");
|
||||
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
||||
Assert.state(this.methodResolver != null && this.modelInitializer != null, "Not initialized");
|
||||
|
||||
BindingContext bindingContext = new InitBinderBindingContext(
|
||||
getWebBindingInitializer(), this.methodResolver.getInitBinderMethods(handlerMethod));
|
||||
@@ -197,6 +201,8 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application
|
||||
private Mono<HandlerResult> handleException(Throwable ex, HandlerMethod handlerMethod,
|
||||
BindingContext bindingContext, ServerWebExchange exchange) {
|
||||
|
||||
Assert.state(this.methodResolver != null, "Not initialized");
|
||||
|
||||
InvocableHandlerMethod invocable = this.methodResolver.getExceptionHandlerMethod(ex, handlerMethod);
|
||||
if (invocable != null) {
|
||||
try {
|
||||
|
||||
@@ -50,6 +50,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||
|
||||
private RequestedContentTypeResolver contentTypeResolver = new RequestedContentTypeResolverBuilder().build();
|
||||
|
||||
@Nullable
|
||||
private StringValueResolver embeddedValueResolver;
|
||||
|
||||
private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
|
||||
|
||||
@@ -62,8 +62,10 @@ public abstract class AbstractView implements View, ApplicationContextAware {
|
||||
|
||||
private Charset defaultCharset = StandardCharsets.UTF_8;
|
||||
|
||||
@Nullable
|
||||
private String requestContextAttribute;
|
||||
|
||||
@Nullable
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.PropertyAccessorFactory;
|
||||
import org.springframework.context.NoSuchMessageException;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Errors;
|
||||
@@ -51,25 +52,34 @@ public class BindStatus {
|
||||
|
||||
private final boolean htmlEscape;
|
||||
|
||||
@Nullable
|
||||
private final String expression;
|
||||
|
||||
@Nullable
|
||||
private final Errors errors;
|
||||
|
||||
|
||||
@Nullable
|
||||
private BindingResult bindingResult;
|
||||
|
||||
@Nullable
|
||||
private Object value;
|
||||
|
||||
@Nullable
|
||||
private Class<?> valueType;
|
||||
|
||||
@Nullable
|
||||
private Object actualValue;
|
||||
|
||||
@Nullable
|
||||
private PropertyEditor editor;
|
||||
|
||||
@Nullable
|
||||
private List<? extends ObjectError> objectErrors;
|
||||
|
||||
private String[] errorCodes;
|
||||
private String[] errorCodes = new String[0];
|
||||
|
||||
@Nullable
|
||||
private String[] errorMessages;
|
||||
|
||||
|
||||
@@ -244,14 +254,13 @@ public class BindStatus {
|
||||
* Return if this status represents a field or object error.
|
||||
*/
|
||||
public boolean isError() {
|
||||
return (this.errorCodes != null && this.errorCodes.length > 0);
|
||||
return (this.errorCodes.length > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the error codes for the field or object, if any.
|
||||
* Returns an empty array instead of null if none.
|
||||
*/
|
||||
@Nullable
|
||||
public String[] getErrorCodes() {
|
||||
return this.errorCodes;
|
||||
}
|
||||
@@ -260,7 +269,7 @@ public class BindStatus {
|
||||
* Return the first error codes for the field or object, if any.
|
||||
*/
|
||||
public String getErrorCode() {
|
||||
return (this.errorCodes.length > 0 ? this.errorCodes[0] : "");
|
||||
return (!ObjectUtils.isEmpty(this.errorCodes) ? this.errorCodes[0] : "");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,16 +277,15 @@ public class BindStatus {
|
||||
* if any. Returns an empty array instead of null if none.
|
||||
*/
|
||||
public String[] getErrorMessages() {
|
||||
initErrorMessages();
|
||||
return this.errorMessages;
|
||||
return initErrorMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first error message for the field or object, if any.
|
||||
*/
|
||||
public String getErrorMessage() {
|
||||
initErrorMessages();
|
||||
return (this.errorMessages.length > 0 ? this.errorMessages[0] : "");
|
||||
String[] errorMessages = initErrorMessages();
|
||||
return (errorMessages.length > 0 ? errorMessages[0] : "");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -287,21 +295,26 @@ public class BindStatus {
|
||||
* @return the error message string
|
||||
*/
|
||||
public String getErrorMessagesAsString(String delimiter) {
|
||||
initErrorMessages();
|
||||
return StringUtils.arrayToDelimitedString(this.errorMessages, delimiter);
|
||||
return StringUtils.arrayToDelimitedString(initErrorMessages(), delimiter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the error messages from the ObjectError list.
|
||||
*/
|
||||
private void initErrorMessages() throws NoSuchMessageException {
|
||||
private String[] initErrorMessages() throws NoSuchMessageException {
|
||||
if (this.errorMessages == null) {
|
||||
this.errorMessages = new String[this.objectErrors.size()];
|
||||
for (int i = 0; i < this.objectErrors.size(); i++) {
|
||||
ObjectError error = this.objectErrors.get(i);
|
||||
this.errorMessages[i] = this.requestContext.getMessage(error, this.htmlEscape);
|
||||
if (this.objectErrors != null) {
|
||||
this.errorMessages = new String[this.objectErrors.size()];
|
||||
for (int i = 0; i < this.objectErrors.size(); i++) {
|
||||
ObjectError error = this.objectErrors.get(i);
|
||||
this.errorMessages[i] = this.requestContext.getMessage(error, this.htmlEscape);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.errorMessages = new String[0];
|
||||
}
|
||||
}
|
||||
return this.errorMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -343,7 +356,7 @@ public class BindStatus {
|
||||
StringBuilder sb = new StringBuilder("BindStatus: ");
|
||||
sb.append("expression=[").append(this.expression).append("]; ");
|
||||
sb.append("value=[").append(this.value).append("]");
|
||||
if (isError()) {
|
||||
if (!ObjectUtils.isEmpty(this.errorCodes)) {
|
||||
sb.append("; errorCodes=").append(Arrays.asList(this.errorCodes));
|
||||
}
|
||||
return sb.toString();
|
||||
|
||||
@@ -39,12 +39,13 @@ class DefaultRendering implements Rendering {
|
||||
|
||||
private final Map<String, Object> model;
|
||||
|
||||
@Nullable
|
||||
private final HttpStatus status;
|
||||
|
||||
private final HttpHeaders headers;
|
||||
|
||||
|
||||
DefaultRendering(Object view, @Nullable Model model, HttpStatus status, @Nullable HttpHeaders headers) {
|
||||
DefaultRendering(Object view, @Nullable Model model, @Nullable HttpStatus status, @Nullable HttpHeaders headers) {
|
||||
this.view = view;
|
||||
this.model = (model != null ? model.asMap() : Collections.emptyMap());
|
||||
this.status = status;
|
||||
@@ -63,6 +64,7 @@ class DefaultRendering implements Rendering {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public HttpStatus status() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.result.view;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -20,6 +21,7 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -34,10 +36,13 @@ class DefaultRenderingBuilder implements Rendering.RedirectBuilder {
|
||||
|
||||
private final Object view;
|
||||
|
||||
@Nullable
|
||||
private Model model;
|
||||
|
||||
@Nullable
|
||||
private HttpStatus status;
|
||||
|
||||
@Nullable
|
||||
private HttpHeaders headers;
|
||||
|
||||
|
||||
@@ -48,38 +53,35 @@ class DefaultRenderingBuilder implements Rendering.RedirectBuilder {
|
||||
|
||||
@Override
|
||||
public DefaultRenderingBuilder modelAttribute(String name, Object value) {
|
||||
initModel();
|
||||
this.model.addAttribute(name, value);
|
||||
initModel().addAttribute(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void initModel() {
|
||||
if (this.model == null) {
|
||||
this.model = new ExtendedModelMap();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultRenderingBuilder modelAttribute(Object value) {
|
||||
initModel();
|
||||
this.model.addAttribute(value);
|
||||
initModel().addAttribute(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultRenderingBuilder modelAttributes(Object... values) {
|
||||
initModel();
|
||||
this.model.addAllAttributes(Arrays.asList(values));
|
||||
initModel().addAllAttributes(Arrays.asList(values));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultRenderingBuilder model(Map<String, ?> map) {
|
||||
initModel();
|
||||
this.model.addAllAttributes(map);
|
||||
initModel().addAllAttributes(map);
|
||||
return this;
|
||||
}
|
||||
|
||||
private Model initModel() {
|
||||
if (this.model == null) {
|
||||
this.model = new ExtendedModelMap();
|
||||
}
|
||||
return this.model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultRenderingBuilder status(HttpStatus status) {
|
||||
this.status = status;
|
||||
@@ -88,22 +90,21 @@ class DefaultRenderingBuilder implements Rendering.RedirectBuilder {
|
||||
|
||||
@Override
|
||||
public DefaultRenderingBuilder header(String headerName, String... headerValues) {
|
||||
initHeaders();
|
||||
this.headers.put(headerName, Arrays.asList(headerValues));
|
||||
initHeaders().put(headerName, Arrays.asList(headerValues));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultRenderingBuilder headers(HttpHeaders headers) {
|
||||
initHeaders();
|
||||
this.headers.putAll(headers);
|
||||
initHeaders().putAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void initHeaders() {
|
||||
private HttpHeaders initHeaders() {
|
||||
if (this.headers == null) {
|
||||
this.headers = new HttpHeaders();
|
||||
}
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -123,6 +124,7 @@ class DefaultRenderingBuilder implements Rendering.RedirectBuilder {
|
||||
return (RedirectView) this.view;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Rendering build() {
|
||||
return new DefaultRendering(this.view, this.model, this.status, this.headers);
|
||||
|
||||
@@ -65,10 +65,13 @@ public class RequestContext {
|
||||
|
||||
private TimeZone timeZone;
|
||||
|
||||
@Nullable
|
||||
private Boolean defaultHtmlEscape;
|
||||
|
||||
@Nullable
|
||||
private Map<String, Errors> errorsMap;
|
||||
|
||||
@Nullable
|
||||
private RequestDataValueProcessor dataValueProcessor;
|
||||
|
||||
|
||||
@@ -79,19 +82,21 @@ public class RequestContext {
|
||||
public RequestContext(ServerWebExchange exchange, Map<String, Object> model, MessageSource messageSource,
|
||||
@Nullable RequestDataValueProcessor dataValueProcessor) {
|
||||
|
||||
Assert.notNull(exchange, "'exchange' is required");
|
||||
Assert.notNull(model, "'model' is required");
|
||||
Assert.notNull(messageSource, "'messageSource' is required");
|
||||
Assert.notNull(exchange, "ServerWebExchange is required");
|
||||
Assert.notNull(model, "Model is required");
|
||||
Assert.notNull(messageSource, "MessageSource is required");
|
||||
this.exchange = exchange;
|
||||
this.model = model;
|
||||
this.messageSource = messageSource;
|
||||
|
||||
LocaleContext localeContext = exchange.getLocaleContext();
|
||||
this.locale = localeContext.getLocale();
|
||||
this.timeZone = (localeContext instanceof TimeZoneAwareLocaleContext ?
|
||||
((TimeZoneAwareLocaleContext)localeContext).getTimeZone() : TimeZone.getDefault());
|
||||
Locale locale = localeContext.getLocale();
|
||||
this.locale = (locale != null ? locale : Locale.getDefault());
|
||||
TimeZone timeZone = (localeContext instanceof TimeZoneAwareLocaleContext ?
|
||||
((TimeZoneAwareLocaleContext) localeContext).getTimeZone() : null);
|
||||
this.timeZone = (timeZone != null ? timeZone : TimeZone.getDefault());
|
||||
|
||||
this.defaultHtmlEscape = null; // TODO
|
||||
this.defaultHtmlEscape = null; // TODO
|
||||
this.dataValueProcessor = dataValueProcessor;
|
||||
}
|
||||
|
||||
@@ -125,7 +130,6 @@ public class RequestContext {
|
||||
|
||||
/**
|
||||
* Return the current TimeZone.
|
||||
* TODO: currently this is the Timezone.getDefault()
|
||||
*/
|
||||
public TimeZone getTimeZone() {
|
||||
return this.timeZone;
|
||||
@@ -168,6 +172,7 @@ public class RequestContext {
|
||||
* specified and an explicit value.
|
||||
* @return whether default HTML escaping is enabled (null = no explicit default)
|
||||
*/
|
||||
@Nullable
|
||||
public Boolean getDefaultHtmlEscape() {
|
||||
return this.defaultHtmlEscape;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* 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.
|
||||
@@ -26,7 +26,9 @@ import freemarker.template.TemplateException;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.ui.freemarker.FreeMarkerConfigurationFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Configures FreeMarker for web usage via the "configLocation" and/or
|
||||
@@ -62,6 +64,7 @@ import org.springframework.ui.freemarker.FreeMarkerConfigurationFactory;
|
||||
public class FreeMarkerConfigurer extends FreeMarkerConfigurationFactory
|
||||
implements FreeMarkerConfig, InitializingBean, ResourceLoaderAware {
|
||||
|
||||
@Nullable
|
||||
private Configuration configuration;
|
||||
|
||||
|
||||
@@ -111,7 +114,8 @@ public class FreeMarkerConfigurer extends FreeMarkerConfigurationFactory
|
||||
*/
|
||||
@Override
|
||||
public Configuration getConfiguration() {
|
||||
Assert.state(this.configuration != null, "No Configuration available");
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
@@ -74,22 +73,28 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
|
||||
private static final String DEFAULT_RESOURCE_LOADER_PATH = "classpath:";
|
||||
|
||||
|
||||
@Nullable
|
||||
private ScriptEngine engine;
|
||||
|
||||
@Nullable
|
||||
private String engineName;
|
||||
|
||||
@Nullable
|
||||
private Boolean sharedEngine;
|
||||
|
||||
@Nullable
|
||||
private String[] scripts;
|
||||
|
||||
@Nullable
|
||||
private String renderObject;
|
||||
|
||||
@Nullable
|
||||
private String renderFunction;
|
||||
|
||||
@Nullable
|
||||
private String[] resourceLoaderPaths;
|
||||
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
@Nullable
|
||||
private volatile ScriptEngineManager scriptEngineManager;
|
||||
|
||||
|
||||
@@ -193,9 +198,6 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
|
||||
String resourceLoaderPath = viewConfig.getResourceLoaderPath();
|
||||
setResourceLoaderPath(resourceLoaderPath == null ? DEFAULT_RESOURCE_LOADER_PATH : resourceLoaderPath);
|
||||
}
|
||||
if (this.resourceLoader == null) {
|
||||
this.resourceLoader = getApplicationContext();
|
||||
}
|
||||
if (this.sharedEngine == null && viewConfig.isSharedEngine() != null) {
|
||||
this.sharedEngine = viewConfig.isSharedEngine();
|
||||
}
|
||||
@@ -214,24 +216,34 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
|
||||
loadScripts(this.engine);
|
||||
}
|
||||
else {
|
||||
setEngine(createEngineFromName());
|
||||
setEngine(createEngineFromName(this.engineName));
|
||||
}
|
||||
|
||||
if (this.renderFunction != null && this.engine != null) {
|
||||
Assert.isInstanceOf(Invocable.class, this.engine, "ScriptEngine must implement Invocable when 'renderFunction' is specified.");
|
||||
Assert.isInstanceOf(Invocable.class, this.engine,
|
||||
"ScriptEngine must implement Invocable when 'renderFunction' is specified");
|
||||
}
|
||||
}
|
||||
|
||||
protected ScriptEngine getEngine() {
|
||||
return Boolean.FALSE.equals(this.sharedEngine) ? createEngineFromName() : this.engine;
|
||||
if (Boolean.FALSE.equals(this.sharedEngine)) {
|
||||
Assert.state(this.engineName != null, "No engine name specified");
|
||||
return createEngineFromName(this.engineName);
|
||||
}
|
||||
else {
|
||||
Assert.state(this.engine != null, "No shared engine available");
|
||||
return this.engine;
|
||||
}
|
||||
}
|
||||
|
||||
protected ScriptEngine createEngineFromName() {
|
||||
if (this.scriptEngineManager == null) {
|
||||
this.scriptEngineManager = new ScriptEngineManager(obtainApplicationContext().getClassLoader());
|
||||
protected ScriptEngine createEngineFromName(String engineName) {
|
||||
ScriptEngineManager scriptEngineManager = this.scriptEngineManager;
|
||||
if (scriptEngineManager == null) {
|
||||
scriptEngineManager = new ScriptEngineManager(obtainApplicationContext().getClassLoader());
|
||||
this.scriptEngineManager = scriptEngineManager;
|
||||
}
|
||||
|
||||
ScriptEngine engine = StandardScriptUtils.retrieveEngineByName(this.scriptEngineManager, this.engineName);
|
||||
ScriptEngine engine = StandardScriptUtils.retrieveEngineByName(scriptEngineManager, engineName);
|
||||
loadScripts(engine);
|
||||
return engine;
|
||||
}
|
||||
@@ -255,10 +267,12 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
|
||||
|
||||
@Nullable
|
||||
protected Resource getResource(String location) {
|
||||
for (String path : this.resourceLoaderPaths) {
|
||||
Resource resource = this.resourceLoader.getResource(path + location);
|
||||
if (resource.exists()) {
|
||||
return resource;
|
||||
if (this.resourceLoaderPaths != null) {
|
||||
for (String path : this.resourceLoaderPaths) {
|
||||
Resource resource = obtainApplicationContext().getResource(path + location);
|
||||
if (resource.exists()) {
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -317,10 +331,10 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
|
||||
}
|
||||
else if (this.renderObject != null) {
|
||||
Object thiz = engine.eval(this.renderObject);
|
||||
html = ((Invocable)engine).invokeMethod(thiz, this.renderFunction, template, model, context);
|
||||
html = ((Invocable) engine).invokeMethod(thiz, this.renderFunction, template, model, context);
|
||||
}
|
||||
else {
|
||||
html = ((Invocable)engine).invokeFunction(this.renderFunction, template, model, context);
|
||||
html = ((Invocable) engine).invokeFunction(this.renderFunction, template, model, context);
|
||||
}
|
||||
|
||||
byte[] bytes = String.valueOf(html).getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
@@ -135,6 +135,7 @@ public final class CloseStatus {
|
||||
|
||||
private final int code;
|
||||
|
||||
@Nullable
|
||||
private final String reason;
|
||||
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ public class HandshakeInfo {
|
||||
|
||||
private final HttpHeaders headers;
|
||||
|
||||
@Nullable
|
||||
private final String protocol;
|
||||
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.http.server.reactive.AbstractListenerReadPublisher;
|
||||
import org.springframework.http.server.reactive.AbstractListenerWriteProcessor;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.socket.CloseStatus;
|
||||
import org.springframework.web.reactive.socket.HandshakeInfo;
|
||||
import org.springframework.web.reactive.socket.WebSocketMessage;
|
||||
@@ -58,10 +59,12 @@ public abstract class AbstractListenerWebSocketSession<T> extends AbstractWebSoc
|
||||
private static final int RECEIVE_BUFFER_SIZE = 8192;
|
||||
|
||||
|
||||
@Nullable
|
||||
private final MonoProcessor<Void> completionMono;
|
||||
|
||||
private final WebSocketReceivePublisher receivePublisher = new WebSocketReceivePublisher();
|
||||
|
||||
@Nullable
|
||||
private volatile WebSocketSendProcessor sendProcessor;
|
||||
|
||||
private final AtomicBoolean sendCalled = new AtomicBoolean();
|
||||
@@ -93,7 +96,9 @@ public abstract class AbstractListenerWebSocketSession<T> extends AbstractWebSoc
|
||||
|
||||
|
||||
protected WebSocketSendProcessor getSendProcessor() {
|
||||
return this.sendProcessor;
|
||||
WebSocketSendProcessor sendProcessor = this.sendProcessor;
|
||||
Assert.state(sendProcessor != null, "No WebSocketSendProcessor available");
|
||||
return sendProcessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -106,10 +111,11 @@ public abstract class AbstractListenerWebSocketSession<T> extends AbstractWebSoc
|
||||
@Override
|
||||
public Mono<Void> send(Publisher<WebSocketMessage> messages) {
|
||||
if (this.sendCalled.compareAndSet(false, true)) {
|
||||
this.sendProcessor = new WebSocketSendProcessor();
|
||||
WebSocketSendProcessor sendProcessor = new WebSocketSendProcessor();
|
||||
this.sendProcessor = sendProcessor;
|
||||
return Mono.from(subscriber -> {
|
||||
messages.subscribe(this.sendProcessor);
|
||||
this.sendProcessor.subscribe(subscriber);
|
||||
messages.subscribe(sendProcessor);
|
||||
sendProcessor.subscribe(subscriber);
|
||||
});
|
||||
}
|
||||
else {
|
||||
@@ -157,18 +163,20 @@ public abstract class AbstractListenerWebSocketSession<T> extends AbstractWebSoc
|
||||
/** Handle an error callback from the WebSocketHandler adapter */
|
||||
void handleError(Throwable ex) {
|
||||
this.receivePublisher.onError(ex);
|
||||
if (this.sendProcessor != null) {
|
||||
this.sendProcessor.cancel();
|
||||
this.sendProcessor.onError(ex);
|
||||
WebSocketSendProcessor sendProcessor = this.sendProcessor;
|
||||
if (sendProcessor != null) {
|
||||
sendProcessor.cancel();
|
||||
sendProcessor.onError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle a close callback from the WebSocketHandler adapter */
|
||||
void handleClose(CloseStatus reason) {
|
||||
this.receivePublisher.onAllDataRead();
|
||||
if (this.sendProcessor != null) {
|
||||
this.sendProcessor.cancel();
|
||||
this.sendProcessor.onComplete();
|
||||
WebSocketSendProcessor sendProcessor = this.sendProcessor;
|
||||
if (sendProcessor != null) {
|
||||
sendProcessor.cancel();
|
||||
sendProcessor.onComplete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,6 +213,7 @@ public abstract class AbstractListenerWebSocketSession<T> extends AbstractWebSoc
|
||||
|
||||
private final class WebSocketReceivePublisher extends AbstractListenerReadPublisher<WebSocketMessage> {
|
||||
|
||||
@Nullable
|
||||
private volatile WebSocketMessage webSocketMessage;
|
||||
|
||||
@Override
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.eclipse.jetty.websocket.api.extensions.Frame;
|
||||
import org.eclipse.jetty.websocket.common.OpCode;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.socket.CloseStatus;
|
||||
import org.springframework.web.reactive.socket.WebSocketHandler;
|
||||
@@ -56,6 +57,7 @@ public class JettyWebSocketHandlerAdapter {
|
||||
|
||||
private final Function<Session, JettyWebSocketSession> sessionFactory;
|
||||
|
||||
@Nullable
|
||||
private JettyWebSocketSession delegateSession;
|
||||
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ import org.springframework.web.reactive.socket.WebSocketSession;
|
||||
*/
|
||||
public class JettyWebSocketSession extends AbstractListenerWebSocketSession<Session> {
|
||||
|
||||
@Nullable
|
||||
private volatile SuspendToken suspendToken;
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import javax.websocket.PongMessage;
|
||||
import javax.websocket.Session;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.socket.CloseStatus;
|
||||
import org.springframework.web.reactive.socket.WebSocketHandler;
|
||||
@@ -47,6 +48,7 @@ public class StandardWebSocketHandlerAdapter extends Endpoint {
|
||||
|
||||
private Function<Session, StandardWebSocketSession> sessionFactory;
|
||||
|
||||
@Nullable
|
||||
private StandardWebSocketSession delegateSession;
|
||||
|
||||
|
||||
@@ -62,8 +64,8 @@ public class StandardWebSocketHandlerAdapter extends Endpoint {
|
||||
|
||||
@Override
|
||||
public void onOpen(Session session, EndpointConfig config) {
|
||||
|
||||
this.delegateSession = this.sessionFactory.apply(session);
|
||||
Assert.state(this.delegateSession != null, "No delegate session");
|
||||
|
||||
session.addMessageHandler(String.class, message -> {
|
||||
WebSocketMessage webSocketMessage = toMessage(message);
|
||||
|
||||
@@ -35,6 +35,7 @@ import reactor.core.publisher.MonoProcessor;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.socket.HandshakeInfo;
|
||||
import org.springframework.web.reactive.socket.WebSocketHandler;
|
||||
@@ -184,6 +185,7 @@ public class UndertowWebSocketClient extends WebSocketClientSupport implements W
|
||||
|
||||
private final HttpHeaders responseHeaders = new HttpHeaders();
|
||||
|
||||
@Nullable
|
||||
private final WebSocketClientNegotiation delegate;
|
||||
|
||||
public DefaultNegotiation(List<String> protocols, HttpHeaders requestHeaders,
|
||||
|
||||
@@ -54,8 +54,10 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Life
|
||||
new NamedThreadLocal<>("JettyWebSocketHandlerAdapter");
|
||||
|
||||
|
||||
@Nullable
|
||||
private WebSocketServerFactory factory;
|
||||
|
||||
@Nullable
|
||||
private volatile ServletContext servletContext;
|
||||
|
||||
private volatile boolean running = false;
|
||||
@@ -66,10 +68,11 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Life
|
||||
@Override
|
||||
public void start() {
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
if (!isRunning() && this.servletContext != null) {
|
||||
ServletContext servletContext = this.servletContext;
|
||||
if (!isRunning() && servletContext != null) {
|
||||
this.running = true;
|
||||
try {
|
||||
this.factory = new WebSocketServerFactory(this.servletContext);
|
||||
this.factory = new WebSocketServerFactory(servletContext);
|
||||
this.factory.setCreator((request, response) -> {
|
||||
WebSocketHandlerContainer container = adapterHolder.get();
|
||||
String protocol = container.getProtocol();
|
||||
@@ -92,11 +95,13 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Life
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
if (isRunning()) {
|
||||
this.running = false;
|
||||
try {
|
||||
this.factory.stop();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException("Failed to stop WebSocketServerFactory", ex);
|
||||
if (this.factory != null) {
|
||||
try {
|
||||
this.factory.stop();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException("Failed to stop WebSocketServerFactory", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,6 +130,7 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Life
|
||||
|
||||
startLazily(servletRequest);
|
||||
|
||||
Assert.state(this.factory != null, "No WebSocketServerFactory available");
|
||||
boolean isUpgrade = this.factory.isUpgradeRequest(servletRequest, servletResponse);
|
||||
Assert.isTrue(isUpgrade, "Not a WebSocket handshake");
|
||||
|
||||
@@ -175,6 +181,7 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Life
|
||||
|
||||
private final JettyWebSocketHandlerAdapter adapter;
|
||||
|
||||
@Nullable
|
||||
private final String protocol;
|
||||
|
||||
public WebSocketHandlerContainer(JettyWebSocketHandlerAdapter adapter, @Nullable String protocol) {
|
||||
|
||||
@@ -261,8 +261,6 @@ public class WebFluxConfigurationSupportTests {
|
||||
|
||||
assertEquals(Ordered.LOWEST_PRECEDENCE - 1, handlerMapping.getOrder());
|
||||
|
||||
assertNotNull(handlerMapping.getPatternRegistry());
|
||||
|
||||
SimpleUrlHandlerMapping urlHandlerMapping = (SimpleUrlHandlerMapping) handlerMapping;
|
||||
WebHandler webHandler = (WebHandler) urlHandlerMapping.getUrlMap().get("/images/**");
|
||||
assertNotNull(webHandler);
|
||||
|
||||
Reference in New Issue
Block a user