3

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.

Haim Raman
  • 11,508
  • 6
  • 44
  • 70
tanvi
  • 927
  • 5
  • 17
  • 32
  • 1
    I notice that you are using lombock, I suspect the mixture of \@Named used on private final AmazonSNS snsClient ( i.e parameter 0) and \@RequiredArgsConstructor usage. lombock will add it as the first argument in the contractor – Haim Raman Jun 13 '19 at 11:03

4 Answers4

2

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

Deepak gupta
  • 109
  • 1
  • 5
  • I am not sure it will not work, is should work in general but you should workaround the \@Named annotation see https://stackoverflow.com/questions/42910148/lombok-and-guice-injection – Haim Raman Jun 13 '19 at 11:20
  • This did not work for me. Guice is now listing two errors: 1) No implementation for com.amazonaws.services.sns.AmazonSNS was bound. while locating com.amazonaws.services.sns.AmazonSNS for parameter 0 at com.amazon.madonnaservice.sns.SNSAccessor.(SNSAccessor.java:35)2) No implementation for com.amazonaws.services.sns.AmazonSNS was bound. while locating com.amazonaws.services.sns.AmazonSNS for parameter 0 at com.amazon.madonnaservice.sns.SNSAccessor.(SNSAccessor.java:35) – tanvi Jun 14 '19 at 07:16
  • See my example, hope this will help – Haim Raman Jun 14 '19 at 14:55
  • I removed the RequiredArgsConstructor annotation and used the Named annotation with the constructor. That worked. – tanvi Jun 18 '19 at 10:21
1

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);
  • I am using install(new SNSModule()); in the main application class. – tanvi Jun 13 '19 at 09:22
  • install uses the passed module in parameter for configuring bindings. You'll have to create an injector. Try it out, it shall work. For reference please checkout http://blog.muhuk.com/2015/05/28/using_guice_effectively.html#.XQIXx7ozb_g – Piyush Upadhyay Jun 13 '19 at 09:43
  • Okay. I looked into the codebase. There is a Injector injector = createInjector(new ServiceModule()); and install(new SNSModule()); is written in the configure() method of the same module. There are a lot of other modules being initialised this way in the same codebase. – tanvi Jun 13 '19 at 09:57
1

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.

Haim Raman
  • 11,508
  • 6
  • 44
  • 70
0

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

Jayant
  • 323
  • 3
  • 13