feat(插件管理): 集群环境依赖redis的插件操作功能

This commit is contained in:
fit2cloud-chenyw 2022-12-16 16:33:19 +08:00
parent 563b57faa5
commit e8eee03422
5 changed files with 170 additions and 7 deletions

View File

@ -3,6 +3,7 @@ package io.dataease.commons.utils;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.util.Arrays;
public class IPUtils {
@ -42,4 +43,12 @@ public class IPUtils {
ipStr = Arrays.stream(ipStr.split(",")).filter(item -> StringUtils.isNotBlank(item) && !StringUtils.equalsIgnoreCase(UNKNOWN, item.trim())).findFirst().orElse(ipStr);
return StringUtils.equals(LOCAL_IP_KEY, ipStr) ? LOCAL_IP_VAL : ipStr;
}
public static String domain() {
try {
return InetAddress.getLocalHost().getHostAddress();
} catch (Exception e) {
return LOCAL_IP_VAL;
}
}
}

View File

@ -0,0 +1,20 @@
package io.dataease.plugins.entity;
import io.dataease.plugins.common.base.domain.MyPlugin;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PluginOperate implements Serializable {
private String type;
private MyPlugin plugin;
private String senderIp;
}

View File

@ -1,13 +1,57 @@
package io.dataease.service.redis.impl;
import com.google.gson.Gson;
import io.dataease.commons.utils.IPUtils;
import io.dataease.commons.utils.LogUtil;
import io.dataease.plugins.common.base.domain.MyPlugin;
import io.dataease.plugins.entity.PluginOperate;
import io.dataease.service.redis.RedisMessageBroadcast;
import io.dataease.service.sys.PluginService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class PluginMsgService implements RedisMessageBroadcast {
private static Gson json = new Gson();
@Resource
private PluginService pluginService;
@Override
public void messageCallBack(Object arg) {
PluginOperate operate = json.fromJson(json.toJson(arg), PluginOperate.class);
String domain = IPUtils.domain();
if (StringUtils.equals(domain, operate.getSenderIp())) return;
String operateType = operate.getType();
MyPlugin plugin = operate.getPlugin();
if (StringUtils.equals("install", operateType)) {
LogUtil.info("start install plugin [{}] in domain {}", plugin.getName(), domain);
install(plugin);
}
if (StringUtils.equals("uninstall", operateType)) {
LogUtil.info("start uninstall plugin [{}] in domain {}", plugin.getName(), domain);
uninstall(plugin);
}
if (StringUtils.equals("update", operateType)) {
LogUtil.info("start update plugin [{}] in domain {}", plugin.getName(), domain);
updateInstall(plugin);
}
}
private void install(MyPlugin plugin) {
pluginService.redisBroadcastInstall(plugin);
}
private void uninstall(MyPlugin plugin) {
pluginService.redisBroadcastUnInstall(plugin);
}
private void updateInstall(MyPlugin plugin) {
if (pluginService.redisBroadcastUnInstall(plugin)) {
pluginService.redisBroadcastInstall(plugin);
}
}
}

View File

@ -0,0 +1,32 @@
package io.dataease.service.sys;
import io.dataease.commons.condition.RedisStatusCondition;
import io.dataease.commons.constants.RedisConstants;
import io.dataease.commons.model.RedisMessage;
import io.dataease.plugins.entity.PluginOperate;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Conditional;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
@Conditional({RedisStatusCondition.class})
public class DistributedPluginService {
@Resource
private RedisTemplate redisTemplate;
public void pushBroadcast(PluginOperate operate) {
if (ObjectUtils.isEmpty(operate) || ObjectUtils.isEmpty(operate.getPlugin()) || StringUtils.isBlank(operate.getSenderIp()))
return;
RedisMessage<PluginOperate> msg = new RedisMessage();
msg.setType(RedisConstants.PLUGIN_INSTALL_MSG);
msg.setData(operate);
redisTemplate.convertAndSend(RedisConstants.GLOBAL_REDIS_TOPIC, msg);
}
}

View File

@ -3,6 +3,7 @@ package io.dataease.service.sys;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ZipUtil;
import com.google.gson.Gson;
import io.dataease.commons.utils.IPUtils;
import io.dataease.dto.MyPluginDTO;
import io.dataease.ext.ExtSysPluginMapper;
import io.dataease.ext.query.GridExample;
@ -17,6 +18,7 @@ import io.dataease.listener.util.CacheUtils;
import io.dataease.plugins.common.base.domain.MyPlugin;
import io.dataease.plugins.common.base.mapper.MyPluginMapper;
import io.dataease.plugins.config.LoadjarUtil;
import io.dataease.plugins.entity.PluginOperate;
import io.dataease.service.datasource.DatasourceService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
@ -56,6 +58,9 @@ public class PluginService {
@Autowired
private LoadjarUtil loadjarUtil;
@Autowired(required = false)
private DistributedPluginService distributedPluginService;
@Value("${version}")
private String version;
@ -71,7 +76,7 @@ public class PluginService {
* @param file
* @return
*/
public Map<String, Object> localInstall(MultipartFile file) throws Exception{
public Map<String, Object> localInstall(MultipartFile file) throws Exception {
//1.上传文件到服务器pluginDir目录下
File dest = DeFileUtils.upload(file, pluginDir + "temp/");
//2.解压目标文件dest 得到plugin.json和jar
@ -113,7 +118,7 @@ public class PluginService {
}
if (pluginExist(myPlugin)) {
String msg = "插件【"+myPlugin.getName()+"】已存在,请先卸载";
String msg = "插件【" + myPlugin.getName() + "】已存在,请先卸载";
LogUtil.error(msg);
DEException.throwException(msg);
}
@ -123,7 +128,7 @@ public class PluginService {
targetDir = makeTargetDir(myPlugin);
String jarPath;
jarPath = DeFileUtils.copy(jarFile, targetDir);
if(myPlugin.getCategory().equalsIgnoreCase("datasource")){
if (myPlugin.getCategory().equalsIgnoreCase("datasource")) {
DeFileUtils.copyFolder(folder + "/" + myPlugin.getDsType() + "Driver", targetDir + myPlugin.getDsType() + "Driver");
}
loadJar(jarPath, myPlugin);
@ -142,13 +147,49 @@ public class PluginService {
DeFileUtils.deleteFile(pluginDir + "temp/");
DeFileUtils.deleteFile(folder);
}
distributeOperate(myPlugin, "install");
return null;
}
public void distributeOperate(MyPlugin plugin, String type) {
if (ObjectUtils.isNotEmpty(distributedPluginService)) {
PluginOperate operate = new PluginOperate();
operate.setPlugin(plugin);
operate.setSenderIp(IPUtils.domain());
operate.setType(type);
if (ObjectUtils.isEmpty(plugin) || StringUtils.isBlank(type) || StringUtils.isBlank(operate.getSenderIp()))
return;
distributedPluginService.pushBroadcast(operate);
}
}
public void loadJar(String jarPath, MyPlugin myPlugin) throws Exception {
loadjarUtil.loadJar(jarPath, myPlugin);
}
public void redisBroadcastInstall(MyPlugin plugin) {
String path = getPath(plugin);
try {
if (FileUtil.exist(path)) {
loadJar(path, plugin);
} else {
LogUtil.error("插件路径不存在 {} ", path);
}
} catch (Exception e) {
LogUtil.error(e);
}
}
public String getPath(MyPlugin plugin) {
String store = plugin.getStore();
String version = plugin.getVersion();
String moduleName = plugin.getModuleName();
String fileName = moduleName + "-" + version + ".jar";
String path = pluginDir + store + "/" + fileName;
return path;
}
private String makeTargetDir(MyPlugin myPlugin) {
String store = myPlugin.getStore();
String dir = pluginDir + store + "/";
@ -161,6 +202,7 @@ public class PluginService {
/**
* 检测插件是否已存在
*
* @param myPlugin
* @return
*/
@ -190,13 +232,29 @@ public class PluginService {
CacheUtils.removeAll(AuthConstants.USER_ROLE_CACHE_NAME);
CacheUtils.removeAll(AuthConstants.USER_PERMISSION_CACHE_NAME);
if(myPlugin.getCategory().equalsIgnoreCase("datasource")){
if(CollectionUtils.isNotEmpty(datasourceService.selectByType(myPlugin.getDsType()))){
if (myPlugin.getCategory().equalsIgnoreCase("datasource")) {
if (CollectionUtils.isNotEmpty(datasourceService.selectByType(myPlugin.getDsType()))) {
DEException.throwException(Translator.get("i18n_plugin_not_allow_delete"));
}
loadjarUtil.deleteModule(myPlugin.getModuleName() + "-" + myPlugin.getVersion());
}
myPluginMapper.deleteByPrimaryKey(pluginId);
distributeOperate(myPlugin, "uninstall");
return true;
}
public Boolean redisBroadcastUnInstall(MyPlugin myPlugin) {
CacheUtils.removeAll(AuthConstants.USER_ROLE_CACHE_NAME);
CacheUtils.removeAll(AuthConstants.USER_CACHE_NAME);
CacheUtils.removeAll(AuthConstants.USER_PERMISSION_CACHE_NAME);
if (myPlugin.getCategory().equalsIgnoreCase("datasource")) {
if (CollectionUtils.isNotEmpty(datasourceService.selectByType(myPlugin.getDsType()))) {
DEException.throwException(Translator.get("i18n_plugin_not_allow_delete"));
}
loadjarUtil.deleteModule(myPlugin.getModuleName() + "-" + myPlugin.getVersion());
}
myPluginMapper.deleteByPrimaryKey(myPlugin.getPluginId());
return true;
}
@ -208,7 +266,7 @@ public class PluginService {
File jarFile = new File(path);
FileUtil.del(jarFile);
if(plugin.getCategory().equalsIgnoreCase("datasource")){
if (plugin.getCategory().equalsIgnoreCase("datasource")) {
File driverFile = new File(pluginDir + plugin.getStore() + "/" + plugin.getDsType() + "Driver");
FileUtil.del(driverFile);
}
@ -259,7 +317,7 @@ public class PluginService {
} catch (InvocationTargetException e) {
e.printStackTrace();
}
if(result.getCategory().equalsIgnoreCase("datasource") && (StringUtils.isEmpty(result.getStore()) || !result.getStore().equalsIgnoreCase("default"))){
if (result.getCategory().equalsIgnoreCase("datasource") && (StringUtils.isEmpty(result.getStore()) || !result.getStore().equalsIgnoreCase("default"))) {
result.setStore("thirdpart");
}