| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
This guide is for business module developers. It focuses on:
fe-extension-loader is the reusable plugin runtime foundation for Doris FE.
It unifies repeated loading logic across modules, including:
Use Loader if your module needs:
Loader may not be a direct fit if:
In those cases, use fe-extension-spi only.
This is the runtime entry class and unified facade.
Primary methods:
Business modules should depend on this class directly.
The manager performs:
Low-level utility class. It does not scan directories. It only handles:
Use it when you already own classloader lifecycle externally.
Classloading behavior:
Purpose:
Used to configure parent-first prefixes:
Common business examples:
Represents one successfully loaded plugin, including:
Business modules typically consume pluginName + factory for registration.
Represents one failed plugin directory load, including:
Failure stages:
Represents the full result of one loadAll call, including:
DirectoryPluginRuntimeManager stores loaded handles in an internal concurrent map.
No separate PluginRuntimeRegistry abstraction is exposed in current implementation.
Your business factory interface should extend PluginFactory.
DirectoryPluginRuntimeManager<MyPluginFactory> runtime =
new DirectoryPluginRuntimeManager<>();ClassLoadingPolicy policy = new ClassLoadingPolicy(
Collections.singletonList("org.apache.doris.mybiz."));LoadReport<MyPluginFactory> report = runtime.loadAll(
pluginRoots,
Thread.currentThread().getContextClassLoader(),
MyPluginFactory.class,
policy);for (LoadFailure failure : report.getFailures()) {
LOG.warn("plugin load failure: dir={}, stage={}, message={}",
failure.getPluginDir(), failure.getStage(), failure.getMessage(), failure.getCause());
}
for (PluginHandle<MyPluginFactory> handle : report.getSuccesses()) {
factoryMap.putIfAbsent(handle.getPluginName(), handle.getFactory());
}Recommended layout:
<pluginRoot>/
<pluginA>/
pluginA.jar
lib/
dep1.jar
dep2.jar
<pluginB>/
pluginB.jar
Rules:
Current default strategy:
Business recommendations:
Loader returns LoadReport and does not force exception.
Business modules choose policy by semantics:
LoadReport should be startup decision input, not just logs.
Recommended goals:
Recommended processing order:
Suggested stage severity grouping:
Recommended decision rules:
public static <F extends PluginFactory> void processLoadReport(
LoadReport<F> report,
Map<String, F> factoryMap,
boolean strictMode,
Set<String> requiredPluginNames) {
Objects.requireNonNull(report, "report");
Objects.requireNonNull(factoryMap, "factoryMap");
Objects.requireNonNull(requiredPluginNames, "requiredPluginNames");
// Step 1: summary metrics
LOG.info("plugin load summary: rootsScanned={}, dirsScanned={}, successCount={}, failureCount={}",
report.getRootsScanned(),
report.getDirsScanned(),
report.getSuccesses().size(),
report.getFailures().size());
// Step 2: failure details
LoadFailure firstNonConflictFailure = null;
for (LoadFailure failure : report.getFailures()) {
LOG.warn("plugin load failure: dir={}, stage={}, message={}",
failure.getPluginDir(), failure.getStage(), failure.getMessage(), failure.getCause());
if (!LoadFailure.STAGE_CONFLICT.equals(failure.getStage()) && firstNonConflictFailure == null) {
firstNonConflictFailure = failure;
}
}
// Step 3: register successful plugins
int registered = 0;
for (PluginHandle<F> handle : report.getSuccesses()) {
F existing = factoryMap.putIfAbsent(handle.getPluginName(), handle.getFactory());
if (existing != null) {
// If business map already contains the name, close discarded external classloader.
closeClassLoaderQuietly(handle.getClassLoader());
LOG.warn("skip duplicated plugin name in business map: {}", handle.getPluginName());
continue;
}
registered++;
}
// Step 4: startup decision (strict/tolerant)
if (strictMode && report.getDirsScanned() > 0 && registered == 0 && firstNonConflictFailure != null) {
throw new IllegalStateException(
"No plugin loaded in strict mode: stage=" + firstNonConflictFailure.getStage()
+ ", dir=" + firstNonConflictFailure.getPluginDir()
+ ", message=" + firstNonConflictFailure.getMessage(),
firstNonConflictFailure.getCause());
}
// Step 5: required plugin checks
for (String required : requiredPluginNames) {
if (!factoryMap.containsKey(required)) {
throw new IllegalStateException("Required plugin is missing: " + required);
}
}
}The closeClassLoaderQuietly implementation pattern can be referenced from:
../fe-authentication/fe-authentication-handler/src/main/java/org/apache/doris/authentication/handler/AuthenticationPluginManager.java
Current authentication module handling is:
Reference implementation:
../fe-authentication/fe-authentication-handler/src/main/java/org/apache/doris/authentication/handler/AuthenticationPluginManager.java
Supported:
Not supported:
Do not depend on runtime hot-reload semantics in V1.
Check:
Check:
Note:
DirectoryPluginRuntimeManager includes parent service-resource filtering and prefers plugin-directory-local discovery.
Check:
Authentication integration sample:
../fe-authentication/fe-authentication-handler/src/main/java/org/apache/doris/authentication/handler/AuthenticationPluginManager.java
Key integration points:
| Back | FazBrowse Home | New Git URL |