← Java

Java · 16 / 18

Offline PDF workflow with JavaFX and SQLite

Follow template, mapping, preview, background jobs and local project persistence through a real desktop app.

  • Large
  • Advanced
  • 120 min

Prerequisites: JUnit, semantic PDF tests and Testcontainers

PDF Batch Studio connects a framework-free processing core to a JavaFX interface. JavaFX has its own UI thread. Long-running PDF work must not block it. The core describes states, the desktop adapter executes work and the interface displays progress. SQLite and Flyway store projects and history. Current source includes developments beyond public release v0.1.0. This tutorial therefore uses the pinned source rather than silently assuming an older download.

01Set up the pinned project

Setup clones the project into its own learning folder and checks desktop modules. Use JDK 21. On a machine with a graphical desktop, ./scripts/desktop-dev.sh starts the app. Windows has scripts/desktop-dev.ps1. Use only safe files from samples.

setup.sh · bashOpen file
git clone https://github.com/wasiliy-strecker/pdf-batch-studio.git pdf-batch-studio-learning
cd pdf-batch-studio-learning
git checkout 9b85992b00f8ff20b46bff2b1ed16b59eb3869af
./mvnw -pl desktop/pdf-batch-desktop -am test

02Understand the state rule in isolation

The small simulator shows DRAFT, READY and QUEUED. It rejects starting before configuration. The real JobStatus contains additional processing, cancellation and terminal states. A disabled button complements the rule but does not solely enforce it.

Workflow.java · javaOpen file
public class Workflow {
  enum State { DRAFT, READY, QUEUED }
  static final class Job {
    State state = State.DRAFT;
    void configure(boolean mappingValid) {
      if (state != State.DRAFT || !mappingValid) throw new IllegalStateException("Cannot configure");
      state = State.READY;
    }
    void queue() {
      if (state != State.READY) throw new IllegalStateException("Not ready");
      state = State.QUEUED;
    }
  }
  public static void main(String[] args) {
    var job = new Job();
    try { job.queue(); throw new AssertionError("Invalid start accepted"); }
    catch (IllegalStateException expected) { System.out.println(expected.getMessage()); }
    job.configure(true); job.queue(); System.out.println(job.state);
  }
}

Run / check

java Workflow.java

03Map template and CSV

Open samples/customer-template.pdf and samples/customers.csv. Map name to PDF field fullName and customerId to customerNumber. Use {customerId}-{name}.pdf as the output name pattern. The engine checks fields and columns before reaching READY.

04Follow preview and background work

Generate a preview first. Then start the batch and observe transitions. Read DesktopWorkflowService and the view-model test. The UI thread displays status while a worker processes documents. Cancellation is checked at defined points rather than arbitrarily killing any file operation.

05Distinguish project and history

A saved project retains template and configuration. Recipient tables and intermediate results stay only in the managed workspace. Passwords are not stored. According to the README, the app does not encrypt the local database or templates. After restarting, check which information remains.

06Verify the result semantically

customers.csv produces three documents. Open the ZIP and check unique names, recipient values and the report. Repeat with customers-duplicate-output.csv. Identical naming values must not silently overwrite results. The desktop test verifies this workflow without a real user contract.

Expected result

A preview and a ZIP containing three individually filled PDFs. A saved project remains after reopening.

Data flow
  1. JavaFX draft
  2. DesktopWorkflowService
  3. shared Java core
  4. PDF/CSV engine
  5. SQLite / result / UI status

Your exercise

Check an invalid CSV with a duplicate header and cancellation between rows. Which data should remain in each case?

Show the solution and explanation

invalid-duplicate-header.csv is rejected during inspection and must not reach READY. Cancellation retains a traceable terminal state and must not report an apparently complete result. Saved project configuration and temporary recipient data have different retention rules. Check them in workflow and repository tests.

Project context and sources

The example is simplified for learning. The linked source version shows its use in the project and the checks applied there.