[jboss-cvs] jboss-seam/src/ioc/org/jboss/seam/ioc/spring ...
Norman Richards
norman.richards at jboss.com
Fri Feb 16 22:26:43 EST 2007
User: nrichards
Date: 07/02/16 22:26:43
Added: src/ioc/org/jboss/seam/ioc/spring
SeamComponentPostProcessor.java
SeamFactoryBean.java SeamNamespaceHandler.java
SeamScope.java SeamScopePostProcessor.java
SeamTargetSource.java SpringComponent.java
TestAnnotation.java TestInterceptor.java
Log:
create main module
Revision Changes Path
1.1 date: 2007/02/17 03:26:43; author: nrichards; state: Exp;jboss-seam/src/ioc/org/jboss/seam/ioc/spring/SeamComponentPostProcessor.java
Index: SeamComponentPostProcessor.java
===================================================================
package org.jboss.seam.ioc.spring;
import org.jboss.seam.Component;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
/**
* Intercepts when spring attempts to obtain an instance of a bean. If the bean is a seam component then we retrieve the
* bean from seam to ensure it gets wrapped and managed by seam as well. This post processor must have a lower
* precedence than any spring autoproxy creators.
*
* @author youngm
*/
public class SeamComponentPostProcessor implements BeanPostProcessor, Ordered {
private int order = Ordered.LOWEST_PRECEDENCE;
/**
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object,
* java.lang.String)
*/
public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
// Check to see if this bean is a component.
Component component = SpringComponent.forSpringBeanName(beanName);
// Not a spring component skip.
if (component == null || !(component instanceof SpringComponent)) {
return bean;
}
// If this bean is a FactoryBean only request the bean from Seam if the component is a FactoryBean as well
// The object created by the factory should come along later
if (bean instanceof FactoryBean && !FactoryBean.class.isAssignableFrom(component.getBeanClass())) {
return bean;
}
// Wrap our bean instance in an object factory for the SpringComponent to use
SpringComponent.setObjectFactory(new ObjectFactory() {
public Object getObject() throws BeansException {
return bean;
}
});
// Return the seam instance
return Component.getInstance(beanName, component.getScope());
}
/**
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object,
* java.lang.String)
*/
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
/**
* This post processor must run after any spring AutoProxyCreator
*
* @see org.springframework.core.Ordered#getOrder()
*/
public int getOrder() {
return order;
}
/**
* @param order the order to set
*/
public void setOrder(int order) {
this.order = order;
}
}
1.1 date: 2007/02/17 03:26:43; author: nrichards; state: Exp;jboss-seam/src/ioc/org/jboss/seam/ioc/spring/SeamFactoryBean.java
Index: SeamFactoryBean.java
===================================================================
package org.jboss.seam.ioc.spring;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpSessionActivationListener;
import org.jboss.seam.Component;
import org.jboss.seam.InterceptionType;
import org.jboss.seam.ScopeType;
import org.jboss.seam.core.Mutable;
import org.jboss.seam.intercept.Proxy;
import org.springframework.aop.framework.DefaultAopProxyFactory;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.AbstractFactoryBean;
/**
* Obtains an instance of a Seam Component in the current context
* given the name and other optional parameters. If proxy is set to
* true then return a scoped proxy of the seam component instance. Use
* <seam:instance/> to simplify use of this factory.
*
* @author youngm
*/
public class SeamFactoryBean
extends AbstractFactoryBean
implements InitializingBean
{
private ScopeType scope;
private String name;
private Boolean create;
private SeamTargetSource targetSource;
private Object proxyInstance;
private boolean proxy = false;
/**
* Initializes the factory. If proxy=true then initialize the proxy.
*
* @see org.springframework.beans.factory.config.AbstractFactoryBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
if (name == null) {
throw new IllegalArgumentException("name must not be null");
}
// If we're creating a proxy then we want this to be a singleton
setSingleton(proxy);
this.targetSource = new SeamTargetSource(name, scope, create);
if (proxy) {
// Not sure if I should allow people to change these proxy
// parameters or not. We'll see what issues we get hard coding them.
ProxyFactory pf = new ProxyFactory();
pf.setProxyTargetClass(true);
pf.setOptimize(true);
pf.setExposeProxy(false);
pf.setFrozen(true);
pf.setAopProxyFactory(new DefaultAopProxyFactory());
pf.setTargetSource(this.targetSource);
// Attempt to piece together all of the possible interfaces to apply
// to our proxy.
List<Class> interfaces = new ArrayList<Class>();
Component component = targetSource.getComponent();
if (component.getInterceptionType() != InterceptionType.NEVER) {
if (component.getType().isSessionBean()) {
interfaces.addAll(component.getBusinessInterfaces());
} else {
interfaces.add(HttpSessionActivationListener.class);
interfaces.add(Mutable.class);
}
interfaces.add(Proxy.class);
}
pf.setInterfaces(interfaces.toArray(new Class[interfaces.size()]));
this.proxyInstance = pf.getProxy();
}
super.afterPropertiesSet();
}
/**
* Return the current instance of a Seam component or the proxy if proxy was set to true.
*
* @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance()
*/
@Override
protected Object createInstance() throws Exception {
if (proxy) {
return proxyInstance;
} else {
return targetSource.getTarget();
}
}
/**
* Return the type of the component if available.
*
* @throws IllegalStateException if the component cannot be found or if seam has not yet been initialized.
*
* @see org.springframework.beans.factory.config.AbstractFactoryBean#getObjectType()
*/
@Override
public Class getObjectType() {
return targetSource.getTargetClass();
}
/**
* The name of the seam component to get an instance of. (required)
*
* @param name the name of the component
*/
public void setName(String name) {
this.name = name;
}
/**
* The scope of the seam component (optional)
*
* @param scope the scope of the component
*/
public void setScope(ScopeType scope) {
this.scope = scope;
}
/**
* Should the factory create an instance of the component if one
* doesn't already exist in this context. If null
*
* Must always be true for STATELESS components.
*
* @param create do we create an instance if needed
*/
public void setCreate(Boolean create) {
this.create = create;
}
/**
* Should the factory wrap the component instance in a proxy so
* the seam component can be safely injected into a singleton.
*
* @param proxy true to proxy the component
*/
public void setProxy(boolean proxy) {
this.proxy = proxy;
}
}
1.1 date: 2007/02/17 03:26:43; author: nrichards; state: Exp;jboss-seam/src/ioc/org/jboss/seam/ioc/spring/SeamNamespaceHandler.java
Index: SeamNamespaceHandler.java
===================================================================
package org.jboss.seam.ioc.spring;
import org.jboss.seam.InterceptionType;
import org.jboss.seam.ScopeType;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.core.Ordered;
import org.springframework.util.ClassUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* NamespaceHandler for a number of seam features in spring.
*
* @author youngm
*/
public class SeamNamespaceHandler
extends NamespaceHandlerSupport
{
public static final String SEAM_SCOPE_POST_PROCESSOR = "org.jboss.seam.ioc.spring.SeamScopePostProcessor";
public static final String SEAM_COMPONENT_POST_PROCESSOR = "org.jboss.seam.ioc.spring.SeamComponentPostProcessor";
public static final String SEAM_COMPONENT_POST_PROCESSOR_BEAN_NAME = "org.jboss.seam.ioc.spring.seamComponentPostProcessor";
/**
* @see org.springframework.beans.factory.xml.NamespaceHandler#init()
*/
public void init() {
registerBeanDefinitionParser("configure-scopes", new SeamConfigureScopeParser());
registerBeanDefinitionParser("instance", new SeamInstanceBeanDefinitionParser());
registerBeanDefinitionDecorator("component", new SeamComponentBeanDefinitionDecorator());
}
/**
* Registers the SeamScopePostProcessor in this bean factory under
* the name defined in SEAM_SCOPE_POST_PROCESSOR.
* <seam:configure-scope/>
*
* @see SeamScopePostProcessor
* @author youngm
*/
private static class SeamConfigureScopeParser
extends AbstractSimpleBeanDefinitionParser
{
/**
* @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#getBeanClass(org.w3c.dom.Element)
*/
@Override
protected Class getBeanClass(Element element) {
return SeamScopePostProcessor.class;
}
/**
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#resolveId(org.w3c.dom.Element,
* org.springframework.beans.factory.support.AbstractBeanDefinition,
* org.springframework.beans.factory.xml.ParserContext)
*/
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
throws BeanDefinitionStoreException
{
return SEAM_SCOPE_POST_PROCESSOR;
}
}
/**
* Makes a SeamFactoryBean available for use in a spring ApplicationContext. <seam:instance
* name="someSeamComponent"/>
*
* @see SeamFactoryBean
* @author youngm
*/
private static class SeamInstanceBeanDefinitionParser
extends AbstractSimpleBeanDefinitionParser
{
protected Class getBeanClass(Element element) {
return SeamFactoryBean.class;
}
}
/**
* Makes an existing spring bean definition a seam component or
* provides hints in the creation of a seam component. Will use
* the bean definitions name and class by default and the classes
* annotatated InterceptionType.
*
* If proxy=true will wrap the spring bean in a cglib proxy for
* safe injection into singletons.
*
* <seam:component/>
*
* @author youngm
*/
private static class SeamComponentBeanDefinitionDecorator implements BeanDefinitionDecorator {
private static final String INTERCEPT_ATTR = "intercept";
private static final String SPRING_NAME_ATTR = "springName";
private static final String SEAM_NAME_ATTR = "seamName";
private static final String BEAN_CLASS_ATTR = "beanClass";
public static final String AUTO_INTERCEPTION_TYPE = "AUTO";
/**
* @see org.springframework.beans.factory.xml.BeanDefinitionDecorator#decorate(org.w3c.dom.Node,
* org.springframework.beans.factory.config.BeanDefinitionHolder,
* org.springframework.beans.factory.xml.ParserContext)
*/
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
// Add the Seam Component Post Processor to the bean factory if it doesn't already exist
if (!parserContext.getRegistry().containsBeanDefinition(SEAM_COMPONENT_POST_PROCESSOR_BEAN_NAME)) {
Class cls = null;
try {
cls = ClassUtils.forName(SEAM_COMPONENT_POST_PROCESSOR);
} catch (ClassNotFoundException e) {
throw new IllegalStateException("Unable to load class '" + SEAM_COMPONENT_POST_PROCESSOR
+ "' make sure you have the jboss-seam-spring.jar in your classpath.");
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
beanDefinition.getPropertyValues().addPropertyValue("order", new Integer(Ordered.LOWEST_PRECEDENCE));
parserContext.getRegistry().registerBeanDefinition(SEAM_COMPONENT_POST_PROCESSOR_BEAN_NAME,
beanDefinition);
}
// get the optional beanClass
String beanClassName = definition.getBeanDefinition().getBeanClassName();
if (node.getAttributes().getNamedItem(BEAN_CLASS_ATTR) != null) {
beanClassName = node.getAttributes().getNamedItem(BEAN_CLASS_ATTR).getNodeValue();
}
String beanName = definition.getBeanName();
// get the name of the seam component to create
String seamName = beanName;
if (node.getAttributes().getNamedItem(SEAM_NAME_ATTR) != null) {
seamName = node.getAttributes().getNamedItem(SEAM_NAME_ATTR).getNodeValue();
}
// get the name of the spring bean to use
String springName = beanName;
if (node.getAttributes().getNamedItem(SPRING_NAME_ATTR) != null) {
springName = node.getAttributes().getNamedItem(SPRING_NAME_ATTR).getNodeValue();
}
// get the interception type to use
InterceptionType interceptionType = null;
if (AUTO_INTERCEPTION_TYPE.equals(node.getAttributes().getNamedItem(INTERCEPT_ATTR).getNodeValue())) {
if (definition.getBeanDefinition().isSingleton()) {
interceptionType = InterceptionType.NEVER;
}
} else {
interceptionType = InterceptionType.valueOf(node.getAttributes().getNamedItem(INTERCEPT_ATTR)
.getNodeValue());
}
// get the requested scope
ScopeType scope = ScopeType.valueOf(node.getAttributes().getNamedItem("scope").getNodeValue());
if (scope != ScopeType.STATELESS
&& !BeanDefinition.SCOPE_PROTOTYPE.equals(definition.getBeanDefinition().getScope())) {
throw new IllegalStateException(
"The spring bean scope must be prototype to use a seam scope other than STATELESS.");
}
// determine if we want to override any existing seam component definitions
boolean override = Boolean.parseBoolean(node.getAttributes().getNamedItem("override").getNodeValue());
if (!(parserContext.getRegistry() instanceof BeanFactory)) {
throw new RuntimeException("For some reason your registry is not a BeanFactory");
}
SpringComponent.addSpringComponent(seamName, springName, beanClassName, scope, (BeanFactory) parserContext
.getRegistry(), interceptionType, override);
return definition;
}
}
}
1.1 date: 2007/02/17 03:26:43; author: nrichards; state: Exp;jboss-seam/src/ioc/org/jboss/seam/ioc/spring/SeamScope.java
Index: SeamScope.java
===================================================================
package org.jboss.seam.ioc.spring;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.core.Events;
import org.jboss.seam.log.LogProvider;
import org.jboss.seam.log.Logging;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
/**
* Allows for the creation of seam scoped component in spring. Seam
* scopes are automatically made available if the
* SeamScopePostProcessor is declared in the current
* BeanFactory. <seam:configure-scopes/>
*
* @author youngm
* @see SeamScopePostProcessor
*/
public class SeamScope
implements Scope
{
private static final LogProvider log = Logging.getLogProvider(SeamScope.class);
private ScopeType scope;
public SeamScope(ScopeType scope) {
this.scope = scope;
}
/**
* Gets an instance of a Seam component providing the current ObjectFactory if needed.
*
* @see org.springframework.beans.factory.config.Scope#get(java.lang.String,
* org.springframework.beans.factory.ObjectFactory)
*/
public Object get(String name, ObjectFactory objectFactory) {
try {
SpringComponent.setObjectFactory(objectFactory);
Component component = SpringComponent.forSpringBeanName(name);
return Component.getInstance(component.getName(), scope, true);
} finally {
SpringComponent.setObjectFactory(null);
}
}
/**
* Not used yet.
*
* @see org.springframework.beans.factory.config.Scope#getConversationId()
*/
public String getConversationId() {
return null;
}
/**
* @see org.springframework.beans.factory.config.Scope#registerDestructionCallback(java.lang.String,
* java.lang.Runnable)
*/
public void registerDestructionCallback(String name, Runnable callback) {
((SpringComponent) SpringComponent.forSpringBeanName(name)).registerDestroyCallback(name, callback);
}
/**
* On remove destroys the seam component.
*
* @see org.springframework.beans.factory.config.Scope#remove(java.lang.String)
*/
public Object remove(String name) {
// copied from Component.callDestory should be able to reuse. Needed because if remove is called then for some
// reason spring doesn't use the destroy callback.
log.debug("destroying: " + name);
Component component = SpringComponent.forSpringBeanName(name);
Object bean = scope.getContext().get(component.getName());
if (component != null) {
if (bean != null) { // in a portal environment, this is possible
if (Events.exists())
Events.instance().raiseEvent("org.jboss.seam.preDestroy." + name);
try {
if (component.hasDestroyMethod()) {
component.callComponentMethod(bean, component.getDestroyMethod());
}
} catch (Exception e) {
log.warn("Could not destroy component: " + component.getName(), e);
}
}
}
scope.getContext().remove(component.getName());
return bean;
}
}
1.1 date: 2007/02/17 03:26:43; author: nrichards; state: Exp;jboss-seam/src/ioc/org/jboss/seam/ioc/spring/SeamScopePostProcessor.java
Index: SeamScopePostProcessor.java
===================================================================
package org.jboss.seam.ioc.spring;
import org.jboss.seam.ScopeType;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.contexts.Lifecycle;
import org.jboss.seam.log.LogProvider;
import org.jboss.seam.log.Logging;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
/**
* Post processor that makes all of the seam scopes available in
* spring and takes all of the beans with those scopes and creates
* Seam Components out of them. <p/> To use simply define the
* namespace hanlder in in your ApplicationContext.
* <seam:configure-scopes/>
*
* @author youngm
*/
public class SeamScopePostProcessor
implements BeanFactoryPostProcessor,
InitializingBean
{
private static final LogProvider log = Logging.getLogProvider(SeamScopePostProcessor.class);
/**
* Default seam scope prefix.
*/
public static final String DEFAULT_SCOPE_PREFIX = "seam.";
private String prefix;
private boolean override = false;
/**
* Null is not a valid prefix so make it the default is used if null or empty.
*
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() throws Exception {
if (prefix == null || "".equals(prefix)) {
prefix = DEFAULT_SCOPE_PREFIX;
}
}
/**
* Add all of the seam scopes to this beanFactory.
*
* @see org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory)
*/
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException
{
for (ScopeType scope : ScopeType.values()) {
// Don't create a scope for Unspecified
if (scope != ScopeType.UNSPECIFIED) {
beanFactory.registerScope(prefix + scope.name(), new SeamScope(scope));
}
}
// Create a mock application context if not available.
// TODO Reuse
boolean unmockApplication = false;
if (!Contexts.isApplicationContextActive()) {
Lifecycle.mockApplication();
unmockApplication = true;
}
try {
// Iterate through all the beans in the factory
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
ScopeType scope;
if (definition.getScope().startsWith(prefix)) {
// Will throw an error if the scope is not found.
scope = ScopeType.valueOf(definition.getScope().replaceFirst(prefix, "").toUpperCase());
} else {
if (log.isDebugEnabled()) {
log.debug("No scope could be derived for bean with name: " + beanName);
}
continue;
}
if (scope == ScopeType.UNSPECIFIED) {
if (log.isDebugEnabled()) {
log.debug("Discarding bean with scope UNSPECIFIED. Spring will throw an error later: "
+ beanName);
}
continue;
}
// Cannot be a seam component without a specified class seam:component will need to be used for this
// bean.
if (definition.getBeanClassName() == null) {
if (log.isDebugEnabled()) {
log.debug("Unable to create component for bean: " + beanName
+ ". No class defined try seam:component instead.");
}
continue;
}
SpringComponent.addSpringComponent(beanName, beanName, definition.getBeanClassName(), scope, beanFactory, null,
override);
}
} finally {
if (unmockApplication) {
Lifecycle.unmockApplication();
}
}
}
/**
* @param casesensitive prefix the prefix to use to identify seam scopes for spring beans. Default is "seam."
*/
public void setPrefix(String prefix) {
this.prefix = prefix;
}
/**
* @param duplicate set to tell the postprocessor to duplicate any preexisting seam components that may have the same
* name as the candidate spring bean. Default: false
*/
public void setOverride(boolean override) {
this.override = override;
}
}
1.1 date: 2007/02/17 03:26:43; author: nrichards; state: Exp;jboss-seam/src/ioc/org/jboss/seam/ioc/spring/SeamTargetSource.java
Index: SeamTargetSource.java
===================================================================
package org.jboss.seam.ioc.spring;
import java.io.Serializable;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.contexts.Lifecycle;
import org.jboss.seam.log.LogProvider;
import org.jboss.seam.log.Logging;
import org.springframework.aop.TargetSource;
/**
* A TargetSource for a seam component instance. Will obtain an instance given a name and optionally a scope and create.
* Used by the SeamFactoryBean to create a proxy for a requested seam component instance.
*
* @author youngm
*/
@SuppressWarnings("serial")
public class SeamTargetSource implements TargetSource, Serializable {
private static final LogProvider log = Logging.getLogProvider(SeamTargetSource.class);
private ScopeType scope;
private String name;
private Boolean create;
/**
* @param name Name of the component: required
* @param scope Name of the scope the component is in: optional
* @param create Whether to create a new instance if one doesn't already exist: optional
*/
public SeamTargetSource(String name, ScopeType scope, Boolean create) {
if (name == null || "".equals(name)) {
throw new IllegalArgumentException("Name is required.");
}
this.name = name;
this.scope = scope;
this.create = create;
}
/**
* Returns a component instance for this TargetSource.
*
* @see org.springframework.aop.TargetSource#getTarget()
*/
public Object getTarget() throws Exception {
if (scope == null && create == null) {
return Component.getInstance(name);
} else if (scope == null) {
return Component.getInstance(name, create);
} else if (create == null) {
return Component.getInstance(name, scope);
} else {
return Component.getInstance(name, scope, create);
}
}
/**
* Obtains the seam component beanClass for this TargetSource.
*
* @see org.springframework.aop.TargetSource#getTargetClass()
*/
public Class getTargetClass() {
return getComponent().getBeanClass();
}
/**
* Get the component for this TargetSource
*
* @return
*/
public Component getComponent() {
// TODO reuse
boolean unmockApplication = false;
if (!Contexts.isApplicationContextActive()) {
Lifecycle.mockApplication();
unmockApplication = true;
}
try {
Component component = Component.forName(name);
if (component == null) {
throw new IllegalStateException("Cannot find targetClass for seam component: " + name
+ ". Make sure Seam is being configured before Spring.");
}
return component;
} finally {
if (unmockApplication) {
Lifecycle.unmockApplication();
}
}
}
/**
* @see org.springframework.aop.TargetSource#isStatic()
*/
public boolean isStatic() {
return false;
}
/**
* Don't think we need to do anything here.
*
* @see org.springframework.aop.TargetSource#releaseTarget(java.lang.Object)
*/
public void releaseTarget(Object target) throws Exception {
// Do Nothing
}
}
1.1 date: 2007/02/17 03:26:43; author: nrichards; state: Exp;jboss-seam/src/ioc/org/jboss/seam/ioc/spring/SpringComponent.java
Index: SpringComponent.java
===================================================================
package org.jboss.seam.ioc.spring;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpSessionActivationListener;
import org.jboss.seam.Component;
import org.jboss.seam.InterceptionType;
import org.jboss.seam.ScopeType;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.contexts.Lifecycle;
import org.jboss.seam.core.Mutable;
import org.jboss.seam.init.Initialization;
import org.jboss.seam.intercept.Proxy;
import org.jboss.seam.ioc.IoCComponent;
import org.jboss.seam.ioc.ProxyUtils;
import org.springframework.aop.framework.Advised;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.util.ClassUtils;
/**
* An extension of Component that allows spring to provide the base instance for a seam component.
*
* @author youngm
*/
public class SpringComponent extends IoCComponent {
private static final String SPRING_COMPONENT_NAME_MAP = "org.jboss.seam.SpringComponentNameMap";
public static final String DESTRUCTION_CALLBACK_NAME_PREFIX = IoCComponent.class.getName()
+ ".DESTRUCTION_CALLBACK.";
private BeanFactory beanfactory;
private InterceptionType interceptionType;
private String springBeanName;
private static final ThreadLocal<ObjectFactory> objectFactory = new ThreadLocal<ObjectFactory>();
public static ObjectFactory getObjectFactory() {
return objectFactory.get();
}
public static void setObjectFactory(ObjectFactory bean) {
objectFactory.set(bean);
}
/**
* Utility to add a SpringComponent to the seam component ApplicationContext.
*
* @param componentName the seam component name to use
* @param springBeanName the spring bean name to map to this seam component
* @param beanClassName the seam beanClass to use
* @param scopeType the scope of this component
* @param beanFactory the beanfactory this spring bean exists in
* @param interceptorType the InterceptorTyp to force the bean to use. Will override any annotations on the bean.
* @param override If a seam component already exists should we override it?
*/
public static void addSpringComponent(String componentName, String springBeanName, String beanClassName,
ScopeType scopeType, BeanFactory beanFactory, InterceptionType interceptorType, boolean override) {
// mock the application context
// TODO reuse
boolean unmockApplication = false;
if (!Contexts.isApplicationContextActive()) {
Lifecycle.mockApplication();
unmockApplication = true;
}
try {
if (!override && Component.forName(componentName) != null) {
throw new IllegalStateException("Cannot add spring component to seam with name: " + componentName
+ ". There is already a seam component with that name.");
}
Map<String, String> springComponentNameMap = getSpringComponentNameMap();
// Add an entry to the spring+seam name association map
springComponentNameMap.put(springBeanName, componentName);
Class beanClass = ClassUtils.forName(beanClassName);
// Add the component to seam
Contexts.getApplicationContext().set(
componentName + Initialization.COMPONENT_SUFFIX,
new SpringComponent(beanClass, componentName, springBeanName, scopeType, beanFactory,
interceptorType));
} catch (ClassNotFoundException e) {
throw new FatalBeanException("Error", e);
} finally {
if (unmockApplication) {
Lifecycle.unmockApplication();
}
}
}
@SuppressWarnings("unchecked")
private static Map<String, String> getSpringComponentNameMap() {
if (Contexts.getApplicationContext().get(SPRING_COMPONENT_NAME_MAP) == null) {
Contexts.getApplicationContext().set(SPRING_COMPONENT_NAME_MAP, new HashMap<String, String>());
}
return (Map<String, String>) Contexts.getApplicationContext().get(SPRING_COMPONENT_NAME_MAP);
}
/**
* Just like Component.forName() but mocks the applicationContext and you provide it with the spring bean name
* instead of the seam component name.
*
* @param name the spring bean name.
* @return the SpringComponent mapped to that spring bean name.
*/
public static SpringComponent forSpringBeanName(String springBeanName) {
// TODO reuse
boolean unmockApplication = false;
if (!Contexts.isApplicationContextActive()) {
Lifecycle.mockApplication();
unmockApplication = true;
}
try {
return (SpringComponent) Component.forName(getSpringComponentNameMap().get(springBeanName));
} finally {
if (unmockApplication) {
Lifecycle.unmockApplication();
}
}
}
/**
* Creates a Spring Seam Component given a beanFactory.
*
* @param clazz the seam beanClass to use
* @param componentName component name
* @param springBeanName the spring bean name
* @param scope component scope
* @param factory the beanfactory this spring component should use
*/
public SpringComponent(Class clazz, String componenentName, String springBeanName, ScopeType scope,
BeanFactory factory, InterceptionType interception) {
super(clazz, componenentName, scope);
this.springBeanName = springBeanName;
this.beanfactory = factory;
this.interceptionType = interception;
}
protected String getIoCName() {
return "Spring";
}
protected Object instantiateIoCBean() throws Exception {
ObjectFactory objectFactory = getObjectFactory();
if (objectFactory == null) {
return beanfactory.getBean(springBeanName);
}
setObjectFactory(null);
Object bean = objectFactory.getObject();
// initialize the bean following Component.instantiateJavaBean()'s
// pattern.
if (getInterceptionType() == InterceptionType.NEVER) {
// Only call postConstruct if the bean is not stateless otherwise in the case of a singleton it wowuld be
// called every time seam request the bean not just when it is created.
if (getScope() != ScopeType.STATELESS) {
callPostConstructMethod(bean);
}
} else if (!(bean instanceof Proxy)) {
// Add all of the interfaces of the bean instance into the Seam
// proxy bean because spring's proxies add a bunch of interfaces too
// that should be accessible.
Set<Class> interfaces = new HashSet<Class>(Arrays.asList(bean.getClass().getInterfaces()));
interfaces.add(HttpSessionActivationListener.class);
interfaces.add(Mutable.class);
interfaces.add(Proxy.class);
// enhance bean
bean = ProxyUtils.enhance(bean, interfaces, this);
}
return bean;
}
/**
* Instantiates a IoC bean and provides it as a java bean to be wrapped by seam.
*
* @see org.jboss.seam.Component#instantiateJavaBean()
*/
protected Object instantiateJavaBean() throws Exception {
return instantiateIoCBean();
}
/**
* Calls the spring destroy callback when seam destroys the component
*
* @see org.jboss.seam.Component#callDestroyMethod(Object)
*/
@Override
public void callDestroyMethod(Object instance) {
super.callDestroyMethod(instance);
// Cannot call the callback on a STATELESS bean because we have no way of storing it.
if (getScope() != ScopeType.STATELESS) {
Runnable callback = (Runnable) getScope().getContext().get(DESTRUCTION_CALLBACK_NAME_PREFIX + getName());
if (callback != null) {
callback.run();
}
}
}
/**
* Registers a destruction callback with this bean.
*
* @param name bean name
* @param destroy the destroy to set
*/
public void registerDestroyCallback(String name, Runnable destroy) {
// Not sure yet how to register a stateless bean's Destruction callback.
if (getScope() != ScopeType.STATELESS) {
getScope().getContext().set(DESTRUCTION_CALLBACK_NAME_PREFIX + name, destroy);
}
}
/**
* Overrides Components inject to unwrap all of the spring AOP layers so that fields can be injected into this bean.
*
* @see org.jboss.seam.Component#inject(java.lang.Object, boolean)
*/
@Override
public void inject(Object bean, boolean enforceRequired) {
if (bean instanceof Advised) {
try {
inject(((Advised) bean).getTargetSource().getTarget(), enforceRequired);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
super.inject(bean, enforceRequired);
}
/**
* Use the InterceptionType override if available otherwise use the annotation or seam default.
*
* @see org.jboss.seam.Component#getInterceptionType()
*/
@Override
public InterceptionType getInterceptionType() {
if (interceptionType == null) {
return super.getInterceptionType();
}
return interceptionType;
}
}
1.1 date: 2007/02/17 03:26:43; author: nrichards; state: Exp;jboss-seam/src/ioc/org/jboss/seam/ioc/spring/TestAnnotation.java
Index: TestAnnotation.java
===================================================================
/**
*
*/
package org.jboss.seam.example.spring;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* @author youngm
*
*/
@Target({TYPE})
@Retention(RUNTIME)
public @interface TestAnnotation {
}
1.1 date: 2007/02/17 03:26:43; author: nrichards; state: Exp;jboss-seam/src/ioc/org/jboss/seam/ioc/spring/TestInterceptor.java
Index: TestInterceptor.java
===================================================================
/**
*
*/
package org.jboss.seam.example.spring;
import java.io.Serializable;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* @author youngm
*
*/
public class TestInterceptor implements MethodInterceptor, Serializable {
/**
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
*/
public Object invoke(MethodInvocation arg0) throws Throwable {
System.out.println("Hit interceptor");
return arg0.proceed();
}
}
More information about the jboss-cvs-commits
mailing list