Commit b6bacd5e authored by Phillip Webb's avatar Phillip Webb

Upgrade to Servlet 3.1, Tomcat 8 and Jetty 9

Upgrade to latest versions of Tomcat and Jetty and to the latest Servlet
API whilst will remaining compatible with Tomcat 7 and Jetty 8.

Fixes gh-1832, gh-369
parent 004904af
...@@ -29,6 +29,7 @@ import javax.servlet.http.HttpServletResponse; ...@@ -29,6 +29,7 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult; import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError; import org.springframework.validation.ObjectError;
import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestAttributes;
...@@ -121,10 +122,10 @@ public class DefaultErrorAttributes implements ErrorAttributes, HandlerException ...@@ -121,10 +122,10 @@ public class DefaultErrorAttributes implements ErrorAttributes, HandlerException
} }
} }
Object message = getAttribute(requestAttributes, "javax.servlet.error.message"); Object message = getAttribute(requestAttributes, "javax.servlet.error.message");
if ((message != null || errorAttributes.get("message") == null) if ((!StringUtils.isEmpty(message) || errorAttributes.get("message") == null)
&& !(error instanceof BindingResult)) { && !(error instanceof BindingResult)) {
errorAttributes.put("message", message == null ? "No message available" errorAttributes.put("message",
: message); StringUtils.isEmpty(message) ? "No message available" : message);
} }
} }
......
...@@ -73,10 +73,10 @@ public class BasicErrorControllerIntegrationTests { ...@@ -73,10 +73,10 @@ public class BasicErrorControllerIntegrationTests {
public void testErrorForMachineClient() throws Exception { public void testErrorForMachineClient() throws Exception {
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity( ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(
"http://localhost:" + this.port, Map.class); "http://localhost:" + this.port, Map.class);
assertThat(entity.getBody().toString(), endsWith("status=500, " String body = entity.getBody().toString();
+ "error=Internal Server Error, " assertThat(body, endsWith("status=500, " + "error=Internal Server Error, "
+ "exception=java.lang.IllegalStateException, " + "exception=java.lang.IllegalStateException, " + "message=Expected!, "
+ "message=Server Error, " + "path=/}")); + "path=/}"));
} }
@Test @Test
......
...@@ -128,7 +128,8 @@ public class BasicErrorControllerMockMvcTests { ...@@ -128,7 +128,8 @@ public class BasicErrorControllerMockMvcTests {
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Documented @Documented
@Import({ EmbeddedServletContainerAutoConfiguration.class, @Import({ EmbeddedServletContainerAutoConfiguration.EmbeddedTomcat.class,
EmbeddedServletContainerAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class, ServerPropertiesAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class, DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
......
...@@ -20,7 +20,9 @@ import java.io.BufferedReader; ...@@ -20,7 +20,9 @@ import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.net.URI; import java.net.URI;
import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
...@@ -94,6 +96,7 @@ public class CliTester implements TestRule { ...@@ -94,6 +96,7 @@ public class CliTester implements TestRule {
private <T extends OptionParsingCommand> Future<T> submitCommand(final T command, private <T extends OptionParsingCommand> Future<T> submitCommand(final T command,
String... args) { String... args) {
clearUrlHandler();
final String[] sources = getSources(args); final String[] sources = getSources(args);
return Executors.newSingleThreadExecutor().submit(new Callable<T>() { return Executors.newSingleThreadExecutor().submit(new Callable<T>() {
@Override @Override
...@@ -112,6 +115,21 @@ public class CliTester implements TestRule { ...@@ -112,6 +115,21 @@ public class CliTester implements TestRule {
}); });
} }
/**
* The TomcatURLStreamHandlerFactory fails if the factory is already set, use
* reflection to reset it.
*/
private void clearUrlHandler() {
try {
Field field = URL.class.getDeclaredField("factory");
field.setAccessible(true);
field.set(null, null);
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
protected String[] getSources(String... args) { protected String[] getSources(String... args) {
final String[] sources = new String[args.length]; final String[] sources = new String[args.length];
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
......
...@@ -82,7 +82,7 @@ ...@@ -82,7 +82,7 @@
<javax-cache.version>1.0.0</javax-cache.version> <javax-cache.version>1.0.0</javax-cache.version>
<javax-mail.version>1.5.2</javax-mail.version> <javax-mail.version>1.5.2</javax-mail.version>
<jedis.version>2.4.2</jedis.version> <jedis.version>2.4.2</jedis.version>
<jetty.version>8.1.15.v20140411</jetty.version> <jetty.version>9.2.4.v20141103</jetty.version>
<jetty-jsp.version>2.2.0.v201112011158</jetty-jsp.version> <jetty-jsp.version>2.2.0.v201112011158</jetty-jsp.version>
<jaxen.version>1.1.6</jaxen.version> <jaxen.version>1.1.6</jaxen.version>
<jdom2.version>2.0.5</jdom2.version> <jdom2.version>2.0.5</jdom2.version>
...@@ -101,7 +101,7 @@ ...@@ -101,7 +101,7 @@
<mysql.version>5.1.32</mysql.version> <mysql.version>5.1.32</mysql.version>
<reactor.version>1.1.5.RELEASE</reactor.version> <reactor.version>1.1.5.RELEASE</reactor.version>
<reactor-spring.version>1.1.3.RELEASE</reactor-spring.version> <reactor-spring.version>1.1.3.RELEASE</reactor-spring.version>
<servlet-api.version>3.0.1</servlet-api.version> <servlet-api.version>3.1.0</servlet-api.version>
<slf4j.version>1.7.7</slf4j.version> <slf4j.version>1.7.7</slf4j.version>
<snakeyaml.version>1.13</snakeyaml.version> <snakeyaml.version>1.13</snakeyaml.version>
<solr.version>4.7.2</solr.version> <solr.version>4.7.2</solr.version>
...@@ -127,7 +127,7 @@ ...@@ -127,7 +127,7 @@
<thymeleaf-extras-springsecurity3.version>2.1.1.RELEASE</thymeleaf-extras-springsecurity3.version> <thymeleaf-extras-springsecurity3.version>2.1.1.RELEASE</thymeleaf-extras-springsecurity3.version>
<thymeleaf-layout-dialect.version>1.2.5</thymeleaf-layout-dialect.version> <thymeleaf-layout-dialect.version>1.2.5</thymeleaf-layout-dialect.version>
<thymeleaf-extras-data-attribute.version>1.3</thymeleaf-extras-data-attribute.version> <thymeleaf-extras-data-attribute.version>1.3</thymeleaf-extras-data-attribute.version>
<tomcat.version>7.0.56</tomcat.version> <tomcat.version>8.0.14</tomcat.version>
<velocity.version>1.7</velocity.version> <velocity.version>1.7</velocity.version>
<velocity-tools.version>2.0</velocity-tools.version> <velocity-tools.version>2.0</velocity-tools.version>
<wsdl4j.version>1.6.3</wsdl4j.version> <wsdl4j.version>1.6.3</wsdl4j.version>
......
...@@ -52,12 +52,11 @@ public class WarPackagingTests { ...@@ -52,12 +52,11 @@ public class WarPackagingTests {
private static final Set<String> JETTY_EXPECTED_IN_WEB_INF_LIB_PROVIDED = new HashSet<String>( private static final Set<String> JETTY_EXPECTED_IN_WEB_INF_LIB_PROVIDED = new HashSet<String>(
Arrays.asList("spring-boot-starter-jetty-", "jetty-util-", "jetty-xml-", Arrays.asList("spring-boot-starter-jetty-", "jetty-util-", "jetty-xml-",
"javax.servlet-", "jetty-continuation-", "jetty-io-", "jetty-http-", "jetty-schemas-", "javax.servlet-", "jetty-io-", "jetty-http-",
"jetty-server-", "jetty-security-", "jetty-servlet-", "jetty-server-", "jetty-security-", "jetty-servlet-",
"jetty-webapp-", "javax.servlet.jsp-", "jetty-webapp-", "javax.servlet.jsp-2", "javax.servlet.jsp-api-",
"org.apache.jasper.glassfish-", "javax.servlet.jsp.jstl-", "javax.servlet.jsp.jstl-1.2.2", "javax.servlet.jsp.jstl-1.2.0",
"org.apache.taglibs.standard.glassfish-", "javax.el-", "com.sun.el-", "javax.el-", "org.eclipse.jdt.core-", "jetty-jsp-"));
"org.eclipse.jdt.core-", "jetty-jsp-"));
private static final String BOOT_VERSION = ManagedDependencies.get() private static final String BOOT_VERSION = ManagedDependencies.get()
.find("spring-boot").getVersion(); .find("spring-boot").getVersion();
......
...@@ -25,11 +25,14 @@ import java.util.Arrays; ...@@ -25,11 +25,14 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.ErrorHandler; import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.server.ssl.SslSocketConnector;
import org.eclipse.jetty.servlet.ErrorPageErrorHandler; import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlet.ServletMapping; import org.eclipse.jetty.servlet.ServletMapping;
...@@ -117,10 +120,10 @@ public class JettyEmbeddedServletContainerFactory extends ...@@ -117,10 +120,10 @@ public class JettyEmbeddedServletContainerFactory extends
if (getSsl() != null) { if (getSsl() != null) {
SslContextFactory sslContextFactory = new SslContextFactory(); SslContextFactory sslContextFactory = new SslContextFactory();
configureSsl(sslContextFactory, getSsl()); configureSsl(sslContextFactory, getSsl());
ServerConnector connector = getSslServerConnectorFactory().getConnector(
SslSocketConnector sslConnector = new SslSocketConnector(sslContextFactory); server, sslContextFactory);
sslConnector.setPort(port); connector.setPort(port);
server.setConnectors(new Connector[] { sslConnector }); server.setConnectors(new Connector[] { connector });
} }
for (JettyServerCustomizer customizer : getServerCustomizers()) { for (JettyServerCustomizer customizer : getServerCustomizers()) {
...@@ -130,6 +133,13 @@ public class JettyEmbeddedServletContainerFactory extends ...@@ -130,6 +133,13 @@ public class JettyEmbeddedServletContainerFactory extends
return getJettyEmbeddedServletContainer(server); return getJettyEmbeddedServletContainer(server);
} }
private SslServerConnectorFactory getSslServerConnectorFactory() {
if (ClassUtils.isPresent("org.eclipse.jetty.server.ssl.SslSocketConnector", null)) {
return new Jetty8SslServerConnectorFactory();
}
return new Jetty9SslServerConnectorFactory();
}
/** /**
* Configure the SSL connection. * Configure the SSL connection.
* @param factory the Jetty {@link SslContextFactory}. * @param factory the Jetty {@link SslContextFactory}.
...@@ -246,7 +256,7 @@ public class JettyEmbeddedServletContainerFactory extends ...@@ -246,7 +256,7 @@ public class JettyEmbeddedServletContainerFactory extends
handler.setBaseResource(resource); handler.setBaseResource(resource);
} }
else { else {
handler.setBaseResource(Resource.newResource(root)); handler.setBaseResource(Resource.newResource(root.getCanonicalFile()));
} }
} }
catch (Exception ex) { catch (Exception ex) {
...@@ -458,4 +468,50 @@ public class JettyEmbeddedServletContainerFactory extends ...@@ -458,4 +468,50 @@ public class JettyEmbeddedServletContainerFactory extends
} }
} }
/**
* Factory to create the SSL {@link ServerConnector}.
*/
private static interface SslServerConnectorFactory {
ServerConnector getConnector(Server server, SslContextFactory sslContextFactory);
}
/**
* {@link SslServerConnectorFactory} for Jetty 9.
*/
private static class Jetty9SslServerConnectorFactory implements
SslServerConnectorFactory {
@Override
public ServerConnector getConnector(Server server,
SslContextFactory sslContextFactory) {
return new ServerConnector(server, new SslConnectionFactory(
sslContextFactory, HttpVersion.HTTP_1_1.asString()),
new HttpConnectionFactory());
}
}
/**
* {@link SslServerConnectorFactory} for Jetty 8.
*/
private static class Jetty8SslServerConnectorFactory implements
SslServerConnectorFactory {
@Override
public ServerConnector getConnector(Server server,
SslContextFactory sslContextFactory) {
try {
Class<?> connectorClass = Class
.forName("org.eclipse.jetty.server.ssl.SslSocketConnector");
return (ServerConnector) connectorClass.getConstructor(
SslContextFactory.class).newInstance(sslContextFactory);
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
}
} }
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
package org.springframework.boot.context.embedded.tomcat; package org.springframework.boot.context.embedded.tomcat;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
...@@ -27,6 +26,7 @@ import javax.servlet.ServletContext; ...@@ -27,6 +26,7 @@ import javax.servlet.ServletContext;
import org.apache.tomcat.JarScanner; import org.apache.tomcat.JarScanner;
import org.apache.tomcat.JarScannerCallback; import org.apache.tomcat.JarScannerCallback;
import org.apache.tomcat.util.scan.StandardJarScanFilter;
import org.apache.tomcat.util.scan.StandardJarScanner; import org.apache.tomcat.util.scan.StandardJarScanner;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
...@@ -45,8 +45,6 @@ class SkipPatternJarScanner extends StandardJarScanner { ...@@ -45,8 +45,6 @@ class SkipPatternJarScanner extends StandardJarScanner {
private static final String JAR_SCAN_FILTER_CLASS = "org.apache.tomcat.JarScanFilter"; private static final String JAR_SCAN_FILTER_CLASS = "org.apache.tomcat.JarScanFilter";
private static final String STANDARD_JAR_SCAN_FILTER_CLASS = "org.apache.tomcat.util.scan.StandardJarScanFilter";
private final JarScanner jarScanner; private final JarScanner jarScanner;
private final SkipPattern pattern; private final SkipPattern pattern;
...@@ -60,34 +58,24 @@ class SkipPatternJarScanner extends StandardJarScanner { ...@@ -60,34 +58,24 @@ class SkipPatternJarScanner extends StandardJarScanner {
private void setPatternToTomcat8SkipFilter(SkipPattern pattern) { private void setPatternToTomcat8SkipFilter(SkipPattern pattern) {
if (ClassUtils.isPresent(JAR_SCAN_FILTER_CLASS, null)) { if (ClassUtils.isPresent(JAR_SCAN_FILTER_CLASS, null)) {
try { new Tomcat8TldSkipSetter(this).setSkipPattern(pattern);
Class<?> filterClass = Class.forName(JAR_SCAN_FILTER_CLASS);
Method setJarScanner = ReflectionUtils.findMethod(
StandardJarScanner.class, "setJarScanFilter", filterClass);
setJarScanner.invoke(this, createStandardJarScanFilter(pattern));
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
} }
} }
private Object createStandardJarScanFilter(SkipPattern pattern) // For Tomcat 7 compatibility
throws ClassNotFoundException, InstantiationException,
IllegalAccessException, InvocationTargetException {
Class<?> filterClass = Class.forName(STANDARD_JAR_SCAN_FILTER_CLASS);
Method setTldSkipMethod = ReflectionUtils.findMethod(filterClass, "setTldSkip",
String.class);
Object scanner = filterClass.newInstance();
setTldSkipMethod.invoke(scanner, pattern.asCommaDelimitedString());
return scanner;
}
@Override
public void scan(ServletContext context, ClassLoader classloader, public void scan(ServletContext context, ClassLoader classloader,
JarScannerCallback callback, Set<String> jarsToSkip) { JarScannerCallback callback, Set<String> jarsToSkip) {
this.jarScanner.scan(context, classloader, callback, Method scanMethod = ReflectionUtils.findMethod(this.jarScanner.getClass(),
(jarsToSkip == null ? this.pattern.asSet() : jarsToSkip)); "scan", ServletContext.class, ClassLoader.class,
JarScannerCallback.class, Set.class);
Assert.notNull(scanMethod, "Unable to find scan method");
try {
scanMethod.invoke(this.jarScanner, context, classloader, callback,
(jarsToSkip == null ? this.pattern.asSet() : jarsToSkip));
}
catch (Exception ex) {
throw new IllegalStateException("Tomcat 7 reflection failed", ex);
}
} }
/** /**
...@@ -101,6 +89,28 @@ class SkipPatternJarScanner extends StandardJarScanner { ...@@ -101,6 +89,28 @@ class SkipPatternJarScanner extends StandardJarScanner {
context.setJarScanner(scanner); context.setJarScanner(scanner);
} }
/**
* Tomcat 8 specific logic to setup the scanner.
*/
private static class Tomcat8TldSkipSetter {
private final StandardJarScanner jarScanner;
public Tomcat8TldSkipSetter(StandardJarScanner jarScanner) {
this.jarScanner = jarScanner;
}
public void setSkipPattern(SkipPattern pattern) {
StandardJarScanFilter filter = new StandardJarScanFilter();
filter.setTldSkip(pattern.asCommaDelimitedString());
this.jarScanner.setJarScanFilter(filter);
}
}
/**
* Skip patterns used by Spring Boot
*/
private static class SkipPattern { private static class SkipPattern {
private Set<String> patterns = new LinkedHashSet<String>(); private Set<String> patterns = new LinkedHashSet<String>();
......
...@@ -16,8 +16,6 @@ ...@@ -16,8 +16,6 @@
package org.springframework.boot.context.embedded.tomcat; package org.springframework.boot.context.embedded.tomcat;
import java.lang.reflect.Method;
import org.apache.catalina.Container; import org.apache.catalina.Container;
import org.apache.catalina.core.StandardContext; import org.apache.catalina.core.StandardContext;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
...@@ -33,9 +31,19 @@ class TomcatEmbeddedContext extends StandardContext { ...@@ -33,9 +31,19 @@ class TomcatEmbeddedContext extends StandardContext {
private ServletContextInitializerLifecycleListener starter; private ServletContextInitializerLifecycleListener starter;
private final boolean overrideLoadOnStart;
public TomcatEmbeddedContext() {
this.overrideLoadOnStart = ReflectionUtils.findMethod(StandardContext.class,
"loadOnStartup", Container[].class).getReturnType() == boolean.class;
}
@Override @Override
public boolean loadOnStartup(Container[] children) { public boolean loadOnStartup(Container[] children) {
return true; if (this.overrideLoadOnStart) {
return true;
}
return super.loadOnStartup(children);
} }
public void deferredLoadOnStartup() { public void deferredLoadOnStartup() {
...@@ -49,12 +57,13 @@ class TomcatEmbeddedContext extends StandardContext { ...@@ -49,12 +57,13 @@ class TomcatEmbeddedContext extends StandardContext {
if (classLoader != null) { if (classLoader != null) {
existingLoader = ClassUtils.overrideThreadContextClassLoader(classLoader); existingLoader = ClassUtils.overrideThreadContextClassLoader(classLoader);
} }
if (ClassUtils.isPresent("org.apache.catalina.deploy.ErrorPage", null)) {
if (this.overrideLoadOnStart) {
// Earlier versions of Tomcat used a version that returned void. If that
// version is used our overridden loadOnStart method won't have been called
// and the original will have already run.
super.loadOnStartup(findChildren()); super.loadOnStartup(findChildren());
} }
else {
callSuper(this, "loadOnStartup", findChildren(), Container[].class);
}
if (existingLoader != null) { if (existingLoader != null) {
ClassUtils.overrideThreadContextClassLoader(existingLoader); ClassUtils.overrideThreadContextClassLoader(existingLoader);
} }
...@@ -68,10 +77,4 @@ class TomcatEmbeddedContext extends StandardContext { ...@@ -68,10 +77,4 @@ class TomcatEmbeddedContext extends StandardContext {
return this.starter; return this.starter;
} }
private void callSuper(Object target, String name, Object value, Class<?> type) {
Method method = ReflectionUtils.findMethod(target.getClass().getSuperclass(),
name, type);
ReflectionUtils.invokeMethod(method, target, value);
}
} }
...@@ -604,15 +604,14 @@ public class TomcatEmbeddedServletContainerFactory extends ...@@ -604,15 +604,14 @@ public class TomcatEmbeddedServletContainerFactory extends
private Object createNativePage(ErrorPage errorPage) { private Object createNativePage(ErrorPage errorPage) {
Object nativePage = null; Object nativePage = null;
try { try {
if (ClassUtils.isPresent("org.apache.catalina.deploy.ErrorPage", null)) { if (ClassUtils.isPresent(
nativePage = new org.apache.catalina.deploy.ErrorPage(); "org.apache.tomcat.util.descriptor.web.ErrorPage", null)) {
nativePage = new org.apache.tomcat.util.descriptor.web.ErrorPage();
} }
else { else if (ClassUtils.isPresent("org.apache.catalina.deploy.ErrorPage",
if (ClassUtils.isPresent( null)) {
"org.apache.tomcat.util.descriptor.web.ErrorPage", null)) { nativePage = BeanUtils.instantiate(ClassUtils.forName(
nativePage = BeanUtils.instantiate(ClassUtils.forName( "org.apache.catalina.deploy.ErrorPage", null));
"org.apache.tomcat.util.descriptor.web.ErrorPage", null));
}
} }
} }
catch (ClassNotFoundException ex) { catch (ClassNotFoundException ex) {
...@@ -627,8 +626,9 @@ public class TomcatEmbeddedServletContainerFactory extends ...@@ -627,8 +626,9 @@ public class TomcatEmbeddedServletContainerFactory extends
public void addToContext(Context context) { public void addToContext(Context context) {
Assert.state(this.nativePage != null, Assert.state(this.nativePage != null,
"Neither Tomcat 7 nor 8 detected so no native error page exists"); "Neither Tomcat 7 nor 8 detected so no native error page exists");
if (ClassUtils.isPresent("org.apache.catalina.deploy.ErrorPage", null)) { if (ClassUtils.isPresent("org.apache.tomcat.util.descriptor.web.ErrorPage",
org.apache.catalina.deploy.ErrorPage errorPage = (org.apache.catalina.deploy.ErrorPage) this.nativePage; null)) {
org.apache.tomcat.util.descriptor.web.ErrorPage errorPage = (org.apache.tomcat.util.descriptor.web.ErrorPage) this.nativePage;
errorPage.setLocation(this.location); errorPage.setLocation(this.location);
errorPage.setErrorCode(this.errorCode); errorPage.setErrorCode(this.errorCode);
errorPage.setExceptionType(this.exceptionType); errorPage.setExceptionType(this.exceptionType);
......
...@@ -22,11 +22,12 @@ import java.net.MalformedURLException; ...@@ -22,11 +22,12 @@ import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import javax.naming.directory.DirContext;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import org.apache.catalina.Context; import org.apache.catalina.Context;
import org.apache.catalina.WebResourceRoot.ResourceSetType;
import org.apache.catalina.core.StandardContext; import org.apache.catalina.core.StandardContext;
import org.apache.naming.resources.FileDirContext;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
...@@ -104,29 +105,57 @@ abstract class TomcatResources { ...@@ -104,29 +105,57 @@ abstract class TomcatResources {
*/ */
private static class Tomcat7Resources extends TomcatResources { private static class Tomcat7Resources extends TomcatResources {
private final Method addResourceJarUrlMethod;
public Tomcat7Resources(Context context) { public Tomcat7Resources(Context context) {
super(context); super(context);
this.addResourceJarUrlMethod = ReflectionUtils.findMethod(context.getClass(),
"addResourceJarUrl", URL.class);
} }
@Override @Override
protected void addJar(String jar) { protected void addJar(String jar) {
URL url = getJarUlr(jar);
if (url != null) {
try {
this.addResourceJarUrlMethod.invoke(getContext(), url);
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
}
private URL getJarUlr(String jar) {
try { try {
getContext().addResourceJarUrl(new URL(jar)); return new URL(jar);
} }
catch (MalformedURLException ex) { catch (MalformedURLException ex) {
// Ignore? // Ignore
return null;
} }
} }
@Override @Override
protected void addDir(String dir, URL url) { protected void addDir(String dir, URL url) {
if (getContext() instanceof ServletContext) { if (getContext() instanceof ServletContext) {
FileDirContext files = new FileDirContext(); try {
files.setDocBase(dir); Class<?> fileDirContextClass = Class
((StandardContext) getContext()).addResourcesDirContext(files); .forName("org.apache.naming.resources.FileDirContext");
Method setDocBaseMethod = ReflectionUtils.findMethod(
fileDirContextClass, "setDocBase", String.class);
Object fileDirContext = fileDirContextClass.newInstance();
setDocBaseMethod.invoke(fileDirContext, dir);
Method addResourcesDirContextMethod = ReflectionUtils.findMethod(
StandardContext.class, "addResourcesDirContext",
DirContext.class);
addResourcesDirContextMethod.invoke(getContext(), fileDirContext);
}
catch (Exception ex) {
throw new IllegalStateException("Tomcat 7 reflection failed", ex);
}
} }
} }
} }
/** /**
...@@ -134,28 +163,8 @@ abstract class TomcatResources { ...@@ -134,28 +163,8 @@ abstract class TomcatResources {
*/ */
static class Tomcat8Resources extends TomcatResources { static class Tomcat8Resources extends TomcatResources {
private Object resources;
private Method createWebResourceSetMethod;
private Enum<?> resourceJarEnum;
@SuppressWarnings({ "rawtypes", "unchecked" })
public Tomcat8Resources(Context context) { public Tomcat8Resources(Context context) {
super(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 @Override
...@@ -178,7 +187,8 @@ abstract class TomcatResources { ...@@ -178,7 +187,8 @@ abstract class TomcatResources {
} }
URL url = new URL(resource); URL url = new URL(resource);
String path = "/META-INF/resources"; String path = "/META-INF/resources";
createWebResourceSet("/", url, path); getContext().getResources().createWebResourceSet(
ResourceSetType.RESOURCE_JAR, "/", url, path);
} }
catch (Exception ex) { catch (Exception ex) {
// Ignore (probably not a directory) // Ignore (probably not a directory)
...@@ -189,12 +199,6 @@ abstract class TomcatResources { ...@@ -189,12 +199,6 @@ abstract class TomcatResources {
return dir.indexOf("!/") < dir.lastIndexOf("!/"); 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);
}
} }
} }
...@@ -21,9 +21,10 @@ import java.util.concurrent.TimeUnit; ...@@ -21,9 +21,10 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.HandlerWrapper; import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.server.ssl.SslConnector;
import org.eclipse.jetty.webapp.Configuration; import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebAppContext;
import org.junit.Test; import org.junit.Test;
...@@ -114,9 +115,11 @@ public class JettyEmbeddedServletContainerFactoryTests extends ...@@ -114,9 +115,11 @@ public class JettyEmbeddedServletContainerFactoryTests extends
this.container.start(); this.container.start();
JettyEmbeddedServletContainer jettyContainer = (JettyEmbeddedServletContainer) this.container; JettyEmbeddedServletContainer jettyContainer = (JettyEmbeddedServletContainer) this.container;
SslConnector sslConnector = (SslConnector) jettyContainer.getServer() ServerConnector connector = (ServerConnector) jettyContainer.getServer()
.getConnectors()[0]; .getConnectors()[0];
assertThat(sslConnector.getSslContextFactory().getIncludeCipherSuites(), SslConnectionFactory connectionFactory = connector
.getConnectionFactory(SslConnectionFactory.class);
assertThat(connectionFactory.getSslContextFactory().getIncludeCipherSuites(),
equalTo(new String[] { "ALPHA", "BRAVO", "CHARLIE" })); equalTo(new String[] { "ALPHA", "BRAVO", "CHARLIE" }));
} }
......
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