You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tika.apache.org by ta...@apache.org on 2025/11/25 20:46:36 UTC

(tika) branch main updated: TIKA-4545 -- propagate annotation through parsers and improve TikaLoader (#2404)

This is an automated email from the ASF dual-hosted git repository.

tallison pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tika.git


The following commit(s) were added to refs/heads/main by this push:
     new e9d8d3182 TIKA-4545 -- propagate annotation through parsers and improve TikaLoader (#2404)
e9d8d3182 is described below

commit e9d8d31821e044dbb7fd68152d8a3c072f1336b7
Author: Tim Allison <ta...@apache.org>
AuthorDate: Tue Nov 25 15:46:31 2025 -0500

    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
---
 tika-annotation-processor/pom.xml                  |   9 -
 .../tika/annotation/TikaComponentProcessor.java    |  38 +-
 .../java/org/apache/tika/config/TikaComponent.java |   5 +-
 tika-core/pom.xml                                  |  22 +
 .../org/apache/tika/parser/AutoDetectParser.java   |  10 +
 .../org/apache/tika/parser/CompositeParser.java    |   4 +
 tika-parsers/pom.xml                               |  26 +
 .../apache/tika/parser/envi/EnviHeaderParser.java  |   2 +
 .../org/apache/tika/parser/gdal/GDALParser.java    |   2 +
 .../geoinfo/GeographicInformationParser.java       |   3 +-
 .../org/apache/tika/parser/grib/GribParser.java    |   2 +
 .../java/org/apache/tika/parser/hdf/HDFParser.java |   2 +
 .../apache/tika/parser/isatab/ISArchiveParser.java |   2 +
 .../apache/tika/parser/netcdf/NetCDFParser.java    |   2 +
 .../services/org.apache.tika.parser.Parser         |  23 -
 .../apache/tika/parser/geopkg/GeoPkgParser.java    |   2 +
 .../apache/tika/parser/sqlite3/SQLite3Parser.java  |   2 +
 .../services/org.apache.tika.parser.Parser         |  17 -
 .../apache/tika/parser/geo/topic/GeoParser.java    |   2 +
 .../tika/parser/journal/GrobidRESTParser.java      |   2 +
 .../apache/tika/parser/journal/JournalParser.java  |   2 +
 .../parser/sentiment/SentimentAnalysisParser.java  |   2 +
 .../services/org.apache.tika.parser.Parser         |  18 -
 .../parser/transcribe/aws/AmazonTranscribe.java    |   2 +
 .../apache/tika/detect/apple/BPListDetector.java   |   2 +
 .../tika/parser/apple/AppleSingleFileParser.java   |   2 +
 .../org/apache/tika/parser/apple/PListParser.java  |   2 +
 .../tika/parser/iwork/IWorkPackageParser.java      |   2 +
 .../parser/iwork/iwana/IWork13PackageParser.java   |   2 +
 .../parser/iwork/iwana/IWork18PackageParser.java   |   2 +
 .../services/org.apache.tika.detect.Detector       |  16 -
 .../services/org.apache.tika.parser.Parser         |  22 -
 .../org/apache/tika/detect/MatroskaDetector.java   |   2 +
 .../org/apache/tika/parser/audio/AudioParser.java  |   2 +
 .../org/apache/tika/parser/audio/MidiParser.java   |   2 +
 .../java/org/apache/tika/parser/mp3/Mp3Parser.java |   2 +
 .../java/org/apache/tika/parser/mp4/MP4Parser.java |   2 +
 .../org/apache/tika/parser/video/FLVParser.java    |   2 +
 .../services/org.apache.tika.detect.Detector       |  16 -
 .../services/org.apache.tika.parser.Parser         |  22 -
 .../org/apache/tika/parser/dgn/DGN8Parser.java     |   2 +
 .../java/org/apache/tika/parser/dwg/DWGParser.java |   2 +
 .../org/apache/tika/parser/dwg/DWGReadParser.java  |   3 +-
 .../java/org/apache/tika/parser/prt/PRTParser.java |   3 +-
 .../services/org.apache.tika.parser.Parser         |  19 -
 .../org/apache/tika/parser/asm/ClassParser.java    |   2 +
 .../apache/tika/parser/code/SourceCodeParser.java  |   2 +
 .../tika/parser/executable/ExecutableParser.java   |   2 +
 .../executable/UniversalExecutableParser.java      |   2 +
 .../java/org/apache/tika/parser/mat/MatParser.java |   3 +-
 .../org/apache/tika/parser/sas/SAS7BDATParser.java |   2 +
 .../services/org.apache.tika.parser.Parser         |  22 -
 .../org/apache/tika/parser/crypto/Pkcs7Parser.java |   2 +
 .../org/apache/tika/parser/crypto/TSDParser.java   |   2 +
 .../services/org.apache.tika.parser.Parser         |  17 -
 .../tika/parser/font/AdobeFontMetricParser.java    |   2 +
 .../apache/tika/parser/font/TrueTypeParser.java    |   2 +
 .../services/org.apache.tika.parser.Parser         |  18 -
 .../tika/parser/html/HtmlEncodingDetector.java     |   2 +
 .../org/apache/tika/parser/html/JSoupParser.java   |   2 +
 .../StandardHtmlEncodingDetector.java              |   2 +
 .../org.apache.tika.detect.EncodingDetector        |   6 +-
 .../services/org.apache.tika.parser.Parser         |  15 -
 .../org/apache/tika/parser/image/BPGParser.java    |   2 +
 .../org/apache/tika/parser/image/HeifParser.java   |   2 +
 .../org/apache/tika/parser/image/ICNSParser.java   |   2 +
 .../org/apache/tika/parser/image/ImageParser.java  |   2 +
 .../org/apache/tika/parser/image/JXLParser.java    |   2 +
 .../org/apache/tika/parser/image/JpegParser.java   |   2 +
 .../org/apache/tika/parser/image/PSDParser.java    |   2 +
 .../org/apache/tika/parser/image/TiffParser.java   |   2 +
 .../org/apache/tika/parser/image/WebPParser.java   |   3 +-
 .../services/org.apache.tika.parser.Parser         |  26 -
 .../org/apache/tika/parser/mail/RFC822Parser.java  |   2 +
 .../org/apache/tika/parser/mbox/MboxParser.java    |   2 +
 .../services/org.apache.tika.parser.Parser         |  19 -
 .../detect/microsoft/POIFSContainerDetector.java   |   2 +
 .../apache/tika/parser/microsoft/EMFParser.java    |   2 +
 .../tika/parser/microsoft/JackcessParser.java      |   2 +
 .../tika/parser/microsoft/MSOwnerFileParser.java   |   2 +
 .../apache/tika/parser/microsoft/OfficeParser.java |   2 +
 .../tika/parser/microsoft/OldExcelParser.java      |   2 +
 .../apache/tika/parser/microsoft/TNEFParser.java   |   2 +
 .../apache/tika/parser/microsoft/WMFParser.java    |   2 +
 .../microsoft/activemime/ActiveMimeParser.java     |   2 +
 .../tika/parser/microsoft/chm/ChmParser.java       |   2 +
 .../tika/parser/microsoft/libpst/LibPstParser.java |   2 +
 .../parser/microsoft/onenote/OneNoteParser.java    |   2 +
 .../tika/parser/microsoft/ooxml/OOXMLParser.java   |   2 +
 .../ooxml/xwpf/ml2006/Word2006MLParser.java        |   3 +-
 .../parser/microsoft/pst/OutlookPSTParser.java     |   2 +
 .../parser/microsoft/pst/PSTMailItemParser.java    |   2 +
 .../tika/parser/microsoft/rtf/RTFParser.java       |   2 +
 .../parser/microsoft/xml/SpreadsheetMLParser.java  |   2 +
 .../tika/parser/microsoft/xml/WordMLParser.java    |   2 +
 .../services/org.apache.tika.detect.Detector       |  16 -
 .../services/org.apache.tika.parser.Parser         |  31 -
 .../apache/tika/detect/ole/MiscOLEDetector.java    |   2 +
 .../java/org/apache/tika/parser/dbf/DBFParser.java |   2 +
 .../java/org/apache/tika/parser/dif/DIFParser.java |   2 +
 .../org/apache/tika/parser/epub/EpubParser.java    |   2 +
 .../org/apache/tika/parser/hwp/HwpV5Parser.java    |   2 +
 .../apache/tika/parser/indesign/IDMLParser.java    |   2 +
 .../java/org/apache/tika/parser/mif/MIFParser.java |   2 +
 .../tika/parser/odf/FlatOpenDocumentParser.java    |   2 +
 .../apache/tika/parser/odf/OpenDocumentParser.java |   2 +
 .../tika/parser/wordperfect/QuattroProParser.java  |   2 +
 .../tika/parser/wordperfect/WordPerfectParser.java |   2 +
 .../services/org.apache.tika.detect.Detector       |  16 -
 .../services/org.apache.tika.parser.Parser         |  24 -
 .../org/apache/tika/parser/feed/FeedParser.java    |   2 +
 .../apache/tika/parser/iptc/IptcAnpaParser.java    |   2 +
 .../services/org.apache.tika.parser.Parser         |  17 -
 .../apache/tika/parser/ocr/TesseractOCRParser.java |   2 +
 .../services/org.apache.tika.parser.Parser         |  15 -
 .../java/org/apache/tika/parser/pdf/PDFParser.java |   2 +
 .../services/org.apache.tika.parser.Parser         |  15 -
 .../detect/gzip/GZipSpecializationDetector.java    |   2 +
 .../apache/tika/parser/pkg/CompressorParser.java   |   2 +
 .../org/apache/tika/parser/pkg/PackageParser.java  |   2 +
 .../java/org/apache/tika/parser/pkg/RarParser.java |   2 +
 .../org/apache/tika/parser/pkg/UnrarParser.java    |   2 +
 .../services/org.apache.tika.detect.Detector       |  16 -
 .../services/org.apache.tika.parser.Parser         |  18 -
 .../apache/tika/parser/csv/TextAndCSVParser.java   |   2 +
 .../tika/parser/strings/Latin1StringsParser.java   |   2 +
 .../apache/tika/parser/strings/StringsParser.java  |   2 +
 .../org/apache/tika/parser/txt/BOMDetector.java    |   5 +
 .../tika/parser/txt/Icu4jEncodingDetector.java     |   2 +
 .../java/org/apache/tika/parser/txt/TXTParser.java |   2 +
 .../tika/parser/txt/UniversalEncodingDetector.java |   2 +
 .../org.apache.tika.detect.EncodingDetector        |   4 +
 .../services/org.apache.tika.parser.Parser         |  22 -
 .../org/apache/tika/parser/http/HttpParser.java    |   2 +
 .../org/apache/tika/parser/wacz/WACZParser.java    |   2 +
 .../org/apache/tika/parser/warc/WARCParser.java    |   2 +
 .../services/org.apache.tika.parser.Parser         |  19 -
 .../java/org/apache/tika/parser/tmx/TMXParser.java |   2 +
 .../apache/tika/parser/xliff/XLIFF12Parser.java    |   2 +
 .../org/apache/tika/parser/xliff/XLZParser.java    |   2 +
 .../org/apache/tika/parser/xml/DcXMLParser.java    |   2 +
 .../apache/tika/parser/xml/FictionBookParser.java  |   2 +
 .../java/org/apache/tika/parser/xml/XMLParser.java |   2 +
 .../org/apache/tika/parser/xml/XMLProfiler.java    |   2 +
 .../services/org.apache.tika.parser.Parser         |  22 -
 .../detect/zip/DefaultZipContainerDetector.java    |   2 +
 .../services/org.apache.tika.detect.Detector       |  16 -
 ...org.apache.tika.detect.zip.ZipContainerDetector |   2 +
 .../tika-parsers-standard-package/pom.xml          |  11 +
 .../config/ComponentRegistryIntegrationTest.java   | 299 ++++++++++
 .../test/resources/configs/test-dc-xml-parser.json |   5 +
 .../resources/configs/test-default-parser.json     |   5 +
 .../configs/test-default-with-exclusions.json      |   9 +
 .../src/test/resources/configs/test-detectors.json |   5 +
 .../resources/configs/test-specific-parsers.json   |   7 +
 tika-serialization/pom.xml                         |  20 +
 .../apache/tika/config/loader/ConfigLoader.java    | 349 +++++++++++
 .../tika/config/loader/KebabCaseConverter.java     |  72 +++
 .../apache/tika/config/loader/ParserLoader.java    |   2 +
 .../apache/tika/config/loader/TikaJsonConfig.java  |  21 +-
 .../org/apache/tika/config/loader/TikaLoader.java  |  35 ++
 .../tika/config/loader/ConfigLoaderTest.java       | 646 +++++++++++++++++++++
 .../services/org.apache.tika.parser.Parser         |   4 -
 .../src/test/resources/META-INF/tika/parsers.idx   |   5 -
 .../test/resources/configs/test-config-loader.json |  32 +
 .../resources/configs/test-interface-no-type.json  |   6 +
 .../test/resources/configs/test-invalid-class.json |   3 +
 .../resources/configs/test-partial-config.json     |  15 +
 .../resources/configs/test-unexpected-field.json   |   8 +
 .../test/resources/configs/test-wrong-type.json    |   3 +
 170 files changed, 1889 insertions(+), 547 deletions(-)

diff --git a/tika-annotation-processor/pom.xml b/tika-annotation-processor/pom.xml
index 9c93eb9ac..459dc4ea6 100644
--- a/tika-annotation-processor/pom.xml
+++ b/tika-annotation-processor/pom.xml
@@ -37,15 +37,6 @@
   </description>
   <url>https://tika.apache.org</url>
 
-  <dependencies>
-    <dependency>
-      <groupId>${project.groupId}</groupId>
-      <artifactId>tika-core</artifactId>
-      <version>${project.version}</version>
-      <scope>provided</scope>
-    </dependency>
-  </dependencies>
-
   <build>
     <plugins>
       <plugin>
diff --git a/tika-annotation-processor/src/main/java/org/apache/tika/annotation/TikaComponentProcessor.java b/tika-annotation-processor/src/main/java/org/apache/tika/annotation/TikaComponentProcessor.java
index f37ac1e3f..a9a2254c9 100644
--- a/tika-annotation-processor/src/main/java/org/apache/tika/annotation/TikaComponentProcessor.java
+++ b/tika-annotation-processor/src/main/java/org/apache/tika/annotation/TikaComponentProcessor.java
@@ -19,6 +19,7 @@ package org.apache.tika.annotation;
 import java.io.IOException;
 import java.io.Writer;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
@@ -54,7 +55,7 @@ import org.apache.tika.config.TikaComponent;
  * to avoid generating SPI files for utility interfaces like Serializable, Initializable, etc.
  */
 @SupportedAnnotationTypes("org.apache.tika.config.TikaComponent")
-@SupportedSourceVersion(SourceVersion.RELEASE_11)
+@SupportedSourceVersion(SourceVersion.RELEASE_17)
 public class TikaComponentProcessor extends AbstractProcessor {
 
     /**
@@ -145,7 +146,7 @@ public class TikaComponentProcessor extends AbstractProcessor {
                         .add(className);
             }
 
-            // Always add to index files (regardless of SPI setting)
+            // Always add to index files for name-based lookup, regardless of spi value
             String indexFileName = SERVICE_INTERFACES.get(serviceInterface);
             if (indexFileName != null) {
                 Map<String, String> index = indexFiles.computeIfAbsent(indexFileName,
@@ -219,14 +220,20 @@ public class TikaComponentProcessor extends AbstractProcessor {
             String serviceInterface = entry.getKey();
             Set<String> implementations = entry.getValue();
 
+            // Sort implementations alphabetically for deterministic output
+            List<String> sortedImplementations = new ArrayList<>(implementations);
+            Collections.sort(sortedImplementations);
+
             try {
                 FileObject file = filer.createResource(StandardLocation.CLASS_OUTPUT, "",
                         "META-INF/services/" + serviceInterface);
 
                 try (Writer writer = file.openWriter()) {
+                    writeApacheLicenseHeader(writer);
+                    writer.write("\n\n");
                     writer.write("# Generated by TikaComponentProcessor\n");
                     writer.write("# Do not edit manually\n");
-                    for (String impl : implementations) {
+                    for (String impl : sortedImplementations) {
                         writer.write(impl);
                         writer.write("\n");
                     }
@@ -256,6 +263,7 @@ public class TikaComponentProcessor extends AbstractProcessor {
                         "META-INF/tika/" + fileName + ".idx");
 
                 try (Writer writer = file.openWriter()) {
+                    writeApacheLicenseHeader(writer);
                     writer.write("# Generated by TikaComponentProcessor\n");
                     writer.write("# Do not edit manually\n");
                     writer.write("# Format: component-name=fully.qualified.ClassName\n");
@@ -277,4 +285,28 @@ public class TikaComponentProcessor extends AbstractProcessor {
             }
         }
     }
+
+    /**
+     * Writes the Apache License 2.0 header to a file.
+     */
+    private void writeApacheLicenseHeader(Writer writer) throws IOException {
+        String header = """
+                #  Licensed to the Apache Software Foundation (ASF) under one or more
+                #  contributor license agreements.  See the NOTICE file distributed with
+                #  this work for additional information regarding copyright ownership.
+                #  The ASF licenses this file to You under the Apache License, Version 2.0
+                #  (the "License"); you may not use this file except in compliance with
+                #  the License.  You may obtain a copy of the License at
+                #
+                #       http://www.apache.org/licenses/LICENSE-2.0
+                #
+                #  Unless required by applicable law or agreed to in writing, software
+                #  distributed under the License is distributed on an "AS IS" BASIS,
+                #  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+                #  See the License for the specific language governing permissions and
+                #  limitations under the License.
+
+                """;
+        writer.write(header);
+    }
 }
diff --git a/tika-core/src/main/java/org/apache/tika/config/TikaComponent.java b/tika-annotation-processor/src/main/java/org/apache/tika/config/TikaComponent.java
similarity index 93%
rename from tika-core/src/main/java/org/apache/tika/config/TikaComponent.java
rename to tika-annotation-processor/src/main/java/org/apache/tika/config/TikaComponent.java
index 8696ab2db..6632bdeb7 100644
--- a/tika-core/src/main/java/org/apache/tika/config/TikaComponent.java
+++ b/tika-annotation-processor/src/main/java/org/apache/tika/config/TikaComponent.java
@@ -34,6 +34,9 @@ import java.lang.annotation.Target;
  *   <li>Component index files (META-INF/tika/{type}.idx) for name-based lookup</li>
  * </ul>
  *
+ * <p>This annotation is only used at compile time by the annotation processor.
+ * It is retained in .class files for tooling but not loaded by the runtime JVM.
+ *
  * <p>Example usage:
  * <pre>
  * {@code @TikaComponent}
@@ -54,7 +57,7 @@ import java.lang.annotation.Target;
  *
  * @since 3.1.0
  */
-@Retention(RetentionPolicy.RUNTIME)
+@Retention(RetentionPolicy.CLASS)
 @Target(ElementType.TYPE)
 public @interface TikaComponent {
 
diff --git a/tika-core/pom.xml b/tika-core/pom.xml
index e0c408e34..5a12c4e3a 100644
--- a/tika-core/pom.xml
+++ b/tika-core/pom.xml
@@ -64,6 +64,15 @@
       <scope>provided</scope>
     </dependency>
 
+    <!-- Annotation processor - contains @TikaComponent and ensures build order.
+         "provided" because it is only used at compile time -->
+    <dependency>
+      <groupId>org.apache.tika</groupId>
+      <artifactId>tika-annotation-processor</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+
     <!-- Test dependencies -->
     <dependency>
       <groupId>com.google.guava</groupId>
@@ -130,6 +139,19 @@
           </execution>
         </executions>
       </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <annotationProcessorPaths>
+            <path>
+              <groupId>org.apache.tika</groupId>
+              <artifactId>tika-annotation-processor</artifactId>
+              <version>${project.version}</version>
+            </path>
+          </annotationProcessorPaths>
+        </configuration>
+      </plugin>
       <plugin>
         <groupId>org.apache.felix</groupId>
         <artifactId>maven-bundle-plugin</artifactId>
diff --git a/tika-core/src/main/java/org/apache/tika/parser/AutoDetectParser.java b/tika-core/src/main/java/org/apache/tika/parser/AutoDetectParser.java
index 86eae692a..7fd9e0b0b 100644
--- a/tika-core/src/main/java/org/apache/tika/parser/AutoDetectParser.java
+++ b/tika-core/src/main/java/org/apache/tika/parser/AutoDetectParser.java
@@ -90,6 +90,16 @@ public class AutoDetectParser extends CompositeParser {
         setAutoDetectParserConfig(AutoDetectParserConfig.DEFAULT);
     }
 
+    public AutoDetectParser(CompositeParser parser, Detector detector, AutoDetectParserConfig autoDetectParserConfig) {
+        super(parser);
+        setDetector(detector);
+        setAutoDetectParserConfig(autoDetectParserConfig);
+    }
+
+    public static Parser build(CompositeParser parser, Detector detector, AutoDetectParserConfig autoDetectParserConfig) {
+        return new AutoDetectParser(parser, detector, autoDetectParserConfig);
+    }
+
     public AutoDetectParser(TikaConfig config) {
         super(config.getMediaTypeRegistry(), getParser(config));
         setFallback(buildFallbackParser(config));
diff --git a/tika-core/src/main/java/org/apache/tika/parser/CompositeParser.java b/tika-core/src/main/java/org/apache/tika/parser/CompositeParser.java
index 3b50b4da7..b3aaebf7a 100644
--- a/tika-core/src/main/java/org/apache/tika/parser/CompositeParser.java
+++ b/tika-core/src/main/java/org/apache/tika/parser/CompositeParser.java
@@ -97,6 +97,10 @@ public class CompositeParser implements Parser {
         this(new MediaTypeRegistry());
     }
 
+    public CompositeParser(CompositeParser compositeParser) {
+        this(compositeParser.registry, compositeParser);
+    }
+
     public Map<MediaType, Parser> getParsers(ParseContext context) {
         Map<MediaType, Parser> map = new HashMap<>();
         for (Parser parser : parsers) {
diff --git a/tika-parsers/pom.xml b/tika-parsers/pom.xml
index 0b078ac05..1985a044c 100644
--- a/tika-parsers/pom.xml
+++ b/tika-parsers/pom.xml
@@ -41,6 +41,14 @@
   </modules>
 
   <dependencies>
+    <!-- Annotation processor - contains @TikaComponent and ensures build order.
+     "provided" because it is only used at compile time -->
+    <dependency>
+      <groupId>org.apache.tika</groupId>
+      <artifactId>tika-annotation-processor</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
     <!-- test dependencies -->
     <dependency>
       <groupId>org.junit.jupiter</groupId>
@@ -93,6 +101,24 @@
           </execution>
         </executions>
       </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <annotationProcessorPaths>
+            <path>
+              <groupId>org.apache.tika</groupId>
+              <artifactId>tika-annotation-processor</artifactId>
+              <version>${project.version}</version>
+            </path>
+            <path>
+              <groupId>org.apache.tika</groupId>
+              <artifactId>tika-core</artifactId>
+              <version>${project.version}</version>
+            </path>
+          </annotationProcessorPaths>
+        </configuration>
+      </plugin>
       <plugin>
         <groupId>org.apache.rat</groupId>
         <artifactId>apache-rat-plugin</artifactId>
diff --git a/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/envi/EnviHeaderParser.java b/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/envi/EnviHeaderParser.java
index 483e258d7..abedbb89d 100644
--- a/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/envi/EnviHeaderParser.java
+++ b/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/envi/EnviHeaderParser.java
@@ -31,6 +31,7 @@ import org.slf4j.LoggerFactory;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.AutoDetectReader;
 import org.apache.tika.detect.EncodingDetector;
 import org.apache.tika.exception.TikaException;
@@ -40,6 +41,7 @@ import org.apache.tika.parser.AbstractEncodingDetectorParser;
 import org.apache.tika.parser.ParseContext;
 import org.apache.tika.sax.XHTMLContentHandler;
 
+@TikaComponent
 public class EnviHeaderParser extends AbstractEncodingDetectorParser {
 
     public static final String ENVI_MIME_TYPE = "application/envi.hdr";
diff --git a/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/gdal/GDALParser.java b/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/gdal/GDALParser.java
index 1ba196942..11f0c2862 100644
--- a/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/gdal/GDALParser.java
+++ b/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/gdal/GDALParser.java
@@ -41,6 +41,7 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.config.TikaTaskTimeout;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TemporaryResources;
@@ -74,6 +75,7 @@ import org.apache.tika.utils.ProcessUtils;
  * {@link ContentHandler} in the
  * {@link #parse(InputStream, ContentHandler, Metadata, ParseContext)} method.
  */
+@TikaComponent
 public class GDALParser implements Parser {
 
     private static final long serialVersionUID = -3869130527323941401L;
diff --git a/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/geoinfo/GeographicInformationParser.java b/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/geoinfo/GeographicInformationParser.java
index beabe6593..0af28f924 100644
--- a/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/geoinfo/GeographicInformationParser.java
+++ b/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/geoinfo/GeographicInformationParser.java
@@ -61,6 +61,7 @@ import org.slf4j.LoggerFactory;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TemporaryResources;
 import org.apache.tika.io.TikaInputStream;
@@ -71,7 +72,7 @@ import org.apache.tika.parser.Parser;
 import org.apache.tika.sax.XHTMLContentHandler;
 import org.apache.tika.utils.DateUtils;
 
-
+@TikaComponent
 public class GeographicInformationParser implements Parser {
 
     public static final String geoInfoType = "text/iso19139+xml";
diff --git a/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/grib/GribParser.java b/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/grib/GribParser.java
index b974a481e..60f9e9bee 100644
--- a/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/grib/GribParser.java
+++ b/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/grib/GribParser.java
@@ -34,6 +34,7 @@ import ucar.nc2.NetcdfFile;
 import ucar.nc2.Variable;
 import ucar.nc2.dataset.NetcdfDataset;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.metadata.Property;
@@ -43,6 +44,7 @@ import org.apache.tika.parser.ParseContext;
 import org.apache.tika.parser.Parser;
 import org.apache.tika.sax.XHTMLContentHandler;
 
+@TikaComponent
 public class GribParser implements Parser {
 
     public static final String GRIB_MIME_TYPE = "application/x-grib2";
diff --git a/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/hdf/HDFParser.java b/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/hdf/HDFParser.java
index 14098f3b6..06e163a56 100644
--- a/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/hdf/HDFParser.java
+++ b/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/hdf/HDFParser.java
@@ -32,6 +32,7 @@ import ucar.nc2.Attribute;
 import ucar.nc2.Group;
 import ucar.nc2.NetcdfFile;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.metadata.TikaCoreProperties;
@@ -48,6 +49,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * "http://www.unidata.ucar.edu/software/netcdf-java/formats/FileTypes.html"
  * >this link</a> for more information.
  */
+@TikaComponent
 public class HDFParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/isatab/ISArchiveParser.java b/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/isatab/ISArchiveParser.java
index 6c7975f82..8c0777b68 100644
--- a/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/isatab/ISArchiveParser.java
+++ b/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/isatab/ISArchiveParser.java
@@ -25,6 +25,7 @@ import java.util.Set;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TemporaryResources;
 import org.apache.tika.io.TikaInputStream;
@@ -34,6 +35,7 @@ import org.apache.tika.parser.ParseContext;
 import org.apache.tika.parser.Parser;
 import org.apache.tika.sax.XHTMLContentHandler;
 
+@TikaComponent
 public class ISArchiveParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/netcdf/NetCDFParser.java b/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/netcdf/NetCDFParser.java
index 9f4ef020c..464ea6803 100644
--- a/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/netcdf/NetCDFParser.java
+++ b/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/java/org/apache/tika/parser/netcdf/NetCDFParser.java
@@ -31,6 +31,7 @@ import ucar.nc2.Dimension;
 import ucar.nc2.NetcdfFile;
 import ucar.nc2.Variable;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TemporaryResources;
 import org.apache.tika.io.TikaInputStream;
@@ -49,6 +50,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * href="http://www.unidata.ucar.edu/software/netcdf-java/">NetCDF for Java</a>
  * API.
  */
+@TikaComponent
 public class NetCDFParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index e8f355d3b..000000000
--- a/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,23 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.parser.envi.EnviHeaderParser
-org.apache.tika.parser.hdf.HDFParser
-org.apache.tika.parser.netcdf.NetCDFParser
-org.apache.tika.parser.gdal.GDALParser
-org.apache.tika.parser.grib.GribParser
-org.apache.tika.parser.isatab.ISArchiveParser
-org.apache.tika.parser.geoinfo.GeographicInformationParser
-org.apache.tika.parser.external.CompositeExternalParser
diff --git a/tika-parsers/tika-parsers-extended/tika-parser-sqlite3-module/src/main/java/org/apache/tika/parser/geopkg/GeoPkgParser.java b/tika-parsers/tika-parsers-extended/tika-parser-sqlite3-module/src/main/java/org/apache/tika/parser/geopkg/GeoPkgParser.java
index e157a09c9..358c03c67 100644
--- a/tika-parsers/tika-parsers-extended/tika-parser-sqlite3-module/src/main/java/org/apache/tika/parser/geopkg/GeoPkgParser.java
+++ b/tika-parsers/tika-parsers-extended/tika-parser-sqlite3-module/src/main/java/org/apache/tika/parser/geopkg/GeoPkgParser.java
@@ -31,6 +31,7 @@ import org.xml.sax.SAXException;
 import org.apache.tika.config.Field;
 import org.apache.tika.config.InitializableProblemHandler;
 import org.apache.tika.config.Param;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaConfigException;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
@@ -65,6 +66,7 @@ import org.apache.tika.parser.sqlite3.SQLite3Parser;
  * <p>
  *   Or use an empty list to parse all columns.
  */
+@TikaComponent
 public class GeoPkgParser extends SQLite3Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-extended/tika-parser-sqlite3-module/src/main/java/org/apache/tika/parser/sqlite3/SQLite3Parser.java b/tika-parsers/tika-parsers-extended/tika-parser-sqlite3-module/src/main/java/org/apache/tika/parser/sqlite3/SQLite3Parser.java
index 34aab4d10..174be66ea 100644
--- a/tika-parsers/tika-parsers-extended/tika-parser-sqlite3-module/src/main/java/org/apache/tika/parser/sqlite3/SQLite3Parser.java
+++ b/tika-parsers/tika-parsers-extended/tika-parser-sqlite3-module/src/main/java/org/apache/tika/parser/sqlite3/SQLite3Parser.java
@@ -29,6 +29,7 @@ import org.xml.sax.SAXException;
 import org.apache.tika.config.Initializable;
 import org.apache.tika.config.InitializableProblemHandler;
 import org.apache.tika.config.Param;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaConfigException;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
@@ -53,6 +54,7 @@ import org.apache.tika.parser.Parser;
  * If using a TikaInputStream, make sure to close it to delete the temp file
  * that has to be created.
  */
+@TikaComponent
 public class SQLite3Parser implements Parser, Initializable {
 
     public static final String SQLITE3_PREFIX = "sqlite3:";
diff --git a/tika-parsers/tika-parsers-extended/tika-parser-sqlite3-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-extended/tika-parser-sqlite3-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index 14509c812..000000000
--- a/tika-parsers/tika-parsers-extended/tika-parser-sqlite3-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,17 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.parser.geopkg.GeoPkgParser
-org.apache.tika.parser.sqlite3.SQLite3Parser
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-nlp-module/src/main/java/org/apache/tika/parser/geo/topic/GeoParser.java b/tika-parsers/tika-parsers-ml/tika-parser-nlp-module/src/main/java/org/apache/tika/parser/geo/topic/GeoParser.java
index cfa52e7e2..164874555 100644
--- a/tika-parsers/tika-parsers-ml/tika-parser-nlp-module/src/main/java/org/apache/tika/parser/geo/topic/GeoParser.java
+++ b/tika-parsers/tika-parsers-ml/tika-parser-nlp-module/src/main/java/org/apache/tika/parser/geo/topic/GeoParser.java
@@ -36,6 +36,7 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
@@ -44,6 +45,7 @@ import org.apache.tika.parser.Parser;
 import org.apache.tika.parser.geo.topic.gazetteer.GeoGazetteerClient;
 import org.apache.tika.parser.geo.topic.gazetteer.Location;
 
+@TikaComponent
 public class GeoParser implements Parser {
     private static final long serialVersionUID = -2241391757440215491L;
     private static final Logger LOG = LoggerFactory.getLogger(GeoParser.class);
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-nlp-module/src/main/java/org/apache/tika/parser/journal/GrobidRESTParser.java b/tika-parsers/tika-parsers-ml/tika-parser-nlp-module/src/main/java/org/apache/tika/parser/journal/GrobidRESTParser.java
index 08968989e..c7b9e5ad1 100644
--- a/tika-parsers/tika-parsers-ml/tika-parser-nlp-module/src/main/java/org/apache/tika/parser/journal/GrobidRESTParser.java
+++ b/tika-parsers/tika-parsers-ml/tika-parser-nlp-module/src/main/java/org/apache/tika/parser/journal/GrobidRESTParser.java
@@ -33,10 +33,12 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.xml.sax.ContentHandler;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.parser.ParseContext;
 
+@TikaComponent
 public class GrobidRESTParser {
 
     private static final Logger LOG = LoggerFactory.getLogger(GrobidRESTParser.class);
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-nlp-module/src/main/java/org/apache/tika/parser/journal/JournalParser.java b/tika-parsers/tika-parsers-ml/tika-parser-nlp-module/src/main/java/org/apache/tika/parser/journal/JournalParser.java
index 92f3fffae..c16049d99 100644
--- a/tika-parsers/tika-parsers-ml/tika-parser-nlp-module/src/main/java/org/apache/tika/parser/journal/JournalParser.java
+++ b/tika-parsers/tika-parsers-ml/tika-parser-nlp-module/src/main/java/org/apache/tika/parser/journal/JournalParser.java
@@ -27,6 +27,7 @@ import java.util.Set;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TemporaryResources;
 import org.apache.tika.io.TikaInputStream;
@@ -36,6 +37,7 @@ import org.apache.tika.parser.ParseContext;
 import org.apache.tika.parser.Parser;
 import org.apache.tika.parser.pdf.PDFParser;
 
+@TikaComponent
 public class JournalParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-nlp-module/src/main/java/org/apache/tika/parser/sentiment/SentimentAnalysisParser.java b/tika-parsers/tika-parsers-ml/tika-parser-nlp-module/src/main/java/org/apache/tika/parser/sentiment/SentimentAnalysisParser.java
index c6fd5597c..775b8836e 100644
--- a/tika-parsers/tika-parsers-ml/tika-parser-nlp-module/src/main/java/org/apache/tika/parser/sentiment/SentimentAnalysisParser.java
+++ b/tika-parsers/tika-parsers-ml/tika-parser-nlp-module/src/main/java/org/apache/tika/parser/sentiment/SentimentAnalysisParser.java
@@ -37,6 +37,7 @@ import org.apache.tika.config.Field;
 import org.apache.tika.config.Initializable;
 import org.apache.tika.config.InitializableProblemHandler;
 import org.apache.tika.config.Param;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaConfigException;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
@@ -48,6 +49,7 @@ import org.apache.tika.parser.Parser;
  * This parser classifies documents based on the sentiment of document.
  * The classifier is powered by Apache OpenNLP's Maximum Entropy Classifier
  */
+@TikaComponent(spi = false)
 public class SentimentAnalysisParser implements Parser, Initializable {
 
     public static final String DEF_MODEL =
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-nlp-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-ml/tika-parser-nlp-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index 76fcbfed8..000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-nlp-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,18 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.parser.geo.topic.GeoParser
-org.apache.tika.parser.journal.JournalParser
-org.apache.tika.parser.sentiment.SentimentAnalysisParser
\ No newline at end of file
diff --git a/tika-parsers/tika-parsers-ml/tika-transcribe-aws/src/main/java/org/apache/tika/parser/transcribe/aws/AmazonTranscribe.java b/tika-parsers/tika-parsers-ml/tika-transcribe-aws/src/main/java/org/apache/tika/parser/transcribe/aws/AmazonTranscribe.java
index 2d5215cd6..67a02bf55 100644
--- a/tika-parsers/tika-parsers-ml/tika-transcribe-aws/src/main/java/org/apache/tika/parser/transcribe/aws/AmazonTranscribe.java
+++ b/tika-parsers/tika-parsers-ml/tika-transcribe-aws/src/main/java/org/apache/tika/parser/transcribe/aws/AmazonTranscribe.java
@@ -67,6 +67,7 @@ import org.apache.tika.config.Field;
 import org.apache.tika.config.Initializable;
 import org.apache.tika.config.InitializableProblemHandler;
 import org.apache.tika.config.Param;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaConfigException;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
@@ -88,6 +89,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * @since Tika 2.0
  */
 
+@TikaComponent
 public class AmazonTranscribe implements Parser, Initializable {
     private static final Logger LOG = LoggerFactory.getLogger(AmazonTranscribe.class);
     private TranscribeAsyncClient amazonTranscribeAsync;
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/detect/apple/BPListDetector.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/detect/apple/BPListDetector.java
index bdbd19ccb..35244435f 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/detect/apple/BPListDetector.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/detect/apple/BPListDetector.java
@@ -31,6 +31,7 @@ import com.dd.plist.PropertyListParser;
 import org.apache.commons.io.IOUtils;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.Detector;
 import org.apache.tika.io.TikaInputStream;
 import org.apache.tika.metadata.Metadata;
@@ -45,6 +46,7 @@ import org.apache.tika.mime.MediaType;
  *
  * @since 1.25
  */
+@TikaComponent
 public class BPListDetector implements Detector {
 
     //xml versions
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/apple/AppleSingleFileParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/apple/AppleSingleFileParser.java
index d97ed1ba7..87b1b8b6e 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/apple/AppleSingleFileParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/apple/AppleSingleFileParser.java
@@ -29,6 +29,7 @@ import org.apache.commons.io.IOUtils;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.exception.TikaMemoryLimitException;
 import org.apache.tika.extractor.EmbeddedDocumentExtractor;
@@ -49,6 +50,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * <p>
  * See <a href="http://kaiser-edv.de/documents/AppleSingle_AppleDouble.pdf">spec document</a>.
  */
+@TikaComponent
 public class AppleSingleFileParser implements Parser {
 
     private static final int MAX_FIELD_LENGTH = 1_073_741_824;
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/apple/PListParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/apple/PListParser.java
index e90324ba2..66ff971bd 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/apple/PListParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/apple/PListParser.java
@@ -43,6 +43,7 @@ import com.dd.plist.UID;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.apple.BPListDetector;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.extractor.EmbeddedDocumentExtractor;
@@ -63,6 +64,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  *
  * @since 1.25
  */
+@TikaComponent
 public class PListParser implements Parser {
 
     private static final String ARR = "array";
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/iwork/IWorkPackageParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/iwork/IWorkPackageParser.java
index 87074304f..8af51bce9 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/iwork/IWorkPackageParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/iwork/IWorkPackageParser.java
@@ -35,6 +35,7 @@ import org.apache.commons.io.input.UnsynchronizedByteArrayInputStream;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.XmlRootExtractor;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
@@ -55,6 +56,7 @@ import org.apache.tika.utils.XMLReaderUtils;
  * <li>Numbers format version 1.x. Currently only tested with Numbers version 2.0.x
  * </ol>
  */
+@TikaComponent
 public class IWorkPackageParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/iwork/iwana/IWork13PackageParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/iwork/iwana/IWork13PackageParser.java
index ac65ff587..dc039d476 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/iwork/iwana/IWork13PackageParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/iwork/iwana/IWork13PackageParser.java
@@ -39,6 +39,7 @@ import org.apache.commons.io.IOUtils;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.extractor.EmbeddedDocumentExtractor;
 import org.apache.tika.extractor.EmbeddedDocumentUtil;
@@ -52,6 +53,7 @@ import org.apache.tika.parser.ParseContext;
 import org.apache.tika.parser.Parser;
 import org.apache.tika.sax.XHTMLContentHandler;
 
+@TikaComponent
 public class IWork13PackageParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/iwork/iwana/IWork18PackageParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/iwork/iwana/IWork18PackageParser.java
index 28a699039..d1abc984c 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/iwork/iwana/IWork18PackageParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/iwork/iwana/IWork18PackageParser.java
@@ -31,6 +31,7 @@ import org.apache.commons.compress.archivers.zip.ZipFile;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TikaInputStream;
 import org.apache.tika.metadata.Metadata;
@@ -42,6 +43,7 @@ import org.apache.tika.parser.Parser;
  * For now, this parser isn't even registered.  It contains
  * code that will detect the newer 2018 .keynote, .numbers, .pages files.
  */
+@TikaComponent
 public class IWork18PackageParser implements Parser {
 
     private final static Set<MediaType> supportedTypes = Collections.unmodifiableSet(
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/resources/META-INF/services/org.apache.tika.detect.Detector b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/resources/META-INF/services/org.apache.tika.detect.Detector
deleted file mode 100644
index 0ccb99ec3..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/resources/META-INF/services/org.apache.tika.detect.Detector
+++ /dev/null
@@ -1,16 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.detect.apple.BPListDetector
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index c922b2e33..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,22 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.parser.apple.AppleSingleFileParser
-org.apache.tika.parser.apple.PListParser
-org.apache.tika.parser.iwork.iwana.IWork13PackageParser
-org.apache.tika.parser.iwork.iwana.IWork18PackageParser
-org.apache.tika.parser.iwork.IWorkPackageParser
-
-
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/detect/MatroskaDetector.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/detect/MatroskaDetector.java
index b2b25f606..48b9bedb3 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/detect/MatroskaDetector.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/detect/MatroskaDetector.java
@@ -22,12 +22,14 @@ import java.io.InputStream;
 
 import org.apache.commons.io.IOUtils;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
 
 /**
  * Detector for Matroska (MKV and WEBM) files based on the EBML header.
  */
+@TikaComponent
 public class MatroskaDetector implements Detector {
 
     /** For serialization compatibility. */
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/audio/AudioParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/audio/AudioParser.java
index 64596b9ac..a00573a59 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/audio/AudioParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/audio/AudioParser.java
@@ -36,6 +36,7 @@ import org.apache.commons.io.input.ProxyInputStream;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.metadata.TikaCoreProperties;
@@ -45,6 +46,7 @@ import org.apache.tika.parser.ParseContext;
 import org.apache.tika.parser.Parser;
 import org.apache.tika.sax.XHTMLContentHandler;
 
+@TikaComponent
 public class AudioParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/audio/MidiParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/audio/MidiParser.java
index d7622e97a..4ae7e657b 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/audio/MidiParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/audio/MidiParser.java
@@ -36,6 +36,7 @@ import javax.sound.midi.Track;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
@@ -43,6 +44,7 @@ import org.apache.tika.parser.ParseContext;
 import org.apache.tika.parser.Parser;
 import org.apache.tika.sax.XHTMLContentHandler;
 
+@TikaComponent
 public class MidiParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/mp3/Mp3Parser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/mp3/Mp3Parser.java
index a8ff69efa..a3e9a7b96 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/mp3/Mp3Parser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/mp3/Mp3Parser.java
@@ -27,6 +27,7 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TailStream;
 import org.apache.tika.metadata.Metadata;
@@ -46,6 +47,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * @see <a href="https://id3.org/id3v2.4.0-structure">MP3 ID3 Version 2.4 Structure Specification</a>
  * @see <a href="https://id3.org/id3v2.4.0-frames">MP3 ID3 Version 2.4 Frames Specification</a>
  */
+@TikaComponent
 public class Mp3Parser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/mp4/MP4Parser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/mp4/MP4Parser.java
index 0b7e30b2a..6a2aaf6a5 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/mp4/MP4Parser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/mp4/MP4Parser.java
@@ -43,6 +43,7 @@ import com.drew.metadata.mp4.media.Mp4VideoDirectory;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.RuntimeSAXException;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TemporaryResources;
@@ -63,6 +64,7 @@ import org.apache.tika.utils.StringUtils;
  * <p>
  * This uses Drew Noakes' metadata-extractor: https://github.com/drewnoakes/metadata-extractor
  */
+@TikaComponent
 public class MP4Parser implements Parser {
     /**
      * Serial version UID
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/video/FLVParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/video/FLVParser.java
index 0040989fd..51c7f9cdb 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/video/FLVParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/video/FLVParser.java
@@ -33,6 +33,7 @@ import org.apache.commons.io.input.UnsynchronizedByteArrayInputStream;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
@@ -63,6 +64,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * hasCuePoints width, cuePoints, lasttimestamp, canSeekToEnd, datasize,
  * duration, videosize, filesize, audiodatarate, hasAudio, stereo audiodelay
  */
+@TikaComponent
 public class FLVParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/resources/META-INF/services/org.apache.tika.detect.Detector b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/resources/META-INF/services/org.apache.tika.detect.Detector
deleted file mode 100644
index 1428c6c4d..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/resources/META-INF/services/org.apache.tika.detect.Detector
+++ /dev/null
@@ -1,16 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.detect.MatroskaDetector
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index 53bc7ab9f..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,22 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.parser.audio.AudioParser
-org.apache.tika.parser.audio.MidiParser
-org.apache.tika.parser.mp3.Mp3Parser
-org.apache.tika.parser.mp4.MP4Parser
-org.apache.tika.parser.video.FLVParser
-
-
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-cad-module/src/main/java/org/apache/tika/parser/dgn/DGN8Parser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-cad-module/src/main/java/org/apache/tika/parser/dgn/DGN8Parser.java
index a0eba36c1..a5f391439 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-cad-module/src/main/java/org/apache/tika/parser/dgn/DGN8Parser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-cad-module/src/main/java/org/apache/tika/parser/dgn/DGN8Parser.java
@@ -28,6 +28,7 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TikaInputStream;
 import org.apache.tika.metadata.Metadata;
@@ -40,6 +41,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
 /**
  * This is a VERY LIMITED parser. It parses metadata out of dgn8 files.
  */
+@TikaComponent
 public class DGN8Parser implements Parser {
 
     Set<MediaType> SUPPORTED_TYPES = Collections.singleton(MediaType.image("vnd.dgn; version=8"));
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-cad-module/src/main/java/org/apache/tika/parser/dwg/DWGParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-cad-module/src/main/java/org/apache/tika/parser/dwg/DWGParser.java
index a433cb258..40e5bfcf7 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-cad-module/src/main/java/org/apache/tika/parser/dwg/DWGParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-cad-module/src/main/java/org/apache/tika/parser/dwg/DWGParser.java
@@ -26,6 +26,7 @@ import org.apache.poi.util.StringUtil;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.EndianUtils;
 import org.apache.tika.metadata.Metadata;
@@ -41,6 +42,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * Note that we use Apache POI for various parts of the processing, as
  * lots of the low level string/int/short concepts are the same.
  */
+@TikaComponent
 public class DWGParser extends AbstractDWGParser {
     public static String DWG_CUSTOM_META_PREFIX = "dwg-custom:";
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-cad-module/src/main/java/org/apache/tika/parser/dwg/DWGReadParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-cad-module/src/main/java/org/apache/tika/parser/dwg/DWGReadParser.java
index 241aa738e..55ae21c1c 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-cad-module/src/main/java/org/apache/tika/parser/dwg/DWGReadParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-cad-module/src/main/java/org/apache/tika/parser/dwg/DWGReadParser.java
@@ -48,6 +48,7 @@ import org.slf4j.LoggerFactory;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.metadata.TikaCoreProperties;
@@ -82,7 +83,7 @@ import org.apache.tika.utils.ProcessUtils;
  * <p>
  * String : cleanDwgReadReplaceWith - * replacement characters dwgReadExecutable
  */
-
+@TikaComponent(spi = false)
 public class DWGReadParser extends AbstractDWGParser {
     private static final Logger LOG = LoggerFactory.getLogger(DWGReadParser.class);
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-cad-module/src/main/java/org/apache/tika/parser/prt/PRTParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-cad-module/src/main/java/org/apache/tika/parser/prt/PRTParser.java
index 4e978329e..4eee2abf8 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-cad-module/src/main/java/org/apache/tika/parser/prt/PRTParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-cad-module/src/main/java/org/apache/tika/parser/prt/PRTParser.java
@@ -28,6 +28,7 @@ import org.apache.commons.io.IOUtils;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.EndianUtils;
 import org.apache.tika.metadata.Metadata;
@@ -41,7 +42,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * A basic text extracting parser for the CADKey PRT (CAD Drawing)
  * format. It outputs text from note entries.
  */
-
+@TikaComponent
 public class PRTParser implements Parser {
 
     public static final String PRT_MIME_TYPE = "application/x-prt";
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-cad-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-cad-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index b3b6bd593..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-cad-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,19 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.parser.dgn.DGN8Parser
-org.apache.tika.parser.dwg.DWGParser
-org.apache.tika.parser.prt.PRTParser
-
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/asm/ClassParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/asm/ClassParser.java
index 1b42334a2..58c488df7 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/asm/ClassParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/asm/ClassParser.java
@@ -24,6 +24,7 @@ import java.util.Set;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
@@ -33,6 +34,7 @@ import org.apache.tika.parser.Parser;
 /**
  * Parser for Java .class files.
  */
+@TikaComponent
 public class ClassParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/code/SourceCodeParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/code/SourceCodeParser.java
index c11f20d36..e6580a6ce 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/code/SourceCodeParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/code/SourceCodeParser.java
@@ -46,6 +46,7 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.AutoDetectReader;
 import org.apache.tika.detect.EncodingDetector;
 import org.apache.tika.exception.TikaException;
@@ -63,6 +64,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * @author Hong-Thai.Nguyen
  * @since 1.6
  */
+@TikaComponent
 public class SourceCodeParser extends AbstractEncodingDetectorParser {
 
     private static final long serialVersionUID = -4543476498190054160L;
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/executable/ExecutableParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/executable/ExecutableParser.java
index e8ed04aeb..18a1b4053 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/executable/ExecutableParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/executable/ExecutableParser.java
@@ -28,6 +28,7 @@ import org.apache.commons.io.IOUtils;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.EndianUtils;
 import org.apache.tika.metadata.MachineMetadata;
@@ -41,6 +42,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
 /**
  * Parser for executable files. Currently supports ELF and PE
  */
+@TikaComponent
 public class ExecutableParser implements Parser, MachineMetadata {
     /**
      * Serial version UID
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/executable/UniversalExecutableParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/executable/UniversalExecutableParser.java
index 20e12a564..8e2c28a28 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/executable/UniversalExecutableParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/executable/UniversalExecutableParser.java
@@ -28,6 +28,7 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.exception.UnsupportedFormatException;
 import org.apache.tika.extractor.EmbeddedDocumentExtractor;
@@ -43,6 +44,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
 /**
  * Parser for universal executable files.
  */
+@TikaComponent
 public class UniversalExecutableParser implements Parser {
     private static final long serialVersionUID = 1L;
 
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/mat/MatParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/mat/MatParser.java
index 042d1db5e..d114791de 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/mat/MatParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/mat/MatParser.java
@@ -33,6 +33,7 @@ import com.jmatio.types.MLStructure;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TemporaryResources;
 import org.apache.tika.io.TikaInputStream;
@@ -44,7 +45,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
 
 //JMatIO imports
 
-
+@TikaComponent
 public class MatParser implements Parser {
 
     public static final String MATLAB_MIME_TYPE = "application/x-matlab-data";
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/sas/SAS7BDATParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/sas/SAS7BDATParser.java
index 4dab3499d..807e78919 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/sas/SAS7BDATParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/java/org/apache/tika/parser/sas/SAS7BDATParser.java
@@ -32,6 +32,7 @@ import com.epam.parso.impl.SasFileReaderImpl;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Database;
 import org.apache.tika.metadata.HttpHeaders;
@@ -49,6 +50,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * Processes the SAS7BDAT data columnar database file used by SAS and
  * other similar languages.
  */
+@TikaComponent
 public class SAS7BDATParser implements Parser {
     private static final long serialVersionUID = -2775485539937983150L;
 
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index 9b73c5db1..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-code-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,22 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.parser.asm.ClassParser
-org.apache.tika.parser.code.SourceCodeParser
-org.apache.tika.parser.executable.ExecutableParser
-org.apache.tika.parser.executable.UniversalExecutableParser
-org.apache.tika.parser.mat.MatParser
-org.apache.tika.parser.sas.SAS7BDATParser
-
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-crypto-module/src/main/java/org/apache/tika/parser/crypto/Pkcs7Parser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-crypto-module/src/main/java/org/apache/tika/parser/crypto/Pkcs7Parser.java
index 0c5ade368..96e020e08 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-crypto-module/src/main/java/org/apache/tika/parser/crypto/Pkcs7Parser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-crypto-module/src/main/java/org/apache/tika/parser/crypto/Pkcs7Parser.java
@@ -30,6 +30,7 @@ import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
@@ -40,6 +41,7 @@ import org.apache.tika.parser.Parser;
 /**
  * Basic parser for PKCS7 data.
  */
+@TikaComponent
 public class Pkcs7Parser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-crypto-module/src/main/java/org/apache/tika/parser/crypto/TSDParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-crypto-module/src/main/java/org/apache/tika/parser/crypto/TSDParser.java
index 2a0e4a0f9..e975fb6ff 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-crypto-module/src/main/java/org/apache/tika/parser/crypto/TSDParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-crypto-module/src/main/java/org/apache/tika/parser/crypto/TSDParser.java
@@ -50,6 +50,7 @@ import org.slf4j.LoggerFactory;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.exception.WriteLimitReachedException;
 import org.apache.tika.extractor.EmbeddedDocumentExtractor;
@@ -65,6 +66,7 @@ import org.apache.tika.utils.RereadableInputStream;
 /**
  * Tika parser for Time Stamped Data Envelope (application/timestamped-data)
  */
+@TikaComponent
 public class TSDParser implements Parser {
     public static final String TSD_MIME_TYPE = "application/timestamped-data";
     private static final long serialVersionUID = 3268158344501763323L;
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-crypto-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-crypto-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index a4e54aba8..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-crypto-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,17 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.parser.crypto.Pkcs7Parser
-org.apache.tika.parser.crypto.TSDParser
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-font-module/src/main/java/org/apache/tika/parser/font/AdobeFontMetricParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-font-module/src/main/java/org/apache/tika/parser/font/AdobeFontMetricParser.java
index aa4c3fed8..3a9614367 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-font-module/src/main/java/org/apache/tika/parser/font/AdobeFontMetricParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-font-module/src/main/java/org/apache/tika/parser/font/AdobeFontMetricParser.java
@@ -28,6 +28,7 @@ import org.apache.fontbox.afm.FontMetrics;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.metadata.Property;
@@ -40,6 +41,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
 /**
  * Parser for AFM Font Files
  */
+@TikaComponent
 public class AdobeFontMetricParser implements Parser {
     // TIKA-1325 Replace these with properties, from a well known standard
     static final String MET_AVG_CHAR_WIDTH = "AvgCharacterWidth";
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-font-module/src/main/java/org/apache/tika/parser/font/TrueTypeParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-font-module/src/main/java/org/apache/tika/parser/font/TrueTypeParser.java
index b88ffdb74..f22018d17 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-font-module/src/main/java/org/apache/tika/parser/font/TrueTypeParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-font-module/src/main/java/org/apache/tika/parser/font/TrueTypeParser.java
@@ -32,6 +32,7 @@ import org.apache.pdfbox.io.RandomAccessReadBufferedFile;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TikaInputStream;
 import org.apache.tika.metadata.Metadata;
@@ -44,6 +45,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
 /**
  * Parser for TrueType font files (TTF).
  */
+@TikaComponent
 public class TrueTypeParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-font-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-font-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index 200afade9..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-font-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,18 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.parser.font.AdobeFontMetricParser
-org.apache.tika.parser.font.TrueTypeParser
-
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-html-module/src/main/java/org/apache/tika/parser/html/HtmlEncodingDetector.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-html-module/src/main/java/org/apache/tika/parser/html/HtmlEncodingDetector.java
index 9f66c9ac0..9e74614df 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-html-module/src/main/java/org/apache/tika/parser/html/HtmlEncodingDetector.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-html-module/src/main/java/org/apache/tika/parser/html/HtmlEncodingDetector.java
@@ -31,6 +31,7 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.EncodingDetector;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.utils.CharsetUtils;
@@ -44,6 +45,7 @@ import org.apache.tika.utils.CharsetUtils;
  *
  * @since Apache Tika 1.2
  */
+@TikaComponent(spi = false)
 public class HtmlEncodingDetector implements EncodingDetector {
 
     // TIKA-357 - use bigger buffer for meta tag sniffing (was 4K)
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-html-module/src/main/java/org/apache/tika/parser/html/JSoupParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-html-module/src/main/java/org/apache/tika/parser/html/JSoupParser.java
index 36115a08f..3be1054fc 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-html-module/src/main/java/org/apache/tika/parser/html/JSoupParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-html-module/src/main/java/org/apache/tika/parser/html/JSoupParser.java
@@ -46,6 +46,7 @@ import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.EncodingDetector;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
@@ -59,6 +60,7 @@ import org.apache.tika.parser.ParseContext;
  * and post-processes the events to produce XHTML and metadata expected by
  * Tika clients.
  */
+@TikaComponent
 public class JSoupParser extends AbstractEncodingDetectorParser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-html-module/src/main/java/org/apache/tika/parser/html/charsetdetector/StandardHtmlEncodingDetector.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-html-module/src/main/java/org/apache/tika/parser/html/charsetdetector/StandardHtmlEncodingDetector.java
index 90af81e18..7408f9737 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-html-module/src/main/java/org/apache/tika/parser/html/charsetdetector/StandardHtmlEncodingDetector.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-html-module/src/main/java/org/apache/tika/parser/html/charsetdetector/StandardHtmlEncodingDetector.java
@@ -25,6 +25,7 @@ import java.nio.charset.Charset;
 import org.apache.commons.io.input.BoundedInputStream;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.EncodingDetector;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
@@ -51,6 +52,7 @@ import org.apache.tika.mime.MediaType;
  * }</pre>
  * <p>
  */
+@TikaComponent(spi = false)
 public final class StandardHtmlEncodingDetector implements EncodingDetector {
     private static final int META_TAG_BUFFER_SIZE = 8192;
 
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-html-module/src/main/resources/META-INF/services/org.apache.tika.detect.EncodingDetector b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-html-module/src/main/resources/META-INF/services/org.apache.tika.detect.EncodingDetector
index 2d7b38cf6..9025ca781 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-html-module/src/main/resources/META-INF/services/org.apache.tika.detect.EncodingDetector
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-html-module/src/main/resources/META-INF/services/org.apache.tika.detect.EncodingDetector
@@ -12,4 +12,8 @@
 #  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
-org.apache.tika.parser.html.HtmlEncodingDetector
\ No newline at end of file
+
+# TODO: This is a manual service file because EncodingDetectors are not re-sorted at runtime
+# (unlike Parsers/Detectors which call ServiceLoaderUtils.sortLoadedClasses()).
+# Need to implement a runtime priority mechanism for proper ordering across modules.
+org.apache.tika.parser.html.HtmlEncodingDetector
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-html-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-html-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index 274e0bd4a..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-html-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,15 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-org.apache.tika.parser.html.JSoupParser
\ No newline at end of file
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/BPGParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/BPGParser.java
index 1b7bb96d8..03c66bc37 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/BPGParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/BPGParser.java
@@ -28,6 +28,7 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.exception.TikaMemoryLimitException;
 import org.apache.tika.io.EndianUtils;
@@ -43,6 +44,7 @@ import org.apache.tika.parser.ParseContext;
  * Documentation on the file format is available from
  * http://bellard.org/bpg/bpg_spec.txt
  */
+@TikaComponent
 public class BPGParser extends AbstractImageParser {
     protected static final int EXTENSION_TAG_EXIF = 1;
     protected static final int EXTENSION_TAG_ICC_PROFILE = 2;
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/HeifParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/HeifParser.java
index 79dc9c498..403f8ba76 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/HeifParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/HeifParser.java
@@ -25,12 +25,14 @@ import java.util.Set;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
 import org.apache.tika.parser.ParseContext;
 
 
+@TikaComponent
 public class HeifParser extends AbstractImageParser {
 
     private static final Set<MediaType> SUPPORTED_TYPES = new HashSet<>(
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/ICNSParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/ICNSParser.java
index 4cb9c65d6..298738567 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/ICNSParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/ICNSParser.java
@@ -28,6 +28,7 @@ import org.apache.commons.io.IOUtils;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.exception.TikaMemoryLimitException;
 import org.apache.tika.metadata.Metadata;
@@ -39,6 +40,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
 /**
  * A basic parser class for Apple ICNS icon files
  */
+@TikaComponent
 public class ICNSParser implements Parser {
     public static final String ICNS_MIME_TYPE = "image/icns";
     private static final long serialVersionUID = 922010233654248327L;
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/ImageParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/ImageParser.java
index 0bee1042c..aeff0b535 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/ImageParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/ImageParser.java
@@ -37,6 +37,7 @@ import org.w3c.dom.Node;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.metadata.Property;
@@ -44,6 +45,7 @@ import org.apache.tika.metadata.TikaCoreProperties;
 import org.apache.tika.mime.MediaType;
 import org.apache.tika.parser.ParseContext;
 
+@TikaComponent
 public class ImageParser extends AbstractImageParser {
     /**
      * Serial version UID
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/JXLParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/JXLParser.java
index 499af8cfa..6ef25e9da 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/JXLParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/JXLParser.java
@@ -24,6 +24,7 @@ import java.util.Set;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
@@ -34,6 +35,7 @@ import org.apache.tika.parser.xmp.JempboxExtractor;
 /**
  * Tries to scrape XMP out of JXL
  */
+@TikaComponent
 public class JXLParser implements Parser {
 
     private static final Set<MediaType> SUPPORTED_TYPES = Collections.singleton(MediaType.image(
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/JpegParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/JpegParser.java
index c64cd98af..b9b998c83 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/JpegParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/JpegParser.java
@@ -24,6 +24,7 @@ import java.util.Set;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TemporaryResources;
 import org.apache.tika.io.TikaInputStream;
@@ -32,6 +33,7 @@ import org.apache.tika.mime.MediaType;
 import org.apache.tika.parser.ParseContext;
 import org.apache.tika.parser.xmp.JempboxExtractor;
 
+@TikaComponent
 public class JpegParser extends AbstractImageParser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/PSDParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/PSDParser.java
index 0a4437dce..10329dc25 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/PSDParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/PSDParser.java
@@ -31,6 +31,7 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.exception.TikaMemoryLimitException;
 import org.apache.tika.io.EndianUtils;
@@ -53,6 +54,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * An MIT-licensed python parser with test files is:
  * https://github.com/psd-tools/psd-tools
  */
+@TikaComponent
 public class PSDParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/TiffParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/TiffParser.java
index 89e507cc1..570a0b6ec 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/TiffParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/TiffParser.java
@@ -24,6 +24,7 @@ import java.util.Set;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TemporaryResources;
 import org.apache.tika.io.TikaInputStream;
@@ -32,6 +33,7 @@ import org.apache.tika.mime.MediaType;
 import org.apache.tika.parser.ParseContext;
 import org.apache.tika.parser.xmp.JempboxExtractor;
 
+@TikaComponent
 public class TiffParser extends AbstractImageParser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/WebPParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/WebPParser.java
index c01cec26d..d66ac2061 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/WebPParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/WebPParser.java
@@ -24,6 +24,7 @@ import java.util.Set;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TemporaryResources;
 import org.apache.tika.io.TikaInputStream;
@@ -33,7 +34,7 @@ import org.apache.tika.parser.ParseContext;
 import org.apache.tika.parser.Parser;
 import org.apache.tika.sax.XHTMLContentHandler;
 
-
+@TikaComponent
 public class WebPParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index 6a6da530f..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,26 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.parser.image.BPGParser
-org.apache.tika.parser.image.ImageParser
-org.apache.tika.parser.image.PSDParser
-org.apache.tika.parser.image.TiffParser
-org.apache.tika.parser.image.WebPParser
-org.apache.tika.parser.image.JpegParser
-org.apache.tika.parser.image.HeifParser
-org.apache.tika.parser.image.ICNSParser
-org.apache.tika.parser.image.JXLParser
-
-
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-mail-module/src/main/java/org/apache/tika/parser/mail/RFC822Parser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-mail-module/src/main/java/org/apache/tika/parser/mail/RFC822Parser.java
index b2b552122..b40343d57 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-mail-module/src/main/java/org/apache/tika/parser/mail/RFC822Parser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-mail-module/src/main/java/org/apache/tika/parser/mail/RFC822Parser.java
@@ -29,6 +29,7 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.Detector;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.exception.ZeroByteFileException;
@@ -49,6 +50,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  *
  * @author jnioche@digitalpebble.com
  */
+@TikaComponent
 public class RFC822Parser implements Parser {
     /**
      * Serial version UID
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-mail-module/src/main/java/org/apache/tika/parser/mbox/MboxParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-mail-module/src/main/java/org/apache/tika/parser/mbox/MboxParser.java
index f4834337b..7f4879302 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-mail-module/src/main/java/org/apache/tika/parser/mbox/MboxParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-mail-module/src/main/java/org/apache/tika/parser/mbox/MboxParser.java
@@ -37,6 +37,7 @@ import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.extractor.EmbeddedDocumentExtractor;
 import org.apache.tika.extractor.EmbeddedDocumentUtil;
@@ -55,6 +56,7 @@ import org.apache.tika.utils.StringUtils;
  * Mbox (mailbox) parser. This version extracts each mail from Mbox and uses the
  * DelegatingParser to process each mail.
  */
+@TikaComponent
 public class MboxParser implements Parser {
 
     public static final String MBOX_MIME_TYPE = "application/mbox";
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-mail-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-mail-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index 03f511c05..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-mail-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,19 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.parser.mail.RFC822Parser
-org.apache.tika.parser.mbox.MboxParser
-
-
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/detect/microsoft/POIFSContainerDetector.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/detect/microsoft/POIFSContainerDetector.java
index ef36eaed3..07c6263f4 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/detect/microsoft/POIFSContainerDetector.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/detect/microsoft/POIFSContainerDetector.java
@@ -44,6 +44,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.Detector;
 import org.apache.tika.io.BoundedInputStream;
 import org.apache.tika.io.TikaInputStream;
@@ -57,6 +58,7 @@ import org.apache.tika.parser.microsoft.OfficeParser;
  * This should work for all OLE2 documents, whether
  * they are ones supported by POI or not.
  */
+@TikaComponent
 public class POIFSContainerDetector implements Detector {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/EMFParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/EMFParser.java
index a5ff9ea20..eb6114dd5 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/EMFParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/EMFParser.java
@@ -36,6 +36,7 @@ import org.apache.poi.util.StringUtil;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.extractor.EmbeddedDocumentExtractor;
 import org.apache.tika.extractor.EmbeddedDocumentUtil;
@@ -62,6 +63,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * We'd have to do something like what PDFBox or XPS do to sort the
  * runs and then put the cow back together from the hamburger...lol...
  */
+@TikaComponent
 public class EMFParser implements Parser {
 
     public static Property EMF_ICON_ONLY = Property.internalBoolean("emf:iconOnly");
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/JackcessParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/JackcessParser.java
index 3e5ff0cb9..a234946a1 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/JackcessParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/JackcessParser.java
@@ -31,6 +31,7 @@ import com.healthmarketscience.jackcess.util.LinkResolver;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.EncryptedDocumentException;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.exception.UnsupportedFormatException;
@@ -51,6 +52,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * Many, many thanks to LexisNexis®/Health Market Science (HMS), Brian O'Neill,
  * and James Ahlborn for relicensing Jackcess to Apache v2.0!
  */
+@TikaComponent
 public class JackcessParser implements Parser {
 
     public static final String SUMMARY_PROPERTY_PREFIX =
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/MSOwnerFileParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/MSOwnerFileParser.java
index f319b6d0a..752ed6f09 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/MSOwnerFileParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/MSOwnerFileParser.java
@@ -26,6 +26,7 @@ import org.apache.commons.io.IOUtils;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.metadata.TikaCoreProperties;
@@ -38,6 +39,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * Parser for temporary MSOFfice files.
  * This currently only extracts the owner's name.
  */
+@TikaComponent
 public class MSOwnerFileParser implements Parser {
 
     private static final int ASCII_CHUNK_LENGTH = 54;
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/OfficeParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/OfficeParser.java
index dade2ca5f..045ffe744 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/OfficeParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/OfficeParser.java
@@ -45,6 +45,7 @@ import org.apache.poi.util.LocaleUtil;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.microsoft.POIFSContainerDetector;
 import org.apache.tika.exception.EncryptedDocumentException;
 import org.apache.tika.exception.TikaException;
@@ -65,6 +66,7 @@ import org.apache.tika.utils.StringUtils;
 /**
  * Defines a Microsoft document content extractor.
  */
+@TikaComponent
 public class OfficeParser extends AbstractOfficeParser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/OldExcelParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/OldExcelParser.java
index 7c8ca2e63..f977c876a 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/OldExcelParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/OldExcelParser.java
@@ -29,6 +29,7 @@ import org.apache.poi.hssf.extractor.OldExcelExtractor;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
@@ -40,6 +41,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * A POI-powered Tika Parser for very old versions of Excel, from
  * pre-OLE2 days, such as Excel 4.
  */
+@TikaComponent
 public class OldExcelParser implements Parser {
     private static final long serialVersionUID = 4611820730372823452L;
 
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/TNEFParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/TNEFParser.java
index 9cd09fea6..23163d9f3 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/TNEFParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/TNEFParser.java
@@ -31,6 +31,7 @@ import org.apache.poi.hsmf.datatypes.MAPIProperty;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.extractor.EmbeddedDocumentExtractor;
 import org.apache.tika.extractor.EmbeddedDocumentUtil;
@@ -47,6 +48,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * A POI-powered Tika Parser for TNEF (Transport Neutral
  * Encoding Format) messages, aka winmail.dat
  */
+@TikaComponent
 public class TNEFParser implements Parser {
     private static final long serialVersionUID = 4611820730372823452L;
 
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/WMFParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/WMFParser.java
index 3c55a14b0..65264c129 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/WMFParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/WMFParser.java
@@ -34,6 +34,7 @@ import org.apache.poi.util.RecordFormatException;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
@@ -45,6 +46,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * This parser offers a very rough capability to extract text if there
  * is text stored in the WMF files.
  */
+@TikaComponent
 public class WMFParser implements Parser {
 
     private static final MediaType MEDIA_TYPE = MediaType.image("wmf");
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/activemime/ActiveMimeParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/activemime/ActiveMimeParser.java
index 5ac13fda5..b9b51049a 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/activemime/ActiveMimeParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/activemime/ActiveMimeParser.java
@@ -28,6 +28,7 @@ import org.apache.poi.util.LittleEndian;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.extractor.EmbeddedDocumentExtractor;
 import org.apache.tika.extractor.EmbeddedDocumentUtil;
@@ -42,6 +43,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * ActiveMime is a macro container format used in some mso files.  See, e.g.
  * <a href="https://mastodon.social/@Ange/110027138524274526">Ange's toot</a>.
  */
+@TikaComponent
 public class ActiveMimeParser implements Parser {
 
     private static final MediaType MEDIA_TYPE = MediaType.application("x-activemime");
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/chm/ChmParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/chm/ChmParser.java
index 06b2dd518..00b8ee46f 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/chm/ChmParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/chm/ChmParser.java
@@ -26,6 +26,7 @@ import java.util.Set;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.extractor.EmbeddedDocumentUtil;
 import org.apache.tika.io.TikaInputStream;
@@ -38,6 +39,7 @@ import org.apache.tika.sax.BodyContentHandler;
 import org.apache.tika.sax.EmbeddedContentHandler;
 import org.apache.tika.sax.XHTMLContentHandler;
 
+@TikaComponent
 public class ChmParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/libpst/LibPstParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/libpst/LibPstParser.java
index 83e88a38c..4afc4030a 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/libpst/LibPstParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/libpst/LibPstParser.java
@@ -37,6 +37,7 @@ import org.apache.tika.config.Field;
 import org.apache.tika.config.Initializable;
 import org.apache.tika.config.InitializableProblemHandler;
 import org.apache.tika.config.Param;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaConfigException;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TemporaryResources;
@@ -55,6 +56,7 @@ import org.apache.tika.utils.StringUtils;
  * the GPL-3 libpst/readpst commandline tool and configuring
  * Tika to call this library via tika-config.xml
  */
+@TikaComponent(spi = false)
 public class LibPstParser implements Parser, Initializable {
 
     public static final MediaType MS_OUTLOOK_PST_MIMETYPE = MediaType.application("vnd.ms-outlook-pst");
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteParser.java
index 55913515f..e7d72d828 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteParser.java
@@ -31,6 +31,7 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TemporaryResources;
 import org.apache.tika.io.TikaInputStream;
@@ -50,6 +51,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * <p>
  * Based on the Microsoft specs MS-ONE and MS-ONESTORE.
  */
+@TikaComponent
 public class OneNoteParser implements Parser {
 
     public static final String ONE_NOTE_PREFIX = "onenote:";
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/OOXMLParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/OOXMLParser.java
index f365dc94f..73bd9c4db 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/OOXMLParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/OOXMLParser.java
@@ -27,6 +27,7 @@ import org.apache.poi.openxml4j.util.ZipSecureFile;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
@@ -36,6 +37,7 @@ import org.apache.tika.parser.microsoft.AbstractOfficeParser;
 /**
  * Office Open XML (OOXML) parser.
  */
+@TikaComponent
 public class OOXMLParser extends AbstractOfficeParser {
     protected static final String SIGNATURE_RELATIONSHIP =
             "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin";
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/xwpf/ml2006/Word2006MLParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/xwpf/ml2006/Word2006MLParser.java
index e2fe9d080..8cbad69f7 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/xwpf/ml2006/Word2006MLParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/xwpf/ml2006/Word2006MLParser.java
@@ -26,6 +26,7 @@ import org.apache.commons.io.input.CloseShieldInputStream;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
@@ -35,7 +36,7 @@ import org.apache.tika.sax.EmbeddedContentHandler;
 import org.apache.tika.sax.XHTMLContentHandler;
 import org.apache.tika.utils.XMLReaderUtils;
 
-
+@TikaComponent
 public class Word2006MLParser extends AbstractOfficeParser {
 
     protected static final Set<MediaType> SUPPORTED_TYPES =
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/pst/OutlookPSTParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/pst/OutlookPSTParser.java
index 08f49daf3..136c8f693 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/pst/OutlookPSTParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/pst/OutlookPSTParser.java
@@ -31,6 +31,7 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.extractor.EmbeddedDocumentExtractor;
 import org.apache.tika.extractor.EmbeddedDocumentUtil;
@@ -46,6 +47,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
 /**
  * Parser for MS Outlook PST email storage files
  */
+@TikaComponent
 public class OutlookPSTParser implements Parser {
 
     public static final MediaType MS_OUTLOOK_PST_MIMETYPE =
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/pst/PSTMailItemParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/pst/PSTMailItemParser.java
index 00ee7c571..c1f3ffa9a 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/pst/PSTMailItemParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/pst/PSTMailItemParser.java
@@ -31,6 +31,7 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.extractor.EmbeddedDocumentExtractor;
 import org.apache.tika.extractor.EmbeddedDocumentUtil;
@@ -50,6 +51,7 @@ import org.apache.tika.sax.EmbeddedContentHandler;
 import org.apache.tika.sax.XHTMLContentHandler;
 import org.apache.tika.utils.StringUtils;
 
+@TikaComponent
 public class PSTMailItemParser implements Parser {
 
     //this is a synthetic file type to represent a notional "pst item"
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/rtf/RTFParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/rtf/RTFParser.java
index 8b17575a7..67d8c2e2d 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/rtf/RTFParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/rtf/RTFParser.java
@@ -26,6 +26,7 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
@@ -36,6 +37,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
 /**
  * RTF parser
  */
+@TikaComponent
 public class RTFParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/xml/SpreadsheetMLParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/xml/SpreadsheetMLParser.java
index 677751e75..d0d94af12 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/xml/SpreadsheetMLParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/xml/SpreadsheetMLParser.java
@@ -27,6 +27,7 @@ import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
 import org.xml.sax.helpers.DefaultHandler;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
 import org.apache.tika.parser.ParseContext;
@@ -39,6 +40,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * <p>
  * See <a href="https://en.wikipedia.org/wiki/Microsoft_Office_XML_formats">https://en.wikipedia.org/wiki/Microsoft_Office_XML_formats</a>
  */
+@TikaComponent
 public class SpreadsheetMLParser extends AbstractXML2003Parser {
 
     final static String CELL = "cell";
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/xml/WordMLParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/xml/WordMLParser.java
index 0590568d2..aaedc12c7 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/xml/WordMLParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/xml/WordMLParser.java
@@ -33,6 +33,7 @@ import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
 import org.xml.sax.helpers.DefaultHandler;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.extractor.EmbeddedDocumentExtractor;
 import org.apache.tika.extractor.EmbeddedDocumentUtil;
 import org.apache.tika.io.TikaInputStream;
@@ -49,6 +50,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * <p>
  * See <a href="https://en.wikipedia.org/wiki/Microsoft_Office_XML_formats">https://en.wikipedia.org/wiki/Microsoft_Office_XML_formats</a>
  */
+@TikaComponent
 public class WordMLParser extends AbstractXML2003Parser {
     //map between wordml and xhtml entities
     private static final Map<String, String> WORDML_TO_XHTML;
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/resources/META-INF/services/org.apache.tika.detect.Detector b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/resources/META-INF/services/org.apache.tika.detect.Detector
deleted file mode 100644
index 409f1e570..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/resources/META-INF/services/org.apache.tika.detect.Detector
+++ /dev/null
@@ -1,16 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.detect.microsoft.POIFSContainerDetector
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index 2096380b8..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,31 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-org.apache.tika.parser.microsoft.rtf.RTFParser
-org.apache.tika.parser.microsoft.EMFParser
-org.apache.tika.parser.microsoft.WMFParser
-org.apache.tika.parser.microsoft.JackcessParser
-org.apache.tika.parser.microsoft.MSOwnerFileParser
-org.apache.tika.parser.microsoft.OfficeParser
-org.apache.tika.parser.microsoft.OldExcelParser
-org.apache.tika.parser.microsoft.TNEFParser
-org.apache.tika.parser.microsoft.activemime.ActiveMimeParser
-org.apache.tika.parser.microsoft.onenote.OneNoteParser
-org.apache.tika.parser.microsoft.ooxml.OOXMLParser
-org.apache.tika.parser.microsoft.ooxml.xwpf.ml2006.Word2006MLParser
-org.apache.tika.parser.microsoft.xml.WordMLParser
-org.apache.tika.parser.microsoft.xml.SpreadsheetMLParser
-org.apache.tika.parser.microsoft.chm.ChmParser
-org.apache.tika.parser.microsoft.pst.OutlookPSTParser
-org.apache.tika.parser.microsoft.pst.PSTMailItemParser
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/detect/ole/MiscOLEDetector.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/detect/ole/MiscOLEDetector.java
index b6372903e..db522f7a3 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/detect/ole/MiscOLEDetector.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/detect/ole/MiscOLEDetector.java
@@ -31,6 +31,7 @@ import org.apache.poi.poifs.filesystem.Entry;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.Detector;
 import org.apache.tika.io.TikaInputStream;
 import org.apache.tika.metadata.Metadata;
@@ -45,6 +46,7 @@ import org.apache.tika.mime.MediaType;
  * This should work for all OLE2 documents, whether
  * they are ones supported by POI or not.
  */
+@TikaComponent
 public class MiscOLEDetector implements Detector {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/dbf/DBFParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/dbf/DBFParser.java
index da47cee94..e17f3c73d 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/dbf/DBFParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/dbf/DBFParser.java
@@ -31,6 +31,7 @@ import java.util.Set;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.EncodingDetector;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TikaInputStream;
@@ -50,6 +51,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * It caches the first 10 rows and then runs encoding dectection
  * on the "character" cells.
  */
+@TikaComponent
 public class DBFParser implements Parser {
 
     private static final int ROWS_TO_BUFFER_FOR_CHARSET_DETECTION = 10;
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/dif/DIFParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/dif/DIFParser.java
index b42270718..d132357a9 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/dif/DIFParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/dif/DIFParser.java
@@ -26,6 +26,7 @@ import org.apache.commons.io.input.CloseShieldInputStream;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
@@ -36,6 +37,7 @@ import org.apache.tika.sax.TaggedContentHandler;
 import org.apache.tika.sax.XHTMLContentHandler;
 import org.apache.tika.utils.XMLReaderUtils;
 
+@TikaComponent
 public class DIFParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/epub/EpubParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/epub/EpubParser.java
index f7ac587be..b5eed282a 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/epub/EpubParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/epub/EpubParser.java
@@ -47,6 +47,7 @@ import org.xml.sax.helpers.AttributesImpl;
 import org.xml.sax.helpers.DefaultHandler;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.EncryptedDocumentException;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.exception.WriteLimitReachedException;
@@ -72,6 +73,7 @@ import org.apache.tika.zip.utils.ZipSalvager;
 /**
  * Epub parser
  */
+@TikaComponent
 public class EpubParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/hwp/HwpV5Parser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/hwp/HwpV5Parser.java
index 0fe186c4e..24e1a814e 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/hwp/HwpV5Parser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/hwp/HwpV5Parser.java
@@ -24,6 +24,7 @@ import java.util.Set;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
@@ -31,6 +32,7 @@ import org.apache.tika.parser.ParseContext;
 import org.apache.tika.parser.Parser;
 import org.apache.tika.sax.XHTMLContentHandler;
 
+@TikaComponent
 public class HwpV5Parser implements Parser {
 
     public static final String HWP_MIME_TYPE = "application/x-hwp-v5";
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/indesign/IDMLParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/indesign/IDMLParser.java
index a76bf90b1..a486202b4 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/indesign/IDMLParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/indesign/IDMLParser.java
@@ -31,6 +31,7 @@ import org.apache.commons.io.IOUtils;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TikaInputStream;
 import org.apache.tika.metadata.Metadata;
@@ -45,6 +46,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
 /**
  * Adobe InDesign IDML Parser.
  */
+@TikaComponent
 public class IDMLParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/mif/MIFParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/mif/MIFParser.java
index fe200e429..d20208f41 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/mif/MIFParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/mif/MIFParser.java
@@ -29,6 +29,7 @@ import org.apache.commons.io.input.CloseShieldInputStream;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.AutoDetectReader;
 import org.apache.tika.detect.EncodingDetector;
 import org.apache.tika.exception.TikaException;
@@ -39,6 +40,7 @@ import org.apache.tika.parser.ParseContext;
 import org.apache.tika.sax.EndDocumentShieldingContentHandler;
 import org.apache.tika.sax.XHTMLContentHandler;
 
+@TikaComponent
 public class MIFParser extends AbstractEncodingDetectorParser {
 
     private static final Set<MediaType> SUPPORTED_TYPES = Collections.unmodifiableSet(new HashSet<>(
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/odf/FlatOpenDocumentParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/odf/FlatOpenDocumentParser.java
index 5f2f1ef4d..0ecac4607 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/odf/FlatOpenDocumentParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/odf/FlatOpenDocumentParser.java
@@ -30,6 +30,7 @@ import org.xml.sax.SAXException;
 import org.xml.sax.helpers.DefaultHandler;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
@@ -40,6 +41,7 @@ import org.apache.tika.sax.EmbeddedContentHandler;
 import org.apache.tika.sax.XHTMLContentHandler;
 import org.apache.tika.utils.XMLReaderUtils;
 
+@TikaComponent
 public class FlatOpenDocumentParser implements Parser {
 
     static final MediaType FLAT_OD =
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/odf/OpenDocumentParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/odf/OpenDocumentParser.java
index 1ed7f7649..cfd205e8c 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/odf/OpenDocumentParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/odf/OpenDocumentParser.java
@@ -35,6 +35,7 @@ import org.xml.sax.SAXException;
 import org.xml.sax.helpers.DefaultHandler;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.EncryptedDocumentException;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.extractor.EmbeddedDocumentUtil;
@@ -52,6 +53,7 @@ import org.apache.tika.utils.XMLReaderUtils;
 /**
  * OpenOffice parser
  */
+@TikaComponent
 public class OpenDocumentParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/wordperfect/QuattroProParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/wordperfect/QuattroProParser.java
index c24f30685..ff600264b 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/wordperfect/QuattroProParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/wordperfect/QuattroProParser.java
@@ -24,6 +24,7 @@ import java.util.Set;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
@@ -39,6 +40,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  *
  * @author Pascal Essiembre
  */
+@TikaComponent
 public class QuattroProParser implements Parser {
 
     private static final long serialVersionUID = 8941810225917012232L;
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/wordperfect/WordPerfectParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/wordperfect/WordPerfectParser.java
index bd81e088c..1d5ff8acb 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/wordperfect/WordPerfectParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/wordperfect/WordPerfectParser.java
@@ -27,6 +27,7 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.EncryptedDocumentException;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.exception.UnsupportedFormatException;
@@ -43,6 +44,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  *
  * @author Pascal Essiembre
  */
+@TikaComponent
 public class WordPerfectParser implements Parser {
 
     final static MediaType WP_BASE = MediaType.application("vnd.wordperfect");
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/resources/META-INF/services/org.apache.tika.detect.Detector b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/resources/META-INF/services/org.apache.tika.detect.Detector
deleted file mode 100644
index da1bd19c8..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/resources/META-INF/services/org.apache.tika.detect.Detector
+++ /dev/null
@@ -1,16 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.detect.ole.MiscOLEDetector
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index 22d33395f..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,24 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-org.apache.tika.parser.hwp.HwpV5Parser
-org.apache.tika.parser.mif.MIFParser
-org.apache.tika.parser.odf.FlatOpenDocumentParser
-org.apache.tika.parser.odf.OpenDocumentParser
-org.apache.tika.parser.wordperfect.QuattroProParser
-org.apache.tika.parser.wordperfect.WordPerfectParser
-org.apache.tika.parser.epub.EpubParser
-org.apache.tika.parser.dif.DIFParser
-org.apache.tika.parser.dbf.DBFParser
-org.apache.tika.parser.indesign.IDMLParser
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-news-module/src/main/java/org/apache/tika/parser/feed/FeedParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-news-module/src/main/java/org/apache/tika/parser/feed/FeedParser.java
index cfa5d3d47..a8c1ab233 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-news-module/src/main/java/org/apache/tika/parser/feed/FeedParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-news-module/src/main/java/org/apache/tika/parser/feed/FeedParser.java
@@ -33,6 +33,7 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.metadata.TikaCoreProperties;
@@ -47,6 +48,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * Uses Rome for parsing the feeds. A feed description is put in a paragraph
  * with its link and title in an anchor.
  */
+@TikaComponent
 public class FeedParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-news-module/src/main/java/org/apache/tika/parser/iptc/IptcAnpaParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-news-module/src/main/java/org/apache/tika/parser/iptc/IptcAnpaParser.java
index dfe38a995..50a951ef9 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-news-module/src/main/java/org/apache/tika/parser/iptc/IptcAnpaParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-news-module/src/main/java/org/apache/tika/parser/iptc/IptcAnpaParser.java
@@ -32,6 +32,7 @@ import java.util.TimeZone;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.metadata.TikaCoreProperties;
@@ -43,6 +44,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
 /**
  * Parser for IPTC ANPA New Wire Feeds
  */
+@TikaComponent
 public class IptcAnpaParser implements Parser {
     /**
      * Serial version UID
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-news-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-news-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index cbfceac2d..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-news-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,17 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.parser.feed.FeedParser
-org.apache.tika.parser.iptc.IptcAnpaParser
\ No newline at end of file
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-ocr-module/src/main/java/org/apache/tika/parser/ocr/TesseractOCRParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-ocr-module/src/main/java/org/apache/tika/parser/ocr/TesseractOCRParser.java
index e06db7d4d..0a79d6cd4 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-ocr-module/src/main/java/org/apache/tika/parser/ocr/TesseractOCRParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-ocr-module/src/main/java/org/apache/tika/parser/ocr/TesseractOCRParser.java
@@ -62,6 +62,7 @@ import org.apache.tika.config.Field;
 import org.apache.tika.config.Initializable;
 import org.apache.tika.config.InitializableProblemHandler;
 import org.apache.tika.config.Param;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.config.TikaTaskTimeout;
 import org.apache.tika.exception.TikaConfigException;
 import org.apache.tika.exception.TikaException;
@@ -94,6 +95,7 @@ import org.apache.tika.utils.XMLReaderUtils;
  * parseContext.set(TesseractOCRConfig.class, config);<br>
  * </p>
  */
+@TikaComponent
 public class TesseractOCRParser extends AbstractExternalProcessParser implements Initializable {
 
     public static final String TESS_META = "tess:";
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-ocr-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-ocr-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index 797066800..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-ocr-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,15 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-org.apache.tika.parser.ocr.TesseractOCRParser
\ No newline at end of file
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/main/java/org/apache/tika/parser/pdf/PDFParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/main/java/org/apache/tika/parser/pdf/PDFParser.java
index 40228bfe4..6c29c364a 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/main/java/org/apache/tika/parser/pdf/PDFParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/main/java/org/apache/tika/parser/pdf/PDFParser.java
@@ -62,6 +62,7 @@ import org.apache.tika.config.Field;
 import org.apache.tika.config.Initializable;
 import org.apache.tika.config.InitializableProblemHandler;
 import org.apache.tika.config.Param;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.EncryptedDocumentException;
 import org.apache.tika.exception.TikaConfigException;
 import org.apache.tika.exception.TikaException;
@@ -122,6 +123,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * If your PDFs contain marked content or tags, consider
  * {@link PDFParserConfig#setExtractMarkedContent(boolean)}
  */
+@TikaComponent
 public class PDFParser implements Parser, RenderingParser, Initializable {
 
     public static final MediaType MEDIA_TYPE = MediaType.application("pdf");
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index 25b9c9f69..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,15 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-org.apache.tika.parser.pdf.PDFParser
\ No newline at end of file
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/detect/gzip/GZipSpecializationDetector.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/detect/gzip/GZipSpecializationDetector.java
index 301cbbcdc..c6ffc3061 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/detect/gzip/GZipSpecializationDetector.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/detect/gzip/GZipSpecializationDetector.java
@@ -26,6 +26,7 @@ import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.input.UnsynchronizedByteArrayInputStream;
 import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.Detector;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
@@ -34,6 +35,7 @@ import org.apache.tika.mime.MediaType;
  * This is designed to detect commonly gzipped file types such as warc.gz.
  * This is a first step.  We still need to implement tar.gz and svg.gz and ???
  */
+@TikaComponent
 public class GZipSpecializationDetector implements Detector {
     public static MediaType GZ = MediaType.application("gzip");
     public static MediaType WARC_GZ = MediaType.application("warc+gz");
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/CompressorParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/CompressorParser.java
index 6028d5016..0a76f0933 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/CompressorParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/CompressorParser.java
@@ -61,6 +61,7 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.exception.TikaMemoryLimitException;
 import org.apache.tika.extractor.EmbeddedDocumentExtractor;
@@ -76,6 +77,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
 /**
  * Parser for various compression formats.
  */
+@TikaComponent
 public class CompressorParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/PackageParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/PackageParser.java
index bfd2a1168..de2d61764 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/PackageParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/PackageParser.java
@@ -60,6 +60,7 @@ import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.EncodingDetector;
 import org.apache.tika.exception.EncryptedDocumentException;
 import org.apache.tika.exception.TikaException;
@@ -86,6 +87,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * are not installed, an IOException will be thrown, and potentially
  * wrapped in a TikaException.
  */
+@TikaComponent
 public class PackageParser extends AbstractEncodingDetectorParser {
 
     //We used to avoid overwriting file types if the file type
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/RarParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/RarParser.java
index 11a6e52a4..c14dc50f3 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/RarParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/RarParser.java
@@ -27,6 +27,7 @@ import com.github.junrar.rarfile.FileHeader;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.EncryptedDocumentException;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.exception.UnsupportedFormatException;
@@ -43,6 +44,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
 /**
  * Parser for Rar files.
  */
+@TikaComponent
 public class RarParser implements Parser {
     private static final long serialVersionUID = 6157727985054451501L;
 
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/UnrarParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/UnrarParser.java
index f4005b526..67796a608 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/UnrarParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/UnrarParser.java
@@ -32,6 +32,7 @@ import org.apache.commons.io.IOUtils;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.EncryptedDocumentException;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.exception.TikaTimeoutException;
@@ -52,6 +53,7 @@ import org.apache.tika.utils.ProcessUtils;
  * and on the path.  This is not the default rar parser and must
  * be selected via the tika-config.xml.
  */
+@TikaComponent(spi = false)
 public class UnrarParser implements Parser {
     private static final long serialVersionUID = 6157727985054451501L;
 
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/resources/META-INF/services/org.apache.tika.detect.Detector b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/resources/META-INF/services/org.apache.tika.detect.Detector
deleted file mode 100644
index a5d143217..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/resources/META-INF/services/org.apache.tika.detect.Detector
+++ /dev/null
@@ -1,16 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.detect.gzip.GZipSpecializationDetector
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index 89ce8c87b..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,18 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.parser.pkg.CompressorParser
-org.apache.tika.parser.pkg.PackageParser
-org.apache.tika.parser.pkg.RarParser
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/csv/TextAndCSVParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/csv/TextAndCSVParser.java
index 8feb63e1f..4fdde18d8 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/csv/TextAndCSVParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/csv/TextAndCSVParser.java
@@ -39,6 +39,7 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.AutoDetectReader;
 import org.apache.tika.detect.EncodingDetector;
 import org.apache.tika.exception.TikaConfigException;
@@ -69,6 +70,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * {@link org.apache.commons.csv.CSVParser} is lost.
  * </p>
  */
+@TikaComponent
 public class TextAndCSVParser extends AbstractEncodingDetectorParser {
 
     static final MediaType CSV = MediaType.text("csv");
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/strings/Latin1StringsParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/strings/Latin1StringsParser.java
index 8657237dc..6476ac3ff 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/strings/Latin1StringsParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/strings/Latin1StringsParser.java
@@ -25,6 +25,7 @@ import java.util.Set;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
 import org.apache.tika.parser.ParseContext;
@@ -47,6 +48,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * <p>
  * The implementation is optimized for fast parsing with only one pass.
  */
+@TikaComponent(spi = false)
 public class Latin1StringsParser implements Parser {
 
     private static final long serialVersionUID = 1L;
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/strings/StringsParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/strings/StringsParser.java
index 63b6a88be..ef3071c4c 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/strings/StringsParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/strings/StringsParser.java
@@ -39,6 +39,7 @@ import org.apache.tika.config.Field;
 import org.apache.tika.config.Initializable;
 import org.apache.tika.config.InitializableProblemHandler;
 import org.apache.tika.config.Param;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.FileCommandDetector;
 import org.apache.tika.exception.TikaConfigException;
 import org.apache.tika.exception.TikaException;
@@ -60,6 +61,7 @@ import org.apache.tika.utils.SystemUtils;
  *
  * @author gtotaro
  */
+@TikaComponent(spi = false)
 public class StringsParser implements Parser, Initializable {
     /**
      * Serial version UID
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/txt/BOMDetector.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/txt/BOMDetector.java
index c96bfda5d..82138be84 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/txt/BOMDetector.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/txt/BOMDetector.java
@@ -24,9 +24,14 @@ import java.nio.charset.UnsupportedCharsetException;
 import org.apache.commons.io.ByteOrderMark;
 import org.apache.commons.io.IOUtils;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.EncodingDetector;
 import org.apache.tika.metadata.Metadata;
 
+/*
+ This is an optional detector that is not loaded automatically by SPI. You need to configure it.
+ */
+@TikaComponent(spi = false)
 public class BOMDetector implements EncodingDetector {
 
     private static final ByteOrderMark[] BOMS =
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/txt/Icu4jEncodingDetector.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/txt/Icu4jEncodingDetector.java
index 2677a4d34..f521b7cac 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/txt/Icu4jEncodingDetector.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/txt/Icu4jEncodingDetector.java
@@ -26,11 +26,13 @@ import java.util.List;
 import java.util.Set;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.EncodingDetector;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
 import org.apache.tika.utils.CharsetUtils;
 
+@TikaComponent(spi = false)
 public class Icu4jEncodingDetector implements EncodingDetector {
 
     @Field
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/txt/TXTParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/txt/TXTParser.java
index f39a2fe01..b010b1f70 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/txt/TXTParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/txt/TXTParser.java
@@ -26,6 +26,7 @@ import org.apache.commons.io.input.CloseShieldInputStream;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.AutoDetectReader;
 import org.apache.tika.detect.EncodingDetector;
 import org.apache.tika.exception.TikaException;
@@ -48,6 +49,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
  * <dd><code>text/plain; charset=...</code></dd>
  * </dl>
  */
+@TikaComponent(spi = false)
 public class TXTParser extends AbstractEncodingDetectorParser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/txt/UniversalEncodingDetector.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/txt/UniversalEncodingDetector.java
index 9dadbb6b3..2c6f1f816 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/txt/UniversalEncodingDetector.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/java/org/apache/tika/parser/txt/UniversalEncodingDetector.java
@@ -21,9 +21,11 @@ import java.io.InputStream;
 import java.nio.charset.Charset;
 
 import org.apache.tika.config.Field;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.EncodingDetector;
 import org.apache.tika.metadata.Metadata;
 
+@TikaComponent(spi = false)
 public class UniversalEncodingDetector implements EncodingDetector {
 
     private static final int BUFSIZE = 1024;
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/resources/META-INF/services/org.apache.tika.detect.EncodingDetector b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/resources/META-INF/services/org.apache.tika.detect.EncodingDetector
index e60217c3d..506ca15f5 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/resources/META-INF/services/org.apache.tika.detect.EncodingDetector
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/resources/META-INF/services/org.apache.tika.detect.EncodingDetector
@@ -13,5 +13,9 @@
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
 
+# TODO: This is a manual service file because EncodingDetectors are not re-sorted at runtime
+# (unlike Parsers/Detectors which call ServiceLoaderUtils.sortLoadedClasses()).
+# Order matters here - first detector that returns non-null wins.
+# Need to implement a runtime priority mechanism for proper ordering across modules.
 org.apache.tika.parser.txt.UniversalEncodingDetector
 org.apache.tika.parser.txt.Icu4jEncodingDetector
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index 4e6ea01b3..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-text-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,22 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-
-org.apache.tika.parser.csv.TextAndCSVParser
-#org.apache.tika.parser.strings.Latin1StringsParser
-#org.apache.tika.parser.strings.StringsParser
-#org.apache.tika.parser.xml.DcXMLParser
-#org.apache.tika.parser.xml.FictionBookParser
-#org.apache.tika.parser.xml.XMLParser
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-webarchive-module/src/main/java/org/apache/tika/parser/http/HttpParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-webarchive-module/src/main/java/org/apache/tika/parser/http/HttpParser.java
index 3bfa09a8f..ab1431a3f 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-webarchive-module/src/main/java/org/apache/tika/parser/http/HttpParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-webarchive-module/src/main/java/org/apache/tika/parser/http/HttpParser.java
@@ -32,6 +32,7 @@ import org.netpreserve.jwarc.MessageHeaders;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.extractor.EmbeddedDocumentExtractor;
 import org.apache.tika.extractor.EmbeddedDocumentUtil;
@@ -42,6 +43,7 @@ import org.apache.tika.parser.ParseContext;
 import org.apache.tika.parser.Parser;
 import org.apache.tika.sax.XHTMLContentHandler;
 
+@TikaComponent
 public class HttpParser implements Parser {
 
     private static final MediaType MEDIA_TYPE = MediaType.application("x-httpresponse");
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-webarchive-module/src/main/java/org/apache/tika/parser/wacz/WACZParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-webarchive-module/src/main/java/org/apache/tika/parser/wacz/WACZParser.java
index fcf4042df..d9f6fdb80 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-webarchive-module/src/main/java/org/apache/tika/parser/wacz/WACZParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-webarchive-module/src/main/java/org/apache/tika/parser/wacz/WACZParser.java
@@ -34,6 +34,7 @@ import org.apache.commons.io.input.CloseShieldInputStream;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.extractor.EmbeddedDocumentExtractor;
 import org.apache.tika.extractor.EmbeddedDocumentUtil;
@@ -45,6 +46,7 @@ import org.apache.tika.parser.ParseContext;
 import org.apache.tika.parser.Parser;
 import org.apache.tika.sax.XHTMLContentHandler;
 
+@TikaComponent
 public class WACZParser implements Parser {
 
     private static final Set<MediaType> SUPPORTED_TYPES = Collections.unmodifiableSet(
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-webarchive-module/src/main/java/org/apache/tika/parser/warc/WARCParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-webarchive-module/src/main/java/org/apache/tika/parser/warc/WARCParser.java
index 9aa1e2f2f..d893914c7 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-webarchive-module/src/main/java/org/apache/tika/parser/warc/WARCParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-webarchive-module/src/main/java/org/apache/tika/parser/warc/WARCParser.java
@@ -34,6 +34,7 @@ import org.netpreserve.jwarc.WarcResponse;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.exception.WriteLimitReachedException;
 import org.apache.tika.extractor.EmbeddedDocumentExtractor;
@@ -52,6 +53,7 @@ import org.apache.tika.utils.StringUtils;
 /**
  * This uses jwarc to parse warc files and arc files
  */
+@TikaComponent
 public class WARCParser implements Parser {
 
     private static final Set<MediaType> SUPPORTED_TYPES = Collections.unmodifiableSet(
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-webarchive-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-webarchive-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index 087707900..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-webarchive-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,19 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.parser.http.HttpParser
-org.apache.tika.parser.warc.WARCParser
-org.apache.tika.parser.wacz.WACZParser
-
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/tmx/TMXParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/tmx/TMXParser.java
index 5fce5de20..5d6622415 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/tmx/TMXParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/tmx/TMXParser.java
@@ -25,6 +25,7 @@ import org.apache.commons.io.input.CloseShieldInputStream;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
@@ -36,6 +37,7 @@ import org.apache.tika.utils.XMLReaderUtils;
 /**
  * Parser for Translation Memory eXchange (TMX) files.
  */
+@TikaComponent
 public class TMXParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xliff/XLIFF12Parser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xliff/XLIFF12Parser.java
index bd789802f..129aced98 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xliff/XLIFF12Parser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xliff/XLIFF12Parser.java
@@ -25,6 +25,7 @@ import org.apache.commons.io.input.CloseShieldInputStream;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
@@ -36,6 +37,7 @@ import org.apache.tika.utils.XMLReaderUtils;
 /**
  * Parser for XLIFF 1.2 files.
  */
+@TikaComponent
 public class XLIFF12Parser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xliff/XLZParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xliff/XLZParser.java
index 4bd947923..045d8a2cc 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xliff/XLZParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xliff/XLZParser.java
@@ -28,6 +28,7 @@ import java.util.zip.ZipInputStream;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TikaInputStream;
 import org.apache.tika.metadata.Metadata;
@@ -40,6 +41,7 @@ import org.apache.tika.sax.XHTMLContentHandler;
 /**
  * Parser for XLZ Archives.
  */
+@TikaComponent
 public class XLZParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xml/DcXMLParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xml/DcXMLParser.java
index a3c50c6cb..d87811ad4 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xml/DcXMLParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xml/DcXMLParser.java
@@ -18,6 +18,7 @@ package org.apache.tika.parser.xml;
 
 import org.xml.sax.ContentHandler;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.metadata.DublinCore;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.metadata.Property;
@@ -28,6 +29,7 @@ import org.apache.tika.sax.TeeContentHandler;
 /**
  * Dublin Core metadata parser
  */
+@TikaComponent
 public class DcXMLParser extends XMLParser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xml/FictionBookParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xml/FictionBookParser.java
index 094db7cc9..46300a1a7 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xml/FictionBookParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xml/FictionBookParser.java
@@ -26,6 +26,7 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.DefaultHandler;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.extractor.EmbeddedDocumentExtractor;
 import org.apache.tika.extractor.EmbeddedDocumentUtil;
 import org.apache.tika.io.TikaInputStream;
@@ -34,6 +35,7 @@ import org.apache.tika.metadata.TikaCoreProperties;
 import org.apache.tika.mime.MediaType;
 import org.apache.tika.parser.ParseContext;
 
+@TikaComponent
 public class FictionBookParser extends XMLParser {
     private static final long serialVersionUID = 4195954546491524374L;
 
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xml/XMLParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xml/XMLParser.java
index 30d48ea1d..b67395e9f 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xml/XMLParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xml/XMLParser.java
@@ -27,6 +27,7 @@ import org.apache.commons.io.input.CloseShieldInputStream;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.mime.MediaType;
@@ -41,6 +42,7 @@ import org.apache.tika.utils.XMLReaderUtils;
 /**
  * XML parser.
  */
+@TikaComponent(spi = false)
 public class XMLParser implements Parser {
 
     /**
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xml/XMLProfiler.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xml/XMLProfiler.java
index 86957ad7f..321f641cb 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xml/XMLProfiler.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/java/org/apache/tika/parser/xml/XMLProfiler.java
@@ -33,6 +33,7 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.DefaultHandler;
 
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.metadata.Property;
@@ -66,6 +67,7 @@ import org.apache.tika.utils.XMLReaderUtils;
  * xmp in other file formats.  Please open a ticket.
  * </p>
  */
+@TikaComponent(spi = false)
 public class XMLProfiler implements Parser {
 
 
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index 20d78d69c..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-xml-module/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,22 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.parser.tmx.TMXParser
-org.apache.tika.parser.xliff.XLIFF12Parser
-org.apache.tika.parser.xliff.XLZParser
-org.apache.tika.parser.xml.DcXMLParser
-org.apache.tika.parser.xml.FictionBookParser
-
-
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-zip-commons/src/main/java/org/apache/tika/detect/zip/DefaultZipContainerDetector.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-zip-commons/src/main/java/org/apache/tika/detect/zip/DefaultZipContainerDetector.java
index af6f0e68c..80bebf1e6 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-zip-commons/src/main/java/org/apache/tika/detect/zip/DefaultZipContainerDetector.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-zip-commons/src/main/java/org/apache/tika/detect/zip/DefaultZipContainerDetector.java
@@ -39,6 +39,7 @@ import org.slf4j.LoggerFactory;
 import org.apache.tika.config.Field;
 import org.apache.tika.config.LoadErrorHandler;
 import org.apache.tika.config.ServiceLoader;
+import org.apache.tika.config.TikaComponent;
 import org.apache.tika.detect.Detector;
 import org.apache.tika.io.BoundedInputStream;
 import org.apache.tika.io.TikaInputStream;
@@ -59,6 +60,7 @@ import org.apache.tika.mime.MediaType;
  * Finally, if the file is not detected as an archive format, this runs
  * commons-compress' compressor format detector.
  */
+@TikaComponent
 public class DefaultZipContainerDetector implements Detector {
 
     //Regrettably, some tiff files can be incorrectly identified
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-zip-commons/src/main/resources/META-INF/services/org.apache.tika.detect.Detector b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-zip-commons/src/main/resources/META-INF/services/org.apache.tika.detect.Detector
deleted file mode 100644
index e01e464da..000000000
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-zip-commons/src/main/resources/META-INF/services/org.apache.tika.detect.Detector
+++ /dev/null
@@ -1,16 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-org.apache.tika.detect.zip.DefaultZipContainerDetector
\ No newline at end of file
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-zip-commons/src/main/resources/META-INF/services/org.apache.tika.detect.zip.ZipContainerDetector b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-zip-commons/src/main/resources/META-INF/services/org.apache.tika.detect.zip.ZipContainerDetector
index 1d5544519..eb2bfe277 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-zip-commons/src/main/resources/META-INF/services/org.apache.tika.detect.zip.ZipContainerDetector
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-zip-commons/src/main/resources/META-INF/services/org.apache.tika.detect.zip.ZipContainerDetector
@@ -12,6 +12,8 @@
 #  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
+#These are a special type of detector, and we're currently relying on this
+#manual file.
 org.apache.tika.detect.zip.IPADetector
 org.apache.tika.detect.zip.JarDetector
 org.apache.tika.detect.zip.KMZDetector
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/pom.xml b/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/pom.xml
index f3df5629e..b1ca71e1a 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/pom.xml
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/pom.xml
@@ -176,6 +176,11 @@
       <version>${zstd.version}</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>tika-serialization</artifactId>
+      <scope>test</scope>
+    </dependency>
     <!-- For legal reasons (incompatible license), jai-imageio-jpeg2000 is to be used
     only in the tests and may not be distributed. See also LEGAL-195 -->
     <dependency>
@@ -190,6 +195,12 @@
       <version>${project.version}</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>tika-serialization</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
     <dependency>
       <groupId>${project.groupId}</groupId>
       <artifactId>tika-core</artifactId>
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/src/test/java/org/apache/tika/config/ComponentRegistryIntegrationTest.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/src/test/java/org/apache/tika/config/ComponentRegistryIntegrationTest.java
new file mode 100644
index 000000000..c30f1b0ec
--- /dev/null
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/src/test/java/org/apache/tika/config/ComponentRegistryIntegrationTest.java
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tika.config;
+
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.jupiter.api.Test;
+
+import org.apache.tika.config.loader.TikaLoader;
+import org.apache.tika.detect.Detector;
+import org.apache.tika.parser.AutoDetectParser;
+import org.apache.tika.parser.Parser;
+
+/**
+ * Integration tests to verify that the annotation processor correctly generated
+ * component index files mapping human-readable names to class names, and that
+ * JSON configuration can load components using these names.
+ */
+public class ComponentRegistryIntegrationTest {
+
+    @Test
+    public void testLoadDetectorByName() throws Exception {
+        // Load config that uses "poifs-container-detector" by name
+        Path jsonConfig = Paths.get(
+                getClass().getResource("/configs/test-detectors.json").toURI());
+        assertNotNull(jsonConfig, "test-detectors.json should exist");
+
+        TikaLoader loader = TikaLoader.load(jsonConfig);
+        Detector detector = loader.loadDetectors();
+
+        assertNotNull(detector, "Detector should be loaded");
+        // The detector will be wrapped in a CompositeDetector, so we need to check differently
+        // For now, just verify it loaded successfully
+    }
+
+    @Test
+    public void testLoadDefaultParser() throws Exception {
+        // Load config that uses "default-parser" by name
+        Path jsonConfig = Paths.get(
+                getClass().getResource("/configs/test-default-parser.json").toURI());
+        assertNotNull(jsonConfig, "test-default-parser.json should exist");
+
+        TikaLoader loader = TikaLoader.load(jsonConfig);
+        Parser parser = loader.loadAutoDetectParser();
+
+        assertNotNull(parser, "Parser should be loaded");
+        assertTrue(parser instanceof AutoDetectParser,
+                "Should have loaded AutoDetectParser (default-parser)");
+    }
+
+    @Test
+    public void testLoadDefaultParserWithExclusions() throws Exception {
+        // Load config that excludes "pdf-parser" and "html-parser" by name
+        // This verifies that the component names can be used in exclusion lists
+        Path jsonConfig = Paths.get(
+                getClass().getResource(
+                "/configs/test-default-with-exclusions.json").toURI());
+        assertNotNull(jsonConfig, "test-default-with-exclusions.json should exist");
+
+        TikaLoader loader = TikaLoader.load(jsonConfig);
+        Parser parser = loader.loadAutoDetectParser();
+
+        assertNotNull(parser, "Parser should be loaded");
+        assertTrue(parser instanceof AutoDetectParser,
+                "Should have loaded AutoDetectParser with exclusions");
+
+        // The config loaded successfully, which means it was able to resolve
+        // the component names "pdf-parser" and "html-parser" for exclusion
+    }
+
+    @Test
+    public void testLoadDcXmlParserByName() throws Exception {
+        // Load config that uses "dc-xml-parser" by name
+        // XMLParser has spi=false, but DcXMLParser should be available
+        Path jsonConfig = Paths.get(
+                getClass().getResource("/configs/test-dc-xml-parser.json").toURI());
+        assertNotNull(jsonConfig, "test-dc-xml-parser.json should exist");
+
+        TikaLoader loader = TikaLoader.load(jsonConfig);
+        Parser parser = loader.loadParsers();
+
+        assertNotNull(parser, "Parser should be loaded");
+        // The parser will be wrapped in a CompositeParser, check that it loaded successfully
+    }
+
+    @Test
+    public void testSpiFalseParserInIndexButNotInSpi() throws Exception {
+        // XMLParser has spi=false:
+        // - SHOULD be in index (for name-based configuration)
+        // - should NOT be in SPI (for auto-discovery via ServiceLoader)
+        Map<String, String> parserIndex = readAllIndexFiles("META-INF/tika/parsers.idx");
+
+        // Verify xml-parser IS in index for name-based config
+        assertTrue(parserIndex.containsKey("xml-parser"),
+                "xml-parser should be in index (spi=false still allows name-based config)");
+        assertEquals("org.apache.tika.parser.xml.XMLParser",
+                parserIndex.get("xml-parser"),
+                "xml-parser should map to XMLParser class");
+
+        // Verify dc-xml-parser is also in index
+        assertTrue(parserIndex.containsKey("dc-xml-parser"),
+                "dc-xml-parser should be in index");
+        assertEquals("org.apache.tika.parser.xml.DcXMLParser",
+                parserIndex.get("dc-xml-parser"),
+                "dc-xml-parser should map to DcXMLParser class");
+
+        // Verify xml-parser is NOT in SPI file
+        Map<String, Boolean> spiParsers = readAllSpiFiles("META-INF/services/org.apache.tika.parser.Parser");
+        assertFalse(spiParsers.containsKey("org.apache.tika.parser.xml.XMLParser"),
+                "XMLParser should NOT be in SPI (spi=false prevents auto-discovery)");
+    }
+
+    @Test
+    public void testIndexFilesFollowKebabCaseConvention() throws Exception {
+        // Test that all component names follow kebab-case convention
+        Map<String, String> parserIndex = readAllIndexFiles("META-INF/tika/parsers.idx");
+        Map<String, String> detectorIndex = readAllIndexFiles("META-INF/tika/detectors.idx");
+
+        assertFalse(parserIndex.isEmpty(), "Parser index should not be empty");
+        assertFalse(detectorIndex.isEmpty(), "Detector index should not be empty");
+
+        // Verify all names follow kebab-case convention
+        for (String name : parserIndex.keySet()) {
+            assertTrue(name.matches("[a-z0-9-]+"),
+                    "Parser name should be kebab-case: " + name);
+        }
+
+        for (String name : detectorIndex.keySet()) {
+            assertTrue(name.matches("[a-z0-9-]+"),
+                    "Detector name should be kebab-case: " + name);
+        }
+    }
+
+    @Test
+    public void testIndexFilesHaveCorrectFormat() throws Exception {
+        // Verify index files have Apache license and correct format
+        Enumeration<URL> resources = getClass().getClassLoader()
+                                               .getResources("META-INF/tika/parsers.idx");
+
+        assertTrue(resources.hasMoreElements(), "At least one parsers.idx should exist");
+
+        URL url = resources.nextElement();
+        try (BufferedReader reader = new BufferedReader(
+                new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) {
+            String firstLine = reader.readLine();
+            assertTrue(firstLine.contains("Licensed to the Apache Software Foundation"),
+                    "First line should contain Apache license");
+
+            String line;
+            boolean foundFormatComment = false;
+            boolean foundEntry = false;
+
+            while ((line = reader.readLine()) != null) {
+                if (line.startsWith("# Format:")) {
+                    foundFormatComment = true;
+                }
+                if (!line.startsWith("#") && !line.trim().isEmpty()) {
+                    foundEntry = true;
+                    // Verify format: name=fully.qualified.ClassName
+                    assertTrue(line.matches("[a-z0-9-]+=\\S+"),
+                            "Line should match format 'name=ClassName': " + line);
+                }
+            }
+
+            assertTrue(foundFormatComment, "Should have format comment");
+            assertTrue(foundEntry, "Should have at least one entry");
+        }
+    }
+
+    @Test
+    public void testNoDuplicateComponentNames() throws Exception {
+        // Verify no duplicate names across all index files
+        Map<String, String> parserIndex = readAllIndexFiles("META-INF/tika/parsers.idx");
+        Map<String, String> detectorIndex = readAllIndexFiles("META-INF/tika/detectors.idx");
+
+        // Check for duplicates within each index (readAllIndexFiles already handles this)
+        // The fact that we can build the maps without issues means no duplicates within files
+
+        // Verify all class names are fully qualified
+        for (Map.Entry<String, String> entry : parserIndex.entrySet()) {
+            String className = entry.getValue();
+            assertTrue(className.contains("."),
+                    "Parser class name should be fully qualified: " + className);
+            assertTrue(className.startsWith("org.apache.tika."),
+                    "Parser class should be in org.apache.tika package: " + className);
+        }
+
+        for (Map.Entry<String, String> entry : detectorIndex.entrySet()) {
+            String className = entry.getValue();
+            assertTrue(className.contains("."),
+                    "Detector class name should be fully qualified: " + className);
+            assertTrue(className.startsWith("org.apache.tika."),
+                    "Detector class should be in org.apache.tika package: " + className);
+        }
+    }
+
+    /**
+     * Reads all index files with the given resource path from all JARs on the classpath
+     * and merges them into a single map.
+     */
+    private Map<String, String> readAllIndexFiles(String resourcePath) throws Exception {
+        Map<String, String> mergedIndex = new HashMap<>();
+        Enumeration<URL> resources = getClass().getClassLoader().getResources(resourcePath);
+
+        assertTrue(resources.hasMoreElements(),
+                "At least one " + resourcePath + " file should exist");
+
+        while (resources.hasMoreElements()) {
+            URL url = resources.nextElement();
+            try (InputStream stream = url.openStream()) {
+                Map<String, String> index = readIndexFile(stream);
+                mergedIndex.putAll(index);
+            }
+        }
+
+        return mergedIndex;
+    }
+
+    /**
+     * Reads an index file in the format: name=fully.qualified.ClassName
+     */
+    private Map<String, String> readIndexFile(InputStream stream) throws Exception {
+        Map<String, String> index = new HashMap<>();
+        try (BufferedReader reader = new BufferedReader(
+                new InputStreamReader(stream, StandardCharsets.UTF_8))) {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                // Skip comments and empty lines
+                if (line.startsWith("#") || line.trim().isEmpty()) {
+                    continue;
+                }
+                String[] parts = line.split("=", 2);
+                if (parts.length == 2) {
+                    index.put(parts[0].trim(), parts[1].trim());
+                }
+            }
+        }
+        return index;
+    }
+
+    /**
+     * Reads all SPI service files from all JARs on the classpath.
+     * Returns a map of class names (as keys) to true for easy lookup.
+     */
+    private Map<String, Boolean> readAllSpiFiles(String resourcePath) throws Exception {
+        Map<String, Boolean> spiClasses = new HashMap<>();
+        Enumeration<URL> resources = getClass().getClassLoader().getResources(resourcePath);
+
+        assertTrue(resources.hasMoreElements(),
+                "At least one " + resourcePath + " file should exist");
+
+        while (resources.hasMoreElements()) {
+            URL url = resources.nextElement();
+            try (BufferedReader reader = new BufferedReader(
+                    new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) {
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    // Skip comments and empty lines
+                    line = line.trim();
+                    if (line.isEmpty() || line.startsWith("#")) {
+                        continue;
+                    }
+                    spiClasses.put(line, true);
+                }
+            }
+        }
+
+        return spiClasses;
+    }
+}
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/src/test/resources/configs/test-dc-xml-parser.json b/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/src/test/resources/configs/test-dc-xml-parser.json
new file mode 100644
index 000000000..9d21ebd1b
--- /dev/null
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/src/test/resources/configs/test-dc-xml-parser.json
@@ -0,0 +1,5 @@
+{
+  "parsers": [
+    {"dc-xml-parser": {}}
+  ]
+}
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/src/test/resources/configs/test-default-parser.json b/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/src/test/resources/configs/test-default-parser.json
new file mode 100644
index 000000000..69fa32d42
--- /dev/null
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/src/test/resources/configs/test-default-parser.json
@@ -0,0 +1,5 @@
+{
+  "parsers": [
+    {"default-parser": {}}
+  ]
+}
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/src/test/resources/configs/test-default-with-exclusions.json b/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/src/test/resources/configs/test-default-with-exclusions.json
new file mode 100644
index 000000000..ab69615f3
--- /dev/null
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/src/test/resources/configs/test-default-with-exclusions.json
@@ -0,0 +1,9 @@
+{
+  "parsers": [
+    {
+      "default-parser": {
+        "exclude": ["pdf-parser", "html-parser"]
+      }
+    }
+  ]
+}
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/src/test/resources/configs/test-detectors.json b/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/src/test/resources/configs/test-detectors.json
new file mode 100644
index 000000000..6c6461a67
--- /dev/null
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/src/test/resources/configs/test-detectors.json
@@ -0,0 +1,5 @@
+{
+  "detectors": [
+    {"poifs-container-detector": {}}
+  ]
+}
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/src/test/resources/configs/test-specific-parsers.json b/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/src/test/resources/configs/test-specific-parsers.json
new file mode 100644
index 000000000..0b2ec60d4
--- /dev/null
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/src/test/resources/configs/test-specific-parsers.json
@@ -0,0 +1,7 @@
+{
+  "parsers": [
+    {"pdf-parser": {}},
+    {"html-parser": {}},
+    {"xml-parser": {}}
+  ]
+}
diff --git a/tika-serialization/pom.xml b/tika-serialization/pom.xml
index f5eea54f9..2d8d28450 100644
--- a/tika-serialization/pom.xml
+++ b/tika-serialization/pom.xml
@@ -47,6 +47,13 @@
       <version>${project.version}</version>
       <scope>provided</scope>
     </dependency>
+    <!-- Annotation processor - contains @TikaComponent -->
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>tika-annotation-processor</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
     <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-core</artifactId>
@@ -64,6 +71,19 @@
   </dependencies>
   <build>
     <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <annotationProcessorPaths>
+            <path>
+              <groupId>${project.groupId}</groupId>
+              <artifactId>tika-annotation-processor</artifactId>
+              <version>${project.version}</version>
+            </path>
+          </annotationProcessorPaths>
+        </configuration>
+      </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-jar-plugin</artifactId>
diff --git a/tika-serialization/src/main/java/org/apache/tika/config/loader/ConfigLoader.java b/tika-serialization/src/main/java/org/apache/tika/config/loader/ConfigLoader.java
new file mode 100644
index 000000000..c0085e549
--- /dev/null
+++ b/tika-serialization/src/main/java/org/apache/tika/config/loader/ConfigLoader.java
@@ -0,0 +1,349 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tika.config.loader;
+
+import java.lang.reflect.Modifier;
+import java.util.Set;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import org.apache.tika.exception.TikaConfigException;
+
+/**
+ * Loader for simple configuration objects from JSON.
+ * <p>
+ * This class handles straightforward POJOs that can be deserialized directly from JSON.
+ * For complex components like Parsers, Detectors, etc., use the specific methods on
+ * {@link TikaLoader} instead (e.g., {@code loadParsers()}, {@code loadDetectors()}).
+ *
+ * <p>Usage:
+ * <pre>
+ * TikaLoader loader = TikaLoader.load(configPath);
+ *
+ * // Load by explicit key
+ * HandlerConfig config = loader.configs().load("handler-config", HandlerConfig.class);
+ *
+ * // Load by class name (auto-converts to kebab-case)
+ * HandlerConfig config = loader.configs().load(HandlerConfig.class);
+ * </pre>
+ */
+public class ConfigLoader {
+
+    /**
+     * Reserved keys for complex components that require special handling.
+     * These cannot be loaded via ConfigLoader - use TikaLoader methods instead.
+     */
+    private static final Set<String> PROHIBITED_KEYS =
+            Set.of("parsers", "detectors", "encoding-detectors", "encodingDetectors", "metadata-filters", "metadataFilters", "renderers", "translators");
+
+    private static final Set<String> PROHIBITED_CLASSES =
+            Set.of("org.apache.tika.parser.Parser", "org.apache.tika.detect.Detector",
+                    "org.apache.tika.renderer.Renderer",
+                    "org.apache.tika.detect.EncodingDetector", "org.apache.tika.metadata.filter.MetadataFilter");
+
+    private final TikaJsonConfig config;
+    private final ObjectMapper objectMapper;
+
+    ConfigLoader(TikaJsonConfig config, ObjectMapper objectMapper) {
+        this.config = config;
+        this.objectMapper = objectMapper;
+    }
+
+    /**
+     * Loads a configuration object using the class name converted to kebab-case.
+     * <p>
+     * For example, {@code HandlerConfig.class} will look for key "handler-config".
+     * Class name suffixes like "Config", "Configuration", "Settings" are stripped first.
+     * <p>
+     * For interfaces, the JSON must specify the implementation (see {@link #load(String, Class)}).
+     *
+     * @param clazz The class to deserialize into (can be interface, abstract, or concrete)
+     * @param <T> The type to load
+     * @return the deserialized object, or null if key not found in config
+     * @throws TikaConfigException if loading fails or class is not instantiable
+     */
+    public <T> T load(Class<T> clazz) throws TikaConfigException {
+        String key = deriveKeyFromClass(clazz);
+        return load(key, clazz);
+    }
+
+    /**
+     * Loads a configuration object using the class name, with a default value.
+     *
+     * @param clazz The class to deserialize into
+     * @param defaultValue The value to return if key not found in config
+     * @param <T> The type to load
+     * @return the deserialized object, or defaultValue if not present
+     * @throws TikaConfigException if loading fails or class is not instantiable
+     */
+    public <T> T load(Class<T> clazz, T defaultValue) throws TikaConfigException {
+        T result = load(clazz);
+        return result != null ? result : defaultValue;
+    }
+
+    /**
+     * Loads a configuration object from the specified JSON key.
+     * <p>
+     * Supports three formats for interfaces:
+     * <ul>
+     *   <li>String value: treated as class name or component name to look up</li>
+     *   <li>Object with "@class": explicit type specification</li>
+     *   <li>Object without "@class": attempts direct deserialization (works for concrete classes)</li>
+     * </ul>
+     *
+     * @param key The JSON key to load from
+     * @param clazz The class to deserialize into (can be interface, abstract, or concrete)
+     * @param <T> The type to load
+     * @return the deserialized object, or null if key not found
+     * @throws TikaConfigException if loading fails or class cannot be instantiated
+     */
+    public <T> T load(String key, Class<T> clazz) throws TikaConfigException {
+        validateKey(key);
+        validateClass(clazz);
+
+        JsonNode node = config.getRootNode().get(key);
+        if (node == null || node.isNull()) {
+            return null;
+        }
+
+        try {
+            // Strategy 1: String value - treat as class name
+            if (node.isTextual()) {
+                return loadFromClassName(node.asText(), clazz);
+            }
+
+            // Strategy 2: Object with @class field - explicit type
+            if (node.isObject() && node.has("@class")) {
+                String className = node.get("@class").asText();
+                Class<?> targetClass = Class.forName(className);
+                if (!clazz.isAssignableFrom(targetClass)) {
+                    throw new TikaConfigException(
+                        "Class " + className + " is not assignable to " + clazz.getName());
+                }
+                // Remove @class field before deserializing (Jackson doesn't recognize it)
+                ObjectNode objectNode = ((ObjectNode) node).deepCopy();
+                objectNode.remove("@class");
+                return objectMapper.treeToValue(objectNode, (Class<T>) targetClass);
+            }
+
+            // Strategy 3: Direct deserialization (for concrete classes)
+            if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) {
+                throw new TikaConfigException(
+                    "Cannot deserialize " + clazz.getName() + " - it is " +
+                    (clazz.isInterface() ? "an interface" : "abstract") + ". " +
+                    "Specify implementation using:\n" +
+                    "  - String value: \"" + key + "\": \"com.example.MyImpl\"\n" +
+                    "  - Object with @class: \"" + key + "\": {\"@class\": \"com.example.MyImpl\", ...}");
+            }
+
+            return objectMapper.treeToValue(node, clazz);
+        } catch (ClassNotFoundException e) {
+            throw new TikaConfigException(
+                "Class not found for '" + key + "': " + e.getMessage(), e);
+        } catch (JsonProcessingException e) {
+            throw new TikaConfigException(
+                "Failed to deserialize '" + key + "' into " + clazz.getName(), e);
+        }
+    }
+
+    /**
+     * Loads a class from a string (fully qualified class name).
+     */
+    @SuppressWarnings("unchecked")
+    private <T> T loadFromClassName(String className, Class<T> expectedType)
+            throws TikaConfigException {
+        try {
+            Class<?> clazz = Class.forName(className);
+            if (!expectedType.isAssignableFrom(clazz)) {
+                throw new TikaConfigException(
+                    "Class " + className + " is not assignable to " + expectedType.getName());
+            }
+
+            // Try to instantiate with no-arg constructor
+            return (T) clazz.getDeclaredConstructor().newInstance();
+        } catch (ClassNotFoundException e) {
+            throw new TikaConfigException("Class not found: " + className, e);
+        } catch (ReflectiveOperationException e) {
+            throw new TikaConfigException(
+                "Failed to instantiate " + className +
+                ". Ensure it has a public no-argument constructor.", e);
+        }
+    }
+
+    /**
+     * Loads a configuration object from the specified JSON key, with a default value.
+     *
+     * @param key The JSON key to load from
+     * @param clazz The class to deserialize into
+     * @param defaultValue The value to return if key not found in config
+     * @param <T> The type to load
+     * @return the deserialized object, or defaultValue if not present
+     * @throws TikaConfigException if loading fails or class is not instantiable
+     */
+    public <T> T load(String key, Class<T> clazz, T defaultValue) throws TikaConfigException {
+        T result = load(key, clazz);
+        return result != null ? result : defaultValue;
+    }
+
+    /**
+     * Loads a configuration object by merging JSON properties into a copy of the default instance.
+     * <p>
+     * This allows partial configuration where only some properties are specified in JSON,
+     * and the rest retain their default values. The original defaultValue object is NOT modified.
+     *
+     * <p>Example:
+     * <pre>
+     * HandlerConfig defaults = new HandlerConfig();
+     * defaults.setTimeout(30000);
+     * defaults.setRetries(2);
+     * defaults.setEnabled(false);
+     *
+     * // JSON: { "enabled": true }
+     * // Result: timeout=30000, retries=2, enabled=true (merged!)
+     * // Note: 'defaults' object remains unchanged
+     * HandlerConfig config = loader.configs().loadWithDefaults("handler-config",
+     *                                                           HandlerConfig.class,
+     *                                                           defaults);
+     * </pre>
+     *
+     * @param key The JSON key to load from
+     * @param clazz The class type (not used for deserialization, but for type safety)
+     * @param defaultValue The object with default values (will NOT be modified)
+     * @param <T> The type to load
+     * @return a new object with defaults merged with JSON properties, or the original default if key not found
+     * @throws TikaConfigException if loading fails
+     */
+    public <T> T loadWithDefaults(String key, Class<T> clazz, T defaultValue)
+            throws TikaConfigException {
+        validateKey(key);
+        validateClass(clazz);
+
+        JsonNode node = config.getRootNode().get(key);
+        if (node == null || node.isNull()) {
+            return defaultValue;
+        }
+
+        try {
+            // Create a deep copy of defaultValue to avoid mutating the original
+            // Using convertValue is efficient and doesn't require serializing to bytes
+            @SuppressWarnings("unchecked")
+            T copy = objectMapper.convertValue(defaultValue, (Class<T>) defaultValue.getClass());
+
+            // Merge JSON properties into the copy
+            return objectMapper.readerForUpdating(copy).readValue(node);
+        } catch (Exception e) {
+            throw new TikaConfigException(
+                "Failed to merge '" + key + "' into " + clazz.getName(), e);
+        }
+    }
+
+    /**
+     * Loads a configuration object by class name with defaults, merging JSON properties.
+     *
+     * @param clazz The class to deserialize into
+     * @param defaultValue The object with default values to merge into
+     * @param <T> The type to load
+     * @return the default object updated with JSON properties, or the original default if key not found
+     * @throws TikaConfigException if loading fails
+     */
+    public <T> T loadWithDefaults(Class<T> clazz, T defaultValue) throws TikaConfigException {
+        String key = deriveKeyFromClass(clazz);
+        return loadWithDefaults(key, clazz, defaultValue);
+    }
+
+    /**
+     * Checks if a configuration key exists in the JSON config.
+     *
+     * @param key The JSON key to check
+     * @return true if the key exists and is not null
+     */
+    public boolean hasKey(String key) {
+        JsonNode node = config.getRootNode().get(key);
+        return node != null && !node.isNull();
+    }
+
+    /**
+     * Derives a kebab-case key from a class name.
+     * <p>
+     * Uses the full class name converted to kebab-case for consistency with
+     * the annotation processor's component naming.
+     *
+     * @param clazz the class to derive the key from
+     * @return kebab-case version of the class name
+     */
+    private String deriveKeyFromClass(Class<?> clazz) {
+        String simpleName = clazz.getSimpleName();
+        return toKebabCase(simpleName);
+    }
+
+    /**
+     * Converts a camelCase or PascalCase string to kebab-case.
+     * Delegates to {@link KebabCaseConverter} for consistent behavior
+     * with the annotation processor.
+     */
+    private String toKebabCase(String name) {
+        return KebabCaseConverter.toKebabCase(name);
+    }
+
+    /**
+     * Validates that the key is not reserved for complex components.
+     */
+    private void validateKey(String key) throws TikaConfigException {
+        if (PROHIBITED_KEYS.contains(key)) {
+            throw new TikaConfigException(
+                "Cannot load '" + key + "' via ConfigLoader. " +
+                "This is a complex component that requires special handling. " +
+                "Use TikaLoader.load" + toPascalCase(key) + "() instead.");
+        }
+    }
+
+    /**
+     * Validates that complex Tika components aren't loaded via this method.
+     * Interfaces and abstract classes are allowed, but require explicit type info in JSON.
+     */
+    private void validateClass(Class<?> clazz) throws TikaConfigException {
+        // Check for known complex component types (defense in depth)
+        String className = clazz.getName();
+        if (PROHIBITED_CLASSES.contains(className)) {
+            throw new TikaConfigException(
+                clazz.getSimpleName() + " is a Tika component interface. " +
+                "Use the appropriate TikaLoader method (e.g., loadParsers(), loadDetectors()).");
+        }
+    }
+
+    /**
+     * Converts kebab-case to PascalCase for error messages.
+     */
+    private String toPascalCase(String kebabCase) {
+        StringBuilder result = new StringBuilder();
+        boolean capitalizeNext = true;
+        for (char c : kebabCase.toCharArray()) {
+            if (c == '-') {
+                capitalizeNext = true;
+            } else if (capitalizeNext) {
+                result.append(Character.toUpperCase(c));
+                capitalizeNext = false;
+            } else {
+                result.append(c);
+            }
+        }
+        return result.toString();
+    }
+}
diff --git a/tika-serialization/src/main/java/org/apache/tika/config/loader/KebabCaseConverter.java b/tika-serialization/src/main/java/org/apache/tika/config/loader/KebabCaseConverter.java
new file mode 100644
index 000000000..8a12a5033
--- /dev/null
+++ b/tika-serialization/src/main/java/org/apache/tika/config/loader/KebabCaseConverter.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tika.config.loader;
+
+import java.util.Locale;
+
+/**
+ * Utility for converting Java class names to kebab-case.
+ * Used for automatic component name generation from class names.
+ *
+ * <p><strong>Note:</strong> This is a copy of the implementation in
+ * {@code org.apache.tika.annotation.KebabCaseConverter} to avoid
+ * a runtime dependency on the annotation processor module. The two
+ * implementations must be kept in sync.
+ *
+ * <p>Examples:
+ * <ul>
+ *   <li>PDFParser → pdf-parser</li>
+ *   <li>OCRParser → ocr-parser</li>
+ *   <li>HTMLParser → html-parser</li>
+ *   <li>DefaultParser → default-parser</li>
+ *   <li>TesseractOCRParser → tesseract-ocr-parser</li>
+ * </ul>
+ */
+class KebabCaseConverter {
+
+    private KebabCaseConverter() {
+        // Utility class
+    }
+
+    /**
+     * Converts a Java class name to kebab-case.
+     *
+     * @param className the simple class name (without package)
+     * @return the kebab-case version of the name
+     */
+    static String toKebabCase(String className) {
+        if (className == null || className.isEmpty()) {
+            return className;
+        }
+
+        // Insert hyphen before uppercase letters that follow lowercase letters
+        // or before uppercase letters that are followed by lowercase letters
+        String result = className
+                // Insert hyphen between lowercase and uppercase: "aB" -> "a-B"
+                .replaceAll("([a-z])([A-Z])", "$1-$2")
+                // Insert hyphen before uppercase letter followed by lowercase
+                // in a sequence of uppercase letters: "HTMLParser" -> "HTML-Parser"
+                .replaceAll("([A-Z]+)([A-Z][a-z])", "$1-$2")
+                // Insert hyphen between letter and digit: "PDF2Text" -> "PDF2-Text"
+                .replaceAll("([a-zA-Z])(\\d)", "$1-$2")
+                // Insert hyphen between digit and letter: "2Text" -> "2-Text"
+                .replaceAll("(\\d)([a-zA-Z])", "$1-$2")
+                .toLowerCase(Locale.ROOT);
+
+        return result;
+    }
+}
diff --git a/tika-serialization/src/main/java/org/apache/tika/config/loader/ParserLoader.java b/tika-serialization/src/main/java/org/apache/tika/config/loader/ParserLoader.java
index 96f668af8..5cfc3cd11 100644
--- a/tika-serialization/src/main/java/org/apache/tika/config/loader/ParserLoader.java
+++ b/tika-serialization/src/main/java/org/apache/tika/config/loader/ParserLoader.java
@@ -211,12 +211,14 @@ public class ParserLoader {
     @SuppressWarnings("unchecked")
     private Parser instantiateParser(Class<?> parserClass, String configJson)
             throws TikaConfigException {
+
         try {
             // Try constructor with String parameter (JSON config)
             try {
                 Constructor<?> constructor = parserClass.getConstructor(String.class);
                 return (Parser) constructor.newInstance(configJson);
             } catch (NoSuchMethodException e) {
+                // TODO -- entrypoint for actual configuration
                 // Fall back to zero-arg constructor
                 return (Parser) ServiceLoaderUtils.newInstance(parserClass,
                         new org.apache.tika.config.ServiceLoader(classLoader));
diff --git a/tika-serialization/src/main/java/org/apache/tika/config/loader/TikaJsonConfig.java b/tika-serialization/src/main/java/org/apache/tika/config/loader/TikaJsonConfig.java
index 7b7bf1c0b..fe8464dd3 100644
--- a/tika-serialization/src/main/java/org/apache/tika/config/loader/TikaJsonConfig.java
+++ b/tika-serialization/src/main/java/org/apache/tika/config/loader/TikaJsonConfig.java
@@ -26,6 +26,7 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
+import com.fasterxml.jackson.databind.DeserializationFeature;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 
@@ -57,7 +58,25 @@ import org.apache.tika.exception.TikaConfigException;
  */
 public class TikaJsonConfig {
 
-    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+    private static final ObjectMapper OBJECT_MAPPER = createObjectMapper();
+
+    private static ObjectMapper createObjectMapper() {
+        ObjectMapper mapper = new ObjectMapper();
+
+        // Fail on unknown properties to catch configuration errors early
+        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
+
+        // Prevent null values being assigned to primitive fields (int, boolean, etc.)
+        mapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);
+
+        // Ensure enums are properly validated (not just numeric values)
+        mapper.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, true);
+
+        // Catch duplicate keys in JSON objects
+        mapper.configure(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY, true);
+
+        return mapper;
+    }
 
     private final JsonNode rootNode;
     private final Map<String, Map<String, JsonNode>> componentsByType;
diff --git a/tika-serialization/src/main/java/org/apache/tika/config/loader/TikaLoader.java b/tika-serialization/src/main/java/org/apache/tika/config/loader/TikaLoader.java
index 867481d45..2432ed28a 100644
--- a/tika-serialization/src/main/java/org/apache/tika/config/loader/TikaLoader.java
+++ b/tika-serialization/src/main/java/org/apache/tika/config/loader/TikaLoader.java
@@ -16,6 +16,7 @@
  */
 package org.apache.tika.config.loader;
 
+import java.io.IOException;
 import java.nio.file.Path;
 import java.util.List;
 
@@ -29,6 +30,9 @@ import org.apache.tika.exception.TikaConfigException;
 import org.apache.tika.metadata.filter.CompositeMetadataFilter;
 import org.apache.tika.metadata.filter.MetadataFilter;
 import org.apache.tika.mime.MediaTypeRegistry;
+import org.apache.tika.parser.AutoDetectParser;
+import org.apache.tika.parser.AutoDetectParserConfig;
+import org.apache.tika.parser.CompositeParser;
 import org.apache.tika.parser.Parser;
 import org.apache.tika.renderer.CompositeRenderer;
 import org.apache.tika.renderer.Renderer;
@@ -78,6 +82,7 @@ public class TikaLoader {
     private EncodingDetector encodingDetectors;
     private MetadataFilter metadataFilter;
     private Renderer renderers;
+    private ConfigLoader configLoader;
 
     private TikaLoader(TikaJsonConfig config, ClassLoader classLoader,
                        MediaTypeRegistry mediaTypeRegistry) {
@@ -222,6 +227,36 @@ public class TikaLoader {
         return renderers;
     }
 
+    public Parser loadAutoDetectParser() throws TikaConfigException, IOException {
+        AutoDetectParserConfig adpConfig = configs().load(AutoDetectParserConfig.class);
+        if (adpConfig == null) {
+            adpConfig = new AutoDetectParserConfig();
+        }
+        return AutoDetectParser.build((CompositeParser)loadParsers(), loadDetectors(), adpConfig);
+    }
+
+    /**
+     * Returns a ConfigLoader for loading simple configuration objects.
+     * <p>
+     * Use this for POJOs and simple config classes. For complex components like
+     * Parsers, Detectors, etc., use the specific load methods on TikaLoader.
+     *
+     * <p>Usage:
+     * <pre>
+     * HandlerConfig config = loader.configs().load("handler-config", HandlerConfig.class);
+     * // Or use kebab-case auto-conversion:
+     * HandlerConfig config = loader.configs().load(HandlerConfig.class);
+     * </pre>
+     *
+     * @return the ConfigLoader instance
+     */
+    public synchronized ConfigLoader configs() {
+        if (configLoader == null) {
+            configLoader = new ConfigLoader(config, objectMapper);
+        }
+        return configLoader;
+    }
+
     /**
      * Gets the underlying JSON configuration.
      *
diff --git a/tika-serialization/src/test/java/org/apache/tika/config/loader/ConfigLoaderTest.java b/tika-serialization/src/test/java/org/apache/tika/config/loader/ConfigLoaderTest.java
new file mode 100644
index 000000000..be207dba3
--- /dev/null
+++ b/tika-serialization/src/test/java/org/apache/tika/config/loader/ConfigLoaderTest.java
@@ -0,0 +1,646 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tika.config.loader;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.apache.tika.exception.TikaConfigException;
+
+/**
+ * Unit tests for {@link ConfigLoader}.
+ */
+public class ConfigLoaderTest {
+
+    private TikaLoader tikaLoader;
+    private ConfigLoader configLoader;
+
+    @BeforeEach
+    public void setUp() throws Exception {
+        Path configPath = Paths.get(
+                getClass().getResource("/configs/test-config-loader.json").toURI());
+        tikaLoader = TikaLoader.load(configPath);
+        configLoader = tikaLoader.configs();
+    }
+
+    // ==================== Test POJOs ====================
+
+    /**
+     * Simple config POJO with properties.
+     */
+    public static class HandlerConfig {
+        private int timeout;
+        private int retries;
+        private boolean enabled;
+
+        public int getTimeout() {
+            return timeout;
+        }
+
+        public void setTimeout(int timeout) {
+            this.timeout = timeout;
+        }
+
+        public int getRetries() {
+            return retries;
+        }
+
+        public void setRetries(int retries) {
+            this.retries = retries;
+        }
+
+        public boolean isEnabled() {
+            return enabled;
+        }
+
+        public void setEnabled(boolean enabled) {
+            this.enabled = enabled;
+        }
+    }
+
+    /**
+     * Config class with suffix that should be stripped.
+     */
+    public static class TikaTaskTimeout {
+        private long millis;
+
+        public long getMillis() {
+            return millis;
+        }
+
+        public void setMillis(long millis) {
+            this.millis = millis;
+        }
+    }
+
+    /**
+     * Config class with "Settings" suffix.
+     */
+    public static class MyFeatureSettings {
+        private String featureName;
+        private int priority;
+
+        public String getFeatureName() {
+            return featureName;
+        }
+
+        public void setFeatureName(String featureName) {
+            this.featureName = featureName;
+        }
+
+        public int getPriority() {
+            return priority;
+        }
+
+        public void setPriority(int priority) {
+            this.priority = priority;
+        }
+    }
+
+    /**
+     * Interface for testing interface handling.
+     */
+    public interface TestHandler {
+        String getName();
+    }
+
+    /**
+     * Simple implementation with no-arg constructor.
+     */
+    public static class SimpleHandlerImpl implements TestHandler {
+        public SimpleHandlerImpl() {
+        }
+
+        @Override
+        public String getName() {
+            return "simple";
+        }
+    }
+
+    /**
+     * Implementation with configuration properties.
+     */
+    public static class ConfiguredHandlerImpl implements TestHandler {
+        private int maxSize;
+        private String prefix;
+
+        public ConfiguredHandlerImpl() {
+        }
+
+        @Override
+        public String getName() {
+            return "configured";
+        }
+
+        public int getMaxSize() {
+            return maxSize;
+        }
+
+        public void setMaxSize(int maxSize) {
+            this.maxSize = maxSize;
+        }
+
+        public String getPrefix() {
+            return prefix;
+        }
+
+        public void setPrefix(String prefix) {
+            this.prefix = prefix;
+        }
+    }
+
+    /**
+     * Abstract class for testing abstract class handling.
+     */
+    public abstract static class AbstractHandler implements TestHandler {
+        public abstract void doSomething();
+    }
+
+    // ==================== Tests ====================
+
+    @Test
+    public void testLoadByExplicitKey() throws Exception {
+        HandlerConfig config = configLoader.load("handler-config", HandlerConfig.class);
+
+        assertNotNull(config);
+        assertEquals(5000, config.getTimeout());
+        assertEquals(3, config.getRetries());
+        assertTrue(config.isEnabled());
+    }
+
+    @Test
+    public void testLoadByClassNameKebabCase() throws Exception {
+        HandlerConfig config = configLoader.load(HandlerConfig.class);
+
+        assertNotNull(config);
+        assertEquals(5000, config.getTimeout());
+    }
+
+    @Test
+    public void testLoadByClassNameTikaTaskTimeout() throws Exception {
+        // TikaTaskTimeout -> "tika-task-timeout" (no suffix stripping)
+        // JSON has "tika-task-timeout"
+        TikaTaskTimeout timeout = configLoader.load(TikaTaskTimeout.class);
+
+        assertNotNull(timeout);
+        assertEquals(30000, timeout.getMillis());
+    }
+
+    @Test
+    public void testLoadByClassNameMyFeatureSettings() throws Exception {
+        // MyFeatureSettings -> "my-feature-settings" (full name, no suffix stripping)
+        // JSON has "my-feature-settings"
+        MyFeatureSettings settings = configLoader.load(MyFeatureSettings.class);
+
+        assertNotNull(settings);
+        assertEquals("test-feature", settings.getFeatureName());
+        assertEquals(10, settings.getPriority());
+    }
+
+    @Test
+    public void testLoadWithDefaultValue() throws Exception {
+        HandlerConfig config = configLoader.load("handler-config", HandlerConfig.class);
+        assertNotNull(config);
+
+        // Non-existent key with default
+        HandlerConfig defaultConfig = new HandlerConfig();
+        defaultConfig.setTimeout(9999);
+
+        HandlerConfig result = configLoader.load("non-existent", HandlerConfig.class, defaultConfig);
+        assertEquals(9999, result.getTimeout());
+    }
+
+    @Test
+    public void testLoadMissingKeyReturnsNull() throws Exception {
+        HandlerConfig config = configLoader.load("non-existent-key", HandlerConfig.class);
+        assertNull(config);
+    }
+
+    @Test
+    public void testLoadInterfaceAsString() throws Exception {
+        // JSON: "simple-handler": "org.apache.tika.config.loader.ConfigLoaderTest$SimpleHandlerImpl"
+        TestHandler handler = configLoader.load("simple-handler", TestHandler.class);
+
+        assertNotNull(handler);
+        assertTrue(handler instanceof SimpleHandlerImpl);
+        assertEquals("simple", handler.getName());
+    }
+
+    @Test
+    public void testLoadInterfaceWithAtClassAndProperties() throws Exception {
+        // JSON: "configured-handler": { "@class": "...", "maxSize": 100000, ... }
+        TestHandler handler = configLoader.load("configured-handler", TestHandler.class);
+
+        assertNotNull(handler);
+        assertTrue(handler instanceof ConfiguredHandlerImpl);
+        assertEquals("configured", handler.getName());
+
+        ConfiguredHandlerImpl impl = (ConfiguredHandlerImpl) handler;
+        assertEquals(100000, impl.getMaxSize());
+        assertEquals("test-", impl.getPrefix());
+    }
+
+    @Test
+    public void testLoadInterfaceWithoutTypeInfoFails() throws Exception {
+        // Create a minimal config with just properties, no @class
+        Path configPath = Paths.get(
+                getClass().getResource("/configs/test-interface-no-type.json").toURI());
+        TikaLoader loader = TikaLoader.load(configPath);
+
+        TikaConfigException ex = assertThrows(TikaConfigException.class, () ->
+                loader.configs().load("handler-no-type", TestHandler.class));
+
+        assertTrue(ex.getMessage().contains("interface"));
+        assertTrue(ex.getMessage().contains("@class"));
+    }
+
+    @Test
+    public void testLoadAbstractClassFails() throws Exception {
+        TikaConfigException ex = assertThrows(TikaConfigException.class, () ->
+                configLoader.load("abstract-handler", AbstractHandler.class));
+
+        assertTrue(ex.getMessage().contains("abstract"));
+    }
+
+    @Test
+    public void testLoadProhibitedKeyParsers() throws Exception {
+        TikaConfigException ex = assertThrows(TikaConfigException.class, () ->
+                configLoader.load("parsers", Object.class));
+
+        assertTrue(ex.getMessage().contains("Cannot load 'parsers'"));
+        assertTrue(ex.getMessage().contains("TikaLoader"));
+    }
+
+    @Test
+    public void testLoadProhibitedKeyDetectors() throws Exception {
+        TikaConfigException ex = assertThrows(TikaConfigException.class, () ->
+                configLoader.load("detectors", Object.class));
+
+        assertTrue(ex.getMessage().contains("Cannot load 'detectors'"));
+    }
+
+    @Test
+    public void testLoadProhibitedKeyMetadataFilters() throws Exception {
+        TikaConfigException ex = assertThrows(TikaConfigException.class, () ->
+                configLoader.load("metadata-filters", Object.class));
+
+        assertTrue(ex.getMessage().contains("Cannot load 'metadata-filters'"));
+    }
+
+    @Test
+    public void testHasKey() throws Exception {
+        assertTrue(configLoader.hasKey("handler-config"));
+        assertTrue(configLoader.hasKey("simple-handler"));
+        assertFalse(configLoader.hasKey("non-existent"));
+    }
+
+    @Test
+    public void testLoadInvalidClassName() throws Exception {
+        Path configPath = Paths.get(
+                getClass().getResource("/configs/test-invalid-class.json").toURI());
+        TikaLoader loader = TikaLoader.load(configPath);
+
+        TikaConfigException ex = assertThrows(TikaConfigException.class, () ->
+                loader.configs().load("handler", TestHandler.class));
+
+        assertTrue(ex.getMessage().contains("Class not found"));
+    }
+
+    @Test
+    public void testLoadWrongTypeAssignment() throws Exception {
+        // String class name that doesn't implement the interface
+        Path configPath = Paths.get(
+                getClass().getResource("/configs/test-wrong-type.json").toURI());
+        TikaLoader loader = TikaLoader.load(configPath);
+
+        TikaConfigException ex = assertThrows(TikaConfigException.class, () ->
+                loader.configs().load("handler", TestHandler.class));
+
+        assertTrue(ex.getMessage().contains("not assignable"));
+    }
+
+    @Test
+    public void testLoadWithUnexpectedFieldFails() throws Exception {
+        // Verify that unexpected/unrecognized fields cause an exception
+        Path configPath = Paths.get(
+                getClass().getResource("/configs/test-unexpected-field.json").toURI());
+        TikaLoader loader = TikaLoader.load(configPath);
+
+        TikaConfigException ex = assertThrows(TikaConfigException.class, () ->
+                loader.configs().load("handler-config", HandlerConfig.class));
+
+        // Should contain information about the unrecognized field
+        assertTrue(ex.getMessage().contains("handler-config") ||
+                   ex.getCause().getMessage().contains("Unrecognized") ||
+                   ex.getCause().getMessage().contains("unexpectedField"),
+                   "Exception should mention the unrecognized field");
+    }
+
+    @Test
+    public void testKebabCaseConversion() throws Exception {
+        // Test that kebab-case conversion works correctly
+        // MyFeatureSettings should look for "my-feature-settings" (full kebab-case, no stripping)
+        MyFeatureSettings settings = configLoader.load(MyFeatureSettings.class);
+        assertNotNull(settings);
+        assertEquals("test-feature", settings.getFeatureName());
+    }
+
+    @Test
+    public void testLoadByClassWithDefault() throws Exception {
+        HandlerConfig config = configLoader.load(HandlerConfig.class);
+        assertNotNull(config);
+
+        // Non-existent class
+        TikaTaskTimeout defaultTimeout = new TikaTaskTimeout();
+        defaultTimeout.setMillis(60000);
+
+        // Use a class name that won't match
+        TikaTaskTimeout result = configLoader.load("NonExistentConfig.class",
+                                                    TikaTaskTimeout.class,
+                                                    defaultTimeout);
+        assertEquals(60000, result.getMillis());
+    }
+
+    // ==================== Tests for loadWithDefaults (Partial Config) ====================
+
+    @Test
+    public void testLoadWithDefaultsPartialConfig() throws Exception {
+        // Load config that merges defaults with partial JSON
+        Path configPath = Paths.get(
+                getClass().getResource("/configs/test-partial-config.json").toURI());
+        TikaLoader loader = TikaLoader.load(configPath);
+
+        // Set up defaults
+        HandlerConfig defaults = new HandlerConfig();
+        defaults.setTimeout(30000);
+        defaults.setRetries(2);
+        defaults.setEnabled(false);
+
+        // JSON only has: { "enabled": true }
+        HandlerConfig config = loader.configs().loadWithDefaults("handler-config",
+                                                                  HandlerConfig.class,
+                                                                  defaults);
+
+        assertNotNull(config);
+        assertEquals(30000, config.getTimeout()); // ✅ From defaults
+        assertEquals(2, config.getRetries());      // ✅ From defaults
+        assertTrue(config.isEnabled());            // ✅ From JSON (overridden)
+    }
+
+    @Test
+    public void testLoadWithDefaultsFullOverride() throws Exception {
+        // Test that JSON can override all defaults
+        Path configPath = Paths.get(
+                getClass().getResource("/configs/test-partial-config.json").toURI());
+        TikaLoader loader = TikaLoader.load(configPath);
+
+        HandlerConfig defaults = new HandlerConfig();
+        defaults.setTimeout(30000);
+        defaults.setRetries(2);
+        defaults.setEnabled(false);
+
+        // JSON has: { "timeout": 10000, "retries": 5, "enabled": false }
+        HandlerConfig config = loader.configs().loadWithDefaults("handler-config-full",
+                                                                  HandlerConfig.class,
+                                                                  defaults);
+
+        assertNotNull(config);
+        assertEquals(10000, config.getTimeout()); // All overridden
+        assertEquals(5, config.getRetries());
+        assertFalse(config.isEnabled());
+    }
+
+    @Test
+    public void testLoadWithDefaultsMissingKey() throws Exception {
+        // When key doesn't exist, should return original defaults unchanged
+        HandlerConfig defaults = new HandlerConfig();
+        defaults.setTimeout(30000);
+        defaults.setRetries(2);
+        defaults.setEnabled(false);
+
+        HandlerConfig config = configLoader.loadWithDefaults("non-existent-key",
+                                                              HandlerConfig.class,
+                                                              defaults);
+
+        assertNotNull(config);
+        assertEquals(30000, config.getTimeout());
+        assertEquals(2, config.getRetries());
+        assertFalse(config.isEnabled());
+    }
+
+    @Test
+    public void testLoadWithDefaultsByClass() throws Exception {
+        // Test the class-name version
+        Path configPath = Paths.get(
+                getClass().getResource("/configs/test-partial-config.json").toURI());
+        TikaLoader loader = TikaLoader.load(configPath);
+
+        HandlerConfig defaults = new HandlerConfig();
+        defaults.setTimeout(30000);
+        defaults.setRetries(2);
+        defaults.setEnabled(false);
+
+        // Uses kebab-case: HandlerConfig -> "handler-config"
+        HandlerConfig config = loader.configs().loadWithDefaults(HandlerConfig.class, defaults);
+
+        assertNotNull(config);
+        assertEquals(30000, config.getTimeout());
+        assertEquals(2, config.getRetries());
+        assertTrue(config.isEnabled()); // Overridden from JSON
+    }
+
+    @Test
+    public void testLoadVsLoadWithDefaults() throws Exception {
+        // Demonstrate difference between load() and loadWithDefaults()
+        Path configPath = Paths.get(
+                getClass().getResource("/configs/test-partial-config.json").toURI());
+        TikaLoader loader = TikaLoader.load(configPath);
+
+        HandlerConfig defaults = new HandlerConfig();
+        defaults.setTimeout(30000);
+        defaults.setRetries(2);
+        defaults.setEnabled(false);
+
+        // Using load() - creates new object, loses defaults
+        HandlerConfig config1 = loader.configs().load("handler-config", HandlerConfig.class);
+        assertEquals(0, config1.getTimeout());  // ❌ Lost default!
+        assertEquals(0, config1.getRetries());  // ❌ Lost default!
+        assertTrue(config1.isEnabled());        // ✅ From JSON
+
+        // Using loadWithDefaults() - merges into defaults
+        HandlerConfig config2 = loader.configs().loadWithDefaults("handler-config",
+                                                                   HandlerConfig.class,
+                                                                   defaults);
+        assertEquals(30000, config2.getTimeout()); // ✅ Kept default!
+        assertEquals(2, config2.getRetries());     // ✅ Kept default!
+        assertTrue(config2.isEnabled());           // ✅ From JSON
+    }
+
+    // ==================== Immutability Tests ====================
+
+    @Test
+    public void testLoadWithDefaultsDoesNotMutateOriginal() throws Exception {
+        // Verify that the original defaults object is NOT modified
+        Path configPath = Paths.get(
+                getClass().getResource("/configs/test-partial-config.json").toURI());
+        TikaLoader loader = TikaLoader.load(configPath);
+
+        HandlerConfig defaults = new HandlerConfig();
+        defaults.setTimeout(30000);
+        defaults.setRetries(2);
+        defaults.setEnabled(false);
+
+        // Load config with partial override (JSON only has "enabled": true)
+        HandlerConfig result = loader.configs().loadWithDefaults("handler-config",
+                                                                  HandlerConfig.class,
+                                                                  defaults);
+
+        // Verify result has merged values
+        assertEquals(30000, result.getTimeout());
+        assertEquals(2, result.getRetries());
+        assertTrue(result.isEnabled());  // Overridden from JSON
+
+        // CRITICAL: Verify original defaults object is unchanged
+        assertEquals(30000, defaults.getTimeout());  // ✅ Still original value
+        assertEquals(2, defaults.getRetries());      // ✅ Still original value
+        assertFalse(defaults.isEnabled());           // ✅ Still original value (NOT changed!)
+
+        // Verify they are different objects
+        assertNotEquals(System.identityHashCode(defaults),
+                       System.identityHashCode(result),
+                       "Result should be a different object than defaults");
+    }
+
+    @Test
+    public void testLoadWithDefaultsReusableDefaults() throws Exception {
+        // Verify defaults can be safely reused for multiple loads
+        Path configPath = Paths.get(
+                getClass().getResource("/configs/test-partial-config.json").toURI());
+        TikaLoader loader = TikaLoader.load(configPath);
+
+        HandlerConfig defaults = new HandlerConfig();
+        defaults.setTimeout(30000);
+        defaults.setRetries(2);
+        defaults.setEnabled(false);
+
+        // Load multiple times with same defaults
+        HandlerConfig config1 = loader.configs().loadWithDefaults("handler-config",
+                                                                   HandlerConfig.class,
+                                                                   defaults);
+        HandlerConfig config2 = loader.configs().loadWithDefaults("handler-config-full",
+                                                                   HandlerConfig.class,
+                                                                   defaults);
+
+        // Verify results are different
+        assertTrue(config1.isEnabled());   // From partial config
+        assertFalse(config2.isEnabled());  // From full config
+
+        // Verify defaults still unchanged and can be used again
+        assertEquals(30000, defaults.getTimeout());
+        assertEquals(2, defaults.getRetries());
+        assertFalse(defaults.isEnabled());
+
+        // Use defaults one more time
+        HandlerConfig config3 = loader.configs().loadWithDefaults("non-existent",
+                                                                   HandlerConfig.class,
+                                                                   defaults);
+        assertEquals(defaults, config3);  // Should return original when key missing
+    }
+
+    @Test
+    public void testLoadWithDefaultsComplexObjectImmutability() throws Exception {
+        // Test with nested/complex objects to ensure deep copy works
+        Path configPath = Paths.get(
+                getClass().getResource("/configs/test-partial-config.json").toURI());
+        TikaLoader loader = TikaLoader.load(configPath);
+
+        TikaTaskTimeout defaults = new TikaTaskTimeout();
+        defaults.setMillis(60000);
+
+        // Note: tika-task-timeout in JSON has millis: 30000
+        TikaTaskTimeout result = loader.configs().loadWithDefaults("tika-task-timeout",
+                                                                    TikaTaskTimeout.class,
+                                                                    defaults);
+
+        // Result should have JSON value
+        assertEquals(30000, result.getMillis());
+
+        // Original should be unchanged
+        assertEquals(60000, defaults.getMillis());
+    }
+
+    @Test
+    public void testLoadWithDefaultsMissingKeyDoesNotClone() throws Exception {
+        // When key is missing, should return the original object (no unnecessary cloning)
+        HandlerConfig defaults = new HandlerConfig();
+        defaults.setTimeout(30000);
+        defaults.setRetries(2);
+        defaults.setEnabled(false);
+
+        HandlerConfig result = configLoader.loadWithDefaults("non-existent-key",
+                                                              HandlerConfig.class,
+                                                              defaults);
+
+        // Should return the exact same object when key is missing
+        assertEquals(defaults, result);
+        assertEquals(System.identityHashCode(defaults),
+                    System.identityHashCode(result),
+                    "Should return same object when key missing (no unnecessary clone)");
+    }
+
+    @Test
+    public void testLoadWithDefaultsThreadSafety() throws Exception {
+        // Demonstrate that defaults can be safely shared across threads
+        Path configPath = Paths.get(
+                getClass().getResource("/configs/test-partial-config.json").toURI());
+        TikaLoader loader = TikaLoader.load(configPath);
+
+        // Shared defaults object
+        HandlerConfig sharedDefaults = new HandlerConfig();
+        sharedDefaults.setTimeout(30000);
+        sharedDefaults.setRetries(2);
+        sharedDefaults.setEnabled(false);
+
+        // Simulate concurrent usage (not a real concurrency test, just demonstrates safety)
+        HandlerConfig result1 = loader.configs().loadWithDefaults("handler-config",
+                                                                   HandlerConfig.class,
+                                                                   sharedDefaults);
+        HandlerConfig result2 = loader.configs().loadWithDefaults("handler-config-full",
+                                                                   HandlerConfig.class,
+                                                                   sharedDefaults);
+
+        // Both results should be valid
+        assertNotNull(result1);
+        assertNotNull(result2);
+
+        // Shared defaults should still be unchanged
+        assertEquals(30000, sharedDefaults.getTimeout());
+        assertEquals(2, sharedDefaults.getRetries());
+        assertFalse(sharedDefaults.isEnabled());
+    }
+}
diff --git a/tika-serialization/src/test/resources/META-INF/services/org.apache.tika.parser.Parser b/tika-serialization/src/test/resources/META-INF/services/org.apache.tika.parser.Parser
deleted file mode 100644
index fbf6d840b..000000000
--- a/tika-serialization/src/test/resources/META-INF/services/org.apache.tika.parser.Parser
+++ /dev/null
@@ -1,4 +0,0 @@
-# Test parsers for tika-serialization module
-org.apache.tika.config.loader.ConfigurableTestParser
-org.apache.tika.config.loader.FallbackTestParser
-org.apache.tika.config.loader.MinimalTestParser
diff --git a/tika-serialization/src/test/resources/META-INF/tika/parsers.idx b/tika-serialization/src/test/resources/META-INF/tika/parsers.idx
deleted file mode 100644
index 99a58d8ed..000000000
--- a/tika-serialization/src/test/resources/META-INF/tika/parsers.idx
+++ /dev/null
@@ -1,5 +0,0 @@
-# Test parsers index for tika-serialization module tests
-configurable-test-parser=org.apache.tika.config.loader.ConfigurableTestParser
-fallback-test-parser=org.apache.tika.config.loader.FallbackTestParser
-minimal-test-parser=org.apache.tika.config.loader.MinimalTestParser
-opt-in-test-parser=org.apache.tika.config.loader.OptInTestParser
diff --git a/tika-serialization/src/test/resources/configs/test-config-loader.json b/tika-serialization/src/test/resources/configs/test-config-loader.json
new file mode 100644
index 000000000..8f6e89a8c
--- /dev/null
+++ b/tika-serialization/src/test/resources/configs/test-config-loader.json
@@ -0,0 +1,32 @@
+{
+  "handler-config": {
+    "timeout": 5000,
+    "retries": 3,
+    "enabled": true
+  },
+
+  "simple-handler": "org.apache.tika.config.loader.ConfigLoaderTest$SimpleHandlerImpl",
+
+  "configured-handler": {
+    "@class": "org.apache.tika.config.loader.ConfigLoaderTest$ConfiguredHandlerImpl",
+    "maxSize": 100000,
+    "prefix": "test-"
+  },
+
+  "tika-task-timeout": {
+    "millis": 30000
+  },
+
+  "parsers": [
+    {"pdf-parser": {}}
+  ],
+
+  "my-feature-settings": {
+    "featureName": "test-feature",
+    "priority": 10
+  },
+
+  "abstract-handler": {
+    "someProperty": "value"
+  }
+}
diff --git a/tika-serialization/src/test/resources/configs/test-interface-no-type.json b/tika-serialization/src/test/resources/configs/test-interface-no-type.json
new file mode 100644
index 000000000..15a3d35b2
--- /dev/null
+++ b/tika-serialization/src/test/resources/configs/test-interface-no-type.json
@@ -0,0 +1,6 @@
+{
+  "handler-no-type": {
+    "maxSize": 50000,
+    "prefix": "no-type-"
+  }
+}
diff --git a/tika-serialization/src/test/resources/configs/test-invalid-class.json b/tika-serialization/src/test/resources/configs/test-invalid-class.json
new file mode 100644
index 000000000..f0bf4bf4e
--- /dev/null
+++ b/tika-serialization/src/test/resources/configs/test-invalid-class.json
@@ -0,0 +1,3 @@
+{
+  "handler": "com.example.NonExistentClass"
+}
diff --git a/tika-serialization/src/test/resources/configs/test-partial-config.json b/tika-serialization/src/test/resources/configs/test-partial-config.json
new file mode 100644
index 000000000..fb010c3e8
--- /dev/null
+++ b/tika-serialization/src/test/resources/configs/test-partial-config.json
@@ -0,0 +1,15 @@
+{
+  "handler-config": {
+    "enabled": true
+  },
+
+  "handler-config-full": {
+    "timeout": 10000,
+    "retries": 5,
+    "enabled": false
+  },
+
+  "tika-task-timeout": {
+    "millis": 30000
+  }
+}
diff --git a/tika-serialization/src/test/resources/configs/test-unexpected-field.json b/tika-serialization/src/test/resources/configs/test-unexpected-field.json
new file mode 100644
index 000000000..ada7f9bdf
--- /dev/null
+++ b/tika-serialization/src/test/resources/configs/test-unexpected-field.json
@@ -0,0 +1,8 @@
+{
+  "handler-config": {
+    "timeout": 5000,
+    "retries": 3,
+    "enabled": true,
+    "unexpectedField": "this should cause an error"
+  }
+}
diff --git a/tika-serialization/src/test/resources/configs/test-wrong-type.json b/tika-serialization/src/test/resources/configs/test-wrong-type.json
new file mode 100644
index 000000000..b25e9f644
--- /dev/null
+++ b/tika-serialization/src/test/resources/configs/test-wrong-type.json
@@ -0,0 +1,3 @@
+{
+  "handler": "java.lang.String"
+}