Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Java CDK Lambda example #228

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .tools/prepare_release_zip.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ create_release_zip python/end-to-end-applications/food-ordering python-food-orde
create_release_zip java/basics java-basics
create_release_zip java/end-to-end-applications/food-ordering java-food-ordering
create_release_zip java/end-to-end-applications/subway-fare-calculator java-subway-fare-calculator
create_release_zip java/integrations/java-gradle-lambda-cdk java-hello-world-lambda-cdk
create_release_zip java/patterns-use-cases java-patterns-use-cases
create_release_zip java/templates/java-gradle java-hello-world-gradle
create_release_zip java/templates/java-maven java-hello-world-maven
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ challenges.

* **Basics:** Small examples highlighting the basic building blocks, like
durable execution or virtual objects.

* **Use Cases and Patterns:** Small specific use cases, like webhooks,
workflows, asynchronous task queuing.

Expand All @@ -27,7 +27,7 @@ challenges.
* **Tutorials:** A step-by-step guide that builds an application and introduces
the Restate concepts on the way.

## Example catalogs
## Example catalogs

Have a look at the example catalog for your preferred SDK language:

Expand Down Expand Up @@ -74,7 +74,7 @@ Or have a look at the general catalog below:

| Example Name | Languages |
|-----------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| <a id="aws-lambda">AWS Lambda + CDK</a> | [<img src="https://skillicons.dev/icons?i=ts" width="24" height="24">](typescript/integrations/deployment-lambda-cdk) [<img src="https://skillicons.dev/icons?i=go" width="24" height="24">](go/integrations/go-lambda-cdk) [<img src="https://skillicons.dev/icons?i=kotlin&theme=light" width="24" height="24">](kotlin/integrations/kotlin-gradle-lambda-cdk) |
| <a id="aws-lambda">AWS Lambda + CDK</a> | [<img src="https://skillicons.dev/icons?i=ts" width="24" height="24">](typescript/integrations/deployment-lambda-cdk) [<img src="https://skillicons.dev/icons?i=go" width="24" height="24">](go/integrations/go-lambda-cdk) [<img src="https://skillicons.dev/icons?i=java&theme=light" width="24" height="24">](java/integrations/java-gradle-lambda-cdk) [<img src="https://skillicons.dev/icons?i=kotlin&theme=light" width="24" height="24">](kotlin/integrations/kotlin-gradle-lambda-cdk) |
| <a id="xstate">XState</a> | [<img src="https://skillicons.dev/icons?i=ts" width="24" height="24">](typescript/integrations/xstate) |
| <a id="knative">Knative</a> | [<img src="https://skillicons.dev/icons?i=go" width="24" height="24">](go/integrations/knative-go) |

Expand Down
2 changes: 1 addition & 1 deletion go/integrations/go-lambda-cdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ For more information on CDK, please see [Getting started with the AWS CDK](https
- Via git clone:
```shell
git clone [email protected]:restatedev/examples.git
cd examples/templates/go-lambda-cdk
cd examples/go/integrations/go-lambda-cdk
```

- Via `wget`:
Expand Down
3 changes: 3 additions & 0 deletions java/integrations/java-gradle-lambda-cdk/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
dist
cdk.out
15 changes: 15 additions & 0 deletions java/integrations/java-gradle-lambda-cdk/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"env": {
"browser": true,
"commonjs": true,
"es2021": true
},
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"overrides": [],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest"
},
"plugins": ["@typescript-eslint"],
"rules": {}
}
10 changes: 10 additions & 0 deletions java/integrations/java-gradle-lambda-cdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
!jest.config.js
node_modules
.cdk.staging
cdk.out
.gradle
lambda/build
*.js
*.d.ts
*.class
cdk.context.json
7 changes: 7 additions & 0 deletions java/integrations/java-gradle-lambda-cdk/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"trailingComma": "all",
"tabWidth": 2,
"semi": true,
"arrowParens": "always",
"printWidth": 120
}
70 changes: 70 additions & 0 deletions java/integrations/java-gradle-lambda-cdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Hello world - Java Lambda (CDK) example

Sample project deploying a Java-based Restate service to AWS Lambda using the AWS Cloud Development Kit (CDK).
The stack uses the Restate CDK constructs library to register the service with against a Restate Cloud environment.

For more information on CDK, please see [Getting started with the AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html).

* [CDK app entry point `lambda-jvm-cdk.ts`](bin/lambda-jvm-cdk.ts)
* [CDK stack consisting of a Lambda function and providing Restate service registration](cdk/lambda-jvm-cdk-stack.ts)
* [Java Lambda handler](lambda) - based on [`hello-world-java`](../../templates/java-gradle)

## Download the example

- Via the CLI:
```shell
restate example java-hello-world-lambda-cdk && cd java-hello-world-lambda-cdk
```

- Via git clone:
```shell
git clone [email protected]:restatedev/examples.git
cd examples/java/integrations/java-gradle-lambda-cdk
```

- Via `wget`:
```shell
wget https://github.com/restatedev/examples/releases/latest/download/java-hello-world-lambda-cdk.zip && unzip java-hello-world-lambda-cdk.zip -d java-hello-world-lambda-cdk && rm java-hello-world-lambda-cdk.zip
```

## Deploy

**Pre-requisites:**

* npm
* gradle
* JDK >= 21
* AWS account, bootstrapped for CDK use
* valid AWS credentials with sufficient privileges to create the necessary resources
* an existing [Restate Cloud](https://restate.dev) environment (environment id + API key)

Install npm dependencies:

```shell
npm install
```

To deploy the stack, export the Restate Cloud environment id and API key, and run `cdk deploy`:

```shell
export RESTATE_ENV_ID=env_... RESTATE_API_KEY=key_...
npx cdk deploy
```

The stack output will print out the Restate server ingress URL.

### Test

You can send a test request to the Restate ingress endpoint to call the newly deployed service:

```shell
curl -k ${restateIngressUrl}/Greeter/greet \
-H "Authorization: Bearer $RESTATE_API_KEY" \
-H 'content-type: application/json' -d '"Restate"'
```

### Useful commands

* `npm run build` compile the Lambda handler and synthesize CDK deployment artifacts
* `npm run deploy` perform a CDK deployment
* `npm run destroy` delete the stack and all its resources
24 changes: 24 additions & 0 deletions java/integrations/java-gradle-lambda-cdk/bin/lambda-jvm-cdk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env node

/*
* Copyright (c) 2024 - Restate Software, Inc., Restate GmbH
*
* This file is part of the Restate examples,
* which is released under the MIT license.
*
* You can find a copy of the license in the file LICENSE
* in the root directory of this repository or package or at
* https://github.com/restatedev/examples/
*/

import "source-map-support/register";
import * as cdk from "aws-cdk-lib";
import { LambdaJvmCdkStack } from "../cdk/lambda-jvm-cdk-stack";

const app = new cdk.App();
new LambdaJvmCdkStack(app, "LambdaJvmCdkStack", {
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: process.env.CDK_DEFAULT_REGION,
},
});
59 changes: 59 additions & 0 deletions java/integrations/java-gradle-lambda-cdk/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"app": "npx ts-node --prefer-ts-exts bin/lambda-jvm-cdk.ts",
"watch": {
"include": ["**"],
"exclude": [
"README.md",
"cdk*.json",
"**/*.d.ts",
"**/*.js",
"tsconfig.json",
"package*.json",
"yarn.lock",
"node_modules",
"test"
]
},
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": ["aws", "aws-cn"],
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
"@aws-cdk/core:enablePartitionLiterals": true,
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
"@aws-cdk/aws-iam:standardizedServicePrincipals": true,
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
"@aws-cdk/aws-route53-patters:useCertificate": true,
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
"@aws-cdk/aws-redshift:columnId": true,
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
"@aws-cdk/aws-kms:aliasNameRef": true,
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright (c) 2024 - Restate Software, Inc., Restate GmbH
*
* This file is part of the Restate examples,
* which is released under the MIT license.
*
* You can find a copy of the license in the file LICENSE
* in the root directory of this repository or package or at
* https://github.com/restatedev/examples/
*/

import * as restate from "@restatedev/restate-cdk";
import * as cdk from "aws-cdk-lib";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as secrets from "aws-cdk-lib/aws-secretsmanager";
import { Construct } from "constructs";

export class LambdaJvmCdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: cdk.StackProps) {
super(scope, id, props);

const handler: lambda.Function = new lambda.Function(this, "GreeterService", {
runtime: lambda.Runtime.JAVA_21,
architecture: lambda.Architecture.ARM_64,
code: lambda.Code.fromAsset("lambda/build/distributions/lambda.zip"),
handler: "my.example.LambdaHandler",
timeout: cdk.Duration.seconds(10),
loggingFormat: lambda.LoggingFormat.JSON,
applicationLogLevelV2: lambda.ApplicationLogLevel.DEBUG,
systemLogLevelV2: lambda.SystemLogLevel.INFO,
});

// Set the RESTATE_ENV_ID and RESTATE_API_KEY environment variables to point to your Restate Cloud environment.
// This construct automatically creates an invoker role that Restate Cloud will be able to assume to invoke handlers
// on behalf of your environment. See https://docs.restate.dev/deploy/cloud for more information.
const restateEnvironment = new restate.RestateCloudEnvironment(this, "RestateCloud", {
environmentId: process.env.RESTATE_ENV_ID! as restate.EnvironmentId,
// Warning: this will result in the API key being baked into the CloudFormation template!
// For improved security, pre-populate the secret and pass it to the construct as a reference.
// See: https://docs.aws.amazon.com/secretsmanager/latest/userguide/cdk.html
apiKey: new secrets.Secret(this, "RestateCloudApiKey", {
secretStringValue: cdk.SecretValue.unsafePlainText(process.env.RESTATE_API_KEY!),
}),
});
const deployer = new restate.ServiceDeployer(this, "ServiceDeployer");

// Alternatively, you can deploy a standalone Restate server using the SingleNodeRestateDeployment construct.
// Please see https://docs.restate.dev/deploy/lambda/self-hosted and the construct documentation for more details.
// const vpc = ec2.Vpc.fromLookup(this, "Vpc", { vpcId: "..." });
// const restateEnvironment = new restate.SingleNodeRestateDeployment(this, "Restate", {
// vpc,
// networkConfiguration: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
// });
// const deployer = new restate.ServiceDeployer(this, "ServiceDeployer", {
// vpc,
// securityGroups: [restateEnvironment.adminSecurityGroup],
// });

deployer.deployService("Greeter", handler.currentVersion, restateEnvironment);
new cdk.CfnOutput(this, "restateIngressUrl", { value: restateEnvironment.ingressUrl });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
arguments=--init-script /var/folders/4x/r9ldvl2j0pldhp2nb9qvxy0w0000gn/T/db3b08fc4a9ef609cb16b96b200fa13e563f396e9bb1ed0905fdab7bc3bc513b.gradle
auto.sync=false
build.scans.enabled=false
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
connection.project.dir=
eclipse.preferences.version=1
gradle.user.home=
java.home=/Library/Java/JavaVirtualMachines/zulu-19.jdk/Contents/Home
jvm.arguments=
offline.mode=false
override.workspace.settings=true
show.console.view=true
show.executions.view=true
53 changes: 53 additions & 0 deletions java/integrations/java-gradle-lambda-cdk/lambda/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import java.net.URI

plugins {
java
}

repositories {
mavenCentral()
}

val restateVersion = "1.2.0"

dependencies {
// Annotation processor
annotationProcessor("dev.restate:sdk-api-gen:$restateVersion")

// Restate SDK
implementation("dev.restate:sdk-api:$restateVersion")
implementation("dev.restate:sdk-http-vertx:$restateVersion")
implementation("dev.restate:sdk-lambda:$restateVersion")
// To use Jackson to read/write state entries (optional)
implementation("dev.restate:sdk-serde-jackson:$restateVersion")

testImplementation(platform("org.junit:junit-bom:5.11.3"))
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation("dev.restate:sdk-testing:$restateVersion")

// AWS Lambda-specific logging, see https://docs.aws.amazon.com/lambda/latest/dg/java-logging.html#java-logging-log4j2
val log4j2version = "2.24.2"
implementation("org.apache.logging.log4j:log4j-core:$log4j2version")
implementation("org.apache.logging.log4j:log4j-layout-template-json:$log4j2version")
implementation("com.amazonaws:aws-lambda-java-log4j2:1.6.0")
}

// Setup Java compiler target
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}

tasks.register<Zip>("lambdaZip") {
from(tasks.compileJava.get())
from(tasks.processResources.get())

into("lib") {
from(configurations.runtimeClasspath)
}
}

tasks.build {
dependsOn("lambdaZip")
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading
Loading