com.github.codingdebugallday.integration.operator.DefaultPluginOperator Maven / Gradle / Ivy
The newest version!
package com.github.codingdebugallday.integration.operator;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import com.github.codingdebugallday.constants.BaseConstants;
import com.github.codingdebugallday.exceptions.PluginException;
import com.github.codingdebugallday.factory.DefaultPluginFactory;
import com.github.codingdebugallday.factory.PluginFactory;
import com.github.codingdebugallday.integration.IntegrationConfiguration;
import com.github.codingdebugallday.integration.listener.PluginInitializerListener;
import com.github.codingdebugallday.integration.listener.PluginInitializerListenerFactory;
import com.github.codingdebugallday.integration.listener.PluginListenerFactory;
import com.github.codingdebugallday.integration.operator.module.PluginInfo;
import com.github.codingdebugallday.integration.operator.verify.DefaultPluginVerify;
import com.github.codingdebugallday.integration.operator.verify.PluginLegalVerify;
import com.github.codingdebugallday.utils.GlobalRegistryInfo;
import com.github.codingdebugallday.utils.PluginFileUtils;
import com.github.codingdebugallday.utils.PluginOperatorInfo;
import org.pf4j.PluginManager;
import org.pf4j.PluginState;
import org.pf4j.PluginWrapper;
import org.pf4j.RuntimeMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
/**
*
* 默认的插件操作者
*
*
* @author isaac 2020/6/16 15:42
* @since 1.0
*/
public class DefaultPluginOperator implements PluginOperator {
private boolean isInit = false;
protected final Logger log = LoggerFactory.getLogger(this.getClass());
private static final DateTimeFormatter FORMAT = DateTimeFormatter.ofPattern(BaseConstants.Pattern.NONE_DATETIME);
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, "IntegrationConfiguration can't be null");
Objects.requireNonNull(pluginManager, "PluginManager can't be null");
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 void initPlugins(PluginInitializerListener pluginInitializerListener) {
if (isInit) {
throw new PluginException("Plugins Already initialized. Cannot be initialized again");
}
try {
// 启动前, 清除空文件
PluginFileUtils.cleanEmptyFile(pluginManager.getPluginsRoot());
pluginInitializerListenerFactory.addPluginInitializerListeners(pluginInitializerListener);
log.info("Plugins start initialize of root path '{}'", pluginManager.getPluginsRoot());
// 触发插件初始化监听器
pluginInitializerListenerFactory.before();
// 开始初始化插件工厂
pluginFactory.initialize();
// 开始加载插件
pluginManager.loadPlugins();
pluginManager.startPlugins();
List pluginWrappers = pluginManager.getStartedPlugins();
if (pluginWrappers == null || pluginWrappers.isEmpty()) {
log.warn("Not found plugin!");
return;
}
boolean isFoundException = false;
for (PluginWrapper pluginWrapper : pluginWrappers) {
String pluginId = pluginWrapper.getPluginId();
GlobalRegistryInfo.addOperatorPluginInfo(pluginId,
PluginOperatorInfo.OperatorType.INSTALL, false);
isFoundException = register(pluginWrapper, pluginId);
}
pluginFactory.build();
isInit = true;
if (isFoundException) {
log.error("Plugins initialize failure");
} else {
log.info("Plugins initialize success");
pluginInitializerListenerFactory.complete();
}
} catch (Exception e) {
pluginInitializerListenerFactory.failure(e);
throw e;
}
}
private boolean register(PluginWrapper pluginWrapper, String pluginId) {
boolean isFoundException = false;
try {
// 依次注册插件信息到Spring boot
pluginFactory.register(pluginWrapper);
} catch (Exception e) {
log.error("Plugin '{}' registry failure. Reason : {}", pluginId, e.getMessage(), e);
isFoundException = true;
}
return isFoundException;
}
@Override
public boolean install(Path path) {
if (isDev()) {
throw new PluginException("Plugin cannot be installed in 'dev' environment");
}
if (path == null) {
throw new IllegalArgumentException("Method:install param 'pluginId' can not be empty");
}
String pluginId = null;
try {
pluginId = load(path);
if (StringUtils.isEmpty(pluginId)) {
log.error("Plugin '{}' install failure, this plugin id is empty.", pluginId);
return false;
}
GlobalRegistryInfo.addOperatorPluginInfo(pluginId, PluginOperatorInfo.OperatorType.INSTALL, true);
if (start(pluginId)) {
log.info("Plugin '{}' install success", pluginId);
return true;
} else {
log.error("Plugin '{}' install failure", pluginId);
return false;
}
} catch (Exception e) {
// 说明load成功, 但是没有启动成功, 则卸载该插件
if (!StringUtils.isEmpty(pluginId)) {
log.error("Plugin '{}' install failure. {}", pluginId, e.getMessage());
log.info("Start uninstall plugin '{}' failure", pluginId);
try {
uninstall(pluginId, false);
} catch (Exception uninstallException) {
log.error("Plugin '{}' uninstall failure. {}", pluginId, uninstallException.getMessage());
}
}
throw new PluginException(e);
} finally {
if (!StringUtils.isEmpty(pluginId)) {
GlobalRegistryInfo.setOperatorPluginInfo(pluginId, false);
}
}
}
private String load(Path path) throws IOException {
String pluginId;
if (!Files.exists(path)) {
throw new FileNotFoundException("Not found this path " + path);
}
// 校验插件文件
pluginLegalVerify.verify(path);
Path pluginsRoot = pluginManager.getPluginsRoot();
if (path.getParent().endsWith(pluginsRoot)) {
// 说明该插件文件存在于插件root目录下。直接加载该插件
pluginId = pluginManager.loadPlugin(path);
} else {
File sourceFile = path.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(path, targetPath, StandardCopyOption.REPLACE_EXISTING);
pluginId = pluginManager.loadPlugin(targetPath);
}
return pluginId;
}
@Override
public boolean uninstall(String pluginId, boolean isBackup) {
if (StringUtils.isEmpty(pluginId)) {
throw new IllegalArgumentException("Method:uninstall param 'pluginId' can not be empty");
}
PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId);
if (pluginWrapper == null) {
throw new PluginException("Plugin uninstall failure, Not found plugin '" + pluginId + "'");
}
if (pluginWrapper.getPluginState() == PluginState.STARTED) {
try {
pluginFactory.unregister(pluginId);
pluginFactory.build();
} catch (Exception e) {
log.error("Plugin '{}' uninstall failure, {}", pluginId, e.getMessage());
}
}
try {
if (pluginManager.unloadPlugin(pluginId)) {
Path pluginPath = pluginWrapper.getPluginPath();
if (isBackup) {
// 将插件文件移到备份文件中
backup(pluginPath, "uninstall", 1);
} else {
// 不备份的话。直接删除该文件
Files.deleteIfExists(pluginPath);
}
log.info("Plugin '{}' uninstall success", pluginId);
return true;
} else {
log.error("Plugin '{}' uninstall failure", pluginId);
return false;
}
} catch (IOException e) {
String error = String.format("Plugin '%s' uninstall failure: %s", pluginId, e.getMessage());
if (BaseConstants.OS_IS_WINDOWS) {
log.warn(error);
return false;
} else {
log.error(error);
throw new PluginException(error, e);
}
}
}
@Override
public boolean start(String pluginId) {
if (StringUtils.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 PluginException("This plugin '" + pluginId + "' have already started");
}
try {
PluginState pluginState = pluginManager.startPlugin(pluginId);
if (pluginState == PluginState.STARTED) {
GlobalRegistryInfo.addOperatorPluginInfo(pluginId, PluginOperatorInfo.OperatorType.START, false);
pluginFactory.register(pluginWrapper);
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);
} catch (Exception e) {
String error = String.format("Plugin '%s' start failure: %s", pluginId, e.getMessage());
log.error(error);
log.info("Start stop plugin {}", pluginId);
try {
stop(pluginId);
} catch (Exception stopException) {
log.error("Plugin '{}' stop failure: {}", pluginId, stopException.getMessage());
}
throw new PluginException(e);
}
return false;
}
@Override
public boolean stop(String pluginId) {
if (StringUtils.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 PluginException("This plugin '" + pluginId + "' is not started");
}
try {
pluginFactory.unregister(pluginId);
pluginFactory.build();
} catch (Exception e) {
log.error("Plugin '{}' stop failure. {}", pluginId, e.getMessage());
}
try {
pluginManager.stopPlugin(pluginId);
log.info("Plugin '{}' stop success", pluginId);
return true;
} catch (Exception e) {
String error = String.format("Plugin '%s' stop failure: %s", pluginId, e.getMessage());
log.error(error);
throw new PluginException(e);
}
}
@Override
public boolean uploadPluginAndStart(MultipartFile pluginFile) {
if (isDev()) {
throw new PluginException("Plugin cannot be installed in the 'dev' environment");
}
if (pluginFile == null) {
throw new IllegalArgumentException("Method:uploadPluginAndStart param 'pluginFile' can not be null");
}
Path path = uploadPlugin(pluginFile);
if (this.install(path)) {
log.info("Plugin upload and start success");
return true;
} else {
log.error("Plugin upload and start failure");
return false;
}
}
@Override
public boolean installConfigFile(Path path) {
if (!Files.exists(path)) {
throw new PluginException("path ' " + path + "' does not exist!");
}
File sourceFile = path.toFile();
String configPath = integrationConfiguration.pluginConfigFilePath() +
File.separator + sourceFile.getName();
try {
Path targetPath = PluginFileUtils.createExistFile(Paths.get(configPath));
if (Files.exists(targetPath)) {
// 如果文件存在, 则移动备份
backup(targetPath, "install-config-backup", 1);
}
Files.copy(path, targetPath, StandardCopyOption.REPLACE_EXISTING);
return true;
} catch (IOException e) {
throw new PluginException(e);
}
}
@Override
public boolean uploadConfigFile(MultipartFile configFile) {
if (configFile == null) {
throw new PluginException("Method:uploadConfigFile param 'configFile' can not be null");
}
String fileName = configFile.getOriginalFilename();
String configPath = integrationConfiguration.pluginConfigFilePath() +
File.separator + fileName;
try {
Path targetPath = PluginFileUtils.createExistFile(Paths.get(configPath));
if (Files.exists(targetPath)) {
// 如果文件存在, 则拷贝备份
backup(targetPath, "upload-config-backup", 2);
}
// 然后写入数据到该文件
Files.write(targetPath, configFile.getBytes());
return true;
} catch (IOException e) {
throw new PluginException(e);
}
}
@Override
public boolean backupPlugin(Path path, String sign) {
Objects.requireNonNull(path);
return backup(path, sign, 2);
}
@Override
public boolean backupPlugin(String pluginId, String sign) {
PluginWrapper wrapper = getPluginWrapper(pluginId, "BackupPlugin by pluginId");
return backupPlugin(wrapper.getPluginPath(), sign);
}
@Override
public List getPluginInfo() {
List startedPlugins = pluginManager.getPlugins();
List pluginInfos = new ArrayList<>();
if (startedPlugins == null) {
return pluginInfos;
}
return startedPlugins.stream()
.filter(Objects::nonNull)
.map(this::getPluginInfo)
.collect(Collectors.toList());
}
@Override
public PluginInfo getPluginInfo(String pluginId) {
PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId);
if (pluginWrapper == null) {
throw new PluginException("Not found plugin '" + pluginId + "'");
}
return getPluginInfo(pluginWrapper);
}
/**
* 通过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());
}
@Override
public Set getPluginFilePaths() {
RuntimeMode environment = integrationConfiguration.environment();
Set paths = new HashSet<>();
if (environment == RuntimeMode.DEVELOPMENT) {
paths.add(integrationConfiguration.pluginPath());
return paths;
}
List files = org.pf4j.util.FileUtils.getJars(Paths.get(integrationConfiguration.pluginPath()));
return files.stream()
.filter(Objects::nonNull)
.map(File::getAbsolutePath)
.collect(Collectors.toSet());
}
@Override
public List getPluginWrapper() {
return pluginManager.getPlugins();
}
@Override
public PluginWrapper getPluginWrapper(String pluginId) {
return pluginManager.getPlugin(pluginId);
}
/**
* 上传插件
*
* @param pluginFile 插件文件
* @return 返回上传的插件路径
*/
protected Path uploadPlugin(MultipartFile pluginFile) {
if (pluginFile == null) {
throw new IllegalArgumentException("Method:uploadPlugin param 'pluginFile' can not be null");
}
// 获取文件的后缀名
String fileName = pluginFile.getOriginalFilename();
assert fileName != null;
String suffixName = fileName.substring(fileName.lastIndexOf('.') + 1);
// 检查文件格式是否合法
if (StringUtils.isEmpty(suffixName)) {
throw new IllegalArgumentException("Invalid file type, please select .jar or .zip file");
}
if (!BaseConstants.Suffix.JAR.equalsIgnoreCase(suffixName) &&
!BaseConstants.Suffix.ZIP.equalsIgnoreCase(suffixName)) {
throw new IllegalArgumentException("Invalid file type, please select .jar or .zip file");
}
String tempPathString = integrationConfiguration.uploadTempPath() + File.separator + fileName;
Path tempPath;
try {
tempPath = PluginFileUtils.createExistFile(Paths.get(tempPathString));
Files.write(tempPath, pluginFile.getBytes());
} catch (IOException e) {
throw new PluginException(e);
}
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);
}
doCopyAndDelete(verifyPath, targetPluginPath, 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 new PluginException(e);
}
}
private void doCopyAndDelete(Path verifyPath, Path targetPluginPath, Path tempPath) throws IOException {
try {
// 拷贝校验的路径到插件路径下
Files.copy(verifyPath, targetPluginPath, StandardCopyOption.REPLACE_EXISTING);
// 删除临时文件
Files.deleteIfExists(tempPath);
} catch (FileSystemException e) {
log.warn("{}", e.getMessage());
}
}
/**
* 得到插件包装类
*
* @param pluginId 插件id
* @param errorMsg 错误信息
* @return PluginWrapper
*/
protected PluginWrapper getPluginWrapper(String pluginId, String errorMsg) {
PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId);
if (pluginWrapper == null) {
throw new PluginException(errorMsg + " -> Not found plugin " + pluginId);
}
return pluginWrapper;
}
/**
* 校验文件失败后, 删除临时文件
*
* @param tempPluginFile 临时文件路径
* @param e 异常信息
*/
protected void verifyFailureDelete(Path tempPluginFile, Exception e) {
try {
Files.deleteIfExists(tempPluginFile);
} catch (IOException e1) {
throw new PluginException("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);
return false;
}
String fileName = sourcePath.getFileName().toString();
String targetName = integrationConfiguration.backupPath() + File.separator;
if (!StringUtils.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 (FileSystemException e) {
log.warn("Backup plugin jar '{}' failure. {}", sourcePath, e.getMessage());
return true;
} catch (IOException e) {
log.error("Backup plugin jar '{}' failure. {}", sourcePath, e.getMessage(), e);
return false;
}
}
/**
* 获取现在的时间
*
* @return String
*/
protected String getNowTimeByFormat() {
LocalDateTime localDateTime = LocalDateTime.now();
return FORMAT.format(localDateTime);
}
/**
* 是否是开发环境
*
* @return boolean
*/
protected boolean isDev() {
return integrationConfiguration.environment() == RuntimeMode.DEVELOPMENT;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy