diff --git a/Core/src/org/sleuthkit/autopsy/timeline/PromptDialogManager.java b/Core/src/org/sleuthkit/autopsy/timeline/PromptDialogManager.java index 131b8db003..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,11 +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.headerTextProperty().bind(task.titleProperty()); - setDialogIcons(currentDialog); + currentDialog.initModality(Modality.NONE); 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 @@ -98,6 +105,8 @@ 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()); @@ -115,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); } /** @@ -139,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); } @@ -152,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 ee581fbd7d..c77ba8ee75 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; @@ -289,25 +291,24 @@ public class TimeLineController { } /** - * rebuld the repo. + * 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. * - * @return False if the repo was not rebuilt because because the user - * aborted after prompt about ingest running. True if the repo was - * rebuilt. + * @param repoBuilder */ @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,28 +319,24 @@ public class TimeLineController { } }); promptDialogManager.showProgressDialog(rebuildRepository); + } + /** + * rebuld the entire repo. + */ + @ThreadConfined(type = ThreadConfined.ThreadType.JFX) + void rebuildRepo() { + rebuildRepoHelper(eventsRepository::rebuildRepository); } /** * 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() { - - 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..0583b0c175 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)); } @@ -321,27 +320,56 @@ 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() { - return rebuildRepository(DBPopulationMode.FULL); - } - - @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - public CancellationProgressTask rebuildTags() { - return rebuildRepository(DBPopulationMode.TAGS_ONLY); + public CancellationProgressTask rebuildRepository(Consumer onStateChange) { + return rebuildRepository(DBPopulationMode.FULL, onStateChange); } /** * - * @param mode the value of mode + * 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) - private CancellationProgressTask rebuildRepository(final DBPopulationMode mode) { + public CancellationProgressTask rebuildTags(Consumer onStateChange) { + return rebuildRepository(DBPopulationMode.TAGS_ONLY, onStateChange); + } + + /** + * rebuild the repo. + * + * @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) { 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 +434,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(stateObservable -> onStateChange.accept(getState())); } void restartProgressHandle(String title, String message, Double workDone, double total, Boolean cancellable) {