diff --git a/doc/XXL-JOB官方文档.md b/doc/XXL-JOB官方文档.md index e075e125..c7bb9828 100644 --- a/doc/XXL-JOB官方文档.md +++ b/doc/XXL-JOB官方文档.md @@ -2585,10 +2585,11 @@ public void execute() { - 11、【优化】调度组件日志完善,提升边界情况下问题定位效率; - 12、【升级】升级多项maven依赖至较新版本,如 netty、groovy、spring、spring-ai、dify 等; - 14、【优化】任务回调失败日志读写磁盘逻辑优化,解决极端情况下大文件读写内存问题; -- 15、【ING】UI框架重构升级,提升交互体验; -- 16、【ING】调整资源加载逻辑,移除不必要的拦截器逻辑,提升页面加载效率; -- 17、【ING】规范API交互协议,通用响应结构体调整为Response; -- 18、【ING】Http通讯组件升级,基于接口代理方式重构; +- 15、【修复】脚本任务process销毁逻辑优化,解决风险情况下脚本进程无法终止问题; +- 16、【ING】UI框架重构升级,提升交互体验; +- 17、【ING】调整资源加载逻辑,移除不必要的拦截器逻辑,提升页面加载效率; +- 18、【ING】规范API交互协议,通用响应结构体调整为Response; +- 19、【ING】Http通讯组件升级,基于接口代理方式重构; ### TODO LIST diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobCodeController.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobCodeController.java index c288290a..ae1fb105 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobCodeController.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobCodeController.java @@ -8,6 +8,7 @@ import com.xxl.job.admin.util.I18nUtil; import com.xxl.job.admin.util.JobGroupPermissionUtil; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.glue.GlueTypeEnum; +import com.xxl.tool.core.StringTool; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import org.springframework.stereotype.Controller; @@ -63,6 +64,9 @@ public class JobCodeController { @RequestParam("glueRemark") String glueRemark) { // valid + if (StringTool.isBlank(glueSource)) { + return ReturnT.ofFail( (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_glue_source")) ); + } if (glueRemark==null) { return ReturnT.ofFail( (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_glue_remark")) ); } diff --git a/xxl-job-admin/src/main/resources/i18n/message_en.properties b/xxl-job-admin/src/main/resources/i18n/message_en.properties index 43805bc7..13221dae 100644 --- a/xxl-job-admin/src/main/resources/i18n/message_en.properties +++ b/xxl-job-admin/src/main/resources/i18n/message_en.properties @@ -137,6 +137,7 @@ jobinfo_opt_run=Run Once jobinfo_opt_run_tips=Please input the address for this trigger. Null will be obtained from the executor jobinfo_opt_registryinfo=Registry Info jobinfo_opt_next_time=Next trigger time +jobinfo_glue_source=GLUE Source jobinfo_glue_remark=Resource Remark jobinfo_glue_remark_limit=Resource Remark length is limited to 4~100 jobinfo_glue_rollback=Version Backtrack diff --git a/xxl-job-admin/src/main/resources/i18n/message_zh_CN.properties b/xxl-job-admin/src/main/resources/i18n/message_zh_CN.properties index 9b53e877..77bc02dc 100644 --- a/xxl-job-admin/src/main/resources/i18n/message_zh_CN.properties +++ b/xxl-job-admin/src/main/resources/i18n/message_zh_CN.properties @@ -137,6 +137,7 @@ jobinfo_opt_run=执行一次 jobinfo_opt_run_tips=请输入本次执行的机器地址,为空则从执行器获取 jobinfo_opt_registryinfo=注册节点 jobinfo_opt_next_time=下次执行时间 +jobinfo_glue_source=GLUE源码 jobinfo_glue_remark=源码备注 jobinfo_glue_remark_limit=源码备注长度限制为4~100 jobinfo_glue_rollback=版本回溯 diff --git a/xxl-job-admin/src/main/resources/i18n/message_zh_TC.properties b/xxl-job-admin/src/main/resources/i18n/message_zh_TC.properties index 7f26f790..183ab960 100755 --- a/xxl-job-admin/src/main/resources/i18n/message_zh_TC.properties +++ b/xxl-job-admin/src/main/resources/i18n/message_zh_TC.properties @@ -137,6 +137,7 @@ jobinfo_opt_run=執行一次 jobinfo_opt_run_tips=請輸入本次執行的機器地址,為空則從執行器獲取 jobinfo_opt_registryinfo=注冊節點 jobinfo_opt_next_time=下次執行時間 +jobinfo_glue_source=GLUE源碼 jobinfo_glue_remark=源碼備註 jobinfo_glue_remark_limit=源碼備註長度限制為4~100 jobinfo_glue_rollback=版本回復 diff --git a/xxl-job-core/src/main/java/com/xxl/job/core/handler/impl/ScriptJobHandler.java b/xxl-job-core/src/main/java/com/xxl/job/core/handler/impl/ScriptJobHandler.java index 6ad3b7c3..725dea94 100644 --- a/xxl-job-core/src/main/java/com/xxl/job/core/handler/impl/ScriptJobHandler.java +++ b/xxl-job-core/src/main/java/com/xxl/job/core/handler/impl/ScriptJobHandler.java @@ -29,9 +29,9 @@ public class ScriptJobHandler extends IJobHandler { File glueSrcPath = new File(XxlJobFileAppender.getGlueSrcPath()); if (glueSrcPath.exists()) { File[] glueSrcFileList = glueSrcPath.listFiles(); - if (glueSrcFileList!=null && glueSrcFileList.length>0) { + if (glueSrcFileList != null) { for (File glueSrcFileItem : glueSrcFileList) { - if (glueSrcFileItem.getName().startsWith(String.valueOf(jobId)+"_")) { + if (glueSrcFileItem.getName().startsWith(jobId +"_")) { glueSrcFileItem.delete(); } } @@ -47,6 +47,7 @@ public class ScriptJobHandler extends IJobHandler { @Override public void execute() throws Exception { + // valid if (!glueType.isScript()) { XxlJobHelper.handleFail("glueType["+ glueType +"] invalid."); return; diff --git a/xxl-job-core/src/main/java/com/xxl/job/core/util/ScriptUtil.java b/xxl-job-core/src/main/java/com/xxl/job/core/util/ScriptUtil.java index a27e27bd..310f7df7 100644 --- a/xxl-job-core/src/main/java/com/xxl/job/core/util/ScriptUtil.java +++ b/xxl-job-core/src/main/java/com/xxl/job/core/util/ScriptUtil.java @@ -1,11 +1,11 @@ package com.xxl.job.core.util; import com.xxl.job.core.context.XxlJobHelper; +import com.xxl.tool.core.ArrayTool; +import com.xxl.tool.core.AssertTool; +import com.xxl.tool.io.FileTool; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; import java.util.ArrayList; import java.util.List; @@ -22,16 +22,18 @@ public class ScriptUtil { /** * make script file * - * @param scriptFileName - * @param content - * @throws IOException + * @param scriptFileName script file name + * @param scriptContent script content + * @throws IOException exception */ - public static void markScriptFile(String scriptFileName, String content) throws IOException { - // make file, filePath/gluesource/666-123456789.py - FileOutputStream fileOutputStream = null; + public static void markScriptFile(String scriptFileName, String scriptContent) throws IOException { + // make file: filePath/gluesource/666-123456789.py + FileTool.writeString(scriptFileName, scriptContent); + + /*FileOutputStream fileOutputStream = null; try { fileOutputStream = new FileOutputStream(scriptFileName); - fileOutputStream.write(content.getBytes("UTF-8")); + fileOutputStream.write(scriptContent.getBytes("UTF-8")); fileOutputStream.close(); } catch (Exception e) { throw e; @@ -39,131 +41,133 @@ public class ScriptUtil { if(fileOutputStream != null){ fileOutputStream.close(); } - } + }*/ } /** * 脚本执行,日志文件实时输出 * - * @param command - * @param scriptFile - * @param logFile - * @param params - * @return - * @throws IOException + * @param command command + * @param scriptFile script file + * @param logFile log file + * @param params params + * @return exit code + * @throws IOException exception */ public static int execToFile(String command, String scriptFile, String logFile, String... params) throws IOException { FileOutputStream fileOutputStream = null; Thread inputThread = null; - Thread errThread = null; + Thread errorThread = null; + Process process = null; try { - // file + // 1、build file OutputStream fileOutputStream = new FileOutputStream(logFile, true); - // command + // 2、build command List cmdarray = new ArrayList<>(); cmdarray.add(command); cmdarray.add(scriptFile); - if (params!=null && params.length>0) { + if (ArrayTool.isNotEmpty(params)) { for (String param:params) { cmdarray.add(param); } } - String[] cmdarrayFinal = cmdarray.toArray(new String[cmdarray.size()]); + String[] cmdarrayFinal = cmdarray.toArray(new String[0]); - // process-exec - final Process process = Runtime.getRuntime().exec(cmdarrayFinal); + // 3、process:exec + process = Runtime.getRuntime().exec(cmdarrayFinal); + Process finalProcess = process; - // log-thread + // 4、read script log: inputStream + errStream final FileOutputStream finalFileOutputStream = fileOutputStream; - inputThread = new Thread(new Runnable() { - @Override - public void run() { - try { - copy(process.getInputStream(), finalFileOutputStream, new byte[1024]); - } catch (IOException e) { - XxlJobHelper.log(e); - } + inputThread = new Thread(() -> { + try { + copy(finalProcess.getInputStream(), finalFileOutputStream, true, false); + } catch (IOException e) { + XxlJobHelper.log(e); } }); - errThread = new Thread(new Runnable() { - @Override - public void run() { - try { - copy(process.getErrorStream(), finalFileOutputStream, new byte[1024]); - } catch (IOException e) { - XxlJobHelper.log(e); - } + errorThread = new Thread(() -> { + try { + // 数据流Copy(Input自动关闭,Output不处理) + copy(finalProcess.getErrorStream(), finalFileOutputStream, true, false); + } catch (IOException e) { + XxlJobHelper.log(e); } }); inputThread.start(); - errThread.start(); + errorThread.start(); - // process-wait + // 5、process:wait for result int exitValue = process.waitFor(); // exit code: 0=success, 1=error - // log-thread join + // 6、thread join, wait for log inputThread.join(); - errThread.join(); + errorThread.join(); return exitValue; } catch (Exception e) { XxlJobHelper.log(e); return -1; } finally { + // 7、close file OutputStream if (fileOutputStream != null) { try { fileOutputStream.close(); } catch (IOException e) { XxlJobHelper.log(e); } - } + // 8、interrupt thread if (inputThread != null && inputThread.isAlive()) { inputThread.interrupt(); } - if (errThread != null && errThread.isAlive()) { - errThread.interrupt(); + if (errorThread != null && errorThread.isAlive()) { + errorThread.interrupt(); + } + // 9、process destroy + if (process != null) { + process.destroy(); } } } - /** - * 数据流Copy(Input自动关闭,Output不处理) - * - * @param inputStream - * @param outputStream - * @param buffer - * @return - * @throws IOException - */ - private static long copy(InputStream inputStream, OutputStream outputStream, byte[] buffer) throws IOException { + + private static final int BUFFER_SIZE = 1024 * 8; + private static int copy(InputStream input, OutputStream output, boolean closeInput, boolean closeOutput) throws IOException { + AssertTool.notNull(input, "No InputStream specified"); + AssertTool.notNull(output, "No OutputStream specified"); + try { - long total = 0; - for (;;) { - int res = inputStream.read(buffer); - if (res == -1) { - break; - } - if (res > 0) { - total += res; - if (outputStream != null) { - outputStream.write(buffer, 0, res); - } - } + int byteCount = 0; + byte[] buffer = new byte[BUFFER_SIZE]; + int bytesRead; + while ((bytesRead = input.read(buffer)) != -1) { + output.write(buffer, 0, bytesRead); + byteCount += bytesRead; } - outputStream.flush(); - //out = null; - inputStream.close(); - inputStream = null; - return total; + output.flush(); + return byteCount; } finally { - if (inputStream != null) { - inputStream.close(); + if (closeInput) { + close(input); + } + if (closeOutput) { + close(output); } } } + private static void close(Closeable closeable) { + if (closeable == null) { + return; + } + try { + closeable.close(); + } catch (IOException ex) { + // ignore + } + } /** * 脚本执行,日志文件实时输出