Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guice: How to change injection on runtime based on a (dynamic web property)

The following is an approximation of the problem I'm facing.

Think we have a password validator with some rules.

public interface RuleChecker{

    //Checks for a password strenght, returns 10
    //for strong or 0 for soft password.
    int check(String pass);
}

And then we have several implementations, our service will only accept the password if it is over 8 score.

public class NoCheck implements RuleChecker {
    public int check(String pass){return 10;}
}

public class LengthCheck implements RuleChecker{
    ...
}
public class AlphanumericCheck implements RuleChecker{
    ...
}
public class AlphaAndLenghtCheckAdapter implements RuleChecker{
    ...
}

But for testing purposes, we want to implement a webservice within the application where we can "admin" those rules, and select which ones to have.

public class PasswordCheckService{

     private RuleChecker checker;

     @Inject
     public PasswordCheckService(final RuleChecker checker){
         this.checker = checker;
     }

     public boolean checkPassword(String password){
         return checker.check(password) > 8;
     }
}

So, is there any way in Guice, to change at runtime, the injection a service has?

Example:

We started the application and by default LengthCheck is selected and injected on the application, at the website we select the NoCheck checkbox and save options, which is stored into the database, can I configure Guice to automatically change the bean the service had injected before? so from now and on there will be no checks on new passwords?

--

As for now, I have found those topics

Google Guice and varying injections at runtime But i dont know if that kind of providers fits my problem.

Guice runtime dependency parameters reinjection That nice question is talking something similar, but not what I'm looking form.

guice: runtime injection/binding at command line This is the closest to my problem but he only does on starting "runtime" and does not change it over the time.

Any helps?

Thank you!

Using the tip of the first comment I implemented this POC but still does not works, if you change select another button the service bean is not updated. https://bitbucket.org/ramonboza/guicedynamicconfig

like image 292
RamonBoza Avatar asked Oct 14 '13 10:10

RamonBoza


People also ask

How does dependency injection work Guice?

Using Guice If it's a simple object, it'll instantiate it and pass it in. If it has dependencies, it will resolve those dependencies, pass them into it's constructor, then pass the resulting object into your object.

What does @inject do Guice?

Note that the only Guice-specific code in the above is the @Inject annotation. This annotation marks an injection point. Guice will attempt to reconcile the dependencies implied by the annotated constructor, method, or field.

What is @named annotation in Guice?

Guice comes with a built-in binding annotation @Named that takes a string: public class RealBillingService implements BillingService { @Inject public RealBillingService(@Named("Checkout") CreditCardProcessor processor, TransactionLog transactionLog) { ... }

What is dependency injection Guice?

Guice is an open source, Java-based dependency injection framework. It is quiet lightweight and is actively developed/managed by Google. This tutorial covers most of the topics required for a basic understanding of Google Guice and to get a feel of how it works.


1 Answers

Create a provider for each field type (login, password, birth date...), with a parameter to change the implementation to return.

public class MyModule extends AbstractModule {

    public void configure() {
       bind(RuleChecker.class).annotatedWith(named("password")).toProvider(PasswordRuleCheckerProvider.class);
       bind(RuleChecker.class).annotatedWith(named("login")).toProvider(LoginRuleCheckerProvider.class);
    }
}

public static class PasswordRuleCheckerProvider implements Provider<RuleChecker> {

    private static CheckType type = CheckType.ALPHANUMERIC;
    // static type setter.

    public RuleChecker get() {
        // it would even be better if you could use singletons here.
        switch(type) {
            case LENGTH:
                return new LengthCheck();
            case ALPHANUMERIC:
                return new AlphanumericCheck();
            case ALPHALENGTH:
                return new AlphaAndLenghtCheckAdapter();
            case NONE:
            default:
                return NoCheck();
        }
    }
}
// Almost same provider for your LoginRuleCheckerProvider. You could do something generic.

In your admin section you change "type" value, so your rules will change. It can affect a limited set of fields, thanks to the annotations. For instance : PasswordRuleCheckerProvider.setType(CheckType.LENGTH);. Will only affect fields with @Named('password').

You have to declare your fields and services like this :

public abstract class DynamicService {
    protected void updateService() {
        // Reinject with the new implementations the members.
        App.getInjector().injectMembers(this);
    }
}

public class PasswordCheckService extends DynamicService  {
    @Inject
    @Named("password")
    private RuleChecker passwordChecker;

    public void changePasswordCheckType(CheckType type) {
        PasswordRuleCheckerProvider.setType(type);
        // Reinject, so you have your new implementation.
        updateService();
    }

    // [...]
}
like image 52
Vianney Dupoy de Guitard Avatar answered Oct 12 '22 03:10

Vianney Dupoy de Guitard