/*
* Copyright 2013 Netflix, Inc.
*
* 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 com.netflix.governator.guice;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collection;
import org.aopalliance.intercept.MethodInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Lists;
import com.google.inject.Binder;
import com.google.inject.Binding;
import com.google.inject.Key;
import com.google.inject.MembersInjector;
import com.google.inject.Module;
import com.google.inject.PrivateBinder;
import com.google.inject.Provider;
import com.google.inject.Scope;
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.AnnotatedBindingBuilder;
import com.google.inject.binder.AnnotatedConstantBindingBuilder;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.matcher.Matcher;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.Message;
import com.google.inject.spi.ModuleAnnotatedMethodScanner;
import com.google.inject.spi.ProvisionListener;
import com.google.inject.spi.TypeConverter;
import com.google.inject.spi.TypeListener;
import com.netflix.governator.configuration.ConfigurationProvider;
import com.netflix.governator.lifecycle.LifecycleListener;
import com.netflix.governator.lifecycle.ResourceLocator;
public class BootstrapBinder implements Binder
{
private final Logger log = LoggerFactory.getLogger(getClass());
private final Binder binder;
private Stage stage;
private LifecycleInjectorMode mode;
private ModuleListBuilder modules;
private boolean disableAutoBinding;
BootstrapBinder(Binder binder, Stage stage, LifecycleInjectorMode mode, ModuleListBuilder modules, Collection<PostInjectorAction> actions, Collection<ModuleTransformer> transformers, boolean disableAutoBinding)
{
this.binder = binder;
this.mode = mode;
this.stage = stage;
this.modules = modules;
Multibinder<ModuleTransformer> transformerBinder = Multibinder.newSetBinder(binder, ModuleTransformer.class);
Multibinder<PostInjectorAction> actionBinder = Multibinder.newSetBinder(binder, PostInjectorAction.class);
for (PostInjectorAction action : actions) {
actionBinder.addBinding().toInstance(action);
}
for (ModuleTransformer transformer : transformers) {
transformerBinder.addBinding().toInstance(transformer);
}
}
private String getBindingLocation() {
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
for (int i = 1; i < stack.length ; i++) {
StackTraceElement elem = stack[i];
if (!elem.getClassName().equals(BootstrapBinder.class.getCanonicalName()))
return elem.toString();
}
return stack[0].toString();
}
@Override
public void bindInterceptor(Matcher<? super Class<?>> classMatcher, Matcher<? super Method> methodMatcher, MethodInterceptor... interceptors)
{
binder.bindInterceptor(classMatcher, methodMatcher, interceptors);
}
@Override
public void bindScope(Class<? extends Annotation> annotationType, Scope scope)
{
binder.bindScope(annotationType, scope);
}
/**
* Bind actions to perform after the injector is created.
*
* @return a binding builder used to add a new element in the set.
*/
public LinkedBindingBuilder<PostInjectorAction> bindPostInjectorAction()
{
return Multibinder.newSetBinder(binder, PostInjectorAction.class).addBinding();
}
/**
* Bind module transform operations to perform on the final list of modul.
*
* @return a binding builder used to add a new element in the set.
*/
public LinkedBindingBuilder<ModuleTransformer> bindModuleTransformer()
{
return Multibinder.newSetBinder(binder, ModuleTransformer.class).addBinding();
}
/**
* Use this to bind a {@link LifecycleListener}. It internally uses a Multibinder to do the
* binding so that you can bind multiple LifecycleListeners
*
* @return a binding builder used to add a new element in the set.
*/
public LinkedBindingBuilder<LifecycleListener> bindLifecycleListener()
{
return Multibinder.newSetBinder(binder, LifecycleListener.class).addBinding();
}
/**
* Use this to bind a {@link ResourceLocator}. It internally uses a Multibinder to do the
* binding so that you can bind multiple ResourceLocators
*
* @return a binding builder used to add a new element in the set.
*/
public LinkedBindingBuilder<ResourceLocator> bindResourceLocator()
{
return Multibinder.newSetBinder(binder, ResourceLocator.class).addBinding();
}
/**
* Use this to bind {@link ConfigurationProvider}s. Do NOT use standard Guice binding.
*
* @return configuration binding builder
*/
public LinkedBindingBuilder<ConfigurationProvider> bindConfigurationProvider()
{
return Multibinder.newSetBinder(binder, ConfigurationProvider.class).addBinding();
}
@Override
public <T> LinkedBindingBuilder<T> bind(Key<T> key)
{
warnOnSpecialized(key.getTypeLiteral().getRawType());
return binder.withSource(getBindingLocation()).bind(key);
}
@Override
public <T> AnnotatedBindingBuilder<T> bind(TypeLiteral<T> typeLiteral)
{
warnOnSpecialized(typeLiteral.getRawType());
return binder.withSource(getBindingLocation()).bind(typeLiteral);
}
@Override
public <T> AnnotatedBindingBuilder<T> bind(Class<T> type)
{
warnOnSpecialized(type);
return binder.withSource(getBindingLocation()).bind(type);
}
@Override
public AnnotatedConstantBindingBuilder bindConstant()
{
return binder.withSource(getBindingLocation()).bindConstant();
}
@Override
public <T> void requestInjection(TypeLiteral<T> type, T instance)
{
binder.withSource(getBindingLocation()).requestInjection(type, instance);
}
@Override
public void requestInjection(Object instance)
{
binder.withSource(getBindingLocation()).requestInjection(instance);
}
@Override
public void requestStaticInjection(Class<?>... types)
{
binder.withSource(getBindingLocation()).requestStaticInjection(types);
}
@Override
public void install(Module module)
{
binder.withSource(getBindingLocation()).install(module);
}
public void include(Class<? extends Module> module) {
this.modules.include(module);
}
public void include(Class<? extends Module> ... modules) {
this.modules.include(Lists.newArrayList(modules));
}
public void include(Collection<Class<? extends Module>> modules) {
this.modules.include(modules);
}
public void include(Module module) {
this.modules.include(module);
}
public void includeModules(Collection<? extends Module> modules) {
this.modules.includeModules(modules);
}
public void includeModules(Module ... modules) {
this.modules.includeModules(Lists.newArrayList(modules));
}
public void exclude(Class<? extends Module> module) {
this.modules.exclude(module);
}
public void exclude(Class<? extends Module> ... modules) {
this.modules.exclude(Lists.newArrayList(modules));
}
public void exclude(Collection<Class<? extends Module>> modules) {
this.modules.exclude(modules);
}
public void inStage(Stage stage) {
this.stage = stage;
}
public void inMode(LifecycleInjectorMode mode) {
this.mode = mode;
}
@Override
public Stage currentStage()
{
return binder.currentStage();
}
@Override
public void addError(String message, Object... arguments)
{
binder.addError(message, arguments);
}
@Override
public void addError(Throwable t)
{
binder.addError(t);
}
@Override
public void addError(Message message)
{
binder.addError(message);
}
@Override
public <T> Provider<T> getProvider(Key<T> key)
{
return binder.getProvider(key);
}
@Override
public <T> Provider<T> getProvider(Class<T> type)
{
return binder.getProvider(type);
}
@Override
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral)
{
return binder.getMembersInjector(typeLiteral);
}
@Override
public <T> MembersInjector<T> getMembersInjector(Class<T> type)
{
return binder.getMembersInjector(type);
}
@Override
public void convertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher, TypeConverter converter)
{
binder.convertToTypes(typeMatcher, converter);
}
@Override
public void bindListener(Matcher<? super TypeLiteral<?>> typeMatcher, TypeListener listener)
{
binder.withSource(getBindingLocation()).bindListener(typeMatcher, listener);
}
@Override
public Binder withSource(Object source)
{
return binder.withSource(source);
}
@Override
public Binder skipSources(@SuppressWarnings("rawtypes") Class... classesToSkip)
{
return binder.skipSources(classesToSkip);
}
@Override
public PrivateBinder newPrivateBinder()
{
return binder.newPrivateBinder();
}
@Override
public void requireExplicitBindings()
{
binder.requireExplicitBindings();
}
@Override
public void disableCircularProxies()
{
binder.disableCircularProxies();
}
public void disableAutoBinding() {
disableAutoBinding = true;
}
private<T> void warnOnSpecialized(Class<T> clazz)
{
if ( ConfigurationProvider.class.isAssignableFrom(clazz) )
{
log.warn("You should use the specialized binding method for ConfigurationProviders");
}
if ( LifecycleListener.class.isAssignableFrom(clazz) )
{
log.warn("You should use the specialized binding method for LifecycleListener");
}
if ( ResourceLocator.class.isAssignableFrom(clazz) )
{
log.warn("You should use the specialized binding method for ResourceLocator");
}
}
Stage getStage() {
return stage;
}
LifecycleInjectorMode getMode() {
return mode;
}
boolean isDisabledAutoBinding() {
return disableAutoBinding;
}
@Override
public <T> Provider<T> getProvider(Dependency<T> dependency) {
return binder.getProvider(dependency);
}
@Override
public void bindListener(Matcher<? super Binding<?>> bindingMatcher,
ProvisionListener... listeners) {
binder.bindListener(bindingMatcher, listeners);
}
@Override
public void requireAtInjectOnConstructors() {
binder.requireAtInjectOnConstructors();
}
@Override
public void requireExactBindingAnnotations() {
binder.requireExactBindingAnnotations();
}
@Override
public void scanModulesForAnnotatedMethods(
ModuleAnnotatedMethodScanner scanner) {
binder.scanModulesForAnnotatedMethods(scanner);
}
}