Repository: localstack/localstack

Issue:
bug: Java 11 Lambda incorrect handler specification
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Current Behavior

Up to localstack 0.12.15 (I haven't checked with 0.12.16), Java 11 Lambda function handler was correctly specified according to [AWS docs](https://docs.aws.amazon.com/lambda/latest/dg/lambda-java.html) e.g.

```
example.Hello::handleRequest
```

With 0.12.17, instead, such specification leads to error during Lambda execution, with stacktrace:

```
2021-08-30T14:08:01:INFO:localstack.utils.run: Thread run method <function LambdaExecutor.execute.<locals>.do_execute at 0x7fc385d26560>(None) failed: Lambda process returned error status code: 1. Result: . Output:
Exception in thread "main" java.lang.ClassNotFoundException: example.Hello::handleRequest
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(Unknown Source)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(Unknown Source)
        at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Unknown Source)
        at cloud.localstack.LambdaExecutor.getHandler(LambdaExecutor.java:142)
        at cloud.localstack.LambdaExecutor.main(LambdaExecutor.java:69) Traceback (most recent call last):
  File "/opt/code/localstack/localstack/utils/run.py", line 138, in run
    result = self.func(self.params, **kwargs)
  File "/opt/code/localstack/localstack/services/awslambda/lambda_executors.py", line 326, in do_execute
    return _run(func_arn=func_arn)
  File "/opt/code/localstack/localstack/utils/cloudwatch/cloudwatch_util.py", line 157, in wrapped
    raise e
  File "/opt/code/localstack/localstack/utils/cloudwatch/cloudwatch_util.py", line 153, in wrapped
    result = func(*args, **kwargs)
  File "/opt/code/localstack/localstack/services/awslambda/lambda_executors.py", line 313, in _run
    raise e
  File "/opt/code/localstack/localstack/services/awslambda/lambda_executors.py", line 299, in _run
    result = self._execute(func_arn, func_details, event, context, version)
  File "/opt/code/localstack/localstack/services/awslambda/lambda_executors.py", line 1101, in _execute
    raise InvocationException(result, log_output)
localstack.services.awslambda.lambda_executors.InvocationException: Lambda process returned error status code: 1. Result: . Output:
Exception in thread "main" java.lang.ClassNotFoundException: example.Hello::handleRequest
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(Unknown Source)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(Unknown Source)
        at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Unknown Source)
        at cloud.localstack.LambdaExecutor.getHandler(LambdaExecutor.java:142)
        at cloud.localstack.LambdaExecutor.main(LambdaExecutor.java:69)
```

I would say that this line in the test case is incorrect: https://github.com/localstack/localstack/blob/master/tests/integration/test_lambda.py#L1711

### Expected Behavior

I would expect to specify Java 11 Lambda handlers as it used to be at least until localstack 0.12.15 and according to AWS docs.

### How are you starting LocalStack?

With a docker-compose file

### Steps To Reproduce

#### How are you starting localstack (e.g., `bin/localstack` command, arguments, or `docker-compose.yml`)

    docker run localstack/localstack

#### Client commands (e.g., AWS SDK code snippet, or sequence of "awslocal" commands)

    awslocal s3 mb s3://mybucket


### Environment

```markdown
- OS: Ubuntu 20.04
- LocalStack: 0.12.17
```


### Anything else?

_No response_


Discussion:
Thanks for reporting @ilgrosso! Can you please share a few more details about your docker-compose setup (especially your lambda executor mode settings i.e. LAMBDA_EXECUTOR). Then we can look into it. 
hi @silv-io my `LAMBDA_EXECUTOR` is `local`.
Hi @ilgrosso, we're working on better support for the `local` executor mode. In the meantime you can try using one of the Docker-based modes which already have more extensive support. If you still have problems with that, feel free to report back. Thank you!
Thanks for reporting @ilgrosso . This should be fixed in #4762 - can you please pull the `latest` Docker image and give it another try? Please let us know if the problem persists. Thanks!
@whummer with `latest`, if I change my code to define the Lambda via

```
withHandler(LegalDocPublisher.class.getName() + "::handleRequest")
```

then I can see the following exception in the Docker logs:

```
2021-10-25T09:07:44:INFO:localstack.services.awslambda.lambda_executors: java  -cp /tmp/localstack/zipfile.4f4d5456/original_lambda_archive.zip:.:original_lambda_archive.zip:*.jar:java/lib/*.jar:/opt/code/localstack/localstack/infra/localstack-utils-fat.jar cloud.localstack.LambdaExecutor /tmp/localstack/lambda.event.ff65f5e2.json
2021-10-25T09:07:45:INFO:localstack.services.awslambda.lambda_executors: Error executing Lambda "arn:aws:lambda:us-east-2:000000000000:function:LegalDocPublisher": Lambda process returned error status code: 1. Result: . Output:
Exception in thread "main" cloud.localstack.lambda_handler.MultipleMatchingHandlersException: Multiple matching headers: [public java.lang.Void it.infocert.spid.gateway.gdf.legaldoc.LegalDocPublisher.handleRequest(com.amazonaws.services.lambda.runtime.events.SQSEvent,com.amazonaws.services.lambda.runtime.Context), public java.lang.Object it.infocert.spid.gateway.gdf.legaldoc.LegalDocPublisher.handleRequest(java.lang.Object,com.amazonaws.services.lambda.runtime.Context)]
        at cloud.localstack.LambdaExecutor.getHandlerMethodByName(LambdaExecutor.java:149)
        at cloud.localstack.LambdaExecutor.main(LambdaExecutor.java:79)   File "/opt/code/localstack/localstack/services/awslambda/lambda_executors.py", line 1232, in _execute
    process.run()
  File "/usr/lib/python3.7/multiprocessing/process.py", line 99, in run
    self._target(*self._args, **self._kwargs)
  File "/opt/code/localstack/localstack/services/awslambda/lambda_executors.py", line 1219, in do_execute
    result = lambda_function_callable(inv_context.event, context)
  File "/opt/code/localstack/localstack/services/awslambda/lambda_api.py", line 947, in execute
    event, context, main_file=main_file, lambda_function=lambda_function
  File "/opt/code/localstack/localstack/services/awslambda/lambda_executors.py", line 1317, in execute_java_lambda
    invocation_result = self._execute_in_custom_runtime(cmd, lambda_function=lambda_function)
  File "/opt/code/localstack/localstack/services/awslambda/lambda_executors.py", line 1175, in _execute_in_custom_runtime
    result,
```
Also getting this error:

`Exception in thread "main" cloud.localstack.lambda_handler.MultipleMatchingHandlersException: Multiple matching headers:
`

This appears to be because our lambda class implements `com.amazonaws.services.lambda.runtime.RequestHandler` and then overrides `handleRequest`.  This makes it look like the class has two `handleRequest` methods. The following then returns two items and so throws a `MultipleMatchingHandlersException`:

https://github.com/localstack/localstack-java-utils/blob/478f8684e3928addf2abf9dcbff10d2d2f2f8c55/src/main/java/cloud/localstack/LambdaExecutor.java#L145

Hi @dbes-gds !
Thanks for reporting this. I will reopen this issue and test this behavior.

Resolving patch:
diff --git a/localstack/constants.py b/localstack/constants.py
--- a/localstack/constants.py
+++ b/localstack/constants.py
@@ -28,7 +28,7 @@
 LOCALHOST_HOSTNAME = "localhost.localstack.cloud"
 
 # version of the Maven dependency with Java utility code
-LOCALSTACK_MAVEN_VERSION = "0.2.16"
+LOCALSTACK_MAVEN_VERSION = "0.2.17"
 
 # map of default service APIs and ports to be spun up (fetch map from localstack_client)
 DEFAULT_SERVICE_PORTS = localstack_client.config.get_service_ports()


Test patch:
diff --git a/tests/integration/test_lambda.py b/tests/integration/test_lambda.py
--- a/tests/integration/test_lambda.py
+++ b/tests/integration/test_lambda.py
@@ -1972,42 +1972,37 @@ def test_trigger_java_lambda_through_sns(self):
         s3_client.delete_bucket(Bucket=bucket_name)
 
 
-def test_java_custom_handler_method_specification(lambda_client, create_lambda_function):
+@pytest.mark.parametrize(
+    "handler,expected_result",
+    [
+        (
+            "cloud.localstack.sample.LambdaHandlerWithInterfaceAndCustom::handleRequestCustom",
+            "CUSTOM",
+        ),
+        ("cloud.localstack.sample.LambdaHandlerWithInterfaceAndCustom", "INTERFACE"),
+        ("cloud.localstack.sample.LambdaHandlerWithInterfaceAndCustom::handleRequest", "INTERFACE"),
+    ],
+)
+def test_java_custom_handler_method_specification(
+    lambda_client, create_lambda_function, handler, expected_result
+):
     java_handler_multiple_handlers = load_file(TEST_LAMBDA_JAVA_MULTIPLE_HANDLERS, mode="rb")
     expected = ['.*"echo": "echo".*']
 
-    function_name_custom_handler = "lambda_custom_handler_%s" % short_uid()
+    function_name = "lambda_handler_test_%s" % short_uid()
     create_lambda_function(
-        func_name=function_name_custom_handler,
-        zip_file=java_handler_multiple_handlers,
-        runtime=LAMBDA_RUNTIME_JAVA11,
-        handler="cloud.localstack.sample.LambdaHandlerWithInterfaceAndCustom::handleRequestCustom",
-    )
-
-    result = lambda_client.invoke(
-        FunctionName=function_name_custom_handler, Payload=b'{"echo":"echo"}'
-    )
-    result_data = result["Payload"].read()
-
-    assert 200 == result["StatusCode"]
-    assert "CUSTOM" == to_str(result_data).strip('"\n ')
-    _check_lambda_logs(function_name_custom_handler, expected_lines=expected)
-
-    function_name_interface = "lambda_interface_%s" % short_uid()
-    create_lambda_function(
-        func_name=function_name_interface,
+        func_name=function_name,
         zip_file=java_handler_multiple_handlers,
         runtime=LAMBDA_RUNTIME_JAVA11,
-        handler="cloud.localstack.sample.LambdaHandlerWithInterfaceAndCustom",
+        handler=handler,
     )
 
-    result = lambda_client.invoke(FunctionName=function_name_interface, Payload=b'{"echo":"echo"}')
+    result = lambda_client.invoke(FunctionName=function_name, Payload=b'{"echo":"echo"}')
     result_data = result["Payload"].read()
 
     assert 200 == result["StatusCode"]
-    assert "INTERFACE" == to_str(result_data).strip('"\n ')
-
-    _check_lambda_logs(function_name_interface, expected_lines=expected)
+    assert expected_result == to_str(result_data).strip('"\n ')
+    _check_lambda_logs(function_name, expected_lines=expected)
 
 
 class TestDockerBehaviour(LambdaTestBase):