diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/LogFileMvcEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/LogFileMvcEndpoint.java index cbadb42e59..66d16e4733 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/LogFileMvcEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/LogFileMvcEndpoint.java @@ -19,6 +19,7 @@ package org.springframework.boot.actuate.endpoint.mvc; import java.io.File; import java.io.IOException; +import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -82,7 +83,8 @@ public class LogFileMvcEndpoint extends AbstractNamedMvcEndpoint { } resource = null; } - new Handler(resource).handleRequest(request, response); + Handler handler = new Handler(resource, request.getServletContext()); + handler.handleRequest(request, response); } private Resource getLogFileResource() { @@ -104,10 +106,11 @@ public class LogFileMvcEndpoint extends AbstractNamedMvcEndpoint { private final Resource resource; - Handler(Resource resource) { + Handler(Resource resource, ServletContext servletContext) { this.resource = resource; getLocations().add(resource); try { + setServletContext(servletContext); afterPropertiesSet(); } catch (Exception ex) { diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationExcludeFilter.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationExcludeFilter.java index efc4ea7fa5..a083b9b26e 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationExcludeFilter.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationExcludeFilter.java @@ -27,8 +27,7 @@ import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.TypeFilter; /** - * A {@link TypeFilter} implementation that matches registered auto-configuration - * classes. + * A {@link TypeFilter} implementation that matches registered auto-configuration classes. * * @author Stephane Nicoll * @since 1.5.0 @@ -37,7 +36,7 @@ public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoad private ClassLoader beanClassLoader; - private List candidateAutoConfigurations; + private volatile List autoConfigurations; @Override public void setBeanClassLoader(ClassLoader beanClassLoader) { @@ -50,22 +49,22 @@ public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoad return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader); } - protected List getCandidateAutoConfigurations() { - if (this.candidateAutoConfigurations == null) { - this.candidateAutoConfigurations = SpringFactoriesLoader.loadFactoryNames( - EnableAutoConfiguration.class, this.beanClassLoader); - } - return this.candidateAutoConfigurations; - } - private boolean isConfiguration(MetadataReader metadataReader) { return metadataReader.getAnnotationMetadata() .isAnnotated(Configuration.class.getName()); } private boolean isAutoConfiguration(MetadataReader metadataReader) { - return getCandidateAutoConfigurations().contains( - metadataReader.getClassMetadata().getClassName()); + return getAutoConfigurations() + .contains(metadataReader.getClassMetadata().getClassName()); + } + + protected List getAutoConfigurations() { + if (this.autoConfigurations == null) { + this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames( + EnableAutoConfiguration.class, this.beanClassLoader); + } + return this.autoConfigurations; } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java index 538ac5c0a4..67d1ccbd96 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java @@ -16,16 +16,12 @@ package org.springframework.boot.autoconfigure.orm.jpa; -import java.net.URL; import java.util.List; import java.util.Map; import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; @@ -53,7 +49,6 @@ import org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor; import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.jta.JtaTransactionManager; -import org.springframework.util.ResourceUtils; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; @@ -69,8 +64,6 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter @Import(DataSourceInitializedPublisher.Registrar.class) public abstract class JpaBaseConfiguration implements BeanFactoryAware { - private static final Log logger = LogFactory.getLog(JpaBaseConfiguration.class); - private final DataSource dataSource; private final JpaProperties properties; @@ -110,8 +103,7 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware { ObjectProvider persistenceUnitManagerProvider) { EntityManagerFactoryBuilder builder = new EntityManagerFactoryBuilder( jpaVendorAdapter, this.properties.getProperties(), - persistenceUnitManagerProvider.getIfAvailable(), - determinePersistenceUnitRootLocation()); + persistenceUnitManagerProvider.getIfAvailable()); builder.setCallback(getVendorCallback()); return builder; } @@ -190,19 +182,6 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware { this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; } - private URL determinePersistenceUnitRootLocation() { - Class source = getClass(); - try { - URL url = source.getProtectionDomain().getCodeSource().getLocation(); - return ResourceUtils.extractJarFileURL(url); - } - catch (Exception ex) { - logger.info("Could not determine persistence " + "unit root location from " - + source + " : " + ex); - } - return null; - } - @Configuration @ConditionalOnWebApplication @ConditionalOnClass(WebMvcConfigurerAdapter.class) 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 a1a854a0e9..1e75bf40c0 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 @@ -675,9 +675,9 @@ 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. + * 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(); diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationExcludeFilterTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationExcludeFilterTests.java index 411f10b048..dde957cb25 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationExcludeFilterTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationExcludeFilterTests.java @@ -41,6 +41,8 @@ import static org.assertj.core.api.Assertions.assertThat; */ public class AutoConfigurationExcludeFilterTests { + private static final Class FILTERED = ExampleFilteredAutoConfiguration.class; + @Rule public ExpectedException thrown = ExpectedException.none(); @@ -59,10 +61,9 @@ public class AutoConfigurationExcludeFilterTests { assertThat(this.context.getBeansOfType(String.class)).hasSize(1); assertThat(this.context.getBean(String.class)).isEqualTo("test"); this.thrown.expect(NoSuchBeanDefinitionException.class); - this.context.getBean(ExampleFilteredAutoConfiguration.class); + this.context.getBean(FILTERED); } - @Configuration @ComponentScan(basePackageClasses = ExampleConfiguration.class, excludeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TestAutoConfigurationExcludeFilter.class)) static class Config { @@ -71,10 +72,12 @@ public class AutoConfigurationExcludeFilterTests { static class TestAutoConfigurationExcludeFilter extends AutoConfigurationExcludeFilter { + @Override - protected List getCandidateAutoConfigurations() { - return Collections.singletonList(ExampleFilteredAutoConfiguration.class.getName()); + protected List getAutoConfigurations() { + return Collections.singletonList(FILTERED.getName()); } + } } 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 e67990ab85..43a65bfa05 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 @@ -493,7 +493,6 @@ public class ServerPropertiesTests { Map map = new HashMap(); map.put("server.tomcat.additional-tld-skip-patterns", "foo.jar,bar.jar"); bindProperties(map); - testCustomTomcatTldSkip("foo.jar", "bar.jar"); } @@ -503,7 +502,6 @@ public class ServerPropertiesTests { 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"); } @@ -511,7 +509,8 @@ public class ServerPropertiesTests { TomcatEmbeddedServletContainerFactory container = new TomcatEmbeddedServletContainerFactory(); this.properties.customize(container); assertThat(container.getTldSkipPatterns()).contains(expectedJars); - assertThat(container.getTldSkipPatterns()).contains("junit-*.jar", "spring-boot-*.jar"); + assertThat(container.getTldSkipPatterns()).contains("junit-*.jar", + "spring-boot-*.jar"); } @Test diff --git a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java index 806a4a637d..3fac71701a 100755 --- a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java +++ b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java @@ -21,10 +21,13 @@ import java.util.HashMap; import java.util.Map; import org.springframework.boot.SpringApplication; +import org.springframework.boot.bind.RelaxedPropertyResolver; +import org.springframework.boot.devtools.restart.Restarter; import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; @@ -59,7 +62,7 @@ public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostPro @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { - if (isLocalApplication(environment)) { + if (isLocalApplication(environment) && canAddProperties(environment)) { PropertySource propertySource = new MapPropertySource("refresh", PROPERTIES); environment.getPropertySources().addLast(propertySource); @@ -70,4 +73,24 @@ public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostPro return environment.getPropertySources().get("remoteUrl") == null; } + private boolean canAddProperties(Environment environment) { + return isRestarterInitialized() || isRemoteRestartEnabled(environment); + } + + private boolean isRestarterInitialized() { + try { + Restarter restarter = Restarter.getInstance(); + return (restarter != null && restarter.getInitialUrls() != null); + } + catch (Exception ex) { + return false; + } + } + + private boolean isRemoteRestartEnabled(Environment environment) { + RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment, + "spring.devtools.remote."); + return resolver.containsProperty("secret"); + } + } diff --git a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/env/DevToolPropertiesIntegrationTests.java b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/env/DevToolPropertiesIntegrationTests.java index 2caae8c0e9..7fa890e272 100644 --- a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/env/DevToolPropertiesIntegrationTests.java +++ b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/env/DevToolPropertiesIntegrationTests.java @@ -16,11 +16,20 @@ package org.springframework.boot.devtools.env; -import org.junit.After; -import org.junit.Test; +import java.net.URL; +import java.util.Collections; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.devtools.restart.RestartInitializer; +import org.springframework.boot.devtools.restart.Restarter; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -32,13 +41,22 @@ import org.springframework.context.annotation.Configuration; */ public class DevToolPropertiesIntegrationTests { + @Rule + public ExpectedException thrown = ExpectedException.none(); + private ConfigurableApplicationContext context; + @Before + public void setup() { + Restarter.initialize(new String[] {}, false, new MockInitializer(), false); + } + @After public void cleanup() { if (this.context != null) { this.context.close(); } + Restarter.clearInstance(); } @Test @@ -59,6 +77,33 @@ public class DevToolPropertiesIntegrationTests { this.context.getBean(MyBean.class); } + @Test + public void postProcessWhenRestarterDisabledAndRemoteSecretNotSetShouldNotAddPropertySource() + throws Exception { + Restarter.clearInstance(); + Restarter.disable(); + SpringApplication application = new SpringApplication( + BeanConditionConfiguration.class); + application.setWebEnvironment(false); + this.context = application.run(); + this.thrown.expect(NoSuchBeanDefinitionException.class); + this.context.getBean(MyBean.class); + } + + @Test + public void postProcessWhenRestarterDisabledAndRemoteSecretSetShouldAddPropertySource() + throws Exception { + Restarter.clearInstance(); + Restarter.disable(); + SpringApplication application = new SpringApplication( + BeanConditionConfiguration.class); + application.setWebEnvironment(false); + application.setDefaultProperties(Collections.singletonMap( + "spring.devtools.remote.secret", "donttell")); + this.context = application.run(); + this.context.getBean(MyBean.class); + } + @Configuration @ConditionalOnProperty("spring.h2.console.enabled") static class ClassConditionConfiguration { @@ -79,4 +124,12 @@ public class DevToolPropertiesIntegrationTests { } + static class MockInitializer implements RestartInitializer { + + @Override + public URL[] getInitialUrls(Thread thread) { + return new URL[] {}; + } + + } } diff --git a/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc b/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc index 55449e04e6..6b662acd1a 100644 --- a/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc +++ b/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc @@ -493,7 +493,7 @@ The following configuration options are available: [[build-tool-plugins-gradle-configuration-layouts]] -==== Available built-in layouts +==== Available layouts The `layout` attribute configures the format of the archive and whether the bootstrap loader should be included or not. The following layouts are available: @@ -530,37 +530,6 @@ loader should be included or not. The following layouts are available: -[[build-tool-plugins-gradle-configuration-custom-layout]] -==== Using a custom layout -If you have custom requirements for how to arrange the dependencies and loader classes -inside the repackaged jar, you can use a custom layout in addition to the built-in values. -Any library which defines one or more `LayoutFactory` implementations and -lists them in `META-INF/spring.factories` can be added to the build script dependencies -and then the layout type becomes available in the `springBoot` configuration. For example - -[source,groovy,indent=0,subs="verbatim,attributes"] ----- -buildscript { - ext { - springBootVersion = '1.5.0.BUILD-SNAPSHOT' - customVersion = '0.0.1.BUILD-SNAPSHOT' - } - repositories { - mavenLocal() - mavenCentral() - } - dependencies { - classpath("com.example:custom-layout:${customVersion}") - classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") - } -} - -springBoot { - layout = 'CUSTOM' -} ----- - - [[build-tool-plugins-understanding-the-gradle-plugin]] === Understanding how the Gradle plugin works When `spring-boot` is applied to your Gradle project a default task named `bootRepackage` diff --git a/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/orm/jpa/TestDatabaseAutoConfiguration.java b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/orm/jpa/TestDatabaseAutoConfiguration.java index 33df6947e7..3e018a5c06 100644 --- a/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/orm/jpa/TestDatabaseAutoConfiguration.java +++ b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/orm/jpa/TestDatabaseAutoConfiguration.java @@ -101,15 +101,20 @@ public class TestDatabaseAutoConfiguration { ConfigurableListableBeanFactory beanFactory) { BeanDefinitionHolder holder = getDataSourceBeanDefinition(beanFactory); if (holder != null) { - logger.info("Replacing '" + holder.getBeanName() - + "' DataSource bean with embedded version"); - registry.registerBeanDefinition(holder.getBeanName(), - createEmbeddedBeanDefinition()); + String beanName = holder.getBeanName(); + boolean primary = holder.getBeanDefinition().isPrimary(); + logger.info("Replacing '" + beanName + "' DataSource bean with " + + (primary ? "primary " : "") + "embedded version"); + registry.registerBeanDefinition(beanName, + createEmbeddedBeanDefinition(primary)); } } - private BeanDefinition createEmbeddedBeanDefinition() { - return new RootBeanDefinition(EmbeddedDataSourceFactoryBean.class); + private BeanDefinition createEmbeddedBeanDefinition(boolean primary) { + BeanDefinition beanDefinition = new RootBeanDefinition( + EmbeddedDataSourceFactoryBean.class); + beanDefinition.setPrimary(primary); + return beanDefinition; } private BeanDefinitionHolder getDataSourceBeanDefinition( diff --git a/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/orm/jpa/AutoConfigureTestDatabaseWithMultipleDatasourcesIntegrationTests.java b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/orm/jpa/AutoConfigureTestDatabaseWithMultipleDatasourcesIntegrationTests.java new file mode 100644 index 0000000000..5428b5da9c --- /dev/null +++ b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/orm/jpa/AutoConfigureTestDatabaseWithMultipleDatasourcesIntegrationTests.java @@ -0,0 +1,92 @@ +/* + * 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. + * 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.autoconfigure.orm.jpa; + +import javax.sql.DataSource; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link AutoConfigureTestDatabase} when there are multiple + * datasources. + * + * @author Greg Potter + */ +@RunWith(SpringRunner.class) +@DataJpaTest +@AutoConfigureTestDatabase +public class AutoConfigureTestDatabaseWithMultipleDatasourcesIntegrationTests { + + @Autowired + private TestEntityManager entities; + + @Autowired + private ExampleRepository repository; + + @Autowired + private DataSource dataSource; + + @Test + public void testRepository() throws Exception { + this.entities.persist(new ExampleEntity("boot", "124")); + this.entities.flush(); + ExampleEntity found = this.repository.findByReference("124"); + assertThat(found.getName()).isEqualTo("boot"); + } + + @Test + public void replacesDefinedDataSourceWithExplicit() throws Exception { + // Look that the datasource is replaced with an H2 DB. + String product = this.dataSource.getConnection().getMetaData() + .getDatabaseProductName(); + assertThat(product).startsWith("H2"); + } + + @Configuration + @EnableAutoConfiguration + static class Config { + + @Bean + @Primary + public DataSource dataSource() { + EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder() + .setType(EmbeddedDatabaseType.HSQL); + return builder.build(); + } + + @Bean + public DataSource secondaryDataSource() { + EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder() + .setType(EmbeddedDatabaseType.HSQL); + return builder.build(); + } + + } + +} diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/TypeUtils.java b/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/TypeUtils.java index 197671b808..24c862c3e7 100644 --- a/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/TypeUtils.java +++ b/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/TypeUtils.java @@ -96,8 +96,8 @@ class TypeUtils { /** * Return the qualified name of the specified element. * @param element the element to handle - * @return the fully qualified name of the element, suitable for a call - * to {@link Class#forName(String)} + * @return the fully qualified name of the element, suitable for a call to + * {@link Class#forName(String)} */ public String getQualifiedName(Element element) { if (element == null) { @@ -106,19 +106,20 @@ class TypeUtils { TypeElement enclosingElement = getEnclosingTypeElement(element.asType()); if (enclosingElement != null) { return getQualifiedName(enclosingElement) + "$" - + ((DeclaredType) element.asType()).asElement().getSimpleName().toString(); + + ((DeclaredType) element.asType()).asElement().getSimpleName() + .toString(); } if (element instanceof TypeElement) { return ((TypeElement) element).getQualifiedName().toString(); } - throw new IllegalStateException("Could not extract qualified name from " - + element); + throw new IllegalStateException( + "Could not extract qualified name from " + element); } /** * Return the type of the specified {@link TypeMirror} including all its generic * information. - * @param type the type to handle + * @param type the type to handle * @return a representation of the type including all its generic information */ public String getType(TypeMirror type) { diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java index 576b94fcac..c9a3033a6e 100644 --- a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java +++ b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java @@ -404,8 +404,7 @@ public class ConfigurationMetadataAnnotationProcessorTests { assertThat(metadata).has(Metadata.withGroup("generic.foo.bar.biz").ofType( "org.springframework.boot.configurationsample.specific.GenericConfig$Bar$Biz")); assertThat(metadata).has(Metadata.withProperty("generic.foo.name") - .ofType(String.class) - .fromSource(GenericConfig.Foo.class)); + .ofType(String.class).fromSource(GenericConfig.Foo.class)); assertThat(metadata).has(Metadata.withProperty("generic.foo.string-to-bar") .ofType("java.util.Map>") .fromSource(GenericConfig.Foo.class)); @@ -413,11 +412,9 @@ public class ConfigurationMetadataAnnotationProcessorTests { .ofType("java.util.Map") .fromSource(GenericConfig.Foo.class)); assertThat(metadata).has(Metadata.withProperty("generic.foo.bar.name") - .ofType("java.lang.String") - .fromSource(GenericConfig.Bar.class)); + .ofType("java.lang.String").fromSource(GenericConfig.Bar.class)); assertThat(metadata).has(Metadata.withProperty("generic.foo.bar.biz.name") - .ofType("java.lang.String") - .fromSource(GenericConfig.Bar.Biz.class)); + .ofType("java.lang.String").fromSource(GenericConfig.Bar.Biz.class)); assertThat(metadata.getItems()).hasSize(9); } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPluginExtension.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPluginExtension.java index d84bb2134c..987336250c 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPluginExtension.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPluginExtension.java @@ -21,13 +21,12 @@ import java.util.Map; import java.util.Set; import groovy.lang.Closure; - import org.gradle.api.Project; import org.gradle.api.plugins.JavaPlugin; import org.springframework.boot.gradle.buildinfo.BuildInfo; import org.springframework.boot.loader.tools.Layout; -import org.springframework.boot.loader.tools.LayoutType; +import org.springframework.boot.loader.tools.Layouts; /** * Gradle DSL Extension for 'Spring Boot'. Most of the time Spring Boot can guess the @@ -89,7 +88,7 @@ public class SpringBootPluginExtension { * the MANIFEST.MF 'Main-Class' to be PropertiesLauncher. Gradle will coerce literal * String values to the correct type. */ - String layout; + LayoutType layout; /** * Libraries that must be unpacked from fat jars in order to run. Use Strings in the @@ -146,7 +145,7 @@ public class SpringBootPluginExtension { * @return the Layout to use or null if not explicitly set */ public Layout convertLayout() { - return (this.layout == null ? null : LayoutType.layout(this.layout)); + return (this.layout == null ? null : this.layout.layout); } public String getMainClass() { @@ -189,11 +188,11 @@ public class SpringBootPluginExtension { this.backupSource = backupSource; } - public String getLayout() { + public LayoutType getLayout() { return this.layout; } - public void setLayout(String layout) { + public void setLayout(LayoutType layout) { this.layout = layout; } @@ -277,4 +276,29 @@ public class SpringBootPluginExtension { } } + /** + * Layout Types. + */ + enum LayoutType { + + JAR(new Layouts.Jar()), + + WAR(new Layouts.War()), + + ZIP(new Layouts.Expanded()), + + DIR(new Layouts.Expanded()), + + MODULE(new Layouts.Module()), + + NONE(new Layouts.None()); + + Layout layout; + + LayoutType(Layout layout) { + this.layout = layout; + } + + } + } diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java index 32d137c754..3eed102ae4 100644 --- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java +++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java @@ -51,6 +51,8 @@ import java.util.zip.ZipEntry; */ public class JarWriter { + private static final String NESTED_LOADER_JAR = "META-INF/loader/spring-boot-loader.jar"; + private static final int BUFFER_SIZE = 32 * 1024; private final JarOutputStream jarOutput; @@ -201,20 +203,9 @@ public class JarWriter { /** * Write the required spring-boot-loader classes to the JAR. * @throws IOException if the classes cannot be written - * @deprecated us {@link #writeLoaderClasses(String)} instead */ - @Deprecated public void writeLoaderClasses() throws IOException { - writeLoaderClasses(Layouts.DEFAULT_LOADER_JAR); - } - - /** - * Write the required spring-boot-loader classes to the JAR. - * @param loaderJarPath the path to the loader jar (in the classpath) - * @throws IOException if the classes cannot be written - */ - public void writeLoaderClasses(String loaderJarPath) throws IOException { - URL loaderJar = getClass().getClassLoader().getResource(loaderJarPath); + URL loaderJar = getClass().getClassLoader().getResource(NESTED_LOADER_JAR); JarInputStream inputStream = new JarInputStream( new BufferedInputStream(loaderJar.openStream())); JarEntry entry; diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layout.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layout.java index 09f7ded6be..38681e107e 100644 --- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layout.java +++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layout.java @@ -40,25 +40,15 @@ public interface Layout { String getLibraryDestination(String libraryName, LibraryScope scope); /** - * Returns the location of classes within the archive. Empty if the location is the - * root path, otherwise ends with a slash ('/'). + * Returns the location of classes within the archive. * @return the classes location */ String getClassesLocation(); /** - * Returns if loader classes should be included to make the archive executable. If - * true, then {@link #getLoaderJarPath()} should point to a valid jar file that - * contains the loader classes. + * Returns if loader classes should be included to make the archive executable. * @return if the layout is executable */ boolean isExecutable(); - /** - * Returns the path to a nested jar that contains the loader, and which will be - * unpacked into the root of the repackaged jar. - * @return the path to a nested jar that contains the loader - */ - String getLoaderJarPath(); - } diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayoutFactory.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayoutFactory.java deleted file mode 100644 index 9b21eceeb3..0000000000 --- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayoutFactory.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.loader.tools; - -/** - * Strategy for creating instances of {@link Layout}. - * - * @author Dave Syer - * - */ -public interface LayoutFactory { - - Layout getLayout(); - - String getName(); - -} diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayoutType.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayoutType.java deleted file mode 100644 index 6901a21117..0000000000 --- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayoutType.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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.loader.tools; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.core.io.support.SpringFactoriesLoader; - -/** - * Archive layout types. - * - * @author Dave Syer - */ -public enum LayoutType { - - /** - * Jar Layout. - */ - JAR(new Layouts.Jar()), - - /** - * War Layout. - */ - WAR(new Layouts.War()), - - /** - * Zip Layout. - */ - ZIP(new Layouts.Expanded()), - - /** - * Dir Layout. - */ - DIR(new Layouts.Expanded()), - - /** - * Module Layout. - */ - MODULE(new Layouts.Module()), - - /** - * No Layout. - */ - NONE(new Layouts.None()); - - private static Map customTypes; - - private final Layout layout; - - public Layout layout() { - return this.layout; - } - - LayoutType(Layout layout) { - this.layout = layout; - } - - public static Layout layout(String value) { - try { - return valueOf(value).layout(); - } - catch (IllegalArgumentException e) { - if (customTypes == null) { - customTypes = new HashMap(); - lookupCustomTypes(); - } - Layout layout = customTypes.get(value); - if (layout == null) { - throw new IllegalArgumentException( - "Cannot resolve custom layout type: " + value); - } - return layout; - } - } - - private static void lookupCustomTypes() { - ClassLoader classLoader = LayoutType.class.getClassLoader(); - List factories = SpringFactoriesLoader - .loadFactories(LayoutFactory.class, classLoader); - for (LayoutFactory factory : factories) { - customTypes.put(factory.getName(), factory.getLayout()); - } - } - -} diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layouts.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layouts.java index 00315e0e9c..3cebdc7a05 100644 --- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layouts.java +++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layouts.java @@ -33,11 +33,6 @@ import java.util.Set; */ public final class Layouts { - /** - * Default value for {@link Layout#getLoaderJarPath()}. - */ - public static final String DEFAULT_LOADER_JAR = "META-INF/loader/spring-boot-loader.jar"; - private Layouts() { } @@ -92,11 +87,6 @@ public final class Layouts { return true; } - @Override - public String getLoaderJarPath() { - return DEFAULT_LOADER_JAR; - } - } /** @@ -126,11 +116,6 @@ public final class Layouts { return false; } - @Override - public String getLoaderJarPath() { - return DEFAULT_LOADER_JAR; - } - } /** @@ -169,11 +154,6 @@ public final class Layouts { return true; } - @Override - public String getLoaderJarPath() { - return DEFAULT_LOADER_JAR; - } - } /** @@ -208,11 +188,6 @@ public final class Layouts { return false; } - @Override - public String getLoaderJarPath() { - return DEFAULT_LOADER_JAR; - } - } } diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java index 7e8564b62b..fb44ec1a93 100644 --- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java +++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java @@ -216,7 +216,7 @@ public class Repackager { } writeNestedLibraries(standardLibraries, seen, writer); if (this.layout.isExecutable()) { - writer.writeLoaderClasses(this.layout.getLoaderJarPath()); + writer.writeLoaderClasses(); } } finally { diff --git a/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/LayoutTypeTests.java b/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/LayoutTypeTests.java deleted file mode 100644 index 3792529f22..0000000000 --- a/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/LayoutTypeTests.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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.loader.tools; - -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * - */ -public class LayoutTypeTests { - - @Test - public void standardType() { - assertThat(LayoutType.layout("DIR")) - .isEqualTo(LayoutType.valueOf("DIR").layout()); - } - - @Test - public void customType() { - assertThat(LayoutType.layout("CUSTOM")).isNotNull(); - } - - public static class TestLayoutFactory implements LayoutFactory { - - @Override - public Layout getLayout() { - return new Layouts.Jar(); - } - - @Override - public String getName() { - return "CUSTOM"; - } - } - -} diff --git a/spring-boot-tools/spring-boot-loader-tools/src/test/resources/META-INF/spring.factories b/spring-boot-tools/spring-boot-loader-tools/src/test/resources/META-INF/spring.factories deleted file mode 100644 index 37eac70aa3..0000000000 --- a/spring-boot-tools/spring-boot-loader-tools/src/test/resources/META-INF/spring.factories +++ /dev/null @@ -1,2 +0,0 @@ -org.springframework.boot.loader.tools.LayoutFactory=\ -org.springframework.boot.loader.tools.LayoutTypeTests.TestLayoutFactory \ No newline at end of file diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFile.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFile.java index 7a3ee76a4f..162395c295 100644 --- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFile.java +++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFile.java @@ -371,6 +371,10 @@ public class JarFile extends java.util.jar.JarFile { return this.pathFromRoot; } + JarFileType getType() { + return this.type; + } + /** * Register a {@literal 'java.protocol.handler.pkgs'} property so that a * {@link URLStreamHandler} will be located to deal with jar URLs. @@ -396,7 +400,10 @@ public class JarFile extends java.util.jar.JarFile { } } - private enum JarFileType { + /** + * The type of a {@link JarFile}. + */ + enum JarFileType { DIRECT, NESTED_DIRECTORY, NESTED_JAR } diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarURLConnection.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarURLConnection.java index ca2ff46c16..7f2b5ce6ed 100644 --- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarURLConnection.java +++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarURLConnection.java @@ -29,6 +29,8 @@ import java.net.URLEncoder; import java.net.URLStreamHandler; import java.security.Permission; +import org.springframework.boot.loader.data.RandomAccessData.ResourceAccess; + /** * {@link java.net.JarURLConnection} used to support {@link JarFile#getUrl()}. * @@ -160,11 +162,14 @@ final class JarURLConnection extends java.net.JarURLConnection { if (this.jarFile == null) { throw FILE_NOT_FOUND_EXCEPTION; } - if (this.jarEntryName.isEmpty()) { + if (this.jarEntryName.isEmpty() + && this.jarFile.getType() == JarFile.JarFileType.DIRECT) { throw new IOException("no entry name specified"); } connect(); - InputStream inputStream = this.jarFile.getInputStream(this.jarEntry); + InputStream inputStream = (this.jarEntryName.isEmpty() + ? this.jarFile.getData().getInputStream(ResourceAccess.ONCE) + : this.jarFile.getInputStream(this.jarEntry)); if (inputStream == null) { throwFileNotFound(this.jarEntryName, this.jarFile); } diff --git a/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/JarFileTests.java b/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/JarFileTests.java index f4cc67b9ff..d9e2993400 100644 --- a/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/JarFileTests.java +++ b/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/JarFileTests.java @@ -28,6 +28,7 @@ import java.net.URLClassLoader; import java.nio.charset.Charset; import java.util.Enumeration; import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; import java.util.jar.Manifest; import java.util.zip.ZipEntry; @@ -54,6 +55,7 @@ import static org.mockito.Mockito.verify; * @author Andy Wilkinson */ public class JarFileTests { + private static final String PROTOCOL_HANDLER = "java.protocol.handler.pkgs"; private static final String HANDLERS_PACKAGE = "org.springframework.boot.loader"; @@ -270,6 +272,12 @@ public class JarFileTests { assertThat(conn.getJarFile()).isSameAs(nestedJarFile); assertThat(conn.getJarFileURL().toString()) .isEqualTo("jar:" + this.rootJarFile.toURI() + "!/nested.jar"); + assertThat(conn.getInputStream()).isNotNull(); + JarInputStream jarInputStream = new JarInputStream(conn.getInputStream()); + assertThat(jarInputStream.getNextJarEntry().getName()).isEqualTo("3.dat"); + assertThat(jarInputStream.getNextJarEntry().getName()).isEqualTo("4.dat"); + assertThat(jarInputStream.getNextJarEntry().getName()).isEqualTo("\u00E4.dat"); + jarInputStream.close(); assertThat(conn.getPermission()).isInstanceOf(FilePermission.class); FilePermission permission = (FilePermission) conn.getPermission(); assertThat(permission.getActions()).isEqualTo("read"); diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RepackageMojo.java b/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RepackageMojo.java index 42de1700b4..309eb72db3 100644 --- a/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RepackageMojo.java +++ b/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RepackageMojo.java @@ -42,7 +42,8 @@ import org.apache.maven.shared.artifact.filter.collection.ScopeFilter; import org.springframework.boot.loader.tools.DefaultLaunchScript; import org.springframework.boot.loader.tools.LaunchScript; -import org.springframework.boot.loader.tools.LayoutType; +import org.springframework.boot.loader.tools.Layout; +import org.springframework.boot.loader.tools.Layouts; import org.springframework.boot.loader.tools.Libraries; import org.springframework.boot.loader.tools.Repackager; @@ -130,7 +131,7 @@ public class RepackageMojo extends AbstractDependencyFilterMojo { * @since 1.0 */ @Parameter - private String layout; + private LayoutType layout; /** * A list of the libraries that must be unpacked from fat jars in order to run. @@ -227,7 +228,7 @@ public class RepackageMojo extends AbstractDependencyFilterMojo { repackager.setMainClass(this.mainClass); if (this.layout != null) { getLog().info("Layout: " + this.layout); - repackager.setLayout(LayoutType.layout(this.layout)); + repackager.setLayout(this.layout.layout()); } return repackager; } @@ -308,6 +309,53 @@ public class RepackageMojo extends AbstractDependencyFilterMojo { } } + /** + * Archive layout types. + */ + public enum LayoutType { + + /** + * Jar Layout. + */ + JAR(new Layouts.Jar()), + + /** + * War Layout. + */ + WAR(new Layouts.War()), + + /** + * Zip Layout. + */ + ZIP(new Layouts.Expanded()), + + /** + * Dir Layout. + */ + DIR(new Layouts.Expanded()), + + /** + * Module Layout. + */ + MODULE(new Layouts.Module()), + + /** + * No Layout. + */ + NONE(new Layouts.None()); + + private final Layout layout; + + public Layout layout() { + return this.layout; + } + + LayoutType(Layout layout) { + this.layout = layout; + } + + } + private static class LoggingRepackager extends Repackager { private final Log log; diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/examples/custom-layout.apt.vm b/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/examples/custom-layout.apt.vm deleted file mode 100644 index a11d046fa1..0000000000 --- a/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/examples/custom-layout.apt.vm +++ /dev/null @@ -1,57 +0,0 @@ - ----- - Use a custom layout - ----- - Dave Syer - ----- - 2016-10-30 - ----- - - Spring Boot repackages the jar file for this project using a custom - layout defined in the additional jar file, provided as a dependency - to the build plugin: - ---- - - ... - - ... - - ... - - ${project.groupId} - ${project.artifactId} - ${project.version} - - - - repackage - - - CUSTOM - - - - - - com.example - custom-layout - 0.0.1.BUILD-SNAPSHOT - - - ... - - ... - - ... - - ... - ---- - - The layout is provided as an implementation of <> - (from spring-boot-loader-tools) listed in - <> inside the <> jar. - - - - diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/index.apt b/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/index.apt index cb767f9e64..dcf5084405 100644 --- a/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/index.apt +++ b/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/index.apt @@ -48,8 +48,6 @@ Spring Boot Maven Plugin * {{{./examples/repackage-disable-attach.html}Local repackaged artifact}} - * {{{./examples/custom-layout.html}Custom layout}} - * {{{./examples/exclude-dependency.html}Exclude a dependency}} * {{{./examples/run-debug.html}Debug the application}} 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 57d96402d3..f5665b4871 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 @@ -17,9 +17,6 @@ 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 javax.servlet.ServletContext; @@ -49,18 +46,19 @@ class SkipPatternJarScanner extends StandardJarScanner { private final JarScanner jarScanner; - private final SkipPattern pattern; + private final Set patterns; SkipPatternJarScanner(JarScanner jarScanner, Set patterns) { Assert.notNull(jarScanner, "JarScanner must not be null"); + Assert.notNull(jarScanner, "Patterns must not be null"); this.jarScanner = jarScanner; - this.pattern = (patterns == null ? new SkipPattern(defaultPatterns()) : new SkipPattern(patterns)); - setPatternToTomcat8SkipFilter(this.pattern); + this.patterns = patterns; + setPatternToTomcat8SkipFilter(); } - private void setPatternToTomcat8SkipFilter(SkipPattern pattern) { + private void setPatternToTomcat8SkipFilter() { if (ClassUtils.isPresent(JAR_SCAN_FILTER_CLASS, null)) { - new Tomcat8TldSkipSetter(this).setSkipPattern(pattern); + new Tomcat8TldSkipSetter(this).setSkipPattern(this.patterns); } } @@ -73,105 +71,13 @@ class SkipPatternJarScanner extends StandardJarScanner { Assert.notNull(scanMethod, "Unable to find scan method"); try { scanMethod.invoke(this.jarScanner, context, classloader, callback, - (jarsToSkip == null ? this.pattern.asSet() : jarsToSkip)); + (jarsToSkip == null ? this.patterns : jarsToSkip)); } catch (Exception ex) { throw new IllegalStateException("Tomcat 7 reflection failed", ex); } } - /** - * 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 @@ -194,43 +100,12 @@ class SkipPatternJarScanner extends StandardJarScanner { this.jarScanner = jarScanner; } - public void setSkipPattern(SkipPattern pattern) { + public void setSkipPattern(Set patterns) { StandardJarScanFilter filter = new StandardJarScanFilter(); - filter.setTldSkip(pattern.asCommaDelimitedString()); + filter.setTldSkip(StringUtils.collectionToCommaDelimitedString(patterns)); this.jarScanner.setJarScanFilter(filter); } } - /** - * Skip patterns used by Spring Boot. - */ - private static class SkipPattern { - - private Set patterns = new LinkedHashSet(); - - SkipPattern(Set patterns) { - for (String pattern : patterns) { - add(pattern); - } - } - - protected void add(String patterns) { - Assert.notNull(patterns, "Patterns must not be null"); - if (patterns.length() > 0 && !patterns.trim().startsWith(",")) { - this.patterns.add(","); - } - this.patterns.add(patterns); - } - - public String asCommaDelimitedString() { - return StringUtils.collectionToCommaDelimitedString(this.patterns); - } - - public Set asSet() { - return Collections.unmodifiableSet(this.patterns); - } - - } - } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TldSkipPatterns.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TldSkipPatterns.java new file mode 100644 index 0000000000..35c0f2c74d --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TldSkipPatterns.java @@ -0,0 +1,139 @@ +/* + * 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. + * 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.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * TLD Skip Patterns used by Spring Boot. + * + * @author Phillip Webb + */ +final class TldSkipPatterns { + + private static final Set TOMCAT; + + static { + // Same as Tomcat + Set patterns = new LinkedHashSet(); + patterns.add("ant-*.jar"); + patterns.add("aspectj*.jar"); + patterns.add("commons-beanutils*.jar"); + patterns.add("commons-codec*.jar"); + patterns.add("commons-collections*.jar"); + patterns.add("commons-dbcp*.jar"); + patterns.add("commons-digester*.jar"); + patterns.add("commons-fileupload*.jar"); + patterns.add("commons-httpclient*.jar"); + patterns.add("commons-io*.jar"); + patterns.add("commons-lang*.jar"); + patterns.add("commons-logging*.jar"); + patterns.add("commons-math*.jar"); + patterns.add("commons-pool*.jar"); + patterns.add("geronimo-spec-jaxrpc*.jar"); + patterns.add("h2*.jar"); + patterns.add("hamcrest*.jar"); + patterns.add("hibernate*.jar"); + patterns.add("jmx*.jar"); + patterns.add("jmx-tools-*.jar"); + patterns.add("jta*.jar"); + patterns.add("junit-*.jar"); + patterns.add("httpclient*.jar"); + patterns.add("log4j-*.jar"); + patterns.add("mail*.jar"); + patterns.add("org.hamcrest*.jar"); + patterns.add("slf4j*.jar"); + patterns.add("tomcat-embed-core-*.jar"); + patterns.add("tomcat-embed-logging-*.jar"); + patterns.add("tomcat-jdbc-*.jar"); + patterns.add("tomcat-juli-*.jar"); + patterns.add("tools.jar"); + patterns.add("wsdl4j*.jar"); + patterns.add("xercesImpl-*.jar"); + patterns.add("xmlParserAPIs-*.jar"); + patterns.add("xml-apis-*.jar"); + TOMCAT = Collections.unmodifiableSet(patterns); + } + + private static final Set ADDITIONAL; + + static { + // Additional typical for Spring Boot applications + Set patterns = new LinkedHashSet(); + patterns.add("antlr-*.jar"); + patterns.add("aopalliance-*.jar"); + patterns.add("aspectjrt-*.jar"); + patterns.add("aspectjweaver-*.jar"); + patterns.add("classmate-*.jar"); + patterns.add("dom4j-*.jar"); + patterns.add("ecj-*.jar"); + patterns.add("ehcache-core-*.jar"); + patterns.add("hibernate-core-*.jar"); + patterns.add("hibernate-commons-annotations-*.jar"); + patterns.add("hibernate-entitymanager-*.jar"); + patterns.add("hibernate-jpa-2.1-api-*.jar"); + patterns.add("hibernate-validator-*.jar"); + patterns.add("hsqldb-*.jar"); + patterns.add("jackson-annotations-*.jar"); + patterns.add("jackson-core-*.jar"); + patterns.add("jackson-databind-*.jar"); + patterns.add("jandex-*.jar"); + patterns.add("javassist-*.jar"); + patterns.add("jboss-logging-*.jar"); + patterns.add("jboss-transaction-api_*.jar"); + patterns.add("jcl-over-slf4j-*.jar"); + patterns.add("jdom-*.jar"); + patterns.add("jul-to-slf4j-*.jar"); + patterns.add("log4j-over-slf4j-*.jar"); + patterns.add("logback-classic-*.jar"); + patterns.add("logback-core-*.jar"); + patterns.add("rome-*.jar"); + patterns.add("slf4j-api-*.jar"); + patterns.add("spring-aop-*.jar"); + patterns.add("spring-aspects-*.jar"); + patterns.add("spring-beans-*.jar"); + patterns.add("spring-boot-*.jar"); + patterns.add("spring-core-*.jar"); + patterns.add("spring-context-*.jar"); + patterns.add("spring-data-*.jar"); + patterns.add("spring-expression-*.jar"); + patterns.add("spring-jdbc-*.jar,"); + patterns.add("spring-orm-*.jar"); + patterns.add("spring-oxm-*.jar"); + patterns.add("spring-tx-*.jar"); + patterns.add("snakeyaml-*.jar"); + patterns.add("tomcat-embed-el-*.jar"); + patterns.add("validation-api-*.jar"); + patterns.add("xml-apis-*.jar"); + ADDITIONAL = Collections.unmodifiableSet(patterns); + } + + static final Set DEFAULT; + + static { + Set patterns = new LinkedHashSet(); + patterns.addAll(TOMCAT); + patterns.addAll(ADDITIONAL); + DEFAULT = Collections.unmodifiableSet(patterns); + } + + private TldSkipPatterns() { + } + +} 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 103acdbf8a..c0cddd65f1 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 @@ -127,7 +127,7 @@ public class TomcatEmbeddedServletContainerFactory private String protocol = DEFAULT_PROTOCOL; private Set tldSkipPatterns = new LinkedHashSet( - SkipPatternJarScanner.defaultPatterns()); + TldSkipPatterns.DEFAULT); private Charset uriEncoding = DEFAULT_CHARSET; @@ -554,23 +554,12 @@ 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 since 1.5.0 in favor of {@link #setTldSkipPatterns(Collection)} */ @Deprecated public void setTldSkip(String tldSkip) { Assert.notNull(tldSkip, "TldSkip must not be null"); - 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); + setTldSkipPatterns(StringUtils.commaDelimitedListToSet(tldSkip)); } /** @@ -581,13 +570,23 @@ public class TomcatEmbeddedServletContainerFactory return this.tldSkipPatterns; } + /** + * 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(Collection patterns) { + Assert.notNull(patterns, "Patterns must not be null"); + this.tldSkipPatterns = new LinkedHashSet(patterns); + } + /** * 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"); + public void addTldSkipPatterns(String... patterns) { + Assert.notNull(patterns, "Patterns must not be null"); this.tldSkipPatterns.addAll(Arrays.asList(patterns)); } diff --git a/spring-boot/src/main/java/org/springframework/boot/system/ApplicationPidFileWriter.java b/spring-boot/src/main/java/org/springframework/boot/system/ApplicationPidFileWriter.java index 94965061e5..a8ff926b43 100644 --- a/spring-boot/src/main/java/org/springframework/boot/system/ApplicationPidFileWriter.java +++ b/spring-boot/src/main/java/org/springframework/boot/system/ApplicationPidFileWriter.java @@ -30,6 +30,7 @@ import org.springframework.boot.ApplicationPid; import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.context.event.ApplicationPreparedEvent; +import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.context.event.SpringApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.core.Ordered; @@ -49,7 +50,8 @@ import org.springframework.util.Assert; *

* Note: access to the Spring {@link Environment} is only possible when the * {@link #setTriggerEventType(Class) triggerEventType} is set to - * {@link ApplicationEnvironmentPreparedEvent} or {@link ApplicationPreparedEvent}. + * {@link ApplicationEnvironmentPreparedEvent}, {@link ApplicationReadyEvent}, or + * {@link ApplicationPreparedEvent}. * * @author Jakub Kubrynski * @author Dave Syer @@ -231,6 +233,10 @@ public class ApplicationPidFileWriter return ((ApplicationPreparedEvent) event).getApplicationContext() .getEnvironment(); } + if (event instanceof ApplicationReadyEvent) { + return ((ApplicationReadyEvent) event).getApplicationContext() + .getEnvironment(); + } return null; } diff --git a/spring-boot/src/test/java/org/springframework/boot/system/ApplicationPidFileWriterTests.java b/spring-boot/src/test/java/org/springframework/boot/system/ApplicationPidFileWriterTests.java index 1e4d0af805..5b6ada3da3 100644 --- a/spring-boot/src/test/java/org/springframework/boot/system/ApplicationPidFileWriterTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/system/ApplicationPidFileWriterTests.java @@ -29,6 +29,7 @@ import org.junit.rules.TemporaryFolder; import org.springframework.boot.SpringApplication; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.context.event.ApplicationPreparedEvent; +import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.boot.context.event.SpringApplicationEvent; import org.springframework.context.ConfigurableApplicationContext; @@ -99,7 +100,7 @@ public class ApplicationPidFileWriterTests { } @Test - public void differentEventTypes() throws Exception { + public void tryEnvironmentPreparedEvent() throws Exception { File file = this.temporaryFolder.newFile(); SpringApplicationEvent event = createEnvironmentPreparedEvent("spring.pid.file", file.getAbsolutePath()); @@ -111,6 +112,19 @@ public class ApplicationPidFileWriterTests { assertThat(FileCopyUtils.copyToString(new FileReader(file))).isNotEmpty(); } + @Test + public void tryReadyEvent() throws Exception { + File file = this.temporaryFolder.newFile(); + SpringApplicationEvent event = createReadyEvent("spring.pid.file", + file.getAbsolutePath()); + ApplicationPidFileWriter listener = new ApplicationPidFileWriter(); + listener.onApplicationEvent(event); + assertThat(FileCopyUtils.copyToString(new FileReader(file))).isEmpty(); + listener.setTriggerEventType(ApplicationReadyEvent.class); + listener.onApplicationEvent(event); + assertThat(FileCopyUtils.copyToString(new FileReader(file))).isNotEmpty(); + } + @Test public void withNoEnvironment() throws Exception { File file = this.temporaryFolder.newFile(); @@ -170,6 +184,15 @@ public class ApplicationPidFileWriterTests { context); } + private SpringApplicationEvent createReadyEvent(String propName, String propValue) { + ConfigurableEnvironment environment = createEnvironment(propName, propValue); + ConfigurableApplicationContext context = mock( + ConfigurableApplicationContext.class); + given(context.getEnvironment()).willReturn(environment); + return new ApplicationReadyEvent(new SpringApplication(), new String[] {}, + context); + } + private ConfigurableEnvironment createEnvironment(String propName, String propValue) { MockPropertySource propertySource = mockPropertySource(propName, propValue); ConfigurableEnvironment environment = new StandardEnvironment();