All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.governator.providers.AdvisesBinder Maven / Gradle / Ivy

package com.netflix.governator.providers;

import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.util.Types;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.function.UnaryOperator;

/**
 * AdvisesBinder is a Guice usage pattern whereby a provisioned object may be modified or even 
 * replaced using rules specified as bindings themselves. This customization is done before the 
 * provisioned object is injected. This functionality is useful for frameworks that wish to provide
 * a default implementation of a type but allow extensions and customizations by installing modules
 * with AdvisesBinder.bindAdvice() bindings.  
 * 
 * {@link Module} method annotations {@literal @}{@link ProvidesWithAdvice} and {@literal @}{@link Advises} 
 * may be used instead of calling AdvisesBinder directly.  
 * 
 * For example,
 *
 * 
 * {@code
   static class AdviseList implements UnaryOperator{@literal <}List{@literal <}String{@literal >}{@literal >} {
        @Override
        public List{@literal <}String{@literal >} apply(List{@literal <}String{@literal >} t) {
            t.add("customized");
            return t;
        }
    }
    
    public static class MyModule extends AbstractModule() {
        TypeLiteral{@literal <}List{@literal <}String{@literal >}{@literal >} LIST_TYPE_LITERAL =  new TypeLiteral{@literal <}List{@literal <}String{@literal >}{@literal >}() {};
        
        @Override
        protected void configure() {
            install(AdvisableAnnotatedMethodScanner.asModule());
            
            AdvisesBinder.bind(binder(), LIST_TYPE_LITERAL).toInstance(new ArrayList{@literal <}{@literal >}());
            AdvisesBinder.bindAdvice(binder(), LIST_TYPE_LITERAL, 0).to(AdviseList.class);
        }
    });
    }

 * 
 * will add "customized" to the empty list.  When List<String> is finally injected it'll have
 * ["customized"].
 * 
 * Note that AdvisesBinder can be used with qualifiers such as {@literal @}Named.
 */
public abstract class AdvisesBinder {
    private AdvisesBinder() {
    }

    public static  LinkedBindingBuilder bind(Binder binder, TypeLiteral type) {
        return newRealAdvisesBinder(binder, Key.get(type));
    }

    public static  LinkedBindingBuilder bind(Binder binder, Class type) {
        return newRealAdvisesBinder(binder, Key.get(type));
    }

    public static  LinkedBindingBuilder bind(Binder binder, TypeLiteral type, Annotation annotation) {
        return newRealAdvisesBinder(binder, Key.get(type, annotation));
    }

    public static  LinkedBindingBuilder bind(Binder binder, Class type, Annotation annotation) {
        return newRealAdvisesBinder(binder, Key.get(type, annotation));
    }

    public static  LinkedBindingBuilder bind(Binder binder, TypeLiteral type, Class annotationType) {
        return newRealAdvisesBinder(binder, Key.get(type, annotationType));
    }

    public static  LinkedBindingBuilder bind(Binder binder, Key key) {
        return newRealAdvisesBinder(binder, key);
    }
    
    public static  LinkedBindingBuilder bind(Binder binder, Class type,  Class annotationType) {
        return newRealAdvisesBinder(binder, Key.get(type, annotationType));
    }
    
    static  Key getAdvisesKeyForNewItem(Binder binder, Key key) {
        binder = binder.skipSources(AdvisesBinder.class);
        
        Annotation annotation = key.getAnnotation();
        String elementName = key.hasAttributes() ? key.getAnnotation().toString() : "";
        AdviceElement element = new AdviceElementImpl(elementName, AdviceElement.Type.SOURCE, 0);
        Key uniqueKey = Key.get(key.getTypeLiteral(), element);
        
        // Bind the original key to a new AdvisedProvider
        binder.bind(key).toProvider(new AdvisedProvider(key.getTypeLiteral(), element.name(), annotation, binder.getProvider(uniqueKey)));

        return uniqueKey;
    }

    static  LinkedBindingBuilder newRealAdvisesBinder(Binder binder, Key key) {
        Key uniqueKey = getAdvisesKeyForNewItem(binder, key);
        return binder.bind(uniqueKey);
    }
    
    public static  LinkedBindingBuilder> bindAdvice(Binder binder, TypeLiteral type, int order) {
        return newRealAdviceBinder(binder, Key.get(type), order);
    }

    public static  LinkedBindingBuilder> bindAdvice(Binder binder, Class type, int order) {
        return newRealAdviceBinder(binder, Key.get(type), order);
    }

    public static  LinkedBindingBuilder> bindAdvice(Binder binder, TypeLiteral type, Annotation annotation, int order) {
        return newRealAdviceBinder(binder, Key.get(type, annotation), order);
    }

    public static  LinkedBindingBuilder> bindAdvice(Binder binder, Class type, Annotation annotation, int order) {
        return newRealAdviceBinder(binder, Key.get(type, annotation), order);
    }

    public static  LinkedBindingBuilder> bindAdvice(Binder binder, TypeLiteral type, Class annotationType, int order) {
        return newRealAdviceBinder(binder, Key.get(type, annotationType), order);
    }

    public static  LinkedBindingBuilder> bindAdvice(Binder binder, Key key, int order) {
        return newRealAdviceBinder(binder, key, order);
    }
    
    public static  LinkedBindingBuilder> bindAdvice(Binder binder, Class type,  Class annotationType, int order) {
        return newRealAdviceBinder(binder, Key.get(type, annotationType), order);
    }
    
    @SuppressWarnings("unchecked")
    static  Key> getAdviceKeyForNewItem(Binder binder, Key key, int order) {
        binder = binder.skipSources(AdvisesBinder.class);
        String elementName = key.hasAttributes() ? key.getAnnotation().toString() : "";
        @SuppressWarnings("unused")
        Annotation annotation = key.getAnnotation();
        
        Type adviceType = Types.newParameterizedType(UnaryOperator.class, key.getTypeLiteral().getType());
        return (Key>) Key.get(adviceType, new AdviceElementImpl(elementName, AdviceElement.Type.ADVICE, order));
    }

    static  LinkedBindingBuilder> newRealAdviceBinder(Binder binder, Key key, int order) {
        Key> uniqueKey = getAdviceKeyForNewItem(binder, key, order);
        return binder.bind(uniqueKey);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy