Java 使用Runtime在一個Java程序中啓動和關閉另一個Java程序

主要使用了

Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", cmd});

調用shell命令的工具類:

    public static String runShell(String cmd) throws Exception {
        StringBuilder result = new StringBuilder();

        Process process = null;
        BufferedReader bufrIn = null;
        BufferedReader bufrError = null;

        try {
            // 執行命令, 返回一個子進程對象(命令在子進程中執行)使用這種方式可以使用|管道符命令
            process = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", cmd});

            // 方法阻塞, 等待命令執行完成(成功會返回0)
            process.waitFor();

            // 獲取命令執行結果, 有兩個結果: 正常的輸出 和 錯誤的輸出(PS: 子進程的輸出就是主進程的輸入)
            bufrIn = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8));
            bufrError = new BufferedReader(new InputStreamReader(process.getErrorStream(), StandardCharsets.UTF_8));

            // 讀取輸出
            String line;
            while ((line = bufrIn.readLine()) != null) {
                result.append(line).append('\n');
            }
            while ((line = bufrError.readLine()) != null) {
                result.append(line).append('\n');
            }

        } finally {
            closeStream(bufrIn);
            closeStream(bufrError);

            // 銷燬子進程
            if (process != null) {
                process.destroy();
            }
        }

        return result.toString();
    }

當有jar包上傳到接口時,調用這個方法,停止正在運行的jar,並啓動新jar

JAR_NAME校驗自定,這裏固定使用一個jar包名,方便jps時找到該進程

  /**
  *jar包上傳及執行
  */
 private ResponseVo myjarPkg(MultipartFile file) throws Exception {
        //myjar目錄
        String myjarDir = "/data/myjar/";
        File fileDir = new File(myjarDir);
        fileDir.mkdirs();

        //文件名校驗 名稱不對則不執行
        if (!JAR_NAME.equals(file.getOriginalFilename())) {
            return new ResponseVo(-1, "不正確的文件");
        }
        File myjarPkg = new File(myjarDir + file.getOriginalFilename());
        //將舊的已存在的刪除
        if (myjarPkg.exists()) {
            FileUtils.deleteQuietly(myjarPkg);
        }
        file.transferTo(myjarPkg);
        //運行myjar程序 先停掉之前的 再啓動新上傳的
        //由jre目錄進入到父級jdk目錄 不直接替換可以防止沒有jre目錄的情況
        String javaHome = System.getProperty("java.home").replace("/jre", "") + "/bin";
        final String java = javaHome + "/java";
        final String jps = javaHome + "/jps";
        String cmd = jps + " -l|grep " + JAR_NAME + "|awk '{print$1}'";
        String pid = ShellUtil.runShell(cmd);
        if (StringUtils.isNotBlank(pid)) {
            cmd = "kill -9 " + pid;
            String killmsg = ShellUtil.runShell(cmd);
            logger.info("kill process pid:{} ,result:{}", pid, killmsg);
            //殺掉之後等3s
            Thread.sleep(3000);
        }
        cmd = "nohup " + java + " -jar " + myjarDir + JAR_NAME + " > " + myjarDir + "myjar.log 2>&1 &";
        String msg = ShellUtil.runShell(cmd);
        logger.info("升級程序執行結果: " + msg);
        return new ResponseVo();
    }

重點是Java啓動的process,不能直接執行java、jps等命令,也獲取不到環境變量,會報command not found

於是我使用來System.getProperty("java.home") 來獲取到執行當前程序的Java路徑,再把jre目錄替換爲jdk目錄,使用jdk目錄下bin目錄中的java及jps命令,可以達到需求

另外需要注意命令字符串中的空格很重要,不能忽略

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章