Skip to content

Commit 9b9e53a

Browse files
authored
1 parent 8ed79c7 commit 9b9e53a

173 files changed

Lines changed: 1620 additions & 1141 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/dev.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ provide the suggested commit message for the user to execute.
4747
Run **without** `-Pfast` before final commit to catch formatting
4848
and style issues.
4949

50+
**`-Pfast` skips test *execution*** (by design): a green `-Pfast`
51+
build — including `-Pfast test` — has run zero tests, and stale
52+
`target/surefire-reports/*` will look current. Verify with a plain
53+
(non-`-Pfast`) `test` run.
54+
5055
- **Forked JVM tests** — Integration tests in `tika-pipes` fork new
5156
JVMs that load classes from the local Maven repo, not from
5257
`target/classes`. You must `./mvnw clean install -Pfast` the
@@ -93,6 +98,12 @@ provide the suggested commit message for the user to execute.
9398
legitimate exception: a path that is the data under test (e.g. an expected
9499
metadata value extracted from a test document) — leave those untouched.
95100

101+
## Metadata Keys & Schema Registry
102+
103+
Adding/renaming a metadata key touches the committed, build-gated registry in
104+
`tika-metadata-schema` — regeneration has real traps. See
105+
`.skills/metadata-schema.md`.
106+
96107
## Testing an End-to-End Change
97108

98109
When a change affects parsing output (e.g., new parser behavior,

.skills/metadata-schema.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Metadata Key Registry & Schema Skill
2+
3+
Working with `tika-metadata-schema` — the committed, build-gated registry of Tika's metadata keys.
4+
File structure and the CLOSED/OPEN/TEMPLATE/UNKNOWN classification are in that module's `README.md`;
5+
this covers conventions, regeneration, and the traps.
6+
7+
## The three registries
8+
9+
Under `tika-metadata-schema/src/main/resources/org/apache/tika/metadata/`:
10+
11+
- `metadata-keys.json` — closed set: every `Property` constant + the synthesized `tk:digest:*` cross-product.
12+
- `metadata-open-namespaces.json``PassthroughPrefix` prefixes for runtime-minted names (`html:`, `message:raw-header:`, `mdb-prop:`).
13+
- `metadata-key-fields.json` — TIKA-4797 `{class, field, key}` table for field-identity migration.
14+
15+
Committed on purpose: they are the reviewable audit trail of the key space (a rename or a dropped key
16+
shows up as a diff). Don't switch to build-time-only generation — that loses the review signal.
17+
18+
## Regenerate (after adding/changing a Property or PassthroughPrefix)
19+
20+
```bash
21+
# if parser Property classes changed, install them first so the scan sees them:
22+
./mvnw -Pfast -DskipTests -pl tika-metadata-schema -am install -Dmaven.repo.local=$(pwd)/.local_m2_repo
23+
# regenerate all three:
24+
./mvnw -pl tika-metadata-schema -Pregen-metadata-schema process-classes -Dmaven.repo.local=$(pwd)/.local_m2_repo
25+
```
26+
27+
Then `git diff` the JSONs, run the gate tests, commit.
28+
29+
**Trap — never use `exec:java`.** `SchemaGenerator` scans `java.class.path`, force-loads Property
30+
classes, and swallows load failures. `exec:java` runs in-process on Maven's classpath → finds zero
31+
keys → emits a near-empty registry that exits 0 and *passes* `MetadataNoUnderscoreTest`. The
32+
`-Pregen-metadata-schema` profile uses the forking `exec` goal (`<classpath/>`) for a real child-JVM
33+
classpath. A hand-rolled `java -cp` also works, but the full repo classpath (~4000 jars) overflows
34+
the 128 KB arg limit — use the profile.
35+
36+
**Trap — incomplete classpath drops keys silently.** Always validate: `git diff` should show only the
37+
intended change (a big key-count drop = classes failed to load), and `MetadataSchemaTest` regenerates
38+
under the full test classpath and asserts a byte-match — the definitive completeness check.
39+
40+
## Gate tests (run WITHOUT `-Pfast`, which skips execution)
41+
42+
```bash
43+
./mvnw -pl tika-metadata-schema test -Dmaven.repo.local=$(pwd)/.local_m2_repo
44+
```
45+
46+
- `MetadataSchemaTest` / `MetadataFieldTableTest` — regenerate in-memory, assert committed files match.
47+
- `MetadataNoUnderscoreTest` — no Tika-coined key/prefix may contain `_`. Scans the JSON, so it
48+
reflects the *registry*, not live code — regenerate before trusting it.
49+
- `MetadataKeyValidatorTest` — registry-driven CLOSED/OPEN/TEMPLATE/UNKNOWN classifier.
50+
- `MetadataCoverageTest` — fails if a scanned module's keys are neither in scope nor listed out-of-scope.
51+
52+
Failures with stale `X-TIKA:`/underscore/`SHA256` keys usually mean *regenerate*, not edit code.
53+
54+
## Naming conventions (frozen for 4.0, TIKA-4794)
55+
56+
- All keys are `Property` constants — no bare `String` keys (`metadata-string-keys.json` retired).
57+
- Tika-coined prefix is `tk:` (`X-TIKA:` is legacy); kebab-case, no underscores.
58+
- External-standard names verbatim, *including* the standard's prefix: `dc:`, `xmp:`, `cp:`, `extended-properties:`.
59+
- HTTP has no namespace → `Content-Type`, `Content-Encoding`, `Location` stay bare (no `http:`).
60+
- Tika-coined message keys *do* get a namespace: `message:`, `multipart:`.
61+
- HttpHeaders keys are SIMPLE — Content-Type is not a bag; parsers use `set()`, not `add()`.
62+
- Digest keys use the JCA name via `DigestDef.getJavaName()`: `tk:digest:SHA-256`, `SHA3-256` (not
63+
`SHA256`/`SHA3_256`). Config *input* still takes the enum name (`"SHA256"`); only the output key changes.
64+
65+
## After a rename: sweep for stale key literals
66+
67+
The compiler won't catch `metadata.get("Message-From")`. Grep the whole repo for old strings and
68+
prefer replacing them with the constant, so the next rename fails to compile instead of at test time:
69+
70+
```bash
71+
grep -rn '"Message-' --include=*.java . | grep -v /target/
72+
grep -rn 'tk:digest:SHA[0-9]' --include=*.java . | grep -v /target/
73+
```
74+
75+
## XML comments can't contain `--`
76+
77+
A double hyphen in an XML comment makes the POM non-parseable and breaks the module build. Reword.

.skills/tika-eval-encoding-regression.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,10 @@ To see what one chain change actually did, Compare the new run against the
158158
(IBM850 / x-MacRoman → windows-1252) vs the prior run and nothing else. A
159159
bigger-than-expected diff means the change fired more broadly than intended.
160160

161-
## Per-file detector attribution (`X-TIKA:encodingDetectionTrace`)
161+
## Per-file detector attribution (`tk:encoding-detection-trace`)
162162

163163
Every JSON extract from a chain with multiple detectors carries
164-
`X-TIKA:encodingDetectionTrace` in metadata. It's a per-detector emission
164+
`tk:encoding-detection-trace` in metadata. It's a per-detector emission
165165
log with the META detector's arbitration tag at the end:
166166

167167
```
@@ -174,8 +174,8 @@ meta detector chose. If the trace shows ONLY Mojibuster firing with a CJK
174174
pick, the bug is in Mojibuster's emission (pool too narrow), not in
175175
JunkFilter's arbitration.
176176

177-
`X-TIKA:encodingDetector` is the simple-name credit string;
178-
`X-TIKA:detectedEncoding` is the final answer (also in `Content-Encoding`).
177+
`tk:encoding-detector` is the simple-name credit string;
178+
`tk:detected-encoding` is the final answer (also in `Content-Encoding`).
179179

180180
## Reproducing a single-file detection without a full chain
181181

.skills/tika-eval-h2-query.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ WHERE DETECTED_ENCODING REGEXP 'big5|gb|euc|shift|jis|2022|949'
135135
```
136136

137137
(`DETECTED_ENCODING` is on `ENCODINGS_A`/`ENCODINGS_B` — join to `PROFILES`/`CONTENTS`
138-
on `ID` — populated from `X-TIKA:detectedEncoding`.)
138+
on `ID` — populated from `tk:detected-encoding`.)
139139

140140
## Tip
141141

docs/modules/ROOT/pages/advanced/embedded-documents.adoc

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,12 @@ documents. They answer: "Which document contained this one?" All fields are defi
4949

5050
=== Nesting Identifiers
5151

52-
`TikaCoreProperties.EMBEDDED_ID` (`X-TIKA:embedded_id`)::
52+
`TikaCoreProperties.EMBEDDED_ID` (`tk:embedded-id`)::
5353
A 1-indexed integer assigned by Tika to each embedded document during parsing. IDs are
5454
assigned in the order documents are encountered by the `RecursiveParserWrapper`. This ID
5555
uniquely identifies each embedded document within a single parse operation.
5656

57-
`TikaCoreProperties.EMBEDDED_ID_PATH` (`X-TIKA:embedded_id_path`)::
57+
`TikaCoreProperties.EMBEDDED_ID_PATH` (`tk:embedded-id-path`)::
5858
A path showing the containment hierarchy using `EMBEDDED_ID` values. For example, `/1/3`
5959
indicates that the file with `EMBEDDED_ID=3` was contained within the file with
6060
`EMBEDDED_ID=1`. This is the most reliable field for tracking containment relationships.
@@ -64,22 +64,22 @@ folder structures or original paths within the containers themselves.
6464

6565
=== Synthetic Paths
6666

67-
`TikaCoreProperties.EMBEDDED_RESOURCE_PATH` (`X-TIKA:embedded_resource_path`)::
67+
`TikaCoreProperties.EMBEDDED_RESOURCE_PATH` (`tk:embedded-resource-path`)::
6868
A synthetic path built by concatenating file names (from `RESOURCE_NAME_KEY`) at each
6969
nesting level. This provides a human-readable path through the containment hierarchy.
7070
+
7171
WARNING: Do not use this field for creating directory structures to write out attachments.
7272
There may be path collisions, illegal characters, or zip slip vulnerabilities. Use
7373
`EMBEDDED_ID_PATH` for reliable containment tracking.
7474

75-
`TikaCoreProperties.FINAL_EMBEDDED_RESOURCE_PATH` (`X-TIKA:final_embedded_resource_path`)::
75+
`TikaCoreProperties.FINAL_EMBEDDED_RESOURCE_PATH` (`tk:final-embedded-resource-path`)::
7676
Similar to `EMBEDDED_RESOURCE_PATH`, but calculated at the end of the full parse. For some
7777
parsers, an embedded file's name isn't known until after its child files have been parsed.
7878
This field may have fewer "unknown" file names than `EMBEDDED_RESOURCE_PATH`.
7979

8080
=== Resource Naming
8181

82-
`TikaCoreProperties.RESOURCE_NAME_KEY` (`X-TIKA:resourceName`)::
82+
`TikaCoreProperties.RESOURCE_NAME_KEY` (`tk:resource-name`)::
8383
The file name (not path) of the resource. Tika makes a best effort to determine a meaningful
8484
name from the container's metadata. When unavailable, Tika falls back to synthetic names
8585
such as `embedded-1.jpeg`.
@@ -95,7 +95,7 @@ the nesting structure. All fields below are defined in `TikaCoreProperties`.
9595

9696
=== Internal Paths
9797

98-
`TikaCoreProperties.INTERNAL_PATH` (`X-TIKA:internalPath`)::
98+
`TikaCoreProperties.INTERNAL_PATH` (`tk:internal-path`)::
9999
The path (including file name) as literally stored within the container. This is what the
100100
container knows about where the file lives in its internal structure:
101101
+
@@ -108,7 +108,7 @@ This differs fundamentally from `EMBEDDED_RESOURCE_PATH`:
108108
* `INTERNAL_PATH` is what the _container stores_ about the file's location within itself
109109
* `EMBEDDED_RESOURCE_PATH` is what _Tika synthesizes_ from the nesting structure
110110

111-
`TikaCoreProperties.ORIGINAL_RESOURCE_NAME` (`X-TIKA:origResourceName`)::
111+
`TikaCoreProperties.ORIGINAL_RESOURCE_NAME` (`tk:orig-resource-name`)::
112112
For some file formats, the file path where the document was last saved on the creator's
113113
system. For example, an `.xlsx` file named `budget.xlsx` may include a metadata property
114114
storing where it was last saved: `C:\Users\Alice\budget.xlsx`. This is not specific to
@@ -118,11 +118,11 @@ embedded files - it's a property that certain file formats preserve about themse
118118

119119
Microsoft Office formats use additional identifiers for embedded objects.
120120

121-
`TikaCoreProperties.EMBEDDED_RELATIONSHIP_ID` (`X-TIKA:embeddedRelationshipId`)::
121+
`TikaCoreProperties.EMBEDDED_RELATIONSHIP_ID` (`tk:embedded-relationship-id`)::
122122
A Microsoft-specific identifier used internally to reference embedded objects within
123123
Office documents. This is the relationship ID from the Office Open XML or OLE structure.
124124

125-
`Office.EMBEDDED_STORAGE_CLASS_ID` (`msoffice:embeddedStorageClassId`)::
125+
`Office.EMBEDDED_STORAGE_CLASS_ID` (`msoffice:embedded-storage-class-id`)::
126126
A UUID that identifies the class of embedded object in Microsoft formats. While not
127127
exactly a MIME type, it provides similar information about what type of object is
128128
embedded. Defined in the `Office` metadata class.
@@ -134,39 +134,39 @@ embedded. Defined in the `Office` metadata class.
134134
|Property |Metadata Key |Source
135135

136136
|`EMBEDDED_ID`
137-
|`X-TIKA:embedded_id`
137+
|`tk:embedded-id`
138138
|Containment
139139

140140
|`EMBEDDED_ID_PATH`
141-
|`X-TIKA:embedded_id_path`
141+
|`tk:embedded-id-path`
142142
|Containment
143143

144144
|`EMBEDDED_RESOURCE_PATH`
145-
|`X-TIKA:embedded_resource_path`
145+
|`tk:embedded-resource-path`
146146
|Containment
147147

148148
|`FINAL_EMBEDDED_RESOURCE_PATH`
149-
|`X-TIKA:final_embedded_resource_path`
149+
|`tk:final-embedded-resource-path`
150150
|Containment
151151

152152
|`RESOURCE_NAME_KEY`
153-
|`X-TIKA:resourceName`
153+
|`tk:resource-name`
154154
|Containment
155155

156156
|`INTERNAL_PATH`
157-
|`X-TIKA:internalPath`
157+
|`tk:internal-path`
158158
|Container
159159

160160
|`ORIGINAL_RESOURCE_NAME`
161-
|`X-TIKA:origResourceName`
161+
|`tk:orig-resource-name`
162162
|Container
163163

164164
|`EMBEDDED_RELATIONSHIP_ID`
165-
|`X-TIKA:embeddedRelationshipId`
165+
|`tk:embedded-relationship-id`
166166
|Container (MS)
167167

168168
|`Office.EMBEDDED_STORAGE_CLASS_ID`
169-
|`msoffice:embeddedStorageClassId`
169+
|`msoffice:embedded-storage-class-id`
170170
|Container (MS)
171171
|===
172172

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ java -jar tika-app.jar -J -t /tmp/tika-app-test/batch-input /tmp/tika-app-test/b
147147
ls /tmp/tika-app-test/batch-output2
148148
----
149149

150-
*Expected:* Creates .json files with text content (X-TIKA:content_handler should be ToTextContentHandler).
150+
*Expected:* Creates .json files with text content (tk:content-handler should be ToTextContentHandler).
151151

152152
=== Test 11: Version Check
153153

@@ -183,7 +183,7 @@ java -jar tika-app.jar --language testPDF.pdf
183183
java -jar tika-app.jar --digest=md5 --json testPDF.pdf
184184
----
185185

186-
*Expected:* JSON output includes `X-TIKA:digest:MD5` field.
186+
*Expected:* JSON output includes `tk:digest:MD5` field.
187187

188188
=== Test 15: URL Input
189189

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ match.
106106
----
107107

108108
The `parse-context > commons-digester-factory` block makes every
109-
extracted record carry an `X-TIKA:digest:SHA256` metadata field —
109+
extracted record carry an `tk:digest:SHA256` metadata field —
110110
required by `tika-eval` for embedded-document alignment across runs.
111111

112112
=== Config B (variant under test)
@@ -233,7 +233,7 @@ Open the `.xlsx` files directly, or use the `regression` skill
233233
== Tips
234234

235235
* *Keep the digester identical between A and B.* tika-eval uses the
236-
`X-TIKA:digest:SHA256` field on embedded documents to align records
236+
`tk:digest:SHA256` field on embedded documents to align records
237237
across the two extracts. If A digests and B doesn't (or different
238238
algorithms), the embedded-doc alignment falls back to filename and
239239
produces false-positive diffs.
@@ -310,7 +310,7 @@ Compare step finishes in 2-5 minutes depending on extract size.
310310
. Open `reports/mimes/mime_diffs_A_to_B.xlsx` to see the headline
311311
MIME-detection differences; the encoding-detector chain change
312312
surfaces as charset diffs in `mimes/mime_diffs_A_to_B_details.xlsx`
313-
(`X-TIKA:detected_encoding`).
313+
(`tk:detected-encoding`).
314314

315315
A regression analysis writeup goes in
316316
`~/Desktop/claude-todo/<reports-dir-name>-analysis.md` per the

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ curl -s -X PUT -T testPDF.pdf http://localhost:9998/tika/xml
100100
curl -s -X PUT -T testPDF.pdf http://localhost:9998/tika/json
101101
----
102102

103-
*Expected:* JSON object with metadata and X-TIKA:content field.
103+
*Expected:* JSON object with metadata and tk:content field.
104104

105105
=== Test 7: PUT /meta
106106

docs/modules/ROOT/pages/advanced/setting-limits.adoc

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ siblings at the current level continue to be processed.
103103
|`throwOnMaxDepth`
104104
|false
105105
|Whether to throw an `EmbeddedLimitReachedException` when maxDepth is reached.
106-
If false, processing continues and `X-TIKA:maxDepthReached=true` is set in metadata.
106+
If false, processing continues and `tk:exception:embedded-depth-limit-reached=true` is set in metadata.
107107

108108
|`maxCount`
109109
|-1 (unlimited)
@@ -113,7 +113,7 @@ stops immediately.
113113
|`throwOnMaxCount`
114114
|false
115115
|Whether to throw an `EmbeddedLimitReachedException` when maxCount is reached.
116-
If false, processing continues and `X-TIKA:maxEmbeddedCountReached=true` is set.
116+
If false, processing continues and `tk:exception:embedded-resource-limit-reached=true` is set.
117117
|===
118118

119119
=== maxDepth Behavior
@@ -380,7 +380,7 @@ Metadata metadata = Metadata.newInstance(context);
380380
|`maxTotalBytes`
381381
|10 MB
382382
|Maximum total estimated size of all metadata in UTF-16 bytes. When exceeded,
383-
additional metadata is silently dropped and `X-TIKA:WARN:truncated_metadata` is set.
383+
additional metadata is silently dropped and `tk:warn:truncated-metadata` is set.
384384

385385
|`maxFieldSize`
386386
|100 KB
@@ -423,7 +423,7 @@ Use this to extract only the metadata you need.
423423
"maxKeySize": 1024,
424424
"maxValuesPerField": 100,
425425
"includeFields": ["dc:title", "dc:creator", "dc:subject"],
426-
"excludeFields": ["pdf:unmappedUnicodeCharsPerPage"]
426+
"excludeFields": ["pdf:unmapped-unicode-chars-per-page"]
427427
}
428428
}
429429
}
@@ -436,15 +436,15 @@ of `includeFields` or size limits:
436436

437437
* `Content-Type` - Required for parser selection
438438
* `Content-Length`, `Content-Encoding`, `Content-Disposition`
439-
* `X-TIKA:content` - The extracted text content
440-
* `X-TIKA:Parsed-By` - Parser chain information
441-
* `X-TIKA:WARN:*` - Warning metadata
439+
* `tk:content` - The extracted text content
440+
* `tk:parsed-by` - Parser chain information
441+
* `tk:warn:*` - Warning metadata
442442
* Access permission fields
443443

444444
=== Detecting Truncation
445445

446446
When metadata is truncated due to limits, Tika sets the metadata field
447-
`X-TIKA:WARN:truncated_metadata` to `true`. You can check for this in your code:
447+
`tk:warn:truncated-metadata` to `true`. You can check for this in your code:
448448

449449
[source,java]
450450
----
@@ -458,7 +458,7 @@ if ("true".equals(metadata.get(TikaCoreProperties.TRUNCATED_METADATA))) {
458458

459459
1. **Always set limits** when processing untrusted content
460460
2. **Use `includeFields`** to capture only the metadata you need
461-
3. **Monitor for truncation** by checking `X-TIKA:WARN:truncated_metadata`
461+
3. **Monitor for truncation** by checking `tk:warn:truncated-metadata`
462462
4. **Combine with process isolation** - limits protect against memory issues,
463463
but xref:advanced/robustness.adoc[process isolation] protects against crashes
464464
5. **Test with adversarial files** - use Tika's `MockParser` to simulate extreme cases

docs/modules/ROOT/pages/advanced/zip-detection.adoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,10 @@ parser.parse(inputStream, handler, metadata, context); // May fail on truncated
9090

9191
The detector also sets additional metadata hints for parsers:
9292

93-
`zip:detectorZipFileOpened`:: `true` if the detector successfully opened the ZIP as a `ZipFile`.
93+
`zip:detector-zip-file-opened`:: `true` if the detector successfully opened the ZIP as a `ZipFile`.
9494
The `ZipFile` object is available via `TikaInputStream.getOpenContainer()` for reuse by parsers.
9595

96-
`zip:detectorDataDescriptorRequired`:: `true` if streaming detection required
96+
`zip:detector-data-descriptor-required`:: `true` if streaming detection required
9797
DATA_DESCRIPTOR support. This hint helps parsers choose the correct streaming mode.
9898

9999
== Related Topics

0 commit comments

Comments
 (0)