Respect Boot locations in MockServletContext

Update SpringApplicationContextLoader to use a MockServletContext
subclass which also respects common Spring Boot resource locations. This
allows resources in `/META-INF/resources`, `/resources`, `/public` and
`/static` to be loaded in the same way as those in `/src/main/webapp`.

SpringBootMockServletContext also returns an empty temporary folder for
'/' when no resource locations can be found. This is primarily to
prevent superfluous warnings from Liquibase.

Fixes gh-2654
pull/2615/merge
Phillip Webb 10 years ago
parent 7d3465f858
commit 0a33d0464d

@ -260,7 +260,7 @@ public class SpringApplicationContextLoader extends AbstractContextLoader {
private void addMockServletContext(
List<ApplicationContextInitializer<?>> initializers,
WebMergedContextConfiguration webConfiguration) {
MockServletContext servletContext = new MockServletContext(
SpringBootMockServletContext servletContext = new SpringBootMockServletContext(
webConfiguration.getResourceBasePath());
initializers.add(0, new ServletContextApplicationContextInitializer(
servletContext));

@ -0,0 +1,110 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import org.springframework.core.io.FileSystemResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.mock.web.MockServletContext;
/**
* {@link MockServletContext} implementation for Spring Boot. Respects well know Spring
* Boot resource locations and uses an empty folder for "/" if no locations can be found.
*
* @author Phillip Webb
*/
public class SpringBootMockServletContext extends MockServletContext {
private static final String[] SPRING_BOOT_RESOURCE_LOCATIONS = new String[] {
"classpath:META-INF/resources", "classpath:resources", "classpath:static",
"classpath:public" };
private final ResourceLoader resourceLoader;
private File emptyRootFolder;
public SpringBootMockServletContext(String resourceBasePath) {
this(resourceBasePath, new FileSystemResourceLoader());
}
public SpringBootMockServletContext(String resourceBasePath,
ResourceLoader resourceLoader) {
super(resourceBasePath, resourceLoader);
this.resourceLoader = resourceLoader;
}
@Override
protected String getResourceLocation(String path) {
if (!path.startsWith("/")) {
path = "/" + path;
}
String resourceLocation = getResourceBasePathLocation(path);
if (exists(resourceLocation)) {
return resourceLocation;
}
for (String prefix : SPRING_BOOT_RESOURCE_LOCATIONS) {
resourceLocation = prefix + path;
if (exists(resourceLocation)) {
return resourceLocation;
}
}
return super.getResourceLocation(path);
}
protected final String getResourceBasePathLocation(String path) {
return super.getResourceLocation(path);
}
private boolean exists(String resourceLocation) {
try {
Resource resource = this.resourceLoader.getResource(resourceLocation);
return resource.exists();
}
catch (Exception ex) {
return false;
}
}
@Override
public URL getResource(String path) throws MalformedURLException {
URL resource = super.getResource(path);
if (resource == null && "/".equals(path)) {
// Liquibase assumes that "/" always exists, if we don't have a folder
// use a temporary location.
try {
if (this.emptyRootFolder == null) {
synchronized (this) {
File tempFolder = File.createTempFile("spr", "servlet");
tempFolder.delete();
tempFolder.mkdirs();
tempFolder.deleteOnExit();
this.emptyRootFolder = tempFolder;
}
}
return this.emptyRootFolder.toURI().toURL();
}
catch (IOException ex) {
}
}
return resource;
}
}

@ -0,0 +1,102 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test;
import java.io.File;
import java.io.FilenameFilter;
import java.net.MalformedURLException;
import java.net.URL;
import javax.servlet.ServletContext;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.context.ServletContextAware;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link SpringBootMockServletContext}.
*
* @author Phillip Webb
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootMockServletContextTests.Config.class)
@WebAppConfiguration("src/test/webapp")
public class SpringBootMockServletContextTests implements ServletContextAware {
private ServletContext servletContext;
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
@Test
public void getResourceLocation() throws Exception {
testResource("/inwebapp", "src/test/webapp");
testResource("/inmetainfresources", "/META-INF/resources");
testResource("/inresources", "/resources");
testResource("/instatic", "/static");
testResource("/inpublic", "/public");
}
private void testResource(String path, String expectedLocation)
throws MalformedURLException {
URL resource = this.servletContext.getResource(path);
assertThat("Path " + path + " not found", resource, not(nullValue()));
assertThat("Path " + path + " not in " + expectedLocation, resource.getPath(),
containsString(expectedLocation));
}
// gh-2654
@Test
public void getRootUrlExistsAndIsEmpty() throws Exception {
SpringBootMockServletContext context = new SpringBootMockServletContext(
"src/test/doesntexist") {
@Override
protected String getResourceLocation(String path) {
// Don't include the Spring Boot defaults for this test
return getResourceBasePathLocation(path);
};
};
URL resource = context.getResource("/");
assertThat(resource, not(nullValue()));
File file = new File(resource.getPath());
String[] contents = file.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return !(".".equals(name) || "..".equals(name));
}
});
assertThat(contents, not(nullValue()));
assertThat(contents.length, equalTo(0));
}
@Configuration
static class Config {
}
}
Loading…
Cancel
Save