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()