Revert "Fix thread safety, clean up add data source wizard classes"

This reverts commit b93d45fbf12824d7172ac6297ce4e6c24fbe1ed4.
This commit is contained in:
Richard Cordovano 2016-01-20 17:30:29 -05:00
parent fc6f972e54
commit eda180e34f
7 changed files with 550 additions and 474 deletions

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2016 Basis Technology Corp. * Copyright 2011-2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -21,11 +21,13 @@ package org.sleuthkit.autopsy.casemodule;
import java.awt.Component; import java.awt.Component;
import java.awt.Dialog; import java.awt.Dialog;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.Action; import javax.swing.Action;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
import org.openide.DialogDisplayer; import org.openide.DialogDisplayer;
@ -35,91 +37,139 @@ import org.openide.util.HelpCtx;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.actions.CallableSystemAction; import org.openide.util.actions.CallableSystemAction;
import org.openide.util.actions.Presenter; import org.openide.util.actions.Presenter;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.Image;
/** /**
* An action that adds a data source to the current case. * 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.
* *
* RC: This action needs to be enabled and disabled as cases are opened and * @author jantonius
* 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 { public final class AddImageAction extends CallableSystemAction implements Presenter.Toolbar {
private static final long serialVersionUID = 1L; // Keys into the WizardDescriptor properties that pass information between stages of the wizard
private static final Logger logger = Logger.getLogger(AddImageAction.class.getName()); // <TYPE>: <DESCRIPTION>
private final ChangeSupport cleanupSupport; // String: time zone that the image is from
private final JButton toolbarButton; 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 WizardDescriptor wizardDescriptor; private WizardDescriptor wizardDescriptor;
private WizardDescriptor.Iterator<WizardDescriptor> iterator; private WizardDescriptor.Iterator<WizardDescriptor> iterator;
private Dialog dialog;
private JButton toolbarButton = new JButton();
/** /**
* Constructs an action that adds a data source to the current case. * The constructor for AddImageAction class
*/ */
public AddImageAction() { public AddImageAction() {
cleanupSupport = new ChangeSupport(this); putValue(Action.NAME, NbBundle.getMessage(AddImageAction.class, "CTL_AddImage")); // set the action Name
putValue(Action.NAME, NbBundle.getMessage(AddImageAction.class, "CTL_AddImage"));
toolbarButton = new JButton(); // set the action for the toolbar button
toolbarButton.addActionListener(AddImageAction.this::actionPerformed); toolbarButton.addActionListener(new ActionListener() {
setEnabled(false);
@Override
public void actionPerformed(ActionEvent e) {
AddImageAction.this.actionPerformed(e);
}
});
this.setEnabled(false); // disable this action class
} }
/** /**
* Displays the first panel of the add data source wizard. * Pop-up the "Add Image" wizard panel.
* *
* @param notUsed An action event, may be null. * @param e
*/ */
@Override @Override
public void actionPerformed(ActionEvent notUsed) { public void actionPerformed(ActionEvent e) {
/*
* 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()) { if (IngestManager.getInstance().isIngestRunning()) {
if (JOptionPane.showConfirmDialog(null, final String msg = NbBundle.getMessage(this.getClass(), "AddImageAction.ingestConfig.ongoingIngest.msg");
NbBundle.getMessage(this.getClass(), "AddImageAction.ingestConfig.ongoingIngest.msg"), if (JOptionPane.showConfirmDialog(null, msg,
NbBundle.getMessage(this.getClass(), "AddImageAction.ingestConfig.ongoingIngest.title"), NbBundle.getMessage(this.getClass(),
"AddImageAction.ingestConfig.ongoingIngest.title"),
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.NO_OPTION) { JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.NO_OPTION) {
return; return;
} }
} }
/* iterator = new AddImageWizardIterator(this);
* Construct and display the wizard.
*/
iterator = new AddImageWizardIterator();
wizardDescriptor = new WizardDescriptor(iterator); wizardDescriptor = new WizardDescriptor(iterator);
wizardDescriptor.setTitle(NbBundle.getMessage(this.getClass(), "AddImageAction.wizard.title")); wizardDescriptor.setTitle(NbBundle.getMessage(this.getClass(), "AddImageAction.wizard.title"));
Dialog dialog = DialogDisplayer.getDefault().createDialog(wizardDescriptor); wizardDescriptor.putProperty(NAME, e);
if (dialog != null) {
dialog.setVisible(false); // hide the old one
}
dialog = DialogDisplayer.getDefault().createDialog(wizardDescriptor);
dialog.setVisible(true); dialog.setVisible(true);
dialog.toFront(); dialog.toFront();
/* // Do any cleanup that needs to happen (potentially: stopping the
* Run any registered cleanup tasks by firing a change event. This will //add-image process, reverting an image)
* cause the stateChanged method of any implementations of the inner, runCleanupTasks();
* 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();
} }
/** /**
* @inheritDoc * 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.
*/ */
@Override @Override
public void performAction() { public void performAction() {
actionPerformed(null);
} }
/** /**
* @inheritDoc * Gets the name of this action. This may be presented as an item in a menu.
*
* @return actionName
*/ */
@Override @Override
public String getName() { public String getName() {
@ -127,7 +177,9 @@ public final class AddImageAction extends CallableSystemAction implements Presen
} }
/** /**
* @inheritDoc * Gets the HelpCtx associated with implementing object
*
* @return HelpCtx or HelpCtx.DEFAULT_HELP
*/ */
@Override @Override
public HelpCtx getHelpCtx() { public HelpCtx getHelpCtx() {
@ -135,7 +187,9 @@ public final class AddImageAction extends CallableSystemAction implements Presen
} }
/** /**
* @inheritDoc * Returns the toolbar component of this action
*
* @return component the toolbar button
*/ */
@Override @Override
public Component getToolbarPresenter() { public Component getToolbarPresenter() {
@ -146,7 +200,9 @@ public final class AddImageAction extends CallableSystemAction implements Presen
} }
/** /**
* @inheritDoc * Set this action to be enabled/disabled
*
* @param value whether to enable this action or not
*/ */
@Override @Override
public void setEnabled(boolean value) { public void setEnabled(boolean value) {
@ -155,80 +211,68 @@ public final class AddImageAction extends CallableSystemAction implements Presen
} }
/** /**
* Does nothing, do not use. * Set the focus to the button of the given name on this wizard dialog.
* *
* @deprecated Classes in this package may call requestFocusForWizardButton * Note: the name of the buttons that available are "Next >", "< Back",
* instead. * "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
public void requestFocusButton(String buttonText) { public void requestFocusButton(String buttonText) {
} // get all buttons on this wizard panel
Object[] wizardButtons = wizardDescriptor.getOptions();
/** for (int i = 0; i < wizardButtons.length; i++) {
* Requests focus for an add data source wizard button. JButton tempButton = (JButton) wizardButtons[i];
* if (tempButton.getText().equals(buttonText)) {
* @param buttonText The text of the button. tempButton.setDefaultCapable(true);
*/ tempButton.requestFocus();
void requestFocusForWizardButton(String buttonText) {
for (Object wizardButton : wizardDescriptor.getOptions()) {
JButton button = (JButton) wizardButton;
if (button.getText().equals(buttonText)) {
button.setDefaultCapable(true);
button.requestFocus();
} }
} }
} }
/** /**
* Enabled instances of this class are called to do clean up after the add * Run and clear any cleanup tasks for wizard closing that might be
* data source wizard is closed. The instances are disabled after the * registered. This should be run even when the wizard exits cleanly, so
* cleanUp method is called. Implementations should not override * that no cleanup actions remain the next time the wizard is run.
* stateChanged, and should not re-enable themselves after cleanUp is */
* called. To stop cleanUp being called, call disable before the wizard is private void runCleanupTasks() {
* dismissed. cleanupSupport.fireChange();
}
ChangeSupport cleanupSupport = new ChangeSupport(this);
/**
* Instances of this class implement the cleanup() method to run cleanup
* code when the wizard exits.
* *
* Instances must be constructed using a reference to an AddImageAction * After enable() has been called on an instance it will run once after the
* object because this is a non-static inner class. * wizard closes (on both a cancel and a normal finish).
* *
* RC: This is a convoluted and error-prone way to implement clean up. * If disable() is called before the wizard exits, the task will not run.
* Fortunately, it is confined to this package.
*/ */
abstract class CleanupTask implements ChangeListener { abstract class CleanupTask implements ChangeListener {
@Override @Override
public void stateChanged(ChangeEvent e) { 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 { try {
cleanup(); cleanup();
} catch (Exception ex) { } catch (Exception ex) {
logger.log(Level.SEVERE, "Error cleaning in add data source wizard clean up task", ex); //NON-NLS Logger logger = Logger.getLogger(this.getClass().getName());
logger.log(Level.WARNING, "Error cleaning up from wizard.", ex); //NON-NLS
} finally { } finally {
/* disable(); // cleanup tasks should only run once.
* Clean up tasks should only be done exactly once.
*/
disable();
} }
} }
/** /**
* Adds this task to the list of tasks to be done when the wizard * Add task to the enabled list to run when the wizard closes.
* closes.
*/ */
void enable() { public void enable() {
cleanupSupport.addChangeListener(this); 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 * Performs cleanup action when called
* *
@ -236,12 +280,11 @@ public final class AddImageAction extends CallableSystemAction implements Presen
*/ */
abstract void cleanup() throws Exception; 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);
}
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2016 Basis Technology Corp. * Copyright 2011-2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -20,97 +20,105 @@ package org.sleuthkit.autopsy.casemodule;
import java.awt.Color; import java.awt.Color;
import java.awt.EventQueue; 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 javax.swing.event.ChangeListener;
import org.openide.WizardDescriptor; import org.openide.WizardDescriptor;
import org.openide.util.ChangeSupport;
import org.openide.util.HelpCtx; import org.openide.util.HelpCtx;
import org.openide.util.Lookup;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
/** /**
* The third and final panel of the add data source wizard. The visual component * The final panel of the add image wizard. It displays a progress bar and
* of this panel displays a progress bar that is managed by the previous panel * status updates.
* and the data source processor. *
* All the real work is kicked off in the previous panel:
* {@link AddImageWizardIngestConfigPanel} (which is a bit weird if you ask m
* -jm)
*/ */
// All the real work is kicked off in the previous panel: class AddImageWizardAddingProgressPanel implements WizardDescriptor.FinishablePanel<WizardDescriptor> {
// {@link AddImageWizardIngestConfigPanel} (which is a bit weird if you ask m
// -jm)
final class AddImageWizardAddingProgressPanel implements WizardDescriptor.FinishablePanel<WizardDescriptor> {
private final ChangeSupport changeSupport; /**
private final DSPProgressMonitorImpl dspProgressMonitor = new DSPProgressMonitorImpl(); * 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 AddImageWizardAddingProgressVisual component; private AddImageWizardAddingProgressVisual component;
private boolean dataSourceAdded = false; private final Set<ChangeListener> listeners = new HashSet<>(1); // or can use ChangeSupport in NB 6.0
/** private DSPProgressMonitorImpl dspProgressMonitorImpl = new DSPProgressMonitorImpl();
* Constructs an instance of the third and final panel of the add data
* source wizard. public DSPProgressMonitorImpl getDSPProgressMonitorImpl() {
*/ return dspProgressMonitorImpl;
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 { private class DSPProgressMonitorImpl implements DataSourceProcessorProgressMonitor {
@Override @Override
public void setIndeterminate(final boolean indeterminate) { public void setIndeterminate(final boolean indeterminate) {
EventQueue.invokeLater(() -> { // update the progress bar asynchronously
getComponent().getProgressBar().setIndeterminate(indeterminate); EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
getComponent().getProgressBar().setIndeterminate(indeterminate);
}
}); });
} }
@Override @Override
public void setProgress(final int progress) { public void setProgress(final int progress) {
// update the progress bar asynchronously // update the progress bar asynchronously
EventQueue.invokeLater(() -> { EventQueue.invokeLater(new Runnable() {
getComponent().getProgressBar().setValue(progress); @Override
public void run() {
getComponent().getProgressBar().setValue(progress);
}
}); });
} }
@Override @Override
public void setProgressText(final String text) { public void setProgressText(final String text) {
// update the progress UI asynchronously // update the progress UI asynchronously
EventQueue.invokeLater(() -> { EventQueue.invokeLater(new Runnable() {
getComponent().setProgressMsgText(text); @Override
public void run() {
getComponent().setProgressMsgText(text);
}
}); });
} }
} }
/** /**
* Gets the visual component for the panel. In this template, the component * 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 * 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 * but never displayed, or not all panels are displayed, it is better to
* create only those which really need to be visible. * create only those which really need to be visible.
* *
* @return The UI component of this wizard panel. * It also separates the view from the control - jm
*
* @return component the UI component of this wizard panel
*/ */
@Override @Override
public AddImageWizardAddingProgressVisual getComponent() { public AddImageWizardAddingProgressVisual getComponent() {
if (null == component) { if (component == null) {
component = new AddImageWizardAddingProgressVisual(); component = new AddImageWizardAddingProgressVisual();
} }
return component; return component;
} }
/** /**
* Gets the help for this panel. When the panel is active, this is used as * Help for this panel. When the panel is active, this is used as the help
* the help for the wizard dialog. * for the wizard dialog.
* *
* @return The help for this panel * @return HelpCtx.DEFAULT_HELP the help for this panel
*/ */
@Override @Override
public HelpCtx getHelp() { public HelpCtx getHelp() {
@ -126,12 +134,17 @@ final class AddImageWizardAddingProgressPanel implements WizardDescriptor.Finish
*/ */
@Override @Override
public boolean isValid() { public boolean isValid() {
return dataSourceAdded; // 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;
} }
/** /**
* Makes the progress bar of the visual component of this panel indicate * Updates the UI to display the add image process has begun.
* that the data source processor is running.
*/ */
void setStateStarted() { void setStateStarted() {
component.getProgressBar().setIndeterminate(true); component.getProgressBar().setIndeterminate(true);
@ -140,85 +153,94 @@ final class AddImageWizardAddingProgressPanel implements WizardDescriptor.Finish
} }
/** /**
* Makes the progress bar of the visual component of this panel indicate * Updates the UI to display the add image process is over.
* that the data source processor is not running.
*/ */
void setStateFinished() { void setStateFinished() {
dataSourceAdded = true; imgAdded = true;
getComponent().setStateFinished(); getComponent().setStateFinished();
fireChangeEvent(); fireChangeEvent();
} }
/** /**
* @inheritDoc5 * Adds a listener to changes of the panel's validity.
*
* @param l the change listener to add
*/ */
@Override @Override
public final void addChangeListener(ChangeListener listener) { public final void addChangeListener(ChangeListener l) {
changeSupport.addChangeListener(listener); synchronized (listeners) {
listeners.add(l);
}
} }
/** /**
* @inheritDoc * Removes a listener to changes of the panel's validity.
*
* @param l the change listener to move
*/ */
@Override @Override
public final void removeChangeListener(ChangeListener listener) { public final void removeChangeListener(ChangeListener l) {
changeSupport.removeChangeListener(listener); synchronized (listeners) {
listeners.remove(l);
}
} }
/** /**
* @inheritDoc * This method is auto-generated. It seems that this method is used to
* listen to any change in this wizard panel.
*/ */
protected final void fireChangeEvent() { protected final void fireChangeEvent() {
changeSupport.fireChange(); Iterator<ChangeListener> it;
synchronized (listeners) {
it = new HashSet<ChangeListener>(listeners).iterator();
}
ChangeEvent ev = new ChangeEvent(this);
while (it.hasNext()) {
it.next().stateChanged(ev);
}
} }
/** /**
* Provides the wizard panel with the current data--either the default data * Load the image locations from the WizardDescriptor settings object, and
* or already-modified settings, if the user used the previous and/or next * the
* buttons. This method can be called multiple times on one instance of
* WizardDescriptor.Panel.
* *
* @param settings The settings. * @param settings the setting to be read from
*/ */
@Override @Override
public void readSettings(WizardDescriptor settings) { public void readSettings(WizardDescriptor settings) {
settings.setOptions(new Object[]{WizardDescriptor.PREVIOUS_OPTION, WizardDescriptor.NEXT_OPTION, WizardDescriptor.FINISH_OPTION, WizardDescriptor.CANCEL_OPTION}); settings.setOptions(new Object[]{WizardDescriptor.PREVIOUS_OPTION, WizardDescriptor.NEXT_OPTION, WizardDescriptor.FINISH_OPTION, WizardDescriptor.CANCEL_OPTION});
if (dataSourceAdded) { if (imgAdded) {
getComponent().setStateFinished(); getComponent().setStateFinished();
} }
} }
/** /**
* Provides the wizard panel with the opportunity to update the settings * this doesn't appear to store anything? plus, there are no settings in
* with its current customized state. Rather than updating its settings with * this panel -jm
* 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 * @param settings the setting to be stored to
*/ */
@Override @Override
public void storeSettings(WizardDescriptor settings) { public void storeSettings(WizardDescriptor settings) {
//why did we do this? -jm
// getComponent().resetInfoPanel();
} }
/** /**
* Displays an error message from the data source processor for the data * forward errors to visual component
* source being added.
* *
* @param errorString The error message to be displayed * should this be modified to handle a list of errors? -jm
* @param critical True if this is a critical error *
*
* @param errorString the error string to be displayed
* @param critical true if this is a critical error
*/ */
// Should this be modified to handle a list of errors? -jm void addErrors(String errorString, boolean critical) {
void displayDataSourceProcessorError(String errorString, boolean critical) {
getComponent().showErrors(errorString, critical); getComponent().showErrors(errorString, critical);
} }
/**
* @inheritDoc
*/
@Override @Override
public boolean isFinishPanel() { public boolean isFinishPanel() {
return true; return true;
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2016 Basis Technology Corp. * Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -20,43 +20,55 @@ package org.sleuthkit.autopsy.casemodule;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; 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.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
import org.openide.WizardDescriptor; import org.openide.WizardDescriptor;
import org.openide.util.HelpCtx; import org.openide.util.HelpCtx;
import org.openide.util.Lookup;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import java.awt.Cursor; import java.awt.Cursor;
import org.openide.util.ChangeSupport;
import org.openide.util.actions.CallableSystemAction;
/** /**
* The first panel of the add data source wizard. The visual component of this * The "Add Image" wizard panel1 handling the logic of selecting image file(s)
* panel allows a user to select data sources to add to the current case. * to add to Case, and pick the time zone.
*/ */
final class AddImageWizardChooseDataSourcePanel implements WizardDescriptor.Panel<WizardDescriptor>, PropertyChangeListener { class AddImageWizardChooseDataSourcePanel implements WizardDescriptor.Panel<WizardDescriptor>, PropertyChangeListener {
private AddImageWizardChooseDataSourceVisual component;
private final ChangeSupport changeSupport;
private boolean nextButtonIsEnabled = false;
/** /**
* Constructs an instance of the first panel of the add data source wizard. * The visual component that displays this panel. If you need to access the
* component from this class, just use getComponent().
*/ */
AddImageWizardChooseDataSourcePanel() { private AddImageWizardAddingProgressPanel progressPanel;
changeSupport = new ChangeSupport(this); 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;
} }
/** /**
* Gets the visual component for the panel. The component is kept separate. * Get the visual component for the panel. In this template, the component
* This can be more efficient: if the wizard is created but never displayed, * is kept separate. This can be more efficient: if the wizard is created
* or not all panels are displayed, it is better to create only those which * but never displayed, or not all panels are displayed, it is better to
* really need to be visible. * create only those which really need to be visible.
* *
* @return The UI component of this wizard panel. * @return component the UI component of this wizard panel
*/ */
@Override @Override
public AddImageWizardChooseDataSourceVisual getComponent() { public AddImageWizardChooseDataSourceVisual getComponent() {
if (null == component) { if (component == null) {
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
component = new AddImageWizardChooseDataSourceVisual(this); component = new AddImageWizardChooseDataSourceVisual(this);
} }
@ -65,103 +77,156 @@ final class AddImageWizardChooseDataSourcePanel implements WizardDescriptor.Pane
} }
/** /**
* Gets the help for this panel. When the panel is active, this is used as * Help for this panel. When the panel is active, this is used as the help
* the help for the wizard dialog. * for the wizard dialog.
* *
* @return The help for this panel * @return HelpCtx.DEFAULT_HELP the help for this panel
*/ */
@Override @Override
public HelpCtx getHelp() { public HelpCtx getHelp() {
// Show no Help button for this panel: // Show no Help button for this panel:
return HelpCtx.DEFAULT_HELP; return HelpCtx.DEFAULT_HELP;
// If you have context help:
// return new HelpCtx(SampleWizardPanel1.class);
} }
/** /**
* Tests whether the panel is finished and it is safe to proceed to the next
* one. If the panel is valid, the "Next" button will be enabled.
* *
* Tests whether or not the panel is finished. If the panel is valid, the * @return boolean true if all the fields are correctly filled, false
* "Finish" button will be enabled. * otherwise
*
* @return True or false.
*/ */
@Override @Override
public boolean isValid() { public boolean isValid() {
// If it is always OK to press Next or Finish, then: return isNextEnable;
// return true;
// If it depends on some condition (form filled out...), then:
// return someCondition();
// and when this condition changes (last form field filled in...) then:
// fireChangeEvent();
/*
* When it is valid, the visual component calls enableNextButton to set
* this flag.
*/
return nextButtonIsEnabled;
} }
/** /**
* Moves the keyboard focus to the next button of the wizard. * Move the keyboard focus to the next button
*/ */
void moveFocusToNext() { void moveFocusToNext() {
if (nextButtonIsEnabled) { // set the focus to the next button of the wizard dialog if it's enabled
CallableSystemAction.get(AddImageAction.class).requestFocusForWizardButton( if (isNextEnable) {
Lookup.getDefault().lookup(AddImageAction.class).requestFocusButton(
NbBundle.getMessage(this.getClass(), "AddImageWizardChooseDataSourcePanel.moveFocusNext")); NbBundle.getMessage(this.getClass(), "AddImageWizardChooseDataSourcePanel.moveFocusNext"));
} }
} }
/** /**
* Enables the "Next" button and fires a change event to update the UI. * Enable the "Next" button and fireChangeEvent to update the GUI
* *
* @param isEnabled True if next button should be enabled, false otherwise. * @param isEnabled true if next button can be enabled, false otherwise
*/ */
void enableNextButton(boolean isEnabled) { public void enableNextButton(boolean isEnabled) {
nextButtonIsEnabled = isEnabled; isNextEnable = isEnabled;
fireChangeEvent(); fireChangeEvent();
} }
private final Set<ChangeListener> listeners = new HashSet<ChangeListener>(1); // or can use ChangeSupport in NB 6.0
/** /**
* @inheritDoc * Adds a listener to changes of the panel's validity.
*
* @param l the change listener to add
*/ */
@Override @Override
public final void addChangeListener(ChangeListener listener) { public final void addChangeListener(ChangeListener l) {
changeSupport.addChangeListener(listener); synchronized (listeners) {
listeners.add(l);
}
} }
/** /**
* @inheritDoc * Removes a listener to changes of the panel's validity.
*
* @param l the change listener to move
*/ */
@Override @Override
public final void removeChangeListener(ChangeListener listener) { public final void removeChangeListener(ChangeListener l) {
changeSupport.removeChangeListener(listener); synchronized (listeners) {
listeners.remove(l);
}
} }
/** /**
* @inheritDoc * This method is auto-generated. It seems that this method is used to
* listen to any change in this wizard panel.
*/ */
protected final void fireChangeEvent() { protected final void fireChangeEvent() {
changeSupport.fireChange(); Iterator<ChangeListener> it;
synchronized (listeners) {
it = new HashSet<ChangeListener>(listeners).iterator();
}
ChangeEvent ev = new ChangeEvent(this);
while (it.hasNext()) {
it.next().stateChanged(ev);
}
} }
// 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 * 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 * 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 * buttons. This method can be called multiple times on one instance of
* WizardDescriptor.Panel. * WizardDescriptor.Panel. s
* *
* @param settings The settings. * @param settings the setting to be read from
*/ */
@Override @Override
public void readSettings(WizardDescriptor settings) { 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) {
}
} }
/** /**
* @inheritDoc * 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 @Override
public void storeSettings(WizardDescriptor settings) { public void storeSettings(WizardDescriptor settings) {
return;
} }
/** /**
* @inheritDoc * The "listener" for any property change in this panel. Any property
* changes will invoke the "fireChangeEvent()" method.
*
* @param evt the property change event
*/ */
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2016 Basis Technology Corp. * Copyright 2011-2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -31,9 +31,7 @@ import javax.swing.JOptionPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
import org.openide.WizardDescriptor; import org.openide.WizardDescriptor;
import org.openide.util.ChangeSupport;
import org.openide.util.HelpCtx; import org.openide.util.HelpCtx;
import org.openide.util.actions.CallableSystemAction;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
@ -42,108 +40,80 @@ import org.sleuthkit.autopsy.ingest.IngestJobSettingsPanel;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
/** /**
* The second panel of the add data source wizard. The visual component of this * second panel of add image wizard, allows user to configure ingest modules.
* 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 * TODO: review this for dead code. think about moving logic of adding image to
* starts the ingest job. * 3rd panel( {@link AddImageWizardAddingProgressPanel}) separate class -jm
*/ */
// JM: Think about moving the logic of adding image to the 3rd panel class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel<WizardDescriptor> {
// ( {@link AddImageWizardAddingProgressPanel})
final class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel<WizardDescriptor> {
private final AddImageWizardChooseDataSourcePanel selectDataSourcePanel;
private final AddImageWizardAddingProgressPanel progressPanel;
private final ChangeSupport changeSupport;
private final List<Content> newContents;
private final IngestJobSettingsPanel ingestJobSettingsPanel; private final IngestJobSettingsPanel ingestJobSettingsPanel;
private Component component;
private volatile boolean ingestModulesConfigured;
private volatile boolean ingestJobStarted;
private AddImageAction.CleanupTask cleanupTask;
private DataSourceProcessor dsProcessor;
/** /**
* Constructs an instance of The second panel of the add data source wizard. * The visual component that displays this panel. If you need to access the
* The visual component of this panel allows a user to configure the ingest * component from this class, just use getComponent().
* 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) { private Component component = null;
this.selectDataSourcePanel = selectDataSourcePanel;
this.progressPanel = progressPanel;
changeSupport = new ChangeSupport(this);
/* private final List<Content> newContents = Collections.synchronizedList(new ArrayList<Content>());
* Create a collection to receive the Content objects returned by the private boolean ingested = false;
* data source processor. private boolean readyToIngest = false;
*/
newContents = Collections.synchronizedList(new ArrayList<Content>()); // 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;
private final AddImageWizardAddingProgressPanel progressPanel;
private final AddImageWizardChooseDataSourcePanel dataSourcePanel;
private DataSourceProcessor dsProcessor;
AddImageWizardIngestConfigPanel(AddImageWizardChooseDataSourcePanel dsPanel, AddImageAction action, AddImageWizardAddingProgressPanel proPanel) {
this.addImageAction = action;
this.progressPanel = proPanel;
this.dataSourcePanel = dsPanel;
/*
* 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()); IngestJobSettings ingestJobSettings = new IngestJobSettings(AddImageWizardIngestConfigPanel.class.getCanonicalName());
showIngestModuleConfigurationWarnings(ingestJobSettings); showWarnings(ingestJobSettings);
ingestJobSettingsPanel = new IngestJobSettingsPanel(ingestJobSettings); this.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;
} }
/** /**
* Gets the visual component for the panel. In this template, the component * 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 * 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 * but never displayed, or not all panels are displayed, it is better to
* create only those which really need to be visible. * create only those which really need to be visible.
* *
* @return The UI component of this wizard panel. * @return component the UI component of this wizard panel
*/ */
@Override @Override
public Component getComponent() { public Component getComponent() {
if (null == component) { if (component == null) {
component = new AddImageWizardIngestConfigVisual(this.ingestJobSettingsPanel); component = new AddImageWizardIngestConfigVisual(this.ingestJobSettingsPanel);
} }
return component; return component;
} }
/** /**
* Gets the help for this panel. When the panel is active, this is used as * Help for this panel. When the panel is active, this is used as the help
* the help for the wizard dialog. * for the wizard dialog.
* *
* @return The help for this panel * @return HelpCtx.DEFAULT_HELP the help for this panel
*/ */
@Override @Override
public HelpCtx getHelp() { public HelpCtx getHelp() {
// Show no Help button for this panel: // Show no Help button for this panel:
return HelpCtx.DEFAULT_HELP; return HelpCtx.DEFAULT_HELP;
// If you have context help:
// return new HelpCtx(SampleWizardPanel1.class);
} }
/** /**
* Tests whether or not the panel is finished. If the panel is valid, the * Tests whether the panel is finished. If the panel is valid, the "Finish"
* "Finish" button will be enabled. * button will be enabled.
* *
* @return True or false. * @return true the finish button should be always enabled at this point
*/ */
@Override @Override
public boolean isValid() { public boolean isValid() {
@ -153,53 +123,52 @@ final class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel<Wi
// return someCondition(); // return someCondition();
// and when this condition changes (last form field filled in...) then: // and when this condition changes (last form field filled in...) then:
// fireChangeEvent(); // fireChangeEvent();
// and uncomment the complicated stuff below.
} }
/** /**
* @inheritDoc * Adds a listener to changes of the panel's validity.
*
* @param l the change listener to add
*/ */
@Override @Override
public final void addChangeListener(ChangeListener listener) { public final void addChangeListener(ChangeListener l) {
changeSupport.addChangeListener(listener);
} }
/** /**
* @inheritDoc * Removes a listener to changes of the panel's validity.
*
* @param l the change listener to move
*/ */
@Override @Override
public final void removeChangeListener(ChangeListener listener) { public final void removeChangeListener(ChangeListener l) {
changeSupport.removeChangeListener(listener);
}
/**
* @inheritDoc
*/
protected final void fireChangeEvent() {
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 * 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 * 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 * buttons. This method can be called multiple times on one instance of
* WizardDescriptor.Panel. * WizardDescriptor.Panel.
* *
* @param settings The settings. * @param settings the setting to be read from
*/ */
@Override @Override
public void readSettings(WizardDescriptor settings) { public void readSettings(WizardDescriptor settings) {
/* JButton cancel = new JButton(
* The user has pushed the next button on the previous panel of the add NbBundle.getMessage(this.getClass(), "AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text"));
* data source wizard. Start the data source processor so it can run
* while the user is doing the ingest module configuration. It is ok to
* do this now because the back button is disabled for this wizard - the
* user cannot go back to choose a different data source.
*
* RC: Not sure why the cancel button is disabled.
*/
JButton cancel = new JButton(NbBundle.getMessage(this.getClass(), "AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text"));
cancel.setEnabled(false); cancel.setEnabled(false);
settings.setOptions(new Object[]{WizardDescriptor.PREVIOUS_OPTION, WizardDescriptor.NEXT_OPTION, WizardDescriptor.FINISH_OPTION, cancel}); settings.setOptions(new Object[]{WizardDescriptor.PREVIOUS_OPTION, WizardDescriptor.NEXT_OPTION, WizardDescriptor.FINISH_OPTION, cancel});
cleanupTask = null;
readyToIngest = false;
newContents.clear();
// Start processing the data source by handing it off to the selected DSP,
// so it gets going in the background while the user is still picking the Ingest modules
startDataSourceProcessing(settings); startDataSourceProcessing(settings);
} }
@ -214,29 +183,16 @@ final class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel<Wi
*/ */
@Override @Override
public void storeSettings(WizardDescriptor settings) { public void storeSettings(WizardDescriptor settings) {
/*
* The user has pushed the next button on this panel. Save the ingest
* job settings for the add data source wizard context and try to start
* the ingest job. It is ok to do this now because the back button is
* disabled for this wizard - the user cannot go back to choose a
* different data source. However, the job will not be started if either
* the data source processor has not finished yet, or it finished but
* did not produce any Content objects for the data source.
*/
IngestJobSettings ingestJobSettings = this.ingestJobSettingsPanel.getSettings(); IngestJobSettings ingestJobSettings = this.ingestJobSettingsPanel.getSettings();
ingestJobSettings.save(); ingestJobSettings.save();
showIngestModuleConfigurationWarnings(ingestJobSettings); showWarnings(ingestJobSettings);
ingestModulesConfigured = true;
tryStartIngestJob(); // Start ingest if it hasn't already been started
readyToIngest = true;
startIngest();
} }
/** private static void showWarnings(IngestJobSettings ingestJobSettings) {
* Displays any warnings returned form operations on the ingest job settings
* for the add data source wizard context.
*
* @param ingestJobSettings The ingest job settings.
*/
private static void showIngestModuleConfigurationWarnings(IngestJobSettings ingestJobSettings) {
List<String> warnings = ingestJobSettings.getWarnings(); List<String> warnings = ingestJobSettings.getWarnings();
if (warnings.isEmpty() == false) { if (warnings.isEmpty() == false) {
StringBuilder warningMessage = new StringBuilder(); StringBuilder warningMessage = new StringBuilder();
@ -248,78 +204,76 @@ final class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel<Wi
} }
/** /**
* Starts the data source processor selected by the user when the previous * Start ingest after verifying we have a new image, we are ready to ingest,
* panel was displayed. * and we haven't already ingested.
*/
private void startIngest() {
if (!newContents.isEmpty() && readyToIngest && !ingested) {
ingested = true;
IngestManager.getInstance().queueIngestJob(newContents, ingestJobSettingsPanel.getSettings());
progressPanel.setStateFinished();
}
}
/**
* Starts the Data source processing by kicking off the selected
* DataSourceProcessor
*/ */
private void startDataSourceProcessing(WizardDescriptor settings) { private void startDataSourceProcessing(WizardDescriptor settings) {
/* final UUID dataSourceId = UUID.randomUUID();
* Create a unique id to use to synch up the data source events that are
* published for the data source being added.
*/
final UUID addDataSourceEventId = UUID.randomUUID();
/* // Add a cleanup task to interrupt the background process if the
* Register a clean up task with the action that invokes the add data // wizard exits while the background process is running.
* source wizard. This action takes responsibility for doing cleanup cleanupTask = addImageAction.new CleanupTask() {
* after the wizard is closed (see the definition of the AddImageAction
* class).
*/
cleanupTask = CallableSystemAction.get(AddImageAction.class).new CleanupTask() {
@Override @Override
void cleanup() throws Exception { void cleanup() throws Exception {
new Thread(() -> { cancelDataSourceProcessing(dataSourceId);
/*
* 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(); cleanupTask.enable();
/* // get the selected DSProcessor
* Publish an adding data source event. dsProcessor = dataSourcePanel.getComponent().getCurrentDSProcessor();
*/
new Thread(() -> { new Thread(() -> {
Case.getCurrentCase().notifyAddingDataSource(addDataSourceEventId); Case.getCurrentCase().notifyAddingDataSource(dataSourceId);
}).start(); }).start();
DataSourceProcessorCallback cbObj = new DataSourceProcessorCallback() {
/*
* Notify the progress panel for this wizard that data source processing
* is starting.
*/
progressPanel.setStateStarted();
/*
* Get the data source processor the user selected and start it.
*/
dsProcessor = selectDataSourcePanel.getComponent().getCurrentDSProcessor();
DataSourceProcessorCallback callback = new DataSourceProcessorCallback() {
@Override @Override
public void doneEDT(DataSourceProcessorCallback.DataSourceProcessorResult result, List<String> errList, List<Content> contents) { public void doneEDT(DataSourceProcessorCallback.DataSourceProcessorResult result, List<String> errList, List<Content> contents) {
dataSourceProcessorDone(addDataSourceEventId, result, errList, contents); dataSourceProcessorDone(dataSourceId, result, errList, contents);
} }
}; };
dsProcessor.run(progressPanel.getDSPProgressMonitor(), callback);
progressPanel.setStateStarted();
// Kick off the DSProcessor
dsProcessor.run(progressPanel.getDSPProgressMonitorImpl(), cbObj);
} }
/* /*
* The callback for the data source processor to invoke when it finishes. * Cancels the data source processing - in case the users presses 'Cancel'
*/ */
private void dataSourceProcessorDone(UUID addDataSourceEventId, DataSourceProcessorCallback.DataSourceProcessorResult result, List<String> errList, List<Content> contents) { private void cancelDataSourceProcessing(UUID dataSourceId) {
/* new Thread(() -> {
* Disable the clean up task. 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<String> errList, List<Content> contents) {
// disable the cleanup task
cleanupTask.disable(); cleanupTask.disable();
/* // Get attention for the process finish
* Get the user's attention. java.awt.Toolkit.getDefaultToolkit().beep(); //BEEP!
*
* RC: Is this really necessary?
*/
java.awt.Toolkit.getDefaultToolkit().beep();
AddImageWizardAddingProgressVisual panel = progressPanel.getComponent(); AddImageWizardAddingProgressVisual panel = progressPanel.getComponent();
if (panel != null) { if (panel != null) {
Window w = SwingUtilities.getWindowAncestor(panel); Window w = SwingUtilities.getWindowAncestor(panel);
@ -327,12 +281,10 @@ final class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel<Wi
w.toFront(); w.toFront();
} }
} }
// Tell the panel we're done
/*
* Notify the progress panel that the data source processor has finished
* its work and display the processing results on the progress panel.
*/
progressPanel.setStateFinished(); progressPanel.setStateFinished();
//check the result and display to user
if (result == DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS) { if (result == DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS) {
progressPanel.getComponent().setProgressBarTextAndColor( progressPanel.getComponent().setProgressBarTextAndColor(
NbBundle.getMessage(this.getClass(), "AddImageWizardIngestConfigPanel.dsProcDone.noErrs.text"), 100, Color.black); NbBundle.getMessage(this.getClass(), "AddImageWizardIngestConfigPanel.dsProcDone.noErrs.text"), 100, Color.black);
@ -340,45 +292,32 @@ final class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel<Wi
progressPanel.getComponent().setProgressBarTextAndColor( progressPanel.getComponent().setProgressBarTextAndColor(
NbBundle.getMessage(this.getClass(), "AddImageWizardIngestConfigPanel.dsProcDone.errs.text"), 100, Color.red); NbBundle.getMessage(this.getClass(), "AddImageWizardIngestConfigPanel.dsProcDone.errs.text"), 100, Color.red);
} }
//if errors, display them on the progress panel
boolean critErr = false; boolean critErr = false;
if (result == DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS) { if (result == DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS) {
critErr = true; critErr = true;
} }
for (String err : errList) { for (String err : errList) {
progressPanel.displayDataSourceProcessorError(err, critErr); // TBD: there probably should be an error level for each error
progressPanel.addErrors(err, critErr);
} }
/*
* Save the Content objects returned by the data source processor.
*/
newContents.clear(); newContents.clear();
newContents.addAll(contents); newContents.addAll(contents);
//notify the UI of the new content added to the case
new Thread(() -> { new Thread(() -> {
if (!newContents.isEmpty()) { if (!newContents.isEmpty()) {
Case.getCurrentCase().notifyDataSourceAdded(newContents.get(0), addDataSourceEventId); Case.getCurrentCase().notifyDataSourceAdded(newContents.get(0), dataSourceId);
} else { } else {
Case.getCurrentCase().notifyFailedAddingDataSource(addDataSourceEventId); Case.getCurrentCase().notifyFailedAddingDataSource(dataSourceId);
} }
}).start(); }).start();
/* // Start ingest if we can
* Try starting the ingest job for this data source. If the data source progressPanel.setStateStarted();
* processor finished before the user finished configuring the ingest startIngest();
* 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());
}
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2016 Basis Technology Corp. * Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -28,52 +28,52 @@ import org.openide.WizardDescriptor;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
/** /**
* The iterator for the add data source wizard panels. * 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.
*/ */
final class AddImageWizardIterator implements WizardDescriptor.Iterator<WizardDescriptor> { class AddImageWizardIterator implements WizardDescriptor.Iterator<WizardDescriptor> {
private int index = 0; private int index = 0;
private List<WizardDescriptor.Panel<WizardDescriptor>> panels; private List<WizardDescriptor.Panel<WizardDescriptor>> panels;
private AddImageAction action;
AddImageWizardIterator(AddImageAction action) {
this.action = action;
}
/** /**
* Lazily create the panels for the add data source wizard. * Initialize panels representing individual wizard's steps and sets various
* properties for them influencing wizard appearance.
*/ */
private List<WizardDescriptor.Panel<WizardDescriptor>> getPanels() { private List<WizardDescriptor.Panel<WizardDescriptor>> getPanels() {
if (null == panels) { if (panels == null) {
panels = new ArrayList<>(); panels = new ArrayList<WizardDescriptor.Panel<WizardDescriptor>>();
/*
* 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(); AddImageWizardAddingProgressPanel progressPanel = new AddImageWizardAddingProgressPanel();
AddImageWizardIngestConfigPanel ingestConfigPanel = new AddImageWizardIngestConfigPanel(dsPanel, progressPanel);
AddImageWizardChooseDataSourcePanel dsPanel = new AddImageWizardChooseDataSourcePanel(progressPanel);
AddImageWizardIngestConfigPanel ingestConfigPanel = new AddImageWizardIngestConfigPanel(dsPanel, action, progressPanel);
panels.add(dsPanel); panels.add(dsPanel);
panels.add(ingestConfigPanel); panels.add(ingestConfigPanel);
panels.add(progressPanel); panels.add(progressPanel);
/*
* Set the appearance of the visual components of the panels.
*/
String[] steps = new String[panels.size()]; String[] steps = new String[panels.size()];
for (int i = 0; i < panels.size(); i++) { for (int i = 0; i < panels.size(); i++) {
Component visualComponent = panels.get(i).getComponent(); Component c = panels.get(i).getComponent();
// Default step name to component name. // Default step name to component name of panel.
steps[i] = visualComponent.getName(); steps[i] = c.getName();
if (visualComponent instanceof JComponent) { if (c instanceof JComponent) { // assume Swing components
JComponent jc = (JComponent) visualComponent; JComponent jc = (JComponent) c;
// Set step number. // Sets step number of a component
jc.putClientProperty("WizardPanel_contentSelectedIndex", i); jc.putClientProperty("WizardPanel_contentSelectedIndex", new Integer(i));
// Sets step name. // Sets steps names for a panel
jc.putClientProperty("WizardPanel_contentData", steps); jc.putClientProperty("WizardPanel_contentData", steps);
// Turn on subtitle creation. // Turn on subtitle creation on each step
jc.putClientProperty("WizardPanel_autoWizardStyle", Boolean.TRUE); jc.putClientProperty("WizardPanel_autoWizardStyle", Boolean.TRUE);
// Show steps on the left side, with image in the background. // Show steps on the left side with the image on the background
jc.putClientProperty("WizardPanel_contentDisplayed", Boolean.TRUE); jc.putClientProperty("WizardPanel_contentDisplayed", Boolean.TRUE);
// Turn on step numbering. // Turn on numbering of all steps
jc.putClientProperty("WizardPanel_contentNumbered", Boolean.TRUE); jc.putClientProperty("WizardPanel_contentNumbered", Boolean.TRUE);
} }
} }
@ -81,10 +81,20 @@ final class AddImageWizardIterator implements WizardDescriptor.Iterator<WizardDe
return panels; return panels;
} }
/**
* Returns the index of the current panel. Note: 0 = panel 1, 1 = panel 2,
* etc
*
* @return index the current panel index
*/
public int getIndex() {
return index;
}
/** /**
* Gets the current panel. * Gets the current panel.
* *
* @return The current panel. * @return panel the current panel
*/ */
@Override @Override
public WizardDescriptor.Panel<WizardDescriptor> current() { public WizardDescriptor.Panel<WizardDescriptor> current() {
@ -98,17 +108,18 @@ final class AddImageWizardIterator implements WizardDescriptor.Iterator<WizardDe
/** /**
* Gets the name of the current panel. * Gets the name of the current panel.
* *
* @return The name of the current panel. * @return name the name of the current panel
*/ */
@Override @Override
public String name() { public String name() {
return NbBundle.getMessage(this.getClass(), "AddImageWizardIterator.stepXofN", Integer.toString(index + 1), getPanels().size()); return NbBundle.getMessage(this.getClass(), "AddImageWizardIterator.stepXofN", Integer.toString(index + 1),
getPanels().size());
} }
/** /**
* Tests whether there is a next panel. * Tests whether there is a next panel.
* *
* @return True or false. * @return boolean true if it has next panel, false if not
*/ */
@Override @Override
public boolean hasNext() { public boolean hasNext() {
@ -118,18 +129,17 @@ final class AddImageWizardIterator implements WizardDescriptor.Iterator<WizardDe
/** /**
* Tests whether there is a previous panel. * Tests whether there is a previous panel.
* *
* @return True or false. * @return boolean true if it has previous panel, false if not
*/ */
@Override @Override
// disable the previous button on all panels
public boolean hasPrevious() { public boolean hasPrevious() {
/*
* Disable the back buttons for the add data source wizard.
*/
return false; return false;
} }
/** /**
* Moves to the next panel. * Moves to the next panel. I.e. increment its index, need not actually
* change any GUI itself.
*/ */
@Override @Override
public void nextPanel() { public void nextPanel() {
@ -140,7 +150,8 @@ final class AddImageWizardIterator implements WizardDescriptor.Iterator<WizardDe
} }
/** /**
* Moves to the previous panel. * Moves to the previous panel. I.e. decrement its index, need not actually
* change any GUI itself.
*/ */
@Override @Override
public void previousPanel() { public void previousPanel() {
@ -153,18 +164,12 @@ final class AddImageWizardIterator implements WizardDescriptor.Iterator<WizardDe
index--; index--;
} }
/** // If nothing unusual changes in the middle of the wizard, simply:
* @inheritDoc
*/
@Override @Override
public void addChangeListener(ChangeListener l) { public void addChangeListener(ChangeListener l) {
} }
/**
* @inheritDoc
*/
@Override @Override
public void removeChangeListener(ChangeListener l) { public void removeChangeListener(ChangeListener l) {
} }
} }

View File

@ -93,6 +93,7 @@ AddImageAction.ingestConfig.ongoingIngest.msg=<html>Ingest is ongoing on another
AddImageAction.ingestConfig.ongoingIngest.title=Ingest in progress AddImageAction.ingestConfig.ongoingIngest.title=Ingest in progress
AddImageTask.run.progress.adding=Adding\: {0} AddImageTask.run.progress.adding=Adding\: {0}
AddImageTask.interrupt.exception.msg=Error stopping add-image process. 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. AddImageWizardAddingProgressPanel.stateStarted.progressBarText=*This process may take some time for large data sources.
AddImageWizardAddingProgressVisual.addingDsComplete.text=Adding Data Source - Complete AddImageWizardAddingProgressVisual.addingDsComplete.text=Adding Data Source - Complete
AddImageWizardAddingProgressVisual.getName.text=Add Data Source AddImageWizardAddingProgressVisual.getName.text=Add Data Source

View File

@ -88,6 +88,7 @@ AddImageAction.ingestConfig.ongoingIngest.msg=<html>\u4ed6\u306e\u30c7\u30fc\u30
AddImageAction.ingestConfig.ongoingIngest.title=\u51e6\u7406\u4e2d AddImageAction.ingestConfig.ongoingIngest.title=\u51e6\u7406\u4e2d
AddImageTask.run.progress.adding=\u8ffd\u52a0\u4e2d\uff1a{0} 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 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 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.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 AddImageWizardAddingProgressVisual.getName.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u8ffd\u52a0