JNotepad/src/main/java/org/jcnc/jnotepad/plugin/PluginLoader.java
2023-09-30 20:38:48 +08:00

248 lines
9.7 KiB
Java

package org.jcnc.jnotepad.plugin;
import org.jcnc.jnotepad.common.manager.ThreadPoolManager;
import org.jcnc.jnotepad.controller.config.PluginConfigController;
import org.jcnc.jnotepad.exception.AppException;
import org.jcnc.jnotepad.model.entity.PluginDescriptor;
import org.jcnc.jnotepad.plugin.interfaces.Plugin;
import org.jcnc.jnotepad.plugin.manager.PluginManager;
import org.jcnc.jnotepad.util.JsonUtil;
import org.jcnc.jnotepad.util.LogUtil;
import org.slf4j.Logger;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
/**
* 插件加载类
*
* @author gewuyou
*/
public class PluginLoader {
private static final PluginLoader INSTANCE = new PluginLoader();
private static final ExecutorService THREAD_POOL = ThreadPoolManager.getThreadPool();
Logger logger = LogUtil.getLogger(this.getClass());
/**
* 从插件jar包中读取 json 文件到 PluginDescriptor 类
*
* @param pluginJar jar 包
*/
public static PluginDescriptor readPlugin(File pluginJar) throws IOException {
InputStream is;
StringBuilder sb;
try (JarFile jarFile = new JarFile(pluginJar)) {
ZipEntry zipEntry = jarFile.getEntry("META-INF/plugin.json");
is = jarFile.getInputStream(zipEntry);
try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
String temp;
sb = new StringBuilder();
while ((temp = br.readLine()) != null) {
sb.append(temp);
}
}
}
return JsonUtil.OBJECT_MAPPER.readValue(sb.toString(), PluginDescriptor.class);
}
public static PluginLoader getInstance() {
return INSTANCE;
}
/**
* 检查插件
*
* @param configPluginDescriptors 配置文件插件信息
* @param pluginDescriptor 插件信息类
* @param pluginDescriptors 插件信息集合
* @return boolean 是否检查通过
* @apiNote
* @since 2023/9/16 14:04
*/
private static boolean checkPlugin(List<PluginDescriptor> configPluginDescriptors, PluginDescriptor pluginDescriptor, List<PluginDescriptor> pluginDescriptors) {
// 如果应用程序配置文件中没有该插件则默认禁用
if (pluginDoesNotExist(pluginDescriptor, configPluginDescriptors)) {
disabledByDefault(configPluginDescriptors, pluginDescriptor, pluginDescriptors);
THREAD_POOL.submit(() -> {
PluginConfigController.getInstance().writeConfig();
ThreadPoolManager.threadContSelfSubtracting();
});
return true;
}
// 如果应用程序配置文件中该插件禁用则不加载
for (PluginDescriptor configPluginDescriptor : configPluginDescriptors) {
if (disableDoNotLoad(pluginDescriptor, pluginDescriptors, configPluginDescriptor)) {
return true;
}
}
// 判断该插件是否已经加载
return loaded(pluginDescriptor, pluginDescriptors);
}
/**
* 插件不存在
*
* @param pluginDescriptor 插件描述类
* @param configPluginDescriptors 配置文件插件信息集合
* @return boolean 插件不存在
* @apiNote
* @since 2023/9/19 19:16
*/
private static boolean pluginDoesNotExist(PluginDescriptor pluginDescriptor, List<PluginDescriptor> configPluginDescriptors) {
for (PluginDescriptor configPluginDescriptor : configPluginDescriptors) {
if (configPluginDescriptor.getId().equals(pluginDescriptor.getId())) {
return false;
}
}
return true;
}
private static boolean loaded(PluginDescriptor pluginDescriptor, List<PluginDescriptor> pluginDescriptors) {
Iterator<PluginDescriptor> iterator = pluginDescriptors.iterator();
while (iterator.hasNext()) {
PluginDescriptor plugin = iterator.next();
if (plugin.getId().equals(pluginDescriptor.getId())) {
if (plugin.getVersion().equals(pluginDescriptor.getVersion())) {
return true;
}
// 如果当前插件版本更低则更新
if (plugin.getVersion().compareTo(pluginDescriptor.getVersion()) < 0) {
// 删除当前的插件
iterator.remove();
} else {
throw new AppException("当前加载的插件版本低于本地版本!");
}
}
}
return false;
}
/**
* 如果插件被禁用则不加载
*
* @param pluginDescriptor 插件描述类
* @param pluginDescriptors 插件描述类集合
* @param configPluginDescriptor 配置文件插件信息
* @return boolean
* @apiNote
* @since 2023/9/19 18:45
*/
private static boolean disableDoNotLoad(PluginDescriptor pluginDescriptor, List<PluginDescriptor> pluginDescriptors, PluginDescriptor configPluginDescriptor) {
if (configPluginDescriptor.getId().equals(pluginDescriptor.getId()) && !configPluginDescriptor.isEnabled()) {
pluginDescriptor.setEnabled(false);
pluginDescriptors.add(pluginDescriptor);
return true;
}
return false;
}
/**
* 默认禁用
*
* @param configPluginDescriptors 配置文件插件信息
* @param pluginDescriptor 插件描述类
* @param pluginDescriptors 插件描述类集合
* @apiNote
* @since 2023/9/19 18:48
*/
private static void disabledByDefault(List<PluginDescriptor> configPluginDescriptors, PluginDescriptor pluginDescriptor, List<PluginDescriptor> pluginDescriptors) {
pluginDescriptor.setEnabled(false);
pluginDescriptors.add(pluginDescriptor);
configPluginDescriptors.add(pluginDescriptor);
}
/**
* 加载插件
*
* @param pluginFilePath 插件文件的路径
*/
public void loadPluginByPath(String pluginFilePath) {
File file = new File(pluginFilePath);
loadPluginByFile(file, PluginConfigController.getInstance().getConfig().getPlugins());
}
/**
* 根据文件加载插件
*
* @param pluginJar 插件jar包
* @param configPluginDescriptors 配置文件插件信息集合
* @apiNote
* @since 2023/9/16 14:05
*/
public void loadPluginByFile(File pluginJar, List<PluginDescriptor> configPluginDescriptors) {
PluginManager pluginManager = PluginManager.getInstance();
Map<String, List<String>> categories = pluginManager.getLoadedPluginsByCategory();
List<PluginDescriptor> pluginDescriptors = pluginManager.getPluginDescriptors();
if (pluginJar.exists() && pluginJar.isFile()) {
try {
PluginDescriptor pluginDescriptor = readPlugin(pluginJar);
// 检查插件状态
if (checkPlugin(configPluginDescriptors, pluginDescriptor, pluginDescriptors)) {
return;
}
pluginDescriptor.setEnabled(true);
pluginDescriptors.add(pluginDescriptor);
// 创建URLClassLoader以加载Jar文件中的类
Class<?> pluginClass = loaderJarFileClass(pluginJar, pluginDescriptor);
if (pluginClass == null) {
return;
}
Plugin plugin;
plugin = (Plugin) pluginClass.getDeclaredConstructor().newInstance();
pluginDescriptor.setPlugin(plugin);
categories.computeIfAbsent(pluginDescriptor.getCategory(), k -> new ArrayList<>()).add(pluginDescriptor.getName());
} catch (IOException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
throw new AppException(e);
} catch (ClassNotFoundException e) {
logger.error("无法找到对应的插件类!", e);
} catch (NoSuchMethodException e) {
logger.error("插件类中没有找到指定方法!", e);
}
} else {
logger.info("PluginDescriptor file not found");
}
}
/**
* 加载类中的class文件并返回插件主类
*
* @param pluginJar 插件jar包
* @param pluginDescriptor 插件描述
* @return java.lang.Class<?> 插件主类
* @apiNote
* @since 2023/9/19 14:00
*/
private Class<?> loaderJarFileClass(File pluginJar, PluginDescriptor pluginDescriptor) throws IOException, ClassNotFoundException {
Class<?> pluginClass;
try (
URLClassLoader classLoader = new URLClassLoader(new URL[]{pluginJar.toURI().toURL()});
JarFile jar = new JarFile(pluginJar)
) {
// 加载插件所需的依赖类
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.getName().endsWith(".class")) {
String className = entry.getName().replace("/", ".").replace(".class", "");
if (!pluginDescriptor.getMainClass().equals(className) && !"module-info".equals(className)) {
classLoader.loadClass(className);
}
}
}
pluginClass = classLoader.loadClass(pluginDescriptor.getMainClass());
}
logger.info("已加载插件:{}", pluginDescriptor.getName());
return pluginClass;
}
}