diff --git a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java
index 3c5c8a6621..ad5ffe8323 100644
--- a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java
+++ b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java
@@ -31,6 +31,7 @@ import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.core.Installer;
import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.autopsy.coreutils.ThreadConfined;
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.timeline.Timeline")
@ActionRegistration(displayName = "#CTL_MakeTimeline", lazy = false)
@@ -58,8 +59,8 @@ public class OpenTimelineAction extends CallableSystemAction {
}
@Override
+ @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public void performAction() {
-
//check case
if (!Case.isCaseOpen()) {
return;
@@ -72,14 +73,12 @@ public class OpenTimelineAction extends CallableSystemAction {
LOGGER.log(Level.INFO, "Could not create timeline, there are no data sources.");// NON-NLS
return;
}
- synchronized (OpenTimelineAction.class) {
if (timeLineController == null) {
timeLineController = new TimeLineController(currentCase);
} else if (timeLineController.getAutopsyCase() != currentCase) {
timeLineController.closeTimeLine();
timeLineController = new TimeLineController(currentCase);
}
- }
timeLineController.openTimeLine();
}
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ProgressUpdate.java b/Core/src/org/sleuthkit/autopsy/timeline/ProgressUpdate.java
new file mode 100644
index 0000000000..31783e2e3f
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/timeline/ProgressUpdate.java
@@ -0,0 +1,48 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.sleuthkit.autopsy.timeline;
+
+import javax.annotation.concurrent.Immutable;
+
+/**
+ * bundles up progress information to be shown in the progress dialog
+ */
+@Immutable
+public class ProgressUpdate {
+ private final int progress;
+ private final int total;
+ private final String headerMessage;
+ private final String detailMessage;
+
+ public int getProgress() {
+ return progress;
+ }
+
+ public int getTotal() {
+ return total;
+ }
+
+ public String getHeaderMessage() {
+ return headerMessage;
+ }
+
+ public String getDetailMessage() {
+ return detailMessage;
+ }
+
+ public ProgressUpdate(int progress, int total, String headerMessage, String detailMessage) {
+ this.progress = progress;
+ this.total = total;
+ this.headerMessage = headerMessage;
+ this.detailMessage = detailMessage;
+ }
+
+ public ProgressUpdate(int progress, int total, String headerMessage) {
+ this(progress, total, headerMessage, "");
+ }
+
+}
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.form b/Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.form
deleted file mode 100644
index 397abea1b4..0000000000
--- a/Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.form
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
-
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.java b/Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.java
deleted file mode 100644
index 9afa6f7c89..0000000000
--- a/Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Autopsy Forensic Browser
- *
- * Copyright 2013 Basis Technology Corp.
- * Contact: carrier sleuthkit org
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.sleuthkit.autopsy.timeline;
-
-import java.awt.Component;
-import java.awt.event.ActionEvent;
-import java.awt.event.KeyEvent;
-import javax.annotation.concurrent.Immutable;
-import javax.swing.AbstractAction;
-import javax.swing.ActionMap;
-import javax.swing.InputMap;
-import javax.swing.JComponent;
-import javax.swing.JFrame;
-import javax.swing.JOptionPane;
-import javax.swing.KeyStroke;
-import javax.swing.SwingUtilities;
-import javax.swing.SwingWorker;
-import org.openide.util.NbBundle;
-import org.openide.windows.WindowManager;
-import org.sleuthkit.autopsy.coreutils.ThreadConfined;
-
-/**
- * Dialog with progress bar that pops up when timeline is being generated
- */
-public class ProgressWindow extends JFrame {
-
- private final SwingWorker, ?> worker;
-
- /**
- * Creates new form TimelineProgressDialog
- */
- @NbBundle.Messages({"Timeline.progressWindow.name=Timeline",
- "Timeline.progressWindow.title=Generating Timeline data"})
- public ProgressWindow(Component parent, boolean modal, SwingWorker, ?> worker) {
- super();
- initComponents();
-
- setLocationRelativeTo(parent);
-
- setAlwaysOnTop(modal);
-
- //set icon the same as main app
- SwingUtilities.invokeLater(() -> {
- setIconImage(WindowManager.getDefault().getMainWindow().getIconImage());
- });
-
- setName(Bundle.Timeline_progressWindow_name());
- setTitle(Bundle.Timeline_progressWindow_title());
- // Close the dialog when Esc is pressed
- String cancelName = "cancel"; // NON-NLS
- InputMap inputMap = getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
-
- inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelName);
- ActionMap actionMap = getRootPane().getActionMap();
-
- actionMap.put(cancelName, new AbstractAction() {
- @Override
- public void actionPerformed(ActionEvent e) {
- cancel();
- }
- });
- this.worker = worker;
- }
-
- /**
- * This method is called from within the constructor to initialize the form.
- * WARNING: Do NOT modify this code. The content of this method is always
- * regenerated by the Form Editor.
- */
- @SuppressWarnings("unchecked")
- // //GEN-BEGIN:initComponents
- private void initComponents() {
-
- progressBar = new javax.swing.JProgressBar();
- progressHeader = new javax.swing.JLabel();
-
- addWindowListener(new java.awt.event.WindowAdapter() {
- public void windowClosing(java.awt.event.WindowEvent evt) {
- closeDialog(evt);
- }
- });
-
- org.openide.awt.Mnemonics.setLocalizedText(progressHeader, NbBundle.getMessage(ProgressWindow.class, "ProgressWindow.progressHeader.text")); // NOI18N
- progressHeader.setMinimumSize(new java.awt.Dimension(10, 14));
-
- javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
- getContentPane().setLayout(layout);
- layout.setHorizontalGroup(
- layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(layout.createSequentialGroup()
- .addContainerGap()
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(progressBar, javax.swing.GroupLayout.DEFAULT_SIZE, 504, Short.MAX_VALUE)
- .addGroup(layout.createSequentialGroup()
- .addComponent(progressHeader, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addGap(0, 0, Short.MAX_VALUE)))
- .addContainerGap())
- );
- layout.setVerticalGroup(
- layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(layout.createSequentialGroup()
- .addContainerGap()
- .addComponent(progressHeader, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
- );
-
- pack();
- }// //GEN-END:initComponents
-
- /**
- * Closes the dialog
- */
- private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog
- cancel();
- }//GEN-LAST:event_closeDialog
-
- @NbBundle.Messages({"Timeline.ProgressWindow.cancel.confdlg.msg=Do you want to cancel timeline creation?",
- "Timeline.ProgressWindow.cancel.confdlg.detail=Cancel timeline creation?"})
- public void cancel() {
- SwingUtilities.invokeLater(() -> {
- if (isVisible()) {
- int showConfirmDialog = JOptionPane.showConfirmDialog(ProgressWindow.this,
- Bundle.Timeline_ProgressWindow_cancel_confdlg_msg(),
- Bundle.Timeline_ProgressWindow_cancel_confdlg_detail(),
- JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
- if (showConfirmDialog == JOptionPane.YES_OPTION) {
- close();
- }
- } else {
- close();
- }
- });
- }
-
- public void close() {
- worker.cancel(false);
- setVisible(false);
- dispose();
- }
- // Variables declaration - do not modify//GEN-BEGIN:variables
- private javax.swing.JProgressBar progressBar;
- private javax.swing.JLabel progressHeader;
- // End of variables declaration//GEN-END:variables
-
- @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
- public void update(ProgressUpdate chunk) {
- progressHeader.setText(chunk.getHeaderMessage());
- if (chunk.getTotal() >= 0) {
- progressBar.setIndeterminate(false);
- progressBar.setMaximum(chunk.getTotal());
- progressBar.setStringPainted(true);
- progressBar.setValue(chunk.getProgress());
- progressBar.setString(chunk.getDetailMessage());
- } else {
- progressBar.setIndeterminate(true);
- progressBar.setStringPainted(true);
- progressBar.setString(chunk.getDetailMessage());
- }
- }
-
- /**
- * bundles up progress information to be shown in the progress dialog
- */
- @Immutable
- public static class ProgressUpdate {
-
- private final int progress;
- private final int total;
- private final String headerMessage;
- private final String detailMessage;
-
- public int getProgress() {
- return progress;
- }
-
- public int getTotal() {
- return total;
- }
-
- public String getHeaderMessage() {
- return headerMessage;
- }
-
- public String getDetailMessage() {
- return detailMessage;
- }
-
- public ProgressUpdate(int progress, int total, String headerMessage, String detailMessage) {
- this.progress = progress;
- this.total = total;
- this.headerMessage = headerMessage;
- this.detailMessage = detailMessage;
- }
-
- public ProgressUpdate(int progress, int total, String headerMessage) {
- this(progress, total, headerMessage, "");
- }
- }
-}
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java
index 9f904eca79..07e8af5824 100644
--- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java
+++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java
@@ -21,6 +21,8 @@ package org.sleuthkit.autopsy.timeline;
import java.awt.HeadlessException;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
+import java.io.IOException;
+import java.net.URL;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.NumberFormat;
@@ -28,6 +30,7 @@ import java.time.ZoneId;
import java.util.Collection;
import java.util.Map;
import java.util.MissingResourceException;
+import java.util.Optional;
import java.util.TimeZone;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -49,10 +52,18 @@ import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
+import javafx.concurrent.WorkerStateEvent;
+import javafx.scene.control.Alert;
+import javafx.scene.control.ButtonType;
+import javafx.scene.control.DialogEvent;
+import javafx.scene.image.Image;
+import javafx.stage.Modality;
+import javafx.stage.Stage;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.Immutable;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
+import org.controlsfx.dialog.ProgressDialog;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Interval;
@@ -136,6 +147,9 @@ public class TimeLineController {
private final ReadOnlyStringWrapper status = new ReadOnlyStringWrapper();
+ @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
+ private ProgressDialog taskProgressDialog;
+
/**
* status is a string that will be displayed in the status bar as a kind of
* user hint/information when it is not empty
@@ -202,7 +216,6 @@ public class TimeLineController {
@GuardedBy("filteredEvents")
private final FilteredEventsModel filteredEvents;
- @GuardedBy("eventsRepository")
private final EventsRepository eventsRepository;
@GuardedBy("this")
@@ -305,6 +318,7 @@ public class TimeLineController {
* the user aborted after prompt about ingest running. True if the
* repo was rebuilt.
*/
+ @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
boolean rebuildRepo() {
if (IngestManager.getInstance().isIngestRunning()) {
//confirm timeline during ingest
@@ -314,40 +328,29 @@ public class TimeLineController {
}
LOGGER.log(Level.INFO, "Beginning generation of timeline"); // NON-NLS
try {
- SwingUtilities.invokeLater(() -> {
- synchronized (TimeLineController.this) {
- if (isWindowOpen()) {
- mainFrame.close();
- }
- }
- });
+ closeTimelineWindow();
final SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase();
final long lastObjId = sleuthkitCase.getLastObjectId();
final long lastArtfID = getCaseLastArtifactID(sleuthkitCase);
final Boolean injestRunning = IngestManager.getInstance().isIngestRunning();
- //TODO: verify this locking is correct? -jm
- synchronized (eventsRepository) {
- eventsRepository.rebuildRepository(() -> {
- synchronized (eventsRepository) {
- eventsRepository.recordLastObjID(lastObjId);
- eventsRepository.recordLastArtifactID(lastArtfID);
- eventsRepository.recordWasIngestRunning(injestRunning);
- }
- synchronized (TimeLineController.this) {
- //TODO: this looks hacky. what is going on? should this be an event?
- needsHistogramRebuild.set(true);
- needsHistogramRebuild.set(false);
- showWindow();
- }
+ final Task rebuildRepository = eventsRepository.rebuildRepository(lastObjId, lastArtfID, injestRunning);
+ Platform.runLater(() -> {
+ rebuildRepository.setOnSucceeded((WorkerStateEvent event) -> {
+ //this is run on jfx thread
- Platform.runLater(() -> {
- //TODO: should this be an event?
- newEventsFlag.set(false);
- historyManager.reset(filteredEvents.zoomParametersProperty().get());
- TimeLineController.this.showFullRange();
- });
+ //TODO: this looks hacky. what is going on? should this be an event?
+ needsHistogramRebuild.set(true);
+ needsHistogramRebuild.set(false);
+ SwingUtilities.invokeLater(TimeLineController.this::showWindow);
+
+ //TODO: should this be an event?
+ newEventsFlag.set(false);
+ historyManager.reset(filteredEvents.zoomParametersProperty().get());
+ TimeLineController.this.showFullRange();
});
- }
+ taskProgressDialog = showProgressDialogForTask(rebuildRepository);
+ });
+
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error when generating timeline, ", ex); // NON-NLS
return false;
@@ -360,25 +363,62 @@ public class TimeLineController {
* tags table and rebuild it by querying for all the tags and inserting them
* in to the TimeLine DB.
*/
+ @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void rebuildTagsTable() {
LOGGER.log(Level.INFO, "starting to rebuild tags table"); // NON-NLS
- SwingUtilities.invokeLater(() -> {
- synchronized (TimeLineController.this) {
- if (isWindowOpen()) {
- mainFrame.close();
- }
- }
- });
- synchronized (eventsRepository) {
- eventsRepository.rebuildTags(() -> {
- showWindow();
- Platform.runLater(() -> {
- showFullRange();
- });
+ closeTimelineWindow();
+ Task rebuildTags = eventsRepository.rebuildTags();
+
+ Platform.runLater(() -> {
+ rebuildTags.setOnSucceeded((WorkerStateEvent event) -> {
+ SwingUtilities.invokeLater(TimeLineController.this::showWindow);
+ showFullRange();;
});
+ taskProgressDialog = showProgressDialogForTask(rebuildTags);
+ });
+
+ }
+
+ private void closeTimelineWindow() {
+ if (isWindowOpen()) {
+ mainFrame.close();
}
}
+ @NbBundle.Messages({"Timeline.progressWindow.title=Populating Timeline data",
+ "Timeline.ProgressWindow.cancel.confdlg.msg=Not all events will be present of accurate if you cancel the timeline population. Do you want to cancel?",
+ "Timeline.ProgressWindow.cancel.confdlg.detail=Cancel timeline population?"})
+ private ProgressDialog showProgressDialogForTask(Task task) {
+ ProgressDialog progressDialog = new ProgressDialog(task);
+ progressDialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
+
+ progressDialog.setTitle(Bundle.Timeline_progressWindow_title());
+ progressDialog.getDialogPane().headerTextProperty().bind(task.titleProperty());
+ try {
+ Image image = new Image(new URL("nbresloc:/org/netbeans/core/startup/frame.gif").openStream());
+ ((Stage) progressDialog.getDialogPane().getScene().getWindow()).getIcons().setAll(image);
+ } catch (IOException iOException) {
+ LOGGER.log(Level.WARNING, "Failed to laod branded icon for progress dialog.", iOException);
+ }
+ progressDialog.setOnCloseRequest((DialogEvent event) -> {
+ Alert confirmation = new Alert(Alert.AlertType.CONFIRMATION, Bundle.Timeline_ProgressWindow_cancel_confdlg_msg(), ButtonType.YES, ButtonType.NO);
+ confirmation.setHeaderText(null);
+ confirmation.setTitle(Bundle.Timeline_ProgressWindow_cancel_confdlg_detail());
+ confirmation.initOwner(progressDialog.getDialogPane().getScene().getWindow());
+ confirmation.initModality(Modality.WINDOW_MODAL);
+ Optional showAndWait = confirmation.showAndWait();
+
+ showAndWait.ifPresent(buttonType -> {
+ if (buttonType == ButtonType.YES) {
+ task.cancel(true);
+ } else {
+ event.consume();
+ }
+ });
+ });
+ return progressDialog;
+ }
+
public void showFullRange() {
synchronized (filteredEvents) {
pushTimeRange(filteredEvents.getSpanningInterval());
@@ -403,6 +443,7 @@ public class TimeLineController {
/**
* show the timeline window and prompt for rebuilding database if necessary.
*/
+ @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void openTimeLine() {
// listen for case changes (specifically images being added, and case changes).
if (Case.isCaseOpen() && !listeningToAutopsy) {
@@ -413,6 +454,16 @@ public class TimeLineController {
}
try {
+ if (eventsRepository.isRebuilding()) {
+ Platform.runLater(() -> {
+ if (taskProgressDialog != null) {
+ ((Stage) taskProgressDialog.getDialogPane().getScene().getWindow()).toFront();
+ }
+ });
+
+ return;
+ }
+
boolean repoRebuilt = false; //has the repo been rebuilt
long timeLineLastObjectId = eventsRepository.getLastObjID();
@@ -545,16 +596,13 @@ public class TimeLineController {
/**
* private method to build gui if necessary and make it visible.
*/
+ @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
synchronized private void showWindow() {
- SwingUtilities.invokeLater(() -> {
- synchronized (TimeLineController.this) {
- if (mainFrame == null) {
- mainFrame = new TimeLineTopComponent(this);
- }
- mainFrame.open();
- mainFrame.toFront();
- }
- });
+ if (mainFrame == null) {
+ mainFrame = new TimeLineTopComponent(this);
+ }
+ mainFrame.open();
+ mainFrame.toFront();
}
synchronized public void pushEventTypeZoom(EventTypeZoomLevel typeZoomeLevel) {
@@ -575,8 +623,7 @@ public class TimeLineController {
} else if (currentZoom.hasTimeRange(timeRange) == false) {
advance(currentZoom.withTimeRange(timeRange));
return true;
- }
- else{
+ } else {
return false;
}
}
@@ -590,7 +637,7 @@ public class TimeLineController {
final Long count = eventCounts.values().stream().reduce(0l, Long::sum);
boolean shouldContinue = true;
- if ((newLOD == DescriptionLoD.FULL|| newLOD ==DescriptionLoD.MEDIUM )&& count > 10_000) {
+ if ((newLOD == DescriptionLoD.FULL || newLOD == DescriptionLoD.MEDIUM) && count > 10_000) {
String format = NumberFormat.getInstance().format(count);
int showConfirmDialog = JOptionPane.showConfirmDialog(mainFrame,
@@ -748,7 +795,8 @@ public class TimeLineController {
*
* @return true if the timeline window is open
*/
- synchronized private boolean isWindowOpen() {
+ @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
+ private boolean isWindowOpen() {
return mainFrame != null && mainFrame.isOpened() && mainFrame.isVisible();
}
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java
index b36c4a56c6..ea083f67d9 100644
--- a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java
+++ b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java
@@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.timeline.db;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -30,6 +31,8 @@ import java.util.Map;
import static java.util.Objects.isNull;
import java.util.Set;
import java.util.concurrent.CancellationException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.stream.Collectors;
@@ -37,10 +40,8 @@ import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
-import javax.annotation.concurrent.GuardedBy;
+import javafx.concurrent.Task;
import javax.swing.JOptionPane;
-import javax.swing.SwingUtilities;
-import javax.swing.SwingWorker;
import org.apache.commons.lang3.StringUtils;
import org.controlsfx.dialog.ProgressDialog;
import org.joda.time.Interval;
@@ -50,7 +51,8 @@ import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.coreutils.Logger;
-import org.sleuthkit.autopsy.timeline.ProgressWindow;
+import org.sleuthkit.autopsy.coreutils.ThreadConfined;
+import org.sleuthkit.autopsy.timeline.ProgressUpdate;
import org.sleuthkit.autopsy.timeline.datamodel.EventCluster;
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
@@ -93,8 +95,7 @@ public class EventsRepository {
private final EventDB eventDB;
- @GuardedBy("this")
- private SwingWorker dbPopulationWorker;
+ private Task dbPopulationWorker;
private final LoadingCache