From 474aed054162b06650f4c4af4e9f47949dfa0f9a Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 7 Mar 2016 15:50:14 +0000 Subject: [PATCH] Make context available when handling refresh failure Previously, if SpringApplication.run failed due to the refresh of the application context throwing an exception, the application context would not be available during run failure handling. This meant that null was passed to any SpringApplicationRunListeners, however the javadoc for the finished method does not indicate that it is possible for null to be passed in. In addition to the possibility of a NullPointerException, a side-effect of this behaviour was that the auto-configuration report was not produced when refresh fails, making it useless as a tool for diagnosing refresh failures. This commit updates SpringApplication to take a reference to the application context as early as possible and, crucially, before it has been refreshed. This means that refresh no longer has to succeed for the context to be passed to any SpringApplicationRunListeners and that they will now receive an inactive context, rather than a null context in the event of a refresh failure. Closes gh-5325 --- .../boot/SpringApplication.java | 29 +++++++++++-------- .../boot/SpringApplicationRunListener.java | 5 ++-- 2 files changed, 20 insertions(+), 14 deletions(-) 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 e07bd354e5..4343af79c3 100644 --- a/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java +++ b/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java @@ -305,7 +305,14 @@ public class SpringApplication { try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); - context = createAndRefreshContext(listeners, applicationArguments); + ConfigurableEnvironment environment = prepareEnvironment(listeners, + applicationArguments); + if (this.bannerMode != Banner.Mode.OFF) { + printBanner(environment); + } + context = createApplicationContext(); + prepareContext(context, environment, listeners, applicationArguments); + refreshContext(context); afterRefresh(context, applicationArguments); listeners.finished(context, null); stopWatch.stop(); @@ -321,10 +328,9 @@ public class SpringApplication { } } - private ConfigurableApplicationContext createAndRefreshContext( + private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { - ConfigurableApplicationContext context; // Create and configure the environment ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); @@ -332,13 +338,12 @@ public class SpringApplication { if (isWebEnvironment(environment) && !this.webEnvironment) { environment = convertToStandardEnvironment(environment); } + return environment; + } - if (this.bannerMode != Banner.Mode.OFF) { - printBanner(environment); - } - - // Create, load, refresh and run the ApplicationContext - context = createApplicationContext(); + private void prepareContext(ConfigurableApplicationContext context, + ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, + ApplicationArguments applicationArguments) { context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); @@ -357,8 +362,9 @@ public class SpringApplication { Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[sources.size()])); listeners.contextLoaded(context); + } - // Refresh the context + private void refreshContext(ConfigurableApplicationContext context) { refresh(context); if (this.registerShutdownHook) { try { @@ -368,7 +374,6 @@ public class SpringApplication { // Not allowed in some environments. } } - return context; } private void configureHeadlessProperty() { @@ -886,7 +891,7 @@ public class SpringApplication { private int getExitCodeFromMappedException(ConfigurableApplicationContext context, Throwable exception) { - if (context == null) { + if (context == null || !context.isActive()) { return 0; } ExitCodeGenerators generators = new ExitCodeGenerators(); diff --git a/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListener.java b/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListener.java index 0f94ff6120..6369d2e9b3 100644 --- a/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListener.java +++ b/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListener.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. @@ -62,7 +62,8 @@ public interface SpringApplicationRunListener { /** * Called immediately before the run method finishes. - * @param context the application context + * @param context the application context or null if a failure occurred before the + * context was created * @param exception any run exception or null if run completed successfully. */ void finished(ConfigurableApplicationContext context, Throwable exception);