/*
* 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.guice;
import com.google.inject.AbstractModule;
import com.google.inject.Binding;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.ProvisionException;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.binder.ScopedBindingBuilder;
import com.google.inject.internal.BindingImpl;
import com.google.inject.internal.Scoping;
import com.google.inject.matcher.Matchers;
import com.google.inject.name.Named;
import com.google.inject.spi.TypeEncounter;
import com.google.inject.spi.TypeListener;
import juzu.Scope;
import juzu.impl.common.Tools;
import juzu.impl.inject.ScopeController;
import juzu.impl.inject.spi.InjectorProvider;
import juzu.impl.inject.spi.InjectionContext;
import javax.annotation.PreDestroy;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */
public class GuiceContext extends InjectionContext<GuiceBean, Object> {
/** . */
private Injector injector;
/** . */
private final ClassLoader classLoader;
/** . */
private final Map<String, Key<?>> nameMap;
/** . */
private final ScopeController scopeController;
public GuiceContext(final GuiceInjector bootstrap) {
//
this.scopeController = new ScopeController();
AbstractModule module = new AbstractModule() {
@Override
protected void configure() {
//
bindListener(Matchers.any(), new TypeListener() {
public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
encounter.register(new PostConstructInjectionListener());
}
});
// Bind guice scopes
for (Scope scope : bootstrap.scopes) {
if (!scope.isBuiltIn()) {
bindScope(scope.getAnnotationType(), new GuiceScope(scope, scopeController));
}
}
// Bind beans
for (BeanBinding<?> beanBinding : bootstrap.bindings) {
// Get a binding key
Key key;
if (beanBinding.qualifiers != null && beanBinding.qualifiers.size() > 0) {
Iterator<Annotation> i = beanBinding.qualifiers.iterator();
// Construction to make compiler happy
// b = a.annotatedWith(i.next());
// while (i.hasNext()) {
// b = a.annotatedWith(i.next());
// }
key = Key.get(beanBinding.type, i.next());
}
else {
key = Key.get(beanBinding.type);
}
LinkedBindingBuilder b = bind(key);
//
ScopedBindingBuilder c;
if (beanBinding instanceof BeanBinding.ToInstance) {
BeanBinding.ToInstance d = (BeanBinding.ToInstance)beanBinding;
b.toInstance(d.instance);
c = b;
}
else if (beanBinding instanceof BeanBinding.ToProviderInstance) {
BeanBinding.ToProviderInstance d = (BeanBinding.ToProviderInstance)beanBinding;
c = b.toProvider(d);
if (beanBinding.scopeType != null) {
c.in(beanBinding.scopeType);
}
}
else {
if (beanBinding instanceof BeanBinding.ToProviderType) {
BeanBinding.ToProviderType d = (BeanBinding.ToProviderType)beanBinding;
c = b.toProvider(d.provider);
}
else {
BeanBinding.ToType d = (BeanBinding.ToType)beanBinding;
if (d.qualifiers != null) {
if (d.implementationType != null) {
c = b.to(d.implementationType);
// Guice trick : need to bind alias so the implementation type will be bound with the qualifier
bind(Key.get(d.implementationType, key.getAnnotation())).toProvider(new BeanAlias(key));
}
else {
c = b.to(beanBinding.type);
}
}
else {
if (d.implementationType != null) {
c = b.to(d.implementationType);
}
else {
c = b;
}
}
}
if (beanBinding.scopeType != null) {
c.in(beanBinding.scopeType);
}
}
}
// Bind the manager itself
bind(InjectionContext.class).toInstance(GuiceContext.this);
}
private <T> void bind(Class<T> clazz, T instance) {
bind(clazz).toInstance(instance);
}
};
//
Map<String, Key<?>> nameMap = new HashMap<String, Key<?>>();
Injector injector = Guice.createInjector(module);
for (Key<?> key : injector.getBindings().keySet()) {
Class<? extends Annotation> annotationType = key.getAnnotationType();
if (annotationType != null && Named.class.isAssignableFrom(annotationType)) {
Named named = (Named)key.getAnnotation();
nameMap.put(named.value(), key);
}
}
//
this.injector = injector;
this.nameMap = nameMap;
this.classLoader = bootstrap.classLoader;
}
public InjectorProvider getProvider() {
return InjectorProvider.GUICE;
}
@Override
public ScopeController getScopeController() {
return scopeController;
}
public ClassLoader getClassLoader() {
return classLoader;
}
public GuiceBean resolveBean(Class<?> type) {
Binding<?> binding = injector.getBinding(type);
return binding != null ? new GuiceBean(binding) : null;
}
public Iterable<GuiceBean> resolveBeans(Class<?> type) {
List<GuiceBean> beans = new ArrayList<GuiceBean>();
Map<Key<?>, Binding<?>> allBindings = injector.getAllBindings();
Collection<Binding<?>> bindings = allBindings.values();
for (Binding<?> binding : bindings) {
Class bindingType = binding.getKey().getTypeLiteral().getRawType();
if (type.isAssignableFrom(bindingType)) {
beans.add(new GuiceBean(binding));
}
}
return beans;
}
public GuiceBean resolveBean(String name) {
Key<?> key = nameMap.get(name);
GuiceBean bean = null;
if (key != null) {
bean = new GuiceBean(injector.getBinding(key));
}
return bean;
}
public Object createContext(GuiceBean bean) throws InvocationTargetException {
try {
return bean.binding.getProvider().get();
}
catch (ProvisionException e) {
throw new InvocationTargetException(Tools.safeCause(e));
}
}
public Object getInstance(GuiceBean bean, Object context) throws InvocationTargetException {
return context;
}
public void releaseContext(GuiceBean bean, Object context) {
Scoping scoping = ((BindingImpl)bean.binding).getScoping();
if (scoping.isNoScope()) {
invokePreDestroy(context);
}
}
static void invokePreDestroy(Object o) {
for (Method method : o.getClass().getMethods()) {
if (
Modifier.isPublic(method.getModifiers()) &&
!Modifier.isStatic(method.getModifiers()) &&
method.getAnnotation(PreDestroy.class) != null) {
try {
method.invoke(o);
}
catch (IllegalAccessException e) {
throw new UnsupportedOperationException("handle me gracefully", e);
}
catch (InvocationTargetException e) {
throw new UnsupportedOperationException("handle me gracefully", e);
}
}
}
}
public void close() {
for (Binding<?> binding : injector.getAllBindings().values()) {
Scoping scoping = ((BindingImpl)binding).getScoping();
if (scoping == Scoping.SINGLETON_INSTANCE) {
invokePreDestroy(binding.getProvider().get());
}
}
}
}