mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-16 17:57:43 +00:00
Merge pull request #958 from rcordovano/ingest_job_configurator_refactor
Ingest job configurator refactor
This commit is contained in:
commit
a8f31e199e
@ -18,8 +18,6 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule;
|
||||
|
||||
|
||||
import org.sleuthkit.autopsy.ingest.IngestJobConfigurator;
|
||||
import org.openide.util.NbBundle;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
@ -37,6 +35,9 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
||||
import org.sleuthkit.autopsy.ingest.IngestJobSettings;
|
||||
import org.sleuthkit.autopsy.ingest.IngestJobSettingsPanel;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
/**
|
||||
* second panel of add image wizard, allows user to configure ingest modules.
|
||||
*
|
||||
@ -46,7 +47,8 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
||||
class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel<WizardDescriptor> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(AddImageWizardIngestConfigPanel.class.getName());
|
||||
private IngestJobConfigurator ingestConfig;
|
||||
private IngestJobSettings ingestJobSettings;
|
||||
|
||||
/**
|
||||
* The visual component that displays this panel. If you need to access the
|
||||
* component from this class, just use getComponent().
|
||||
@ -60,10 +62,10 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel<WizardDe
|
||||
// task that will clean up the created database file if the wizard is cancelled before it finishes
|
||||
private AddImageAction.CleanupTask cleanupTask;
|
||||
|
||||
private AddImageAction addImageAction;
|
||||
private final AddImageAction addImageAction;
|
||||
|
||||
private AddImageWizardAddingProgressPanel progressPanel;
|
||||
private AddImageWizardChooseDataSourcePanel dataSourcePanel;
|
||||
private final AddImageWizardAddingProgressPanel progressPanel;
|
||||
private final AddImageWizardChooseDataSourcePanel dataSourcePanel;
|
||||
|
||||
private DataSourceProcessor dsProcessor;
|
||||
|
||||
@ -73,8 +75,8 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel<WizardDe
|
||||
this.progressPanel = proPanel;
|
||||
this.dataSourcePanel = dsPanel;
|
||||
|
||||
ingestConfig = new IngestJobConfigurator(AddImageWizardIngestConfigPanel.class.getCanonicalName());
|
||||
List<String> messages = ingestConfig.getIngestJobConfigWarnings();
|
||||
this.ingestJobSettings = new IngestJobSettings(AddImageWizardIngestConfigPanel.class.getCanonicalName());
|
||||
List<String> messages = this.ingestJobSettings.getWarnings();
|
||||
if (messages.isEmpty() == false) {
|
||||
StringBuilder warning = new StringBuilder();
|
||||
for (String message : messages) {
|
||||
@ -95,7 +97,7 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel<WizardDe
|
||||
@Override
|
||||
public Component getComponent() {
|
||||
if (component == null) {
|
||||
component = new AddImageWizardIngestConfigVisual(ingestConfig.getIngestJobConfigPanel());
|
||||
component = new AddImageWizardIngestConfigVisual(new IngestJobSettingsPanel(this.ingestJobSettings));
|
||||
}
|
||||
return component;
|
||||
}
|
||||
@ -188,8 +190,16 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel<WizardDe
|
||||
*/
|
||||
@Override
|
||||
public void storeSettings(WizardDescriptor settings) {
|
||||
ingestConfig.saveIngestJobConfig();
|
||||
|
||||
this.ingestJobSettings.save();
|
||||
List<String> messages = this.ingestJobSettings.getWarnings();
|
||||
if (messages.isEmpty() == false) {
|
||||
StringBuilder warning = new StringBuilder();
|
||||
for (String message : messages) {
|
||||
warning.append(message).append("\n");
|
||||
}
|
||||
JOptionPane.showMessageDialog(null, warning.toString());
|
||||
}
|
||||
|
||||
// Start ingest if it hasn't already been started
|
||||
readyToIngest = true;
|
||||
startIngest();
|
||||
@ -202,9 +212,11 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel<WizardDe
|
||||
private void startIngest() {
|
||||
if (!newContents.isEmpty() && readyToIngest && !ingested) {
|
||||
ingested = true;
|
||||
ingestConfig.startIngestJobs(newContents);
|
||||
IngestManager ingestManager = IngestManager.getInstance();
|
||||
for (Content content : newContents) {
|
||||
ingestManager.startIngestJob(content, ingestJobSettings, true);
|
||||
}
|
||||
progressPanel.setStateFinished();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,8 +21,6 @@ IngestJob.progress.dataSourceIngest.displayName={0} for {1}
|
||||
IngestJob.progress.fileIngest.displayName=Analyzing files from {0}
|
||||
IngestJob.progress.cancelling={0} (Cancelling...)
|
||||
IngestJob.cancellationDialog.title=Cancel Ingest
|
||||
IngestJobConfigurationPanel.processUnallocCheckbox.toolTipText=Processes unallocated space, such as deleted files. Produces more complete results, but it may take longer to process on large images.
|
||||
IngestJobConfigurationPanel.processUnallocCheckbox.text=Process Unallocated Space
|
||||
IngestDialog.title.text=Ingest Modules
|
||||
IngestDialog.startButton.title=Start
|
||||
IngestDialog.closeButton.title=Close
|
||||
@ -50,8 +48,6 @@ IngestMessageTopComponent.displayReport.option.GenRpt=Generate Report
|
||||
IngestMessageTopComponent.msgDlg.ingestRpt.text=Ingest Report
|
||||
IngestMonitor.mgrErrMsg.lowDiskSpace.title=Ingest stopped - low disk space on {0}
|
||||
IngestMonitor.mgrErrMsg.lowDiskSpace.msg=Stopping ingest due to low disk space on disk {0}. \nEnsure the Case drive has at least 1GB free space and restart ingest.
|
||||
IngestJobConfigurationPanel.advancedButton.text=Advanced
|
||||
IngestJobConfigurationPanel.advancedButton.actionCommand=Advanced
|
||||
IngestManager.StartIngestJobsTask.run.displayName=Starting ingest
|
||||
IngestManager.StartIngestJobsTask.run.cancelling={0} (Cancelling...)
|
||||
IngestMessagePanel.sortByComboBox.model.time=Time
|
||||
@ -62,11 +58,11 @@ IngestManager.StartIngestJobsTask.run.startupErr.dlgMsg=Unable to start up one o
|
||||
IngestManager.StartIngestJobsTask.run.startupErr.dlgSolution=Please disable the failed modules or fix the errors and then restart ingest \nby right clicking on the data source and selecting Run Ingest Modules.
|
||||
IngestManager.StartIngestJobsTask.run.startupErr.dlgErrorList=\nErrors\: \n{0}
|
||||
IngestManager.StartIngestJobsTask.run.startupErr.dlgTitle=Ingest Failure
|
||||
IngestJobConfigurator.createModuleSettingsFolderForContext.exception.msg=Failed to create ingest module settings folder, cannot save settings.
|
||||
IngestJobConfigurator.createModuleSettingsFolderForContext.exception.title=Ingest Job Initialization Failure
|
||||
IngestJobConfigurator.saveJobSettings.usermsg=Failed to save ingest job settings for {0} module.
|
||||
IngestJobConfigurator.saveJobSettings.usermsg.title=Ingest Job Settings
|
||||
IngestJobConfigurationPanel.descriptionLabel.text=
|
||||
IngestJobSettings.createModuleSettingsFolder.warning=Failed to create ingest module settings folder, cannot save settings.
|
||||
IngestJobSettings.missingModule.warning=Previously loaded {0} module could not be found.
|
||||
IngestJobSettings.save.warning=Failed to save ingest job settings for {0} module.
|
||||
IngestJobSettings.moduleSettingsLoad.warning=Error loading ingest job settings for {0} module for {1} context, using defaults.
|
||||
IngestJobSettings.moduleSettingsSave.warning=Error saving ingest job settings for {0} module for {1} context.
|
||||
IngestProgressSnapshotDialog.title.text=Ingest Progress Snapshot
|
||||
IngestProgressSnapshotPanel.refreshButton.text=Refresh
|
||||
IngestProgressSnapshotPanel.closeButton.text=Close
|
||||
@ -85,3 +81,8 @@ DataSourceIngestCancellationPanel.cancelCurrentModuleRadioButton.text=Cancel cur
|
||||
FileIngestCancellationPanel.cancelIngestJobRadioButton.text=Cancel data source ingest and file ingest
|
||||
FileIngestCancellationPanel.cancelFileIngestRadioButton.text=Cancel file ingest only
|
||||
DataSourceIngestCancellationPanel.cancelAllModulesRadioButton.text=Cancel all ingest modules
|
||||
IngestJobSettingsPanel.advancedButton.actionCommand=Advanced
|
||||
IngestJobSettingsPanel.advancedButton.text=Advanced
|
||||
IngestJobSettingsPanel.processUnallocCheckbox.toolTipText=Processes unallocated space, such as deleted files. Produces more complete results, but it may take longer to process on large images.
|
||||
IngestJobSettingsPanel.processUnallocCheckbox.text=Process Unallocated Space
|
||||
IngestJobSettingsPanel.descriptionLabel.text=
|
||||
|
@ -6,10 +6,6 @@ IngestDialog.title.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u
|
||||
IngestJob.progress.cancelling={0}\uff08\u30ad\u30e3\u30f3\u30bb\u30eb\u4e2d\u2026\uff09
|
||||
IngestJob.progress.dataSourceIngest.displayName={1}\u306e{0}
|
||||
IngestJob.progress.fileIngest.displayName={0}\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u89e3\u6790\u4e2d
|
||||
IngestJobConfigurationPanel.advancedButton.actionCommand=\u30a2\u30c9\u30d0\u30f3\u30b9
|
||||
IngestJobConfigurationPanel.advancedButton.text=\u30a2\u30c9\u30d0\u30f3\u30b9
|
||||
IngestJobConfigurationPanel.processUnallocCheckbox.text=\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u306e\u51e6\u7406
|
||||
IngestJobConfigurationPanel.processUnallocCheckbox.toolTipText=\u524a\u9664\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u7b49\u306e\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u3092\u51e6\u7406\u3002\u3088\u308a\u5b8c\u5168\u306a\u7d50\u679c\u304c\u51fa\u307e\u3059\u304c\u3001\u5927\u304d\u3044\u30a4\u30e1\u30fc\u30b8\u3067\u306f\u51e6\u7406\u6642\u9593\u304c\u9577\u304f\u306a\u308b\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002
|
||||
IngestManager.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc
|
||||
IngestManager.moduleErr.errListenToUpdates.msg=Ingest Manager\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3067\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002
|
||||
IngestManager.StartIngestJobsTask.run.cancelling={0}\uff08\u30ad\u30e3\u30f3\u30bb\u30eb\u4e2d\u2026\uff09
|
||||
@ -57,10 +53,8 @@ IngestManager.StartIngestJobsTask.run.startupErr.dlgErrorList=\n\u30a8\u30e9\u30
|
||||
IngestManager.StartIngestJobsTask.run.startupErr.dlgMsg=\uff11\u3064\u307e\u305f\u306f\u8907\u6570\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u30b9\u30bf\u30fc\u30c8\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b8\u30e7\u30d6\u306f\u30ad\u30e3\u30f3\u30bb\u30eb\u3055\u308c\u307e\u3057\u305f\u3002
|
||||
IngestManager.StartIngestJobsTask.run.startupErr.dlgSolution=\u5931\u6557\u3057\u305f\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u7121\u52b9\u5316\u3059\u308b\u304b\u30a8\u30e9\u30fc\u3092\u89e3\u6c7a\u3057\u3001\u305d\u306e\u5f8c\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u53f3\u30af\u30ea\u30c3\u30af\u3057\u3001\n\u300c\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u5b9f\u884c\u300d\u3092\u9078\u629e\u3057\u3066\u3001\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u30ea\u30b9\u30bf\u30fc\u30c8\u3057\u3066\u4e0b\u3055\u3044\u3002
|
||||
IngestManager.StartIngestJobsTask.run.startupErr.dlgTitle=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u5931\u6557
|
||||
IngestJobConfigurator.createModuleSettingsFolderForContext.exception.msg=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u8a2d\u5b9a\u30d5\u30a9\u30eb\u30c0\u306e\u4f5c\u6210\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3067\u304d\u307e\u305b\u3093\u3002
|
||||
IngestJobConfigurator.createModuleSettingsFolderForContext.exception.title=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b8\u30e7\u30d6\u521d\u671f\u5316\u306e\u5931\u6557
|
||||
IngestJobConfigurator.saveJobSettings.usermsg={0}\u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b8\u30e7\u30d6\u8a2d\u5b9a\u306e\u4fdd\u5b58\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002
|
||||
IngestJobConfigurator.saveJobSettings.usermsg.title=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b8\u30e7\u30d6\u8a2d\u5b9a
|
||||
IngestJobSettings.createModuleSettingsFolder.warning=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u8a2d\u5b9a\u30d5\u30a9\u30eb\u30c0\u306e\u4f5c\u6210\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3067\u304d\u307e\u305b\u3093\u3002
|
||||
IngestJobSettings.save.error={0}\u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b8\u30e7\u30d6\u8a2d\u5b9a\u306e\u4fdd\u5b58\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002
|
||||
IngestJob.progress.dataSourceIngest.initialDisplayName={0}\u3092\u89e3\u6790\u4e2d
|
||||
IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.dataSource=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9
|
||||
IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.elapsedTime=\u7d4c\u904e\u6642\u9593\uff08\u6642\uff1a\u5206\uff1a\u79d2\uff09
|
||||
@ -75,3 +69,7 @@ IngestProgressSnapshotDialog.title.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30
|
||||
IngestProgressSnapshotPanel.closeButton.text=\u9589\u3058\u308b
|
||||
IngestProgressSnapshotPanel.refreshButton.text=\u30ea\u30d5\u30ec\u30c3\u30b7\u30e5
|
||||
IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.activity=\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3
|
||||
IngestJobSettingsPanel.advancedButton.actionCommand=\u30a2\u30c9\u30d0\u30f3\u30b9
|
||||
IngestJobSettingsPanel.advancedButton.text=\u30a2\u30c9\u30d0\u30f3\u30b9
|
||||
IngestJobSettingsPanel.processUnallocCheckbox.toolTipText=\u524a\u9664\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u7b49\u306e\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u3092\u51e6\u7406\u3002\u3088\u308a\u5b8c\u5168\u306a\u7d50\u679c\u304c\u51fa\u307e\u3059\u304c\u3001\u5927\u304d\u3044\u30a4\u30e1\u30fc\u30b8\u3067\u306f\u51e6\u7406\u6642\u9593\u304c\u9577\u304f\u306a\u308b\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002
|
||||
IngestJobSettingsPanel.processUnallocCheckbox.text=\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u306e\u51e6\u7406
|
||||
|
@ -141,19 +141,16 @@ final class IngestJob {
|
||||
* Starts an ingest job for a data source.
|
||||
*
|
||||
* @param dataSource The data source to ingest.
|
||||
* @param ingestModuleTemplates The ingest module templates to use to create
|
||||
* the ingest pipelines for the job.
|
||||
* @param processUnallocatedSpace Whether or not the job should include
|
||||
* processing of unallocated space.
|
||||
* @param settings The settings for the job.
|
||||
* @return A collection of ingest module start up errors, empty on success.
|
||||
*/
|
||||
static List<IngestModuleError> startJob(Content dataSource, List<IngestModuleTemplate> ingestModuleTemplates, boolean processUnallocatedSpace) {
|
||||
static List<IngestModuleError> startJob(Content dataSource, IngestJobSettings settings) {
|
||||
List<IngestModuleError> errors = new ArrayList<>();
|
||||
if (IngestJob.jobCreationIsEnabled) {
|
||||
long jobId = nextJobId.incrementAndGet();
|
||||
IngestJob job = new IngestJob(jobId, dataSource, processUnallocatedSpace);
|
||||
IngestJob job = new IngestJob(jobId, dataSource, settings.getProcessUnallocatedSpace());
|
||||
IngestJob.jobsById.put(jobId, job);
|
||||
errors = job.start(ingestModuleTemplates);
|
||||
errors = job.start(settings.getEnabledIngestModuleTemplates());
|
||||
if (errors.isEmpty() && job.hasIngestPipeline()) {
|
||||
IngestManager.getInstance().fireIngestJobStarted(jobId);
|
||||
IngestJob.logger.log(Level.INFO, "Ingest job {0} started", jobId);
|
||||
|
@ -18,298 +18,78 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.ingest;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.io.NbObjectInputStream;
|
||||
import org.openide.util.io.NbObjectOutputStream;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
|
||||
/**
|
||||
* Provides a mechanism for creating and persisting an ingest job configuration
|
||||
* for a particular context and for launching ingest jobs that process one or
|
||||
* more data sources using the ingest job configuration.
|
||||
*
|
||||
* @deprecated Use the IngestModuleSettings and IngestJobConfigurationPanel
|
||||
* classes and IngestManager.startIngestJob() instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class IngestJobConfigurator {
|
||||
|
||||
private static final String ENABLED_INGEST_MODULES_KEY = "Enabled_Ingest_Modules"; //NON-NLS
|
||||
private static final String DISABLED_INGEST_MODULES_KEY = "Disabled_Ingest_Modules"; //NON-NLS
|
||||
private static final String PARSE_UNALLOC_SPACE_KEY = "Process_Unallocated_Space"; //NON-NLS
|
||||
private static final String MODULE_SETTINGS_FOLDER_PATH = new StringBuilder(PlatformUtil.getUserConfigDirectory()).append(File.separator).append("IngestModuleSettings").toString(); //NON-NLS
|
||||
private static final String MODULE_SETTINGS_FILE_EXT = ".settings"; //NON-NLS
|
||||
private static final Logger logger = Logger.getLogger(IngestJobConfigurator.class.getName());
|
||||
private final String launcherContext;
|
||||
private String moduleSettingsFolderForContext = null;
|
||||
private final List<String> warnings = new ArrayList<>();
|
||||
private IngestJobConfigurationPanel ingestConfigPanel = null;
|
||||
private final IngestJobSettings settings;
|
||||
private final IngestJobSettingsPanel settingsPanel;
|
||||
|
||||
/**
|
||||
* Constructs an ingest job launcher that creates and persists an ingest job
|
||||
* configuration for a particular context and launches ingest jobs that
|
||||
* process one or more data sources using the ingest job configuration.
|
||||
* Constructs an ingest job launcher that creates and persists ingest job
|
||||
* settings for a particular context and launches ingest jobs that
|
||||
* process one or more data sources using the settings.
|
||||
*
|
||||
* @param launcherContext The context identifier.
|
||||
* @param context The context identifier.
|
||||
*/
|
||||
public IngestJobConfigurator(String launcherContext) {
|
||||
this.launcherContext = launcherContext;
|
||||
|
||||
createModuleSettingsFolderForContext();
|
||||
|
||||
// Get the ingest module factories discovered by the ingest module
|
||||
// loader.
|
||||
List<IngestModuleFactory> moduleFactories = IngestModuleFactoryLoader.getIngestModuleFactories();
|
||||
HashSet<String> loadedModuleNames = new HashSet<>();
|
||||
for (IngestModuleFactory moduleFactory : moduleFactories) {
|
||||
loadedModuleNames.add(moduleFactory.getModuleDisplayName());
|
||||
}
|
||||
|
||||
// Get the enabled and disabled ingest modules settings for the current
|
||||
// context. Observe that the default settings make all loaded ingest
|
||||
// modules enabled.
|
||||
HashSet<String> enabledModuleNames = getModulesNamesFromSetting(ENABLED_INGEST_MODULES_KEY, makeCommaSeparatedList(loadedModuleNames));
|
||||
HashSet<String> disabledModuleNames = getModulesNamesFromSetting(DISABLED_INGEST_MODULES_KEY, "");
|
||||
|
||||
// Check for missing modules.
|
||||
List<String> missingModuleNames = new ArrayList<>();
|
||||
for (String moduleName : enabledModuleNames) {
|
||||
if (!loadedModuleNames.contains(moduleName)) {
|
||||
missingModuleNames.add(moduleName);
|
||||
}
|
||||
}
|
||||
for (String moduleName : disabledModuleNames) {
|
||||
if (!loadedModuleNames.contains(moduleName)) {
|
||||
missingModuleNames.add(moduleName);
|
||||
}
|
||||
}
|
||||
for (String moduleName : missingModuleNames) {
|
||||
enabledModuleNames.remove(moduleName);
|
||||
disabledModuleNames.remove(moduleName);
|
||||
warnings.add(String.format("Previously loaded %s module could not be found", moduleName)); //NON-NLS
|
||||
}
|
||||
|
||||
// Create ingest module templates.
|
||||
List<IngestModuleTemplate> moduleTemplates = new ArrayList<>();
|
||||
for (IngestModuleFactory moduleFactory : moduleFactories) {
|
||||
IngestModuleTemplate moduleTemplate = new IngestModuleTemplate(moduleFactory, loadJobSettings(moduleFactory));
|
||||
String moduleName = moduleTemplate.getModuleName();
|
||||
if (enabledModuleNames.contains(moduleName)) {
|
||||
moduleTemplate.setEnabled(true);
|
||||
} else if (disabledModuleNames.contains(moduleName)) {
|
||||
moduleTemplate.setEnabled(false);
|
||||
} else {
|
||||
// The module factory was loaded, but the module name does not
|
||||
// appear in the enabled/disabled module settings. Treat the
|
||||
// module as a new module and enable it by default.
|
||||
moduleTemplate.setEnabled(true);
|
||||
enabledModuleNames.add(moduleName);
|
||||
}
|
||||
moduleTemplates.add(moduleTemplate);
|
||||
}
|
||||
|
||||
// Update the enabled/disabled ingest module settings to reflect any
|
||||
// missing modules or newly discovered modules.
|
||||
ModuleSettings.setConfigSetting(launcherContext, ENABLED_INGEST_MODULES_KEY, makeCommaSeparatedList(enabledModuleNames));
|
||||
ModuleSettings.setConfigSetting(launcherContext, DISABLED_INGEST_MODULES_KEY, makeCommaSeparatedList(disabledModuleNames));
|
||||
|
||||
// Get the process unallocated space flag setting. If the setting does
|
||||
// not exist yet, default it to true.
|
||||
if (ModuleSettings.settingExists(launcherContext, PARSE_UNALLOC_SPACE_KEY) == false) {
|
||||
ModuleSettings.setConfigSetting(launcherContext, PARSE_UNALLOC_SPACE_KEY, "true"); //NON-NLS
|
||||
}
|
||||
boolean processUnallocatedSpace = Boolean.parseBoolean(ModuleSettings.getConfigSetting(launcherContext, PARSE_UNALLOC_SPACE_KEY));
|
||||
|
||||
// Make the configuration panel for the context.
|
||||
ingestConfigPanel = new IngestJobConfigurationPanel(moduleTemplates, processUnallocatedSpace);
|
||||
}
|
||||
|
||||
private void createModuleSettingsFolderForContext() {
|
||||
try {
|
||||
StringBuilder folderPath = new StringBuilder(MODULE_SETTINGS_FOLDER_PATH);
|
||||
folderPath.append(File.separator);
|
||||
folderPath.append(launcherContext);
|
||||
folderPath.append(File.separator);
|
||||
File folder = new File(folderPath.toString());
|
||||
if (!folder.exists()) {
|
||||
Files.createDirectories(folder.toPath());
|
||||
}
|
||||
moduleSettingsFolderForContext = folder.getAbsolutePath();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Failed to create ingest module settings directory", ex); //NON-NLS
|
||||
JOptionPane.showMessageDialog(null,
|
||||
NbBundle.getMessage(this.getClass(),
|
||||
"IngestJobConfigurator.createModuleSettingsFolderForContext.exception.msg"),
|
||||
NbBundle.getMessage(this.getClass(),
|
||||
"IngestJobConfigurator.createModuleSettingsFolderForContext.exception.title"),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private HashSet<String> getModulesNamesFromSetting(String key, String defaultSetting) {
|
||||
// Get the ingest modules setting from the user's config file.
|
||||
// If there is no such setting yet, create the default setting.
|
||||
if (ModuleSettings.settingExists(launcherContext, key) == false) {
|
||||
ModuleSettings.setConfigSetting(launcherContext, key, defaultSetting);
|
||||
}
|
||||
HashSet<String> moduleNames = new HashSet<>();
|
||||
String modulesSetting = ModuleSettings.getConfigSetting(launcherContext, key);
|
||||
if (!modulesSetting.isEmpty()) {
|
||||
String[] settingNames = modulesSetting.split(", ");
|
||||
for (String name : settingNames) {
|
||||
// Map some old core module names to the current core module names.
|
||||
switch (name) {
|
||||
case "Thunderbird Parser": //NON-NLS
|
||||
case "MBox Parser": //NON-NLS
|
||||
moduleNames.add("Email Parser"); //NON-NLS
|
||||
break;
|
||||
case "File Extension Mismatch Detection": //NON-NLS
|
||||
moduleNames.add("Extension Mismatch Detector"); //NON-NLS
|
||||
break;
|
||||
case "EWF Verify": //NON-NLS
|
||||
case "E01 Verify": //NON-NLS
|
||||
moduleNames.add("E01 Verifier"); //NON-NLS
|
||||
break;
|
||||
default:
|
||||
moduleNames.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return moduleNames;
|
||||
}
|
||||
|
||||
private IngestModuleIngestJobSettings loadJobSettings(IngestModuleFactory factory) {
|
||||
IngestModuleIngestJobSettings settings = null;
|
||||
File settingsFile = new File(getModuleSettingsFilePath(factory));
|
||||
if (settingsFile.exists()) {
|
||||
try (NbObjectInputStream in = new NbObjectInputStream(new FileInputStream(settingsFile.getAbsolutePath()))) {
|
||||
settings = (IngestModuleIngestJobSettings) in.readObject();
|
||||
} catch (IOException | ClassNotFoundException ex) {
|
||||
String logMessage = String.format("Error loading ingest job settings for %s module for %s context, using defaults", factory.getModuleDisplayName(), launcherContext); //NON-NLS
|
||||
logger.log(Level.WARNING, logMessage, ex);
|
||||
}
|
||||
}
|
||||
if (settings == null) {
|
||||
settings = factory.getDefaultIngestJobSettings();
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
private void saveJobSettings(IngestModuleFactory factory, IngestModuleIngestJobSettings settings) {
|
||||
try {
|
||||
try (NbObjectOutputStream out = new NbObjectOutputStream(new FileOutputStream(getModuleSettingsFilePath(factory)))) {
|
||||
out.writeObject(settings);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
String logMessage = String.format("Error saving ingest job settings for %s module for %s context", factory.getModuleDisplayName(), launcherContext); //NON-NLS
|
||||
logger.log(Level.SEVERE, logMessage, ex);
|
||||
String userMessage = NbBundle.getMessage(this.getClass(), "IngestJobConfigurator.saveJobSettings.usermsg", factory.getModuleDisplayName());
|
||||
JOptionPane.showMessageDialog(null, userMessage,
|
||||
NbBundle.getMessage(this.getClass(),
|
||||
"IngestJobConfigurator.saveJobSettings.usermsg.title"),
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private String getModuleSettingsFilePath(IngestModuleFactory factory) {
|
||||
StringBuilder filePath = new StringBuilder(this.moduleSettingsFolderForContext);
|
||||
filePath.append(File.separator);
|
||||
filePath.append(factory.getClass().getCanonicalName());
|
||||
filePath.append(MODULE_SETTINGS_FILE_EXT);
|
||||
return filePath.toString();
|
||||
@Deprecated
|
||||
public IngestJobConfigurator(String context) {
|
||||
this.settings = new IngestJobSettings(context);
|
||||
this.settingsPanel = new IngestJobSettingsPanel(settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets any warnings generated when the persisted ingest job configuration
|
||||
* for the specified context is retrieved and loaded.
|
||||
* Gets any warnings generated when the persisted ingest job settings
|
||||
* for the specified context are loaded or saved.
|
||||
*
|
||||
* @return A collection of warning messages.
|
||||
* @return A collection of warning messages, possibly empty.
|
||||
*/
|
||||
@Deprecated
|
||||
public List<String> getIngestJobConfigWarnings() {
|
||||
return warnings;
|
||||
return this.settings.getWarnings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user interface panel the launcher uses to obtain the user's
|
||||
* ingest job configuration for the specified context.
|
||||
* ingest job settings for the specified context.
|
||||
*
|
||||
* @return A JPanel with components that can be used to create an ingest job
|
||||
* configuration.
|
||||
* @return The panel.
|
||||
*/
|
||||
@Deprecated
|
||||
public JPanel getIngestJobConfigPanel() {
|
||||
return ingestConfigPanel;
|
||||
return settingsPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists the ingest job configuration for the specified context.
|
||||
* Persists the ingest job settings for the specified context.
|
||||
*/
|
||||
@Deprecated
|
||||
public void saveIngestJobConfig() {
|
||||
List<IngestModuleTemplate> moduleTemplates = ingestConfigPanel.getIngestModuleTemplates();
|
||||
|
||||
// Save the enabled/disabled ingest module settings for the current context.
|
||||
HashSet<String> enabledModuleNames = new HashSet<>();
|
||||
HashSet<String> disabledModuleNames = new HashSet<>();
|
||||
for (IngestModuleTemplate moduleTemplate : moduleTemplates) {
|
||||
saveJobSettings(moduleTemplate.getModuleFactory(), moduleTemplate.getModuleSettings());
|
||||
String moduleName = moduleTemplate.getModuleName();
|
||||
if (moduleTemplate.isEnabled()) {
|
||||
enabledModuleNames.add(moduleName);
|
||||
} else {
|
||||
disabledModuleNames.add(moduleName);
|
||||
}
|
||||
}
|
||||
ModuleSettings.setConfigSetting(launcherContext, ENABLED_INGEST_MODULES_KEY, makeCommaSeparatedList(enabledModuleNames));
|
||||
ModuleSettings.setConfigSetting(launcherContext, DISABLED_INGEST_MODULES_KEY, makeCommaSeparatedList(disabledModuleNames));
|
||||
|
||||
// Save the process unallocated space setting for the current context.
|
||||
String processUnalloc = Boolean.toString(ingestConfigPanel.getProcessUnallocSpace());
|
||||
ModuleSettings.setConfigSetting(launcherContext, PARSE_UNALLOC_SPACE_KEY, processUnalloc);
|
||||
}
|
||||
|
||||
private static String makeCommaSeparatedList(HashSet<String> input) {
|
||||
if (input == null || input.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.addAll(input);
|
||||
StringBuilder csvList = new StringBuilder();
|
||||
for (int i = 0; i < list.size() - 1; ++i) {
|
||||
csvList.append(list.get(i)).append(", ");
|
||||
}
|
||||
csvList.append(list.get(list.size() - 1));
|
||||
return csvList.toString();
|
||||
this.settings.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches ingest jobs for one or more data sources using the ingest job
|
||||
* configuration for the selected context.
|
||||
* settings for the specified context.
|
||||
*
|
||||
* @param dataSources The data sources to ingest.
|
||||
*/
|
||||
@Deprecated
|
||||
public void startIngestJobs(List<Content> dataSources) {
|
||||
// Filter out the disabled ingest module templates.
|
||||
List<IngestModuleTemplate> enabledModuleTemplates = new ArrayList<>();
|
||||
List<IngestModuleTemplate> moduleTemplates = ingestConfigPanel.getIngestModuleTemplates();
|
||||
for (IngestModuleTemplate moduleTemplate : moduleTemplates) {
|
||||
if (moduleTemplate.isEnabled()) {
|
||||
enabledModuleTemplates.add(moduleTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
if ((!enabledModuleTemplates.isEmpty()) && (dataSources != null) && (!dataSources.isEmpty())) {
|
||||
IngestManager.getInstance().startIngestJobs(dataSources, enabledModuleTemplates, ingestConfigPanel.getProcessUnallocSpace());
|
||||
IngestManager ingestManager = IngestManager.getInstance();
|
||||
for (Content dataSource : dataSources) {
|
||||
ingestManager.startIngestJob(dataSource, this.settings, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
385
Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java
Normal file
385
Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java
Normal file
@ -0,0 +1,385 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.ingest;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.io.NbObjectInputStream;
|
||||
import org.openide.util.io.NbObjectOutputStream;
|
||||
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
|
||||
/**
|
||||
* Encapsulates the ingest job settings for a particular context such as the Add
|
||||
* Data Source wizard or the Run Ingest Modules dialog.
|
||||
*/
|
||||
public class IngestJobSettings {
|
||||
|
||||
private static final String ENABLED_MODULES_KEY = "Enabled_Ingest_Modules"; //NON-NLS
|
||||
private static final String DISABLED_MODULES_KEY = "Disabled_Ingest_Modules"; //NON-NLS
|
||||
private static final String PARSE_UNALLOC_SPACE_KEY = "Process_Unallocated_Space"; //NON-NLS
|
||||
private static final String PROCESS_UNALLOC_SPACE_DEFAULT = "true"; //NON-NLS
|
||||
private static final String MODULE_SETTINGS_FOLDER = "IngestModuleSettings"; //NON-NLS
|
||||
private static final String MODULE_SETTINGS_FOLDER_PATH = Paths.get(PlatformUtil.getUserConfigDirectory(), IngestJobSettings.MODULE_SETTINGS_FOLDER).toAbsolutePath().toString();
|
||||
private static final String MODULE_SETTINGS_FILE_EXT = ".settings"; //NON-NLS
|
||||
private static final Logger logger = Logger.getLogger(IngestJobSettings.class.getName());
|
||||
private final String context;
|
||||
private String moduleSettingsFolderPath;
|
||||
private final List<IngestModuleTemplate> moduleTemplates;
|
||||
private boolean processUnallocatedSpace;
|
||||
private final List<String> warnings;
|
||||
|
||||
/**
|
||||
* Constructs an ingest job settings object for a given context.
|
||||
*
|
||||
* @param context The context identifier string.
|
||||
*/
|
||||
public IngestJobSettings(String context) {
|
||||
this.context = context;
|
||||
this.moduleTemplates = new ArrayList<>();
|
||||
this.processUnallocatedSpace = Boolean.parseBoolean(IngestJobSettings.PROCESS_UNALLOC_SPACE_DEFAULT);
|
||||
this.warnings = new ArrayList<>();
|
||||
this.createSavedModuleSettingsFolder();
|
||||
this.load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves these ingest job settings.
|
||||
*/
|
||||
public void save() {
|
||||
this.store();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and clears any accumulated warnings associated with these ingest job
|
||||
* settings.
|
||||
*
|
||||
* @return A list of warning messages, possibly empty.
|
||||
*/
|
||||
public List<String> getWarnings() {
|
||||
List<String> warningMessages = new ArrayList<>(this.warnings);
|
||||
this.warnings.clear();
|
||||
return warningMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ingest module templates part of these ingest job settings.
|
||||
*
|
||||
* @return The list of ingest module templates.
|
||||
*/
|
||||
List<IngestModuleTemplate> getIngestModuleTemplates() {
|
||||
return Collections.unmodifiableList(this.moduleTemplates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the enabled ingest module templates part of these ingest job
|
||||
* settings.
|
||||
*
|
||||
* @return The list of enabled ingest module templates.
|
||||
*/
|
||||
List<IngestModuleTemplate> getEnabledIngestModuleTemplates() {
|
||||
List<IngestModuleTemplate> enabledModuleTemplates = new ArrayList<>();
|
||||
for (IngestModuleTemplate moduleTemplate : this.moduleTemplates) {
|
||||
if (moduleTemplate.isEnabled()) {
|
||||
enabledModuleTemplates.add(moduleTemplate);
|
||||
}
|
||||
}
|
||||
return enabledModuleTemplates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ingest module templates part of these ingest job settings.
|
||||
*
|
||||
* @param moduleTemplates The ingest module templates.
|
||||
*/
|
||||
void setIngestModuleTemplates(List<IngestModuleTemplate> moduleTemplates) {
|
||||
this.moduleTemplates.clear();
|
||||
this.moduleTemplates.addAll(moduleTemplates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the process unallocated space flag part of these ingest job
|
||||
* settings.
|
||||
*
|
||||
* @return True or false.
|
||||
*/
|
||||
boolean getProcessUnallocatedSpace() {
|
||||
return this.processUnallocatedSpace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the process unallocated space flag for these ingest job settings.
|
||||
*
|
||||
* @param processUnallocatedSpace True or false.
|
||||
*/
|
||||
void setProcessUnallocatedSpace(boolean processUnallocatedSpace) {
|
||||
this.processUnallocatedSpace = processUnallocatedSpace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the folder for saving the individual ingest module settings part
|
||||
* of these ingest job settings.
|
||||
*/
|
||||
private void createSavedModuleSettingsFolder() {
|
||||
try {
|
||||
Path folder = Paths.get(IngestJobSettings.MODULE_SETTINGS_FOLDER_PATH, context);
|
||||
Files.createDirectories(folder);
|
||||
this.moduleSettingsFolderPath = folder.toAbsolutePath().toString();
|
||||
} catch (IOException | SecurityException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to create ingest module settings directory " + this.moduleSettingsFolderPath, ex); //NON-NLS
|
||||
this.warnings.add(NbBundle.getMessage(IngestJobSettings.class, "IngestJobSettings.createModuleSettingsFolder.warning")); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the saved or default ingest job settings context into memory.
|
||||
*/
|
||||
private void load() {
|
||||
/**
|
||||
* Get the ingest module factories discovered by the ingest module
|
||||
* loader.
|
||||
*/
|
||||
List<IngestModuleFactory> moduleFactories = IngestModuleFactoryLoader.getIngestModuleFactories();
|
||||
HashSet<String> loadedModuleNames = new HashSet<>();
|
||||
for (IngestModuleFactory moduleFactory : moduleFactories) {
|
||||
loadedModuleNames.add(moduleFactory.getModuleDisplayName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the enabled/disabled ingest modules settings for this context. By
|
||||
* default, all loaded modules are enabled.
|
||||
*/
|
||||
HashSet<String> enabledModuleNames = getModulesNamesFromSetting(IngestJobSettings.ENABLED_MODULES_KEY, makeCommaSeparatedValuesList(loadedModuleNames));
|
||||
HashSet<String> disabledModuleNames = getModulesNamesFromSetting(IngestJobSettings.DISABLED_MODULES_KEY, ""); //NON-NLS
|
||||
|
||||
/**
|
||||
* Check for missing modules and create warnings if any are found.
|
||||
*/
|
||||
List<String> missingModuleNames = new ArrayList<>();
|
||||
for (String moduleName : enabledModuleNames) {
|
||||
if (!loadedModuleNames.contains(moduleName)) {
|
||||
missingModuleNames.add(moduleName);
|
||||
}
|
||||
}
|
||||
for (String moduleName : disabledModuleNames) {
|
||||
if (!loadedModuleNames.contains(moduleName)) {
|
||||
missingModuleNames.add(moduleName);
|
||||
}
|
||||
}
|
||||
for (String moduleName : missingModuleNames) {
|
||||
enabledModuleNames.remove(moduleName);
|
||||
disabledModuleNames.remove(moduleName);
|
||||
String warning = NbBundle.getMessage(IngestJobSettings.class, "IngestJobSettings.missingModule.warning", moduleName); //NON-NLS
|
||||
logger.log(Level.WARNING, warning);
|
||||
this.warnings.add(warning);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create ingest module templates. Each template encapsulates a module
|
||||
* factory, the module settings for this context, and an enabled flag.
|
||||
*/
|
||||
for (IngestModuleFactory moduleFactory : moduleFactories) {
|
||||
IngestModuleTemplate moduleTemplate = new IngestModuleTemplate(moduleFactory, loadModuleSettings(moduleFactory));
|
||||
String moduleName = moduleTemplate.getModuleName();
|
||||
if (enabledModuleNames.contains(moduleName)) {
|
||||
moduleTemplate.setEnabled(true);
|
||||
} else if (disabledModuleNames.contains(moduleName)) {
|
||||
moduleTemplate.setEnabled(false);
|
||||
} else {
|
||||
// The module factory was loaded, but the module name does not
|
||||
// appear in the enabled/disabled module settings. Treat the
|
||||
// module as a new module and enable it by default.
|
||||
moduleTemplate.setEnabled(true);
|
||||
enabledModuleNames.add(moduleName);
|
||||
}
|
||||
this.moduleTemplates.add(moduleTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the enabled/disabled ingest module settings for this context
|
||||
* to reflect any missing modules or newly discovered modules.
|
||||
*/
|
||||
ModuleSettings.setConfigSetting(this.context, IngestJobSettings.ENABLED_MODULES_KEY, makeCommaSeparatedValuesList(enabledModuleNames));
|
||||
ModuleSettings.setConfigSetting(this.context, IngestJobSettings.DISABLED_MODULES_KEY, makeCommaSeparatedValuesList(disabledModuleNames));
|
||||
|
||||
// Get the process unallocated space flag setting. If the setting does
|
||||
// not exist yet, default it to true.
|
||||
if (ModuleSettings.settingExists(this.context, IngestJobSettings.PARSE_UNALLOC_SPACE_KEY) == false) {
|
||||
ModuleSettings.setConfigSetting(this.context, IngestJobSettings.PARSE_UNALLOC_SPACE_KEY, IngestJobSettings.PROCESS_UNALLOC_SPACE_DEFAULT);
|
||||
}
|
||||
this.processUnallocatedSpace = Boolean.parseBoolean(ModuleSettings.getConfigSetting(this.context, IngestJobSettings.PARSE_UNALLOC_SPACE_KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the module names for a given key within these ingest job settings.
|
||||
*
|
||||
* @param key The key string.
|
||||
* @param defaultSetting The default list of module names.
|
||||
* @return The list of module names associated with the key.
|
||||
*/
|
||||
private HashSet<String> getModulesNamesFromSetting(String key, String defaultSetting) {
|
||||
if (ModuleSettings.settingExists(this.context, key) == false) {
|
||||
ModuleSettings.setConfigSetting(this.context, key, defaultSetting);
|
||||
}
|
||||
HashSet<String> moduleNames = new HashSet<>();
|
||||
String modulesSetting = ModuleSettings.getConfigSetting(this.context, key);
|
||||
if (!modulesSetting.isEmpty()) {
|
||||
String[] settingNames = modulesSetting.split(", ");
|
||||
for (String name : settingNames) {
|
||||
// Map some old core module names to the current core module names.
|
||||
switch (name) {
|
||||
case "Thunderbird Parser": //NON-NLS
|
||||
case "MBox Parser": //NON-NLS
|
||||
moduleNames.add("Email Parser"); //NON-NLS
|
||||
break;
|
||||
case "File Extension Mismatch Detection": //NON-NLS
|
||||
moduleNames.add("Extension Mismatch Detector"); //NON-NLS
|
||||
break;
|
||||
case "EWF Verify": //NON-NLS
|
||||
case "E01 Verify": //NON-NLS
|
||||
moduleNames.add("E01 Verifier"); //NON-NLS
|
||||
break;
|
||||
default:
|
||||
moduleNames.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return moduleNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the saved or default ingest job settings for a given ingest module
|
||||
* for these ingest job settings.
|
||||
*
|
||||
* @param factory The ingest module factory for an ingest module.
|
||||
* @return The ingest module settings.
|
||||
*/
|
||||
private IngestModuleIngestJobSettings loadModuleSettings(IngestModuleFactory factory) {
|
||||
IngestModuleIngestJobSettings settings = null;
|
||||
File settingsFile = new File(getModuleSettingsFilePath(factory));
|
||||
if (settingsFile.exists()) {
|
||||
try (NbObjectInputStream in = new NbObjectInputStream(new FileInputStream(settingsFile.getAbsolutePath()))) {
|
||||
settings = (IngestModuleIngestJobSettings) in.readObject();
|
||||
} catch (IOException | ClassNotFoundException ex) {
|
||||
String warning = NbBundle.getMessage(IngestJobSettings.class, "IngestJobSettings.moduleSettingsLoad.warning", factory.getModuleDisplayName(), this.context); //NON-NLS
|
||||
logger.log(Level.WARNING, warning, ex);
|
||||
this.warnings.add(warning);
|
||||
}
|
||||
}
|
||||
if (settings == null) {
|
||||
settings = factory.getDefaultIngestJobSettings();
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute path for the ingest job settings file for a given
|
||||
* ingest module for these ingest job settings.
|
||||
*
|
||||
* @param factory The ingest module factory for an ingest module.
|
||||
* @return The file path.
|
||||
*/
|
||||
private String getModuleSettingsFilePath(IngestModuleFactory factory) {
|
||||
String fileName = factory.getClass().getCanonicalName() + IngestJobSettings.MODULE_SETTINGS_FILE_EXT;
|
||||
Path path = Paths.get(this.moduleSettingsFolderPath, fileName);
|
||||
return path.toAbsolutePath().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the ingest job settings for this context.
|
||||
*/
|
||||
private void store() {
|
||||
/**
|
||||
* Save the enabled/disabled ingest module settings.
|
||||
*/
|
||||
HashSet<String> enabledModuleNames = new HashSet<>();
|
||||
HashSet<String> disabledModuleNames = new HashSet<>();
|
||||
for (IngestModuleTemplate moduleTemplate : moduleTemplates) {
|
||||
saveModuleSettings(moduleTemplate.getModuleFactory(), moduleTemplate.getModuleSettings());
|
||||
String moduleName = moduleTemplate.getModuleName();
|
||||
if (moduleTemplate.isEnabled()) {
|
||||
enabledModuleNames.add(moduleName);
|
||||
} else {
|
||||
disabledModuleNames.add(moduleName);
|
||||
}
|
||||
}
|
||||
ModuleSettings.setConfigSetting(this.context, ENABLED_MODULES_KEY, makeCommaSeparatedValuesList(enabledModuleNames));
|
||||
ModuleSettings.setConfigSetting(this.context, DISABLED_MODULES_KEY, makeCommaSeparatedValuesList(disabledModuleNames));
|
||||
|
||||
/**
|
||||
* Save the process unallocated space setting.
|
||||
*/
|
||||
String processUnalloc = Boolean.toString(this.processUnallocatedSpace);
|
||||
ModuleSettings.setConfigSetting(this.context, PARSE_UNALLOC_SPACE_KEY, processUnalloc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the ingest job settings for this context for a given ingest
|
||||
* module.
|
||||
*
|
||||
* @param factory The ingest module factory for the module.
|
||||
* @param settings The ingest job settings for the ingest module
|
||||
*/
|
||||
private void saveModuleSettings(IngestModuleFactory factory, IngestModuleIngestJobSettings settings) {
|
||||
try {
|
||||
try (NbObjectOutputStream out = new NbObjectOutputStream(new FileOutputStream(getModuleSettingsFilePath(factory)))) {
|
||||
out.writeObject(settings);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
String warning = NbBundle.getMessage(IngestJobSettings.class, "IngestJobSettings.moduleSettingsSave.warning", factory.getModuleDisplayName(), this.context); //NON-NLS
|
||||
logger.log(Level.SEVERE, warning, ex);
|
||||
this.warnings.add(warning);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a comma-separated values list from a hash set of strings.
|
||||
*
|
||||
* @param input A hash set of strings.
|
||||
* @return The contents of the hash set as a single string of
|
||||
* comma-separated values.
|
||||
*/
|
||||
private static String makeCommaSeparatedValuesList(HashSet<String> input) {
|
||||
if (input == null || input.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.addAll(input);
|
||||
StringBuilder csvList = new StringBuilder();
|
||||
for (int i = 0; i < list.size() - 1; ++i) {
|
||||
csvList.append(list.get(i)).append(", ");
|
||||
}
|
||||
csvList.append(list.get(list.size() - 1));
|
||||
return csvList.toString();
|
||||
}
|
||||
|
||||
}
|
@ -144,10 +144,10 @@
|
||||
<Component class="javax.swing.JButton" name="advancedButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/ingest/Bundle.properties" key="IngestJobConfigurationPanel.advancedButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/ingest/Bundle.properties" key="IngestJobSettingsPanel.advancedButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="actionCommand" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/ingest/Bundle.properties" key="IngestJobConfigurationPanel.advancedButton.actionCommand" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/ingest/Bundle.properties" key="IngestJobSettingsPanel.advancedButton.actionCommand" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
@ -160,7 +160,7 @@
|
||||
<Component class="javax.swing.JLabel" name="descriptionLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/ingest/Bundle.properties" key="IngestJobConfigurationPanel.descriptionLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/ingest/Bundle.properties" key="IngestJobSettingsPanel.descriptionLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
@ -221,10 +221,10 @@
|
||||
<Component class="javax.swing.JCheckBox" name="processUnallocCheckbox">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/ingest/Bundle.properties" key="IngestJobConfigurationPanel.processUnallocCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/ingest/Bundle.properties" key="IngestJobSettingsPanel.processUnallocCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/ingest/Bundle.properties" key="IngestJobConfigurationPanel.processUnallocCheckbox.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/ingest/Bundle.properties" key="IngestJobSettingsPanel.processUnallocCheckbox.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
@ -35,39 +35,47 @@ import javax.swing.table.TableColumn;
|
||||
import org.sleuthkit.autopsy.corecomponents.AdvancedConfigurationDialog;
|
||||
|
||||
/**
|
||||
* User interface component to allow a user to set ingest module options and
|
||||
* enable/disable the modules.
|
||||
* User interface component to allow a user to make ingest job settings.
|
||||
*/
|
||||
class IngestJobConfigurationPanel extends javax.swing.JPanel {
|
||||
public final class IngestJobSettingsPanel extends javax.swing.JPanel {
|
||||
|
||||
private List<IngestModuleModel> modules = new ArrayList<>();
|
||||
private boolean processUnallocatedSpace = false;
|
||||
private IngestModuleModel selectedModule = null;
|
||||
private final IngestJobSettings settings;
|
||||
private final List<IngestModuleModel> modules;
|
||||
private IngestModuleModel selectedModule;
|
||||
|
||||
IngestJobConfigurationPanel(List<IngestModuleTemplate> moduleTemplates, boolean processUnallocatedSpace) {
|
||||
for (IngestModuleTemplate moduleTemplate : moduleTemplates) {
|
||||
modules.add(new IngestModuleModel(moduleTemplate));
|
||||
/**
|
||||
* Construct a user interface component to allow a user to make ingest job
|
||||
* settings.
|
||||
*
|
||||
* @param settings The initial settings for the ingest job.
|
||||
*/
|
||||
public IngestJobSettingsPanel(IngestJobSettings settings) {
|
||||
this.settings = settings;
|
||||
this.modules = new ArrayList<>();
|
||||
for (IngestModuleTemplate moduleTemplate : settings.getIngestModuleTemplates()) {
|
||||
this.modules.add(new IngestModuleModel(moduleTemplate));
|
||||
}
|
||||
this.processUnallocatedSpace = processUnallocatedSpace;
|
||||
initComponents();
|
||||
customizeComponents();
|
||||
}
|
||||
|
||||
List<IngestModuleTemplate> getIngestModuleTemplates() {
|
||||
/**
|
||||
* Gets the ingest settings made using this panel.
|
||||
*
|
||||
* @return The settings.
|
||||
*/
|
||||
IngestJobSettings getSettings() {
|
||||
List<IngestModuleTemplate> moduleTemplates = new ArrayList<>();
|
||||
for (IngestModuleModel module : modules) {
|
||||
IngestModuleTemplate moduleTemplate = module.getIngestModuleTemplate();
|
||||
if (module.hasModuleSettingsPanel()) {
|
||||
IngestModuleIngestJobSettings settings = module.getModuleSettingsPanel().getSettings();
|
||||
moduleTemplate.setModuleSettings(settings);
|
||||
IngestModuleIngestJobSettings moduleSettings = module.getModuleSettingsPanel().getSettings();
|
||||
moduleTemplate.setModuleSettings(moduleSettings);
|
||||
}
|
||||
moduleTemplates.add(moduleTemplate);
|
||||
}
|
||||
return moduleTemplates;
|
||||
}
|
||||
|
||||
boolean getProcessUnallocSpace() {
|
||||
return processUnallocCheckbox.isSelected();
|
||||
this.settings.setIngestModuleTemplates(moduleTemplates);
|
||||
return this.settings;
|
||||
}
|
||||
|
||||
private void customizeComponents() {
|
||||
@ -113,7 +121,7 @@ class IngestJobConfigurationPanel extends javax.swing.JPanel {
|
||||
}
|
||||
});
|
||||
|
||||
processUnallocCheckbox.setSelected(processUnallocatedSpace);
|
||||
processUnallocCheckbox.setSelected(this.settings.getProcessUnallocatedSpace());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -160,8 +168,8 @@ class IngestJobConfigurationPanel extends javax.swing.JPanel {
|
||||
jPanel1.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(160, 160, 160)));
|
||||
jPanel1.setPreferredSize(new java.awt.Dimension(338, 257));
|
||||
|
||||
advancedButton.setText(org.openide.util.NbBundle.getMessage(IngestJobConfigurationPanel.class, "IngestJobConfigurationPanel.advancedButton.text")); // NOI18N
|
||||
advancedButton.setActionCommand(org.openide.util.NbBundle.getMessage(IngestJobConfigurationPanel.class, "IngestJobConfigurationPanel.advancedButton.actionCommand")); // NOI18N
|
||||
advancedButton.setText(org.openide.util.NbBundle.getMessage(IngestJobSettingsPanel.class, "IngestJobSettingsPanel.advancedButton.text")); // NOI18N
|
||||
advancedButton.setActionCommand(org.openide.util.NbBundle.getMessage(IngestJobSettingsPanel.class, "IngestJobSettingsPanel.advancedButton.actionCommand")); // NOI18N
|
||||
advancedButton.setEnabled(false);
|
||||
advancedButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
@ -169,7 +177,7 @@ class IngestJobConfigurationPanel extends javax.swing.JPanel {
|
||||
}
|
||||
});
|
||||
|
||||
descriptionLabel.setText(org.openide.util.NbBundle.getMessage(IngestJobConfigurationPanel.class, "IngestJobConfigurationPanel.descriptionLabel.text")); // NOI18N
|
||||
descriptionLabel.setText(org.openide.util.NbBundle.getMessage(IngestJobSettingsPanel.class, "IngestJobSettingsPanel.descriptionLabel.text")); // NOI18N
|
||||
|
||||
jScrollPane1.setBorder(null);
|
||||
jScrollPane1.setPreferredSize(new java.awt.Dimension(250, 180));
|
||||
@ -208,8 +216,8 @@ class IngestJobConfigurationPanel extends javax.swing.JPanel {
|
||||
|
||||
processUnallocPanel.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(160, 160, 160)));
|
||||
|
||||
processUnallocCheckbox.setText(org.openide.util.NbBundle.getMessage(IngestJobConfigurationPanel.class, "IngestJobConfigurationPanel.processUnallocCheckbox.text")); // NOI18N
|
||||
processUnallocCheckbox.setToolTipText(org.openide.util.NbBundle.getMessage(IngestJobConfigurationPanel.class, "IngestJobConfigurationPanel.processUnallocCheckbox.toolTipText")); // NOI18N
|
||||
processUnallocCheckbox.setText(org.openide.util.NbBundle.getMessage(IngestJobSettingsPanel.class, "IngestJobSettingsPanel.processUnallocCheckbox.text")); // NOI18N
|
||||
processUnallocCheckbox.setToolTipText(org.openide.util.NbBundle.getMessage(IngestJobSettingsPanel.class, "IngestJobSettingsPanel.processUnallocCheckbox.toolTipText")); // NOI18N
|
||||
processUnallocCheckbox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
processUnallocCheckboxActionPerformed(evt);
|
||||
@ -261,7 +269,7 @@ class IngestJobConfigurationPanel extends javax.swing.JPanel {
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void processUnallocCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_processUnallocCheckboxActionPerformed
|
||||
processUnallocatedSpace = processUnallocCheckbox.isSelected();
|
||||
this.settings.setProcessUnallocatedSpace(processUnallocCheckbox.isSelected());
|
||||
}//GEN-LAST:event_processUnallocCheckboxActionPerformed
|
||||
|
||||
private void advancedButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_advancedButtonActionPerformed
|
@ -46,7 +46,8 @@ import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
|
||||
/**
|
||||
* Manages the execution of ingest jobs.
|
||||
* Manages the creation and execution of ingest jobs, i.e., processing of data
|
||||
* sources by ingest modules.
|
||||
*/
|
||||
public class IngestManager {
|
||||
|
||||
@ -64,7 +65,7 @@ public class IngestManager {
|
||||
private final ExecutorService fileIngestThreadPool;
|
||||
private final ExecutorService fireIngestEventsThreadPool = Executors.newSingleThreadExecutor();
|
||||
private final AtomicLong nextThreadId = new AtomicLong(0L);
|
||||
private final ConcurrentHashMap<Long, Future<Void>> startIngestJobsTasks = new ConcurrentHashMap<>(); // Maps thread ids to cancellation handles.
|
||||
private final ConcurrentHashMap<Long, Future<Void>> startIngestJobsCallables = new ConcurrentHashMap<>(); // Maps thread ids to cancellation handles.
|
||||
private final AtomicLong ingestErrorMessagePosts = new AtomicLong(0L);
|
||||
private final ConcurrentHashMap<Long, IngestThreadActivitySnapshot> ingestThreadActivitySnapshots = new ConcurrentHashMap<>(); // Maps ingest thread ids to progress ingestThreadActivitySnapshots.
|
||||
private final ConcurrentHashMap<String, Long> ingestModuleRunTimes = new ConcurrentHashMap<>();
|
||||
@ -73,6 +74,60 @@ public class IngestManager {
|
||||
private volatile IngestMessageTopComponent ingestMessageBox;
|
||||
private int numberOfFileIngestThreads = DEFAULT_NUMBER_OF_FILE_INGEST_THREADS;
|
||||
|
||||
/**
|
||||
* Ingest job events.
|
||||
*/
|
||||
public enum IngestJobEvent {
|
||||
|
||||
/**
|
||||
* Property change event fired when an ingest job is started. The old
|
||||
* value of the PropertyChangeEvent object is set to the ingest job id,
|
||||
* and the new value is set to null.
|
||||
*/
|
||||
STARTED,
|
||||
/**
|
||||
* Property change event fired when an ingest job is completed. The old
|
||||
* value of the PropertyChangeEvent object is set to the ingest job id,
|
||||
* and the new value is set to null.
|
||||
*/
|
||||
COMPLETED,
|
||||
/**
|
||||
* Property change event fired when an ingest job is canceled. The old
|
||||
* value of the PropertyChangeEvent object is set to the ingest job id,
|
||||
* and the new value is set to null.
|
||||
*/
|
||||
CANCELLED,
|
||||
};
|
||||
|
||||
/**
|
||||
* Ingest module events.
|
||||
*/
|
||||
public enum IngestModuleEvent {
|
||||
|
||||
/**
|
||||
* Property change event fired when an ingest module adds new data to a
|
||||
* case, usually by posting to the blackboard. The old value of the
|
||||
* PropertyChangeEvent is a ModuleDataEvent object, and the new value is
|
||||
* set to null.
|
||||
*/
|
||||
DATA_ADDED,
|
||||
/**
|
||||
* Property change event fired when an ingest module adds new content to
|
||||
* a case or changes a recorded attribute of existing content. For
|
||||
* example, if a module adds an extracted or carved file to a case, the
|
||||
* module should fire this event. The old value of the
|
||||
* PropertyChangeEvent is a ModuleContentEvent object, and the new value
|
||||
* is set to null.
|
||||
*/
|
||||
CONTENT_CHANGED,
|
||||
/**
|
||||
* Property change event fired when the ingest of a file is completed.
|
||||
* The old value of the PropertyChangeEvent is the Autopsy object ID of
|
||||
* the file. The new value is the AbstractFile for that ID.
|
||||
*/
|
||||
FILE_DONE,
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the ingest manager.
|
||||
*
|
||||
@ -90,6 +145,123 @@ public class IngestManager {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of file ingest threads the ingest manager will use to do
|
||||
* ingest jobs.
|
||||
*
|
||||
* @return The number of file ingest threads.
|
||||
*/
|
||||
public int getNumberOfFileIngestThreads() {
|
||||
return numberOfFileIngestThreads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an ingest job, i.e., processing by ingest modules, for a data
|
||||
* source.
|
||||
*
|
||||
* @param dataSource The data source to be processed.
|
||||
* @param settings The ingest job settings.
|
||||
* @param doStartupErrorsMsgBox Whether or not to display ingest module
|
||||
* startup errors in a message box.
|
||||
*/
|
||||
public synchronized void startIngestJob(Content dataSource, IngestJobSettings settings, boolean doStartupErrorsMsgBox) {
|
||||
if (!isIngestRunning()) {
|
||||
clearIngestMessageBox();
|
||||
}
|
||||
|
||||
if (!ingestMonitor.isRunning()) {
|
||||
ingestMonitor.start();
|
||||
}
|
||||
|
||||
long taskId = nextThreadId.incrementAndGet();
|
||||
Future<Void> task = startIngestJobsThreadPool.submit(new StartIngestJobsCallable(taskId, dataSource, settings, doStartupErrorsMsgBox));
|
||||
startIngestJobsCallables.put(taskId, task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries whether any ingest jobs are in progress.
|
||||
*
|
||||
* @return True or false.
|
||||
*/
|
||||
public boolean isIngestRunning() {
|
||||
return IngestJob.ingestJobsAreRunning();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels all ingest jobs in progress.
|
||||
*/
|
||||
public void cancelAllIngestJobs() {
|
||||
// Stop creating new ingest jobs.
|
||||
for (Future<Void> handle : startIngestJobsCallables.values()) {
|
||||
handle.cancel(true);
|
||||
}
|
||||
|
||||
// Cancel all the jobs already created.
|
||||
IngestJob.cancelAllJobs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an ingest job event property change listener.
|
||||
*
|
||||
* @param listener The PropertyChangeListener to register.
|
||||
*/
|
||||
public void addIngestJobEventListener(final PropertyChangeListener listener) {
|
||||
ingestJobEventPublisher.addPropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an ingest job event property change listener.
|
||||
*
|
||||
* @param listener The PropertyChangeListener to unregister.
|
||||
*/
|
||||
public void removeIngestJobEventListener(final PropertyChangeListener listener) {
|
||||
ingestJobEventPublisher.removePropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an ingest module event property change listener.
|
||||
*
|
||||
* @param listener The PropertyChangeListener to register.
|
||||
*/
|
||||
public void addIngestModuleEventListener(final PropertyChangeListener listener) {
|
||||
ingestModuleEventPublisher.addPropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an ingest module event property change listener.
|
||||
*
|
||||
* @param listener The PropertyChangeListener to unregister.
|
||||
*/
|
||||
public void removeIngestModuleEventListener(final PropertyChangeListener listener) {
|
||||
ingestModuleEventPublisher.removePropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an ingest job and ingest module event property change listener.
|
||||
*
|
||||
* @param listener The PropertyChangeListener to register.
|
||||
* @deprecated Use addIngestJobEventListener() and/or
|
||||
* addIngestModuleEventListener().
|
||||
*/
|
||||
@Deprecated
|
||||
public static void addPropertyChangeListener(final PropertyChangeListener listener) {
|
||||
instance.ingestJobEventPublisher.addPropertyChangeListener(listener);
|
||||
instance.ingestModuleEventPublisher.addPropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an ingest job and ingest module event property change listener.
|
||||
*
|
||||
* @param listener The PropertyChangeListener to unregister.
|
||||
* @deprecated Use removeIngestJobEventListener() and/or
|
||||
* removeIngestModuleEventListener().
|
||||
*/
|
||||
@Deprecated
|
||||
public static void removePropertyChangeListener(final PropertyChangeListener listener) {
|
||||
instance.ingestJobEventPublisher.removePropertyChangeListener(listener);
|
||||
instance.ingestModuleEventPublisher.removePropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the ingest monitor and submits task execution tasks (Callable
|
||||
* objects) to the data source ingest and file ingest thread pools. The task
|
||||
@ -97,7 +269,7 @@ public class IngestManager {
|
||||
* the application runs
|
||||
*/
|
||||
private IngestManager() {
|
||||
startDataSourceIngestTask();
|
||||
startDataSourceIngestThread();
|
||||
|
||||
numberOfFileIngestThreads = UserPreferences.numberOfFileIngestThreads();
|
||||
if ((numberOfFileIngestThreads < MIN_NUMBER_OF_FILE_INGEST_THREADS) || (numberOfFileIngestThreads > MAX_NUMBER_OF_FILE_INGEST_THREADS)) {
|
||||
@ -106,7 +278,7 @@ public class IngestManager {
|
||||
}
|
||||
fileIngestThreadPool = Executors.newFixedThreadPool(numberOfFileIngestThreads);
|
||||
for (int i = 0; i < numberOfFileIngestThreads; ++i) {
|
||||
startFileIngestTask();
|
||||
startFileIngestThread();
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,20 +291,11 @@ public class IngestManager {
|
||||
ingestMessageBox = IngestMessageTopComponent.findInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of file ingest threads the ingest manager will use.
|
||||
*
|
||||
* @return The number of file ingest threads.
|
||||
*/
|
||||
public int getNumberOfFileIngestThreads() {
|
||||
return numberOfFileIngestThreads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits a ExecuteIngestTasksTask Callable to the data source ingest task
|
||||
* thread pool.
|
||||
*/
|
||||
private void startDataSourceIngestTask() {
|
||||
private void startDataSourceIngestThread() {
|
||||
long threadId = nextThreadId.incrementAndGet();
|
||||
dataSourceIngestThreadPool.submit(new ExecuteIngestTasksRunnable(threadId, IngestTasksScheduler.getInstance().getDataSourceIngestTaskQueue()));
|
||||
ingestThreadActivitySnapshots.put(threadId, new IngestThreadActivitySnapshot(threadId));
|
||||
@ -142,22 +305,12 @@ public class IngestManager {
|
||||
* Submits a ExecuteIngestTasksTask Callable to the data source ingest
|
||||
* thread pool.
|
||||
*/
|
||||
private void startFileIngestTask() {
|
||||
private void startFileIngestThread() {
|
||||
long threadId = nextThreadId.incrementAndGet();
|
||||
fileIngestThreadPool.submit(new ExecuteIngestTasksRunnable(threadId, IngestTasksScheduler.getInstance().getFileIngestTaskQueue()));
|
||||
ingestThreadActivitySnapshots.put(threadId, new IngestThreadActivitySnapshot(threadId));
|
||||
}
|
||||
|
||||
synchronized void startIngestJobs(final List<Content> dataSources, final List<IngestModuleTemplate> moduleTemplates, boolean processUnallocatedSpace) {
|
||||
if (!isIngestRunning()) {
|
||||
clearIngestMessageBox();
|
||||
}
|
||||
|
||||
long taskId = nextThreadId.incrementAndGet();
|
||||
Future<Void> task = startIngestJobsThreadPool.submit(new StartIngestJobsCallable(taskId, dataSources, moduleTemplates, processUnallocatedSpace));
|
||||
startIngestJobsTasks.put(taskId, task);
|
||||
}
|
||||
|
||||
private void subscribeToCaseEvents() {
|
||||
Case.addPropertyChangeListener(new PropertyChangeListener() {
|
||||
@Override
|
||||
@ -191,15 +344,6 @@ public class IngestManager {
|
||||
ingestErrorMessagePosts.set(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if any ingest jobs are in progress.
|
||||
*
|
||||
* @return True if any ingest jobs are in progress, false otherwise.
|
||||
*/
|
||||
public boolean isIngestRunning() {
|
||||
return IngestJob.ingestJobsAreRunning();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called each time a module in a data source pipeline starts
|
||||
*
|
||||
@ -292,132 +436,6 @@ public class IngestManager {
|
||||
return new ArrayList<>(ingestThreadActivitySnapshots.values());
|
||||
}
|
||||
|
||||
public void cancelAllIngestJobs() {
|
||||
// Stop creating new ingest jobs.
|
||||
for (Future<Void> handle : startIngestJobsTasks.values()) {
|
||||
handle.cancel(true);
|
||||
}
|
||||
|
||||
// Cancel all the jobs already created.
|
||||
IngestJob.cancelAllJobs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ingest job events.
|
||||
*/
|
||||
public enum IngestJobEvent {
|
||||
|
||||
/**
|
||||
* Property change event fired when an ingest job is started. The old
|
||||
* value of the PropertyChangeEvent object is set to the ingest job id,
|
||||
* and the new value is set to null.
|
||||
*/
|
||||
STARTED,
|
||||
/**
|
||||
* Property change event fired when an ingest job is completed. The old
|
||||
* value of the PropertyChangeEvent object is set to the ingest job id,
|
||||
* and the new value is set to null.
|
||||
*/
|
||||
COMPLETED,
|
||||
/**
|
||||
* Property change event fired when an ingest job is canceled. The old
|
||||
* value of the PropertyChangeEvent object is set to the ingest job id,
|
||||
* and the new value is set to null.
|
||||
*/
|
||||
CANCELLED,
|
||||
};
|
||||
|
||||
/**
|
||||
* Ingest module events.
|
||||
*/
|
||||
public enum IngestModuleEvent {
|
||||
|
||||
/**
|
||||
* Property change event fired when an ingest module adds new data to a
|
||||
* case, usually by posting to the blackboard. The old value of the
|
||||
* PropertyChangeEvent is a ModuleDataEvent object, and the new value is
|
||||
* set to null.
|
||||
*/
|
||||
DATA_ADDED,
|
||||
/**
|
||||
* Property change event fired when an ingest module adds new content to
|
||||
* a case or changes a recorded attribute of existing content. For
|
||||
* example, if a module adds an extracted or carved file to a case, the
|
||||
* module should fire this event. The old value of the
|
||||
* PropertyChangeEvent is a ModuleContentEvent object, and the new value
|
||||
* is set to null.
|
||||
*/
|
||||
CONTENT_CHANGED,
|
||||
/**
|
||||
* Property change event fired when the ingest of a file is completed.
|
||||
* The old value of the PropertyChangeEvent is the Autopsy object ID of
|
||||
* the file. The new value is the AbstractFile for that ID.
|
||||
*/
|
||||
FILE_DONE,
|
||||
};
|
||||
|
||||
/**
|
||||
* Add an ingest job event property change listener.
|
||||
*
|
||||
* @param listener The PropertyChangeListener to register.
|
||||
*/
|
||||
public void addIngestJobEventListener(final PropertyChangeListener listener) {
|
||||
ingestJobEventPublisher.addPropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an ingest job event property change listener.
|
||||
*
|
||||
* @param listener The PropertyChangeListener to unregister.
|
||||
*/
|
||||
public void removeIngestJobEventListener(final PropertyChangeListener listener) {
|
||||
ingestJobEventPublisher.removePropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an ingest module event property change listener.
|
||||
*
|
||||
* @param listener The PropertyChangeListener to register.
|
||||
*/
|
||||
public void addIngestModuleEventListener(final PropertyChangeListener listener) {
|
||||
ingestModuleEventPublisher.addPropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an ingest module event property change listener.
|
||||
*
|
||||
* @param listener The PropertyChangeListener to unregister.
|
||||
*/
|
||||
public void removeIngestModuleEventListener(final PropertyChangeListener listener) {
|
||||
ingestModuleEventPublisher.removePropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an ingest job and ingest module event property change listener.
|
||||
*
|
||||
* @deprecated Use addIngestJobEventListener() and/or
|
||||
* addIngestModuleEventListener().
|
||||
* @param listener The PropertyChangeListener to register.
|
||||
*/
|
||||
@Deprecated
|
||||
public static void addPropertyChangeListener(final PropertyChangeListener listener) {
|
||||
instance.ingestJobEventPublisher.addPropertyChangeListener(listener);
|
||||
instance.ingestModuleEventPublisher.addPropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an ingest job and ingest module event property change listener.
|
||||
*
|
||||
* @deprecated Use removeIngestJobEventListener() and/or
|
||||
* removeIngestModuleEventListener().
|
||||
* @param listener The PropertyChangeListener to unregister.
|
||||
*/
|
||||
@Deprecated
|
||||
public static void removePropertyChangeListener(final PropertyChangeListener listener) {
|
||||
instance.ingestJobEventPublisher.removePropertyChangeListener(listener);
|
||||
instance.ingestModuleEventPublisher.removePropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire an ingest event signifying an ingest job started.
|
||||
*
|
||||
@ -513,26 +531,34 @@ public class IngestManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates ingest jobs.
|
||||
* Creates and starts an ingest job, i.e., processing by ingest modules, for
|
||||
* a data source.
|
||||
*/
|
||||
private final class StartIngestJobsCallable implements Callable<Void> {
|
||||
|
||||
private final long threadId;
|
||||
private final List<Content> dataSources;
|
||||
private final List<IngestModuleTemplate> moduleTemplates;
|
||||
private final boolean processUnallocatedSpace;
|
||||
private final Content dataSource;
|
||||
private final IngestJobSettings settings;
|
||||
private final boolean doStartupErrorsMsgBox;
|
||||
private ProgressHandle progress;
|
||||
|
||||
StartIngestJobsCallable(long threadId, List<Content> dataSources, List<IngestModuleTemplate> moduleTemplates, boolean processUnallocatedSpace) {
|
||||
StartIngestJobsCallable(long threadId, Content dataSource, IngestJobSettings settings, boolean doStartupErrorsMsgBox) {
|
||||
this.threadId = threadId;
|
||||
this.dataSources = dataSources;
|
||||
this.moduleTemplates = moduleTemplates;
|
||||
this.processUnallocatedSpace = processUnallocatedSpace;
|
||||
this.dataSource = dataSource;
|
||||
this.settings = settings;
|
||||
this.doStartupErrorsMsgBox = doStartupErrorsMsgBox;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void call() {
|
||||
try {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a progress bar.
|
||||
*/
|
||||
final String displayName = NbBundle.getMessage(this.getClass(),
|
||||
"IngestManager.StartIngestJobsTask.run.displayName");
|
||||
progress = ProgressHandleFactory.createHandle(displayName, new Cancellable() {
|
||||
@ -543,62 +569,50 @@ public class IngestManager {
|
||||
"IngestManager.StartIngestJobsTask.run.cancelling",
|
||||
displayName));
|
||||
}
|
||||
Future<?> handle = startIngestJobsTasks.remove(threadId);
|
||||
Future<?> handle = startIngestJobsCallables.remove(threadId);
|
||||
handle.cancel(true);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
progress.start(dataSources.size());
|
||||
progress.switchToIndeterminate();
|
||||
progress.start();
|
||||
|
||||
if (!ingestMonitor.isRunning()) {
|
||||
ingestMonitor.start();
|
||||
}
|
||||
|
||||
int dataSourceProcessed = 0;
|
||||
for (Content dataSource : dataSources) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Start an ingest job for the data source.
|
||||
List<IngestModuleError> errors = IngestJob.startJob(dataSource, moduleTemplates, processUnallocatedSpace);
|
||||
if (!errors.isEmpty()) {
|
||||
// Report the errors to the user. They have already been logged.
|
||||
StringBuilder moduleStartUpErrors = new StringBuilder();
|
||||
for (IngestModuleError error : errors) {
|
||||
String moduleName = error.getModuleDisplayName();
|
||||
moduleStartUpErrors.append(moduleName);
|
||||
moduleStartUpErrors.append(": ");
|
||||
moduleStartUpErrors.append(error.getModuleError().getLocalizedMessage());
|
||||
moduleStartUpErrors.append("\n");
|
||||
}
|
||||
StringBuilder notifyMessage = new StringBuilder();
|
||||
notifyMessage.append(NbBundle.getMessage(this.getClass(),
|
||||
"IngestManager.StartIngestJobsTask.run.startupErr.dlgMsg"));
|
||||
notifyMessage.append("\n");
|
||||
notifyMessage.append(NbBundle.getMessage(this.getClass(),
|
||||
"IngestManager.StartIngestJobsTask.run.startupErr.dlgSolution"));
|
||||
notifyMessage.append("\n");
|
||||
notifyMessage.append(NbBundle.getMessage(this.getClass(),
|
||||
"IngestManager.StartIngestJobsTask.run.startupErr.dlgErrorList",
|
||||
moduleStartUpErrors.toString()));
|
||||
notifyMessage.append("\n\n");
|
||||
JOptionPane.showMessageDialog(null, notifyMessage.toString(),
|
||||
NbBundle.getMessage(this.getClass(),
|
||||
"IngestManager.StartIngestJobsTask.run.startupErr.dlgTitle"), JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
progress.progress(++dataSourceProcessed);
|
||||
|
||||
if (!Thread.currentThread().isInterrupted()) {
|
||||
break;
|
||||
/**
|
||||
* Create and start an ingest job for the data source.
|
||||
*/
|
||||
List<IngestModuleError> errors = IngestJob.startJob(dataSource, this.settings);
|
||||
if (!errors.isEmpty() && this.doStartupErrorsMsgBox) {
|
||||
// Report the errors to the user. They have already been logged.
|
||||
StringBuilder moduleStartUpErrors = new StringBuilder();
|
||||
for (IngestModuleError error : errors) {
|
||||
String moduleName = error.getModuleDisplayName();
|
||||
moduleStartUpErrors.append(moduleName);
|
||||
moduleStartUpErrors.append(": ");
|
||||
moduleStartUpErrors.append(error.getModuleError().getLocalizedMessage());
|
||||
moduleStartUpErrors.append("\n");
|
||||
}
|
||||
StringBuilder notifyMessage = new StringBuilder();
|
||||
notifyMessage.append(NbBundle.getMessage(this.getClass(),
|
||||
"IngestManager.StartIngestJobsTask.run.startupErr.dlgMsg"));
|
||||
notifyMessage.append("\n");
|
||||
notifyMessage.append(NbBundle.getMessage(this.getClass(),
|
||||
"IngestManager.StartIngestJobsTask.run.startupErr.dlgSolution"));
|
||||
notifyMessage.append("\n");
|
||||
notifyMessage.append(NbBundle.getMessage(this.getClass(),
|
||||
"IngestManager.StartIngestJobsTask.run.startupErr.dlgErrorList",
|
||||
moduleStartUpErrors.toString()));
|
||||
notifyMessage.append("\n\n");
|
||||
JOptionPane.showMessageDialog(null, notifyMessage.toString(),
|
||||
NbBundle.getMessage(this.getClass(),
|
||||
"IngestManager.StartIngestJobsTask.run.startupErr.dlgTitle"), JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Failed to create ingest job", ex); //NON-NLS
|
||||
} finally {
|
||||
progress.finish();
|
||||
startIngestJobsTasks.remove(threadId);
|
||||
startIngestJobsCallables.remove(threadId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -758,4 +772,5 @@ public class IngestManager {
|
||||
return processedFilesCount;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -44,13 +44,13 @@ public final class RunIngestModulesDialog extends JDialog {
|
||||
|
||||
private static final String TITLE = NbBundle.getMessage(RunIngestModulesDialog.class, "IngestDialog.title.text");
|
||||
private static Dimension DIMENSIONS = new Dimension(500, 300);
|
||||
private List<Content> dataSources = new ArrayList<>();
|
||||
private IngestJobConfigurator ingestJobLauncher;
|
||||
private final List<Content> dataSources = new ArrayList<>();
|
||||
private IngestJobSettings ingestJobSettings;
|
||||
|
||||
public RunIngestModulesDialog(JFrame frame, String title, boolean modal) {
|
||||
super(frame, title, modal);
|
||||
ingestJobLauncher = new IngestJobConfigurator(RunIngestModulesDialog.class.getCanonicalName());
|
||||
List<String> messages = ingestJobLauncher.getIngestJobConfigWarnings();
|
||||
ingestJobSettings = new IngestJobSettings(RunIngestModulesDialog.class.getCanonicalName());
|
||||
List<String> messages = ingestJobSettings.getWarnings();
|
||||
if (messages.isEmpty() == false) {
|
||||
StringBuilder warning = new StringBuilder();
|
||||
for (String message : messages) {
|
||||
@ -79,28 +79,31 @@ public final class RunIngestModulesDialog extends JDialog {
|
||||
// set the location of the popUp Window on the center of the screen
|
||||
setLocation((screenDimension.width - w) / 2, (screenDimension.height - h) / 2);
|
||||
|
||||
add(ingestJobLauncher.getIngestJobConfigPanel(), BorderLayout.PAGE_START);
|
||||
add(new IngestJobSettingsPanel(ingestJobSettings), BorderLayout.PAGE_START);
|
||||
JButton startButton = new JButton(NbBundle.getMessage(this.getClass(), "IngestDialog.startButton.title"));
|
||||
JButton closeButton = new JButton(NbBundle.getMessage(this.getClass(), "IngestDialog.closeButton.title"));
|
||||
startButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
ingestJobLauncher.saveIngestJobConfig();
|
||||
ingestJobLauncher.startIngestJobs(dataSources);
|
||||
ingestJobSettings.save();
|
||||
IngestManager ingestManager = IngestManager.getInstance();
|
||||
for (Content dataSource : dataSources) {
|
||||
ingestManager.startIngestJob(dataSource, ingestJobSettings, true);
|
||||
}
|
||||
close();
|
||||
}
|
||||
});
|
||||
closeButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
ingestJobLauncher.saveIngestJobConfig();
|
||||
ingestJobSettings.save();
|
||||
close();
|
||||
}
|
||||
});
|
||||
this.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
ingestJobLauncher.saveIngestJobConfig();
|
||||
ingestJobSettings.save();
|
||||
close();
|
||||
}
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user