diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageTask.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageTask.java index e41674555c..dc9774ed72 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageTask.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageTask.java @@ -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 errorList = new ArrayList<>(); - - private final DataSourceProcessorProgressMonitor progressMonitor; - private final DataSourceProcessorCallback callbackObj; - - private final List newContents = Collections.synchronizedList(new ArrayList()); - - 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 errorMessages = new ArrayList<>(); + List 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 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 errorMessages, List 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; - } - } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java index 1db3ab9c5c..489c713af8 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java @@ -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(); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java index dab3325883..6e1d353e22 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java @@ -68,6 +68,7 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel errList, List contents) { dataSourceProcessorDone(dataSourceId, result, errList, contents); } - }; progressPanel.setStateStarted(); @@ -258,9 +259,6 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel { - Case.getCurrentCase().notifyFailedAddingDataSource(dataSourceId); - }).start(); dsProcessor.cancel(); } @@ -272,11 +270,6 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel { - 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; + } } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddLocalFilesTask.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddLocalFilesTask.java index 0c91dc1737..6d1785133a 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddLocalFilesTask.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddLocalFilesTask.java @@ -42,7 +42,6 @@ class AddLocalFilesTask implements Runnable { private final List 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. diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ImageDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/ImageDSProcessor.java index 5a6688908c..fa9f92188c 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/ImageDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/ImageDSProcessor.java @@ -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() { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java index 4d59cd4d91..5c7cca0a59 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java @@ -190,7 +190,7 @@ public class ImageFilePanel extends JPanel implements DocumentListener { .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); }// //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 diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskDSProcessor.java index 3cfa0a6364..c699eb740d 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskDSProcessor.java @@ -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() { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java index 8d3afe1f9f..4eae9e4e7e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java @@ -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 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(); } /** diff --git a/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DataSourceProcessor.java b/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DataSourceProcessor.java index 1ef3c854ec..8e81590231 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DataSourceProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DataSourceProcessor.java @@ -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(); diff --git a/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DataSourceProcessorCallback.java b/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DataSourceProcessorCallback.java index 356f582412..c039355904 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DataSourceProcessorCallback.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DataSourceProcessorCallback.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2014 Basis Technology Corp. + * Copyright 2013-2016 Basis Technology Corp. * Contact: carrier sleuthkit 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 errList, List newContents) { - + public void done(DataSourceProcessorResult result, List errList, List newDataSources) { final DataSourceProcessorResult resultf = result; final List errListf = errList; - final List newContentsf = newContents; - - // Invoke doneEDT() that runs on the EDT . - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - doneEDT(resultf, errListf, newContentsf); - } + final List 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 errList, List newContents); + public void doneEDT(DataSourceProcessorResult result, List errList, List newDataSources) { + } };