diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/plugin/PathBasedPluginLoader.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/plugin/PathBasedPluginLoader.java index 11f5bd953..7c13cdc57 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/plugin/PathBasedPluginLoader.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/plugin/PathBasedPluginLoader.java @@ -12,128 +12,111 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.ServiceLoader; +import java.util.*; @Slf4j @RequiredArgsConstructor @Component -public class PathBasedPluginLoader implements PluginLoader -{ +public class PathBasedPluginLoader implements PluginLoader { private final CommonConfig common; private final ApplicationHome applicationHome; - + + // Cache for plugin JAR paths to avoid redundant filesystem scans + private static final Map> cachedPluginJars = new HashMap<>(); + @Override - public List loadPlugins() - { + public List loadPlugins() { List plugins = new ArrayList<>(); - + + // Find plugin JARs using caching List pluginJars = findPluginsJars(); - if (pluginJars.isEmpty()) - { + if (pluginJars.isEmpty()) { + log.debug("No plugin JARs found."); return plugins; } - for (String pluginJar : pluginJars) - { + // Load plugins from JARs + pluginJars.parallelStream().forEach(pluginJar -> { log.debug("Inspecting plugin jar candidate: {}", pluginJar); List loadedPlugins = loadPluginCandidates(pluginJar); - if (loadedPlugins.isEmpty()) - { + if (loadedPlugins.isEmpty()) { log.debug(" - no plugins found in the jar file"); + } else { + synchronized (plugins) { + plugins.addAll(loadedPlugins); + } } - else - { - for (LowcoderPlugin plugin : loadedPlugins) - { - plugins.add(plugin); - } - } - } - + }); + return plugins; } - - protected List findPluginsJars() - { + + protected List findPluginsJars() { + String cacheKey = common.getPluginDirs().toString(); + + // Use cached JAR paths if available + if (cachedPluginJars.containsKey(cacheKey)) { + log.debug("Using cached plugin jar candidates for key: {}", cacheKey); + return cachedPluginJars.get(cacheKey); + } + List candidates = new ArrayList<>(); - if (CollectionUtils.isNotEmpty(common.getPluginDirs())) - { - for (String pluginDir : common.getPluginDirs()) - { + if (CollectionUtils.isNotEmpty(common.getPluginDirs())) { + for (String pluginDir : common.getPluginDirs()) { final Path pluginPath = getAbsoluteNormalizedPath(pluginDir); - if (pluginPath != null) - { + if (pluginPath != null) { candidates.addAll(findPluginCandidates(pluginPath)); } } } - + + // Cache the results + cachedPluginJars.put(cacheKey, candidates); return candidates; } - - protected List findPluginCandidates(Path pluginsDir) - { - List pluginCandidates = new ArrayList<>(); - try - { - Files.walk(pluginsDir) - .filter(Files::isRegularFile) - .filter(path -> StringUtils.endsWithIgnoreCase(path.toAbsolutePath().toString(), ".jar")) - .forEach(path -> pluginCandidates.add(path.toString())); - } - catch(IOException cause) - { + protected List findPluginCandidates(Path pluginsDir) { + try { + return Files.walk(pluginsDir) + .filter(Files::isRegularFile) + .filter(path -> StringUtils.endsWithIgnoreCase(path.toAbsolutePath().toString(), ".jar")) + .map(Path::toString) + .toList(); // Use Java 16+ `toList()` for better performance + } catch (IOException cause) { log.error("Error walking plugin folder! - {}", cause.getMessage()); + return Collections.emptyList(); } - - return pluginCandidates; } - - protected List loadPluginCandidates(String pluginJar) - { + + protected List loadPluginCandidates(String pluginJar) { List pluginCandidates = new ArrayList<>(); - try - { + try { Path pluginPath = Path.of(pluginJar); PluginClassLoader pluginClassLoader = new PluginClassLoader(pluginPath.getFileName().toString(), pluginPath); ServiceLoader pluginServices = ServiceLoader.load(LowcoderPlugin.class, pluginClassLoader); - if (pluginServices != null ) - { - Iterator pluginIterator = pluginServices.iterator(); - while(pluginIterator.hasNext()) - { - LowcoderPlugin plugin = pluginIterator.next(); + if (pluginServices != null) { + for (LowcoderPlugin plugin : pluginServices) { log.debug(" - loaded plugin: {} - {}", plugin.pluginId(), plugin.description()); pluginCandidates.add(plugin); } } - } - catch(Throwable cause) - { + } catch (Throwable cause) { log.warn("Error loading plugin!", cause); } - + return pluginCandidates; } - - private Path getAbsoluteNormalizedPath(String path) - { - if (StringUtils.isNotBlank(path)) - { + + private Path getAbsoluteNormalizedPath(String path) { + if (StringUtils.isNotBlank(path)) { Path absPath = Path.of(path); - if (!absPath.isAbsolute()) - { + if (!absPath.isAbsolute()) { absPath = Path.of(applicationHome.getDir().getAbsolutePath(), absPath.toString()); } return absPath.normalize().toAbsolutePath(); } - return null; } -} +} \ No newline at end of file