diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 846af747ac..afcf23ed57 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -127,7 +127,6 @@ public class Case { private static final String LOG_FOLDER = "Log"; //NON-NLS private static final String REPORTS_FOLDER = "Reports"; //NON-NLS private static final String TEMP_FOLDER = "Temp"; //NON-NLS - private static final int MIN_SECS_BETWEEN_TSK_ERROR_REPORTS = 60; private static final String MODULE_FOLDER = "ModuleOutput"; //NON-NLS private static final long EXECUTOR_AWAIT_TIMEOUT_SECS = 5; private static final String CASE_ACTION_THREAD_NAME = "%s-case-action"; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java index 4c804cf703..8ab241fa3b 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java @@ -66,8 +66,8 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; * View correlation results from other cases */ @ServiceProvider(service = DataContentViewer.class, position = 8) -@Messages({"DataContentViewerOtherCases.title=Other Data Sources", - "DataContentViewerOtherCases.toolTip=Displays instances of the selected file/artifact from other data sources.",}) +@Messages({"DataContentViewerOtherCases.title=Other Occurrences", + "DataContentViewerOtherCases.toolTip=Displays instances of the selected file/artifact from other occurrences.",}) public class DataContentViewerOtherCases extends javax.swing.JPanel implements DataContentViewer { private final static Logger LOGGER = Logger.getLogger(DataContentViewerOtherCases.class.getName()); @@ -370,7 +370,7 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D * * @param node The node to view * - * @return A collection of Central Repository artifacts to display + * @return A collection of central repository artifacts to display */ private Collection getArtifactsFromCorrelatableAttributes(Node node) { Collection ret = new ArrayList<>(); @@ -450,7 +450,7 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D /** * Query the db for artifact instances from other cases correlated to the - * given Central Repository artifact. + * given central repository artifact. * * @param eamArtifact The artifact to correlate against * @@ -474,12 +474,12 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D } /** - * Get the Global File Instances matching the given eamArtifact and convert - * them to Central Repository Artifact Instancess. + * Get the global file instances matching the given eamArtifact and convert + * them to central repository artifact instancess. * * @param eamArtifact Artifact to use for ArtifactTypeEnum matching * - * @return List of Central Repository Artifact Instances, empty list if none + * @return List of central repository artifact instances, empty list if none * found */ public Collection getReferenceInstancesAsArtifactInstances(EamArtifact eamArtifact) { @@ -530,8 +530,8 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D * * @param node The node being viewed. */ - @Messages({"DataContentViewerOtherCases.table.isempty=There are no associated artifacts or files from other cases to display.", - "DataContentViewerOtherCases.table.noArtifacts=Correlation cannot be performed on the selected file; likely missing MD5 hash."}) + @Messages({"DataContentViewerOtherCases.table.isempty=There are no associated artifacts or files from other occurrences to display.", + "DataContentViewerOtherCases.table.noArtifacts=Correlation cannot be performed on the selected file."}) private void populateTable(Node node) { String dataSourceName = getDataSourceNameFromNode(node); String deviceId = getDeviceIdFromNode(node); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java index cdf4d3d360..c7aa222fd6 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java @@ -190,7 +190,8 @@ public class IngestEventsListener { } } - @NbBundle.Messages({"IngestEventsListener.prevcases.text=Previous Cases", + @NbBundle.Messages({"IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)", + "IngestEventsListener.prevCaseComment.text=Previous Case: ", "IngestEventsListener.ingestmodule.name=Correlation Engine"}) private void postCorrelatedBadArtifactToBlackboard(BlackboardArtifact bbArtifact, List caseDisplayNames) { @@ -200,9 +201,9 @@ public class IngestEventsListener { String MODULE_NAME = Bundle.IngestEventsListener_ingestmodule_name(); BlackboardArtifact tifArtifact = af.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT); BlackboardAttribute att = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, - Bundle.IngestEventsListener_prevcases_text()); + Bundle.IngestEventsListener_prevTaggedSet_text()); BlackboardAttribute att2 = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, - "Previous Case: " + caseDisplayNames.stream().distinct().collect(Collectors.joining(",", "", ""))); + Bundle.IngestEventsListener_prevCaseComment_text() + caseDisplayNames.stream().distinct().collect(Collectors.joining(",", "", ""))); tifArtifact.addAttribute(att); tifArtifact.addAttribute(att2); tifArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, bbArtifact.getArtifactID())); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java index 356d73df94..bfd23e38c2 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java @@ -54,7 +54,8 @@ import org.sleuthkit.datamodel.TskDataException; * Ingest module for inserting entries into the Central Repository database on * ingest of a data source */ -@Messages({"IngestModule.prevcases.text=Previous Cases"}) +@Messages({"IngestModule.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)", + "IngestModule.prevCaseComment.text=Previous Case: "}) class IngestModule implements FileIngestModule { private final static Logger LOGGER = Logger.getLogger(IngestModule.class.getName()); @@ -289,9 +290,9 @@ class IngestModule implements FileIngestModule { String MODULE_NAME = IngestModuleFactory.getModuleName(); BlackboardArtifact tifArtifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT); BlackboardAttribute att = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, - Bundle.IngestModule_prevcases_text()); + Bundle.IngestModule_prevTaggedSet_text()); BlackboardAttribute att2 = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, - "Previous Case: " + caseDisplayNames.stream().distinct().collect(Collectors.joining(",", "", ""))); + Bundle.IngestModule_prevCaseComment_text() + caseDisplayNames.stream().distinct().collect(Collectors.joining(",", "", ""))); tifArtifact.addAttribute(att); tifArtifact.addAttribute(att2); @@ -319,7 +320,7 @@ class IngestModule implements FileIngestModule { String MODULE_NAME = IngestModuleFactory.getModuleName(); BlackboardArtifact tifArtifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT); BlackboardAttribute att = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, - Bundle.IngestModule_prevcases_text()); + Bundle.IngestModule_prevCaseComment_text()); tifArtifact.addAttribute(att); try { diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageTagsDialog.form b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageTagsDialog.form index d894f64fe6..f1ad5a38e4 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageTagsDialog.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageTagsDialog.form @@ -100,8 +100,8 @@ - - + +
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageTagsDialog.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageTagsDialog.java index 43cc72beb4..01d984bda0 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageTagsDialog.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageTagsDialog.java @@ -72,12 +72,15 @@ final class ManageTagsDialog extends javax.swing.JDialog { display(); } + + @Messages({"ManageTagsDialog.init.failedConnection.msg=Cannot connect to central cepository.", + "ManageTagsDialog.init.failedGettingTags.msg=Unable to retrieve list of tags.", + "ManageTagsDialog.tagColumn.header.text=Tags", + "ManageTagsDialog.notableColumn.header.text=Notable"}) private void setupHelpTextArea() { helpTextArea.setText(Bundle.ManageTagDialog_tagInfo_text()); } - @Messages({"ManageTagsDialog.init.failedConnection.msg=Cannot connect to central repository.", - "ManageTagsDialog.init.failedGettingTags.msg=Unable to retrieve list of tags."}) private void customizeComponents() { lbWarnings.setText(""); EamDb dbManager; @@ -105,6 +108,7 @@ final class ManageTagsDialog extends javax.swing.JDialog { Collections.sort(tagNames); DefaultTableModel model = (DefaultTableModel) tblTagNames.getModel(); + model.setColumnIdentifiers(new String[] {Bundle.ManageTagsDialog_tagColumn_header_text(), Bundle.ManageTagsDialog_notableColumn_header_text()}); for (String tagName : tagNames) { boolean enabled = badTags.contains(tagName); model.addRow(new Object[]{tagName, enabled}); @@ -158,7 +162,7 @@ final class ManageTagsDialog extends javax.swing.JDialog { }, new String [] { - "Tag", "Implies Known Bad" + "", "" } ) { Class[] types = new Class [] { diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Installer.java b/Core/src/org/sleuthkit/autopsy/corecomponents/Installer.java index 948b54ad07..c908f3b35d 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Installer.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Installer.java @@ -19,8 +19,6 @@ package org.sleuthkit.autopsy.corecomponents; import java.awt.Insets; -import java.io.File; -import java.util.Collection; import java.util.Map; import java.util.TreeMap; import java.util.logging.Level; @@ -28,15 +26,9 @@ import javax.swing.BorderFactory; import javax.swing.UIManager; import javax.swing.UIManager.LookAndFeelInfo; import javax.swing.UnsupportedLookAndFeelException; -import org.netbeans.spi.sendopts.OptionProcessor; import org.netbeans.swing.tabcontrol.plaf.DefaultTabbedContainerUI; import org.openide.modules.ModuleInstall; -import org.openide.util.Lookup; import org.openide.windows.WindowManager; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.CaseActionException; -import org.sleuthkit.autopsy.casemodule.CaseMetadata; -import org.sleuthkit.autopsy.casemodule.OpenFromArguments; import org.sleuthkit.autopsy.casemodule.StartupWindowProvider; import org.sleuthkit.autopsy.coreutils.Logger; @@ -63,35 +55,11 @@ public class Installer extends ModuleInstall { @Override public void restored() { super.restored(); - setLookAndFeel(); UIManager.put("ViewTabDisplayerUI", "org.sleuthkit.autopsy.corecomponents.NoTabsTabDisplayerUI"); UIManager.put(DefaultTabbedContainerUI.KEY_VIEW_CONTENT_BORDER, BorderFactory.createEmptyBorder()); UIManager.put("TabbedPane.contentBorderInsets", new Insets(0, 0, 0, 0)); - - /* - * Open the case if a case metadata file was double-clicked. This only - * works if the user has associated files with ".aut" extensions with - * Autopsy. - */ WindowManager.getDefault().invokeWhenUIReady(() -> { - Collection processors = Lookup.getDefault().lookupAll(OptionProcessor.class); - for (OptionProcessor processor : processors) { - if (processor instanceof OpenFromArguments) { - OpenFromArguments argsProcessor = (OpenFromArguments) processor; - final String caseFile = argsProcessor.getDefaultArg(); - if (caseFile != null && !caseFile.isEmpty() && caseFile.endsWith(CaseMetadata.getFileExtension()) && new File(caseFile).exists()) { //NON-NLS - try { - Case.openAsCurrentCase(caseFile); - } catch (CaseActionException ex) { - logger.log(Level.SEVERE, String.format("Error opening case with metadata file path %s", caseFile), ex); //NON-NLS - - //JIRA-3035: notify user! - } - return; - } - } - } StartupWindowProvider.getInstance().open(); }); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/SpecialDirectoryNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/SpecialDirectoryNode.java index a25034481a..7bc23015ac 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/SpecialDirectoryNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/SpecialDirectoryNode.java @@ -28,8 +28,8 @@ import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.FileSearchAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.RunIngestModulesAction; -import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.SpecialDirectory; +import org.sleuthkit.datamodel.Content; /** * Parent class for special directory types (Local and Virtual) @@ -48,8 +48,7 @@ public abstract class SpecialDirectoryNode extends AbstractAbstractFileNode actions = new ArrayList<>(); @@ -58,7 +57,7 @@ public abstract class SpecialDirectoryNode extends AbstractAbstractFileNode 0 && pendingTable.getSelectedRow() >= 0) { this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); @@ -1759,8 +1759,8 @@ job.getErrorsOccurred(), // STATUS options[0]); } }//GEN-LAST:event_bnShowCaseLogActionPerformed - - @Messages({"AutoIngestControlPanel.jobPrioritization.errorMessage=An error occurred when prioritizing the job."}) + + @Messages({"AutoIngestControlPanel.jobPrioritization.errorMessage=An error occurred when prioritizing the job."}) private void bnPrioritizeJobActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnPrioritizeJobActionPerformed if (pendingTableModel.getRowCount() > 0 && pendingTable.getSelectedRow() >= 0) { this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.java index 6782140e7e..213ca60d23 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.experimental.autoingest; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.awt.Cursor; import java.awt.EventQueue; import java.nio.file.Path; @@ -44,6 +45,8 @@ import org.sleuthkit.autopsy.core.ServicesMonitor; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestMonitor.JobsSnapshot; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** * A dashboard for monitoring an automated ingest cluster. @@ -55,28 +58,30 @@ public final class AutoIngestDashboard extends JPanel implements Observer { private static final int GENERIC_COL_MAX_WIDTH = 2000; private static final int PENDING_TABLE_COL_PREFERRED_WIDTH = 280; private static final int RUNNING_TABLE_COL_PREFERRED_WIDTH = 175; - private static final int ACTIVITY_TIME_COL_MIN_WIDTH = 250; - private static final int ACTIVITY_TIME_COL_MAX_WIDTH = 450; + private static final int STAGE_TIME_COL_MIN_WIDTH = 250; + private static final int STAGE_TIME_COL_MAX_WIDTH = 450; private static final int TIME_COL_MIN_WIDTH = 30; private static final int TIME_COL_MAX_WIDTH = 250; private static final int TIME_COL_PREFERRED_WIDTH = 140; private static final int NAME_COL_MIN_WIDTH = 100; private static final int NAME_COL_MAX_WIDTH = 250; private static final int NAME_COL_PREFERRED_WIDTH = 140; - private static final int ACTIVITY_COL_MIN_WIDTH = 70; - private static final int ACTIVITY_COL_MAX_WIDTH = 2000; - private static final int ACTIVITY_COL_PREFERRED_WIDTH = 300; + private static final int STAGE_COL_MIN_WIDTH = 70; + private static final int STAGE_COL_MAX_WIDTH = 2000; + private static final int STAGE_COL_PREFERRED_WIDTH = 300; private static final int STATUS_COL_MIN_WIDTH = 55; private static final int STATUS_COL_MAX_WIDTH = 250; private static final int STATUS_COL_PREFERRED_WIDTH = 55; private static final int COMPLETED_TIME_COL_MIN_WIDTH = 30; private static final int COMPLETED_TIME_COL_MAX_WIDTH = 2000; private static final int COMPLETED_TIME_COL_PREFERRED_WIDTH = 280; + private static final String UPDATE_TASKS_THREAD_NAME = "AID-update-tasks-%d"; private static final Logger logger = Logger.getLogger(AutoIngestDashboard.class.getName()); private final DefaultTableModel pendingTableModel; private final DefaultTableModel runningTableModel; private final DefaultTableModel completedTableModel; private AutoIngestMonitor autoIngestMonitor; + private ExecutorService updateExecutor; /** * Creates a dashboard for monitoring an automated ingest cluster. @@ -213,6 +218,7 @@ public final class AutoIngestDashboard extends JPanel implements Observer { pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.CASE_DIRECTORY_PATH.getColumnHeader())); pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.STATUS.getColumnHeader())); pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.MANIFEST_FILE_PATH.getColumnHeader())); + pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.JOB.getColumnHeader())); /* * Set up a column to display the cases associated with the jobs. @@ -275,6 +281,7 @@ public final class AutoIngestDashboard extends JPanel implements Observer { runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.STATUS.getColumnHeader())); runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.CASE_DIRECTORY_PATH.getColumnHeader())); runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.MANIFEST_FILE_PATH.getColumnHeader())); + runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.JOB.getColumnHeader())); /* * Set up a column to display the cases associated with the jobs. @@ -311,10 +318,10 @@ public final class AutoIngestDashboard extends JPanel implements Observer { * jobs. */ column = runningTable.getColumn(JobsTableModelColumns.STAGE.getColumnHeader()); - column.setMinWidth(ACTIVITY_COL_MIN_WIDTH); - column.setMaxWidth(ACTIVITY_COL_MAX_WIDTH); - column.setPreferredWidth(ACTIVITY_COL_PREFERRED_WIDTH); - column.setWidth(ACTIVITY_COL_PREFERRED_WIDTH); + column.setMinWidth(STAGE_COL_MIN_WIDTH); + column.setMaxWidth(STAGE_COL_MAX_WIDTH); + column.setPreferredWidth(STAGE_COL_PREFERRED_WIDTH); + column.setWidth(STAGE_COL_PREFERRED_WIDTH); /* * Set up a column to display the ingest activity times associated with @@ -323,9 +330,9 @@ public final class AutoIngestDashboard extends JPanel implements Observer { column = runningTable.getColumn(JobsTableModelColumns.STAGE_TIME.getColumnHeader()); column.setCellRenderer(new DurationCellRenderer()); column.setMinWidth(GENERIC_COL_MIN_WIDTH); - column.setMaxWidth(ACTIVITY_TIME_COL_MAX_WIDTH); - column.setPreferredWidth(ACTIVITY_TIME_COL_MIN_WIDTH); - column.setWidth(ACTIVITY_TIME_COL_MIN_WIDTH); + column.setMaxWidth(STAGE_TIME_COL_MAX_WIDTH); + column.setPreferredWidth(STAGE_TIME_COL_MIN_WIDTH); + column.setWidth(STAGE_TIME_COL_MIN_WIDTH); /* * Prevent sorting when a column header is clicked. @@ -348,6 +355,7 @@ public final class AutoIngestDashboard extends JPanel implements Observer { completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.HOST_NAME.getColumnHeader())); completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.CASE_DIRECTORY_PATH.getColumnHeader())); completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.MANIFEST_FILE_PATH.getColumnHeader())); + completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.JOB.getColumnHeader())); /* * Set up a column to display the cases associated with the jobs. @@ -419,12 +427,20 @@ public final class AutoIngestDashboard extends JPanel implements Observer { autoIngestMonitor = new AutoIngestMonitor(); autoIngestMonitor.addObserver(this); autoIngestMonitor.startUp(); + updateExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(UPDATE_TASKS_THREAD_NAME).build()); + updateExecutor.submit(new UpdateJobsSnapshotTask()); } @Override - public void update(Observable observable, Object argument) { - JobsSnapshot jobsSnapshot = (JobsSnapshot) argument; - EventQueue.invokeLater(new RefreshComponentsTask(jobsSnapshot)); + public void update(Observable observable, Object arg) { + /* + * By creating a task to get the latest jobs snapshot, the possibility + * of queuing a refresh task for the EDT with snaphot rendered stale by + * the handling of a user prioritization action, etc. on the EDT is + * avoided. This is why the snapshot pushed ny the auto ingest jobs + * monitor is ignored. + */ + updateExecutor.submit(new UpdateJobsSnapshotTask()); } /** @@ -438,7 +454,6 @@ public final class AutoIngestDashboard extends JPanel implements Observer { List runningJobs = jobsSnapshot.getRunningJobs(); List completedJobs = jobsSnapshot.getCompletedJobs(); pendingJobs.sort(new AutoIngestJob.PriorityComparator()); - runningJobs.sort(new AutoIngestJob.CaseNameAndProcessingHostComparator()); completedJobs.sort(new AutoIngestJob.ReverseCompletedDateComparator()); refreshTable(pendingJobs, pendingTable, pendingTableModel); refreshTable(runningJobs, runningTable, runningTableModel); @@ -459,24 +474,19 @@ public final class AutoIngestDashboard extends JPanel implements Observer { Path currentRow = getSelectedEntry(table, tableModel); tableModel.setRowCount(0); for (AutoIngestJob job : jobs) { - if (job.getVersion() < 1) { - // Ignore version '0' nodes since they don't carry enough - // data to populate the table. - continue; - } - AutoIngestJob.StageDetails status = job.getStageDetails(); + AutoIngestJob.StageDetails status = job.getProcessingStageDetails(); tableModel.addRow(new Object[]{ job.getManifest().getCaseName(), // CASE job.getManifest().getDataSourcePath().getFileName(), job.getProcessingHostName(), // HOST_NAME job.getManifest().getDateFileCreated(), // CREATED_TIME job.getProcessingStageStartDate(), // STARTED_TIME job.getCompletedDate(), // COMPLETED_TIME - status.getDescription(), // ACTIVITY + status.getDescription(), // STAGE job.getErrorsOccurred(), // STATUS - ((Date.from(Instant.now()).getTime()) - (status.getStartDate().getTime())), // ACTIVITY_TIME + ((Date.from(Instant.now()).getTime()) - (status.getStartDate().getTime())), // STAGE_TIME job.getCaseDirectoryPath(), // CASE_DIRECTORY_PATH - job.getManifest().getFilePath() // MANIFEST_FILE_PATH - //DLG: Put job object in the table + job.getManifest().getFilePath(), // MANIFEST_FILE_PATH + job }); } setSelectedEntry(table, tableModel, currentRow); @@ -542,7 +552,6 @@ public final class AutoIngestDashboard extends JPanel implements Observer { */ private enum JobsTableModelColumns { - // DLG: Go through the bundle.properties file and delete any unused key-value pairs. CASE(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.Case")), DATA_SOURCE(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.ImageFolder")), HOST_NAME(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.HostName")), @@ -553,8 +562,8 @@ public final class AutoIngestDashboard extends JPanel implements Observer { STAGE_TIME(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.StageTime")), STATUS(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.Status")), CASE_DIRECTORY_PATH(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.CaseFolder")), - MANIFEST_FILE_PATH(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.ManifestFilePath")); //DLG:, - //DLG: JOB(""); + MANIFEST_FILE_PATH(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.ManifestFilePath")), + JOB(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.Job")); private final String header; @@ -566,15 +575,6 @@ public final class AutoIngestDashboard extends JPanel implements Observer { return header; } - /* - * DLG: We need to add the AutoIngestJob object for the row to the - * table. As a model you can look in AutoIngestControlPanel to see how a - * boolean is stored in a hidden IS_LOCAL_JOB column and do something - * similar for the job. Once youy hjave done that, you can change the - * button event handler for the Prioritize button to make it pass the - * AutoIngestJob to the AutoIngestMonitor instead of the manifest file - * path. - */ private static final String[] headers = { CASE.getColumnHeader(), DATA_SOURCE.getColumnHeader(), @@ -586,11 +586,28 @@ public final class AutoIngestDashboard extends JPanel implements Observer { STATUS.getColumnHeader(), STAGE_TIME.getColumnHeader(), CASE_DIRECTORY_PATH.getColumnHeader(), - MANIFEST_FILE_PATH.getColumnHeader() //DLG: , - //DLG: JOB.getColumnHeader() + MANIFEST_FILE_PATH.getColumnHeader(), + JOB.getColumnHeader() }; }; + /** + * A task that gets the latest auto ingest jobs snapshot from the autop + * ingest monitor and queues a components refresh task for execution in the + * EDT. + */ + private class UpdateJobsSnapshotTask implements Runnable { + + /** + * @inheritDoc + */ + @Override + public void run() { + JobsSnapshot jobsSnapshot = AutoIngestDashboard.this.autoIngestMonitor.getJobsSnapshot(); + EventQueue.invokeLater(new RefreshComponentsTask(jobsSnapshot)); + } + } + /** * A task that refreshes the UI components on this panel to reflect a * snapshot of the pending, running and completed auto ingest jobs lists of @@ -835,13 +852,13 @@ public final class AutoIngestDashboard extends JPanel implements Observer { private void prioritizeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prioritizeButtonActionPerformed if (pendingTableModel.getRowCount() > 0 && pendingTable.getSelectedRow() >= 0) { setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - Path manifestFilePath = (Path) (pendingTableModel.getValueAt(pendingTable.getSelectedRow(), JobsTableModelColumns.MANIFEST_FILE_PATH.ordinal())); + AutoIngestJob job = (AutoIngestJob) (pendingTableModel.getValueAt(pendingTable.getSelectedRow(), JobsTableModelColumns.JOB.ordinal())); JobsSnapshot jobsSnapshot; try { - jobsSnapshot = autoIngestMonitor.prioritizeJob(manifestFilePath); + jobsSnapshot = autoIngestMonitor.prioritizeJob(job); refreshTables(jobsSnapshot); } catch (AutoIngestMonitor.AutoIngestMonitorException ex) { - String errorMessage = String.format(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.PrioritizeError"), manifestFilePath); + String errorMessage = String.format(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.PrioritizeError"), job.getManifest().getFilePath()); logger.log(Level.SEVERE, errorMessage, ex); MessageNotifyUtil.Message.error(errorMessage); } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboardTopComponent.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboardTopComponent.java index 24369b9da2..19a99031f8 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboardTopComponent.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboardTopComponent.java @@ -50,10 +50,6 @@ public final class AutoIngestDashboardTopComponent extends TopComponent { @Messages({ "AutoIngestDashboardTopComponent.exceptionMessage.failedToCreateDashboard=Failed to create Auto Ingest Dashboard.",}) public static void openTopComponent() { - /* - * DLG: Please make the top component initial size big enough to show - * the whole dashboard. - */ final AutoIngestDashboardTopComponent tc = (AutoIngestDashboardTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID); if (tc != null) { topComponentInitialized = true; @@ -67,7 +63,7 @@ public final class AutoIngestDashboardTopComponent extends TopComponent { try { dashboard = AutoIngestDashboard.createDashboard(); tc.add(dashboard); - dashboard.setSize(992, 744); + dashboard.setSize(dashboard.getPreferredSize()); if (tc.isOpened() == false) { tc.open(); } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJob.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJob.java index d4986c82c7..74a1cb37e6 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJob.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJob.java @@ -34,8 +34,8 @@ import org.sleuthkit.autopsy.coreutils.NetworkUtils; import org.sleuthkit.autopsy.ingest.IngestJob; /** - * An automated ingest job for a manifest. The manifest specifies a co-located - * data source and a case to which the data source is to be added. + * An automated ingest job, which is an ingest job performed by the automated + * ingest service. */ @ThreadSafe public final class AutoIngestJob implements Comparable, Serializable { @@ -44,9 +44,13 @@ public final class AutoIngestJob implements Comparable, Serializa private static final int CURRENT_VERSION = 1; private static final int DEFAULT_PRIORITY = 0; private static final String LOCAL_HOST_NAME = NetworkUtils.getLocalHostName(); - private final int version; + + /* + * Version 0 fields. + */ private final Manifest manifest; - private final String nodeName; + @GuardedBy("this") + private String nodeName; @GuardedBy("this") private String caseDirectoryPath; @GuardedBy("this") @@ -56,8 +60,6 @@ public final class AutoIngestJob implements Comparable, Serializa @GuardedBy("this") private Date stageStartDate; @GuardedBy("this") - private StageDetails stageDetails; - @GuardedBy("this") transient private DataSourceProcessor dataSourceProcessor; @GuardedBy("this") transient private IngestJob ingestJob; @@ -69,96 +71,116 @@ public final class AutoIngestJob implements Comparable, Serializa private Date completedDate; @GuardedBy("this") private boolean errorsOccurred; + + /* + * Version 1 fields. + */ + private final int version; // For possible future use. @GuardedBy("this") private ProcessingStatus processingStatus; @GuardedBy("this") private int numberOfCrashes; + @GuardedBy("this") + private StageDetails stageDetails; /** - * Constructs a new automated ingest job for a manifest. All job state not - * specified in the manifest is set to the default state for a new job. + * Constructs a new automated ingest job. All job state not specified in the + * job manifest is set to the default state for a new job. * - * @param manifest The manifest. + * @param manifest The manifest for an automated ingest job. */ AutoIngestJob(Manifest manifest) { - this.version = CURRENT_VERSION; + /* + * Version 0 fields. + */ this.manifest = manifest; - this.nodeName = AutoIngestJob.LOCAL_HOST_NAME; + this.nodeName = ""; this.caseDirectoryPath = ""; this.priority = DEFAULT_PRIORITY; this.stage = Stage.PENDING; this.stageStartDate = manifest.getDateFileCreated(); - this.stageDetails = this.getStageDetails(); this.dataSourceProcessor = null; this.ingestJob = null; this.cancelled = false; this.completed = false; this.completedDate = new Date(0); this.errorsOccurred = false; + + /* + * Version 1 fields. + */ + this.version = CURRENT_VERSION; this.processingStatus = ProcessingStatus.PENDING; this.numberOfCrashes = 0; + this.stageDetails = this.getProcessingStageDetails(); } /** - * Constructs an automated ingest job for a manifest. The manifest specifies - * a co-located data source and a case to which the data source is to be - * added. + * Constructs an automated ingest job using the coordination service node + * data for the job. * - * Note: Manifest objects will be phased out and no longer be part of the - * AutoIngestJob class. - * - * @param nodeData The node data. + * @param nodeData The coordination service node data for an automated + * ingest job. */ AutoIngestJob(AutoIngestJobNodeData nodeData) { - this.version = nodeData.getVersion(); + /* + * Version 0 fields. + */ this.manifest = new Manifest(nodeData.getManifestFilePath(), nodeData.getManifestFileDate(), nodeData.getCaseName(), nodeData.getDeviceId(), nodeData.getDataSourcePath(), Collections.emptyMap()); this.nodeName = nodeData.getProcessingHostName(); this.caseDirectoryPath = nodeData.getCaseDirectoryPath().toString(); this.priority = nodeData.getPriority(); this.stage = nodeData.getProcessingStage(); this.stageStartDate = nodeData.getProcessingStageStartDate(); - this.stageDetails = this.getStageDetails(); - this.dataSourceProcessor = null; - this.ingestJob = null; - this.cancelled = false; - this.completed = false; + this.dataSourceProcessor = null; // Transient data not in node data. + this.ingestJob = null; // Transient data not in node data. + this.cancelled = false; // Transient data not in node data. + this.completed = false; // Transient data not in node data. this.completedDate = nodeData.getCompletedDate(); this.errorsOccurred = nodeData.getErrorsOccurred(); + + /* + * Version 1 fields. + */ + this.version = CURRENT_VERSION; this.processingStatus = nodeData.getProcessingStatus(); this.numberOfCrashes = nodeData.getNumberOfCrashes(); + this.stageDetails = this.getProcessingStageDetails(); } /** - * Gets the auto ingest job manifest. + * Gets the job manifest. * - * @return The manifest. + * @return The job manifest. */ Manifest getManifest() { return this.manifest; } /** - * Sets the path to the case directory of the case associated with this job. + * Sets the path to the case directory for the job. * - * @param caseDirectoryPath The path to the case directory. + * @param caseDirectoryPath The path to the case directory. Can be the empty + * path if the case directory has not been created + * yet. */ synchronized void setCaseDirectoryPath(Path caseDirectoryPath) { - this.caseDirectoryPath = caseDirectoryPath.toString(); + if (null != caseDirectoryPath) { + this.caseDirectoryPath = caseDirectoryPath.toString(); + } else { + this.caseDirectoryPath = ""; + } } /** - * Gets the path to the case directory of the case associated with this job, - * may be null. + * Gets the path to the case directory for job, may be the empty path if the + * case directory has not been created yet. * - * @return The case directory path or null if the case directory has not - * been created yet. + * @return The case directory path. Will be the empty path if the case + * directory has not been created yet. */ synchronized Path getCaseDirectoryPath() { - if (!caseDirectoryPath.isEmpty()) { - return Paths.get(caseDirectoryPath); - } else { - return null; - } + return Paths.get(caseDirectoryPath); } /** @@ -181,7 +203,13 @@ public final class AutoIngestJob implements Comparable, Serializa return this.priority; } - synchronized void setStage(Stage newStage) { + /** + * Sets the processing stage of the job. The start date/time for the stage + * is set when the stage is set. + * + * @param newStage The processing stage. + */ + synchronized void setProcessingStage(Stage newStage) { if (Stage.CANCELLING == this.stage && Stage.COMPLETED != newStage) { return; } @@ -189,15 +217,35 @@ public final class AutoIngestJob implements Comparable, Serializa this.stageStartDate = Date.from(Instant.now()); } + /** + * Gets the processing stage of the job. + * + * @return The processing stage. + */ synchronized Stage getProcessingStage() { return this.stage; } + /** + * Gets the date/time the current processing stage of the job started. + * + * @return The current processing stage start date/time. + */ synchronized Date getProcessingStageStartDate() { return new Date(this.stageStartDate.getTime()); } - synchronized StageDetails getStageDetails() { + /** + * Gets any available details associated with the current processing stage + * of the job, e.g., the currently running data source level ingest module, + * an ingest module in the process of being cancelled, etc. If no additional + * details are available, the stage details will be the same as the + * processing stage. + * + * @return A stage details object consisting of a descrition and associated + * date/time. + */ + synchronized StageDetails getProcessingStageDetails() { String description; Date startDate; if (Stage.CANCELLING != this.stage && null != this.ingestJob) { @@ -235,24 +283,43 @@ public final class AutoIngestJob implements Comparable, Serializa return this.stageDetails; } - synchronized void setStageDetails(StageDetails stageDetails) { - this.stageDetails = stageDetails; - } - + /** + * Sets the data source processor for the job. Used for job cancellation. + * + * @param dataSourceProcessor A data source processor for the job. + */ synchronized void setDataSourceProcessor(DataSourceProcessor dataSourceProcessor) { this.dataSourceProcessor = dataSourceProcessor; } + /** + * Sets the ingest job for the auto ingest job. Used for obtaining + * processing stage details, cancelling the currently running data source + * ingest module, and cancelling the job. + * + * @param ingestJob The ingest job for the auto ingest job. + */ synchronized void setIngestJob(IngestJob ingestJob) { this.ingestJob = ingestJob; } + /** + * Gets the ingest job for the auto ingest job. + * + * @return The ingest job, may be null. + * + * TODO (JIRA-3059): Provide an AutoIngestJob ingest module cancellation API + * instead. + */ synchronized IngestJob getIngestJob() { return this.ingestJob; } + /** + * Cancels the job. + */ synchronized void cancel() { - setStage(Stage.CANCELLING); + setProcessingStage(Stage.CANCELLING); cancelled = true; errorsOccurred = true; if (null != dataSourceProcessor) { @@ -263,15 +330,33 @@ public final class AutoIngestJob implements Comparable, Serializa } } + /** + * Indicates whether or not the job has been cancelled. This is transient + * state used by the auto ingest manager that is not saved as coordination + * service node data for the job. + * + * @return True or false. + */ synchronized boolean isCanceled() { return cancelled; } + /** + * Marks the job as completed. This is transient state used by the auto + * ingest manager that is not saved as coordination service node data for + * the job. + */ synchronized void setCompleted() { - setStage(Stage.COMPLETED); + setProcessingStage(Stage.COMPLETED); completed = true; } + /** + * Indicates whether or not the job has been completed. This is transient + * state that is not saved as coordination service node data for the job. + * + * @return True or false. + */ synchronized boolean isCompleted() { return completed; } @@ -314,55 +399,108 @@ public final class AutoIngestJob implements Comparable, Serializa return this.errorsOccurred; } + /** + * Gets the processing host name for this job. + * + * @return The processing host name. + */ synchronized String getProcessingHostName() { return nodeName; } - int getVersion() { - return this.version; + /** + * Sets the processing host name for this job. + * + * @param processingHostName The processing host name. + */ + synchronized void setProcessingHostName(String processingHostName) { + this.nodeName = processingHostName; } + /** + * Gets the processing status of the job. + * + * @return The processing status. + */ synchronized ProcessingStatus getProcessingStatus() { return this.processingStatus; } + /** + * Sets the processing status of the job. + * + * @param processingStatus The processing status. + */ synchronized void setProcessingStatus(ProcessingStatus processingStatus) { this.processingStatus = processingStatus; } + /** + * Gets the number of time this job has "crashed" during processing. + * + * @return The number of crashes. + */ synchronized int getNumberOfCrashes() { return this.numberOfCrashes; } + /** + * Sets the number of time this job has "crashed" during processing. + * + * @param numberOfCrashes The number of crashes. + */ synchronized void setNumberOfCrashes(int numberOfCrashes) { this.numberOfCrashes = numberOfCrashes; } + /** + * Indicates whether some other job is "equal to" this job. Two jobs are + * equal if they have the same manifest file path. + * + * @param otherJob The job to which this job is to be compared. + * + * @return True or false. + */ @Override - public boolean equals(Object obj) { - if (!(obj instanceof AutoIngestJob)) { + public boolean equals(Object otherJob) { + if (!(otherJob instanceof AutoIngestJob)) { return false; } - if (obj == this) { + if (otherJob == this) { return true; } - return this.getManifest().getFilePath().equals(((AutoIngestJob) obj).getManifest().getFilePath()); - } - - @Override - public int hashCode() { - int hash = 71 * (Objects.hashCode(this.caseDirectoryPath)); - return hash; - } - - @Override - public int compareTo(AutoIngestJob o) { - return -this.getManifest().getDateFileCreated().compareTo(o.getManifest().getDateFileCreated()); + return this.getManifest().getFilePath().equals(((AutoIngestJob) otherJob).getManifest().getFilePath()); } /** - * Custom comparator that allows us to sort List on reverse - * chronological date modified (descending) + * Returns a hash code value for the job. The hash code is derived from the + * manifest file path. + * + * @return The hash code. + */ + @Override + public int hashCode() { + int hash = 71 * (Objects.hashCode(this.getManifest().getFilePath())); + return hash; + } + + /** + * Compares one job to another in a way that orders jobs by manifest + * creation date. + * + * @param otherJob The job to which this job is to be compared. + * + * @return A negative integer, zero, or a positive integer as this job is + * less than, equal to, or greater than the specified job. + */ + @Override + public int compareTo(AutoIngestJob otherJob) { + return -this.getManifest().getDateFileCreated().compareTo(otherJob.getManifest().getDateFileCreated()); + } + + /** + * Comparator that supports doing a descending sort of jobs based on job + * completion date. */ static class ReverseCompletedDateComparator implements Comparator { @@ -374,7 +512,8 @@ public final class AutoIngestJob implements Comparable, Serializa } /** - * Comparator that sorts auto ingest jobs by priority in descending order. + * Comparator that supports doing a descending sort of jobs based on job + * priority. */ public static class PriorityComparator implements Comparator { @@ -386,27 +525,26 @@ public final class AutoIngestJob implements Comparable, Serializa } /** - * Custom comparator that allows us to sort List on case name - * alphabetically except for jobs for the current host, which are placed at - * the top of the list. + * Comparator that supports doing an alphabetical sort of jobs based on a + * combination of case name and processing host. */ static class CaseNameAndProcessingHostComparator implements Comparator { @Override - public int compare(AutoIngestJob o1, AutoIngestJob o2) { - if (o1.getProcessingHostName().equalsIgnoreCase(LOCAL_HOST_NAME)) { - return -1; // o1 is for current case, float to top - } else if (o2.getProcessingHostName().equalsIgnoreCase(LOCAL_HOST_NAME)) { - return 1; // o2 is for current case, float to top + public int compare(AutoIngestJob aJob, AutoIngestJob anotherJob) { + if (aJob.getProcessingHostName().equalsIgnoreCase(LOCAL_HOST_NAME)) { + return -1; // aJob is for this, float to top + } else if (anotherJob.getProcessingHostName().equalsIgnoreCase(LOCAL_HOST_NAME)) { + return 1; // anotherJob is for this, float to top } else { - return o1.getManifest().getCaseName().compareToIgnoreCase(o2.getManifest().getCaseName()); + return aJob.getManifest().getCaseName().compareToIgnoreCase(anotherJob.getManifest().getCaseName()); } } } /** - * Processing status for the auto ingest job for the manifest. + * Processing statuses for an auto ingest job. */ enum ProcessingStatus { PENDING, @@ -415,6 +553,9 @@ public final class AutoIngestJob implements Comparable, Serializa DELETED } + /** + * Processing stages for an auto ingest job. + */ enum Stage { PENDING("Pending"), @@ -443,6 +584,9 @@ public final class AutoIngestJob implements Comparable, Serializa } + /** + * Processing stage details for an auto ingest job. + */ @Immutable static final class StageDetails implements Serializable { diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobNodeData.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobNodeData.java index df2e321af2..3544e9ef49 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobNodeData.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobNodeData.java @@ -32,8 +32,22 @@ import javax.lang.model.type.TypeKind; final class AutoIngestJobNodeData { private static final int CURRENT_VERSION = 1; - private static final int MAX_POSSIBLE_NODE_DATA_SIZE = 131629; private static final int DEFAULT_PRIORITY = 0; + + /* + * This number is the sum of each piece of data, based on it's type. For the + * types boolean, int, and long, values 1, 4, and 8 will be added + * respectively. For String objects, the length of the string, plus either a + * byte or short respesenting the length of the string, will be added. + * + * This field is used to set the size of the buffer during the byte array + * creation in the 'toArray()' method. Since the final size of the array + * isn't immediately known at the time of creation, this number is used to + * create an array as large as could possibly be needed to store all the + * data. This avoids the need to continuously enlarge the buffer. Once the + * buffer has all the necessary data, it will be resized as appropriate. + */ + private static final int MAX_POSSIBLE_NODE_DATA_SIZE = 131629; /* * Version 0 fields. @@ -48,16 +62,16 @@ final class AutoIngestJobNodeData { * Version 1 fields. */ private int version; - private String manifestFilePath; + private String manifestFilePath; // 'short' length used in byte array private long manifestFileDate; - private String caseName; - private String deviceId; - private String dataSourcePath; - private String caseDirectoryPath; - private String processingHostName; + private String caseName; // 'byte' length used in byte array + private String deviceId; // 'byte' length used in byte array + private String dataSourcePath; // 'short' length used in byte array + private String caseDirectoryPath; // 'short' length used in byte array + private String processingHostName; // 'short' length used in byte array private byte processingStage; private long processingStageStartDate; - private String processingStageDetailsDescription; + private String processingStageDetailsDescription; // 'byte' length used in byte array private long processingStageDetailsStartDate; /** @@ -84,7 +98,7 @@ final class AutoIngestJobNodeData { setProcessingHostName(job.getProcessingHostName()); setProcessingStage(job.getProcessingStage()); setProcessingStageStartDate(job.getProcessingStageStartDate()); - setProcessingStageDetails(job.getStageDetails()); + setProcessingStageDetails(job.getProcessingStageDetails()); } /** @@ -107,7 +121,7 @@ final class AutoIngestJobNodeData { this.numberOfCrashes = 0; this.completedDate = 0L; this.errorsOccurred = false; - this.version = CURRENT_VERSION; + this.version = 0; this.manifestFilePath = ""; this.manifestFileDate = 0L; this.caseName = ""; @@ -151,7 +165,7 @@ final class AutoIngestJobNodeData { this.processingStage = buffer.get(); this.processingStageStartDate = buffer.getLong(); this.processingStageDetailsDescription = getStringFromBuffer(buffer, TypeKind.BYTE); - this.processingStageDetailsStartDate = buffer.getLong();; + this.processingStageDetailsStartDate = buffer.getLong(); this.processingHostName = getStringFromBuffer(buffer, TypeKind.SHORT); } @@ -318,14 +332,14 @@ final class AutoIngestJobNodeData { /** * Gets the path to the case directory of the case associated with the job. * - * @return The case directory path or null if the case directory has not - * been created yet. + * @return The case directory path or an empty string path if the case + * directory has not been created yet. */ synchronized Path getCaseDirectoryPath() { if (!caseDirectoryPath.isEmpty()) { return Paths.get(caseDirectoryPath); } else { - return null; + return Paths.get(""); } } @@ -517,7 +531,16 @@ final class AutoIngestJobNodeData { return array; } - // DGL: Document what is going on here and how the max buffer sie constant is calculated. + /** + * This method retrieves a string from a given buffer. Depending on the type + * specified, either a 'byte' or a 'short' will first be read out of the + * buffer which gives the length of the string so it can be properly parsed. + * + * @param buffer The buffer from which the string will be read. + * @param lengthType The size of the length data. + * + * @return The string read from the buffer. + */ private String getStringFromBuffer(ByteBuffer buffer, TypeKind lengthType) { int length = 0; String output = ""; @@ -540,7 +563,16 @@ final class AutoIngestJobNodeData { return output; } - // DGL: Document what is going on here and how the max buffer sie constant is calculated. + /** + * This method puts a given string into a given buffer. Depending on the + * type specified, either a 'byte' or a 'short' will be inserted prior to + * the string which gives the length of the string so it can be properly + * parsed. + * + * @param stringValue The string to write to the buffer. + * @param buffer The buffer to which the string will be written. + * @param lengthType The size of the length data. + */ private void putStringIntoBuffer(String stringValue, ByteBuffer buffer, TypeKind lengthType) { switch (lengthType) { case BYTE: diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java index c0944e9aaa..2cf88cbaa9 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java @@ -647,10 +647,10 @@ public final class AutoIngestManager extends Observable implements PropertyChang } /* - * Add the job to the pending jobs queue and update the coordinatino + * Add the job to the pending jobs queue and update the coordination * service node data for the job. */ - if (null != completedJob && null != completedJob.getCaseDirectoryPath()) { + if (null != completedJob && !completedJob.getCaseDirectoryPath().toString().isEmpty()) { try { completedJob.setErrorsOccurred(false); completedJob.setCompletedDate(new Date(0)); @@ -849,7 +849,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang if (null != ingestJob) { IngestJob.DataSourceIngestModuleHandle moduleHandle = ingestJob.getSnapshot().runningDataSourceIngestModule(); if (null != moduleHandle) { - currentJob.setStage(AutoIngestJob.Stage.CANCELLING_MODULE); + currentJob.setProcessingStage(AutoIngestJob.Stage.CANCELLING_MODULE); moduleHandle.cancel(); SYS_LOGGER.log(Level.INFO, "Cancelling {0} module for manifest {1}", new Object[]{moduleHandle.displayName(), currentJob.getManifest().getFilePath()}); } @@ -1117,7 +1117,9 @@ public final class AutoIngestManager extends Observable implements PropertyChang AutoIngestJob job = new AutoIngestJob(manifest); job.setPriority(nodeData.getPriority()); Path caseDirectory = PathUtils.findCaseDirectory(rootOutputDirectory, manifest.getCaseName()); - job.setCaseDirectoryPath(caseDirectory); + if (null != caseDirectory) { + job.setCaseDirectoryPath(caseDirectory); + } newPendingJobsList.add(job); /* @@ -1295,7 +1297,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang AutoIngestJob job = new AutoIngestJob(manifest); job.setCaseDirectoryPath(caseDirectoryPath); job.setProcessingStatus(AutoIngestJob.ProcessingStatus.COMPLETED); - job.setStage(AutoIngestJob.Stage.COMPLETED); + job.setProcessingStage(AutoIngestJob.Stage.COMPLETED); job.setCompletedDate(nodeData.getCompletedDate()); job.setErrorsOccurred(true); newCompletedJobsList.add(new AutoIngestJob(nodeData)); @@ -1864,10 +1866,11 @@ public final class AutoIngestManager extends Observable implements PropertyChang */ private void processJob() throws CoordinationServiceException, SharedConfigurationException, ServicesMonitorException, DatabaseServerDownException, KeywordSearchServerDownException, CaseManagementException, AnalysisStartupException, FileExportException, AutoIngestAlertFileException, AutoIngestJobLoggerException, InterruptedException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException, AutoIngestJobNodeData.InvalidDataException { Path manifestPath = currentJob.getManifest().getFilePath(); - currentJob.setProcessingStatus(AutoIngestJob.ProcessingStatus.PROCESSING); - updateCoordinationServiceNode(currentJob); SYS_LOGGER.log(Level.INFO, "Started processing of {0}", manifestPath); - currentJob.setStage(AutoIngestJob.Stage.STARTING); + currentJob.setProcessingStatus(AutoIngestJob.ProcessingStatus.PROCESSING); + currentJob.setProcessingStage(AutoIngestJob.Stage.STARTING); + currentJob.setProcessingHostName(AutoIngestManager.LOCAL_HOST_NAME); + updateCoordinationServiceNode(currentJob); setChanged(); notifyObservers(Event.JOB_STARTED); eventPublisher.publishRemotely(new AutoIngestJobStartedEvent(currentJob)); @@ -1890,6 +1893,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang // The job may get retried currentJob.setProcessingStatus(AutoIngestJob.ProcessingStatus.PENDING); } + currentJob.setProcessingHostName(""); updateCoordinationServiceNode(currentJob); boolean retry = (!currentJob.isCanceled() && !currentJob.isCompleted()); @@ -1986,7 +1990,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang if (AutoIngestUserPreferences.getSharedConfigEnabled()) { Path manifestPath = currentJob.getManifest().getFilePath(); SYS_LOGGER.log(Level.INFO, "Downloading shared configuration for {0}", manifestPath); - currentJob.setStage(AutoIngestJob.Stage.UPDATING_SHARED_CONFIG); + currentJob.setProcessingStage(AutoIngestJob.Stage.UPDATING_SHARED_CONFIG); new SharedConfiguration().downloadConfiguration(); } } @@ -2004,7 +2008,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang private void verifyRequiredSevicesAreRunning() throws ServicesMonitorException, DatabaseServerDownException, KeywordSearchServerDownException { Path manifestPath = currentJob.getManifest().getFilePath(); SYS_LOGGER.log(Level.INFO, "Checking services availability for {0}", manifestPath); - currentJob.setStage(AutoIngestJob.Stage.CHECKING_SERVICES); + currentJob.setProcessingStage(AutoIngestJob.Stage.CHECKING_SERVICES); if (!isServiceUp(ServicesMonitor.Service.REMOTE_CASE_DATABASE.toString())) { throw new DatabaseServerDownException("Case database server is down"); } @@ -2051,7 +2055,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang Manifest manifest = currentJob.getManifest(); String caseName = manifest.getCaseName(); SYS_LOGGER.log(Level.INFO, "Opening case {0} for {1}", new Object[]{caseName, manifest.getFilePath()}); - currentJob.setStage(AutoIngestJob.Stage.OPENING_CASE); + currentJob.setProcessingStage(AutoIngestJob.Stage.OPENING_CASE); /* * Acquire and hold a case name lock so that only one node at as * time can scan the output directory at a time. This prevents @@ -2164,7 +2168,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang DataSource dataSource = identifyDataSource(caseForJob); if (null == dataSource) { - currentJob.setStage(AutoIngestJob.Stage.COMPLETED); + currentJob.setProcessingStage(AutoIngestJob.Stage.COMPLETED); return; } @@ -2174,7 +2178,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang runDataSourceProcessor(caseForJob, dataSource); if (dataSource.getContent().isEmpty()) { - currentJob.setStage(AutoIngestJob.Stage.COMPLETED); + currentJob.setProcessingStage(AutoIngestJob.Stage.COMPLETED); return; } @@ -2219,7 +2223,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang Manifest manifest = currentJob.getManifest(); Path manifestPath = manifest.getFilePath(); SYS_LOGGER.log(Level.INFO, "Identifying data source for {0} ", manifestPath); - currentJob.setStage(AutoIngestJob.Stage.IDENTIFYING_DATA_SOURCE); + currentJob.setProcessingStage(AutoIngestJob.Stage.IDENTIFYING_DATA_SOURCE); Path caseDirectoryPath = currentJob.getCaseDirectoryPath(); AutoIngestJobLogger jobLogger = new AutoIngestJobLogger(manifestPath, manifest.getDataSourceFileName(), caseDirectoryPath); Path dataSourcePath = manifest.getDataSourcePath(); @@ -2255,7 +2259,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang Manifest manifest = currentJob.getManifest(); Path manifestPath = manifest.getFilePath(); SYS_LOGGER.log(Level.INFO, "Adding data source for {0} ", manifestPath); - currentJob.setStage(AutoIngestJob.Stage.ADDING_DATA_SOURCE); + currentJob.setProcessingStage(AutoIngestJob.Stage.ADDING_DATA_SOURCE); UUID taskId = UUID.randomUUID(); DataSourceProcessorCallback callBack = new AddDataSourceCallback(caseForJob, dataSource, taskId); DataSourceProcessorProgressMonitor progressMonitor = new DoNothingDSPProgressMonitor(); @@ -2420,7 +2424,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang Manifest manifest = currentJob.getManifest(); Path manifestPath = manifest.getFilePath(); SYS_LOGGER.log(Level.INFO, "Starting ingest modules analysis for {0} ", manifestPath); - currentJob.setStage(AutoIngestJob.Stage.ANALYZING_DATA_SOURCE); + currentJob.setProcessingStage(AutoIngestJob.Stage.ANALYZING_DATA_SOURCE); Path caseDirectoryPath = currentJob.getCaseDirectoryPath(); AutoIngestJobLogger jobLogger = new AutoIngestJobLogger(manifestPath, manifest.getDataSourceFileName(), caseDirectoryPath); IngestJobEventListener ingestJobEventListener = new IngestJobEventListener(); @@ -2456,7 +2460,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang } jobLogger.logAnalysisCompleted(); } else { - currentJob.setStage(AutoIngestJob.Stage.CANCELLING); + currentJob.setProcessingStage(AutoIngestJob.Stage.CANCELLING); currentJob.setErrorsOccurred(true); AutoIngestAlertFile.create(caseDirectoryPath); // Do this first, it is more important than the case log jobLogger.logAnalysisCancelled(); @@ -2519,7 +2523,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang Manifest manifest = currentJob.getManifest(); Path manifestPath = manifest.getFilePath(); SYS_LOGGER.log(Level.INFO, "Exporting files for {0}", manifestPath); - currentJob.setStage(AutoIngestJob.Stage.EXPORTING_FILES); + currentJob.setProcessingStage(AutoIngestJob.Stage.EXPORTING_FILES); Path caseDirectoryPath = currentJob.getCaseDirectoryPath(); AutoIngestJobLogger jobLogger = new AutoIngestJobLogger(manifestPath, manifest.getDataSourceFileName(), caseDirectoryPath); try { @@ -2770,9 +2774,10 @@ public final class AutoIngestManager extends Observable implements PropertyChang try { synchronized (jobsLock) { if (currentJob != null) { - currentJob.getStageDetails(); + currentJob.getProcessingStageDetails(); setChanged(); notifyObservers(Event.JOB_STATUS_UPDATED); + updateCoordinationServiceNode(currentJob); eventPublisher.publishRemotely(new AutoIngestJobStatusEvent(currentJob)); } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMonitor.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMonitor.java index 2000bba6f0..3fcc5648ba 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMonitor.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMonitor.java @@ -19,13 +19,10 @@ package org.sleuthkit.autopsy.experimental.autoingest; import com.google.common.util.concurrent.ThreadFactoryBuilder; -import java.awt.Cursor; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Observable; @@ -34,7 +31,6 @@ import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import javax.annotation.concurrent.GuardedBy; -import org.openide.util.Exceptions; import org.sleuthkit.autopsy.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; import org.sleuthkit.autopsy.coreutils.Logger; @@ -143,8 +139,6 @@ public final class AutoIngestMonitor extends Observable implements PropertyChang */ private void handleJobStartedEvent(AutoIngestJobStartedEvent event) { synchronized (jobsLock) { - // DLG: TEST! Remove job from pending queue, if present - // DLG: TEST! Add job to running jobs list jobsSnapshot.removePendingJob(event.getJob()); jobsSnapshot.addOrReplaceRunningJob(event.getJob()); setChanged(); @@ -159,8 +153,12 @@ public final class AutoIngestMonitor extends Observable implements PropertyChang */ private void handleJobStatusEvent(AutoIngestJobStatusEvent event) { synchronized (jobsLock) { - // DLG: TEST! Replace job in running list with job from event - jobsSnapshot.addOrReplaceRunningJob(event.getJob()); + /* + * Currently this event is only published for running jobs. + */ + AutoIngestJob job = event.getJob(); + jobsSnapshot.removePendingJob(job); + jobsSnapshot.addOrReplaceRunningJob(job); setChanged(); notifyObservers(jobsSnapshot); } @@ -173,10 +171,10 @@ public final class AutoIngestMonitor extends Observable implements PropertyChang */ private void handleJobCompletedEvent(AutoIngestJobCompletedEvent event) { synchronized (jobsLock) { - // DLG: TEST! Remove job from event from running list, if present - // DLG: TEST! Add job to completed list - jobsSnapshot.removeRunningJob(event.getJob()); - jobsSnapshot.addOrReplaceCompletedJob(event.getJob()); + AutoIngestJob job = event.getJob(); + jobsSnapshot.removePendingJob(job); + jobsSnapshot.removeRunningJob(job); + jobsSnapshot.addOrReplaceCompletedJob(job); setChanged(); notifyObservers(jobsSnapshot); } @@ -241,6 +239,13 @@ public final class AutoIngestMonitor extends Observable implements PropertyChang for (String node : nodeList) { try { AutoIngestJobNodeData nodeData = new AutoIngestJobNodeData(coordinationService.getNodeData(CoordinationService.CategoryNode.MANIFESTS, node)); + if (nodeData.getVersion() < 1) { + /* + * Ignore version '0' nodes that have not been + * "upgraded" since they don't carry enough data. + */ + continue; + } AutoIngestJob job = new AutoIngestJob(nodeData); ProcessingStatus processingStatus = nodeData.getProcessingStatus(); switch (processingStatus) { @@ -275,9 +280,9 @@ public final class AutoIngestMonitor extends Observable implements PropertyChang /** * Bumps the priority of an auto ingest job. * - * @param manifestPath The manifest file path for the job to be prioritized. + * @param job The job to be prioritized. */ - JobsSnapshot prioritizeJob(Path manifestFilePath) throws AutoIngestMonitorException { + JobsSnapshot prioritizeJob(AutoIngestJob job) throws AutoIngestMonitorException { int highestPriority = 0; AutoIngestJob prioritizedJob = null; synchronized (jobsLock) { @@ -285,11 +290,11 @@ public final class AutoIngestMonitor extends Observable implements PropertyChang * Get the highest known priority and make sure the job is still in * the pending jobs queue. */ - for (AutoIngestJob job : jobsSnapshot.getPendingJobs()) { - if (job.getPriority() > highestPriority) { - highestPriority = job.getPriority(); + for (AutoIngestJob pendingJob : jobsSnapshot.getPendingJobs()) { + if (pendingJob.getPriority() > highestPriority) { + highestPriority = pendingJob.getPriority(); } - if (job.getManifest().getFilePath().equals(manifestFilePath)) { + if (pendingJob.equals(job)) { prioritizedJob = job; } } @@ -300,27 +305,25 @@ public final class AutoIngestMonitor extends Observable implements PropertyChang */ if (null != prioritizedJob) { ++highestPriority; - String manifestNodePath = prioritizedJob.getManifest().getFilePath().toString(); + String manifestNodePath = job.getManifest().getFilePath().toString(); try { AutoIngestJobNodeData nodeData = new AutoIngestJobNodeData(coordinationService.getNodeData(CoordinationService.CategoryNode.MANIFESTS, manifestNodePath)); nodeData.setPriority(highestPriority); coordinationService.setNodeData(CoordinationService.CategoryNode.MANIFESTS, manifestNodePath, nodeData.toArray()); } catch (AutoIngestJobNodeData.InvalidDataException | CoordinationServiceException | InterruptedException ex) { - throw new AutoIngestMonitorException("Error bumping priority for job " + prioritizedJob.toString(), ex); + throw new AutoIngestMonitorException("Error bumping priority for job " + job.toString(), ex); } prioritizedJob.setPriority(highestPriority); - } - /* - * Publish a prioritization event. - */ - if (null != prioritizedJob) { - final String caseName = prioritizedJob.getManifest().getCaseName(); + /* + * Publish a prioritization event. + */ + final String caseName = job.getManifest().getCaseName(); new Thread(() -> { eventPublisher.publishRemotely(new AutoIngestCasePrioritizedEvent(LOCAL_HOST_NAME, caseName)); }).start(); - } + } return jobsSnapshot; } } @@ -344,7 +347,7 @@ public final class AutoIngestMonitor extends Observable implements PropertyChang synchronized (jobsLock) { jobsSnapshot = newJobsSnapshot; setChanged(); - notifyObservers(jobsSnapshot); + notifyObservers(null); } } } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties index a22410a70e..1633373467 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties @@ -11,45 +11,16 @@ AutoIngestDashboard.JobsTableModel.ColumnHeader.CompletedTime=Job Completed AutoIngestDashboard.JobsTableModel.ColumnHeader.Stage=Stage AutoIngestDashboard.JobsTableModel.ColumnHeader.Status=Status AutoIngestDashboard.JobsTableModel.ColumnHeader.ManifestFilePath= Manifest File Path -AutoIngestDashboard.bnResume.text=Resume -AutoIngestDashboard.bnPause.confirmHeader=Are you sure you want to pause? -AutoIngestDashboard.bnPause.warningText=Pause will occur after the current job completes processing. This could take a long time. Continue? -AutoIngestDashboard.bnPause.toolTipTextResume=Resume processing of Pending Jobs -AutoIngestDashboard.bnPause.pausing=Pausing after current job completes... -AutoIngestDashboard.Cancelling=Cancelling... AutoIngestDashboard.pendingTable.toolTipText=The Pending table displays the order upcoming Jobs will be processed with the top of the list first AutoIngestDashboard.runningTable.toolTipText=The Running table displays the currently running Job and information about it AutoIngestDashboard.completedTable.toolTipText=The Completed table shows all Jobs that have been processed already AutoIngestDashboard.JobsTableModel.ColumnHeader.StageTime=Time in Stage -AutoIngestDashboard.JobsTableModel.ColumnHeader.CaseFolder=Case Folder -AutoIngestDashboard.JobsTableModel.ColumnHeader.LocalJob= Local Job? -AutoIngestDashboard.DeletionFailed=Deletion failed for job -AutoIngestDashboard.ShowLogFailed.Title=Unable to display case log -AutoIngestDashboard.ShowLogFailed.Message=Case log file does not exist -AutoIngestDashboard.bnPrioritizeCase.toolTipText=Move all images associated with a case to top of Pending queue. -AutoIngestDashboard.ExitConsequences=This will cancel any currently running job on this host. Exiting while a job is running potentially leaves the case in an inconsistent or corrupted state. -AutoIngestDashboard.ExitingStatus=Exiting... -AutoIngestDashboard.OK=OK -AutoIngestDashboard.Cancel=Cancel -AutoIngestDashboard.AutoIngestStartupFailed.Message=Failed to start automated ingest.\nPlease see auto ingest system log for details. -AutoIngestDashboard.AutoIngestStartupFailed.Title=Automated Ingest Error -AutoIngestDashboard.AutoIngestStartupError=Failed to start automated ingest. Verify Multi-user Settings. -AutoIngestDashboard.AutoIngestStartupWarning.Title=Automated Ingest Warning -AutoIngestDashboard.AutoIngestStartupWarning.Message=Failed to establish remote communications with other automated ingest nodes.\nAuto ingest dashboard will only be able to display local ingest job events.\nPlease verify Multi-User settings (Options->Multi-User). See application log for details. -AutoIngestDashboard.UpdatingSharedConfig=Updating shared configuration -AutoIngestDashboard.SharedConfigurationDisabled=Shared configuration disabled -AutoIngestDashboard.EnableConfigurationSettings=Enable shared configuration from the options panel before uploading -AutoIngestDashboard.ErrorUploadingConfiguration=Error uploading configuration -AutoIngestDashboard.UploadSuccessTitle=Success -AutoIngestDashboard.UploadSuccess=Shared configuration successfully uploaded -AutoIngestDashboard.UploadFailedTitle=Failed -AutoIngestDashboard.ConfigLocked=The shared configuration directory is locked because upload from another node is in progress. \nIf this is an error, you can unlock the directory and then retry the upload. -AutoIngestDashboard.ConfigLockedTitle=Configuration directory locked +AutoIngestDashboard.JobsTableModel.ColumnHeader.CaseFolder=Case +AutoIngestDashboard.JobsTableModel.ColumnHeader.Job=Job AutoIngestDashboard.tbServicesStatusMessage.Message=Case databases {0}, keyword search {1}, coordination {2}, messaging {3} AutoIngestDashboard.tbServicesStatusMessage.Message.Up=up AutoIngestDashboard.tbServicesStatusMessage.Message.Down=down AutoIngestDashboard.tbServicesStatusMessage.Message.Unknown=unknown -AutoIngestDashboard.PauseDueToSystemError=Paused due to system error, please consult the auto ingest system log ConfirmationDialog.DoNotDelete=Do not delete ConfirmationDialog.Delete=Permanently delete ConfirmationDialog.DeleteAreYouSure=The entire case will be removed. Are you sure you want to delete case @@ -260,7 +231,6 @@ FileExporterSettingsPanel.BrowseReportTooltip_1=Browse for the Reports Folder FileExporterSettingsPanel.NewRuleTooltip_1=Clear the rule editor to begin a new rule FileExporterSettingsPanel.DeleteTooltip_1=Delete the selected rule FileExporterSettingsPanel.SaveTooltip_1=Save the current rule -AutoIngestDashboard.bnPrioritizeFolder.label= AutoIngestCasePanel.rbDays.text=Days AutoIngestCasePanel.rbWeeks.text=Weeks AutoIngestCasePanel.rbMonths.text=Months diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Manifest.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Manifest.java index e3c4cb6bb1..5a42a01e80 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Manifest.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Manifest.java @@ -42,7 +42,7 @@ public final class Manifest implements Serializable { this.dateFileCreated = dateFileCreated; this.caseName = caseName; this.deviceId = deviceId; - if (dataSourcePath != null) { + if (null != dataSourcePath) { this.dataSourcePath = dataSourcePath.toString(); } else { this.dataSourcePath = ""; @@ -55,7 +55,7 @@ public final class Manifest implements Serializable { } public Date getDateFileCreated() { - return this.dateFileCreated; + return new Date(this.dateFileCreated.getTime()); } public String getCaseName() { diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/dashboardWsmode.xml b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/dashboardWsmode.xml index 49305ab59f..3f681aa72f 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/dashboardWsmode.xml +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/dashboardWsmode.xml @@ -3,7 +3,7 @@ - + diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenHelpAction.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenHelpAction.java deleted file mode 100755 index 4b3be4813a..0000000000 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenHelpAction.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2013 Basis Technology Corp. - * Contact: carrier sleuthkit 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.imagegallery.actions; - -import java.awt.Desktop; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.IOException; -import java.net.URI; -import java.util.logging.Level; -import org.openide.awt.ActionID; -import org.openide.awt.ActionReference; -import org.openide.awt.ActionRegistration; -import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.coreutils.Logger; - -@ActionID( - category = "Help", - id = "org.sleuthkit.autopsy.imagegallery.actions.OpenHelpAction" -) -@ActionRegistration( - displayName = "#CTL_OpenHelpAction" -) -@ActionReference(path = "Menu/Help", position = 350) -@Messages("CTL_OpenHelpAction=Image / Video Gallery Help") -public final class OpenHelpAction implements ActionListener { - - @Override - public void actionPerformed(ActionEvent e) { - try { - Desktop.getDesktop().browse(URI.create("http://sleuthkit.org/autopsy/docs/user-docs/4.4.1/image_gallery_page.html")); //NON-NLS - } catch (IOException ex) { - Logger.getLogger(OpenHelpAction.class.getName()).log(Level.SEVERE, "failed to open help page", ex); //NON-NLS - } - } -} diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Toolbar.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Toolbar.java index 72f9622729..2dc85cbfd9 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Toolbar.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Toolbar.java @@ -136,7 +136,15 @@ public class Toolbar extends ToolBar { tagGroupMenuButton.setText(followUpGroupAction.getText()); tagGroupMenuButton.setGraphic(followUpGroupAction.getGraphic()); } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Could create follow up tag menu item", ex); //NON-NLS + /* + * The problem appears to be a timing issue where a case is closed + * before this initialization is completed, which It appears to be + * harmless, so we are temporarily changing this log message to a + * WARNING. + * + * TODO (JIRA-3010): SEVERE error logged by image Gallery UI + */ + LOGGER.log(Level.WARNING, "Could not create Follow Up tag menu item", ex); //NON-NLS } tagGroupMenuButton.showingProperty().addListener(showing -> { if (tagGroupMenuButton.isShowing()) { diff --git a/build.xml b/build.xml index 685f285fda..deda12f21e 100755 --- a/build.xml +++ b/build.xml @@ -265,7 +265,6 @@ - diff --git a/docs/doxygen-user/central_repo.dox b/docs/doxygen-user/central_repo.dox index 66b446023a..74bf0dde4c 100644 --- a/docs/doxygen-user/central_repo.dox +++ b/docs/doxygen-user/central_repo.dox @@ -2,22 +2,22 @@ \section cr_overview Overview -The Central Repository allows a user to find matching artifacts both across cases and across data sources in the same case. -It is a combination of an ingest module that extracts, stores, and compares properties against lists of known or known bad +The central repository allows a user to find matching artifacts both across cases and across data sources in the same case. +It is a combination of an ingest module that extracts, stores, and compares properties against lists of notable properties, a database that stores these properties, and an additional panel in Autopsy to display other instances of each -property. The Central Repository database can either be SQLite or PostgreSQL. +property. The central repository database can either be SQLite or PostgreSQL. The following are some use cases for the Central Repository: - Finding Other Instances of a Property - If you find a file or Autopsy artifact (such as a Web History item), there is a content viewer in the bottom right that will show you other cases that had this same file or that had items with the same feature (such as Domain name). You will also be able to see what other data sources in the same case had this feature. - Alerting When Previously Notable Properties Occur - - You can use the Central Repository to record which properties were associated with files and artifacts that were evidence (or notable). Once these properties have been tagged as 'BAD' they will be added to the Interesting Items section of the tree when seen again in any future cases. + - You can use the Central Repository to record which properties were associated with files and artifacts that were evidence (or notable). Once these properties have been tagged as notable they will be added to the Interesting Items section of the tree when seen again in any future cases. \section cr_terms Terms and Concepts -- Central Repository - The Autopsy feature containing the Central Repository Database and Correlation Engine Ingest Module. Also responsible for displaying correlated properties to the user +- Central Repository - The Autopsy feature containing the central repository database and Correlation Engine Ingest Module. Also responsible for displaying correlated properties to the user - Central Repository Database - the SQLite or PostgreSQL database that holds all the data -- Correlation Engine Ingest Module - The ingest module responsible for adding new properties to the database and comparing these properties against the Known/Known Bad lists +- Correlation Engine Ingest Module - The ingest module responsible for adding new properties to the database and comparing these properties against existing notable properties - Property - The data being stored/correlated. These can be file paths/MD5 hashes, email addresses, phone numbers, etc. \section cr_setup Setup @@ -62,14 +62,14 @@ Enter or browse to a folder for the database. If the database file does not exis In Autopsy, you are allowed to define your own Tag names, tag files and artifacts,and add comments when you tag a file or artifact. The purpose of this feature is to associate one or more of those tags with this module to be used for Correlation. Associating a tag with the Correlation Engine means -that when a file/artifact is tagged by the user, any property created from that file/artifact in the Correlation Database is marked as Bad. After -this point, whenever the Correlation Engine Ingest Module creates a property that matches this Bad one, it is automatically flagged +that when a file/artifact is tagged by the user, any property created from that file/artifact in the Correlation Database is marked as notable. After +this point, whenever the Correlation Engine Ingest Module creates a property that matches this notable one, it is automatically flagged and added to the list of Interesting Items. \image html central_repo_manage_tags.png -If a case is open, checking the Implies Known Bad checkbox will give you the option to add the known bad status to anything in the current case -that has already been tagged. For example, if you create a tag named "Alpha", tag a few items and then go into Manage Tags and check the box for the Alpha tag, you can optionally choose to have the status for those tagged items changed in the Central Repository. The effect is the same as if you had checked the box in Manage Tags before tagging the items. Note that data from any previous cases will not be changed. +If a case is open, checking the Notable checkbox will give you the option to add the notable status to anything in the current case +that has already been tagged. For example, if you create a tag named "Alpha", tag a few items and then go into Manage Tags and check the box for the Alpha tag, you can optionally choose to have the status for those tagged items changed in the central repository. The effect is the same as if you had checked the box in Manage Tags before tagging the items. Note that data from any previous cases will not be changed. By default there is a tag called "Evidence" as the only tag associated with this module. To associate one or more tag(s) with this module, check the Correlate box next to the tag name(s) and click OK. To create additional tags, use the Tags options panel. @@ -99,9 +99,9 @@ Descriptions of the property types: \subsection cr_ingest_module Correlation Engine Module The Correlation Engine ingest module is responsible for adding properties to the database and comparing each property -against the list of Known/Known Bad property. It is best to run all ingest modules to get the most out of the Correlation +against the list of notable properties. It is best to run all ingest modules to get the most out of the Correlation Engine. For example, if Hash Lookup is not run then the Correlation Engine module will not put any files into the -database. If the Correlation Engine module is not run on a particular case but the Central Repository is enabled, +database. If the Correlation Engine module is not run on a particular case but a central repository is enabled, there will still be some limited functionality. The Content Viewer will still display matching properties from other cases/data sources where the Correlation Engine was run. @@ -109,13 +109,13 @@ other cases/data sources where the Correlation Engine was run. Any file or artifact that a user tags with one of the tags associated with the Correlation Engine will be added to the database as a file or artifact of interest. Any future data source ingest, where this module is enabled, -will use those files or artifacts as if they were part of the Known Bad list, causing matching files from that +will use those files or artifacts in a similar manner as a Known Bad hash set, causing matching files from that ingest to be added to the Interesting Artifacts list in that currently open case. \image html central_repo_tag_file.png If a tag is accidentally added to a file or artifact, it can be removed though the context menu. This will remove its -Known Bad status in the Central Repository. +notable status in the Central Repository. \subsection cr_case_properties Edit Central Repository Case Properties @@ -128,17 +128,17 @@ This is where the examiner can store a number of details about the case. \section cr_viewing_results Viewing Results -Results from enabling the Central Repository and running the Correlation Engine Ingest Module can be seen in two places: +Results from enabling a central repository and running the Correlation Engine Ingest Module can be seen in two places: - The Content Viewer for each file or artifact will display all matching properties from other cases/data sources -- The Interesting Files node of the result tree will contain any files or results that matched properties previously flagged as BAD +- The Interesting Files node of the result tree will contain any files or results that matched properties previously marked as notable \subsection cr_content_viewer Content Viewer -The \ref content_viewer_page panel is where previous instances of properties are displayed. This module adds a new tab to the Content Viewer. The tab for this module is called "Other Data Sources". It can display data that is found in other cases, other data sources for the same case, or imported global artifacts. +The \ref content_viewer_page panel is where previous instances of properties are displayed. This module adds a new tab to the Content Viewer. The tab for this module is called "Other Occurrences". It can display data that is found in other cases, other data sources for the same case, or imported global artifacts. -If at least one other case or data source has been ingested with this module enabled, there is a potential that data will be displayed in the Other Data Sources content viewer. Note that the Correlation Engine Ingest Module does not have to have been run on the current data source to see correlated files from other cases/data sources. If the selected file or artifact is associated by one of the supported Correlation Types, to one or more file(s) or artifact(s) in the database, the associated files/artifacts will be displayed. Note: the Content Viewer will display ALL associated files and artifacts available in the database. It ignores the user's enabled/disabled Correlation Properties. +If at least one other case or data source has been ingested with this module enabled, there is a potential that data will be displayed in the Other Occurrences content viewer. Note that the Correlation Engine Ingest Module does not have to have been run on the current data source to see correlated files from other cases/data sources. If the selected file or artifact is associated by one of the supported Correlation Types, to one or more file(s) or artifact(s) in the database, the associated files/artifacts will be displayed. Note: the Content Viewer will display ALL associated files and artifacts available in the database. It ignores the user's enabled/disabled Correlation Properties. -By default, the rows in the content viewer will have background colors to indicate if they are known to be of interest. Files/artifacts that are Known Bad will have a Red background, Unknown will have Yellow background, and Known will have a White background. +By default, the rows in the content viewer will have background colors to indicate if they are known to be of interest. Files/artifacts that are notable will have a Red background, unknown will have Yellow background, and known will have a White background. \image html central_repo_content_viewer.png @@ -173,8 +173,8 @@ details will include: - Case Creation Date - Case Examiner contact information - Case Examiner's notes -T -hese details would have been entered by the examiner of the selected case, by visiting + +These details would have been entered by the examiner of the selected case, by visiting the Case -> Central Repository Case Properties menu, when that case was open. Show Commonality Details @@ -187,11 +187,11 @@ In the Results tree of an open case is an entry called Interesting Items. When t \image html central_repo_interesting_items.png -As an example, if the Files Correlatable Property is enabled, and the ingest is currently processing a file, for example "badfile.exe", and the MD5 hash for that file already exists in the database as a KNOWN BAD file, then an entry in the Interesting Items tree will be added for the current instance of "badfile.exe" in the data source currently being ingested. +As an example, if the Files Correlatable Property is enabled, and the ingest is currently processing a file, for example "badfile.exe", and the MD5 hash for that file already exists in the database as a notable file, then an entry in the Interesting Items tree will be added for the current instance of "badfile.exe" in the data source currently being ingested. The same type of thing will happen for each enabled Correlatable Property. -In the case of the phone number correlatable type, the Interesting Items tree will start a sub-tree for each phone number. The sub-tree will then contain each instance of that Known Bad phone number. +In the case of the phone number correlatable type, the Interesting Items tree will start a sub-tree for each phone number. The sub-tree will then contain each instance of that notable phone number. diff --git a/docs/doxygen-user/images/central_repo_content_viewer.png b/docs/doxygen-user/images/central_repo_content_viewer.png index c665b882d2..b5751f67cc 100644 Binary files a/docs/doxygen-user/images/central_repo_content_viewer.png and b/docs/doxygen-user/images/central_repo_content_viewer.png differ diff --git a/docs/doxygen-user/images/central_repo_interesting_items.png b/docs/doxygen-user/images/central_repo_interesting_items.png index 80282eba7e..2c689eb67b 100644 Binary files a/docs/doxygen-user/images/central_repo_interesting_items.png and b/docs/doxygen-user/images/central_repo_interesting_items.png differ diff --git a/docs/doxygen-user/images/central_repo_manage_tags.png b/docs/doxygen-user/images/central_repo_manage_tags.png index 0c0e2e1abd..ce78a3a373 100644 Binary files a/docs/doxygen-user/images/central_repo_manage_tags.png and b/docs/doxygen-user/images/central_repo_manage_tags.png differ diff --git a/docs/doxygen-user/images/central_repo_options.png b/docs/doxygen-user/images/central_repo_options.png index cc62b2a57c..29ebb0cb4d 100644 Binary files a/docs/doxygen-user/images/central_repo_options.png and b/docs/doxygen-user/images/central_repo_options.png differ diff --git a/docs/doxygen-user/images/content-viewer-1.PNG b/docs/doxygen-user/images/content-viewer-1.PNG index 8079e2bda0..72389d2204 100755 Binary files a/docs/doxygen-user/images/content-viewer-1.PNG and b/docs/doxygen-user/images/content-viewer-1.PNG differ diff --git a/docs/doxygen-user/images/content-viewer-2.PNG b/docs/doxygen-user/images/content-viewer-2.PNG index c42ce5be74..fbdb6f7fab 100755 Binary files a/docs/doxygen-user/images/content-viewer-2.PNG and b/docs/doxygen-user/images/content-viewer-2.PNG differ diff --git a/docs/doxygen-user/images/content-viewer-3.PNG b/docs/doxygen-user/images/content-viewer-3.PNG index b90c28f87b..2971f99620 100755 Binary files a/docs/doxygen-user/images/content-viewer-3.PNG and b/docs/doxygen-user/images/content-viewer-3.PNG differ diff --git a/docs/doxygen-user/images/content-viewer-4.PNG b/docs/doxygen-user/images/content-viewer-4.PNG index 86104803df..22aa80e5d8 100755 Binary files a/docs/doxygen-user/images/content-viewer-4.PNG and b/docs/doxygen-user/images/content-viewer-4.PNG differ diff --git a/docs/doxygen-user/images/content-viewer-5.PNG b/docs/doxygen-user/images/content-viewer-5.PNG index 3b8b76562c..7e0fe38983 100755 Binary files a/docs/doxygen-user/images/content-viewer-5.PNG and b/docs/doxygen-user/images/content-viewer-5.PNG differ diff --git a/docs/doxygen-user/images/keyword-search-ad-hoc.PNG b/docs/doxygen-user/images/keyword-search-ad-hoc.PNG index f68e3c7842..6cf2a60c68 100755 Binary files a/docs/doxygen-user/images/keyword-search-ad-hoc.PNG and b/docs/doxygen-user/images/keyword-search-ad-hoc.PNG differ diff --git a/docs/doxygen-user/images/keyword-search-hits.PNG b/docs/doxygen-user/images/keyword-search-hits.PNG index 94c9f9389f..b1e0193f27 100755 Binary files a/docs/doxygen-user/images/keyword-search-hits.PNG and b/docs/doxygen-user/images/keyword-search-hits.PNG differ diff --git a/docs/doxygen-user/images/keyword_results.PNG b/docs/doxygen-user/images/keyword_results.PNG index ab44b64a2a..0ea7ef04d5 100755 Binary files a/docs/doxygen-user/images/keyword_results.PNG and b/docs/doxygen-user/images/keyword_results.PNG differ diff --git a/docs/doxygen-user/images/screenshot.PNG b/docs/doxygen-user/images/screenshot.PNG index 7cfd309aab..b3d7359da6 100755 Binary files a/docs/doxygen-user/images/screenshot.PNG and b/docs/doxygen-user/images/screenshot.PNG differ diff --git a/docs/doxygen-user/images/ui-layout-1.PNG b/docs/doxygen-user/images/ui-layout-1.PNG index 12aa8f5e7e..4e65754731 100755 Binary files a/docs/doxygen-user/images/ui-layout-1.PNG and b/docs/doxygen-user/images/ui-layout-1.PNG differ