diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboard.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboard.java index 62d4e4bca0..270b3c3dc8 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboard.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboard.java @@ -21,22 +21,26 @@ package org.sleuthkit.autopsy.experimental.autoingest; import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.awt.Cursor; import java.awt.EventQueue; +import java.util.Observable; +import java.util.Observer; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import org.sleuthkit.autopsy.healthmonitor.HealthMonitorDashboard; /** * A dashboard for monitoring the existing AutoIngestNodes and their status. */ @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives -final class AinStatusDashboard extends javax.swing.JPanel { +final class AinStatusDashboard extends javax.swing.JPanel implements Observer { private final AutoIngestMonitor autoIngestMonitor; private final AinStatusPanel nodesPanel; private final static String AIN_REFRESH_THREAD_NAME = "AID-refresh-jobs-%d"; private final static int AIN_REFRESH_INTERVAL_SECS = 30; - private final static int AIN_DELAY_BEFORE_FIRST_REFRESH = 1; + private final static int AIN_DELAY_BEFORE_FIRST_REFRESH = 0; private final ScheduledThreadPoolExecutor scheduledRefreshThreadPoolExecutor; + private AtomicBoolean scheduledRefreshStarted = new AtomicBoolean(false); /** * Creates new form AutoIngestNodeStatus @@ -56,11 +60,7 @@ final class AinStatusDashboard extends javax.swing.JPanel { * Adds this panel as an observer of AutoIngestMonitor. */ void startUp() { - scheduledRefreshThreadPoolExecutor.scheduleWithFixedDelay(() -> { - EventQueue.invokeLater(() -> { - refreshTables(); - }); - }, AIN_DELAY_BEFORE_FIRST_REFRESH, AIN_REFRESH_INTERVAL_SECS, TimeUnit.SECONDS); + autoIngestMonitor.addObserver(this); } AutoIngestMonitor getMonitor() { @@ -179,4 +179,14 @@ final class AinStatusDashboard extends javax.swing.JPanel { private javax.swing.JButton refreshButton; // End of variables declaration//GEN-END:variables + @Override + public void update(Observable o, Object arg) { + if (!scheduledRefreshStarted.getAndSet(true)) { + scheduledRefreshThreadPoolExecutor.scheduleWithFixedDelay(() -> { + EventQueue.invokeLater(() -> { + refreshTables(); + }); + }, AIN_DELAY_BEFORE_FIRST_REFRESH, AIN_REFRESH_INTERVAL_SECS, TimeUnit.SECONDS); + } + } } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.java index bb71d008f6..9743592ba3 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.java @@ -28,10 +28,13 @@ import java.beans.PropertyChangeListener; import java.io.File; import java.nio.file.Paths; import java.util.HashSet; +import java.util.Observable; +import java.util.Observer; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import javax.swing.JPanel; import javax.swing.SwingWorker; import javax.swing.UIManager; @@ -46,7 +49,7 @@ import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestNodeRefreshEvents * A dashboard for monitoring an automated ingest cluster. */ @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives -final class AutoIngestDashboard extends JPanel { +final class AutoIngestDashboard extends JPanel implements Observer { private final static String ADMIN_ACCESS_FILE_NAME = "_aiaa"; // NON-NLS private final static String ADMIN_ACCESS_FILE_PATH = Paths.get(PlatformUtil.getUserConfigDirectory(), ADMIN_ACCESS_FILE_NAME).toString(); @@ -60,6 +63,8 @@ final class AutoIngestDashboard extends JPanel { private AutoIngestJobsPanel runningJobsPanel; private AutoIngestJobsPanel completedJobsPanel; private final ScheduledThreadPoolExecutor scheduledRefreshThreadPoolExecutor; + private AtomicBoolean scheduledRefreshStarted = new AtomicBoolean(false); + /** * Maintain a mapping of each service to it's last status update. */ @@ -240,14 +245,11 @@ final class AutoIngestDashboard extends JPanel { ServicesMonitor.getInstance().addSubscriber(servicesList, propChangeListener); autoIngestMonitor = new AutoIngestMonitor(); + autoIngestMonitor.addObserver(this); new Thread(() -> { try { autoIngestMonitor.startUp(); - scheduledRefreshThreadPoolExecutor.scheduleWithFixedDelay(() -> { - EventQueue.invokeLater(() -> { - refreshTables(); - }); - }, AID_DELAY_BEFORE_FIRST_REFRESH, AID_REFRESH_INTERVAL_SECS, TimeUnit.SECONDS); + } catch (AutoIngestMonitor.AutoIngestMonitorException ex) { LOGGER.log(Level.SEVERE, "Unable to start up Auto Ingest Monitor", ex); } @@ -263,6 +265,17 @@ final class AutoIngestDashboard extends JPanel { } } + @Override + public void update(Observable observable, Object arg) { + if (!scheduledRefreshStarted.getAndSet(true)) { + scheduledRefreshThreadPoolExecutor.scheduleWithFixedDelay(() -> { + EventQueue.invokeLater(() -> { + refreshTables(); + }); + }, AID_DELAY_BEFORE_FIRST_REFRESH, AID_REFRESH_INTERVAL_SECS, TimeUnit.SECONDS); + } + } + /** * Reloads the table models using a jobs snapshot and refreshes the JTables * that use the models. diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMonitor.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMonitor.java index 4e0de76bad..8f7a9c0696 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMonitor.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMonitor.java @@ -30,6 +30,7 @@ import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Observable; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledThreadPoolExecutor; @@ -58,7 +59,7 @@ import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestNodeControlEvent. * An auto ingest monitor responsible for monitoring and reporting the * processing of auto ingest jobs. */ -final class AutoIngestMonitor implements PropertyChangeListener { +final class AutoIngestMonitor extends Observable implements PropertyChangeListener { private static final Logger LOGGER = Logger.getLogger(AutoIngestMonitor.class.getName()); private static final int DEFAULT_PRIORITY = 0; @@ -171,6 +172,8 @@ final class AutoIngestMonitor implements PropertyChangeListener { synchronized (jobsLock) { jobsSnapshot.removePendingJob(event.getJob()); jobsSnapshot.addOrReplaceRunningJob(event.getJob()); + setChanged(); + notifyObservers(); } } @@ -198,6 +201,8 @@ final class AutoIngestMonitor implements PropertyChangeListener { break; } } + setChanged(); + notifyObservers(); } } @@ -212,6 +217,8 @@ final class AutoIngestMonitor implements PropertyChangeListener { jobsSnapshot.removePendingJob(job); jobsSnapshot.removeRunningJob(job); jobsSnapshot.addOrReplaceCompletedJob(job); + setChanged(); + notifyObservers(); } } @@ -239,14 +246,18 @@ final class AutoIngestMonitor implements PropertyChangeListener { * @param event A node state change event. */ private void handleAutoIngestNodeStateEvent(AutoIngestNodeStateEvent event) { + AutoIngestNodeState oldNodeState = null; if (event.getEventType() == AutoIngestManager.Event.SHUTDOWN) { // Remove node from collection. - nodeStates.remove(event.getNodeName()); + oldNodeState = nodeStates.remove(event.getNodeName()); } else { // Otherwise either create an entry for the given node name or update // an existing entry in the map. nodeStates.put(event.getNodeName(), new AutoIngestNodeState(event.getNodeName(), event.getEventType())); } + setChanged(); + // Trigger a dashboard refresh. + notifyObservers(oldNodeState == null ? nodeStates.get(event.getNodeName()) : oldNodeState); } /** @@ -786,6 +797,9 @@ final class AutoIngestMonitor implements PropertyChangeListener { // Ask running auto ingest nodes to report their status. refreshNodeState(); + + setChanged(); + notifyObservers(); } }