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/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..4a04a4a630 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;
@@ -49,10 +51,16 @@ import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
+import javafx.concurrent.Worker;
+import javafx.scene.control.ButtonType;
+import javafx.scene.control.DialogPane;
+import javafx.scene.image.Image;
+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;
@@ -97,7 +105,8 @@ import org.sleuthkit.datamodel.TskCoreException;
* *
Since eventsRepository is internally synchronized, only compound
* access to it needs external synchronization
*
Other state including listeningToAutopsy, mainFrame, viewMode, and the
- * listeners should only be accessed with this object's intrinsic lock held
+ * listeners should only be accessed with this object's intrinsic lock held, or
+ * on the EDT as indicated.
*
*
*/
@@ -136,6 +145,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
@@ -181,11 +193,11 @@ public class TimeLineController {
return taskTitle.getReadOnlyProperty();
}
- @GuardedBy("this")
+ @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private TimeLineTopComponent mainFrame;
//are the listeners currently attached
- @GuardedBy("this")
+ @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private boolean listeningToAutopsy = false;
private final PropertyChangeListener caseListener = new AutopsyCaseListener();
@@ -202,7 +214,6 @@ public class TimeLineController {
@GuardedBy("filteredEvents")
private final FilteredEventsModel filteredEvents;
- @GuardedBy("eventsRepository")
private final EventsRepository eventsRepository;
@GuardedBy("this")
@@ -305,6 +316,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
@@ -312,46 +324,26 @@ public class TimeLineController {
return false;
}
}
- LOGGER.log(Level.INFO, "Beginning generation of timeline"); // NON-NLS
- try {
- SwingUtilities.invokeLater(() -> {
- synchronized (TimeLineController.this) {
- if (isWindowOpen()) {
- mainFrame.close();
- }
+ SwingUtilities.invokeLater(this::closeTimelineWindow);
+ Platform.runLater(() -> {
+ final Worker rebuildRepository = eventsRepository.rebuildRepository();
+ rebuildRepository.stateProperty().addListener((stateProperty, oldState, newSate) -> {
+ //this will be on JFX thread
+ if (newSate == Worker.State.SUCCEEDED) {
+ //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();
+
}
});
- 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();
- }
-
- Platform.runLater(() -> {
- //TODO: should this be an event?
- newEventsFlag.set(false);
- historyManager.reset(filteredEvents.zoomParametersProperty().get());
- TimeLineController.this.showFullRange();
- });
- });
- }
- } catch (TskCoreException ex) {
- LOGGER.log(Level.SEVERE, "Error when generating timeline, ", ex); // NON-NLS
- return false;
- }
+ showProgressDialog(rebuildRepository);
+ });
return true;
}
@@ -361,48 +353,76 @@ public class TimeLineController {
* in to the TimeLine DB.
*/
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(() -> {
+
+ SwingUtilities.invokeLater(this::closeTimelineWindow);
+ Platform.runLater(() -> {
+ Worker rebuildTags = eventsRepository.rebuildTags();
+ rebuildTags.stateProperty().addListener((stateProperty, oldState, newSate) -> {
+ //this will be on JFX thread
+ if (newSate == Worker.State.SUCCEEDED) {
+ SwingUtilities.invokeLater(TimeLineController.this::showWindow);
showFullRange();
- });
+ }
});
+ showProgressDialog(rebuildTags);
+ });
+ }
+
+ @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
+ private void closeTimelineWindow() {
+ if (isWindowOpen()) {
+ mainFrame.close();
}
}
+ @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
+ @NbBundle.Messages({"Timeline.progressWindow.title=Populating Timeline Data"})
+ private void showProgressDialog(Worker task) {
+ taskProgressDialog = new ProgressDialog(task);
+ taskProgressDialog.setTitle(Bundle.Timeline_progressWindow_title());
+ DialogPane dialogPane = taskProgressDialog.getDialogPane();
+ dialogPane.getButtonTypes().add(ButtonType.CANCEL);
+ Stage dialogStage = (Stage) dialogPane.getScene().getWindow();
+ dialogPane.setPrefWidth(400);
+ taskProgressDialog.headerTextProperty().bind(task.titleProperty());
+ dialogStage.getIcons().setAll(LOGO);
+ taskProgressDialog.setOnCloseRequest(closeRequestEvent -> task.cancel());
+ }
+
+ private static final Image LOGO;
+
+ static {
+ Image x = null;
+ try {
+ x = new Image(new URL("nbresloc:/org/netbeans/core/startup/frame.gif").openStream());
+ } catch (IOException ex) {
+ LOGGER.log(Level.WARNING, "Failed to laod branded icon for progress dialog.", ex);
+ }
+ LOGO = x;
+ }
+
public void showFullRange() {
synchronized (filteredEvents) {
pushTimeRange(filteredEvents.getSpanningInterval());
}
}
- synchronized public void closeTimeLine() {
+ @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
+ public void closeTimeLine() {
if (mainFrame != null) {
listeningToAutopsy = false;
IngestManager.getInstance().removeIngestModuleEventListener(ingestModuleListener);
IngestManager.getInstance().removeIngestJobEventListener(ingestJobListener);
Case.removePropertyChangeListener(caseListener);
- SwingUtilities.invokeLater(() -> {
- synchronized (TimeLineController.this) {
- mainFrame.close();
- mainFrame = null;
- }
- });
+ mainFrame.close();
+ mainFrame = null;
}
}
/**
* 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 +433,15 @@ 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();
@@ -465,7 +494,7 @@ public class TimeLineController {
}
}
- private long getCaseLastArtifactID(final SleuthkitCase sleuthkitCase) {
+ public static long getCaseLastArtifactID(final SleuthkitCase sleuthkitCase) {
long caseLastArtfId = -1;
String query = "select Max(artifact_id) as max_id from blackboard_artifacts"; // NON-NLS
try (CaseDbQuery dbQuery = sleuthkitCase.executeQuery(query)) {
@@ -545,16 +574,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 +601,7 @@ public class TimeLineController {
} else if (currentZoom.hasTimeRange(timeRange) == false) {
advance(currentZoom.withTimeRange(timeRange));
return true;
- }
- else{
+ } else {
return false;
}
}
@@ -590,7 +615,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 +773,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..d2b6046aa2 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,19 +31,23 @@ import java.util.Map;
import static java.util.Objects.isNull;
import java.util.Set;
import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.stream.Collectors;
+import javafx.application.Platform;
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.Service;
+import javafx.concurrent.Task;
+import javafx.concurrent.Worker;
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;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
@@ -50,7 +55,9 @@ 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.ingest.IngestManager;
+import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.datamodel.EventCluster;
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
@@ -93,8 +100,7 @@ public class EventsRepository {
private final EventDB eventDB;
- @GuardedBy("this")
- private SwingWorker dbPopulationWorker;
+ private final DBPopulationService dbPopulationService = new DBPopulationService(this);
private final LoadingCache