Class PluginLoader
ServiceLoader.
Each plugin JAR ships a service registration file under
META-INF/services/ listing its implementation of
TestDiscovery (and, optionally, SourcePatcher). This loader
walks the classpath, instantiates every registered provider, applies the
run-time TestDiscoveryConfig via configure, and verifies that
every provider declares a unique pluginId().
Lifecycle
Instances are intended to be created once per CLI run and injected into the
Command implementations that need them. The loader itself is
stateless — no instance fields — so a single loader can be shared between
commands that participate in the same orchestration. The lifecycle of the
loaded providers is owned by the caller: a typical usage closes them in a
finally block via closeAll(List).
Thread safety
This class is thread-safe. ServiceLoader.load(Class) resolution is
idempotent per classloader, and no shared mutable state is maintained.
- Since:
- 1.0.0
- See Also:
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoidCloses every provider in the list, logging anyIOExceptionatLevel.WARNINGand continuing so that all providers are attempted.voidcloseAllCredentialDetectors(List<org.egothor.methodatlas.api.CredentialDetector> detectors) Closes every detector in the list, logging anyIOExceptionatLevel.WARNINGand continuing so that all detectors are attempted.List<org.egothor.methodatlas.api.CredentialDetector> loadCredentialDetectors(org.egothor.methodatlas.api.CredentialDetectorConfig config) Loads allCredentialDetectorproviders registered viaServiceLoader, configures each withconfig, and returns them in registration order.List<org.egothor.methodatlas.api.SourcePatcher> loadPatchers(org.egothor.methodatlas.api.TestDiscoveryConfig config) Loads allSourcePatcherproviders registered viaServiceLoader, configures each one withconfig, and returns them sorted bySourcePatcher.pluginId().List<org.egothor.methodatlas.api.TestDiscovery> loadProviders(org.egothor.methodatlas.api.TestDiscoveryConfig config) Loads allTestDiscoveryproviders registered viaServiceLoader, configures each one withconfig, and returns them sorted byTestDiscovery.pluginId().List<org.egothor.methodatlas.api.CommentRedactor> loadRedactors(org.egothor.methodatlas.api.TestDiscoveryConfig config) Loads allCommentRedactorproviders registered viaServiceLoader, configures each one withconfig, and returns them sorted byCommentRedactor.pluginId().static voidrequireUniqueCredentialDetectorIds(List<org.egothor.methodatlas.api.CredentialDetector> detectors) Verifies that everyCredentialDetectorin the list has a uniqueCredentialDetector.detectorId().static voidrequireUniqueDiscoveryIds(List<org.egothor.methodatlas.api.TestDiscovery> providers) Verifies that everyTestDiscoveryprovider in the list has a uniqueTestDiscovery.pluginId().static voidrequireUniquePatcherIds(List<org.egothor.methodatlas.api.SourcePatcher> patchers) Verifies that everySourcePatcherin the list has a uniqueSourcePatcher.pluginId().static voidrequireUniqueRedactorIds(List<org.egothor.methodatlas.api.CommentRedactor> redactors) Verifies that everyCommentRedactorin the list has a uniqueCommentRedactor.pluginId().
-
Constructor Details
-
PluginLoader
public PluginLoader()Creates a new plugin loader. The loader carries no instance state and is safe to share across commands within a single CLI run.
-
-
Method Details
-
loadProviders
public List<org.egothor.methodatlas.api.TestDiscovery> loadProviders(org.egothor.methodatlas.api.TestDiscoveryConfig config) Loads allTestDiscoveryproviders registered viaServiceLoader, configures each one withconfig, and returns them sorted byTestDiscovery.pluginId().The sort makes cross-language output order independent of classpath order (records are emitted provider by provider), so the same inputs produce the same output in every environment.
The returned providers are open resources. Callers must close them through
closeAll(List)in afinallyblock to release any per-provider resources (file handles, native processes, etc.).Time complexity is
O(p)in the number of providers; the ServiceLoader lookup itself is dominated by classpath scanning.- Parameters:
config- run-time configuration forwarded to every provider viaTestDiscovery.configure(org.egothor.methodatlas.api.TestDiscoveryConfig); must not benull- Returns:
- non-empty list of configured providers ordered by plugin id
- Throws:
IllegalStateException- if no providers are found on the classpath, or if two providers share the sameTestDiscovery.pluginId()
-
loadPatchers
public List<org.egothor.methodatlas.api.SourcePatcher> loadPatchers(org.egothor.methodatlas.api.TestDiscoveryConfig config) Loads allSourcePatcherproviders registered viaServiceLoader, configures each one withconfig, and returns them sorted bySourcePatcher.pluginId().Unlike
loadProviders(org.egothor.methodatlas.api.TestDiscoveryConfig), returning an empty list is legitimate: languages that do not support source write-back (such as TypeScript or Python) ship no patcher.- Parameters:
config- run-time configuration forwarded to every patcher viaSourcePatcher.configure(org.egothor.methodatlas.api.TestDiscoveryConfig); must not benull- Returns:
- possibly-empty list of configured patchers ordered by plugin id
- Throws:
IllegalStateException- if two patchers share the sameSourcePatcher.pluginId()
-
loadRedactors
public List<org.egothor.methodatlas.api.CommentRedactor> loadRedactors(org.egothor.methodatlas.api.TestDiscoveryConfig config) Loads allCommentRedactorproviders registered viaServiceLoader, configures each one withconfig, and returns them sorted byCommentRedactor.pluginId().An empty list is legitimate here; whether it is acceptable is decided by
CommentRedaction, which fails closed for any file without a redactor.- Parameters:
config- run-time configuration forwarded to every redactor; must not benull- Returns:
- possibly-empty list of configured redactors ordered by plugin id
- Throws:
IllegalStateException- if two redactors share the same id
-
requireUniqueRedactorIds
public static void requireUniqueRedactorIds(List<org.egothor.methodatlas.api.CommentRedactor> redactors) Verifies that everyCommentRedactorin the list has a uniqueCommentRedactor.pluginId().- Parameters:
redactors- redactors to validate; must not benull- Throws:
IllegalStateException- if two or more redactors share the same id
-
closeAll
Closes every provider in the list, logging anyIOExceptionatLevel.WARNINGand continuing so that all providers are attempted.This method never throws: a provider whose
closefails leaves its resources in an indeterminate state, but the orchestration layer always exits cleanly. A failed close is a potential resource leak, so it is logged atWARNINGto be visible at the default log threshold.- Parameters:
providers- list of providers to close; must not benull
-
requireUniqueDiscoveryIds
public static void requireUniqueDiscoveryIds(List<org.egothor.methodatlas.api.TestDiscovery> providers) Verifies that everyTestDiscoveryprovider in the list has a uniqueTestDiscovery.pluginId().This method is
staticbecause it is a pure validation with no instance dependencies — test code calls it directly with handcrafted provider lists, and the instance loader calls it after aServiceLoadersweep. Time complexity isO(p)in the number of providers.- Parameters:
providers- list of providers to validate; must not benull- Throws:
IllegalStateException- if two or more providers share the same id
-
requireUniquePatcherIds
public static void requireUniquePatcherIds(List<org.egothor.methodatlas.api.SourcePatcher> patchers) Verifies that everySourcePatcherin the list has a uniqueSourcePatcher.pluginId().- Parameters:
patchers- list of patchers to validate; must not benull- Throws:
IllegalStateException- if two or more patchers share the same id
-
loadCredentialDetectors
public List<org.egothor.methodatlas.api.CredentialDetector> loadCredentialDetectors(org.egothor.methodatlas.api.CredentialDetectorConfig config) Loads allCredentialDetectorproviders registered viaServiceLoader, configures each withconfig, and returns them in registration order. An empty list is legitimate — it means no credential detector is on the classpath and the feature is unavailable.- Parameters:
config- runtime configuration forwarded to each detector; nevernull- Returns:
- possibly-empty list of configured detectors in registration order
- Throws:
IllegalStateException- if two detectors share the samedetectorId()
-
requireUniqueCredentialDetectorIds
public static void requireUniqueCredentialDetectorIds(List<org.egothor.methodatlas.api.CredentialDetector> detectors) Verifies that everyCredentialDetectorin the list has a uniqueCredentialDetector.detectorId().- Parameters:
detectors- detectors to validate; nevernull- Throws:
IllegalStateException- if two or more detectors share the same id
-
closeAllCredentialDetectors
public void closeAllCredentialDetectors(List<org.egothor.methodatlas.api.CredentialDetector> detectors) Closes every detector in the list, logging anyIOExceptionatLevel.WARNINGand continuing so that all detectors are attempted. Never throws.- Parameters:
detectors- detectors to close; must not benull
-