/*
* Copyright (C) 2013 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.container.spring;
import org.exoplatform.commons.utils.SecurityHelper;
import org.exoplatform.container.AbstractComponentAdapter;
import org.exoplatform.container.AbstractInterceptor;
import org.exoplatform.container.configuration.ConfigurationManager;
import org.exoplatform.container.spi.ComponentAdapter;
import org.exoplatform.container.spi.ContainerException;
import org.exoplatform.container.spi.Interceptor;
import org.exoplatform.container.xml.Component;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import java.lang.annotation.Annotation;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.inject.Named;
/**
* The implementation of an {@link Interceptor} allowing eXo Kernel to interact with a spring container
*
* @author <a href="mailto:nfilotto@exoplatform.com">Nicolas Filotto</a>
* @version $Id$
*
*/
public class SpringContainer extends AbstractInterceptor
{
/**
* The serial version UID
*/
private static final long serialVersionUID = -4841328894117928913L;
/**
* The logger
*/
private static final Log LOG = ExoLogger
.getLogger("exo.kernel.container.ext.provider.impl.spring.v3.SpringContainer");
/**
* The Spring {@link ApplicationContext}
*/
private ApplicationContext ctx;
/**
* {@inheritDoc}
*/
@Override
public <T> T getComponentInstance(Object componentKey, Class<T> bindType)
{
T result = super.getComponentInstance(componentKey, bindType);
if (ctx != null && result == null)
{
if (componentKey instanceof Class<?> && !((Class<?>)componentKey).isAnnotation())
{
return bindType.cast(getInstanceOfType((Class<?>)componentKey));
}
else if (!(componentKey instanceof String) && !(componentKey instanceof Class<?>))
{
return null;
}
String beanName = keyToBeanName(componentKey);
if (ctx.containsBean(beanName) && bindType.isAssignableFrom(ctx.getType(beanName)))
{
return bindType.cast(ctx.getBean(beanName));
}
String[] names = ctx.getBeanNamesForType(bindType);
if (names != null && names.length > 0)
{
for (int i = 0, length = names.length; i < length; i++)
{
String name = names[i];
if (componentKey instanceof String)
{
Named n = ctx.findAnnotationOnBean(name, Named.class);
if (n != null && componentKey.equals(n.value()))
{
return bindType.cast(ctx.getBean(name));
}
}
else
{
@SuppressWarnings("unchecked")
Annotation a = ctx.findAnnotationOnBean(name, (Class<? extends Annotation>)componentKey);
if (a != null)
{
return bindType.cast(ctx.getBean(name));
}
}
}
}
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public <T> T getComponentInstanceOfType(final Class<T> componentType)
{
T result = super.getComponentInstanceOfType(componentType);
if (ctx != null && result == null)
{
result = getInstanceOfType(componentType);
}
return result;
}
private <T> T getInstanceOfType(final Class<T> componentType)
{
T result;
PrivilegedAction<T> action = new PrivilegedAction<T>()
{
public T run()
{
String name = classToBeanName(componentType);
if (ctx.containsBean(name) && componentType.isAssignableFrom(ctx.getType(name)))
{
return componentType.cast(ctx.getBean(name));
}
String[] names = ctx.getBeanNamesForType(componentType);
if (names != null && names.length > 0)
{
return componentType.cast(ctx.getBean(names[0]));
}
return null;
}
};
result = SecurityHelper.doPrivilegedAction(action);
return result;
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
@Override
public <T> ComponentAdapter<T> getComponentAdapter(Object componentKey, Class<T> bindType)
{
ComponentAdapter<?> result = super.getComponentAdapter(componentKey, bindType);
if (ctx != null && result == null)
{
if (componentKey instanceof Class<?> && !((Class<?>)componentKey).isAnnotation())
{
return getAdapterOfType(bindType);
}
else if (!(componentKey instanceof String) && !(componentKey instanceof Class<?>))
{
return null;
}
String beanName = keyToBeanName(componentKey);
if (ctx.containsBean(beanName) && bindType.isAssignableFrom(ctx.getType(beanName)))
{
return createComponentAdapter(bindType, beanName);
}
String[] names = ctx.getBeanNamesForType(bindType);
if (names != null && names.length > 0)
{
for (int i = 0, length = names.length; i < length; i++)
{
String name = names[i];
if (componentKey instanceof String)
{
Named n = ctx.findAnnotationOnBean(name, Named.class);
if (n != null && componentKey.equals(n.value()))
{
return createComponentAdapter(bindType, name);
}
}
else
{
Annotation a = ctx.findAnnotationOnBean(name, (Class<? extends Annotation>)componentKey);
if (a != null)
{
return createComponentAdapter(bindType, name);
}
}
}
}
}
return (ComponentAdapter<T>)result;
}
/**
* {@inheritDoc}
*/
@Override
public <T> ComponentAdapter<T> getComponentAdapterOfType(Class<T> componentType)
{
ComponentAdapter<T> result = super.getComponentAdapterOfType(componentType);
if (ctx != null && result == null)
{
result = getAdapterOfType(componentType);
}
return result;
}
private <T> ComponentAdapter<T> getAdapterOfType(Class<T> componentType)
{
String name = classToBeanName(componentType);
if (ctx.containsBean(name) && componentType.isAssignableFrom(ctx.getType(name)))
{
return createComponentAdapter(componentType, name);
}
String[] names = ctx.getBeanNamesForType(componentType);
if (names != null && names.length > 0)
{
return createComponentAdapter(componentType, names[0]);
}
return null;
}
private <T> ComponentAdapter<T> createComponentAdapter(final Class<T> type, final String name)
{
return new AbstractComponentAdapter<T>(type, type)
{
/**
* The serial UID
*/
private static final long serialVersionUID = -4625398501079851570L;
public T getComponentInstance() throws ContainerException
{
return type.cast(ctx.getBean(name));
}
public boolean isSingleton()
{
return ctx.isSingleton(name);
}
};
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
@Override
public <T> List<ComponentAdapter<T>> getComponentAdaptersOfType(Class<T> componentType)
{
List<ComponentAdapter<T>> result = super.getComponentAdaptersOfType(componentType);
if (ctx != null)
{
result = new ArrayList<ComponentAdapter<T>>(result);
String[] names = ctx.getBeanNamesForType(componentType);
if (names != null)
{
for (int i = 0, length = names.length; i < length; i++)
{
String name = names[i];
result.add((ComponentAdapter<T>)createComponentAdapter(ctx.getType(name), name));
}
}
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public <T> List<T> getComponentInstancesOfType(Class<T> componentType) throws ContainerException
{
List<T> result = super.getComponentInstancesOfType(componentType);
if (ctx != null)
{
result = new ArrayList<T>(result);
result.addAll(ctx.getBeansOfType(componentType).values());
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public void start()
{
ConfigurationManager cm = super.getComponentInstanceOfType(ConfigurationManager.class);
// We check if the component has been defined in the configuration of the current container
// The goal is to enable the SpringContainer only if it is needed
Component component = cm.getComponent(ApplicationContextProvider.class);
if (component == null)
{
if (LOG.isDebugEnabled())
{
LOG.debug("No ApplicationContextProvider has been defined, thus the SpringContainer will be disabled."
+ " To enable the Spring Integration please define an ApplicationContextProvider");
}
}
else
{
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
Collection<ComponentAdapter<?>> adapters = delegate.getComponentAdapters();
for (ComponentAdapter<?> adapter : adapters)
{
Object key = adapter.getComponentKey();
String name = keyToBeanName(key);
String factoryName = name + "#factory";
RootBeanDefinition def =
new RootBeanDefinition(adapter.getComponentImplementation(), AbstractBeanDefinition.AUTOWIRE_NO, false);
def.setScope(BeanDefinition.SCOPE_PROTOTYPE);
def.setFactoryBeanName(factoryName);
def.setFactoryMethodName("getInstance");
def.setLazyInit(true);
def.setTargetType(adapter.getComponentImplementation());
if (key instanceof String)
{
def.addQualifier(new AutowireCandidateQualifier(Named.class, key));
}
else if (key instanceof Class<?> && ((Class<?>)key).isAnnotation())
{
def.addQualifier(new AutowireCandidateQualifier((Class<?>)key));
}
else
{
def.setPrimary(true);
}
bf.registerBeanDefinition(name, def);
bf.registerSingleton(factoryName, new ComponentAdapterFactoryBean(adapter));
}
GenericApplicationContext parentContext = new GenericApplicationContext(bf);
parentContext.refresh();
ApplicationContextProvider provider = super.getComponentInstanceOfType(ApplicationContextProvider.class);
ctx = provider.getApplicationContext(parentContext);
LOG.info("A SpringContainer has been enabled using the ApplicationContextProvider " + provider.getClass());
}
super.start();
}
/**
* {@inheritDoc}
*/
@Override
public void stop()
{
super.stop();
if (ctx instanceof DisposableBean)
{
try
{
((DisposableBean)ctx).destroy();
}
catch (Exception e)
{
LOG.warn("Could not destroy the container: " + e.getMessage());
}
finally
{
ctx = null;
}
}
}
private static String classToBeanName(Class<?> type)
{
return type.getName();
}
private static String keyToBeanName(Object key)
{
if (key instanceof Class<?>)
{
return classToBeanName((Class<?>)key);
}
else
{
if (key instanceof String)
{
return (String)key;
}
else
{
return classToBeanName(key.getClass());
}
}
}
/**
* {@inheritDoc}
*/
public String getId()
{
return "SpringIntegration";
}
static class ComponentAdapterFactoryBean<T>
{
private final ComponentAdapter<T> adapter;
private ComponentAdapterFactoryBean(ComponentAdapter<T> adapter)
{
this.adapter = adapter;
}
public T getInstance() throws Exception
{
return adapter.getComponentInstance();
}
}
}