From 9ce28220c7aeffb414443fca6f58c64eed179827 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 19 Oct 2018 09:43:03 +0200 Subject: [PATCH 01/11] remove public access to dataSourcesMap, don't swallow exceptions --- .../autopsy/timeline/FilteredEventsModel.java | 77 ++++----- .../autopsy/timeline/TimeLineController.java | 154 +++++++++--------- .../autopsy/timeline/TimeLineModule.java | 38 +++-- .../timeline/ui/filtering/FilterSetPanel.java | 21 ++- .../timeline/ui/filtering/FilterTreeItem.java | 9 +- .../datamodel/CompoundFilterStateImpl.java | 50 +++--- .../filtering/datamodel/RootFilterState.java | 20 ++- .../filtering/datamodel/TagsFilterState.java | 5 +- 8 files changed, 197 insertions(+), 177 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java index 795e804c27..ea6cb00b04 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java @@ -70,12 +70,14 @@ import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifactTag; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.DescriptionLoD; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.Tag; import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TimelineManager; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskDataException; import org.sleuthkit.datamodel.timeline.EventType; import org.sleuthkit.datamodel.timeline.EventTypeZoomLevel; import org.sleuthkit.datamodel.timeline.TimelineEvent; @@ -132,7 +134,7 @@ public final class FilteredEventsModel { private final LoadingCache idToEventCache; private final LoadingCache> eventCountsCache; /** Map from datasource id to datasource name. */ - private final ObservableMap datasourcesMap = FXCollections.observableHashMap(); + private final ObservableMap datasourcesMap = FXCollections.observableHashMap(); private final ObservableSet< String> hashSets = FXCollections.observableSet(); private final ObservableList tagNames = FXCollections.observableArrayList(); // end caches @@ -157,22 +159,22 @@ public final class FilteredEventsModel { minCache = CacheBuilder.newBuilder() .build(new CacheLoaderImpl<>(ignored -> eventManager.getMinTime())); - datasourcesMap.addListener((MapChangeListener.Change change) -> { - DataSourceFilter dataSourceFilter = new DataSourceFilter(change.getValueAdded(), change.getKey()); + datasourcesMap.addListener((MapChangeListener.Change change) -> { + DataSourceFilter dataSourceFilter = new DataSourceFilter(change.getValueAdded().getName(), change.getKey()); RootFilterState rootFilter = filterProperty().get(); - rootFilter.getDataSourcesFilterState().getFilter().getSubFilters().add(dataSourceFilter); - requestedFilter.set(rootFilter.copyOf()); + rootFilter.getDataSourcesFilterState().getFilter().addSubFilter(dataSourceFilter); + requestedFilter.set(rootFilter); }); hashSets.addListener((SetChangeListener.Change< ? extends String> change) -> { HashSetFilter hashSetFilter = new HashSetFilter(change.getElementAdded()); RootFilterState rootFilter = filterProperty().get(); - rootFilter.getHashHitsFilterState().getFilter().getSubFilters().add(hashSetFilter); - requestedFilter.set(rootFilter.copyOf()); + rootFilter.getHashHitsFilterState().getFilter().addSubFilter(hashSetFilter); + requestedFilter.set(rootFilter); }); tagNames.addListener((ListChangeListener.Change change) -> { RootFilterState rootFilter = filterProperty().get(); syncTagsFilter(rootFilter); - requestedFilter.set(rootFilter.copyOf()); + requestedFilter.set(rootFilter); }); requestedFilter.set(getDefaultFilter()); @@ -182,7 +184,7 @@ public final class FilteredEventsModel { if (zoomState != null) { synchronized (FilteredEventsModel.this) { requestedTypeZoom.set(zoomState.getTypeZoomLevel()); - requestedFilter.set(zoomState.getFilterState()); + requestedFilter.set(zoomState.getFilterState().copyOf()); requestedTimeRange.set(zoomState.getTimeRange()); requestedLOD.set(zoomState.getDescriptionLOD()); } @@ -244,10 +246,7 @@ public final class FilteredEventsModel { } /** - * Use the given SleuthkitCase to update the data used to determine the - * available filters. - * - * @param skCase + * Update the data used to determine the available filters. */ synchronized private void populateFilterData() throws TskCoreException { SleuthkitCase skCase = autoCase.getSleuthkitCase(); @@ -255,7 +254,13 @@ public final class FilteredEventsModel { //because there is no way to remove a datasource we only add to this map. for (Long id : eventManager.getDataSourceIDs()) { - datasourcesMap.putIfAbsent(id, skCase.getContentById(id).getDataSource().getName()); + try { + if (datasourcesMap.containsKey(id) == false) { + datasourcesMap.put(id, skCase.getDataSource(id)); + } + } catch (TskDataException ex) { + throw new TskCoreException("Error looking up datasource for id " + id, ex); + } } //should this only be tags applied to files or event bearing artifacts? @@ -501,30 +506,22 @@ public final class FilteredEventsModel { return postTagsAdded(updatedEventIDs); } - synchronized public boolean handleContentTagDeleted(ContentTagDeletedEvent evt) { + synchronized public boolean handleContentTagDeleted(ContentTagDeletedEvent evt) throws TskCoreException { DeletedContentTagInfo deletedTagInfo = evt.getDeletedTagInfo(); - try { - Content content = autoCase.getSleuthkitCase().getContentById(deletedTagInfo.getContentID()); - boolean tagged = autoCase.getServices().getTagsManager().getContentTagsByContent(content).isEmpty() == false; - Set updatedEventIDs = deleteTag(content.getId(), null, deletedTagInfo.getTagID(), tagged); - return postTagsDeleted(updatedEventIDs); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "unable to determine tagged status of content.", ex); //NON-NLS - } - return false; + + Content content = autoCase.getSleuthkitCase().getContentById(deletedTagInfo.getContentID()); + boolean tagged = autoCase.getServices().getTagsManager().getContentTagsByContent(content).isEmpty() == false; + Set updatedEventIDs = deleteTag(content.getId(), null, deletedTagInfo.getTagID(), tagged); + return postTagsDeleted(updatedEventIDs); } - synchronized public boolean handleArtifactTagDeleted(BlackBoardArtifactTagDeletedEvent evt) { + synchronized public boolean handleArtifactTagDeleted(BlackBoardArtifactTagDeletedEvent evt) throws TskCoreException { DeletedBlackboardArtifactTagInfo deletedTagInfo = evt.getDeletedTagInfo(); - try { - BlackboardArtifact artifact = autoCase.getSleuthkitCase().getBlackboardArtifact(deletedTagInfo.getArtifactID()); - boolean tagged = autoCase.getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact).isEmpty() == false; - Set updatedEventIDs = deleteTag(artifact.getObjectID(), artifact.getArtifactID(), deletedTagInfo.getTagID(), tagged); - return postTagsDeleted(updatedEventIDs); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "unable to determine tagged status of artifact.", ex); //NON-NLS - } - return false; + + BlackboardArtifact artifact = autoCase.getSleuthkitCase().getBlackboardArtifact(deletedTagInfo.getArtifactID()); + boolean tagged = autoCase.getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact).isEmpty() == false; + Set updatedEventIDs = deleteTag(artifact.getObjectID(), artifact.getArtifactID(), deletedTagInfo.getTagID(), tagged); + return postTagsDeleted(updatedEventIDs); } /** @@ -664,21 +661,19 @@ public final class FilteredEventsModel { return updatedEventIDs; } - synchronized void invalidateAllCaches() { + synchronized void invalidateAllCaches() throws TskCoreException { minCache.invalidateAll(); maxCache.invalidateAll(); idToEventCache.invalidateAll(); invalidateCaches(Collections.emptyList()); } - synchronized private void invalidateCaches(Collection updatedEventIDs) { + synchronized private void invalidateCaches(Collection updatedEventIDs) throws TskCoreException { idToEventCache.invalidateAll(updatedEventIDs); eventCountsCache.invalidateAll(); - try { - populateFilterData(); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Failed topopulate filter data.", ex); //NON-NLS - } + + populateFilterData(); + eventbus.post(new CacheInvalidatedEvent()); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index 3e0b588cf5..b4a0522d51 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -56,6 +56,7 @@ import org.joda.time.Interval; import org.joda.time.ReadablePeriod; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; +import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; @@ -68,6 +69,7 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.coreutils.History; import org.sleuthkit.autopsy.coreutils.LoggedTask; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.ingest.IngestManager; @@ -98,9 +100,9 @@ import org.sleuthkit.datamodel.timeline.TimelineFilter.EventTypeFilter; * it needs external synchronization * *
  • Since eventsRepository is internally synchronized, only compound * access to it needs external synchronization
  • - *
  • Other state including mainFrame, viewMode, and the - * listeners should only be accessed with this object's intrinsic lock held, or - * on the EDT as indicated. + *
  • Other state including mainFrame, viewMode, and the listeners should only + * be accessed with this object's intrinsic lock held, or on the EDT as + * indicated. *
  • * */ @@ -189,7 +191,6 @@ public class TimeLineController { @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private TimeLineTopComponent topComponent; - @GuardedBy("this") private final ReadOnlyObjectWrapper viewMode = new ReadOnlyObjectWrapper<>(ViewMode.COUNTS); @@ -282,10 +283,10 @@ public class TimeLineController { * TimeLineController. Do we need to do this with datasource or hash hit * filters? */ - historyManager.currentState().addListener((ObservableValue observable, ZoomState oldValue, ZoomState newValue) -> { - ZoomState historyManagerParams = newValue; - filteredEvents.syncTagsFilter(historyManagerParams.getFilterState()); - currentParams.set(historyManagerParams); + historyManager.currentState().addListener(( observable, oldState, newState) -> { + ZoomState historyManagerState = newState; + filteredEvents.syncTagsFilter(historyManagerState.getFilterState()); + currentParams.set(historyManagerState); }); try { @@ -709,76 +710,79 @@ public class TimeLineController { } - void handleIngestModuleEvent(PropertyChangeEvent evt) { - /** - * Checking for a current case is a stop gap measure until a - * different way of handling the closing of cases is worked out. - * Currently, remote events may be received for a case that is - * already closed. - */ - try { - Case.getCurrentCaseThrows(); - } catch (NoCurrentCaseException notUsed) { - // Case is closed, do nothing. - return; - } - - // ignore remote events. The node running the ingest should update the Case DB - // @@@ We should signal though that there is more data and flush caches... - if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.REMOTE) { - return; - } - - switch (IngestManager.IngestModuleEvent.valueOf(evt.getPropertyName())) { - case CONTENT_CHANGED: - // new files were already added to the events table from SleuthkitCase. - break; - case DATA_ADDED: - ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue(); - if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) { - executor.submit(() -> filteredEvents.setHashHit(eventData.getArtifacts(), true)); - } - break; - case FILE_DONE: - /* - * Since the known state or hash hit state may have changed - * invalidate caches. - */ - //@@@ This causes HUGE slow downs during ingest when TL is open. - // executor.submit(filteredEvents::invalidateAllCaches); - - // known state should have been udpated automatically via SleuthkitCase.setKnown(); - // hashes should have been updated from event - } + /** + * Checking for a current case is a stop gap measure until a different + * way of handling the closing of cases is worked out. Currently, remote + * events may be received for a case that is already closed. + */ + try { + Case.getCurrentCaseThrows(); + } catch (NoCurrentCaseException notUsed) { + // Case is closed, do nothing. + return; } - - void handleCaseEvent(PropertyChangeEvent evt) { - switch (Case.Events.valueOf(evt.getPropertyName())) { - case BLACKBOARD_ARTIFACT_TAG_ADDED: - executor.submit(() -> filteredEvents.handleArtifactTagAdded((BlackBoardArtifactTagAddedEvent) evt)); - break; - case BLACKBOARD_ARTIFACT_TAG_DELETED: - executor.submit(() -> filteredEvents.handleArtifactTagDeleted((BlackBoardArtifactTagDeletedEvent) evt)); - break; - case CONTENT_TAG_ADDED: - executor.submit(() -> filteredEvents.handleContentTagAdded((ContentTagAddedEvent) evt)); - break; - case CONTENT_TAG_DELETED: - executor.submit(() -> filteredEvents.handleContentTagDeleted((ContentTagDeletedEvent) evt)); - break; - case DATA_SOURCE_ADDED: - executor.submit(() -> filteredEvents.postAutopsyEventLocally((AutopsyEvent) evt)); - break; - case CURRENT_CASE: - //close timeline on case changes. - SwingUtilities.invokeLater(TimeLineController.this::shutDownTimeLine); - break; - case EVENT_ADDED: - executor.submit(filteredEvents::invalidateAllCaches); - break; + // ignore remote events. The node running the ingest should update the Case DB + // @@@ We should signal though that there is more data and flush caches... + if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.REMOTE) { + return; + } + + switch (IngestManager.IngestModuleEvent.valueOf(evt.getPropertyName())) { + case CONTENT_CHANGED: + // new files were already added to the events table from SleuthkitCase. + break; + case DATA_ADDED: + ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue(); + if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) { + executor.submit(() -> filteredEvents.setHashHit(eventData.getArtifacts(), true)); + } + break; + case FILE_DONE: + /* + * Since the known state or hash hit state may have changed + * invalidate caches. + */ + //@@@ This causes HUGE slow downs during ingest when TL is open. + // executor.submit(filteredEvents::invalidateAllCaches); + + // known state should have been udpated automatically via SleuthkitCase.setKnown(); + // hashes should have been updated from event } } -} + void handleCaseEvent(PropertyChangeEvent evt) { + switch (Case.Events.valueOf(evt.getPropertyName())) { + case BLACKBOARD_ARTIFACT_TAG_ADDED: + executor.submit(() -> filteredEvents.handleArtifactTagAdded((BlackBoardArtifactTagAddedEvent) evt)); + break; + case BLACKBOARD_ARTIFACT_TAG_DELETED: + executor.submit(() -> filteredEvents.handleArtifactTagDeleted((BlackBoardArtifactTagDeletedEvent) evt)); + break; + case CONTENT_TAG_ADDED: + executor.submit(() -> filteredEvents.handleContentTagAdded((ContentTagAddedEvent) evt)); + break; + case CONTENT_TAG_DELETED: + executor.submit(() -> filteredEvents.handleContentTagDeleted((ContentTagDeletedEvent) evt)); + break; + case DATA_SOURCE_ADDED: + executor.submit(() -> filteredEvents.postAutopsyEventLocally((AutopsyEvent) evt)); + break; + case CURRENT_CASE: + //close timeline on case changes. + SwingUtilities.invokeLater(TimeLineController.this::shutDownTimeLine); + break; + case EVENT_ADDED: + executor.submit(() -> { + try { + filteredEvents.invalidateAllCaches(); + } catch (TskCoreException ex) { + MessageNotifyUtil.Message.error("Error invalidating timeline caches."); + logger.log(Level.SEVERE, "Error invalidating timeline caches.", ex); + } + }); + break; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineModule.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineModule.java index 3cbf982401..cf65c87fed 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineModule.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineModule.java @@ -20,18 +20,19 @@ package org.sleuthkit.autopsy.timeline; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.logging.Level; import javafx.application.Platform; import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.datamodel.TskCoreException; - /** * Manages listeners and the controller. - * + * */ public class TimeLineModule { @@ -48,17 +49,17 @@ public class TimeLineModule { /** * Get instance of the controller for the current case - * @return - * @throws NoCurrentCaseException + * + * @return the controller for the current case. + * + * @throws NoCurrentCaseException If there is no case open. + * @throws TskCoreException If there was a problem accessing the case + * database. */ - public static TimeLineController getController() throws NoCurrentCaseException { + public static TimeLineController getController() throws NoCurrentCaseException, TskCoreException { synchronized (controllerLock) { if (controller == null) { - try { - controller = new TimeLineController(Case.getCurrentCaseThrows()); - } catch (NoCurrentCaseException | TskCoreException ex) { - throw new NoCurrentCaseException("Error getting TimeLineController for the current case.", ex); - } + controller = new TimeLineController(Case.getCurrentCaseThrows()); } return controller; } @@ -84,16 +85,18 @@ public class TimeLineModule { @Override public void propertyChange(PropertyChangeEvent evt) { try { - TimeLineController tlController = getController(); - tlController.handleCaseEvent(evt); + getController().handleCaseEvent(evt); } catch (NoCurrentCaseException ex) { // ignore - return; + + } catch (TskCoreException ex) { + MessageNotifyUtil.Message.error("Error creating timeline controller."); + logger.log(Level.SEVERE, "Error creating timeline controller", ex); } if (Case.Events.valueOf(evt.getPropertyName()).equals(CURRENT_CASE)) { // we care only about case closing here - if (evt.getNewValue() == null) { + if (evt.getNewValue() == null) { synchronized (controllerLock) { if (controller != null) { controller.shutDownTimeLine(); @@ -113,11 +116,12 @@ public class TimeLineModule { @Override public void propertyChange(PropertyChangeEvent evt) { try { - TimeLineController tlController = getController(); - tlController.handleIngestModuleEvent(evt); + getController().handleIngestModuleEvent(evt); } catch (NoCurrentCaseException ex) { // ignore - return; + } catch (TskCoreException ex) { + MessageNotifyUtil.Message.error("Error creating timeline controller."); + logger.log(Level.SEVERE, "Error creating timeline controller", ex); } } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java index a46d228c8d..845e473500 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java @@ -20,8 +20,11 @@ package org.sleuthkit.autopsy.timeline.ui.filtering; import java.util.Arrays; import javafx.application.Platform; +import javafx.beans.InvalidationListener; import javafx.beans.Observable; import javafx.beans.binding.Bindings; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableMap; import javafx.fxml.FXML; @@ -119,11 +122,18 @@ final public class FilterSetPanel extends BorderPane { expansionMap.put(controller.getEventsModel().getFilterState().getFilter(), true); expansionMap.put(controller.getEventsModel().getFilterState().getEventTypeFilterState().getFilter(), true); - this.filteredEvents.eventTypeZoomProperty().addListener((Observable observable) -> applyFilters()); - this.filteredEvents.descriptionLODProperty().addListener((Observable observable1) -> applyFilters()); - this.filteredEvents.timeRangeProperty().addListener((Observable observable2) -> applyFilters()); + InvalidationListener applyFiltersListener = observable -> applyFilters(); - this.filteredEvents.filterProperty().addListener((observable, oldValue, newValue) -> refresh()); + this.filteredEvents.eventTypeZoomProperty().addListener(applyFiltersListener); + this.filteredEvents.descriptionLODProperty().addListener(applyFiltersListener); + this.filteredEvents.timeRangeProperty().addListener(applyFiltersListener); + + this.filteredEvents.filterProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, RootFilterState oldValue, RootFilterState newValue) { + refresh(); + } + }); refresh(); hiddenDescriptionsListView.setItems(controller.getQuickHideFilters()); @@ -163,8 +173,9 @@ final public class FilterSetPanel extends BorderPane { } private void refresh() { + FilterTreeItem filterTreeItem = new FilterTreeItem(filteredEvents.getFilterState(), expansionMap); Platform.runLater(() -> { - filterTreeTable.setRoot(new FilterTreeItem(filteredEvents.getFilterState().copyOf(), expansionMap)); + filterTreeTable.setRoot(filterTreeItem); }); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterTreeItem.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterTreeItem.java index 2a5d69b3d3..a30c52d690 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterTreeItem.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterTreeItem.java @@ -74,12 +74,9 @@ final public class FilterTreeItem extends TreeItem> { } }); - compoundFilter.selectedProperty().addListener(new InvalidationListener() { - @Override - public void invalidated(Observable observable) { - if (compoundFilter.isSelected()) { - setExpanded(true); - } + compoundFilter.selectedProperty().addListener( observable -> { + if (compoundFilter.isSelected()) { + setExpanded(true); } }); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java index 13f86afec2..ead4199fee 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.timeline.ui.filtering.datamodel; +import com.google.common.collect.Lists; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; @@ -27,9 +28,9 @@ import javafx.collections.ObservableList; import org.sleuthkit.datamodel.timeline.TimelineFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.CompoundFilter; -class CompoundFilterStateImpl> - extends DefaultFilterState - implements CompoundFilterState { +class CompoundFilterStateImpl> + extends DefaultFilterState + implements CompoundFilterState { private final ObservableList> subFilterStates = FXCollections.observableArrayList(); @@ -39,12 +40,12 @@ class CompoundFilterStateImpl change) -> { while (change.next()) { - change.getAddedSubList().forEach(CompoundFilterStateImpl.this::addSubFilterState); + change.getAddedSubList().forEach(CompoundFilterStateImpl.this::addStateForSubFilter); } }); @@ -60,7 +61,7 @@ class CompoundFilterStateImpl> subFilterStates) { + CompoundFilterStateImpl(FilterType filter, Collection> subFilterStates) { super(filter); subFilterStates.forEach(this::addSubFilterState); @@ -77,7 +78,7 @@ class CompoundFilterStateImpl { if (isSelected() && getSubFilterStates().stream().noneMatch(FilterState::isSelected)) { - getSubFilterStates().forEach(subFilterState -> subFilterState.setSelected(true)); + subFilterStates.forEach(subFilterState -> subFilterState.setSelected(true)); } }); @@ -91,27 +92,24 @@ class CompoundFilterStateImpl subFilter : getSubFilterStates()) { - subFilter.setDisabled(inactive); - } + subFilterStates.forEach(subFilterState -> subFilterState.setDisabled(inactive)); } @SuppressWarnings("unchecked") - private > void addSubFilterState(SubFilterType subFilter) { + private void addStateForSubFilter(SubFilterType subFilter) { if (subFilter instanceof CompoundFilter) { - addSubFilterState((FilterState) new CompoundFilterStateImpl<>((S) subFilter)); + addSubFilterState((FilterState) new CompoundFilterStateImpl<>((CompoundFilter) subFilter)); } else { addSubFilterState(new DefaultFilterState<>(subFilter)); } - } - private void addSubFilterState(FilterState newFilterModel) { - getSubFilterStates().add(newFilterModel); - newFilterModel.selectedProperty().addListener(selectedProperty -> { - //set this compound filter model selected af any of the subfilters are selected. - setSelected(getSubFilterStates().stream().anyMatch(FilterState::isSelected)); + private void addSubFilterState(FilterState newSubFilterState) { + subFilterStates.add(newSubFilterState); + newSubFilterState.selectedProperty().addListener(selectedProperty -> { + //set this compound filter state selected af any of the subfilters are selected. + setSelected(subFilterStates.stream().anyMatch(FilterState::isSelected)); }); } @@ -121,12 +119,12 @@ class CompoundFilterStateImpl copyOf() { + public CompoundFilterStateImpl copyOf() { @SuppressWarnings("unchecked") - CompoundFilterStateImpl copy = new CompoundFilterStateImpl<>((C) getFilter().copyOf(), - getSubFilterStates().stream().map(FilterState::copyOf).collect(Collectors.toList()) - ); + CompoundFilterStateImpl copy + = new CompoundFilterStateImpl<>((FilterType) getFilter().copyOf(), + Lists.transform(subFilterStates, FilterState::copyOf)); copy.setSelected(isSelected()); copy.setDisabled(isDisabled()); @@ -135,16 +133,16 @@ class CompoundFilterStateImpl activeSubFilters = getSubFilterStates().stream() + List activeSubFilters = subFilterStates.stream() .filter(FilterState::isActive) .map(FilterState::getActiveFilter) .collect(Collectors.toList()); - C copy = (C) getFilter().copyOf(); + FilterType copy = (FilterType) getFilter().copyOf(); copy.getSubFilters().clear(); copy.getSubFilters().addAll(activeSubFilters); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java index 024b80776a..3f6a468a03 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java @@ -22,7 +22,6 @@ import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyBooleanWrapper; import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import org.python.google.common.collect.Lists; import org.sleuthkit.datamodel.timeline.TimelineFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.DataSourceFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.DataSourcesFilter; @@ -194,10 +193,23 @@ public class RootFilterState implements FilterState, CompoundFilterS if (getClass() != obj.getClass()) { return false; } - RootFilter activeFilter = getActiveFilter(); - RootFilter activeFilter1 = ((RootFilterState) obj).getActiveFilter(); - return activeFilter.equals(activeFilter1); + RootFilterState otherFilterState = (RootFilterState) obj; + + RootFilter activeFilter = getActiveFilter(); + RootFilter activeFilter1 = otherFilterState.getActiveFilter(); + + if (false == activeFilter.equals(activeFilter1)) { + return false; + } + + RootFilter filter = getFilter(); + RootFilter filter1 = otherFilterState.getFilter(); + + if (false == filter.equals(filter1)) { + return false; + } + return true; } @Override diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/TagsFilterState.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/TagsFilterState.java index eceb4eefd6..c4be631ccf 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/TagsFilterState.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/TagsFilterState.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.timeline.ui.filtering.datamodel; +import com.google.common.collect.Lists; import java.util.Collection; import java.util.Set; import java.util.stream.Collectors; @@ -51,10 +52,8 @@ public class TagsFilterState extends CompoundFilterStateImpl Date: Wed, 31 Oct 2018 15:19:42 +0100 Subject: [PATCH 02/11] pass arround copies in the right places some cleanup --- .../autopsy/timeline/FilteredEventsModel.java | 7 ++++--- .../autopsy/timeline/TimeLineController.java | 4 ++-- .../autopsy/timeline/TimeLineModule.java | 3 ++- .../timeline/ui/filtering/FilterSetPanel.java | 16 ++++++++-------- .../timeline/ui/filtering/FilterTreeItem.java | 2 -- .../ui/filtering/datamodel/RootFilterState.java | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java index ea6cb00b04..cb5dc7f628 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java @@ -161,7 +161,7 @@ public final class FilteredEventsModel { datasourcesMap.addListener((MapChangeListener.Change change) -> { DataSourceFilter dataSourceFilter = new DataSourceFilter(change.getValueAdded().getName(), change.getKey()); - RootFilterState rootFilter = filterProperty().get(); + RootFilterState rootFilter = filterProperty().get().copyOf(); rootFilter.getDataSourcesFilterState().getFilter().addSubFilter(dataSourceFilter); requestedFilter.set(rootFilter); }); @@ -184,7 +184,7 @@ public final class FilteredEventsModel { if (zoomState != null) { synchronized (FilteredEventsModel.this) { requestedTypeZoom.set(zoomState.getTypeZoomLevel()); - requestedFilter.set(zoomState.getFilterState().copyOf()); + requestedFilter.set(zoomState.getFilterState()); requestedTimeRange.set(zoomState.getTimeRange()); requestedLOD.set(zoomState.getDescriptionLOD()); } @@ -251,9 +251,10 @@ public final class FilteredEventsModel { synchronized private void populateFilterData() throws TskCoreException { SleuthkitCase skCase = autoCase.getSleuthkitCase(); hashSets.addAll(eventManager.getHashSetNames()); + Set dataSourceIDs = eventManager.getDataSourceIDs(); //because there is no way to remove a datasource we only add to this map. - for (Long id : eventManager.getDataSourceIDs()) { + for (Long id : dataSourceIDs) { try { if (datasourcesMap.containsKey(id) == false) { datasourcesMap.put(id, skCase.getDataSource(id)); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index b4a0522d51..7da596dd90 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -572,9 +572,9 @@ public class TimeLineController { synchronized public void pushFilters(RootFilterState filter) { ZoomState currentZoom = filteredEvents.zoomStateProperty().get(); if (currentZoom == null) { - advance(InitialZoomState.withFilterState(filter.copyOf())); + advance(InitialZoomState.withFilterState(filter)); } else if (currentZoom.hasFilterState(filter) == false) { - advance(currentZoom.withFilterState(filter.copyOf())); + advance(currentZoom.withFilterState(filter)); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineModule.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineModule.java index cf65c87fed..9421dadcaf 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineModule.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineModule.java @@ -22,6 +22,7 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.logging.Level; import javafx.application.Platform; +import javax.swing.SwingUtilities; import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; @@ -99,7 +100,7 @@ public class TimeLineModule { if (evt.getNewValue() == null) { synchronized (controllerLock) { if (controller != null) { - controller.shutDownTimeLine(); + SwingUtilities.invokeLater(controller::shutDownTimeLine); } controller = null; } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java index 845e473500..b1605abd7c 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java @@ -124,14 +124,14 @@ final public class FilterSetPanel extends BorderPane { InvalidationListener applyFiltersListener = observable -> applyFilters(); - this.filteredEvents.eventTypeZoomProperty().addListener(applyFiltersListener); - this.filteredEvents.descriptionLODProperty().addListener(applyFiltersListener); - this.filteredEvents.timeRangeProperty().addListener(applyFiltersListener); + filteredEvents.eventTypeZoomProperty().addListener(applyFiltersListener); + filteredEvents.descriptionLODProperty().addListener(applyFiltersListener); + filteredEvents.timeRangeProperty().addListener(applyFiltersListener); - this.filteredEvents.filterProperty().addListener(new ChangeListener() { + filteredEvents.filterProperty().addListener(new InvalidationListener() { @Override - public void changed(ObservableValue observable, RootFilterState oldValue, RootFilterState newValue) { - refresh(); + public void invalidated(Observable observable) { + refresh(); } }); refresh(); @@ -173,7 +173,7 @@ final public class FilterSetPanel extends BorderPane { } private void refresh() { - FilterTreeItem filterTreeItem = new FilterTreeItem(filteredEvents.getFilterState(), expansionMap); + FilterTreeItem filterTreeItem = new FilterTreeItem(filteredEvents.getFilterState().copyOf(), expansionMap); Platform.runLater(() -> { filterTreeTable.setRoot(filterTreeItem); }); @@ -181,7 +181,7 @@ final public class FilterSetPanel extends BorderPane { private void applyFilters() { Platform.runLater(() -> { - controller.pushFilters(((RootFilterState) filterTreeTable.getRoot().getValue())); + controller.pushFilters(((RootFilterState) filterTreeTable.getRoot().getValue().copyOf())); }); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterTreeItem.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterTreeItem.java index a30c52d690..5fe5d6a210 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterTreeItem.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterTreeItem.java @@ -18,8 +18,6 @@ */ package org.sleuthkit.autopsy.timeline.ui.filtering; -import javafx.beans.InvalidationListener; -import javafx.beans.Observable; import javafx.collections.ListChangeListener; import javafx.collections.ObservableMap; import javafx.scene.control.TreeItem; diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java index 3f6a468a03..3f50a39615 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java @@ -36,7 +36,7 @@ import org.sleuthkit.datamodel.timeline.TimelineFilter.TextFilter; /** */ -public class RootFilterState implements FilterState, CompoundFilterState< TimelineFilter, RootFilter> { +public class RootFilterState implements CompoundFilterState< TimelineFilter, RootFilter> { private final CompoundFilterState eventTypeFilterState; private final DefaultFilterState knownFilterState; From b108c861955df45cf0067d93c8e5b75a302a80f1 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Thu, 1 Nov 2018 09:06:16 +0100 Subject: [PATCH 03/11] copies now make new state for new subfilters --- .../datamodel/CompoundFilterStateImpl.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java index ead4199fee..ebcc5b6426 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java @@ -43,11 +43,6 @@ class CompoundFilterStateImpl change) -> { - while (change.next()) { - change.getAddedSubList().forEach(CompoundFilterStateImpl.this::addStateForSubFilter); - } - }); configureListeners(); } @@ -69,6 +64,13 @@ class CompoundFilterStateImpl change) -> { + while (change.next()) { + change.getAddedSubList().forEach(this::addStateForSubFilter); + } + }); + /* * enforce the following relationship between a compound filter and its * subfilters: if a compound filter's active property changes, disable @@ -81,7 +83,6 @@ class CompoundFilterStateImpl subFilterState.setSelected(true)); } }); - } /** @@ -97,7 +98,6 @@ class CompoundFilterStateImpl) { addSubFilterState((FilterState) new CompoundFilterStateImpl<>((CompoundFilter) subFilter)); } else { @@ -120,7 +120,6 @@ class CompoundFilterStateImpl copyOf() { - @SuppressWarnings("unchecked") CompoundFilterStateImpl copy = new CompoundFilterStateImpl<>((FilterType) getFilter().copyOf(), From e9bb6ed8f10f45c4bea993b78f3c62c91304870a Mon Sep 17 00:00:00 2001 From: millmanorama Date: Thu, 1 Nov 2018 12:04:30 +0100 Subject: [PATCH 04/11] sync all filters with data from case --- .../autopsy/timeline/FilteredEventsModel.java | 49 ++++++++++--------- .../autopsy/timeline/TimeLineController.java | 2 +- .../timeline/ui/filtering/FilterSetPanel.java | 18 +++---- 3 files changed, 32 insertions(+), 37 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java index cb5dc7f628..de69ad585b 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java @@ -32,16 +32,13 @@ import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.logging.Level; -import javafx.beans.Observable; +import javafx.beans.InvalidationListener; import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.collections.FXCollections; -import javafx.collections.ListChangeListener; -import javafx.collections.MapChangeListener; import javafx.collections.ObservableList; import javafx.collections.ObservableMap; import javafx.collections.ObservableSet; -import javafx.collections.SetChangeListener; import static org.apache.commons.lang3.StringUtils.isBlank; import org.joda.time.DateTimeZone; import org.joda.time.Interval; @@ -159,26 +156,19 @@ public final class FilteredEventsModel { minCache = CacheBuilder.newBuilder() .build(new CacheLoaderImpl<>(ignored -> eventManager.getMinTime())); - datasourcesMap.addListener((MapChangeListener.Change change) -> { - DataSourceFilter dataSourceFilter = new DataSourceFilter(change.getValueAdded().getName(), change.getKey()); - RootFilterState rootFilter = filterProperty().get().copyOf(); - rootFilter.getDataSourcesFilterState().getFilter().addSubFilter(dataSourceFilter); - requestedFilter.set(rootFilter); - }); - hashSets.addListener((SetChangeListener.Change< ? extends String> change) -> { - HashSetFilter hashSetFilter = new HashSetFilter(change.getElementAdded()); + InvalidationListener filterSyncListener = observable -> { RootFilterState rootFilter = filterProperty().get(); - rootFilter.getHashHitsFilterState().getFilter().addSubFilter(hashSetFilter); + syncFilters(rootFilter); requestedFilter.set(rootFilter); - }); - tagNames.addListener((ListChangeListener.Change change) -> { - RootFilterState rootFilter = filterProperty().get(); - syncTagsFilter(rootFilter); - requestedFilter.set(rootFilter); - }); + }; + + datasourcesMap.addListener(filterSyncListener); + getHashSets().addListener(filterSyncListener); + getTagNames().addListener(filterSyncListener); + requestedFilter.set(getDefaultFilter()); - requestedZoomState.addListener((Observable observable) -> { + requestedZoomState.addListener(observable -> { final ZoomState zoomState = requestedZoomState.get(); if (zoomState != null) { @@ -269,9 +259,10 @@ public final class FilteredEventsModel { } /** - * "sync" the given tags filter with the tagnames in use: Disable filters - * for tags that are not in use in the case, and add new filters for tags - * that don't have them. New filters are selected by default. + * "sync" the given root filter with the state of the casee: Disable filters + * for tags that are not in use in the case, and add new filters for tags, + * hashsets, and datasources. that don't have them. New filters are selected + * by default. * * @param rootFilterState the filter state to modify so it is consistent * with the tags in use in the case @@ -281,7 +272,17 @@ public final class FilteredEventsModel { rootFilterState.getTagsFilterState().getFilter().addSubFilter(new TagNameFilter(tagName)); }); for (FilterState filterState : rootFilterState.getTagsFilterState().getSubFilterStates()) { - filterState.setDisabled(tagNames.contains(filterState.getFilter().getTagName()) == false); + tagFilterState.setDisabled(tagNames.contains(tagFilterState.getFilter().getTagName()) == false); + } + + DataSourcesFilter dataSourcesFilter = rootFilter.getDataSourcesFilterState().getFilter(); + for (Map.Entry entry : datasourcesMap.entrySet()) { + dataSourcesFilter.addSubFilter(new DataSourceFilter(entry.getValue().getName(), entry.getKey())); + } + + HashHitsFilter hashSetsFilter = rootFilter.getHashHitsFilterState().getFilter(); + for (String hashSet : getHashSets()) { + hashSetsFilter.addSubFilter(new HashSetFilter(hashSet)); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index 7da596dd90..d0f1ac3fcd 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -285,7 +285,7 @@ public class TimeLineController { */ historyManager.currentState().addListener(( observable, oldState, newState) -> { ZoomState historyManagerState = newState; - filteredEvents.syncTagsFilter(historyManagerState.getFilterState()); + filteredEvents.syncFilters(historyManagerState.getFilterState()); currentParams.set(historyManagerState); }); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java index b1605abd7c..84bd48dbc2 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java @@ -21,10 +21,8 @@ package org.sleuthkit.autopsy.timeline.ui.filtering; import java.util.Arrays; import javafx.application.Platform; import javafx.beans.InvalidationListener; -import javafx.beans.Observable; import javafx.beans.binding.Bindings; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; +import javafx.beans.binding.BooleanBinding; import javafx.collections.FXCollections; import javafx.collections.ObservableMap; import javafx.fxml.FXML; @@ -46,6 +44,7 @@ import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.actions.ResetFilters; +import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.CompoundFilterState; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState; import org.sleuthkit.datamodel.timeline.TimelineFilter; @@ -119,8 +118,8 @@ final public class FilterSetPanel extends BorderPane { legendColumn.setCellFactory(col -> new LegendCell(this.controller)); //type is the only filter expanded initialy - expansionMap.put(controller.getEventsModel().getFilterState().getFilter(), true); - expansionMap.put(controller.getEventsModel().getFilterState().getEventTypeFilterState().getFilter(), true); + expansionMap.put(filteredEvents.getFilterState().getFilter(), true); + expansionMap.put(filteredEvents.getFilterState().getEventTypeFilterState().getFilter(), true); InvalidationListener applyFiltersListener = observable -> applyFilters(); @@ -128,12 +127,7 @@ final public class FilterSetPanel extends BorderPane { filteredEvents.descriptionLODProperty().addListener(applyFiltersListener); filteredEvents.timeRangeProperty().addListener(applyFiltersListener); - filteredEvents.filterProperty().addListener(new InvalidationListener() { - @Override - public void invalidated(Observable observable) { - refresh(); - } - }); + filteredEvents.filterProperty().addListener(observable -> refresh()); refresh(); hiddenDescriptionsListView.setItems(controller.getQuickHideFilters()); @@ -173,7 +167,7 @@ final public class FilterSetPanel extends BorderPane { } private void refresh() { - FilterTreeItem filterTreeItem = new FilterTreeItem(filteredEvents.getFilterState().copyOf(), expansionMap); + FilterTreeItem filterTreeItem = new FilterTreeItem(filteredEvents.filterProperty().getValue(), expansionMap); Platform.runLater(() -> { filterTreeTable.setRoot(filterTreeItem); }); From 05fe1c16c41b6a799dec011244a162d868200bb5 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 2 Nov 2018 10:00:48 +0100 Subject: [PATCH 05/11] fixes for codacy issues --- .../autopsy/timeline/FilteredEventsModel.java | 4 ++-- .../autopsy/timeline/TimeLineController.java | 8 +++----- .../timeline/ui/filtering/FilterSetPanel.java | 2 -- .../datamodel/CompoundFilterStateImpl.java | 8 ++++++++ .../ui/filtering/datamodel/RootFilterState.java | 13 +++++-------- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java index de69ad585b..52cb0c51a6 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java @@ -246,7 +246,7 @@ public final class FilteredEventsModel { //because there is no way to remove a datasource we only add to this map. for (Long id : dataSourceIDs) { try { - if (datasourcesMap.containsKey(id) == false) { + if (datasourcesMap.get(id) == null) { datasourcesMap.put(id, skCase.getDataSource(id)); } } catch (TskDataException ex) { @@ -663,7 +663,7 @@ public final class FilteredEventsModel { return updatedEventIDs; } - synchronized void invalidateAllCaches() throws TskCoreException { + synchronized protected void invalidateAllCaches() throws TskCoreException { minCache.invalidateAll(); maxCache.invalidateAll(); idToEventCache.invalidateAll(); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index d0f1ac3fcd..ebb16cef85 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -40,7 +40,6 @@ import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.beans.property.ReadOnlyStringProperty; import javafx.beans.property.ReadOnlyStringWrapper; -import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.collections.ObservableSet; @@ -56,7 +55,6 @@ import org.joda.time.Interval; import org.joda.time.ReadablePeriod; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; @@ -76,8 +74,10 @@ import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.autopsy.timeline.events.ViewInTimelineRequestedEvent; import org.sleuthkit.autopsy.timeline.ui.detailview.datamodel.DetailViewEvent; +import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState; import org.sleuthkit.autopsy.timeline.utils.IntervalUtils; +import org.sleuthkit.autopsy.timeline.zooming.TimeUnits; import org.sleuthkit.autopsy.timeline.zooming.ZoomState; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -85,8 +85,6 @@ import org.sleuthkit.datamodel.DescriptionLoD; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.timeline.EventType; import org.sleuthkit.datamodel.timeline.EventTypeZoomLevel; -import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState; -import org.sleuthkit.autopsy.timeline.zooming.TimeUnits; import org.sleuthkit.datamodel.timeline.TimelineFilter.DescriptionFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.EventTypeFilter; @@ -283,7 +281,7 @@ public class TimeLineController { * TimeLineController. Do we need to do this with datasource or hash hit * filters? */ - historyManager.currentState().addListener(( observable, oldState, newState) -> { + historyManager.currentState().addListener((observable, oldState, newState) -> { ZoomState historyManagerState = newState; filteredEvents.syncFilters(historyManagerState.getFilterState()); currentParams.set(historyManagerState); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java index 84bd48dbc2..0fe559d19a 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java @@ -22,7 +22,6 @@ import java.util.Arrays; import javafx.application.Platform; import javafx.beans.InvalidationListener; import javafx.beans.binding.Bindings; -import javafx.beans.binding.BooleanBinding; import javafx.collections.FXCollections; import javafx.collections.ObservableMap; import javafx.fxml.FXML; @@ -44,7 +43,6 @@ import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.actions.ResetFilters; -import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.CompoundFilterState; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState; import org.sleuthkit.datamodel.timeline.TimelineFilter; diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java index ebcc5b6426..26674d03a3 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java @@ -28,6 +28,14 @@ import javafx.collections.ObservableList; import org.sleuthkit.datamodel.timeline.TimelineFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.CompoundFilter; +/** + * + * Defualt implementation of CompoundFilterState + * + * @param The type of the subfilters in the underlying + * CompoundFilter + * @param The type of the underlying CompoundFilter + */ class CompoundFilterStateImpl> extends DefaultFilterState implements CompoundFilterState { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java index 3f50a39615..06ddb935f9 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java @@ -22,6 +22,8 @@ import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyBooleanWrapper; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import org.apache.commons.lang3.ObjectUtils; +import static org.apache.commons.lang3.ObjectUtils.notEqual; import org.sleuthkit.datamodel.timeline.TimelineFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.DataSourceFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.DataSourcesFilter; @@ -199,24 +201,19 @@ public class RootFilterState implements CompoundFilterState< TimelineFilter, Roo RootFilter activeFilter = getActiveFilter(); RootFilter activeFilter1 = otherFilterState.getActiveFilter(); - if (false == activeFilter.equals(activeFilter1)) { + if (notEqual(activeFilter, activeFilter1)) { return false; } RootFilter filter = getFilter(); RootFilter filter1 = otherFilterState.getFilter(); - if (false == filter.equals(filter1)) { - return false; - } - return true; + return filter.equals(filter1); } @Override public int hashCode() { - int hash = 7; - - return hash; + return 7; } @Override From 200ed6552651f78d5083c432eb9cf440b89a6073 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 2 Nov 2018 12:18:29 +0100 Subject: [PATCH 06/11] invalidate caches and so check for new datasoruces for the filters on DATA_SOURCE_ADDED --- .../autopsy/timeline/TimeLineController.java | 22 ++++++++----------- .../filtering/datamodel/RootFilterState.java | 1 + 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index ebb16cef85..66ecef6bf9 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -91,15 +91,14 @@ import org.sleuthkit.datamodel.timeline.TimelineFilter.EventTypeFilter; /** * Controller in the MVC design along with FilteredEventsModel TimeLineView. * Forwards interpreted user gestures form views to model. Provides model to - * view. Is entry point for timeline module. + * view. * * Concurrency Policy:
      *
    • Since filteredEvents is internally synchronized, only compound access to * it needs external synchronization
    • - * *
    • Since eventsRepository is internally synchronized, only compound - * access to it needs external synchronization
    • - *
    • Other state including mainFrame, viewMode, and the listeners should only - * be accessed with this object's intrinsic lock held, or on the EDT as + * + *
    • Other state including topComponent, viewMode, and the listeners should + * only be accessed with this object's intrinsic lock held, or on the EDT as * indicated. *
    • *
    @@ -129,16 +128,12 @@ public class TimeLineController { } private final ExecutorService executor = Executors.newSingleThreadExecutor(); - private final ReadOnlyListWrapper> tasks = new ReadOnlyListWrapper<>(FXCollections.observableArrayList()); - private final ReadOnlyDoubleWrapper taskProgress = new ReadOnlyDoubleWrapper(-1); - private final ReadOnlyStringWrapper taskMessage = new ReadOnlyStringWrapper(); - private final ReadOnlyStringWrapper taskTitle = new ReadOnlyStringWrapper(); - private final ReadOnlyStringWrapper statusMessage = new ReadOnlyStringWrapper(); + private final EventBus eventbus = new EventBus("TimeLineController_EventBus"); /** @@ -750,6 +745,7 @@ public class TimeLineController { } } + @SuppressWarnings("fallthrough") void handleCaseEvent(PropertyChangeEvent evt) { switch (Case.Events.valueOf(evt.getPropertyName())) { case BLACKBOARD_ARTIFACT_TAG_ADDED: @@ -764,9 +760,6 @@ public class TimeLineController { case CONTENT_TAG_DELETED: executor.submit(() -> filteredEvents.handleContentTagDeleted((ContentTagDeletedEvent) evt)); break; - case DATA_SOURCE_ADDED: - executor.submit(() -> filteredEvents.postAutopsyEventLocally((AutopsyEvent) evt)); - break; case CURRENT_CASE: //close timeline on case changes. SwingUtilities.invokeLater(TimeLineController.this::shutDownTimeLine); @@ -780,6 +773,9 @@ public class TimeLineController { logger.log(Level.SEVERE, "Error invalidating timeline caches.", ex); } }); + //intentional fall through + case DATA_SOURCE_ADDED: + filteredEvents.postAutopsyEventLocally((AutopsyEvent) evt); break; } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java index 06ddb935f9..9eb774dd73 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.timeline.ui.filtering.datamodel; +import com.google.common.collect.Lists; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyBooleanWrapper; import javafx.collections.FXCollections; From 8503d8b756ee4a71628b49e3d7f783369981ae03 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 7 Nov 2018 12:27:03 +0100 Subject: [PATCH 07/11] log Exceptions from executor thread. correctly populate filters for new datasources. refactor cache invalidation some. --- .../autopsy/timeline/FilteredEventsModel.java | 25 +++++--- .../autopsy/timeline/TimeLineController.java | 61 +++++++++++++------ .../timeline/events/EventAddedEvent.java | 8 +++ 3 files changed, 67 insertions(+), 27 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java index 52cb0c51a6..66acf01a85 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java @@ -28,6 +28,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -39,7 +40,8 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.collections.ObservableMap; import javafx.collections.ObservableSet; -import static org.apache.commons.lang3.StringUtils.isBlank; +import org.apache.commons.collections4.CollectionUtils; +import static org.apache.commons.collections4.CollectionUtils.emptyIfNull; import org.joda.time.DateTimeZone; import org.joda.time.Interval; import org.openide.util.NbBundle; @@ -663,15 +665,22 @@ public final class FilteredEventsModel { return updatedEventIDs; } - synchronized protected void invalidateAllCaches() throws TskCoreException { + /** + * Invalidate the timeline caches for the given event IDs. Also forces the + * filter values to be updated with any new values from the case data.( data + * sources, tags, etc) + * + * @param updatedEventIDs A collection of the event IDs whose cached event + * objects should be invalidated. Can be null or an + * empty sett to invalidate the general caches, such + * as min/max time, or the counts per event type. + * + * @throws TskCoreException + */ + public synchronized void invalidateCaches(Collection updatedEventIDs) throws TskCoreException { minCache.invalidateAll(); maxCache.invalidateAll(); - idToEventCache.invalidateAll(); - invalidateCaches(Collections.emptyList()); - } - - synchronized private void invalidateCaches(Collection updatedEventIDs) throws TskCoreException { - idToEventCache.invalidateAll(updatedEventIDs); + idToEventCache.invalidateAll(emptyIfNull(updatedEventIDs)); eventCountsCache.invalidateAll(); populateFilterData(); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index 66ecef6bf9..ad20e06185 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -19,14 +19,18 @@ package org.sleuthkit.autopsy.timeline; import com.google.common.eventbus.EventBus; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import java.beans.PropertyChangeEvent; import java.time.ZoneId; import java.util.Collection; import java.util.Collections; +import static java.util.Collections.singleton; import java.util.Optional; import java.util.TimeZone; import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Level; import javafx.application.Platform; @@ -72,6 +76,7 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; +import org.sleuthkit.autopsy.timeline.events.EventAddedEvent; import org.sleuthkit.autopsy.timeline.events.ViewInTimelineRequestedEvent; import org.sleuthkit.autopsy.timeline.ui.detailview.datamodel.DetailViewEvent; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState; @@ -127,7 +132,7 @@ public class TimeLineController { return timeZone.getReadOnlyProperty(); } - private final ExecutorService executor = Executors.newSingleThreadExecutor(); + private final ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); private final ReadOnlyListWrapper> tasks = new ReadOnlyListWrapper<>(FXCollections.observableArrayList()); private final ReadOnlyDoubleWrapper taskProgress = new ReadOnlyDoubleWrapper(-1); private final ReadOnlyStringWrapper taskMessage = new ReadOnlyStringWrapper(); @@ -659,6 +664,7 @@ public class TimeLineController { taskTitle.bind(task.titleProperty()); switch (task.getState()) { case READY: + //TODO: Check future result for errors.... executor.submit(task); break; case SCHEDULED: @@ -729,7 +735,9 @@ public class TimeLineController { case DATA_ADDED: ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue(); if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) { - executor.submit(() -> filteredEvents.setHashHit(eventData.getArtifacts(), true)); + logFutureException(executor.submit(() -> filteredEvents.setHashHit(eventData.getArtifacts(), true)), + "Error executing task in response to DATA_ADDED event.", + "Error executing response to new data."); } break; case FILE_DONE: @@ -745,38 +753,53 @@ public class TimeLineController { } } - @SuppressWarnings("fallthrough") void handleCaseEvent(PropertyChangeEvent evt) { + + ListenableFuture future = Futures.immediateFuture(null); switch (Case.Events.valueOf(evt.getPropertyName())) { case BLACKBOARD_ARTIFACT_TAG_ADDED: - executor.submit(() -> filteredEvents.handleArtifactTagAdded((BlackBoardArtifactTagAddedEvent) evt)); + future = executor.submit(() -> filteredEvents.handleArtifactTagAdded((BlackBoardArtifactTagAddedEvent) evt)); break; case BLACKBOARD_ARTIFACT_TAG_DELETED: - executor.submit(() -> filteredEvents.handleArtifactTagDeleted((BlackBoardArtifactTagDeletedEvent) evt)); + future = executor.submit(() -> filteredEvents.handleArtifactTagDeleted((BlackBoardArtifactTagDeletedEvent) evt)); break; case CONTENT_TAG_ADDED: - executor.submit(() -> filteredEvents.handleContentTagAdded((ContentTagAddedEvent) evt)); + future = executor.submit(() -> filteredEvents.handleContentTagAdded((ContentTagAddedEvent) evt)); break; case CONTENT_TAG_DELETED: - executor.submit(() -> filteredEvents.handleContentTagDeleted((ContentTagDeletedEvent) evt)); + future = executor.submit(() -> filteredEvents.handleContentTagDeleted((ContentTagDeletedEvent) evt)); break; case CURRENT_CASE: //close timeline on case changes. SwingUtilities.invokeLater(TimeLineController.this::shutDownTimeLine); break; - case EVENT_ADDED: - executor.submit(() -> { - try { - filteredEvents.invalidateAllCaches(); - } catch (TskCoreException ex) { - MessageNotifyUtil.Message.error("Error invalidating timeline caches."); - logger.log(Level.SEVERE, "Error invalidating timeline caches.", ex); - } - }); - //intentional fall through case DATA_SOURCE_ADDED: - filteredEvents.postAutopsyEventLocally((AutopsyEvent) evt); + future = executor.submit(() -> { + filteredEvents.invalidateCaches(null); + return null; + }); + break; + case EVENT_ADDED: + future = executor.submit(() -> { + filteredEvents.invalidateCaches(singleton(((EventAddedEvent) evt).getEventID())); + return null; + + }); break; } + logFutureException(future, + "Error executing task in response to " + evt.getPropertyName() + " event.", + "Error executing task in response to case event."); + } + + private void logFutureException(ListenableFuture future, String errorLogMessage, String errorUserMessage) { + future.addListener(() -> { + try { + future.get(); + } catch (InterruptedException | ExecutionException ex) { + logger.log(Level.SEVERE, errorLogMessage, ex); + MessageNotifyUtil.Message.error(errorUserMessage); + } + }, MoreExecutors.directExecutor()); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/events/EventAddedEvent.java b/Core/src/org/sleuthkit/autopsy/timeline/events/EventAddedEvent.java index 71492565a4..3ec593464e 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/events/EventAddedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/events/EventAddedEvent.java @@ -26,7 +26,15 @@ import org.sleuthkit.autopsy.events.AutopsyEvent; */ public class EventAddedEvent extends AutopsyEvent { + private final Long eventID; + + public EventAddedEvent(org.sleuthkit.datamodel.TimelineManager.EventAddedEvent event) { super(Case.Events.EVENT_ADDED.name(), null, event.getEvent()); + eventID = event.getEvent().getEventID(); + } + + public Long getEventID() { + return eventID; } } From 6198b1fcf382c07b128864e1931a19e50225806b Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 7 Nov 2018 12:27:51 +0100 Subject: [PATCH 08/11] new filters have the correct enabled/disabled state --- .../timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java index 26674d03a3..5806067a44 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java @@ -119,6 +119,7 @@ class CompoundFilterStateImpl Date: Wed, 7 Nov 2018 12:28:09 +0100 Subject: [PATCH 09/11] fix comments and method name --- Core/src/org/sleuthkit/autopsy/timeline/ui/ViewFrame.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/ViewFrame.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/ViewFrame.java index d87f3bbb8f..05ead27080 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/ViewFrame.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/ViewFrame.java @@ -511,13 +511,13 @@ final public class ViewFrame extends BorderPane { * NOTE: This ViewFrame must be registered with the filteredEventsModel's * EventBus in order for this handler to be invoked. * - * @param event The DataSourceAnalysisCompletedEvent to handle. + * @param event The CacheInvalidatedEvent to handle. */ @Subscribe @NbBundle.Messages({ "# {0} - datasource name", "ViewFrame.notification.analysisComplete=The event data has changed, the visualization may be out of date."}) - public void handleEventAdded(FilteredEventsModel.CacheInvalidatedEvent event) { + public void handleCacheInvalidated(FilteredEventsModel.CacheInvalidatedEvent event) { Platform.runLater(() -> { if (hostedView.needsRefresh() == false) { hostedView.setNeedsRefresh(); From 08be5da09ad0855679dbc5a2536df98a29af707b Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 7 Nov 2018 15:13:34 +0100 Subject: [PATCH 10/11] fix codacy issues --- .../autopsy/timeline/FilteredEventsModel.java | 2 -- .../autopsy/timeline/TimeLineController.java | 18 +++++++++--------- .../filtering/datamodel/RootFilterState.java | 1 - 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java index 66acf01a85..dfc367b44c 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java @@ -28,7 +28,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -40,7 +39,6 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.collections.ObservableMap; import javafx.collections.ObservableSet; -import org.apache.commons.collections4.CollectionUtils; import static org.apache.commons.collections4.CollectionUtils.emptyIfNull; import org.joda.time.DateTimeZone; import org.joda.time.Interval; diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index ad20e06185..7b002df581 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -116,6 +116,15 @@ public class TimeLineController { private static final ReadOnlyObjectWrapper timeZone = new ReadOnlyObjectWrapper<>(TimeZone.getDefault()); + private final ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); + private final ReadOnlyListWrapper> tasks = new ReadOnlyListWrapper<>(FXCollections.observableArrayList()); + private final ReadOnlyDoubleWrapper taskProgress = new ReadOnlyDoubleWrapper(-1); + private final ReadOnlyStringWrapper taskMessage = new ReadOnlyStringWrapper(); + private final ReadOnlyStringWrapper taskTitle = new ReadOnlyStringWrapper(); + private final ReadOnlyStringWrapper statusMessage = new ReadOnlyStringWrapper(); + + private final EventBus eventbus = new EventBus("TimeLineController_EventBus"); + public static ZoneId getTimeZoneID() { return timeZone.get().toZoneId(); } @@ -132,15 +141,6 @@ public class TimeLineController { return timeZone.getReadOnlyProperty(); } - private final ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); - private final ReadOnlyListWrapper> tasks = new ReadOnlyListWrapper<>(FXCollections.observableArrayList()); - private final ReadOnlyDoubleWrapper taskProgress = new ReadOnlyDoubleWrapper(-1); - private final ReadOnlyStringWrapper taskMessage = new ReadOnlyStringWrapper(); - private final ReadOnlyStringWrapper taskTitle = new ReadOnlyStringWrapper(); - private final ReadOnlyStringWrapper statusMessage = new ReadOnlyStringWrapper(); - - private final EventBus eventbus = new EventBus("TimeLineController_EventBus"); - /** * Status is a string that will be displayed in the status bar as a kind of * user hint/information when it is not empty diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java index 9eb774dd73..f99b0090e0 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java @@ -23,7 +23,6 @@ import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyBooleanWrapper; import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import org.apache.commons.lang3.ObjectUtils; import static org.apache.commons.lang3.ObjectUtils.notEqual; import org.sleuthkit.datamodel.timeline.TimelineFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.DataSourceFilter; From ea91542b89e6c1053e32b64978fede962125e700 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 9 Nov 2018 18:44:45 +0100 Subject: [PATCH 11/11] fix merge --- .../autopsy/timeline/FilteredEventsModel.java | 40 ++++++++++--------- .../autopsy/timeline/TimeLineController.java | 6 +-- .../autopsy/timeline/TimeLineModule.java | 4 +- .../timeline/events/EventAddedEvent.java | 13 +++--- .../timeline/ui/filtering/FilterSetPanel.java | 6 +-- .../datamodel/CompoundFilterStateImpl.java | 2 +- .../filtering/datamodel/RootFilterState.java | 2 +- 7 files changed, 37 insertions(+), 36 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java index dfc367b44c..2796255350 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java @@ -40,6 +40,7 @@ import javafx.collections.ObservableList; import javafx.collections.ObservableMap; import javafx.collections.ObservableSet; import static org.apache.commons.collections4.CollectionUtils.emptyIfNull; +import static org.apache.commons.lang3.StringUtils.isBlank; import org.joda.time.DateTimeZone; import org.joda.time.Interval; import org.openide.util.NbBundle; @@ -58,6 +59,7 @@ import org.sleuthkit.autopsy.timeline.events.TagsAddedEvent; import org.sleuthkit.autopsy.timeline.events.TagsDeletedEvent; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState; +import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.TagsFilterState; import org.sleuthkit.autopsy.timeline.utils.CacheLoaderImpl; import org.sleuthkit.autopsy.timeline.utils.CheckedFunction; import org.sleuthkit.autopsy.timeline.utils.FilterUtils; @@ -159,12 +161,12 @@ public final class FilteredEventsModel { InvalidationListener filterSyncListener = observable -> { RootFilterState rootFilter = filterProperty().get(); syncFilters(rootFilter); - requestedFilter.set(rootFilter); + requestedFilter.set(rootFilter.copyOf()); }; - + datasourcesMap.addListener(filterSyncListener); - getHashSets().addListener(filterSyncListener); - getTagNames().addListener(filterSyncListener); + hashSets.addListener(filterSyncListener); + tagNames.addListener(filterSyncListener); requestedFilter.set(getDefaultFilter()); @@ -242,7 +244,7 @@ public final class FilteredEventsModel { SleuthkitCase skCase = autoCase.getSleuthkitCase(); hashSets.addAll(eventManager.getHashSetNames()); Set dataSourceIDs = eventManager.getDataSourceIDs(); - + //because there is no way to remove a datasource we only add to this map. for (Long id : dataSourceIDs) { try { @@ -267,24 +269,24 @@ public final class FilteredEventsModel { * @param rootFilterState the filter state to modify so it is consistent * with the tags in use in the case */ - public void syncTagsFilter(RootFilterState rootFilterState) { - tagNames.forEach((tagName) -> { - rootFilterState.getTagsFilterState().getFilter().addSubFilter(new TagNameFilter(tagName)); - }); - for (FilterState filterState : rootFilterState.getTagsFilterState().getSubFilterStates()) { + public void syncFilters(RootFilterState rootFilterState) { + TagsFilterState tagsFilterState = rootFilterState.getTagsFilterState(); + for (TagName tagName : tagNames) { + tagsFilterState.getFilter().addSubFilter(new TagNameFilter(tagName)); + } + for (FilterState tagFilterState : rootFilterState.getTagsFilterState().getSubFilterStates()) { tagFilterState.setDisabled(tagNames.contains(tagFilterState.getFilter().getTagName()) == false); } - - DataSourcesFilter dataSourcesFilter = rootFilter.getDataSourcesFilterState().getFilter(); + + DataSourcesFilter dataSourcesFilter = rootFilterState.getDataSourcesFilterState().getFilter(); for (Map.Entry entry : datasourcesMap.entrySet()) { dataSourcesFilter.addSubFilter(new DataSourceFilter(entry.getValue().getName(), entry.getKey())); } - - HashHitsFilter hashSetsFilter = rootFilter.getHashHitsFilterState().getFilter(); - for (String hashSet : getHashSets()) { + + HashHitsFilter hashSetsFilter = rootFilterState.getHashHitsFilterState().getFilter(); + for (String hashSet : hashSets) { hashSetsFilter.addSubFilter(new HashSetFilter(hashSet)); } - } /** @@ -347,7 +349,7 @@ public final class FilteredEventsModel { public synchronized RootFilterState getDefaultFilter() { DataSourcesFilter dataSourcesFilter = new DataSourcesFilter(); datasourcesMap.entrySet().forEach(dataSourceEntry - -> dataSourcesFilter.addSubFilter(new DataSourceFilter(dataSourceEntry.getValue(), dataSourceEntry.getKey())) + -> dataSourcesFilter.addSubFilter(new DataSourceFilter(dataSourceEntry.getValue().getName(), dataSourceEntry.getKey())) ); HashHitsFilter hashHitsFilter = new HashHitsFilter(); @@ -678,8 +680,8 @@ public final class FilteredEventsModel { public synchronized void invalidateCaches(Collection updatedEventIDs) throws TskCoreException { minCache.invalidateAll(); maxCache.invalidateAll(); - idToEventCache.invalidateAll(emptyIfNull(updatedEventIDs)); - eventCountsCache.invalidateAll(); + idToEventCache.invalidateAll(emptyIfNull(updatedEventIDs)); + eventCountsCache.invalidateAll(); populateFilterData(); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index 7b002df581..abeac3dd7d 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -285,6 +285,7 @@ public class TimeLineController { ZoomState historyManagerState = newState; filteredEvents.syncFilters(historyManagerState.getFilterState()); currentParams.set(historyManagerState); + }); try { @@ -721,7 +722,6 @@ public class TimeLineController { // Case is closed, do nothing. return; } - // ignore remote events. The node running the ingest should update the Case DB // @@@ We should signal though that there is more data and flush caches... if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.REMOTE) { @@ -754,7 +754,6 @@ public class TimeLineController { } void handleCaseEvent(PropertyChangeEvent evt) { - ListenableFuture future = Futures.immediateFuture(null); switch (Case.Events.valueOf(evt.getPropertyName())) { case BLACKBOARD_ARTIFACT_TAG_ADDED: @@ -781,9 +780,8 @@ public class TimeLineController { break; case EVENT_ADDED: future = executor.submit(() -> { - filteredEvents.invalidateCaches(singleton(((EventAddedEvent) evt).getEventID())); + filteredEvents.invalidateCaches(singleton(((EventAddedEvent) evt).getAddedEventID())); return null; - }); break; } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineModule.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineModule.java index 9421dadcaf..96cb9845c4 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineModule.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineModule.java @@ -56,6 +56,7 @@ public class TimeLineModule { * @throws NoCurrentCaseException If there is no case open. * @throws TskCoreException If there was a problem accessing the case * database. + * */ public static TimeLineController getController() throws NoCurrentCaseException, TskCoreException { synchronized (controllerLock) { @@ -89,7 +90,7 @@ public class TimeLineModule { getController().handleCaseEvent(evt); } catch (NoCurrentCaseException ex) { // ignore - + return; } catch (TskCoreException ex) { MessageNotifyUtil.Message.error("Error creating timeline controller."); logger.log(Level.SEVERE, "Error creating timeline controller", ex); @@ -120,6 +121,7 @@ public class TimeLineModule { getController().handleIngestModuleEvent(evt); } catch (NoCurrentCaseException ex) { // ignore + return; } catch (TskCoreException ex) { MessageNotifyUtil.Message.error("Error creating timeline controller."); logger.log(Level.SEVERE, "Error creating timeline controller", ex); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/events/EventAddedEvent.java b/Core/src/org/sleuthkit/autopsy/timeline/events/EventAddedEvent.java index 3ec593464e..d12069c8cb 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/events/EventAddedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/events/EventAddedEvent.java @@ -25,16 +25,15 @@ import org.sleuthkit.autopsy.events.AutopsyEvent; * */ public class EventAddedEvent extends AutopsyEvent { + + private final long addedEventID; - private final Long eventID; + public long getAddedEventID() { + return addedEventID; + } - public EventAddedEvent(org.sleuthkit.datamodel.TimelineManager.EventAddedEvent event) { super(Case.Events.EVENT_ADDED.name(), null, event.getEvent()); - eventID = event.getEvent().getEventID(); - } - - public Long getEventID() { - return eventID; + addedEventID = event.getEvent().getEventID(); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java index 0fe559d19a..91b97383db 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java @@ -119,13 +119,14 @@ final public class FilterSetPanel extends BorderPane { expansionMap.put(filteredEvents.getFilterState().getFilter(), true); expansionMap.put(filteredEvents.getFilterState().getEventTypeFilterState().getFilter(), true); + InvalidationListener applyFiltersListener = observable -> applyFilters(); filteredEvents.eventTypeZoomProperty().addListener(applyFiltersListener); filteredEvents.descriptionLODProperty().addListener(applyFiltersListener); filteredEvents.timeRangeProperty().addListener(applyFiltersListener); - filteredEvents.filterProperty().addListener(observable -> refresh()); + filteredEvents.filterProperty().addListener(observable -> refresh()); refresh(); hiddenDescriptionsListView.setItems(controller.getQuickHideFilters()); @@ -165,9 +166,8 @@ final public class FilterSetPanel extends BorderPane { } private void refresh() { - FilterTreeItem filterTreeItem = new FilterTreeItem(filteredEvents.filterProperty().getValue(), expansionMap); Platform.runLater(() -> { - filterTreeTable.setRoot(filterTreeItem); + filterTreeTable.setRoot(new FilterTreeItem(filteredEvents.filterProperty().get().copyOf(), expansionMap)); }); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java index 5806067a44..9207b0ccd3 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterStateImpl.java @@ -156,4 +156,4 @@ class CompoundFilterStateImpl