From 67dd1243073e766178dd70dd2f45aa5bc77ec529 Mon Sep 17 00:00:00 2001
From: Kai Reinhard <K.Reinhard@micromata.de>
Date: Sat, 05 Jan 2019 22:03:29 +0000
Subject: [PATCH] Job monitor: Cancellation of jobs...

---
 borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/AbstractCommandLineJob.java |    2 
 borgbutler-webapp/src/components/views/jobs/Job.jsx                                    |   32 +++++++++-
 borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/AbstractJob.java            |    4 
 borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/JobQueue.java               |   16 +++++
 borgbutler-core/src/main/java/de/micromata/borgbutler/BorgCommands.java                |    8 +-
 borgbutler-core/src/main/java/de/micromata/borgbutler/BorgJob.java                     |    1 
 borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/queue/JsonJob.java |    4 +
 borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/JobsRest.java      |   34 +++++++----
 borgbutler-core/src/main/java/de/micromata/borgbutler/BorgQueueExecutor.java           |   28 ++++++++
 9 files changed, 104 insertions(+), 25 deletions(-)

diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgCommands.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgCommands.java
index 7f0a706..b525573 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgCommands.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgCommands.java
@@ -35,7 +35,7 @@
                 .setParams("--version")
                 .setDescription("Getting borg version.");
         JobResult<String> jobResult = execute(command).getResult();
-        if (jobResult.getStatus() != JobResult.Status.OK) {
+        if (jobResult == null || jobResult.getStatus() != JobResult.Status.OK) {
             return null;
         }
         String version = jobResult.getResultObject();
@@ -56,7 +56,7 @@
                 .setParams("--json") // --progress has no effect.
                 .setDescription("Loading info of repo '" + repoConfig.getDisplayName() + "'.");
         JobResult<String> jobResult = execute(command).getResult();
-        if (jobResult.getStatus() != JobResult.Status.OK) {
+        if (jobResult == null || jobResult.getStatus() != JobResult.Status.OK) {
             return null;
         }
         String result = jobResult.getResultObject();
@@ -89,7 +89,7 @@
                 .setParams("--json") // --progress has no effect.
                 .setDescription("Loading list of archives of repo '" + repoConfig.getDisplayName() + "'.");
         JobResult<String> jobResult = execute(command).getResult();
-        if (jobResult.getStatus() != JobResult.Status.OK) {
+        if (jobResult == null || jobResult.getStatus() != JobResult.Status.OK) {
             log.error("Can't load archives from repo '" + repository.getName() + "'.");
             return;
         }
@@ -131,7 +131,7 @@
                 .setParams("--json", "--log-json", "--progress")
                 .setDescription("Loading info of archive '" + archive.getName() + "' of repo '" + repoConfig.getDisplayName() + "'.");
         JobResult<String> jobResult = execute(command).getResult();
-        if (jobResult.getStatus() != JobResult.Status.OK) {
+        if (jobResult == null || jobResult.getStatus() != JobResult.Status.OK) {
             return;
         }
         String result = jobResult.getResultObject();
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgJob.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgJob.java
index 79e78ba..d5f057e 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgJob.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgJob.java
@@ -108,6 +108,7 @@
     @Override
     public BorgJob<?> clone() {
         BorgJob<?> clone = new BorgJob<>();
+        clone.setUniqueJobNumber(getUniqueJobNumber());
         clone.setTitle(getTitle());
         clone.setExecuteStarted(isExecuteStarted());
         clone.setCommandLineAsString(getCommandLineAsString());
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgQueueExecutor.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgQueueExecutor.java
index e4dc1a8..5e440fe 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgQueueExecutor.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgQueueExecutor.java
@@ -28,11 +28,33 @@
      */
     public List<String> getRepos() {
         List<String> list = new ArrayList<>();
-        list.addAll(queueMap.keySet());
+        synchronized (queueMap) {
+            list.addAll(queueMap.keySet());
+        }
         Collections.sort(list);
         return list;
     }
 
+    public void cancelJob(long uniqueJobNumber) {
+        AbstractJob<?> job = getQueuedJobByUniqueJobNumber(uniqueJobNumber);
+        if (job == null) {
+            log.info("Can't cancel job #" + uniqueJobNumber + ". Not found as queued job (may-be already cancelled or finished). Nothing to do.");
+            return;
+        }
+        job.cancel();
+    }
+
+    private AbstractJob<?> getQueuedJobByUniqueJobNumber(long uniqueJobNumber) {
+        Iterator<JobQueue<String>> it = queueMap.values().iterator();
+        while (it.hasNext()) {
+            AbstractJob<?> job = it.next().getQueuedJobByUniqueJobNumber(uniqueJobNumber);
+            if (job != null) {
+                return job;
+            }
+        }
+        return null;
+    }
+
     /**
      * For displaying purposes.
      *
@@ -56,7 +78,9 @@
     }
 
     private JobQueue<String> getQueue(String repo) {
-        return queueMap.get(getQueueName(repo));
+        synchronized (queueMap) {
+            return queueMap.get(getQueueName(repo));
+        }
     }
 
     private JobQueue<String> ensureAndGetQueue(String repo) {
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/AbstractCommandLineJob.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/AbstractCommandLineJob.java
index aee1b4f..591980d 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/AbstractCommandLineJob.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/AbstractCommandLineJob.java
@@ -124,7 +124,7 @@
     @Override
     protected void cancelRunningProcess() {
         if (watchdog != null) {
-            log.info("Cancelling job: " + getId());
+            log.info("Cancelling job #" + getUniqueJobNumber() + ": " + getId());
             watchdog.destroyProcess();
             watchdog = null;
             setCancelled();
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/AbstractJob.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/AbstractJob.java
index 8b6e8e8..df97791 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/AbstractJob.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/AbstractJob.java
@@ -27,8 +27,8 @@
     @Setter(AccessLevel.PACKAGE)
     private Future<JobResult<T>> future;
     @Getter
-    @Setter(AccessLevel.PACKAGE)
-    private long uniqueJobNumber;
+    @Setter(AccessLevel.PROTECTED)
+    private long uniqueJobNumber = -1;
 
     public void cancel() {
         if (this.getStatus() == Status.QUEUED) {
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/JobQueue.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/JobQueue.java
index 5e5a95c..819cb0e 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/JobQueue.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/JobQueue.java
@@ -29,6 +29,22 @@
     }
 
     /**
+     * Searches only for queued jobs (not done jobs).
+     * @param uniqueJobNumber
+     * @return The job if any job with the given unique job number is queued, otherwise null.
+     */
+    public AbstractJob<T> getQueuedJobByUniqueJobNumber(long uniqueJobNumber) {
+        Iterator<AbstractJob<T>> it = queue.iterator();
+        while (it.hasNext()) {
+            AbstractJob<T> job = it.next();
+            if (job.getUniqueJobNumber() == uniqueJobNumber) {
+                return job;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Appends the job if not alread in the queue. Starts the execution if no execution thread is already running.
      *
      * @param job
diff --git a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/JobsRest.java b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/JobsRest.java
index 5991560..382b331 100644
--- a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/JobsRest.java
+++ b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/JobsRest.java
@@ -59,34 +59,43 @@
     }
 
     @Path("/cancel")
-    @PUT
-    @Produces(MediaType.APPLICATION_JSON)
+    @GET
     /**
-     * @param testMode If true, then a test job list is created.
-     * @param prettyPrinter If true then the json output will be in pretty format.
-     * @return Job queues as json string.
-     * @see JsonUtils#toJson(Object, boolean)
+     * @param uniqueJobNumberString The id of the job to cancel.
      */
-    public void cancelJob(@QueryParam("job") String job, @QueryParam("prettyPrinter") boolean prettyPrinter) {
-        log.info("Cancelling job");
+    public void cancelJob(@QueryParam("uniqueJobNumber") String uniqueJobNumberString) {
+        Long uniqueJobNumber = null;
+        try {
+            uniqueJobNumber = Long.parseLong(uniqueJobNumberString);
+        } catch (NumberFormatException ex) {
+            log.error("Can't cancel job, because unique job number couln't be parsed (long value expected): " + uniqueJobNumberString);
+            return;
+        }
+        BorgQueueExecutor.getInstance().cancelJob(uniqueJobNumber);
     }
 
     /**
      * Only for test purposes and development.
+     *
      * @param prettyPrinter
      * @return
      */
     private String returnTestList(boolean prettyPrinter) {
         if (testList == null) {
             testList = new ArrayList<>();
+            long uniqueJobNumber = 100000;
             JsonJobQueue queue = new JsonJobQueue().setRepo("My Computer");
-            addTestJob(queue, "info", "my-macbook", 0, 2342);
-            addTestJob(queue, "list", "my-macbook", -1, -1);
+            addTestJob(queue, "info", "my-macbook", 0, 2342)
+                    .setUniqueJobNumber(uniqueJobNumber++);
+            addTestJob(queue, "list", "my-macbook", -1, -1)
+                    .setUniqueJobNumber(uniqueJobNumber++);
             testList.add(queue);
 
             queue = new JsonJobQueue().setRepo("My Server");
-            addTestJob(queue, "list", "my-server", 0, 1135821);
-            addTestJob(queue, "info", "my-server", -1, -1);
+            addTestJob(queue, "list", "my-server", 0, 1135821)
+                    .setUniqueJobNumber(uniqueJobNumber++);
+            addTestJob(queue, "info", "my-server", -1, -1)
+                    .setUniqueJobNumber(uniqueJobNumber++);
             testList.add(queue);
         } else {
             for (JsonJobQueue jobQueue : testList) {
@@ -117,6 +126,7 @@
 
     /**
      * Only for test purposes and development.
+     *
      * @param queue
      * @param operation
      * @param host
diff --git a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/queue/JsonJob.java b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/queue/JsonJob.java
index 8430945..be2457f 100644
--- a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/queue/JsonJob.java
+++ b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/queue/JsonJob.java
@@ -31,11 +31,15 @@
     @Getter
     @Setter
     private String commandLineAsString;
+    @Getter
+    @Setter
+    private long uniqueJobNumber;
 
     public JsonJob() {
     }
 
     public JsonJob(BorgJob<?> borgJob) {
+        this.uniqueJobNumber = borgJob.getUniqueJobNumber();
         this.cancellationRequested = borgJob.isCancellationRequested();
         this.status = borgJob.getStatus();
         this.title = borgJob.getTitle();
diff --git a/borgbutler-webapp/src/components/views/jobs/Job.jsx b/borgbutler-webapp/src/components/views/jobs/Job.jsx
index 4b3ba06..5d45234 100644
--- a/borgbutler-webapp/src/components/views/jobs/Job.jsx
+++ b/borgbutler-webapp/src/components/views/jobs/Job.jsx
@@ -1,14 +1,22 @@
 import React from 'react';
 import {Button, Card, CardBody, Collapse, Progress} from 'reactstrap';
-import {IconCancelJob} from '../../general/IconComponents'
+import {IconCancel} from '../../general/IconComponents'
+import {getRestServiceUrl} from "../../../utilities/global";
 
 class Job extends React.Component {
     constructor(props) {
         super(props);
         this.toggle = this.toggle.bind(this);
+        this.cacnelJob = this.cancelJob.bind(this);
         this.state = {collapse: false};
     }
 
+    cancelJob = (jobId) => {
+        fetch(getRestServiceUrl('jobs/cancel', {
+            uniqueJobNumber: jobId
+        }));
+    };
+
     toggle() {
         this.setState({collapse: !this.state.collapse});
     }
@@ -25,15 +33,31 @@
         } else {
             content = <Progress color={'info'} value={100}>{job.status}</Progress>
         }
+        let cancelDisabled = undefined;
+        if (job.status !== 'RUNNING' && job.status !== 'QUEUED') {
+            cancelDisabled = true;
+        }
         return (
             <div>
                 <Button color="link" onClick={this.toggle}>{job.description}</Button>
-                <div>{content}<IconCancelJob/></div>
+                <div>{content}
+                    <Button color={'danger'} onClick={() => this.cancelJob(job.uniqueJobNumber)} disabled={cancelDisabled}><IconCancel/></Button>
+                </div>
                 <Collapse isOpen={this.state.collapse}>
                     <Card>
                         <CardBody>
-                            <table><tbody><tr><th>Status</th><td>{job.status}</td></tr>
-                            <tr><th>Command line</th><td>{job.commandLineAsString}</td></tr></tbody></table>
+                            <table>
+                                <tbody>
+                                <tr>
+                                    <th>Status</th>
+                                    <td>{job.status}</td>
+                                </tr>
+                                <tr>
+                                    <th>Command line</th>
+                                    <td>{job.commandLineAsString}</td>
+                                </tr>
+                                </tbody>
+                            </table>
                         </CardBody>
                     </Card>
                 </Collapse>

--
Gitblit v1.10.0