From b93d45fbf12824d7172ac6297ce4e6c24fbe1ed4 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 18 Jan 2016 19:06:01 -0500 Subject: [PATCH] Fix thread safety, clean up add data source wizard classes --- .../autopsy/casemodule/AddImageAction.java | 247 ++++++------- .../AddImageWizardAddingProgressPanel.java | 178 ++++------ .../AddImageWizardChooseDataSourcePanel.java | 177 +++------- .../AddImageWizardIngestConfigPanel.java | 333 +++++++++++------- .../casemodule/AddImageWizardIterator.java | 95 +++-- .../autopsy/casemodule/Bundle.properties | 1 - .../autopsy/casemodule/Bundle_ja.properties | 1 - 7 files changed, 478 insertions(+), 554 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageAction.java index ee30eb8283..2bb2a58bd3 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2014 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,13 +21,11 @@ package org.sleuthkit.autopsy.casemodule; import java.awt.Component; import java.awt.Dialog; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.util.logging.Level; import javax.swing.Action; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JOptionPane; -import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.openide.DialogDisplayer; @@ -37,139 +35,91 @@ import org.openide.util.HelpCtx; import org.openide.util.NbBundle; import org.openide.util.actions.CallableSystemAction; import org.openide.util.actions.Presenter; -import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.datamodel.Image; /** - * The action to add an image to the current Case. This action should be - * disabled on creation and it will be enabled on new case creation or case - * opened. + * An action that adds a data source to the current case. * - * @author jantonius + * RC: This action needs to be enabled and disabled as cases are opened and + * closed. Currently this is done using + * CallableSystemAction.get(AddImageAction.class).setEnabled(). */ -// TODO: need annotation because there's a "Lookup.getDefault().lookup(AddImageAction.class)" -// used in AddImageWizardPanel1 (among other places). It really shouldn't be done like that. -@ServiceProvider(service = AddImageAction.class) public final class AddImageAction extends CallableSystemAction implements Presenter.Toolbar { - // Keys into the WizardDescriptor properties that pass information between stages of the wizard - // : - // String: time zone that the image is from - static final String TIMEZONE_PROP = "timeZone"; //NON-NLS - // String[]: array of paths to each data source selected - static final String DATASOURCEPATH_PROP = "dataSrcPath"; //NON-NLS - // String data source type selected - static final String DATASOURCETYPE_PROP = "dataSrcType"; //NON-NLS - // CleanupTask: task to clean up the database file if wizard errors/is cancelled after it is created - static final String IMAGECLEANUPTASK_PROP = "finalFileCleanup"; //NON-NLS - // int: the next availble id for a new image - static final String IMAGEID_PROP = "imageId"; //NON-NLS - // AddImageProcess: the next availble id for a new image - static final String PROCESS_PROP = "process"; //NON-NLS - // boolean: whether or not to lookup files in the hashDB - static final String LOOKUPFILES_PROP = "lookupFiles"; //NON-NLS - // boolean: whether or not to skip processing orphan files on FAT filesystems - static final String NOFATORPHANS_PROP = "nofatorphans"; //NON-NLS - - static final Logger logger = Logger.getLogger(AddImageAction.class.getName()); - + private static final long serialVersionUID = 1L; + private static final Logger logger = Logger.getLogger(AddImageAction.class.getName()); + private final ChangeSupport cleanupSupport; + private final JButton toolbarButton; private WizardDescriptor wizardDescriptor; private WizardDescriptor.Iterator iterator; - private Dialog dialog; - private JButton toolbarButton = new JButton(); /** - * The constructor for AddImageAction class + * Constructs an action that adds a data source to the current case. */ public AddImageAction() { - putValue(Action.NAME, NbBundle.getMessage(AddImageAction.class, "CTL_AddImage")); // set the action Name - - // set the action for the toolbar button - toolbarButton.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - AddImageAction.this.actionPerformed(e); - } - }); - - this.setEnabled(false); // disable this action class + cleanupSupport = new ChangeSupport(this); + putValue(Action.NAME, NbBundle.getMessage(AddImageAction.class, "CTL_AddImage")); + toolbarButton = new JButton(); + toolbarButton.addActionListener(AddImageAction.this::actionPerformed); + setEnabled(false); } /** - * Pop-up the "Add Image" wizard panel. + * Displays the first panel of the add data source wizard. * - * @param e + * @param notUsed An action event, may be null. */ @Override - public void actionPerformed(ActionEvent e) { + public void actionPerformed(ActionEvent notUsed) { + /* + * If ingest is running, confirm that the user wants to add another data + * source at this time, instead of waiting for the current ingest job to + * complete. + */ if (IngestManager.getInstance().isIngestRunning()) { - final String msg = NbBundle.getMessage(this.getClass(), "AddImageAction.ingestConfig.ongoingIngest.msg"); - if (JOptionPane.showConfirmDialog(null, msg, - NbBundle.getMessage(this.getClass(), - "AddImageAction.ingestConfig.ongoingIngest.title"), + if (JOptionPane.showConfirmDialog(null, + NbBundle.getMessage(this.getClass(), "AddImageAction.ingestConfig.ongoingIngest.msg"), + NbBundle.getMessage(this.getClass(), "AddImageAction.ingestConfig.ongoingIngest.title"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.NO_OPTION) { return; } } - iterator = new AddImageWizardIterator(this); + /* + * Construct and display the wizard. + */ + iterator = new AddImageWizardIterator(); wizardDescriptor = new WizardDescriptor(iterator); wizardDescriptor.setTitle(NbBundle.getMessage(this.getClass(), "AddImageAction.wizard.title")); - wizardDescriptor.putProperty(NAME, e); - - if (dialog != null) { - dialog.setVisible(false); // hide the old one - } - dialog = DialogDisplayer.getDefault().createDialog(wizardDescriptor); + Dialog dialog = DialogDisplayer.getDefault().createDialog(wizardDescriptor); dialog.setVisible(true); dialog.toFront(); - // Do any cleanup that needs to happen (potentially: stopping the - //add-image process, reverting an image) - runCleanupTasks(); + /* + * Run any registered cleanup tasks by firing a change event. This will + * cause the stateChanged method of any implementations of the inner, + * abstract CleanupTask class to call their cleanup methods (assuming + * they have not done an override of stateChanged), after which the + * CleanupTasks are unregistered. + * + * RC: This is a convoluted and error-prone way to implement clean up. + * Fortunately, it is confined to this package. + */ + cleanupSupport.fireChange(); } /** - * Closes the current dialog and wizard, and opens a new one. Used in the - * "Add another image" action on the last panel - */ - void restart() { - // Simulate clicking finish for the current dialog - wizardDescriptor.setValue(WizardDescriptor.FINISH_OPTION); - dialog.setVisible(false); - - // let the previous call to AddImageAction.actionPerformed() finish up - // after the wizard, this will run when its it's done - final Runnable r = new Runnable() { - @Override - public void run() { - actionPerformed(null); - } - }; - - SwingUtilities.invokeLater(r); - } - - public interface IndexImageTask { - - void runTask(Image newImage); - } - - /** - * This method does nothing. Use the "actionPerformed(ActionEvent e)" - * instead of this method. + * @inheritDoc */ @Override public void performAction() { + actionPerformed(null); } /** - * Gets the name of this action. This may be presented as an item in a menu. - * - * @return actionName + * @inheritDoc */ @Override public String getName() { @@ -177,9 +127,7 @@ public final class AddImageAction extends CallableSystemAction implements Presen } /** - * Gets the HelpCtx associated with implementing object - * - * @return HelpCtx or HelpCtx.DEFAULT_HELP + * @inheritDoc */ @Override public HelpCtx getHelpCtx() { @@ -187,9 +135,7 @@ public final class AddImageAction extends CallableSystemAction implements Presen } /** - * Returns the toolbar component of this action - * - * @return component the toolbar button + * @inheritDoc */ @Override public Component getToolbarPresenter() { @@ -200,9 +146,7 @@ public final class AddImageAction extends CallableSystemAction implements Presen } /** - * Set this action to be enabled/disabled - * - * @param value whether to enable this action or not + * @inheritDoc */ @Override public void setEnabled(boolean value) { @@ -211,68 +155,80 @@ public final class AddImageAction extends CallableSystemAction implements Presen } /** - * Set the focus to the button of the given name on this wizard dialog. + * Does nothing, do not use. * - * Note: the name of the buttons that available are "Next >", "< Back", - * "Cancel", and "Finish". If you change the name of any of those buttons, - * use the latest name instead. - * - * @param buttonText the text of the button + * @deprecated Classes in this package may call requestFocusForWizardButton + * instead. */ + @Deprecated public void requestFocusButton(String buttonText) { - // get all buttons on this wizard panel - Object[] wizardButtons = wizardDescriptor.getOptions(); - for (int i = 0; i < wizardButtons.length; i++) { - JButton tempButton = (JButton) wizardButtons[i]; - if (tempButton.getText().equals(buttonText)) { - tempButton.setDefaultCapable(true); - tempButton.requestFocus(); + } + + /** + * Requests focus for an add data source wizard button. + * + * @param buttonText The text of the button. + */ + void requestFocusForWizardButton(String buttonText) { + for (Object wizardButton : wizardDescriptor.getOptions()) { + JButton button = (JButton) wizardButton; + if (button.getText().equals(buttonText)) { + button.setDefaultCapable(true); + button.requestFocus(); } } } /** - * Run and clear any cleanup tasks for wizard closing that might be - * registered. This should be run even when the wizard exits cleanly, so - * that no cleanup actions remain the next time the wizard is run. - */ - private void runCleanupTasks() { - cleanupSupport.fireChange(); - } - - ChangeSupport cleanupSupport = new ChangeSupport(this); - - /** - * Instances of this class implement the cleanup() method to run cleanup - * code when the wizard exits. + * Enabled instances of this class are called to do clean up after the add + * data source wizard is closed. The instances are disabled after the + * cleanUp method is called. Implementations should not override + * stateChanged, and should not re-enable themselves after cleanUp is + * called. To stop cleanUp being called, call disable before the wizard is + * dismissed. * - * After enable() has been called on an instance it will run once after the - * wizard closes (on both a cancel and a normal finish). + * Instances must be constructed using a reference to an AddImageAction + * object because this is a non-static inner class. * - * If disable() is called before the wizard exits, the task will not run. + * RC: This is a convoluted and error-prone way to implement clean up. + * Fortunately, it is confined to this package. */ abstract class CleanupTask implements ChangeListener { @Override public void stateChanged(ChangeEvent e) { - // fired by AddImageAction.runCleanupTasks() after the wizard closes + /* + * actionPerformed fires this event after the add data source wizard + * is closed. + */ try { cleanup(); } catch (Exception ex) { - Logger logger = Logger.getLogger(this.getClass().getName()); - logger.log(Level.WARNING, "Error cleaning up from wizard.", ex); //NON-NLS + logger.log(Level.SEVERE, "Error cleaning in add data source wizard clean up task", ex); //NON-NLS } finally { - disable(); // cleanup tasks should only run once. + /* + * Clean up tasks should only be done exactly once. + */ + disable(); } } /** - * Add task to the enabled list to run when the wizard closes. + * Adds this task to the list of tasks to be done when the wizard + * closes. */ - public void enable() { + void enable() { cleanupSupport.addChangeListener(this); } + /** + * Removes this task from the list of tasks to be done when the wizard + * closes. + */ + void disable() { + cleanupSupport.removeChangeListener(this); + } + /** * Performs cleanup action when called * @@ -280,11 +236,12 @@ public final class AddImageAction extends CallableSystemAction implements Presen */ abstract void cleanup() throws Exception; - /** - * Remove task from the enabled list. - */ - public void disable() { - cleanupSupport.removeChangeListener(this); - } } + + @Deprecated + public interface IndexImageTask { + + void runTask(Image newImage); + } + } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java index 7204664d2f..f64f41091d 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2014 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,105 +20,97 @@ package org.sleuthkit.autopsy.casemodule; import java.awt.Color; import java.awt.EventQueue; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; -import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.openide.WizardDescriptor; +import org.openide.util.ChangeSupport; import org.openide.util.HelpCtx; -import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; /** - * The final panel of the add image wizard. It displays a progress bar and - * status updates. - * - * All the real work is kicked off in the previous panel: - * {@link AddImageWizardIngestConfigPanel} (which is a bit weird if you ask m - * -jm) + * The third and final panel of the add data source wizard. The visual component + * of this panel displays a progress bar that is managed by the previous panel + * and the data source processor. */ -class AddImageWizardAddingProgressPanel implements WizardDescriptor.FinishablePanel { +// All the real work is kicked off in the previous panel: +// {@link AddImageWizardIngestConfigPanel} (which is a bit weird if you ask m +// -jm) +final class AddImageWizardAddingProgressPanel implements WizardDescriptor.FinishablePanel { - /** - * flag to indicate that the image adding process is finished and this panel - * is completed(valid) - */ - private boolean imgAdded = false; - /** - * The visual component that displays this panel. If you need to access the - * component from this class, just use getComponent(). - */ + private final ChangeSupport changeSupport; + private final DSPProgressMonitorImpl dspProgressMonitor = new DSPProgressMonitorImpl(); private AddImageWizardAddingProgressVisual component; - private final Set listeners = new HashSet<>(1); // or can use ChangeSupport in NB 6.0 + private boolean dataSourceAdded = false; - private DSPProgressMonitorImpl dspProgressMonitorImpl = new DSPProgressMonitorImpl(); - - public DSPProgressMonitorImpl getDSPProgressMonitorImpl() { - return dspProgressMonitorImpl; + /** + * Constructs an instance of the third and final panel of the add data + * source wizard. + */ + AddImageWizardAddingProgressPanel() { + changeSupport = new ChangeSupport(this); } + /** + * Gets the data source progress monitor. Allows the previous panel to hand + * off the progress monitor to the data source processor. + */ + DSPProgressMonitorImpl getDSPProgressMonitor() { + return dspProgressMonitor; + } + + /** + * A data source processor progress monitor that acts as a bridge between + * the data source processor started by the previous panel and the progress + * bar displayed by the visual component of this panel. + */ private class DSPProgressMonitorImpl implements DataSourceProcessorProgressMonitor { @Override public void setIndeterminate(final boolean indeterminate) { - // update the progress bar asynchronously - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - getComponent().getProgressBar().setIndeterminate(indeterminate); - } + EventQueue.invokeLater(() -> { + getComponent().getProgressBar().setIndeterminate(indeterminate); }); } @Override public void setProgress(final int progress) { // update the progress bar asynchronously - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - getComponent().getProgressBar().setValue(progress); - } + EventQueue.invokeLater(() -> { + getComponent().getProgressBar().setValue(progress); }); } @Override public void setProgressText(final String text) { // update the progress UI asynchronously - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - getComponent().setProgressMsgText(text); - } + EventQueue.invokeLater(() -> { + getComponent().setProgressMsgText(text); }); } } /** - * Get the visual component for the panel. In this template, the component + * Gets the visual component for the panel. In this template, the component * is kept separate. This can be more efficient: if the wizard is created * but never displayed, or not all panels are displayed, it is better to * create only those which really need to be visible. * - * It also separates the view from the control - jm - * - * @return component the UI component of this wizard panel + * @return The UI component of this wizard panel. */ @Override public AddImageWizardAddingProgressVisual getComponent() { - if (component == null) { + if (null == component) { component = new AddImageWizardAddingProgressVisual(); } return component; } /** - * Help for this panel. When the panel is active, this is used as the help - * for the wizard dialog. + * Gets the help for this panel. When the panel is active, this is used as + * the help for the wizard dialog. * - * @return HelpCtx.DEFAULT_HELP the help for this panel + * @return The help for this panel */ @Override public HelpCtx getHelp() { @@ -134,17 +126,12 @@ class AddImageWizardAddingProgressPanel implements WizardDescriptor.FinishablePa */ @Override public boolean isValid() { - // set the focus to the next button of the wizard dialog if it's enabled - if (imgAdded) { - Lookup.getDefault().lookup(AddImageAction.class).requestFocusButton( - NbBundle.getMessage(this.getClass(), "AddImageWizardAddingProgressPanel.isValid.focusNext")); - } - - return imgAdded; + return dataSourceAdded; } /** - * Updates the UI to display the add image process has begun. + * Makes the progress bar of the visual component of this panel indicate + * that the data source processor is running. */ void setStateStarted() { component.getProgressBar().setIndeterminate(true); @@ -153,94 +140,85 @@ class AddImageWizardAddingProgressPanel implements WizardDescriptor.FinishablePa } /** - * Updates the UI to display the add image process is over. + * Makes the progress bar of the visual component of this panel indicate + * that the data source processor is not running. */ void setStateFinished() { - imgAdded = true; + dataSourceAdded = true; getComponent().setStateFinished(); fireChangeEvent(); } /** - * Adds a listener to changes of the panel's validity. - * - * @param l the change listener to add + * @inheritDoc5 */ @Override - public final void addChangeListener(ChangeListener l) { - synchronized (listeners) { - listeners.add(l); - } + public final void addChangeListener(ChangeListener listener) { + changeSupport.addChangeListener(listener); } /** - * Removes a listener to changes of the panel's validity. - * - * @param l the change listener to move + * @inheritDoc */ @Override - public final void removeChangeListener(ChangeListener l) { - synchronized (listeners) { - listeners.remove(l); - } + public final void removeChangeListener(ChangeListener listener) { + changeSupport.removeChangeListener(listener); } /** - * This method is auto-generated. It seems that this method is used to - * listen to any change in this wizard panel. + * @inheritDoc */ protected final void fireChangeEvent() { - Iterator it; - synchronized (listeners) { - it = new HashSet(listeners).iterator(); - } - ChangeEvent ev = new ChangeEvent(this); - while (it.hasNext()) { - it.next().stateChanged(ev); - } + changeSupport.fireChange(); } /** - * Load the image locations from the WizardDescriptor settings object, and - * the + * Provides the wizard panel with the current data--either the default data + * or already-modified settings, if the user used the previous and/or next + * buttons. This method can be called multiple times on one instance of + * WizardDescriptor.Panel. * - * @param settings the setting to be read from + * @param settings The settings. */ @Override public void readSettings(WizardDescriptor settings) { settings.setOptions(new Object[]{WizardDescriptor.PREVIOUS_OPTION, WizardDescriptor.NEXT_OPTION, WizardDescriptor.FINISH_OPTION, WizardDescriptor.CANCEL_OPTION}); - if (imgAdded) { + if (dataSourceAdded) { getComponent().setStateFinished(); } } /** - * this doesn't appear to store anything? plus, there are no settings in - * this panel -jm + * Provides the wizard panel with the opportunity to update the settings + * with its current customized state. Rather than updating its settings with + * every change in the GUI, it should collect them, and then only save them + * when requested to by this method. This method can be called multiple + * times on one instance of WizardDescriptor.Panel. * * @param settings the setting to be stored to */ @Override public void storeSettings(WizardDescriptor settings) { - //why did we do this? -jm - // getComponent().resetInfoPanel(); } /** - * forward errors to visual component + * Displays an error message from the data source processor for the data + * source being added. * - * should this be modified to handle a list of errors? -jm - * - * - * @param errorString the error string to be displayed - * @param critical true if this is a critical error + * @param errorString The error message to be displayed + * @param critical True if this is a critical error */ - void addErrors(String errorString, boolean critical) { + // Should this be modified to handle a list of errors? -jm + void displayDataSourceProcessorError(String errorString, boolean critical) { getComponent().showErrors(errorString, critical); } + /** + * @inheritDoc + */ @Override public boolean isFinishPanel() { return true; } + } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourcePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourcePanel.java index 88762ba96a..ba7d7fb7ba 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourcePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourcePanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,55 +20,43 @@ package org.sleuthkit.autopsy.casemodule; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; -import java.util.logging.Level; import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.coreutils.Logger; -import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.openide.WizardDescriptor; import org.openide.util.HelpCtx; -import org.openide.util.Lookup; import org.openide.windows.WindowManager; import java.awt.Cursor; +import org.openide.util.ChangeSupport; +import org.openide.util.actions.CallableSystemAction; /** - * The "Add Image" wizard panel1 handling the logic of selecting image file(s) - * to add to Case, and pick the time zone. + * The first panel of the add data source wizard. The visual component of this + * panel allows a user to select data sources to add to the current case. */ -class AddImageWizardChooseDataSourcePanel implements WizardDescriptor.Panel, PropertyChangeListener { +final class AddImageWizardChooseDataSourcePanel implements WizardDescriptor.Panel, PropertyChangeListener { + + private AddImageWizardChooseDataSourceVisual component; + private final ChangeSupport changeSupport; + private boolean nextButtonIsEnabled = false; /** - * The visual component that displays this panel. If you need to access the - * component from this class, just use getComponent(). + * Constructs an instance of the first panel of the add data source wizard. */ - private AddImageWizardAddingProgressPanel progressPanel; - private AddImageWizardChooseDataSourceVisual component; - private boolean isNextEnable = false; - private static final String PROP_LASTDATASOURCE_PATH = "LBL_LastDataSource_PATH"; //NON-NLS - private static final String PROP_LASTDATASOURCE_TYPE = "LBL_LastDataSource_TYPE"; //NON-NLS - // paths to any set hash lookup databases (can be null) - private String NSRLPath, knownBadPath; - - AddImageWizardChooseDataSourcePanel(AddImageWizardAddingProgressPanel proPanel) { - - this.progressPanel = proPanel; - + AddImageWizardChooseDataSourcePanel() { + changeSupport = new ChangeSupport(this); } /** - * Get the visual component for the panel. In this template, the component - * is kept separate. This can be more efficient: if the wizard is created - * but never displayed, or not all panels are displayed, it is better to - * create only those which really need to be visible. + * Gets the visual component for the panel. The component is kept separate. + * This can be more efficient: if the wizard is created but never displayed, + * or not all panels are displayed, it is better to create only those which + * really need to be visible. * - * @return component the UI component of this wizard panel + * @return The UI component of this wizard panel. */ @Override public AddImageWizardChooseDataSourceVisual getComponent() { - if (component == null) { + if (null == component) { WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); component = new AddImageWizardChooseDataSourceVisual(this); } @@ -77,156 +65,103 @@ class AddImageWizardChooseDataSourcePanel implements WizardDescriptor.Panel listeners = new HashSet(1); // or can use ChangeSupport in NB 6.0 /** - * Adds a listener to changes of the panel's validity. - * - * @param l the change listener to add + * @inheritDoc */ @Override - public final void addChangeListener(ChangeListener l) { - synchronized (listeners) { - listeners.add(l); - } + public final void addChangeListener(ChangeListener listener) { + changeSupport.addChangeListener(listener); } /** - * Removes a listener to changes of the panel's validity. - * - * @param l the change listener to move + * @inheritDoc */ @Override - public final void removeChangeListener(ChangeListener l) { - synchronized (listeners) { - listeners.remove(l); - } + public final void removeChangeListener(ChangeListener listener) { + changeSupport.removeChangeListener(listener); } /** - * This method is auto-generated. It seems that this method is used to - * listen to any change in this wizard panel. + * @inheritDoc */ protected final void fireChangeEvent() { - Iterator it; - synchronized (listeners) { - it = new HashSet(listeners).iterator(); - } - ChangeEvent ev = new ChangeEvent(this); - while (it.hasNext()) { - it.next().stateChanged(ev); - } + changeSupport.fireChange(); } - // You can use a settings object to keep track of state. Normally the - // settings object will be the WizardDescriptor, so you can use - // WizardDescriptor.getProperty & putProperty to store information entered - // by the user. /** * Provides the wizard panel with the current data--either the default data * or already-modified settings, if the user used the previous and/or next * buttons. This method can be called multiple times on one instance of - * WizardDescriptor.Panel. s + * WizardDescriptor.Panel. * - * @param settings the setting to be read from + * @param settings The settings. */ @Override public void readSettings(WizardDescriptor settings) { - - //reset settings if supports it - //getComponent().reset(); - // Prepopulate the image directory from the properties file - try { - - // Load hash database settings, enable or disable the checkbox - this.NSRLPath = null; - this.knownBadPath = null; - //JCheckBox lookupFilesCheckbox = component.getLookupFilesCheckbox(); - //lookupFilesCheckbox.setSelected(false); - //lookupFilesCheckbox.setEnabled(this.NSRLPath != null || this.knownBadPath != null); - - // If there is a process object in the settings, revert it and remove it from the settings - AddImageAction.CleanupTask cleanupTask = (AddImageAction.CleanupTask) settings.getProperty(AddImageAction.IMAGECLEANUPTASK_PROP); - if (cleanupTask != null) { - try { - cleanupTask.cleanup(); - } catch (Exception ex) { - Logger logger = Logger.getLogger(AddImageWizardChooseDataSourcePanel.class.getName()); - logger.log(Level.WARNING, "Error cleaning up image task", ex); //NON-NLS - } finally { - cleanupTask.disable(); - } - } - } catch (Exception e) { - } - } /** - * Provides the wizard panel with the opportunity to update the settings - * with its current customized state. Rather than updating its settings with - * every change in the GUI, it should collect them, and then only save them - * when requested to by this method. This method can be called multiple - * times on one instance of WizardDescriptor.Panel. - * - * @param settings the setting to be stored to + * @inheritDoc */ @Override public void storeSettings(WizardDescriptor settings) { - - return; } /** - * The "listener" for any property change in this panel. Any property - * changes will invoke the "fireChangeEvent()" method. - * - * @param evt the property change event + * @inheritDoc */ @Override public void propertyChange(PropertyChangeEvent evt) { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java index 46114c52fb..5d83ec3994 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2015 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,7 +31,9 @@ import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import javax.swing.event.ChangeListener; import org.openide.WizardDescriptor; +import org.openide.util.ChangeSupport; import org.openide.util.HelpCtx; +import org.openide.util.actions.CallableSystemAction; import org.sleuthkit.datamodel.Content; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; @@ -40,80 +42,108 @@ import org.sleuthkit.autopsy.ingest.IngestJobSettingsPanel; import org.sleuthkit.autopsy.ingest.IngestManager; /** - * second panel of add image wizard, allows user to configure ingest modules. - * - * TODO: review this for dead code. think about moving logic of adding image to - * 3rd panel( {@link AddImageWizardAddingProgressPanel}) separate class -jm + * The second panel of the add data source wizard. The visual component of this + * panel allows a user to configure the ingest modules for the ingest job for + * the data source, and this panel both runs the data source processor and + * starts the ingest job. */ -class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel { - - private final IngestJobSettingsPanel ingestJobSettingsPanel; - - /** - * The visual component that displays this panel. If you need to access the - * component from this class, just use getComponent(). - */ - private Component component = null; - - private final List newContents = Collections.synchronizedList(new ArrayList()); - private boolean ingested = false; - private boolean readyToIngest = false; - - // task that will clean up the created database file if the wizard is cancelled before it finishes - private AddImageAction.CleanupTask cleanupTask; - - private final AddImageAction addImageAction; +// JM: Think about moving the logic of adding image to the 3rd panel +// ( {@link AddImageWizardAddingProgressPanel}) +final class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel { + private final AddImageWizardChooseDataSourcePanel selectDataSourcePanel; private final AddImageWizardAddingProgressPanel progressPanel; - private final AddImageWizardChooseDataSourcePanel dataSourcePanel; - + private final ChangeSupport changeSupport; + private final List newContents; + private final IngestJobSettingsPanel ingestJobSettingsPanel; + private Component component; + private volatile boolean ingestModulesConfigured; + private volatile boolean ingestJobStarted; + private AddImageAction.CleanupTask cleanupTask; private DataSourceProcessor dsProcessor; - AddImageWizardIngestConfigPanel(AddImageWizardChooseDataSourcePanel dsPanel, AddImageAction action, AddImageWizardAddingProgressPanel proPanel) { - this.addImageAction = action; - this.progressPanel = proPanel; - this.dataSourcePanel = dsPanel; + /** + * Constructs an instance of The second panel of the add data source wizard. + * The visual component of this panel allows a user to configure the ingest + * modules for the ingest job for the data source. + * + * @param selectDataSourcePanel The second panel of the add data source + * wizard. + * @param progressPanel The third panel of the data source wizard. + */ + AddImageWizardIngestConfigPanel(AddImageWizardChooseDataSourcePanel selectDataSourcePanel, AddImageWizardAddingProgressPanel progressPanel) { + this.selectDataSourcePanel = selectDataSourcePanel; + this.progressPanel = progressPanel; + changeSupport = new ChangeSupport(this); + /* + * Create a collection to receive the Content objects returned by the + * data source processor. + */ + newContents = Collections.synchronizedList(new ArrayList()); + + /* + * Get the ingest job settings for the add data source wizard context + * and use them to create an ingest module configuration panel. + */ IngestJobSettings ingestJobSettings = new IngestJobSettings(AddImageWizardIngestConfigPanel.class.getCanonicalName()); - showWarnings(ingestJobSettings); - this.ingestJobSettingsPanel = new IngestJobSettingsPanel(ingestJobSettings); + showIngestModuleConfigurationWarnings(ingestJobSettings); + ingestJobSettingsPanel = new IngestJobSettingsPanel(ingestJobSettings); + + /* + * This flag is used to stop the data source processing thread from + * starting the ingest job before the user has finished configuring the + * ingest modules. It is only set in the EDT. + */ + ingestModulesConfigured = false; + + /* + * This flag is required because because the storeSettings method is + * currently called twice during the course of executing the add data + * source wizard, whether invoked from the new case wizard or + * independently. This means that the tryStartDataSourceIngestJob method + * gets called three times: once when the data source processor + * completes, and twice when the two storeSettings calls are made. + * + * TODO (AUT-1864): Figure out whether storeSettings is always called + * twice by NetBeans, or the extra call is due to an Autopsy bug. + */ + ingestJobStarted = false; } /** - * Get the visual component for the panel. In this template, the component + * Gets the visual component for the panel. In this template, the component * is kept separate. This can be more efficient: if the wizard is created * but never displayed, or not all panels are displayed, it is better to * create only those which really need to be visible. * - * @return component the UI component of this wizard panel + * @return The UI component of this wizard panel. */ @Override public Component getComponent() { - if (component == null) { + if (null == component) { component = new AddImageWizardIngestConfigVisual(this.ingestJobSettingsPanel); } return component; } /** - * Help for this panel. When the panel is active, this is used as the help - * for the wizard dialog. + * Gets the help for this panel. When the panel is active, this is used as + * the help for the wizard dialog. * - * @return HelpCtx.DEFAULT_HELP the help for this panel + * @return The help for this panel */ @Override public HelpCtx getHelp() { // Show no Help button for this panel: return HelpCtx.DEFAULT_HELP; - // If you have context help: - // return new HelpCtx(SampleWizardPanel1.class); } /** - * Tests whether the panel is finished. If the panel is valid, the "Finish" - * button will be enabled. + * Tests whether or not the panel is finished. If the panel is valid, the + * "Finish" button will be enabled. * - * @return true the finish button should be always enabled at this point + * @return True or false. */ @Override public boolean isValid() { @@ -123,52 +153,53 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel warnings = ingestJobSettings.getWarnings(); if (warnings.isEmpty() == false) { StringBuilder warningMessage = new StringBuilder(); @@ -204,76 +248,78 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel { + /* + * Publish a failed adding data source event, using the + * event id associated with the adding data source event. + */ + Case.getCurrentCase().notifyFailedAddingDataSource(addDataSourceEventId); + }).start(); + dsProcessor.cancel(); } }; - cleanupTask.enable(); - // get the selected DSProcessor - dsProcessor = dataSourcePanel.getComponent().getCurrentDSProcessor(); - + /* + * Publish an adding data source event. + */ new Thread(() -> { - Case.getCurrentCase().notifyAddingDataSource(dataSourceId); + Case.getCurrentCase().notifyAddingDataSource(addDataSourceEventId); }).start(); - DataSourceProcessorCallback cbObj = new DataSourceProcessorCallback() { - @Override - public void doneEDT(DataSourceProcessorCallback.DataSourceProcessorResult result, List errList, List contents) { - dataSourceProcessorDone(dataSourceId, result, errList, contents); - } - - }; + /* + * Notify the progress panel for this wizard that data source processing + * is starting. + */ progressPanel.setStateStarted(); - // Kick off the DSProcessor - dsProcessor.run(progressPanel.getDSPProgressMonitorImpl(), cbObj); - + /* + * Get the data source processor the user selected and start it. + */ + dsProcessor = selectDataSourcePanel.getComponent().getCurrentDSProcessor(); + DataSourceProcessorCallback callback = new DataSourceProcessorCallback() { + @Override + public void doneEDT(DataSourceProcessorCallback.DataSourceProcessorResult result, List errList, List contents) { + dataSourceProcessorDone(addDataSourceEventId, result, errList, contents); + } + }; + dsProcessor.run(progressPanel.getDSPProgressMonitor(), callback); } /* - * Cancels the data source processing - in case the users presses 'Cancel' + * The callback for the data source processor to invoke when it finishes. */ - private void cancelDataSourceProcessing(UUID dataSourceId) { - new Thread(() -> { - Case.getCurrentCase().notifyFailedAddingDataSource(dataSourceId); - }).start(); - dsProcessor.cancel(); - } - - /* - * Callback for the data source processor. Invoked by the DSP on the EDT - * thread, when it finishes processing the data source. - */ - private void dataSourceProcessorDone(UUID dataSourceId, DataSourceProcessorCallback.DataSourceProcessorResult result, List errList, List contents) { - // disable the cleanup task + private void dataSourceProcessorDone(UUID addDataSourceEventId, DataSourceProcessorCallback.DataSourceProcessorResult result, List errList, List contents) { + /* + * Disable the clean up task. + */ cleanupTask.disable(); - // Get attention for the process finish - java.awt.Toolkit.getDefaultToolkit().beep(); //BEEP! + /* + * Get the user's attention. + * + * RC: Is this really necessary? + */ + java.awt.Toolkit.getDefaultToolkit().beep(); AddImageWizardAddingProgressVisual panel = progressPanel.getComponent(); if (panel != null) { Window w = SwingUtilities.getWindowAncestor(panel); @@ -281,10 +327,12 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel { if (!newContents.isEmpty()) { - Case.getCurrentCase().notifyDataSourceAdded(newContents.get(0), dataSourceId); + Case.getCurrentCase().notifyDataSourceAdded(newContents.get(0), addDataSourceEventId); } else { - Case.getCurrentCase().notifyFailedAddingDataSource(dataSourceId); + Case.getCurrentCase().notifyFailedAddingDataSource(addDataSourceEventId); } }).start(); - // Start ingest if we can - progressPanel.setStateStarted(); - startIngest(); - + /* + * Try starting the ingest job for this data source. If the data source + * processor finished before the user finished configuring the ingest + * modules, the job will not be started yet. + */ + tryStartIngestJob(); } + + /** + * Starts an ingest job for the data source, but only if the data source + * processor has been run and has produced content, and the ingest modules + * are configured. + */ + synchronized private void tryStartIngestJob() { + if (!newContents.isEmpty() && ingestModulesConfigured && !ingestJobStarted) { + ingestJobStarted = true; // See contructor for comment on this flag. + IngestManager.getInstance().queueIngestJob(newContents, ingestJobSettingsPanel.getSettings()); + } + } + } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIterator.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIterator.java index 72f778e9de..034f3e5391 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIterator.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIterator.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,52 +28,52 @@ import org.openide.WizardDescriptor; import org.openide.util.NbBundle; /** - * The iterator class for the "Add Image" wizard panel. This class is used to - * iterate on the sequence of panels of the "Add Image" wizard panel. + * The iterator for the add data source wizard panels. */ -class AddImageWizardIterator implements WizardDescriptor.Iterator { +final class AddImageWizardIterator implements WizardDescriptor.Iterator { private int index = 0; private List> panels; - private AddImageAction action; - - AddImageWizardIterator(AddImageAction action) { - this.action = action; - } /** - * Initialize panels representing individual wizard's steps and sets various - * properties for them influencing wizard appearance. + * Lazily create the panels for the add data source wizard. */ private List> getPanels() { - if (panels == null) { - panels = new ArrayList>(); + if (null == panels) { + panels = new ArrayList<>(); + /* + * Create the wizard panels. The first panel is used to select a + * data source. The second panel is used to configure the ingest + * modules. The third panel has a progress bar that tracks progress + * as the Sleuthkit layer adds the data source to the case database. + */ + AddImageWizardChooseDataSourcePanel dsPanel = new AddImageWizardChooseDataSourcePanel(); AddImageWizardAddingProgressPanel progressPanel = new AddImageWizardAddingProgressPanel(); - - AddImageWizardChooseDataSourcePanel dsPanel = new AddImageWizardChooseDataSourcePanel(progressPanel); - AddImageWizardIngestConfigPanel ingestConfigPanel = new AddImageWizardIngestConfigPanel(dsPanel, action, progressPanel); - + AddImageWizardIngestConfigPanel ingestConfigPanel = new AddImageWizardIngestConfigPanel(dsPanel, progressPanel); panels.add(dsPanel); panels.add(ingestConfigPanel); panels.add(progressPanel); + /* + * Set the appearance of the visual components of the panels. + */ String[] steps = new String[panels.size()]; for (int i = 0; i < panels.size(); i++) { - Component c = panels.get(i).getComponent(); - // Default step name to component name of panel. - steps[i] = c.getName(); - if (c instanceof JComponent) { // assume Swing components - JComponent jc = (JComponent) c; - // Sets step number of a component - jc.putClientProperty("WizardPanel_contentSelectedIndex", new Integer(i)); - // Sets steps names for a panel + Component visualComponent = panels.get(i).getComponent(); + // Default step name to component name. + steps[i] = visualComponent.getName(); + if (visualComponent instanceof JComponent) { + JComponent jc = (JComponent) visualComponent; + // Set step number. + jc.putClientProperty("WizardPanel_contentSelectedIndex", i); + // Sets step name. jc.putClientProperty("WizardPanel_contentData", steps); - // Turn on subtitle creation on each step + // Turn on subtitle creation. jc.putClientProperty("WizardPanel_autoWizardStyle", Boolean.TRUE); - // Show steps on the left side with the image on the background + // Show steps on the left side, with image in the background. jc.putClientProperty("WizardPanel_contentDisplayed", Boolean.TRUE); - // Turn on numbering of all steps + // Turn on step numbering. jc.putClientProperty("WizardPanel_contentNumbered", Boolean.TRUE); } } @@ -81,20 +81,10 @@ class AddImageWizardIterator implements WizardDescriptor.Iterator current() { @@ -108,18 +98,17 @@ class AddImageWizardIterator implements WizardDescriptor.IteratorIngest is ongoing on another AddImageAction.ingestConfig.ongoingIngest.title=Ingest in progress AddImageTask.run.progress.adding=Adding\: {0} AddImageTask.interrupt.exception.msg=Error stopping add-image process. -AddImageWizardAddingProgressPanel.isValid.focusNext=Next > AddImageWizardAddingProgressPanel.stateStarted.progressBarText=*This process may take some time for large data sources. AddImageWizardAddingProgressVisual.addingDsComplete.text=Adding Data Source - Complete AddImageWizardAddingProgressVisual.getName.text=Add Data Source diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties index 4fd28db906..7446ec1364 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties @@ -88,7 +88,6 @@ AddImageAction.ingestConfig.ongoingIngest.msg=\u4ed6\u306e\u30c7\u30fc\u30 AddImageAction.ingestConfig.ongoingIngest.title=\u51e6\u7406\u4e2d AddImageTask.run.progress.adding=\u8ffd\u52a0\u4e2d\uff1a{0} AddImageTask.interrupt.exception.msg=\u30a4\u30e1\u30fc\u30b8\u8ffd\u52a0\u30d7\u30ed\u30bb\u30b9\u306e\u505c\u6b62\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -AddImageWizardAddingProgressPanel.isValid.focusNext=\u6b21 > AddImageWizardAddingProgressPanel.stateStarted.progressBarText=*\u5927\u304d\u3044\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u5834\u5408\u3001\u3053\u306e\u30d7\u30ed\u30bb\u30b9\u306f\u6642\u9593\u304c\u304b\u304b\u308b\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 AddImageWizardAddingProgressVisual.addingDsComplete.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u8ffd\u52a0 - \u5b8c\u4e86 AddImageWizardAddingProgressVisual.getName.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u8ffd\u52a0