amazon-web-servicesspring-bootaws-lambda

Class Not found com.cicdpipeline.LambdaHandler: in AWS Lambda Springboot project deployment


My existing project I am trying to convert to make to deploy using AWS lambda. And when I modified pom.xml dependencies and added handler class and deploying to lambda function, I am getting the following error,

{
"errorMessage": "Class not found: com.cicdpipeline.LambdaHandler",
"errorType": "java.lang.ClassNotFoundException"
}

I added following dependencies in pom.xml

 <dependency>
   <groupId>com.amazonaws</groupId>
   <artifactId>aws-lambda-java-core</artifactId>
   <version>1.2.1</version>
 </dependency>
<dependency>
   <groupId>com.amazonaws.serverless</groupId>
   <artifactId>aws-serverless-java-container-spring</artifactId>
   <version>1.9.1</version>
  </dependency>

and added following plugin,

<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-shade-plugin</artifactId>
 <version>3.3.0</version>
 <configuration>
    <createDependencyReducedPom>false</createDependencyReducedPom>
 </configuration>
 <executions>
    <execution>
        <phase>package</phase>
        <goals>
            <goal>shade</goal>
        </goals>
        <configuration>
            <artifactSet>
                <excludes>
                    <exclude>org.apache.tomcat.embed:*</exclude>
                </excludes>
             </artifactSet>
        </configuration>
     </execution>
   </executions>
 </plugin>

My main application file,

package com.cicdpipeline;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

 @SpringBootApplication
  public class CicdpipelineApplication {

  public static void main(String[] args) {
    SpringApplication.run(CicdpipelineApplication.class, args);
  }
}

Lambda Handler class I added the same place where main app resides (com.cicdpipeline)

package com.cicdpipeline;

import javax.ws.rs.core.Application;

import com.amazonaws.serverless.exceptions.ContainerInitializationException;
import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;


public class LambdaHandler implements RequestStreamHandler {
private static SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
static {
    try {
        handler = SpringLambdaContainerHandler.getAwsProxyHandler(CicdpipelineApplication.class);
    } catch (ContainerInitializationException e) {
        // Re-throw the exception to force another cold start
        e.printStackTrace();
        throw new RuntimeException("Could not initialize Spring Boot application", e);
    }
}
@Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
    handler.proxyStream(inputStream, outputStream, context);
    // just in case it wasn't closed by the mapper
    outputStream.close();
}
}

And In Lambda function Runtime settings I added package.class.method like the following,

 com.cicdpipeline.LambdaHandler::SpringLambdaContainerHandler

How to resolve the problem while converting existing springboot project to support aws lambda deployment? or any good documentation for reference?


Solution

  • looks like configuration not happening correctly, here is rough sample code to kick start your application,

    your pom.xml with all complete all needed must have dependencies:

    <dependencies>
        <!-- Spring Boot Starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    
        <!-- AWS Lambda Core -->
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-core</artifactId>
            <version>1.2.1</version>
        </dependency>
    
        <!-- AWS Lambda Events -->
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-events</artifactId>
            <version>3.11.1</version>
        </dependency>
    
        <!-- AWS Serverless Java Container -->
        <dependency>
            <groupId>com.amazonaws.serverless</groupId>
            <artifactId>aws-serverless-java-container-spring</artifactId>
            <version>1.9.1</version>
        </dependency>
    </dependencies>
    

    use correct plugin configuration:

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.3.0</version>
        <configuration>
            <createDependencyReducedPom>false</createDependencyReducedPom>
        </configuration>
        <executions>
            <execution>
                <phase>package</phase>
                <goals>
                    <goal>shade</goal>
                </goals>
                <configuration>
                    <transformers>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                            <mainClass>com.cicdpipeline.CicdpipelineApplication</mainClass>
                        </transformer>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                    </transformers>
                </configuration>
            </execution>
        </executions>
    </plugin>
    

    Fix your LambdaHandler java class

    package com.cicdpipeline;
    
    import com.amazonaws.serverless.exceptions.ContainerInitializationException;
    import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
    import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
    import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
    import com.amazonaws.services.lambda.runtime.Context;
    import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    public class LambdaHandler implements RequestStreamHandler {
        private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
    
        static {
            try {
                handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(CicdpipelineApplication.class);
            } catch (ContainerInitializationException e) {
                // Re-throw the exception to force another cold start
                throw new RuntimeException("Could not initialize Spring Boot application", e);
            }
        }
    
        @Override
        public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) 
                throws IOException {
            handler.proxyStream(inputStream, outputStream, context);
        }
    }
    

    Update your main java class in your case is Spring Boot App right,

    package com.cicdpipeline;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.servlet.HandlerAdapter;
    import org.springframework.web.servlet.HandlerMapping;
    import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
    import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
    
    @SpringBootApplication
    public class CicdpipelineApplication {
    
        @Bean
        public HandlerMapping handlerMapping() {
            return new RequestMappingHandlerMapping();
        }
    
        @Bean
        public HandlerAdapter handlerAdapter() {
            return new RequestMappingHandlerAdapter();
        }
    
        public static void main(String[] args) {
            SpringApplication.run(CicdpipelineApplication.class, args);
        }
    }
    

    The Lambda handler setting in AWS Console must be look like such only,

    com.cicdpipeline.LambdaHandler::handleRequest
    

    instead of that your i seen wrong typo thing,

    com.cicdpipeline.LambdaHandler::SpringLambdaContainerHandler
    

    pplication.propeties file in src/main/resources:

    spring.main.web-application-type=servlet
    spring.main.banner-mode=off
    logging.level.root=INFO
    

    Build the project:

    mvn clean package
    

    controller:

    package com.cicdpipeline.controller;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class TestController {
        
        @GetMapping("/test")
        public String test() {
            return "Hello from Lambda!";
        }
    }
    

    try to setup like This should work with no issues, Ref. https://docs.aws.amazon.com/codedeploy/latest/userguide/applications-create-lambda.html