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失败"); } } 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"; } 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); } } } } } } 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(); } 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); } } 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); } 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);