Guice Configuration Error No implementation was bound

Viewed 6325

I am trying to keep a singleton AmazonSNS to access an SNS. I have written a module for SNS (only one SNS is added currently) and an accessor to publish the message. My code is as follows:

public class SNSModule extends AbstractModule {

    @Override
    protected void configure() {
    }

    @Provides
    @Named("PSSNSRegionName")
    private Regions getPSSNSRegionName(
            @Named(BeanConstants.P_S_SNS_REGION) final String regionName) {
        return Regions.fromName(regionName);
    }

    @Provides
    @Singleton
    @Named(BeanConstants.P_S_SNS)
    public AmazonSNS getPSSNS(
            @NonNull @Named("PaymentSuccessSNSRegionName") final Regions region,
            final Config config) {
        return AmazonSNSClientBuilder.standard()
                .withCredentials(new AWSCredentialsProviderImpl(config.getSnsMaterialSet()))
                .withRegion(region)
                .build();
    }

}

The SNS Accessor is as follows:

@RequiredArgsConstructor(access = AccessLevel.PUBLIC, onConstructor = @__(@Inject))
public class SNSAccessor {

    @Named(BeanConstants.P_S_SNS)
    private final AmazonSNS snsClient;
    private static final String COLON = ":";
    private static final short ARN_LENGTH = 6;

    public PublishResult publishToSNS(@NonNull final String snsTopicArn, @NonNull final String messageToPublish) {
        try {
            String[] arnParts = snsTopicArn.split(COLON);
            Preconditions.checkArgument(
                    snsTopicArn.split(COLON).length == ARN_LENGTH,
                    "Expected arn to have 6 parts but found: " + arnParts.length
            );
            return snsClient.publish(snsTopicArn, messageToPublish);
        } catch (InternalErrorException e) {
            log.error("InternalErrorException publishing notification to SNS", e);
            throw new RetriableException(e);
        } catch (Exception e) {
            log.error("Exception publishing notification to SNS", e);
            throw new NonRetriableException(e);
        }
    }
}

I was able to build the package but it threw ConfigurationException at runtime.

Caused by: com.google.inject.ConfigurationException: Guice configuration errors:

1) No implementation for com.amazonaws.services.sns.AmazonSNS was bound.
  while locating com.amazonaws.services.sns.AmazonSNS
    for parameter 0 at com.xyz.service.sns.SNSAccessor.<init>(SNSAccessor.java:29)

Could you please help me figure out what I am doing wrong? I have installed the SNSModule in main correctly.

4 Answers

Lombok's @RequiredArgsConstructor with onConstructor option does not work with the annotated dependency injections. You need to write a constructor explicitly for this case.

The configurations that you are mapping and binding in SNSModule looks correct.

Since you have not posted the main application class code. I suspect you might have missed out to create an injector on SNSModule. Add the following lines in the main-application-class:

Injector injector = Guice.createInjector(SNSModule);

The solution is quite tricky, the following worked for me on a sample project.
lombok 1.8.4 Guice 4.2.2

You should create your own annotation and replace the @Named annotation with it see BindingAnnotations
e.g.

package org.company.PSSNS

import com.google.inject.BindingAnnotation;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@BindingAnnotation
@Target({PARAMETER,METHOD,FIELD})
@Retention(RUNTIME)
public @interface PSSNS {}

Then add a lombok.config file see here

in the file add the following line

lombok.copyableAnnotations += org.company.PSSNS

in your class

@RequiredArgsConstructor(access = AccessLevel.PUBLIC, onConstructor = @__(@Inject))
public class SNSAccessor {

    @PSSNS
    private final AmazonSNS snsClient;
    private static final String COLON = ":";
    private static final short ARN_LENGTH = 6;

Replace it in the Guice Module as well

public class SNSModule extends AbstractModule {


    @Provides
    @Singleton
    @PSSNS
    public AmazonSNS getPSSNS(
            @NonNull @Named("PaymentSuccessSNSRegionName") final Regions region,
            final Config config) {
        return AmazonSNSClientBuilder.standard()
                .withCredentials(new AWSCredentialsProviderImpl(config.getSnsMaterialSet()))
                .withRegion(region)
                .build();
    }

}

When I tested that I needed to clean build from maven

Look at the generated class and verify you can see it on the constructor.
This should allow Guice to inject all your constructor arguments.

Lombok doesn't copy any annotations on the fields to the constructor parameters by default. If you want to keep using @RequiredArgsConstructor(access = AccessLevel.PUBLIC, onConstructor = @__(@Inject)), then add configurations for the same in your lombok.config file something like this:

lombok.copyableAnnotations += com.google.inject.name.Named

Related