Reputation: 31
I have a Spring Boot demo project. I am trying to deploy them in AWS Lambda, but I get ClassNotFoundException even thought my jar that I upload contains of the necessary dependencies.
Here goes my code:
pom.xml
<dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-events</artifactId>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>com.amazonaws.serverless</groupId>
<artifactId>aws-serverless-java-container-spring</artifactId>
<version>[0.1,)</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Application Class
@SpringBootApplication
public class DemoApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
System.out.println("Welcome to demo project");
}
}
Controller Class
@RestController
public class DemoController {
@GetMapping(value="/getValue")
public String getId() {
return " Call from controller";
}
}
LambdaHandler Class
public class DemoLambdaHandler implements RequestStreamHandler {
public static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
static {
try {
handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(DemoApplication.class);
} catch (ContainerInitializationException e) {
e.printStackTrace();
throw new RuntimeException("Container not initialized", e);
}
}
@Override
public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException {
// TODO Auto-generated method stub
handler.proxyStream(input, output, context);
}
}
Not sure what I could be missing in this. Kindly help. Below is my inspected jar
Upvotes: 2
Views: 5941
Reputation: 31
I had the same issue, I fixed it marking Spring Boot as a dependency.
By default, Spring Boot plugin will pack your classes into "BOOT-INF/classes" directory, but AWS is looking for the handler class from the root directory so it can't find it.
You can check this by extracting your .jar file and seeing if your file is in:
BOOT-INF/classes/com/example/demo/handler/LambdaHandler.class
instead of:
com/example/demo/handler/LambdaHandler.class
To solve it, just mark your package as exec in your pom.xml file:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.6.1</version>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
More info here: Spring Boot as a Dependency
Upvotes: 2
Reputation: 1
Please add these two dependencies in pomfile
1)aws-lambda-java-core
2)aws-lambda-java-events
This should fix it
Upvotes: -2
Reputation: 10734
Try building the project with Maven and ensure you have the required maven-shade-plugin in your POM so that the JAR that is built with the dependencies when you run mvn package. If you are missing this plugin, you will create a JAR that does not contain the dependencies and you will encounter ClassNotFoundException.
To learn how to build and deploy a Lambda function by using the Lambda runtime Java API, see this AWS tutorial. It will walk you step by step through the process of building Lambda functions that work:
This tutorial then uses the Lambda functions within Step Functions to create a workflow.
Lambda functions are created using the Lambda runtime Java API -- com.amazonaws.services.lambda.runtime.RequestHandler. Please do not use Spring BOOT APIs to create a Lambda function.
However, If you want to invoke a Lambda function from a Spring boot app and then for example display the result in a view, then you can use the Lambda Client Java API - which is software.amazon.awssdk.services.lambda.LambdaClient.
So in summary:
com.amazonaws.services.lambda.runtime.RequestHandler is used to create a Lambda function by using the Java Lambda runtime API.
software.amazon.awssdk.services.lambda.LambdaClient is the Lambda client Java API V2 that lets you interact with deployed Lambda functions. For example, you can use this client to invoke a Lambda function from a Java app - like a Spring boot web app. To see a working example of how to use the Lambda client API to invoke a Lambda function, see https://github.com/awsdocs/aws-doc-sdk-examples/blob/master/javav2/example_code/lambda/src/main/java/com/example/lambda/LambdaInvoke.java.
Upvotes: 0
Reputation: 684
Did you find a solution to this? I have been getting the same exact error.
What I can tell so far is that unlike other languages like nodejs, the docker image for java has the command specified like CMD [ "com.example.LambdaHandler::handleRequest" ]
, instead the nodejs one is CMD [ "app.handler" ]
. The difference here is that the nodejs one specifies what the file is e.g. app.js with function called handler.
But the java one only has the class path without the ability to specify the path to the fat JAR file even when the jar file is located in "var/task". How would lambda know the JAR file name?
Upvotes: 2