Skip to content

Commit 7d6fe60

Browse files
authored
TIKA-4816 metadata key api
2 parents 9c29b46 + afcec06 commit 7d6fe60

441 files changed

Lines changed: 7632 additions & 3222 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.

.skills/metadata-schema/SKILL.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,19 @@ this covers conventions, regeneration, and the traps.
3535
Under `tika-metadata-schema/src/main/resources/org/apache/tika/metadata/`:
3636

3737
- `metadata-keys.json` — closed set: every `Property` constant + the synthesized `tk:digest:*` cross-product.
38-
- `metadata-open-namespaces.json``PassthroughPrefix` prefixes for runtime-minted names (`html:`, `message:raw-header:`, `mdb-prop:`).
38+
- `metadata-open-namespaces.json``KeyPrefix` prefixes for runtime-minted names (`html:`, `message:raw-header:`, `mdb-prop:`).
3939
- `metadata-key-fields.json` — TIKA-4797 `{class, field, key}` table for field-identity migration.
4040

4141
Committed on purpose: they are the reviewable audit trail of the key space (a rename or a dropped key
4242
shows up as a diff). Don't switch to build-time-only generation — that loses the review signal.
4343

44-
## Regenerate (after adding/changing a Property or PassthroughPrefix)
44+
## Regenerate (after adding/changing a Property or KeyPrefix)
4545

4646
```bash
4747
tika-metadata-schema/regen.sh
4848
```
4949

50-
This does the full sequence in one shot: `-am install` so newly added Property/PassthroughPrefix
50+
This does the full sequence in one shot: `-am install` so newly added Property/KeyPrefix
5151
classes are on the scan classpath, regenerate all three registries via the forked-exec profile, print
5252
a before/after key-count check (catches an incomplete classpath scan), `git diff --stat` the
5353
registries, then run the gate tests. Flags: `--skip-install` (only safe if nothing outside
@@ -91,6 +91,8 @@ Failures with stale `X-TIKA:`/underscore/`SHA256` keys usually mean *regenerate*
9191
## Naming conventions (frozen for 4.0, TIKA-4794)
9292

9393
- All keys are `Property` constants — no bare `String` keys (`metadata-string-keys.json` retired).
94+
Sole exception: `TikaCoreProperties.EMBEDDED_RESOURCE_TYPE_KEY`, an internal building block
95+
that constructs the `Property` name for `EMBEDDED_RESOURCE_TYPE` — not an independent key.
9496
- Tika-coined prefix is `tk:` (`X-TIKA:` is legacy); kebab-case, no underscores.
9597
- External-standard names verbatim, *including* the standard's prefix: `dc:`, `xmp:`, `cp:`, `extended-properties:`.
9698
- HTTP has no namespace → `Content-Type`, `Content-Encoding`, `Location` stay bare (no `http:`).

CHANGES.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,34 @@ Release 4.0.0 - ???
22

33
BREAKING CHANGES
44

5+
* Metadata's reserved tk: (and legacy X-TIKA:) namespace is now a trust
6+
boundary for String-keyed writes. A String write to a reserved key throws
7+
IllegalArgumentException instead of 3.x's silent success or silent drop,
8+
and the public Property factories reject reserved names outright -- a
9+
curated tk: constant can only be built from Tika's own package-private
10+
factories.
11+
Metadata#setAll(Properties) is removed with no replacement; use
12+
putAll(Metadata) or individual set/add calls. PassthroughPrefix is
13+
renamed to KeyPrefix, and document/tool-derived names are written via the
14+
new Metadata#add(KeyPrefix, String, String) route (append-only, with
15+
built-in skip-and-WARN bounds on hostile names) or its Instant overload
16+
for source-typed dates. A batch of key families was also renamed into
17+
namespaced spellings (audio:, ner:, envi:, ogg:streams-, grobid:,
18+
iso19115:, gdal:, geotopic:, mif:, idml:, and GeoParser's Optional_NAME<n>
19+
fields); see the metadata key migration guide (metadata-changes-4x.adoc)
20+
for the full renamed-key table.
21+
Metadata no longer implements CreativeCommons, Geographic, HttpHeaders,
22+
Message, ClimateForcast, TIFF, or TikaMimeKeys: inherited constants like
23+
Metadata.CONTENT_TYPE move to their home interface (HttpHeaders.CONTENT_TYPE,
24+
etc.). The TikaMimeKeys and ClimateForcast interfaces are deleted outright;
25+
ClimateForecast (corrected spelling) replaces the latter. The Property
26+
factories internalClosedChoise/internalOpenChoise/externalClosedChoise/
27+
externalOpenChoise are renamed to ...Choice (typo fix, no forwarders), and
28+
the dead enum constants PropertyType.STRUCTURE and ValueType.{LOCALE,
29+
MIME_TYPE, PROPER_NAME, URL, XPATH} are removed. The remaining metadata
30+
API cleanups (cf: prefix, writelimiter package, smaller removals) are
31+
enumerated in the migration guide, migrating-to-4x.adoc (TIKA-4816).
32+
533
* tika-server: the /translate endpoints have been removed (TIKA-4809).
634

735
* tika-server: /detect/stream and /language/stream and /language/string have

docs/modules/ROOT/pages/advanced/integration-testing/tika-server.adoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,10 +364,10 @@ curl -s -X PUT -H "X-Tika-OCRskipOcr: true" -T testPDF.pdf http://localhost:9998
364364

365365
[source,bash]
366366
----
367-
curl -s -X PUT -H "Content-Disposition: attachment; filename=myfile.pdf" -T testPDF.pdf http://localhost:9998/meta/resourceName
367+
curl -s -X PUT -H "Content-Disposition: attachment; filename=myfile.pdf" -T testPDF.pdf http://localhost:9998/meta/tk:resource-name
368368
----
369369

370-
*Expected:* Returns the filename from Content-Disposition header.
370+
*Expected:* Returns the `tk:resource-name` value taken from the Content-Disposition filename.
371371

372372
== Error Handling
373373

docs/modules/ROOT/pages/developers/index.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ with custom parsers, detectors, and other components.
2222

2323
* xref:developers/serialization.adoc[Serialization and Configuration] - JSON configuration,
2424
@TikaComponent annotation, and creating custom components
25-
* xref:developers/metadata-keys.adoc[Adding a Metadata Key] - the Property/PassthroughPrefix
25+
* xref:developers/metadata-keys.adoc[Adding a Metadata Key] - the Property/KeyPrefix
2626
registry, naming conventions, and regenerating the schema
2727

2828
== Coming Soon

docs/modules/ROOT/pages/developers/metadata-keys.adoc

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,30 @@
1616
= Adding a Metadata Key
1717

1818
Every metadata key Tika can emit is a `Property` constant (or, for runtime-minted names like scraped
19-
HTML `<meta>` tags, a `PassthroughPrefix`) — there are no bare `String` keys. That closed/open key
19+
HTML `<meta>` tags, a `KeyPrefix`) — there are no bare `String` keys naming a population of writable
20+
metadata. (One `String` constant remains as an exception: `TikaCoreProperties.EMBEDDED_RESOURCE_TYPE_KEY`
21+
is an internal building block that constructs the `Property` name for `EMBEDDED_RESOURCE_TYPE` — not an
22+
independent key — and is excluded from the registry's key count for that reason.) That closed/open key
2023
space is tracked in a generated, build-gated registry, so adding a key involves one extra step beyond
2124
writing the Java.
2225

2326
== Add the constant
2427

25-
Add the `Property` to its interface as usual:
28+
A curated `tk:` key can only be minted from inside `org.apache.tika.metadata`, via the
29+
package-private `reserved*` factories -- a public `Property` factory rejects a `tk:`/`X-TIKA:`
30+
name at construction time. Add it to `TikaCoreProperties` (or another class in that package)
31+
as usual:
2632

2733
[source,java]
2834
----
29-
Property MY_NEW_KEY = Property.internalText(TIKA_META_PREFIX + "my-new-key");
35+
Property MY_NEW_KEY = Property.reservedInternalText(TIKA_META_PREFIX + "my-new-key");
36+
----
37+
38+
A parser module coining its own key uses a public factory in its own namespace instead:
39+
40+
[source,java]
41+
----
42+
Property MY_NEW_KEY = Property.internalText("myformat:my-new-key");
3043
----
3144

3245
Naming conventions (frozen for 4.0):
@@ -36,14 +49,34 @@ Naming conventions (frozen for 4.0):
3649
`cp:`, `extended-properties:`).
3750
* HTTP headers stay bare — no `http:` namespace (`Content-Type`, `Content-Encoding`, `Location`).
3851

52+
== Document-derived names: declare a KeyPrefix
53+
54+
When the key *names* come from the document or an external tool (custom document properties,
55+
format-specific attribute names, NER labels), they can't be constants. Declare a `KeyPrefix`
56+
once, as a `static final` field — never per-parse, never from document text — and write through
57+
`Metadata#add(KeyPrefix, String, String)`:
58+
59+
[source,java]
60+
----
61+
static final KeyPrefix MYFORMAT = KeyPrefix.file("myformat:", "myformat's own header fields");
62+
...
63+
metadata.add(MYFORMAT, nameFromDocument, value); // String value
64+
metadata.add(MYFORMAT, nameFromDocument, date.toInstant()); // source-typed date
65+
----
66+
67+
The route is append-only (repeated names accumulate, losslessly transcribing the source) and
68+
never throws on hostile input: blank, over-length, or flooding names are skipped with a WARN.
69+
Use `KeyPrefix.file(...)` for names read out of the document, `KeyPrefix.tool(...)` for names
70+
coined by an external tool or service.
71+
3972
== Regenerate the registry
4073

4174
The registry — three JSON files under `tika-metadata-schema/src/main/resources/`, listing every
4275
declared key, every open-namespace prefix, and a field-provenance table — is generated from the live
43-
`Property`/`PassthroughPrefix` declarations, never hand-edited. A committed copy is the reviewable
76+
`Property`/`KeyPrefix` declarations, never hand-edited. A committed copy is the reviewable
4477
audit trail (a rename or dropped key shows up as a diff), and CI fails if it's stale.
4578

46-
Run this after adding, renaming, or removing a `Property` or `PassthroughPrefix`:
79+
Run this after adding, renaming, or removing a `Property` or `KeyPrefix`:
4780

4881
[source,bash]
4982
----

docs/modules/ROOT/pages/developers/serialization.adoc

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -329,19 +329,21 @@ framework keys.
329329

330330
== Creating a Custom Component
331331

332-
Complete example of a custom metadata filter:
332+
Complete example of a custom metadata filter. `fieldName` is config-supplied, so it could
333+
legitimately name a reserved `tk:` key; use `setTrusted` rather than `set` -- filters run
334+
inside the trusted bracket of `MetadataFilter`, the sanctioned route for a write keyed by
335+
a name that isn't a compile-time constant:
333336

334337
[source,java]
335338
----
336339
package com.example.tika;
337340
338341
import org.apache.tika.annotation.TikaComponent;
339-
import org.apache.tika.exception.TikaException;
340342
import org.apache.tika.metadata.Metadata;
341-
import org.apache.tika.metadata.filter.MetadataFilter;
343+
import org.apache.tika.metadata.filter.MetadataFilterBase;
342344
343345
@TikaComponent
344-
public class UpperCaseFilter implements MetadataFilter {
346+
public class UpperCaseFilter extends MetadataFilterBase {
345347
346348
private String fieldName = "title";
347349
@@ -354,18 +356,19 @@ public class UpperCaseFilter implements MetadataFilter {
354356
}
355357
356358
@Override
357-
public void filter(Metadata metadata) throws TikaException {
359+
protected void filter(Metadata metadata) {
358360
String value = metadata.get(fieldName);
359361
if (value != null) {
360-
metadata.set(fieldName, value.toUpperCase());
362+
metadata.setTrusted(fieldName, value.toUpperCase());
361363
}
362364
}
363365
}
364366
----
365367

366368
Configure in JSON. Metadata filters are loaded via `parse-context` (they
367-
implement the `MetadataFilter` interface, which is a `ParseContext`-keyed
368-
component):
369+
extend the `MetadataFilter` abstract class -- here via `MetadataFilterBase`,
370+
whose per-`Metadata` hook suits context-free filters -- which is a
371+
`ParseContext`-keyed component):
369372

370373
[source,json]
371374
----

0 commit comments

Comments
 (0)