Commit ed3df32c authored by Phillip Webb's avatar Phillip Webb

Extract resource handling to TomcatResources

Extract the nasty Tomcat resource handling reflection code to its own
class.
parent 72a2e5bc
......@@ -20,9 +20,6 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
......@@ -40,12 +37,10 @@ import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Valve;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.Tomcat.FixContextListener;
import org.apache.coyote.AbstractProtocol;
import org.apache.naming.resources.FileDirContext;
import org.springframework.beans.BeanUtils;
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
......@@ -589,95 +584,7 @@ public class TomcatEmbeddedServletContainerFactory extends
if (servletContext.getAttribute(this.MERGED_WEB_XML) == null) {
servletContext.setAttribute(this.MERGED_WEB_XML, getEmptyWebXml());
}
addClasspathResources(context);
}
private void addClasspathResources(Context context) {
ClassLoader loader = getClass().getClassLoader();
if (loader instanceof URLClassLoader) {
for (URL url : ((URLClassLoader) loader).getURLs()) {
String file = url.getFile();
if (file.endsWith(".jar") || file.endsWith(".jar!/")) {
addJarContext(context, url);
}
else if (url.toString().startsWith("file:")) {
addDirContext(context, url);
}
}
}
}
private void addJarContext(Context context, URL url) {
String jar = url.toString();
if (!jar.startsWith("jar:")) {
// A jar file in the file system. Convert to Jar URL.
jar = "jar:" + jar + "!/";
}
if (ClassUtils.isPresent("org.apache.catalina.deploy.ErrorPage", null)) {
// Tomcat 7
try {
context.addResourceJarUrl(new URL(jar));
}
catch (MalformedURLException e) {
// Ignore?
}
}
else {
// Tomcat 8
addResourceSet(context, "RESOURCE_JAR", jar);
}
}
private void addDirContext(Context context, URL url) {
String dir = url.toString().substring("file:".length());
if (new File(dir).isDirectory()) {
if (ClassUtils.isPresent("org.apache.catalina.deploy.ErrorPage", null)) {
// Tomcat 7
if (context instanceof StandardContext) {
FileDirContext files = new FileDirContext();
files.setDocBase(dir);
((StandardContext) context).addResourcesDirContext(files);
}
}
else {
// Tomcat 8
addResourceSet(context, "RESOURCE_JAR", url.toString());
}
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void addResourceSet(Context context, String name, String dir) {
Object resources;
Method create;
Class type;
try {
Method getResources = ReflectionUtils.findMethod(context.getClass(),
"getResources");
resources = getResources.invoke(context);
type = ClassUtils.resolveClassName(
"org.apache.catalina.WebResourceRoot.ResourceSetType", null);
create = ReflectionUtils.findMethod(resources.getClass(),
"createWebResourceSet", type, String.class, URL.class,
String.class);
}
catch (Exception e) {
throw new IllegalStateException("Tomcat 8 reflection failed", e);
}
try {
if (dir.indexOf("!/") < dir.lastIndexOf("!/")) {
// It's a nested jar but we now don't want the suffix because Tomcat
// is going to try and locate it as a root URL (not the resource
// inside it)
dir = dir.substring(0, dir.length() - 2);
}
URL url = new URL(dir);
String path = "/META-INF/resources";
create.invoke(resources, Enum.valueOf(type, name), "/", url, path);
}
catch (Exception e) {
// Ignore (probably not a directory)
}
TomcatResources.get(context).addClasspathResources();
}
private String getEmptyWebXml() {
......
/*
* Copyright 2012-2014 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.context.embedded.tomcat;
import java.io.File;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import javax.servlet.ServletContext;
import org.apache.catalina.Context;
import org.apache.catalina.core.StandardContext;
import org.apache.naming.resources.FileDirContext;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* Abstraction to add resources that works with both Tomcat 8 and 7.
*
* @author Dave Syer
* @author Phillip Webb
*/
abstract class TomcatResources {
private final Context context;
public TomcatResources(Context context) {
this.context = context;
}
/**
* Add resources from the classpath
*/
public void addClasspathResources() {
ClassLoader loader = getClass().getClassLoader();
if (loader instanceof URLClassLoader) {
for (URL url : ((URLClassLoader) loader).getURLs()) {
String file = url.getFile();
if (file.endsWith(".jar") || file.endsWith(".jar!/")) {
String jar = url.toString();
if (!jar.startsWith("jar:")) {
// A jar file in the file system. Convert to Jar URL.
jar = "jar:" + jar + "!/";
}
addJar(jar);
}
else if (url.toString().startsWith("file:")) {
String dir = url.toString().substring("file:".length());
if (new File(dir).isDirectory()) {
addDir(dir, url);
}
}
}
}
}
protected final Context getContext() {
return this.context;
}
/**
* Called to add a JAR to the resources.
* @param jar the URL spec for the jar
*/
protected abstract void addJar(String jar);
/**
* Called to add a dir to the resource.
* @param dir the dir
* @param url the URL
*/
protected abstract void addDir(String dir, URL url);
/**
* Return a {@link TomcatResources} instance for the currently running Tomcat version.
* @param context the tomcat context
* @return a {@link TomcatResources} instance.
*/
public static TomcatResources get(Context context) {
if (ClassUtils.isPresent("org.apache.catalina.deploy.ErrorPage", null)) {
return new Tomcat7Resources(context);
}
return new Tomcat8Resources(context);
}
/**
* {@link TomcatResources} for Tomcat 7.
*/
private static class Tomcat7Resources extends TomcatResources {
public Tomcat7Resources(Context context) {
super(context);
}
@Override
protected void addJar(String jar) {
try {
getContext().addResourceJarUrl(new URL(jar));
}
catch (MalformedURLException ex) {
// Ignore?
}
}
@Override
protected void addDir(String dir, URL url) {
if (getContext() instanceof ServletContext) {
FileDirContext files = new FileDirContext();
files.setDocBase(dir);
((StandardContext) getContext()).addResourcesDirContext(files);
}
}
}
/**
* {@link TomcatResources} for Tomcat 8.
*/
static class Tomcat8Resources extends TomcatResources {
private Object resources;
private Method createWebResourceSetMethod;
private Enum<?> resourceJarEnum;
@SuppressWarnings({ "rawtypes", "unchecked" })
public Tomcat8Resources(Context context) {
super(context);
try {
this.resources = ReflectionUtils.findMethod(context.getClass(),
"getResources").invoke(context);
Class resourceSetType = ClassUtils.resolveClassName(
"org.apache.catalina.WebResourceRoot.ResourceSetType", null);
this.createWebResourceSetMethod = ReflectionUtils.findMethod(
this.resources.getClass(), "createWebResourceSet",
resourceSetType, String.class, URL.class, String.class);
this.resourceJarEnum = Enum.valueOf(resourceSetType, "RESOURCE_JAR");
}
catch (Exception ex) {
throw new IllegalStateException("Tomcat 8 reflection failed", ex);
}
}
@Override
protected void addJar(String jar) {
addResourceSet(jar);
}
@Override
protected void addDir(String dir, URL url) {
addResourceSet(url.toString());
}
private void addResourceSet(String resource) {
try {
if (isInsideNestedJar(resource)) {
// It's a nested jar but we now don't want the suffix because Tomcat
// is going to try and locate it as a root URL (not the resource
// inside it)
resource = resource.substring(0, resource.length() - 2);
}
URL url = new URL(resource);
String path = "/META-INF/resources";
createWebResourceSet("/", url, path);
}
catch (Exception ex) {
// Ignore (probably not a directory)
}
}
private boolean isInsideNestedJar(String dir) {
return dir.indexOf("!/") < dir.lastIndexOf("!/");
}
private void createWebResourceSet(String webAppMount, URL url, String path)
throws Exception {
this.createWebResourceSetMethod.invoke(this.resources, this.resourceJarEnum,
webAppMount, url, path);
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment