Autopsy  4.19.1
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-2020 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;
88 import org.sleuthkit.datamodel.AbstractFile;
89 import org.sleuthkit.datamodel.BlackboardArtifact;
90 import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT;
91 import org.sleuthkit.datamodel.TskCoreException;
92 import org.sleuthkit.datamodel.TimelineEventType;
93 import org.sleuthkit.datamodel.TimelineFilter.EventTypeFilter;
94 import org.sleuthkit.datamodel.TimelineLevelOfDetail;
95 
111 @NbBundle.Messages({"Timeline.dialogs.title= Timeline",
112  "TimeLinecontroller.updateNowQuestion=Do you want to update the events database now?"})
113 public class TimeLineController {
114 
115  private static final Logger logger = Logger.getLogger(TimeLineController.class.getName());
116 
117  private static final ReadOnlyObjectWrapper<TimeZone> timeZone = new ReadOnlyObjectWrapper<>(TimeZone.getDefault());
118 
119  private final ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(
120  new ThreadFactoryBuilder().setNameFormat("Timeline Controller BG thread").build()));
121  private final ReadOnlyListWrapper<Task<?>> tasks = new ReadOnlyListWrapper<>(FXCollections.observableArrayList());
122  private final ReadOnlyDoubleWrapper taskProgress = new ReadOnlyDoubleWrapper(-1);
123  private final ReadOnlyStringWrapper taskMessage = new ReadOnlyStringWrapper();
124  private final ReadOnlyStringWrapper taskTitle = new ReadOnlyStringWrapper();
125  private final ReadOnlyStringWrapper statusMessage = new ReadOnlyStringWrapper();
126 
127  private final EventBus eventbus = new EventBus("TimeLineController_EventBus");
128 
129  public static ZoneId getTimeZoneID() {
130  return timeZone.get().toZoneId();
131  }
132 
133  public static DateTimeFormatter getZonedFormatter() {
134  return DateTimeFormat.forPattern("YYYY-MM-dd HH:mm:ss").withZone(getJodaTimeZone()); //NON-NLS
135  }
136 
137  public static DateTimeZone getJodaTimeZone() {
138  return DateTimeZone.forTimeZone(timeZoneProperty().get());
139  }
140 
141  public static ReadOnlyObjectProperty<TimeZone> timeZoneProperty() {
142  return timeZone.getReadOnlyProperty();
143  }
144 
145  public static TimeZone getTimeZone() {
146  return timeZone.get();
147  }
148 
155  public ReadOnlyStringProperty statusMessageProperty() {
156  return statusMessage.getReadOnlyProperty();
157  }
158 
159  public void setStatusMessage(String string) {
160  statusMessage.set(string);
161  }
162  private final Case autoCase;
163 
165  private final ObservableList<DescriptionFilterState> quickHideFilters = FXCollections.observableArrayList();
166 
167  public ObservableList<DescriptionFilterState> getQuickHideFilters() {
168  return quickHideFilters;
169  }
170 
174  public Case getAutopsyCase() {
175  return autoCase;
176  }
177 
178  synchronized public ReadOnlyListProperty<Task<?>> getTasks() {
179  return tasks.getReadOnlyProperty();
180  }
181 
182  synchronized public ReadOnlyDoubleProperty taskProgressProperty() {
183  return taskProgress.getReadOnlyProperty();
184  }
185 
186  synchronized public ReadOnlyStringProperty taskMessageProperty() {
187  return taskMessage.getReadOnlyProperty();
188  }
189 
190  synchronized public ReadOnlyStringProperty taskTitleProperty() {
191  return taskTitle.getReadOnlyProperty();
192  }
193 
195  private TimeLineTopComponent topComponent;
196 
197  @GuardedBy("this")
198  private final ReadOnlyObjectWrapper<ViewMode> viewMode = new ReadOnlyObjectWrapper<>(ViewMode.COUNTS);
199 
200  @GuardedBy("filteredEvents")
201  private final EventsModel filteredEvents;
202 
203  @GuardedBy("this")
204  private final EventsModelParams InitialZoomState;
205 
206  @GuardedBy("this")
207  private final History<EventsModelParams> historyManager = new History<>();
208 
209  @GuardedBy("this")
210  private final ReadOnlyObjectWrapper<EventsModelParams> currentParams = new ReadOnlyObjectWrapper<>();
211 
212  //selected events (ie shown in the result viewer)
213  @GuardedBy("this")
214  private final ObservableList<Long> selectedEventIDs = FXCollections.<Long>observableArrayList();
215 
216  @GuardedBy("this")
217  private final ReadOnlyObjectWrapper<Interval> selectedTimeRange = new ReadOnlyObjectWrapper<>();
218 
219  private final PromptDialogManager promptDialogManager = new PromptDialogManager(this);
220 
226  synchronized public ObservableList<Long> getSelectedEventIDs() {
227  return selectedEventIDs;
228  }
229 
235  synchronized public ReadOnlyObjectProperty<Interval> selectedTimeRangeProperty() {
236  return selectedTimeRange.getReadOnlyProperty();
237  }
238 
244  synchronized public Interval getSelectedTimeRange() {
245  return selectedTimeRange.get();
246  }
247 
248  synchronized public ReadOnlyBooleanProperty canAdvanceProperty() {
249  return historyManager.getCanAdvance();
250  }
251 
252  synchronized public ReadOnlyBooleanProperty canRetreatProperty() {
253  return historyManager.getCanRetreat();
254  }
255 
256  synchronized public ReadOnlyObjectProperty<ViewMode> viewModeProperty() {
257  return viewMode.getReadOnlyProperty();
258  }
259 
265  synchronized public void setViewMode(ViewMode viewMode) {
266  if (this.viewMode.get() != viewMode) {
267  this.viewMode.set(viewMode);
268  }
269  }
270 
276  synchronized public ViewMode getViewMode() {
277  return viewMode.get();
278  }
279 
280  TimeLineController(Case autoCase) throws TskCoreException {
281  this.autoCase = autoCase;
282  filteredEvents = new EventsModel(autoCase, currentParams.getReadOnlyProperty());
283  /*
284  * as the history manager's current state changes, modify the tags
285  * filter to be in sync, and expose that as propery from
286  * TimeLineController. Do we need to do this with datasource or hash hit
287  * filters?
288  */
289  historyManager.currentState().addListener((observable, oldState, newState) -> {
290  EventsModelParams historyManagerState = newState;
291  filteredEvents.addDataSourceFilters(historyManagerState.getEventFilterState());
292  currentParams.set(historyManagerState);
293 
294  });
295 
296  try {
297  InitialZoomState = new EventsModelParams(filteredEvents.getSpanningInterval(),
298  TimelineEventType.HierarchyLevel.CATEGORY,
299  filteredEvents.eventFilterProperty().get(),
300  TimelineLevelOfDetail.LOW);
301  } catch (TskCoreException ex) {
302  throw new TskCoreException("Error getting spanning interval.", ex);
303  }
304  historyManager.advance(InitialZoomState);
305 
306  //clear the selected events when the view mode changes
307  viewMode.addListener(observable -> {
308  try {
309  selectEventIDs(Collections.emptySet());
310  } catch (TskCoreException ex) {
311  logger.log(Level.SEVERE, "Error clearing the timeline selection.", ex);
312  }
313  });
314  }
315 
320  return filteredEvents;
321  }
322 
323  public void applyDefaultFilters() {
324  pushFilters(filteredEvents.getDefaultEventFilterState());
325  }
326 
327  public void zoomOutToActivity() throws TskCoreException {
328  Interval boundingEventsInterval = filteredEvents.getSpanningInterval(getJodaTimeZone());
329  advance(filteredEvents.modelParamsProperty().get().withTimeRange(boundingEventsInterval));
330  }
331 
332  private final ObservableSet<DetailViewEvent> pinnedEvents = FXCollections.observableSet();
333  private final ObservableSet<DetailViewEvent> pinnedEventsUnmodifiable = FXCollections.unmodifiableObservableSet(pinnedEvents);
334 
335  public void pinEvent(DetailViewEvent event) {
336  pinnedEvents.add(event);
337  }
338 
339  public void unPinEvent(DetailViewEvent event) {
340  pinnedEvents.removeIf(event::equals);
341  }
342 
343  public ObservableSet<DetailViewEvent> getPinnedEvents() {
344  return pinnedEventsUnmodifiable;
345  }
346 
350  boolean showFullRange() throws TskCoreException {
351  synchronized (filteredEvents) {
352  return pushTimeRange(filteredEvents.getSpanningInterval());
353  }
354  }
355 
363  @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
364  private void showInListView(ViewInTimelineRequestedEvent requestEvent) throws TskCoreException {
365  synchronized (filteredEvents) {
366  setViewMode(ViewMode.LIST);
367  selectEventIDs(requestEvent.getEventIDs());
368  try {
369  if (pushTimeRange(requestEvent.getInterval()) == false) {
370  eventbus.post(requestEvent);
371  }
372  } catch (TskCoreException ex) {
373  throw new TskCoreException("Error pushing requested timerange.", ex);
374  }
375  }
376  }
377 
381  void shutDownTimeLineListeners() {
383  }
384 
388  @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
389  public void shutDownTimeLineGui() {
390  if (topComponent != null) {
391  topComponent.close();
392  topComponent = null;
393  }
394  }
395 
405  void showTimeLine(AbstractFile file, BlackboardArtifact artifact) {
406  Platform.runLater(() -> {
407  //if there is an existing prompt or progressdialog,...
408  if (promptDialogManager.bringCurrentDialogToFront()) {
409  //... just show that
410  } else {
411 
412  if ( //confirm timeline during ingest
413  IngestManager.getInstance().isIngestRunning()
414  && promptDialogManager.confirmDuringIngest() == false) {
415  return; //if they cancel, do nothing.
416  }
417  try {
418  if (file == null && artifact == null) {
419  SwingUtilities.invokeLater(TimeLineController.this::showWindow);
420  } else {
421  //prompt user to pick specific event and time range
422  ShowInTimelineDialog showInTimelineDilaog = (file == null)
423  ? new ShowInTimelineDialog(this, artifact)
424  : new ShowInTimelineDialog(this, file);
425  Optional<ViewInTimelineRequestedEvent> dialogResult = showInTimelineDilaog.showAndWait();
426  dialogResult.ifPresent(viewInTimelineRequestedEvent -> {
427  SwingUtilities.invokeLater(this::showWindow);
428  try {
429  showInListView(viewInTimelineRequestedEvent); //show requested event in list view
430  } catch (TskCoreException ex) {
431  logger.log(Level.SEVERE, "Error showing requested events in listview: " + viewInTimelineRequestedEvent, ex);
432  new Alert(Alert.AlertType.ERROR, "There was an error opening Timeline.").showAndWait();
433  }
434  });
435 
436  }
437  } catch (TskCoreException tskCoreException) {
438  logger.log(Level.SEVERE, "Error showing Timeline ", tskCoreException);
439  new Alert(Alert.AlertType.ERROR, "There was an error opening Timeline.").showAndWait();
440  }
441  }
442  });
443  }
444 
452  synchronized public void pushPeriod(ReadablePeriod period) throws TskCoreException {
453  synchronized (filteredEvents) {
454  pushTimeRange(IntervalUtils.getIntervalAroundMiddle(filteredEvents.getTimeRange(), period));
455  }
456  }
457 
458  synchronized public void pushZoomOutTime() throws TskCoreException {
459  final Interval timeRange = filteredEvents.getTimeRange();
460  long toDurationMillis = timeRange.toDurationMillis() / 4;
461  DateTime start = timeRange.getStart().minus(toDurationMillis);
462  DateTime end = timeRange.getEnd().plus(toDurationMillis);
463  pushTimeRange(new Interval(start, end));
464  }
465 
466  synchronized public void pushZoomInTime() throws TskCoreException {
467  final Interval timeRange = filteredEvents.getTimeRange();
468  long toDurationMillis = timeRange.toDurationMillis() / 4;
469  DateTime start = timeRange.getStart().plus(toDurationMillis);
470  DateTime end = timeRange.getEnd().minus(toDurationMillis);
471  pushTimeRange(new Interval(start, end));
472  }
473 
479  synchronized private void showWindow() {
480  if (topComponent == null) {
481  topComponent = new TimeLineTopComponent(this);
482  }
483  if (topComponent.isOpened() == false) {
484  topComponent.open();
485  }
486  topComponent.toFront();
487  /*
488  * Make this top component active so its ExplorerManager's lookup gets
489  * proxied in Utilities.actionsGlobalContext()
490  */
491  topComponent.requestActive();
492  }
493 
494  synchronized public TimeLineTopComponent getTopComponent() {
495  return topComponent;
496  }
497 
498  synchronized public void pushEventTypeZoom(TimelineEventType.HierarchyLevel typeZoomeLevel) {
499  EventsModelParams currentZoom = filteredEvents.modelParamsProperty().get();
500  if (currentZoom == null) {
501  advance(InitialZoomState.withTypeZoomLevel(typeZoomeLevel));
502  } else if (currentZoom.hasTypeZoomLevel(typeZoomeLevel) == false) {
503  advance(currentZoom.withTypeZoomLevel(typeZoomeLevel));
504  }
505  }
506 
516  synchronized public boolean pushTimeRange(Interval timeRange) throws TskCoreException {
517  //clamp timerange to case
518  Interval clampedTimeRange;
519  if (timeRange == null) {
520  clampedTimeRange = this.filteredEvents.getSpanningInterval();
521  } else {
522  Interval spanningInterval = this.filteredEvents.getSpanningInterval();
523  if (spanningInterval.overlaps(timeRange)) {
524  clampedTimeRange = spanningInterval.overlap(timeRange);
525  } else {
526  clampedTimeRange = spanningInterval;
527  }
528  }
529 
530  EventsModelParams currentZoom = filteredEvents.modelParamsProperty().get();
531  if (currentZoom == null) {
532  advance(InitialZoomState.withTimeRange(clampedTimeRange));
533  return true;
534  } else if (currentZoom.hasTimeRange(clampedTimeRange) == false) {
535  advance(currentZoom.withTimeRange(clampedTimeRange));
536  return true;
537  } else {
538  return false;
539  }
540  }
541 
552  synchronized public boolean pushTimeUnit(TimeUnits timeUnit) throws TskCoreException {
553  if (timeUnit == TimeUnits.FOREVER) {
554  return showFullRange();
555  } else {
556  return pushTimeRange(IntervalUtils.getIntervalAroundMiddle(filteredEvents.getTimeRange(), timeUnit.toUnitPeriod()));
557  }
558  }
559 
560  synchronized public void pushDescrLOD(TimelineLevelOfDetail newLOD) {
561  EventsModelParams currentZoom = filteredEvents.modelParamsProperty().get();
562  if (currentZoom == null) {
563  advance(InitialZoomState.withDescrLOD(newLOD));
564  } else if (currentZoom.hasDescrLOD(newLOD) == false) {
565  advance(currentZoom.withDescrLOD(newLOD));
566  }
567  }
568 
569  @SuppressWarnings("AssignmentToMethodParameter") //clamp timerange to case
570  synchronized public void pushTimeAndType(Interval timeRange, TimelineEventType.HierarchyLevel typeZoom) throws TskCoreException {
571  Interval overlappingTimeRange = this.filteredEvents.getSpanningInterval().overlap(timeRange);
572  EventsModelParams currentZoom = filteredEvents.modelParamsProperty().get();
573  if (currentZoom == null) {
574  advance(InitialZoomState.withTimeAndType(overlappingTimeRange, typeZoom));
575  } else if (currentZoom.hasTimeRange(overlappingTimeRange) == false && currentZoom.hasTypeZoomLevel(typeZoom) == false) {
576  advance(currentZoom.withTimeAndType(overlappingTimeRange, typeZoom));
577  } else if (currentZoom.hasTimeRange(overlappingTimeRange) == false) {
578  advance(currentZoom.withTimeRange(overlappingTimeRange));
579  } else if (currentZoom.hasTypeZoomLevel(typeZoom) == false) {
580  advance(currentZoom.withTypeZoomLevel(typeZoom));
581  }
582  }
583 
584  synchronized public void pushFilters(RootFilterState filter) {
585  EventsModelParams currentZoom = filteredEvents.modelParamsProperty().get();
586  if (currentZoom == null) {
587  advance(InitialZoomState.withFilterState(filter));
588  } else if (currentZoom.hasFilterState(filter) == false) {
589  advance(currentZoom.withFilterState(filter));
590  }
591  }
592 
593  synchronized public void advance() {
594  historyManager.advance();
595  }
596 
597  synchronized public void retreat() {
598  historyManager.retreat();
599  }
600 
601  synchronized private void advance(EventsModelParams newState) {
602  historyManager.advance(newState);
603  }
604 
611  final synchronized public void selectEventIDs(Collection<Long> eventIDs) throws TskCoreException {
612  selectedTimeRange.set(filteredEvents.getSpanningInterval(eventIDs));
613  selectedEventIDs.setAll(eventIDs);
614  }
615 
616  public void selectTimeAndType(Interval interval, TimelineEventType type) throws TskCoreException {
617  final Interval timeRange = filteredEvents.getSpanningInterval().overlap(interval);
618 
619  final LoggedTask<Collection<Long>> selectTimeAndTypeTask = new LoggedTask<Collection<Long>>("Select Time and Type", true) { //NON-NLS
620  @Override
621  protected Collection< Long> call() throws Exception {
622  synchronized (TimeLineController.this) {
623  return filteredEvents.getEventIDs(timeRange, new SqlFilterState<>(new EventTypeFilter(type), true));
624  }
625  }
626 
627  @Override
628  protected void succeeded() {
629  super.succeeded();
630  try {
631  synchronized (TimeLineController.this) {
632  selectedTimeRange.set(timeRange);
633  selectedEventIDs.setAll(get());
634 
635  }
636  } catch (InterruptedException | ExecutionException ex) {
637  logger.log(Level.SEVERE, getTitle() + " Unexpected error", ex); //NON-NLS
638  }
639  }
640  };
641 
642  monitorTask(selectTimeAndTypeTask);
643  }
644 
651  synchronized public void monitorTask(final Task<?> task) {
652  //TODO: refactor this to use JavaFX Service? -jm
653  if (task != null) {
654  Platform.runLater(() -> {
655 
656  //is this actually threadsafe, could we get a finished task stuck in the list?
657  task.stateProperty().addListener((Observable observable) -> {
658  switch (task.getState()) {
659  case READY:
660  case RUNNING:
661  case SCHEDULED:
662  break;
663  case SUCCEEDED:
664  case CANCELLED:
665  case FAILED:
666  tasks.remove(task);
667  if (tasks.isEmpty() == false) {
668  taskProgress.bind(tasks.get(0).progressProperty());
669  taskMessage.bind(tasks.get(0).messageProperty());
670  taskTitle.bind(tasks.get(0).titleProperty());
671  }
672  break;
673  }
674  });
675  tasks.add(task);
676  taskProgress.bind(task.progressProperty());
677  taskMessage.bind(task.messageProperty());
678  taskTitle.bind(task.titleProperty());
679  switch (task.getState()) {
680  case READY:
681  //TODO: Check future result for errors....
682  executor.submit(task);
683  break;
684  case SCHEDULED:
685  case RUNNING:
686 
687  case SUCCEEDED:
688  case CANCELLED:
689  case FAILED:
690  tasks.remove(task);
691  if (tasks.isEmpty() == false) {
692  taskProgress.bind(tasks.get(0).progressProperty());
693  taskMessage.bind(tasks.get(0).messageProperty());
694  taskTitle.bind(tasks.get(0).titleProperty());
695  }
696  break;
697  }
698  });
699  }
700  }
701 
708  synchronized public void registerForEvents(Object listener) {
709  eventbus.register(listener);
710  }
711 
717  synchronized public void unRegisterForEvents(Object listener) {
718  eventbus.unregister(listener);
719  }
720 
721  static synchronized public void setTimeZone(TimeZone timeZone) {
722  TimeLineController.timeZone.set(timeZone);
723 
724  }
725 
726  void handleIngestModuleEvent(PropertyChangeEvent evt) {
732  try {
734  } catch (NoCurrentCaseException notUsed) {
735  // Case is closed, do nothing.
736  return;
737  }
738  // ignore remote events. The node running the ingest should update the Case DB
739  // @@@ We should signal though that there is more data and flush caches...
740  if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.REMOTE) {
741  return;
742  }
743 
744  switch (IngestManager.IngestModuleEvent.valueOf(evt.getPropertyName())) {
745  case CONTENT_CHANGED:
746  // new files were already added to the events table from SleuthkitCase.
747  break;
748  case DATA_ADDED:
749  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
750  if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == TSK_HASHSET_HIT.getTypeID()) {
751  logFutureException(executor.submit(() -> filteredEvents.updateEventsForHashSetHits(eventData.getArtifacts())),
752  "Error executing task in response to DATA_ADDED event.",
753  "Error executing response to new data.");
754  }
755  break;
756  case FILE_DONE:
757  /*
758  * Since the known state or hash hit state may have changed
759  * invalidate caches.
760  */
761  //@@@ This causes HUGE slow downs during ingest when TL is open.
762  // executor.submit(filteredEvents::invalidateAllCaches);
763 
764  // known state should have been udpated automatically via SleuthkitCase.setKnown();
765  // hashes should have been updated from event
766  }
767  }
768 
769  void handleCaseEvent(PropertyChangeEvent evt) {
770  ListenableFuture<?> future = Futures.immediateFuture(null);
771  switch (Case.Events.valueOf(evt.getPropertyName())) {
772  case BLACKBOARD_ARTIFACT_TAG_ADDED:
773  future = executor.submit(() -> filteredEvents.handleArtifactTagAdded((BlackBoardArtifactTagAddedEvent) evt));
774  break;
775  case BLACKBOARD_ARTIFACT_TAG_DELETED:
776  future = executor.submit(() -> filteredEvents.handleArtifactTagDeleted((BlackBoardArtifactTagDeletedEvent) evt));
777  break;
778  case CONTENT_TAG_ADDED:
779  future = executor.submit(() -> filteredEvents.handleContentTagAdded((ContentTagAddedEvent) evt));
780  break;
781  case CONTENT_TAG_DELETED:
782  future = executor.submit(() -> filteredEvents.handleContentTagDeleted((ContentTagDeletedEvent) evt));
783  break;
784  case DATA_SOURCE_ADDED:
785  future = executor.submit(() -> {
786  filteredEvents.handleDataSourceAdded();
787  return null;
788  });
789  break;
790  case TIMELINE_EVENT_ADDED:
791  future = executor.submit(() -> {
792  filteredEvents.invalidateCaches(singleton(((TimelineEventAddedEvent) evt).getAddedEventID()));
793  return null;
794  });
795  break;
796  }
797  logFutureException(future,
798  "Error executing task in response to " + evt.getPropertyName() + " event.",
799  "Error executing task in response to case event.");
800  }
801 
802  private void logFutureException(ListenableFuture<?> future, String errorLogMessage, String errorUserMessage) {
803  future.addListener(() -> {
804  try {
805  future.get();
806  } catch (InterruptedException | ExecutionException ex) {
807  logger.log(Level.SEVERE, errorLogMessage, ex);
808  }
809  }, MoreExecutors.directExecutor());
810  }
811 }
synchronized ReadOnlyDoubleProperty taskProgressProperty()
EventsModelParams withTimeAndType(Interval timeRange, TimelineEventType.HierarchyLevel zoomLevel)
static final ReadOnlyObjectWrapper< TimeZone > timeZone
void logFutureException(ListenableFuture<?> future, String errorLogMessage, String errorUserMessage)
synchronized void setViewMode(ViewMode viewMode)
synchronized ReadOnlyObjectProperty< Interval > selectedTimeRangeProperty()
synchronized boolean pushTimeUnit(TimeUnits timeUnit)
synchronized boolean pushTimeRange(Interval timeRange)
synchronized ReadOnlyStringProperty taskTitleProperty()
static void shutDownTaskExecutor(ExecutorService executor)
synchronized void unRegisterForEvents(Object listener)
static ReadOnlyObjectProperty< TimeZone > timeZoneProperty()
synchronized ReadOnlyStringProperty taskMessageProperty()
EventsModelParams withTypeZoomLevel(TimelineEventType.HierarchyLevel zoomLevel)
static Interval getIntervalAroundMiddle(Interval interval, ReadablePeriod period)
synchronized void pushEventTypeZoom(TimelineEventType.HierarchyLevel typeZoomeLevel)
synchronized void registerForEvents(Object listener)
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)
boolean hasTypeZoomLevel(TimelineEventType.HierarchyLevel typeZoom)
EventsModelParams withDescrLOD(TimelineLevelOfDetail descrLOD)
synchronized void advance(EventsModelParams newState)
synchronized TimeLineTopComponent getTopComponent()
synchronized ReadOnlyObjectProperty< ViewMode > viewModeProperty()
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
synchronized ReadOnlyBooleanProperty canAdvanceProperty()
EventsModelParams withFilterState(RootFilterState filter)
ObservableSet< DetailViewEvent > getPinnedEvents()
synchronized ReadOnlyListProperty< Task<?> > getTasks()
synchronized ReadOnlyBooleanProperty canRetreatProperty()
synchronized void pushDescrLOD(TimelineLevelOfDetail newLOD)
void selectTimeAndType(Interval interval, TimelineEventType type)

Copyright © 2012-2021 Basis Technology. Generated on: Thu Sep 30 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.