finish converting image analyzer to use History.java

This commit is contained in:
jmillman 2014-08-28 10:21:18 -04:00
parent ddb9d0c245
commit 9012e245f0
4 changed files with 116 additions and 316 deletions

View File

@ -37,7 +37,7 @@ import javax.annotation.concurrent.ThreadSafe;
* current/historical/future states * current/historical/future states
*/ */
@ThreadSafe @ThreadSafe
public class HistoryManager<T> { public class History<T> {
@GuardedBy("this") @GuardedBy("this")
private final ObservableStack<T> historyStack = new ObservableStack<>(); private final ObservableStack<T> historyStack = new ObservableStack<>();
@ -78,12 +78,12 @@ public class HistoryManager<T> {
return canRetreat.getReadOnlyProperty(); return canRetreat.getReadOnlyProperty();
} }
public HistoryManager(T initialState) { public History(T initialState) {
this(); this();
currentState.set(initialState); currentState.set(initialState);
} }
public HistoryManager() { public History() {
canAdvance.bind(forwardStack.emptyProperty().not()); canAdvance.bind(forwardStack.emptyProperty().not());
canRetreat.bind(historyStack.emptyProperty().not()); canRetreat.bind(historyStack.emptyProperty().not());
} }
@ -112,15 +112,15 @@ public class HistoryManager<T> {
* @return the state retreated to, or null if there were no history states. * @return the state retreated to, or null if there were no history states.
*/ */
synchronized public T retreat() { synchronized public T retreat() {
final T peek = historyStack.pop(); final T pop = historyStack.pop();
if (peek != null && peek.equals(currentState.get()) == false) { if (pop != null && pop.equals(currentState.get()) == false) {
forwardStack.push(currentState.get()); forwardStack.push(currentState.get());
currentState.set(peek); currentState.set(pop);
} else if (peek != null && peek.equals(currentState.get())) { } else if (pop != null && pop.equals(currentState.get())) {
return retreat(); return retreat();
} }
return peek; return pop;
} }
/** /**
@ -129,12 +129,8 @@ public class HistoryManager<T> {
* by invoking the equals method. Throws away any forward states. * by invoking the equals method. Throws away any forward states.
* *
* @param newState the new state to advance to * @param newState the new state to advance to
* @throws IllegalArgumentException if the newState is null
*/ */
synchronized public void advance(T newState) throws IllegalArgumentException { synchronized public void advance(T newState) throws IllegalArgumentException {
if (newState == null) {
throw new IllegalArgumentException("newState must be non-null");
}
if (currentState.equals(newState) == false) { if (currentState.equals(newState) == false) {
if (currentState.get() != null) { if (currentState.get() != null) {
historyStack.push(currentState.get()); historyStack.push(currentState.get());
@ -149,7 +145,9 @@ public class HistoryManager<T> {
} }
public void clear() { public void clear() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. historyStack.clear();
forwardStack.clear();
currentState.set(null);
} }
/** /**

View File

@ -1,196 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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 javafx.beans.property.Property;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
/**
* A basic history implementation. Keeps a history (and forward) stack of state
* objects of type <T>. exposes current state and availability of
* advance/retreat operations via methods and JFX {@link Property}s
*
* @param <T> the type of objects used to represent the
* current/historical/future states
*/
@ThreadSafe
public class HistoryManager<T> {
@GuardedBy("this")
private final ObservableStack<T> historyStack = new ObservableStack<>();
@GuardedBy("this")
private final ObservableStack<T> forwardStack = new ObservableStack<>();
@GuardedBy("this")
private final ReadOnlyObjectWrapper<T> currentState = new ReadOnlyObjectWrapper<>();
@GuardedBy("this")
private final ReadOnlyBooleanWrapper canAdvance = new ReadOnlyBooleanWrapper();
@GuardedBy("this")
private final ReadOnlyBooleanWrapper canRetreat = new ReadOnlyBooleanWrapper();
synchronized public T getCurrentState() {
return currentState.get();
}
synchronized public boolean canAdvance() {
return canAdvance.get();
}
synchronized public boolean canRetreat() {
return canRetreat.get();
}
synchronized public ReadOnlyObjectProperty<T> currentState() {
return currentState.getReadOnlyProperty();
}
synchronized public ReadOnlyBooleanProperty getCanAdvance() {
return canAdvance.getReadOnlyProperty();
}
synchronized public ReadOnlyBooleanProperty getCanRetreat() {
return canRetreat.getReadOnlyProperty();
}
public HistoryManager(T initialState) {
this();
currentState.set(initialState);
}
public HistoryManager() {
canAdvance.bind(forwardStack.emptyProperty().not());
canRetreat.bind(historyStack.emptyProperty().not());
}
/**
* advance through the forward states by one, and put the current state in
* the history. Is a no-op if there are no forward states.
*
* @return the state advanced to, or null if there were no forward states.
*/
synchronized public T advance() {
final T peek = forwardStack.peek();
if (peek != null && peek.equals(currentState.get()) == false) {
historyStack.push(currentState.get());
currentState.set(peek);
forwardStack.pop();
}
return peek;
}
/**
* retreat through the history states by one, and add the current state to
* the forward states. Is a no-op if there are no history states.
*
* @return the state retreated to, or null if there were no history states.
*/
synchronized public T retreat() {
final T peek = historyStack.pop();
if (peek != null && peek.equals(currentState.get()) == false) {
forwardStack.push(currentState.get());
currentState.set(peek);
} else if (peek != null && peek.equals(currentState.get())) {
return retreat();
}
return peek;
}
/**
* put the current state in the history and advance to the given state. It
* is a no-op if the current state is equal to the given state as determined
* by invoking the equals method. Throws away any forward states.
*
* @param newState the new state to advance to
* @throws IllegalArgumentException if the newState is null
*/
synchronized public void advance(T newState) throws IllegalArgumentException {
if (newState == null) {
throw new IllegalArgumentException("newState must be non-null");
}
if (currentState.equals(newState) == false) {
if (currentState.get() != null) {
historyStack.push(currentState.get());
}
currentState.set(newState);
if (newState.equals(forwardStack.peek())) {
forwardStack.pop();
} else {
forwardStack.clear();
}
}
}
public void clear() {
historyStack.clear();
forwardStack.clear();
currentState.set(null);
}
/**
* A simple extension to SimpleListProperty to add a stack api
*
* TODO: this really should not extend SimpleListProperty but should
* delegate to an appropriate observable implementation while implementing
* the {@link Deque} interface
*/
private static class ObservableStack<T> extends SimpleListProperty<T> {
public ObservableStack() {
super(FXCollections.<T>synchronizedObservableList(FXCollections.<T>observableArrayList()));
}
public void push(T item) {
synchronized (this) {
add(0, item);
}
}
public T pop() {
synchronized (this) {
if (isEmpty()) {
return null;
} else {
return remove(0);
}
}
}
public T peek() {
synchronized (this) {
if (isEmpty()) {
return null;
} else {
return get(0);
}
}
}
}
}

View File

@ -62,6 +62,7 @@ import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE;
import static org.sleuthkit.autopsy.casemodule.Case.Events.DATA_SOURCE_ADDED; import static org.sleuthkit.autopsy.casemodule.Case.Events.DATA_SOURCE_ADDED;
import org.sleuthkit.autopsy.coreutils.History;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.timeline.events.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.events.FilteredEventsModel;
@ -170,7 +171,7 @@ public class TimeLineController {
private final ZoomParams InitialZoomState; private final ZoomParams InitialZoomState;
@GuardedBy("this") @GuardedBy("this")
private final HistoryManager<ZoomParams> historyManager = new HistoryManager<>(); private final History<ZoomParams> historyManager = new History<>();
//all members should be access with the intrinsict lock of this object held //all members should be access with the intrinsict lock of this object held
//selected events (ie shown in the result viewer) //selected events (ie shown in the result viewer)

View File

@ -63,6 +63,7 @@ import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory; import org.netbeans.api.progress.ProgressHandleFactory;
import org.openide.util.Exceptions; import org.openide.util.Exceptions;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.History;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.imageanalyzer.datamodel.Category; import org.sleuthkit.autopsy.imageanalyzer.datamodel.Category;
import org.sleuthkit.autopsy.imageanalyzer.datamodel.DrawableAttribute; import org.sleuthkit.autopsy.imageanalyzer.datamodel.DrawableAttribute;
@ -77,8 +78,6 @@ import org.sleuthkit.autopsy.imageanalyzer.gui.NoGroupsDialog;
import org.sleuthkit.autopsy.imageanalyzer.gui.SummaryTablePane; import org.sleuthkit.autopsy.imageanalyzer.gui.SummaryTablePane;
import org.sleuthkit.autopsy.imageanalyzer.progress.ProgressAdapterBase; import org.sleuthkit.autopsy.imageanalyzer.progress.ProgressAdapterBase;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.timeline.HistoryManager;
import org.sleuthkit.autopsy.timeline.zooming.ZoomParams;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -93,71 +92,68 @@ import org.sleuthkit.datamodel.TskData;
* Connects different parts of Eureka together and is hub for flow of control. * Connects different parts of Eureka together and is hub for flow of control.
*/ */
public class ImageAnalyzerController implements FileUpdateListener { public class ImageAnalyzerController implements FileUpdateListener {
private static final Logger LOGGER = Logger.getLogger(ImageAnalyzerController.class.getName()); private static final Logger LOGGER = Logger.getLogger(ImageAnalyzerController.class.getName());
private final Region infoOverLayBackground = new Region() { private final Region infoOverLayBackground = new Region() {
{ {
setBackground(new Background(new BackgroundFill(Color.GREY, CornerRadii.EMPTY, Insets.EMPTY))); setBackground(new Background(new BackgroundFill(Color.GREY, CornerRadii.EMPTY, Insets.EMPTY)));
setOpacity(.4); setOpacity(.4);
} }
}; };
private static ImageAnalyzerController instance; private static ImageAnalyzerController instance;
public static synchronized ImageAnalyzerController getDefault() { public static synchronized ImageAnalyzerController getDefault() {
if (instance == null) { if (instance == null) {
instance = new ImageAnalyzerController(); instance = new ImageAnalyzerController();
} }
return instance; return instance;
} }
@GuardedBy("this") @GuardedBy("this")
private final HistoryManager<GroupViewState> historyManager = new HistoryManager<>(); private final History<GroupViewState> historyManager = new History<>();
private final ReadOnlyBooleanWrapper listeningEnabled = new ReadOnlyBooleanWrapper(false); private final ReadOnlyBooleanWrapper listeningEnabled = new ReadOnlyBooleanWrapper(false);
private final ReadOnlyIntegerWrapper queueSizeProperty = new ReadOnlyIntegerWrapper(0); private final ReadOnlyIntegerWrapper queueSizeProperty = new ReadOnlyIntegerWrapper(0);
private final ReadOnlyBooleanWrapper regroupDisabled = new ReadOnlyBooleanWrapper(false); private final ReadOnlyBooleanWrapper regroupDisabled = new ReadOnlyBooleanWrapper(false);
private final ReadOnlyBooleanWrapper stale = new ReadOnlyBooleanWrapper(false); private final ReadOnlyBooleanWrapper stale = new ReadOnlyBooleanWrapper(false);
private final ReadOnlyBooleanWrapper metaDataCollapsed = new ReadOnlyBooleanWrapper(false); private final ReadOnlyBooleanWrapper metaDataCollapsed = new ReadOnlyBooleanWrapper(false);
private final FileIDSelectionModel selectionModel = FileIDSelectionModel.getInstance(); private final FileIDSelectionModel selectionModel = FileIDSelectionModel.getInstance();
private DBWorkerThread dbWorkerThread; private DBWorkerThread dbWorkerThread;
private DrawableDB db; private DrawableDB db;
private final GroupManager groupManager = new GroupManager(this); private final GroupManager groupManager = new GroupManager(this);
private StackPane fullUIStackPane; private StackPane fullUIStackPane;
private StackPane centralStackPane; private StackPane centralStackPane;
private Node infoOverlay; private Node infoOverlay;
public ReadOnlyBooleanProperty getMetaDataCollapsed() { public ReadOnlyBooleanProperty getMetaDataCollapsed() {
return metaDataCollapsed.getReadOnlyProperty(); return metaDataCollapsed.getReadOnlyProperty();
} }
public void setMetaDataCollapsed(Boolean metaDataCollapsed) { public void setMetaDataCollapsed(Boolean metaDataCollapsed) {
this.metaDataCollapsed.set(metaDataCollapsed); this.metaDataCollapsed.set(metaDataCollapsed);
} }
private GroupViewState getViewState() { private GroupViewState getViewState() {
return historyManager.getCurrentState(); return historyManager.getCurrentState();
} }
public ReadOnlyBooleanProperty regroupDisabled() { public ReadOnlyBooleanProperty regroupDisabled() {
return regroupDisabled.getReadOnlyProperty(); return regroupDisabled.getReadOnlyProperty();
} }
public ReadOnlyObjectProperty<GroupViewState> viewState() { public ReadOnlyObjectProperty<GroupViewState> viewState() {
return historyManager.currentState(); return historyManager.currentState();
} }
@ -173,28 +169,28 @@ public class ImageAnalyzerController implements FileUpdateListener {
* an executor to submit async ui related background tasks to. * an executor to submit async ui related background tasks to.
*/ */
final ExecutorService bgTaskExecutor = Executors.newSingleThreadExecutor(new BasicThreadFactory.Builder().namingPattern("ui task -%d").build()); final ExecutorService bgTaskExecutor = Executors.newSingleThreadExecutor(new BasicThreadFactory.Builder().namingPattern("ui task -%d").build());
public synchronized FileIDSelectionModel getSelectionModel() { public synchronized FileIDSelectionModel getSelectionModel() {
return selectionModel; return selectionModel;
} }
public GroupManager getGroupManager() { public GroupManager getGroupManager() {
return groupManager; return groupManager;
} }
public void setListeningEnabled(boolean enabled) { public void setListeningEnabled(boolean enabled) {
listeningEnabled.set(enabled); listeningEnabled.set(enabled);
} }
ReadOnlyBooleanProperty listeningEnabled() { ReadOnlyBooleanProperty listeningEnabled() {
return listeningEnabled.getReadOnlyProperty(); return listeningEnabled.getReadOnlyProperty();
} }
boolean isListeningEnabled() { boolean isListeningEnabled() {
return listeningEnabled.get(); return listeningEnabled.get();
} }
void setStale(Boolean b) { void setStale(Boolean b) {
Platform.runLater(() -> { Platform.runLater(() -> {
stale.set(b); stale.set(b);
@ -203,37 +199,37 @@ public class ImageAnalyzerController implements FileUpdateListener {
new PerCaseProperties(Case.getCurrentCase()).setConfigSetting(EurekaModule.MODULE_NAME, PerCaseProperties.STALE, b.toString()); new PerCaseProperties(Case.getCurrentCase()).setConfigSetting(EurekaModule.MODULE_NAME, PerCaseProperties.STALE, b.toString());
} }
} }
public ReadOnlyBooleanProperty stale() { public ReadOnlyBooleanProperty stale() {
return stale.getReadOnlyProperty(); return stale.getReadOnlyProperty();
} }
boolean isStale() { boolean isStale() {
return stale.get(); return stale.get();
} }
private ImageAnalyzerController() { private ImageAnalyzerController() {
listeningEnabled.addListener((observable, oldValue, newValue) -> { listeningEnabled.addListener((observable, oldValue, newValue) -> {
if (newValue && !oldValue && Case.existsCurrentCase() && EurekaModule.isCaseStale(Case.getCurrentCase())) { if (newValue && !oldValue && Case.existsCurrentCase() && EurekaModule.isCaseStale(Case.getCurrentCase())) {
queueTask(new CopyAnalyzedFiles()); queueTask(new CopyAnalyzedFiles());
} }
}); });
groupManager.getAnalyzedGroups().addListener((Observable o) -> { groupManager.getAnalyzedGroups().addListener((Observable o) -> {
checkForGroups(); checkForGroups();
}); });
groupManager.getUnSeenGroups().addListener((Observable observable) -> { groupManager.getUnSeenGroups().addListener((Observable observable) -> {
//if there are unseen groups and none being viewed
if (groupManager.getUnSeenGroups().size() > 0 && (getViewState() == null || getViewState().getGroup() == null)) { if (groupManager.getUnSeenGroups().size() > 0 && (getViewState() == null || getViewState().getGroup() == null)) {
advance(GroupViewState.tile(groupManager.getUnSeenGroups().get(0)));
setViewState(GroupViewState.tile(groupManager.getUnSeenGroups().get(0)));
} }
}); });
regroupDisabled.addListener((Observable observable) -> { regroupDisabled.addListener((Observable observable) -> {
checkForGroups(); checkForGroups();
}); });
IngestManager.getInstance().addIngestModuleEventListener((evt) -> { IngestManager.getInstance().addIngestModuleEventListener((evt) -> {
Platform.runLater(this::updateRegroupDisabled); Platform.runLater(this::updateRegroupDisabled);
}); });
@ -257,7 +253,7 @@ public class ImageAnalyzerController implements FileUpdateListener {
case SCHEDULED: case SCHEDULED:
case RUNNING: case RUNNING:
break; break;
case FAILED: case FAILED:
LOGGER.log(Level.WARNING, "task :" + task.getTitle() + " failed", task.getException()); LOGGER.log(Level.WARNING, "task :" + task.getTitle() + " failed", task.getException());
case CANCELLED: case CANCELLED:
@ -270,11 +266,11 @@ public class ImageAnalyzerController implements FileUpdateListener {
break; break;
} }
}); });
synchronized (bgTasks) { synchronized (bgTasks) {
bgTasks.add(task); bgTasks.add(task);
} }
bgTaskExecutor.execute(task); bgTaskExecutor.execute(task);
} }
@ -286,18 +282,20 @@ public class ImageAnalyzerController implements FileUpdateListener {
return historyManager.getCanRetreat(); return historyManager.getCanRetreat();
} }
synchronized public void advance(GroupViewState viewState) { synchronized public void advance(GroupViewState newState) {
historyManager.advance(viewState); if (viewState().get() == null ||( viewState().get().getGroup() != newState.getGroup())) {
historyManager.advance(newState);
}
} }
synchronized public GroupViewState advance() { synchronized public GroupViewState advance() {
return historyManager.advance(); return historyManager.advance();
} }
synchronized public GroupViewState retreat() { synchronized public GroupViewState retreat() {
return historyManager.retreat(); return historyManager.retreat();
} }
private void updateRegroupDisabled() { private void updateRegroupDisabled() {
regroupDisabled.set(getFileUpdateQueueSizeProperty().get() > 0 || IngestManager.getInstance().isIngestRunning()); regroupDisabled.set(getFileUpdateQueueSizeProperty().get() > 0 || IngestManager.getInstance().isIngestRunning());
} }
@ -309,7 +307,7 @@ public class ImageAnalyzerController implements FileUpdateListener {
*/ */
public final void checkForGroups() { public final void checkForGroups() {
if (groupManager.getAnalyzedGroups().isEmpty()) { if (groupManager.getAnalyzedGroups().isEmpty()) {
setViewState(null); advance(null);
if (IngestManager.getInstance().isIngestRunning()) { if (IngestManager.getInstance().isIngestRunning()) {
if (listeningEnabled.get() == false) { if (listeningEnabled.get() == false) {
replaceNotification(fullUIStackPane, replaceNotification(fullUIStackPane,
@ -320,7 +318,7 @@ public class ImageAnalyzerController implements FileUpdateListener {
new NoGroupsDialog("No groups are fully analyzed yet, but ingest is still ongoing. Please Wait.", new NoGroupsDialog("No groups are fully analyzed yet, but ingest is still ongoing. Please Wait.",
new ProgressIndicator())); new ProgressIndicator()));
} }
} else if (getFileUpdateQueueSizeProperty().get() > 0) { } else if (getFileUpdateQueueSizeProperty().get() > 0) {
replaceNotification(fullUIStackPane, replaceNotification(fullUIStackPane,
new NoGroupsDialog("No groups are fully analyzed yet, but image / video data is still being populated. Please Wait.", new NoGroupsDialog("No groups are fully analyzed yet, but image / video data is still being populated. Please Wait.",
@ -328,7 +326,7 @@ public class ImageAnalyzerController implements FileUpdateListener {
} else if (db != null && db.countAllFiles() <= 0) { // there are no files in db } else if (db != null && db.countAllFiles() <= 0) { // there are no files in db
replaceNotification(fullUIStackPane, replaceNotification(fullUIStackPane,
new NoGroupsDialog("There are no images/videos in the added datasources.")); new NoGroupsDialog("There are no images/videos in the added datasources."));
} else if (!groupManager.isRegrouping()) { } else if (!groupManager.isRegrouping()) {
replaceNotification(centralStackPane, replaceNotification(centralStackPane,
new NoGroupsDialog("There are no fully analyzed groups to display:" new NoGroupsDialog("There are no fully analyzed groups to display:"
@ -347,7 +345,7 @@ public class ImageAnalyzerController implements FileUpdateListener {
clearNotification(); clearNotification();
} }
} }
private void clearNotification() { private void clearNotification() {
//remove the ingest spinner //remove the ingest spinner
if (fullUIStackPane != null) { if (fullUIStackPane != null) {
@ -358,26 +356,26 @@ public class ImageAnalyzerController implements FileUpdateListener {
centralStackPane.getChildren().remove(infoOverlay); centralStackPane.getChildren().remove(infoOverlay);
} }
} }
private void replaceNotification(StackPane stackPane, Node newNode) { private void replaceNotification(StackPane stackPane, Node newNode) {
clearNotification(); clearNotification();
infoOverlay = new StackPane(infoOverLayBackground, newNode); infoOverlay = new StackPane(infoOverLayBackground, newNode);
if (stackPane != null) { if (stackPane != null) {
stackPane.getChildren().add(infoOverlay); stackPane.getChildren().add(infoOverlay);
} }
} }
private void restartWorker() { private void restartWorker() {
if (dbWorkerThread != null) { if (dbWorkerThread != null) {
dbWorkerThread.cancelAllTasks(); dbWorkerThread.cancelAllTasks();
} }
dbWorkerThread = new DBWorkerThread(); dbWorkerThread = new DBWorkerThread();
getFileUpdateQueueSizeProperty().addListener((o) -> { getFileUpdateQueueSizeProperty().addListener((o) -> {
Platform.runLater(this::updateRegroupDisabled); Platform.runLater(this::updateRegroupDisabled);
}); });
Thread th = new Thread(dbWorkerThread); Thread th = new Thread(dbWorkerThread);
th.setDaemon(false); // we want it to go away when it is done th.setDaemon(false); // we want it to go away when it is done
th.start(); th.start();
@ -389,7 +387,7 @@ public class ImageAnalyzerController implements FileUpdateListener {
* @param c * @param c
*/ */
public synchronized void setCase(Case c) { public synchronized void setCase(Case c) {
this.db = DrawableDB.getDrawableDB(c.getCaseDirectory(), this); this.db = DrawableDB.getDrawableDB(c.getCaseDirectory(), this);
db.addUpdatedFileListener(this); db.addUpdatedFileListener(this);
setListeningEnabled(EurekaModule.isEnabledforCase(c)); setListeningEnabled(EurekaModule.isEnabledforCase(c));
@ -398,7 +396,7 @@ public class ImageAnalyzerController implements FileUpdateListener {
// if we add this line icons are made as files are analyzed rather than on demand. // if we add this line icons are made as files are analyzed rather than on demand.
// db.addUpdatedFileListener(IconCache.getDefault()); // db.addUpdatedFileListener(IconCache.getDefault());
restartWorker(); restartWorker();
groupManager.setDB(db); groupManager.setDB(db);
SummaryTablePane.getDefault().handleCategoryChanged(Collections.EMPTY_LIST); SummaryTablePane.getDefault().handleCategoryChanged(Collections.EMPTY_LIST);
} }
@ -417,12 +415,12 @@ public class ImageAnalyzerController implements FileUpdateListener {
for (final long fileId : fileIDs) { for (final long fileId : fileIDs) {
//get grouping(s) this file would be in //get grouping(s) this file would be in
Set<GroupKey> groupsForFile = groupManager.getGroupKeysForFileID(fileId); Set<GroupKey> groupsForFile = groupManager.getGroupKeysForFileID(fileId);
for (GroupKey gk : groupsForFile) { for (GroupKey gk : groupsForFile) {
groupManager.removeFromGroup(gk, fileId); groupManager.removeFromGroup(gk, fileId);
} }
} }
break; break;
case FILE_UPDATED: case FILE_UPDATED:
@ -441,7 +439,7 @@ public class ImageAnalyzerController implements FileUpdateListener {
//get grouping(s) this file would be in //get grouping(s) this file would be in
Set<GroupKey> groupsForFile = groupManager.getGroupKeysForFileID(fileId); Set<GroupKey> groupsForFile = groupManager.getGroupKeysForFileID(fileId);
for (GroupKey gk : groupsForFile) { for (GroupKey gk : groupsForFile) {
Grouping g = groupManager.getGroupForKey(gk); Grouping g = groupManager.getGroupForKey(gk);
@ -458,7 +456,7 @@ public class ImageAnalyzerController implements FileUpdateListener {
} }
} }
} }
Category.fireChange(fileIDs); Category.fireChange(fileIDs);
if (evt.getChangedAttribute() == DrawableAttribute.TAGS) { if (evt.getChangedAttribute() == DrawableAttribute.TAGS) {
TagUtils.fireChange(fileIDs); TagUtils.fireChange(fileIDs);
@ -474,10 +472,9 @@ public class ImageAnalyzerController implements FileUpdateListener {
LOGGER.info("resetting EurekaControler to initial state."); LOGGER.info("resetting EurekaControler to initial state.");
selectionModel.clearSelection(); selectionModel.clearSelection();
Platform.runLater(() -> { Platform.runLater(() -> {
historyManager.clear(); historyManager.clear();
}); });
EurekaToolbar.getDefault().reset(); EurekaToolbar.getDefault().reset();
groupManager.clear(); groupManager.clear();
if (db != null) { if (db != null) {
@ -498,21 +495,21 @@ public class ImageAnalyzerController implements FileUpdateListener {
} }
dbWorkerThread.addTask(innerTask); dbWorkerThread.addTask(innerTask);
} }
public DrawableFile getFileFromId(Long fileID) throws TskCoreException { public DrawableFile getFileFromId(Long fileID) throws TskCoreException {
return db.getFileFromID(fileID); return db.getFileFromID(fileID);
} }
public void setStacks(StackPane fullUIStack, StackPane centralStack) { public void setStacks(StackPane fullUIStack, StackPane centralStack) {
fullUIStackPane = fullUIStack; fullUIStackPane = fullUIStack;
this.centralStackPane = centralStack; this.centralStackPane = centralStack;
Platform.runLater(this::checkForGroups); Platform.runLater(this::checkForGroups);
} }
public final ReadOnlyIntegerProperty getFileUpdateQueueSizeProperty() { public final ReadOnlyIntegerProperty getFileUpdateQueueSizeProperty() {
return queueSizeProperty.getReadOnlyProperty(); return queueSizeProperty.getReadOnlyProperty();
} }
public ReadOnlyIntegerProperty bgTaskQueueSizeProperty() { public ReadOnlyIntegerProperty bgTaskQueueSizeProperty() {
return bgTasks.sizeProperty(); return bgTasks.sizeProperty();
} }
@ -551,7 +548,7 @@ public class ImageAnalyzerController implements FileUpdateListener {
workQueue.add(it); workQueue.add(it);
queueSizeProperty.set(workQueue.size()); queueSizeProperty.set(workQueue.size());
} }
@Override @Override
public void run() { public void run() {
// nearly infinite loop waiting for tasks // nearly infinite loop waiting for tasks
@ -565,17 +562,17 @@ public class ImageAnalyzerController implements FileUpdateListener {
if (it.cancelled == false) { if (it.cancelled == false) {
it.run(); it.run();
} }
queueSizeProperty.set(workQueue.size()); queueSizeProperty.set(workQueue.size());
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
Exceptions.printStackTrace(ex); Exceptions.printStackTrace(ex);
} }
} }
} }
} }
public SleuthkitCase getSleuthKitCase() throws IllegalStateException { public SleuthkitCase getSleuthKitCase() throws IllegalStateException {
if (Case.isCaseOpen()) { if (Case.isCaseOpen()) {
return Case.getCurrentCase().getSleuthkitCase(); return Case.getCurrentCase().getSleuthkitCase();
@ -588,13 +585,13 @@ public class ImageAnalyzerController implements FileUpdateListener {
* Abstract base class for task to be done on {@link DBWorkerThread} * Abstract base class for task to be done on {@link DBWorkerThread}
*/ */
static public abstract class InnerTask extends ProgressAdapterBase implements Runnable { static public abstract class InnerTask extends ProgressAdapterBase implements Runnable {
protected volatile boolean cancelled = false; protected volatile boolean cancelled = false;
public void cancel() { public void cancel() {
updateState(Worker.State.CANCELLED); updateState(Worker.State.CANCELLED);
} }
protected boolean isCancelled() { protected boolean isCancelled() {
return getState() == Worker.State.CANCELLED; return getState() == Worker.State.CANCELLED;
} }
@ -604,14 +601,14 @@ public class ImageAnalyzerController implements FileUpdateListener {
* Abstract base class for tasks associated with an obj id in the database * Abstract base class for tasks associated with an obj id in the database
*/ */
static private abstract class TaskWithID extends InnerTask { static private abstract class TaskWithID extends InnerTask {
protected Long obj_id; // id of image or file protected Long obj_id; // id of image or file
public TaskWithID(Long id) { public TaskWithID(Long id) {
super(); super();
this.obj_id = id; this.obj_id = id;
} }
public Long getId() { public Long getId() {
return obj_id; return obj_id;
} }
@ -625,7 +622,7 @@ public class ImageAnalyzerController implements FileUpdateListener {
* of the update tasks. * of the update tasks.
*/ */
class MarkAllFilesAsAnalyzed extends InnerTask { class MarkAllFilesAsAnalyzed extends InnerTask {
@Override @Override
public void run() { public void run() {
db.markAllFilesAnalyzed(); db.markAllFilesAnalyzed();
@ -637,9 +634,9 @@ public class ImageAnalyzerController implements FileUpdateListener {
* task that updates one file in database with results from ingest * task that updates one file in database with results from ingest
*/ */
class UpdateFile extends InnerTask { class UpdateFile extends InnerTask {
private final AbstractFile file; private final AbstractFile file;
public UpdateFile(AbstractFile f) { public UpdateFile(AbstractFile f) {
super(); super();
this.file = f; this.file = f;
@ -659,9 +656,9 @@ public class ImageAnalyzerController implements FileUpdateListener {
* task that updates one file in database with results from ingest * task that updates one file in database with results from ingest
*/ */
class RemoveFile extends InnerTask { class RemoveFile extends InnerTask {
private final AbstractFile file; private final AbstractFile file;
public RemoveFile(AbstractFile f) { public RemoveFile(AbstractFile f) {
super(); super();
this.file = f; this.file = f;
@ -684,16 +681,16 @@ public class ImageAnalyzerController implements FileUpdateListener {
* adds them to the Drawable DB * adds them to the Drawable DB
*/ */
class CopyAnalyzedFiles extends InnerTask { class CopyAnalyzedFiles extends InnerTask {
final private String DRAWABLE_QUERY = "name LIKE '%." + StringUtils.join(EurekaModule.getAllSupportedExtensions(), "' or name LIKE '%.") + "'"; final private String DRAWABLE_QUERY = "name LIKE '%." + StringUtils.join(EurekaModule.getAllSupportedExtensions(), "' or name LIKE '%.") + "'";
private ProgressHandle progressHandle = ProgressHandleFactory.createHandle("populating analyzed image/video database"); private ProgressHandle progressHandle = ProgressHandleFactory.createHandle("populating analyzed image/video database");
@Override @Override
public void run() { public void run() {
progressHandle.start(); progressHandle.start();
updateMessage("populating analyzed image/video database"); updateMessage("populating analyzed image/video database");
try { try {
//grap all files with supported mime types //grap all files with supported mime types
final List<AbstractFile> files = getSleuthKitCase().findAllFilesWhere(DRAWABLE_QUERY + " or tsk_files.obj_id in (select tsk_files.obj_id from tsk_files , blackboard_artifacts, blackboard_attributes" final List<AbstractFile> files = getSleuthKitCase().findAllFilesWhere(DRAWABLE_QUERY + " or tsk_files.obj_id in (select tsk_files.obj_id from tsk_files , blackboard_artifacts, blackboard_attributes"
@ -703,7 +700,7 @@ public class ImageAnalyzerController implements FileUpdateListener {
+ " and blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FILE_TYPE_SIG.getTypeID() + " and blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FILE_TYPE_SIG.getTypeID()
+ " and blackboard_attributes.value_text in ('" + StringUtils.join(EurekaModule.getSupportedMimes(), "','") + "'))"); + " and blackboard_attributes.value_text in ('" + StringUtils.join(EurekaModule.getSupportedMimes(), "','") + "'))");
progressHandle.switchToDeterminate(files.size()); progressHandle.switchToDeterminate(files.size());
updateProgress(0.0); updateProgress(0.0);
//do in transaction //do in transaction
@ -717,7 +714,7 @@ public class ImageAnalyzerController implements FileUpdateListener {
} }
final Boolean hasMimeType = EurekaModule.hasSupportedMimeType(f); final Boolean hasMimeType = EurekaModule.hasSupportedMimeType(f);
final boolean known = f.getKnown() == TskData.FileKnown.KNOWN; final boolean known = f.getKnown() == TskData.FileKnown.KNOWN;
if (known) { if (known) {
db.removeFile(f.getId(), tr); //remove known files db.removeFile(f.getId(), tr); //remove known files
} else { } else {
@ -737,38 +734,38 @@ public class ImageAnalyzerController implements FileUpdateListener {
} }
} }
} }
units++; units++;
final int prog = units; final int prog = units;
progressHandle.progress(f.getName(), units); progressHandle.progress(f.getName(), units);
updateProgress(prog - 1 / (double) files.size()); updateProgress(prog - 1 / (double) files.size());
updateMessage(f.getName()); updateMessage(f.getName());
} }
progressHandle.finish(); progressHandle.finish();
progressHandle = ProgressHandleFactory.createHandle("commiting image/video database"); progressHandle = ProgressHandleFactory.createHandle("commiting image/video database");
updateMessage("commiting image/video database"); updateMessage("commiting image/video database");
updateProgress(1.0); updateProgress(1.0);
progressHandle.start(); progressHandle.start();
db.commitTransaction(tr, true); db.commitTransaction(tr, true);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
Logger.getLogger(CopyAnalyzedFiles.class.getName()).log(Level.WARNING, "failed to transfer all database contents", ex); Logger.getLogger(CopyAnalyzedFiles.class.getName()).log(Level.WARNING, "failed to transfer all database contents", ex);
} catch (IllegalStateException ex) { } catch (IllegalStateException ex) {
Logger.getLogger(CopyAnalyzedFiles.class.getName()).log(Level.SEVERE, "Case was closed out from underneath CopyDataSource task", ex); Logger.getLogger(CopyAnalyzedFiles.class.getName()).log(Level.SEVERE, "Case was closed out from underneath CopyDataSource task", ex);
} }
progressHandle.finish(); progressHandle.finish();
updateMessage( updateMessage(
""); "");
updateProgress( updateProgress(
-1.0); -1.0);
setStale(false); setStale(false);
} }
} }
/** /**
@ -788,9 +785,9 @@ public class ImageAnalyzerController implements FileUpdateListener {
*/ */
// (name like '.jpg' or name like '.png' ...) // (name like '.jpg' or name like '.png' ...)
final private String DRAWABLE_QUERY = "name LIKE '%." + StringUtils.join(EurekaModule.getAllSupportedExtensions(), "' or name LIKE '%.") + "'"; final private String DRAWABLE_QUERY = "name LIKE '%." + StringUtils.join(EurekaModule.getAllSupportedExtensions(), "' or name LIKE '%.") + "'";
private ProgressHandle progressHandle = ProgressHandleFactory.createHandle("prepopulating image/video database"); private ProgressHandle progressHandle = ProgressHandleFactory.createHandle("prepopulating image/video database");
public PrePopulateDataSourceFiles(Long id) { public PrePopulateDataSourceFiles(Long id) {
super(id); super(id);
} }
@ -830,7 +827,7 @@ public class ImageAnalyzerController implements FileUpdateListener {
// updateProgress(prog - 1 / (double) files.size()); // updateProgress(prog - 1 / (double) files.size());
// updateMessage(f.getName()); // updateMessage(f.getName());
} }
progressHandle.finish(); progressHandle.finish();
progressHandle = ProgressHandleFactory.createHandle("commiting image/video database"); progressHandle = ProgressHandleFactory.createHandle("commiting image/video database");
// updateMessage("commiting image/video database"); // updateMessage("commiting image/video database");
@ -838,13 +835,13 @@ public class ImageAnalyzerController implements FileUpdateListener {
progressHandle.start(); progressHandle.start();
db.commitTransaction(tr, false); db.commitTransaction(tr, false);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
Logger.getLogger(PrePopulateDataSourceFiles.class.getName()).log(Level.WARNING, "failed to transfer all database contents", ex); Logger.getLogger(PrePopulateDataSourceFiles.class.getName()).log(Level.WARNING, "failed to transfer all database contents", ex);
} catch (IllegalStateException ex) { } catch (IllegalStateException ex) {
Logger.getLogger(PrePopulateDataSourceFiles.class.getName()).log(Level.SEVERE, "Case was closed out from underneath CopyDataSource task", ex); Logger.getLogger(PrePopulateDataSourceFiles.class.getName()).log(Level.SEVERE, "Case was closed out from underneath CopyDataSource task", ex);
} }
progressHandle.finish(); progressHandle.finish();
// updateMessage(""); // updateMessage("");