Merge pull request #795 from rcordovano/snapshots_improvements

Snapshots improvements
This commit is contained in:
Richard Cordovano 2014-06-19 13:29:05 -04:00
commit b3d1f71a65
12 changed files with 231 additions and 189 deletions

View File

@ -20,8 +20,6 @@ package org.sleuthkit.autopsy.actions;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import org.openide.awt.ActionID; import org.openide.awt.ActionID;
import org.openide.awt.ActionReference; import org.openide.awt.ActionReference;
import org.openide.awt.ActionRegistration; import org.openide.awt.ActionRegistration;
@ -29,7 +27,6 @@ import org.openide.util.HelpCtx;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.util.actions.SystemAction; import org.openide.util.actions.SystemAction;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestProgressSnapshotDialog; import org.sleuthkit.autopsy.ingest.IngestProgressSnapshotDialog;
@ActionID( @ActionID(
@ -46,16 +43,6 @@ public final class ShowIngestProgressSnapshotAction extends SystemAction impleme
private static final String ACTION_NAME = NbBundle.getMessage(ShowIngestProgressSnapshotAction.class, "ShowIngestProgressSnapshotAction.actionName.text"); private static final String ACTION_NAME = NbBundle.getMessage(ShowIngestProgressSnapshotAction.class, "ShowIngestProgressSnapshotAction.actionName.text");
public ShowIngestProgressSnapshotAction() {
setEnabled(false);
IngestManager.getInstance().addIngestJobEventListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
setEnabled(IngestManager.getInstance().isIngestRunning());
}
});
}
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
IngestProgressSnapshotDialog dialog = new IngestProgressSnapshotDialog(); IngestProgressSnapshotDialog dialog = new IngestProgressSnapshotDialog();

View File

@ -68,12 +68,14 @@ IngestJobConfigurator.createModuleSettingsFolderForContext.exception.title=Inges
IngestJobConfigurator.saveJobSettings.usermsg=Failed to save ingest job settings for {0} module. IngestJobConfigurator.saveJobSettings.usermsg=Failed to save ingest job settings for {0} module.
IngestJobConfigurator.saveJobSettings.usermsg.title=Ingest Job Settings IngestJobConfigurator.saveJobSettings.usermsg.title=Ingest Job Settings
IngestJobConfigurationPanel.descriptionLabel.text= IngestJobConfigurationPanel.descriptionLabel.text=
IngestProgressSnapshotDialog.title.text=Ingest Task Progress Snapshots IngestProgressSnapshotDialog.title.text=Ingest Progress Snapshot
IngestProgressSnapshotPanel.refreshButton.text=Refresh IngestProgressSnapshotPanel.refreshButton.text=Refresh
IngestProgressSnapshotPanel.closeButton.text=Close IngestProgressSnapshotPanel.closeButton.text=Close
IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.threadID=Thread ID IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.threadID=Thread ID
IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.dataSource=Data Source IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.dataSource=Data Source
IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.ingestModule=Ingest Module IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.activity=Activity
IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.file=File IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.file=File
IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.startTime=Start Time IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.startTime=Start Time
IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.elapsedTime=Elapsed Time (H\:M\:S) IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.elapsedTime=Elapsed Time (H\:M\:S)
IngestProgressSnapshotPanel.fileProcessedPerSecondLabel.text=Files/Sec: {0}
IngestManager.IngestThreadActivitySnapshot.idleThread=IDLE

View File

@ -32,6 +32,7 @@ import org.sleuthkit.datamodel.Content;
*/ */
final class DataSourceIngestPipeline { final class DataSourceIngestPipeline {
private static final IngestManager ingestManager = IngestManager.getInstance();
private final IngestJobContext context; private final IngestJobContext context;
private List<DataSourceIngestModuleDecorator> modules = new ArrayList<>(); private List<DataSourceIngestModuleDecorator> modules = new ArrayList<>();
@ -90,7 +91,7 @@ final class DataSourceIngestPipeline {
progress.setDisplayName(NbBundle.getMessage(this.getClass(), progress.setDisplayName(NbBundle.getMessage(this.getClass(),
"IngestJob.progress.dataSourceIngest.displayName", "IngestJob.progress.dataSourceIngest.displayName",
module.getDisplayName(), dataSource.getName())); module.getDisplayName(), dataSource.getName()));
task.updateProgressStatus(module.getDisplayName(), null); ingestManager.setIngestTaskProgress(task, module.getDisplayName());
module.process(dataSource, new DataSourceIngestModuleProgress(progress)); module.process(dataSource, new DataSourceIngestModuleProgress(progress));
} catch (Exception ex) { // Catch-all exception firewall } catch (Exception ex) { // Catch-all exception firewall
errors.add(new IngestModuleError(module.getDisplayName(), ex)); errors.add(new IngestModuleError(module.getDisplayName(), ex));
@ -99,6 +100,7 @@ final class DataSourceIngestPipeline {
break; break;
} }
} }
ingestManager.setIngestTaskProgressCompleted(task);
return errors; return errors;
} }

View File

@ -18,21 +18,15 @@
*/ */
package org.sleuthkit.autopsy.ingest; package org.sleuthkit.autopsy.ingest;
import org.sleuthkit.datamodel.Content;
final class DataSourceIngestTask extends IngestTask { final class DataSourceIngestTask extends IngestTask {
DataSourceIngestTask(IngestJob job, ProgressSnapshots snapshots) { DataSourceIngestTask(IngestJob job) {
super(job, snapshots); super(job);
} }
Content getDataSource() {
return getIngestJob().getDataSource();
}
@Override @Override
void execute(long threadId) throws InterruptedException { void execute(long threadId) throws InterruptedException {
super.execute(threadId); super.setThreadId(threadId);
getIngestJob().process(this); getIngestJob().process(this);
} }
} }

View File

@ -30,8 +30,9 @@ import org.sleuthkit.datamodel.AbstractFile;
*/ */
final class FileIngestPipeline { final class FileIngestPipeline {
private static final IngestManager ingestManager = IngestManager.getInstance();
private final IngestJobContext context; private final IngestJobContext context;
private List<FileIngestModuleDecorator> modules = new ArrayList<>(); private final List<FileIngestModuleDecorator> modules = new ArrayList<>();
FileIngestPipeline(IngestJobContext context, List<IngestModuleTemplate> moduleTemplates) { FileIngestPipeline(IngestJobContext context, List<IngestModuleTemplate> moduleTemplates) {
this.context = context; this.context = context;
@ -96,7 +97,7 @@ final class FileIngestPipeline {
AbstractFile file = task.getFile(); AbstractFile file = task.getFile();
for (FileIngestModuleDecorator module : modules) { for (FileIngestModuleDecorator module : modules) {
try { try {
task.updateProgressStatus(module.getDisplayName(), file); ingestManager.setIngestTaskProgress(task, module.getDisplayName());
module.process(file); module.process(file);
} catch (Exception ex) { // Catch-all exception firewall } catch (Exception ex) { // Catch-all exception firewall
errors.add(new IngestModuleError(module.getDisplayName(), ex)); errors.add(new IngestModuleError(module.getDisplayName(), ex));
@ -109,6 +110,7 @@ final class FileIngestPipeline {
if (!context.isJobCancelled()) { if (!context.isJobCancelled()) {
IngestManager.getInstance().fireFileIngestDone(file.getId()); IngestManager.getInstance().fireFileIngestDone(file.getId());
} }
ingestManager.setIngestTaskProgressCompleted(task);
return errors; return errors;
} }

View File

@ -29,8 +29,8 @@ final class FileIngestTask extends IngestTask {
private final AbstractFile file; private final AbstractFile file;
FileIngestTask(IngestJob job, AbstractFile file, ProgressSnapshots snapshots) { FileIngestTask(IngestJob job, AbstractFile file) {
super(job, snapshots); super(job);
this.file = file; this.file = file;
} }
@ -40,7 +40,7 @@ final class FileIngestTask extends IngestTask {
@Override @Override
void execute(long threadId) throws InterruptedException { void execute(long threadId) throws InterruptedException {
super.execute(threadId); super.setThreadId(threadId);
getIngestJob().process(this); getIngestJob().process(this);
} }

View File

@ -180,10 +180,7 @@ final class IngestJob {
* @return True or false. * @return True or false.
*/ */
boolean hasDataSourceIngestPipeline() { boolean hasDataSourceIngestPipeline() {
if (dataSourceIngestPipeline.isEmpty()) { return (dataSourceIngestPipeline.isEmpty() == false);
return false;
}
return true;
} }
/** /**
@ -192,10 +189,7 @@ final class IngestJob {
* @return True or false. * @return True or false.
*/ */
boolean hasFileIngestPipeline() { boolean hasFileIngestPipeline() {
if (fileIngestPipelines.peek().isEmpty()) { return (fileIngestPipelines.peek().isEmpty() == false);
return false;
}
return true;
} }
void process(DataSourceIngestTask task) throws InterruptedException { void process(DataSourceIngestTask task) throws InterruptedException {
@ -206,7 +200,7 @@ final class IngestJob {
logIngestModuleErrors(errors); logIngestModuleErrors(errors);
} }
} }
if (dataSourceIngestProgress != null) { if (null != dataSourceIngestProgress) {
dataSourceIngestProgress.finish(); dataSourceIngestProgress.finish();
// This is safe because this method will be called at most once per // This is safe because this method will be called at most once per
// ingest job and finish() will not be called while that single // ingest job and finish() will not be called while that single

View File

@ -21,6 +21,8 @@ package org.sleuthkit.autopsy.ingest;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport; import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -40,6 +42,7 @@ import org.sleuthkit.datamodel.Content;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.datamodel.AbstractFile;
/** /**
* Manages the execution of ingest jobs. * Manages the execution of ingest jobs.
@ -63,13 +66,16 @@ public class IngestManager {
private final ConcurrentHashMap<Long, Future<Void>> startIngestJobThreads = new ConcurrentHashMap<>(); // Maps thread ids to cancellation handles. private final ConcurrentHashMap<Long, Future<Void>> startIngestJobThreads = new ConcurrentHashMap<>(); // Maps thread ids to cancellation handles.
private final ConcurrentHashMap<Long, Future<?>> dataSourceIngestThreads = new ConcurrentHashMap<>(); // Maps thread ids to cancellation handles. private final ConcurrentHashMap<Long, Future<?>> dataSourceIngestThreads = new ConcurrentHashMap<>(); // Maps thread ids to cancellation handles.
private final ConcurrentHashMap<Long, Future<?>> fileIngestThreads = new ConcurrentHashMap<>(); // Maps thread ids to cancellation handles. private final ConcurrentHashMap<Long, Future<?>> fileIngestThreads = new ConcurrentHashMap<>(); // Maps thread ids to cancellation handles.
private final ConcurrentHashMap<Long, IngestThreadActivitySnapshot> ingestThreadActivitySnapshots = new ConcurrentHashMap<>(); // Maps ingest thread ids to progress ingestThreadActivitySnapshots.
private final Object processedFilesSnapshotLock = new Object();
private ProcessedFilesSnapshot processedFilesSnapshot = new ProcessedFilesSnapshot();
private volatile IngestMessageTopComponent ingestMessageBox; private volatile IngestMessageTopComponent ingestMessageBox;
private int numberOfFileIngestThreads = DEFAULT_NUMBER_OF_FILE_INGEST_THREADS; private int numberOfFileIngestThreads = DEFAULT_NUMBER_OF_FILE_INGEST_THREADS;
/** /**
* Gets the ingest manager. * Gets the ingest manager.
* *
* @returns A singleton IngestManager object. * @return A singleton IngestManager object.
*/ */
public synchronized static IngestManager getInstance() { public synchronized static IngestManager getInstance() {
if (instance == null) { if (instance == null) {
@ -113,14 +119,17 @@ public class IngestManager {
/** /**
* Gets the number of data source ingest threads the ingest manager will * Gets the number of data source ingest threads the ingest manager will
* use. * use.
*
* @return The number of data source ingest threads.
*/ */
public int getNumberOfDataSourceIngestThreads() { public int getNumberOfDataSourceIngestThreads() {
return DEFAULT_NUMBER_OF_DATA_SOURCE_INGEST_THREADS; return DEFAULT_NUMBER_OF_DATA_SOURCE_INGEST_THREADS;
} }
/** /**
* Gets the maximum number of file ingest threads the ingest manager will * Gets the number of file ingest threads the ingest manager will use.
* use. *
* @return The number of file ingest threads.
*/ */
public int getNumberOfFileIngestThreads() { public int getNumberOfFileIngestThreads() {
return numberOfFileIngestThreads; return numberOfFileIngestThreads;
@ -134,6 +143,7 @@ public class IngestManager {
long threadId = nextThreadId.incrementAndGet(); long threadId = nextThreadId.incrementAndGet();
Future<?> handle = dataSourceIngestThreadPool.submit(new ExecuteIngestTasksThread(threadId, IngestScheduler.getInstance().getDataSourceIngestTaskQueue())); Future<?> handle = dataSourceIngestThreadPool.submit(new ExecuteIngestTasksThread(threadId, IngestScheduler.getInstance().getDataSourceIngestTaskQueue()));
dataSourceIngestThreads.put(threadId, handle); dataSourceIngestThreads.put(threadId, handle);
ingestThreadActivitySnapshots.put(threadId, new IngestThreadActivitySnapshot(threadId));
} }
/** /**
@ -144,6 +154,7 @@ public class IngestManager {
long threadId = nextThreadId.incrementAndGet(); long threadId = nextThreadId.incrementAndGet();
Future<?> handle = fileIngestThreadPool.submit(new ExecuteIngestTasksThread(threadId, IngestScheduler.getInstance().getFileIngestTaskQueue())); Future<?> handle = fileIngestThreadPool.submit(new ExecuteIngestTasksThread(threadId, IngestScheduler.getInstance().getFileIngestTaskQueue()));
fileIngestThreads.put(threadId, handle); fileIngestThreads.put(threadId, handle);
ingestThreadActivitySnapshots.put(threadId, new IngestThreadActivitySnapshot(threadId));
} }
/** /**
@ -203,10 +214,38 @@ public class IngestManager {
return IngestScheduler.getInstance().ingestJobsAreRunning(); return IngestScheduler.getInstance().ingestJobsAreRunning();
} }
List<IngestTask.ProgressSnapshot> getIngestTaskProgressSnapshots() { void setIngestTaskProgress(DataSourceIngestTask task, String ingestModuleDisplayName) {
return IngestScheduler.getInstance().getIngestTaskProgressSnapshots(); ingestThreadActivitySnapshots.put(task.getThreadId(), new IngestThreadActivitySnapshot(task.getThreadId(), ingestModuleDisplayName, task.getDataSource()));
}
void setIngestTaskProgress(FileIngestTask task, String ingestModuleDisplayName) {
ingestThreadActivitySnapshots.put(task.getThreadId(), new IngestThreadActivitySnapshot(task.getThreadId(), ingestModuleDisplayName, task.getDataSource(), task.getFile()));
} }
void setIngestTaskProgressCompleted(DataSourceIngestTask task) {
ingestThreadActivitySnapshots.put(task.getThreadId(), new IngestThreadActivitySnapshot(task.getThreadId()));
}
void setIngestTaskProgressCompleted(FileIngestTask task) {
ingestThreadActivitySnapshots.put(task.getThreadId(), new IngestThreadActivitySnapshot(task.getThreadId()));
synchronized (processedFilesSnapshotLock) {
processedFilesSnapshot.incrementProcessedFilesCount();
}
}
List<IngestThreadActivitySnapshot> getIngestThreadActivitySnapshots() {
return new ArrayList(ingestThreadActivitySnapshots.values());
}
ProcessedFilesSnapshot getProcessedFilesSnapshot() {
ProcessedFilesSnapshot snapshot;
synchronized (processedFilesSnapshotLock) {
snapshot = processedFilesSnapshot;
processedFilesSnapshot = new ProcessedFilesSnapshot();
}
return snapshot;
}
public void cancelAllIngestJobs() { public void cancelAllIngestJobs() {
// Stop creating new ingest jobs. // Stop creating new ingest jobs.
for (Future<Void> handle : startIngestJobThreads.values()) { for (Future<Void> handle : startIngestJobThreads.values()) {
@ -421,7 +460,7 @@ public class IngestManager {
return -1; return -1;
} }
} }
/** /**
* Creates ingest jobs. * Creates ingest jobs.
*/ */
@ -494,7 +533,7 @@ public class IngestManager {
notifyMessage.append("\n\n"); notifyMessage.append("\n\n");
JOptionPane.showMessageDialog(null, notifyMessage.toString(), JOptionPane.showMessageDialog(null, notifyMessage.toString(),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"IngestManager.StartIngestJobsTask.run.startupErr.dlgTitle"), JOptionPane.ERROR_MESSAGE); "IngestManager.StartIngestJobsTask.run.startupErr.dlgTitle"), JOptionPane.ERROR_MESSAGE);
} }
progress.progress(++dataSourceProcessed); progress.progress(++dataSourceProcessed);
@ -502,11 +541,14 @@ public class IngestManager {
break; break;
} }
} }
} catch (InterruptedException ex) {
// Reset interrupted status.
Thread.currentThread().interrupt();
} finally { } finally {
progress.finish(); progress.finish();
startIngestJobThreads.remove(threadId); startIngestJobThreads.remove(threadId);
return null;
} }
return null;
} }
} }
@ -578,4 +620,80 @@ public class IngestManager {
} }
} }
} }
static final class IngestThreadActivitySnapshot {
private final long threadId;
private final Date startTime;
private final String activity;
private final String dataSourceName;
private final String fileName;
IngestThreadActivitySnapshot(long threadId) {
this.threadId = threadId;
startTime = new Date();
this.activity = NbBundle.getMessage(this.getClass(), "IngestManager.IngestThreadActivitySnapshot.idleThread");
this.dataSourceName = "";
this.fileName = "";
}
IngestThreadActivitySnapshot(long threadId, String activity, Content dataSource) {
this.threadId = threadId;
startTime = new Date();
this.activity = activity;
this.dataSourceName = dataSource.getName();
this.fileName = "";
}
IngestThreadActivitySnapshot(long threadId, String activity, Content dataSource, AbstractFile file) {
this.threadId = threadId;
startTime = new Date();
this.activity = activity;
this.dataSourceName = dataSource.getName();
this.fileName = file.getName();
}
long getThreadId() {
return threadId;
}
Date getStartTime() {
return startTime;
}
String getActivity() {
return activity;
}
String getDataSourceName() {
return dataSourceName;
}
String getFileName() {
return fileName;
}
}
static final class ProcessedFilesSnapshot {
private final Date startTime;
private long processedFilesCount;
ProcessedFilesSnapshot() {
this.startTime = new Date();
this.processedFilesCount = 0;
}
void incrementProcessedFilesCount() {
++processedFilesCount;
}
Date getStartTime() {
return startTime;
}
long getProcessedFilesCount() {
return processedFilesCount;
}
}
} }

View File

@ -19,9 +19,10 @@
<Group type="102" alignment="1" attributes="0"> <Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0"> <Group type="103" groupAlignment="1" attributes="0">
<Component id="snapshotsScrollPane" max="32767" attributes="0"/> <Component id="snapshotsScrollPane" pref="881" max="32767" attributes="0"/>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<EmptySpace min="0" pref="733" max="32767" attributes="0"/> <Component id="fileProcessedPerSecondLabel" min="-2" pref="173" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="refreshButton" linkSize="1" min="-2" max="-2" attributes="0"/> <Component id="refreshButton" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="closeButton" linkSize="1" min="-2" max="-2" attributes="0"/> <Component id="closeButton" linkSize="1" min="-2" max="-2" attributes="0"/>
@ -40,6 +41,7 @@
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="refreshButton" linkSize="2" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="refreshButton" linkSize="2" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="closeButton" linkSize="2" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="closeButton" linkSize="2" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="fileProcessedPerSecondLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
@ -54,7 +56,7 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents> <SubComponents>
<Component class="javax.swing.JTable" name="snapshotsTable"> <Component class="javax.swing.JTable" name="threadActivitySnapshotsTable">
<Properties> <Properties>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor"> <Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor">
<Table columnCount="0" rowCount="0"/> <Table columnCount="0" rowCount="0"/>
@ -89,5 +91,12 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="closeButtonActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="closeButtonActionPerformed"/>
</Events> </Events>
</Component> </Component>
<Component class="javax.swing.JLabel" name="fileProcessedPerSecondLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/ingest/Bundle.properties" key="IngestProgressSnapshotPanel.fileProcessedPerSecondLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -18,8 +18,6 @@
*/ */
package org.sleuthkit.autopsy.ingest; package org.sleuthkit.autopsy.ingest;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import javax.swing.JDialog; import javax.swing.JDialog;
@ -27,79 +25,75 @@ import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn; import javax.swing.table.TableColumn;
import org.apache.commons.lang3.time.DurationFormatUtils; import org.apache.commons.lang3.time.DurationFormatUtils;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.datamodel.AbstractFile;
public class IngestProgressSnapshotPanel extends javax.swing.JPanel { public class IngestProgressSnapshotPanel extends javax.swing.JPanel {
private final JDialog parent; private final JDialog parent;
private final SnapshotsTableModel tableModel; private final IngestThreadActivitySnapshotsTableModel threadActivityTableModel;
IngestProgressSnapshotPanel(JDialog parent) { IngestProgressSnapshotPanel(JDialog parent) {
this.parent = parent; this.parent = parent;
tableModel = new SnapshotsTableModel(); threadActivityTableModel = new IngestThreadActivitySnapshotsTableModel();
initComponents(); initComponents();
customizeComponents(); customizeComponents();
IngestManager.getInstance().addIngestJobEventListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
refreshButton.setEnabled(IngestManager.getInstance().isIngestRunning() == true);
}
});
} }
private void customizeComponents() { private void customizeComponents() {
snapshotsTable.setModel(tableModel); threadActivitySnapshotsTable.setModel(threadActivityTableModel);
int width = snapshotsScrollPane.getPreferredSize().width; int width = snapshotsScrollPane.getPreferredSize().width;
for (int i = 0; i < snapshotsTable.getColumnCount(); ++i) { for (int i = 0; i < threadActivitySnapshotsTable.getColumnCount(); ++i) {
TableColumn column = snapshotsTable.getColumnModel().getColumn(i); TableColumn column = threadActivitySnapshotsTable.getColumnModel().getColumn(i);
switch (i) { switch (i) {
case 0: case 0:
column.setPreferredWidth(((int) (width * 0.05))); column.setPreferredWidth(((int) (width * 0.02)));
break; break;
case 1: case 1:
column.setPreferredWidth(((int) (width * 0.15))); column.setPreferredWidth(((int) (width * 0.20)));
break; break;
case 2: case 2:
column.setPreferredWidth(((int) (width * 0.25))); column.setPreferredWidth(((int) (width * 0.15)));
break; break;
case 3: case 3:
column.setPreferredWidth(((int) (width * 0.35))); column.setPreferredWidth(((int) (width * 0.35)));
break; break;
case 4: case 4:
column.setPreferredWidth(((int) (width * 0.10))); column.setPreferredWidth(((int) (width * 0.18)));
break; break;
case 5: case 5:
column.setPreferredWidth(((int) (width * 0.10))); column.setPreferredWidth(((int) (width * 0.10)));
break; break;
} }
} }
snapshotsTable.setFillsViewportHeight(true); threadActivitySnapshotsTable.setFillsViewportHeight(true);
fileProcessedPerSecondLabel.setText(NbBundle.getMessage(this.getClass(),
"IngestProgressSnapshotPanel.fileProcessedPerSecondLabel.text", 0));
} }
private class SnapshotsTableModel extends AbstractTableModel { private class IngestThreadActivitySnapshotsTableModel extends AbstractTableModel {
private final String[] columnNames = {NbBundle.getMessage(this.getClass(), private final String[] columnNames = {NbBundle.getMessage(this.getClass(),
"IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.threadID"), "IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.threadID"),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.dataSource"), "IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.activity"),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.ingestModule"), "IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.dataSource"),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.file"), "IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.file"),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.startTime"), "IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.startTime"),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.elapsedTime")}; "IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.elapsedTime")};
private List<IngestTask.ProgressSnapshot> snapshots; private List<IngestManager.IngestThreadActivitySnapshot> snapshots;
private SnapshotsTableModel() { private IngestThreadActivitySnapshotsTableModel() {
refresh(); refresh();
} }
private void refresh() { private void refresh() {
snapshots = IngestManager.getInstance().getIngestTaskProgressSnapshots(); snapshots = IngestManager.getInstance().getIngestThreadActivitySnapshots();
fireTableDataChanged(); fireTableDataChanged();
} }
@ -120,25 +114,20 @@ public class IngestProgressSnapshotPanel extends javax.swing.JPanel {
@Override @Override
public Object getValueAt(int rowIndex, int columnIndex) { public Object getValueAt(int rowIndex, int columnIndex) {
IngestTask.ProgressSnapshot snapshot = snapshots.get(rowIndex); IngestManager.IngestThreadActivitySnapshot snapshot = snapshots.get(rowIndex);
Object cellValue; Object cellValue;
switch (columnIndex) { switch (columnIndex) {
case 0: case 0:
cellValue = snapshot.getThreadId(); cellValue = snapshot.getThreadId();
break; break;
case 1: case 1:
cellValue = snapshot.getDataSource().getName(); cellValue = snapshot.getActivity();
break; break;
case 2: case 2:
cellValue = snapshot.getModuleDisplayName(); cellValue = snapshot.getDataSourceName();
break; break;
case 3: case 3:
AbstractFile file = snapshot.getFile(); cellValue = snapshot.getFileName();
if (file != null) {
cellValue = file.getName();
} else {
cellValue = "";
}
break; break;
case 4: case 4:
cellValue = snapshot.getStartTime(); cellValue = snapshot.getStartTime();
@ -146,10 +135,7 @@ public class IngestProgressSnapshotPanel extends javax.swing.JPanel {
case 5: case 5:
Date now = new Date(); Date now = new Date();
long elapsedTime = now.getTime() - snapshot.getStartTime().getTime(); long elapsedTime = now.getTime() - snapshot.getStartTime().getTime();
cellValue = DurationFormatUtils.formatDurationHMS(elapsedTime); cellValue = DurationFormatUtils.formatDurationHMS(elapsedTime);
// TODO: Restore when we go to Java 8
// long elapsedTime = Duration.between(snapshot.getStartTime(), LocalTime.now()).toMillis();
// cellValue = DurationFormatUtils.formatDurationHMS(elapsedTime);
break; break;
default: default:
cellValue = null; cellValue = null;
@ -169,11 +155,12 @@ public class IngestProgressSnapshotPanel extends javax.swing.JPanel {
private void initComponents() { private void initComponents() {
snapshotsScrollPane = new javax.swing.JScrollPane(); snapshotsScrollPane = new javax.swing.JScrollPane();
snapshotsTable = new javax.swing.JTable(); threadActivitySnapshotsTable = new javax.swing.JTable();
refreshButton = new javax.swing.JButton(); refreshButton = new javax.swing.JButton();
closeButton = new javax.swing.JButton(); closeButton = new javax.swing.JButton();
fileProcessedPerSecondLabel = new javax.swing.JLabel();
snapshotsTable.setModel(new javax.swing.table.DefaultTableModel( threadActivitySnapshotsTable.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] { new Object [][] {
}, },
@ -181,7 +168,7 @@ public class IngestProgressSnapshotPanel extends javax.swing.JPanel {
} }
)); ));
snapshotsScrollPane.setViewportView(snapshotsTable); snapshotsScrollPane.setViewportView(threadActivitySnapshotsTable);
org.openide.awt.Mnemonics.setLocalizedText(refreshButton, org.openide.util.NbBundle.getMessage(IngestProgressSnapshotPanel.class, "IngestProgressSnapshotPanel.refreshButton.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(refreshButton, org.openide.util.NbBundle.getMessage(IngestProgressSnapshotPanel.class, "IngestProgressSnapshotPanel.refreshButton.text")); // NOI18N
refreshButton.addActionListener(new java.awt.event.ActionListener() { refreshButton.addActionListener(new java.awt.event.ActionListener() {
@ -197,6 +184,8 @@ public class IngestProgressSnapshotPanel extends javax.swing.JPanel {
} }
}); });
org.openide.awt.Mnemonics.setLocalizedText(fileProcessedPerSecondLabel, org.openide.util.NbBundle.getMessage(IngestProgressSnapshotPanel.class, "IngestProgressSnapshotPanel.fileProcessedPerSecondLabel.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
@ -204,9 +193,10 @@ public class IngestProgressSnapshotPanel extends javax.swing.JPanel {
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(snapshotsScrollPane) .addComponent(snapshotsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 881, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(0, 733, Short.MAX_VALUE) .addComponent(fileProcessedPerSecondLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 173, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(refreshButton) .addComponent(refreshButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(closeButton))) .addComponent(closeButton)))
@ -223,7 +213,8 @@ public class IngestProgressSnapshotPanel extends javax.swing.JPanel {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(refreshButton) .addComponent(refreshButton)
.addComponent(closeButton)) .addComponent(closeButton)
.addComponent(fileProcessedPerSecondLabel))
.addContainerGap()) .addContainerGap())
); );
@ -236,14 +227,19 @@ public class IngestProgressSnapshotPanel extends javax.swing.JPanel {
}//GEN-LAST:event_closeButtonActionPerformed }//GEN-LAST:event_closeButtonActionPerformed
private void refreshButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_refreshButtonActionPerformed private void refreshButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_refreshButtonActionPerformed
if (IngestManager.getInstance().isIngestRunning() == true) { threadActivityTableModel.refresh();
tableModel.refresh(); IngestManager.ProcessedFilesSnapshot snapshot = IngestManager.getInstance().getProcessedFilesSnapshot();
} Date now = new Date();
long elapsedTime = now.getTime() - snapshot.getStartTime().getTime();
double filesPerSecond = (double) snapshot.getProcessedFilesCount() / (double) elapsedTime * 1000.0;
fileProcessedPerSecondLabel.setText(NbBundle.getMessage(this.getClass(),
"IngestProgressSnapshotPanel.fileProcessedPerSecondLabel.text", filesPerSecond));
}//GEN-LAST:event_refreshButtonActionPerformed }//GEN-LAST:event_refreshButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton closeButton; private javax.swing.JButton closeButton;
private javax.swing.JLabel fileProcessedPerSecondLabel;
private javax.swing.JButton refreshButton; private javax.swing.JButton refreshButton;
private javax.swing.JScrollPane snapshotsScrollPane; private javax.swing.JScrollPane snapshotsScrollPane;
private javax.swing.JTable snapshotsTable; private javax.swing.JTable threadActivitySnapshotsTable;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
} }

View File

@ -55,7 +55,6 @@ final class IngestScheduler {
// private volatile boolean cancellingAllTasks = false; TODO: Uncomment this with related code, if desired // private volatile boolean cancellingAllTasks = false; TODO: Uncomment this with related code, if desired
private final DataSourceIngestTaskQueue dataSourceTaskDispenser = new DataSourceIngestTaskQueue(); private final DataSourceIngestTaskQueue dataSourceTaskDispenser = new DataSourceIngestTaskQueue();
private final FileIngestTaskQueue fileTaskDispenser = new FileIngestTaskQueue(); private final FileIngestTaskQueue fileTaskDispenser = new FileIngestTaskQueue();
private final IngestTask.ProgressSnapshots taskProgressSnapShots = new IngestTask.ProgressSnapshots();
// The following five collections lie at the heart of the scheduler. // The following five collections lie at the heart of the scheduler.
// //
// The pending tasks queues are used to schedule tasks for an ingest job. If // The pending tasks queues are used to schedule tasks for an ingest job. If
@ -132,7 +131,7 @@ final class IngestScheduler {
} }
synchronized private void scheduleDataSourceIngestTask(IngestJob job) throws InterruptedException { synchronized private void scheduleDataSourceIngestTask(IngestJob job) throws InterruptedException {
DataSourceIngestTask task = new DataSourceIngestTask(job, taskProgressSnapShots); DataSourceIngestTask task = new DataSourceIngestTask(job);
tasksInProgress.add(task); tasksInProgress.add(task);
try { try {
// Should not block, queue is (theoretically) unbounded. // Should not block, queue is (theoretically) unbounded.
@ -147,7 +146,7 @@ final class IngestScheduler {
synchronized private void scheduleFileIngestTasks(IngestJob job) throws InterruptedException { synchronized private void scheduleFileIngestTasks(IngestJob job) throws InterruptedException {
List<AbstractFile> topLevelFiles = getTopLevelFiles(job.getDataSource()); List<AbstractFile> topLevelFiles = getTopLevelFiles(job.getDataSource());
for (AbstractFile firstLevelFile : topLevelFiles) { for (AbstractFile firstLevelFile : topLevelFiles) {
FileIngestTask task = new FileIngestTask(job, firstLevelFile, taskProgressSnapShots); FileIngestTask task = new FileIngestTask(job, firstLevelFile);
if (shouldEnqueueFileTask(task)) { if (shouldEnqueueFileTask(task)) {
tasksInProgress.add(task); tasksInProgress.add(task);
pendingRootDirectoryTasks.add(task); pendingRootDirectoryTasks.add(task);
@ -223,7 +222,7 @@ final class IngestScheduler {
for (Content child : directory.getChildren()) { for (Content child : directory.getChildren()) {
if (child instanceof AbstractFile) { if (child instanceof AbstractFile) {
AbstractFile file = (AbstractFile) child; AbstractFile file = (AbstractFile) child;
FileIngestTask childTask = new FileIngestTask(directoryTask.getIngestJob(), file, taskProgressSnapShots); FileIngestTask childTask = new FileIngestTask(directoryTask.getIngestJob(), file);
if (file.hasChildren()) { if (file.hasChildren()) {
// Found a subdirectory, put the task in the // Found a subdirectory, put the task in the
// pending directory tasks queue. // pending directory tasks queue.
@ -321,7 +320,7 @@ final class IngestScheduler {
void scheduleAdditionalFileIngestTask(IngestJob job, AbstractFile file) throws InterruptedException { void scheduleAdditionalFileIngestTask(IngestJob job, AbstractFile file) throws InterruptedException {
if (enabled) { if (enabled) {
FileIngestTask task = new FileIngestTask(job, file, taskProgressSnapShots); FileIngestTask task = new FileIngestTask(job, file);
if (shouldEnqueueFileTask(task)) { if (shouldEnqueueFileTask(task)) {
// Send the file task directly to file tasks queue, no need to // Send the file task directly to file tasks queue, no need to
// update the pending root directory or pending directory tasks // update the pending root directory or pending directory tasks
@ -365,10 +364,6 @@ final class IngestScheduler {
return !ingestJobsById.isEmpty(); return !ingestJobsById.isEmpty();
} }
List<IngestTask.ProgressSnapshot> getIngestTaskProgressSnapshots() {
return taskProgressSnapShots.getSnapshots();
}
synchronized void cancelIngestJob(IngestJob job) { synchronized void cancelIngestJob(IngestJob job) {
long jobId = job.getId(); long jobId = job.getId();
removeAllPendingTasksForJob(pendingRootDirectoryTasks, jobId); removeAllPendingTasksForJob(pendingRootDirectoryTasks, jobId);

View File

@ -18,91 +18,34 @@
*/ */
package org.sleuthkit.autopsy.ingest; package org.sleuthkit.autopsy.ingest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
class IngestTask { abstract class IngestTask {
private final static long NOT_SET = Long.MIN_VALUE;
private final IngestJob job; private final IngestJob job;
private final ProgressSnapshots snapshots;
private long threadId; private long threadId;
IngestTask(IngestJob job, ProgressSnapshots snapshots) { IngestTask(IngestJob job) {
this.job = job; this.job = job;
this.snapshots = snapshots; threadId = NOT_SET;
} }
IngestJob getIngestJob() { IngestJob getIngestJob() {
return job; return job;
} }
void updateProgressStatus(String ingestModuleDisplayName, AbstractFile file) { Content getDataSource() {
snapshots.update(new ProgressSnapshot(threadId, job.getDataSource(), ingestModuleDisplayName, file)); return getIngestJob().getDataSource();
}
long getThreadId() {
return threadId;
} }
void execute(long threadId) throws InterruptedException { void setThreadId(long threadId) {
this.threadId = threadId; this.threadId = threadId;
} }
public static final class ProgressSnapshot { abstract void execute(long threadId) throws InterruptedException;
private final long threadId;
private final Content dataSource;
private final String ingestModuleDisplayName;
private final AbstractFile file;
private final Date startTime;
// TODO: Restore when we go to Java 8
// private final LocalTime startTime;
private ProgressSnapshot(long threadId, Content dataSource, String ingestModuleDisplayName, AbstractFile file) {
this.threadId = threadId;
this.dataSource = dataSource;
this.ingestModuleDisplayName = ingestModuleDisplayName;
this.file = file;
startTime = new Date();
// TODO: Restore when we go to Java 8
// startTime = LocalTime.now();
}
long getThreadId() {
return threadId;
}
Content getDataSource() {
return dataSource;
}
String getModuleDisplayName() {
return ingestModuleDisplayName;
}
AbstractFile getFile() {
return file;
}
Date getStartTime() {
return startTime;
}
// TODO: Restore when we go to Java 8
// LocalTime getStartTime() {
// return startTime;
// }
}
static final class ProgressSnapshots {
private final ConcurrentHashMap<Long, IngestTask.ProgressSnapshot> snapshots = new ConcurrentHashMap<>(); // Maps ingest thread ids to progress snapshots.
void update(ProgressSnapshot snapshot) {
snapshots.put(snapshot.getThreadId(), snapshot);
}
List<ProgressSnapshot> getSnapshots() {
return new ArrayList(snapshots.values());
}
}
} }