001/*
002 * Copyright 2012-2017 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 *      http://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.boot.test.mock.web;
018
019import java.io.File;
020import java.io.IOException;
021import java.net.MalformedURLException;
022import java.net.URL;
023
024import org.springframework.core.io.FileSystemResourceLoader;
025import org.springframework.core.io.Resource;
026import org.springframework.core.io.ResourceLoader;
027import org.springframework.mock.web.MockServletContext;
028
029/**
030 * {@link MockServletContext} implementation for Spring Boot. Respects well-known Spring
031 * Boot resource locations and uses an empty directory for "/" if no locations can be
032 * found.
033 *
034 * @author Phillip Webb
035 * @since 1.4.0
036 */
037public class SpringBootMockServletContext extends MockServletContext {
038
039        private static final String[] SPRING_BOOT_RESOURCE_LOCATIONS = new String[] {
040                        "classpath:META-INF/resources", "classpath:resources", "classpath:static",
041                        "classpath:public" };
042
043        private final ResourceLoader resourceLoader;
044
045        private File emptyRootFolder;
046
047        public SpringBootMockServletContext(String resourceBasePath) {
048                this(resourceBasePath, new FileSystemResourceLoader());
049        }
050
051        public SpringBootMockServletContext(String resourceBasePath,
052                        ResourceLoader resourceLoader) {
053                super(resourceBasePath, resourceLoader);
054                this.resourceLoader = resourceLoader;
055        }
056
057        @Override
058        protected String getResourceLocation(String path) {
059                if (!path.startsWith("/")) {
060                        path = "/" + path;
061                }
062                String resourceLocation = getResourceBasePathLocation(path);
063                if (exists(resourceLocation)) {
064                        return resourceLocation;
065                }
066                for (String prefix : SPRING_BOOT_RESOURCE_LOCATIONS) {
067                        resourceLocation = prefix + path;
068                        if (exists(resourceLocation)) {
069                                return resourceLocation;
070                        }
071                }
072                return super.getResourceLocation(path);
073        }
074
075        protected final String getResourceBasePathLocation(String path) {
076                return super.getResourceLocation(path);
077        }
078
079        private boolean exists(String resourceLocation) {
080                try {
081                        Resource resource = this.resourceLoader.getResource(resourceLocation);
082                        return resource.exists();
083                }
084                catch (Exception ex) {
085                        return false;
086                }
087        }
088
089        @Override
090        public URL getResource(String path) throws MalformedURLException {
091                URL resource = super.getResource(path);
092                if (resource == null && "/".equals(path)) {
093                        // Liquibase assumes that "/" always exists, if we don't have a directory
094                        // use a temporary location.
095                        try {
096                                if (this.emptyRootFolder == null) {
097                                        synchronized (this) {
098                                                File tempFolder = File.createTempFile("spr", "servlet");
099                                                tempFolder.delete();
100                                                tempFolder.mkdirs();
101                                                tempFolder.deleteOnExit();
102                                                this.emptyRootFolder = tempFolder;
103                                        }
104                                }
105                                return this.emptyRootFolder.toURI().toURL();
106                        }
107                        catch (IOException ex) {
108                                // Ignore
109                        }
110                }
111                return resource;
112        }
113
114}