Skip to content

Commit e9d8d31

Browse files
authored
TIKA-4545 -- propagate annotation through parsers and improve TikaLoader (#2404)
* TIKA-4545 -- baseline improvements to support integration Generated-by: Claude Sonnet 4.5 (claude-sonnet-4-5-20250929) Significant design and implementation on ConfigLoader with Claude Other commits were fully human or with light assist
1 parent 2dbadc6 commit e9d8d31

170 files changed

Lines changed: 1889 additions & 547 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.

tika-annotation-processor/pom.xml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,6 @@
3737
</description>
3838
<url>https://tika.apache.org</url>
3939

40-
<dependencies>
41-
<dependency>
42-
<groupId>${project.groupId}</groupId>
43-
<artifactId>tika-core</artifactId>
44-
<version>${project.version}</version>
45-
<scope>provided</scope>
46-
</dependency>
47-
</dependencies>
48-
4940
<build>
5041
<plugins>
5142
<plugin>

tika-annotation-processor/src/main/java/org/apache/tika/annotation/TikaComponentProcessor.java

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.io.IOException;
2020
import java.io.Writer;
2121
import java.util.ArrayList;
22+
import java.util.Collections;
2223
import java.util.HashMap;
2324
import java.util.LinkedHashMap;
2425
import java.util.LinkedHashSet;
@@ -54,7 +55,7 @@
5455
* to avoid generating SPI files for utility interfaces like Serializable, Initializable, etc.
5556
*/
5657
@SupportedAnnotationTypes("org.apache.tika.config.TikaComponent")
57-
@SupportedSourceVersion(SourceVersion.RELEASE_11)
58+
@SupportedSourceVersion(SourceVersion.RELEASE_17)
5859
public class TikaComponentProcessor extends AbstractProcessor {
5960

6061
/**
@@ -145,7 +146,7 @@ private void processComponent(TypeElement element) {
145146
.add(className);
146147
}
147148

148-
// Always add to index files (regardless of SPI setting)
149+
// Always add to index files for name-based lookup, regardless of spi value
149150
String indexFileName = SERVICE_INTERFACES.get(serviceInterface);
150151
if (indexFileName != null) {
151152
Map<String, String> index = indexFiles.computeIfAbsent(indexFileName,
@@ -219,14 +220,20 @@ private void writeServiceFiles() {
219220
String serviceInterface = entry.getKey();
220221
Set<String> implementations = entry.getValue();
221222

223+
// Sort implementations alphabetically for deterministic output
224+
List<String> sortedImplementations = new ArrayList<>(implementations);
225+
Collections.sort(sortedImplementations);
226+
222227
try {
223228
FileObject file = filer.createResource(StandardLocation.CLASS_OUTPUT, "",
224229
"META-INF/services/" + serviceInterface);
225230

226231
try (Writer writer = file.openWriter()) {
232+
writeApacheLicenseHeader(writer);
233+
writer.write("\n\n");
227234
writer.write("# Generated by TikaComponentProcessor\n");
228235
writer.write("# Do not edit manually\n");
229-
for (String impl : implementations) {
236+
for (String impl : sortedImplementations) {
230237
writer.write(impl);
231238
writer.write("\n");
232239
}
@@ -256,6 +263,7 @@ private void writeIndexFiles() {
256263
"META-INF/tika/" + fileName + ".idx");
257264

258265
try (Writer writer = file.openWriter()) {
266+
writeApacheLicenseHeader(writer);
259267
writer.write("# Generated by TikaComponentProcessor\n");
260268
writer.write("# Do not edit manually\n");
261269
writer.write("# Format: component-name=fully.qualified.ClassName\n");
@@ -277,4 +285,28 @@ private void writeIndexFiles() {
277285
}
278286
}
279287
}
288+
289+
/**
290+
* Writes the Apache License 2.0 header to a file.
291+
*/
292+
private void writeApacheLicenseHeader(Writer writer) throws IOException {
293+
String header = """
294+
# Licensed to the Apache Software Foundation (ASF) under one or more
295+
# contributor license agreements. See the NOTICE file distributed with
296+
# this work for additional information regarding copyright ownership.
297+
# The ASF licenses this file to You under the Apache License, Version 2.0
298+
# (the "License"); you may not use this file except in compliance with
299+
# the License. You may obtain a copy of the License at
300+
#
301+
# http://www.apache.org/licenses/LICENSE-2.0
302+
#
303+
# Unless required by applicable law or agreed to in writing, software
304+
# distributed under the License is distributed on an "AS IS" BASIS,
305+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
306+
# See the License for the specific language governing permissions and
307+
# limitations under the License.
308+
309+
""";
310+
writer.write(header);
311+
}
280312
}

tika-core/src/main/java/org/apache/tika/config/TikaComponent.java renamed to tika-annotation-processor/src/main/java/org/apache/tika/config/TikaComponent.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
* <li>Component index files (META-INF/tika/{type}.idx) for name-based lookup</li>
3535
* </ul>
3636
*
37+
* <p>This annotation is only used at compile time by the annotation processor.
38+
* It is retained in .class files for tooling but not loaded by the runtime JVM.
39+
*
3740
* <p>Example usage:
3841
* <pre>
3942
* {@code @TikaComponent}
@@ -54,7 +57,7 @@
5457
*
5558
* @since 3.1.0
5659
*/
57-
@Retention(RetentionPolicy.RUNTIME)
60+
@Retention(RetentionPolicy.CLASS)
5861
@Target(ElementType.TYPE)
5962
public @interface TikaComponent {
6063

tika-core/pom.xml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@
6464
<scope>provided</scope>
6565
</dependency>
6666

67+
<!-- Annotation processor - contains @TikaComponent and ensures build order.
68+
"provided" because it is only used at compile time -->
69+
<dependency>
70+
<groupId>org.apache.tika</groupId>
71+
<artifactId>tika-annotation-processor</artifactId>
72+
<version>${project.version}</version>
73+
<scope>provided</scope>
74+
</dependency>
75+
6776
<!-- Test dependencies -->
6877
<dependency>
6978
<groupId>com.google.guava</groupId>
@@ -130,6 +139,19 @@
130139
</execution>
131140
</executions>
132141
</plugin>
142+
<plugin>
143+
<groupId>org.apache.maven.plugins</groupId>
144+
<artifactId>maven-compiler-plugin</artifactId>
145+
<configuration>
146+
<annotationProcessorPaths>
147+
<path>
148+
<groupId>org.apache.tika</groupId>
149+
<artifactId>tika-annotation-processor</artifactId>
150+
<version>${project.version}</version>
151+
</path>
152+
</annotationProcessorPaths>
153+
</configuration>
154+
</plugin>
133155
<plugin>
134156
<groupId>org.apache.felix</groupId>
135157
<artifactId>maven-bundle-plugin</artifactId>

tika-core/src/main/java/org/apache/tika/parser/AutoDetectParser.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,16 @@ public AutoDetectParser(Detector detector, Parser... parsers) {
9090
setAutoDetectParserConfig(AutoDetectParserConfig.DEFAULT);
9191
}
9292

93+
public AutoDetectParser(CompositeParser parser, Detector detector, AutoDetectParserConfig autoDetectParserConfig) {
94+
super(parser);
95+
setDetector(detector);
96+
setAutoDetectParserConfig(autoDetectParserConfig);
97+
}
98+
99+
public static Parser build(CompositeParser parser, Detector detector, AutoDetectParserConfig autoDetectParserConfig) {
100+
return new AutoDetectParser(parser, detector, autoDetectParserConfig);
101+
}
102+
93103
public AutoDetectParser(TikaConfig config) {
94104
super(config.getMediaTypeRegistry(), getParser(config));
95105
setFallback(buildFallbackParser(config));

tika-core/src/main/java/org/apache/tika/parser/CompositeParser.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ public CompositeParser() {
9797
this(new MediaTypeRegistry());
9898
}
9999

100+
public CompositeParser(CompositeParser compositeParser) {
101+
this(compositeParser.registry, compositeParser);
102+
}
103+
100104
public Map<MediaType, Parser> getParsers(ParseContext context) {
101105
Map<MediaType, Parser> map = new HashMap<>();
102106
for (Parser parser : parsers) {

tika-parsers/pom.xml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@
4141
</modules>
4242

4343
<dependencies>
44+
<!-- Annotation processor - contains @TikaComponent and ensures build order.
45+
"provided" because it is only used at compile time -->
46+
<dependency>
47+
<groupId>org.apache.tika</groupId>
48+
<artifactId>tika-annotation-processor</artifactId>
49+
<version>${project.version}</version>
50+
<scope>provided</scope>
51+
</dependency>
4452
<!-- test dependencies -->
4553
<dependency>
4654
<groupId>org.junit.jupiter</groupId>
@@ -93,6 +101,24 @@
93101
</execution>
94102
</executions>
95103
</plugin>
104+
<plugin>
105+
<groupId>org.apache.maven.plugins</groupId>
106+
<artifactId>maven-compiler-plugin</artifactId>
107+
<configuration>
108+
<annotationProcessorPaths>
109+
<path>
110+
<groupId>org.apache.tika</groupId>
111+
<artifactId>tika-annotation-processor</artifactId>
112+
<version>${project.version}</version>
113+
</path>
114+
<path>
115+
<groupId>org.apache.tika</groupId>
116+
<artifactId>tika-core</artifactId>
117+
<version>${project.version}</version>
118+
</path>
119+
</annotationProcessorPaths>
120+
</configuration>
121+
</plugin>
96122
<plugin>
97123
<groupId>org.apache.rat</groupId>
98124
<artifactId>apache-rat-plugin</artifactId>

tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/envi/EnviHeaderParser.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.xml.sax.ContentHandler;
3232
import org.xml.sax.SAXException;
3333

34+
import org.apache.tika.config.TikaComponent;
3435
import org.apache.tika.detect.AutoDetectReader;
3536
import org.apache.tika.detect.EncodingDetector;
3637
import org.apache.tika.exception.TikaException;
@@ -40,6 +41,7 @@
4041
import org.apache.tika.parser.ParseContext;
4142
import org.apache.tika.sax.XHTMLContentHandler;
4243

44+
@TikaComponent
4345
public class EnviHeaderParser extends AbstractEncodingDetectorParser {
4446

4547
public static final String ENVI_MIME_TYPE = "application/envi.hdr";

tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/gdal/GDALParser.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.xml.sax.SAXException;
4242

4343
import org.apache.tika.config.Field;
44+
import org.apache.tika.config.TikaComponent;
4445
import org.apache.tika.config.TikaTaskTimeout;
4546
import org.apache.tika.exception.TikaException;
4647
import org.apache.tika.io.TemporaryResources;
@@ -74,6 +75,7 @@
7475
* {@link ContentHandler} in the
7576
* {@link #parse(InputStream, ContentHandler, Metadata, ParseContext)} method.
7677
*/
78+
@TikaComponent
7779
public class GDALParser implements Parser {
7880

7981
private static final long serialVersionUID = -3869130527323941401L;

tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/geoinfo/GeographicInformationParser.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import org.xml.sax.ContentHandler;
6262
import org.xml.sax.SAXException;
6363

64+
import org.apache.tika.config.TikaComponent;
6465
import org.apache.tika.exception.TikaException;
6566
import org.apache.tika.io.TemporaryResources;
6667
import org.apache.tika.io.TikaInputStream;
@@ -71,7 +72,7 @@
7172
import org.apache.tika.sax.XHTMLContentHandler;
7273
import org.apache.tika.utils.DateUtils;
7374

74-
75+
@TikaComponent
7576
public class GeographicInformationParser implements Parser {
7677

7778
public static final String geoInfoType = "text/iso19139+xml";

0 commit comments

Comments
 (0)