Skip to content

Commit cbf7174

Browse files
authored
Merge pull request #2992 from apache/TIKA-4807
2 parents 075174a + 2dc9f26 commit cbf7174

8 files changed

Lines changed: 188 additions & 11 deletions

File tree

.skills/metadata-schema.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,26 @@ shows up as a diff). Don't switch to build-time-only generation — that loses t
1717

1818
## Regenerate (after adding/changing a Property or PassthroughPrefix)
1919

20+
```bash
21+
tika-metadata-schema/regen.sh
22+
```
23+
24+
This does the full sequence in one shot: `-am install` so newly added Property/PassthroughPrefix
25+
classes are on the scan classpath, regenerate all three registries via the forked-exec profile, print
26+
a before/after key-count check (catches an incomplete classpath scan), `git diff --stat` the
27+
registries, then run the gate tests. Flags: `--skip-install` (only safe if nothing outside
28+
`tika-metadata-schema` itself changed since the last install) and `--skip-tests` for a faster inner
29+
loop. Then review the diff and commit the Property change and the regenerated JSON together.
30+
31+
The manual sequence the script replaces, for reference or if you need to run a step in isolation:
32+
2033
```bash
2134
# if parser Property classes changed, install them first so the scan sees them:
2235
./mvnw -Pfast -DskipTests -pl tika-metadata-schema -am install -Dmaven.repo.local=$(pwd)/.local_m2_repo
2336
# regenerate all three:
2437
./mvnw -pl tika-metadata-schema -Pregen-metadata-schema process-classes -Dmaven.repo.local=$(pwd)/.local_m2_repo
2538
```
2639

27-
Then `git diff` the JSONs, run the gate tests, commit.
28-
2940
**Trap — never use `exec:java`.** `SchemaGenerator` scans `java.class.path`, force-loads Property
3041
classes, and swallows load failures. `exec:java` runs in-process on Maven's classpath → finds zero
3142
keys → emits a near-empty registry that exits 0 and *passes* `MetadataNoUnderscoreTest`. The

docs/modules/ROOT/nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
** xref:advanced/integration-testing/run-uat-script.adoc[Tika-Server REST UAT Script]
7777
* xref:developers/index.adoc[Developers]
7878
** xref:developers/serialization.adoc[Serialization and Configuration]
79+
** xref:developers/metadata-keys.adoc[Adding a Metadata Key]
7980
* xref:faq.adoc[FAQ]
8081
* xref:security.adoc[Security]
8182
* xref:roadmap.adoc[Roadmap]

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ 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
26+
registry, naming conventions, and regenerating the schema
2527

2628
== Coming Soon
2729

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one or more
2+
// contributor license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright ownership.
4+
// The ASF licenses this file to You under the Apache License, Version 2.0
5+
// (the "License"); you may not use this file except in compliance with
6+
// the License. You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
= Adding a Metadata Key
17+
18+
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
20+
space is tracked in a generated, build-gated registry, so adding a key involves one extra step beyond
21+
writing the Java.
22+
23+
== Add the constant
24+
25+
Add the `Property` to its interface as usual:
26+
27+
[source,java]
28+
----
29+
Property MY_NEW_KEY = Property.internalText(TIKA_META_PREFIX + "my-new-key");
30+
----
31+
32+
Naming conventions (frozen for 4.0):
33+
34+
* Tika-coined keys use the `tk:` namespace, kebab-case, no underscores.
35+
* External-standard names are used verbatim, including the standard's own prefix (`dc:`, `xmp:`,
36+
`cp:`, `extended-properties:`).
37+
* HTTP headers stay bare — no `http:` namespace (`Content-Type`, `Content-Encoding`, `Location`).
38+
39+
== Regenerate the registry
40+
41+
The registry — three JSON files under `tika-metadata-schema/src/main/resources/`, listing every
42+
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
44+
audit trail (a rename or dropped key shows up as a diff), and CI fails if it's stale.
45+
46+
Run this after adding, renaming, or removing a `Property` or `PassthroughPrefix`:
47+
48+
[source,bash]
49+
----
50+
tika-metadata-schema/regen.sh
51+
----
52+
53+
It installs the modules the change touched, regenerates all three registry files, sanity-checks the
54+
diff, and runs the gate tests. Commit the Java change and the regenerated JSON together.
55+
56+
Details, flags, and the traps this script exists to avoid (classpath scanning quirks, `exec:java`
57+
vs. a forked classpath) are documented in `tika-metadata-schema/README.md`.
58+
59+
== After a rename
60+
61+
The compiler won't catch a stale string literal like `metadata.get("Message-From")`. Grep the repo
62+
for the old key and replace it with the constant:
63+
64+
[source,bash]
65+
----
66+
grep -rn '"Message-' --include=*.java . | grep -v /target/
67+
----

tika-metadata-schema/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ declare a `Property` field, force-loads them, reads the global `Property` table,
3636
sorted JSON. `MetadataSchemaTest` regenerates in-memory and asserts it matches the committed file, so
3737
the registry can never drift from the declarations.
3838

39-
Regenerate after adding/changing a `Property` **or** a `PassthroughPrefix` (writes both files):
39+
Regenerate after adding/changing a `Property` **or** a `PassthroughPrefix` (writes all three files):
4040
```
41-
java -cp <tika-metadata-schema + deps classpath> \
42-
org.apache.tika.metadata.schema.SchemaGenerator \
43-
src/main/resources/org/apache/tika/metadata/metadata-keys.json \
44-
src/main/resources/org/apache/tika/metadata/metadata-open-namespaces.json
41+
tika-metadata-schema/regen.sh
4542
```
43+
Installs the dependency modules, regenerates the registries via the forked-exec profile, sanity-checks
44+
the key-count diff, and runs the gate tests — see `.skills/metadata-schema.md` for flags and the
45+
manual steps this replaces.
4646

4747
## `metadata-open-namespaces.json` — the open sets (generated + gated)
4848
The **prefixes** under which parsers mint file-controlled key names at runtime — names that are not

tika-metadata-schema/regen.sh

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Regenerates and validates the metadata key registry (tika-metadata-schema).
4+
#
5+
# Run this after adding, renaming, or removing a Property or PassthroughPrefix
6+
# constant anywhere in tika-core or the standard parser bundle. It replaces the
7+
# multi-step manual sequence in .skills/metadata-schema.md with one command:
8+
# install the dependency modules, regenerate the three registry files, sanity
9+
# check the diff, then run the gate tests.
10+
#
11+
# Usage:
12+
# tika-metadata-schema/regen.sh [--skip-install] [--skip-tests]
13+
#
14+
# --skip-install skip the -am install step (only safe if no Property/
15+
# PassthroughPrefix classes outside tika-metadata-schema
16+
# changed since the last install)
17+
# --skip-tests skip the final gate-test run, for a faster inner loop
18+
#
19+
# See tika-metadata-schema/README.md and .skills/metadata-schema.md for the
20+
# design and the traps this script exists to route around.
21+
22+
set -euo pipefail
23+
24+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
25+
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
26+
cd "$REPO_ROOT"
27+
28+
MVN_REPO_OPT="-Dmaven.repo.local=$REPO_ROOT/.local_m2_repo"
29+
30+
SKIP_INSTALL=0
31+
SKIP_TESTS=0
32+
for arg in "$@"; do
33+
case "$arg" in
34+
--skip-install) SKIP_INSTALL=1 ;;
35+
--skip-tests) SKIP_TESTS=1 ;;
36+
-h|--help)
37+
sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//'
38+
exit 0
39+
;;
40+
*)
41+
echo "Unknown argument: $arg" >&2
42+
exit 1
43+
;;
44+
esac
45+
done
46+
47+
REGISTRY_DIR="tika-metadata-schema/src/main/resources/org/apache/tika/metadata"
48+
REGISTRY_FILES=(
49+
"$REGISTRY_DIR/metadata-keys.json"
50+
"$REGISTRY_DIR/metadata-open-namespaces.json"
51+
"$REGISTRY_DIR/metadata-key-fields.json"
52+
)
53+
54+
if [ "$SKIP_INSTALL" -eq 0 ]; then
55+
echo "==> Installing tika-metadata-schema + its dependency modules (tika-core, standard parsers)"
56+
echo " so newly added Property/PassthroughPrefix classes are on the scan classpath."
57+
echo " (skip with --skip-install if you already did this)"
58+
./mvnw -Pfast -DskipTests -pl tika-metadata-schema -am install "$MVN_REPO_OPT"
59+
fi
60+
61+
echo "==> Recording committed key counts, to catch an incomplete classpath scan later"
62+
BEFORE_COUNTS=()
63+
for f in "${REGISTRY_FILES[@]}"; do
64+
if git cat-file -e "HEAD:$f" 2>/dev/null; then
65+
BEFORE_COUNTS+=("$(git show "HEAD:$f" | wc -l)")
66+
else
67+
BEFORE_COUNTS+=("0")
68+
fi
69+
done
70+
71+
echo "==> Regenerating the registry (forked exec — see .skills/metadata-schema.md for why exec:java is unsafe)"
72+
./mvnw -pl tika-metadata-schema -Pregen-metadata-schema process-classes "$MVN_REPO_OPT"
73+
74+
echo "==> Comparing key counts before/after (a large drop usually means classes failed to load):"
75+
for i in "${!REGISTRY_FILES[@]}"; do
76+
f="${REGISTRY_FILES[$i]}"
77+
before="${BEFORE_COUNTS[$i]}"
78+
after=$(wc -l < "$f")
79+
line=" $f: $before -> $after lines"
80+
if [ "$before" -gt 0 ] && [ "$after" -lt $((before * 90 / 100)) ]; then
81+
echo "$line *** WARNING: >10% drop, check --skip-install and module installs ***"
82+
else
83+
echo "$line"
84+
fi
85+
done
86+
87+
echo "==> git diff of the registries (review before committing):"
88+
git --no-pager diff --stat -- "${REGISTRY_FILES[@]}"
89+
90+
if [ "$SKIP_TESTS" -eq 0 ]; then
91+
echo "==> Running gate tests (MetadataSchemaTest, MetadataFieldTableTest, MetadataNoUnderscoreTest, MetadataCoverageTest, ...)"
92+
./mvnw -pl tika-metadata-schema test "$MVN_REPO_OPT"
93+
fi
94+
95+
echo "==> Done. Review the diff above, then commit the Property/PassthroughPrefix change and the regenerated JSON together."

tika-metadata-schema/src/test/java/org/apache/tika/metadata/schema/MetadataFieldTableTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,6 @@ public void committedFieldTableMatchesDeclarations() throws Exception {
4141
committed = new String(in.readAllBytes(), StandardCharsets.UTF_8);
4242
}
4343
assertEquals(committed, SchemaGenerator.fieldTable(),
44-
"metadata-key-fields.json is stale. Run SchemaGenerator.main (3rd arg) and commit it.");
44+
"metadata-key-fields.json is stale. Run tika-metadata-schema/regen.sh and commit it.");
4545
}
4646
}

tika-metadata-schema/src/test/java/org/apache/tika/metadata/schema/MetadataSchemaTest.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,17 @@ public class MetadataSchemaTest {
3838
@Test
3939
public void committedKeysMatchDeclarations() throws Exception {
4040
assertEquals(committed(KEYS), SchemaGenerator.generate(),
41-
"metadata-keys.json is stale. Run SchemaGenerator.main and commit the result.");
41+
"metadata-keys.json is stale. Run tika-metadata-schema/regen.sh and commit the "
42+
+ "result.");
4243
}
4344

4445
@Test
4546
public void committedOpenNamespacesMatchDeclarations() throws Exception {
4647
// generate() first, so the classpath scan force-loads the PassthroughPrefix declarations.
4748
SchemaGenerator.generate();
4849
assertEquals(committed(OPEN), SchemaGenerator.passthroughJson(),
49-
"metadata-open-namespaces.json is stale. Run SchemaGenerator.main and commit the "
50-
+ "result.");
50+
"metadata-open-namespaces.json is stale. Run tika-metadata-schema/regen.sh and "
51+
+ "commit the result.");
5152
}
5253

5354
private static String committed(String resource) throws Exception {

0 commit comments

Comments
 (0)