|
1 | 1 | # Java Dynamic Deduplication Sample |
2 | 2 |
|
3 | | -This sample is a Spring Boot application used by Keploy CI to validate Java dynamic deduplication in native and Docker runs. It mirrors the Go dedup sample by exposing a broad set of endpoints and committing 1000 replay fixtures across four testsets. |
| 3 | +A Spring Boot application used by Keploy CI to validate Java dynamic deduplication in native and Docker runs. It mirrors the Go dedup sample by exposing a broad set of endpoints and committing 1000 replay fixtures across four testsets. |
4 | 4 |
|
5 | 5 | CI does not record this sample. The `keploy/` directory is checked in so the pipeline only builds the app and runs replay with `--dedup`. |
6 | 6 |
|
7 | | -Build the application after installing the Java SDK locally: |
| 7 | +## How dedup works for Java |
| 8 | + |
| 9 | +Keploy's dynamic dedup engine asks the application for per-test code coverage between every replayed test, then skips future tests whose coverage signature it has already seen. The Java SDK (`io.keploy.dedup.KeployDedupAgent`, bundled in the app via the `keploy-sdk` dependency) handles that coverage exchange. |
| 10 | + |
| 11 | +The SDK reads coverage **in-process** through JaCoCo's runtime API (`org.jacoco.agent.rt.RT.getAgent().getExecutionData(...)`). All you have to do is attach the JaCoCo Java agent — no TCP server, no port choice, no `--pass-through-ports`: |
| 12 | + |
| 13 | +``` |
| 14 | +-javaagent:target/jacocoagent.jar |
| 15 | +``` |
| 16 | + |
| 17 | +The SDK still falls back to JaCoCo's TCP server mode if the in-process API is unavailable for some reason, which is why the `KEPLOY_JACOCO_HOST` and `KEPLOY_JACOCO_PORT` environment variables are still honoured. |
| 18 | + |
| 19 | +## Build |
8 | 20 |
|
9 | 21 | ```bash |
10 | 22 | mvn -B -DskipTests package |
11 | 23 | ``` |
12 | 24 |
|
13 | | -Run it natively with JaCoCo TCP server mode: |
| 25 | +This produces `target/java-dedup-0.0.1-SNAPSHOT.jar` and copies `target/jacocoagent.jar` next to it. |
| 26 | + |
| 27 | +## Native run |
| 28 | + |
| 29 | +Start the app with the JaCoCo agent attached: |
14 | 30 |
|
15 | 31 | ```bash |
16 | | -java -javaagent:target/jacocoagent.jar=address=127.0.0.1,port=36320,destfile=target/jacoco-keploy.exec,output=tcpserver \ |
| 32 | +java -javaagent:target/jacocoagent.jar \ |
17 | 33 | -jar target/java-dedup-0.0.1-SNAPSHOT.jar |
18 | 34 | ``` |
19 | 35 |
|
| 36 | +Replay with dynamic dedup: |
| 37 | + |
| 38 | +```bash |
| 39 | +keploy test \ |
| 40 | + -c "java -javaagent:target/jacocoagent.jar -jar target/java-dedup-0.0.1-SNAPSHOT.jar" \ |
| 41 | + --dedup --language java --delay 20 |
| 42 | +``` |
| 43 | + |
20 | 44 | To regenerate the committed fixtures locally, record high-volume traffic against the running app: |
21 | 45 |
|
22 | 46 | ```bash |
23 | 47 | ./run_random_1000.sh |
24 | 48 | ``` |
25 | 49 |
|
26 | | -When replaying with dynamic deduplication, pass the JaCoCo port through Keploy so the SDK can talk to the local JaCoCo TCP server: |
| 50 | +## Docker run |
| 51 | + |
| 52 | +Build the JAR first, then build the image. The Dockerfile already wires the JaCoCo agent into the entrypoint: |
27 | 53 |
|
28 | 54 | ```bash |
29 | | -keploy test \ |
30 | | - -c "java -javaagent:target/jacocoagent.jar=address=127.0.0.1,port=36320,destfile=target/jacoco-keploy.exec,output=tcpserver -jar target/java-dedup-0.0.1-SNAPSHOT.jar" \ |
31 | | - --dedup --pass-through-ports 36320 |
| 55 | +mvn -B -DskipTests package |
| 56 | +docker compose build |
32 | 57 | ``` |
33 | 58 |
|
34 | | -Run it with Docker Compose after the Maven package step has created `target/java-dedup-0.0.1-SNAPSHOT.jar`: |
| 59 | +Run with Keploy in dedup mode: |
35 | 60 |
|
36 | 61 | ```bash |
37 | | -docker compose build |
38 | | -docker compose up |
| 62 | +keploy test \ |
| 63 | + -c "docker compose up" \ |
| 64 | + --container-name "dedup-java" \ |
| 65 | + --dedup --language java --delay 20 |
39 | 66 | ``` |
40 | 67 |
|
41 | 68 | The Compose file supports `JAVA_DEDUP_IMAGE`, `JAVA_DEDUP_CONTAINER_NAME`, and `JAVA_DEDUP_HOST_PORT` so CI can isolate repeated replay runs without recording new tests. |
42 | 69 |
|
43 | | -The Compose app bind-mounts host `/tmp` into the container so Keploy and the Java SDK use the same Unix socket paths for `/tmp/coverage_control.sock` and `/tmp/coverage_data.sock`. The image runs as a non-root user. For a more restricted container run with a read-only root filesystem, dropped capabilities, `no-new-privileges`, and writable host `/tmp` bind-mounted for Keploy's Unix sockets: |
| 70 | +The Compose app bind-mounts host `/tmp` into the container so Keploy and the Java SDK use the same Unix socket paths for `/tmp/coverage_control.sock` and `/tmp/coverage_data.sock`. |
| 71 | + |
| 72 | +### Restricted Docker |
| 73 | + |
| 74 | +For a more restricted container — read-only root filesystem, dropped capabilities, and `no-new-privileges` — overlay the restricted compose file: |
44 | 75 |
|
45 | 76 | ```bash |
46 | 77 | docker compose -f docker-compose.yml -f docker-compose.restricted.yml build |
47 | | -docker compose -f docker-compose.yml -f docker-compose.restricted.yml up |
| 78 | +keploy test \ |
| 79 | + -c "docker compose -f docker-compose.yml -f docker-compose.restricted.yml up" \ |
| 80 | + --container-name "dedup-java" \ |
| 81 | + --dedup --language java --delay 20 |
48 | 82 | ``` |
| 83 | + |
| 84 | +Host `/tmp` is still bind-mounted so the SDK and Keploy share the dedup Unix sockets. |
0 commit comments