Skip to content

Commit b639e6f

Browse files
authored
Merge pull request #6 from crobbins215/upgrade/camunda-8.9.1
Upgrade to Camunda 8.9.1 / PDFBox 3.0.7 (v1.4.0)
2 parents 91e1643 + 4ef1529 commit b639e6f

7 files changed

Lines changed: 86 additions & 26 deletions

File tree

CLAUDE.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project
6+
7+
Camunda 8 outbound connector that merges and splits PDF documents inside BPMN workflows. Built on the Camunda Connector SDK (8.9.1) and Apache PDFBox (3.0.7), targeting Java 21 (`.tool-versions` pins `temurin-21.0.8` + `maven 3.8.4`).
8+
9+
> **Runtime note:** the `camunda/connectors:8.9.1` base image ships **Java 25 + Spring Boot 4.0.5 + Tomcat 11**. We compile to Java 21 bytecode and rely on JVM backward compatibility — don't be surprised when container logs report Java 25.
10+
11+
## Common commands
12+
13+
```bash
14+
mvn clean package # Build thin + shaded fat JAR; also regenerates element-templates/pdf-connector.json
15+
mvn clean verify # Run all tests + JaCoCo report (target/site/jacoco/)
16+
mvn -Pcoverage-enforced verify # Same, but fails build under 80% instruction / 75% branch coverage
17+
mvn test -Dtest=PdfConnectorTest # Single test class
18+
mvn test -Dtest=PdfConnectorTest#shouldMergeTwoPdfs # Single test method
19+
```
20+
21+
The full Camunda runtime integration test (`integration/PdfConnectorIntegrationTest`) is `@Disabled` by default — it pulls `camunda/camunda:8.9.1` via Docker. Enable manually when Docker is available.
22+
23+
Run the connector locally inside a Spring Boot runtime by starting the **test-scope** main class `io.camunda.example.classic.LocalConnectorRuntime` (Camunda config lives in `src/main/resources/application.properties`, copied from the `.template` file — never commit the real one).
24+
25+
Enable the secret-scan pre-commit hook once per clone: `git config core.hooksPath .githooks`. Bypass with `SKIP_SECRET_CHECK=1` only if absolutely necessary.
26+
27+
## Architecture
28+
29+
This connector uses the **Operations API** pattern (`OutboundConnectorProvider` + `@Operation`), which is newer than the classic `OutboundConnectorFunction` single-entry-point style. One class exposes multiple operations and the SDK routes by `operationId`:
30+
31+
- [src/main/java/io/camunda/example/operations/PdfConnectorProvider.java](src/main/java/io/camunda/example/operations/PdfConnectorProvider.java) — the `@OutboundConnector` + `@ElementTemplate` entry point. Each `@Operation`-annotated method (mergePdfs, splitByPage, splitByRange, splitByBookmark, splitBySize) is a thin delegate to `PdfOperations`. The `@Variable` parameter is a discriminated request record nested under `PdfConnectorRequest`.
32+
- [src/main/java/io/camunda/example/operations/PdfOperations.java](src/main/java/io/camunda/example/operations/PdfOperations.java) — all PDFBox logic. Static methods load PDFs via `Loader.loadPDF(RandomAccessReadBuffer)`, manipulate them, and create new `Document` objects via `context.create(DocumentCreationRequest...)`. Errors must be thrown as `ConnectorException` with one of the documented codes (`PDF_MERGE_ERROR`, `PDF_SPLIT_ERROR`, `NO_BOOKMARKS`, `INVALID_PAGE_RANGE`).
33+
- [src/main/java/io/camunda/dto/PdfConnectorRequest.java](src/main/java/io/camunda/dto/PdfConnectorRequest.java) and [PdfConnectorResult.java](src/main/java/io/camunda/dto/PdfConnectorResult.java) — Java records with `@TemplateProperty` and Jakarta Bean Validation annotations. **The element template is generated from these records at `mvn package` time** by the `element-template-generator-maven-plugin`, so any field/label/group change must be made in the record (do not hand-edit `element-templates/pdf-connector.json`).
34+
- [src/main/resources/META-INF/services/io.camunda.connector.api.outbound.OutboundConnectorProvider](src/main/resources/META-INF/services/io.camunda.connector.api.outbound.OutboundConnectorProvider) — SPI registration; must list `PdfConnectorProvider` for the runtime to discover the connector.
35+
36+
### Shading (important)
37+
38+
`maven-shade-plugin` relocates `org.apache.pdfbox``io.camunda.connector.pdf.shaded.pdfbox` (and fontbox similarly) in the fat JAR. This avoids classpath clashes with other connectors in the same runtime. The connector SDK is `<scope>provided</scope>` and supplied by the runtime — do not bundle it.
39+
40+
### Test layout quirk
41+
42+
`LocalConnectorRuntime` (the dev entry point) lives under `src/test/java/io/camunda/example/classic/`, not `src/main/java`. It is a Spring Boot starter that pulls in `spring-boot-starter-camunda-connectors` (test scope). Don't move it to main without also moving its dependencies out of test scope.
43+
44+
## Conventions
45+
46+
- Versions in `pom.xml`, `Dockerfile` (`COPY target/pdf-merge-split-connector-<version>.jar`), and the README release notes must stay in sync. Bump all three on release.
47+
- The Dockerfile copies the connector JAR to `/opt/custom/`, not `/opt/app/`. The 8.9.x base image's `start.sh` sets `-Dloader.path=/opt/custom/`; `/opt/app/` is reserved for the runtime jar itself and putting the connector there will crash with a `NoClassDefFoundError` for `OutboundConnectorProvider`. (Earlier 8.8.x images accepted `/opt/app/`.)
48+
- `element-template-generator-core` is a build-time-only dependency and must remain `<scope>provided</scope>` — it transitively pulls in slf4j-api, Jackson, scala-library, etc., which collide with the runtime if shaded into the fat JAR.
49+
- README documents user-facing behavior; keep operation input/output JSON schemas there in sync with the DTOs.
50+
- Coverage is reported on every `verify` but only enforced under the `coverage-enforced` profile — CI runs the unenforced variant.

Dockerfile

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
FROM camunda/connectors:8.8.3
1+
FROM camunda/connectors:8.9.1
22

3-
# Copy the connector JAR
4-
COPY target/pdf-merge-split-connector-1.3.1.jar /opt/app/
3+
# Connector JARs go in /opt/custom/ (loader.path for Spring Boot PropertiesLauncher).
4+
# In 8.8.x and earlier, /opt/app/ worked; the 8.9.x base image changed this.
5+
COPY target/pdf-merge-split-connector-1.4.0.jar /opt/custom/
56

67
# The base image will automatically pick up the connector

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ In many IDP (Intelligent Document Processing) scenarios, users deal with scanned
2323
This connector uses the **Operations API** approach with `OutboundConnectorProvider`, allowing multiple PDF operations within a single connector without manual routing.
2424

2525
**Key Technologies:**
26-
- Apache PDFBox 3.0.3 for PDF manipulation
27-
- Camunda Connector SDK 8.8.3
26+
- Apache PDFBox 3.0.7 for PDF manipulation
27+
- Camunda Connector SDK 8.9.1
2828
- Java 21
2929

3030
**Operations Implementation:** [`io.camunda.example.operations.PdfConnectorProvider`](src/main/java/io/camunda/example/operations/PdfConnectorProvider.java)
@@ -429,6 +429,15 @@ The generated element template can be found in [element-templates/pdf-connector.
429429

430430
## Release Notes
431431

432+
**Version 1.4.0**
433+
434+
Changes:
435+
-**Camunda 8.9** - Upgraded Connector SDK and `camunda-process-test-spring` to `8.9.1` to match the latest stable cluster
436+
-**Docker** - Base image bumped to `camunda/connectors:8.9.1`
437+
-**PDFBox** - Upgraded to `3.0.7` (patch-level)
438+
-**Test stack** - JUnit Jupiter `5.14.4`, AssertJ `3.27.7`, Mockito `5.23.0`, Awaitility `4.3.0`
439+
-**Build plugins** - JaCoCo `0.8.14`, maven-shade-plugin `3.6.2`, maven-surefire-plugin `3.5.5`
440+
432441
**Version 1.3.1**
433442

434443
Changes:

pom.xml

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,20 @@
88
<groupId>io.camunda.connector</groupId>
99
<artifactId>pdf-merge-split-connector</artifactId>
1010
<packaging>jar</packaging>
11-
<version>1.3.1</version>
11+
<version>1.4.0</version>
1212

1313
<properties>
1414
<maven.compiler.release>21</maven.compiler.release>
1515
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
1616
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
17-
<version.connectors>8.8.3</version.connectors>
18-
<version.camunda>8.8.3</version.camunda>
19-
<version.pdfbox>3.0.3</version.pdfbox>
17+
<version.connectors>8.9.1</version.connectors>
18+
<version.camunda>8.9.1</version.camunda>
19+
<version.pdfbox>3.0.7</version.pdfbox>
2020
<version.assertj>3.27.7</version.assertj>
21-
<version.junit-jupiter>5.11.3</version.junit-jupiter>
22-
<version.awaitility>4.2.0</version.awaitility>
23-
<version.mockito>5.19.0</version.mockito>
24-
<jacoco.version>0.8.12</jacoco.version>
21+
<version.junit-jupiter>6.0.3</version.junit-jupiter>
22+
<version.awaitility>4.3.0</version.awaitility>
23+
<version.mockito>5.23.0</version.mockito>
24+
<jacoco.version>0.8.14</jacoco.version>
2525
</properties>
2626

2727
<dependencies>
@@ -39,11 +39,12 @@
3939
<version>${version.pdfbox}</version>
4040
</dependency>
4141

42-
<!-- Element template generator annotations (compile-time only) -->
42+
<!-- Element template generator annotations (compile-time only; provided by runtime) -->
4343
<dependency>
4444
<groupId>io.camunda.connector</groupId>
4545
<artifactId>element-template-generator-core</artifactId>
4646
<version>${version.connectors}</version>
47+
<scope>provided</scope>
4748
</dependency>
4849

4950
<!-- test dependencies -->
@@ -118,7 +119,7 @@
118119
<plugin>
119120
<groupId>org.apache.maven.plugins</groupId>
120121
<artifactId>maven-surefire-plugin</artifactId>
121-
<version>3.2.5</version>
122+
<version>3.5.5</version>
122123
</plugin>
123124
<!-- JaCoCo plugin for code coverage -->
124125
<plugin>
@@ -144,7 +145,7 @@
144145
<plugin>
145146
<groupId>org.apache.maven.plugins</groupId>
146147
<artifactId>maven-shade-plugin</artifactId>
147-
<version>3.5.1</version>
148+
<version>3.6.2</version>
148149
<executions>
149150
<execution>
150151
<phase>package</phase>

src/main/java/io/camunda/dto/PdfConnectorRequest.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package io.camunda.dto;
22

33
import io.camunda.connector.api.document.Document;
4-
import io.camunda.connector.generator.dsl.Property;
4+
import io.camunda.connector.generator.java.annotation.FeelMode;
55
import io.camunda.connector.generator.java.annotation.TemplateProperty;
66
import jakarta.validation.constraints.*;
77
import java.util.List;
@@ -19,7 +19,7 @@ public record MergePdfs(
1919
group = "operation",
2020
label = "PDF Documents",
2121
description = "List of PDF documents to merge (in order)",
22-
feel = Property.FeelMode.required,
22+
feel = FeelMode.required,
2323
type = TemplateProperty.PropertyType.String
2424
)
2525
@NotNull @NotEmpty
@@ -43,7 +43,7 @@ public record SplitByPage(
4343
group = "operation",
4444
label = "PDF Document",
4545
description = "The PDF document to split",
46-
feel = Property.FeelMode.required,
46+
feel = FeelMode.required,
4747
type = TemplateProperty.PropertyType.String
4848
)
4949
@NotNull
@@ -76,7 +76,7 @@ public record SplitByRange(
7676
group = "operation",
7777
label = "PDF Document",
7878
description = "The PDF document to split",
79-
feel = Property.FeelMode.required,
79+
feel = FeelMode.required,
8080
type = TemplateProperty.PropertyType.String
8181
)
8282
@NotNull
@@ -87,7 +87,7 @@ public record SplitByRange(
8787
group = "operation",
8888
label = "Page Ranges",
8989
description = "Comma-separated page ranges (e.g., '1-3,5-7,10-15'). Pages are 1-indexed.",
90-
feel = Property.FeelMode.optional
90+
feel = FeelMode.optional
9191
)
9292
@NotBlank
9393
String pageRanges,
@@ -110,7 +110,7 @@ public record SplitByBookmark(
110110
group = "operation",
111111
label = "PDF Document",
112112
description = "The PDF document to split by bookmarks",
113-
feel = Property.FeelMode.required,
113+
feel = FeelMode.required,
114114
type = TemplateProperty.PropertyType.String
115115
)
116116
@NotNull
@@ -143,7 +143,7 @@ public record SplitBySize(
143143
group = "operation",
144144
label = "PDF Document",
145145
description = "The PDF document to split by size",
146-
feel = Property.FeelMode.required,
146+
feel = FeelMode.required,
147147
type = TemplateProperty.PropertyType.String
148148
)
149149
@NotNull

src/test/java/io/camunda/example/classic/integration/PdfConnectorIntegrationTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* Integration test for PDF Merge & Split Connector.
1515
* Tests the connector in a full Camunda runtime environment.
1616
*
17-
* NOTE: This test requires Docker to be running and will pull camunda/camunda:8.8.3 image.
17+
* NOTE: This test requires Docker to be running and will pull camunda/camunda:8.9.1 image.
1818
* Disabled by default for faster builds. Enable when Docker is available.
1919
*/
2020
@Disabled("Requires Docker - enable manually when needed")

src/test/java/io/camunda/example/classic/integration/TestConnectorRuntimeApplication.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@
77
import org.springframework.boot.SpringApplication;
88
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
99
import org.springframework.boot.autoconfigure.SpringBootApplication;
10-
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration;
1110
import org.springframework.context.annotation.Bean;
1211
import org.springframework.context.annotation.Primary;
1312
import org.springframework.test.context.bean.override.mockito.MockitoBean;
1413

15-
@SpringBootApplication(exclude = {ElasticsearchRestClientAutoConfiguration.class})
14+
@SpringBootApplication
1615
@ImportAutoConfiguration({
1716
io.camunda.connector.runtime.InboundConnectorsAutoConfiguration.class,
1817
io.camunda.connector.runtime.OutboundConnectorsAutoConfiguration.class,

0 commit comments

Comments
 (0)