diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java index 7b9ebcb31a..a1a854a0e9 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java @@ -19,8 +19,10 @@ package org.springframework.boot.autoconfigure.web; import java.io.File; import java.net.InetAddress; import java.nio.charset.Charset; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; @@ -72,6 +74,7 @@ import org.springframework.boot.web.servlet.ServletContextInitializer; import org.springframework.context.EnvironmentAware; import org.springframework.core.Ordered; import org.springframework.core.env.Environment; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** @@ -671,6 +674,13 @@ public class ServerProperties */ private int acceptCount = 0; + /** + * Comma-separated list of additional patterns that match jars to ignore for + * TLD scanning. The special '?' and '*' characters can be used in the pattern + * to match one and only one character and zero or more characters respectively. + */ + private List additionalTldSkipPatterns = new ArrayList(); + public int getMaxThreads() { return this.maxThreads; } @@ -779,6 +789,14 @@ public class ServerProperties this.acceptCount = acceptCount; } + public List getAdditionalTldSkipPatterns() { + return this.additionalTldSkipPatterns; + } + + public void setAdditionalTldSkipPatterns(List additionalTldSkipPatterns) { + this.additionalTldSkipPatterns = additionalTldSkipPatterns; + } + void customizeTomcat(ServerProperties serverProperties, TomcatEmbeddedServletContainerFactory factory) { if (getBasedir() != null) { @@ -819,6 +837,9 @@ public class ServerProperties if (this.acceptCount > 0) { customizeAcceptCount(factory); } + if (!ObjectUtils.isEmpty(this.additionalTldSkipPatterns)) { + factory.getTldSkipPatterns().addAll(this.additionalTldSkipPatterns); + } } private void customizeAcceptCount(TomcatEmbeddedServletContainerFactory factory) { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java index 5ef13ccf81..e67990ab85 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java @@ -488,6 +488,32 @@ public class ServerPropertiesTests { verify(container).setAccessLogRotate(false); } + @Test + public void customTomcatTldSkip() { + Map map = new HashMap(); + map.put("server.tomcat.additional-tld-skip-patterns", "foo.jar,bar.jar"); + bindProperties(map); + + testCustomTomcatTldSkip("foo.jar", "bar.jar"); + } + + @Test + public void customTomcatTldSkipAsList() { + Map map = new HashMap(); + map.put("server.tomcat.additional-tld-skip-patterns[0]", "biz.jar"); + map.put("server.tomcat.additional-tld-skip-patterns[1]", "bah.jar"); + bindProperties(map); + + testCustomTomcatTldSkip("biz.jar", "bah.jar"); + } + + private void testCustomTomcatTldSkip(String... expectedJars) { + TomcatEmbeddedServletContainerFactory container = new TomcatEmbeddedServletContainerFactory(); + this.properties.customize(container); + assertThat(container.getTldSkipPatterns()).contains(expectedJars); + assertThat(container.getTldSkipPatterns()).contains("junit-*.jar", "spring-boot-*.jar"); + } + @Test public void defaultUseForwardHeadersUndertow() throws Exception { UndertowEmbeddedServletContainerFactory container = spy( diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 9e982f6e61..fbca0920c1 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -201,6 +201,7 @@ content into your application; rather pick only the properties that you need. server.tomcat.accesslog.rename-on-rotate=false # Defer inclusion of the date stamp in the file name until rotate time. server.tomcat.accesslog.rotate=true # Enable access log rotation. server.tomcat.accesslog.suffix=.log # Log file name suffix. + server.tomcat.additional-tld-skip-patterns= # Comma-separated list of additional patterns that match jars to ignore for TLD scanning. server.tomcat.background-processor-delay=30 # Delay in seconds between the invocation of backgroundProcess methods. server.tomcat.basedir= # Tomcat base directory. If not specified a temporary directory will be used. server.tomcat.internal-proxies=10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|\\ diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/SkipPatternJarScanner.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/SkipPatternJarScanner.java index 26a2c11001..57d96402d3 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/SkipPatternJarScanner.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/SkipPatternJarScanner.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2016 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. @@ -17,10 +17,10 @@ package org.springframework.boot.context.embedded.tomcat; import java.lang.reflect.Method; +import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; -import java.util.StringTokenizer; import javax.servlet.ServletContext; @@ -40,7 +40,8 @@ import org.springframework.util.StringUtils; * {@link JarScanner} due to API changes introduced in Tomcat 8. * * @author Phillip Webb - * @see #apply(TomcatEmbeddedContext, String) + * @author Stephane Nicoll + * @see #apply(TomcatEmbeddedContext, Set) */ class SkipPatternJarScanner extends StandardJarScanner { @@ -50,10 +51,10 @@ class SkipPatternJarScanner extends StandardJarScanner { private final SkipPattern pattern; - SkipPatternJarScanner(JarScanner jarScanner, String pattern) { + SkipPatternJarScanner(JarScanner jarScanner, Set patterns) { Assert.notNull(jarScanner, "JarScanner must not be null"); this.jarScanner = jarScanner; - this.pattern = (pattern == null ? new SkipPattern() : new SkipPattern(pattern)); + this.pattern = (patterns == null ? new SkipPattern(defaultPatterns()) : new SkipPattern(patterns)); setPatternToTomcat8SkipFilter(this.pattern); } @@ -79,14 +80,106 @@ class SkipPatternJarScanner extends StandardJarScanner { } } + /** + * Return the default skip patterns to use. + * @return the default skip patterns + */ + static Set defaultPatterns() { + return new LinkedHashSet(Arrays.asList( + // Same as Tomcat + "ant-*.jar", + "aspectj*.jar", + "commons-beanutils*.jar", + "commons-codec*.jar", + "commons-collections*.jar", + "commons-dbcp*.jar", + "commons-digester*.jar", + "commons-fileupload*.jar", + "commons-httpclient*.jar", + "commons-io*.jar", + "commons-lang*.jar", + "commons-logging*.jar", + "commons-math*.jar", + "commons-pool*.jar", + "geronimo-spec-jaxrpc*.jar", + "h2*.jar", + "hamcrest*.jar", + "hibernate*.jar", + "jmx*.jar", + "jmx-tools-*.jar", + "jta*.jar", + "junit-*.jar", + "httpclient*.jar", + "log4j-*.jar", + "mail*.jar", + "org.hamcrest*.jar", + "slf4j*.jar", + "tomcat-embed-core-*.jar", + "tomcat-embed-logging-*.jar", + "tomcat-jdbc-*.jar", + "tomcat-juli-*.jar", + "tools.jar", + "wsdl4j*.jar", + "xercesImpl-*.jar", + "xmlParserAPIs-*.jar", + "xml-apis-*.jar", + + // Additional + "antlr-*.jar", + "aopalliance-*.jar", + "aspectjrt-*.jar", + "aspectjweaver-*.jar", + "classmate-*.jar", + "dom4j-*.jar", + "ecj-*.jar", + "ehcache-core-*.jar", + "hibernate-core-*.jar", + "hibernate-commons-annotations-*.jar", + "hibernate-entitymanager-*.jar", + "hibernate-jpa-2.1-api-*.jar", + "hibernate-validator-*.jar", + "hsqldb-*.jar", + "jackson-annotations-*.jar", + "jackson-core-*.jar", + "jackson-databind-*.jar", + "jandex-*.jar", + "javassist-*.jar", + "jboss-logging-*.jar", + "jboss-transaction-api_*.jar", + "jcl-over-slf4j-*.jar", + "jdom-*.jar", + "jul-to-slf4j-*.jar", + "log4j-over-slf4j-*.jar", + "logback-classic-*.jar", + "logback-core-*.jar", + "rome-*.jar", + "slf4j-api-*.jar", + "spring-aop-*.jar", + "spring-aspects-*.jar", + "spring-beans-*.jar", + "spring-boot-*.jar", + "spring-core-*.jar", + "spring-context-*.jar", + "spring-data-*.jar", + "spring-expression-*.jar", + "spring-jdbc-*.jar,", + "spring-orm-*.jar", + "spring-oxm-*.jar", + "spring-tx-*.jar", + "snakeyaml-*.jar", + "tomcat-embed-el-*.jar", + "validation-api-*.jar", + "xml-apis-*.jar")); + } + /** * Apply this decorator the specified context. * @param context the context to apply to - * @param pattern the jar skip pattern or {@code null} for defaults + * @param patterns the jar skip patterns or {@code null} for defaults */ - public static void apply(TomcatEmbeddedContext context, String pattern) { + static void apply(TomcatEmbeddedContext context, Set patterns) { SkipPatternJarScanner scanner = new SkipPatternJarScanner(context.getJarScanner(), - pattern); + patterns); context.setJarScanner(scanner); } @@ -116,97 +209,9 @@ class SkipPatternJarScanner extends StandardJarScanner { private Set patterns = new LinkedHashSet(); - protected SkipPattern() { - // Same as Tomcat - add("ant-*.jar"); - add("aspectj*.jar"); - add("commons-beanutils*.jar"); - add("commons-codec*.jar"); - add("commons-collections*.jar"); - add("commons-dbcp*.jar"); - add("commons-digester*.jar"); - add("commons-fileupload*.jar"); - add("commons-httpclient*.jar"); - add("commons-io*.jar"); - add("commons-lang*.jar"); - add("commons-logging*.jar"); - add("commons-math*.jar"); - add("commons-pool*.jar"); - add("geronimo-spec-jaxrpc*.jar"); - add("h2*.jar"); - add("hamcrest*.jar"); - add("hibernate*.jar"); - add("jmx*.jar"); - add("jmx-tools-*.jar"); - add("jta*.jar"); - add("junit-*.jar"); - add("httpclient*.jar"); - add("log4j-*.jar"); - add("mail*.jar"); - add("org.hamcrest*.jar"); - add("slf4j*.jar"); - add("tomcat-embed-core-*.jar"); - add("tomcat-embed-logging-*.jar"); - add("tomcat-jdbc-*.jar"); - add("tomcat-juli-*.jar"); - add("tools.jar"); - add("wsdl4j*.jar"); - add("xercesImpl-*.jar"); - add("xmlParserAPIs-*.jar"); - add("xml-apis-*.jar"); - - // Additional - add("antlr-*.jar"); - add("aopalliance-*.jar"); - add("aspectjrt-*.jar"); - add("aspectjweaver-*.jar"); - add("classmate-*.jar"); - add("dom4j-*.jar"); - add("ecj-*.jar"); - add("ehcache-core-*.jar"); - add("hibernate-core-*.jar"); - add("hibernate-commons-annotations-*.jar"); - add("hibernate-entitymanager-*.jar"); - add("hibernate-jpa-2.1-api-*.jar"); - add("hibernate-validator-*.jar"); - add("hsqldb-*.jar"); - add("jackson-annotations-*.jar"); - add("jackson-core-*.jar"); - add("jackson-databind-*.jar"); - add("jandex-*.jar"); - add("javassist-*.jar"); - add("jboss-logging-*.jar"); - add("jboss-transaction-api_*.jar"); - add("jcl-over-slf4j-*.jar"); - add("jdom-*.jar"); - add("jul-to-slf4j-*.jar"); - add("log4j-over-slf4j-*.jar"); - add("logback-classic-*.jar"); - add("logback-core-*.jar"); - add("rome-*.jar"); - add("slf4j-api-*.jar"); - add("spring-aop-*.jar"); - add("spring-aspects-*.jar"); - add("spring-beans-*.jar"); - add("spring-boot-*.jar"); - add("spring-core-*.jar"); - add("spring-context-*.jar"); - add("spring-data-*.jar"); - add("spring-expression-*.jar"); - add("spring-jdbc-*.jar,"); - add("spring-orm-*.jar"); - add("spring-oxm-*.jar"); - add("spring-tx-*.jar"); - add("snakeyaml-*.jar"); - add("tomcat-embed-el-*.jar"); - add("validation-api-*.jar"); - add("xml-apis-*.jar"); - } - - SkipPattern(String patterns) { - StringTokenizer tokenizer = new StringTokenizer(patterns, ","); - while (tokenizer.hasMoreElements()) { - add(tokenizer.nextToken()); + SkipPattern(Set patterns) { + for (String pattern : patterns) { + add(pattern); } } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java index cc644a08c0..103acdbf8a 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -125,7 +126,8 @@ public class TomcatEmbeddedServletContainerFactory private String protocol = DEFAULT_PROTOCOL; - private String tldSkip; + private Set tldSkipPatterns = new LinkedHashSet( + SkipPatternJarScanner.defaultPatterns()); private Charset uriEncoding = DEFAULT_CHARSET; @@ -204,7 +206,7 @@ public class TomcatEmbeddedServletContainerFactory catch (NoSuchMethodError ex) { // Tomcat is < 8.0.30. Continue } - SkipPatternJarScanner.apply(context, this.tldSkip); + SkipPatternJarScanner.apply(context, this.tldSkipPatterns); WebappLoader loader = new WebappLoader(context.getParentClassLoader()); loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName()); loader.setDelegate(true); @@ -552,10 +554,41 @@ public class TomcatEmbeddedServletContainerFactory * A comma-separated list of jars to ignore for TLD scanning. See Tomcat's * catalina.properties for typical values. Defaults to a list drawn from that source. * @param tldSkip the jars to skip when scanning for TLDs etc + * @deprecated since 1.5.0 in favor of {@link #setTldSkipPatterns(List)} */ + @Deprecated public void setTldSkip(String tldSkip) { Assert.notNull(tldSkip, "TldSkip must not be null"); - this.tldSkip = tldSkip; + setTldSkipPatterns(Arrays.asList( + StringUtils.commaDelimitedListToStringArray(tldSkip))); + } + + /** + * Set the patterns that match jars to ignore for TLD scanning. See Tomcat's + * catalina.properties for typical values. Defaults to a list drawn from that source. + * @param patterns the jar patterns to skip when scanning for TLDs etc + */ + public void setTldSkipPatterns(List patterns) { + Assert.notNull(patterns, "patterns must not be null"); + this.tldSkipPatterns = new LinkedHashSet(patterns); + } + + /** + * Returns a mutable set of the patterns that match jars to ignore for TLD scanning. + * @return the list of jars to ignore for TLD scanning + */ + public Set getTldSkipPatterns() { + return this.tldSkipPatterns; + } + + /** + * Add patterns that match jars to ignore for TLD scanning. See Tomcat's + * catalina.properties for typical values. + * @param patterns the additional jar patterns to skip when scanning for TLDs etc + */ + public void addAdditionalTldSkipPatterns(String... patterns) { + Assert.notNull(patterns, "patterns must not be null"); + this.tldSkipPatterns.addAll(Arrays.asList(patterns)); } /**