/* * Copyright 2008-2014 the original author or authors * * 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 org.kaleidofoundry.core.context; import static org.kaleidofoundry.core.plugin.PluginHelper.getPluginName; import java.lang.reflect.ParameterizedType; import java.util.LinkedHashSet; import java.util.Set; import org.aopalliance.intercept.MethodInterceptor; import org.kaleidofoundry.core.lang.annotation.Task; import org.kaleidofoundry.core.lang.annotation.TaskLabel; import org.kaleidofoundry.core.plugin.Declare; import org.kaleidofoundry.core.plugin.PluginFactory; import org.kaleidofoundry.core.plugin.PluginImplementationRegistry; import org.kaleidofoundry.core.plugin.model.Plugin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.Inject; import com.google.inject.Scope; import com.google.inject.Scopes; import com.google.inject.matcher.Matchers; import com.google.inject.name.Named; import com.google.inject.name.Names; /** * This module is used to bind an interface (generic T type) to its multiple implementation, using guice mecanism<br> * This class extends guice module, using kaleido plugin mecanism<br/> * <p> * The default and unnamed T implementation is computed using {@link #getUnnamedImplementation()} <br/> * The guice {@link Named} binding use internal plugin registry to bind plugin name to implementation <br/> * <br/> * You can custom binding, overriding {@link #configure()} method. By this way you can add binding for your custom guice binding annotation. * For more informations : http://code.google.com/p/google-guice/wiki/BindingAnnotations * </p> * * @author jraduget * @param <T> */ @Task(comment = "providers for RuntimeContext", labels = TaskLabel.ImplementIt) public abstract class AbstractModule<T> extends com.google.inject.AbstractModule { static final Logger LOGGER = LoggerFactory.getLogger(AbstractModule.class); // class of generic type T private final Class<T> annotatedInterface; /** * */ @SuppressWarnings("unchecked") protected AbstractModule() { annotatedInterface = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; } /** * @return default unnamed implementation to use if name is not qualified with {@link Named} */ public abstract Class<? extends T> getUnnamedImplementation(); /* * (non-Javadoc) * @see com.google.inject.AbstractModule#configure() */ @Override protected void configure() { // ** bindings *************************************************************************************************** // 1. bind default and unnamed file store implementation bind(annotatedInterface).to(getUnnamedImplementation()).in(scope(getUnnamedImplementation())); // 2. bind standard kaleido implementation first final Set<Class<? extends T>> standardImpls = implementations(true); // bind with Names.named("...") for (final Class<? extends T> standard : standardImpls) { bind(annotatedInterface).annotatedWith(Names.named(getPluginName(standard))).to(standard).in(scope(standard)); } // 3. bind custom non kaleido implementation after final Set<Class<? extends T>> customImpls = implementations(false); for (final Class<? extends T> custom : customImpls) { bind(annotatedInterface).annotatedWith(Names.named(getPluginName(custom))).to(custom).in(scope(custom)); } // ** interceptor ************************************************************************************************ // interceptor for constructors **** // bindInterceptor(ConstructorInterceptor doesn't exists (use ProvisionInterception in guice 2.1) // ContextInjectionConstructorInterceptor constructorContextInterceptor = new ContextInjectionConstructorInterceptor(); // requestInjection(constructorContextInterceptor); // bindInterceptor(Matchers.any(), Matchers.annotatedWith(Context.class), constructorContextInterceptor); // interceptor for fields **** // -> doesn't exists we use TypeListener (ContextTypeListener) instead // interceptor for methods **** final MethodInterceptor methodContextInterceptor = new ContextInjectionMethodInterceptor(); requestInjection(methodContextInterceptor); bindInterceptor(Matchers.any(), Matchers.annotatedWith(Inject.class), methodContextInterceptor); // ** listeners ************************************************************************************************** // listeners only for fields.... bindListener(Matchers.any(), new ContextTypeListener()); } /** * @param standard does we search kaleido standard implementation using <code>true</code> or not * @return collection of standards implementations */ public Set<Class<? extends T>> implementations(final boolean standard) { final PluginImplementationRegistry pluginImplRegistry = PluginFactory.getImplementationRegistry(); final Set<Plugin<T>> storePluginImpls = pluginImplRegistry.findByInterface(annotatedInterface); final Set<Class<? extends T>> result = new LinkedHashSet<Class<? extends T>>(); for (final Plugin<T> pi : storePluginImpls) { if (pi.isStandard() == standard) { result.add(pi.getAnnotatedClass()); } } return result; } /** * @param c * @return if class argument annotated with {@link Declare}, return the guice {@link Scope} that maps {@link Provider#singletons()} <br/> * otherwise return {@link Scopes#NO_SCOPE} */ public Scope scope(final Class<? extends T> c) { final Provider declarePlugin = c.getAnnotation(Provider.class); if (declarePlugin != null) { return declarePlugin.scope()==org.kaleidofoundry.core.context.Scope.singleton ? Scopes.SINGLETON : Scopes.NO_SCOPE; } else { return Scopes.NO_SCOPE; } } }