2

I am trying to re-use a guice module from a Library which has multiple providers, I want to use few providers from the library and provide few in my own module.

Library Module -

public class LibraryModule extends AbstractModule {

    @Override
    protected void configure() {
    }

    @Provides
    @Singleton
    @Named("dbCredentials")
    private AWSCredentialsProvider getCredentialsProvider(@Named("app.keySet") String keySet) {
        return new SomeCredentialsProvider(keySet);
    }

    @Provides
    @Singleton
    private AmazonDynamoDB getDynamoDBClient(@Named("dbCredentials") AWSCredentialsProvider credentialsProvider,
                                             @Named("aws.region") String region) {

        return AmazonDynamoDBClientBuilder.standard()
                                          .withCredentials(credentialsProvider)
                                          .withRegion(region)
                                          .build();
    }

    @Provides
    @Singleton
    @Named("dbMapper")
    private DynamoDBMapper getDynamoDBMapper(AmazonDynamoDB dynamoDBClient) {

        return new DynamoDBMapper(dynamoDBClient);
    }
...... more providers using  dbMapper

Now where I want to use this module I want to give some different implementation of AmazonDynamoDB which uses the default credential provider.

public class MyModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(AmazonDynamoDB.class).toProvider(AmazonDynamoDBClientProvider.class).in(Singleton.class);
        install(new LibraryModule());
    }

AmazonDynamoDBClientProvider class -

public class AmazonDynamoDBClientProvider implements Provider<AmazonDynamoDB> {

    private final String region;

    @Inject
    public AmazonDynamoDBClientProvider(@Named("aws.region") String region) {
        this.region = region;
    }

    @Override
    public AmazonDynamoDB get() {
        return AmazonDynamoDBClientBuilder.standard()
                                          .withRegion(region)
                                          .build();
    }
}

but when I try to do this it fails in the provider of library while trying to create the AmazonDynamoDB by saying A binding to com.amazonaws.services.dynamodbv2.AmazonDynamoDB was already configured

I wanted to know if it is possible to omit providers for classes which have already been bound in the parent module? If yes how do we do that? I was unable to find a solution for this problem.

iluxa
  • 6,941
  • 18
  • 36
Manthan Jamdagni
  • 894
  • 2
  • 17
  • 31

1 Answers1

1

If you are in the position to change LibraryModule, then you should give it flexibility to bind either its default implementation or the one you're supplying:

class LibraryModule extends AbstractModule {

  // Default implementation based on the code you've shown in LibraryModule
  static class DefaultDynamoDBProvider implements Provider<AmazonDynamoDB> {
    private final AWSCredentialsProvider credentialsProvider;
    private final String region;

    @Inject DefaultDynamoDBProvider(
        @Named("dbCredentials") AWSCredentialsProvider credentialsProvider,
        @Named("aws.region") String region) {
      this.credentialsProvider = credentialsProvider;
      this.region = region;
    }
    @Override AmazonDynamoDB get() {
      return AmazonDynamoDBClientBuilder
          .standard()
          .withCredentials(credentialsProvider)
          .withRegion(region)
          .build();
    }    
  }

  private Class<Provider<AmazonDynamoDB>> dynamoDbProviderClass;

  // if constructed with this constructor, uses default provider
  LibraryModule() {
    this(DefaultDynamoDBProvider.class);
  }

  // here, uses the supplied one. Builder pattern would be better
  LibraryModule(Class<Provider<AmazonDynamoDB>> providerClass) {
    this.dynamoDbProviderClass = providerClass;
  }

  protected void configure() {
    bind(AmazonDynamoDB.class)
        .toProvider(dynamoDbProviderClass)
        .in(Singleton.class);
  }
}

If however touching LibraryModule is impossible, take a look at Overriding Binding in Guice, while keeping in mind that overriding bindings is an anti-pattern and should be avoided at all costs:

  • Modules.override javadoc says "Prefer to write smaller modules that can be reused and tested without overrides," and supplies examples having to do with unit testing

  • Guice bindings are hard to track down, but at least there's a promise that once you've found the binding, it's the right one. Modules.override() complicate this task further.

  • Modules are supposed to be independent of one another. In our scenario, someone may have installed LibraryModule and connector to Dynamo just fine, but once he also installed OrderProcessingModule (let's assume that's what you're writing), his connection broke. Now he has no recourse: it's either Dynamo or Order Processing, or yet another even-more-complicated override

iluxa
  • 6,941
  • 18
  • 36