/*
* Copyright 2013 eXo Platform SAS
*
* Licensed 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 juzu.impl.inject.spi.spring;
import juzu.Scope;
import juzu.Handler;
import juzu.impl.inject.ScopeController;
import juzu.impl.fs.spi.ReadFileSystem;
import juzu.impl.inject.spi.Injector;
import juzu.impl.inject.spi.InjectionContext;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.annotation.CustomAutowireConfigurer;
import org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver;
import org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.core.io.UrlResource;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
/** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */
public class SpringInjector extends Injector {
/** . */
private ClassLoader classLoader;
/** . */
private LinkedHashMap<String, AbstractBean> beans;
/** The live instances. */
final HashMap<String, Object> instances;
/** . */
private final LinkedHashSet<Scope> scopes;
/** . */
private URL configurationURL;
/** . */
private BeanFactory parent;
/** . */
final ScopeMetadataResolverImpl scopeResolver;
/** . */
final ScopeController scopeController;
public SpringInjector() {
this.classLoader = null;
this.beans = new LinkedHashMap<String, AbstractBean>();
this.instances = new HashMap<String, Object>();
this.scopes = new LinkedHashSet<Scope>();
this.configurationURL = null;
this.scopeResolver = new ScopeMetadataResolverImpl(scopes);
this.scopeController = new ScopeController();
this.parent = null;
}
private SpringInjector(SpringInjector that) {
this.classLoader = that.classLoader;
this.beans = new LinkedHashMap<String, AbstractBean>(that.beans);
this.instances = new HashMap<String, Object>(that.instances);
this.scopes = new LinkedHashSet<Scope>(that.scopes);
this.configurationURL = that.configurationURL;
this.scopeResolver = new ScopeMetadataResolverImpl(scopes);
this.scopeController = new ScopeController();
this.parent = that.parent;
}
public void setParent(Object parent) {
this.parent = (BeanFactory)parent;
}
private Injector declareBean(AbstractBean bean) {
beans.put(bean.name, bean);
return this;
}
@Override
public boolean isProvided() {
return false;
}
@Override
public <T> Injector declareBean(Class<T> type, Scope beanScope, Iterable<Annotation> qualifiers, Class<? extends T> implementationType) {
if (implementationType == null) {
implementationType = type;
}
return declareBean(new DeclaredBean(implementationType, beanScope, qualifiers));
}
@Override
public <T> Injector bindBean(Class<T> type, Iterable<Annotation> qualifiers, T instance) {
return declareBean(new SingletonBean(instance, qualifiers));
}
@Override
public <T> Injector bindProvider(Class<T> beanType, Scope beanScope, Iterable<Annotation> beanQualifiers, final Provider<? extends T> provider) {
return declareBean(new SingletonProviderBean(beanType, beanScope, beanQualifiers, provider));
}
@Override
public <T> Injector declareProvider(Class<T> type, Scope beanScope, Iterable<Annotation> qualifiers, Class<? extends Provider<T>> provider) {
return declareBean(new DeclaredProviderBean(type, beanScope, qualifiers, provider));
}
@Override
public <P> Injector addFileSystem(ReadFileSystem<P> fs) {
return this;
}
@Override
public Injector addScope(Scope scope) {
scopes.add(scope);
return this;
}
@Override
public Injector setClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
return this;
}
@Override
public InjectionContext<?, ?> create(Handler<Class<?>, Boolean> filter) throws Exception {
//
DefaultListableBeanFactory factory;
if (parent != null) {
factory = new DefaultListableBeanFactory(parent);
} else {
if (configurationURL != null) {
factory = new XmlBeanFactory(new UrlResource(configurationURL));
}
else {
factory = new DefaultListableBeanFactory();
}
}
//
factory.setBeanClassLoader(classLoader);
factory.setInstantiationStrategy(new SingletonInstantiationStrategy(new CglibSubclassingInstantiationStrategy(), instances));
// Register scopes
for (Scope scope : scopes) {
if (!scope.isBuiltIn()) {
factory.registerScope(scope.name().toLowerCase(), new SpringScope(factory, scope, scopeController));
}
}
//
for (Map.Entry<String, AbstractBean> entry : beans.entrySet()) {
AbstractBean bean = entry.getValue();
String name = entry.getKey();
bean.configure(name, this, factory);
}
//
AutowiredAnnotationBeanPostProcessor beanPostProcessor = new AutowiredAnnotationBeanPostProcessor();
beanPostProcessor.setAutowiredAnnotationType(Inject.class);
beanPostProcessor.setBeanFactory(factory);
factory.addBeanPostProcessor(beanPostProcessor);
//
CommonAnnotationBeanPostProcessor commonAnnotationBeanProcessor = new CommonAnnotationBeanPostProcessor();
factory.addBeanPostProcessor(commonAnnotationBeanProcessor);
//
Set cqt = new HashSet();
cqt.add(Named.class);
CustomAutowireConfigurer configurer = new CustomAutowireConfigurer();
configurer.setCustomQualifierTypes(cqt);
QualifierAnnotationAutowireCandidateResolver customResolver = new QualifierAnnotationAutowireCandidateResolver();
factory.setAutowireCandidateResolver(customResolver);
configurer.postProcessBeanFactory(factory);
//
return new SpringContext(scopeController, factory, classLoader);
}
@Override
public Injector get() {
return new SpringInjector(this);
}
}