Autopsy  4.12.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
TimeLineController.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2014-2018 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.timeline;
20 
21 import com.google.common.eventbus.EventBus;
22 import com.google.common.util.concurrent.Futures;
23 import com.google.common.util.concurrent.ListenableFuture;
24 import com.google.common.util.concurrent.ListeningExecutorService;
25 import com.google.common.util.concurrent.MoreExecutors;
26 import com.google.common.util.concurrent.ThreadFactoryBuilder;
27 import java.beans.PropertyChangeEvent;
28 import java.time.ZoneId;
29 import java.util.Collection;
30 import java.util.Collections;
31 import static java.util.Collections.singleton;
32 import java.util.Optional;
33 import java.util.TimeZone;
34 import java.util.concurrent.ExecutionException;
35 import java.util.concurrent.Executors;
36 import java.util.logging.Level;
37 import javafx.application.Platform;
38 import javafx.beans.Observable;
39 import javafx.beans.property.ReadOnlyBooleanProperty;
40 import javafx.beans.property.ReadOnlyDoubleProperty;
41 import javafx.beans.property.ReadOnlyDoubleWrapper;
42 import javafx.beans.property.ReadOnlyListProperty;
43 import javafx.beans.property.ReadOnlyListWrapper;
44 import javafx.beans.property.ReadOnlyObjectProperty;
45 import javafx.beans.property.ReadOnlyObjectWrapper;
46 import javafx.beans.property.ReadOnlyStringProperty;
47 import javafx.beans.property.ReadOnlyStringWrapper;
48 import javafx.collections.FXCollections;
49 import javafx.collections.ObservableList;
50 import javafx.collections.ObservableSet;
51 import javafx.concurrent.Task;
52 import static javafx.concurrent.Worker.State.FAILED;
53 import static javafx.concurrent.Worker.State.SUCCEEDED;
54 import javafx.scene.control.Alert;
55 import javax.annotation.concurrent.GuardedBy;
56 import javax.swing.SwingUtilities;
57 import org.joda.time.DateTime;
58 import org.joda.time.DateTimeZone;
59 import org.joda.time.Interval;
60 import org.joda.time.ReadablePeriod;
61 import org.joda.time.format.DateTimeFormat;
62 import org.joda.time.format.DateTimeFormatter;
63 import org.openide.util.NbBundle;
89 import org.sleuthkit.datamodel.AbstractFile;
90 import org.sleuthkit.datamodel.BlackboardArtifact;
91 import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT;
92 import org.sleuthkit.datamodel.TimelineEvent;
93 import org.sleuthkit.datamodel.TskCoreException;
94 import org.sleuthkit.datamodel.TimelineEventType;
95 import org.sleuthkit.datamodel.TimelineFilter.EventTypeFilter;
96 
112 @NbBundle.Messages({"Timeline.dialogs.title= Timeline",
113  "TimeLinecontroller.updateNowQuestion=Do you want to update the events database now?"})
114 public class TimeLineController {
115 
116  private static final Logger logger = Logger.getLogger(TimeLineController.class.getName());
117 
118  private static final ReadOnlyObjectWrapper<TimeZone> timeZone = new ReadOnlyObjectWrapper<>(TimeZone.getDefault());
119 
120  private final ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(
121  new ThreadFactoryBuilder().setNameFormat("Timeline Controller BG thread").build()));
122  private final ReadOnlyListWrapper<Task<?>> tasks = new ReadOnlyListWrapper<>(FXCollections.observableArrayList());
123  private final ReadOnlyDoubleWrapper taskProgress = new ReadOnlyDoubleWrapper(-1);
124  private final ReadOnlyStringWrapper taskMessage = new ReadOnlyStringWrapper();
125  private final ReadOnlyStringWrapper taskTitle = new ReadOnlyStringWrapper();
126  private final ReadOnlyStringWrapper statusMessage = new ReadOnlyStringWrapper();
127 
128  private final EventBus eventbus = new EventBus("TimeLineController_EventBus");
129 
130  public static ZoneId getTimeZoneID() {
131  return timeZone.get().toZoneId();
132  }
133 
134  public static DateTimeFormatter getZonedFormatter() {
135  return DateTimeFormat.forPattern("YYYY-MM-dd HH:mm:ss").withZone(getJodaTimeZone()); //NON-NLS
136  }
137 
138  public static DateTimeZone getJodaTimeZone() {
139  return DateTimeZone.forTimeZone(timeZoneProperty().get());
140  }
141 
142  public static ReadOnlyObjectProperty<TimeZone> timeZoneProperty() {
143  return timeZone.getReadOnlyProperty();
144  }
145 
146  public static TimeZone getTimeZone() {
147  return timeZone.get();
148  }
149 
156  public ReadOnlyStringProperty statusMessageProperty() {
157  return statusMessage.getReadOnlyProperty();
158  }
159 
160  public void setStatusMessage(String string) {
161  statusMessage.set(string);
162  }
163  private final Case autoCase;
164 
166  private final ObservableList<DescriptionFilterState> quickHideFilters = FXCollections.observableArrayList();
167 
168  public ObservableList<DescriptionFilterState> getQuickHideFilters() {
169  return quickHideFilters;
170  }
171 
175  public Case getAutopsyCase() {
176  return autoCase;
177  }
178 
179  synchronized public ReadOnlyListProperty<Task<?>> getTasks() {
180  return tasks.getReadOnlyProperty();
181  }
182 
183  synchronized public ReadOnlyDoubleProperty taskProgressProperty() {
184  return taskProgress.getReadOnlyProperty();
185  }
186 
187  synchronized public ReadOnlyStringProperty taskMessageProperty() {
188  return taskMessage.getReadOnlyProperty();
189  }
190 
191  synchronized public ReadOnlyStringProperty taskTitleProperty() {
192  return taskTitle.getReadOnlyProperty();
193  }
194 
196  private TimeLineTopComponent topComponent;
197 
198  @GuardedBy("this")
199  private final ReadOnlyObjectWrapper<ViewMode> viewMode = new ReadOnlyObjectWrapper<>(ViewMode.COUNTS);
200 
201  @GuardedBy("filteredEvents")
202  private final FilteredEventsModel filteredEvents;
203 
204  @GuardedBy("this")
205  private final ZoomState InitialZoomState;
206 
207  @GuardedBy("this")
208  private final History<ZoomState> historyManager = new History<>();
209 
210  @GuardedBy("this")
211  private final ReadOnlyObjectWrapper<ZoomState> currentParams = new ReadOnlyObjectWrapper<>();
212 
213  //selected events (ie shown in the result viewer)
214  @GuardedBy("this")
215  private final ObservableList<Long> selectedEventIDs = FXCollections.<Long>observableArrayList();
216 
217  @GuardedBy("this")
218  private final ReadOnlyObjectWrapper<Interval> selectedTimeRange = new ReadOnlyObjectWrapper<>();
219 
220  private final PromptDialogManager promptDialogManager = new PromptDialogManager(this);
221 
227  synchronized public ObservableList<Long> getSelectedEventIDs() {
228  return selectedEventIDs;
229  }
230 
236  synchronized public ReadOnlyObjectProperty<Interval> selectedTimeRangeProperty() {
237  return selectedTimeRange.getReadOnlyProperty();
238  }
239 
245  synchronized public Interval getSelectedTimeRange() {
246  return selectedTimeRange.get();
247  }
248 
249  synchronized public ReadOnlyBooleanProperty canAdvanceProperty() {
250  return historyManager.getCanAdvance();
251  }
252 
253  synchronized public ReadOnlyBooleanProperty canRetreatProperty() {
254  return historyManager.getCanRetreat();
255  }
256 
257  synchronized public ReadOnlyObjectProperty<ViewMode> viewModeProperty() {
258  return viewMode.getReadOnlyProperty();
259  }
260 
266  synchronized public void setViewMode(ViewMode viewMode) {
267  if (this.viewMode.get() != viewMode) {
268  this.viewMode.set(viewMode);
269  }
270  }
271 
277  synchronized public ViewMode getViewMode() {
278  return viewMode.get();
279  }
280 
281  TimeLineController(Case autoCase) throws TskCoreException {
282  this.autoCase = autoCase;
283  filteredEvents = new FilteredEventsModel(autoCase, currentParams.getReadOnlyProperty());
284  /*
285  * as the history manager's current state changes, modify the tags
286  * filter to be in sync, and expose that as propery from
287  * TimeLineController. Do we need to do this with datasource or hash hit
288  * filters?
289  */
290  historyManager.currentState().addListener((observable, oldState, newState) -> {
291  ZoomState historyManagerState = newState;
292  filteredEvents.syncFilters(historyManagerState.getFilterState());
293  currentParams.set(historyManagerState);
294 
295  });
296 
297  try {
298  InitialZoomState = new ZoomState(filteredEvents.getSpanningInterval(),
299  TimelineEventType.TypeLevel.BASE_TYPE,
300  filteredEvents.filterProperty().get(),
301  TimelineEvent.DescriptionLevel.SHORT);
302  } catch (TskCoreException ex) {
303  throw new TskCoreException("Error getting spanning interval.", ex);
304  }
305  historyManager.advance(InitialZoomState);
306 
307  //clear the selected events when the view mode changes
308  viewMode.addListener(observable -> {
309  try {
310  selectEventIDs(Collections.emptySet());
311  } catch (TskCoreException ex) {
312  logger.log(Level.SEVERE, "Error clearing the timeline selection.", ex);
313  }
314  });
315  }
316 
321  return filteredEvents;
322  }
323 
324  public void applyDefaultFilters() {
325  pushFilters(filteredEvents.getDefaultFilter());
326  }
327 
328  public void zoomOutToActivity() throws TskCoreException {
329  Interval boundingEventsInterval = filteredEvents.getBoundingEventsInterval(getJodaTimeZone());
330  advance(filteredEvents.zoomStateProperty().get().withTimeRange(boundingEventsInterval));
331  }
332 
333  private final ObservableSet<DetailViewEvent> pinnedEvents = FXCollections.observableSet();
334  private final ObservableSet<DetailViewEvent> pinnedEventsUnmodifiable = FXCollections.unmodifiableObservableSet(pinnedEvents);
335 
336  public void pinEvent(DetailViewEvent event) {
337  pinnedEvents.add(event);
338  }
339 
340  public void unPinEvent(DetailViewEvent event) {
341  pinnedEvents.removeIf(event::equals);
342  }
343 
344  public ObservableSet<DetailViewEvent> getPinnedEvents() {
345  return pinnedEventsUnmodifiable;
346  }
347 
351  private boolean showFullRange() throws TskCoreException {
352  synchronized (filteredEvents) {
353  return pushTimeRange(filteredEvents.getSpanningInterval());
354  }
355  }
356 
365  private void showInListView(ViewInTimelineRequestedEvent requestEvent) throws TskCoreException {
366  synchronized (filteredEvents) {
367  setViewMode(ViewMode.LIST);
368  selectEventIDs(requestEvent.getEventIDs());
369  try {
370  if (pushTimeRange(requestEvent.getInterval()) == false) {
371  eventbus.post(requestEvent);
372  }
373  } catch (TskCoreException ex) {
374  throw new TskCoreException("Error pushing requested timerange.", ex);
375  }
376  }
377  }
378 
384  public void shutDownTimeLine() {
385  if (topComponent != null) {
386  topComponent.close();
387  topComponent = null;
388  }
389  }
390 
400  void showTimeLine(AbstractFile file, BlackboardArtifact artifact) {
401  Platform.runLater(() -> {
402  //if there is an existing prompt or progressdialog,...
403  if (promptDialogManager.bringCurrentDialogToFront()) {
404  //... just show that
405  } else {
406 
407  if ( //confirm timeline during ingest
408  IngestManager.getInstance().isIngestRunning()
409  && promptDialogManager.confirmDuringIngest() == false) {
410  return; //if they cancel, do nothing.
411  }
412  try {
413  if (file == null && artifact == null) {
414  SwingUtilities.invokeLater(TimeLineController.this::showWindow);
415  this.showFullRange();
416  } else {
417 
418  //prompt user to pick specific event and time range
419  ShowInTimelineDialog showInTimelineDilaog = (file == null)
420  ? new ShowInTimelineDialog(this, artifact)
421  : new ShowInTimelineDialog(this, file);
422  Optional<ViewInTimelineRequestedEvent> dialogResult = showInTimelineDilaog.showAndWait();
423  dialogResult.ifPresent(viewInTimelineRequestedEvent -> {
424  SwingUtilities.invokeLater(this::showWindow);
425  try {
426  showInListView(viewInTimelineRequestedEvent); //show requested event in list view
427  } catch (TskCoreException ex) {
428  logger.log(Level.SEVERE, "Error showing requested events in listview: " + viewInTimelineRequestedEvent, ex);
429  new Alert(Alert.AlertType.ERROR, "There was an error opening Timeline.").showAndWait();
430  }
431  });
432 
433  }
434  } catch (TskCoreException tskCoreException) {
435  logger.log(Level.SEVERE, "Error showing Timeline ", tskCoreException);
436  new Alert(Alert.AlertType.ERROR, "There was an error opening Timeline.").showAndWait();
437  }
438  }
439  });
440  }
441 
449  synchronized public void pushPeriod(ReadablePeriod period) throws TskCoreException {
450  synchronized (filteredEvents) {
451  pushTimeRange(IntervalUtils.getIntervalAroundMiddle(filteredEvents.getTimeRange(), period));
452  }
453  }
454 
455  synchronized public void pushZoomOutTime() throws TskCoreException {
456  final Interval timeRange = filteredEvents.getTimeRange();
457  long toDurationMillis = timeRange.toDurationMillis() / 4;
458  DateTime start = timeRange.getStart().minus(toDurationMillis);
459  DateTime end = timeRange.getEnd().plus(toDurationMillis);
460  pushTimeRange(new Interval(start, end));
461  }
462 
463  synchronized public void pushZoomInTime() throws TskCoreException {
464  final Interval timeRange = filteredEvents.getTimeRange();
465  long toDurationMillis = timeRange.toDurationMillis() / 4;
466  DateTime start = timeRange.getStart().plus(toDurationMillis);
467  DateTime end = timeRange.getEnd().minus(toDurationMillis);
468  pushTimeRange(new Interval(start, end));
469  }
470 
476  synchronized private void showWindow() {
477  if (topComponent == null) {
478  topComponent = new TimeLineTopComponent(this);
479  }
480  if (topComponent.isOpened() == false) {
481  topComponent.open();
482  }
483  topComponent.toFront();
484  /*
485  * Make this top component active so its ExplorerManager's lookup gets
486  * proxied in Utilities.actionsGlobalContext()
487  */
488  topComponent.requestActive();
489  }
490 
491  synchronized public TimeLineTopComponent getTopComponent(){
492  return topComponent;
493  }
494 
495  synchronized public void pushEventTypeZoom(TimelineEventType.TypeLevel typeZoomeLevel) {
496  ZoomState currentZoom = filteredEvents.zoomStateProperty().get();
497  if (currentZoom == null) {
498  advance(InitialZoomState.withTypeZoomLevel(typeZoomeLevel));
499  } else if (currentZoom.hasTypeZoomLevel(typeZoomeLevel) == false) {
500  advance(currentZoom.withTypeZoomLevel(typeZoomeLevel));
501  }
502  }
503 
513  synchronized public boolean pushTimeRange(Interval timeRange) throws TskCoreException {
514  //clamp timerange to case
515  Interval clampedTimeRange;
516  if (timeRange == null) {
517  clampedTimeRange = this.filteredEvents.getSpanningInterval();
518  } else {
519  Interval spanningInterval = this.filteredEvents.getSpanningInterval();
520  if (spanningInterval.overlaps(timeRange)) {
521  clampedTimeRange = spanningInterval.overlap(timeRange);
522  } else {
523  clampedTimeRange = spanningInterval;
524  }
525  }
526 
527  ZoomState currentZoom = filteredEvents.zoomStateProperty().get();
528  if (currentZoom == null) {
529  advance(InitialZoomState.withTimeRange(clampedTimeRange));
530  return true;
531  } else if (currentZoom.hasTimeRange(clampedTimeRange) == false) {
532  advance(currentZoom.withTimeRange(clampedTimeRange));
533  return true;
534  } else {
535  return false;
536  }
537  }
538 
549  synchronized public boolean pushTimeUnit(TimeUnits timeUnit) throws TskCoreException {
550  if (timeUnit == TimeUnits.FOREVER) {
551  return showFullRange();
552  } else {
553  return pushTimeRange(IntervalUtils.getIntervalAroundMiddle(filteredEvents.getTimeRange(), timeUnit.toUnitPeriod()));
554  }
555  }
556 
557  synchronized public void pushDescrLOD(TimelineEvent.DescriptionLevel newLOD) {
558  ZoomState currentZoom = filteredEvents.zoomStateProperty().get();
559  if (currentZoom == null) {
560  advance(InitialZoomState.withDescrLOD(newLOD));
561  } else if (currentZoom.hasDescrLOD(newLOD) == false) {
562  advance(currentZoom.withDescrLOD(newLOD));
563  }
564  }
565 
566  @SuppressWarnings("AssignmentToMethodParameter") //clamp timerange to case
567  synchronized public void pushTimeAndType(Interval timeRange, TimelineEventType.TypeLevel typeZoom) throws TskCoreException {
568  Interval overlappingTimeRange = this.filteredEvents.getSpanningInterval().overlap(timeRange);
569  ZoomState currentZoom = filteredEvents.zoomStateProperty().get();
570  if (currentZoom == null) {
571  advance(InitialZoomState.withTimeAndType(overlappingTimeRange, typeZoom));
572  } else if (currentZoom.hasTimeRange(overlappingTimeRange) == false && currentZoom.hasTypeZoomLevel(typeZoom) == false) {
573  advance(currentZoom.withTimeAndType(overlappingTimeRange, typeZoom));
574  } else if (currentZoom.hasTimeRange(overlappingTimeRange) == false) {
575  advance(currentZoom.withTimeRange(overlappingTimeRange));
576  } else if (currentZoom.hasTypeZoomLevel(typeZoom) == false) {
577  advance(currentZoom.withTypeZoomLevel(typeZoom));
578  }
579  }
580 
581  synchronized public void pushFilters(RootFilterState filter) {
582  ZoomState currentZoom = filteredEvents.zoomStateProperty().get();
583  if (currentZoom == null) {
584  advance(InitialZoomState.withFilterState(filter));
585  } else if (currentZoom.hasFilterState(filter) == false) {
586  advance(currentZoom.withFilterState(filter));
587  }
588  }
589 
590  synchronized public void advance() {
591  historyManager.advance();
592  }
593 
594  synchronized public void retreat() {
595  historyManager.retreat();
596  }
597 
598  synchronized private void advance(ZoomState newState) {
599  historyManager.advance(newState);
600  }
601 
608  final synchronized public void selectEventIDs(Collection<Long> eventIDs) throws TskCoreException {
609  selectedTimeRange.set(filteredEvents.getSpanningInterval(eventIDs));
610  selectedEventIDs.setAll(eventIDs);
611  }
612 
613  public void selectTimeAndType(Interval interval, TimelineEventType type) throws TskCoreException {
614  final Interval timeRange = filteredEvents.getSpanningInterval().overlap(interval);
615 
616  final LoggedTask<Collection<Long>> selectTimeAndTypeTask = new LoggedTask<Collection<Long>>("Select Time and Type", true) { //NON-NLS
617  @Override
618  protected Collection< Long> call() throws Exception {
619  synchronized (TimeLineController.this) {
620  return filteredEvents.getEventIDs(timeRange, new SqlFilterState<>(new EventTypeFilter(type), true));
621  }
622  }
623 
624  @Override
625  protected void succeeded() {
626  super.succeeded();
627  try {
628  synchronized (TimeLineController.this) {
629  selectedTimeRange.set(timeRange);
630  selectedEventIDs.setAll(get());
631 
632  }
633  } catch (InterruptedException | ExecutionException ex) {
634  logger.log(Level.SEVERE, getTitle() + " Unexpected error", ex); //NON-NLS
635  }
636  }
637  };
638 
639  monitorTask(selectTimeAndTypeTask);
640  }
641 
648  synchronized public void monitorTask(final Task<?> task) {
649  //TODO: refactor this to use JavaFX Service? -jm
650  if (task != null) {
651  Platform.runLater(() -> {
652 
653  //is this actually threadsafe, could we get a finished task stuck in the list?
654  task.stateProperty().addListener((Observable observable) -> {
655  switch (task.getState()) {
656  case READY:
657  case RUNNING:
658  case SCHEDULED:
659  break;
660  case SUCCEEDED:
661  case CANCELLED:
662  case FAILED:
663  tasks.remove(task);
664  if (tasks.isEmpty() == false) {
665  taskProgress.bind(tasks.get(0).progressProperty());
666  taskMessage.bind(tasks.get(0).messageProperty());
667  taskTitle.bind(tasks.get(0).titleProperty());
668  }
669  break;
670  }
671  });
672  tasks.add(task);
673  taskProgress.bind(task.progressProperty());
674  taskMessage.bind(task.messageProperty());
675  taskTitle.bind(task.titleProperty());
676  switch (task.getState()) {
677  case READY:
678  //TODO: Check future result for errors....
679  executor.submit(task);
680  break;
681  case SCHEDULED:
682  case RUNNING:
683 
684  case SUCCEEDED:
685  case CANCELLED:
686  case FAILED:
687  tasks.remove(task);
688  if (tasks.isEmpty() == false) {
689  taskProgress.bind(tasks.get(0).progressProperty());
690  taskMessage.bind(tasks.get(0).messageProperty());
691  taskTitle.bind(tasks.get(0).titleProperty());
692  }
693  break;
694  }
695  });
696  }
697  }
698 
705  synchronized public void registerForEvents(Object listener) {
706  eventbus.register(listener);
707  }
708 
714  synchronized public void unRegisterForEvents(Object listener) {
715  eventbus.unregister(listener);
716  }
717 
718  static synchronized public void setTimeZone(TimeZone timeZone) {
719  TimeLineController.timeZone.set(timeZone);
720 
721  }
722 
723  void handleIngestModuleEvent(PropertyChangeEvent evt) {
729  try {
731  } catch (NoCurrentCaseException notUsed) {
732  // Case is closed, do nothing.
733  return;
734  }
735  // ignore remote events. The node running the ingest should update the Case DB
736  // @@@ We should signal though that there is more data and flush caches...
737  if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.REMOTE) {
738  return;
739  }
740 
741  switch (IngestManager.IngestModuleEvent.valueOf(evt.getPropertyName())) {
742  case CONTENT_CHANGED:
743  // new files were already added to the events table from SleuthkitCase.
744  break;
745  case DATA_ADDED:
746  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
747  if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == TSK_HASHSET_HIT.getTypeID()) {
748  logFutureException(executor.submit(() -> filteredEvents.setHashHit(eventData.getArtifacts(), true)),
749  "Error executing task in response to DATA_ADDED event.",
750  "Error executing response to new data.");
751  }
752  break;
753  case FILE_DONE:
754  /*
755  * Since the known state or hash hit state may have changed
756  * invalidate caches.
757  */
758  //@@@ This causes HUGE slow downs during ingest when TL is open.
759  // executor.submit(filteredEvents::invalidateAllCaches);
760 
761  // known state should have been udpated automatically via SleuthkitCase.setKnown();
762  // hashes should have been updated from event
763  }
764  }
765 
766  void handleCaseEvent(PropertyChangeEvent evt) {
767  ListenableFuture<?> future = Futures.immediateFuture(null);
768  switch (Case.Events.valueOf(evt.getPropertyName())) {
769  case BLACKBOARD_ARTIFACT_TAG_ADDED:
770  future = executor.submit(() -> filteredEvents.handleArtifactTagAdded((BlackBoardArtifactTagAddedEvent) evt));
771  break;
772  case BLACKBOARD_ARTIFACT_TAG_DELETED:
773  future = executor.submit(() -> filteredEvents.handleArtifactTagDeleted((BlackBoardArtifactTagDeletedEvent) evt));
774  break;
775  case CONTENT_TAG_ADDED:
776  future = executor.submit(() -> filteredEvents.handleContentTagAdded((ContentTagAddedEvent) evt));
777  break;
778  case CONTENT_TAG_DELETED:
779  future = executor.submit(() -> filteredEvents.handleContentTagDeleted((ContentTagDeletedEvent) evt));
780  break;
781  case CURRENT_CASE:
782  //close timeline on case changes.
783  SwingUtilities.invokeLater(TimeLineController.this::shutDownTimeLine);
784  break;
785  case DATA_SOURCE_ADDED:
786  future = executor.submit(() -> {
787  filteredEvents.invalidateCaches(null);
788  return null;
789  });
790  break;
791  case TIMELINE_EVENT_ADDED:
792  future = executor.submit(() -> {
793  filteredEvents.invalidateCaches(singleton(((TimelineEventAddedEvent) evt).getAddedEventID()));
794  return null;
795  });
796  break;
797  }
798  logFutureException(future,
799  "Error executing task in response to " + evt.getPropertyName() + " event.",
800  "Error executing task in response to case event.");
801  }
802 
803  private void logFutureException(ListenableFuture<?> future, String errorLogMessage, String errorUserMessage) {
804  future.addListener(() -> {
805  try {
806  future.get();
807  } catch (InterruptedException | ExecutionException ex) {
808  logger.log(Level.SEVERE, errorLogMessage, ex);
809  MessageNotifyUtil.Message.error(errorUserMessage);
810  }
811  }, MoreExecutors.directExecutor());
812  }
813 }
synchronized ReadOnlyDoubleProperty taskProgressProperty()
synchronized void pushDescrLOD(TimelineEvent.DescriptionLevel newLOD)
boolean hasFilterState(RootFilterState filterSet)
Definition: ZoomState.java:84
static final ReadOnlyObjectWrapper< TimeZone > timeZone
void logFutureException(ListenableFuture<?> future, String errorLogMessage, String errorUserMessage)
ZoomState withFilterState(RootFilterState filter)
Definition: ZoomState.java:80
boolean hasTypeZoomLevel(TimelineEventType.TypeLevel typeZoom)
Definition: ZoomState.java:88
synchronized void setViewMode(ViewMode viewMode)
synchronized ReadOnlyObjectProperty< Interval > selectedTimeRangeProperty()
boolean hasDescrLOD(TimelineEvent.DescriptionLevel newLOD)
Definition: ZoomState.java:96
synchronized boolean pushTimeUnit(TimeUnits timeUnit)
synchronized boolean pushTimeRange(Interval timeRange)
synchronized ReadOnlyStringProperty taskTitleProperty()
synchronized void unRegisterForEvents(Object listener)
ZoomState withTimeAndType(Interval timeRange, TimelineEventType.TypeLevel zoomLevel)
Definition: ZoomState.java:64
static ReadOnlyObjectProperty< TimeZone > timeZoneProperty()
synchronized ReadOnlyStringProperty taskMessageProperty()
static Interval getIntervalAroundMiddle(Interval interval, ReadablePeriod period)
synchronized void registerForEvents(Object listener)
boolean hasTimeRange(Interval timeRange)
Definition: ZoomState.java:92
static synchronized void setTimeZone(TimeZone timeZone)
final synchronized void selectEventIDs(Collection< Long > eventIDs)
synchronized void monitorTask(final Task<?> task)
synchronized void pushPeriod(ReadablePeriod period)
synchronized void pushFilters(RootFilterState filter)
synchronized void pushEventTypeZoom(TimelineEventType.TypeLevel typeZoomeLevel)
ZoomState withDescrLOD(TimelineEvent.DescriptionLevel descrLOD)
Definition: ZoomState.java:76
synchronized TimeLineTopComponent getTopComponent()
synchronized ReadOnlyObjectProperty< ViewMode > viewModeProperty()
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
ZoomState withTypeZoomLevel(TimelineEventType.TypeLevel zoomLevel)
Definition: ZoomState.java:68
synchronized ReadOnlyBooleanProperty canAdvanceProperty()
ZoomState withTimeRange(Interval timeRange)
Definition: ZoomState.java:72
ObservableSet< DetailViewEvent > getPinnedEvents()
synchronized ReadOnlyListProperty< Task<?> > getTasks()
synchronized ReadOnlyBooleanProperty canRetreatProperty()
synchronized void advance(ZoomState newState)
void selectTimeAndType(Interval interval, TimelineEventType type)

Copyright © 2012-2018 Basis Technology. Generated on: Wed Sep 18 2019
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.