Configuration
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
Baseline date: 2026-10-12. This page was written against Apache Tika 4.1.0 (the latest release on Maven Central on that
date) with the 3.3.x differences noted, Java 21 and later for the examples (Tika 4.x needs Java 17, 3.x needs Java 11),
Spring Boot 4.x on Spring Framework 7 and the Docker image apache/tika:4.1.0-1. Tika 4.x is configured with one JSON
file, usually called tika-config.json, read by TikaLoader from the tika-serialization module. The same file
configures the library, tika-app, tika-server, tika-grpc and Tika Pipes: which parsers and detectors run, their
options, the limits in the ParseContext, the metadata filters, the output format, and, for the services, the server
and the pipeline. The 3.x tika-config.xml and the TikaConfig class are gone.
This page covers the file itself: every top-level section, how components are named, the parsers list and
default-parser, the TikaLoader API, where a typo is rejected and where it is silently ignored, secrets from
environment variables, Windows paths, extra jars, the @TikaComponent model behind all of it, and the migration from
3.x. The per-component options are on their own pages:
PDF Parsing for pdf-parser and the pages block,
OCR with Tesseract for tesseract-ocr-parser and text-recognizers,
VLM Parsers & Inference for engines and inference, and
Extending Tika for writing your own parser or detector. The ParseContext
mechanics are in Parser API & AutoDetectParser, the
metadata keys that the filters work on are in Metadata, the output formats are
in Content Handlers & Output Formats and the
detectors are in Media Types & Detection.
How this page was checked. The examples were compiled and run against Tika 4.1.0 (tika-core,
tika-parsers-standard-package as a pom dependency, tika-serialization, and tika-app on the classpath for the
CLI runs). The list of top-level keys, the environment-variable syntax and the TikaLoader methods were read from the
4.1.0 sources (TikaJsonConfig, EnvInterpolator, TikaLoader, TikaComponent, XmlToJsonConfigConverter, TikaCLI)
and the tika-serialization 4.1.0 jar. Every error message quoted below is the real output of a run. The sample files
are the small Bookshelf stand-ins used across this section: a one-heading manuscript.docx titled "The Lighthouse
Keeper" by Ana Torres and a one-page catalogue.pdf titled "Spring catalogue" by Bookshelf Press. The tika-server,
tika-grpc, Docker and Pipes sections of the file were not run for this page; they are described from the 4.1.x
documentation and named as such.
<dependencies>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-parsers-standard-package</artifactId>
<version>4.1.0</version>
<type>pom</type>
</dependency>
<!-- TikaLoader lives here, not in tika-core -->
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-serialization</artifactId>
<version>4.1.0</version>
</dependency>
</dependencies>
dependencies {
implementation(platform("org.apache.tika:tika-bom:4.1.0"))
implementation("org.apache.tika:tika-core")
implementation("org.apache.tika:tika-parsers-standard-package")
implementation("org.apache.tika:tika-serialization")
}
Source: Migrating to Tika 4.x ("TikaLoader is in the tika-serialization module").
Why a configuration file
Without a file, Tika discovers everything through the Java ServiceLoader: every parser, detector and encoding detector
on the classpath, with default options and no limits beyond the defaults. That is fine for a first program. A real
intake service needs to decide:
-
which formats it accepts, and which parsers it never wants to run (an executable parser for an upload form, say);
-
parser options (PDF text ordering, OCR language, timeouts);
-
limits: how much text, how deep the embedded documents go, how long a parse may take;
-
which metadata fields leave the parse, and under which names;
-
the output format of the extracted content;
-
for tika-server and Pipes, the forked JVMs, the fetchers and emitters, and which risky features are switched on.
In 4.x all of that lives in one JSON object. Two properties set it apart from the 3.x XML:
-
It is an allow-list. Components are referenced by a registered component name (
pdf-parser), never by a Java class name, so a configuration file cannot make Tika instantiate an arbitrary class. -
It is strict. An unknown top-level key, an unknown option in a component’s body or an unregistered component name fails at load time with a message that names the valid choices. There are four documented exceptions, listed under Strict validation: where a typo is caught.
The startup file is trusted: it is written by the operator. Per-request configuration that arrives over the wire (a
tika-server request body, a Pipes FetchEmitTuple) uses the same JSON shapes but is read in a restricted mode, never
reads environment variables and is off by default in tika-server. That distinction matters for every security decision
on this page.
From the file to the components
What happens, in order, verified in the 4.1.0 sources:
-
TikaLoader.load(path)callsTikaJsonConfig.load, which parses the file with Jackson.//and/* */comments are accepted (the Bookshelf file below has one). -
Still inside
TikaJsonConfig.load,EnvInterpolatorwalks every string value and replaces each${env:NAME}reference. An unset variable fails the load. -
validateKeys()compares every top-level key with a fixed set of 24 names. Anything else fails the load. -
The loader then checks that
java.io.tmpdirexists and is writable (it fails early, naming the directory, rather than on the first spool) and reads the three global sections (metadata-list,service-loader,xml-reader-utils). -
Nothing else is built yet. Each
load...method builds its component type on first call, resolving each name through the component registry (theMETA-INF/tika/*.idxfiles written by the@TikaComponentannotation processor), and caches the result: a secondloadParsers()returns the same object. -
loadParseContext()is the exception to the caching: it builds a newParseContextfrom theparse-contextsection on every call, which is what you want, because a context belongs to one parse.
Source: the 4.1.0 sources of
TikaJsonConfig,
EnvInterpolator
and TikaLoader.
The top-level sections
A tika-config.json is a single JSON object. Every section is optional, and an omitted section uses its defaults. The
table lists every key that the 4.1.0 loader accepts: this is the KNOWN_KEYS set in TikaJsonConfig, and the same list
appears in the error message for an unknown key. The 4.1.x configuration page shows a skeleton that it calls
exhaustive, but it leaves out three keys the 4.1.0 code accepts: engines, inference and presets.
| Key | Shape | What it configures | Read by |
|---|---|---|---|
|
array |
The parsers to load and their options. Naming any parser drops all others unless the list has a |
|
|
array |
The media-type detectors; |
|
|
array |
The charset detectors; |
|
|
array |
Filters that rewrite metadata after the parse, in order. No default filter exists. |
|
|
array |
Page renderers (for parsers that render pages for OCR). |
|
|
array |
(4.1.0+) The OCR or VLM engines that recognise text in images and rendered pages, by engine name; |
text-recognizer loader, used by the parsers |
|
object |
(4.1.0+) Named engine instances (Tesseract, a VLM, an embedding engine) that |
engine loader |
|
object |
(4.1.0+) Inference bindings (pages, text, media) dispatched after the parse. |
inference loader, hooked into |
|
object |
The translator implementation ( |
|
|
object |
The handler type and write limit for emitted content; Markdown if absent. |
|
|
object |
|
|
|
object |
Components and configs put into each |
|
|
object |
(4.1.0+) Named, vetted |
tika-server |
|
object |
tika-server options: |
tika-server |
|
object |
tika-grpc options such as |
tika-grpc |
|
object |
The Pipes process manager: |
Tika Pipes, tika-server, |
|
object |
Named fetcher instances ( |
Tika Pipes plugins |
|
object |
Named emitter instances (file system, Solr, Elasticsearch, …). |
Tika Pipes plugins |
|
object |
The one iterator that feeds a pipeline. |
Tika Pipes |
|
object |
Per-document status reporters. |
Tika Pipes |
|
string or array |
Where the pf4j plugin directories are. |
Tika Pipes plugin manager |
|
object |
Jackson read limits for metadata JSON: only |
global, at load |
|
object |
SPI load-failure policy (see the note below the table). |
global, at load |
|
object |
The shared XML parser pool: |
global, at load |
Two notes on the table. First, service-loader is accepted and deserialized at load time, but in the 4.1.0
tika-serialization jar its config class (GlobalSettings.ServiceLoaderConfig) has no properties at all (checked with
javap), so this page cannot tell you which option it takes; the 3.x attributes dynamic and loadErrorHandler have no
documented 4.x equivalent. Second, TikaLoader itself does not read server, grpc, pipes, fetchers, emitters,
pipes-iterator, pipes-reporters, plugin-roots or presets. It only accepts them, so one file can serve every
process. Their own consumers validate them when they start, so a misspelled option inside pipes is reported by Pipes,
not by TikaLoader.load.
Source: Apache Tika 4.1.x, Configuration, top-level JSON
structure, KNOWN_KEYS in the 4.1.0 TikaJsonConfig source,
Apache Tika 4.1.x, Tika Server (server fields and
presets), Apache Tika 4.1.x, Tika gRPC and
Apache Tika 4.1.x, Pipes Configuration.
Array sections and object sections
The array sections (parsers, detectors, encoding-detectors, metadata-filters, renderers, text-recognizers)
are ordered lists. Each element is either a string or an object with one key:
{
"detectors": [
"poifs-container-detector",
{ "default-detector": { "exclude": ["misc-ole-detector"] } }
],
"metadata-filters": [
{ "include-field-metadata-filter": { "include": ["tk:content", "Content-Type", "dc:title"] } },
"date-normalizing-metadata-filter"
]
}
The string form creates the component with its defaults; the object form passes the body as its configuration. Object
sections (parse-context, content-handler-factory, fetchers, engines, …) key each entry by name instead, with
the configuration object as the value. In fetchers, emitters and engines there is one more level: the outer key is
your instance id (covers, es), the inner key is the component name.
{
"fetchers": {
"inbox": { "file-system-fetcher": { "basePath": "/srv/bookshelf/inbox" } }
},
"parse-context": {
"commons-digester-factory": { "digests": [ { "algorithm": "SHA256" } ] }
}
}
Component names
Everything in the file is referenced by its component name: a kebab-case identifier such as pdf-parser,
default-detector, output-limits or file-system-fetcher. The @TikaComponent annotation processor derives it from
the class’s simple name (PDFParser becomes pdf-parser, ShelfCodeFilter becomes shelf-code-filter) unless the
class declares its own name, and writes it into an index file such as META-INF/tika/parsers.idx. The loader looks
names up in those index files. A class name is not accepted in its place: the failure message ends with "Arbitrary class
names are not allowed for security reasons".
To list the names on your classpath, use tika-app:
java -jar tika-app-4.1.0.jar --list-parser-names
java -jar tika-app-4.1.0.jar --list-detector-names
Output (first lines of the parser list, tab-separated class and name):
org.apache.tika.parser.apple.AppleSingleFileParser apple-single-file-parser
org.apache.tika.parser.apple.PListParser p-list-parser
org.apache.tika.parser.asm.ClassParser class-parser
org.apache.tika.parser.audio.AudioParser audio-parser
org.apache.tika.parser.audio.MidiParser midi-parser
With tika-parsers-standard-package 4.1.0 the parser list had 88 lines and the detector list 8. The derived names are
not always the obvious ones (p-list-parser, bp-list-detector, poifs-container-detector), so copy them from this
output instead of guessing. An unknown name also fails the load with the list of the registered ones, which is the
other way to find a name. Names of components outside the parser and detector lists (parse-context entries, metadata
filters, digesters) are in the .idx files inside each jar; for tika-core 4.1.0 the parse-context names are
embedded-limits, exception-reporting, output-limits, timeout-limits, skip-embedded-document-selector,
spooling-strategy, standard-metadata-limiter-factory, auto-detect-parser-config, simple-password-provider,
text-recognizers, inference, media, pages, basic-content-handler-factory and sax-output-config.
Source: Apache Tika 4.1.x, Configuration, component names,
Apache Tika 4.1.x, tika-app CLI and the
META-INF/tika/parse-context.idx file of the tika-core 4.1.0 jar.
The parsers list and default-parser
A parsers list loads only the parsers it names. To change one parser and keep all the others, add a default-parser
entry. Configuring a parser automatically excludes its default copy, so a configured pdf-parser next to
default-parser does not run twice.
{
"parsers": [
{ "pdf-parser": { "sortByPosition": true } },
{ "default-parser": { "exclude": ["executable-parser"] } }
]
}
exclude on default-parser takes component names and is only needed to disable a parser without replacing it. The
Bookshelf upload form never accepts programs, so executable-parser is excluded.
To see what "only the parsers it names" means, the next run loads a file with just pdf-parser and parses both sample
files. tk:parsed-by shows which parser ran.
{ "parsers": [ { "pdf-parser": { "sortByPosition": true } } ] }
files/catalogue.pdf -> application/pdf parsedBy=org.apache.tika.parser.CompositeParser,org.apache.tika.parser.pdf.PDFParser chars=68
files/manuscript.docx -> application/vnd.openxmlformats-officedocument.wordprocessingml.document parsedBy=org.apache.tika.parser.EmptyParser chars=0
The manuscript is still detected as a Word document (detection is a separate section), but no parser handles it, so
EmptyParser returns no text and no error. Leaving out default-parser by accident is a silent loss of every other
format; keep it unless you really want a Tika limited to the listed parsers.
detectors works the same way with a default-detector entry. encoding-detectors has a default-encoding-detector,
but it must not be mixed with explicit encoding-detector entries. See
Character Encoding Detection.
Source: Apache Tika 4.1.x, Configuration, the parsers list and default-parser and Apache Tika 4.1.x, Encoding Detectors.
MIME filtering with _mime-include and _mime-exclude
Any parser entry can be restricted to some media types, or kept away from some, with two framework directives. The leading underscore marks them as keys read by the loader, not by the parser; the loader strips them and wraps the parser in a MIME-filtering decorator.
{
"parsers": [
{ "pdf-parser": {
"_mime-include": ["application/pdf"],
"_mime-exclude": ["application/pdf+fdf"],
"sortByPosition": true } },
{ "default-parser": { "_mime-exclude": ["image/heic"] } }
]
}
On a default-parser entry, a real-type filter also removes the parser for that type. A bare string is read as a
one-element list, but a value that is neither a string nor an array silently becomes an empty filter. Since 4.1.0 the
pre-4.1 image/ocr-* pseudo-types are refused in these lists: filter the real type (image/tiff) on the engine’s own
entry instead.
This replaces the 3.x <mime> and <mime-exclude> elements, and exclude replaces <parser-exclude>.
Engines under parsers are deprecated
In 4.0 an OCR engine (tesseract-ocr-parser), a VLM parser or the image-embedding parser was configured as a parsers
entry. Since 4.1.0 that is deprecated: it still works with a startup WARN that says what the entry does, and it fails
config load in 4.2.0. The 4.1 shape names the engine under engines and lists it in text-recognizers, and every
default parser stays loaded:
{
"engines": {
"tesseract": { "tesseract-ocr-parser": { "language": "eng+spa" } }
},
"text-recognizers": [
{ "engine": "tesseract" }
]
}
"text-recognizers": [] turns OCR off everywhere, and
{"parse-context": {"text-recognizers": {"enabled": false}}} turns it off for one request. The engine options are on
OCR with Tesseract; the VLM and inference engines are on
VLM Parsers & Inference.
The other library sections
metadata-filters
metadata-filters is a top-level array run in order, each filter seeing the output of the previous one. There is no
default-metadata-filter and no implicit filtering: an absent or empty section means the metadata is emitted unchanged.
The built-in filters in tika-core are include-field-metadata-filter, exclude-field-metadata-filter,
field-name-mapping-filter, remove-by-mime-metadata-filter, clear-by-attachment-type-metadata-filter,
date-normalizing-metadata-filter, capture-group-metadata-filter, geo-point-metadata-filter,
legacy-key-migration-filter and no-op-filter. The filter options and recipes are on
Metadata.
Where the filters run is the important part:
-
tika-app, tika-server and Tika Pipes apply the configured chain before writing or emitting output.
-
A library caller applies it itself.
AutoDetectParser.parsedoes not run the filters; callloader.loadMetadataFilters().filter(list, context)on the metadata, as the Bookshelf program below does. -
In tika-app 4.1.0 the chain was applied to the
-J(recursive JSON) output but not to-mor-j. With the Bookshelf include filter,-mstill printed every PDF key while-Jprinted only the four included fields. -
An include filter drops what it does not name, including
tk:content. The same-Jrun returned no extracted text at all, because the filter’s list did not includetk:content. Add it when the text has to survive. -
A per-request
metadata-filterslist inparse-contextreplaces the configured chain for that request.
Run of -J -t with the Bookshelf filter (include lists Content-Type, dc:title, dc:creator, xmpTPg:NPages):
[{"dc:creator":"Bookshelf Press","dc:title":"Spring catalogue","Content-Type":"application/pdf","xmpTPg:NPages":"1"}]
content-handler-factory
The handler that tika-app, tika-server, Pipes and loadContentHandlerFactory() use for the extracted content. If the
section is missing, the loader returns a BasicContentHandlerFactory of type MARKDOWN with no write limit, the 4.x
default everywhere (3.x defaulted to XHTML).
{
"content-handler-factory": {
"basic-content-handler-factory": { "type": "TEXT", "writeLimit": 500000 }
}
}
The types are those of BasicContentHandlerFactory.HANDLER_TYPE (TEXT, HTML, XML, BODY, IGNORE,
MARKDOWN); see Content Handlers & Output Formats.
Source: the Javadoc of loadContentHandlerFactory in the 4.1.0 TikaLoader source and
Migrating to Tika 4.x, default content
handler.
auto-detect-parser
The AutoDetectParserConfig, read from the root of the file by loadAutoDetectParser(). In 4.1.0 it has two
properties: throwOnZeroBytes (default true: an empty file throws ZeroByteFileException) and
contentHandlerDecoratorFactory. The 3.x limits that lived here (outputThreshold, maximumCompressionRatio,
maximumDepth, …) moved to parse-context.
{ "auto-detect-parser": { "throwOnZeroBytes": false } }
parse-context
parse-context holds the objects and configs that each ParseContext from loadParseContext() carries. The ones a
Bookshelf worker sets are the limits:
| Entry | Properties (4.1.0 setters) | Purpose |
|---|---|---|
|
|
Text size and zip-bomb protection |
|
|
How deep and how many embedded documents |
|
|
The unified 4.x timeout model |
|
|
How much exception detail reaches the output |
|
|
Hashes of each document |
|
|
Page rendering and OCR policy for every rendering parser (see PDF Parsing) |
|
the parser’s own options |
Per-parse defaults for a self-configuring parser |
Two kinds of entries behave differently. Plain config objects such as output-limits are deserialized when the context
is built, so a misspelled property fails loadParseContext(). Self-configuring components, which include every
parser, are left as JSON in the context and resolved by the component when it runs; a typo there surfaces only when that
parser parses (see Strict validation: where a typo is caught). When a key has a JSON config, it wins over a value
set in code with context.set(XConfig.class, ...).
The limits are explained on Parser API & AutoDetectParser and Embedded Documents & Recursive Parsing.
Source: Apache Tika 4.1.x, Setting limits,
Apache Tika 4.1.x, Digesters,
Apache Tika 4.1.x, Pages and the tika-core 4.1.0
signatures of OutputLimits, EmbeddedLimits, TimeoutLimits and ExceptionReporting.
translator and renderers
translator takes one translator (loadTranslator() returns DefaultTranslator when absent; tika-core registers
default-translator and empty-translator, and tika-translate adds the online ones, covered in
Language Detection & Translation). renderers is a
list wrapped into a CompositeRenderer; the PDF renderer options are on PDF
Parsing.
Source: the registerComponentConfigs() method of the 4.1.0 TikaLoader source and the META-INF/tika/translators.idx
file of the tika-core 4.1.0 jar.
The Bookshelf configuration
The Bookshelf intake worker reads one file at startup. It keeps every default parser except the executable parser, orders PDF text by position, keeps four metadata fields, emits Markdown, and caps text, embedded depth and time.
{
// Bookshelf intake: the startup configuration of every worker
"parsers": [
{ "pdf-parser": { "sortByPosition": true, "extractInlineImages": false } },
{ "default-parser": { "exclude": ["executable-parser"] } }
],
"detectors": [
{ "default-detector": {} }
],
"metadata-filters": [
{ "include-field-metadata-filter": {
"include": ["Content-Type", "dc:title", "dc:creator", "xmpTPg:NPages"] } }
],
"content-handler-factory": {
"basic-content-handler-factory": { "type": "MARKDOWN", "writeLimit": 500000 }
},
"auto-detect-parser": { "throwOnZeroBytes": false },
"parse-context": {
"output-limits": { "writeLimit": 500000, "throwOnWriteLimit": false },
"embedded-limits": { "maxDepth": 5, "maxCount": 200 },
"timeout-limits": { "totalTaskTimeoutMillis": 120000, "progressTimeoutMillis": 30000 },
"exception-reporting": { "level": "MESSAGE_REDACTED" }
}
}
The program builds the shared components once and a new ParseContext, Metadata and handler for each document.
import java.nio.file.Path;
import java.util.List;
import org.apache.tika.config.EmbeddedLimits;
import org.apache.tika.config.OutputLimits;
import org.apache.tika.config.TimeoutLimits;
import org.apache.tika.config.loader.TikaLoader;
import org.apache.tika.detect.Detector;
import org.apache.tika.io.TikaInputStream;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.filter.MetadataFilter;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.parser.Parser;
import org.apache.tika.sax.ContentHandlerFactory;
import org.xml.sax.ContentHandler;
public class BookshelfConfig {
public static void main(String[] args) throws Exception {
// 1. read, interpolate and validate tika-config.json once, at startup
TikaLoader loader = TikaLoader.load(Path.of("tika-config.json"));
// 2. build the shared, thread-safe components (cached by the loader)
Parser parser = loader.loadAutoDetectParser();
Detector detector = loader.loadDetectors();
MetadataFilter filter = loader.loadMetadataFilters();
ContentHandlerFactory handlers = loader.loadContentHandlerFactory();
System.out.println("parser = " + parser.getClass().getSimpleName());
System.out.println("detector = " + detector.getClass().getSimpleName());
System.out.println("filter = " + filter.getClass().getSimpleName());
System.out.println("handler = " + handlers.handlerTypeName());
for (String name : List.of("catalogue.pdf", "manuscript.docx")) {
// 3. a fresh ParseContext per document, built from "parse-context"
ParseContext context = loader.loadParseContext();
Metadata metadata = Metadata.newInstance(context);
ContentHandler handler = handlers.createHandler();
try (TikaInputStream tis = TikaInputStream.get(Path.of("files", name), metadata)) {
parser.parse(tis, handler, metadata, context);
}
List<Metadata> list = List.of(metadata);
filter.filter(list, context); // library callers apply the filters themselves
String text = handler.toString().strip();
System.out.println("--- " + name);
System.out.println("limits = " + OutputLimits.get(context).getWriteLimit()
+ " chars, depth " + EmbeddedLimits.get(context).getMaxDepth()
+ ", timeout " + TimeoutLimits.get(context).getTotalTaskTimeoutMillis() + " ms");
for (String key : metadata.names()) {
System.out.println(key + " = " + metadata.get(key));
}
System.out.println("first line: " + text.lines().findFirst().orElse(""));
}
}
}
Output (log lines on standard error omitted):
parser = AutoDetectParser
detector = DefaultDetector
filter = CompositeMetadataFilter
handler = MARKDOWN
--- catalogue.pdf
limits = 500000 chars, depth 5, timeout 120000 ms
dc:creator = Bookshelf Press
dc:title = Spring catalogue
Content-Type = application/pdf
xmpTPg:NPages = 1
first line: Spring catalogue
--- manuscript.docx
limits = 500000 chars, depth 5, timeout 120000 ms
dc:title = The Lighthouse Keeper
dc:creator = Ana Torres
Content-Type = application/vnd.openxmlformats-officedocument.wordprocessingml.document
first line: The Lighthouse Keeper
The startup log also has two INFO lines worth knowing: temporary files go to java.io.tmpdir=... (the loader’s temp
directory check) and text recognizers: none found among the loaded parsers; images and rendered pages are not
enriched, because Tesseract was not installed on the test machine. With Tesseract on the path, that second line names
the engine and the media types it covers.
Source: composed for this page from Apache Tika 4.1.x, Configuration, Migrating to Tika 4.x, TikaConfig replaced by TikaLoader and TikaLoader, Javadoc 4.1.0.
The TikaLoader API
org.apache.tika.config.loader.TikaLoader (module tika-serialization) replaces TikaConfig. Its public methods in
4.1.0, read from the jar with javap:
| Method | What it does |
|---|---|
|
Reads, interpolates and validates the file, using the thread context class loader. Throws |
|
Same, with an explicit class loader for component lookup (plugin containers, application servers). |
|
No file: every component from SPI. Returns one cached, process-wide instance (synchronized). |
|
No file, with this class loader. Builds a new loader on every call. |
|
The configured parsers as one |
|
An |
|
The configured detectors and charset detectors as one composite each. Cached. |
|
The chain as a |
|
The configured factory, or Markdown with no limit. Cached. |
|
The renderers as a |
|
A new |
|
One config object from |
|
The generic form behind the |
|
The parsed |
|
The |
|
Writes the loaded components back as pretty-printed JSON. See the caution below. |
|
The shared default |
|
The class loader in use. |
How to use it well:
-
One loader per configuration, created at startup. It is cheap to keep and expensive to rebuild (the parser list is built from the registry). The components it returns are meant to be shared:
AutoDetectParser, the detectors and the filters are thread-safe in the usual Tika sense. In Spring, expose the loader and the parser as singleton beans; the Spring Boot Integration page of this section covers the lifecycle. -
A new
ParseContextper parse, fromloadParseContext(). A context carries per-parse state and must not be shared between concurrent parses. -
loadDefault()is a process-wide singleton. Use it only where every caller wants the SPI defaults; anything with limits or exclusions needs its own file. -
Fail at startup.
loadthrows for a syntax error, an unknown top-level key or an unset environment variable, but component names and options are checked only when the matchingload...method runs. CallloadAutoDetectParser(),loadParseContext()andloadMetadataFilters()once during startup so a bad entry stops the service before the first document instead of on it. -
toJson()is a diagnostic, not a round-trip. In 4.1.0 it serialises the parsers' internal state: a file with only{ "pdf-parser": { "sortByPosition": true } }came back withdefaultConfigandpdfparserConfigblocks of 34 properties each, arendererand acontentEnrichersobject, plus the full SPI detector and encoding-detector lists. Loading that output again failed withFailed to instantiate component 'org.apache.tika.parser.pdf.PDFParser'. Keep your hand-written file as the source of truth. (tika-app’s old--dump-current-configand related options are commented out in the 4.1.0TikaCLIsource with the note "rework with json serialization".)
Source: TikaLoader, Javadoc 4.1.0,
Migrating to Tika 4.x, TikaConfig replaced by
TikaLoader and the 4.1.0 TikaLoader source (caching, loadDefault, temp directory check).
Strict validation: where a typo is caught
The 4.x loader rejects unknown keys almost everywhere. Each configuration below was loaded with
TikaLoader.load(path) followed by loadAutoDetectParser(), loadParseContext() and loadMetadataFilters(); the
messages are the real 4.1.0 output, shortened where marked.
| Mistake | Fails at | Message |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
a class name instead of a component name |
the matching |
|
|
|
|
|
|
|
The 4.1.x documentation names four places that do not reject a typo, so a misspelling there has no effect and no error:
-
the
metadata-listbody: onlymaxStringLength,maxNestingDepthandmaxNumberLengthare read (the 4.1.0TikaLoadersource confirms it reads exactly those three); -
the body of a self-configuring component under
parse-context, which is handed to the component untouched; -
_mime-includeor_mime-excludewith a value that is neither a JSON array nor a string, which yields an empty filter; -
a non-string entry inside
excludeondefault-parser, such as[{"pdf-parser": {}}], which is skipped, so that parser stays enabled.
For the second case this page found the behaviour more nuanced than "no effect". A
{"parse-context": {"pdf-parser": {"sortByPositon": true}}} passed every load call. The typo surfaced only when the
PDF was parsed, as TikaException: TIKA-198: Illegal IOException from org.apache.tika.parser.pdf.PDFParser caused by the
same Unrecognized field "sortByPositon", while the Word document parsed normally. So the mistake is not silent, but it
appears per document, at parse time, and only for the formats that parser handles. Parse one sample of each important
format in a startup or CI check after changing parse-context parser blocks.
Source: Apache Tika 4.1.x, Configuration, where a typo is caught, and where it is not; the experiments were run for this page against Tika 4.1.0.
Secrets from the environment
Since 4.1.0 a string value in the startup file may reference an environment variable as ${env:NAME}, alone or inside
a longer string. The loader substitutes the value when it reads the file. The exact pattern in the 4.1.0
EnvInterpolator source is \$\{env:([A-Za-z_][A-Za-z0-9_]*)}, so a variable name starts with a letter or an
underscore and contains letters, digits and underscores.
{
"server": { "port": 9998 },
"fetchers": {
"covers": { "file-system-fetcher": { "basePath": "${env:BOOKSHELF_INBOX}/covers" } }
},
"emitters": {
"es": { "es-emitter": { "esUrl": "http://${env:ES_HOST}:9200/bookshelf",
"apiKey": "${env:ES_API_KEY}" } }
}
}
The rules, checked against the source and with a run:
-
Only
${env:NAME}is interpolated.${NAME},$NAME,${sys:...}and any other text stay literal, so a prompt or a regex that contains${is safe. In the test,"${env:BOOKSHELF_INBOX}/covers"became/srv/bookshelf/inbox/coverswhile a"${BOOKSHELF_INBOX} stays literal"value was left unchanged. -
Only string values are interpolated, in objects and arrays at any depth. Keys and non-string values (numbers, booleans) are not.
-
An unset variable fails the load with its name and its JSON path, never a value: a missing secret stops the service at startup instead of reaching Elasticsearch as an empty key.
-
The forked parse workers of tika-server and Pipes inherit the parent’s environment and read the same file, so a reference resolves there too.
-
Only the startup file is interpolated. Per-request configuration (tika-server
/configendpoints, Pipes tuples) never is: a request cannot read the server’s environment. Inline presets in the file resolve at startup with the rest of it. -
The reference keeps the secret out of
tika-config.json, not out of every file. With a persistent config store (configStoreTypefileor Ignite) the resolved fetcher and emitter configurations are written to that store; the default in-memory store writes nothing. -
The Tika 4.1.0 release notes list environment-variable interpolation in
tika-config.jsonas new in 4.1.0, so a 4.0.x loader does not resolve these references. This page did not check whether any 3.x component had a comparable mechanism in the XML; templating the file before start was the general workaround.
Run it with the variables set by the platform (a Kubernetes Secret, a systemd EnvironmentFile, the CI secret
store), never with the values in a script:
export BOOKSHELF_INBOX=/srv/bookshelf/inbox
export ES_HOST=search.internal
# ES_API_KEY comes from the secret store, never from this file
java -jar tika-server-standard-4.1.0.jar -c tika-config.json
TikaLoader.getConfig().getRootNode() returns the JSON after interpolation. Do not log it in production: it contains
the resolved secrets.
Source: Apache Tika 4.1.x, Configuration, secrets from the
environment, the 4.1.0 EnvInterpolator and TikaJsonConfig sources, and the Tika 4.1.0 release notes on
the Apache Tika home page ("env-var interpolation in tika-config.json").
Windows file paths
JSON treats the backslash as an escape character. Path options such as tesseractPath or imageMagickPath need
forward slashes or doubled backslashes; a single backslash is a JSON parse error (the table above shows the message).
{
"engines": {
"tesseract": { "tesseract-ocr-parser": {
"tesseractPath": "C:/Program Files/Tesseract-OCR/",
"tessdataPath": "C:\\Program Files\\Tesseract-OCR\\tessdata" } }
},
"text-recognizers": [ { "engine": "tesseract" } ]
}
Forward slashes are the safer choice: they also survive being pasted into a shell, a YAML file or a Helm value. The
Tesseract options themselves are on OCR with Tesseract; this example’s
tesseractPath and tessdataPath names come from the 4.1.x Tesseract page and were not run on Windows for this page.
Adding extra jars: tika.extras.dir
To add components (an extra parser, encoding detector or their dependencies) to tika-app or tika-server without
repackaging, put their jars in a directory and point the tika.extras.dir system property at it:
java -Dtika.extras.dir=/opt/bookshelf/tika-extras -jar tika-app-4.1.0.jar --config=tika-config.json catalogue.pdf
java -Dtika.extras.dir=/opt/bookshelf/tika-extras -jar tika-server-standard-4.1.0.jar -c tika-config.json
Every jar in that directory joins the classpath that Tika’s service loading scans, so SPI-registered components (and
@TikaComponent names, through their .idx files) are picked up, and the jars are forwarded to the forked JVMs of Pipes
and tika-server. The property is off by default and there is no implicit directory. In the Docker images, mount the
directory at /tika-extras instead, which is already on the container classpath.
Treat that directory as trusted code: anything in it runs with the full privileges of the Tika process. It must not be
writable by less-trusted users, nor reachable by request handling on a server. pf4j-managed Pipes plugins (plugins/,
plugin-roots) are a separate mechanism, covered on the Tika Pipes page of this section.
In your own Java application you do not need tika.extras.dir: add the component as a normal dependency.
This page did not run tika.extras.dir; the description comes from the 4.1.x configuration and Docker pages.
The @TikaComponent and serialization model
Behind the JSON is the serialization system of tika-serialization, built on Jackson. The @TikaComponent annotation
(org.apache.tika.annotation.TikaComponent, shipped in tika-core) is required for any class that should be
configurable from JSON. At compile time the tika-annotation-processor reads it and:
-
derives the component name (or takes
name); -
adds a
name=classline to the index file of its type, such asMETA-INF/tika/metadata-filters.idx; -
adds the class to
META-INF/services/<interface>forServiceLoader, unlessspi = false.
The registry built from the index files is the security allow-list: only registered classes can be instantiated from JSON.
| Attribute | Default | Meaning |
|---|---|---|
|
derived from the simple class name, in kebab case |
The component name used in JSON. |
|
|
Also register in |
|
auto-detected |
The class used as the |
|
none |
Marks the component as the default implementation of an interface, used when no explicit configuration is given (for example |
Three more ideas complete the model:
-
Self-configuring components.
SelfConfiguringis a marker interface.Parserextends it, so every parser reads its own JSON from theParseContextwhen it runs, throughParseContextConfig.getConfig(context, "pdf-parser", PDFParserConfig.class, defaultConfig). That is how per-request configuration reaches a parser, and why a typo in a parser block underparse-contextsurfaces at parse time. -
Framework directives start with an underscore (
_mime-include,_mime-exclude,_min-width,_min-height) so they cannot collide with a component’s own option names. Theexcludekey of thedefault-...markers is the exception, because those markers carry only framework keys. -
Restricted mode for wire input. The startup file is trusted. Per-request JSON (tika-server request bodies, Pipes
FetchEmitTuple) is read in restricted mode: the whole tree is scanned before anything is built, and only types that shape this request’s output can be instantiated from the wire (MetadataFilter,ContentHandlerFactory,ContentHandlerDecoratorFactory,DigesterFactory,MetadataWriteLimiterFactory,UnpackSelector). Types with exec, I/O or network capability (Parser,Detector,EncodingDetector,Renderer,Translator,EmbeddedDocumentExtractor) are blocked. The check is fail-closed: a new context-key interface is blocked until it is consciously allow-listed.
The restricted mode does not stop a request from configuring an already-loaded parser, because a parser’s config
subtree is passed through unexamined. A request carrying
{"parse-context": {"pages": {"text": "EXTRACT_AND_OCR"}}} reaches the PDF parser and takes effect. That is why
tika-server gates per-request configuration separately with allowPerRequestConfig, off by default. Treat "callers may
send per-request config" as "callers may set any parser option, including ones that start external processes such as
OCR".
Source: Apache Tika 4.1.x, Serialization and
Configuration, the 4.1.0
TikaComponent
source and TikaComponent, Javadoc
4.1.0.
A custom component: the Bookshelf shelf-code filter
The Bookshelf catalogue files each book under a shelf code built from its title: "The Lighthouse Keeper" goes to
SHELF-LIG. A metadata filter is the right place for that rule, because filters are the supported way to shape Tika’s
output. The annotation processor has to run at compile time, so the Maven build declares it as an annotation processor
path:
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.bookshelf</groupId>
<artifactId>bookshelf-tika-components</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<tika.version>4.1.0</tika.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>${tika.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-serialization</artifactId>
<version>${tika.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-parsers-standard-package</artifactId>
<version>${tika.version}</version>
<type>pom</type>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.0</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.apache.tika</groupId>
<artifactId>tika-annotation-processor</artifactId>
<version>${tika.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>
The Gradle equivalent puts the processor on the annotationProcessor configuration (not run for this page):
dependencies {
implementation(platform("org.apache.tika:tika-bom:4.1.0"))
implementation("org.apache.tika:tika-core")
implementation("org.apache.tika:tika-serialization")
implementation("org.apache.tika:tika-parsers-standard-package")
annotationProcessor("org.apache.tika:tika-annotation-processor:4.1.0")
}
The filter extends MetadataFilterBase, whose per-Metadata hook suits a filter that needs no context. Options are
plain bean properties, set from the JSON body.
package com.example.bookshelf;
import java.util.Locale;
import org.apache.tika.annotation.TikaComponent;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.TikaCoreProperties;
import org.apache.tika.metadata.filter.MetadataFilterBase;
/** Derives a shelf code such as "BKS-LIG" from the document title. */
@TikaComponent
public class ShelfCodeFilter extends MetadataFilterBase {
private String prefix = "BKS";
private String targetField = "bookshelf:shelf-code";
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getTargetField() {
return targetField;
}
public void setTargetField(String targetField) {
this.targetField = targetField;
}
@Override
protected void filter(Metadata metadata) {
String title = metadata.get(TikaCoreProperties.TITLE);
if (title == null || title.isBlank()) {
return;
}
String letters = title.replaceAll("(?i)^(the|a|an)\\s+", "").replaceAll("[^\\p{L}]", "");
String code = letters.substring(0, Math.min(3, letters.length())).toUpperCase(Locale.ROOT);
metadata.set(targetField, prefix + "-" + code);
}
}
mvn compile generated both files. The index line (license header omitted):
# Generated by TikaComponentProcessor
# Do not edit manually
# Format: component-name=fully.qualified.ClassName[:key=contextKeyClass]
shelf-code-filter=com.example.bookshelf.ShelfCodeFilter:key=org.apache.tika.metadata.filter.MetadataFilter
and META-INF/services/org.apache.tika.metadata.filter.MetadataFilter lists com.example.bookshelf.ShelfCodeFilter.
The filter is now configurable by name, before an include filter that keeps the new field:
{
"metadata-filters": [
{ "shelf-code-filter": { "prefix": "SHELF" } },
{ "include-field-metadata-filter": {
"include": ["Content-Type", "dc:title", "bookshelf:shelf-code"] } }
]
}
package com.example.bookshelf;
import java.nio.file.Path;
import java.util.List;
import org.apache.tika.config.loader.TikaLoader;
import org.apache.tika.io.TikaInputStream;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.filter.MetadataFilter;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.parser.Parser;
import org.apache.tika.sax.BodyContentHandler;
public class ShelfCodeDemo {
public static void main(String[] args) throws Exception {
TikaLoader loader = TikaLoader.load(Path.of("tika-config.json"));
Parser parser = loader.loadAutoDetectParser();
MetadataFilter filters = loader.loadMetadataFilters();
ParseContext context = loader.loadParseContext();
Metadata metadata = Metadata.newInstance(context);
try (TikaInputStream tis = TikaInputStream.get(Path.of("files/manuscript.docx"), metadata)) {
parser.parse(tis, new BodyContentHandler(-1), metadata, context);
}
filters.filter(List.of(metadata), context);
for (String name : metadata.names()) {
System.out.println(name + " = " + metadata.get(name));
}
}
}
Output:
bookshelf:shelf-code = SHELF-LIG
dc:title = The Lighthouse Keeper
Content-Type = application/vnd.openxmlformats-officedocument.wordprocessingml.document
Two notes. The filter writes a custom bookshelf: key with Metadata.set; a filter whose target field is
configurable and could name a reserved tk: key must use setTrusted instead, as the 4.x serialization page explains
(writing a reserved tk: key by string name throws in 4.x). And the same class referenced by its class name,
{"com.example.bookshelf.ShelfCodeFilter": {}}, fails with Unknown type: com.example.bookshelf.ShelfCodeFilter: the
name is the only way in. Custom parsers and detectors follow the same pattern and are covered on
Extending Tika.
Source: Apache Tika 4.1.x, Serialization and
Configuration, creating a custom component (its UpperCaseFilter example) and the tika-core 4.1.0 pom.xml, which
configures tika-annotation-processor the same way.
Using the file from tika-app, tika-server, Docker and Pipes
The same file is passed to every entry point.
# tika-app (run from the unzipped tika-app-4.1.0.zip, so lib/ sits next to the jar)
java -jar tika-app-4.1.0.jar --config=tika-config.json -t catalogue.pdf
# tika-server (unzipped tika-server-standard-4.1.0.zip); the file must have a "server" section, see below
java -jar tika-server-standard-4.1.0.jar -c tika-config.json
# Docker: mount the file and pass -c after the image name
docker run -d -p 127.0.0.1:9998:9998 \
-v "$(pwd)/tika-config.json:/tika-config.json" \
apache/tika:4.1.0-1 -c /tika-config.json
Output of the tika-app line (the TikaCLI class run from the 4.1.0 tika-app jar with its dependencies on the
classpath):
Bookshelf spring catalogue
Forty titles from independent publishers.
With a bad file tika-app stops before reading any document:
Exception in thread "main" org.apache.tika.exception.TikaConfigException: Unknown configuration key(s): [parser].
Valid keys: [...]. In tika-app 4.1.0 --config is spelled --config=<file>; the batch mode also accepts
-c <file>. A single .json argument with no other options is treated as an async (Pipes) configuration, not as a
document.
tika-server requires a server section: in 4.1.0 a file without one makes the server refuse to start
(TikaConfigException: Couldn't find 'server' element), and an empty "server": {} is enough. So the
Bookshelf file above needs that one extra line before it is passed to tika-server or the Docker image. The server
also reads the pipes and parse-context sections its forked parsing uses; tika-grpc reads its own grpc section in
the same way. Those
deployments are covered by the Tika Server (REST API),
Docker & Kubernetes Deployment and
Tika Pipes pages of this section; the
commands above were not run for this page except the tika-app one.
Source: Apache Tika 4.1.x, tika-app CLI,
Apache Tika 4.1.x, Tika Server, configuration,
Apache Tika 4.1.x, Docker and the 4.1.0 TikaCLI source.
Migrating from 3.x tika-config.xml and TikaConfig
Tika 4.x does not read tika-config.xml at all, and TikaConfig is removed. Migration has three parts: convert the
parsers section with the tool, migrate every other section by hand, and replace TikaConfig in code.
XML and JSON side by side
| Aspect | 3.x tika-config.xml |
4.x tika-config.json |
|---|---|---|
Root |
|
a JSON object |
Component reference |
full class name, |
component name, |
Parameters |
|
|
Keep the defaults |
|
|
Disable a parser |
|
|
MIME restriction |
|
|
Limits |
|
|
Zero-byte handling |
|
|
Metadata filters |
|
|
Encoding detectors |
|
|
Translator, renderers |
|
|
XML parser pool |
|
|
Service loader |
|
|
Server, pipes, fetchers, emitters |
separate XML sections and tika-server options |
|
Locate the file |
|
pass the path: |
Secrets |
no documented interpolation (not checked in the sources) |
|
Unknown keys |
not tested for this page |
fatal almost everywhere |
The tika.config / TIKA_CONFIG lookup is in the 3.3.2 TikaConfig source. This page found no reference to either
name in the 4.1.0 tika-core, tika-serialization and tika-app jars, so do not rely on them in 4.x; the
tika-server jar was not checked. The limits row maps the 3.x AutoDetectParserConfig setters to the 4.x
OutputLimits and EmbeddedLimits properties by meaning (outputThreshold and maximumCompressionRatio to
zipBombThreshold and zipBombRatio, maximumDepth to maxXmlDepth, maximumPackageEntryDepth to
maxPackageEntryDepth); that pairing is this page’s reading of the two classes, not an official table, and the 3.x
spoolToDisk, digesterFactory, metadataWriteFilterFactory and embeddedDocumentExtractorFactory settings have
different 4.x homes (spooling-strategy, commons-digester-factory, standard-metadata-limiter-factory, and a
directly bound EmbeddedDocumentExtractor).
Source: Migrating to Tika 4.x, configuration:
XML to JSON, Apache Tika 3.3.2, Configuring Tika and the 3.3.2
TikaConfig, ConfigBase and AutoDetectParserConfig sources.
Converting the parsers section
tika-app 4.1.0 has a converter. It writes the JSON to standard output and the diagnostics to standard error, and it
takes no other argument:
java -jar tika-app-4.1.0.jar --convert-config-xml-to-json=tika-config.xml > tika-config.json
The Bookshelf 3.x file it was run on (3.x form, kept for migration only):
<?xml version="1.0" encoding="UTF-8"?>
<properties>
<parsers>
<parser class="org.apache.tika.parser.DefaultParser">
<parser-exclude class="org.apache.tika.parser.pdf.PDFParser"/>
<parser-exclude class="org.apache.tika.parser.executable.ExecutableParser"/>
</parser>
<parser class="org.apache.tika.parser.pdf.PDFParser">
<params>
<param name="sortByPosition" type="bool">true</param>
<param name="extractInlineImages" type="bool">true</param>
<param name="ocrStrategy" type="string">auto</param>
</params>
</parser>
<parser class="org.apache.tika.parser.ocr.TesseractOCRParser">
<params>
<param name="language" type="string">eng+spa</param>
<param name="timeoutSeconds" type="int">120</param>
</params>
</parser>
</parsers>
<detectors>
<detector class="org.apache.tika.detect.DefaultDetector"/>
</detectors>
<service-loader initializableProblemHandler="throw"/>
</properties>
Standard output:
{
"parsers" : [ {
"default-parser" : {
"exclude" : [ "pdf-parser", "executable-parser" ]
}
}, {
"pdf-parser" : {
"sortByPosition" : true,
"extractInlineImages" : true,
"pages" : {
"text" : "auto"
}
}
}, {
"tesseract-ocr-parser" : {
"language" : "eng+spa",
"timeoutMillis" : 120000
}
} ]
}
Standard error carried a "CONFIGURATION OPTIMIZATION NOTICE" saying that pdf-parser is excluded from default-parser
but also configured separately, that the exclusion is redundant, and that it can be removed.
What the run shows:
-
Only
parsersis converted. The<detectors>and<service-loader>sections were dropped with no message on either stream. The usage text says so ("parsers section only"), but nothing in the output does: compare section by section. -
Class names became component names, and
excludelists for<parser-exclude>. -
Renamed 3.x options were mapped:
ocrStrategybecame the 4.1pages.text, and Tesseract’stimeoutSecondsbecametimeoutMillis, with the value multiplied by 1000. Tesseract’sotherTesseractSettingslist becomes theotherTesseractConfigmap. -
The value
autowas copied as written. The converted file loaded as it was and parsed both sample files, so the lower-case form is accepted in 4.1.0; the 4.1.x documentation spells the policy values in upper case (AUTO,EXTRACT, …), which is the safer form to keep. -
The Tesseract entry stays under
parsers, which 4.1.0 accepts with a deprecation WARN and 4.2.0 will reject. On the test machine, with no Tesseract installed, the WARN read:org.apache.tika.parser.ocr.TesseractOCRParser is named under "parsers", which is deprecated for engines since 4.1.0 and unsupported in 4.2.0: configure it under "engines" and name it in "text-recognizers". It advertises no media types (engine unavailable, or configured to skip) and never runs; .... Move it toenginesandtext-recognizersas shown earlier. -
A custom or third-party parser class that the converter cannot find in the registry gets a kebab-case name derived from its simple class name, which may not be its real registered name. Check it against
--list-parser-names.
The converter class is org.apache.tika.cli.XmlToJsonConfigConverter; its supported parameter types are bool, int,
long, double, float, string, list and map. The converted tika-config.json after the manual fixes:
{
"parsers": [
{ "pdf-parser": { "sortByPosition": true, "extractInlineImages": true, "pages": { "text": "AUTO" } } },
{ "default-parser": { "exclude": ["executable-parser"] } }
],
"engines": {
"tesseract": { "tesseract-ocr-parser": { "language": "eng+spa", "timeoutMillis": 120000 } }
},
"text-recognizers": [ { "engine": "tesseract" } ],
"detectors": [ { "default-detector": {} } ]
}
This final file follows the 4.1.x documentation for engines and text-recognizers. Note the behaviour change it
brings: on the test machine, which had no Tesseract binary, it failed to load with Text recognizer engine
"tesseract" advertises no media types (a _mime-include list does not count). Is the engine unavailable (missing native
binary, unreachable inference server) or configured to skip enrichment?. An engine named in text-recognizers must be
usable at startup, while the deprecated entry under parsers was quietly inert. That is the intended 4.1 behaviour (a
missing OCR engine stops the service instead of silently producing no OCR text), so install Tesseract, or use the
-full Docker image, before deploying the migrated file. It was not loaded with Tesseract installed for this page.
Source: Migrating to Tika 4.x, automatic
conversion, the 4.1.0
XmlToJsonConfigConverter
source, and a run of the 4.1.0 converter.
Migrating the other sections by hand
A 3.x file with limits and filters (3.x form, not run for this page):
<properties>
<autoDetectParserConfig>
<params>
<outputThreshold>1000000</outputThreshold>
<maximumCompressionRatio>100</maximumCompressionRatio>
<maximumDepth>100</maximumDepth>
<throwOnZeroBytes>false</throwOnZeroBytes>
</params>
</autoDetectParserConfig>
<metadataFilters>
<metadataFilter class="org.apache.tika.metadata.filter.IncludeFieldMetadataFilter">
<params>
<include>
<field>Content-Type</field>
<field>dc:title</field>
</include>
</params>
</metadataFilter>
</metadataFilters>
</properties>
The 4.x equivalent. Note the metadata key names: a 3.x filter that names X-TIKA:content or other 3.x keys has to use
the 4.x tk: names (see Metadata, or add legacy-key-migration-filter while
consumers catch up).
{
"auto-detect-parser": { "throwOnZeroBytes": false },
"parse-context": {
"output-limits": { "zipBombThreshold": 1000000, "zipBombRatio": 100, "maxXmlDepth": 100 }
},
"metadata-filters": [
{ "include-field-metadata-filter": { "include": ["tk:content", "Content-Type", "dc:title"] } }
]
}
TikaConfig to TikaLoader in code
3.x form, kept for migration only:
// Tika 3.x: TikaConfig and tika-config.xml (removed in 4.x)
TikaConfig config = new TikaConfig(Path.of("tika-config.xml"));
Parser parser = config.getParser();
Detector detector = config.getDetector();
AutoDetectParser autoDetect = new AutoDetectParser(config);
ParseContext context = new ParseContext();
4.x:
// Tika 4.x: TikaLoader (tika-serialization) and tika-config.json
TikaLoader loader = TikaLoader.load(Path.of("tika-config.json"));
Parser parsers = loader.loadParsers();
Detector detector = loader.loadDetectors();
Parser autoDetect = loader.loadAutoDetectParser(); // declared type is Parser
ParseContext context = loader.loadParseContext(); // limits and per-parser config from the file
// no file: the SPI defaults, as new TikaConfig() was in 3.x
TikaLoader defaults = TikaLoader.loadDefault();
The other code-level changes that come with the move:
-
add
tika-serializationto the build if you only hadtika-core; -
a 3.x
AutoDetectParser(TikaConfig)constructor call becomesloadAutoDetectParser();new AutoDetectParser()without a config still works and uses the SPI defaults; -
JSON config now wins over a programmatic
context.set(XConfig.class, ...)for the same key, the reverse of earlier 4.x builds; -
parseContext.get(SomeConfig.class)no longer returns a config that came from JSON, because configs are cached by component name and class; -
the
Tikafacade andnew DefaultParser()still work without a loader for simple cases.
The 3.x TikaConfig Javadoc is linked in the references for comparison; there is no 4.1.0 Javadoc page for it.
Memory, performance and robustness
-
Load once.
TikaLoader.loadparses the file, and the firstloadParsers()instantiates every parser in the list (withdefault-parser, all of them). Do it at startup and share the results; never build a loader per document. -
Contexts are cheap and per parse.
loadParseContext()builds a new context from the parsed JSON each time; that is the intended per-document call. -
Limits belong in the file.
output-limits,embedded-limitsandtimeout-limitsinparse-contextare the 4.x defences against huge outputs, zip bombs, deep nesting and parsers that hang. A configuration without them inherits the defaults, which do not cap text (writeLimit-1). -
Fewer parsers, less risk. Excluding parsers you never need (executables, CAD, fonts for a book intake) reduces the attack surface and the memory that loaded parsers hold. Excluding them is cheaper than catching their failures.
-
Fail fast at startup with the three
load...calls and one sample document per important format, as described under strict validation, so a typo in a parser block does not wait for the first PDF. -
In-process is not isolation. A configuration file does not make in-process parsing safe for untrusted files: use
PipesForkParser, tika-server or Pipes, whose forks read the same file.
Pitfalls and security
-
A
parserslist withoutdefault-parserkeeps only the listed parsers; every other format falls toEmptyParserwith no error. Shown above withmanuscript.docx. -
${NAME}is not${env:NAME}. Only theenv:form is interpolated; the others are passed to the component as literal text, so a fetcher would look for a directory literally named${BOOKSHELF_INBOX}. -
The resolved secrets are in memory and in persistent config stores. Do not log
getConfig().getRootNode(), and do not enable a file-based config store without protecting it. -
Per-request configuration is parser configuration.
allowPerRequestConfig(tika-server) andallowPerRequestConfig/allowComponentManagement(tika-grpc) let a caller set any parser option, including OCR, which starts external processes. Keep them off for untrusted callers; offerpresetsinstead. -
Include filters drop the content unless
tk:contentis listed. -
Library callers must apply the metadata filters themselves; the parser does not.
-
tika-app
-mand-jignored the filter chain in this page’s 4.1.0 run, while-Japplied it. -
toJson()output does not reload in 4.1.0. Keep the hand-written file in version control. -
The converter silently drops every section except
parsers. -
Engines under
parserswork in 4.1.x with a WARN and fail in 4.2.0. -
Single backslashes in Windows paths are a JSON parse error.
-
tika.extras.diris a code directory. Anything in it runs with the process’s privileges; make it read-only for the service account. -
Class names are refused, by design. Do not look for a way around it: register the class with
@TikaComponent.
1.x to 4.x at a glance
| Topic | 1.x (the book, Tika in Action) | 2.x | 3.x | 4.x |
|---|---|---|---|---|
File |
|
|
|
|
Java entry |
|
|
|
|
Component reference |
class name |
class name |
class name |
registered component name; class names refused |
Defaults plus changes |
|
Same |
Same |
|
Parameters |
not checked |
not checked |
|
plain JSON properties, strictly validated |
Limits |
in code ( |
|
|
|
Finding the file |
not checked |
not checked |
|
explicit path only (no reference found in the 4.1.0 jars checked) |
Secrets |
not checked |
not checked |
not checked |
|
Registration |
|
|
|
|
Migration help |
n/a |
n/a |
n/a |
|
The 3.x cells rest on the 3.3.2 configuration page and the 3.3.2 sources. The 1.x and 2.x cells that are filled in
rest on the same sources and the book, and "not checked" means this page did not look at those releases. The
AutoDetectParserConfig "since 2.1.1" note is from its Javadoc. Tika in Action predates JSON configuration entirely;
its chapter 11 (extending Tika) registers custom parsers through META-INF/services, which @TikaComponent now
generates for you.
Source: Migrating to Tika 4.x, Apache Tika 3.3.2, Configuring Tika and Tika in Action, chapter 11.
Checklist
-
Depend on
tika-serializationand load the file withTikaLoader.load(Path)once, at startup. -
Keep
default-parser(anddefault-detector) in the lists unless you want only what you name; exclude parsers you never need by component name. -
Copy component names from
--list-parser-namesor from the load error, never guess them; never use class names. -
Put limits in
parse-context:output-limits,embedded-limits,timeout-limits, andexception-reportingfor services. -
Call
loadAutoDetectParser(),loadParseContext()andloadMetadataFilters()during startup, and parse one sample per important format in CI after config changes. -
Use one
ParseContextper parse, fromloadParseContext(). -
Apply
loadMetadataFilters()yourself in library code, and listtk:contentin include filters. -
Reference secrets as
${env:NAME}; never write them into the file, never log the resolved JSON. -
Use forward slashes in Windows paths.
-
Treat
tika.extras.diras a trusted code location. -
Register custom components with
@TikaComponentand thetika-annotation-processor. -
Migrating: convert
parserswith--convert-config-xml-to-json, migrate every other section by hand, move OCR engines toenginesandtext-recognizers, switch metadata keys totk:names, and replaceTikaConfigwithTikaLoader.