/*******************************************************************************
* Copyright © 2012-2015 eBay Software Foundation
* This program is dual licensed under the MIT and Apache 2.0 licenses.
* Please see LICENSE for more information.
*******************************************************************************/
package com.ebay.jetstream.spring.context.support;
import java.io.IOException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.ApplicationListener;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.AbstractXmlApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import com.ebay.jetstream.spring.beans.factory.support.UpdateableListableBeanFactory;
/**
* Base class for {@link org.springframework.context.ApplicationContext} implementations which are supposed to support
* multiple refreshs, creating a new internal bean factory instance every time. Typically (but not necessarily), such a
* context will be driven by a set of config locations to load bean definitions from.
*
* <p>
* The only method to be implemented by subclasses is {@link #loadBeanDefinitions}, which gets invoked on each refresh.
* A concrete implementation is supposed to load bean definitions into the given
* {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}, typically delegating to one or more
* specific bean definition readers.
*
* <p>
* <b>Note that there is a similar base class for WebApplicationContexts.</b>
* {@link org.springframework.web.context.support.AbstractRefreshableWebApplicationContext} provides the same
* subclassing strategy, but additionally pre-implements all context functionality for web environments. There is also a
* pre-defined way to receive config locations for a web context.
*
* <p>
* Concrete standalone subclasses of this base class, reading in a specific bean definition format, are
* {@link ClassPathXmlApplicationContext} and {@link FileSystemXmlApplicationContext}, which both derive from the common
* {@link AbstractXmlApplicationContext} base class.
*
* @author Juergen Hoeller
* @since 1.1.3
* @see #loadBeanDefinitions
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory
* @see org.springframework.web.context.support.AbstractRefreshableWebApplicationContext
* @see AbstractXmlApplicationContext
* @see ClassPathXmlApplicationContext
* @see FileSystemXmlApplicationContext
*
* JETSTREAM 06-30-2008: renamed from AbstractRefreshableApplicationContext to Custom..., renamed member variables to
* eliminate warnings, and added locking support for re-creating individual beans in the container. 08-21-2008:
* renamed again to AbstractUpdateableApplicationContext and added support for BeanChangeAwareness via an extension
* to DefaultListableBeanFactory
*/
public abstract class AbstractUpdateableApplicationContext extends AbstractApplicationContext {
private Boolean m_allowBeanDefinitionOverriding;
private Boolean m_allowCircularReferences;
/** Bean factory for this context */
private UpdateableListableBeanFactory m_beanFactory;
/** Synchronization monitor for the internal BeanFactory */
private final Object m_beanFactoryMonitor = new Object();
/**
* Create a new AbstractUpdateableApplicationContext with no parent.
*/
public AbstractUpdateableApplicationContext() {
}
/**
* Create a new AbstractUpdateableApplicationContext with the given parent context.
*
* @param parent
* the parent context
*/
public AbstractUpdateableApplicationContext(ApplicationContext parent) {
super(parent);
}
@Override
public void addApplicationListener(ApplicationListener listener) {
super.addApplicationListener(listener);
}
@Override
protected final void closeBeanFactory() {
synchronized (m_beanFactoryMonitor) {
m_beanFactory = null;
}
}
/**
* Create an internal bean factory for this context. Called for each {@link #refresh()} attempt.
* <p>
* The default implementation creates a {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
* with the {@link #getInternalParentBeanFactory() internal bean factory} of this context's parent as parent bean
* factory. Can be overridden in subclasses, for example to customize DefaultListableBeanFactory's settings.
*
* @return the bean factory for this context
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowEagerClassLoading
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowCircularReferences
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
*/
protected UpdateableListableBeanFactory createBeanFactory() {
return new UpdateableListableBeanFactory(getInternalParentBeanFactory());
}
/**
* Customize the internal bean factory used by this context. Called for each {@link #refresh()} attempt.
* <p>
* The default implementation applies this context's {@link #setAllowBeanDefinitionOverriding
* "m_allowBeanDefinitionOverriding"} and {@link #setAllowCircularReferences "m_allowCircularReferences"} settings, if
* specified. Can be overridden in subclasses to customize any of {@link DefaultListableBeanFactory}'s settings.
*
* @param m_beanFactory
* the newly created bean factory for this context
* @see DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
* @see DefaultListableBeanFactory#setAllowCircularReferences
* @see DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
* @see DefaultListableBeanFactory#setAllowEagerClassLoading
*/
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (m_allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(m_allowBeanDefinitionOverriding.booleanValue());
}
if (m_allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(m_allowCircularReferences.booleanValue());
}
}
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
UpdateableListableBeanFactory beanFactory = null;
synchronized (m_beanFactoryMonitor) {
beanFactory = m_beanFactory;
if (beanFactory == null) {
throw new IllegalStateException("BeanFactory not initialized or already closed - "
+ "call 'refresh' before accessing beans via the ApplicationContext");
}
}
return beanFactory.waitForUpdates();
}
/**
* Determine whether this context currently holds a bean factory, i.e. has been refreshed at least once and not been
* closed yet.
*/
protected final boolean hasBeanFactory() {
synchronized (m_beanFactoryMonitor) {
return m_beanFactory != null;
}
}
/**
* Load bean definitions into the given bean factory, typically through delegating to one or more bean definition
* readers.
*
* @param m_beanFactory
* the bean factory to load bean definitions into
* @throws IOException
* if loading of bean definition files failed
* @throws BeansException
* if parsing of the bean definitions failed
* @see org.springframework.beans.factory.support.PropertiesBeanDefinitionReader
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
*/
protected abstract void loadBeanDefinitions(UpdateableListableBeanFactory beanFactory) throws IOException,
BeansException;
/**
* This implementation performs an actual refresh of this context's underlying bean factory, shutting down the
* previous bean factory (if any) and initializing a fresh bean factory for the next phase of the context's lifecycle.
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
UpdateableListableBeanFactory beanFactory = createBeanFactory();
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (m_beanFactoryMonitor) {
m_beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing XML document for application context ["
+ getDisplayName() + "]", ex);
}
}
/**
* Set whether it should be allowed to override bean definitions by registering a different definition with the same
* name, automatically replacing the former. If not, an exception will be thrown. Default is "true".
*
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
*/
public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) {
m_allowBeanDefinitionOverriding = Boolean.valueOf(allowBeanDefinitionOverriding);
}
/**
* Set whether to allow circular references between beans - and automatically try to resolve them.
* <p>
* Default is "true". Turn this off to throw an exception when encountering a circular reference, disallowing them
* completely.
*
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowCircularReferences
*/
public void setAllowCircularReferences(boolean allowCircularReferences) {
m_allowCircularReferences = Boolean.valueOf(allowCircularReferences);
}
}