Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(plugin): support set parent class package and adapter scheduler #875

Merged
merged 5 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion configure/etc/conf/plugins.properties
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ datacap-convert-json=convert/datacap-convert-json/pom.xml
datacap-convert-xml=convert/datacap-convert-xml/pom.xml
datacap-convert-csv=convert/datacap-convert-csv/pom.xml
datacap-convert-none=convert/datacap-convert-none/pom.xml
datacap-executor-local=executor/datacap-executor-local/pom.xml
datacap-executor-local=executor/datacap-executor-local/pom.xml
datacap-executor-seatunnel=executor/datacap-executor-seatunnel/pom.xml
datacap-scheduler-local=scheduler/datacap-scheduler-local/pom.xml
30 changes: 30 additions & 0 deletions configure/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,36 @@
"ALL"
],
"url": "https://cdn.north.devlive.org/applications/datacap/plugins/2024.4.0-SNAPSHOT/executor/datacap-executor-local-bin.tar.gz"
},
{
"key": "datacap-executor-seatunnel",
"label": "SeatunnelExecutor",
"description": "A Seatunnel execution plugin for DataCap.",
"i18nFormat": true,
"type": "Executor",
"version": "2024.4.0-SNAPSHOT",
"author": "datacap-community",
"logo": "https://cdn.north.devlive.org/applications/datacap/resources/logo/executor/seatunnel.svg",
"released": "2024-11-21 23:11:15",
"supportVersion": [
"ALL"
],
"url": "https://cdn.north.devlive.org/applications/datacap/plugins/2024.4.0-SNAPSHOT/executor/datacap-executor-seatunnel-bin.tar.gz"
},
{
"key": "datacap-scheduler-local",
"label": "LocalScheduler",
"description": "A local scheduling plugin for DataCap.",
"i18nFormat": true,
"type": "Scheduler",
"version": "2024.4.0-SNAPSHOT",
"author": "datacap-community",
"logo": "https://cdn.north.devlive.org/applications/datacap/resources/logo/scheduler/local.svg",
"released": "2024-11-21 23:11:15",
"supportVersion": [
"ALL"
],
"url": "https://cdn.north.devlive.org/applications/datacap/plugins/2024.4.0-SNAPSHOT/scheduler/datacap-scheduler-local-bin.tar.gz"
}
]
}
10 changes: 10 additions & 0 deletions configure/schema/2024.4.0/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,13 @@ WHERE `version` IS NULL;
UPDATE `datacap_dataset`
SET `executor` = 'LocalExecutor'
WHERE `executor` = 'Default';

ALTER TABLE `datacap_dataset`
ALTER COLUMN `executor` SET DEFAULT 'LocalExecutor';

UPDATE `datacap_dataset`
SET `scheduler` = 'LocalScheduler'
WHERE `scheduler` = 'Default';

ALTER TABLE `datacap_dataset`
ALTER COLUMN `scheduler` SET DEFAULT 'LocalScheduler';
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
package io.edurt.datacap.plugin;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Set;

@Getter
@Builder
@SuppressFBWarnings(value = {"EI_EXPOSE_REP", "EI_EXPOSE_REP2"})
public class PluginConfigure
{
private static final Set<String> LOADER_PACKAGES = Set.of(
"java.",
"javax.",
"com.google.",
"org.",
"ch.qos.logback.",
"org.slf4j."
);

@Setter
private Path pluginsDir;
private boolean autoReload;
Expand All @@ -23,11 +36,27 @@ public class PluginConfigure

// 自动清理, 只有卸载时才会生效
// Auto cleanup, only effective when unloading
public boolean autoCleanup;
private boolean autoCleanup;

// 同一目录下多个插件是否共享类加载器
// Whether multiple plugins in the same directory share the class loader
public boolean shareClassLoaderWhenSameDir;
private boolean shareClassLoaderWhenSameDir;

// 优先使用父类加载器包列表
// List of parent class loader packages to be prioritized
@Builder.Default
private Set<String> parentClassLoaderPackages = new HashSet<>(LOADER_PACKAGES);

/**
* 添加父类加载器包
* Add parent class loader packages
*
* @param packageNames 父类加载器包名称 Parent class loader package name
*/
public void addParentClassLoaderPackage(Set<String> packageNames)
{
this.parentClassLoaderPackages.addAll(packageNames);
}

public static PluginConfigure defaultConfig()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ private boolean installTarPlugin(Path sourcePath, Path tempDir)
TarPluginLoader tarPluginLoader = new TarPluginLoader();
// 先加载插件到临时目录
// Load plugins to temporary directory
List<Plugin> plugins = tarPluginLoader.load(sourcePath, tempDir);
List<Plugin> plugins = tarPluginLoader.load(sourcePath, tempDir, config.getParentClassLoaderPackages());
if (!plugins.isEmpty()) {
// 查找解压后的子目录
// Find extracted subdirectory
Expand Down Expand Up @@ -574,7 +574,7 @@ private void loadPluginFromDirectory(Path pluginDir)
log.debug("Found plugin version: {}", pluginVersion);

PluginClassLoader loader;
if (config.shareClassLoaderWhenSameDir) {
if (config.isShareClassLoaderWhenSameDir()) {
log.info("Use shared ClassLoader for plugin: {} at {}", pluginBaseName, pluginDir);
// 多个插件在同一目录下,使用同一个类加载器
// Multiple plugins in the same directory, use the same class loader
Expand All @@ -586,7 +586,8 @@ private void loadPluginFromDirectory(Path pluginDir)
pluginDir,
pluginBaseName,
pluginVersion,
true
true,
config.getParentClassLoaderPackages()
);
}
catch (Exception e) {
Expand All @@ -604,7 +605,8 @@ private void loadPluginFromDirectory(Path pluginDir)
pluginDir,
pluginBaseName,
pluginVersion,
true
true,
config.getParentClassLoaderPackages()
);
}

Expand All @@ -613,7 +615,7 @@ private void loadPluginFromDirectory(Path pluginDir)
return;
}

List<Plugin> modules = PluginContextManager.runWithClassLoader(loader, () -> PluginLoaderFactory.loadPlugins(pluginDir));
List<Plugin> modules = PluginContextManager.runWithClassLoader(loader, () -> PluginLoaderFactory.loadPlugins(pluginDir, config.getParentClassLoaderPackages()));

for (Plugin module : modules) {
PluginContextManager.runWithClassLoader(loader, () -> {
Expand All @@ -628,7 +630,7 @@ private void loadPluginFromDirectory(Path pluginDir)
String pluginName = module.getName();
// 保存类加载器信息
// Save class loader information
if (config.shareClassLoaderWhenSameDir) {
if (config.isShareClassLoaderWhenSameDir()) {
pluginClassLoaders.putIfAbsent(pluginName, loader);
}
else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;

@Slf4j
Expand All @@ -25,7 +26,7 @@ public SpiType getType()
}

@Override
public List<Plugin> load(Path path)
public List<Plugin> load(Path path, Set<String> parentClassLoaderPackages)
{
try {
// 处理传入的是pom.xml文件的情况
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand All @@ -26,7 +27,7 @@ public SpiType getType()
}

@Override
public List<Plugin> load(Path path)
public List<Plugin> load(Path path, Set<String> parentClassLoaderPackages)
{
try {
// 获取目录名作为插件名
Expand All @@ -41,7 +42,8 @@ public List<Plugin> load(Path path)
path,
pluginName,
version,
true
true,
parentClassLoaderPackages
);

try (Stream<Path> pathStream = Files.walk(path)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import java.nio.file.Path;
import java.util.List;
import java.util.Set;

@Slf4j
@SuppressFBWarnings(value = {"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"})
Expand Down Expand Up @@ -50,7 +51,7 @@ private String getEffectivePluginName(Path path)
}

@Override
public List<Plugin> load(Path path)
public List<Plugin> load(Path path, Set<String> parentClassLoaderPackages)
{
try {
if (isExcludedPath(path)) {
Expand All @@ -76,7 +77,8 @@ public List<Plugin> load(Path path)
path,
pluginName,
version,
true
true,
parentClassLoaderPackages
);

return PluginContextManager.runWithClassLoader(classLoader, () -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
package io.edurt.datacap.plugin.loader;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Set;

/**
* 插件专用类加载器
* Plugin-specific ClassLoader
*/
@Slf4j
@SuppressFBWarnings(value = {"EI_EXPOSE_REP2"})
public class PluginClassLoader
extends URLClassLoader
implements AutoCloseable
Expand All @@ -27,13 +30,15 @@ public class PluginClassLoader
private final String pluginVersion;

private final boolean parentFirst;
private final Set<String> parentClassLoaders;

public PluginClassLoader(URL[] urls, ClassLoader parent, String pluginName, String pluginVersion, boolean parentFirst)
public PluginClassLoader(URL[] urls, ClassLoader parent, String pluginName, String pluginVersion, boolean parentFirst, Set<String> parentClassLoaders)
{
super(urls, parent);
this.pluginName = pluginName;
this.pluginVersion = pluginVersion;
this.parentFirst = parentFirst;
this.parentClassLoaders = parentClassLoaders;
this.name = String.join("-", "loader", pluginName.toLowerCase(), pluginVersion.toLowerCase());
log.debug("Created PluginClassLoader for {} with URLs: {}", pluginName, Arrays.toString(urls));
}
Expand All @@ -57,13 +62,11 @@ protected Class<?> loadClass(String name, boolean resolve)
try {
// 系统类和框架类使用父加载器
// Use parent loader for system classes and framework classes
if (name.startsWith("java.") ||
name.startsWith("javax.") ||
name.startsWith("com.google.") ||
name.startsWith("org.") ||
if (parentClassLoaders.stream().anyMatch(name::startsWith)
// 添加 Plugin 相关的包到父优先加载列表
// Add Plugin related packages to parent-first list
(parentFirst && name.startsWith("io.edurt.datacap.plugin"))) {
|| (parentFirst && name.startsWith("io.edurt.datacap.plugin"))
) {
return super.loadClass(name, resolve);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,5 @@ default boolean isValidDirectory(Path path)

// 加载插件
// Load plugins
List<Plugin> load(Path path);
List<Plugin> load(Path path, Set<String> parentClassLoaderPackages);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
Expand Down Expand Up @@ -88,10 +89,12 @@ public static PluginLoader getLoader(String type)
*
* @param pluginDir 插件目录
* plugin directory
* @param parentClassLoaderPackages 父类加载器包名集合
* parent class loader package names
* @return 加载的插件模块列表
* list of loaded plugin modules
*/
public static List<Plugin> loadPlugins(Path pluginDir)
public static List<Plugin> loadPlugins(Path pluginDir, Set<String> parentClassLoaderPackages)
{
if (pluginDir == null) {
log.warn("Plugin directory is null");
Expand All @@ -105,7 +108,7 @@ public static List<Plugin> loadPlugins(Path pluginDir)
PluginLoader loader = entry.getValue();

try {
List<Plugin> modules = loader.load(pluginDir);
List<Plugin> modules = loader.load(pluginDir, parentClassLoaderPackages);
if (modules != null && !modules.isEmpty()) {
log.info("Successfully loaded [ {} ] plugin(s) using loader type [ {} ]", modules.size(), type);
modules.forEach(v -> v.setClassLoader(loader.getClass().getName()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Set;

@Slf4j
@SuppressFBWarnings(value = {"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", "OBL_UNSATISFIED_OBLIGATION"})
Expand All @@ -26,7 +27,7 @@ public SpiType getType()
}

@Override
public List<Plugin> load(Path path)
public List<Plugin> load(Path path, Set<String> parentClassLoaderPackages)
{
try {
Path pomFile = path.resolve("pom.xml");
Expand Down Expand Up @@ -57,7 +58,8 @@ public List<Plugin> load(Path path)
path,
model.getArtifactId(),
version,
true
true,
parentClassLoaderPackages
);

Class<?> pluginClass = classLoader.loadClass(mainClass);
Expand Down
Loading
Loading