Skip to content

Commit 1031100

Browse files
committed
TIKA-4840: host-provided Jackson for pipes plugins; one strict PluginJson mapper for plugin configs
1 parent 26381aa commit 1031100

46 files changed

Lines changed: 267 additions & 83 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGES.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
Release 4.1.0 - unreleased
22

3+
* Pipes plugins no longer bundle their own Jackson: jackson-core, -databind
4+
and -annotations are provided by the host (tika-serialization) and the
5+
plugins parent pom now bans bundling them, so a mapper can cross the
6+
plugin boundary without a second copy of the Jackson classes (seven plugin
7+
zips shipped one). Plugin configuration JSON is parsed by one shared
8+
mapper, PluginJson (tika-plugins-core), which rejects unknown keys,
9+
numbers for enums and duplicate keys, and accepts
10+
// and /* */ comments; the 33 per-plugin *Config classes use it instead of
11+
their own bare ObjectMapper (TIKA-4840).
12+
313
* The Kafka pipes iterator no longer stops at the first empty poll. A newly
414
subscribed consumer spends its first poll(s) joining the group and returns
515
empty even when the topic has a backlog, so the iterator could enqueue zero

docs/modules/ROOT/pages/pipes/plugins/writing-a-plugin.adoc

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,12 @@ public class MyFetcher extends AbstractTikaExtension implements Fetcher {
124124
}
125125
}
126126
----
127-
<1> Config arrives as a **JSON string**, and the plugin parses it with its own Jackson. Nothing
128-
richer crosses the boundary — see <<classloading>>.
127+
<1> Config arrives as a **JSON string**; nothing richer crosses the boundary — see
128+
<<classloading>>. The in-tree plugins parse it with `org.apache.tika.plugins.PluginJson`
129+
(`PluginJson.read(json, MyFetcherConfig.class)`), the host's strict mapper: comments allowed,
130+
unknown keys, duplicate keys and numbers-for-enums rejected. Using it is optional — the
131+
boundary is the `Fetcher`, not how it is configured — but see the Jackson rule under
132+
<<classloading>> before deciding.
129133

130134
IMPORTANT: `Fetcher` implementations must be thread-safe. One instance serves every concurrent
131135
request against that fetcher id.
@@ -275,6 +279,20 @@ of a plugin that loads cleanly and then behaves as if it were not there — or o
275279
The same reasoning covers logging: leave `org.slf4j` and `org.apache.logging.log4j` to the host so
276280
plugin logs land in the host's configuration.
277281

282+
Jackson is the one library with a choice, and it is either/or:
283+
284+
* **Host Jackson (the default, what the in-tree plugins do):** `jackson-core`, `jackson-databind`
285+
and `jackson-annotations` `provided`, absent from `lib/`. Every Tika host carries them (via
286+
`tika-serialization`), and you may then use `PluginJson` and other host Jackson types.
287+
* **Your own Jackson:** only if a library you depend on needs a version the host does not ship.
288+
Bundle it (`compile` scope), and then never touch a host Jackson object — not `PluginJson`, not
289+
`JsonMetadataList`, nothing returning an `ObjectMapper` or `JsonNode`. Your `ObjectMapper` and
290+
the host's are different `Class` objects, and the first assignment between them fails with a
291+
`LinkageError` or `ClassCastException`. Parse `ExtensionConfig.json()` with your own mapper.
292+
293+
Never both. The in-tree plugins' parent pom enforces the first choice; a third-party plugin
294+
choosing the second must not inherit that rule.
295+
278296
Everything else — your own transitive libraries — belongs in `lib/`.
279297

280298
== Installing and configuring
@@ -367,6 +385,8 @@ java -Dtika.plugin.dev.mode=true ...
367385
* `tika-core`, `tika-pipes-api`, `tika-plugins-core`, `tika-serialization`,
368386
`tika-pipes-core`, `tika-pipes-iterator-commons`, `pf4j` and the logging
369387
implementations `provided`, and absent from `lib/`.
388+
* Jackson either `provided` (and then `PluginJson` is yours to use) or bundled (and then no host
389+
Jackson type is) — never a mix.
370390
* The zip — not an unpacked directory — dropped in a `plugin-roots` directory.
371391
* `getName()` unique against every other loaded plugin.
372392

tika-pipes/tika-pipes-plugins/pom.xml

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,23 @@
8888
<version>${project.version}</version>
8989
<scope>provided</scope>
9090
</dependency>
91+
<!-- host-supplied via tika-serialization: a plugin that bundles its own Jackson gets a
92+
second ObjectMapper class, and any mapper crossing the plugin boundary fails to link -->
93+
<dependency>
94+
<groupId>com.fasterxml.jackson.core</groupId>
95+
<artifactId>jackson-core</artifactId>
96+
<scope>provided</scope>
97+
</dependency>
98+
<dependency>
99+
<groupId>com.fasterxml.jackson.core</groupId>
100+
<artifactId>jackson-databind</artifactId>
101+
<scope>provided</scope>
102+
</dependency>
103+
<dependency>
104+
<groupId>com.fasterxml.jackson.core</groupId>
105+
<artifactId>jackson-annotations</artifactId>
106+
<scope>provided</scope>
107+
</dependency>
91108
<!-- logging is host-supplied -->
92109
<dependency>
93110
<groupId>org.slf4j</groupId>
@@ -169,6 +186,7 @@
169186
<excludes>
170187
<exclude>org.slf4j</exclude>
171188
<exclude>org.apache.logging.log4j</exclude>
189+
<exclude>com.fasterxml.jackson.core</exclude>
172190
</excludes>
173191
<!-- the artifacts this module declares provided; anything else in these
174192
groups needs a provided declaration here before it may be depended on -->
@@ -178,8 +196,11 @@
178196
<include>org.apache.logging.log4j:log4j-api</include>
179197
<include>org.apache.logging.log4j:log4j-core</include>
180198
<include>org.apache.logging.log4j:log4j-slf4j2-impl</include>
199+
<include>com.fasterxml.jackson.core:jackson-core</include>
200+
<include>com.fasterxml.jackson.core:jackson-databind</include>
201+
<include>com.fasterxml.jackson.core:jackson-annotations</include>
181202
</includes>
182-
<message>logging is host-supplied: add a provided-scope declaration in tika-pipes-plugins/pom.xml (and to this rule's include list) rather than bundling this artifact in the plugin zip</message>
203+
<message>logging and Jackson are host-supplied: add a provided-scope declaration in tika-pipes-plugins/pom.xml (and to this rule's include list) rather than bundling this artifact in the plugin zip. A plugin whose SDK genuinely needs its own Jackson may bundle it (compile scope, skip this execution in its pom) but must then use no host Jackson type -- not PluginJson -- since the two ObjectMapper classes cannot be assigned to each other.</message>
183204
</bannedDependencies>
184205
</rules>
185206
<fail>true</fail>

tika-pipes/tika-pipes-plugins/tika-pipes-atlassian-jwt/pom.xml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,6 @@
5353
<groupId>com.google.guava</groupId>
5454
<artifactId>guava</artifactId>
5555
</dependency>
56-
<dependency>
57-
<groupId>com.fasterxml.jackson.core</groupId>
58-
<artifactId>jackson-databind</artifactId>
59-
</dependency>
60-
<dependency>
61-
<groupId>com.fasterxml.jackson.core</groupId>
62-
<artifactId>jackson-annotations</artifactId>
63-
</dependency>
6456
<dependency>
6557
<groupId>org.mockito</groupId>
6658
<artifactId>mockito-core</artifactId>

tika-pipes/tika-pipes-plugins/tika-pipes-atlassian-jwt/src/main/java/org/apache/tika/pipes/fetcher/atlassianjwt/config/AtlassianJwtFetcherConfig.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@
2525
import com.fasterxml.jackson.databind.ObjectMapper;
2626

2727
import org.apache.tika.exception.TikaConfigException;
28+
import org.apache.tika.plugins.PluginJson;
2829

2930
public class AtlassianJwtFetcherConfig {
3031

31-
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
32+
private static final ObjectMapper OBJECT_MAPPER = PluginJson.mapper();
3233

3334
public static AtlassianJwtFetcherConfig load(final String json)
3435
throws TikaConfigException {

tika-pipes/tika-pipes-plugins/tika-pipes-az-blob/pom.xml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,6 @@
4646
<groupId>com.azure</groupId>
4747
<artifactId>azure-storage-blob</artifactId>
4848
</dependency>
49-
<dependency>
50-
<groupId>com.fasterxml.jackson.core</groupId>
51-
<artifactId>jackson-databind</artifactId>
52-
</dependency>
5349
</dependencies>
5450

5551
<build>

tika-pipes/tika-pipes-plugins/tika-pipes-az-blob/src/main/java/org/apache/tika/pipes/emitter/azblob/AZBlobEmitterConfig.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.fasterxml.jackson.databind.ObjectMapper;
2222

2323
import org.apache.tika.exception.TikaConfigException;
24+
import org.apache.tika.plugins.PluginJson;
2425

2526
public record AZBlobEmitterConfig(
2627
String sasToken,
@@ -31,7 +32,7 @@ public record AZBlobEmitterConfig(
3132
@JsonProperty(defaultValue = "false") boolean overwriteExisting
3233
) {
3334

34-
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
35+
private static final ObjectMapper OBJECT_MAPPER = PluginJson.mapper();
3536

3637
public static AZBlobEmitterConfig load(final String json)
3738
throws TikaConfigException {

tika-pipes/tika-pipes-plugins/tika-pipes-az-blob/src/main/java/org/apache/tika/pipes/fetcher/azblob/config/AZBlobFetcherConfig.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@
2020
import com.fasterxml.jackson.databind.ObjectMapper;
2121

2222
import org.apache.tika.exception.TikaConfigException;
23+
import org.apache.tika.plugins.PluginJson;
2324

2425
public class AZBlobFetcherConfig {
2526

26-
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
27+
private static final ObjectMapper OBJECT_MAPPER = PluginJson.mapper();
2728

2829
public static AZBlobFetcherConfig load(final String json)
2930
throws TikaConfigException {

tika-pipes/tika-pipes-plugins/tika-pipes-az-blob/src/main/java/org/apache/tika/pipes/iterator/azblob/AZBlobPipesIteratorConfig.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@
2323

2424
import org.apache.tika.exception.TikaConfigException;
2525
import org.apache.tika.pipes.pipesiterator.PipesIteratorConfig;
26+
import org.apache.tika.plugins.PluginJson;
2627

2728
public class AZBlobPipesIteratorConfig extends PipesIteratorConfig {
2829

29-
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
30+
private static final ObjectMapper OBJECT_MAPPER = PluginJson.mapper();
3031

3132
public static AZBlobPipesIteratorConfig load(final String json)
3233
throws TikaConfigException {

tika-pipes/tika-pipes-plugins/tika-pipes-csv/src/main/java/org/apache/tika/pipes/iterator/csv/CSVPipesIteratorConfig.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@
2424

2525
import org.apache.tika.exception.TikaConfigException;
2626
import org.apache.tika.pipes.pipesiterator.PipesIteratorConfig;
27+
import org.apache.tika.plugins.PluginJson;
2728

2829
public class CSVPipesIteratorConfig extends PipesIteratorConfig {
2930

30-
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
31+
private static final ObjectMapper OBJECT_MAPPER = PluginJson.mapper();
3132

3233
public static CSVPipesIteratorConfig load(final String json)
3334
throws TikaConfigException {

0 commit comments

Comments
 (0)