blob: 74a40a3d8b32165feba39a0e3de750e2d3333627 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.geronimo.gshell.spring;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.ResourceEntityResolver;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.beans.support.ResourceEditorRegistrar;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.ObjectUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Support for {@link BeanContainerContext} implementations.
*
* This is basically a merged and trimmed down version of a Spring AbstractXmlApplicationContext.
*
* @version $Rev$ $Date$
*/
public abstract class BeanContainerContextSupport
extends DefaultResourceLoader
implements BeanContainerContext, DisposableBean
{
protected final Logger log = LoggerFactory.getLogger(getClass());
private String id = ObjectUtils.identityToString(this);
private final BeanContainerContext parent;
private final List<BeanFactoryPostProcessor> beanFactoryPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
private boolean active = false;
private final Object activeMonitor = new Object();
private final Object startupShutdownMonitor = new Object();
private final Thread shutdownHook;
private final ResourcePatternResolver resourcePatternResolver;
public BeanContainerContextSupport() {
this(null);
}
public BeanContainerContextSupport(final BeanContainerContext parent) {
// parent could be null
this.parent = parent;
this.resourcePatternResolver = new PathMatchingResourcePatternResolver(this);
this.shutdownHook = new Thread() {
public void run() {
doClose();
}
};
Runtime.getRuntime().addShutdownHook(shutdownHook);
}
//
// BeanContainerContext
//
public void setId(final String id) {
this.id = id;
}
public String getId() {
return id;
}
public BeanContainerContext getParent() {
return parent;
}
public void refresh() throws BeansException, IllegalStateException {
synchronized (startupShutdownMonitor) {
// Prepare this context for refreshing.
synchronized (activeMonitor) {
active = true;
}
log.debug("Refreshing: {}", this);
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
}
catch (BeansException e) {
log.error("Refresh failed", e);
// Destroy already created singletons to avoid dangling resources.
beanFactory.destroySingletons();
// Reset 'active' flag.
synchronized (this.activeMonitor) {
this.active = false;
}
// Propagate exception to caller.
throw e;
}
}
}
private ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
log.info("Bean factory for application context [{}]: {}", getId(), ObjectUtils.identityToString(beanFactory));
log.debug("{} beans defined in {}", beanFactory.getBeanDefinitionCount(), this);
return beanFactory;
}
private void prepareBeanFactory(final ConfigurableListableBeanFactory beanFactory) {
assert beanFactory != null;
// Tell the internal bean factory to use the context's class loader.
beanFactory.setBeanClassLoader(getClassLoader());
// Populate the bean factory with context-specific resource editors.
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this));
// BeanFactory interface not registered as resolvable type in a plain factory.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(BeanContainerContext.class, this);
}
private void finishBeanFactoryInitialization(final ConfigurableListableBeanFactory beanFactory) {
assert beanFactory != null;
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
public void addBeanFactoryPostProcessor(final BeanFactoryPostProcessor beanFactoryPostProcessor) {
beanFactoryPostProcessors.add(beanFactoryPostProcessor);
}
private void invokeBeanFactoryPostProcessors(final ConfigurableListableBeanFactory beanFactory) {
assert beanFactory != null;
// Invoke factory processors registered with the context instance.
for (BeanFactoryPostProcessor factoryProcessor : beanFactoryPostProcessors) {
factoryProcessor.postProcessBeanFactory(beanFactory);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
// Separate between BeanFactoryPostProcessors that implement PriorityOrdered, Ordered, and the rest.
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
List<String> orderedPostProcessorNames = new ArrayList<String>();
List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
for (String name : postProcessorNames) {
if (isTypeMatch(name, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add((BeanFactoryPostProcessor) beanFactory.getBean(name));
}
else if (isTypeMatch(name, Ordered.class)) {
orderedPostProcessorNames.add(name);
}
else {
nonOrderedPostProcessorNames.add(name);
}
}
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
// noinspection unchecked
Collections.sort(priorityOrderedPostProcessors, new OrderComparator());
invokeBeanFactoryPostProcessors(beanFactory, priorityOrderedPostProcessors);
// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
for (String name : orderedPostProcessorNames) {
orderedPostProcessors.add((BeanFactoryPostProcessor) getBean(name));
}
// noinspection unchecked
Collections.sort(orderedPostProcessors, new OrderComparator());
invokeBeanFactoryPostProcessors(beanFactory, orderedPostProcessors);
// Finally, invoke all other BeanFactoryPostProcessors.
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
for (String name : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add((BeanFactoryPostProcessor) getBean(name));
}
invokeBeanFactoryPostProcessors(beanFactory, nonOrderedPostProcessors);
}
private void invokeBeanFactoryPostProcessors(final ConfigurableListableBeanFactory beanFactory, final List<BeanFactoryPostProcessor> postProcessors) {
assert beanFactory != null;
assert postProcessors != null;
for (BeanFactoryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanFactory(beanFactory);
}
}
private void registerBeanPostProcessors(final ConfigurableListableBeanFactory beanFactory) {
assert beanFactory != null;
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
// Register BeanPostProcessorChecker that logs an info message when
// a bean is created during BeanPostProcessor instantiation, i.e. when
// a bean is not eligible for getting processed by all BeanPostProcessors.
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
// Separate between BeanPostProcessors that implement PriorityOrdered, Ordered, and the rest.
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
List<String> orderedPostProcessorNames = new ArrayList<String>();
List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
for (String name : postProcessorNames) {
if (isTypeMatch(name, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add((BeanPostProcessor)beanFactory.getBean(name));
}
else if (isTypeMatch(name, Ordered.class)) {
orderedPostProcessorNames.add(name);
}
else {
nonOrderedPostProcessorNames.add(name);
}
}
// First, register the BeanPostProcessors that implement PriorityOrdered.
// noinspection unchecked
Collections.sort(priorityOrderedPostProcessors, new OrderComparator());
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
// Next, register the BeanPostProcessors that implement Ordered.
List<BeanPostProcessor> orderedPostProcessors = new ArrayList<BeanPostProcessor>();
for (String name : orderedPostProcessorNames) {
orderedPostProcessors.add((BeanPostProcessor)getBean(name));
}
// noinspection unchecked
Collections.sort(orderedPostProcessors, new OrderComparator());
registerBeanPostProcessors(beanFactory, orderedPostProcessors);
// Finally, register all other BeanPostProcessors.
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
for (String name : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add((BeanPostProcessor)getBean(name));
}
registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
}
private void registerBeanPostProcessors(final ConfigurableListableBeanFactory beanFactory, final List<BeanPostProcessor> postProcessors) {
assert beanFactory != null;
assert postProcessors != null;
for (BeanPostProcessor postProcessor : postProcessors) {
beanFactory.addBeanPostProcessor(postProcessor);
}
}
public void destroy() {
close();
}
public void close() {
synchronized (startupShutdownMonitor) {
doClose();
Runtime.getRuntime().removeShutdownHook(shutdownHook);
}
}
private void doClose() {
if (isActive()) {
log.info("Closing: {}", this);
// Destroy all cached singletons in the context's BeanFactory.
destroyBeans();
// Close the state of this context itself.
closeBeanFactory();
synchronized (activeMonitor) {
active = false;
}
}
}
private void destroyBeans() {
getBeanFactory().destroySingletons();
}
public boolean isActive() {
synchronized (activeMonitor) {
return active;
}
}
//
// BeanFactory
//
public Object getBean(final String name) throws BeansException {
return getBeanFactory().getBean(name);
}
public Object getBean(final String name, final Class requiredType) throws BeansException {
return getBeanFactory().getBean(name, requiredType);
}
public Object getBean(final String name, final Object[] args) throws BeansException {
return getBeanFactory().getBean(name, args);
}
public boolean containsBean(final String name) {
return getBeanFactory().containsBean(name);
}
public boolean isSingleton(final String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().isSingleton(name);
}
public boolean isPrototype(final String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().isPrototype(name);
}
public boolean isTypeMatch(final String name, final Class targetType) throws NoSuchBeanDefinitionException {
return getBeanFactory().isTypeMatch(name, targetType);
}
public Class getType(final String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().getType(name);
}
public String[] getAliases(final String name) {
return getBeanFactory().getAliases(name);
}
//
// ListableBeanFactory
//
public boolean containsBeanDefinition(final String name) {
return getBeanFactory().containsBeanDefinition(name);
}
public int getBeanDefinitionCount() {
return getBeanFactory().getBeanDefinitionCount();
}
public String[] getBeanDefinitionNames() {
return getBeanFactory().getBeanDefinitionNames();
}
public String[] getBeanNamesForType(final Class type) {
return getBeanFactory().getBeanNamesForType(type);
}
public String[] getBeanNamesForType(final Class type, final boolean includePrototypes, final boolean allowEagerInit) {
return getBeanFactory().getBeanNamesForType(type, includePrototypes, allowEagerInit);
}
public Map getBeansOfType(final Class type) throws BeansException {
return getBeanFactory().getBeansOfType(type);
}
public Map getBeansOfType(final Class type, final boolean includePrototypes, final boolean allowEagerInit) throws BeansException {
return getBeanFactory().getBeansOfType(type, includePrototypes, allowEagerInit);
}
//
// HierarchicalBeanFactory
//
public BeanFactory getParentBeanFactory() {
return getParent();
}
public boolean containsLocalBean(final String name) {
return getBeanFactory().containsLocalBean(name);
}
//
// ResourcePatternResolver
//
public Resource[] getResources(final String locationPattern) throws IOException {
return resourcePatternResolver.getResources(locationPattern);
}
//
// BeanPostProcessorChecker
//
/**
* Logs a message when a bean is created during BeanPostProcessor instantiation.
* i.e. when a bean is not eligible for getting processed by all BeanPostProcessors.
*/
private class BeanPostProcessorChecker
implements BeanPostProcessor
{
private final ConfigurableListableBeanFactory beanFactory;
private final int beanPostProcessorTargetCount;
public BeanPostProcessorChecker(final ConfigurableListableBeanFactory beanFactory, final int beanPostProcessorTargetCount) {
assert beanFactory != null;
this.beanFactory = beanFactory;
this.beanPostProcessorTargetCount = beanPostProcessorTargetCount;
}
public Object postProcessBeforeInitialization(final Object bean, final String beanName) {
return bean;
}
public Object postProcessAfterInitialization(final Object bean, final String beanName) {
assert bean != null;
assert beanName != null;
if (!(bean instanceof BeanPostProcessor) && beanFactory.getBeanPostProcessorCount() < beanPostProcessorTargetCount) {
log.info("Bean '{}' is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)", beanName);
}
return bean;
}
}
//
// AbstractRefreshableApplicationContext
//
private DefaultListableBeanFactory beanFactory;
private final Object beanFactoryMonitor = new Object();
private void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(getParent() != null ? getParent().getBeanFactory() : null);
beanFactory.setAllowBeanDefinitionOverriding(true);
beanFactory.setAllowCircularReferences(true);
beanFactory.setAllowEagerClassLoading(false);
loadBeanDefinitions(beanFactory);
synchronized (beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException e) {
throw new BeanContainerContextException("I/O error parsing XML document for application context: " + this, e);
}
}
private void loadBeanDefinitions(final DefaultListableBeanFactory beanFactory) throws IOException {
assert beanFactory != null;
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.setResourceLoader(this);
reader.setEntityResolver(new ResourceEntityResolver(this));
reader.setNamespaceAware(true);
// TODO: Make this configurable via system properties, default to VALIDATION_NONE, disabling validation speeds things up a lot
reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_NONE);
loadBeanDefinitions(reader);
}
protected abstract void loadBeanDefinitions(final XmlBeanDefinitionReader reader) throws BeansException, IOException;
private void closeBeanFactory() {
synchronized (beanFactoryMonitor) {
beanFactory = null;
}
}
private boolean hasBeanFactory() {
synchronized (beanFactoryMonitor) {
return beanFactory != null;
}
}
public final ConfigurableListableBeanFactory getBeanFactory() {
synchronized (beanFactoryMonitor) {
if (beanFactory == null) {
throw new IllegalStateException("BeanFactory not initialized or already closed");
}
return beanFactory;
}
}
}