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 67d1ccbd96..5191e41296 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 @@ -27,6 +27,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.boot.ApplicationInfo; import org.springframework.boot.autoconfigure.AutoConfigurationPackages; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -105,6 +106,9 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware { jpaVendorAdapter, this.properties.getProperties(), persistenceUnitManagerProvider.getIfAvailable()); builder.setCallback(getVendorCallback()); + if (this.beanFactory.containsBean("springApplicationInfo")) { + builder.setApplicationInfo(this.beanFactory.getBean(ApplicationInfo.class)); + } return builder; } diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index d810cdc8da..1abe153083 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -94,8 +94,8 @@ You can also use the `spring.main.banner-mode` property to determine if the bann to be printed on `System.out` (`console`), using the configured logger (`log`) or not at all (`off`). -The printed banner will be registered as a singleton bean under the name -`springBootBanner`. +The printed banner will be available via an `ApplicationInfo` registered as a singleton +bean under the name `springApplicationInfo`. [NOTE] ==== diff --git a/spring-boot-starters/spring-boot-starter-remote-shell/src/main/resources/commands/crash/login.groovy b/spring-boot-starters/spring-boot-starter-remote-shell/src/main/resources/commands/crash/login.groovy index 465bd4a012..d260b1563d 100644 --- a/spring-boot-starters/spring-boot-starter-remote-shell/src/main/resources/commands/crash/login.groovy +++ b/spring-boot-starters/spring-boot-starter-remote-shell/src/main/resources/commands/crash/login.groovy @@ -11,9 +11,9 @@ welcome = { -> // Try to print using the banner interface if (beanFactory != null) { try { - def banner = beanFactory.getBean("springBootBanner") + def appInfo = beanFactory.getBean("springApplicationInfo") def out = new java.io.ByteArrayOutputStream() - banner.printBanner(environment, null, new java.io.PrintStream(out)) + appInfo.banner.printBanner(environment, null, new java.io.PrintStream(out)) return out.toString() } catch (Exception ex) { // Ignore diff --git a/spring-boot/src/main/java/org/springframework/boot/ApplicationInfo.java b/spring-boot/src/main/java/org/springframework/boot/ApplicationInfo.java new file mode 100644 index 0000000000..08b38d4f43 --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/ApplicationInfo.java @@ -0,0 +1,64 @@ +/* + * 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; + +/** + * Provide application-related information such as the {@link ApplicationArguments} and + * the {@link Banner}. + * + * @author Stephane Nicoll + * @since 1.4.1 + */ +public final class ApplicationInfo { + + private final Class mainApplicationClass; + + private final ApplicationArguments applicationArguments; + + private final Banner banner; + + protected ApplicationInfo(SpringApplication application, ApplicationArguments applicationArguments, Banner banner) { + this.mainApplicationClass = application.getMainApplicationClass(); + this.applicationArguments = applicationArguments; + this.banner = banner; + } + + /** + * Returns the main application class that has been deduced or explicitly configured. + * @return the main application class or {@code null} + */ + public Class getMainApplicationClass() { + return this.mainApplicationClass; + } + + /** + * Returns the {@link ApplicationArguments} used to start this instance. + * @return the application arguments + */ + public ApplicationArguments getApplicationArguments() { + return this.applicationArguments; + } + + /** + * Returns the {@link Banner} used by this instance. + * @return the banner or {@code null} + */ + public Banner getBanner() { + return this.banner; + } + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java b/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java index d38392cdbe..65d1190299 100644 --- a/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java +++ b/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java @@ -359,6 +359,10 @@ public class SpringApplication { if (printedBanner != null) { context.getBeanFactory().registerSingleton("springBootBanner", printedBanner); } + ApplicationInfo applicationInfo = new ApplicationInfo(this, applicationArguments, + printedBanner); + context.getBeanFactory().registerSingleton("springApplicationInfo", + applicationInfo); // Load the sources Set sources = getSources(); diff --git a/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityManagerFactoryBuilder.java b/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityManagerFactoryBuilder.java index 1542ead327..e8b1f2ce5c 100644 --- a/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityManagerFactoryBuilder.java +++ b/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityManagerFactoryBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 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. @@ -16,6 +16,7 @@ package org.springframework.boot.orm.jpa; +import java.net.URL; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -24,10 +25,15 @@ import java.util.Set; import javax.sql.DataSource; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.boot.ApplicationInfo; import org.springframework.orm.jpa.JpaVendorAdapter; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager; import org.springframework.util.ClassUtils; +import org.springframework.util.ResourceUtils; /** * Convenient builder for JPA EntityManagerFactory instances. Collects common @@ -39,10 +45,13 @@ import org.springframework.util.ClassUtils; * * @author Dave Syer * @author Phillip Webb + * @author Stephane Nicoll * @since 1.3.0 */ public class EntityManagerFactoryBuilder { + private static final Log logger = LogFactory.getLog(EntityManagerFactoryBuilder.class); + private JpaVendorAdapter jpaVendorAdapter; private PersistenceUnitManager persistenceUnitManager; @@ -51,6 +60,8 @@ public class EntityManagerFactoryBuilder { private EntityManagerFactoryBeanCallback callback; + private Class applicationClass; + /** * Create a new instance passing in the common pieces that will be shared if multiple * EntityManagerFactory instances are created. @@ -78,6 +89,34 @@ public class EntityManagerFactoryBuilder { this.callback = callback; } + /** + * An optional {@link ApplicationInfo} used to further tune the entity manager. + * @param applicationInfo the application info + */ + public void setApplicationInfo(ApplicationInfo applicationInfo) { + this.applicationClass = applicationInfo.getMainApplicationClass(); + } + + /** + * Determine a persistence unit root location to use if no {@code persistence.xml} or + * {@code orm.xml} are present in the project. + * @return the persistence unit root location or {@code null} + */ + protected String determinePersistenceUnitRootLocation() { + if (this.applicationClass != null) { + try { + URL mainLocation = this.applicationClass.getProtectionDomain(). + getCodeSource().getLocation(); + return ResourceUtils.extractJarFileURL(mainLocation).toString(); + } + catch (Exception ex) { + logger.info("Could not determine persistence unit root location: " + ex); + } + } + return null; + } + + /** * A fluent builder for a LocalContainerEntityManagerFactoryBean. */ @@ -181,6 +220,10 @@ public class EntityManagerFactoryBuilder { entityManagerFactoryBean.getJpaPropertyMap() .putAll(EntityManagerFactoryBuilder.this.jpaProperties); entityManagerFactoryBean.getJpaPropertyMap().putAll(this.properties); + String rootLocation = determinePersistenceUnitRootLocation(); + if (rootLocation != null) { + entityManagerFactoryBean.setPersistenceUnitRootLocation(rootLocation); + } if (EntityManagerFactoryBuilder.this.callback != null) { EntityManagerFactoryBuilder.this.callback .execute(entityManagerFactoryBean); diff --git a/spring-boot/src/test/java/org/springframework/boot/BannerTests.java b/spring-boot/src/test/java/org/springframework/boot/BannerTests.java index b8efaa65b5..b79b678ebd 100644 --- a/spring-boot/src/test/java/org/springframework/boot/BannerTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/BannerTests.java @@ -100,6 +100,9 @@ public class BannerTests { application.setWebEnvironment(false); this.context = application.run(); assertThat(this.context.containsBean("springBootBanner")).isTrue(); + assertThat(this.context.containsBean("springApplicationInfo")).isTrue(); + assertThat(this.context.getBean( + "springApplicationInfo", ApplicationInfo.class).getBanner()).isNotNull(); } @Test @@ -109,7 +112,9 @@ public class BannerTests { Banner banner = mock(Banner.class); application.setBanner(banner); this.context = application.run(); - Banner printedBanner = (Banner) this.context.getBean("springBootBanner"); + ApplicationInfo applicationInfo = this.context.getBean("springApplicationInfo", + ApplicationInfo.class); + Banner printedBanner = applicationInfo.getBanner(); assertThat(ReflectionTestUtils.getField(printedBanner, "banner")) .isEqualTo(banner); verify(banner).printBanner(any(Environment.class), @@ -127,6 +132,8 @@ public class BannerTests { application.setWebEnvironment(false); this.context = application.run(); assertThat(this.context.containsBean("springBootBanner")).isFalse(); + assertThat(this.context.getBean("springApplicationInfo", ApplicationInfo.class). + getBanner()).isNull(); } @Test @@ -143,6 +150,8 @@ public class BannerTests { this.context = application.run(); assertThat(this.out.toString()).contains("I printed a deprecated banner"); assertThat(this.context.containsBean("springBootBanner")).isFalse(); + assertThat(this.context.getBean("springApplicationInfo", ApplicationInfo.class). + getBanner()).isNull(); } static class DummyBanner implements Banner { diff --git a/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java b/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java index 10aafc820e..5c1cd44b3f 100644 --- a/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java @@ -778,6 +778,21 @@ public class SpringApplicationTests { assertThat(System.getProperty("java.awt.headless")).isEqualTo("false"); } + @Test + public void getApplicationInfo() { + TestSpringApplication application = new TestSpringApplication( + ExampleConfig.class); + application.setWebEnvironment(false); + this.context = application.run("foo"); + ApplicationInfo applicationInfo = this.context.getBean(ApplicationInfo.class); + assertThat(application.getMainApplicationClass()).isEqualTo(application + .getMainApplicationClass()); + assertThat(applicationInfo.getApplicationArguments()).isNotNull(); + assertThat(applicationInfo.getApplicationArguments().getNonOptionArgs()) + .containsExactly("foo"); + assertThat(applicationInfo.getBanner()).isNotNull(); + } + @Test public void getApplicationArgumentsBean() throws Exception { TestSpringApplication application = new TestSpringApplication(