First cut at merging latest AIM

This commit is contained in:
Eugene Livis 2016-09-05 10:22:05 -04:00
parent efed6a333b
commit 885aba8daf
12 changed files with 768 additions and 548 deletions

View File

@ -28,8 +28,6 @@ import java.util.logging.Level;
* Utility for creating and checking for the existence of an automated ingest * Utility for creating and checking for the existence of an automated ingest
* alert file. The purpose of the file is to put a marker in the case directory * alert file. The purpose of the file is to put a marker in the case directory
* when an error or warning occurs in connection with an automated ingest job. * when an error or warning occurs in connection with an automated ingest job.
* If there is an error creating an alert file, it is logged to the auto ingest
* system log.
*/ */
final class AutoIngestAlertFile { final class AutoIngestAlertFile {
@ -54,7 +52,7 @@ final class AutoIngestAlertFile {
* *
* @return True or false. * @return True or false.
*/ */
static void create(Path caseDirectoryPath) { static void create(Path caseDirectoryPath) throws AutoIngestAlertFileException {
try { try {
Files.createFile(caseDirectoryPath.resolve(ERROR_FILE_NAME)); Files.createFile(caseDirectoryPath.resolve(ERROR_FILE_NAME));
} catch (FileAlreadyExistsException ignored) { } catch (FileAlreadyExistsException ignored) {
@ -67,15 +65,44 @@ final class AutoIngestAlertFile {
* for that case. * for that case.
*/ */
if (!exists(caseDirectoryPath)) { if (!exists(caseDirectoryPath)) {
AutoIngestSystemLogger.getLogger().log(Level.SEVERE, String.format("Error creating automated ingest alert file in %s", caseDirectoryPath), ex); throw new AutoIngestAlertFileException(String.format("Error creating automated ingest alert file in %s", caseDirectoryPath), ex);
} }
} }
} }
/**
* Exception thrown when there is a problem creating an alert file.
*/
final static class AutoIngestAlertFileException extends Exception {
private static final long serialVersionUID = 1L;
/**
* Constructs an exception to throw when there is a problem creating an
* alert file.
*
* @param message The exception message.
*/
private AutoIngestAlertFileException(String message) {
super(message);
}
/**
* Constructs an exception to throw when there is a problem creating an
* alert file.
*
* @param message The exception message.
* @param cause The cause of the exception, if it was an exception.
*/
private AutoIngestAlertFileException(String message, Throwable cause) {
super(message, cause);
}
}
/** /**
* Prevents instantiation of this utility class. * Prevents instantiation of this utility class.
*/ */
private AutoIngestAlertFile() { private AutoIngestAlertFile() {
} }
} }

View File

@ -24,23 +24,23 @@ import org.sleuthkit.autopsy.events.AutopsyEvent;
/** /**
* Event published when a case is deleted by the automated ingest manager. * Event published when a case is deleted by the automated ingest manager.
*/ */
public final class AutoIngestCaseDeletedEvent extends AutopsyEvent implements Serializable { @Immutable
final class AutoIngestCaseDeletedEvent extends AutopsyEvent implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final AutoIngestManager.CaseDeletionResult result; private final String caseName;
private final String nodeName; private final String nodeName;
/** /**
* Constructs an event that is published when a case is deleted by the * Constructs an event that is published when a case is deleted by the
* automated ingest manager. * automated ingest manager.
* *
* @param result The deletion result // RJCTODO: Get rid of logical * @param caseName The case name.
* deletion
* @param nodeName The host name of the node that deleted the case. * @param nodeName The host name of the node that deleted the case.
*/ */
public AutoIngestCaseDeletedEvent(AutoIngestManager.CaseDeletionResult result, String nodeName) { AutoIngestCaseDeletedEvent(String caseName, String nodeName) {
super(AutoIngestManager.Event.CASE_DELETED.toString(), null, null); super(AutoIngestManager.Event.CASE_DELETED.toString(), null, null);
this.result = result; this.caseName = caseName;
this.nodeName = nodeName; this.nodeName = nodeName;
} }
@ -49,8 +49,8 @@ public final class AutoIngestCaseDeletedEvent extends AutopsyEvent implements Se
* *
* @return * @return
*/ */
public String getNodeName() { String getCaseName() {
return nodeName; return caseName;
} }
/** /**
@ -58,8 +58,8 @@ public final class AutoIngestCaseDeletedEvent extends AutopsyEvent implements Se
* *
* @return * @return
*/ */
public AutoIngestManager.CaseDeletionResult getResult() { String getNodeName() {
return result; return nodeName;
} }
} }

View File

@ -25,7 +25,7 @@ import org.sleuthkit.autopsy.events.AutopsyEvent;
* Event published when an automated ingest manager prioritizes all or part of a * Event published when an automated ingest manager prioritizes all or part of a
* case. * case.
*/ */
public final class AutoIngestCasePrioritizedEvent extends AutopsyEvent implements Serializable { public final class AutoIngestCasePrioritizedEvent extends AutopsyEvent implements Serializable { // RJCTODO: Rename to AutoIngestPrioritizationEvent
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final String caseName; private final String caseName;

View File

@ -19,67 +19,62 @@
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="1" attributes="0">
<Component id="lbPending" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="pendingScrollPane" min="-2" pref="920" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="bnPrioritizeCase" max="32767" attributes="0"/>
<Component id="bnPrioritizeJob" max="32767" attributes="0"/>
</Group>
</Group>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="1" attributes="0"> <Component id="bnPause" linkSize="1" min="-2" max="-2" attributes="0"/>
<Component id="lbPending" alignment="0" min="-2" max="-2" attributes="0"/> <EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
<Component id="pendingScrollPane" min="-2" pref="920" max="-2" attributes="0"/> <Component id="bnRefresh" linkSize="1" min="-2" pref="100" max="-2" attributes="0"/>
</Group> <EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/> <Component id="bnOptions" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
<Component id="bnOpenLogDir" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
<Component id="bnExit" linkSize="1" min="-2" pref="94" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="bnPrioritizeCase" min="-2" pref="117" max="-2" attributes="0"/> <Component id="runningScrollPane" min="-2" pref="920" max="-2" attributes="0"/>
<Component id="bnPrioritizeFolder" min="-2" pref="117" max="-2" attributes="0"/> <Component id="completedScrollPane" min="-2" pref="920" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="bnCancelJob" linkSize="1" pref="117" max="32767" attributes="0"/>
<Component id="bnShowProgress" linkSize="1" pref="116" max="32767" attributes="0"/>
<Component id="bnCancelModule" linkSize="1" alignment="0" pref="117" max="32767" attributes="0"/>
<Component id="bnDeleteCase" linkSize="1" alignment="0" pref="117" max="32767" attributes="0"/>
<Component id="bnShowCaseLog" max="32767" attributes="0"/>
<Component id="bnReprocessJob" alignment="0" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
<Group type="102" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0"> <Component id="lbStatus" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0"> <EmptySpace type="unrelated" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="50" max="-2" attributes="0"/> <Component id="tbStatusMessage" min="-2" pref="861" max="-2" attributes="0"/>
<Component id="bnPause" linkSize="1" min="-2" max="-2" attributes="0"/> </Group>
<EmptySpace min="-2" pref="50" max="-2" attributes="0"/> <Component id="lbCompleted" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="bnRefresh" linkSize="1" min="-2" pref="100" max="-2" attributes="0"/> <Component id="lbRunning" alignment="0" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="50" max="-2" attributes="0"/> <Group type="102" alignment="0" attributes="0">
<Component id="bnOptions" linkSize="1" min="-2" max="-2" attributes="0"/> <Component id="lbServicesStatus" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="50" max="-2" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="bnOpenLogDir" linkSize="1" min="-2" max="-2" attributes="0"/> <Component id="tbServicesStatusMessage" min="-2" pref="861" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="50" max="-2" attributes="0"/>
<Component id="bnExit" linkSize="1" min="-2" pref="94" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="runningScrollPane" min="-2" pref="920" max="-2" attributes="0"/>
<Component id="completedScrollPane" min="-2" pref="920" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="bnCancelJob" linkSize="1" pref="117" max="32767" attributes="0"/>
<Component id="bnShowProgress" linkSize="1" pref="116" max="32767" attributes="0"/>
<Component id="bnCancelModule" linkSize="1" alignment="0" pref="117" max="32767" attributes="0"/>
<Component id="bnDeleteCase" linkSize="1" alignment="0" pref="117" max="32767" attributes="0"/>
<Component id="bnShowCaseLog" max="32767" attributes="0"/>
</Group>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="lbStatus" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="tbStatusMessage" min="-2" pref="861" max="-2" attributes="0"/>
</Group>
<Component id="lbCompleted" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lbRunning" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
<EmptySpace max="32767" attributes="0"/> <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="lbServicesStatus" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="tbServicesStatusMessage" min="-2" pref="861" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -106,7 +101,7 @@
<EmptySpace min="-2" pref="82" max="-2" attributes="0"/> <EmptySpace min="-2" pref="82" max="-2" attributes="0"/>
<Component id="bnPrioritizeCase" min="-2" max="-2" attributes="0"/> <Component id="bnPrioritizeCase" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="bnPrioritizeFolder" min="-2" max="-2" attributes="0"/> <Component id="bnPrioritizeJob" min="-2" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
@ -127,9 +122,11 @@
</Group> </Group>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="102" max="-2" attributes="0"/> <EmptySpace min="-2" pref="68" max="-2" attributes="0"/>
<Component id="bnReprocessJob" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="bnDeleteCase" linkSize="2" min="-2" max="-2" attributes="0"/> <Component id="bnDeleteCase" linkSize="2" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="bnShowCaseLog" min="-2" max="-2" attributes="0"/> <Component id="bnShowCaseLog" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
@ -461,5 +458,15 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnOpenLogDirActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnOpenLogDirActionPerformed"/>
</Events> </Events>
</Component> </Component>
<Component class="javax.swing.JButton" name="bnReprocessJob">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnReprocessJob.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnReprocessJobActionPerformed"/>
</Events>
</Component>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -54,10 +54,13 @@ import org.openide.LifecycleManager;
import org.openide.NotifyDescriptor; import org.openide.NotifyDescriptor;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.core.ServicesMonitor; import org.sleuthkit.autopsy.core.ServicesMonitor;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.NetworkUtils; import org.sleuthkit.autopsy.coreutils.NetworkUtils;
import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestProgressSnapshotDialog; import org.sleuthkit.autopsy.ingest.IngestProgressSnapshotDialog;
import org.sleuthkit.autopsy.experimental.AutoIngestManager.CaseDeletionResult;
import org.sleuthkit.autopsy.experimental.AutoIngestManager.JobsSnapshot;
import org.sleuthkit.autopsy.experimental.configuration.OptionsDialog; import org.sleuthkit.autopsy.experimental.configuration.OptionsDialog;
/** /**
@ -91,7 +94,7 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
private static final int COMPLETED_TIME_COL_PREFERRED_WIDTH = 280; 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 String UPDATE_TASKS_THREAD_NAME = "AID-update-tasks-%d";
private static final String LOCAL_HOST_NAME = NetworkUtils.getLocalHostName(); private static final String LOCAL_HOST_NAME = NetworkUtils.getLocalHostName();
private static final Logger LOGGER = AutoIngestSystemLogger.getLogger(); private static final Logger SYS_LOGGER = AutoIngestSystemLogger.getLogger();
private static AutoIngestDashboard instance; private static AutoIngestDashboard instance;
private final DefaultTableModel pendingTableModel; private final DefaultTableModel pendingTableModel;
private final DefaultTableModel runningTableModel; private final DefaultTableModel runningTableModel;
@ -121,7 +124,8 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
STAGE_TIME(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.StageTime")), STAGE_TIME(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.StageTime")),
STATUS(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.Status")), STATUS(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.Status")),
CASE_DIRECTORY_PATH(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.CaseFolder")), CASE_DIRECTORY_PATH(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.CaseFolder")),
IS_LOCAL_JOB(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.LocalJob")); IS_LOCAL_JOB(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.LocalJob")),
MANIFEST_FILE_PATH(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.ManifestFilePath"));
private final String header; private final String header;
@ -144,7 +148,8 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
STATUS.getColumnHeader(), STATUS.getColumnHeader(),
STAGE_TIME.getColumnHeader(), STAGE_TIME.getColumnHeader(),
CASE_DIRECTORY_PATH.getColumnHeader(), CASE_DIRECTORY_PATH.getColumnHeader(),
IS_LOCAL_JOB.getColumnHeader()}; IS_LOCAL_JOB.getColumnHeader(),
MANIFEST_FILE_PATH.getColumnHeader()};
} }
/** /**
@ -232,6 +237,29 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
return null; return null;
} }
/**
* Gets a status string for a given service.
*
* @param service The service to test.
*
* @return The status string.
*/
private String getServiceStatus(ServicesMonitor.Service service) {
String serviceStatus = NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message.Unknown");
try {
ServicesMonitor servicesMonitor = ServicesMonitor.getInstance();
serviceStatus = servicesMonitor.getServiceStatus(service.toString());
if (serviceStatus.compareTo(ServicesMonitor.ServiceStatus.UP.toString()) == 0) {
serviceStatus = NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message.Up");
} else {
serviceStatus = NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message.Down");
}
} catch (ServicesMonitor.ServicesMonitorException ex) {
SYS_LOGGER.log(Level.SEVERE, String.format("Dashboard error getting service status for %s", service), ex);
}
return serviceStatus;
}
@Override @Override
protected void done() { protected void done() {
tbServicesStatusMessage.setText(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message", caseDatabaseServerStatus, keywordSearchServiceStatus, keywordSearchServiceStatus, messagingStatus)); tbServicesStatusMessage.setText(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message", caseDatabaseServerStatus, keywordSearchServiceStatus, keywordSearchServiceStatus, messagingStatus));
@ -249,30 +277,8 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
} }
/** /**
* Gets a status string for a given service. * Sets up the JTable that presents a view of the system-wide pending jobs
* * queue.
* @param service The service to test.
*
* @return up, down, or unknown
*/
private String getServiceStatus(ServicesMonitor.Service service) {
String serviceStatus = NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message.Unknown");
try {
ServicesMonitor servicesMonitor = ServicesMonitor.getInstance();
serviceStatus = servicesMonitor.getServiceStatus(service.toString());
if (serviceStatus.compareTo(ServicesMonitor.ServiceStatus.UP.toString()) == 0) {
serviceStatus = NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message.Up");
} else {
serviceStatus = NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message.Down");
}
} catch (ServicesMonitor.ServicesMonitorException ex) {
LOGGER.log(Level.SEVERE, String.format("Dashboard error getting service status for %s", service), ex);
}
return serviceStatus;
}
/**
* Sets up the JTable that presents a view of the
*/ */
private void initPendingJobsTable() { private void initPendingJobsTable() {
/* /*
@ -287,6 +293,7 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.CASE_DIRECTORY_PATH.getColumnHeader())); pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.CASE_DIRECTORY_PATH.getColumnHeader()));
pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.IS_LOCAL_JOB.getColumnHeader())); pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.IS_LOCAL_JOB.getColumnHeader()));
pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.STATUS.getColumnHeader())); pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.STATUS.getColumnHeader()));
pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.MANIFEST_FILE_PATH.getColumnHeader()));
/* /*
* Set up a column to display the cases associated with the jobs. * Set up a column to display the cases associated with the jobs.
@ -344,7 +351,8 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
} }
/** /**
* Sets up the header and columns for the running auto ingest jobs table. * Sets up the JTable that presents a view of the system-wide running jobs
* list.
*/ */
private void initRunningJobsTable() { private void initRunningJobsTable() {
/* /*
@ -357,6 +365,7 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.STATUS.getColumnHeader())); runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.STATUS.getColumnHeader()));
runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.CASE_DIRECTORY_PATH.getColumnHeader())); runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.CASE_DIRECTORY_PATH.getColumnHeader()));
runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.IS_LOCAL_JOB.getColumnHeader())); runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.IS_LOCAL_JOB.getColumnHeader()));
runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.MANIFEST_FILE_PATH.getColumnHeader()));
/* /*
* Set up a column to display the cases associated with the jobs. * Set up a column to display the cases associated with the jobs.
@ -438,8 +447,8 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
} }
/** /**
* Sets up the header, columns, and selection listener for the completed * Sets up the JTable that presents a view of the system-wide competed jobs
* auto ingest jobs table. * list.
*/ */
private void initCompletedJobsTable() { private void initCompletedJobsTable() {
/* /*
@ -452,6 +461,7 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.IS_LOCAL_JOB.getColumnHeader())); completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.IS_LOCAL_JOB.getColumnHeader()));
completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.HOST_NAME.getColumnHeader())); completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.HOST_NAME.getColumnHeader()));
completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.CASE_DIRECTORY_PATH.getColumnHeader())); completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.CASE_DIRECTORY_PATH.getColumnHeader()));
completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.MANIFEST_FILE_PATH.getColumnHeader()));
/* /*
* Set up a column to display the cases associated with the jobs. * Set up a column to display the cases associated with the jobs.
@ -558,11 +568,8 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
* @param enable Enable/disable the buttons. * @param enable Enable/disable the buttons.
*/ */
private void enablePendingTableButtons(Boolean enable) { private void enablePendingTableButtons(Boolean enable) {
// RJCTODO: Restore prioritization feature bnPrioritizeCase.setEnabled(enable);
// bnPrioritizeCase.setEnabled(enable); bnPrioritizeJob.setEnabled(enable);
// bnPrioritizeFolder.setEnabled(enable);
bnPrioritizeCase.setEnabled(false);
bnPrioritizeFolder.setEnabled(false);
} }
/** /**
@ -573,12 +580,12 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
private void startUp() { private void startUp() {
/* /*
* Start up the auto ingest manager (AIM). * Starts up the auto ingest manager (AIM).
*/ */
try { try {
manager.startUp(); manager.startUp();
} catch (AutoIngestManager.AutoIngestManagerStartupException ex) { } catch (AutoIngestManager.AutoIngestManagerStartupException ex) {
LOGGER.log(Level.SEVERE, "Dashboard error starting up auto ingest", ex); SYS_LOGGER.log(Level.SEVERE, "Dashboard error starting up auto ingest", ex);
tbStatusMessage.setText(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.AutoIngestStartupError")); tbStatusMessage.setText(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.AutoIngestStartupError"));
manager = null; manager = null;
@ -594,20 +601,6 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
return; return;
} }
/*
* Attempt to connect the AIM to any other auto ingest nodes (AINs) if
* this is a cluster.
*/
try {
manager.establishRemoteCommunications();
} catch (Exception ex) {
LOGGER.log(Level.SEVERE, "Dashboard error establishing remote communications for auto ingest", ex);
JOptionPane.showMessageDialog(this,
NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.AutoIngestStartupWarning.Message"),
NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.AutoIngestStartupWarning.Title"),
JOptionPane.WARNING_MESSAGE);
}
/* /*
* Subscribe to services monitor events. * Subscribe to services monitor events.
*/ */
@ -621,18 +614,10 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
manager.addObserver(this); manager.addObserver(this);
/* /*
* Populate the pending, running, and completed auto ingest tables. * Populate the pending, running, and completed auto ingest job tables.
*/ */
updateExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(UPDATE_TASKS_THREAD_NAME).build()); updateExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(UPDATE_TASKS_THREAD_NAME).build());
updateExecutor.submit(new UpdateAllJobsTablesTask()); updateExecutor.submit(new UpdateAllJobsTablesTask());
/*
* TODO (RC): This does not seem right, given that the AIM does its
* first image folder scan on start up. I think it could actually delay
* the population of the auto ingest job tables, since the table refresh
* and the scan both acquire the job lists manager. Does the AIM send an
* event after a scan? Need to check this.
*/
manager.scanInputDirsNow(); manager.scanInputDirsNow();
bnPause.setEnabled(true); bnPause.setEnabled(true);
@ -743,7 +728,7 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
isPaused = true; isPaused = true;
}); });
break; break;
case PAUSED_FOR_SYSTEM_ERROR: // RJCTODO: Consider making this more detailed again, probably not, too much maintenance and is iverkill case PAUSED_FOR_SYSTEM_ERROR:
EventQueue.invokeLater(() -> { EventQueue.invokeLater(() -> {
tbStatusMessage.setText(org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.PauseDueToSystemError")); tbStatusMessage.setText(org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.PauseDueToSystemError"));
bnOptions.setEnabled(true); bnOptions.setEnabled(true);
@ -777,9 +762,9 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
* running, and that the auto ingest manager will not actually pause until * running, and that the auto ingest manager will not actually pause until
* the current auto ingest job completes. * the current auto ingest job completes.
* *
* @param buttonClicked Is this pause requests in response to a user gesture * @param buttonClicked Is this pause request in response to a user gesture
* or a nofification from the auto ingest manager * or a nofification from the auto ingest manager
* (AMI)? * (AIM)?
*/ */
private void pause(boolean buttonClicked) { private void pause(boolean buttonClicked) {
/** /**
@ -1058,15 +1043,16 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
job.getNodeName(), // HOST_NAME job.getNodeName(), // HOST_NAME
job.getManifest().getDateFileCreated(), // CREATED_TIME job.getManifest().getDateFileCreated(), // CREATED_TIME
job.getStageStartDate(), // STARTED_TIME job.getStageStartDate(), // STARTED_TIME
job.getStageStartDate(), // COMPLETED_TIME job.getCompletedDate(), // COMPLETED_TIME
status.getDescription(), // ACTIVITY status.getDescription(), // ACTIVITY
(null != job.getCaseDirectoryPath()) ? AutoIngestAlertFile.exists(job.getCaseDirectoryPath()) : false, // STATUS // RJCTODO: awkward? job.hasErrors(), // STATUS
((Date.from(Instant.now()).getTime()) - (status.getStartDate().getTime())), // ACTIVITY_TIME ((Date.from(Instant.now()).getTime()) - (status.getStartDate().getTime())), // ACTIVITY_TIME
job.getCaseDirectoryPath(), // CASE_DIRECTORY_PATH // RJCTODO: What about nulls? job.getCaseDirectoryPath(), // CASE_DIRECTORY_PATH
job.getNodeName().equals(LOCAL_HOST_NAME)}); // IS_LOCAL_JOB // RJCTODO: move method that also does this job.getNodeName().equals(LOCAL_HOST_NAME), // IS_LOCAL_JOB
job.getManifest().getFilePath()}); // MANIFEST_FILE_PATH
} }
} catch (Exception ex) { } catch (Exception ex) {
LOGGER.log(Level.SEVERE, "Dashboard error refreshing table", ex); // NON-NLS // RJCTODO: Consider AID log SYS_LOGGER.log(Level.SEVERE, "Dashboard error refreshing table", ex);
} }
} }
@ -1100,10 +1086,11 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
bnShowCaseLog = new javax.swing.JButton(); bnShowCaseLog = new javax.swing.JButton();
tbStatusMessage = new javax.swing.JTextField(); tbStatusMessage = new javax.swing.JTextField();
lbStatus = new javax.swing.JLabel(); lbStatus = new javax.swing.JLabel();
bnPrioritizeFolder = new javax.swing.JButton(); bnPrioritizeJob = new javax.swing.JButton();
lbServicesStatus = new javax.swing.JLabel(); lbServicesStatus = new javax.swing.JLabel();
tbServicesStatusMessage = new javax.swing.JTextField(); tbServicesStatusMessage = new javax.swing.JTextField();
bnOpenLogDir = new javax.swing.JButton(); bnOpenLogDir = new javax.swing.JButton();
bnReprocessJob = new javax.swing.JButton();
pendingTable.setModel(pendingTableModel); pendingTable.setModel(pendingTableModel);
pendingTable.setToolTipText(org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.pendingTable.toolTipText")); // NOI18N pendingTable.setToolTipText(org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.pendingTable.toolTipText")); // NOI18N
@ -1257,11 +1244,11 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
lbStatus.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N lbStatus.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(lbStatus, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.lbStatus.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(lbStatus, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.lbStatus.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(bnPrioritizeFolder, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.bnPrioritizeFolder.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(bnPrioritizeJob, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.bnPrioritizeJob.text")); // NOI18N
bnPrioritizeFolder.setToolTipText(org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.bnPrioritizeFolder.toolTipText")); // NOI18N bnPrioritizeJob.setToolTipText(org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.bnPrioritizeJob.toolTipText")); // NOI18N
bnPrioritizeFolder.addActionListener(new java.awt.event.ActionListener() { bnPrioritizeJob.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
bnPrioritizeFolderActionPerformed(evt); bnPrioritizeJobActionPerformed(evt);
} }
}); });
@ -1280,6 +1267,13 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
} }
}); });
org.openide.awt.Mnemonics.setLocalizedText(bnReprocessJob, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.bnReprocessJob.text")); // NOI18N
bnReprocessJob.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
bnReprocessJobActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
@ -1287,53 +1281,50 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(lbPending, javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(pendingScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 920, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(bnPrioritizeCase, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(bnPrioritizeJob, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(bnPause)
.addComponent(lbPending, javax.swing.GroupLayout.Alignment.LEADING) .addGap(18, 18, 18)
.addComponent(pendingScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 920, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(bnRefresh, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGap(18, 18, 18)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(bnOptions)
.addComponent(bnPrioritizeCase, javax.swing.GroupLayout.PREFERRED_SIZE, 117, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(18, 18, 18)
.addComponent(bnPrioritizeFolder, javax.swing.GroupLayout.PREFERRED_SIZE, 117, javax.swing.GroupLayout.PREFERRED_SIZE))) .addComponent(bnOpenLogDir)
.addGap(18, 18, 18)
.addComponent(bnExit, javax.swing.GroupLayout.PREFERRED_SIZE, 94, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addComponent(runningScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 920, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(50, 50, 50) .addComponent(completedScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 920, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(bnPause) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGap(50, 50, 50) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(bnRefresh, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(bnCancelJob, javax.swing.GroupLayout.DEFAULT_SIZE, 117, Short.MAX_VALUE)
.addGap(50, 50, 50) .addComponent(bnShowProgress, javax.swing.GroupLayout.DEFAULT_SIZE, 116, Short.MAX_VALUE)
.addComponent(bnOptions) .addComponent(bnCancelModule, javax.swing.GroupLayout.DEFAULT_SIZE, 117, Short.MAX_VALUE)
.addGap(50, 50, 50) .addComponent(bnDeleteCase, javax.swing.GroupLayout.DEFAULT_SIZE, 117, Short.MAX_VALUE)
.addComponent(bnOpenLogDir) .addComponent(bnShowCaseLog, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(50, 50, 50) .addComponent(bnReprocessJob, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
.addComponent(bnExit, javax.swing.GroupLayout.PREFERRED_SIZE, 94, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(layout.createSequentialGroup()
.addGroup(layout.createSequentialGroup() .addComponent(lbStatus)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(runningScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 920, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(tbStatusMessage, javax.swing.GroupLayout.PREFERRED_SIZE, 861, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(completedScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 920, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(lbCompleted)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(lbRunning)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addGroup(layout.createSequentialGroup()
.addComponent(bnCancelJob, javax.swing.GroupLayout.DEFAULT_SIZE, 117, Short.MAX_VALUE) .addComponent(lbServicesStatus)
.addComponent(bnShowProgress, javax.swing.GroupLayout.DEFAULT_SIZE, 116, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(bnCancelModule, javax.swing.GroupLayout.DEFAULT_SIZE, 117, Short.MAX_VALUE) .addComponent(tbServicesStatusMessage, javax.swing.GroupLayout.PREFERRED_SIZE, 861, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addComponent(bnDeleteCase, javax.swing.GroupLayout.DEFAULT_SIZE, 117, Short.MAX_VALUE) .addGap(0, 0, Short.MAX_VALUE)))
.addComponent(bnShowCaseLog, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) .addContainerGap())
.addGroup(layout.createSequentialGroup()
.addComponent(lbStatus)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(tbStatusMessage, javax.swing.GroupLayout.PREFERRED_SIZE, 861, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(lbCompleted)
.addComponent(lbRunning))
.addGap(0, 0, Short.MAX_VALUE)))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addComponent(lbServicesStatus)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(tbServicesStatusMessage, javax.swing.GroupLayout.PREFERRED_SIZE, 861, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
); );
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {bnCancelJob, bnCancelModule, bnDeleteCase, bnExit, bnOpenLogDir, bnOptions, bnPause, bnRefresh, bnShowProgress}); layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {bnCancelJob, bnCancelModule, bnDeleteCase, bnExit, bnOpenLogDir, bnOptions, bnPause, bnRefresh, bnShowProgress});
@ -1358,7 +1349,7 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
.addGap(82, 82, 82) .addGap(82, 82, 82)
.addComponent(bnPrioritizeCase) .addComponent(bnPrioritizeCase)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(bnPrioritizeFolder))) .addComponent(bnPrioritizeJob)))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lbRunning) .addComponent(lbRunning)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@ -1374,9 +1365,11 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
.addComponent(runningScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 133, javax.swing.GroupLayout.PREFERRED_SIZE))) .addComponent(runningScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 133, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(102, 102, 102) .addGap(68, 68, 68)
.addComponent(bnReprocessJob)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(bnDeleteCase) .addComponent(bnDeleteCase)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(bnShowCaseLog)) .addComponent(bnShowCaseLog))
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
@ -1419,55 +1412,47 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
* @param evt The button click event. * @param evt The button click event.
*/ */
private void bnDeleteCaseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnDeleteCaseActionPerformed private void bnDeleteCaseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnDeleteCaseActionPerformed
// RJCTODO: Re-implement if (completedTableModel.getRowCount() < 0 || completedTable.getSelectedRow() < 0) {
// if (completedTableModel.getRowCount() < 0 || completedTable.getSelectedRow() < 0) { return;
// return; }
// }
// String caseName = (String) completedTable.getValueAt(completedTable.getSelectedRow(), JobsTableModelColumns.CASE.ordinal());
// String caseName = (String) completedTable.getValueAt(completedTable.getSelectedRow(), JobsTableModelColumns.CASE.ordinal()); Object[] options = {
// Object[] options = { org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "ConfirmationDialog.Delete"),
// org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "ConfirmationDialog.Delete"), org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "ConfirmationDialog.DoNotDelete")
// org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "ConfirmationDialog.DoNotDelete") };
// }; Object[] msgContent = {org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "ConfirmationDialog.DeleteAreYouSure") + "\"" + caseName + "\"?"};
// int reply = JOptionPane.showOptionDialog(this,
// // Add checkbox to allow user to delete images in input folder as well msgContent,
// JCheckBox deleteInputChk = new JCheckBox("Delete input images for this case in shared input folder"); org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "ConfirmationDialog.ConfirmDeletionHeader"),
// Object[] msgContent = {org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "ConfirmationDialog.DeleteAreYouSure") + "\"" + caseName + "\"?", deleteInputChk}; JOptionPane.DEFAULT_OPTION,
// int reply = JOptionPane.showOptionDialog(this, JOptionPane.WARNING_MESSAGE,
// msgContent, null,
// org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "ConfirmationDialog.ConfirmDeletionHeader"), options,
// JOptionPane.DEFAULT_OPTION, options[JOptionPane.NO_OPTION]);
// JOptionPane.WARNING_MESSAGE, if (reply == JOptionPane.YES_OPTION) {
// null, bnDeleteCase.setEnabled(false);
// options, bnShowCaseLog.setEnabled(false);
// options[JOptionPane.NO_OPTION]); if (completedTableModel.getRowCount() > 0 && completedTable.getSelectedRow() >= 0) {
// if (reply == JOptionPane.YES_OPTION) { Path caseDirectoryPath = (Path) completedTableModel.getValueAt(completedTable.getSelectedRow(), JobsTableModelColumns.CASE_DIRECTORY_PATH.ordinal());
// bnDeleteCase.setEnabled(false); completedTable.clearSelection();
// bnShowCaseLog.setEnabled(false); this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// if (completedTableModel.getRowCount() > 0 && completedTable.getSelectedRow() >= 0) { CaseDeletionResult result = manager.deleteCase(caseName, caseDirectoryPath);
// String caseOutputFolderPath = completedTableModel.getValueAt(completedTable.getSelectedRow(), JobsTableModelColumns.CASE_FOLDER.ordinal()).toString(); this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
// String caseAutFilePath = completedTableModel.getValueAt(completedTable.getSelectedRow(), JobsTableModelColumns.CASE.ordinal()).toString() + CaseMetadata.getFileExtension(); if (CaseDeletionResult.FAILED == result) {
// completedTable.clearSelection(); JOptionPane.showMessageDialog(this,
// this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); String.format("Could not delete case %s. It may be in in use.", caseName),
// AutoIngestManager.CaseDeletionResult deletionResult = manager.deleteCase(Paths.get(caseOutputFolderPath), deleteInputChk.isSelected(), caseAutFilePath); org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.DeletionFailed"),
// this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); JOptionPane.INFORMATION_MESSAGE);
// if (deletionResult.getCaseDeletionStatus() == AutoIngestManager.CaseDeletionResult.Status.FAILED) { } else if (CaseDeletionResult.PARTIALLY_DELETED == result) {
// JOptionPane.showMessageDialog(this, "Could not delete case " + caseName + " because it is in use", JOptionPane.showMessageDialog(this,
// org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.DeletionFailed"), JOptionPane.INFORMATION_MESSAGE); String.format("Could not delete case %s. See system log for details.", caseName),
// } else if (deletionResult.getCaseDeletionStatus() == AutoIngestManager.CaseDeletionResult.Status.PARTIALLY_COMPLETED) { org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.DeletionFailed"),
// String str = "Deleted case \"" + caseName + "\", but not all files could be deleted.\nTo delete these files, stop automated ingest and delete \n" JOptionPane.INFORMATION_MESSAGE);
// + caseOutputFolderPath + "\nand \n" + deletionResult.getCaseImageFolderPath() + "\nif present."; }
// JOptionPane.showMessageDialog(this, str, updateExecutor.submit(new UpdateAllJobsTablesTask());
// org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.DeletionFailed"), JOptionPane.INFORMATION_MESSAGE); }
// } }
// /**
// * Need to update both the pending jobs table and the completed
// * jobs table since pending jobs for the deleted case are also
// * deleted.
// */
// updateExecutor.submit(new UpdateAllJobsTablesTask());
// }
// }
}//GEN-LAST:event_bnDeleteCaseActionPerformed }//GEN-LAST:event_bnDeleteCaseActionPerformed
/** /**
@ -1583,22 +1568,15 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
* @param evt The button click event. * @param evt The button click event.
*/ */
private void bnPrioritizeCaseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnPrioritizeCaseActionPerformed private void bnPrioritizeCaseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnPrioritizeCaseActionPerformed
// RJCTODO: Re-implement if (pendingTableModel.getRowCount() > 0 && pendingTable.getSelectedRow() >= 0) {
// if (pendingTableModel.getRowCount() > 0 && pendingTable.getSelectedRow() >= 0) { this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); String caseName = (pendingTableModel.getValueAt(pendingTable.getSelectedRow(), JobsTableModelColumns.CASE.ordinal())).toString();
// String caseName = (pendingTableModel.getValueAt(pendingTable.getSelectedRow(), JobsTableModelColumns.CASE.ordinal())).toString(); List<AutoIngestJob> prioritizedQueue = manager.prioritizeCase(caseName);
// try { refreshTable(prioritizedQueue, pendingTableModel, null);
// List<AutoIngestJob> prioritizedQueue = manager.prioritizeCase(caseName); pendingTable.clearSelection();
// refreshTable(prioritizedQueue, pendingTableModel, null); enablePendingTableButtons(false);
// } catch (IOException ex) { AutoIngestDashboard.this.setCursor(Cursor.getDefaultCursor());
// logger.log(Level.SEVERE, String.format("Error while prioritizing case %s", caseName), ex); }
// MessageNotifyUtil.Message.error("An error occurred while prioritizing the case.");
// } finally {
// pendingTable.clearSelection();
// enablePendingTableButtons(false);
// AutoIngestDashboard.this.setCursor(Cursor.getDefaultCursor());
// }
// }
}//GEN-LAST:event_bnPrioritizeCaseActionPerformed }//GEN-LAST:event_bnPrioritizeCaseActionPerformed
/** /**
@ -1612,16 +1590,20 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
int selectedRow = completedTable.getSelectedRow(); int selectedRow = completedTable.getSelectedRow();
if (selectedRow != -1) { if (selectedRow != -1) {
Path caseDirectoryPath = (Path) completedTableModel.getValueAt(selectedRow, JobsTableModelColumns.CASE_DIRECTORY_PATH.ordinal()); Path caseDirectoryPath = (Path) completedTableModel.getValueAt(selectedRow, JobsTableModelColumns.CASE_DIRECTORY_PATH.ordinal());
Path pathToLog = AutoIngestJobLogger.getLogPath(caseDirectoryPath); if (null != caseDirectoryPath) {
if (pathToLog.toFile().exists()) { Path pathToLog = AutoIngestJobLogger.getLogPath(caseDirectoryPath);
Desktop.getDesktop().edit(pathToLog.toFile()); if (pathToLog.toFile().exists()) {
Desktop.getDesktop().edit(pathToLog.toFile());
} else {
JOptionPane.showMessageDialog(this, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.ShowLogFailed.Message"),
org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.ShowLogFailed.Title"), JOptionPane.ERROR_MESSAGE);
}
} else { } else {
JOptionPane.showMessageDialog(this, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.ShowLogFailed.Message"), MessageNotifyUtil.Message.warn("The case directory for this job has been deleted.");
org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.ShowLogFailed.Title"), JOptionPane.ERROR_MESSAGE);
} }
} }
} catch (IOException ex) { } catch (IOException ex) {
LOGGER.log(Level.SEVERE, "Dashboard error attempting to display case auto ingest log", ex); SYS_LOGGER.log(Level.SEVERE, "Dashboard error attempting to display case auto ingest log", ex);
Object[] options = {org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "DisplayLogDialog.okay")}; Object[] options = {org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "DisplayLogDialog.okay")};
JOptionPane.showOptionDialog(this, JOptionPane.showOptionDialog(this,
org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "DisplayLogDialog.cannotFindLog"), org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "DisplayLogDialog.cannotFindLog"),
@ -1634,25 +1616,17 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
} }
}//GEN-LAST:event_bnShowCaseLogActionPerformed }//GEN-LAST:event_bnShowCaseLogActionPerformed
private void bnPrioritizeFolderActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnPrioritizeFolderActionPerformed private void bnPrioritizeJobActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnPrioritizeJobActionPerformed
// RJCTODO: Re-implement if (pendingTableModel.getRowCount() > 0 && pendingTable.getSelectedRow() >= 0) {
// if (pendingTableModel.getRowCount() > 0 && pendingTable.getSelectedRow() >= 0) { this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); Path manifestFilePath = (Path) (pendingTableModel.getValueAt(pendingTable.getSelectedRow(), JobsTableModelColumns.MANIFEST_FILE_PATH.ordinal()));
// String caseName = (pendingTableModel.getValueAt(pendingTable.getSelectedRow(), JobsTableModelColumns.CASE.ordinal())).toString(); List<AutoIngestJob> prioritizedQueue = manager.prioritizeJob(manifestFilePath);
// String folderName = (pendingTableModel.getValueAt(pendingTable.getSelectedRow(), JobsTableModelColumns.DATA_SOURCE.ordinal())).toString(); refreshTable(prioritizedQueue, pendingTableModel, null);
// try { pendingTable.clearSelection();
// List<AutoIngestJob> prioritizedQueue = manager.prioritizeJob(caseName, folderName); enablePendingTableButtons(false);
// refreshTable(prioritizedQueue, pendingTableModel, null); AutoIngestDashboard.this.setCursor(Cursor.getDefaultCursor());
// } catch (IOException ex) { }
// logger.log(Level.SEVERE, String.format("Error while prioritizing folder %s", folderName), ex); }//GEN-LAST:event_bnPrioritizeJobActionPerformed
// MessageNotifyUtil.Message.error("An error occurred while prioritizing the folder.");
// } finally {
// pendingTable.clearSelection();
// enablePendingTableButtons(false);
// AutoIngestDashboard.this.setCursor(Cursor.getDefaultCursor());
// }
// }
}//GEN-LAST:event_bnPrioritizeFolderActionPerformed
private void bnOpenLogDirActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOpenLogDirActionPerformed private void bnOpenLogDirActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOpenLogDirActionPerformed
Path logDirPath = Paths.get(PlatformUtil.getUserDirectory().getAbsolutePath(), "var", "log"); Path logDirPath = Paths.get(PlatformUtil.getUserDirectory().getAbsolutePath(), "var", "log");
@ -1661,11 +1635,24 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
Desktop.getDesktop().open(logDir); Desktop.getDesktop().open(logDir);
} catch (IOException ex) { } catch (IOException ex) {
DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message( DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(
String.format("Unable to open log directory %s:\n%s", logDirPath, ex.getLocalizedMessage()), // RJCTODO: Localize String.format("Unable to open log directory %s:\n%s", logDirPath, ex.getLocalizedMessage()),
NotifyDescriptor.ERROR_MESSAGE)); NotifyDescriptor.ERROR_MESSAGE));
} }
}//GEN-LAST:event_bnOpenLogDirActionPerformed }//GEN-LAST:event_bnOpenLogDirActionPerformed
private void bnReprocessJobActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnReprocessJobActionPerformed
if (completedTableModel.getRowCount() < 0 || completedTable.getSelectedRow() < 0) {
return;
}
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
Path manifestPath = (Path) completedTableModel.getValueAt(completedTable.getSelectedRow(), JobsTableModelColumns.MANIFEST_FILE_PATH.ordinal());
JobsSnapshot jobsSnapshot = manager.reprocessJob(manifestPath);
refreshTable(jobsSnapshot.getCompletedJobs(), completedTableModel, null);
refreshTable(jobsSnapshot.getPendingJobs(), pendingTableModel, null);
refreshTable(jobsSnapshot.getRunningJobs(), runningTableModel, null);
AutoIngestDashboard.this.setCursor(Cursor.getDefaultCursor());
}//GEN-LAST:event_bnReprocessJobActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton bnCancelJob; private javax.swing.JButton bnCancelJob;
private javax.swing.JButton bnCancelModule; private javax.swing.JButton bnCancelModule;
@ -1675,8 +1662,9 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
private javax.swing.JButton bnOptions; private javax.swing.JButton bnOptions;
private javax.swing.JButton bnPause; private javax.swing.JButton bnPause;
private javax.swing.JButton bnPrioritizeCase; private javax.swing.JButton bnPrioritizeCase;
private javax.swing.JButton bnPrioritizeFolder; private javax.swing.JButton bnPrioritizeJob;
private javax.swing.JButton bnRefresh; private javax.swing.JButton bnRefresh;
private javax.swing.JButton bnReprocessJob;
private javax.swing.JButton bnShowCaseLog; private javax.swing.JButton bnShowCaseLog;
private javax.swing.JButton bnShowProgress; private javax.swing.JButton bnShowProgress;
private javax.swing.JScrollPane completedScrollPane; private javax.swing.JScrollPane completedScrollPane;

View File

@ -27,13 +27,13 @@ import java.util.Date;
import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.NetworkUtils; import org.sleuthkit.autopsy.coreutils.NetworkUtils;
import org.sleuthkit.autopsy.ingest.IngestJob; import org.sleuthkit.autopsy.ingest.IngestJob;
/** /**
* An automated ingest job auto ingest jobs associated with a manifest file. A * An automated ingest job for a manifest. The manifest specifies a co-located
* manifest file specifies a co-located data source and a case to which the data * data source and a case to which the data source is to be added.
* source is to be added.
*/ */
@ThreadSafe @ThreadSafe
public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializable { public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializable {
@ -51,14 +51,38 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
@GuardedBy("this") @GuardedBy("this")
private Date stageStartDate; private Date stageStartDate;
@GuardedBy("this") @GuardedBy("this")
transient private DataSourceProcessor dataSourceProcessor;
@GuardedBy("this")
transient private IngestJob ingestJob; transient private IngestJob ingestJob;
@GuardedBy("this")
transient private boolean cancelled; // RJCTODO: Document
@GuardedBy("this")
transient private boolean completed; // RJCTODO: Document
@GuardedBy("this")
private Date completedDate;
@GuardedBy("this")
private boolean errorsOccurred;
/** /**
* RJCTODO * 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.
* *
* @param manifest * @param manifest The manifest
* @param caseDirectoryPath The path to the case directory for the job, may
* be null.
* @param priority The priority of the job. The higher the number,
* the higher the priority.
* @param nodeName If the job is in progress, the node doing the
* processing, otherwise the locla host.
* @param stage The processing stage for display purposes.
* @param completedDate The date when the job was completed. Use the
* epoch (January 1, 1970, 00:00:00 GMT) to
* indicate the the job is not completed, i.e., new
* Date(0L).
*/ */
AutoIngestJob(Manifest manifest, Path caseDirectoryPath, int priority, String nodeName, Stage stage) { // RJCTODO: The null case directory is error-prone and the nodeName is confusing.
AutoIngestJob(Manifest manifest, Path caseDirectoryPath, int priority, String nodeName, Stage stage, Date completedDate, boolean errorsOccurred) {
this.manifest = manifest; this.manifest = manifest;
if (null != caseDirectoryPath) { if (null != caseDirectoryPath) {
this.caseDirectoryPath = caseDirectoryPath.toString(); this.caseDirectoryPath = caseDirectoryPath.toString();
@ -69,12 +93,14 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
this.nodeName = nodeName; this.nodeName = nodeName;
this.stage = stage; this.stage = stage;
this.stageStartDate = manifest.getDateFileCreated(); this.stageStartDate = manifest.getDateFileCreated();
this.completedDate = completedDate;
this.errorsOccurred = errorsOccurred;
} }
/** /**
* RJCTODO * Gets the auto ingest jobmanifest.
* *
* @return * @return The manifest.
*/ */
Manifest getManifest() { Manifest getManifest() {
return this.manifest; return this.manifest;
@ -86,6 +112,7 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
* *
* @return True or false * @return True or false
*/ */
// RJCTODO: Use this or lose this
synchronized boolean hasCaseDirectoryPath() { synchronized boolean hasCaseDirectoryPath() {
return (false == this.caseDirectoryPath.isEmpty()); return (false == this.caseDirectoryPath.isEmpty());
} }
@ -114,14 +141,21 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
} }
} }
/**
* Sets the priority of the job. A higher number indicates a higher
* priority.
*
* @param priority The priority.
*/
synchronized void setPriority(Integer priority) { synchronized void setPriority(Integer priority) {
this.priority = priority; this.priority = priority;
} }
/** /**
* RJCTODO * Gets the priority of the job. A higher number indicates a higher
* priority.
* *
* @return * @return The priority.
*/ */
synchronized Integer getPriority() { synchronized Integer getPriority() {
return this.priority; return this.priority;
@ -143,11 +177,7 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
* @param stateStartedDate * @param stateStartedDate
*/ */
synchronized void setStage(Stage newState, Date stateStartedDate) { synchronized void setStage(Stage newState, Date stateStartedDate) {
if (Stage.CANCELLED == this.stage && Stage.COMPLETED != newState) { if (Stage.CANCELLING == this.stage && Stage.COMPLETED != newState) {
/**
* Do not overwrite canceling status with anything other than
* completed status.
*/
return; return;
} }
this.stage = newState; this.stage = newState;
@ -214,10 +244,12 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
return new StageDetails(description, startDate); return new StageDetails(description, startDate);
} }
synchronized void setDataSourceProcessor(DataSourceProcessor dataSourceProcessor) {
this.dataSourceProcessor = dataSourceProcessor;
}
/** /**
* RJCTODO * RJCTODO
*
* @param ingestStatus
*/ */
// RJCTODO: Consider moving this class into AIM and making this private // RJCTODO: Consider moving this class into AIM and making this private
synchronized void setIngestJob(IngestJob ingestJob) { synchronized void setIngestJob(IngestJob ingestJob) {
@ -226,8 +258,6 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
/** /**
* RJCTODO * RJCTODO
*
* @return
*/ */
// RJCTODO: Consider moving this class into AIM and making this private. // RJCTODO: Consider moving this class into AIM and making this private.
// Or move the AID into a separate package. Or do not worry about it. // Or move the AID into a separate package. Or do not worry about it.
@ -235,6 +265,83 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
return this.ingestJob; return this.ingestJob;
} }
/**
* RJCTODO
*/
synchronized void cancel() {
setStage(Stage.CANCELLING);
cancelled = true;
errorsOccurred = true;
if (null != dataSourceProcessor) {
dataSourceProcessor.cancel();
}
if (null != ingestJob) {
ingestJob.cancel(IngestJob.CancellationReason.USER_CANCELLED);
}
}
/**
* RJCTODO
*/
synchronized boolean isCancelled() {
return cancelled;
}
/**
* RJCTODO
*/
synchronized void setCompleted() {
setStage(Stage.COMPLETED);
completed = true;
}
/**
* RJCTODO
*
* @return
*/
synchronized boolean isCompleted() {
return completed;
}
/**
* Sets the date the job was completed, with or without cancellation or
* errors.
*
* @param completedDate The completion date.
*/
synchronized void setCompletedDate(Date completedDate) {
this.completedDate = completedDate;
}
/**
* Gets the date the job was completed, with or without cancellation or
* errors.
*
* @return True or false.
*/
synchronized Date getCompletedDate() {
return completedDate; // RJCTODO: Consider returning null if == 0 (epoch)
}
/**
* Sets whether or not erros occurred during the processing of the job.
*
* @param errorsOccurred True or false;
*/
synchronized void setErrorsOccurred(boolean errorsOccurred) {
this.errorsOccurred = errorsOccurred;
}
/**
* Queries whether or not erros occurred during the processing of the job.
*
* @return True or false.
*/
synchronized boolean hasErrors() {
return this.errorsOccurred;
}
/** /**
* RJCTODO Gets name of the node associated with the job, possibly a remote * RJCTODO Gets name of the node associated with the job, possibly a remote
* hose if the job is in progress. * hose if the job is in progress.
@ -309,30 +416,21 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
} }
/** /**
* Custom comparator that sorts the pending list with prioritized cases * Comparator that sorts auto ingest jobs by priority in descending order.
* first, then nonprioritized cases. Prioritized cases are last in, first
* out. Nonprioritized cases are first in, first out. Prioritized times are
* from the creation time of the "prioritized" state file. Non prioritized
* are from the folder creation time.
*/ */
public static class PriorityComparator implements Comparator<AutoIngestJob> { public static class PriorityComparator implements Comparator<AutoIngestJob> {
/** /**
* RJCTODO * RJCTODO
* *
* @param o1 * @param job
* @param o2 * @param anotherJob
* *
* @return * @return
*/ */
@Override @Override
public int compare(AutoIngestJob o1, AutoIngestJob o2) { public int compare(AutoIngestJob job, AutoIngestJob anotherJob) {
Integer result = o1.getPriority().compareTo(o2.getPriority()); return -(job.getPriority().compareTo(anotherJob.getPriority()));
if (0 != result) {
return result;
} else {
return o1.getManifest().getDateFileCreated().compareTo(o2.getManifest().getDateFileCreated());
}
} }
} }
@ -364,6 +462,10 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
} }
} }
/**
* RJCTODO
*/
// RJCTODO: Combine this enum with StageDetails to make a single class.
enum Stage { enum Stage {
PENDING("Pending"), PENDING("Pending"),
@ -377,8 +479,7 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
ANALYZING_FILES("Analyzing files"), ANALYZING_FILES("Analyzing files"),
EXPORTING_FILES("Exporting files"), EXPORTING_FILES("Exporting files"),
CANCELLING_MODULE("Cancelling module"), CANCELLING_MODULE("Cancelling module"),
CANCELLED("Cancelled"), CANCELLING("Cancelling"),
INTERRUPTED("Cancelled"),
COMPLETED("Completed"); COMPLETED("Completed");
private final String displayText; private final String displayText;

View File

@ -28,7 +28,6 @@ import java.nio.file.Paths;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.Instant; import java.time.Instant;
import java.util.Date; import java.util.Date;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.NetworkUtils; import org.sleuthkit.autopsy.coreutils.NetworkUtils;
import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationService;
import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationService.Lock; import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationService.Lock;
@ -59,6 +58,7 @@ final class AutoIngestJobLogger {
private static final String DATE_FORMAT_STRING = "yyyy/MM/dd HH:mm:ss"; private static final String DATE_FORMAT_STRING = "yyyy/MM/dd HH:mm:ss";
private static final SimpleDateFormat logDateFormat = new SimpleDateFormat(DATE_FORMAT_STRING); private static final SimpleDateFormat logDateFormat = new SimpleDateFormat(DATE_FORMAT_STRING);
private final Path manifestPath; private final Path manifestPath;
private final String manifestFileName;
private final String dataSourceFileName; private final String dataSourceFileName;
private final Path caseDirectoryPath; private final Path caseDirectoryPath;
private final String hostName; private final String hostName;
@ -73,7 +73,7 @@ final class AutoIngestJobLogger {
*/ */
INFO, INFO,
/** /**
* Qualifies a log message about an unexpected event or condition during * Qualifies a log message about an unexpected event or condtion during
* automated ingest processing. * automated ingest processing.
*/ */
WARNING, WARNING,
@ -111,6 +111,7 @@ final class AutoIngestJobLogger {
*/ */
AutoIngestJobLogger(Path manifestPath, String dataSourceFileName, Path caseDirectoryPath) { AutoIngestJobLogger(Path manifestPath, String dataSourceFileName, Path caseDirectoryPath) {
this.manifestPath = manifestPath; this.manifestPath = manifestPath;
manifestFileName = manifestPath.getFileName().toString();
this.dataSourceFileName = dataSourceFileName; this.dataSourceFileName = dataSourceFileName;
this.caseDirectoryPath = caseDirectoryPath; this.caseDirectoryPath = caseDirectoryPath;
hostName = NetworkUtils.getLocalHostName(); hostName = NetworkUtils.getLocalHostName();
@ -119,151 +120,146 @@ final class AutoIngestJobLogger {
/** /**
* Logs the cancellation of an auto ingest job during processing. * Logs the cancellation of an auto ingest job during processing.
* *
* @throws InterruptedException if interrupted while blocked waiting to * @throws AutoIngestJobLoggerException if there is an error writing the log
* acquire an exclusive lock on the log file. * message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/ */
void logJobCancelled() throws InterruptedException { void logJobCancelled() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.WARNING, "Auto ingest job cancelled during processing"); log(MessageCategory.WARNING, "Auto ingest job cancelled during processing");
} }
/** /**
* Logs the presence of a manifest file without a matching data source. * Logs the presence of a manifest file without a matching data source.
* *
* @throws InterruptedException if interrupted while blocked waiting to * @throws AutoIngestJobLoggerException if there is an error writing the log
* acquire an exclusive lock on the log file. * message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/ */
void logMissingDataSource() throws InterruptedException { void logMissingDataSource() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, "Data source file not found"); // RJCTODO: Check for this log(MessageCategory.ERROR, "Data source file not found");
} }
/** /**
* Logs an error identifying the type of a data source. * Logs a failure to extract an archived data source.
* *
* @param dataSource The data source. * @throws AutoIngestJobLoggerException if there is an error writing the log
* @param errorMessage The error. * message.
* * @throws InterruptedException if interrupted while blocked waiting
* @throws InterruptedException if interrupted while blocked waiting to * to acquire an exclusive lock on the
* acquire an exclusive lock on the log file * log file.
* path.
*/ */
void logDataSourceTypeIdError(String errorMessage) throws InterruptedException { void logFailedToExtractDataSource() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, String.format("Error identifying data source type: %s", errorMessage)); log(MessageCategory.ERROR, "Failed to extract data source from archive");
} }
/** /**
* RJCTODO * Logs a failure to parse a Cellebrite logical report data source.
* *
* @throws InterruptedException if interrupted while blocked waiting to * @throws AutoIngestJobLoggerException if there is an error writing the log
* acquire an exclusive lock on the log file. * message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/ */
void logFailedToIdentifyDataSource() throws InterruptedException { void logFailedToParseLogicalReportDataSource() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, String.format("Failed to identifying data source type, cannot ingest")); log(MessageCategory.ERROR, "Failed to parse Cellebrite logical report data source");
} }
/** /**
* RJCTODO * Logs a failure to identify an image data source as either a drive or
* phone image.
* *
* @param dataSourceType * @throws AutoIngestJobLoggerException if there is an error writing the log
* * message.
* @throws InterruptedException if interrupted while blocked waiting to * @throws InterruptedException if interrupted while blocked waiting
* acquire an exclusive lock on the log file. * to acquire an exclusive lock on the
* log file.
*/ */
void logDataSourceTypeId(String dataSourceType) throws InterruptedException { void logFailedToIdentifyImageDataSource() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.INFO, String.format("Identified data source as %s", dataSourceType)); log(MessageCategory.ERROR, String.format("Failed to identifying data source as drive or phone image"));
} }
/** /**
* Logs cancellation of the addition of a data source to the case database. * Logs cancellation of the addition of a data source to the case database.
* *
* @param dataSourceType The data source type. * @throws AutoIngestJobLoggerException if there is an error writing the log
* * message.
* @throws InterruptedException if interrupted while blocked waiting to * @throws InterruptedException if interrupted while blocked waiting
* acquire an exclusive lock on the log file. * to acquire an exclusive lock on the
* log file.
*/ */
void logDataSourceProcessorCancelled(String dataSourceType) throws InterruptedException { // RJCTODO: Is this used now? void logDataSourceProcessorCancelled() throws AutoIngestJobLoggerException, InterruptedException { // RJCTODO: Is this used now?
log(MessageCategory.WARNING, String.format("Cancelled adding data source to case as %s", dataSourceType)); log(MessageCategory.WARNING, "Cancelled adding data source to case");
} }
/** /**
* Logs the addition of a data source to the case database. * Logs the addition of a data source to the case database.
* *
* @param dataSourceType The data source type. * @throws AutoIngestJobLoggerException if there is an error writing the log
* * message.
* @throws InterruptedException if interrupted while blocked waiting to * @throws InterruptedException if interrupted while blocked waiting
* acquire an exclusive lock on the log file. * to acquire an exclusive lock on the
* log file.
*/ */
void logDataSourceAdded(String dataSourceType) throws InterruptedException { void logDataSourceAdded() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.INFO, String.format("Added data source to case as %s", dataSourceType)); log(MessageCategory.INFO, "Added data source to case");
} }
/** /**
* Logs a critical error reported by a data source processor when adding a * Logs an failure adding a data source to the case database.
* data source to the case database.
* *
* @param dataSourceType The data source type. * @throws AutoIngestJobLoggerException if there is an error writing the log
* @param errorMessage The error message. * message.
* * @throws InterruptedException if interrupted while blocked waiting
* @throws InterruptedException if interrupted while blocked waiting to * to acquire an exclusive lock on the
* acquire an exclusive lock on the log file. * log file.
*/ */
void logDataSourceProcessorError(String dataSourceType, String errorMessage) throws InterruptedException { void logFailedToAddDataSource() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, String.format("Critical error adding data source to case as %s: %s", dataSourceType, errorMessage)); log(MessageCategory.ERROR, "Failed to add data source to case");
} }
/** /**
* Logs a non-critical error reported by a data source processor when adding * Logs failure of a data source to produce content.
* a data source to the case database.
* *
* @param dataSourceType The data source type. * @throws AutoIngestJobLoggerException if there is an error writing the log
* @param errorMessage The error message. * message.
* * @throws InterruptedException if interrupted while blocked waiting
* @throws InterruptedException if interrupted while blocked waiting to * to acquire an exclusive lock on the
* acquire an exclusive lock on the log file. * log file.
*/ */
void logDataSourceProcessorWarning(String dataSourceType, String errorMessage) throws InterruptedException { void logNoDataSourceContent() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.WARNING, String.format("Critical error adding data source to case as %s: %s", dataSourceType, errorMessage)); log(MessageCategory.ERROR, "Data source failed to produce content");
} }
/** /**
* Logs an error adding a data source to the case database. * Logs failure to analyze a data source due to ingest job settings errors.
* *
* @param dataSourceType The data source type. * @throws AutoIngestJobLoggerException if there is an error writing the log
* @param dataSource The data source. * message.
* * @throws InterruptedException if interrupted while blocked waiting
* @throws InterruptedException if interrupted while blocked waiting to * to acquire an exclusive lock on the
* acquire an exclusive lock on the log file. * log file.
*/ */
void logFailedToAddDataSource(String dataSourceType) throws InterruptedException { // RJCTODO: Why this and logDataSourceProcessorError? Bd handling of critical vs. non-critical? void logIngestJobSettingsErrors() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, String.format("Failed to add data source to case as %s", dataSourceType)); log(MessageCategory.ERROR, "Failed to analyze data source due to settings errors");
}
/**
* RJCTODO: Document and homogenize messages
*
* @param errors
*
* @throws InterruptedException if interrupted while blocked waiting to
* acquire an exclusive lock on the log file.
*/
void logIngestJobSettingsErrors(List<String> errors) throws InterruptedException {
for (String error : errors) {
log(MessageCategory.ERROR, String.format("Settings error, analysis of data source by ingest modules not started: %s", error));
}
} }
/** /**
* Logs failure to analyze a data source due to ingest module startup * Logs failure to analyze a data source due to ingest module startup
* errors. * errors.
* *
* @param errors The ingest module errors. * @throws AutoIngestJobLoggerException if there is an error writing the log
* * message.
* @throws InterruptedException if interrupted while blocked waiting to * @throws InterruptedException if interrupted while blocked waiting
* acquire an exclusive lock on the log file. * to acquire an exclusive lock on the
* log file.
*/ */
void logIngestModuleStartupErrors(List<IngestModuleError> errors) throws InterruptedException { void logIngestModuleStartupErrors() throws AutoIngestJobLoggerException, InterruptedException {
for (IngestModuleError error : errors) { log(MessageCategory.ERROR, "Failed to analyze data source due to ingest module startup errors");
log(MessageCategory.ERROR, String.format("Analysis of data source by ingest modules not started, %s startup error: %s", error.getModuleDisplayName(), error.getThrowable().getLocalizedMessage()));
}
} }
/** /**
@ -272,21 +268,27 @@ final class AutoIngestJobLogger {
* *
* @param ex The ingest manager exception. * @param ex The ingest manager exception.
* *
* @throws InterruptedException if interrupted while blocked waiting to * @throws AutoIngestJobLoggerException if there is an error writing the log
* acquire an exclusive lock on the log file. * message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/ */
void logAnalysisStartupError(IngestManagerException ex) throws InterruptedException { void logAnalysisStartupError() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, String.format("Analysis of data source by ingest modules not started: %s", ex.getLocalizedMessage())); log(MessageCategory.ERROR, "Failed to analyze data source due to ingest job startup error");
} }
/** /**
* Logs the completion of analysis of a data source by the ingest modules. * Logs the completion of analysis of a data source by the ingest modules.
* *
* @throws InterruptedException if interrupted while blocked waiting to * @throws AutoIngestJobLoggerException if there is an error writing the log
* acquire an exclusive lock on the log file. * message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/ */
void logAnalysisCompleted() throws InterruptedException { void logAnalysisCompleted() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.INFO, "Analysis of data source by ingest modules completed"); log(MessageCategory.INFO, "Analysis of data source completed");
} }
/** /**
@ -296,67 +298,79 @@ final class AutoIngestJobLogger {
* @param cancelledModuleName The display name of the cancelled ingest * @param cancelledModuleName The display name of the cancelled ingest
* module. * module.
* *
* @throws InterruptedException if interrupted while blocked waiting to * @throws AutoIngestJobLoggerException if there is an error writing the log
* acquire an exclusive lock on the log file. * message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/ */
void logIngestModuleCancelled(String cancelledModuleName) throws InterruptedException { void logIngestModuleCancelled(String cancelledModuleName) throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.WARNING, String.format("%s analysis of data source cancelled", cancelledModuleName)); log(MessageCategory.WARNING, String.format("%s analysis of data source cancelled", cancelledModuleName));
} }
/** /**
* Logs the cancellation of analysis of a data source by the ingest modules. * Logs the cancellation of analysis of a data source by the ingest modules.
* *
* @param reason The reason for cancellation. * @throws AutoIngestJobLoggerException if there is an error writing the log
* * message.
* @throws InterruptedException if interrupted while blocked waiting to * @throws InterruptedException if interrupted while blocked waiting
* acquire an exclusive lock on the log file. * to acquire an exclusive lock on the
* log file.
*/ */
void logAnalysisCancelled(String reason) throws InterruptedException { void logAnalysisCancelled() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.WARNING, String.format("Analysis of data source by ingest modules cancelled: %s", reason)); log(MessageCategory.WARNING, "Analysis of data source cancelled");
} }
/** /**
* Logs that automated file export is not enabled. * Logs that automated file export is not enabled.
* *
* @throws InterruptedException if interrupted while blocked waiting to * @throws AutoIngestJobLoggerException if there is an error writing the log
* acquire an exclusive lock on the log file. * message.
* @throws InterruptedException if interrupted while blocked waiting to * @throws InterruptedException if interrupted while blocked waiting
* acquire an exclusive lock on the log file. * to acquire an exclusive lock on the
* log file.
*/ */
void logFileExportDisabled() throws InterruptedException { void logFileExportDisabled() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.WARNING, "Automated file export is not enabled"); log(MessageCategory.WARNING, "Automated file export is not enabled");
} }
/** /**
* RJCTODO * Logs completion of file export.
* *
* @throws InterruptedException if interrupted while blocked waiting to * @throws AutoIngestJobLoggerException if there is an error writing the log
* acquire an exclusive lock on the log file. * message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/ */
void logFileExportCompleted() throws InterruptedException { void logFileExportCompleted() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.INFO, "Automated file export completed"); log(MessageCategory.INFO, "Automated file export completed");
} }
/** /**
* RJCTODO * Logs failure to complete file export.
* *
* @param ex * @throws AutoIngestJobLoggerException if there is an error writing the log
* * message.
* @throws InterruptedException if interrupted while blocked waiting to * @throws InterruptedException if interrupted while blocked waiting
* acquire an exclusive lock on the log file. * to acquire an exclusive lock on the
* log file.
*/ */
void logFileExportError(Exception ex) throws InterruptedException { void logFileExportError() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, String.format("Error exporting files: %s", ex.getMessage())); log(MessageCategory.ERROR, "Error exporting files");
} }
/** /**
* Logs discovery of a crashed auto ingest job for which recovery will be * Logs discovery of a crashed auto ingest job for which recovery will be
* attempted. * attempted.
* *
* @throws InterruptedException if interrupted while blocked waiting to * @throws AutoIngestJobLoggerException if there is an error writing the log
* acquire an exclusive lock on the log file. * message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/ */
void logCrashRecoveryWithRetry() throws InterruptedException { void logCrashRecoveryWithRetry() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, "Detected crash while processing, reprocessing"); log(MessageCategory.ERROR, "Detected crash while processing, reprocessing");
} }
@ -364,24 +378,16 @@ final class AutoIngestJobLogger {
* Logs discovery of a crashed auto ingest job for which recovery will not * Logs discovery of a crashed auto ingest job for which recovery will not
* be attempted because the retry limit for the job has been reached. * be attempted because the retry limit for the job has been reached.
* *
* @throws InterruptedException if interrupted while blocked waiting to * @throws AutoIngestJobLoggerException if there is an error writing the log
* acquire an exclusive lock on the log file. * message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/ */
void logCrashRecoveryNoRetry() throws InterruptedException { void logCrashRecoveryNoRetry() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, "Detected crash while processing, reached retry limit for processing"); log(MessageCategory.ERROR, "Detected crash while processing, reached retry limit for processing");
} }
/**
* Logs an unexpected runtime exception, e.g., an exception caught by the
* automated ingest job processing exception firewall.
*
* @throws InterruptedException if interrupted while blocked waiting to
* acquire an exclusive lock on the log file.
*/
void logErrorCondition(String message) throws InterruptedException {
log(MessageCategory.ERROR, message);
}
/** /**
* Writes a message to the case auto ingest log. * Writes a message to the case auto ingest log.
* <p> * <p>
@ -392,25 +398,55 @@ final class AutoIngestJobLogger {
* @param category The message category. * @param category The message category.
* @param message The message. * @param message The message.
* *
* @throws InterruptedException if interrupted while blocked waiting to * @throws AutoIngestJobLoggerException if there is an error writing the log
* acquire an exclusive lock on the log file. * message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/ */
private void log(MessageCategory category, String message) throws InterruptedException { private void log(MessageCategory category, String message) throws AutoIngestJobLoggerException, InterruptedException {
String prefix = String.format("Failed to write case auto ingest message (\"%s\") for %s", message, manifestPath);
try (Lock lock = CoordinationService.getInstance(CoordinationServiceNamespace.getRoot()).tryGetExclusiveLock(CoordinationService.CategoryNode.CASES, getLogPath(caseDirectoryPath).toString(), LOCK_TIME_OUT, LOCK_TIME_OUT_UNIT)) { try (Lock lock = CoordinationService.getInstance(CoordinationServiceNamespace.getRoot()).tryGetExclusiveLock(CoordinationService.CategoryNode.CASES, getLogPath(caseDirectoryPath).toString(), LOCK_TIME_OUT, LOCK_TIME_OUT_UNIT)) {
if (null != lock) { if (null != lock) {
File logFile = getLogPath(caseDirectoryPath).toFile(); File logFile = getLogPath(caseDirectoryPath).toFile();
try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(logFile, logFile.exists())), true)) { try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(logFile, logFile.exists())), true)) {
writer.println(String.format("%s %s: %s\\%s: %-8s: %s", logDateFormat.format((Date.from(Instant.now()).getTime())), hostName, manifestPath, dataSourceFileName, category.toString(), message)); writer.println(String.format("%s %s: %s: %s: %-8s: %s", logDateFormat.format((Date.from(Instant.now()).getTime())), hostName, manifestFileName, dataSourceFileName, category.toString(), message));
} catch (IOException ex) { } catch (IOException ex) {
AutoIngestSystemLogger.getLogger().log(Level.SEVERE, String.format("%s due to I/O error", prefix), ex); throw new AutoIngestJobLoggerException(String.format("Failed to write case auto ingest log message (\"%s\") for %s", message, manifestPath), ex);
} }
} else { } else {
AutoIngestSystemLogger.getLogger().log(Level.SEVERE, String.format("%s due to lock timeout", prefix)); throw new AutoIngestJobLoggerException(String.format("Failed to write case auto ingest log message (\"%s\") for %s due to time out acquiring log lock", message, manifestPath));
} }
} catch (CoordinationServiceException ex) { } catch (CoordinationServiceException ex) {
AutoIngestSystemLogger.getLogger().log(Level.SEVERE, String.format("%s due to coordination service error", prefix), ex); throw new AutoIngestJobLoggerException(String.format("Failed to write case auto ingest log message (\"%s\") for %s", message, manifestPath), ex);
}
}
/**
* Exception thrown when there is a problem writing a log message.
*/
final static class AutoIngestJobLoggerException extends Exception {
private static final long serialVersionUID = 1L;
/**
* Constructs an exception to throw when there is a problem writing a
* log message.
*
* @param message The exception message.
*/
private AutoIngestJobLoggerException(String message) {
super(message);
}
/**
* Constructs an exception to throw when there is a problem writing a
* log message.
*
* @param message The exception message.
* @param cause The cause of the exception, if it was an exception.
*/
private AutoIngestJobLoggerException(String message, Throwable cause) {
super(message, cause);
} }
} }

View File

@ -1,19 +1,20 @@
AutoIngestDashboard.bnRefresh.text=&Refresh AutoIngestDashboard.bnRefresh.text=&Refresh
AutoIngestDashboard.lbCompleted.text=Completed AutoIngestDashboard.lbCompleted.text=Completed Jobs
AutoIngestDashboard.lbRunning.text=Running AutoIngestDashboard.lbRunning.text=Running Jobs
AutoIngestDashboard.lbPending.text=Pending AutoIngestDashboard.lbPending.text=Pending Jobs
AutoIngestDashboard.bnCancelModule.text=Cancel &Module AutoIngestDashboard.bnCancelModule.text=Cancel &Module
AutoIngestDashboard.bnExit.text=&Exit AutoIngestDashboard.bnExit.text=&Exit
AutoIngestDashboard.bnOptions.text=&Options AutoIngestDashboard.bnOptions.text=&Options
AutoIngestDashboard.JobsTableModel.ColumnHeader.Case=Case AutoIngestDashboard.JobsTableModel.ColumnHeader.Case=Case
AutoIngestDashboard.JobsTableModel.ColumnHeader.ImageFolder=Data Source AutoIngestDashboard.JobsTableModel.ColumnHeader.ImageFolder=Data Source
AutoIngestDashboard.JobsTableModel.ColumnHeader.HostName=Host Name AutoIngestDashboard.JobsTableModel.ColumnHeader.HostName=Host Name
AutoIngestDashboard.JobsTableModel.ColumnHeader.CreatedTime=Created Time AutoIngestDashboard.JobsTableModel.ColumnHeader.CreatedTime=Job Created
AutoIngestDashboard.JobsTableModel.ColumnHeader.StartedTime=Started Time AutoIngestDashboard.JobsTableModel.ColumnHeader.StartedTime=Stage Started
AutoIngestDashboard.JobsTableModel.ColumnHeader.CompletedTime=Completed Time AutoIngestDashboard.JobsTableModel.ColumnHeader.CompletedTime=Job Completed
AutoIngestDashboard.JobsTableModel.ColumnHeader.Stage=Stage AutoIngestDashboard.JobsTableModel.ColumnHeader.Stage=Stage
AutoIngestDashboard.JobsTableModel.ColumnHeader.Status=Status AutoIngestDashboard.JobsTableModel.ColumnHeader.Status=Status
AutoIngestDashboard.bnShowProgress.text=&Show Progress AutoIngestDashboard.JobsTableModel.ColumnHeader.ManifestFilePath= Manifest File Path
AutoIngestDashboard.bnShowProgress.text=Ingest Progress
AutoIngestDashboard.bnResume.text=Resume AutoIngestDashboard.bnResume.text=Resume
AutoIngestDashboard.bnPause.text=Pause AutoIngestDashboard.bnPause.text=Pause
AutoIngestDashboard.bnPause.confirmHeader=Are you sure you want to pause? AutoIngestDashboard.bnPause.confirmHeader=Are you sure you want to pause?
@ -42,7 +43,7 @@ AutoIngestDashboard.ExitConsequences=This will cancel any currently running job
AutoIngestDashboard.ExitingStatus=Exiting... AutoIngestDashboard.ExitingStatus=Exiting...
AutoIngestDashboard.OK=OK AutoIngestDashboard.OK=OK
AutoIngestDashboard.Cancel=Cancel AutoIngestDashboard.Cancel=Cancel
AutoIngestDashboard.AutoIngestStartupFailed.Message=Failed to start automated ingest.\nPlease see application log for details. AutoIngestDashboard.AutoIngestStartupFailed.Message=Failed to start automated ingest.\nPlease see auto ingest system log for details.
AutoIngestDashboard.AutoIngestStartupFailed.Title=Automated Ingest Error AutoIngestDashboard.AutoIngestStartupFailed.Title=Automated Ingest Error
AutoIngestDashboard.AutoIngestStartupError=Failed to start automated ingest. Verify Multi-user Settings. AutoIngestDashboard.AutoIngestStartupError=Failed to start automated ingest. Verify Multi-user Settings.
AutoIngestDashboard.AutoIngestStartupWarning.Title=Automated Ingest Warning AutoIngestDashboard.AutoIngestStartupWarning.Title=Automated Ingest Warning
@ -60,7 +61,7 @@ AutoIngestDashboard.tbServicesStatusMessage.Message=Case databases {0}, keyword
AutoIngestDashboard.tbServicesStatusMessage.Message.Up=up AutoIngestDashboard.tbServicesStatusMessage.Message.Up=up
AutoIngestDashboard.tbServicesStatusMessage.Message.Down=down AutoIngestDashboard.tbServicesStatusMessage.Message.Down=down
AutoIngestDashboard.tbServicesStatusMessage.Message.Unknown=unknown AutoIngestDashboard.tbServicesStatusMessage.Message.Unknown=unknown
AutoIngestDashboard.PauseDueToSystemError=Paused due to system error, please consult the auto ingest system log" AutoIngestDashboard.PauseDueToSystemError=Paused due to system error, please consult the auto ingest system log
ConfirmationDialog.DoNotDelete=Do not delete ConfirmationDialog.DoNotDelete=Do not delete
ConfirmationDialog.Delete=Permanently delete ConfirmationDialog.Delete=Permanently delete
ConfirmationDialog.DeleteAreYouSure=The entire case will be removed. Are you sure you want to delete case ConfirmationDialog.DeleteAreYouSure=The entire case will be removed. Are you sure you want to delete case
@ -166,7 +167,7 @@ ReviewModeCasePanel.bnShowLog.text=&Show Log
AutoIngestDashboard.bnPrioritizeCase.toolTipText=Move all images associated with a case to top of Pending queue. AutoIngestDashboard.bnPrioritizeCase.toolTipText=Move all images associated with a case to top of Pending queue.
AutoIngestDashboard.bnPrioritizeCase.text=Prioriti&ze Case AutoIngestDashboard.bnPrioritizeCase.text=Prioriti&ze Case
AutoIngestDashboard.bnShowCaseLog.toolTipText=Display case log file for selected case AutoIngestDashboard.bnShowCaseLog.toolTipText=Display case log file for selected case
AutoIngestDashboard.bnShowCaseLog.text=Show &Log AutoIngestDashboard.bnShowCaseLog.text=Show Case &Log
ReviewModeCasePanel.bnShowLog.toolTipText=Display case log file for selected case ReviewModeCasePanel.bnShowLog.toolTipText=Display case log file for selected case
CopyFilesPanel.bnCancelPendingJob.text=Ca&ncel CopyFilesPanel.bnCancelPendingJob.text=Ca&ncel
CopyFilesPanel.tbDestinationCase.text= CopyFilesPanel.tbDestinationCase.text=
@ -197,8 +198,6 @@ CaseImportPanel.Complete=Complete
CaseImportPanel.Blank= CaseImportPanel.Blank=
CaseImportPanel.DeleteWarning=Make sure no important files are in the case source directory CaseImportPanel.DeleteWarning=Make sure no important files are in the case source directory
AutoIngestDashboard.lbStatus.text=Status: AutoIngestDashboard.lbStatus.text=Status:
AutoIngestDashboard.bnPrioritizeFolder.text=Prioritize &Folder
AutoIngestDashboard.bnPrioritizeFolder.toolTipText=Move this folder to the top of the Pending queue.
SingleUserCaseImporter.NonUniqueOutputFolder=Output folder not unique. Skipping SingleUserCaseImporter.NonUniqueOutputFolder=Output folder not unique. Skipping
SingleUserCaseImporter.WillImport=Will import: SingleUserCaseImporter.WillImport=Will import:
SingleUserCaseImporter.None=None SingleUserCaseImporter.None=None
@ -283,4 +282,7 @@ FileExporterSettingsPanel.BrowseReportTooltip_1=Browse for the Reports Folder
FileExporterSettingsPanel.NewRuleTooltip_1=Clear the rule editor to begin a new rule FileExporterSettingsPanel.NewRuleTooltip_1=Clear the rule editor to begin a new rule
FileExporterSettingsPanel.DeleteTooltip_1=Delete the selected rule FileExporterSettingsPanel.DeleteTooltip_1=Delete the selected rule
FileExporterSettingsPanel.SaveTooltip_1=Save the current rule FileExporterSettingsPanel.SaveTooltip_1=Save the current rule
AutoIngestDashboard.bnOpenLogDir.text=Open Log Directory AutoIngestDashboard.bnOpenLogDir.text=Open System Logs Directory
AutoIngestDashboard.bnPrioritizeJob.text=Prioritize Job
AutoIngestDashboard.bnPrioritizeJob.toolTipText=Move this folder to the top of the Pending queue.
AutoIngestDashboard.bnReprocessJob.text=Reprocess Job

View File

@ -19,13 +19,13 @@
package org.sleuthkit.autopsy.experimental.autoingest; package org.sleuthkit.autopsy.experimental.autoingest;
/** /**
* RJCTODO * Namespace elements for auto ingest coordination service nodes.
*/ */
final class CoordinationServiceNamespace { final class CoordinationServiceNamespace {
static final String ROOT_COORD_SCV_NAMESPACE = "autopsy"; // RJCTODO: Move this elsewhere private static final String ROOT = "autopsy";
static String getRoot() { static String getRoot() {
return ROOT_COORD_SCV_NAMESPACE; return ROOT;
} }
private CoordinationServiceNamespace() { private CoordinationServiceNamespace() {

View File

@ -19,34 +19,35 @@
package org.sleuthkit.autopsy.experimental.autoingest; package org.sleuthkit.autopsy.experimental.autoingest;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Date;
/** /**
* RJCTODO * A coordination service node data transfer object for an auto ingest job
* manifest. The data include: processing status, priority, the number of times
* the auto ingest job for the manifest has crashed during processing, and the
* date the auto ingest job for the manifest was completed.
*/ */
// RJCTODO: Consider making this encapsulate the locking as well, and to set the data as well
final class ManifestNodeData { final class ManifestNodeData {
enum ProcessingStatus {
PENDING,
PROCESSING,
COMPLETED,
}
private static final int DEFAULT_PRIORITY = 0; private static final int DEFAULT_PRIORITY = 0;
private final boolean nodeDataIsSet; private final boolean coordSvcNodeDataWasSet;
private ProcessingStatus status; private ProcessingStatus status;
private int priority; private int priority;
private int numberOfCrashes; private int numberOfCrashes;
private long completedDate;
private boolean errorsOccurred;
/** /**
* RJCTODO * Constructs a coordination service node data data transfer object for an
* auto ingest manifest from the raw bytes obtained from the coordination
* service.
* *
* @param nodeData * @param nodeData The raw bytes received from the coordination service.
*/ */
ManifestNodeData(byte[] nodeData) { ManifestNodeData(byte[] nodeData) {
ByteBuffer buffer = ByteBuffer.wrap(nodeData); ByteBuffer buffer = ByteBuffer.wrap(nodeData);
this.nodeDataIsSet = buffer.hasRemaining(); this.coordSvcNodeDataWasSet = buffer.hasRemaining();
if (this.nodeDataIsSet) { if (this.coordSvcNodeDataWasSet) {
int rawStatus = buffer.getInt(); int rawStatus = buffer.getInt();
if (ProcessingStatus.PENDING.ordinal() == rawStatus) { if (ProcessingStatus.PENDING.ordinal() == rawStatus) {
this.status = ProcessingStatus.PENDING; this.status = ProcessingStatus.PENDING;
@ -54,97 +55,183 @@ final class ManifestNodeData {
this.status = ProcessingStatus.PROCESSING; this.status = ProcessingStatus.PROCESSING;
} else if (ProcessingStatus.COMPLETED.ordinal() == rawStatus) { } else if (ProcessingStatus.COMPLETED.ordinal() == rawStatus) {
this.status = ProcessingStatus.COMPLETED; this.status = ProcessingStatus.COMPLETED;
}else if (ProcessingStatus.DELETED.ordinal() == rawStatus) {
this.status = ProcessingStatus.DELETED;
} }
this.priority = buffer.getInt(); this.priority = buffer.getInt();
this.numberOfCrashes = buffer.getInt(); this.numberOfCrashes = buffer.getInt();
this.completedDate = buffer.getLong();
int errorFlag = buffer.getInt();
this.errorsOccurred = (1 == errorFlag);
} else { } else {
this.status = ProcessingStatus.PENDING; this.status = ProcessingStatus.PENDING;
this.priority = DEFAULT_PRIORITY; this.priority = DEFAULT_PRIORITY;
this.numberOfCrashes = 0; this.numberOfCrashes = 0;
this.completedDate = 0L;
this.errorsOccurred = false;
} }
} }
/** /**
* RJCTODO * Constructs a coordination service node data data transfer object for an
* auto ingest manifest from values provided by the auto ingest system.
*
* @param status The processing status of the manifest.
* @param priority The priority of the manifest.
* @param numberOfCrashes The number of times auto ingest jobs for the
* manifest have crashed during processing.
* @param completedDate The date the auto ingest job for the manifest was
* completed.
*/ */
ManifestNodeData(ProcessingStatus status, int priority, int numberOfCrashes) { ManifestNodeData(ProcessingStatus status, int priority, int numberOfCrashes, Date completedDate, boolean errorOccurred) {
this.nodeDataIsSet = false; this.coordSvcNodeDataWasSet = false;
this.status = status; this.status = status;
this.priority = priority; this.priority = priority;
this.numberOfCrashes = numberOfCrashes; this.numberOfCrashes = numberOfCrashes;
this.completedDate = completedDate.getTime();
this.errorsOccurred = errorOccurred;
} }
/** /**
* RJCTODO * Indicates whether or not the coordination service node data was set,
* i.e., this object was constructed from raw bytes from the ccordination
* service node for the manifest.
* *
* @return * @return True or false.
*/ */
boolean isSet() { // RJCTODO: This is confusing, consider changing the API so that the use case is to
return this.nodeDataIsSet; // check the length of the node data from the coordination service before
// constructing an instance of this object. That would be much more clear!
boolean coordSvcNodeDataWasSet() {
return this.coordSvcNodeDataWasSet;
} }
/** /**
* RJCTODO * Gets the processing status of the manifest
* *
* @return * @return The processing status of the manifest.
*/ */
ProcessingStatus getStatus() { ProcessingStatus getStatus() {
return this.status; return this.status;
} }
/** /**
* Sets the processing status of the manifest
* *
* @param status * @param status The processing status of the manifest.
*/ */
void setStatus(ProcessingStatus status) { void setStatus(ProcessingStatus status) {
this.status = status; this.status = status;
} }
/** /**
* Gets the priority of the manifest.
* *
* @return * @return The priority of the manifest.
*/ */
int getPriority() { int getPriority() {
return this.priority; return this.priority;
} }
/** /**
* Sets the priority of the manifest. A higher number indicates a higheer
* priority.
* *
* @param priority * @param priority The priority of the manifest.
*/ */
void setPriority(int priority) { void setPriority(int priority) {
this.priority = priority; this.priority = priority;
} }
/** /**
* RJCTODO * Gets the number of times auto ingest jobs for the manifest have crashed
* during processing.
* *
* @return * @return The number of times auto ingest jobs for the manifest have
* crashed during processing.
*/ */
int getNumberOfCrashes() { int getNumberOfCrashes() {
return this.numberOfCrashes; return this.numberOfCrashes;
} }
/** /**
* RJCTODO * Sets the number of times auto ingest jobs for the manifest have crashed
* during processing.
* *
* @param attempts * @param numberOfCrashes The number of times auto ingest jobs for the
* manifest have crashed during processing.
*/ */
void setNumberOfCrashes(int attempts) { void setNumberOfCrashes(int numberOfCrashes) {
this.numberOfCrashes = attempts; this.numberOfCrashes = numberOfCrashes;
} }
/** /**
* RJCTODO * Gets the date the auto ingest job for the manifest was completed.
* *
* @return * @return The date the auto ingest job for the manifest was completed. The
* epoch (January 1, 1970, 00:00:00 GMT) indicates the date is not
* set, i.e., Date.getTime() returns 0L.
*/
Date getCompletedDate() {
return new Date(this.completedDate);
}
/**
* Sets the date the auto ingest job for the manifest was completed.
*
* @param completedDate The date the auto ingest job for the manifest was
* completed. Use the epoch (January 1, 1970, 00:00:00
* GMT) to indicate the date is not set, i.e., new
* Date(0L).
*/
void setCompletedDate(Date completedDate) {
this.completedDate = completedDate.getTime();
}
/**
* Queries whether or not any errors occurred during the processing of the
* auto ingest job for the manifest.
*
* @return True or false.
*/
boolean getErrorsOccurred() {
return this.errorsOccurred;
}
/**
* Sets whether or not any errors occurred during the processing of the auto
* ingest job for the manifest.
*
* @param errorsOccurred True or false.
*/
void setErrorsOccurred(boolean errorsOccurred) {
this.errorsOccurred = errorsOccurred;
}
/**
* Gets the node data as raw bytes that can be sent to the coordination
* service.
*
* @return The manifest node data as a byte array.
*/ */
byte[] toArray() { byte[] toArray() {
ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES * 3); ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES * 4 + Long.BYTES);
buffer.putInt(this.status.ordinal()); buffer.putInt(this.status.ordinal());
buffer.putInt(this.priority); buffer.putInt(this.priority);
buffer.putInt(this.numberOfCrashes); buffer.putInt(this.numberOfCrashes);
buffer.putLong(this.completedDate);
buffer.putInt(this.errorsOccurred ? 1 : 0);
return buffer.array(); return buffer.array();
} }
/**
* Processing status for the auto ingest job for the manifest.
*/
enum ProcessingStatus {
PENDING,
PROCESSING,
COMPLETED,
DELETED
}
} }

View File

@ -28,11 +28,9 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import org.sleuthkit.autopsy.casemodule.CaseMetadata; import org.sleuthkit.autopsy.casemodule.CaseMetadata;
import org.sleuthkit.autopsy.casemodule.GeneralFilter; import org.sleuthkit.autopsy.casemodule.GeneralFilter;
import org.sleuthkit.autopsy.coreutils.Logger;
final class PathUtils { final class PathUtils {
private static final Logger logger = Logger.getLogger(PathUtils.class.getName());
private static final List<String> CASE_METADATA_FILE_EXTS = Arrays.asList(new String[]{CaseMetadata.getFileExtension()}); private static final List<String> CASE_METADATA_FILE_EXTS = Arrays.asList(new String[]{CaseMetadata.getFileExtension()});
private static final GeneralFilter caseMetadataFileFilter = new GeneralFilter(CASE_METADATA_FILE_EXTS, "Autopsy Case File"); private static final GeneralFilter caseMetadataFileFilter = new GeneralFilter(CASE_METADATA_FILE_EXTS, "Autopsy Case File");
@ -71,7 +69,7 @@ final class PathUtils {
* *
* @return A list of the output case folder paths. * @return A list of the output case folder paths.
*/ */
static List<Path> findCaseFolders(Path folderToSearch) { static List<Path> findCaseFolders(Path folderToSearch) { // RJCTODO: Rename
File searchFolder = new File(folderToSearch.toString()); File searchFolder = new File(folderToSearch.toString());
if (!searchFolder.isDirectory()) { if (!searchFolder.isDirectory()) {
return Collections.emptyList(); return Collections.emptyList();
@ -112,32 +110,6 @@ final class PathUtils {
return caseDataFiles.length != 0; return caseDataFiles.length != 0;
} }
/**
* Extracts the path to the case images folder path from an image folder
* path.
*
* @param rootImageFoldersPath The root image folders path.
* @param imageFolderPath The image folder path.
*
* @return The root input folder path for a case.
*/
static Path caseImagesPathFromImageFolderPath(Path rootImageFoldersPath, Path imageFolderPath) {
return rootImageFoldersPath.resolve(imageFolderPath.subpath(0, rootImageFoldersPath.getNameCount() + 1).getFileName());
}
/**
* Extracts the case name from an image folder path.
*
* @param rootImageFoldersPath The root image folders path.
* @param imageFolderPath The image folder path.
*
* @return The case name.
*/
static String caseNameFromImageFolderPath(Path rootImageFoldersPath, Path imageFolderPath) {
Path caseImagesPath = PathUtils.caseImagesPathFromImageFolderPath(rootImageFoldersPath, imageFolderPath);
return caseImagesPath.getFileName().toString();
}
/** /**
* Extracts the case name from a case folder path. * Extracts the case name from a case folder path.
* *
@ -163,7 +135,7 @@ final class PathUtils {
* *
* @return A case folder path with a time stamp suffix. * @return A case folder path with a time stamp suffix.
*/ */
static Path createCaseFolderPath(Path caseFoldersPath, String caseName) { static Path createCaseFolderPath(Path caseFoldersPath, String caseName) { // RJCTODO: Rename
String folderName = caseName + "_" + TimeStampUtils.createTimeStamp(); String folderName = caseName + "_" + TimeStampUtils.createTimeStamp();
return Paths.get(caseFoldersPath.toString(), folderName); return Paths.get(caseFoldersPath.toString(), folderName);
} }

View File

@ -61,8 +61,8 @@ public final class ReviewModeCasePanel extends JPanel {
private static final int STATUS_COL_MIN_WIDTH = 55; private static final int STATUS_COL_MIN_WIDTH = 55;
private static final int STATUS_COL_MAX_WIDTH = 250; private static final int STATUS_COL_MAX_WIDTH = 250;
private static final int STATUS_COL_PREFERRED_WIDTH = 60; private static final int STATUS_COL_PREFERRED_WIDTH = 60;
private static final int MILLISECONDS_TO_WAIT_BEFORE_STARTING = 500; private static final int MILLISECONDS_TO_WAIT_BEFORE_STARTING = 500; // RJCTODO: Shorten name
private static final int MILLISECONDS_TO_WAIT_BETWEEN_UPDATES = 30000; private static final int MILLISECONDS_TO_WAIT_BETWEEN_UPDATES = 30000; // RJCTODO: Shorten name
private ScheduledThreadPoolExecutor casesTableRefreshExecutor; private ScheduledThreadPoolExecutor casesTableRefreshExecutor;
/* /*
@ -84,7 +84,7 @@ public final class ReviewModeCasePanel extends JPanel {
CREATEDTIME, CREATEDTIME,
COMPLETEDTIME, COMPLETEDTIME,
STATUS_ICON, STATUS_ICON,
OUTPUTFOLDER OUTPUTFOLDER // RJCTODO: Change name
} }
private final String[] columnNames = {CASE_HEADER, CREATEDTIME_HEADER, COMPLETEDTIME_HEADER, STATUS_ICON_HEADER, OUTPUT_FOLDER_HEADER}; private final String[] columnNames = {CASE_HEADER, CREATEDTIME_HEADER, COMPLETEDTIME_HEADER, STATUS_ICON_HEADER, OUTPUT_FOLDER_HEADER};
private DefaultTableModel caseTableModel; private DefaultTableModel caseTableModel;
@ -325,7 +325,7 @@ public final class ReviewModeCasePanel extends JPanel {
autoIngestCase.getCaseName(), autoIngestCase.getCaseName(),
autoIngestCase.getCreationDate(), autoIngestCase.getCreationDate(),
autoIngestCase.getLastAccessedDate(), autoIngestCase.getLastAccessedDate(),
autoIngestCase.getStatus(), (AutoIngestCase.CaseStatus.OK != autoIngestCase.getStatus()),
autoIngestCase.getCaseDirectoryPath().toString()}); autoIngestCase.getCaseDirectoryPath().toString()});
} }
} }