Autopsy  4.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-2016 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 java.awt.HeadlessException;
22 import java.beans.PropertyChangeEvent;
23 import java.beans.PropertyChangeListener;
24 import java.io.IOException;
25 import java.time.ZoneId;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.MissingResourceException;
29 import java.util.TimeZone;
30 import java.util.concurrent.ExecutionException;
31 import java.util.concurrent.ExecutorService;
32 import java.util.concurrent.Executors;
33 import java.util.function.Consumer;
34 import java.util.function.Function;
35 import java.util.logging.Level;
36 import javafx.application.Platform;
37 import javafx.beans.Observable;
38 import javafx.beans.property.ReadOnlyBooleanProperty;
39 import javafx.beans.property.ReadOnlyBooleanWrapper;
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.concurrent.Task;
51 import javafx.concurrent.Worker;
52 import javax.annotation.concurrent.GuardedBy;
53 import javax.annotation.concurrent.Immutable;
54 import javax.swing.SwingUtilities;
55 import org.joda.time.DateTime;
56 import org.joda.time.DateTimeZone;
57 import org.joda.time.Interval;
58 import org.joda.time.ReadablePeriod;
59 import org.joda.time.format.DateTimeFormat;
60 import org.joda.time.format.DateTimeFormatter;
61 import org.openide.util.NbBundle;
85 
102 @NbBundle.Messages({"Timeline.confirmation.dialogs.title=Update Timeline database?",
103  "TimeLinecontroller.updateNowQuestion=Do you want to update the events database now?"})
104 public class TimeLineController {
105 
106  private static final Logger LOGGER = Logger.getLogger(TimeLineController.class.getName());
107 
108  private static final ReadOnlyObjectWrapper<TimeZone> timeZone = new ReadOnlyObjectWrapper<>(TimeZone.getDefault());
109 
110  public static ZoneId getTimeZoneID() {
111  return timeZone.get().toZoneId();
112  }
113 
114  public static DateTimeFormatter getZonedFormatter() {
115  return DateTimeFormat.forPattern("YYYY-MM-dd HH:mm:ss").withZone(getJodaTimeZone()); // NON-NLS //NOI18N
116  }
117 
118  public static DateTimeZone getJodaTimeZone() {
119  return DateTimeZone.forTimeZone(getTimeZone().get());
120  }
121 
122  public static ReadOnlyObjectProperty<TimeZone> getTimeZone() {
123  return timeZone.getReadOnlyProperty();
124  }
125 
126  private final ExecutorService executor = Executors.newSingleThreadExecutor();
127 
128  private final ReadOnlyListWrapper<Task<?>> tasks = new ReadOnlyListWrapper<>(FXCollections.observableArrayList());
129 
130  private final ReadOnlyDoubleWrapper taskProgress = new ReadOnlyDoubleWrapper(-1);
131 
132  private final ReadOnlyStringWrapper taskMessage = new ReadOnlyStringWrapper();
133 
134  private final ReadOnlyStringWrapper taskTitle = new ReadOnlyStringWrapper();
135 
136  private final ReadOnlyStringWrapper status = new ReadOnlyStringWrapper();
137 
144  public ReadOnlyStringProperty getStatusProperty() {
145  return status.getReadOnlyProperty();
146  }
147 
148  public void setStatus(String string) {
149  status.set(string);
150  }
151  private final Case autoCase;
152  private final PerCaseTimelineProperties perCaseTimelineProperties;
153 
155  private final ObservableList<DescriptionFilter> quickHideFilters = FXCollections.observableArrayList();
156 
157  public ObservableList<DescriptionFilter> getQuickHideFilters() {
158  return quickHideFilters;
159  }
160 
164  public Case getAutopsyCase() {
165  return autoCase;
166  }
167 
168  synchronized public ReadOnlyListProperty<Task<?>> getTasks() {
169  return tasks.getReadOnlyProperty();
170  }
171 
172  synchronized public ReadOnlyDoubleProperty taskProgressProperty() {
173  return taskProgress.getReadOnlyProperty();
174  }
175 
176  synchronized public ReadOnlyStringProperty taskMessageProperty() {
177  return taskMessage.getReadOnlyProperty();
178  }
179 
180  synchronized public ReadOnlyStringProperty taskTitleProperty() {
181  return taskTitle.getReadOnlyProperty();
182  }
183 
185  private TimeLineTopComponent mainFrame;
186 
187  //are the listeners currently attached
188  @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
189  private boolean listeningToAutopsy = false;
190 
191  private final PropertyChangeListener caseListener = new AutopsyCaseListener();
192  private final PropertyChangeListener ingestJobListener = new AutopsyIngestJobListener();
193  private final PropertyChangeListener ingestModuleListener = new AutopsyIngestModuleListener();
194 
195  @GuardedBy("this")
196  private final ReadOnlyObjectWrapper<VisualizationMode> viewMode = new ReadOnlyObjectWrapper<>(VisualizationMode.COUNTS);
197 
198  synchronized public ReadOnlyObjectProperty<VisualizationMode> viewModeProperty() {
199  return viewMode.getReadOnlyProperty();
200  }
201 
202  @GuardedBy("filteredEvents")
203  private final FilteredEventsModel filteredEvents;
204 
205  private final EventsRepository eventsRepository;
206 
207  @GuardedBy("this")
208  private final ZoomParams InitialZoomState;
209 
210  @GuardedBy("this")
211  private final History<ZoomParams> historyManager = new History<>();
212 
213  @GuardedBy("this")
214  private final ReadOnlyObjectWrapper<ZoomParams> currentParams = new ReadOnlyObjectWrapper<>();
215 
216  //selected events (ie shown in the result viewer)
217  @GuardedBy("this")
218  private final ObservableList<Long> selectedEventIDs = FXCollections.<Long>synchronizedObservableList(FXCollections.<Long>observableArrayList());
219 
223  synchronized public ObservableList<Long> getSelectedEventIDs() {
224  return selectedEventIDs;
225  }
226 
227  @GuardedBy("this")
228  private final ReadOnlyObjectWrapper<Interval> selectedTimeRange = new ReadOnlyObjectWrapper<>();
229 
233  synchronized public ReadOnlyObjectProperty<Interval> getSelectedTimeRange() {
234  return selectedTimeRange.getReadOnlyProperty();
235  }
236 
237  public ReadOnlyBooleanProperty eventsDBStaleProperty() {
238  return eventsDBStale.getReadOnlyProperty();
239  }
240 
241  synchronized public ReadOnlyBooleanProperty getCanAdvance() {
242  return historyManager.getCanAdvance();
243  }
244 
245  synchronized public ReadOnlyBooleanProperty getCanRetreat() {
246  return historyManager.getCanRetreat();
247  }
248  private final ReadOnlyBooleanWrapper eventsDBStale = new ReadOnlyBooleanWrapper(true);
249 
250  private final PromptDialogManager promptDialogManager = new PromptDialogManager(this);
251 
252  public TimeLineController(Case autoCase) throws IOException {
253  this.autoCase = autoCase;
254  this.perCaseTimelineProperties = new PerCaseTimelineProperties(autoCase);
255  eventsDBStale.set(perCaseTimelineProperties.isDBStale());
256  eventsRepository = new EventsRepository(autoCase, currentParams.getReadOnlyProperty());
257  /*
258  * as the history manager's current state changes, modify the tags
259  * filter to be in sync, and expose that as propery from
260  * TimeLineController. Do we need to do this with datasource or hash hit
261  * filters?
262  */
263  historyManager.currentState().addListener((Observable observable) -> {
264  ZoomParams historyManagerParams = historyManager.getCurrentState();
265  eventsRepository.syncTagsFilter(historyManagerParams.getFilter().getTagsFilter());
266  currentParams.set(historyManagerParams);
267  });
268  filteredEvents = eventsRepository.getEventsModel();
269 
270  InitialZoomState = new ZoomParams(filteredEvents.getSpanningInterval(),
272  filteredEvents.filterProperty().get(),
274  historyManager.advance(InitialZoomState);
275  }
276 
281  return filteredEvents;
282  }
283 
284  public void applyDefaultFilters() {
285  pushFilters(filteredEvents.getDefaultFilter());
286  }
287 
288  public void zoomOutToActivity() {
289  Interval boundingEventsInterval = filteredEvents.getBoundingEventsInterval();
290  advance(filteredEvents.zoomParametersProperty().get().withTimeRange(boundingEventsInterval));
291  }
292 
302  private void rebuildRepoHelper(Function<Consumer<Worker.State>, CancellationProgressTask<?>> repoBuilder) {
303  SwingUtilities.invokeLater(this::closeTimelineWindow);
304  boolean ingestRunning = IngestManager.getInstance().isIngestRunning();
305  final CancellationProgressTask<?> rebuildRepository = repoBuilder.apply(newSate -> {
306  setIngestRunning(ingestRunning);
307  //this will be on JFX thread
308  switch (newSate) {
309  case SUCCEEDED:
310  setEventsDBStale(false);
311  SwingUtilities.invokeLater(TimeLineController.this::showWindow);
312  historyManager.reset(filteredEvents.zoomParametersProperty().get());
314  break;
315  case FAILED:
316  case CANCELLED:
317  setEventsDBStale(true);
318  break;
319  }
320  });
321  promptDialogManager.showProgressDialog(rebuildRepository);
322  }
323 
328  void rebuildRepo() {
329  rebuildRepoHelper(eventsRepository::rebuildRepository);
330  }
331 
337  @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
338  void rebuildTagsTable() {
339  rebuildRepoHelper(eventsRepository::rebuildTags);
340  }
341 
342  @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
343  private void closeTimelineWindow() {
344  if (isWindowOpen()) {
345  mainFrame.close();
346  }
347  }
348 
349  public void showFullRange() {
350  synchronized (filteredEvents) {
351  pushTimeRange(filteredEvents.getSpanningInterval());
352  }
353  }
354 
356  public void shutDownTimeLine() {
357  if (mainFrame != null) {
358  listeningToAutopsy = false;
361  Case.removePropertyChangeListener(caseListener);
362  mainFrame.close();
363  mainFrame = null;
364  }
365  }
366 
371  void openTimeLine() {
372  // listen for case changes (specifically images being added, and case changes).
373  if (Case.isCaseOpen() && !listeningToAutopsy) {
376  Case.addPropertyChangeListener(caseListener);
377  listeningToAutopsy = true;
378  }
379 
380  Platform.runLater(() -> {
381  try {
382  if (promptDialogManager.bringCurrentDialogToFront()) {
383  return;
384  }
385  if (IngestManager.getInstance().isIngestRunning()) {
386  //confirm timeline during ingest
387  if (promptDialogManager.confirmDuringIngest() == false) {
388  return;
389  }
390  }
391 
392  /*
393  * if the repo was not rebuilt at minimum rebuild the tags which
394  * may have been updated without our knowing it, since we
395  * can't/aren't checking them. This should at least be quick.
396  * //TODO: can we check the tags to see if we need to do this?
397  */
398  if (checkAndPromptForRebuild() == false) {
399  rebuildTagsTable();
400  }
401 
402  } catch (HeadlessException | MissingResourceException ex) {
403  LOGGER.log(Level.SEVERE, "Unexpected error when generating timeline, ", ex); // NON-NLS //NOI18N
404  }
405  });
406  }
407 
408  @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
409  private boolean checkAndPromptForRebuild() {
410  //if the repo is empty just (r)ebuild it with out asking, they can always cancel part way through;
411  if (eventsRepository.countAllEvents() == 0) {
412  rebuildRepo();
413  return true;
414  }
415 
416  ArrayList<String> rebuildReasons = getRebuildReasons();
417  if (rebuildReasons.isEmpty() == false) {
418  if (promptDialogManager.confirmRebuild(rebuildReasons)) {
419  rebuildRepo();
420  return true;
421  }
422  }
423  return false;
424  }
425 
427  @NbBundle.Messages({"TimeLineController.errorTitle=Timeline error.",
428  "TimeLineController.outOfDate.errorMessage=Error determing if the timeline is out of date. We will assume it should be updated. See the logs for more details.",
429  "TimeLineController.rebuildReasons.outOfDateError=Could not determine if the timeline data is out of date.",
430  "TimeLineController.rebuildReasons.outOfDate=The event data is out of date: Not all events will be visible.",
431  "TimeLineController.rebuildReasons.ingestWasRunning=The Timeline events database was previously populated while ingest was running: Some events may be missing, incomplete, or inaccurate.",
432  "TimeLineController.rebuildReasons.incompleteOldSchema=The Timeline events database was previously populated without incomplete information: Some features may be unavailable or non-functional unless you update the events database."})
433  private ArrayList<String> getRebuildReasons() {
434  ArrayList<String> rebuildReasons = new ArrayList<>();
435 
436  try {
437  //if ingest was running during last rebuild, prompt to rebuild
438  if (perCaseTimelineProperties.wasIngestRunning()) {
439  rebuildReasons.add(Bundle.TimeLineController_rebuildReasons_ingestWasRunning());
440  }
441 
442  } catch (IOException ex) {
443  LOGGER.log(Level.SEVERE, "Error determing the state of the timeline db. We will assume the it is out of date.", ex); // NON-NLS
444  MessageNotifyUtil.Notify.error(Bundle.TimeLineController_errorTitle(),
445  Bundle.TimeLineController_outOfDate_errorMessage());
446  rebuildReasons.add(Bundle.TimeLineController_rebuildReasons_outOfDateError());
447  }
448  //if the events db is stale, prompt to rebuild
449  if (isEventsDBStale()) {
450  rebuildReasons.add(Bundle.TimeLineController_rebuildReasons_outOfDate());
451  }
452  // if the TLDB schema has been upgraded since last time TL ran, prompt for rebuild
453  if (eventsRepository.hasNewColumns() == false) {
454  rebuildReasons.add(Bundle.TimeLineController_rebuildReasons_incompleteOldSchema());
455  }
456  return rebuildReasons;
457  }
458 
465  synchronized public void pushPeriod(ReadablePeriod period) {
466  synchronized (filteredEvents) {
467  final DateTime middleOf = IntervalUtils.middleOf(filteredEvents.timeRangeProperty().get());
468  pushTimeRange(IntervalUtils.getIntervalAround(middleOf, period));
469  }
470  }
471 
472  synchronized public void pushZoomOutTime() {
473  final Interval timeRange = filteredEvents.timeRangeProperty().get();
474  long toDurationMillis = timeRange.toDurationMillis() / 4;
475  DateTime start = timeRange.getStart().minus(toDurationMillis);
476  DateTime end = timeRange.getEnd().plus(toDurationMillis);
477  pushTimeRange(new Interval(start, end));
478  }
479 
480  synchronized public void pushZoomInTime() {
481  final Interval timeRange = filteredEvents.timeRangeProperty().get();
482  long toDurationMillis = timeRange.toDurationMillis() / 4;
483  DateTime start = timeRange.getStart().plus(toDurationMillis);
484  DateTime end = timeRange.getEnd().minus(toDurationMillis);
485  pushTimeRange(new Interval(start, end));
486  }
487 
488  synchronized public void setViewMode(VisualizationMode visualizationMode) {
489  if (viewMode.get() != visualizationMode) {
490  viewMode.set(visualizationMode);
491  }
492  }
493 
494  public void selectEventIDs(Collection<Long> events) {
495  final LoggedTask<Interval> selectEventIDsTask = new LoggedTask<Interval>("Select Event IDs", true) { // NON-NLS //NOI18N
496  @Override
497  protected Interval call() throws Exception {
498  return filteredEvents.getSpanningInterval(events);
499  }
500 
501  @Override
502  protected void succeeded() {
503  super.succeeded();
504  try {
505  synchronized (TimeLineController.this) {
506  selectedTimeRange.set(get());
507  selectedEventIDs.setAll(events);
508 
509  }
510  } catch (InterruptedException | ExecutionException ex) {
511  LOGGER.log(Level.SEVERE, getTitle() + " Unexpected error", ex); // NON-NLS //NOI18N
512  }
513  }
514  };
515 
516  monitorTask(selectEventIDsTask);
517  }
518 
523  synchronized private void showWindow() {
524  if (mainFrame == null) {
525  mainFrame = new TimeLineTopComponent(this);
526  }
527  mainFrame.open();
528  mainFrame.toFront();
529  }
530 
531  synchronized public void pushEventTypeZoom(EventTypeZoomLevel typeZoomeLevel) {
532  ZoomParams currentZoom = filteredEvents.zoomParametersProperty().get();
533  if (currentZoom == null) {
534  advance(InitialZoomState.withTypeZoomLevel(typeZoomeLevel));
535  } else if (currentZoom.hasTypeZoomLevel(typeZoomeLevel) == false) {
536  advance(currentZoom.withTypeZoomLevel(typeZoomeLevel));
537  }
538  }
539 
540  @SuppressWarnings("AssignmentToMethodParameter") //clamp timerange to case
541  synchronized public boolean pushTimeRange(Interval timeRange) {
542  timeRange = this.filteredEvents.getSpanningInterval().overlap(timeRange);
543  ZoomParams currentZoom = filteredEvents.zoomParametersProperty().get();
544  if (currentZoom == null) {
545  advance(InitialZoomState.withTimeRange(timeRange));
546  return true;
547  } else if (currentZoom.hasTimeRange(timeRange) == false) {
548  advance(currentZoom.withTimeRange(timeRange));
549  return true;
550  } else {
551  return false;
552  }
553  }
554 
555  @NbBundle.Messages({"# {0} - the number of events",
556  "Timeline.pushDescrLOD.confdlg.msg=You are about to show details for {0} events. This might be very slow or even crash Autopsy.\n\nDo you want to continue?",
557  "Timeline.pushDescrLOD.confdlg.title=Change description level of detail?"})
558  synchronized public void pushDescrLOD(DescriptionLoD newLOD) {
559  ZoomParams currentZoom = filteredEvents.zoomParametersProperty().get();
560  if (currentZoom == null) {
561  advance(InitialZoomState.withDescrLOD(newLOD));
562  } else if (currentZoom.hasDescrLOD(newLOD) == false) {
563  advance(currentZoom.withDescrLOD(newLOD));
564  }
565  }
566 
567  @SuppressWarnings("AssignmentToMethodParameter") //clamp timerange to case
568  synchronized public void pushTimeAndType(Interval timeRange, EventTypeZoomLevel typeZoom) {
569  timeRange = this.filteredEvents.getSpanningInterval().overlap(timeRange);
570  ZoomParams currentZoom = filteredEvents.zoomParametersProperty().get();
571  if (currentZoom == null) {
572  advance(InitialZoomState.withTimeAndType(timeRange, typeZoom));
573  } else if (currentZoom.hasTimeRange(timeRange) == false && currentZoom.hasTypeZoomLevel(typeZoom) == false) {
574  advance(currentZoom.withTimeAndType(timeRange, typeZoom));
575  } else if (currentZoom.hasTimeRange(timeRange) == false) {
576  advance(currentZoom.withTimeRange(timeRange));
577  } else if (currentZoom.hasTypeZoomLevel(typeZoom) == false) {
578  advance(currentZoom.withTypeZoomLevel(typeZoom));
579  }
580  }
581 
582  synchronized public void pushFilters(RootFilter filter) {
583  ZoomParams currentZoom = filteredEvents.zoomParametersProperty().get();
584  if (currentZoom == null) {
585  advance(InitialZoomState.withFilter(filter.copyOf()));
586  } else if (currentZoom.hasFilter(filter) == false) {
587  advance(currentZoom.withFilter(filter.copyOf()));
588  }
589  }
590 
591  synchronized public void advance() {
592  historyManager.advance();
593  }
594 
595  synchronized public void retreat() {
596  historyManager.retreat();
597  }
598 
599  synchronized private void advance(ZoomParams newState) {
600  historyManager.advance(newState);
601  }
602 
603  public void selectTimeAndType(Interval interval, EventType type) {
604  final Interval timeRange = filteredEvents.getSpanningInterval().overlap(interval);
605 
606  final LoggedTask<Collection<Long>> selectTimeAndTypeTask = new LoggedTask<Collection<Long>>("Select Time and Type", true) { // NON-NLS //NOI18N
607  @Override
608  protected Collection< Long> call() throws Exception {
609  synchronized (TimeLineController.this) {
610  return filteredEvents.getEventIDs(timeRange, new TypeFilter(type));
611  }
612  }
613 
614  @Override
615  protected void succeeded() {
616  super.succeeded();
617  try {
618  synchronized (TimeLineController.this) {
619  selectedTimeRange.set(timeRange);
620  selectedEventIDs.setAll(get());
621 
622  }
623  } catch (InterruptedException | ExecutionException ex) {
624  LOGGER.log(Level.SEVERE, getTitle() + " Unexpected error", ex); // NON-NLS //NOI18N
625  }
626  }
627  };
628 
629  monitorTask(selectTimeAndTypeTask);
630  }
631 
638  synchronized public void monitorTask(final Task<?> task) {
639  //TODO: refactor this to use JavaFX Service? -jm
640  if (task != null) {
641  Platform.runLater(() -> {
642 
643  //is this actually threadsafe, could we get a finished task stuck in the list?
644  task.stateProperty().addListener((Observable observable) -> {
645  switch (task.getState()) {
646  case READY:
647  case RUNNING:
648  case SCHEDULED:
649  break;
650  case SUCCEEDED:
651  case CANCELLED:
652  case FAILED:
653  tasks.remove(task);
654  if (tasks.isEmpty() == false) {
655  taskProgress.bind(tasks.get(0).progressProperty());
656  taskMessage.bind(tasks.get(0).messageProperty());
657  taskTitle.bind(tasks.get(0).titleProperty());
658  }
659  break;
660  }
661  });
662  tasks.add(task);
663  taskProgress.bind(task.progressProperty());
664  taskMessage.bind(task.messageProperty());
665  taskTitle.bind(task.titleProperty());
666  switch (task.getState()) {
667  case READY:
668  executor.submit(task);
669  break;
670  case SCHEDULED:
671  case RUNNING:
672 
673  case SUCCEEDED:
674  case CANCELLED:
675  case FAILED:
676  tasks.remove(task);
677  if (tasks.isEmpty() == false) {
678  taskProgress.bind(tasks.get(0).progressProperty());
679  taskMessage.bind(tasks.get(0).messageProperty());
680  taskTitle.bind(tasks.get(0).titleProperty());
681  }
682  break;
683  }
684  });
685  }
686  }
687 
688  static synchronized public void setTimeZone(TimeZone timeZone) {
689  TimeLineController.timeZone.set(timeZone);
690  }
691 
692  Interval getSpanningInterval(Collection<Long> eventIDs) {
693  return filteredEvents.getSpanningInterval(eventIDs);
694 
695  }
696 
702  @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
703  private boolean isWindowOpen() {
704  return mainFrame != null && mainFrame.isOpened() && mainFrame.isVisible();
705  }
706 
715  private void confirmOutOfDateRebuildIfWindowOpen() throws MissingResourceException, HeadlessException {
716  if (isWindowOpen()) {
717  Platform.runLater(this::checkAndPromptForRebuild);
718 
719  }
720  }
721 
727  public boolean isEventsDBStale() {
728  return eventsDBStale.get();
729  }
730 
735  private void setEventsDBStale(final Boolean stale) {
736  eventsDBStale.set(stale);
737  try {
738  perCaseTimelineProperties.setDbStale(stale);
739  } catch (IOException ex) {
740  MessageNotifyUtil.Notify.error("Timeline", "Failed to mark the timeline db as " + (stale ? "" : "not ") + "stale. Some results may be out of date or missing.");
741  LOGGER.log(Level.SEVERE, "Error marking the timeline db as stale.", ex);
742  }
743  }
744 
745  private void setIngestRunning(boolean ingestRunning) {
746  try {
747  perCaseTimelineProperties.setIngestRunning(ingestRunning);
748  } catch (IOException ex) {
749  MessageNotifyUtil.Notify.error("Timeline", "Failed to mark the timeline db as populated while ingest was" + (ingestRunning ? "" : "not ") + "running. Some results may be out of date or missing.");
750  LOGGER.log(Level.SEVERE, "Error marking the ingest state while the timeline db was populated.", ex);
751  }
752  }
753 
754  private class AutopsyIngestModuleListener implements PropertyChangeListener {
755 
756  @Override
757  public void propertyChange(PropertyChangeEvent evt) {
764  try {
766  } catch (IllegalStateException notUsed) {
770  return;
771  }
772 
773  switch (IngestManager.IngestModuleEvent.valueOf(evt.getPropertyName())) {
774  case CONTENT_CHANGED:
775  break;
776  case DATA_ADDED:
777  case FILE_DONE:
778  Platform.runLater(() -> setEventsDBStale(true));
779  break;
780  }
781  }
782  }
783 
784  @Immutable
785  private class AutopsyIngestJobListener implements PropertyChangeListener {
786 
787  @Override
788  public void propertyChange(PropertyChangeEvent evt) {
789  switch (IngestManager.IngestJobEvent.valueOf(evt.getPropertyName())) {
790  case CANCELLED:
791  case COMPLETED:
792  SwingUtilities.invokeLater(TimeLineController.this::confirmOutOfDateRebuildIfWindowOpen);
793  }
794  }
795  }
796 
797  @Immutable
798  private class AutopsyCaseListener implements PropertyChangeListener {
799 
800  @Override
801  public void propertyChange(PropertyChangeEvent evt) {
802  switch (Case.Events.valueOf(evt.getPropertyName())) {
803  case BLACKBOARD_ARTIFACT_TAG_ADDED:
804  executor.submit(() -> filteredEvents.handleArtifactTagAdded((BlackBoardArtifactTagAddedEvent) evt));
805  break;
806  case BLACKBOARD_ARTIFACT_TAG_DELETED:
807  executor.submit(() -> filteredEvents.handleArtifactTagDeleted((BlackBoardArtifactTagDeletedEvent) evt));
808  break;
809  case CONTENT_TAG_ADDED:
810  executor.submit(() -> filteredEvents.handleContentTagAdded((ContentTagAddedEvent) evt));
811  break;
812  case CONTENT_TAG_DELETED:
813  executor.submit(() -> filteredEvents.handleContentTagDeleted((ContentTagDeletedEvent) evt));
814  break;
815  case DATA_SOURCE_ADDED:
816  Platform.runLater(() -> {
817  setEventsDBStale(true);
818  SwingUtilities.invokeLater(TimeLineController.this::confirmOutOfDateRebuildIfWindowOpen);
819  });
820  break;
821  case CURRENT_CASE:
822  OpenTimelineAction.invalidateController();
823  SwingUtilities.invokeLater(TimeLineController.this::shutDownTimeLine);
824  break;
825  }
826  }
827  }
828 }
synchronized ReadOnlyDoubleProperty taskProgressProperty()
void removeIngestModuleEventListener(final PropertyChangeListener listener)
ZoomParams withDescrLOD(DescriptionLoD descrLOD)
Definition: ZoomParams.java:74
static synchronized IngestManager getInstance()
ZoomParams withTypeZoomLevel(EventTypeZoomLevel zoomLevel)
Definition: ZoomParams.java:66
static final ReadOnlyObjectWrapper< TimeZone > timeZone
static ReadOnlyObjectProperty< TimeZone > getTimeZone()
synchronized ReadOnlyBooleanProperty getCanRetreat()
void selectTimeAndType(Interval interval, EventType type)
synchronized ReadOnlyBooleanProperty getCanAdvance()
void removeIngestJobEventListener(final PropertyChangeListener listener)
synchronized ReadOnlyStringProperty taskTitleProperty()
boolean hasTypeZoomLevel(EventTypeZoomLevel typeZoom)
Definition: ZoomParams.java:86
synchronized ReadOnlyStringProperty taskMessageProperty()
boolean hasDescrLOD(DescriptionLoD newLOD)
Definition: ZoomParams.java:94
ZoomParams withTimeRange(Interval timeRange)
Definition: ZoomParams.java:70
void addIngestJobEventListener(final PropertyChangeListener listener)
static Interval getIntervalAround(DateTime aroundInstant, ReadablePeriod period)
static synchronized void setTimeZone(TimeZone timeZone)
synchronized void monitorTask(final Task<?> task)
synchronized void pushPeriod(ReadablePeriod period)
synchronized void advance(ZoomParams newState)
ZoomParams withTimeAndType(Interval timeRange, EventTypeZoomLevel zoomLevel)
Definition: ZoomParams.java:62
synchronized void pushDescrLOD(DescriptionLoD newLOD)
static synchronized void removePropertyChangeListener(PropertyChangeListener listener)
Definition: Case.java:1305
synchronized void pushFilters(RootFilter filter)
final PerCaseTimelineProperties perCaseTimelineProperties
static void error(String title, String message)
void addIngestModuleEventListener(final PropertyChangeListener listener)
static synchronized void addPropertyChangeListener(PropertyChangeListener listener)
Definition: Case.java:1292
synchronized static Logger getLogger(String name)
Definition: Logger.java:166
ZoomParams withFilter(RootFilter filter)
Definition: ZoomParams.java:78
static DateTime middleOf(Interval interval)
synchronized ReadOnlyListProperty< Task<?> > getTasks()
synchronized void setViewMode(VisualizationMode visualizationMode)
synchronized void pushEventTypeZoom(EventTypeZoomLevel typeZoomeLevel)

Copyright © 2012-2015 Basis Technology. Generated on: Wed Apr 6 2016
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.