/* * 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.guice; import com.google.inject.AbstractModule; import com.google.inject.Binder; import com.google.inject.Binding; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Provider; import com.google.inject.name.Names; 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 java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * The implementation of an {@link Interceptor} allowing eXo Kernel to interact with a Google Guice container * * @author <a href="mailto:nfilotto@exoplatform.com">Nicolas Filotto</a> * @version $Id$ * */ public class GuiceContainer extends AbstractInterceptor { /** * The serial version UID */ private static final long serialVersionUID = -6662420267945445921L; /** * The logger */ private static final Log LOG = ExoLogger.getLogger("exo.kernel.container.ext.provider.impl.guice.v3.GuiceContainer"); /** * The Google Guice injector */ private Injector injector; /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public <T> T getComponentInstance(Object componentKey, Class<T> bindType, boolean autoRegistration) { T result = super.getComponentInstance(componentKey, bindType, autoRegistration); if (result == null && injector != null) { final Binding<?> binding; if (componentKey instanceof Class<?> && !((Class<?>)componentKey).isAnnotation()) { binding = injector.getExistingBinding(Key.get((Class<?>)componentKey)); } else { if (componentKey instanceof String) { binding = injector.getExistingBinding(Key.get(bindType, Names.named((String)componentKey))); } else if (componentKey instanceof Class<?>) { binding = injector.getExistingBinding(Key.get(bindType, (Class<? extends Annotation>)componentKey)); } else { return null; } } if (binding == null || binding.getProvider().toString().startsWith(ComponentAdapterProvider.class.getName())) { return null; } result = bindType.cast(binding.getProvider().get()); } return result; } /** * {@inheritDoc} */ @Override public <T> T getComponentInstanceOfType(Class<T> componentType, boolean autoRegistration) { T result = super.getComponentInstanceOfType(componentType, autoRegistration); if (result == null && injector != null) { Binding<?> binding = injector.getExistingBinding(Key.get(componentType)); if (binding == null || binding.getProvider().toString().startsWith(ComponentAdapterProvider.class.getName())) { return null; } result = componentType.cast(binding.getProvider().get()); } return result; } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public <T> ComponentAdapter<T> getComponentAdapter(Object componentKey, Class<T> bindType, boolean autoRegistration) { ComponentAdapter<T> result = super.getComponentAdapter(componentKey, bindType, autoRegistration); if (result == null && injector != null) { final Binding<?> binding; if (componentKey instanceof Class<?> && !((Class<?>)componentKey).isAnnotation()) { binding = injector.getExistingBinding(Key.get((Class<?>)componentKey)); } else { if (componentKey instanceof String) { binding = injector.getExistingBinding(Key.get(bindType, Names.named((String)componentKey))); } else if (componentKey instanceof Class<?>) { binding = injector.getExistingBinding(Key.get(bindType, (Class<? extends Annotation>)componentKey)); } else { return null; } } if (binding == null || binding.getProvider().toString().startsWith(ComponentAdapterProvider.class.getName())) { return null; } result = createComponentAdapter(bindType, binding); } return result; } private <T> ComponentAdapter<T> createComponentAdapter(final Class<T> type, final Binding<?> binding) { return new AbstractComponentAdapter<T>(type, type) { /** * The serial UID */ private static final long serialVersionUID = 4241559622835718141L; public T getComponentInstance() throws ContainerException { return type.cast(binding.getProvider().get()); } public boolean isSingleton() { return false; } }; } /** * {@inheritDoc} */ @Override public <T> ComponentAdapter<T> getComponentAdapterOfType(Class<T> componentType, boolean autoRegistration) { ComponentAdapter<T> result = super.getComponentAdapterOfType(componentType, autoRegistration); if (result == null && injector != null) { final Binding<?> binding = injector.getExistingBinding(Key.get(componentType)); if (binding == null || binding.getProvider().toString().startsWith(ComponentAdapterProvider.class.getName())) { return null; } result = createComponentAdapter(componentType, binding); } return result; } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public <T> List<ComponentAdapter<T>> getComponentAdaptersOfType(Class<T> componentType) { List<ComponentAdapter<T>> result = super.getComponentAdaptersOfType(componentType); if (injector != null) { result = new ArrayList<ComponentAdapter<T>>(result); for (Binding<?> b : injector.getAllBindings().values()) { if (b.getProvider().toString().startsWith(ComponentAdapterProvider.class.getName())) { continue; } else if (componentType.isAssignableFrom(b.getKey().getTypeLiteral().getRawType())) { result.add((ComponentAdapter<T>)createComponentAdapter(b.getKey().getTypeLiteral().getRawType(), b)); } } } return result; } /** * {@inheritDoc} */ @Override public <T> List<T> getComponentInstancesOfType(Class<T> componentType) throws ContainerException { List<T> result = super.getComponentInstancesOfType(componentType); if (injector != null) { result = new ArrayList<T>(result); for (Binding<?> b : injector.getAllBindings().values()) { if (b.getProvider().toString().startsWith(ComponentAdapterProvider.class.getName())) { continue; } else if (componentType.isAssignableFrom(b.getKey().getTypeLiteral().getRawType())) { result.add(componentType.cast(b.getProvider().get())); } } } return result; } /** * {@inheritDoc} */ @Override public void start() { ConfigurationManager cm = super.getComponentInstanceOfType(ConfigurationManager.class, false); // We check if the component has been defined in the configuration of the current container // The goal is to enable the GuicegContainer only if it is needed Component component = cm.getComponent(ModuleProvider.class); if (component == null) { if (LOG.isDebugEnabled()) { LOG.debug("No ModuleProvider has been defined, thus the GuiceContainer will be disabled." + " To enable the Guice Integration please define a ModuleProvider"); } } else { ModuleProvider provider = super.getComponentInstanceOfType(ModuleProvider.class, false); injector = Guice.createInjector(provider.getModule(), new AbstractModule() { @SuppressWarnings({"unchecked", "rawtypes"}) @Override protected void configure() { Collection<ComponentAdapter<?>> adapters = delegate.getComponentAdapters(); Binder binder = binder(); for (ComponentAdapter<?> adapter : adapters) { Object key = adapter.getComponentKey(); Class<?> type; Annotation annotation = null; Class<? extends Annotation> annotationType = null; if (key instanceof Class<?> && !((Class<?>)key).isAnnotation()) { type = (Class<?>)key; } else { if (key instanceof String) { annotation = Names.named((String)key); } else if (key instanceof Class<?>) { annotationType = (Class<? extends Annotation>)key; } type = adapter.getComponentImplementation(); } if (annotation == null && annotationType == null) { binder.bind(type).toProvider(new ComponentAdapterProvider(type, adapter)); } else { // As we don't know the type, we will bind it for each super classes and interfaces too ComponentAdapterProvider provider = new ComponentAdapterProvider(type, adapter); bindAll(binder, type, provider, annotation, annotationType); } } } }); LOG.info("A GuiceContainer has been enabled using the ModuleProvider " + provider.getClass()); } super.start(); } @SuppressWarnings({"rawtypes", "unchecked"}) private static void bindAll(Binder binder, Class<?> clazz, ComponentAdapterProvider provider, Annotation annotation, Class<? extends Annotation> annotationType) { if (clazz == null || clazz.equals(Object.class)) return; if (annotation == null) { binder.bind(clazz).annotatedWith(annotationType).toProvider(provider); } else { binder.bind(clazz).annotatedWith(annotation).toProvider(provider); } for (Class<?> c : clazz.getInterfaces()) { bindAll(binder, c, provider, annotation, annotationType); } bindAll(binder, clazz.getSuperclass(), provider, annotation, annotationType); } /** * {@inheritDoc} */ @Override public void stop() { super.stop(); injector = null; } /** * {@inheritDoc} */ public String getId() { return "GuiceIntegration"; } private static class ComponentAdapterProvider<T> implements Provider<T> { private final Class<T> type; private final ComponentAdapter<T> adapter; private ComponentAdapterProvider(Class<T> type, ComponentAdapter<T> adapter) { this.type = type; this.adapter = adapter; } /** * {@inheritDoc} */ public T get() { return type.cast(adapter.getComponentInstance()); } } }