Skip to content

Commit 81efc84

Browse files
committed
Some Java update related changes
1 parent e9d3a54 commit 81efc84

File tree

6 files changed

+85
-185
lines changed

6 files changed

+85
-185
lines changed

MODERNIZATION.md

Lines changed: 65 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,73 @@
1-
# Modernization Plan for Programming AWS Lambda Examples
1+
# Modernization Summary for Programming AWS Lambda Examples
22

3-
## Completed Work
3+
## Completed Work (November 2025)
44

5-
### Test Harness (Done)
5+
### Test Harness
66
- Created `test-harness/` with deploy, test, and teardown scripts
7-
- Smoke tests for chapters 2, 3, 4, 5-api, 5-event-sources, 7
7+
- Smoke tests for chapters 2, 3, 4, 5-api, 5-data-pipeline, 5-event-sources, 7
88
- Integration tests for chapter 6 (Maven failsafe)
9-
- GitHub Actions workflow (needs IAM permissions to run)
10-
- All 9 chapters deploy successfully with `java8.al2` runtime
11-
12-
### Immediate Fixes Applied
13-
- Updated runtime from `java8` (deprecated) to `java8.al2`
14-
- Shortened stack prefixes for S3 bucket name limits
15-
16-
### Phase 1 Complete (November 2025)
17-
- **Java 21 Runtime**: All pom.xml and template.yaml files updated
18-
- **Security Updates**: Jackson 2.17.2, Log4j 2.23.1
19-
- **AWS SDK v2 Migration**: All chapters migrated to SDK v2
20-
- DynamoDB (chapters 4, 5-api, 7)
21-
- S3, SNS (chapters 5-data-pipeline, 6)
22-
- CloudFormation, CloudWatch Logs (chapter 6 integration tests)
23-
- X-Ray SDK v2 instrumentor (chapter 7)
24-
25-
### Phase 3 Partial (November 2025)
26-
- **JUnit 5 Migration**: Chapter 6 tests migrated to JUnit Jupiter 5.10.3
27-
- **system-stubs**: Replaced system-rules with system-stubs-jupiter 2.1.6
28-
- **Mockito 5.12.0**: Updated for Java 21 compatibility
29-
- **S3Event tests**: Fixed to build events programmatically (no Jackson deserialization)
30-
31-
### Known Issues (Resolved)
32-
- **Chapter 6 integration tests**: Fixed stack name passing to Maven failsafe plugin
33-
- Root cause: Maven property `${integration.test.stack.name}` resolves at project load time, before `-D` override
34-
- Solution: Use shell environment variable `${env.STACK_NAME}` instead of Maven property
35-
- Test harness exports `STACK_NAME`, failsafe passes it via `environmentVariables`, test reads `System.getenv()`
9+
- GitHub Actions workflow (requires IAM permissions - see below)
10+
11+
### Java 21 Runtime
12+
- All `pom.xml` files updated to Java 21
13+
- All `template.yaml` files updated to `java21` runtime
14+
- Applied `var` keyword for cleaner code in chapters 3, 5-api, 6, 7
15+
16+
### Security Updates
17+
- Jackson 2.17.2 (fixed CVEs from 2.10.1)
18+
- Log4j 2.23.1
19+
20+
### AWS SDK v2 Migration
21+
All chapters migrated from AWS SDK v1 to v2:
22+
- DynamoDB (chapters 4, 5-api, 7)
23+
- S3, SNS (chapters 5-data-pipeline, 6)
24+
- CloudFormation, CloudWatch Logs (chapter 6 integration tests)
25+
- X-Ray SDK v2 instrumentor (chapter 7)
26+
27+
### Testing Modernization
28+
- JUnit 5 (Jupiter 5.10.3)
29+
- Mockito 5.12.0
30+
- system-stubs-jupiter 2.1.6 (replaced system-rules for Java 9+ compatibility)
31+
- S3Event tests build events programmatically
3632

3733
---
3834

39-
## Phase 1: Critical Updates (Do First)
35+
## Current Dependency Versions
4036

41-
### 1. Java 21 Runtime
42-
**Files to change:**
43-
- All `pom.xml` files: Update `maven.compiler.source/target` from `1.8` to `21`
44-
- All `template.yaml` files: Update `Runtime` from `java8.al2` to `java21`
37+
```xml
38+
<properties>
39+
<!-- Java -->
40+
<maven.compiler.source>21</maven.compiler.source>
41+
<maven.compiler.target>21</maven.compiler.target>
4542

46-
### 2. Security Updates
47-
**jackson-databind** (currently 2.10.1 - has CVEs)
48-
- Update to `2.17.2` in all pom.xml files
49-
- Locations: chapter5-api, chapter5-data-pipeline, chapter6, chapter7
43+
<!-- AWS SDK v2 -->
44+
<aws.sdk.version>2.29.6</aws.sdk.version>
5045

51-
**log4j** (currently 2.17.0)
52-
- Update to `2.23.1` in chapter6, chapter7
46+
<!-- Lambda Libraries -->
47+
<aws.lambda.java.core.version>1.2.3</aws.lambda.java.core.version>
48+
<aws.lambda.java.events.version>3.14.0</aws.lambda.java.events.version>
5349

54-
### 3. AWS SDK v2 Migration (End-of-support Dec 2025)
55-
**Largest effort - affects all chapters using AWS services**
50+
<!-- Logging -->
51+
<log4j.version>2.23.1</log4j.version>
5652

57-
Package changes:
58-
- `com.amazonaws:aws-java-sdk-*``software.amazon.awssdk:*`
59-
- BOM: `1.11.600``2.29.x`
53+
<!-- JSON -->
54+
<jackson.version>2.17.2</jackson.version>
6055

61-
Code changes required:
62-
- DynamoDB client API changes (chapters 4, 5-api, 7)
63-
- S3 client API changes (chapters 5-data-pipeline, 6, 8)
64-
- SNS client API changes (chapters 5-data-pipeline, 6)
65-
- CloudFormation/Logs client changes (chapter 6 integration tests)
56+
<!-- Testing -->
57+
<junit.version>5.10.3</junit.version>
58+
<mockito.version>5.12.0</mockito.version>
59+
<system-stubs.version>2.1.6</system-stubs.version>
6660

67-
**Note:** `aws-lambda-java-core` and `aws-lambda-java-events` are separate artifacts and remain compatible.
61+
<!-- X-Ray SDK v2 -->
62+
<xray.version>2.18.1</xray.version>
63+
</properties>
64+
```
6865

6966
---
7067

71-
## Phase 2: High Value Improvements
68+
## Future Improvements
7269

73-
### 4. Lambda SnapStart
70+
### Lambda SnapStart
7471
Add to template.yaml for fast cold starts:
7572
```yaml
7673
Globals:
@@ -80,14 +77,14 @@ Globals:
8077
```
8178
Requires `AutoPublishAlias` on each function.
8279

83-
### 5. ARM64 Architecture
80+
### ARM64 Architecture
8481
20% better price/performance:
8582
```yaml
8683
Architectures:
8784
- arm64
8885
```
8986

90-
### 6. AWS Lambda Powertools
87+
### AWS Lambda Powertools
9188
Replace manual logging/metrics/tracing (chapter 7):
9289
```xml
9390
<dependency>
@@ -97,111 +94,26 @@ Replace manual logging/metrics/tracing (chapter 7):
9794
</dependency>
9895
```
9996

100-
---
101-
102-
## Phase 3: Testing & Code Quality
103-
104-
### 7. JUnit 5 Migration
105-
- `junit:4.12` → `junit-jupiter:5.10.3`
106-
- `mockito-core:3.0.0` → `mockito-core:5.12.0`
107-
- Affects chapter 6 test code
108-
109-
### 8. Replace system-rules
110-
**Critical for Java 9+ compatibility**
111-
- `system-rules:1.19.0` → `system-stubs-jupiter:2.1.6`
112-
- Used in chapter 6 bulk-events-stage and single-event-stage tests
113-
- Currently skipped in test harness due to reflection errors
114-
115-
### 9. Java 21 Language Features
116-
Modernize code with:
117-
- Records for DTOs (WeatherEvent, etc.)
118-
- Pattern matching for instanceof
119-
- Text blocks for JSON templates
120-
121-
---
122-
123-
## Phase 4: Nice to Have
124-
125-
### 10. SAM Template Improvements
97+
### SAM Template Improvements
12698
- Use `Globals` section for DRY configuration
127-
- Consider SAM Connectors syntax
128-
- Add structured logging config:
129-
```yaml
130-
LoggingConfig:
131-
LogFormat: JSON
132-
```
133-
134-
### 11. Build Improvements
135-
- Consider maven-shade-plugin consistently (simpler than assembly)
136-
- Update maven plugins to latest versions
137-
138-
### 12. Additional AWS Features
139-
- Function URLs (alternative to API Gateway for simple cases)
140-
- Response streaming (for long-running responses)
99+
- Add structured logging: `LoggingConfig: { LogFormat: JSON }`
141100

142101
---
143102

144-
## Recommended Dependency Versions
145-
146-
```xml
147-
<properties>
148-
<!-- Java -->
149-
<maven.compiler.source>21</maven.compiler.source>
150-
<maven.compiler.target>21</maven.compiler.target>
151-
152-
<!-- AWS SDK v2 -->
153-
<aws.sdk.version>2.29.6</aws.sdk.version>
154-
155-
<!-- Lambda Libraries -->
156-
<aws.lambda.java.core.version>1.2.3</aws.lambda.java.core.version>
157-
<aws.lambda.java.events.version>3.14.0</aws.lambda.java.events.version>
158-
159-
<!-- Powertools -->
160-
<powertools.version>2.0.0</powertools.version>
161-
162-
<!-- Logging -->
163-
<log4j.version>2.23.1</log4j.version>
103+
## Test Harness Usage
164104

165-
<!-- JSON -->
166-
<jackson.version>2.17.2</jackson.version>
105+
### Local Testing
106+
```bash
107+
# Deploy all
108+
./test-harness/deploy/deploy-all.sh
167109
168-
<!-- Testing -->
169-
<junit.version>5.10.3</junit.version>
170-
<mockito.version>5.12.0</mockito.version>
171-
<system-stubs.version>2.1.6</system-stubs.version>
110+
# Test all
111+
./test-harness/test/test-all.sh
172112
173-
<!-- X-Ray SDK v2 -->
174-
<xray.version>2.18.1</xray.version>
175-
</properties>
113+
# Cleanup
114+
./test-harness/teardown/teardown-all.sh
176115
```
177116

178-
---
179-
180-
## Implementation Order
181-
182-
1. **Start with chapter 2** (simplest - no dependencies)
183-
- Update Java version
184-
- Update runtime
185-
- Test with harness
186-
187-
2. **Then chapter 3** (adds aws-lambda-java-core)
188-
189-
3. **Then chapter 4** (adds DynamoDB - first SDK v2 migration)
190-
191-
4. **Then chapter 5-api** (DynamoDB + Jackson + API Gateway)
192-
193-
5. **Continue with remaining chapters**
194-
195-
6. **Chapter 6 last** (most complex - has tests that need JUnit 5 migration)
196-
197-
---
198-
199-
## Test Harness Notes
200-
201-
### Known Issues
202-
- Chapter 6 integration test uses old CloudWatch logs (may pass when it should fail)
203-
- Unit tests skipped due to system-rules incompatibility with Java 9+
204-
205117
### GitHub Actions
206118
Requires IAM permissions on the GitHub Actions role:
207119
- `iam:CreateRole`, `iam:DeleteRole`, `iam:GetRole`, `iam:PassRole`
@@ -210,15 +122,3 @@ Requires IAM permissions on the GitHub Actions role:
210122
- `iam:TagRole`, `iam:UntagRole`
211123

212124
Scope to: `arn:aws:iam::ACCOUNT:role/lb-test-*`
213-
214-
### Local Testing
215-
```bash
216-
# Deploy all
217-
./test-harness/deploy/deploy-all.sh
218-
219-
# Test all
220-
./test-harness/test/test-all.sh
221-
222-
# Cleanup
223-
./test-harness/teardown/teardown-all.sh
224-
```

chapter3/src/main/java/book/ContextLambda.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
public class ContextLambda {
99
public Map<String, Object> handler(Object input, Context context) {
10-
Map<String, Object> toReturn = new HashMap<>();
10+
var toReturn = new HashMap<String, Object>();
1111
toReturn.put("getMemoryLimitInMB", context.getMemoryLimitInMB() + "");
1212
toReturn.put("getFunctionName", context.getFunctionName());
1313
toReturn.put("getFunctionVersion", context.getFunctionVersion());

chapter3/src/main/java/book/ListMapLambda.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,19 @@
88

99
public class ListMapLambda {
1010
public List<Integer> handlerList(List<Integer> input) {
11-
List<Integer> newList = new ArrayList<>();
11+
var newList = new ArrayList<Integer>();
1212
input.forEach(x -> newList.add(100 + x));
1313
return newList;
1414
}
1515

1616
public Map<String, String> handlerMap(Map<String, String> input) {
17-
Map<String, String> newMap = new HashMap<>();
17+
var newMap = new HashMap<String, String>();
1818
input.forEach((k, v) -> newMap.put("New Map -> " + k, v));
1919
return newMap;
2020
}
2121

2222
public Map<String, Map<String, Integer>> handlerNestedCollection(List<Map<String, Integer>> input) {
23-
Map<String, Map<String, Integer>> newMap = new HashMap<>();
23+
var newMap = new HashMap<String, Map<String, Integer>>();
2424
IntStream.range(0, input.size())
2525
.forEach(i -> newMap.put("Nested at position " + i, input.get(i)));
2626
return newMap;

chapter5-api/src/main/java/book/api/WeatherEventLambda.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,16 @@ public class WeatherEventLambda {
1818
private final String tableName = System.getenv("LOCATIONS_TABLE");
1919

2020
public ApiGatewayResponse handler(ApiGatewayRequest request) throws IOException {
21-
final WeatherEvent weatherEvent = objectMapper.readValue(request.body, WeatherEvent.class);
21+
var weatherEvent = objectMapper.readValue(request.body, WeatherEvent.class);
2222

23-
Map<String, AttributeValue> item = new HashMap<>();
23+
var item = new HashMap<String, AttributeValue>();
2424
item.put("locationName", AttributeValue.builder().s(weatherEvent.locationName).build());
2525
item.put("temperature", AttributeValue.builder().n(String.valueOf(weatherEvent.temperature)).build());
2626
item.put("timestamp", AttributeValue.builder().n(String.valueOf(weatherEvent.timestamp)).build());
2727
item.put("longitude", AttributeValue.builder().n(String.valueOf(weatherEvent.longitude)).build());
2828
item.put("latitude", AttributeValue.builder().n(String.valueOf(weatherEvent.latitude)).build());
2929

30-
PutItemRequest putItemRequest = PutItemRequest.builder()
30+
var putItemRequest = PutItemRequest.builder()
3131
.tableName(tableName)
3232
.item(item)
3333
.build();

chapter6/integration-tests/src/test/java/book/pipeline/PipelineIT.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ public PipelineIT() {
3535

3636
@Test
3737
public void endToEndTest() throws InterruptedException {
38-
String bucketName = resolvePhysicalId("PipelineStartBucket");
39-
String key = UUID.randomUUID().toString();
40-
File file = new File(getClass().getResource("/bulk_data.json").getFile());
38+
var bucketName = resolvePhysicalId("PipelineStartBucket");
39+
var key = UUID.randomUUID().toString();
40+
var file = new File(getClass().getResource("/bulk_data.json").getFile());
4141

4242
// 1. Upload bulk_data file to S3
4343
s3.putObject(PutObjectRequest.builder()
@@ -47,8 +47,8 @@ public void endToEndTest() throws InterruptedException {
4747

4848
// 2. Check for executions of SingleEventLambda
4949
Thread.sleep(30000);
50-
String singleEventLambda = resolvePhysicalId("SingleEventLambda");
51-
Set<String> logMessages = getLogMessages(singleEventLambda);
50+
var singleEventLambda = resolvePhysicalId("SingleEventLambda");
51+
var logMessages = getLogMessages(singleEventLambda);
5252
assertThat(logMessages, hasItems(
5353
"WeatherEvent{locationName='Brooklyn, NY', temperature=91.0, timestamp=1564428897, longitude=-73.99, latitude=40.7}",
5454
"WeatherEvent{locationName='Oxford, UK', temperature=64.0, timestamp=1564428898, longitude=-1.25, latitude=51.75}",
@@ -65,23 +65,23 @@ public void endToEndTest() throws InterruptedException {
6565
logs.deleteLogGroup(DeleteLogGroupRequest.builder()
6666
.logGroupName(getLogGroup(singleEventLambda))
6767
.build());
68-
String bulkEventsLambda = resolvePhysicalId("BulkEventsLambda");
68+
var bulkEventsLambda = resolvePhysicalId("BulkEventsLambda");
6969
logs.deleteLogGroup(DeleteLogGroupRequest.builder()
7070
.logGroupName(getLogGroup(bulkEventsLambda))
7171
.build());
7272
}
7373

7474
private String resolvePhysicalId(String logicalId) {
75-
DescribeStackResourceRequest request = DescribeStackResourceRequest.builder()
75+
var request = DescribeStackResourceRequest.builder()
7676
.stackName(stackName)
7777
.logicalResourceId(logicalId)
7878
.build();
79-
DescribeStackResourceResponse response = cfn.describeStackResource(request);
79+
var response = cfn.describeStackResource(request);
8080
return response.stackResourceDetail().physicalResourceId();
8181
}
8282

8383
private Set<String> getLogMessages(String lambdaName) {
84-
String logGroup = getLogGroup(lambdaName);
84+
var logGroup = getLogGroup(lambdaName);
8585

8686
return logs.describeLogStreams(DescribeLogStreamsRequest.builder()
8787
.logGroupName(logGroup)

0 commit comments

Comments
 (0)