I could need some help here...
I'm using Spring Cloud Function, and I want to deploy my functions on AWS Lambda, using the adapter for AWS.
My application class looks like this:
package example;
@SpringBootApplication
public class SpringCloudFunctionApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudFunctionApiGatewayApplication.class, args);
}
}
Function 1 looks like this:
package example;
@Component
public class StoreFunction implements Consumer<Message<DemoEntity>>{
@Override
public void accept(Message<DemoEntity> t) {
System.out.println("Stored entity " + ((DemoEntity)t.getPayload()).getName());
return;
}
}
Finally, my function handler looks like this:
package example;
public class TestFunctionHandler extends SpringBootApiGatewayRequestHandler {
}
This setup works perfectly. When deploying to Lambda, I provide example.TestFunctionHandler as handler in the AWS console, and Spring Cloud recognizes automatically that example.QueryFunction is the only function in the context.
The log output looks like this:
START RequestId: 3bd996e7-ef5e-11e8-9829-1f50e2b93b6c Version: $LATEST
20:27:45.821 [main] INFO org.springframework.cloud.function.adapter.aws.SpringFunctionInitializer - Initializing: class de.margul.awstutorials.springcloudfunction.apigateway.SpringCloudFunctionApiGatewayApplication
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot ::
2018-11-23 20:27:48.221 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : Starting LambdaRTEntry on ip-10-153-127-174.ec2.internal with PID 1 (/var/runtime/lib/LambdaJavaRTEntry-1.0.jar started by sbx_user1060 in /)
2018-11-23 20:27:48.242 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : No active profile set, falling back to default profiles: default
2018-11-23 20:27:52.081 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : Started LambdaRTEntry in 5.941 seconds (JVM running for 7.429)
Stored entity John Doe
END RequestId: 3bd996e7-ef5e-11e8-9829-1f50e2b93b6c
REPORT RequestId: 3bd996e7-ef5e-11e8-9829-1f50e2b93b6c Duration: 7113.98 ms Billed Duration: 7200 ms Memory Size: 1088 MB Max Memory Used: 113 MB
Now, my problem comes here. I want to have multiple functions in one project. I know, on Lambda there can be only one function per deployment. However, for code maintenance reasons (there is some shared code as well as configurations in the real project), we want to have all functions in one project, deploy the project multiple times, and define in the deployment, which is the relevant function.
Using the native AWS SDK for Lambda, that was easy (like in this example in section 4.2): One implementation of RequestStreamHandler, with multiple methods (even if RequestStreamHandler has only one handleRequest() method). The point was that one could define the relevant function as handler: package.ClassName::methodName
However, that does not work with Spring Cloud Function (as we can have only one handler, which is TestFunctionHandler in this case). The documentations mentions that multiple functions are possible, by specifying function.name in application.properties, or as Lambda environment variable FUNCTION_NAME. Anyhow, I don't get that working.
My function 2 looks like this:
package example;
@Component
public class QueryFunction implements Function<Message<String>, Message<DemoEntity>>{
@Override
public Message<DemoEntity> apply(Message<String> m) {
String name = m.getPayload();
DemoEntity response = new DemoEntity();
response.setName(name);
Message<DemoEntity> message = MessageBuilder
.withPayload(response)
.setHeader("contentType", "application/json")
.build();
return message;
}
}
In my application.properties, I have this line:
function.name = example.StoreFunction
The same applies if I create an environment variable FUNCTION_NAME: example.StoreFunction
If I now deploy the library and trigger it, I get the following logs:
START RequestId: 67e64098-ef5d-11e8-bdbf-9ddadadef0ce Version: $LATEST
20:21:50.802 [main] INFO org.springframework.cloud.function.adapter.aws.SpringFunctionInitializer - Initializing: class example.SpringCloudFunctionApiGatewayApplication
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot ::
2018-11-23 20:21:53.684 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : Starting LambdaRTEntry on ip-10-153-127-174.ec2.internal with PID 1 (/var/runtime/lib/LambdaJavaRTEntry-1.0.jar started by sbx_user1059 in /)
2018-11-23 20:21:53.687 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : No active profile set, falling back to default profiles: default
2018-11-23 20:21:57.488 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : Started LambdaRTEntry in 6.353 seconds (JVM running for 8.326)
No function defined: java.lang.IllegalStateException
java.lang.IllegalStateException: No function defined
at org.springframework.cloud.function.adapter.aws.SpringFunctionInitializer.apply(SpringFunctionInitializer.java:134)
at org.springframework.cloud.function.adapter.aws.SpringBootRequestHandler.handleRequest(SpringBootRequestHandler.java:48)
END RequestId: 67e64098-ef5d-11e8-bdbf-9ddadadef0ce
REPORT RequestId: 67e64098-ef5d-11e8-bdbf-9ddadadef0ce Duration: 7454.73 ms Billed Duration: 7500 ms Memory Size: 1088 MB Max Memory Used: 130 MB
Every help is highly appreciated!
Ok, the problem was obviously my limited knowledge about Spring Beans.
After I viewed a list of all available beans in the context, it was clear that I had to use the class name, but starting with a lower case, i. e. function.name = storeFunction or function.name = queryFunction.
Edit to explain my solution in detail:
In my project, I have several functions like this:
@Component
public class StoreFunction implements Consumer<String>{
@Override
public void accept(String s) {
// Logic comes here
}
}
@Component
public class QueryFunction implements Function<String, String>{
@Override
public void apply(String s) {
return s;
}
}
Then, I register them as beans, e. g. like this:
@SpringBootApplication
public class SpringCloudFunctionApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudFunctionApiGatewayApplication.class, args);
}
@Bean
StoreFunction storeFunction(){
return new StoreFunction();
}
@Bean
QueryFunction queryFunction(){
return new QueryFunction();
}
}
Now, I have the two beans storeFunction and queryFunction (the names of the @Bean methods above) available in my Spring context.
Finally, I have to tell Spring which of the functions to call. That can be done by creating an environment variable FUNCTION_NAME and setting it to one of the bean names.
When I now deploy the project to AWS Lambda, I have to tell Lambda which function the invoke (as Lambda only can invoke one function per deployment).
Btw, I created a tutorial for that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With