Clojure And AWS Lambda
At my job I was helping someone setup a lambda in Clojure. Without giving much away this is how we did it:
Shell
lein new app clojure-aws-lambda
The project.clj
should look like this:
Clojure
(defproject clojure-aws-lambda "0.1.0-SNAPSHOT"
:main com.marcelo.clojure-aws-lambda.core
:uberjar-name "lambda.jar"
:dependencies [[org.clojure/clojure "1.11.1"]
[com.amazonaws/aws-lambda-java-core "1.2.1"]]
:aot :all)
Then the code is on src/com/marcelo/clojure_aws_lambda/core.clj
:
Clojure
(ns com.marcelo.clojure-aws-lambda.core
(:gen-class
:implements [com.amazonaws.services.lambda.runtime.RequestStreamHandler]
:main false
:prefix "RequestHandler-"))
(defn RequestHandler-handleRequest
[_self input-stream _output-stream _context]
(let [raw-payload (slurp input-stream)]
(print "Received payload " raw-payload)))
We can generate a uberjar with:
Shell
lein uberjar
Compiling com.marcelo.clojure-aws-lambda.core
Created /Users/marcelo/coding/clojure-aws-lambda/target/clojure-aws-lambda-0.1.0-SNAPSHOT.jar
Created /Users/marcelo/coding/clojure-aws-lambda/target/lambda.jar
We can inspect the generate jar and confirm that we have a class:
Shell
jar -tf target/lambda.jar | rg "core.class$"
com/marcelo/clojure_aws_lambda/core.class
We can decompile this class to see if it is what we wanted:
Shell
javap -c -classpath target/lambda.jar com.marcelo.clojure_aws_lambda.Core
The result is:
Plaintext
public class com.marcelo.clojure_aws_lambda.core implements com.amazonaws.services.lambda.runtime.RequestStreamHandler {
public static {};
Code:
0: ldc #16 // String com.marcelo.clojure-aws-lambda.core
2: ldc #18 // String RequestHandler-equals
4: invokestatic #24 // Method clojure/lang/Var.internPrivate:(Ljava/lang/String;Ljava/lang/String;)Lclojure/lang/Var;
7: putstatic #26 // Field equals__var:Lclojure/lang/Var;
10: ldc #16 // String com.marcelo.clojure-aws-lambda.core
12: ldc #28 // String RequestHandler-toString
14: invokestatic #24 // Method clojure/lang/Var.internPrivate:(Ljava/lang/String;Ljava/lang/String;)Lclojure/lang/Var;
17: putstatic #30 // Field toString__var:Lclojure/lang/Var;
20: ldc #16 // String com.marcelo.clojure-aws-lambda.core
22: ldc #32 // String RequestHandler-hashCode
24: invokestatic #24 // Method clojure/lang/Var.internPrivate:(Ljava/lang/String;Ljava/lang/String;)Lclojure/lang/Var;
27: putstatic #34 // Field hashCode__var:Lclojure/lang/Var;
30: ldc #16 // String com.marcelo.clojure-aws-lambda.core
32: ldc #36 // String RequestHandler-clone
34: invokestatic #24 // Method clojure/lang/Var.internPrivate:(Ljava/lang/String;Ljava/lang/String;)Lclojure/lang/Var;
37: putstatic #38 // Field clone__var:Lclojure/lang/Var;
40: ldc #16 // String com.marcelo.clojure-aws-lambda.core
42: ldc #40 // String RequestHandler-handleRequest
44: invokestatic #24 // Method clojure/lang/Var.internPrivate:(Ljava/lang/String;Ljava/lang/String;)Lclojure/lang/Var;
47: putstatic #42 // Field handleRequest__var:Lclojure/lang/Var;
50: ldc #44 // String /com/marcelo/clojure_aws_lambda/core
52: ldc #2 // class com/marcelo/clojure_aws_lambda/core
54: invokestatic #50 // Method clojure/lang/Util.loadWithClass:(Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
57: pop
58: return
public com.marcelo.clojure_aws_lambda.core();
Code:
0: aload_0
1: invokespecial #53 // Method java/lang/Object."<init>":()V
4: return
public boolean equals(java.lang.Object);
Code:
0: getstatic #26 // Field equals__var:Lclojure/lang/Var;
3: dup
4: invokevirtual #59 // Method clojure/lang/Var.isBound:()Z
7: ifeq 16
10: invokevirtual #63 // Method clojure/lang/Var.get:()Ljava/lang/Object;
13: goto 18
16: pop
17: aconst_null
18: dup
19: ifnull 41
22: checkcast #65 // class clojure/lang/IFn
25: aload_0
26: aload_1
27: invokeinterface #69, 3 // InterfaceMethod clojure/lang/IFn.invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
32: checkcast #71 // class java/lang/Boolean
35: invokevirtual #74 // Method java/lang/Boolean.booleanValue:()Z
38: goto 47
41: pop
42: aload_0
43: aload_1
44: invokespecial #76 // Method java/lang/Object.equals:(Ljava/lang/Object;)Z
47: ireturn
public java.lang.String toString();
Code:
0: getstatic #30 // Field toString__var:Lclojure/lang/Var;
3: dup
4: invokevirtual #59 // Method clojure/lang/Var.isBound:()Z
7: ifeq 16
10: invokevirtual #63 // Method clojure/lang/Var.get:()Ljava/lang/Object;
13: goto 18
16: pop
17: aconst_null
18: dup
19: ifnull 37
22: checkcast #65 // class clojure/lang/IFn
25: aload_0
26: invokeinterface #81, 2 // InterfaceMethod clojure/lang/IFn.invoke:(Ljava/lang/Object;)Ljava/lang/Object;
31: checkcast #83 // class java/lang/String
34: goto 42
37: pop
38: aload_0
39: invokespecial #85 // Method java/lang/Object.toString:()Ljava/lang/String;
42: areturn
public int hashCode();
Code:
0: getstatic #34 // Field hashCode__var:Lclojure/lang/Var;
3: dup
4: invokevirtual #59 // Method clojure/lang/Var.isBound:()Z
7: ifeq 16
10: invokevirtual #63 // Method clojure/lang/Var.get:()Ljava/lang/Object;
13: goto 18
16: pop
17: aconst_null
18: dup
19: ifnull 40
22: checkcast #65 // class clojure/lang/IFn
25: aload_0
26: invokeinterface #81, 2 // InterfaceMethod clojure/lang/IFn.invoke:(Ljava/lang/Object;)Ljava/lang/Object;
31: checkcast #89 // class java/lang/Number
34: invokevirtual #92 // Method java/lang/Number.intValue:()I
37: goto 45
40: pop
41: aload_0
42: invokespecial #94 // Method java/lang/Object.hashCode:()I
45: ireturn
public java.lang.Object clone();
Code:
0: getstatic #38 // Field clone__var:Lclojure/lang/Var;
3: dup
4: invokevirtual #59 // Method clojure/lang/Var.isBound:()Z
7: ifeq 16
10: invokevirtual #63 // Method clojure/lang/Var.get:()Ljava/lang/Object;
13: goto 18
16: pop
17: aconst_null
18: dup
19: ifnull 34
22: checkcast #65 // class clojure/lang/IFn
25: aload_0
26: invokeinterface #81, 2 // InterfaceMethod clojure/lang/IFn.invoke:(Ljava/lang/Object;)Ljava/lang/Object;
31: goto 39
34: pop
35: aload_0
36: invokespecial #97 // Method java/lang/Object.clone:()Ljava/lang/Object;
39: areturn
public void handleRequest(java.io.InputStream, java.io.OutputStream, com.amazonaws.services.lambda.runtime.Context);
Code:
0: getstatic #42 // Field handleRequest__var:Lclojure/lang/Var;
3: dup
4: invokevirtual #59 // Method clojure/lang/Var.isBound:()Z
7: ifeq 16
10: invokevirtual #63 // Method clojure/lang/Var.get:()Ljava/lang/Object;
13: goto 18
16: pop
17: aconst_null
18: dup
19: ifnull 38
22: checkcast #65 // class clojure/lang/IFn
25: aload_0
26: aload_1
27: aload_2
28: aload_3
29: invokeinterface #102, 5 // InterfaceMethod clojure/lang/IFn.invoke:(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
34: pop
35: goto 49
38: pop
39: new #104 // class java/lang/UnsupportedOperationException
42: dup
43: ldc #106 // String handleRequest (com.marcelo.clojure-aws-lambda.core/RequestHandler-handleRequest not defined?)
45: invokespecial #109 // Method java/lang/UnsupportedOperationException."<init>":(Ljava/lang/String;)V
48: athrow
49: return
}
Among all the information we can see our class class implementing AWS stuff and with the method public void handleRequest(java.io.InputStream, java.io.OutputStream, com.amazonaws.services.lambda.runtime.Context)
.