From ac8ff3750ccf8ed3de614676cd6bc049e8719cde Mon Sep 17 00:00:00 2001 From: John Blum Date: Tue, 10 Mar 2015 15:54:04 -0700 Subject: [PATCH] SGF-382 - Add logging to the SpringContextBootstrappingInitializer init method to capture any Spring context/GemFire errors on startup. --- ...SpringContextBootstrappingInitializer.java | 59 ++++++++---- ...ngContextBootstrappingInitializerTest.java | 91 ++++++++++++++----- 2 files changed, 111 insertions(+), 39 deletions(-) diff --git a/src/main/java/org/springframework/data/gemfire/support/SpringContextBootstrappingInitializer.java b/src/main/java/org/springframework/data/gemfire/support/SpringContextBootstrappingInitializer.java index c39ab6c1..0cb2b488 100644 --- a/src/main/java/org/springframework/data/gemfire/support/SpringContextBootstrappingInitializer.java +++ b/src/main/java/org/springframework/data/gemfire/support/SpringContextBootstrappingInitializer.java @@ -19,7 +19,10 @@ package org.springframework.data.gemfire.support; import java.util.Arrays; import java.util.Properties; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextException; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -68,6 +71,8 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic /* package-private */ static volatile ContextRefreshedEvent contextRefreshedEvent; + protected final Log logger = initLogger(); + /** * Gets a reference to the Spring ApplicationContext constructed, configured and initialized inside the GemFire * Server-based JVM process. @@ -147,6 +152,17 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic return listener; } + /** + * Initialization method for the logger used to log important messages from this initializer. + * + * @return a Apache Commons Log used to log messages from this initializer + * @see org.apache.commons.logging.LogFactory#getLog(Class) + * @see org.apache.commons.logging.Log + */ + protected Log initLogger() { + return LogFactory.getLog(getClass()); + } + /** * Creates (constructs and configures) a ConfigurableApplicationContext instance based on the specified locations * of the context configuration meta-data files. The created ConfigurableApplicationContext is not automatically @@ -257,6 +273,8 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic * @param parameters a Properties object containing the configuration parameters and settings defined in the * GemFire cache.xml <initializer> block for the declared SpringContextBootstrappingInitializer * GemFire Declarable object. + * @throws org.springframework.context.ApplicationContextException if the Spring ApplicationContext could not be + * successfully created, configured and initialized. * @see #createApplicationContext(String[], String[]) * @see #initApplicationContext(org.springframework.context.ConfigurableApplicationContext) * @see #refreshApplicationContext(org.springframework.context.ConfigurableApplicationContext) @@ -264,31 +282,38 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic */ @Override public void init(final Properties parameters) { - synchronized (SpringContextBootstrappingInitializer.class) { - if (applicationContext == null || !applicationContext.isActive()) { - String basePackages = parameters.getProperty(BASE_PACKAGES_PARAMETER); - String contextConfigLocations = parameters.getProperty(CONTEXT_CONFIG_LOCATIONS_PARAMETER); + try { + synchronized (SpringContextBootstrappingInitializer.class) { + if (applicationContext == null || !applicationContext.isActive()) { + String basePackages = parameters.getProperty(BASE_PACKAGES_PARAMETER); + String contextConfigLocations = parameters.getProperty(CONTEXT_CONFIG_LOCATIONS_PARAMETER); - Assert.isTrue(StringUtils.hasText(basePackages) || StringUtils.hasText(contextConfigLocations), - "Either 'basePackages' or the 'contextConfigLocations' parameter must be specified."); + Assert.isTrue(StringUtils.hasText(basePackages) || StringUtils.hasText(contextConfigLocations), + "Either 'basePackages' or the 'contextConfigLocations' parameter must be specified."); - String[] basePackagesArray = StringUtils.delimitedListToStringArray(basePackages, - COMMA_DELIMITER, CHARS_TO_DELETE); + String[] basePackagesArray = StringUtils.delimitedListToStringArray(basePackages, + COMMA_DELIMITER, CHARS_TO_DELETE); - String[] contextConfigLocationsArray = StringUtils.delimitedListToStringArray(contextConfigLocations, - COMMA_DELIMITER, CHARS_TO_DELETE); + String[] contextConfigLocationsArray = StringUtils.delimitedListToStringArray(contextConfigLocations, + COMMA_DELIMITER, CHARS_TO_DELETE); - ConfigurableApplicationContext localApplicationContext = refreshApplicationContext( - initApplicationContext(createApplicationContext( basePackagesArray, contextConfigLocationsArray))); + ConfigurableApplicationContext localApplicationContext = refreshApplicationContext( + initApplicationContext(createApplicationContext(basePackagesArray, contextConfigLocationsArray))); - Assert.state(localApplicationContext.isRunning(), String.format( - "The Spring ApplicationContext (%1$s) failed to be properly initialized with the context config files (%2$s) or base packages (%3$s)!", - nullSafeGetApplicationContextId(applicationContext), Arrays.toString(contextConfigLocationsArray), - Arrays.toString(basePackagesArray))); + Assert.state(localApplicationContext.isRunning(), String.format( + "The Spring ApplicationContext (%1$s) failed to be properly initialized with the context config files (%2$s) or base packages (%3$s)!", + nullSafeGetApplicationContextId(localApplicationContext), Arrays.toString(contextConfigLocationsArray), + Arrays.toString(basePackagesArray))); - applicationContext = localApplicationContext; + applicationContext = localApplicationContext; + } } } + catch (Throwable cause) { + String message = "Failed to bootstrap the Spring ApplicationContext!"; + logger.error(message, cause); + throw new ApplicationContextException(message, cause); + } } /** diff --git a/src/test/java/org/springframework/data/gemfire/support/SpringContextBootstrappingInitializerTest.java b/src/test/java/org/springframework/data/gemfire/support/SpringContextBootstrappingInitializerTest.java index f17c7765..a69f9d43 100644 --- a/src/test/java/org/springframework/data/gemfire/support/SpringContextBootstrappingInitializerTest.java +++ b/src/test/java/org/springframework/data/gemfire/support/SpringContextBootstrappingInitializerTest.java @@ -20,6 +20,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; import static org.mockito.Matchers.same; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -29,10 +32,12 @@ import static org.mockito.Mockito.when; import java.util.Properties; +import org.apache.commons.logging.Log; import org.junit.After; import org.junit.Assert; import org.junit.Test; import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextException; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.event.ContextClosedEvent; @@ -274,46 +279,88 @@ public class SpringContextBootstrappingInitializerTest { try { new SpringContextBootstrappingInitializer().init(createParameters(createParameters( - SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER, ""), - SpringContextBootstrappingInitializer.BASE_PACKAGES_PARAMETER, "")); + SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER, ""), + SpringContextBootstrappingInitializer.BASE_PACKAGES_PARAMETER, "")); } - catch (IllegalArgumentException expected) { + catch (ApplicationContextException expected) { + assertTrue(expected.getMessage().contains("Failed to bootstrap the Spring ApplicationContext!")); + assertTrue(expected.getCause() instanceof IllegalArgumentException); assertEquals("Either 'basePackages' or the 'contextConfigLocations' parameter must be specified.", - expected.getMessage()); - throw expected; + expected.getCause().getMessage()); + throw (IllegalArgumentException) expected.getCause(); } } @Test(expected = IllegalStateException.class) public void testInitWhenApplicationContextIsNotRunning() { - assertNull(SpringContextBootstrappingInitializer.applicationContext); + try { + assertNull(SpringContextBootstrappingInitializer.applicationContext); - final ConfigurableApplicationContext mockApplicationContext = mock(ConfigurableApplicationContext.class, - "testInitWhenApplicationContextIsNotRunning"); + final ConfigurableApplicationContext mockApplicationContext = mock(ConfigurableApplicationContext.class, + "testInitWhenApplicationContextIsNotRunning"); - when(mockApplicationContext.getId()).thenReturn("testInitWhenApplicationContextIsNotRunning"); - when(mockApplicationContext.isRunning()).thenReturn(false); + when(mockApplicationContext.getId()).thenReturn("testInitWhenApplicationContextIsNotRunning"); + when(mockApplicationContext.isRunning()).thenReturn(false); + + SpringContextBootstrappingInitializer initializer = new SpringContextBootstrappingInitializer() { + @Override + protected ConfigurableApplicationContext createApplicationContext(final String[] basePackages, + final String[] configLocations) { + return mockApplicationContext; + } + }; + + initializer.init(createParameters(SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER, + "/path/to/spring/application/context.xml")); + + verify(mockApplicationContext, times(1)).addApplicationListener(same(initializer)); + verify(mockApplicationContext, times(1)).registerShutdownHook(); + verify(mockApplicationContext, times(1)).refresh(); + + SpringContextBootstrappingInitializer.getApplicationContext(); + } + catch (ApplicationContextException expected) { + assertTrue(expected.getMessage().contains("Failed to bootstrap the Spring ApplicationContext!")); + assertTrue(expected.getCause() instanceof IllegalStateException); + assertEquals("The Spring ApplicationContext (testInitWhenApplicationContextIsNotRunning) failed to be properly initialized with the context config files ([/path/to/spring/application/context.xml]) or base packages ([])!", + expected.getCause().getMessage()); + throw (IllegalStateException) expected.getCause(); + } + } + + @Test(expected = IllegalStateException.class) + public void testInitLogsErrors() throws Throwable { + final Log mockLogger = mock(Log.class, "testInitLogsErrors.MockLog"); SpringContextBootstrappingInitializer initializer = new SpringContextBootstrappingInitializer() { - @Override - protected ConfigurableApplicationContext createApplicationContext(final String[] basePackages, + @Override protected Log initLogger() { + return mockLogger; + } + + @Override protected ConfigurableApplicationContext createApplicationContext(final String[] basePackages, final String[] configLocations) { - return mockApplicationContext; + throw new IllegalStateException("TEST"); } }; - initializer.init(createParameters(SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER, - "/path/to/spring/application/context.xml")); - - verify(mockApplicationContext, times(1)).addApplicationListener(same(initializer)); - verify(mockApplicationContext, times(1)).registerShutdownHook(); - verify(mockApplicationContext, times(1)).refresh(); - - SpringContextBootstrappingInitializer.getApplicationContext(); + try { + initializer.init(createParameters(SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER, + "classpath/to/spring/application/context.xml")); + } + catch (ApplicationContextException expected) { + assertTrue(expected.getMessage().contains("Failed to bootstrap the Spring ApplicationContext!")); + assertTrue(expected.getCause() instanceof IllegalStateException); + assertEquals("TEST", expected.getCause().getMessage()); + throw expected.getCause(); + } + finally { + verify(mockLogger, times(1)).error(eq("Failed to bootstrap the Spring ApplicationContext!"), + any(RuntimeException.class)); + } } protected static void assertNotifiedWithEvent(final TestApplicationListener listener, final ContextRefreshedEvent expectedEvent) { - Assert.assertTrue(listener.isNotified()); + assertTrue(listener.isNotified()); Assert.assertSame(expectedEvent, listener.getActualEvent()); }