From c12dae87f1dba43ccc2c5966a134ddf88b5eb102 Mon Sep 17 00:00:00 2001 From: dengzedong <dengzedong@email> Date: 星期四, 10 十月 2024 14:03:56 +0800 Subject: [PATCH] 打包历史发布功能,动态替换dll和jar 项目启动加载已发布的dll和jar --- iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/common/utils/DllUtils.java | 109 +++++++++++++++++++++++--- iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/ModelServiceApplication.java | 23 +++++ iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/common/MdkConstant.java | 8 ++ iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/service/impl/MpkFileServiceImpl.java | 76 ++++++++++++++++++ iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/service/MpkFileService.java | 3 iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/controller/admin/MpkFileController.java | 7 + iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/controller/admin/MdkController.java | 4 7 files changed, 207 insertions(+), 23 deletions(-) diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/ModelServiceApplication.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/ModelServiceApplication.java index 4cb632e..c649998 100644 --- a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/ModelServiceApplication.java +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/ModelServiceApplication.java @@ -1,27 +1,37 @@ package com.iailab; +import com.iailab.module.model.mpk.common.MdkConstant; +import com.iailab.module.model.mpk.common.utils.DllUtils; import iail.mdk.model.common.Environment; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; +import javax.annotation.PostConstruct; import java.io.File; import java.io.InputStream; import java.util.Properties; @EnableAsync @SpringBootApplication +@Slf4j public class ModelServiceApplication implements CommandLineRunner { - static { + @Value("${mpk.bak-file-path}") + private String mpkBakFilePath; + + @PostConstruct + void init() { //加载动态链接库 try { Properties properties = new Properties(); InputStream in = ModelServiceApplication.class.getClassLoader().getResourceAsStream("iailmdk.properties"); properties.load(in); String mdkInitPath = properties.getProperty("mdk-init-path"); - System.out.println("mdkInitPath=" + mdkInitPath); + log.info("mdkInitPath=" + mdkInitPath); File file = new File(mdkInitPath + File.separator + "IAIL.MDK.Mid.Windows.dll"); if (!file.exists()) { throw new RuntimeException("动态链接库IAIL.MDK.Mid.Windows.dll文件不存在," + file.getAbsolutePath()); @@ -31,9 +41,16 @@ env.init(); } catch (Exception e) { e.printStackTrace(); - System.out.println("动态链接库IAIL.MDK.Mid.Windows.dll初始化失败"); + log.error("动态链接库IAIL.MDK.Mid.Windows.dll初始化失败"); } + //加载项目已发布的dll和jar + try { + DllUtils.loadProjectPublish(mpkBakFilePath + File.separator + MdkConstant.PROJECT_PUBLISH); + } catch (Exception e) { + e.printStackTrace(); + log.error("加载项目已发布的dll和jar失败"); + } } diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/common/MdkConstant.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/common/MdkConstant.java index 37c39da..b3be1da 100644 --- a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/common/MdkConstant.java +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/common/MdkConstant.java @@ -34,4 +34,12 @@ * jar */ String JAR = "jar"; + /** + * 分隔符 dll、jar文件名 + */ + String SPLIT = "__"; + /** + * 项目发布dll和jar目录 + */ + String PROJECT_PUBLISH = "projectPublish"; } \ No newline at end of file diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/common/utils/DllUtils.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/common/utils/DllUtils.java index d4831ad..94b3964 100644 --- a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/common/utils/DllUtils.java +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/common/utils/DllUtils.java @@ -1,19 +1,22 @@ package com.iailab.module.model.mpk.common.utils; +import com.iailab.module.model.mpk.common.MdkConstant; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import sun.misc.URLClassPath; import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; +import java.util.HashMap; import java.util.Iterator; import java.util.Vector; @Slf4j public class DllUtils { + + private static HashMap<String, URLClassLoader> classLoaderCache = new HashMap<>(); /** * @description: 加载dll到指定class下 @@ -22,14 +25,14 @@ **/ public static void loadDll(Class<?> clazz, String dllPath) { try { - Method method = Runtime.class.getDeclaredMethod("load0", Class.class,String.class); + Method method = Runtime.class.getDeclaredMethod("load0", Class.class, String.class); boolean accessible = method.isAccessible(); method.setAccessible(true); - method.invoke(Runtime.getRuntime(), clazz,dllPath); + method.invoke(Runtime.getRuntime(), clazz, dllPath); method.setAccessible(accessible); - log.info("成功加载dll:"+ dllPath); + log.info("成功加载dll:" + dllPath); } catch (Exception e) { - throw new RuntimeException("加载dll异常",e); + throw new RuntimeException("加载dll异常", e); } } @@ -38,7 +41,7 @@ * @author: dzd * @date: 2024/9/30 14:31 **/ - public static synchronized void uploadDll(URLClassLoader classLoader) { + public static synchronized void unloadDll(URLClassLoader classLoader) { try { Field field = ClassLoader.class.getDeclaredField("nativeLibraries"); field.setAccessible(true); @@ -60,7 +63,7 @@ log.info("成功卸载dll:" + name); } } catch (Exception e) { - throw new RuntimeException("卸载dll异常",e); + throw new RuntimeException("卸载dll异常", e); } } @@ -69,7 +72,7 @@ * @author: dzd * @date: 2024/9/30 14:52 **/ - public static synchronized void uploadDllName(URLClassLoader classLoader,String dllName) { + public static synchronized void unloadDllName(URLClassLoader classLoader, String dllName) { try { Field field = ClassLoader.class.getDeclaredField("nativeLibraries"); field.setAccessible(true); @@ -93,7 +96,7 @@ log.info("成功卸载dll:" + name); } } catch (Exception e) { - throw new RuntimeException("卸载dll异常",e); + throw new RuntimeException("卸载dll异常", e); } } @@ -105,24 +108,100 @@ public static synchronized URLClassLoader loadJar(String jarPath) { File jarFile = new File(jarPath); if (!jarFile.exists()) { - throw new RuntimeException("jar沒有找到!"); + throw new RuntimeException("jar沒有找到!"+jarPath); } else { try { - URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{jarFile.toURI().toURL()}); - log.info("成功加载jar包:"+ jarFile.getAbsolutePath()); + // 设置classloader的patent为null,限制使用双亲委派,防止其他classloader找到class,导致dll加载到其他classloader + URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{jarFile.toURI().toURL()},null,null); + log.info("成功加载jar包:" + jarFile.getAbsolutePath()); return urlClassLoader; } catch (Exception e) { - throw new RuntimeException("加载jar异常",e); + throw new RuntimeException("加载jar异常", e); } } } - public static synchronized void uploadJar(URLClassLoader urlClassLoader) { + public static synchronized void unloadJar(URLClassLoader urlClassLoader) { try { urlClassLoader.close(); log.info("成功卸载jar包。"); } catch (Exception e) { - throw new RuntimeException("卸载jar异常",e); + throw new RuntimeException("卸载jar异常", e); } } + + public static synchronized void addClassLoaderCache(String projectId, URLClassLoader urlClassLoader) { + classLoaderCache.put(projectId, urlClassLoader); + } + + public static synchronized URLClassLoader getClassLoader(String projectId) { + return classLoaderCache.get(projectId); + } + + public static synchronized void removeClassLoaderCache(String projectId) { + if (classLoaderCache.containsKey(projectId)) { + URLClassLoader urlClassLoader = classLoaderCache.get(projectId); + unloadDll(urlClassLoader); + unloadJar(urlClassLoader); + classLoaderCache.remove(projectId); + } + } + + public static void removeOldFile(String bakPath,String projectId) { + File dir = new File(bakPath); + if (dir.exists() && dir.isDirectory()) { + File[] files = dir.listFiles(); + if (null != files && files.length > 0) { + for (File file : files) { + if (file.getName().startsWith(projectId)) { + file.delete(); + } + } + } + } + } + + /** + * @description: 项目启动加载已发布的dll和jar + * @author: dzd + * @date: 2024/10/10 11:58 + **/ + public static void loadProjectPublish(String bakPath) { + File dir = new File(bakPath); + if (dir.exists() && dir.isDirectory()) { + File[] files = dir.listFiles(); + if (null != files && files.length > 0) { + for (File file : files) { + String fileName = file.getName(); + if (fileName.endsWith(".jar")) { + String[] split = fileName.substring(0,fileName.length() - 4).split(MdkConstant.SPLIT); + String projectId = split[0]; + String historyId = split[1]; + + String jarFilePath = bakPath + File.separator + projectId + MdkConstant.SPLIT + historyId + ".jar"; + String dllFilePath = bakPath + File.separator + projectId + MdkConstant.SPLIT + historyId + ".dll"; + + URLClassLoader urlClassLoader = null; + try { + // 加载新的jar + urlClassLoader = DllUtils.loadJar(jarFilePath); + } catch (Exception e) { + throw new RuntimeException("加载jar异常",e); + } + + try { + // 加载新的dll + DllUtils.loadDll(urlClassLoader.loadClass("iail.mdk.model.common.Environment"),dllFilePath); + } catch (Exception e) { + DllUtils.unloadJar(urlClassLoader); + throw new RuntimeException("加载dll异常",e); + } + // 都加载成功后加入缓存 + DllUtils.addClassLoaderCache(projectId,urlClassLoader); + } + } + } + } + + } } diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/controller/admin/MdkController.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/controller/admin/MdkController.java index 6141714..81ae60c 100644 --- a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/controller/admin/MdkController.java +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/controller/admin/MdkController.java @@ -104,8 +104,8 @@ return error(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"运行异常"); } finally { if (classLoader != null) { - DllUtils.uploadDll(classLoader); - DllUtils.uploadJar(classLoader); + DllUtils.unloadDll(classLoader); + DllUtils.unloadJar(classLoader); } System.gc(); } diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/controller/admin/MpkFileController.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/controller/admin/MpkFileController.java index f7c2e75..8f8ebed 100644 --- a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/controller/admin/MpkFileController.java +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/controller/admin/MpkFileController.java @@ -125,8 +125,13 @@ @PostMapping("/upload") @Operation(summary = "python文件上传") - public CommonResult<Map<String,String>> importExcel(@RequestParam("file") MultipartFile file) throws Exception { + public CommonResult<Map<String,String>> upload(@RequestParam("file") MultipartFile file) throws Exception { Map<String,String> result = mpkFileService.savePyFile(file); return success(result); } + + @PostMapping("/publish") + public CommonResult<String> publish(@RequestBody Map<String, Object> params) { + return mpkFileService.publish(params); + } } \ No newline at end of file diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/service/MpkFileService.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/service/MpkFileService.java index 53da080..b33499c 100644 --- a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/service/MpkFileService.java +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/service/MpkFileService.java @@ -1,6 +1,7 @@ package com.iailab.module.model.mpk.service; import com.iailab.framework.common.page.PageData; +import com.iailab.framework.common.pojo.CommonResult; import com.iailab.framework.common.service.BaseService; import com.iailab.module.model.mpk.dto.MpkFileDTO; import com.iailab.module.model.mpk.entity.MpkFileEntity; @@ -34,4 +35,6 @@ byte[] packageModel(List<String> ids,String projectId,String projectName,String fileName,String log,String version) throws IOException, InterruptedException; Map<String,String> savePyFile(MultipartFile file) throws IOException; + + CommonResult<String> publish(Map<String, Object> params); } \ No newline at end of file diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/service/impl/MpkFileServiceImpl.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/service/impl/MpkFileServiceImpl.java index a33aa2e..c46b3df 100644 --- a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/service/impl/MpkFileServiceImpl.java +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mpk/service/impl/MpkFileServiceImpl.java @@ -8,12 +8,14 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.iailab.framework.common.page.PageData; +import com.iailab.framework.common.pojo.CommonResult; import com.iailab.framework.common.service.impl.BaseServiceImpl; import com.iailab.framework.common.util.date.DateUtils; import com.iailab.framework.common.util.object.ConvertUtils; import com.iailab.framework.security.core.util.SecurityFrameworkUtils; import com.iailab.module.infra.api.config.ConfigApi; import com.iailab.module.model.mpk.common.MdkConstant; +import com.iailab.module.model.mpk.common.utils.DllUtils; import com.iailab.module.model.mpk.common.utils.GenUtils; import com.iailab.module.model.mpk.dao.MpkFileDao; import com.iailab.module.model.mpk.dto.GeneratorCodeHistoryDTO; @@ -36,6 +38,7 @@ import org.springframework.web.multipart.MultipartFile; import java.io.*; +import java.net.URLClassLoader; import java.nio.file.Files; import java.util.*; import java.util.stream.Collectors; @@ -368,6 +371,16 @@ //生成dll文件 String dllSavePath = dirPath.getAbsolutePath() + File.separator + MdkConstant.LIBS + File.separator + "IAIL.MDK.Mid.Jni.dll"; createDllFile(dirPath.getAbsolutePath(),cppFilePaths,dllSavePath); + // 打包历史id + String historyId = UUID.randomUUID().toString(); + //备份dll,发布时使用 + File dllFile = new File(dllSavePath); + if (dllFile.exists()) { + File dllBakFile = new File(mpkBakFilePath + File.separator + projectId + MdkConstant.SPLIT + historyId + ".dll"); + FileUtil.copy(dllFile, dllBakFile, true); + }else { + log.error("dll文件不存在,无法备份。" + dllSavePath); + } //utils + env java文件 File utilsJavaFile = new File(dirPath.getAbsolutePath() + File.separator + "IAILMDK" + File.separator + "iail" + File.separator + "mdk" + File.separator + "model" + File.separator + "utils" + File.separator + "AlgsUtils.java"); @@ -383,10 +396,18 @@ // 删除java源文件 deleteJavaFile(javaFilePaths); // 打jar包 - pkgJar(dirPath.getAbsolutePath()); + String jarSavePath = pkgJar(dirPath.getAbsolutePath()); + //备份jar包,发布时使用 + File jarFile = new File(jarSavePath); + if (jarFile.exists()) { + File jarBakFile = new File(mpkBakFilePath + File.separator + projectId + MdkConstant.SPLIT + historyId + ".jar"); + FileUtil.copy(jarFile, jarBakFile, true); + }else { + log.error("jar文件不存在,无法备份。" + jarSavePath); + } + // 本次更新日志 ProjectPackageHistoryDTO dto = new ProjectPackageHistoryDTO(); - String historyId = UUID.randomUUID().toString(); dto.setId(historyId); dto.setProjectId(projectId); dto.setFileName(zipFileName); @@ -470,6 +491,57 @@ return result; } + @Override + public CommonResult<String> publish(Map<String, Object> params) { + String historyId = (String) params.get("historyId"); + String projectId = (String) params.get("projectId"); + + // 判断新的dll和jar文件是否存在 + String jarFileBakPath = mpkBakFilePath + File.separator + projectId + MdkConstant.SPLIT + historyId + ".jar"; + String dllFileBakPath = mpkBakFilePath + File.separator + projectId + MdkConstant.SPLIT + historyId + ".dll"; + if (!FileUtil.exist(jarFileBakPath)) { + throw new RuntimeException("jar文件不存在," + jarFileBakPath); + } + if (!FileUtil.exist(dllFileBakPath)) { + throw new RuntimeException("dll文件不存在" + dllFileBakPath); + } + + try { + // 卸载之前的dll和jar + DllUtils.removeClassLoaderCache(projectId); + // 删除之前的dll和jar备份文件 + DllUtils.removeOldFile(mpkBakFilePath + File.separator + MdkConstant.PROJECT_PUBLISH,projectId); + } catch (Exception e) { + throw new RuntimeException("卸载之前的dll和jar异常",e); + } + + // 备份新的dll和jar + String jarFilePath = mpkBakFilePath + File.separator + MdkConstant.PROJECT_PUBLISH + File.separator + projectId + MdkConstant.SPLIT + historyId + ".jar"; + String dllFilePath = mpkBakFilePath + File.separator + MdkConstant.PROJECT_PUBLISH + File.separator + projectId + MdkConstant.SPLIT + historyId + ".dll"; + FileUtil.copy(jarFileBakPath,jarFilePath,true); + FileUtil.copy(dllFileBakPath,dllFilePath,true); + + URLClassLoader urlClassLoader = null; + try { + // 加载新的jar + urlClassLoader = DllUtils.loadJar(jarFilePath); + } catch (Exception e) { + throw new RuntimeException("加载新的jar异常",e); + } + + try { + // 加载新的dll + DllUtils.loadDll(urlClassLoader.loadClass("iail.mdk.model.common.Environment"),dllFilePath); + } catch (Exception e) { + DllUtils.unloadJar(urlClassLoader); + throw new RuntimeException("加载新的dll异常",e); + } + // 都加载成功后加入缓存 + DllUtils.addClassLoaderCache(projectId,urlClassLoader); + + return CommonResult.success(); + } + private void createLog(String projectId, String projectName, String dirPath, ProjectPackageHistoryDTO dto, String version) throws IOException { Map<String, Object> map = new HashMap<>(); map.put("projectId", projectId); -- Gitblit v1.9.3