Skip to content
Merged
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
46 changes: 46 additions & 0 deletions fr-batch-service/docs/job-plugins/developer-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ Roles below).
| `GET` | `/jobs/definitions/{id}` | `PLUGIN_VIEWER`+ | Get a single definition |
| `PUT` | `/jobs/definitions/{id}/enable` | `PLUGIN_ADMIN` | Enable a definition |
| `PUT` | `/jobs/definitions/{id}/disable` | `PLUGIN_ADMIN` | Disable a definition |
| `PUT` | `/jobs/definitions/{id}/jar` | `PLUGIN_ADMIN` | Replace the JAR in place (see [Replace JAR](#replace-jar)) |
| `DELETE` | `/jobs/definitions/{id}[?force=true]` | `PLUGIN_ADMIN` | Unload, delete definition, then delete JAR file (see [Delete](#delete)) |
| `GET` | `/jobs/plugins` | Public | List merged plugin metadata |

Expand Down Expand Up @@ -297,6 +298,51 @@ curl -u admin:admin123 -X DELETE \
"http://localhost:8080/api/batch-service/jobs/definitions/1?force=true"
```

### Replace JAR

`PUT /jobs/definitions/{id}/jar` replaces the JAR of an existing definition in place, for example to
roll back to a previous working version. The definition keeps its ID, so its execution history and
parameter templates stay linked. It is a multipart request with the same fields as an upload except
`jobName`, which cannot change: `file`, `version`, and `mainClassName`.

The operation runs under the same per-definition lock as load, unload, reload, and delete:

1. The new JAR is validated first, like an upload (`.jar` extension, ZIP magic bytes,
fully-qualified main class name, signature policy), and must contain the main class entry. No
plugin code runs at this point. It is stored under a unique file name
(`{jobName}-{version}-{random}.jar`), so the current JAR is never overwritten. An invalid JAR
changes nothing: the plugin stays loaded if it was loaded.
1. The plugin is then unloaded, without force. Running executions reject the replacement; the new
JAR is discarded and nothing else changes.
1. One database transaction updates the JAR path, checksum, version, and main class, and resets the
approval to `PENDING`. The definition stays `UNLOADED`; `enabled` is unchanged. In non-production
profiles the auto-approval is applied again after commit, as on upload.
1. The previous JAR is deleted after that transaction commits.

After a successful replacement, approve the definition (unless it was auto-approved) and load it
again. Loading still checks that the plugin's job name matches the definition.

| Outcome | Response |
| --------------------- | ------------------------------------------------------------------------ |
| Replaced | `200 OK` with the updated definition |
| Unknown ID | `404 Not Found` |
| Running executions | `409 Conflict`; new JAR discarded, nothing is unloaded or changed |
| Invalid JAR | `400 Bad Request`; the definition and the plugin are unchanged |
| Database update fails | Error response; rolled back, new JAR deleted, current JAR kept, unloaded |

A missing previous JAR does not fail the request. If the previous JAR cannot be removed after
commit, the request still returns `200`, a `WARN` is logged, and the `JAR_REPLACED` success audit
names the orphan JAR path. `JAR_REPLACED` is audited `SUCCESS` only after the whole operation;
otherwise it is audited `FAILURE` with the definition ID, the reason, and what was kept.

```bash
curl -u admin:admin123 -X PUT \
-F "file=@target/example-payment-job-1.0.0.jar" \
-F "version=1.0.0" \
-F "mainClassName=com.example.payment.PaymentJobPlugin" \
http://localhost:8080/api/batch-service/jobs/definitions/1/jar
```

### Job Execution

| Method | Path | Auth | Description |
Expand Down
12 changes: 8 additions & 4 deletions fr-batch-service/docs/production-checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,14 @@ ______________________________________________________________________
definition in one operation (running executions are not stopped; the JAR is deleted after the
database commit, and an orphan JAR is logged and named in the `JOB_DELETED` audit)
1. If DELETE returns `409` because the definition has execution history (`force=true` does not
bypass this), disable it with `PUT /jobs/definitions/{id}/disable` and unload it with
`POST /jobs/definitions/{id}/unload?force=true` instead. Note that a replacement upload with
the same job name is rejected as a duplicate while that definition exists.
1. Upload the previous working version
bypass this), keep the definition and replace its JAR in place instead:
`PUT /jobs/definitions/{id}/jar` with the previous working JAR (`file`, `version`,
`mainClassName`). It returns `409` while executions are running; wait for them to finish. The
definition keeps its ID and execution history, stays unloaded, and its approval is reset to
`PENDING`.
1. Approve it with `PUT /jobs/definitions/{id}/approve` and load it with
`POST /jobs/definitions/{id}/load`. After a successful DELETE, upload the previous working
version with `POST /jobs/upload` instead.
- [ ] **Rollback tested**: A rollback drill has been completed within the RTO window.

______________________________________________________________________
Expand Down
4 changes: 3 additions & 1 deletion fr-batch-service/docs/runbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,9 @@ docker compose exec mysql mysql -u root -p"${DB_PASSWORD}" -e \

**Resolution**:

1. Corrupted JAR → re-upload: `POST /jobs/upload`
1. Corrupted JAR → replace it in place: `PUT /jobs/definitions/{id}/jar` (multipart `file`,
`version`, `mainClassName`), then approve (`PUT /jobs/definitions/{id}/approve`) and load
(`POST /jobs/definitions/{id}/load`). The definition keeps its ID and execution history.
1. Missing dependency in JAR → check `MANIFEST.MF` classpath
1. Signature mismatch → re-sign JAR or temporarily set `app.plugins.signature.mode=permissive`
during investigation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
* Lifecycle event types tracked by the audit trail.
*
* <p>Every event maps to a specific operation in the plugin lifecycle:
* upload → approve/reject → load → (run) → unload → delete.
* upload → approve/reject → load → (run) → unload → replace JAR → delete.
*/
public enum AuditEventType {
JAR_UPLOADED,
JAR_REPLACED,
JOB_LOADED,
JOB_UNLOADED,
JOB_DELETED,
Expand Down
Loading
Loading