Autopsy  4.16.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-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;
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.TskCoreException;
93 import org.sleuthkit.datamodel.TimelineEventType;
94 import org.sleuthkit.datamodel.TimelineFilter.EventTypeFilter;
95 import org.sleuthkit.datamodel.TimelineLevelOfDetail;
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 EventsModel filteredEvents;
203 
204  @GuardedBy("this")
205  private final EventsModelParams InitialZoomState;
206 
207  @GuardedBy("this")
208  private final History<EventsModelParams> historyManager = new History<>();
209 
210  @GuardedBy("this")
211  private final ReadOnlyObjectWrapper<EventsModelParams> 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 EventsModel(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  EventsModelParams historyManagerState = newState;
292  filteredEvents.addDataSourceFilters(historyManagerState.getEventFilterState());
293  currentParams.set(historyManagerState);
294 
295  });
296 
297  try {
298  InitialZoomState = new EventsModelParams(filteredEvents.getSpanningInterval(),
299  TimelineEventType.HierarchyLevel.CATEGORY,
300  filteredEvents.eventFilterProperty().get(),
301  TimelineLevelOfDetail.LOW);
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.getDefaultEventFilterState());
326  }
327 
328  public void zoomOutToActivity() throws TskCoreException {
329  Interval boundingEventsInterval = filteredEvents.getSpanningInterval(getJodaTimeZone());
330  advance(filteredEvents.modelParamsProperty().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 
379 
383  void shutDownTimeLineListeners() {
385  }
386 
390  @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
391  public void shutDownTimeLineGui() {
392  if (topComponent != null) {
393  topComponent.close();
394  topComponent = null;
395  }
396  }
397 
398 
399 
409  void showTimeLine(AbstractFile file, BlackboardArtifact artifact) {
410  Platform.runLater(() -> {
411  //if there is an existing prompt or progressdialog,...
412  if (promptDialogManager.bringCurrentDialogToFront()) {
413  //... just show that
414  } else {
415 
416  if ( //confirm timeline during ingest
417  IngestManager.getInstance().isIngestRunning()
418  && promptDialogManager.confirmDuringIngest() == false) {
419  return; //if they cancel, do nothing.
420  }
421  try {
422  if (file == null && artifact == null) {
423  SwingUtilities.invokeLater(TimeLineController.this::showWindow);
424  this.showFullRange();
425  } else {
426 
427  //prompt user to pick specific event and time range
428  ShowInTimelineDialog showInTimelineDilaog = (file == null)
429  ? new ShowInTimelineDialog(this, artifact)
430  : new ShowInTimelineDialog(this, file);
431  Optional<ViewInTimelineRequestedEvent> dialogResult = showInTimelineDilaog.showAndWait();
432  dialogResult.ifPresent(viewInTimelineRequestedEvent -> {
433  SwingUtilities.invokeLater(this::showWindow);
434  try {
435  showInListView(viewInTimelineRequestedEvent); //show requested event in list view
436  } catch (TskCoreException ex) {
437  logger.log(Level.SEVERE, "Error showing requested events in listview: " + viewInTimelineRequestedEvent, ex);
438  new Alert(Alert.AlertType.ERROR, "There was an error opening Timeline.").showAndWait();
439  }
440  });
441 
442  }
443  } catch (TskCoreException tskCoreException) {
444  logger.log(Level.SEVERE, "Error showing Timeline ", tskCoreException);
445  new Alert(Alert.AlertType.ERROR, "There was an error opening Timeline.").showAndWait();
446  }
447  }
448  });
449  }
450 
458  synchronized public void pushPeriod(ReadablePeriod period) throws TskCoreException {
459  synchronized (filteredEvents) {
460  pushTimeRange(IntervalUtils.getIntervalAroundMiddle(filteredEvents.getTimeRange(), period));
461  }
462  }
463 
464  synchronized public void pushZoomOutTime() throws TskCoreException {
465  final Interval timeRange = filteredEvents.getTimeRange();
466  long toDurationMillis = timeRange.toDurationMillis() / 4;
467  DateTime start = timeRange.getStart().minus(toDurationMillis);
468  DateTime end = timeRange.getEnd().plus(toDurationMillis);
469  pushTimeRange(new Interval(start, end));
470  }
471 
472  synchronized public void pushZoomInTime() throws TskCoreException {
473  final Interval timeRange = filteredEvents.getTimeRange();
474  long toDurationMillis = timeRange.toDurationMillis() / 4;
475  DateTime start = timeRange.getStart().plus(toDurationMillis);
476  DateTime end = timeRange.getEnd().minus(toDurationMillis);
477  pushTimeRange(new Interval(start, end));
478  }
479 
485  synchronized private void showWindow() {
486  if (topComponent == null) {
487  topComponent = new TimeLineTopComponent(this);
488  }
489  if (topComponent.isOpened() == false) {
490  topComponent.open();
491  }
492  topComponent.toFront();
493  /*
494  * Make this top component active so its ExplorerManager's lookup gets
495  * proxied in Utilities.actionsGlobalContext()
496  */
497  topComponent.requestActive();
498  }
499 
500  synchronized public TimeLineTopComponent getTopComponent(){
501  return topComponent;
502  }
503 
504  synchronized public void pushEventTypeZoom(TimelineEventType.HierarchyLevel typeZoomeLevel) {
505  EventsModelParams currentZoom = filteredEvents.modelParamsProperty().get();
506  if (currentZoom == null) {
507  advance(InitialZoomState.withTypeZoomLevel(typeZoomeLevel));
508  } else if (currentZoom.hasTypeZoomLevel(typeZoomeLevel) == false) {
509  advance(currentZoom.withTypeZoomLevel(typeZoomeLevel));
510  }
511  }
512 
522  synchronized public boolean pushTimeRange(Interval timeRange) throws TskCoreException {
523  //clamp timerange to case
524  Interval clampedTimeRange;
525  if (timeRange == null) {
526  clampedTimeRange = this.filteredEvents.getSpanningInterval();
527  } else {
528  Interval spanningInterval = this.filteredEvents.getSpanningInterval();
529  if (spanningInterval.overlaps(timeRange)) {
530  clampedTimeRange = spanningInterval.overlap(timeRange);
531  } else {
532  clampedTimeRange = spanningInterval;
533  }
534  }
535 
536  EventsModelParams currentZoom = filteredEvents.modelParamsProperty().get();
537  if (currentZoom == null) {
538  advance(InitialZoomState.withTimeRange(clampedTimeRange));
539  return true;
540  } else if (currentZoom.hasTimeRange(clampedTimeRange) == false) {
541  advance(currentZoom.withTimeRange(clampedTimeRange));
542  return true;
543  } else {
544  return false;
545  }
546  }
547 
558  synchronized public boolean pushTimeUnit(TimeUnits timeUnit) throws TskCoreException {
559  if (timeUnit == TimeUnits.FOREVER) {
560  return showFullRange();
561  } else {
562  return pushTimeRange(IntervalUtils.getIntervalAroundMiddle(filteredEvents.getTimeRange(), timeUnit.toUnitPeriod()));
563  }
564  }
565 
566  synchronized public void pushDescrLOD(TimelineLevelOfDetail newLOD) {
567  EventsModelParams currentZoom = filteredEvents.modelParamsProperty().get();
568  if (currentZoom == null) {
569  advance(InitialZoomState.withDescrLOD(newLOD));
570  } else if (currentZoom.hasDescrLOD(newLOD) == false) {
571  advance(currentZoom.withDescrLOD(newLOD));
572  }
573  }
574 
575  @SuppressWarnings("AssignmentToMethodParameter") //clamp timerange to case
576  synchronized public void pushTimeAndType(Interval timeRange, TimelineEventType.HierarchyLevel typeZoom) throws TskCoreException {
577  Interval overlappingTimeRange = this.filteredEvents.getSpanningInterval().overlap(timeRange);
578  EventsModelParams currentZoom = filteredEvents.modelParamsProperty().get();
579  if (currentZoom == null) {
580  advance(InitialZoomState.withTimeAndType(overlappingTimeRange, typeZoom));
581  } else if (currentZoom.hasTimeRange(overlappingTimeRange) == false && currentZoom.hasTypeZoomLevel(typeZoom) == false) {
582  advance(currentZoom.withTimeAndType(overlappingTimeRange, typeZoom));
583  } else if (currentZoom.hasTimeRange(overlappingTimeRange) == false) {
584  advance(currentZoom.withTimeRange(overlappingTimeRange));
585  } else if (currentZoom.hasTypeZoomLevel(typeZoom) == false) {
586  advance(currentZoom.withTypeZoomLevel(typeZoom));
587  }
588  }
589 
590  synchronized public void pushFilters(RootFilterState filter) {
591  EventsModelParams currentZoom = filteredEvents.modelParamsProperty().get();
592  if (currentZoom == null) {
593  advance(InitialZoomState.withFilterState(filter));
594  } else if (currentZoom.hasFilterState(filter) == false) {
595  advance(currentZoom.withFilterState(filter));
596  }
597  }
598 
599  synchronized public void advance() {
600  historyManager.advance();
601  }
602 
603  synchronized public void retreat() {
604  historyManager.retreat();
605  }
606 
607  synchronized private void advance(EventsModelParams newState) {
608  historyManager.advance(newState);
609  }
610 
617  final synchronized public void selectEventIDs(Collection<Long> eventIDs) throws TskCoreException {
618  selectedTimeRange.set(filteredEvents.getSpanningInterval(eventIDs));
619  selectedEventIDs.setAll(eventIDs);
620  }
621 
622  public void selectTimeAndType(Interval interval, TimelineEventType type) throws TskCoreException {
623  final Interval timeRange = filteredEvents.getSpanningInterval().overlap(interval);
624 
625  final LoggedTask<Collection<Long>> selectTimeAndTypeTask = new LoggedTask<Collection<Long>>("Select Time and Type", true) { //NON-NLS
626  @Override
627  protected Collection< Long> call() throws Exception {
628  synchronized (TimeLineController.this) {
629  return filteredEvents.getEventIDs(timeRange, new SqlFilterState<>(new EventTypeFilter(type), true));
630  }
631  }
632 
633  @Override
634  protected void succeeded() {
635  super.succeeded();
636  try {
637  synchronized (TimeLineController.this) {
638  selectedTimeRange.set(timeRange);
639  selectedEventIDs.setAll(get());
640 
641  }
642  } catch (InterruptedException | ExecutionException ex) {
643  logger.log(Level.SEVERE, getTitle() + " Unexpected error", ex); //NON-NLS
644  }
645  }
646  };
647 
648  monitorTask(selectTimeAndTypeTask);
649  }
650 
657  synchronized public void monitorTask(final Task<?> task) {
658  //TODO: refactor this to use JavaFX Service? -jm
659  if (task != null) {
660  Platform.runLater(() -> {
661 
662  //is this actually threadsafe, could we get a finished task stuck in the list?
663  task.stateProperty().addListener((Observable observable) -> {
664  switch (task.getState()) {
665  case READY:
666  case RUNNING:
667  case SCHEDULED:
668  break;
669  case SUCCEEDED:
670  case CANCELLED:
671  case FAILED:
672  tasks.remove(task);
673  if (tasks.isEmpty() == false) {
674  taskProgress.bind(tasks.get(0).progressProperty());
675  taskMessage.bind(tasks.get(0).messageProperty());
676  taskTitle.bind(tasks.get(0).titleProperty());
677  }
678  break;
679  }
680  });
681  tasks.add(task);
682  taskProgress.bind(task.progressProperty());
683  taskMessage.bind(task.messageProperty());
684  taskTitle.bind(task.titleProperty());
685  switch (task.getState()) {
686  case READY:
687  //TODO: Check future result for errors....
688  executor.submit(task);
689  break;
690  case SCHEDULED:
691  case RUNNING:
692 
693  case SUCCEEDED:
694  case CANCELLED:
695  case FAILED:
696  tasks.remove(task);
697  if (tasks.isEmpty() == false) {
698  taskProgress.bind(tasks.get(0).progressProperty());
699  taskMessage.bind(tasks.get(0).messageProperty());
700  taskTitle.bind(tasks.get(0).titleProperty());
701  }
702  break;
703  }
704  });
705  }
706  }
707 
714  synchronized public void registerForEvents(Object listener) {
715  eventbus.register(listener);
716  }
717 
723  synchronized public void unRegisterForEvents(Object listener) {
724  eventbus.unregister(listener);
725  }
726 
727  static synchronized public void setTimeZone(TimeZone timeZone) {
728  TimeLineController.timeZone.set(timeZone);
729 
730  }
731 
732  void handleIngestModuleEvent(PropertyChangeEvent evt) {
738  try {
740  } catch (NoCurrentCaseException notUsed) {
741  // Case is closed, do nothing.
742  return;
743  }
744  // ignore remote events. The node running the ingest should update the Case DB
745  // @@@ We should signal though that there is more data and flush caches...
746  if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.REMOTE) {
747  return;
748  }
749 
750  switch (IngestManager.IngestModuleEvent.valueOf(evt.getPropertyName())) {
751  case CONTENT_CHANGED:
752  // new files were already added to the events table from SleuthkitCase.
753  break;
754  case DATA_ADDED:
755  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
756  if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == TSK_HASHSET_HIT.getTypeID()) {
757  logFutureException(executor.submit(() -> filteredEvents.updateEventsForHashSetHits(eventData.getArtifacts())),
758  "Error executing task in response to DATA_ADDED event.",
759  "Error executing response to new data.");
760  }
761  break;
762  case FILE_DONE:
763  /*
764  * Since the known state or hash hit state may have changed
765  * invalidate caches.
766  */
767  //@@@ This causes HUGE slow downs during ingest when TL is open.
768  // executor.submit(filteredEvents::invalidateAllCaches);
769 
770  // known state should have been udpated automatically via SleuthkitCase.setKnown();
771  // hashes should have been updated from event
772  }
773  }
774 
775  void handleCaseEvent(PropertyChangeEvent evt) {
776  ListenableFuture<?> future = Futures.immediateFuture(null);
777  switch (Case.Events.valueOf(evt.getPropertyName())) {
778  case BLACKBOARD_ARTIFACT_TAG_ADDED:
779  future = executor.submit(() -> filteredEvents.handleArtifactTagAdded((BlackBoardArtifactTagAddedEvent) evt));
780  break;
781  case BLACKBOARD_ARTIFACT_TAG_DELETED:
782  future = executor.submit(() -> filteredEvents.handleArtifactTagDeleted((BlackBoardArtifactTagDeletedEvent) evt));
783  break;
784  case CONTENT_TAG_ADDED:
785  future = executor.submit(() -> filteredEvents.handleContentTagAdded((ContentTagAddedEvent) evt));
786  break;
787  case CONTENT_TAG_DELETED:
788  future = executor.submit(() -> filteredEvents.handleContentTagDeleted((ContentTagDeletedEvent) evt));
789  break;
790  case DATA_SOURCE_ADDED:
791  future = executor.submit(() -> {
792  filteredEvents.handleDataSourceAdded();
793  return null;
794  });
795  break;
796  case TIMELINE_EVENT_ADDED:
797  future = executor.submit(() -> {
798  filteredEvents.invalidateCaches(singleton(((TimelineEventAddedEvent) evt).getAddedEventID()));
799  return null;
800  });
801  break;
802  }
803  logFutureException(future,
804  "Error executing task in response to " + evt.getPropertyName() + " event.",
805  "Error executing task in response to case event.");
806  }
807 
808  private void logFutureException(ListenableFuture<?> future, String errorLogMessage, String errorUserMessage) {
809  future.addListener(() -> {
810  try {
811  future.get();
812  } catch (InterruptedException | ExecutionException ex) {
813  logger.log(Level.SEVERE, errorLogMessage, ex);
814  }
815  }, MoreExecutors.directExecutor());
816  }
817 }
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-2020 Basis Technology. Generated on: Tue Sep 22 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.