Skip to content

Commit 3139b2c

Browse files
committed
Issue #148 fix WORKDIR in distroless stage, document docker cp fallback for macOS/Colima
1 parent 7db5b28 commit 3139b2c

7 files changed

Lines changed: 143 additions & 17 deletions

File tree

jdt2jar/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ RUN ["mkdir", "-p", "/empty-work/tmp", "/empty-app"]
1010
FROM gcr.io/distroless/base-debian13:nonroot
1111
COPY --from=build --chown=65532:65532 /empty-work /work
1212
COPY --from=build --chown=65532:65532 /empty-app /app
13+
WORKDIR /work
1314
ENV JAVA_TOOL_OPTIONS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -Djava.io.tmpdir=/work/tmp -XX:+ExitOnOutOfMemoryError"
1415
COPY --from=build /opt/jre /jre
1516
COPY --from=build /build/jdt2jar/target/jdt2jar.jar /app/jdt2jar.jar

jdt2jar/README.md

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# jdt2jar
22

3-
`jdt2jar` compiles a JTD schema into a standalone validator JAR at build time. The generated JAR runs on JDK 21+ with no JDK 24+ runtime dependency.
3+
`jdt2jar` compiles a JTD schema into a standalone validator JAR at build time. The generated JAR runs on JDK 21+ with no JDK 25+ runtime dependency.
44

55
## Use Case
66

77
This tool bridges the gap between the interpreter and codegen paths:
88

99
- **Interpreter** ([`json-java21-jtd`](../json-java21-jtd/README.md)): ideal for infrequent config parsing — simple, no build step, runs on JDK 21+.
10-
- **Codegen** ([`json-java21-jtd-codegen`](../json-java21-jtd-codegen/README.md)): ideal for repeated hot-path validation — ~9x faster, but requires JDK 24+ at runtime.
11-
- **jdt2jar**: pre-compiles schemas into validator JARs at build time (using JDK 24+), then deploys them to any JDK 21+ runtime. Best for CI/CD pipelines, distroless containers, or environments where you want JIT-optimised validators without shipping a JDK 24+ runtime.
10+
- **Codegen** ([`json-java21-jtd-codegen`](../json-java21-jtd-codegen/README.md)): ideal for repeated hot-path validation — ~9x faster, but requires JDK 25+ at runtime.
11+
- **jdt2jar**: pre-compiles schemas into validator JARs at build time (using JDK 25+), then deploys them to any JDK 21+ runtime. Best for CI/CD pipelines, distroless containers, or environments where you want JIT-optimised validators without shipping a JDK 25+ runtime.
1212

1313
> **Future note**: `java.util.json` has entered the JDK incubator (`jdk.incubator.json`). Once the API stabilises in the JDK itself, generated bytecode validators can depend directly on future JDK classes rather than this backport, making them even more efficient with zero library overhead.
1414
@@ -39,35 +39,47 @@ A minimal distroless container image is available for offline schema compilation
3939
docker pull ghcr.io/simbo1905/java.util.json.java21/jdt2jar:latest
4040

4141
# Pull a specific release version
42-
docker pull ghcr.io/simbo1905/java.util.json.java21/jdt2jar:2026.02.05
42+
docker pull ghcr.io/simbo1905/java.util.json.java21/jdt2jar:2026.05.20
4343
```
4444

4545
### Build Locally
4646

47-
Requires Docker and JDK 24+ (for the build stage). Build from the repository root:
47+
Requires Docker and JDK 25+ (for the build stage). Build from the repository root:
4848

4949
```bash
5050
docker build -t jdt2jar -f jdt2jar/Dockerfile .
5151
```
5252

5353
### Usage
5454

55+
The container's working directory is `/work`. Mount your local directory there, or use `docker cp` for environments where volume mounts are restricted (e.g., Colima on macOS).
56+
57+
#### Option 1: Volume mount (Linux, Docker Desktop with file sharing enabled)
58+
5559
```bash
56-
# Show help
57-
docker run --rm ghcr.io/simbo1905/java.util.json.java21/jdt2jar:latest --help
60+
# bash / zsh
61+
docker run --rm -v "$(pwd):/work" ghcr.io/simbo1905/java.util.json.java21/jdt2jar:latest schema.jtd.json --output schema-validator.jar --main
5862

59-
# Compile a schema to a validator JAR (using docker cp for file I/O)
60-
cid=$(docker create --name jdt2jar-build ghcr.io/simbo1905/java.util.json.java21/jdt2jar:latest /work/person.jtd.json --output /work/person-validator.jar --main)
61-
docker cp person.jtd.json jdt2jar-build:/work/person.jtd.json
63+
# PowerShell
64+
docker run --rm -v "${PWD}:/work" ghcr.io/simbo1905/java.util.json.java21/jdt2jar:latest schema.jtd.json --output schema-validator.jar --main
65+
66+
# cmd.exe
67+
docker run --rm -v "%CD%:/work" ghcr.io/simbo1905/java.util.json.java21/jdt2jar:latest schema.jtd.json --output schema-validator.jar --main
68+
```
69+
70+
#### Option 2: docker cp (works everywhere, including Colima on macOS)
71+
72+
```bash
73+
# Compile a schema
74+
cid=$(docker create --name jdt2jar-build ghcr.io/simbo1905/java.util.json.java21/jdt2jar:latest /work/schema.jtd.json --output /work/schema-validator.jar --main)
75+
docker cp schema.jtd.json jdt2jar-build:/work/schema.jtd.json
6276
docker start -a jdt2jar-build
63-
docker cp jdt2jar-build:/work/person-validator.jar .
77+
docker cp jdt2jar-build:/work/schema-validator.jar .
6478
docker rm jdt2jar-build
6579

66-
# Validate a payload with the generated JAR
67-
java -jar person-validator.jar --validate payload.json
68-
# Or validate inside a container
69-
cid=$(docker create --name jdt2jar-validate --entrypoint /jre/bin/java ghcr.io/simbo1905/java.util.json.java21/jdt2jar:latest -jar /work/person-validator.jar --validate /work/payload.json)
70-
docker cp person-validator.jar jdt2jar-validate:/work/person-validator.jar
80+
# Validate a payload
81+
cid=$(docker create --name jdt2jar-validate --entrypoint /jre/bin/java ghcr.io/simbo1905/java.util.json.java21/jdt2jar:latest -jar /work/schema-validator.jar --validate /work/payload.json)
82+
docker cp schema-validator.jar jdt2jar-validate:/work/schema-validator.jar
7183
docker cp payload.json jdt2jar-validate:/work/payload.json
7284
docker start -a jdt2jar-validate
7385
docker rm jdt2jar-validate
@@ -76,7 +88,7 @@ docker rm jdt2jar-validate
7688
### Image Properties
7789

7890
- **Base**: `gcr.io/distroless/base-debian13:nonroot`
79-
- **Runtime**: jlink-minimized JDK 24 (~40 MB)
91+
- **Runtime**: jlink-minimized JDK 25 LTS (~40 MB)
8092
- **Total size**: ~111 MB disk / ~31 MB content
8193
- **User**: `nonroot` (uid 65532)
8294
- **Shell**: none (distroless)

tmp/invalid.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"name":123,"age":-1}

tmp/simple.jar

90.6 KB
Binary file not shown.

tmp/simple.jtd.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"properties":{"name":{"type":"string"},"age":{"type":"uint8"}}}

tmp/test.jdt.json

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
{
2+
"definitions": {
3+
"user": {
4+
"properties": {
5+
"id": { "type": "string" },
6+
"email": { "type": "string" },
7+
"displayName": { "type": "string" },
8+
"role": {
9+
"enum": ["admin", "manager", "contributor"]
10+
}
11+
},
12+
"optionalProperties": {
13+
"timezone": { "type": "string" }
14+
}
15+
},
16+
17+
"comment": {
18+
"properties": {
19+
"id": { "type": "string" },
20+
"authorId": { "type": "string" },
21+
"message": { "type": "string" },
22+
"createdAt": { "type": "timestamp" }
23+
}
24+
},
25+
26+
"taskEvent": {
27+
"discriminator": "type",
28+
"mapping": {
29+
"created": {
30+
"properties": {
31+
"type": { "enum": ["created"] },
32+
"createdBy": { "type": "string" },
33+
"createdAt": { "type": "timestamp" }
34+
}
35+
},
36+
37+
"statusChanged": {
38+
"properties": {
39+
"type": { "enum": ["statusChanged"] },
40+
"from": {
41+
"enum": ["todo", "in_progress", "blocked", "done"]
42+
},
43+
"to": {
44+
"enum": ["todo", "in_progress", "blocked", "done"]
45+
},
46+
"changedBy": { "type": "string" },
47+
"changedAt": { "type": "timestamp" }
48+
}
49+
},
50+
51+
"commentAdded": {
52+
"properties": {
53+
"type": { "enum": ["commentAdded"] },
54+
"comment": { "ref": "comment" },
55+
"addedAt": { "type": "timestamp" }
56+
}
57+
}
58+
}
59+
}
60+
},
61+
62+
"properties": {
63+
"projectId": { "type": "string" },
64+
"name": { "type": "string" },
65+
66+
"owner": {
67+
"ref": "user"
68+
},
69+
70+
"tasks": {
71+
"elements": {
72+
"properties": {
73+
"id": { "type": "string" },
74+
"title": { "type": "string" },
75+
76+
"status": {
77+
"enum": ["todo", "in_progress", "blocked", "done"]
78+
},
79+
80+
"assignee": {
81+
"ref": "user"
82+
},
83+
84+
"labels": {
85+
"elements": {
86+
"type": "string"
87+
}
88+
},
89+
90+
"activity": {
91+
"elements": {
92+
"ref": "taskEvent"
93+
}
94+
}
95+
},
96+
97+
"optionalProperties": {
98+
"description": { "type": "string" },
99+
"dueDate": { "type": "timestamp" },
100+
101+
"metadata": {
102+
"values": {
103+
"type": "string"
104+
}
105+
}
106+
}
107+
}
108+
}
109+
}
110+
}

tmp/valid.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"name":"Alice","age":30}

0 commit comments

Comments
 (0)