From 843fefac2546908bbab92da60a72eb95979496d9 Mon Sep 17 00:00:00 2001 From: jmillman Date: Thu, 24 Mar 2016 15:46:01 -0400 Subject: [PATCH 1/2] prevent progressdialog from blocking input to the main timeline window --- .../autopsy/timeline/PromptDialogManager.java | 2 + .../autopsy/timeline/TimeLineController.java | 40 ++++++++----------- .../autopsy/timeline/db/EventsRepository.java | 20 +++++----- 3 files changed, 29 insertions(+), 33 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/PromptDialogManager.java b/Core/src/org/sleuthkit/autopsy/timeline/PromptDialogManager.java index 131b8db003..6f58bade53 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/PromptDialogManager.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/PromptDialogManager.java @@ -88,6 +88,7 @@ public class PromptDialogManager { @ThreadConfined(type = ThreadConfined.ThreadType.JFX) public void showProgressDialog(CancellationProgressTask task) { currentDialog = new ProgressDialog(task); + currentDialog.initModality(Modality.NONE); currentDialog.headerTextProperty().bind(task.titleProperty()); setDialogIcons(currentDialog); currentDialog.setTitle(Bundle.PromptDialogManager_progressDialog_title()); @@ -98,6 +99,7 @@ public class PromptDialogManager { //co-ordinate task cancelation and dialog hiding. task.setOnCancelled(cancelled -> currentDialog.close()); task.setOnSucceeded(succeeded -> currentDialog.close()); + task.setOnFailed(failed -> currentDialog.close()); dialogPane.getButtonTypes().setAll(ButtonType.CANCEL); final Node cancelButton = dialogPane.lookupButton(ButtonType.CANCEL); cancelButton.disableProperty().bind(task.cancellableProperty().not()); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index ee581fbd7d..4a925800bf 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -30,6 +30,8 @@ import java.util.TimeZone; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.function.Consumer; +import java.util.function.Function; import java.util.logging.Level; import javafx.application.Platform; import javafx.beans.Observable; @@ -288,26 +290,17 @@ public class TimeLineController { advance(filteredEvents.zoomParametersProperty().get().withTimeRange(boundingEventsInterval)); } - /** - * rebuld the repo. - * - * @return False if the repo was not rebuilt because because the user - * aborted after prompt about ingest running. True if the repo was - * rebuilt. - */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - void rebuildRepo() { + private void rebuildRepoHelper(Function, CancellationProgressTask> repoBuilder) { SwingUtilities.invokeLater(this::closeTimelineWindow); - final CancellationProgressTask rebuildRepository = eventsRepository.rebuildRepository(); boolean ingestRunning = IngestManager.getInstance().isIngestRunning(); - rebuildRepository.stateProperty().addListener((stateProperty, oldState, newSate) -> { - //this will be on JFX thread + final CancellationProgressTask rebuildRepository = repoBuilder.apply(newSate -> { setIngestRunning(ingestRunning); + //this will be on JFX thread switch (newSate) { case SUCCEEDED: setEventsDBStale(false); SwingUtilities.invokeLater(TimeLineController.this::showWindow); - historyManager.reset(filteredEvents.zoomParametersProperty().get()); TimeLineController.this.showFullRange(); break; @@ -318,7 +311,18 @@ public class TimeLineController { } }); promptDialogManager.showProgressDialog(rebuildRepository); + } + /** + * rebuld the repo. + * + * @return False if the repo was not rebuilt because because the user + * aborted after prompt about ingest running. True if the repo was + * rebuilt. + */ + @ThreadConfined(type = ThreadConfined.ThreadType.JFX) + void rebuildRepo() { + rebuildRepoHelper(eventsRepository::rebuildRepository); } /** @@ -329,17 +333,7 @@ public class TimeLineController { */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) void rebuildTagsTable() { - - SwingUtilities.invokeLater(this::closeTimelineWindow); - CancellationProgressTask 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(); - } - }); - promptDialogManager.showProgressDialog(rebuildTags); + rebuildRepoHelper(eventsRepository::rebuildTags); } @ThreadConfined(type = ThreadConfined.ThreadType.AWT) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java index c8554cfdc3..bc490faed4 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java @@ -35,6 +35,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; import java.util.logging.Level; import java.util.stream.Collectors; import javafx.application.Platform; @@ -44,6 +45,7 @@ import javafx.beans.property.ReadOnlyObjectProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.collections.ObservableMap; +import javafx.concurrent.Worker; import javax.swing.JOptionPane; import org.apache.commons.lang3.StringUtils; import org.joda.time.Interval; @@ -177,7 +179,6 @@ public class EventsRepository { } - public TimeLineEvent getEventById(Long eventID) { return idToEventCache.getUnchecked(eventID); } @@ -310,8 +311,6 @@ public class EventsRepository { } } - - public boolean areFiltersEquivalent(RootFilter f1, RootFilter f2) { return SQLHelper.getSQLWhere(f1).equals(SQLHelper.getSQLWhere(f2)); } @@ -322,13 +321,13 @@ public class EventsRepository { } @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - public CancellationProgressTask rebuildRepository() { - return rebuildRepository(DBPopulationMode.FULL); + public CancellationProgressTask rebuildRepository(Consumer onStateChange) { + return rebuildRepository(DBPopulationMode.FULL, onStateChange); } @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - public CancellationProgressTask rebuildTags() { - return rebuildRepository(DBPopulationMode.TAGS_ONLY); + public CancellationProgressTask rebuildTags(Consumer onStateChange) { + return rebuildRepository(DBPopulationMode.TAGS_ONLY, onStateChange); } /** @@ -336,12 +335,12 @@ public class EventsRepository { * @param mode the value of mode */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - private CancellationProgressTask rebuildRepository(final DBPopulationMode mode) { + private CancellationProgressTask rebuildRepository(final DBPopulationMode mode, Consumer onStateChange) { LOGGER.log(Level.INFO, "(re)starting {0} db population task", mode); //NON-NLS if (dbWorker != null) { dbWorker.cancel(); } - dbWorker = new DBPopulationWorker(mode); + dbWorker = new DBPopulationWorker(mode, onStateChange); workerExecutor.execute(dbWorker); return dbWorker; } @@ -406,10 +405,11 @@ public class EventsRepository { } } - DBPopulationWorker(DBPopulationMode mode) { + DBPopulationWorker(DBPopulationMode mode, Consumer onStateChange) { skCase = autoCase.getSleuthkitCase(); tagsManager = autoCase.getServices().getTagsManager(); this.dbPopulationMode = mode; + this.stateProperty().addListener(observable -> onStateChange.accept(getState())); } void restartProgressHandle(String title, String message, Double workDone, double total, Boolean cancellable) { From 767a169d77a2a90cd6ea3006636f47c6612e90d4 Mon Sep 17 00:00:00 2001 From: jmillman Date: Thu, 24 Mar 2016 15:56:14 -0400 Subject: [PATCH 2/2] cleanup PromptDialogManager, EventsRepository and TimeLineController --- .../autopsy/timeline/PromptDialogManager.java | 32 +++++++++--------- .../autopsy/timeline/TimeLineController.java | 15 +++++---- .../autopsy/timeline/db/EventsRepository.java | 33 +++++++++++++++++-- 3 files changed, 56 insertions(+), 24 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/PromptDialogManager.java b/Core/src/org/sleuthkit/autopsy/timeline/PromptDialogManager.java index 6f58bade53..893ff1dec4 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/PromptDialogManager.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/PromptDialogManager.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015 Basis Technology Corp. + * Copyright 2015-16 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -61,9 +61,9 @@ public class PromptDialogManager { static { Image x = null; try { - x = new Image(new URL("nbresloc:/org/netbeans/core/startup/frame.gif").openStream()); //NOI18N + x = new Image(new URL("nbresloc:/org/netbeans/core/startup/frame.gif").openStream()); //NON-NLS } catch (IOException ex) { - LOGGER.log(Level.WARNING, "Failed to load branded icon for progress dialog.", ex); //NOI18N NON-NLS + LOGGER.log(Level.WARNING, "Failed to load branded icon for progress dialog.", ex); //NON-NLS } LOGO = x; } @@ -75,6 +75,12 @@ public class PromptDialogManager { this.controller = controller; } + /** + * bring the currently managed dialog (if there is one) to the front + * + * @return true if a dialog was brought to the front, or false of there is + * no currently managed open dialog + */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) boolean bringCurrentDialogToFront() { if (currentDialog != null && currentDialog.isShowing()) { @@ -86,12 +92,12 @@ public class PromptDialogManager { @NbBundle.Messages({"PromptDialogManager.progressDialog.title=Populating Timeline Data"}) @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - public void showProgressDialog(CancellationProgressTask task) { + void showProgressDialog(CancellationProgressTask task) { currentDialog = new ProgressDialog(task); currentDialog.initModality(Modality.NONE); - currentDialog.headerTextProperty().bind(task.titleProperty()); - setDialogIcons(currentDialog); currentDialog.setTitle(Bundle.PromptDialogManager_progressDialog_title()); + setDialogIcons(currentDialog); + currentDialog.headerTextProperty().bind(task.titleProperty()); DialogPane dialogPane = currentDialog.getDialogPane(); dialogPane.setPrefSize(400, 200); //override autosizing which fails for some reason @@ -100,6 +106,7 @@ public class PromptDialogManager { task.setOnCancelled(cancelled -> currentDialog.close()); task.setOnSucceeded(succeeded -> currentDialog.close()); task.setOnFailed(failed -> currentDialog.close()); + dialogPane.getButtonTypes().setAll(ButtonType.CANCEL); final Node cancelButton = dialogPane.lookupButton(ButtonType.CANCEL); cancelButton.disableProperty().bind(task.cancellableProperty().not()); @@ -117,14 +124,7 @@ public class PromptDialogManager { @ThreadConfined(type = ThreadConfined.ThreadType.JFX) static private void setDialogIcons(Dialog dialog) { - Stage stage = (Stage) dialog.getDialogPane().getScene().getWindow(); - stage.getIcons().setAll(LOGO); - } - - @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - static private void setDialogTitle(Dialog dialog) { - Stage stage = (Stage) dialog.getDialogPane().getScene().getWindow(); - stage.setTitle(Bundle.Timeline_confirmation_dialogs_title()); + ((Stage) dialog.getDialogPane().getScene().getWindow()).getIcons().setAll(LOGO); } /** @@ -141,7 +141,7 @@ public class PromptDialogManager { currentDialog.initModality(Modality.APPLICATION_MODAL); currentDialog.setHeaderText(Bundle.PromptDialogManager_confirmDuringIngest_headerText()); setDialogIcons(currentDialog); - setDialogTitle(currentDialog); + currentDialog.setTitle(Bundle.Timeline_confirmation_dialogs_title()); return currentDialog.showAndWait().map(SHOW_TIMELINE::equals).orElse(false); } @@ -154,7 +154,7 @@ public class PromptDialogManager { currentDialog.initModality(Modality.APPLICATION_MODAL); currentDialog.setHeaderText(Bundle.PromptDialogManager_rebuildPrompt_headerText()); setDialogIcons(currentDialog); - setDialogTitle(currentDialog); + currentDialog.setTitle(Bundle.Timeline_confirmation_dialogs_title()); DialogPane dialogPane = currentDialog.getDialogPane(); ListView listView = new ListView<>(FXCollections.observableArrayList(rebuildReasons)); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index 4a925800bf..c77ba8ee75 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -290,6 +290,14 @@ public class TimeLineController { advance(filteredEvents.zoomParametersProperty().get().withTimeRange(boundingEventsInterval)); } + /** + * rebuild the repo using the given repo builder (expected to be a member + * reference to {@link EventsRepository#rebuildRepository(java.util.function.Consumer) + * } or {@link EventsRepository#rebuildTags(java.util.function.Consumer) }) + * and display the ui when it is done. + * + * @param repoBuilder + */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) private void rebuildRepoHelper(Function, CancellationProgressTask> repoBuilder) { SwingUtilities.invokeLater(this::closeTimelineWindow); @@ -314,11 +322,7 @@ public class TimeLineController { } /** - * rebuld the repo. - * - * @return False if the repo was not rebuilt because because the user - * aborted after prompt about ingest running. True if the repo was - * rebuilt. + * rebuld the entire repo. */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) void rebuildRepo() { @@ -329,7 +333,6 @@ public class TimeLineController { * Since tags might have changed while TimeLine wasn't listening, drop the * tags table and rebuild it by querying for all the tags and inserting them * in to the TimeLine DB. - * */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) void rebuildTagsTable() { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java index bc490faed4..0583b0c175 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java @@ -320,19 +320,48 @@ public class EventsRepository { return dbWorker.isRunning(); } + /** + * + * rebuild the entire repo. + * + * @param onStateChange called when he background task changes state. + * Clients can use this to handle failure, or cleanup + * operations for example. + * + * @return the task that will rebuild the repo in a background thread. The + * task has already been started. + */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) public CancellationProgressTask rebuildRepository(Consumer onStateChange) { return rebuildRepository(DBPopulationMode.FULL, onStateChange); } + /** + * + * drop and rebuild the tags in the repo. + * + * @param onStateChange called when he background task changes state. + * Clients can use this to handle failure, or cleanup + * operations for example. + * + * @return the task that will rebuild the repo in a background thread. The + * task has already been started. + */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) public CancellationProgressTask rebuildTags(Consumer onStateChange) { return rebuildRepository(DBPopulationMode.TAGS_ONLY, onStateChange); } /** + * rebuild the repo. * - * @param mode the value of mode + * @param mode the rebuild mode to use. + * @param onStateChange called when he background task changes state. + * Clients can use this to handle failure, or cleanup + * operations for example. + * + * @return the task that will rebuild the repo in a background thread. The + * task has already been started. */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) private CancellationProgressTask rebuildRepository(final DBPopulationMode mode, Consumer onStateChange) { @@ -409,7 +438,7 @@ public class EventsRepository { skCase = autoCase.getSleuthkitCase(); tagsManager = autoCase.getServices().getTagsManager(); this.dbPopulationMode = mode; - this.stateProperty().addListener(observable -> onStateChange.accept(getState())); + this.stateProperty().addListener(stateObservable -> onStateChange.accept(getState())); } void restartProgressHandle(String title, String message, Double workDone, double total, Boolean cancellable) {