Fix package tangle

Fix package tangle between the `org.springframework.boot` and the
`event` sub-package. A new `SpringApplicationRunParticipant` abstraction
has been introduced, with the `event` package solely responsible for
handling event multi-casting.
This commit is contained in:
Phillip Webb 2014-02-03 10:29:17 -08:00
parent c0a5c763b6
commit 042e512d3d
4 changed files with 248 additions and 128 deletions

View File

@ -16,6 +16,7 @@
package org.springframework.boot;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -31,28 +32,18 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.boot.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.event.ApplicationFailedEvent;
import org.springframework.boot.event.ApplicationPreparedEvent;
import org.springframework.boot.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.GenericTypeResolver;
@ -72,6 +63,7 @@ import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;
import org.springframework.web.context.ConfigurableWebApplicationContext;
@ -243,36 +235,6 @@ public class SpringApplication {
return true;
}
/**
* Returns objects loaded via the {@link SpringFactoriesLoader}.
*/
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
ClassLoader classLoader = SpringApplication.class.getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = new ArrayList<T>(names.size());
// Create instances from the names
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
@SuppressWarnings("unchecked")
T instance = (T) instanceClass.newInstance();
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : "
+ name, ex);
}
}
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
@ -302,23 +264,19 @@ public class SpringApplication {
System.setProperty("java.awt.headless", Boolean.toString(this.headless));
ApplicationEventMulticaster multicaster = createApplicationEventMulticaster();
try {
// Allow logging and stuff to initialize very early
multicaster.multicastEvent(new ApplicationStartedEvent(this, args));
Collection<SpringApplicationRunParticipant> participants = createRunParticipants(args);
for (SpringApplicationRunParticipant participant : participants) {
participant.started();
}
try {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
addPropertySources(environment, args);
setupProfiles(environment);
// Notify listeners of the environment creation
multicaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this,
args, environment));
// Load the sources (might have changed when environment was applied)
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
for (SpringApplicationRunParticipant participant : participants) {
participant.environmentPrepared(environment);
}
if (this.showBanner) {
printBanner();
@ -326,48 +284,93 @@ public class SpringApplication {
// Create, load, refresh and run the ApplicationContext
context = createApplicationContext();
registerApplicationEventMulticaster(context, multicaster);
context.registerShutdownHook();
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
for (SpringApplicationRunParticipant participant : participants) {
participant.contextPrepared(context);
}
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
}
// Load the sources
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
for (SpringApplicationRunParticipant participant : participants) {
participant.contextLoaded(context);
}
// Notify listeners of intention to refresh
multicaster.multicastEvent(new ApplicationPreparedEvent(this, args, context));
// Refresh the context
refresh(context);
afterRefresh(context, args);
for (SpringApplicationRunParticipant participant : participants) {
participant.finished(context, null);
}
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(
getApplicationLog(), stopWatch);
}
afterRefresh(context, args);
return context;
}
catch (RuntimeException ex) {
handleFailure(context, multicaster, ex, args);
throw ex;
catch (Exception ex) {
for (SpringApplicationRunParticipant participant : participants) {
finishWithException(participant, context, ex);
}
if (context != null) {
context.close();
}
ReflectionUtils.rethrowRuntimeException(ex);
return context;
}
catch (Error ex) {
handleFailure(context, multicaster, ex, args);
throw ex;
finally {
}
}
private ApplicationEventMulticaster createApplicationEventMulticaster() {
ApplicationEventMulticaster multicaster = new SpringApplicationEventMulticaster();
for (ApplicationListener<?> listener : getListeners()) {
multicaster.addApplicationListener(listener);
private Collection<SpringApplicationRunParticipant> createRunParticipants(
String[] args) {
List<SpringApplicationRunParticipant> participants = new ArrayList<SpringApplicationRunParticipant>();
participants.addAll(getSpringFactoriesInstances(
SpringApplicationRunParticipant.class, new Class<?>[] {
SpringApplication.class, String[].class }, this, args));
return participants;
}
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
@SuppressWarnings("unchecked")
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = new ArrayList<T>(names.size());
// Create instances from the names
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getConstructor(parameterTypes);
T instance = (T) constructor.newInstance(args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : "
+ name, ex);
}
}
return multicaster;
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
private ConfigurableEnvironment getOrCreateEnvironment() {
@ -450,17 +453,6 @@ public class SpringApplication {
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
private void registerApplicationEventMulticaster(
ConfigurableApplicationContext context,
ApplicationEventMulticaster multicaster) {
context.getBeanFactory().registerSingleton(
AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME,
multicaster);
if (multicaster instanceof BeanFactoryAware) {
((BeanFactoryAware) multicaster).setBeanFactory(context.getBeanFactory());
}
}
/**
* Apply any relevant post processing the {@link ApplicationContext}. Subclasses can
* apply additional processing as required.
@ -624,29 +616,23 @@ public class SpringApplication {
((AbstractApplicationContext) applicationContext).refresh();
}
private void afterRefresh(ConfigurableApplicationContext context, String[] args) {
protected void afterRefresh(ConfigurableApplicationContext context, String[] args) {
runCommandLineRunners(context, args);
}
protected void handleFailure(ConfigurableApplicationContext context,
ApplicationEventMulticaster multicaster, Throwable exception, String... args) {
private void finishWithException(SpringApplicationRunParticipant participant,
ConfigurableApplicationContext context, Exception exception) {
try {
multicaster.multicastEvent(new ApplicationFailedEvent(this, args, context,
exception));
participant.finished(context, exception);
}
catch (Exception ex) {
// We don't want to fail here and mask the original exception
if (this.log.isDebugEnabled()) {
this.log.error("Error handling failed", ex);
}
else {
this.log.warn("Error handling failed (" + ex.getMessage() == null ? "no error message"
: ex.getMessage() + ")");
}
}
finally {
if (context != null) {
context.close();
String message = ex.getMessage();
message = (message == null ? "no error message" : message);
this.log.warn("Error handling failed (" + message + ")");
}
}
}
@ -974,38 +960,4 @@ public class SpringApplication {
return new LinkedHashSet<E>(list);
}
/**
* {@link ApplicationEventMulticaster} and {@link ApplicationEventPublisher} for
* {@link SpringApplication} events.
*/
private static class SpringApplicationEventMulticaster extends
SimpleApplicationEventMulticaster implements ApplicationEventPublisher {
@Override
public void publishEvent(ApplicationEvent event) {
multicastEvent(event);
}
@Override
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event) {
List<ApplicationListener<?>> listeners = new ArrayList<ApplicationListener<?>>();
listeners.addAll(super.getApplicationListeners(event));
if (event instanceof ApplicationFailedEvent) {
Collections.reverse(listeners);
}
return listeners;
}
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
super.addApplicationListener(listener);
if (listener instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) listener)
.setApplicationEventPublisher(this);
}
}
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 2012-2014 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;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.support.SpringFactoriesLoader;
/**
* Strategy class to allow dynamic participation when the {@link SpringApplication}
* {@code run} method is called. Participants are loaded via the
* {@link SpringFactoriesLoader} and should declare a public constructor that accepts a
* {@link SpringApplication} instance and a {@code String[]} of arguments. A new
* {@link SpringApplicationRunParticipant} instance will be created for each run.
*
* @author Phillip Webb
*/
public interface SpringApplicationRunParticipant {
/**
* Called immediately when the run method has first started. Can be used for very
* early initialization.
*/
void started();
/**
* Called once the environment has been prepared, but before the
* {@link ApplicationContext} has been created.
* @param environment the environment
*/
void environmentPrepared(ConfigurableEnvironment environment);
/**
* Called once the {@link ApplicationContext} has been created and prepared, but
* before sources have been loaded.
* @param context the application context
*/
void contextPrepared(ConfigurableApplicationContext context);
/**
* Called once the application context has been loaded but before it has been
* refreshed.
* @param context the application context
*/
void contextLoaded(ConfigurableApplicationContext context);
/**
* Called immediately before the run method finishes.
* @param context the application context
* @param exception any run exception or null if run completed successfully.
*/
void finished(ConfigurableApplicationContext context, Throwable exception);
}

View File

@ -0,0 +1,95 @@
/*
* Copyright 2012-2014 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.event;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunParticipant;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
/**
* {@link SpringApplicationRunParticipant} to publish {@link SpringApplicationEvent}s.
*
* @author Phillip Webb
*/
public class EventPublishingRunParticipant implements SpringApplicationRunParticipant {
private final ApplicationEventMulticaster multicaster;
private SpringApplication application;
private String[] args;
public EventPublishingRunParticipant(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.multicaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.multicaster.addApplicationListener(listener);
}
}
@Override
public void started() {
publishEvent(new ApplicationStartedEvent(this.application, this.args));
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
publishEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args,
environment));
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
registerApplicationEventMulticaster(context);
}
private void registerApplicationEventMulticaster(
ConfigurableApplicationContext context) {
context.getBeanFactory().registerSingleton(
AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME,
this.multicaster);
if (this.multicaster instanceof BeanFactoryAware) {
((BeanFactoryAware) this.multicaster)
.setBeanFactory(context.getBeanFactory());
}
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
publishEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}
@Override
public void finished(ConfigurableApplicationContext context, Throwable exception) {
if (exception != null) {
publishEvent(new ApplicationFailedEvent(this.application, this.args, context,
exception));
}
}
private void publishEvent(SpringApplicationEvent event) {
this.multicaster.multicastEvent(event);
}
}

View File

@ -1,3 +1,7 @@
# Run Participants
org.springframework.boot.SpringApplicationRunParticipant=\
org.springframework.boot.event.EventPublishingRunParticipant
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.initializer.ContextIdApplicationContextInitializer,\