From 3b6408569b692bf2c12d8b3e355d3fba5c41a41a Mon Sep 17 00:00:00 2001
From: Kai Reinhard <K.Reinhard@micromata.de>
Date: Sun, 06 Jan 2019 22:08:17 +0000
Subject: [PATCH] BorgQueueStatistics

---
 borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/JobQueue.java          |   27 ++++++++++---
 borgbutler-webapp/src/containers/WebApp.jsx                                       |   26 ++++++++++++-
 borgbutler-core/src/main/java/de/micromata/borgbutler/BorgQueueStatistics.java    |   19 +++++++++
 borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/JobsRest.java |   11 +++++
 borgbutler-core/src/main/java/de/micromata/borgbutler/BorgQueueExecutor.java      |   22 ++++++++++
 5 files changed, 95 insertions(+), 10 deletions(-)

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 a2e1968..023ba42 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgQueueExecutor.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgQueueExecutor.java
@@ -15,6 +15,7 @@
 public class BorgQueueExecutor {
     private Logger log = LoggerFactory.getLogger(BorgQueueExecutor.class);
     private static final BorgQueueExecutor instance = new BorgQueueExecutor();
+    private static final String NONE_REPO_QUEUE = "--NO_REPO--";
 
     public static BorgQueueExecutor getInstance() {
         return instance;
@@ -23,6 +24,25 @@
     // key is the repo name.
     private Map<String, JobQueue<String>> queueMap = new HashMap<>();
 
+    public BorgQueueStatistics getStatistics() {
+        BorgQueueStatistics statistics = new BorgQueueStatistics();
+        Iterator<JobQueue<String>> it = queueMap.values().iterator();
+        while (it.hasNext()) {
+            JobQueue<?> queue = it.next();
+            statistics.totalNumberOfQueues++;
+            int queueSize = queue.getQueueSize();
+            if (queueSize > 0) {
+                statistics.numberOfActiveQueues++;
+                statistics.numberOfRunningAndQueuedJobs += queueSize;
+            }
+            int oldJobsSize = queue.getOldJobsSize();
+            if (oldJobsSize > 0) {
+                statistics.numberOfOldJobs += oldJobsSize;
+            }
+        }
+        return statistics;
+    }
+
     /**
      * @return A list of all repos with queues.
      */
@@ -98,7 +118,7 @@
     }
 
     private String getQueueName(BorgRepoConfig repoConfig) {
-        return repoConfig != null ? repoConfig.getId() : "--NO_REPO--";
+        return repoConfig != null ? repoConfig.getId() : NONE_REPO_QUEUE;
     }
 
     public BorgJob<Void> execute(BorgCommand command) {
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgQueueStatistics.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgQueueStatistics.java
new file mode 100644
index 0000000..dd41f30
--- /dev/null
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgQueueStatistics.java
@@ -0,0 +1,19 @@
+package de.micromata.borgbutler;
+
+import lombok.Getter;
+
+/**
+ * Statistics of all the job queues, especially the number of total queued and running jobs.
+ * This is used e. g. by the client for showing a badge near to the menu entry "job monitor" with the number
+ * of Jobs in the queues.
+ */
+public class BorgQueueStatistics {
+    @Getter
+    int numberOfRunningAndQueuedJobs = 0;
+    @Getter
+    int numberOfOldJobs = 0;
+    @Getter
+    int numberOfActiveQueues = 0;
+    @Getter
+    int totalNumberOfQueues = 0;
+}
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 8c9eb47..4b686ae 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
@@ -9,21 +9,34 @@
 import java.util.concurrent.Executors;
 
 public class JobQueue<T> {
-    private static final int MAX_DONE_JOBS_SIZE = 50;
+    private static final int MAX_OLD_JOBS_SIZE = 50;
     private static long jobSequence = 0;
     private Logger log = LoggerFactory.getLogger(JobQueue.class);
     private List<AbstractJob<T>> queue = new ArrayList<>();
-    private List<AbstractJob<T>> doneJobs = new LinkedList<>();
+    /**
+     * Finished, failed and cancelled jobs.
+     */
+    private List<AbstractJob<T>> oldJobs = new LinkedList<>();
     private ExecutorService executorService = Executors.newSingleThreadExecutor();
 
     private static synchronized void setNextJobId(AbstractJob<?> job) {
         job.setUniqueJobNumber(jobSequence++);
     }
 
+    /**
+     * @return the number of running and queued jobs of this queue or 0 if no job is in the queue.
+     */
     public int getQueueSize() {
         return queue.size();
     }
 
+    /**
+     * @return the number of old jobs (done, failed or cancelled) stored. The size of stored old jobs is limited.
+     */
+    public int getOldJobsSize() {
+        return oldJobs.size();
+    }
+
     public Iterator<AbstractJob<T>> getQueueIterator() {
         return Collections.unmodifiableList(queue).iterator();
     }
@@ -93,13 +106,13 @@
                 AbstractJob<T> job = it.next();
                 if (job.isFinished()) {
                     it.remove();
-                    synchronized (doneJobs) {
-                        doneJobs.add(0, job);
+                    synchronized (oldJobs) {
+                        oldJobs.add(0, job);
                     }
                 }
-                synchronized (doneJobs) {
-                    while (doneJobs.size() > MAX_DONE_JOBS_SIZE) {
-                        doneJobs.remove(doneJobs.size() - 1);
+                synchronized (oldJobs) {
+                    while (oldJobs.size() > MAX_OLD_JOBS_SIZE) {
+                        oldJobs.remove(oldJobs.size() - 1);
                     }
                 }
             }
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 a74ab74..14f1525 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
@@ -66,6 +66,17 @@
         return JsonUtils.toJson(queueList, prettyPrinter);
     }
 
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("/statistics")
+    /**
+     * @return The total number of jobs queued or running (and other statistics): {@link de.micromata.borgbutler.BorgQueueStatistics}.
+     * @see JsonUtils#toJson(Object, boolean)
+     */
+    public String getStatistics() {
+        return JsonUtils.toJson(BorgQueueExecutor.getInstance().getStatistics());
+    }
+
     private JsonJobQueue getQueue(String repo) {
         BorgQueueExecutor borgQueueExecutor = BorgQueueExecutor.getInstance();
         BorgRepoConfig repoConfig = ConfigurationHandler.getConfiguration().getRepoConfig(repo);
diff --git a/borgbutler-webapp/src/containers/WebApp.jsx b/borgbutler-webapp/src/containers/WebApp.jsx
index 0223721..639ee35 100644
--- a/borgbutler-webapp/src/containers/WebApp.jsx
+++ b/borgbutler-webapp/src/containers/WebApp.jsx
@@ -2,6 +2,7 @@
 import createBrowserHistory from 'history/createBrowserHistory';
 import {Route, Router, Switch} from 'react-router';
 import {connect} from 'react-redux';
+import {Badge} from 'reactstrap';
 
 import Menu from '../components/general/Menu';
 import Start from '../components/views/Start';
@@ -11,7 +12,7 @@
 import ConfigurationPage from '../components/views/config/ConfigurationPage';
 import RestServices from '../components/views/develop/RestServices';
 import JobMonitorView from '../components/views/jobs/JobMonitorView';
-import {isDevelopmentMode} from '../utilities/global';
+import {getRestServiceUrl, isDevelopmentMode} from '../utilities/global';
 import LogPage from '../components/views/logging/LogPage';
 import Footer from '../components/views/footer/Footer';
 import {loadVersion} from '../actions';
@@ -23,13 +24,34 @@
 
     componentDidMount = () => {
         this.props.loadVersion();
+        this.interval = setInterval(() => this.fetchJobStatistics(), 5000);
+    };
+
+    fetchJobStatistics = () => {
+        fetch(getRestServiceUrl('jobs/statistics'), {
+            method: 'GET',
+            headers: {
+                'Accept': 'application/json'
+            }
+        })
+            .then(response => response.json())
+            .then(json => {
+                this.setState({
+                    statistics: json
+                });
+            })
+            .catch();
     };
 
     render() {
+        let jobsBadge = '';
+        if (this.state && this.state.statistics && this.state.statistics.numberOfRunningAndQueuedJobs > 0) {
+            jobsBadge = <Badge color="danger" pill>{this.state.statistics.numberOfRunningAndQueuedJobs}</Badge>;
+        }
         let routes = [
             ['Start', '/', Start],
             ['Repositories', '/repos', RepoListView],
-            ['Job monitor', '/jobmonitor', JobMonitorView],
+            ['Job monitor', '/jobmonitor', JobMonitorView, {badge: jobsBadge}],
             [getTranslation('logviewer'), '/logging', LogPage],
             [getTranslation('configuration'), '/config', ConfigurationPage]
         ];

--
Gitblit v1.10.0