19 package org.sleuthkit.autopsy.timeline;
21 import java.awt.HeadlessException;
22 import java.beans.PropertyChangeEvent;
23 import java.beans.PropertyChangeListener;
24 import java.sql.ResultSet;
25 import java.sql.SQLException;
26 import java.text.NumberFormat;
27 import java.time.ZoneId;
28 import java.util.Collection;
30 import java.util.MissingResourceException;
31 import java.util.TimeZone;
32 import java.util.concurrent.ExecutionException;
33 import java.util.concurrent.ExecutorService;
34 import java.util.concurrent.Executors;
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 javax.annotation.concurrent.GuardedBy;
52 import javax.annotation.concurrent.Immutable;
53 import javax.swing.JOptionPane;
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;
62 import org.openide.windows.WindowManager;
102 "Timeline.do_repopulate.msg");
104 private static final ReadOnlyObjectWrapper<TimeZone>
timeZone =
new ReadOnlyObjectWrapper<>(TimeZone.getDefault());
107 return timeZone.get().toZoneId();
111 return DateTimeFormat.forPattern(
"YYYY-MM-dd HH:mm:ss").withZone(
getJodaTimeZone());
115 return DateTimeZone.forTimeZone(
getTimeZone().
get());
119 return timeZone.getReadOnlyProperty();
122 private final ExecutorService
executor = Executors.newSingleThreadExecutor();
124 private final ReadOnlyListWrapper<Task<?>>
tasks =
new ReadOnlyListWrapper<>(FXCollections.observableArrayList());
126 private final ReadOnlyDoubleWrapper
progress =
new ReadOnlyDoubleWrapper(-1);
128 private final ReadOnlyStringWrapper
message =
new ReadOnlyStringWrapper();
130 private final ReadOnlyStringWrapper
taskTitle =
new ReadOnlyStringWrapper();
132 synchronized public ReadOnlyListProperty<Task<?>>
getTasks() {
133 return tasks.getReadOnlyProperty();
137 return progress.getReadOnlyProperty();
141 return message.getReadOnlyProperty();
145 return taskTitle.getReadOnlyProperty();
165 return viewMode.getReadOnlyProperty();
168 @GuardedBy(
"filteredEvents")
183 private final ObservableList<Long>
selectedEventIDs = FXCollections.<Long>synchronizedObservableList(FXCollections.<Long>observableArrayList());
193 private final ReadOnlyObjectWrapper<Interval>
selectedTimeRange = new ReadOnlyObjectWrapper<>();
209 return needsHistogramRebuild.getReadOnlyProperty();
219 private final ReadOnlyBooleanWrapper
newEventsFlag =
new ReadOnlyBooleanWrapper(
false);
250 boolean rebuildRepo() {
253 if (showIngestConfirmation() != JOptionPane.YES_OPTION) {
257 LOGGER.log(Level.INFO,
"Beginning generation of timeline");
259 SwingUtilities.invokeLater(() -> {
264 final SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase();
265 final long lastObjId = sleuthkitCase.getLastObjectId();
267 final Boolean injestRunning = IngestManager.getInstance().isIngestRunning();
278 needsHistogramRebuild.set(
true);
279 needsHistogramRebuild.set(
false);
283 Platform.runLater(() -> {
284 newEventsFlag.set(
false);
289 }
catch (TskCoreException ex) {
290 LOGGER.log(Level.SEVERE,
"Error when generating timeline, ", ex);
315 synchronized void openTimeLine() {
328 boolean rebuildingRepo =
false;
329 if (timeLineLastObjectId == -1) {
330 rebuildingRepo = rebuildRepo();
332 if (rebuildingRepo ==
false
334 if (showLastPopulatedWhileIngestingConfirmation() == JOptionPane.YES_OPTION) {
335 rebuildingRepo = rebuildRepo();
338 final SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase();
339 if ((rebuildingRepo ==
false)
340 && (sleuthkitCase.getLastObjectId() != timeLineLastObjectId
345 if (rebuildingRepo ==
false) {
350 }
catch (TskCoreException ex) {
351 LOGGER.log(Level.SEVERE,
"Error when generating timeline, ", ex);
352 }
catch (HeadlessException | MissingResourceException ex) {
353 LOGGER.log(Level.SEVERE,
"Unexpected error when generating timeline, ", ex);
357 @SuppressWarnings(
"deprecation")
359 long caseLastArtfId = -1;
360 String query =
"select Max(artifact_id) as max_id from blackboard_artifacts";
362 try (
CaseDbQuery dbQuery = sleuthkitCase.executeQuery(query)) {
363 ResultSet resultSet = dbQuery.getResultSet();
364 while (resultSet.next()) {
365 caseLastArtfId = resultSet.getLong(
"max_id");
368 LOGGER.log(Level.SEVERE,
"Error getting last artifact id: ", ex);
370 return caseLastArtfId;
388 long toDurationMillis = timeRange.toDurationMillis() / 4;
389 DateTime start = timeRange.getStart().minus(toDurationMillis);
390 DateTime end = timeRange.getEnd().plus(toDurationMillis);
396 long toDurationMillis = timeRange.toDurationMillis() / 4;
397 DateTime start = timeRange.getStart().plus(toDurationMillis);
398 DateTime end = timeRange.getEnd().minus(toDurationMillis);
403 if (
viewMode.get() != visualizationMode) {
411 protected Interval call()
throws Exception {
416 protected void succeeded() {
423 }
catch (InterruptedException ex) {
425 .getName()).log(Level.SEVERE, getTitle() +
" interrupted unexpectedly", ex);
426 }
catch (ExecutionException ex) {
428 .getName()).log(Level.SEVERE, getTitle() +
" unexpectedly threw " + ex.getCause(), ex);
441 LOGGER.log(Level.WARNING,
"Tried to show timeline with invalid window. Rebuilding GUI.");
449 SwingUtilities.invokeLater(() -> {
458 if (currentZoom == null) {
468 if (currentZoom == null) {
470 }
else if (currentZoom.
hasTimeRange(timeRange) ==
false) {
477 final Long count = eventCounts.values().stream().reduce(0l, Long::sum);
479 boolean shouldContinue =
true;
482 int showConfirmDialog = JOptionPane.showConfirmDialog(
mainFrame,
483 NbBundle.getMessage(
this.getClass(),
484 "Timeline.pushDescrLOD.confdlg.msg",
485 NumberFormat.getInstance().format(count)),
487 "Timeline.pushDescrLOD.confdlg.details"),
488 JOptionPane.YES_NO_OPTION);
490 shouldContinue = (showConfirmDialog == JOptionPane.YES_OPTION);
493 if (shouldContinue) {
495 if (currentZoom == null) {
497 }
else if (currentZoom.
hasDescrLOD(newLOD) ==
false) {
501 return shouldContinue;
507 if (currentZoom == null) {
511 }
else if (currentZoom.
hasTimeRange(timeRange) ==
false) {
520 if (currentZoom == null) {
522 }
else if (currentZoom.
hasFilter(filter) ==
false) {
545 protected Collection< Long> call()
throws Exception {
552 protected void succeeded() {
559 }
catch (InterruptedException ex) {
561 .getName()).log(Level.SEVERE, getTitle() +
" interrupted unexpectedly", ex);
562 }
catch (ExecutionException ex) {
564 .getName()).log(Level.SEVERE, getTitle() +
" unexpectedly threw " + ex.getCause(), ex);
580 Platform.runLater(() -> {
583 task.stateProperty().addListener((Observable observable) -> {
584 switch (task.getState()) {
593 if (tasks.isEmpty() ==
false) {
594 progress.bind(tasks.get(0).progressProperty());
595 message.bind(tasks.get(0).messageProperty());
596 taskTitle.bind(tasks.get(0).titleProperty());
602 progress.bind(task.progressProperty());
603 message.bind(task.messageProperty());
604 taskTitle.bind(task.titleProperty());
605 switch (task.getState()) {
607 executor.submit(task);
616 if (tasks.isEmpty() ==
false) {
617 progress.bind(tasks.get(0).progressProperty());
618 message.bind(tasks.get(0).messageProperty());
619 taskTitle.bind(tasks.get(0).titleProperty());
631 Interval getSpanningInterval(Collection<Long> eventIDs) {
640 return showOutOfDateConfirmation() == JOptionPane.YES_OPTION
645 synchronized int showLastPopulatedWhileIngestingConfirmation() {
646 return JOptionPane.showConfirmDialog(
mainFrame,
647 DO_REPOPULATE_MESSAGE,
649 "Timeline.showLastPopulatedWhileIngestingConf.confDlg.details"),
650 JOptionPane.YES_NO_OPTION,
651 JOptionPane.QUESTION_MESSAGE);
655 synchronized int showOutOfDateConfirmation() throws MissingResourceException, HeadlessException {
656 return JOptionPane.showConfirmDialog(
mainFrame,
658 "Timeline.propChg.confDlg.timelineOOD.msg"),
660 "Timeline.propChg.confDlg.timelineOOD.details"),
661 JOptionPane.YES_NO_OPTION);
664 synchronized int showIngestConfirmation() throws MissingResourceException, HeadlessException {
665 return JOptionPane.showConfirmDialog(
mainFrame,
667 "Timeline.initTimeline.confDlg.genBeforeIngest.msg"),
669 "Timeline.initTimeline.confDlg.genBeforeIngest.details"),
670 JOptionPane.YES_NO_OPTION);
678 case CONTENT_CHANGED:
689 Platform.runLater(() -> {
690 newEventsFlag.set(
true);
717 switch (
Case.
Events.valueOf(evt.getPropertyName())) {
718 case DATA_SOURCE_ADDED:
final ReadOnlyListWrapper< Task<?> > tasks
synchronized void pushZoomInTime()
final ReadOnlyStringWrapper message
void removeIngestModuleEventListener(final PropertyChangeListener listener)
final ReadOnlyObjectWrapper< VisualizationMode > viewMode
final ObservableList< Long > selectedEventIDs
ZoomParams withFilter(Filter filter)
static synchronized IngestManager getInstance()
static final Logger LOGGER
boolean hasTimeRange(Interval timeRange)
FilteredEventsModel getEventsModel()
final PropertyChangeListener ingestModuleListener
synchronized void rebuildRepository(Runnable r)
ZoomParams withTypeZoomLevel(EventTypeZoomLevel zoomLevel)
static final ReadOnlyObjectWrapper< TimeZone > timeZone
TimeLineTopComponent mainFrame
static ReadOnlyObjectProperty< TimeZone > getTimeZone()
long getCaseLastArtifactID(final SleuthkitCase sleuthkitCase)
void propertyChange(PropertyChangeEvent evt)
synchronized void showWindow()
final EventsRepository eventsRepository
synchronized ReadOnlyBooleanProperty getCanRetreat()
synchronized ZoomParams retreat()
FilteredEventsModel getEventsModel()
synchronized void setController(TimeLineController controller)
boolean isIngestRunning()
final PropertyChangeListener ingestJobListener
synchronized boolean pushDescrLOD(DescriptionLOD newLOD)
void selectEventIDs(Collection< Long > events)
synchronized void pushTimeAndType(Interval timeRange, EventTypeZoomLevel typeZoom)
synchronized ZoomParams advance()
synchronized ReadOnlyObjectProperty< Interval > getSelectedTimeRange()
final History< ZoomParams > historyManager
ReadOnlyBooleanProperty getNeedsHistogramRebuild()
Interval getBoundingEventsInterval()
void selectTimeAndType(Interval interval, EventType type)
synchronized ReadOnlyBooleanProperty getCanAdvance()
void removeIngestJobEventListener(final PropertyChangeListener listener)
boolean listeningToAutopsy
final ReadOnlyObjectWrapper< Interval > selectedTimeRange
synchronized void closeTimeLine()
boolean hasTypeZoomLevel(EventTypeZoomLevel typeZoom)
ZoomParams withDescrLOD(DescriptionLOD descrLOD)
synchronized ReadOnlyObjectProperty< Interval > timeRange()
void applyDefaultFilters()
void recordLastObjID(Long lastObjID)
ZoomParams withTimeRange(Interval timeRange)
void propertyChange(PropertyChangeEvent evt)
void addIngestJobEventListener(final PropertyChangeListener listener)
static ZoneId getTimeZoneID()
final ReadOnlyBooleanWrapper needsHistogramRebuild
void recordWasIngestRunning(Boolean wasIngestRunning)
boolean getWasIngestRunning()
static Interval getIntervalAround(DateTime aroundInstant, ReadablePeriod period)
synchronized ReadOnlyStringProperty getMessage()
static synchronized void setTimeZone(TimeZone timeZone)
synchronized boolean outOfDatePromptAndRebuild()
synchronized void monitorTask(final Task<?> task)
synchronized void pushPeriod(ReadablePeriod period)
synchronized ReadOnlyDoubleProperty getProgress()
Map< EventType, Long > getEventCounts(Interval timeRange)
boolean hasFilter(Filter filterSet)
void recordLastArtifactID(long lastArtfID)
synchronized void advance(ZoomParams newState)
synchronized ObservableList< Long > getSelectedEventIDs()
ZoomParams withTimeAndType(Interval timeRange, EventTypeZoomLevel zoomLevel)
final Interval getSpanningInterval()
ReadOnlyBooleanProperty getNewEventsFlag()
static synchronized void removePropertyChangeListener(PropertyChangeListener listener)
Interval getSpanningInterval(Collection< Long > eventIDs)
static DateTimeZone getJodaTimeZone()
boolean hasDescrLOD(DescriptionLOD newLOD)
void addIngestModuleEventListener(final PropertyChangeListener listener)
static synchronized void addPropertyChangeListener(PropertyChangeListener listener)
synchronized ReadOnlyStringProperty getTaskTitle()
final ReadOnlyDoubleWrapper progress
void propertyChange(PropertyChangeEvent evt)
final FilteredEventsModel filteredEvents
final ReadOnlyStringWrapper taskTitle
final PropertyChangeListener caseListener
Set< Long > getEventIDs(Interval timeRange, Filter filter)
synchronized ReadOnlyObjectProperty< VisualizationMode > getViewMode()
synchronized void pushZoomOutTime()
final ZoomParams InitialZoomState
final ReadOnlyBooleanWrapper newEventsFlag
static DateTimeFormatter getZonedFormatter()
synchronized void pushTimeRange(Interval timeRange)
final ExecutorService executor
static DateTime middleOf(Interval interval)
synchronized ReadOnlyListProperty< Task<?> > getTasks()
synchronized void pushFilters(Filter filter)
static boolean isCaseOpen()
synchronized void setViewMode(VisualizationMode visualizationMode)
static Logger getLogger(String name)
synchronized void pushEventTypeZoom(EventTypeZoomLevel typeZoomeLevel)
static Filter getDefaultFilter()
static final String DO_REPOPULATE_MESSAGE
synchronized ReadOnlyObjectProperty< ZoomParams > getRequestedZoomParamters()