14
votes

I have a java aws lambda function or handler as AHandler that does some stuff e.g. It has been subscribed to SNS events, It parses that SNS event and log relevant data to the database.

I have another java aws lambda BHandler, Objective of this BHandler to receive a request from AHandler and provide a response back to AHandler. Because BHandler's objective is to provide a response with some json data. and that would be used by the AHandler.

May I see any clear example which tells how we can do such things ?

I saw this example call lambda function from a java class and Invoke lambda function from java

My question talks about that situation, when one aws java lambda function (or handler) calls to another aws java lambda function when both are in same region, same account,same vpc execution stuff, same rights. In that case aws java lambda function can directly call( or invoke) to another or still it has to provide aws key,region etc stuff (as in above links) ? A clear example/explanation would be very helpful.

EDIT

The AHandler who is calling another Lambda function (BHandler) , exist on same account have given complete AWSLambdaFullAccess with everything e.g.

“iam:PassRole", "lambda:*",

Here is the code to call :

Note : Below code works when I call the same function with everything same from a normal java main function. But its not working like calling from on lambda function (like ALambdaHandler calling BLambdaHandler as a function call). Even its not returning any exception. Its just showing timeout, its got stuck at the code of: lambdaClient.invoke

String awsAccessKeyId = PropertyManager.getSetting("awsAccessKeyId");
        String awsSecretAccessKey = PropertyManager.getSetting("awsSecretAccessKey");
        String regionName = PropertyManager.getSetting("regionName");
        String geoIPFunctionName = PropertyManager.getSetting("FunctionName");

        Region region;
        AWSCredentials credentials;
        AWSLambdaClient lambdaClient;

        credentials = new BasicAWSCredentials(awsAccessKeyId,
                awsSecretAccessKey);

        lambdaClient = (credentials == null) ? new AWSLambdaClient()
                : new AWSLambdaClient(credentials);
        region = Region.getRegion(Regions.fromName(regionName));
        lambdaClient.setRegion(region);


        String returnGeoIPDetails = null;

        try {


            InvokeRequest invokeRequest = new InvokeRequest();
            invokeRequest.setFunctionName(FunctionName);
            invokeRequest.setPayload(ipInput);


            returnDetails = byteBufferToString(
                    lambdaClient.invoke(invokeRequest).getPayload(),
                    Charset.forName("UTF-8"),logger);
        } catch (Exception e) {

            logger.log(e.getMessage());
        }

EDIT I did everything as suggested by others and followed everything. At the end I reached to AWS support, and the problem was related to some VPC configurations stuff, and that got solved.If you have encountered similar stuff, then may be check security configs, VPC stuff.

4
The only way for anyone to invoke a Lambda function is via the Lambda API. It doesn't matter if they are in the same account, region, or even on the same physical machine (which you would have no way of knowing anyway). You have to use the InvokeFunction API call.garnaat
Seems that does matter, when i call aws lambda function from a independent java program from laptop (intellij)it works well, but when i call the same aws lambda function from another aws lambda function on same account, same region , it gives timeout. there would be different configuration for both, but not sure where such documentation or example exist.Sumit Arora
BTW, you should not specify aws credentials in Lambda environment. This is bad design. You should initiate LambdaClient like new AWSLambdaClient(new DefaultAWSCredentialsProviderChain()); in this case AWS sdk runs with the permissions that you have in your execution role.Çağatay Gürtürk
Hi Sumit, What exactly was the VPC config that AWS Support recommend? I am facing similar issue but couldn't get a solution even after trying almost everything suggested in the answers below.Rupesh
@rupesh , I did that work few years ago , so now I am away with this and involved in other work , so I do not have any detailsSumit Arora

4 Answers

14
votes

We have achieved this by using com.amazonaws.services.lambda.model.InvokeRequest. Here is code sample.

public class LambdaInvokerFromCode {
     public void runWithoutPayload(String functionName) {
            runWithPayload(functionName, null);
        }

        public void runWithPayload(String functionName, String payload) {
            AWSLambdaAsyncClient client = new AWSLambdaAsyncClient();
            client.withRegion(Regions.US_EAST_1);

            InvokeRequest request = new InvokeRequest();
            request.withFunctionName(functionName).withPayload(payload);
            InvokeResult invoke = client.invoke(request);
            System.out.println("Result invoking " + functionName + ": " + invoke);
    }



    public static void main(String[] args) {
            String KeyName ="41159569322017486.json";
            String status = "success";
            String body = "{\"bucketName\":\""+DBUtils.S3BUCKET_BULKORDER+"\",\"keyName\":\""+KeyName+"\", \"status\":\""+status+"\"}";
            System.out.println(body);

            JSONObject inputjson = new JSONObject(body); 
            String bucketName = inputjson.getString("bucketName");
            String keyName = inputjson.getString("keyName");
            String Status = inputjson.getString("status");
            String destinationKeyName = keyName+"_"+status;
            LambdaInvokerFromCode obj = new LambdaInvokerFromCode();
            obj.runWithPayload(DBUtils.FILE_RENAME_HANDLER_NAME,body);
        }
}
2
votes

Edit: For such a scenario, consider using Step Functions.

1
votes

We had similar problem and tried to gather various implementations to achieve this. Turns out it had nothing to do with the code.

Few basic rules:

  1. Ensure proper policy and role for your lambda function, at minimum: { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:::" }, { "Effect": "Allow", "Action": [ "lambda:InvokeFunction" ], "Resource": [ "" ] } ] }
  2. Have functions in same regions.

  3. No VPC configurations needed. If your applications have VPC, make sure your lambda function has appropriate role policy (refer AWSLambdaVPCAccessExecutionRole)

  4. Most important (primarily why it was failing for us), set right timeouts and heap sizes. Calling Lambda is going to wait until called one is finished. Simple math of 2x the called lambda values works. Also this was only with java lambda function calling another java lambda function. With node js lambda function calling another lambda function did not have this issue.

Following are some implementations that works for us:

  1. Using service interface


    import com.amazonaws.regions.Regions;
    import com.amazonaws.services.lambda.AWSLambdaAsyncClientBuilder;
    import com.amazonaws.services.lambda.invoke.LambdaInvokerFactory;
    import com.amazonaws.services.lambda.runtime.Context;
    import com.amazonaws.services.lambda.runtime.RequestHandler;

    public class LambdaFunctionHandler implements RequestHandler {

        @Override
        public String handleRequest(Object input, Context context) {
            context.getLogger().log("Input: " + input);

            FineGrainedService fg = LambdaInvokerFactory.builder()
                    .lambdaClient(
                            AWSLambdaAsyncClientBuilder.standard()
                            .withRegion(Regions.US_EAST_2)
                            .build()
                    )
                    .build(FineGrainedService.class);

            context.getLogger().log("Response back from FG" + fg.getClass());

            String fgRespone = fg.callFineGrained("Call from Gateway");
            context.getLogger().log("fgRespone: " + fgRespone);

            // TODO: implement your handler
            return "Hello from Gateway Lambda!";
        }

    }


    import com.amazonaws.services.lambda.invoke.LambdaFunction;

    public interface FineGrainedService {

        @LambdaFunction(functionName="SimpleFineGrained")
        String callFineGrained(String input);
    }


  1. Using invoker


    import java.nio.ByteBuffer;

    import com.amazonaws.services.lambda.AWSLambdaClient;
    import com.amazonaws.services.lambda.model.InvokeRequest;
    import com.amazonaws.services.lambda.runtime.Context;
    import com.amazonaws.services.lambda.runtime.RequestHandler;

    public class LambdaFunctionHandler implements RequestHandler {

        @Override
        public String handleRequest(Object input, Context context) {
            context.getLogger().log("Input: " + input);

            AWSLambdaClient lambdaClient = new AWSLambdaClient();
            try {
                InvokeRequest invokeRequest = new InvokeRequest();
                invokeRequest.setFunctionName("SimpleFineGrained");
                invokeRequest.setPayload("From gateway");

                context.getLogger().log("Before Invoke");
                ByteBuffer payload = lambdaClient.invoke(invokeRequest).getPayload();
                context.getLogger().log("After Inoke");

                context.getLogger().log(payload.toString());
                context.getLogger().log("After Payload logger");

            } catch (Exception e) {
                // TODO: handle exception
            }

            // TODO: implement your handler
            return "Hello from Lambda!";
        }

    }

AWSLambdaClient should be created from builder.

0
votes

You can use LambdaClient to invoke Lambda asynchronously by passing InvocationType.EVENT parameter. Look at an example:

LambdaClient lambdaClient = LambdaClient.builder().build();
InvokeRequest invokeRequest = InvokeRequest.builder()
        .functionName("functionName")
        .invocationType(InvocationType.EVENT)
        .payload(SdkBytes.fromUtf8String("payload"))
        .build();
InvokeResponse response = lambdaClient.invoke(invokeRequest);