001/*
002 * Copyright 2002-2018 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.config;
018
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.LinkedHashMap;
022import java.util.List;
023import java.util.Map;
024
025import org.springframework.beans.factory.BeanInitializationException;
026import org.springframework.core.Ordered;
027import org.springframework.core.io.ResourceLoader;
028import org.springframework.lang.Nullable;
029import org.springframework.web.reactive.handler.AbstractUrlHandlerMapping;
030import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
031import org.springframework.web.reactive.resource.ResourceTransformerSupport;
032import org.springframework.web.reactive.resource.ResourceUrlProvider;
033import org.springframework.web.reactive.resource.ResourceWebHandler;
034import org.springframework.web.server.WebHandler;
035
036/**
037 * Stores registrations of resource handlers for serving static resources such
038 * as images, css files and others through Spring WebFlux including setting cache
039 * headers optimized for efficient loading in a web browser. Resources can be
040 * served out of locations under web application root, from the classpath, and
041 * others.
042 *
043 * <p>To create a resource handler, use {@link #addResourceHandler(String...)}
044 * providing the URL path patterns for which the handler should be invoked to
045 * serve static resources (e.g. {@code "/resources/**"}).
046 *
047 * <p>Then use additional methods on the returned
048 * {@link ResourceHandlerRegistration} to add one or more locations from which
049 * to serve static content from (e.g. {{@code "/"},
050 * {@code "classpath:/META-INF/public-web-resources/"}}) or to specify a cache
051 * period for served resources.
052 *
053 * @author Rossen Stoyanchev
054 * @author Brian Clozel
055 * @since 5.0
056 */
057public class ResourceHandlerRegistry {
058
059        private final ResourceLoader resourceLoader;
060
061        private final List<ResourceHandlerRegistration> registrations = new ArrayList<>();
062
063        private int order = Ordered.LOWEST_PRECEDENCE - 1;
064
065        @Nullable
066        private ResourceUrlProvider resourceUrlProvider;
067
068
069        /**
070         * Create a new resource handler registry for the given resource loader
071         * (typically an application context).
072         * @param resourceLoader the resource loader to use
073         */
074        public ResourceHandlerRegistry(ResourceLoader resourceLoader) {
075                this.resourceLoader = resourceLoader;
076        }
077
078        /**
079         * Configure the {@link ResourceUrlProvider} that can be used by
080         * {@link org.springframework.web.reactive.resource.ResourceTransformer} instances.
081         * @param resourceUrlProvider the resource URL provider to use
082         * @since 5.0.11
083         */
084        public void setResourceUrlProvider(@Nullable ResourceUrlProvider resourceUrlProvider) {
085                this.resourceUrlProvider = resourceUrlProvider;
086        }
087
088
089
090        /**
091         * Add a resource handler for serving static resources based on the specified
092         * URL path patterns. The handler will be invoked for every incoming request
093         * that matches to one of the specified path patterns.
094         * <p>Patterns like {@code "/static/**"} or {@code "/css/{filename:\\w+\\.css}"}
095         * are allowed. See {@link org.springframework.web.util.pattern.PathPattern}
096         * for more details on the syntax.
097         * @return a {@link ResourceHandlerRegistration} to use to further configure
098         * the registered resource handler
099         */
100        public ResourceHandlerRegistration addResourceHandler(String... patterns) {
101                ResourceHandlerRegistration registration = new ResourceHandlerRegistration(this.resourceLoader, patterns);
102                this.registrations.add(registration);
103                return registration;
104        }
105
106        /**
107         * Whether a resource handler has already been registered for the given path pattern.
108         */
109        public boolean hasMappingForPattern(String pathPattern) {
110                for (ResourceHandlerRegistration registration : this.registrations) {
111                        if (Arrays.asList(registration.getPathPatterns()).contains(pathPattern)) {
112                                return true;
113                        }
114                }
115                return false;
116        }
117
118        /**
119         * Specify the order to use for resource handling relative to other
120         * {@code HandlerMapping}s configured in the Spring configuration.
121         * <p>The default value used is {@code Integer.MAX_VALUE-1}.
122         */
123        public ResourceHandlerRegistry setOrder(int order) {
124                this.order = order;
125                return this;
126        }
127
128        /**
129         * Return a handler mapping with the mapped resource handlers; or {@code null} in case
130         * of no registrations.
131         */
132        @Nullable
133        protected AbstractUrlHandlerMapping getHandlerMapping() {
134                if (this.registrations.isEmpty()) {
135                        return null;
136                }
137                Map<String, WebHandler> urlMap = new LinkedHashMap<>();
138                for (ResourceHandlerRegistration registration : this.registrations) {
139                        for (String pathPattern : registration.getPathPatterns()) {
140                                ResourceWebHandler handler = registration.getRequestHandler();
141                                handler.getResourceTransformers().forEach(transformer -> {
142                                        if (transformer instanceof ResourceTransformerSupport) {
143                                                ((ResourceTransformerSupport) transformer).setResourceUrlProvider(this.resourceUrlProvider);
144                                        }
145                                });
146                                try {
147                                        handler.afterPropertiesSet();
148                                }
149                                catch (Throwable ex) {
150                                        throw new BeanInitializationException("Failed to init ResourceHttpRequestHandler", ex);
151                                }
152                                urlMap.put(pathPattern, handler);
153                        }
154                }
155                return new SimpleUrlHandlerMapping(urlMap, this.order);
156        }
157
158}