* added possibility do declare lists and registries as inner beans
git-svn-id: svn+ssh://svn.synyx.de/var/svn/synyx/opensource/hera/trunk@3663 5a64d73e-33d6-4ccc-9058-23f8668ecac9
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
package org.synyx.hera.core.config;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.BeanReference;
|
||||
|
||||
|
||||
/**
|
||||
* {@link BeanPostProcessor} to handle {@link BeanReference} beans and replacing
|
||||
* it by its reference target.
|
||||
*
|
||||
* @author Oliver Gierke - gierke@synyx.de
|
||||
*/
|
||||
public class BeanReferenceBeanPostProcessor implements BeanPostProcessor,
|
||||
BeanFactoryAware {
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org
|
||||
* .springframework.beans.factory.BeanFactory)
|
||||
*/
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @seeorg.springframework.beans.factory.config.BeanPostProcessor#
|
||||
* postProcessAfterInitialization(java.lang.Object, java.lang.String)
|
||||
*/
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
|
||||
// Skip non RuntimeBeanReferences
|
||||
if (!(bean instanceof BeanReference)) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
BeanReference reference = (BeanReference) bean;
|
||||
|
||||
return beanFactory.getBean(reference.getBeanName());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @seeorg.springframework.beans.factory.config.BeanPostProcessor#
|
||||
* postProcessBeforeInitialization(java.lang.Object, java.lang.String)
|
||||
*/
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
@@ -15,31 +15,38 @@
|
||||
*/
|
||||
package org.synyx.hera.core.config;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanNameReference;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
|
||||
/**
|
||||
* Bean definition parser to register {@code <list />} elements from the plugin
|
||||
* namespace.
|
||||
*
|
||||
* @author Oliver Gierke - gierke@synyx.de
|
||||
*/
|
||||
public class PluginListDefinitionParser extends
|
||||
AbstractSingleBeanDefinitionParser {
|
||||
public class PluginListDefinitionParser extends AbstractBeanDefinitionParser {
|
||||
|
||||
protected static final String PACKAGE = "org.synyx.hera.core.support.";
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
/**
|
||||
* Returns the name of the {@link BeanFactoryPostProcessor} to be
|
||||
* registered.
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
|
||||
* #getBeanClassName(org.w3c.dom.Element)
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected String getBeanClassName(Element element) {
|
||||
protected String getPostProcessorName() {
|
||||
|
||||
return PACKAGE + "BeanListBeanFactoryPostProcessor";
|
||||
}
|
||||
@@ -48,18 +55,116 @@ public class PluginListDefinitionParser extends
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
|
||||
* #doParse(org.w3c.dom.Element,
|
||||
* org.springframework.beans.factory.support.BeanDefinitionBuilder)
|
||||
* @seeorg.springframework.beans.factory.xml.AbstractBeanDefinitionParser#
|
||||
* parseInternal(org.w3c.dom.Element,
|
||||
* org.springframework.beans.factory.xml.ParserContext)
|
||||
*/
|
||||
@Override
|
||||
protected AbstractBeanDefinition parseInternal(Element element,
|
||||
ParserContext context) {
|
||||
|
||||
if (context.isNested()) {
|
||||
return parseNested(element, context);
|
||||
} else {
|
||||
return parseStandalone(element, context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a nested {@code <list />} or {@code <registry />} element.
|
||||
* Registers the {@link BeanFactoryPostProcessor} as in standalone mode but
|
||||
* returns a reference to the list or registry created by instead of the
|
||||
* {@link BeanFactoryPostProcessor} itself.
|
||||
*
|
||||
* @param element
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
private AbstractBeanDefinition parseNested(Element element,
|
||||
ParserContext context) {
|
||||
|
||||
// Step 1 - Create reference wrapped in a bean definition
|
||||
BeanDefinitionBuilder builder =
|
||||
BeanDefinitionBuilder
|
||||
.genericBeanDefinition(RuntimeBeanNameReference.class
|
||||
.getName());
|
||||
|
||||
// Extract id attribute from element or generate custom one
|
||||
String idAttribute = element.getAttribute("id");
|
||||
String listId =
|
||||
StringUtils.hasText(idAttribute) ? idAttribute
|
||||
: BeanDefinitionReaderUtils.generateBeanName(builder
|
||||
.getBeanDefinition(), context.getRegistry(),
|
||||
true);
|
||||
|
||||
// Let reference point to the bean with the calculated id
|
||||
builder.addConstructorArgValue(listId);
|
||||
|
||||
// Step 2 - Register BeanPostProcessor to unwrap the reference
|
||||
registerReferenceResolver(element, context);
|
||||
|
||||
// Step 3 - Set the id to let the BeanFactoryPostProcessor (BFPP)
|
||||
// register the list as reference target
|
||||
element.setAttribute("id", listId);
|
||||
|
||||
// Step 4 - Register BFPP ourselves
|
||||
AbstractBeanDefinition definition = parseStandalone(element, context);
|
||||
String beanFactoryxPostProcessorId =
|
||||
resolveId(element, definition, context);
|
||||
|
||||
BeanDefinitionHolder holder =
|
||||
new BeanDefinitionHolder(definition,
|
||||
beanFactoryxPostProcessorId);
|
||||
registerBeanDefinition(holder, context.getRegistry());
|
||||
|
||||
// Step 5 - Return the reference to the list created by the BFPP
|
||||
return getSourcedBeanDefinition(builder, element, context);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers a {@link BeanReferenceBeanPostProcessor} to automatically
|
||||
* unwrap {@link BeanDefinition}s that contain a reference to other beans.
|
||||
*
|
||||
* @param element
|
||||
* @param context
|
||||
*/
|
||||
private void registerReferenceResolver(Element element,
|
||||
ParserContext context) {
|
||||
|
||||
BeanDefinitionBuilder builder =
|
||||
BeanDefinitionBuilder
|
||||
.genericBeanDefinition(BeanReferenceBeanPostProcessor.class);
|
||||
|
||||
BeanDefinition definition =
|
||||
getSourcedBeanDefinition(builder, element, context);
|
||||
|
||||
registerBeanDefinition(new BeanDefinitionHolder(definition, generateId(
|
||||
definition, context)), context.getRegistry());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a standalone {@code <list />} or {@code <registry />} element and
|
||||
* registers a {@link BeanFactoryPostProcessor} to automatically register
|
||||
* all beans of the type specified in {@code class} property under the given
|
||||
* id.
|
||||
*
|
||||
* @param element
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void doParse(Element element, BeanDefinitionBuilder builder) {
|
||||
private AbstractBeanDefinition parseStandalone(Element element,
|
||||
ParserContext context) {
|
||||
|
||||
ManagedMap map = new ManagedMap();
|
||||
map.put(element.getAttribute("id"), element.getAttribute("class"));
|
||||
|
||||
BeanDefinitionBuilder builder =
|
||||
BeanDefinitionBuilder
|
||||
.genericBeanDefinition(getPostProcessorName());
|
||||
builder.addPropertyValue("lists", map);
|
||||
|
||||
String initFactories = element.getAttribute("init-factories");
|
||||
@@ -67,6 +172,40 @@ public class PluginListDefinitionParser extends
|
||||
if (StringUtils.hasText(initFactories)) {
|
||||
builder.addPropertyValue("allowEagerInit", initFactories);
|
||||
}
|
||||
|
||||
return getSourcedBeanDefinition(builder, element, context);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the bean definition prepared by the builder and has connected it
|
||||
* to the {@code source} object.
|
||||
*
|
||||
* @param builder
|
||||
* @param source
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
private AbstractBeanDefinition getSourcedBeanDefinition(
|
||||
BeanDefinitionBuilder builder, Object source, ParserContext context) {
|
||||
|
||||
AbstractBeanDefinition definition = builder.getRawBeanDefinition();
|
||||
definition.setSource(context.extractSource(source));
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a custom id for the given {@link BeanDefinition}.
|
||||
*
|
||||
* @param definition
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
private String generateId(BeanDefinition definition, ParserContext context) {
|
||||
|
||||
return context.getReaderContext().generateBeanName(definition);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,12 +15,9 @@
|
||||
*/
|
||||
package org.synyx.hera.core.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
|
||||
/**
|
||||
* Simple extension of {@link PluginListDefinitionParser}. Simply registers a
|
||||
* {@code PluginListDefinitionParser} instead of the original class.
|
||||
* {@code PluginRegistryBeanFactoryPostProcessor} instead of the original class.
|
||||
*
|
||||
* @author Oliver Gierke - gierke@synyx.de
|
||||
*/
|
||||
@@ -34,7 +31,7 @@ public class PluginRegistryDefinitionParser extends PluginListDefinitionParser {
|
||||
* #getBeanClassName(org.w3c.dom.Element)
|
||||
*/
|
||||
@Override
|
||||
protected String getBeanClassName(Element element) {
|
||||
protected String getPostProcessorName() {
|
||||
|
||||
return PACKAGE + "PluginRegistryBeanFactoryPostProcessor";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.synyx.hera.core;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke - gierke@synyx.de
|
||||
*/
|
||||
public class SamplePluginHost {
|
||||
|
||||
private PluginRegistry<SamplePlugin, String> registry =
|
||||
PluginRegistry.create();
|
||||
|
||||
|
||||
/**
|
||||
* @param registry the registry to set
|
||||
*/
|
||||
public void setRegistry(PluginRegistry<SamplePlugin, String> registry) {
|
||||
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the registry
|
||||
*/
|
||||
public PluginRegistry<SamplePlugin, String> getRegistry() {
|
||||
|
||||
return registry;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,18 @@
|
||||
package org.synyx.hera.core.config;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.synyx.hera.core.PluginRegistry;
|
||||
import org.synyx.hera.core.SamplePlugin;
|
||||
import org.synyx.hera.core.SamplePluginHost;
|
||||
|
||||
|
||||
/**
|
||||
@@ -24,11 +28,22 @@ public class PluginConfigurationIntegrationTest {
|
||||
List<SamplePlugin> samplePlugins;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("bar")
|
||||
PluginRegistry<SamplePlugin, String> pluginRegistry;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("host")
|
||||
SamplePluginHost host;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("otherHost")
|
||||
SamplePluginHost otherHost;
|
||||
|
||||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
|
||||
assertSame(pluginRegistry, host.getRegistry());
|
||||
assertNotSame(pluginRegistry, otherHost.getRegistry());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,17 @@
|
||||
|
||||
<plugin:registry id="bar" class="org.synyx.hera.core.SamplePlugin" />
|
||||
|
||||
<bean class="org.synyx.hera.core.SamplePluginImplementation" />
|
||||
|
||||
<bean class="org.synyx.hera.core.SamplePluginImplementation" />
|
||||
|
||||
<bean id="host" class="org.synyx.hera.core.SamplePluginHost">
|
||||
<property name="registry" ref="bar" />
|
||||
</bean>
|
||||
|
||||
<!-- Uncommented till TODO in PluginListDefinitionParser is resolved -->
|
||||
<bean id="otherHost" class="org.synyx.hera.core.SamplePluginHost">
|
||||
<property name="registry">
|
||||
<plugin:registry id="tadaa" class="org.synyx.hera.core.SamplePlugin" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -9,4 +9,4 @@ log4j.rootLogger=WARN, stdout
|
||||
|
||||
# Hibernate logging options (INFO only shows startup messages)
|
||||
log4j.logger.org.springframework=INFO
|
||||
log4j.logger.org.synyx.plugin=DEBUG
|
||||
log4j.logger.org.synyx.hera=DEBUG
|
||||
Reference in New Issue
Block a user