Lambda & SNS & Spring - integration

Viewed 759

This is how I am dealing with SNS events in Lambda:

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.SNSEvent;

public class SnsHandler implements RequestHandler<SNSEvent, Object> {

    public Object handleRequest(SNSEvent event, Context context) {
        System.out.println("sns event");
        System.out.println(event.getRecords().get(0).getSNS().getMessage());
        return null;
    }
}

I got already Spring application which works with API gateway HTTP requests via SpringLambdaContainerHandler. This is how I am integrating it with lambda:

import com.amazonaws.serverless.exceptions.ContainerInitializationException;
import com.amazonaws.serverless.proxy.internal.model.AwsProxyRequest;
import com.amazonaws.serverless.proxy.internal.model.AwsProxyResponse;
import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.maciejsobala.aws.webpush.AwsWebPushApp;

public class HttpHandler implements RequestHandler<AwsProxyRequest, AwsProxyResponse> {

    SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
    public AwsProxyResponse handleRequest(AwsProxyRequest awsProxyRequest, Context context) {

        awsProxyRequest.setPath("/" + awsProxyRequest.getPathParameters().get("proxy")); //because I am using {proxy} in my API gateway
        if (handler == null) {
            try {
                handler = SpringLambdaContainerHandler.getAwsProxyHandler(AwsWebPushApp.class);
            } catch (ContainerInitializationException e) {
                e.printStackTrace();
                return null;
            }
        }
        return handler.proxy(awsProxyRequest, context);
    }
}

Now I would like to reuse my Spring application (I got some repository and other logic there) in the SnsHandler.

My first approach was to reuse similar code as in HttpHandler (if that would work, I wanted to create separate endpoint for sns action and protect it somehow, to be executable only from SnsHandler):

public class SnsHandler implements RequestHandler<SNSEvent, Object> {

    SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
    AwsProxyRequest awsProxyRequest = new AwsProxyRequest();
    ApiGatewayRequestContext requestContext = new ApiGatewayRequestContext();
    ApiGatewayRequestIdentity identity = new ApiGatewayRequestIdentity();

    {
        awsProxyRequest.setPath("/ping");
        awsProxyRequest.setHttpMethod("GET");
        identity.setSourceIp("someIp");
        requestContext.setIdentity(identity);
        awsProxyRequest.setRequestContext(requestContext);
    }

    public Object handleRequest(SNSEvent event, Context context) {
        System.out.println("sns event");
        System.out.println(event.getRecords().get(0).getSNS().getMessage());


        AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
        applicationContext.register(AwsWebPushApp.class);


        if (handler == null) {
            try {
                handler = SpringLambdaContainerHandler.getAwsProxyHandler(AwsWebPushApp.class);
            } catch (ContainerInitializationException e) {
                e.printStackTrace();
                return null;
            }
        }
        handler.proxy(awsProxyRequest, context);
        return null;
    }
}

Unfornatelly I got

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
... some stack trace...
Caused by: java.lang.NullPointerException
at org.springframework.web.util.UrlPathHelper.getRemainingPath(UrlPathHelper.java:245)
...rest of the stack trace...

I got two questions here:

  1. What is the correct approach (that what I am trying to implement is only a work-around), to integrate SNS with Spring application, inside Lambda function?
  2. If there is no better approach, how can I get raw of the given exception. In other words: How should I properly fake http request?

I took a look at http://cloud.spring.io/spring-cloud-aws/spring-cloud-aws.html but as far as I can see, that is not fitting into Lambda env.

1 Answers

To run a Spring application from a non HTTP trigger in AWS Lambda I would suggestion looking at the Spring Cloud Functions project.

This will provide you Spring support with a Lambda function that can be triggered by an SNS event.

Related