Merge pull request #1720 from millmanorama/TL-don't-block-ui-rebuilding-repo

Tl don't block ui rebuilding repo
This commit is contained in:
Richard Cordovano 2015-11-16 16:29:07 -05:00
commit a7d624c501
5 changed files with 280 additions and 455 deletions

View File

@ -31,6 +31,7 @@ import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.core.Installer; import org.sleuthkit.autopsy.core.Installer;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.timeline.Timeline") @ActionID(category = "Tools", id = "org.sleuthkit.autopsy.timeline.Timeline")
@ActionRegistration(displayName = "#CTL_MakeTimeline", lazy = false) @ActionRegistration(displayName = "#CTL_MakeTimeline", lazy = false)
@ -58,8 +59,8 @@ public class OpenTimelineAction extends CallableSystemAction {
} }
@Override @Override
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public void performAction() { public void performAction() {
//check case //check case
if (!Case.isCaseOpen()) { if (!Case.isCaseOpen()) {
return; return;
@ -72,14 +73,12 @@ public class OpenTimelineAction extends CallableSystemAction {
LOGGER.log(Level.INFO, "Could not create timeline, there are no data sources.");// NON-NLS LOGGER.log(Level.INFO, "Could not create timeline, there are no data sources.");// NON-NLS
return; return;
} }
synchronized (OpenTimelineAction.class) {
if (timeLineController == null) { if (timeLineController == null) {
timeLineController = new TimeLineController(currentCase); timeLineController = new TimeLineController(currentCase);
} else if (timeLineController.getAutopsyCase() != currentCase) { } else if (timeLineController.getAutopsyCase() != currentCase) {
timeLineController.closeTimeLine(); timeLineController.closeTimeLine();
timeLineController = new TimeLineController(currentCase); timeLineController = new TimeLineController(currentCase);
} }
}
timeLineController.openTimeLine(); timeLineController.openTimeLine();
} }

View File

@ -1,65 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
</SyntheticProperties>
<Events>
<EventHandler event="windowClosing" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="closeDialog"/>
</Events>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="progressBar" pref="504" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="progressHeader" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="progressHeader" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="progressBar" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JProgressBar" name="progressBar">
</Component>
<Component class="javax.swing.JLabel" name="progressHeader">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/timeline/Bundle.properties" key="ProgressWindow.progressHeader.text" replaceFormat="NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 14]"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -1,217 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.annotation.concurrent.Immutable;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
/**
* Dialog with progress bar that pops up when timeline is being generated
*/
public class ProgressWindow extends JFrame {
private final SwingWorker<?, ?> worker;
/**
* Creates new form TimelineProgressDialog
*/
@NbBundle.Messages({"Timeline.progressWindow.name=Timeline",
"Timeline.progressWindow.title=Generating Timeline data"})
public ProgressWindow(Component parent, boolean modal, SwingWorker<?, ?> worker) {
super();
initComponents();
setLocationRelativeTo(parent);
setAlwaysOnTop(modal);
//set icon the same as main app
SwingUtilities.invokeLater(() -> {
setIconImage(WindowManager.getDefault().getMainWindow().getIconImage());
});
setName(Bundle.Timeline_progressWindow_name());
setTitle(Bundle.Timeline_progressWindow_title());
// Close the dialog when Esc is pressed
String cancelName = "cancel"; // NON-NLS
InputMap inputMap = getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelName);
ActionMap actionMap = getRootPane().getActionMap();
actionMap.put(cancelName, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
cancel();
}
});
this.worker = worker;
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
progressBar = new javax.swing.JProgressBar();
progressHeader = new javax.swing.JLabel();
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent evt) {
closeDialog(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(progressHeader, NbBundle.getMessage(ProgressWindow.class, "ProgressWindow.progressHeader.text")); // NOI18N
progressHeader.setMinimumSize(new java.awt.Dimension(10, 14));
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(progressBar, javax.swing.GroupLayout.DEFAULT_SIZE, 504, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(progressHeader, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, Short.MAX_VALUE)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(progressHeader, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
pack();
}// </editor-fold>//GEN-END:initComponents
/**
* Closes the dialog
*/
private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog
cancel();
}//GEN-LAST:event_closeDialog
@NbBundle.Messages({"Timeline.ProgressWindow.cancel.confdlg.msg=Do you want to cancel timeline creation?",
"Timeline.ProgressWindow.cancel.confdlg.detail=Cancel timeline creation?"})
public void cancel() {
SwingUtilities.invokeLater(() -> {
if (isVisible()) {
int showConfirmDialog = JOptionPane.showConfirmDialog(ProgressWindow.this,
Bundle.Timeline_ProgressWindow_cancel_confdlg_msg(),
Bundle.Timeline_ProgressWindow_cancel_confdlg_detail(),
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
if (showConfirmDialog == JOptionPane.YES_OPTION) {
close();
}
} else {
close();
}
});
}
public void close() {
worker.cancel(false);
setVisible(false);
dispose();
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JProgressBar progressBar;
private javax.swing.JLabel progressHeader;
// End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public void update(ProgressUpdate chunk) {
progressHeader.setText(chunk.getHeaderMessage());
if (chunk.getTotal() >= 0) {
progressBar.setIndeterminate(false);
progressBar.setMaximum(chunk.getTotal());
progressBar.setStringPainted(true);
progressBar.setValue(chunk.getProgress());
progressBar.setString(chunk.getDetailMessage());
} else {
progressBar.setIndeterminate(true);
progressBar.setStringPainted(true);
progressBar.setString(chunk.getDetailMessage());
}
}
/**
* bundles up progress information to be shown in the progress dialog
*/
@Immutable
public static class ProgressUpdate {
private final int progress;
private final int total;
private final String headerMessage;
private final String detailMessage;
public int getProgress() {
return progress;
}
public int getTotal() {
return total;
}
public String getHeaderMessage() {
return headerMessage;
}
public String getDetailMessage() {
return detailMessage;
}
public ProgressUpdate(int progress, int total, String headerMessage, String detailMessage) {
this.progress = progress;
this.total = total;
this.headerMessage = headerMessage;
this.detailMessage = detailMessage;
}
public ProgressUpdate(int progress, int total, String headerMessage) {
this(progress, total, headerMessage, "");
}
}
}

View File

@ -21,6 +21,8 @@ package org.sleuthkit.autopsy.timeline;
import java.awt.HeadlessException; import java.awt.HeadlessException;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.net.URL;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.text.NumberFormat; import java.text.NumberFormat;
@ -49,10 +51,16 @@ import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.scene.control.ButtonType;
import javafx.scene.control.DialogPane;
import javafx.scene.image.Image;
import javafx.stage.Stage;
import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import org.controlsfx.dialog.ProgressDialog;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.DateTimeZone; import org.joda.time.DateTimeZone;
import org.joda.time.Interval; import org.joda.time.Interval;
@ -97,7 +105,8 @@ import org.sleuthkit.datamodel.TskCoreException;
* * <li>Since eventsRepository is internally synchronized, only compound * * <li>Since eventsRepository is internally synchronized, only compound
* access to it needs external synchronization <li> * access to it needs external synchronization <li>
* <li>Other state including listeningToAutopsy, mainFrame, viewMode, and the * <li>Other state including listeningToAutopsy, mainFrame, viewMode, and the
* listeners should only be accessed with this object's intrinsic lock held * listeners should only be accessed with this object's intrinsic lock held, or
* on the EDT as indicated.
* </li> * </li>
* <ul> * <ul>
*/ */
@ -136,6 +145,9 @@ public class TimeLineController {
private final ReadOnlyStringWrapper status = new ReadOnlyStringWrapper(); private final ReadOnlyStringWrapper status = new ReadOnlyStringWrapper();
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
private ProgressDialog taskProgressDialog;
/** /**
* status is a string that will be displayed in the status bar as a kind of * status is a string that will be displayed in the status bar as a kind of
* user hint/information when it is not empty * user hint/information when it is not empty
@ -181,11 +193,11 @@ public class TimeLineController {
return taskTitle.getReadOnlyProperty(); return taskTitle.getReadOnlyProperty();
} }
@GuardedBy("this") @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private TimeLineTopComponent mainFrame; private TimeLineTopComponent mainFrame;
//are the listeners currently attached //are the listeners currently attached
@GuardedBy("this") @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private boolean listeningToAutopsy = false; private boolean listeningToAutopsy = false;
private final PropertyChangeListener caseListener = new AutopsyCaseListener(); private final PropertyChangeListener caseListener = new AutopsyCaseListener();
@ -202,7 +214,6 @@ public class TimeLineController {
@GuardedBy("filteredEvents") @GuardedBy("filteredEvents")
private final FilteredEventsModel filteredEvents; private final FilteredEventsModel filteredEvents;
@GuardedBy("eventsRepository")
private final EventsRepository eventsRepository; private final EventsRepository eventsRepository;
@GuardedBy("this") @GuardedBy("this")
@ -305,6 +316,7 @@ public class TimeLineController {
* the user aborted after prompt about ingest running. True if the * the user aborted after prompt about ingest running. True if the
* repo was rebuilt. * repo was rebuilt.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
boolean rebuildRepo() { boolean rebuildRepo() {
if (IngestManager.getInstance().isIngestRunning()) { if (IngestManager.getInstance().isIngestRunning()) {
//confirm timeline during ingest //confirm timeline during ingest
@ -312,46 +324,26 @@ public class TimeLineController {
return false; return false;
} }
} }
LOGGER.log(Level.INFO, "Beginning generation of timeline"); // NON-NLS SwingUtilities.invokeLater(this::closeTimelineWindow);
try { Platform.runLater(() -> {
SwingUtilities.invokeLater(() -> { final Worker<Void> rebuildRepository = eventsRepository.rebuildRepository();
synchronized (TimeLineController.this) { rebuildRepository.stateProperty().addListener((stateProperty, oldState, newSate) -> {
if (isWindowOpen()) { //this will be on JFX thread
mainFrame.close(); if (newSate == Worker.State.SUCCEEDED) {
} //TODO: this looks hacky. what is going on? should this be an event?
needsHistogramRebuild.set(true);
needsHistogramRebuild.set(false);
SwingUtilities.invokeLater(TimeLineController.this::showWindow);
//TODO: should this be an event?
newEventsFlag.set(false);
historyManager.reset(filteredEvents.zoomParametersProperty().get());
TimeLineController.this.showFullRange();
} }
}); });
final SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase(); showProgressDialog(rebuildRepository);
final long lastObjId = sleuthkitCase.getLastObjectId(); });
final long lastArtfID = getCaseLastArtifactID(sleuthkitCase);
final Boolean injestRunning = IngestManager.getInstance().isIngestRunning();
//TODO: verify this locking is correct? -jm
synchronized (eventsRepository) {
eventsRepository.rebuildRepository(() -> {
synchronized (eventsRepository) {
eventsRepository.recordLastObjID(lastObjId);
eventsRepository.recordLastArtifactID(lastArtfID);
eventsRepository.recordWasIngestRunning(injestRunning);
}
synchronized (TimeLineController.this) {
//TODO: this looks hacky. what is going on? should this be an event?
needsHistogramRebuild.set(true);
needsHistogramRebuild.set(false);
showWindow();
}
Platform.runLater(() -> {
//TODO: should this be an event?
newEventsFlag.set(false);
historyManager.reset(filteredEvents.zoomParametersProperty().get());
TimeLineController.this.showFullRange();
});
});
}
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error when generating timeline, ", ex); // NON-NLS
return false;
}
return true; return true;
} }
@ -361,48 +353,76 @@ public class TimeLineController {
* in to the TimeLine DB. * in to the TimeLine DB.
*/ */
void rebuildTagsTable() { void rebuildTagsTable() {
LOGGER.log(Level.INFO, "starting to rebuild tags table"); // NON-NLS
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(this::closeTimelineWindow);
synchronized (TimeLineController.this) { Platform.runLater(() -> {
if (isWindowOpen()) { Worker<Void> rebuildTags = eventsRepository.rebuildTags();
mainFrame.close(); rebuildTags.stateProperty().addListener((stateProperty, oldState, newSate) -> {
} //this will be on JFX thread
} if (newSate == Worker.State.SUCCEEDED) {
}); SwingUtilities.invokeLater(TimeLineController.this::showWindow);
synchronized (eventsRepository) {
eventsRepository.rebuildTags(() -> {
showWindow();
Platform.runLater(() -> {
showFullRange(); showFullRange();
}); }
}); });
showProgressDialog(rebuildTags);
});
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void closeTimelineWindow() {
if (isWindowOpen()) {
mainFrame.close();
} }
} }
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
@NbBundle.Messages({"Timeline.progressWindow.title=Populating Timeline Data"})
private void showProgressDialog(Worker<Void> task) {
taskProgressDialog = new ProgressDialog(task);
taskProgressDialog.setTitle(Bundle.Timeline_progressWindow_title());
DialogPane dialogPane = taskProgressDialog.getDialogPane();
dialogPane.getButtonTypes().add(ButtonType.CANCEL);
Stage dialogStage = (Stage) dialogPane.getScene().getWindow();
dialogPane.setPrefWidth(400);
taskProgressDialog.headerTextProperty().bind(task.titleProperty());
dialogStage.getIcons().setAll(LOGO);
taskProgressDialog.setOnCloseRequest(closeRequestEvent -> task.cancel());
}
private static final Image LOGO;
static {
Image x = null;
try {
x = new Image(new URL("nbresloc:/org/netbeans/core/startup/frame.gif").openStream());
} catch (IOException ex) {
LOGGER.log(Level.WARNING, "Failed to laod branded icon for progress dialog.", ex);
}
LOGO = x;
}
public void showFullRange() { public void showFullRange() {
synchronized (filteredEvents) { synchronized (filteredEvents) {
pushTimeRange(filteredEvents.getSpanningInterval()); pushTimeRange(filteredEvents.getSpanningInterval());
} }
} }
synchronized public void closeTimeLine() { @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public void closeTimeLine() {
if (mainFrame != null) { if (mainFrame != null) {
listeningToAutopsy = false; listeningToAutopsy = false;
IngestManager.getInstance().removeIngestModuleEventListener(ingestModuleListener); IngestManager.getInstance().removeIngestModuleEventListener(ingestModuleListener);
IngestManager.getInstance().removeIngestJobEventListener(ingestJobListener); IngestManager.getInstance().removeIngestJobEventListener(ingestJobListener);
Case.removePropertyChangeListener(caseListener); Case.removePropertyChangeListener(caseListener);
SwingUtilities.invokeLater(() -> { mainFrame.close();
synchronized (TimeLineController.this) { mainFrame = null;
mainFrame.close();
mainFrame = null;
}
});
} }
} }
/** /**
* show the timeline window and prompt for rebuilding database if necessary. * show the timeline window and prompt for rebuilding database if necessary.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void openTimeLine() { void openTimeLine() {
// listen for case changes (specifically images being added, and case changes). // listen for case changes (specifically images being added, and case changes).
if (Case.isCaseOpen() && !listeningToAutopsy) { if (Case.isCaseOpen() && !listeningToAutopsy) {
@ -413,6 +433,15 @@ public class TimeLineController {
} }
try { try {
if (eventsRepository.isRebuilding()) {
Platform.runLater(() -> {
if (taskProgressDialog != null) {
((Stage) taskProgressDialog.getDialogPane().getScene().getWindow()).toFront();
}
});
return;
}
boolean repoRebuilt = false; //has the repo been rebuilt boolean repoRebuilt = false; //has the repo been rebuilt
long timeLineLastObjectId = eventsRepository.getLastObjID(); long timeLineLastObjectId = eventsRepository.getLastObjID();
@ -465,7 +494,7 @@ public class TimeLineController {
} }
} }
private long getCaseLastArtifactID(final SleuthkitCase sleuthkitCase) { public static long getCaseLastArtifactID(final SleuthkitCase sleuthkitCase) {
long caseLastArtfId = -1; long caseLastArtfId = -1;
String query = "select Max(artifact_id) as max_id from blackboard_artifacts"; // NON-NLS String query = "select Max(artifact_id) as max_id from blackboard_artifacts"; // NON-NLS
try (CaseDbQuery dbQuery = sleuthkitCase.executeQuery(query)) { try (CaseDbQuery dbQuery = sleuthkitCase.executeQuery(query)) {
@ -545,16 +574,13 @@ public class TimeLineController {
/** /**
* private method to build gui if necessary and make it visible. * private method to build gui if necessary and make it visible.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
synchronized private void showWindow() { synchronized private void showWindow() {
SwingUtilities.invokeLater(() -> { if (mainFrame == null) {
synchronized (TimeLineController.this) { mainFrame = new TimeLineTopComponent(this);
if (mainFrame == null) { }
mainFrame = new TimeLineTopComponent(this); mainFrame.open();
} mainFrame.toFront();
mainFrame.open();
mainFrame.toFront();
}
});
} }
synchronized public void pushEventTypeZoom(EventTypeZoomLevel typeZoomeLevel) { synchronized public void pushEventTypeZoom(EventTypeZoomLevel typeZoomeLevel) {
@ -575,8 +601,7 @@ public class TimeLineController {
} else if (currentZoom.hasTimeRange(timeRange) == false) { } else if (currentZoom.hasTimeRange(timeRange) == false) {
advance(currentZoom.withTimeRange(timeRange)); advance(currentZoom.withTimeRange(timeRange));
return true; return true;
} } else {
else{
return false; return false;
} }
} }
@ -590,7 +615,7 @@ public class TimeLineController {
final Long count = eventCounts.values().stream().reduce(0l, Long::sum); final Long count = eventCounts.values().stream().reduce(0l, Long::sum);
boolean shouldContinue = true; boolean shouldContinue = true;
if ((newLOD == DescriptionLoD.FULL|| newLOD ==DescriptionLoD.MEDIUM )&& count > 10_000) { if ((newLOD == DescriptionLoD.FULL || newLOD == DescriptionLoD.MEDIUM) && count > 10_000) {
String format = NumberFormat.getInstance().format(count); String format = NumberFormat.getInstance().format(count);
int showConfirmDialog = JOptionPane.showConfirmDialog(mainFrame, int showConfirmDialog = JOptionPane.showConfirmDialog(mainFrame,
@ -748,7 +773,8 @@ public class TimeLineController {
* *
* @return true if the timeline window is open * @return true if the timeline window is open
*/ */
synchronized private boolean isWindowOpen() { @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private boolean isWindowOpen() {
return mainFrame != null && mainFrame.isOpened() && mainFrame.isVisible(); return mainFrame != null && mainFrame.isOpened() && mainFrame.isVisible();
} }

View File

@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.timeline.db;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -30,19 +31,23 @@ import java.util.Map;
import static java.util.Objects.isNull; import static java.util.Objects.isNull;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.collections.ObservableMap; import javafx.collections.ObservableMap;
import javax.annotation.concurrent.GuardedBy; import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.controlsfx.dialog.ProgressDialog;
import org.joda.time.Interval; import org.joda.time.Interval;
import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory; import org.netbeans.api.progress.ProgressHandleFactory;
@ -50,7 +55,9 @@ import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.timeline.ProgressWindow; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.datamodel.EventCluster; import org.sleuthkit.autopsy.timeline.datamodel.EventCluster;
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent; import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
@ -93,8 +100,7 @@ public class EventsRepository {
private final EventDB eventDB; private final EventDB eventDB;
@GuardedBy("this") private final DBPopulationService dbPopulationService = new DBPopulationService(this);
private SwingWorker<Void, ProgressWindow.ProgressUpdate> dbPopulationWorker;
private final LoadingCache<Object, Long> maxCache; private final LoadingCache<Object, Long> maxCache;
@ -177,15 +183,15 @@ public class EventsRepository {
// return eventDB.getMinTime(); // return eventDB.getMinTime();
} }
public void recordLastArtifactID(long lastArtfID) { private void recordLastArtifactID(long lastArtfID) {
eventDB.recordLastArtifactID(lastArtfID); eventDB.recordLastArtifactID(lastArtfID);
} }
public void recordWasIngestRunning(Boolean wasIngestRunning) { private void recordWasIngestRunning(Boolean wasIngestRunning) {
eventDB.recordWasIngestRunning(wasIngestRunning); eventDB.recordWasIngestRunning(wasIngestRunning);
} }
public void recordLastObjID(Long lastObjID) { private void recordLastObjID(Long lastObjID) {
eventDB.recordLastObjID(lastObjID); eventDB.recordLastObjID(lastObjID);
} }
@ -324,77 +330,137 @@ public class EventsRepository {
} }
} }
synchronized public void rebuildRepository(Runnable r) { static private final Executor workerExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("eventrepository-worker-%d").build());
if (dbPopulationWorker != null) {
dbPopulationWorker.cancel(true);
} @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
dbPopulationWorker = new DBPopulationWorker(r); public Worker<Void> rebuildRepository() {
dbPopulationWorker.execute(); return rebuildRepository(DBPopulationService.DBPopulationMode.FULL);
} }
synchronized public void rebuildTags(Runnable r) { @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
if (dbPopulationWorker != null) { public Worker<Void> rebuildTags() {
dbPopulationWorker.cancel(true); return rebuildRepository(DBPopulationService.DBPopulationMode.TAGS_ONLY);
}
dbPopulationWorker = new RebuildTagsWorker(r);
dbPopulationWorker.execute();
} }
/** /**
* A base class for swing workers that shows a {@link ProgressWorker} and
* updates a {@link ProgressHandle} as it performs its background work, and
* calls a call-back when finished.
* *
* //TODO: I prefer the JavaFX task API as it has built in progress * @param mode the value of mode
* properties that can be bound to a javafx progress indicator. Convert
* these to a JavaFX implementation,and replace {@link ProgressWindow} with
* {@link ProgressDialog}
*/ */
private abstract class DBProgressWorker extends SwingWorker<Void, ProgressWindow.ProgressUpdate> { @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
private Worker<Void> rebuildRepository(final DBPopulationService.DBPopulationMode mode) {
LOGGER.log(Level.INFO, "(re) starting {0}db population task", mode);
dbPopulationService.setDBPopulationMode(mode);
dbPopulationService.restart();
return dbPopulationService;
}
//TODO: can we avoid this with a state listener? does it amount to the same thing? private static class DBPopulationService extends Service<Void> {
//post population operation to execute
final Runnable postPopulationOperation; enum DBPopulationMode {
FULL,
TAGS_ONLY;
}
private final EventsRepository eventRepo;
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
private DBPopulationMode dbPopulationMode = DBPopulationMode.FULL;
DBPopulationService(EventsRepository eventRepo) {
this.eventRepo = eventRepo;
setExecutor(workerExecutor);
}
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
public final void setDBPopulationMode(DBPopulationMode value) {
dbPopulationMode = value;
}
@Override
protected Task<Void> createTask() {
DBPopulationMode dbPopMode = dbPopulationMode;
switch (dbPopMode) {
case FULL:
return eventRepo.new DBPopulationWorker();
case TAGS_ONLY:
return eventRepo.new RebuildTagsWorker();
default:
throw new IllegalArgumentException("Unknown db population mode: " + dbPopMode + ". Skipping db population.");
}
}
}
/**
*
* @param lastObjId the value of lastObjId
* @param lastArtfID the value of lastArtfID
* @param injestRunning the value of injestRunning
*/
public void recordDBPopulationState(final long lastObjId, final long lastArtfID, final Boolean injestRunning) {
recordLastObjID(lastObjId);
recordLastArtifactID(lastArtfID);
recordWasIngestRunning(injestRunning);
}
public boolean isRebuilding() {
FutureTask<Boolean> task = new FutureTask<>(dbPopulationService::isRunning);
Platform.runLater(task);
try {
return task.get();
} catch (InterruptedException | ExecutionException exception) {
LOGGER.log(Level.SEVERE, "There was an error determining the state of the db population service.", exception);
}
return false;
}
/**
* A base class for Tasks that show a updates a {@link ProgressHandle} as it
* performs its background work on the events DB.
*
* //TODO: I don't like the coupling to ProgressHandle, but the
* alternatives I can think of seem even worse. -jm
*/
private abstract class DBProgressWorker extends Task<Void> {
final SleuthkitCase skCase; final SleuthkitCase skCase;
final TagsManager tagsManager; final TagsManager tagsManager;
final ProgressWindow progressDialog;
volatile ProgressHandle progressHandle; volatile ProgressHandle progressHandle;
DBProgressWorker(Runnable postPopulationOperation, String initialProgressDisplayName) { DBProgressWorker(String initialProgressDisplayName) {
progressDialog = new ProgressWindow(null, false, this); progressHandle = ProgressHandleFactory.createHandle(initialProgressDisplayName, this::cancel);
progressDialog.setVisible(true);
progressHandle = ProgressHandleFactory.createHandle(initialProgressDisplayName, () -> cancel(true));
skCase = autoCase.getSleuthkitCase(); skCase = autoCase.getSleuthkitCase();
tagsManager = autoCase.getServices().getTagsManager(); tagsManager = autoCase.getServices().getTagsManager();
this.postPopulationOperation = postPopulationOperation;
}
/**
* update progress UIs
*
* @param chunk
*/
final protected void update(ProgressWindow.ProgressUpdate chunk) {
SwingUtilities.invokeLater(() -> {
progressDialog.update(chunk);
});
if (chunk.getTotal() >= 0) {
progressHandle.progress(chunk.getProgress());
}
progressHandle.setDisplayName(chunk.getHeaderMessage());
progressHandle.progress(chunk.getDetailMessage());
} }
@Override @Override
protected void done() { protected void updateTitle(String title) {
super.done(); super.updateTitle(title);
progressDialog.close(); progressHandle.setDisplayName(title);
postPopulationOperation.run(); //execute post db population operation }
@Override
protected void updateMessage(String message) {
super.updateMessage(message);
progressHandle.progress(message);
}
@Override
protected void updateProgress(double workDone, double max) {
super.updateProgress(workDone, max);
if (workDone >= 0) {
progressHandle.progress((int) workDone);
}
}
@Override
protected void updateProgress(long workDone, long max) {
super.updateProgress(workDone, max);
super.updateProgress(workDone, max);
if (workDone >= 0) {
progressHandle.progress((int) workDone);
}
} }
} }
@ -405,17 +471,15 @@ public class EventsRepository {
private class RebuildTagsWorker extends DBProgressWorker { private class RebuildTagsWorker extends DBProgressWorker {
@NbBundle.Messages("RebuildTagsWorker.task.displayName=refreshing tags") @NbBundle.Messages("RebuildTagsWorker.task.displayName=refreshing tags")
RebuildTagsWorker(Runnable postPopulationOperation) { RebuildTagsWorker() {
super(postPopulationOperation, Bundle.RebuildTagsWorker_task_displayName()); super(Bundle.RebuildTagsWorker_task_displayName());
} }
@Override @Override
@NbBundle.Messages({"progressWindow.msg.refreshingFileTags=refreshing file tags", @NbBundle.Messages({"progressWindow.msg.refreshingFileTags=refreshing file tags",
"progressWindow.msg.refreshingResultTags=refreshing result tags", "progressWindow.msg.refreshingResultTags=refreshing result tags",
"progressWindow.msg.commitingTags=committing tag changes"}) "progressWindow.msg.commitingTags=committing tag changes"})
protected Void doInBackground() throws Exception { protected Void call() throws Exception {
int currentWorkTotal;
progressHandle.start(); progressHandle.start();
EventDB.EventTransaction trans = eventDB.beginTransaction(); EventDB.EventTransaction trans = eventDB.beginTransaction();
LOGGER.log(Level.INFO, "dropping old tags"); // NON-NLS LOGGER.log(Level.INFO, "dropping old tags"); // NON-NLS
@ -424,15 +488,16 @@ public class EventsRepository {
LOGGER.log(Level.INFO, "updating content tags"); // NON-NLS LOGGER.log(Level.INFO, "updating content tags"); // NON-NLS
List<ContentTag> contentTags = tagsManager.getAllContentTags(); List<ContentTag> contentTags = tagsManager.getAllContentTags();
progressHandle.finish(); progressHandle.finish();
progressHandle = ProgressHandleFactory.createHandle(Bundle.progressWindow_msg_refreshingFileTags(), progressHandle = ProgressHandleFactory.createHandle(Bundle.progressWindow_msg_refreshingFileTags(), this::cancel);
() -> cancel(true)); updateTitle(Bundle.progressWindow_msg_refreshingFileTags());
progressHandle.start(currentWorkTotal = contentTags.size()); int currentWorkTotal = contentTags.size();
progressHandle.start(currentWorkTotal);
for (int i = 0; i < currentWorkTotal; i++) { for (int i = 0; i < currentWorkTotal; i++) {
if (isCancelled()) { if (isCancelled()) {
break; break;
} }
update(new ProgressWindow.ProgressUpdate(i, currentWorkTotal, Bundle.progressWindow_msg_refreshingFileTags())); updateProgress(i, currentWorkTotal);
ContentTag contentTag = contentTags.get(i); ContentTag contentTag = contentTags.get(i);
eventDB.addTag(contentTag.getContent().getId(), null, contentTag); eventDB.addTag(contentTag.getContent().getId(), null, contentTag);
} }
@ -440,15 +505,15 @@ public class EventsRepository {
LOGGER.log(Level.INFO, "updating artifact tags"); // NON-NLS LOGGER.log(Level.INFO, "updating artifact tags"); // NON-NLS
List<BlackboardArtifactTag> artifactTags = tagsManager.getAllBlackboardArtifactTags(); List<BlackboardArtifactTag> artifactTags = tagsManager.getAllBlackboardArtifactTags();
progressHandle.finish(); progressHandle.finish();
progressHandle = ProgressHandleFactory.createHandle(Bundle.progressWindow_msg_refreshingResultTags(), progressHandle = ProgressHandleFactory.createHandle(Bundle.progressWindow_msg_refreshingResultTags(), this::cancel);
() -> cancel(true)); updateTitle(Bundle.progressWindow_msg_refreshingResultTags());
progressHandle.start(currentWorkTotal = artifactTags.size()); currentWorkTotal = artifactTags.size();
progressHandle.start(currentWorkTotal);
for (int i = 0; i < currentWorkTotal; i++) { for (int i = 0; i < currentWorkTotal; i++) {
if (isCancelled()) { if (isCancelled()) {
break; break;
} }
update(new ProgressWindow.ProgressUpdate(i, currentWorkTotal, Bundle.progressWindow_msg_refreshingResultTags())); updateProgress(i, currentWorkTotal);
BlackboardArtifactTag artifactTag = artifactTags.get(i); BlackboardArtifactTag artifactTag = artifactTags.get(i);
eventDB.addTag(artifactTag.getContent().getId(), artifactTag.getArtifact().getArtifactID(), artifactTag); eventDB.addTag(artifactTag.getContent().getId(), artifactTag.getArtifact().getArtifactID(), artifactTag);
} }
@ -457,7 +522,8 @@ public class EventsRepository {
progressHandle.finish(); progressHandle.finish();
progressHandle = ProgressHandleFactory.createHandle(Bundle.progressWindow_msg_commitingTags()); progressHandle = ProgressHandleFactory.createHandle(Bundle.progressWindow_msg_commitingTags());
progressHandle.start(); progressHandle.start();
update(new ProgressWindow.ProgressUpdate(0, -1, Bundle.progressWindow_msg_commitingTags())); updateTitle(Bundle.progressWindow_msg_commitingTags());
updateProgress(-.5, 1);
if (isCancelled()) { if (isCancelled()) {
eventDB.rollBackTransaction(trans); eventDB.rollBackTransaction(trans);
@ -492,25 +558,33 @@ public class EventsRepository {
private class DBPopulationWorker extends DBProgressWorker { private class DBPopulationWorker extends DBProgressWorker {
@NbBundle.Messages("DBPopulationWorker.task.displayName=(re)initializing events database") @NbBundle.Messages("DBPopulationWorker.task.displayName=(re)initializing events database")
public DBPopulationWorker(Runnable postPopulationOperation) { DBPopulationWorker() {
super(postPopulationOperation, Bundle.DBPopulationWorker_task_displayName()); super(Bundle.DBPopulationWorker_task_displayName());
} }
@Override @Override
@NbBundle.Messages({"progressWindow.msg.populateMacEventsFiles=Populating MAC time events for files:", @NbBundle.Messages({"progressWindow.msg.populateMacEventsFiles=Populating MAC time events for files",
"progressWindow.msg.reinit_db=(re)initializing events database", "progressWindow.msg.reinit_db=(Re)Initializing events database",
"progressWindow.msg.gatheringData=Gather event data",
"progressWindow.msg.commitingDb=committing events db"}) "progressWindow.msg.commitingDb=committing events db"})
protected Void doInBackground() throws Exception { protected Void call() throws Exception {
LOGGER.log(Level.INFO, "Beginning population of timeline db."); // NON-NLS
progressHandle.start(); progressHandle.start();
update(new ProgressWindow.ProgressUpdate(0, -1, Bundle.progressWindow_msg_reinit_db())); updateProgress(-.5, 1);
updateTitle(Bundle.progressWindow_msg_reinit_db());
//reset database //TODO: can we do more incremental updates? -jm //reset database //TODO: can we do more incremental updates? -jm
eventDB.reInitializeDB(); eventDB.reInitializeDB();
updateTitle(Bundle.progressWindow_msg_gatheringData());
long lastObjId = skCase.getLastObjectId();
long lastArtfID = TimeLineController.getCaseLastArtifactID(skCase);
boolean injestRunning = IngestManager.getInstance().isIngestRunning();
//grab ids of all files //grab ids of all files
List<Long> fileIDs = skCase.findAllFileIdsWhere("name != '.' AND name != '..'"); List<Long> fileIDs = skCase.findAllFileIdsWhere("name != '.' AND name != '..'");
final int numFiles = fileIDs.size(); final int numFiles = fileIDs.size();
progressHandle.switchToDeterminate(numFiles); progressHandle.switchToDeterminate(numFiles);
update(new ProgressWindow.ProgressUpdate(0, numFiles, Bundle.progressWindow_msg_populateMacEventsFiles())); updateTitle(Bundle.progressWindow_msg_populateMacEventsFiles());
//insert file events into db //insert file events into db
EventDB.EventTransaction trans = eventDB.beginTransaction(); EventDB.EventTransaction trans = eventDB.beginTransaction();
@ -526,8 +600,8 @@ public class EventsRepository {
LOGGER.log(Level.WARNING, "Failed to get data for file : {0}", fID); // NON-NLS LOGGER.log(Level.WARNING, "Failed to get data for file : {0}", fID); // NON-NLS
} else { } else {
insertEventsForFile(f, trans); insertEventsForFile(f, trans);
update(new ProgressWindow.ProgressUpdate(i, numFiles, updateProgress(i, numFiles);
Bundle.progressWindow_msg_populateMacEventsFiles(), f.getName())); updateMessage(f.getName());
} }
} catch (TskCoreException tskCoreException) { } catch (TskCoreException tskCoreException) {
LOGGER.log(Level.SEVERE, "Failed to insert MAC time events for file : " + fID, tskCoreException); // NON-NLS LOGGER.log(Level.SEVERE, "Failed to insert MAC time events for file : " + fID, tskCoreException); // NON-NLS
@ -550,7 +624,8 @@ public class EventsRepository {
progressHandle.finish(); progressHandle.finish();
progressHandle = ProgressHandleFactory.createHandle(Bundle.progressWindow_msg_commitingDb()); progressHandle = ProgressHandleFactory.createHandle(Bundle.progressWindow_msg_commitingDb());
progressHandle.start(); progressHandle.start();
update(new ProgressWindow.ProgressUpdate(0, -1, Bundle.progressWindow_msg_commitingDb())); updateProgress(-0.5, 1);
updateTitle(Bundle.progressWindow_msg_commitingDb());
if (isCancelled()) { if (isCancelled()) {
eventDB.rollBackTransaction(trans); eventDB.rollBackTransaction(trans);
@ -561,6 +636,7 @@ public class EventsRepository {
populateFilterData(skCase); populateFilterData(skCase);
invalidateCaches(); invalidateCaches();
recordDBPopulationState(lastObjId, lastArtfID, injestRunning);
progressHandle.finish(); progressHandle.finish();
return null; return null;
} }
@ -573,10 +649,12 @@ public class EventsRepository {
timeMap.put(FileSystemTypes.FILE_CHANGED, f.getCtime()); timeMap.put(FileSystemTypes.FILE_CHANGED, f.getCtime());
timeMap.put(FileSystemTypes.FILE_MODIFIED, f.getMtime()); timeMap.put(FileSystemTypes.FILE_MODIFIED, f.getMtime());
/* if there are no legitimate ( greater tan zero ) time stamps ( eg, /*
* if there are no legitimate ( greater tan zero ) time stamps ( eg,
* logical/local files) skip the rest of the event generation: this * logical/local files) skip the rest of the event generation: this
* should result in droping logical files, since they do not have * should result in droping logical files, since they do not have
* legitimate time stamps. */ * legitimate time stamps.
*/
if (Collections.max(timeMap.values()) > 0) { if (Collections.max(timeMap.values()) > 0) {
final String uniquePath = f.getUniquePath(); final String uniquePath = f.getUniquePath();
final String parentPath = f.getParentPath(); final String parentPath = f.getParentPath();
@ -593,8 +671,10 @@ public class EventsRepository {
List<ContentTag> tags = tagsManager.getContentTagsByContent(f); List<ContentTag> tags = tagsManager.getContentTagsByContent(f);
for (Map.Entry<FileSystemTypes, Long> timeEntry : timeMap.entrySet()) { for (Map.Entry<FileSystemTypes, Long> timeEntry : timeMap.entrySet()) {
/* if the time is legitimate ( greater than zero ) insert it /*
* into the db */ * if the time is legitimate ( greater than zero ) insert it
* into the db
*/
if (timeEntry.getValue() > 0) { if (timeEntry.getValue() > 0) {
eventDB.insertEvent(timeEntry.getValue(), timeEntry.getKey(), eventDB.insertEvent(timeEntry.getValue(), timeEntry.getKey(),
datasourceID, f.getId(), null, uniquePath, medDesc, datasourceID, f.getId(), null, uniquePath, medDesc,
@ -636,12 +716,12 @@ public class EventsRepository {
progressHandle.finish(); progressHandle.finish();
progressHandle = ProgressHandleFactory.createHandle(Bundle.progressWindow_populatingXevents(type.getDisplayName()), () -> cancel(true)); progressHandle = ProgressHandleFactory.createHandle(Bundle.progressWindow_populatingXevents(type.getDisplayName()), () -> cancel(true));
progressHandle.start(numArtifacts); progressHandle.start(numArtifacts);
updateTitle(Bundle.progressWindow_populatingXevents(type.getDisplayName()));
for (int i = 0; i < numArtifacts; i++) { for (int i = 0; i < numArtifacts; i++) {
try { try {
//for each artifact, extract the relevant information for the descriptions //for each artifact, extract the relevant information for the descriptions
insertEventForArtifact(type, blackboardArtifacts.get(i), trans); insertEventForArtifact(type, blackboardArtifacts.get(i), trans);
update(new ProgressWindow.ProgressUpdate(i, numArtifacts, updateProgress(i, numArtifacts);
Bundle.progressWindow_populatingXevents(type.getDisplayName())));
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "There was a problem inserting event for artifact: " + blackboardArtifacts.get(i).getArtifactID(), ex); // NON-NLS LOGGER.log(Level.SEVERE, "There was a problem inserting event for artifact: " + blackboardArtifacts.get(i).getArtifactID(), ex); // NON-NLS
} }
@ -653,8 +733,10 @@ public class EventsRepository {
private void insertEventForArtifact(final ArtifactEventType type, BlackboardArtifact bbart, EventDB.EventTransaction trans) throws TskCoreException { private void insertEventForArtifact(final ArtifactEventType type, BlackboardArtifact bbart, EventDB.EventTransaction trans) throws TskCoreException {
ArtifactEventType.AttributeEventDescription eventDescription = ArtifactEventType.buildEventDescription(type, bbart); ArtifactEventType.AttributeEventDescription eventDescription = ArtifactEventType.buildEventDescription(type, bbart);
/* if the time is legitimate ( greater than zero ) insert it into /*
* the db */ * if the time is legitimate ( greater than zero ) insert it into
* the db
*/
if (eventDescription != null && eventDescription.getTime() > 0) { if (eventDescription != null && eventDescription.getTime() > 0) {
long objectID = bbart.getObjectID(); long objectID = bbart.getObjectID();
AbstractFile f = skCase.getAbstractFileById(objectID); AbstractFile f = skCase.getAbstractFileById(objectID);