com.gitee.starblues.integration.operator.DefaultPluginOperator Maven / Gradle / Ivy
package com.gitee.starblues.integration.operator;
import com.gitee.starblues.factory.DefaultPluginFactory;
import com.gitee.starblues.factory.PluginFactory;
import com.gitee.starblues.factory.PluginRegistryInfo;
import com.gitee.starblues.integration.IntegrationConfiguration;
import com.gitee.starblues.integration.listener.PluginInitializerListener;
import com.gitee.starblues.integration.listener.PluginInitializerListenerFactory;
import com.gitee.starblues.integration.listener.PluginListenerFactory;
import com.gitee.starblues.integration.operator.module.PluginInfo;
import com.gitee.starblues.integration.operator.verify.DefaultPluginVerify;
import com.gitee.starblues.integration.operator.verify.PluginLegalVerify;
import com.gitee.starblues.utils.GlobalRegistryInfo;
import com.gitee.starblues.utils.PluginFileUtils;
import com.gitee.starblues.utils.PluginOperatorInfo;
import org.pf4j.*;
import org.pf4j.util.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
/**
* 默认的插件操作者
* @author starBlues
* @version 2.4.4
*/
public class DefaultPluginOperator implements PluginOperator {
private boolean isInit = false;
protected final Logger log = LoggerFactory.getLogger(this.getClass());
private final static DateTimeFormatter FORMAT = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
protected final GenericApplicationContext applicationContext;
protected final IntegrationConfiguration integrationConfiguration;
protected final PluginManager pluginManager;
protected final PluginFactory pluginFactory;
protected final PluginInitializerListenerFactory pluginInitializerListenerFactory;
protected PluginLegalVerify pluginLegalVerify;
public DefaultPluginOperator(ApplicationContext applicationContext,
IntegrationConfiguration integrationConfiguration,
PluginManager pluginManager,
PluginListenerFactory pluginListenerFactory) {
Objects.requireNonNull(integrationConfiguration, "applicationContext can't be null");
Objects.requireNonNull(integrationConfiguration, "IntegrationConfiguration can't be null");
Objects.requireNonNull(pluginManager, "PluginManager can't be null");
this.applicationContext = (GenericApplicationContext) applicationContext;
this.integrationConfiguration = integrationConfiguration;
this.pluginManager = pluginManager;
this.pluginFactory = new DefaultPluginFactory(applicationContext, pluginListenerFactory);
this.pluginInitializerListenerFactory = new PluginInitializerListenerFactory(applicationContext);
this.pluginLegalVerify = new DefaultPluginVerify(pluginManager);
}
/**
* 设置插件校验器
* @param uploadPluginVerify uploadPluginVerify
*/
public void setUploadPluginVerify(PluginLegalVerify uploadPluginVerify) {
if(uploadPluginVerify != null){
this.pluginLegalVerify = uploadPluginVerify;
}
}
@Override
public synchronized boolean initPlugins(PluginInitializerListener pluginInitializerListener) throws Exception {
if(isInit){
throw new RuntimeException("Plugins Already initialized. Cannot be initialized again");
}
try {
pluginInitializerListenerFactory.addPluginInitializerListeners(pluginInitializerListener);
log.info("Plugins start initialize of root path '{}'", pluginManager.getPluginsRoot().toString());
// 触发插件初始化监听器
pluginInitializerListenerFactory.before();
if(!integrationConfiguration.enable()){
// 如果禁用的话, 直接返回
pluginInitializerListenerFactory.complete();
return false;
}
// 启动前, 清除空文件
PluginFileUtils.cleanEmptyFile(pluginManager.getPluginsRoot());
// 开始初始化插件工厂
pluginFactory.initialize();
// 开始加载插件
pluginManager.loadPlugins();
pluginManager.startPlugins();
List pluginWrappers = pluginManager.getStartedPlugins();
if(pluginWrappers == null || pluginWrappers.isEmpty()){
log.warn("Not found plugin!");
pluginInitializerListenerFactory.complete();
return false;
}
boolean isFoundException = false;
for (PluginWrapper pluginWrapper : pluginWrappers) {
String pluginId = pluginWrapper.getPluginId();
GlobalRegistryInfo.addOperatorPluginInfo(pluginId,
PluginOperatorInfo.OperatorType.INSTALL, false);
try {
// 依次注册插件信息到Spring boot
pluginFactory.registry(PluginRegistryInfo.build(pluginWrapper, pluginManager,
applicationContext, true));
} catch (Exception e){
log.error("Plugin '{}' registry failure. Reason : {}", pluginId, e.getMessage(), e);
isFoundException = true;
}
}
pluginFactory.build();
isInit = true;
if(isFoundException){
log.error("Plugins initialize failure");
pluginInitializerListenerFactory.failure(new Exception("Plugins initialize failure"));
return false;
} else {
log.info("Plugins initialize success");
pluginInitializerListenerFactory.complete();
return true;
}
} catch (Exception e){
pluginInitializerListenerFactory.failure(e);
throw e;
}
}
@Override
public boolean verify(Path jarPath) throws Exception {
pluginLegalVerify.verify(jarPath);
return true;
}
@Override
public synchronized PluginInfo install(Path jarPath) throws Exception {
if(isDev()){
throw new RuntimeException("Plugin cannot be installed in 'dev' environment");
}
if(jarPath == null){
throw new IllegalArgumentException("Method:install param 'pluginId' can not be empty");
}
String pluginId = null;
try {
PluginInfo pluginInfo = load(jarPath);
if(pluginInfo == null){
log.error("Plugin '{}' install failure, this pluginInfo id is empty.", pluginId);
return null;
}
PluginDescriptor pluginDescriptor = pluginInfo.getPluginDescriptor();
if(pluginDescriptor == null){
log.error("Plugin install failure.");
return null;
}
pluginId = pluginDescriptor.getPluginId();
GlobalRegistryInfo.addOperatorPluginInfo(pluginId, PluginOperatorInfo.OperatorType.INSTALL, true);
if(start(pluginId)){
log.info("Plugin '{}' install success", pluginId);
return getPluginInfo(pluginId);
} else {
try {
uninstall(pluginId, false);
} catch (Exception uninstallException){
log.error("Plugin '{}' uninstall failure. {}", pluginId, uninstallException.getMessage());
}
return null;
}
} catch (Exception e){
// 说明load成功, 但是没有启动成功, 则卸载该插件
log.error("Plugin '{}' install failure. {}", pluginId, e.getMessage());
if(!ObjectUtils.isEmpty(pluginId)){
try {
uninstall(pluginId, false);
} catch (Exception uninstallException){
log.error("Plugin '{}' uninstall failure. {}", pluginId, uninstallException.getMessage());
}
}
throw e;
} finally {
if(!ObjectUtils.isEmpty(pluginId)){
GlobalRegistryInfo.setOperatorPluginInfo(pluginId, false);
}
}
}
@Override
public synchronized boolean uninstall(String pluginId, boolean isBackup) throws Exception {
if(isDev()){
throw new RuntimeException("Plugin cannot be uninstalled in 'dev' environment");
}
if(ObjectUtils.isEmpty(pluginId)){
throw new IllegalArgumentException("Method:uninstall param 'pluginId' can not be empty");
}
PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId);
if(pluginWrapper == null){
throw new Exception("Plugin uninstall failure, Not found plugin '" + pluginId + "'");
}
Exception exception = null;
if(pluginWrapper.getPluginState() == PluginState.STARTED){
try {
pluginFactory.unRegistry(pluginId);
pluginFactory.build();
} catch (Exception e){
log.error("Plugin '{}' uninstall failure, {}", pluginId, e.getMessage());
exception = e;
}
}
try {
if (pluginManager.unloadPlugin(pluginId)) {
Path pluginPath = pluginWrapper.getPluginPath();
boolean opPluginFile;
if(isBackup){
// 将插件文件移到备份文件中
opPluginFile = backup(pluginPath, "uninstall", 1);
} else {
// 不备份的话。直接删除该文件
opPluginFile = Files.deleteIfExists(pluginPath);
}
if(opPluginFile){
log.info("Plugin '{}' uninstall success", pluginId);
} else {
log.error("Plugin '{}' uninstall failure. process plugin file failure", pluginId);
}
return opPluginFile;
} else {
log.error("Plugin '{}' uninstall failure", pluginId);
return false;
}
} catch (Exception e){
if(exception != null){
exception.printStackTrace();
}
log.error("Plugin '{}' uninstall failure. {}", pluginId, e.getMessage());
throw e;
}
}
@Override
public PluginInfo load(Path jarPath) throws Exception {
if(!Files.exists(jarPath)){
throw new FileNotFoundException("Not found this path " + jarPath);
}
// 校验插件文件
pluginLegalVerify.verify(jarPath);
Path pluginsRoot = pluginManager.getPluginsRoot();
Path pluginPath = null;
if(jarPath.getParent().compareTo(pluginsRoot) == 0){
// 说明该插件文件存在于插件root目录下。直接加载该插件
pluginPath = jarPath;
} else {
File sourceFile = jarPath.toFile();
String targetPathString = pluginsRoot.toString() + File.separator +
sourceFile.getName();
Path targetPath = Paths.get(targetPathString);
if(Files.exists(targetPath)){
// 如果存在该文件, 则移动备份
backup(targetPath, "install-backup", 1);
}
PluginFileUtils.createExistFile(targetPath);
Files.copy(jarPath, targetPath, StandardCopyOption.REPLACE_EXISTING);
pluginPath = targetPath;
}
String pluginId = null;
try {
pluginId = pluginManager.loadPlugin(pluginPath);
if(pluginId != null){
return getPluginInfo(pluginId);
}
return null;
} catch (Exception e){
if(pluginId != null){
log.error("Load plugin success, but get pluginInfo failure. so remove plugin '{}'", pluginId);
try {
pluginManager.unloadPlugin(pluginId);
} finally {
Files.delete(pluginPath);
}
} else {
log.error("Load plugin failure");
}
return null;
}
}
@Override
public PluginInfo load(MultipartFile pluginFile) throws Exception {
if(isDev()){
throw new RuntimeException("Plugin cannot uploadPluginAndStart in the 'dev' environment");
}
if(pluginFile == null){
throw new IllegalArgumentException("Method:uploadPluginAndStart param 'pluginFile' can not be null");
}
Path jarPath = uploadPlugin(pluginFile);
return load(jarPath);
}
@Override
public boolean unload(String pluginId, boolean isBackup) throws Exception {
PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId);
if(pluginWrapper == null){
throw new Exception("Plugin unload failure, Not found plugin '" + pluginId + "'");
}
pluginManager.unloadPlugin(pluginId);
if(isBackup){
backup(pluginWrapper.getPluginPath(), "unload", 1);
}
return true;
}
@Override
public synchronized boolean start(String pluginId) throws Exception {
if(ObjectUtils.isEmpty(pluginId)){
throw new IllegalArgumentException("Method:start param 'pluginId' can not be empty");
}
PluginWrapper pluginWrapper = getPluginWrapper(pluginId, "Start");
if(pluginWrapper.getPluginState() == PluginState.STARTED){
throw new Exception("This plugin '" + pluginId + "' have already started");
}
try {
PluginState pluginState = pluginManager.startPlugin(pluginId);
if(pluginState == PluginState.STARTED){
GlobalRegistryInfo.addOperatorPluginInfo(pluginId, PluginOperatorInfo.OperatorType.START, false);
pluginFactory.registry(PluginRegistryInfo.build(pluginWrapper, pluginManager,
applicationContext,false));
pluginFactory.build();
log.info("Plugin '{}' start success", pluginId);
return true;
}
log.error("Plugin '{}' start failure, plugin state is not start. Current plugin state is '{}'",
pluginId, pluginState.toString());
} catch (Exception e){
log.error("Plugin '{}' start failure. {}", pluginId, e.getMessage(), e);
log.info("Start stop plugin '{}'", pluginId);
try {
stop(pluginId);
} catch (Exception stopException){
log.error("Plugin '{}' stop failure. {}", pluginId, e.getMessage());
}
}
return false;
}
@Override
public synchronized boolean stop(String pluginId) throws Exception {
if(ObjectUtils.isEmpty(pluginId)){
throw new IllegalArgumentException("Method:stop param 'pluginId' can not be empty");
}
PluginWrapper pluginWrapper = getPluginWrapper(pluginId, "Stop");
if(pluginWrapper.getPluginState() != PluginState.STARTED){
throw new Exception("This plugin '" + pluginId + "' is not started");
}
try {
pluginFactory.unRegistry(pluginId);
pluginFactory.build();
log.info("Plugin '{}' unRegistry success", pluginId);
} catch (Exception e){
log.error("Plugin '{}' stop failure. {}", pluginId, e.getMessage(), e);
}
try {
pluginManager.stopPlugin(pluginId);
log.info("Plugin '{}' stop success", pluginId);
return true;
} catch (Exception e){
log.error("Plugin '{}' stop failure. {}", pluginId, e.getMessage());
throw e;
}
}
@Override
public synchronized PluginInfo uploadPluginAndStart(MultipartFile pluginFile) throws Exception {
if(isDev()){
throw new RuntimeException("Plugin cannot uploadPluginAndStart in the 'dev' environment");
}
if(pluginFile == null){
throw new IllegalArgumentException("Method:uploadPluginAndStart param 'pluginFile' can not be null");
}
Path path = uploadPlugin(pluginFile);
return this.install(path);
}
@Override
public synchronized boolean installConfigFile(Path configFilePath) throws Exception {
if(isDev()){
throw new RuntimeException("Plugin config file cannot be installed in the 'dev' environment");
}
if(!Files.exists(configFilePath)){
throw new FileNotFoundException("configFilePath '" + configFilePath + "' does not exist!");
}
File sourceFile = configFilePath.toFile();
String configPath = integrationConfiguration.pluginConfigFilePath() +
File.separator + sourceFile.getName();
Path targetPath = PluginFileUtils.createExistFile(Paths.get(configPath));
if(Files.exists(targetPath)){
// 如果文件存在, 则移动备份
backup(targetPath, "install-config-backup",1);
}
Files.copy(configFilePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
return true;
}
@Override
public synchronized boolean uploadConfigFile(MultipartFile configFile) throws Exception {
if(isDev()){
throw new RuntimeException("Plugin config file cannot be uploaded in the 'dev' environment");
}
if(configFile == null){
throw new IllegalArgumentException("Method:uploadConfigFile param 'configFile' can not be null");
}
String fileName = configFile.getOriginalFilename();
String configPath = integrationConfiguration.pluginConfigFilePath() +
File.separator + fileName;
Path targetPath = PluginFileUtils.createExistFile(Paths.get(configPath));
if(Files.exists(targetPath)){
// 如果文件存在, 则拷贝备份
backup(targetPath, "upload-config-backup",2);
}
// 然后写入数据到该文件
Files.write(targetPath, configFile.getBytes());
return true;
}
@Override
public synchronized boolean backupPlugin(Path backDirPath, String sign) throws Exception {
if(isDev()){
throw new RuntimeException("Plugin cannot backup in the 'dev' environment");
}
Objects.requireNonNull(backDirPath);
return backup(backDirPath, sign, 2);
}
@Override
public synchronized boolean backupPlugin(String pluginId, String sign) throws Exception {
if(isDev()){
throw new RuntimeException("Plugin cannot backup in the 'dev' environment");
}
PluginWrapper pluginManager = getPluginWrapper(pluginId, "BackupPlugin by pluginId");
return backupPlugin(pluginManager.getPluginPath(), sign);
}
@Override
public List getPluginInfo() {
List startedPlugins = pluginManager.getPlugins();
List pluginInfos = new ArrayList<>();
if(startedPlugins == null){
return pluginInfos;
}
return startedPlugins.stream()
.filter(pluginWrapper -> pluginWrapper != null)
.map(pw -> {
return getPluginInfo(pw);
})
.collect(Collectors.toList());
}
@Override
public PluginInfo getPluginInfo(String pluginId) {
PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId);
if(pluginWrapper == null){
log.warn("Not found plugin '{}'", pluginId);
return null;
}
return getPluginInfo(pluginWrapper);
}
@Override
public Set getPluginFilePaths() throws Exception {
RuntimeMode environment = integrationConfiguration.environment();
if(environment == RuntimeMode.DEVELOPMENT){
return new HashSet<>(integrationConfiguration.pluginPath());
}
List pluginPath = integrationConfiguration.pluginPath();
Set pathString = new HashSet<>(pluginPath.size());
for (String path : pluginPath) {
if(ObjectUtils.isEmpty(path)){
continue;
}
List jars = FileUtils.getJars(Paths.get(path));
for (File jar : jars) {
pathString.add(jar.getAbsolutePath());
}
}
return pathString;
}
@Override
public List getPluginWrapper() {
return pluginManager.getPlugins();
}
@Override
public PluginWrapper getPluginWrapper(String pluginId) {
return pluginManager.getPlugin(pluginId);
}
/**
* 上传插件
* @param pluginFile 插件文件
* @return 返回上传的插件路径
* @throws Exception 异常信息
*/
protected Path uploadPlugin(MultipartFile pluginFile) throws Exception {
if(pluginFile == null){
throw new IllegalArgumentException("Method:uploadPlugin param 'pluginFile' can not be null");
}
// 获取文件的后缀名
String fileName = pluginFile.getOriginalFilename();
String suffixName = fileName.substring(fileName.lastIndexOf(".") + 1);
//检查文件格式是否合法
if(StringUtils.isEmpty(suffixName)){
throw new IllegalArgumentException("Invalid file type, please select .jar or .zip file");
}
if(!"jar".equalsIgnoreCase(suffixName) && !"zip".equalsIgnoreCase(suffixName)){
throw new IllegalArgumentException("Invalid file type, please select .jar or .zip file");
}
String tempPathString = integrationConfiguration.uploadTempPath() + File.separator + fileName;
Path tempPath = PluginFileUtils.createExistFile(Paths.get(tempPathString));
Files.write(tempPath, pluginFile.getBytes());
try {
Path verifyPath = pluginLegalVerify.verify(tempPath);
if(verifyPath != null){
String targetPathString = pluginManager.getPluginsRoot().toString() +
File.separator + fileName;
Path targetPluginPath = Paths.get(targetPathString);
if(Files.exists(targetPluginPath)){
// 存在则拷贝一份
backup(targetPluginPath, "upload", 2);
}
// 拷贝校验的路径到插件路径下
Files.copy(verifyPath, targetPluginPath, StandardCopyOption.REPLACE_EXISTING);
// 删除临时文件
Files.deleteIfExists(tempPath);
return targetPluginPath;
} else {
Exception exception =
new Exception(fileName + " verify failure, verifyPath is null");
verifyFailureDelete(tempPath, exception);
throw exception;
}
} catch (Exception e){
// 出现异常, 删除刚才上传的临时文件
verifyFailureDelete(tempPath, e);
throw e;
}
}
/**
* 得到插件包装类
* @param pluginId 插件id
* @param errorMsg 错误信息
* @return PluginWrapper
* @throws Exception 插件装配异常
*/
protected PluginWrapper getPluginWrapper(String pluginId, String errorMsg) throws Exception {
PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId);
if (pluginWrapper == null) {
throw new Exception(errorMsg + " -> Not found plugin " + pluginId);
}
return pluginWrapper;
}
/**
* 校验文件失败后, 删除临时文件
* @param tempPluginFile 临时文件路径
* @param e 异常信息
* @throws Exception Exception
*/
protected void verifyFailureDelete(Path tempPluginFile, Exception e) throws Exception {
try {
Files.deleteIfExists(tempPluginFile);
}catch (IOException e1){
throw new Exception("Verify failure and delete temp file failure : " + e.getMessage(), e);
}
}
/**
* 备份
* @param sourcePath 源文件的路径
* @param sign 文件标志
* @param type 类型 1移动 2拷贝
* @return 结果
*/
protected boolean backup(Path sourcePath, String sign, int type) {
try {
if(isDev()){
// 如果是开发环境, 则不进行备份
return false;
}
if(sourcePath == null){
return false;
}
if(!Files.exists(sourcePath)){
log.error("Path '{}' does not exist", sourcePath.toString());
return false;
}
String fileName = sourcePath.getFileName().toString();
String targetName = integrationConfiguration.backupPath() + File.separator;
if(!ObjectUtils.isEmpty(sign)){
targetName = targetName + sign;
}
targetName = targetName + "_" +getNowTimeByFormat();
Path target = Paths.get(targetName + "_" + fileName);
if(!Files.exists(target.getParent())){
Files.createDirectories(target.getParent());
}
File sourceFile = sourcePath.toFile();
if(sourceFile.length() == 0){
// 源文件字节为0, 说明为删除的插件。不需要备份
return true;
}
if(type == 1){
// 是移动的话, 则删除源文件
Files.move(sourcePath, target, StandardCopyOption.REPLACE_EXISTING);
} else {
// 拷贝
Files.copy(sourcePath, target, StandardCopyOption.REPLACE_EXISTING);
}
return true;
} catch (IOException e) {
log.error("Backup plugin jar '{}' failure. {}", sourcePath.toString(), e.getMessage(), e);
return false;
}
}
/**
* 获取现在的时间
* @return String
*/
protected String getNowTimeByFormat(){
LocalDateTime localDateTime = LocalDateTime.now();
return FORMAT.format(localDateTime);
}
/**
* 是否是开发环境
* @return bolean
*/
protected boolean isDev(){
return integrationConfiguration.environment() == RuntimeMode.DEVELOPMENT;
}
/**
* 通过PluginWrapper得到插件信息
* @param pluginWrapper pluginWrapper
* @return PluginInfo
*/
private PluginInfo getPluginInfo(PluginWrapper pluginWrapper) {
return new PluginInfo(pluginWrapper.getDescriptor(), pluginWrapper.getPluginState(),
pluginWrapper.getPluginPath().toAbsolutePath().toString(),
pluginManager.getRuntimeMode().toString());
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy