Autopsy  4.14.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;
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  private boolean showFullRange() throws TskCoreException {
351  synchronized (filteredEvents) {
352  return pushTimeRange(filteredEvents.getSpanningInterval());
353  }
354  }
355 
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 
383  public void shutDownTimeLine() {
384  if (topComponent != null) {
385  topComponent.close();
386  topComponent = null;
387  }
388  }
389 
399  void showTimeLine(AbstractFile file, BlackboardArtifact artifact) {
400  Platform.runLater(() -> {
401  //if there is an existing prompt or progressdialog,...
402  if (promptDialogManager.bringCurrentDialogToFront()) {
403  //... just show that
404  } else {
405 
406  if ( //confirm timeline during ingest
407  IngestManager.getInstance().isIngestRunning()
408  && promptDialogManager.confirmDuringIngest() == false) {
409  return; //if they cancel, do nothing.
410  }
411  try {
412  if (file == null && artifact == null) {
413  SwingUtilities.invokeLater(TimeLineController.this::showWindow);
414  this.showFullRange();
415  } else {
416 
417  //prompt user to pick specific event and time range
418  ShowInTimelineDialog showInTimelineDilaog = (file == null)
419  ? new ShowInTimelineDialog(this, artifact)
420  : new ShowInTimelineDialog(this, file);
421  Optional<ViewInTimelineRequestedEvent> dialogResult = showInTimelineDilaog.showAndWait();
422  dialogResult.ifPresent(viewInTimelineRequestedEvent -> {
423  SwingUtilities.invokeLater(this::showWindow);
424  try {
425  showInListView(viewInTimelineRequestedEvent); //show requested event in list view
426  } catch (TskCoreException ex) {
427  logger.log(Level.SEVERE, "Error showing requested events in listview: " + viewInTimelineRequestedEvent, ex);
428  new Alert(Alert.AlertType.ERROR, "There was an error opening Timeline.").showAndWait();
429  }
430  });
431 
432  }
433  } catch (TskCoreException tskCoreException) {
434  logger.log(Level.SEVERE, "Error showing Timeline ", tskCoreException);
435  new Alert(Alert.AlertType.ERROR, "There was an error opening Timeline.").showAndWait();
436  }
437  }
438  });
439  }
440 
448  synchronized public void pushPeriod(ReadablePeriod period) throws TskCoreException {
449  synchronized (filteredEvents) {
450  pushTimeRange(IntervalUtils.getIntervalAroundMiddle(filteredEvents.getTimeRange(), period));
451  }
452  }
453 
454  synchronized public void pushZoomOutTime() throws TskCoreException {
455  final Interval timeRange = filteredEvents.getTimeRange();
456  long toDurationMillis = timeRange.toDurationMillis() / 4;
457  DateTime start = timeRange.getStart().minus(toDurationMillis);
458  DateTime end = timeRange.getEnd().plus(toDurationMillis);
459  pushTimeRange(new Interval(start, end));
460  }
461 
462  synchronized public void pushZoomInTime() throws TskCoreException {
463  final Interval timeRange = filteredEvents.getTimeRange();
464  long toDurationMillis = timeRange.toDurationMillis() / 4;
465  DateTime start = timeRange.getStart().plus(toDurationMillis);
466  DateTime end = timeRange.getEnd().minus(toDurationMillis);
467  pushTimeRange(new Interval(start, end));
468  }
469 
475  synchronized private void showWindow() {
476  if (topComponent == null) {
477  topComponent = new TimeLineTopComponent(this);
478  }
479  if (topComponent.isOpened() == false) {
480  topComponent.open();
481  }
482  topComponent.toFront();
483  /*
484  * Make this top component active so its ExplorerManager's lookup gets
485  * proxied in Utilities.actionsGlobalContext()
486  */
487  topComponent.requestActive();
488  }
489 
490  synchronized public TimeLineTopComponent getTopComponent(){
491  return topComponent;
492  }
493 
494  synchronized public void pushEventTypeZoom(TimelineEventType.HierarchyLevel typeZoomeLevel) {
495  EventsModelParams currentZoom = filteredEvents.modelParamsProperty().get();
496  if (currentZoom == null) {
497  advance(InitialZoomState.withTypeZoomLevel(typeZoomeLevel));
498  } else if (currentZoom.hasTypeZoomLevel(typeZoomeLevel) == false) {
499  advance(currentZoom.withTypeZoomLevel(typeZoomeLevel));
500  }
501  }
502 
512  synchronized public boolean pushTimeRange(Interval timeRange) throws TskCoreException {
513  //clamp timerange to case
514  Interval clampedTimeRange;
515  if (timeRange == null) {
516  clampedTimeRange = this.filteredEvents.getSpanningInterval();
517  } else {
518  Interval spanningInterval = this.filteredEvents.getSpanningInterval();
519  if (spanningInterval.overlaps(timeRange)) {
520  clampedTimeRange = spanningInterval.overlap(timeRange);
521  } else {
522  clampedTimeRange = spanningInterval;
523  }
524  }
525 
526  EventsModelParams currentZoom = filteredEvents.modelParamsProperty().get();
527  if (currentZoom == null) {
528  advance(InitialZoomState.withTimeRange(clampedTimeRange));
529  return true;
530  } else if (currentZoom.hasTimeRange(clampedTimeRange) == false) {
531  advance(currentZoom.withTimeRange(clampedTimeRange));
532  return true;
533  } else {
534  return false;
535  }
536  }
537 
548  synchronized public boolean pushTimeUnit(TimeUnits timeUnit) throws TskCoreException {
549  if (timeUnit == TimeUnits.FOREVER) {
550  return showFullRange();
551  } else {
552  return pushTimeRange(IntervalUtils.getIntervalAroundMiddle(filteredEvents.getTimeRange(), timeUnit.toUnitPeriod()));
553  }
554  }
555 
556  synchronized public void pushDescrLOD(TimelineLevelOfDetail newLOD) {
557  EventsModelParams currentZoom = filteredEvents.modelParamsProperty().get();
558  if (currentZoom == null) {
559  advance(InitialZoomState.withDescrLOD(newLOD));
560  } else if (currentZoom.hasDescrLOD(newLOD) == false) {
561  advance(currentZoom.withDescrLOD(newLOD));
562  }
563  }
564 
565  @SuppressWarnings("AssignmentToMethodParameter") //clamp timerange to case
566  synchronized public void pushTimeAndType(Interval timeRange, TimelineEventType.HierarchyLevel typeZoom) throws TskCoreException {
567  Interval overlappingTimeRange = this.filteredEvents.getSpanningInterval().overlap(timeRange);
568  EventsModelParams currentZoom = filteredEvents.modelParamsProperty().get();
569  if (currentZoom == null) {
570  advance(InitialZoomState.withTimeAndType(overlappingTimeRange, typeZoom));
571  } else if (currentZoom.hasTimeRange(overlappingTimeRange) == false && currentZoom.hasTypeZoomLevel(typeZoom) == false) {
572  advance(currentZoom.withTimeAndType(overlappingTimeRange, typeZoom));
573  } else if (currentZoom.hasTimeRange(overlappingTimeRange) == false) {
574  advance(currentZoom.withTimeRange(overlappingTimeRange));
575  } else if (currentZoom.hasTypeZoomLevel(typeZoom) == false) {
576  advance(currentZoom.withTypeZoomLevel(typeZoom));
577  }
578  }
579 
580  synchronized public void pushFilters(RootFilterState filter) {
581  EventsModelParams currentZoom = filteredEvents.modelParamsProperty().get();
582  if (currentZoom == null) {
583  advance(InitialZoomState.withFilterState(filter));
584  } else if (currentZoom.hasFilterState(filter) == false) {
585  advance(currentZoom.withFilterState(filter));
586  }
587  }
588 
589  synchronized public void advance() {
590  historyManager.advance();
591  }
592 
593  synchronized public void retreat() {
594  historyManager.retreat();
595  }
596 
597  synchronized private void advance(EventsModelParams newState) {
598  historyManager.advance(newState);
599  }
600 
607  final synchronized public void selectEventIDs(Collection<Long> eventIDs) throws TskCoreException {
608  selectedTimeRange.set(filteredEvents.getSpanningInterval(eventIDs));
609  selectedEventIDs.setAll(eventIDs);
610  }
611 
612  public void selectTimeAndType(Interval interval, TimelineEventType type) throws TskCoreException {
613  final Interval timeRange = filteredEvents.getSpanningInterval().overlap(interval);
614 
615  final LoggedTask<Collection<Long>> selectTimeAndTypeTask = new LoggedTask<Collection<Long>>("Select Time and Type", true) { //NON-NLS
616  @Override
617  protected Collection< Long> call() throws Exception {
618  synchronized (TimeLineController.this) {
619  return filteredEvents.getEventIDs(timeRange, new SqlFilterState<>(new EventTypeFilter(type), true));
620  }
621  }
622 
623  @Override
624  protected void succeeded() {
625  super.succeeded();
626  try {
627  synchronized (TimeLineController.this) {
628  selectedTimeRange.set(timeRange);
629  selectedEventIDs.setAll(get());
630 
631  }
632  } catch (InterruptedException | ExecutionException ex) {
633  logger.log(Level.SEVERE, getTitle() + " Unexpected error", ex); //NON-NLS
634  }
635  }
636  };
637 
638  monitorTask(selectTimeAndTypeTask);
639  }
640 
647  synchronized public void monitorTask(final Task<?> task) {
648  //TODO: refactor this to use JavaFX Service? -jm
649  if (task != null) {
650  Platform.runLater(() -> {
651 
652  //is this actually threadsafe, could we get a finished task stuck in the list?
653  task.stateProperty().addListener((Observable observable) -> {
654  switch (task.getState()) {
655  case READY:
656  case RUNNING:
657  case SCHEDULED:
658  break;
659  case SUCCEEDED:
660  case CANCELLED:
661  case FAILED:
662  tasks.remove(task);
663  if (tasks.isEmpty() == false) {
664  taskProgress.bind(tasks.get(0).progressProperty());
665  taskMessage.bind(tasks.get(0).messageProperty());
666  taskTitle.bind(tasks.get(0).titleProperty());
667  }
668  break;
669  }
670  });
671  tasks.add(task);
672  taskProgress.bind(task.progressProperty());
673  taskMessage.bind(task.messageProperty());
674  taskTitle.bind(task.titleProperty());
675  switch (task.getState()) {
676  case READY:
677  //TODO: Check future result for errors....
678  executor.submit(task);
679  break;
680  case SCHEDULED:
681  case RUNNING:
682 
683  case SUCCEEDED:
684  case CANCELLED:
685  case FAILED:
686  tasks.remove(task);
687  if (tasks.isEmpty() == false) {
688  taskProgress.bind(tasks.get(0).progressProperty());
689  taskMessage.bind(tasks.get(0).messageProperty());
690  taskTitle.bind(tasks.get(0).titleProperty());
691  }
692  break;
693  }
694  });
695  }
696  }
697 
704  synchronized public void registerForEvents(Object listener) {
705  eventbus.register(listener);
706  }
707 
713  synchronized public void unRegisterForEvents(Object listener) {
714  eventbus.unregister(listener);
715  }
716 
717  static synchronized public void setTimeZone(TimeZone timeZone) {
718  TimeLineController.timeZone.set(timeZone);
719 
720  }
721 
722  void handleIngestModuleEvent(PropertyChangeEvent evt) {
728  try {
730  } catch (NoCurrentCaseException notUsed) {
731  // Case is closed, do nothing.
732  return;
733  }
734  // ignore remote events. The node running the ingest should update the Case DB
735  // @@@ We should signal though that there is more data and flush caches...
736  if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.REMOTE) {
737  return;
738  }
739 
740  switch (IngestManager.IngestModuleEvent.valueOf(evt.getPropertyName())) {
741  case CONTENT_CHANGED:
742  // new files were already added to the events table from SleuthkitCase.
743  break;
744  case DATA_ADDED:
745  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
746  if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == TSK_HASHSET_HIT.getTypeID()) {
747  logFutureException(executor.submit(() -> filteredEvents.updateEventsForHashSetHits(eventData.getArtifacts())),
748  "Error executing task in response to DATA_ADDED event.",
749  "Error executing response to new data.");
750  }
751  break;
752  case FILE_DONE:
753  /*
754  * Since the known state or hash hit state may have changed
755  * invalidate caches.
756  */
757  //@@@ This causes HUGE slow downs during ingest when TL is open.
758  // executor.submit(filteredEvents::invalidateAllCaches);
759 
760  // known state should have been udpated automatically via SleuthkitCase.setKnown();
761  // hashes should have been updated from event
762  }
763  }
764 
765  void handleCaseEvent(PropertyChangeEvent evt) {
766  ListenableFuture<?> future = Futures.immediateFuture(null);
767  switch (Case.Events.valueOf(evt.getPropertyName())) {
768  case BLACKBOARD_ARTIFACT_TAG_ADDED:
769  future = executor.submit(() -> filteredEvents.handleArtifactTagAdded((BlackBoardArtifactTagAddedEvent) evt));
770  break;
771  case BLACKBOARD_ARTIFACT_TAG_DELETED:
772  future = executor.submit(() -> filteredEvents.handleArtifactTagDeleted((BlackBoardArtifactTagDeletedEvent) evt));
773  break;
774  case CONTENT_TAG_ADDED:
775  future = executor.submit(() -> filteredEvents.handleContentTagAdded((ContentTagAddedEvent) evt));
776  break;
777  case CONTENT_TAG_DELETED:
778  future = executor.submit(() -> filteredEvents.handleContentTagDeleted((ContentTagDeletedEvent) evt));
779  break;
780  case CURRENT_CASE:
781  //close timeline on case changes.
782  SwingUtilities.invokeLater(TimeLineController.this::shutDownTimeLine);
783  break;
784  case DATA_SOURCE_ADDED:
785  future = executor.submit(() -> {
786  filteredEvents.invalidateCaches(null);
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()
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: Wed Apr 8 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.