mirror of
https://github.com/elisspace/autopsy.git
synced 2026-10-03 07:49:52 +00:00
Update DSP docs, make AddImageTask thread-safe, DSPs always callback
This commit is contained in:
@@ -19,15 +19,13 @@
|
||||
package org.sleuthkit.autopsy.casemodule;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.SleuthkitJNI;
|
||||
@@ -35,66 +33,220 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskDataException;
|
||||
|
||||
/*
|
||||
* A background task that adds the given image to database using the Sleuthkit
|
||||
* JNI interface.
|
||||
*
|
||||
* It updates the given ProgressMonitor as it works through adding the image,
|
||||
* and et the end, calls the specified Callback.
|
||||
* A runnable that adds an image data source to the case database.
|
||||
*/
|
||||
class AddImageTask implements Runnable {
|
||||
|
||||
private final Logger logger = Logger.getLogger(AddImageTask.class.getName());
|
||||
|
||||
private final Case currentCase;
|
||||
|
||||
// true if the process was requested to cancel
|
||||
private final Object lock = new Object(); // synchronization object for cancelRequested
|
||||
private volatile boolean cancelRequested = false;
|
||||
|
||||
//true if revert has been invoked.
|
||||
private boolean reverted = false;
|
||||
|
||||
// true if there was a critical error in adding the data source
|
||||
private boolean hasCritError = false;
|
||||
|
||||
private final List<String> errorList = new ArrayList<>();
|
||||
|
||||
private final DataSourceProcessorProgressMonitor progressMonitor;
|
||||
private final DataSourceProcessorCallback callbackObj;
|
||||
|
||||
private final List<Content> newContents = Collections.synchronizedList(new ArrayList<Content>());
|
||||
|
||||
private SleuthkitJNI.CaseDbHandle.AddImageProcess addImageProcess;
|
||||
private Thread dirFetcher;
|
||||
|
||||
private final String deviceId;
|
||||
private final String imagePath;
|
||||
String timeZone;
|
||||
boolean noFatOrphans;
|
||||
|
||||
private final String dataSourceId;
|
||||
private final String timeZone;
|
||||
private final boolean ignoreFatOrphanFiles;
|
||||
private final DataSourceProcessorProgressMonitor progressMonitor;
|
||||
private final DataSourceProcessorCallback callback;
|
||||
private boolean criticalErrorOccurred;
|
||||
|
||||
/*
|
||||
* A thread that updates the progressMonitor with the name of the directory
|
||||
* currently being processed by the AddImageTask
|
||||
* The cancellation requested flag and SleuthKit add image process are
|
||||
* guarded by a monitor (called a lock here to avoid confusion with the
|
||||
* progress monitor) to synchronize cancelling the process (setting the flag
|
||||
* and calling its stop method) and calling either its commit or revert
|
||||
* method. The built-in monitor of the add image process can't be used for
|
||||
* this because it is already used to synchronize its run (init part),
|
||||
* commit, revert, and currentDirectory methods.
|
||||
*
|
||||
* TODO (AUT-2021): Merge SleuthkitJNI.AddImageProcess and AddImageTask
|
||||
*/
|
||||
private class CurrentDirectoryFetcher implements Runnable {
|
||||
private final Object tskAddImageProcessLock;
|
||||
private boolean tskAddImageProcessStopped;
|
||||
private SleuthkitJNI.CaseDbHandle.AddImageProcess tskAddImageProcess;
|
||||
|
||||
DataSourceProcessorProgressMonitor progressMonitor;
|
||||
SleuthkitJNI.CaseDbHandle.AddImageProcess process;
|
||||
/**
|
||||
* Constructs a runnable task that adds an image to the case database.
|
||||
*
|
||||
* @param deviceId An ASCII-printable identifier for the device
|
||||
* associated with the data source that is
|
||||
* intended to be unique across multiple cases
|
||||
* (e.g., a UUID).
|
||||
* @param imagePath Path to the image file.
|
||||
* @param timeZone The time zone to use when processing dates
|
||||
* and times for the image, obtained from
|
||||
* java.util.TimeZone.getID.
|
||||
* @param ignoreFatOrphanFiles Whether to parse orphans if the image has a
|
||||
* FAT filesystem.
|
||||
* @param progressMonitor Progress monitor to report progress during
|
||||
* processing.
|
||||
* @param callback Callback to call when processing is done.
|
||||
*/
|
||||
AddImageTask(String deviceId, String imagePath, String timeZone, boolean ignoreFatOrphanFiles, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
this.deviceId = deviceId;
|
||||
this.imagePath = imagePath;
|
||||
this.timeZone = timeZone;
|
||||
this.ignoreFatOrphanFiles = ignoreFatOrphanFiles;
|
||||
this.callback = callback;
|
||||
this.progressMonitor = progressMonitor;
|
||||
tskAddImageProcessLock = new Object();
|
||||
}
|
||||
|
||||
CurrentDirectoryFetcher(DataSourceProcessorProgressMonitor aProgressMonitor, SleuthkitJNI.CaseDbHandle.AddImageProcess proc) {
|
||||
this.progressMonitor = aProgressMonitor;
|
||||
this.process = proc;
|
||||
/**
|
||||
* Adds the image to the case database.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
progressMonitor.setIndeterminate(true);
|
||||
progressMonitor.setProgress(0);
|
||||
Case currentCase = Case.getCurrentCase();
|
||||
List<String> errorMessages = new ArrayList<>();
|
||||
List<Content> newDataSources = new ArrayList<>();
|
||||
try {
|
||||
currentCase.getSleuthkitCase().acquireExclusiveLock();
|
||||
synchronized (tskAddImageProcessLock) {
|
||||
tskAddImageProcess = currentCase.makeAddImageProcess(timeZone, true, ignoreFatOrphanFiles);
|
||||
}
|
||||
Thread progressUpdateThread = new Thread(new ProgressUpdater(progressMonitor, tskAddImageProcess));
|
||||
progressUpdateThread.start();
|
||||
runAddImageProcess(errorMessages);
|
||||
if (null != progressUpdateThread) {
|
||||
progressUpdateThread.interrupt();
|
||||
}
|
||||
commitOrRevertAddImageProcess(currentCase, errorMessages, newDataSources);
|
||||
progressMonitor.setProgress(100);
|
||||
} finally {
|
||||
currentCase.getSleuthkitCase().releaseExclusiveLock();
|
||||
DataSourceProcessorCallback.DataSourceProcessorResult result;
|
||||
if (criticalErrorOccurred) {
|
||||
result = DataSourceProcessorResult.CRITICAL_ERRORS;
|
||||
} else if (!errorMessages.isEmpty()) {
|
||||
result = DataSourceProcessorResult.NONCRITICAL_ERRORS;
|
||||
} else {
|
||||
result = DataSourceProcessorResult.NO_ERRORS;
|
||||
}
|
||||
callback.done(result, errorMessages, newDataSources);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempts to cancel adding the image to the case database.
|
||||
*/
|
||||
public void cancelTask() {
|
||||
synchronized (tskAddImageProcessLock) {
|
||||
if (null != tskAddImageProcess) {
|
||||
try {
|
||||
/*
|
||||
* All this does is set a flag that will make the TSK add
|
||||
* image process exit when the flag is checked between
|
||||
* processing steps. The state of the flag is not
|
||||
* accessible, so record it here so that it is known that
|
||||
* the revert method of the process object needs to be
|
||||
* called.
|
||||
*/
|
||||
tskAddImageProcess.stop();
|
||||
tskAddImageProcessStopped = true;
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error cancelling adding image %s to the case database", imagePath), ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the TSK add image process.
|
||||
*
|
||||
* @param errorMessages Error messages, if any, are added to this list for
|
||||
* eventual return via the callback.
|
||||
*/
|
||||
private void runAddImageProcess(List<String> errorMessages) {
|
||||
try {
|
||||
tskAddImageProcess.run(deviceId, new String[]{imagePath});
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Critical error occurred adding image %s", imagePath), ex); //NON-NLS
|
||||
criticalErrorOccurred = true;
|
||||
errorMessages.add(ex.getMessage());
|
||||
} catch (TskDataException ex) {
|
||||
logger.log(Level.WARNING, String.format("Non-critical error occurred adding image %s", imagePath), ex); //NON-NLS
|
||||
errorMessages.add(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits or reverts the results of the TSK add image process. If the
|
||||
* process was stopped before it completed or there was a critical error the
|
||||
* results are reverted, otherwise they are committed.
|
||||
*
|
||||
* @param currentCase The current case.
|
||||
* @param errorMessages Error messages, if any, are added to this list for
|
||||
* eventual return via the callback.
|
||||
* @param newDataSources If the new image is successfully committed, it is
|
||||
* added to this list for eventual return via the
|
||||
* callback.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private void commitOrRevertAddImageProcess(Case currentCase, List<String> errorMessages, List<Content> newDataSources) {
|
||||
synchronized (tskAddImageProcessLock) {
|
||||
if (tskAddImageProcessStopped || criticalErrorOccurred) {
|
||||
try {
|
||||
tskAddImageProcess.revert();
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error reverting adding image %s to the case database", imagePath), ex); //NON-NLS
|
||||
errorMessages.add(ex.getMessage());
|
||||
criticalErrorOccurred = true;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
long imageId = tskAddImageProcess.commit();
|
||||
if (imageId != 0) {
|
||||
Image newImage = currentCase.getSleuthkitCase().getImageById(imageId);
|
||||
String verificationError = newImage.verifyImageSize();
|
||||
if (!verificationError.isEmpty()) {
|
||||
errorMessages.add(verificationError);
|
||||
}
|
||||
newDataSources.add(newImage);
|
||||
} else {
|
||||
String errorMessage = String.format("Error commiting adding image %s to the case database, no object id returned", imagePath); //NON-NLS
|
||||
logger.log(Level.SEVERE, errorMessage);
|
||||
errorMessages.add(errorMessage);
|
||||
criticalErrorOccurred = true;
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error committing adding image %s to the case database", imagePath), ex); //NON-NLS
|
||||
errorMessages.add(ex.getMessage());
|
||||
criticalErrorOccurred = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Runnable that updates the progress monitor with the name of the
|
||||
* directory currently being processed by the SleuthKit add image process.
|
||||
*/
|
||||
private class ProgressUpdater implements Runnable {
|
||||
|
||||
private final DataSourceProcessorProgressMonitor progressMonitor;
|
||||
private final SleuthkitJNI.CaseDbHandle.AddImageProcess tskAddImageProcess;
|
||||
|
||||
/**
|
||||
* Constructs a Runnable that updates the progress monitor with the name
|
||||
* of the directory currently being processed by the SleuthKit.
|
||||
*
|
||||
* @param progressMonitor
|
||||
* @param tskAddImageProcess
|
||||
*/
|
||||
ProgressUpdater(DataSourceProcessorProgressMonitor progressMonitor, SleuthkitJNI.CaseDbHandle.AddImageProcess tskAddImageProcess) {
|
||||
this.progressMonitor = progressMonitor;
|
||||
this.tskAddImageProcess = tskAddImageProcess;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the currently processing directory
|
||||
* Updates the progress monitor with the name of the directory currently
|
||||
* being processed by the SleuthKit add image process.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
String currDir = process.currentDirectory();
|
||||
String currDir = tskAddImageProcess.currentDirectory();
|
||||
if (currDir != null) {
|
||||
if (!currDir.isEmpty()) {
|
||||
progressMonitor.setProgressText(
|
||||
@@ -102,223 +254,20 @@ class AddImageTask implements Runnable {
|
||||
currDir));
|
||||
}
|
||||
}
|
||||
// this sleep here prevents the UI from locking up
|
||||
// due to too frequent updates to the progressMonitor above
|
||||
/*
|
||||
* The sleep here throttles the UI updates and provides a
|
||||
* non-standard mechanism for completing this task by
|
||||
* interrupting the thread in which it is running.
|
||||
*
|
||||
* TODO (AUT-1870): Replace this with giving the task to a
|
||||
* java.util.concurrent.ScheduledThreadPoolExecutor that is
|
||||
* shut down when the main task completes.
|
||||
*/
|
||||
Thread.sleep(500);
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
// nothing to do, thread was interrupted externally
|
||||
// signaling the end of AddImageProcess
|
||||
} catch (InterruptedException expected) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a runnable task that adds an image to the case database.
|
||||
*
|
||||
* @param dataSourceId An ASCII-printable identifier for the data
|
||||
* source that is intended to be unique across
|
||||
* multiple cases (e.g., a UUID).
|
||||
* @param imagePath Path to the image file.
|
||||
* @param timeZone The time zone to use when processing dates
|
||||
* and times for the image, obtained from
|
||||
* java.util.TimeZone.getID.
|
||||
* @param ignoreFatOrphanFiles Whether to parse orphans if the image has a
|
||||
* FAT filesystem.
|
||||
* @param monitor Progress monitor to report progress during
|
||||
* processing.
|
||||
* @param cbObj Callback to call when processing is done.
|
||||
*/
|
||||
AddImageTask(String dataSourceId, String imagePath, String timeZone, boolean ignoreFatOrphanFiles, DataSourceProcessorProgressMonitor monitor, DataSourceProcessorCallback cbObj) {
|
||||
currentCase = Case.getCurrentCase();
|
||||
this.dataSourceId = dataSourceId;
|
||||
this.imagePath = imagePath;
|
||||
this.timeZone = timeZone;
|
||||
this.noFatOrphans = ignoreFatOrphanFiles;
|
||||
this.callbackObj = cbObj;
|
||||
this.progressMonitor = monitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the addImage process, but does not commit the results.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
errorList.clear();
|
||||
try {
|
||||
currentCase.getSleuthkitCase().acquireExclusiveLock();
|
||||
addImageProcess = currentCase.makeAddImageProcess(timeZone, true, noFatOrphans);
|
||||
dirFetcher = new Thread(new CurrentDirectoryFetcher(progressMonitor, addImageProcess));
|
||||
try {
|
||||
progressMonitor.setIndeterminate(true);
|
||||
progressMonitor.setProgress(0);
|
||||
dirFetcher.start();
|
||||
addImageProcess.run(dataSourceId, new String[]{imagePath});
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Core errors occurred while running add image on " + imagePath, ex); //NON-NLS
|
||||
hasCritError = true;
|
||||
errorList.add(ex.getMessage());
|
||||
} catch (TskDataException ex) {
|
||||
logger.log(Level.WARNING, "Data errors occurred while running add image " + imagePath, ex); //NON-NLS
|
||||
errorList.add(ex.getMessage());
|
||||
}
|
||||
postProcess();
|
||||
} finally {
|
||||
currentCase.getSleuthkitCase().releaseExclusiveLock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit the newly added image to DB
|
||||
*
|
||||
*
|
||||
* @throws Exception if commit or adding the image to the case failed
|
||||
*/
|
||||
private void commitImage() throws Exception {
|
||||
|
||||
long imageId = 0;
|
||||
try {
|
||||
imageId = addImageProcess.commit();
|
||||
} catch (TskCoreException e) {
|
||||
logger.log(Level.WARNING, "Errors occurred while committing the image " + imagePath, e); //NON-NLS
|
||||
errorList.add(e.getMessage());
|
||||
} finally {
|
||||
if (imageId != 0) {
|
||||
// get the newly added Image so we can return to caller
|
||||
Image newImage = currentCase.getSleuthkitCase().getImageById(imageId);
|
||||
|
||||
//while we have the image, verify the size of its contents
|
||||
String verificationErrors = newImage.verifyImageSize();
|
||||
if (verificationErrors.equals("") == false) {
|
||||
//data error (non-critical)
|
||||
errorList.add(verificationErrors);
|
||||
}
|
||||
|
||||
// Add the image to the list of new content
|
||||
newContents.add(newImage);
|
||||
}
|
||||
|
||||
logger.log(Level.INFO, "Image committed, imageId: {0}", imageId); //NON-NLS
|
||||
logger.log(Level.INFO, PlatformUtil.getAllMemUsageInfo());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post processing after the addImageProcess is done.
|
||||
*
|
||||
*/
|
||||
private void postProcess() {
|
||||
|
||||
// cancel the directory fetcher
|
||||
dirFetcher.interrupt();
|
||||
|
||||
if (cancelRequested() || hasCritError) {
|
||||
logger.log(Level.WARNING, "Critical errors or interruption in add image process on {0}. Image will not be committed.", imagePath); //NON-NLS
|
||||
revert();
|
||||
}
|
||||
|
||||
if (!errorList.isEmpty()) {
|
||||
logger.log(Level.INFO, "There were errors that occurred in add image process for {0}", imagePath); //NON-NLS
|
||||
}
|
||||
|
||||
// When everything happens without an error:
|
||||
if (!(cancelRequested() || hasCritError)) {
|
||||
try {
|
||||
if (addImageProcess != null) {
|
||||
// commit image
|
||||
try {
|
||||
commitImage();
|
||||
} catch (Exception ex) {
|
||||
errorList.add(ex.getMessage());
|
||||
// Log error/display warning
|
||||
logger.log(Level.SEVERE, "Error adding image " + imagePath + " to case.", ex); //NON-NLS
|
||||
}
|
||||
} else {
|
||||
logger.log(Level.SEVERE, "Missing image process object"); //NON-NLS
|
||||
}
|
||||
|
||||
// Tell the progress monitor we're done
|
||||
progressMonitor.setProgress(100);
|
||||
} catch (Exception ex) {
|
||||
//handle unchecked exceptions post image add
|
||||
errorList.add(ex.getMessage());
|
||||
|
||||
logger.log(Level.WARNING, "Unexpected errors occurred while running post add image cleanup for " + imagePath, ex); //NON-NLS
|
||||
logger.log(Level.SEVERE, "Error adding image " + imagePath + " to case", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
doCallBack();
|
||||
}
|
||||
|
||||
/*
|
||||
* Call the callback with results, new content, and errors, if any
|
||||
*/
|
||||
private void doCallBack() {
|
||||
DataSourceProcessorCallback.DataSourceProcessorResult result;
|
||||
if (cancelRequested) {
|
||||
result = DataSourceProcessorCallback.DataSourceProcessorResult.CANCELLED;
|
||||
} else if (hasCritError) {
|
||||
result = DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS;
|
||||
} else if (!errorList.isEmpty()) {
|
||||
result = DataSourceProcessorCallback.DataSourceProcessorResult.NONCRITICAL_ERRORS;
|
||||
} else {
|
||||
result = DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS;
|
||||
}
|
||||
callbackObj.done(result, errorList, newContents);
|
||||
}
|
||||
|
||||
/*
|
||||
* cancel the image addition, if possible
|
||||
*/
|
||||
public void cancelTask() {
|
||||
|
||||
synchronized (lock) {
|
||||
cancelRequested = true;
|
||||
try {
|
||||
interrupt();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Failed to interrupt the add image task..."); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt the add image process if it is still running
|
||||
*/
|
||||
private void interrupt() throws Exception {
|
||||
|
||||
try {
|
||||
logger.log(Level.INFO, "interrupt() add image process"); //NON-NLS
|
||||
addImageProcess.stop(); //it might take time to truly stop processing and writing to db
|
||||
} catch (TskCoreException ex) {
|
||||
throw new Exception(NbBundle.getMessage(this.getClass(), "AddImageTask.interrupt.exception.msg"), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Revert - if image has already been added but not committed yet
|
||||
*/
|
||||
private void revert() {
|
||||
|
||||
if (!reverted) {
|
||||
logger.log(Level.INFO, "Revert after add image process"); //NON-NLS
|
||||
try {
|
||||
addImageProcess.revert();
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Error reverting add image process", ex); //NON-NLS
|
||||
}
|
||||
reverted = true;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean cancelRequested() {
|
||||
synchronized (lock) {
|
||||
return cancelRequested;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +146,7 @@ final class AddImageWizardChooseDataSourceVisual extends JPanel {
|
||||
*
|
||||
* @param panel instance of ImageTypePanel to change to
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
private void updateCurrentPanel(JPanel panel) {
|
||||
currentPanel = panel;
|
||||
typePanel.removeAll();
|
||||
|
||||
@@ -68,6 +68,7 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel<WizardDe
|
||||
private final AddImageWizardChooseDataSourcePanel dataSourcePanel;
|
||||
|
||||
private DataSourceProcessor dsProcessor;
|
||||
private boolean cancelled;
|
||||
|
||||
AddImageWizardIngestConfigPanel(AddImageWizardChooseDataSourcePanel dsPanel, AddImageAction action, AddImageWizardAddingProgressPanel proPanel) {
|
||||
this.addImageAction = action;
|
||||
@@ -228,6 +229,7 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel<WizardDe
|
||||
@Override
|
||||
void cleanup() throws Exception {
|
||||
cancelDataSourceProcessing(dataSourceId);
|
||||
cancelled = true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -244,7 +246,6 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel<WizardDe
|
||||
public void doneEDT(DataSourceProcessorCallback.DataSourceProcessorResult result, List<String> errList, List<Content> contents) {
|
||||
dataSourceProcessorDone(dataSourceId, result, errList, contents);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
progressPanel.setStateStarted();
|
||||
@@ -258,9 +259,6 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel<WizardDe
|
||||
* Cancels the data source processing - in case the users presses 'Cancel'
|
||||
*/
|
||||
private void cancelDataSourceProcessing(UUID dataSourceId) {
|
||||
new Thread(() -> {
|
||||
Case.getCurrentCase().notifyFailedAddingDataSource(dataSourceId);
|
||||
}).start();
|
||||
dsProcessor.cancel();
|
||||
}
|
||||
|
||||
@@ -272,11 +270,6 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel<WizardDe
|
||||
// disable the cleanup task
|
||||
cleanupTask.disable();
|
||||
|
||||
// If the user cancelled, the wizard is already closed, so exit.
|
||||
if (result == DataSourceProcessorCallback.DataSourceProcessorResult.CANCELLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get attention for the process finish
|
||||
java.awt.Toolkit.getDefaultToolkit().beep(); //BEEP!
|
||||
AddImageWizardAddingProgressVisual panel = progressPanel.getComponent();
|
||||
@@ -308,21 +301,23 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel<WizardDe
|
||||
progressPanel.addErrors(err, critErr);
|
||||
}
|
||||
|
||||
newContents.clear();
|
||||
newContents.addAll(contents);
|
||||
|
||||
//notify the UI of the new content added to the case
|
||||
new Thread(() -> {
|
||||
if (!newContents.isEmpty()) {
|
||||
Case.getCurrentCase().notifyDataSourceAdded(newContents.get(0), dataSourceId);
|
||||
if (!contents.isEmpty()) {
|
||||
Case.getCurrentCase().notifyDataSourceAdded(contents.get(0), dataSourceId);
|
||||
} else {
|
||||
Case.getCurrentCase().notifyFailedAddingDataSource(dataSourceId);
|
||||
}
|
||||
}).start();
|
||||
|
||||
// Start ingest if we can
|
||||
progressPanel.setStateStarted();
|
||||
startIngest();
|
||||
if (!cancelled) {
|
||||
newContents.clear();
|
||||
newContents.addAll(contents);
|
||||
progressPanel.setStateStarted();
|
||||
startIngest();
|
||||
} else {
|
||||
cancelled = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,6 @@ class AddLocalFilesTask implements Runnable {
|
||||
private final List<String> localFilePaths;
|
||||
private final DataSourceProcessorProgressMonitor progress;
|
||||
private final DataSourceProcessorCallback callback;
|
||||
private volatile boolean cancellationRequested;
|
||||
|
||||
/**
|
||||
* Constructs a runnable that adds a set of local/logical files and/or
|
||||
@@ -92,9 +91,7 @@ class AddLocalFilesTask implements Runnable {
|
||||
errors.add(ex.getMessage());
|
||||
} finally {
|
||||
DataSourceProcessorCallback.DataSourceProcessorResult result;
|
||||
if (cancellationRequested) {
|
||||
result = DataSourceProcessorCallback.DataSourceProcessorResult.CANCELLED;
|
||||
} else if (!errors.isEmpty()) {
|
||||
if (!errors.isEmpty()) {
|
||||
result = DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS;
|
||||
} else {
|
||||
result = DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS;
|
||||
@@ -103,16 +100,6 @@ class AddLocalFilesTask implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels adding the data source.
|
||||
*
|
||||
* TODO (AUT-1907): Implement cancellation by deleting rows added to the
|
||||
* case database.
|
||||
*/
|
||||
void cancel() {
|
||||
cancellationRequested = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates task progress as the file manager adds the local/logical files
|
||||
* and/or directories to the case database.
|
||||
|
||||
@@ -122,7 +122,7 @@ public class ImageDSProcessor implements DataSourceProcessor {
|
||||
* are valid and complete.
|
||||
*
|
||||
* @return True if the settings are valid and complete and the processor is
|
||||
* ready to have its run method called; false otherwise.
|
||||
* ready to have its run method called, false otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean isPanelValid() {
|
||||
@@ -130,10 +130,11 @@ public class ImageDSProcessor implements DataSourceProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a data source to the case database using a separate thread and the
|
||||
* settings provided by the selection and configuration panel. Returns as
|
||||
* soon as the background task is started. The background task uses the
|
||||
* callback object to signal task completion and return results.
|
||||
* Adds a data source to the case database using a background task in a
|
||||
* separate thread and the settings provided by the selection and
|
||||
* configuration panel. Returns as soon as the background task is started.
|
||||
* The background task uses a callback object to signal task completion and
|
||||
* return results.
|
||||
*
|
||||
* This method should not be called unless isPanelValid returns true.
|
||||
*
|
||||
@@ -155,10 +156,11 @@ public class ImageDSProcessor implements DataSourceProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a data source to the case database using a separate thread and the
|
||||
* given settings instead of those provided by the configuration panel.
|
||||
* Returns as soon as the background task is started and uses the callback
|
||||
* object to signal task completion and return results.
|
||||
* Adds a data source to the case database using a background task in a
|
||||
* separate thread and the given settings instead of those provided by the
|
||||
* selection and configuration panel. Returns as soon as the background task
|
||||
* is started and uses the callback object to signal task completion and
|
||||
* return results.
|
||||
*
|
||||
* @param deviceId An ASCII-printable identifier for the device
|
||||
* associated with the data source that is
|
||||
@@ -183,7 +185,8 @@ public class ImageDSProcessor implements DataSourceProcessor {
|
||||
* Requests cancellation of the background task that adds a data source to
|
||||
* the case database, after the task is started using the run method. This
|
||||
* is a "best effort" cancellation, with no guarantees that the case
|
||||
* database will be unchanged.
|
||||
* database will be unchanged. If cancellation succeeded, the list of new
|
||||
* data sources returned by the background task will be empty.
|
||||
*/
|
||||
@Override
|
||||
public void cancel() {
|
||||
|
||||
@@ -190,7 +190,7 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed
|
||||
String oldText = pathTextField.getText();
|
||||
// set the current directory of the FileChooser if the ImagePath Field is valid
|
||||
|
||||
@@ -101,7 +101,7 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
|
||||
* are valid and complete.
|
||||
*
|
||||
* @return True if the settings are valid and complete and the processor is
|
||||
* ready to have its run method called; false otherwise.
|
||||
* ready to have its run method called, false otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean isPanelValid() {
|
||||
@@ -109,10 +109,11 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a data source to the case database using a separate thread and the
|
||||
* settings provided by the selection and configuration panel. Returns as
|
||||
* soon as the background task is started. The background task uses the
|
||||
* callback object to signal task completion and return results.
|
||||
* Adds a data source to the case database using a background task in a
|
||||
* separate thread and the settings provided by the selection and
|
||||
* configuration panel. Returns as soon as the background task is started.
|
||||
* The background task uses a callback object to signal task completion and
|
||||
* return results.
|
||||
*
|
||||
* This method should not be called unless isPanelValid returns true.
|
||||
*
|
||||
@@ -134,10 +135,11 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a data source to the case database using a separate thread and the
|
||||
* given settings instead of those provided by the panel. Returns as soon as
|
||||
* the background task is started and uses the callback object to signal
|
||||
* task completion and return results.
|
||||
* Adds a data source to the case database using a background task in a
|
||||
* separate thread and the given settings instead of those provided by the
|
||||
* selection and configuration panel. Returns as soon as the background task
|
||||
* is started and uses the callback object to signal task completion and
|
||||
* return results.
|
||||
*
|
||||
* @param deviceId An ASCII-printable identifier for the device
|
||||
* associated with the data source that is
|
||||
@@ -162,7 +164,8 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
|
||||
* Requests cancellation of the background task that adds a data source to
|
||||
* the case database, after the task is started using the run method. This
|
||||
* is a "best effort" cancellation, with no guarantees that the case
|
||||
* database will be unchanged.
|
||||
* database will be unchanged. If cancellation succeeded, the list of new
|
||||
* data sources returned by the background task will be empty.
|
||||
*/
|
||||
@Override
|
||||
public void cancel() {
|
||||
|
||||
@@ -39,7 +39,6 @@ public class LocalFilesDSProcessor implements DataSourceProcessor {
|
||||
|
||||
private static final String DATA_SOURCE_TYPE = NbBundle.getMessage(LocalFilesDSProcessor.class, "LocalFilesDSProcessor.dsType");
|
||||
private final LocalFilesPanel configPanel;
|
||||
private AddLocalFilesTask backgroundTask;
|
||||
/*
|
||||
* TODO: Remove the setDataSourceOptionsCalled flag and the settings fields
|
||||
* when the deprecated method setDataSourceOptions is removed.
|
||||
@@ -100,7 +99,7 @@ public class LocalFilesDSProcessor implements DataSourceProcessor {
|
||||
* are valid and complete.
|
||||
*
|
||||
* @return True if the settings are valid and complete and the processor is
|
||||
* ready to have its run method called; false otherwise.
|
||||
* ready to have its run method called, false otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean isPanelValid() {
|
||||
@@ -108,10 +107,11 @@ public class LocalFilesDSProcessor implements DataSourceProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a data source to the case database using a separate thread and the
|
||||
* settings provided by the selection and configuration panel. Returns as
|
||||
* soon as the background task is started. The background task uses the
|
||||
* callback object to signal task completion and return results.
|
||||
* Adds a data source to the case database using a background task in a
|
||||
* separate thread and the settings provided by the selection and
|
||||
* configuration panel. Returns as soon as the background task is started.
|
||||
* The background task uses a callback object to signal task completion and
|
||||
* return results.
|
||||
*
|
||||
* This method should not be called unless isPanelValid returns true.
|
||||
*
|
||||
@@ -130,11 +130,11 @@ public class LocalFilesDSProcessor implements DataSourceProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a data source to the case database using a separate thread and the
|
||||
* given settings instead of those provided by the selection and
|
||||
* configuration panel. Returns as soon as the background task is started
|
||||
* and uses the callback object to signal task completion and return
|
||||
* results.
|
||||
* Adds a data source to the case database using a background task in a
|
||||
* separate thread and the given settings instead of those provided by the
|
||||
* selection and configuration panel. Returns as soon as the background task
|
||||
* is started and uses the callback object to signal task completion and
|
||||
* return results.
|
||||
*
|
||||
* @param deviceId An ASCII-printable identifier for the
|
||||
* device associated with the data source
|
||||
@@ -153,22 +153,21 @@ public class LocalFilesDSProcessor implements DataSourceProcessor {
|
||||
* @param callback Callback to call when processing is done.
|
||||
*/
|
||||
public void run(String deviceId, String rootVirtualDirectoryName, List<String> localFilePaths, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
backgroundTask = new AddLocalFilesTask(deviceId, rootVirtualDirectoryName, localFilePaths, progressMonitor, callback);
|
||||
new Thread(backgroundTask).start();
|
||||
new Thread(new AddLocalFilesTask(deviceId, rootVirtualDirectoryName, localFilePaths, progressMonitor, callback)).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests cancellation of the background task that adds a data source to
|
||||
* the case database, after the task is started using the run method.
|
||||
* This is a "best effort" cancellation, with no guarantees that the case
|
||||
* database will be unchanged.
|
||||
* the case database, after the task is started using the run method. This
|
||||
* is a "best effort" cancellation, with no guarantees that the case
|
||||
* database will be unchanged. If cancellation succeeded, the list of new
|
||||
* data sources returned by the background task will be empty.
|
||||
*
|
||||
* TODO (AUT-1907): Implement cancellation by deleting rows added to the
|
||||
* case database.
|
||||
*/
|
||||
@Override
|
||||
public void cancel() {
|
||||
backgroundTask.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,11 +31,15 @@ import javax.swing.JPanel;
|
||||
* provide a UI panel to allow a user to select a data source and do any
|
||||
* configuration required by the data source processor. The selection and
|
||||
* configuration panel should support addition of the add data source wizard as
|
||||
* a property change listener and should fire DSP_PANEL_EVENT property changes
|
||||
* to communicate with the wizard.
|
||||
* a property change listener and should fire DSP_PANEL_EVENT property change
|
||||
* events to communicate with the wizard.
|
||||
*
|
||||
* Data source processors should perform all processing on a separate thread,
|
||||
* reporting results using a callback object.
|
||||
* Data source processors should perform all processing in a background task in
|
||||
* a separate thread, reporting results using a callback object.
|
||||
*
|
||||
* It is recommended that implementers provide an overload of the run method
|
||||
* that allows the data source processor to be run independently of the
|
||||
* selection and configuration panel.
|
||||
*/
|
||||
public interface DataSourceProcessor {
|
||||
|
||||
@@ -58,7 +62,9 @@ public interface DataSourceProcessor {
|
||||
/**
|
||||
* This event is fired to make the add data source wizard move focus to
|
||||
* the wizard's next button.
|
||||
* @deprecated Use UPDATE_UI.
|
||||
*/
|
||||
@Deprecated
|
||||
FOCUS_NEXT
|
||||
};
|
||||
|
||||
@@ -86,15 +92,16 @@ public interface DataSourceProcessor {
|
||||
* are valid and complete.
|
||||
*
|
||||
* @return True if the settings are valid and complete and the processor is
|
||||
* ready to have its run method called; false otherwise.
|
||||
* ready to have its run method called, false otherwise.
|
||||
*/
|
||||
boolean isPanelValid();
|
||||
|
||||
/**
|
||||
* Adds a data source to the case database using a separate thread and the
|
||||
* settings provided by the selection and configuration panel. Returns as
|
||||
* soon as the background task is started. The background task uses the
|
||||
* callback object to signal task completion and return results.
|
||||
* Adds a data source to the case database using a background task in a
|
||||
* separate thread and the settings provided by the selection and
|
||||
* configuration panel. Returns as soon as the background task is started.
|
||||
* The background task uses a callback object to signal task completion and
|
||||
* return results.
|
||||
*
|
||||
* This method should not be called unless isPanelValid returns true.
|
||||
*
|
||||
@@ -109,7 +116,8 @@ public interface DataSourceProcessor {
|
||||
* Requests cancellation of the background task that adds a data source to
|
||||
* the case database, after the task is started using the run method. This
|
||||
* is a "best effort" cancellation, with no guarantees that the case
|
||||
* database will be unchanged.
|
||||
* database will be unchanged. If cancellation succeeded, the list of new
|
||||
* data sources returned by the background task will be empty.
|
||||
*/
|
||||
void cancel();
|
||||
|
||||
|
||||
+47
-36
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2014 Basis Technology Corp.
|
||||
* Copyright 2013-2016 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -23,60 +23,71 @@ import java.util.List;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
|
||||
/**
|
||||
* Abstract class for a callback for a DataSourceProcessor.
|
||||
*
|
||||
* Ensures that DSP invokes the caller overridden method, doneEDT(), in the EDT
|
||||
* thread.
|
||||
* An abstract base class for callback objects to be given to data source
|
||||
* processors for use by the background tasks that add data sources to a case
|
||||
* database. The callback objects are used to signal task completion and return
|
||||
* results.
|
||||
*
|
||||
* Concrete implementations of DataSourceProcessorCallback should override
|
||||
* either the done method or the doneEDT method, but not both.
|
||||
*/
|
||||
public abstract class DataSourceProcessorCallback {
|
||||
|
||||
public enum DataSourceProcessorResult {
|
||||
|
||||
NO_ERRORS, ///< No errors were encountered while ading the data source
|
||||
CRITICAL_ERRORS, ///< No data was added to the database. There were fundamental errors processing the data (such as no data or system failure).
|
||||
NONCRITICAL_ERRORS, ///< There was data added to the database, but there were errors from data corruption or a small number of minor issues.
|
||||
/**
|
||||
* Adding the data source was cancelled.
|
||||
* No errors occurred while ading the data source to the case database.
|
||||
*/
|
||||
CANCELLED
|
||||
NO_ERRORS,
|
||||
/**
|
||||
* Critical errors occurred while ading the data source to the case
|
||||
* database. The data source was not added to the case database.
|
||||
*/
|
||||
CRITICAL_ERRORS,
|
||||
/**
|
||||
* Non-critical errors occurred while adding the data source to the case
|
||||
* database. The data source was added to the database, but the data
|
||||
* source may have been corrupted in some way.
|
||||
*/
|
||||
NONCRITICAL_ERRORS
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by a DSP implementation when it is done adding a data source to
|
||||
* the database. Users of the DSP can override this method if they do not
|
||||
* want to be notified on the EDT. Otherwise, this method will call
|
||||
* doneEDT() with the same arguments.
|
||||
* Called by a data source processor when it is done adding a data source to
|
||||
* the case database, this method adds a task to call the doneEDT method to
|
||||
* the EDT task queue.
|
||||
*
|
||||
* @param result Code for status
|
||||
* @param errList List of error strings
|
||||
* @param newContents List of root Content objects that were added to
|
||||
* database. Typically only one is given.
|
||||
* Concrete implementations of DataSourceProcessorCallback should override
|
||||
* this method if the callback SHOULD NOT be done in the EDT.
|
||||
*
|
||||
* @param result Result code.
|
||||
* @param errList List of error messages, possibly empty.
|
||||
* @param newDataSources A list of the data sources added, empty if critical
|
||||
* errors occurred or processing was successfully
|
||||
* cancelled.
|
||||
*/
|
||||
public void done(DataSourceProcessorResult result, List<String> errList, List<Content> newContents) {
|
||||
|
||||
public void done(DataSourceProcessorResult result, List<String> errList, List<Content> newDataSources) {
|
||||
final DataSourceProcessorResult resultf = result;
|
||||
final List<String> errListf = errList;
|
||||
final List<Content> newContentsf = newContents;
|
||||
|
||||
// Invoke doneEDT() that runs on the EDT .
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doneEDT(resultf, errListf, newContentsf);
|
||||
}
|
||||
final List<Content> newContentsf = newDataSources;
|
||||
EventQueue.invokeLater(() -> {
|
||||
doneEDT(resultf, errListf, newContentsf);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by done() if the default implementation is used. Users of DSPs
|
||||
* that have UI updates to do after the DSP is finished adding the DS can
|
||||
* implement this method to receive the updates on the EDT.
|
||||
* Called by a data source processor when it is done adding a data source to
|
||||
* the case database, if the default done method has not been overridden.
|
||||
*
|
||||
* @param result Code for status
|
||||
* @param errList List of error strings
|
||||
* @param newContents List of root Content objects that were added to
|
||||
* database. Typically only one is given.
|
||||
* Concrete implementations of DataSourceProcessorCallback should override
|
||||
* this method if the callback SHOULD be done in the EDT.
|
||||
*
|
||||
* @param result Result code.
|
||||
* @param errList List of error messages, possibly empty.
|
||||
* @param newDataSources A list of the data sources added, empty if critical
|
||||
* errors occurred or processing was successfully
|
||||
* cancelled.
|
||||
*/
|
||||
public abstract void doneEDT(DataSourceProcessorResult result, List<String> errList, List<Content> newContents);
|
||||
public void doneEDT(DataSourceProcessorResult result, List<String> errList, List<Content> newDataSources) {
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user