diff --git a/src/main/java/net/geedge/api/controller/APIController.java b/src/main/java/net/geedge/api/controller/APIController.java index cad62e5..b557b9d 100644 --- a/src/main/java/net/geedge/api/controller/APIController.java +++ b/src/main/java/net/geedge/api/controller/APIController.java @@ -1,6 +1,7 @@ package net.geedge.api.controller; import cn.hutool.core.codec.Base32Codec; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.thread.ThreadUtil; import cn.hutool.log.Log; @@ -29,6 +30,8 @@ public class APIController { private final AdbUtil adbUtil; + static final List ACTIVE_TASKS = Collections.synchronizedList(new ArrayList<>()); + @Autowired public APIController(EnvApiYml envApiYml) { this.adbUtil = AdbUtil.getInstance(envApiYml.getAdb(), new CommandExec(null)); @@ -305,6 +308,7 @@ public class APIController { T.FileUtil.del(destination); } PlaybookRunnable playbookRunnable = new PlaybookRunnable(apiYml, apkFile, playbookAirDir, id, packageName); + playbookRunnable.setName(T.StrUtil.concat(true, id, "-", apkFile.getName())); ThreadUtil.execAsync(playbookRunnable); return R.ok(); } @@ -319,6 +323,20 @@ public class APIController { return R.ok().putData(status); } + @DeleteMapping("/playbook/{id}") + public R cancel(@PathVariable("id") String id){ + if (T.StrUtil.isEmpty(id)) { + throw new APIException(RCode.BAD_REQUEST); + } + if (CollUtil.isNotEmpty(ACTIVE_TASKS)) { + ACTIVE_TASKS.stream().filter(thread -> T.StrUtil.startWith(thread.getName(), id)).forEach(thread -> { + log.info(String.format("playbook thread: %s has been canceled", thread.getName())); + thread.interrupt(); + }); + } + return R.ok(); + } + @GetMapping("/playbook/{id}/log") public R getJobResultLog(@PathVariable("id") String id, @@ -388,6 +406,7 @@ public class APIController { private File apkFile; private String packageName; private File playbookDir; + private boolean interrupt; public PlaybookRunnable(EnvApiYml envApiYml, File apkFile, File playbookDir, String tid, String packageName) { this.envApiYml = envApiYml; @@ -395,10 +414,12 @@ public class APIController { this.apkFile = apkFile; this.packageName = packageName; this.playbookDir = playbookDir; + this.interrupt = false; } @Override public void run() { + ACTIVE_TASKS.add(this); File logFile = FileUtil.file(Constant.TEMP_PATH, tid, "result.log"); File statusFile = FileUtil.file(Constant.TEMP_PATH, tid, "result.json"); AdbUtil.CommandResult tcpdumpPackage = null; @@ -414,6 +435,7 @@ public class APIController { T.FileUtil.writeString(T.JSONUtil.toJsonStr(resultMap), statusFile, "UTF-8"); // install apk + if (interrupt) return; AdbUtil.CommandResult install = adbUtil.install(apkFile.getAbsolutePath(), true, true); if (0 != install.exitCode()) { T.FileUtil.appendString(String.format("ERROR: Install apk failed: exit code %s \n", install.exitCode()), logFile, "UTF-8"); @@ -421,6 +443,7 @@ public class APIController { } // clear app data + if (interrupt) return; AdbUtil.CommandResult clearData = adbUtil.clearAppData(packageName); if (0 != clearData.exitCode()) { T.FileUtil.appendString(String.format("ERROR: Clear %s data error: exit code %s \n", packageName, install.exitCode()), logFile, "UTF-8"); @@ -428,6 +451,7 @@ public class APIController { } // star tcpdump: package name + if (interrupt) return; tcpdumpPackage = adbUtil.startTcpdump(packageName); if (0 != tcpdumpPackage.exitCode()) { T.FileUtil.appendString(String.format("ERROR: Start tcpdump %s failed: exit code %s \n", packageName, tcpdumpPackage.exitCode()), logFile, "UTF-8"); @@ -435,6 +459,7 @@ public class APIController { } // star tcpdump: all + if (interrupt) return; tcpdumpAll = adbUtil.startTcpdump(T.StrUtil.EMPTY); if (0 != tcpdumpAll.exitCode()) { T.FileUtil.appendString(String.format("ERROR: Start tcpdump all failed: exit code %s \n", tcpdumpAll.exitCode()), logFile, "UTF-8"); @@ -442,6 +467,7 @@ public class APIController { } // exec playbook + if (interrupt) return; AdbUtil.CommandResult airtestResult = adbUtil.execPlaybook(playbookDir.getPath(), logFile); if (0 != airtestResult.exitCode()) { T.FileUtil.appendString(String.format("ERROR: Exec playbook failed: exit code %s \n", airtestResult.exitCode()), logFile, "UTF-8"); @@ -449,9 +475,11 @@ public class APIController { } // stop package tcpdump + if (interrupt) return; stopTcpdump(tcpdumpPackage, logFile, packageName); // stop all tcpdump + if (interrupt) return; stopTcpdump(tcpdumpAll, logFile, T.StrUtil.EMPTY); resultMap = T.MapUtil.builder() @@ -464,17 +492,28 @@ public class APIController { .put("status", "error") .build(); T.FileUtil.writeString(T.JSONUtil.toJsonStr(resultMap), statusFile, "UTF-8"); - - AdbUtil.CommandResult packageTcpdump = adbUtil.stopTcpdump(tcpdumpPackage.output()); - adbUtil.execShellCommand(String.format("shell rm -rf %s", packageTcpdump.output())); - AdbUtil.CommandResult allTcpdump = adbUtil.stopTcpdump(tcpdumpAll.output()); - adbUtil.execShellCommand(String.format("shell rm -rf %s", allTcpdump.output())); } finally { + if (T.StrUtil.isNotEmpty(tcpdumpPackage.output())){ + AdbUtil.CommandResult packageTcpdump = adbUtil.stopTcpdump(tcpdumpPackage.output()); + adbUtil.execShellCommand(String.format("shell rm -rf %s", packageTcpdump.output())); + } + if (T.StrUtil.isNotEmpty(tcpdumpAll.output())){ + AdbUtil.CommandResult allTcpdump = adbUtil.stopTcpdump(tcpdumpAll.output()); + adbUtil.execShellCommand(String.format("shell rm -rf %s", allTcpdump.output())); + } adbUtil.stopApp(packageName); T.FileUtil.appendString(String.format("Job succeeded"), logFile, "UTF-8"); + ACTIVE_TASKS.remove(this); } } + @Override + public void interrupt() { + super.interrupt(); + this.interrupt = true; + adbUtil.setInterrupt(true); + } + private void stopTcpdump(AdbUtil.CommandResult tcpdump, File logFile, String packageName) { // stop tcpdump AdbUtil.CommandResult stopTcpdump = adbUtil.stopTcpdump(tcpdump.output()); diff --git a/src/main/java/net/geedge/api/util/AdbUtil.java b/src/main/java/net/geedge/api/util/AdbUtil.java index 0030261..09e8692 100644 --- a/src/main/java/net/geedge/api/util/AdbUtil.java +++ b/src/main/java/net/geedge/api/util/AdbUtil.java @@ -9,9 +9,7 @@ import net.geedge.common.Constant; import net.geedge.common.RCode; import net.geedge.common.T; -import java.io.BufferedReader; -import java.io.File; -import java.io.InputStreamReader; +import java.io.*; import java.nio.file.Paths; import java.util.*; import java.util.concurrent.*; @@ -37,6 +35,8 @@ public class AdbUtil { private CommandExec commandExec; + private boolean interrupt; + private ExecutorService threadPool; @@ -44,6 +44,10 @@ public class AdbUtil { return T.StrUtil.isNotEmpty(this.serial) ? serial : String.format("%s:%s", this.host, this.port); } + public void setInterrupt(boolean interrupt) { + this.interrupt = interrupt; + } + public record CommandResult(Integer exitCode, String output) { } @@ -870,6 +874,10 @@ public class AdbUtil { bufferedReader = new BufferedReader(inputStreamReader); String line; while ((line = bufferedReader.readLine()) != null) { + if (T.ObjectUtil.isNotNull(interrupt) && interrupt){ + log.info("[PlaybookRunnable] [execPlaybook] [stop exec playbook]"); + process.destroyForcibly(); + } // 处理每一行输出 T.FileUtil.appendString(T.StrUtil.concat(true, line, "\n"), logFile, "UTF-8"); }