diff --git a/Core/src/org/sleuthkit/autopsy/actions/TagAction.java b/Core/src/org/sleuthkit/autopsy/actions/TagAction.java index 8db091b54b..6b5615a812 100755 --- a/Core/src/org/sleuthkit/autopsy/actions/TagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/TagAction.java @@ -54,6 +54,6 @@ import org.sleuthkit.datamodel.BlackboardArtifact; * that the directory tree would refresh. But, we haven't had a chance to add * that so, we fire these events and the tree refreshes based on them. */ - IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent("TagAction", BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE)); + IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent("TagAction", BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE)); //NON-NLS } } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java index 463ef55096..a8393e42bf 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java @@ -107,7 +107,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { org.openide.awt.Mnemonics.setLocalizedText(jLabel4, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jLabel4.text")); // NOI18N - restartRequiredLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/warning16.png"))); // NOI18N + restartRequiredLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/warning16.png"))); // NOI18N NON-NLS org.openide.awt.Mnemonics.setLocalizedText(restartRequiredLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.restartRequiredLabel.text")); // NOI18N javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java index 717d1aad81..38f7662e0c 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java @@ -39,6 +39,7 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -62,7 +63,7 @@ public class EmailExtracted implements AutopsyVisitableItem { private static final String MAIL_FOLDER = NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.mailFolder.text"); private static final String MAIL_PATH_SEPARATOR = "/"; private SleuthkitCase skCase; - private EmailResults emailResults; + private final EmailResults emailResults; public EmailExtracted(SleuthkitCase skCase) { @@ -71,7 +72,7 @@ public class EmailExtracted implements AutopsyVisitableItem { } private final class EmailResults extends Observable { - private Map>> accounts = new LinkedHashMap<>(); + private final Map>> accounts = new LinkedHashMap<>(); EmailResults() { update(); @@ -91,6 +92,10 @@ public class EmailExtracted implements AutopsyVisitableItem { public void update() { accounts.clear(); + if (skCase == null) { + return; + } + try { int artId = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(); int pathAttrId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID(); @@ -291,6 +296,13 @@ public class EmailExtracted implements AutopsyVisitableItem { || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) { emailResults.update(); } + else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { + // case was closed. Remove listeners so that we don't get called with a stale case handle + if (evt.getNewValue() == null) { + removeNotify(); + skCase = null; + } + } } }; @@ -298,6 +310,7 @@ public class EmailExtracted implements AutopsyVisitableItem { protected void addNotify() { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); + Case.addPropertyChangeListener(pcl); emailResults.update(); emailResults.addObserver(this); } @@ -306,6 +319,7 @@ public class EmailExtracted implements AutopsyVisitableItem { protected void removeNotify() { IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestModuleEventListener(pcl); + Case.removePropertyChangeListener(pcl); emailResults.deleteObserver(this); } @@ -484,6 +498,9 @@ public class EmailExtracted implements AutopsyVisitableItem { @Override protected Node createNodeForKey(Long artifactId) { + if (skCase == null) { + return null; + } try { BlackboardArtifact artifact = skCase.getBlackboardArtifact(artifactId); return new BlackboardArtifactNode(artifact); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java index 0d53d65db8..5486a952dc 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java @@ -32,6 +32,7 @@ import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; @@ -67,7 +68,7 @@ import org.sleuthkit.datamodel.TskException; */ public class ExtractedContent implements AutopsyVisitableItem { - private SleuthkitCase skCase; + private SleuthkitCase skCase; // set to null after case has been closed public static final String NAME = NbBundle.getMessage(RootNode.class, "ExtractedContentNode.name.text"); public ExtractedContent(SleuthkitCase skCase) { @@ -86,7 +87,7 @@ public class ExtractedContent implements AutopsyVisitableItem { public class RootNode extends DisplayableItemNode { public RootNode(SleuthkitCase skCase) { - super(Children.create(new TypeFactory(skCase), true), Lookups.singleton(NAME)); + super(Children.create(new TypeFactory(), true), Lookups.singleton(NAME)); super.setName(NAME); super.setDisplayName(NAME); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/extracted_content.png"); //NON-NLS @@ -125,15 +126,12 @@ public class ExtractedContent implements AutopsyVisitableItem { * more specific form elsewhere in the tree. */ private class TypeFactory extends ChildFactory.Detachable { - - private SleuthkitCase skCase; private final ArrayList doNotShow; // maps the artifact type to its child node - private HashMap typeNodeList = new HashMap<>(); + private final HashMap typeNodeList = new HashMap<>(); - public TypeFactory(SleuthkitCase skCase) { + public TypeFactory() { super(); - this.skCase = skCase; // these are shown in other parts of the UI tree doNotShow = new ArrayList<>(); @@ -161,6 +159,13 @@ public class ExtractedContent implements AutopsyVisitableItem { || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) { refresh(true); } + else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { + // case was closed. Remove listeners so that we don't get called with a stale case handle + if (evt.getNewValue() == null) { + removeNotify(); + skCase = null; + } + } } }; @@ -168,17 +173,23 @@ public class ExtractedContent implements AutopsyVisitableItem { protected void addNotify() { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); + Case.addPropertyChangeListener(pcl); } @Override protected void removeNotify() { IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestModuleEventListener(pcl); + Case.removePropertyChangeListener(pcl); typeNodeList.clear(); } @Override protected boolean createKeys(List list) { + if (skCase == null) { + return false; + } + try { List inUse = skCase.getBlackboardArtifactTypesInUse(); inUse.removeAll(doNotShow); @@ -208,7 +219,7 @@ public class ExtractedContent implements AutopsyVisitableItem { @Override protected Node createNodeForKey(BlackboardArtifact.ARTIFACT_TYPE key) { - TypeNode node = new TypeNode(key, skCase); + TypeNode node = new TypeNode(key); typeNodeList.put(key, node); return node; } @@ -224,18 +235,20 @@ public class ExtractedContent implements AutopsyVisitableItem { private BlackboardArtifact.ARTIFACT_TYPE type; private long childCount = 0; - private SleuthkitCase skCase; - TypeNode(BlackboardArtifact.ARTIFACT_TYPE type, SleuthkitCase skCase) { - super(Children.create(new ArtifactFactory(type, skCase), true), Lookups.singleton(type.getDisplayName())); + TypeNode(BlackboardArtifact.ARTIFACT_TYPE type) { + super(Children.create(new ArtifactFactory(type), true), Lookups.singleton(type.getDisplayName())); super.setName(type.getLabel()); - this.skCase = skCase; this.type = type; this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/" + getIcon(type)); //NON-NLS updateDisplayName(); } final void updateDisplayName() { + if (skCase == null) { + return; + } + // NOTE: This completely destroys our lazy-loading ideal // a performance increase might be had by adding a // "getBlackboardArtifactCount()" method to skCase @@ -333,14 +346,12 @@ public class ExtractedContent implements AutopsyVisitableItem { /** * Creates children for a given artifact type */ - private static class ArtifactFactory extends ChildFactory.Detachable { + private class ArtifactFactory extends ChildFactory.Detachable { - private SleuthkitCase skCase; private BlackboardArtifact.ARTIFACT_TYPE type; - public ArtifactFactory(BlackboardArtifact.ARTIFACT_TYPE type, SleuthkitCase skCase) { + public ArtifactFactory(BlackboardArtifact.ARTIFACT_TYPE type) { super(); - this.skCase = skCase; this.type = type; } @@ -375,6 +386,10 @@ public class ExtractedContent implements AutopsyVisitableItem { @Override protected boolean createKeys(List list) { + if (skCase == null) { + return false; + } + try { List arts = skCase.getBlackboardArtifacts(type.getTypeID()); list.addAll(arts); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java index 142d6a3f42..067e9562b0 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java @@ -40,6 +40,7 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -56,7 +57,7 @@ public class HashsetHits implements AutopsyVisitableItem { private static final String HASHSET_HITS = BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getLabel(); private static final String DISPLAY_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getDisplayName(); private static final Logger logger = Logger.getLogger(HashsetHits.class.getName()); - private final SleuthkitCase skCase; + private SleuthkitCase skCase; private final HashsetResults hashsetResults; public HashsetHits(SleuthkitCase skCase) { @@ -76,7 +77,7 @@ public class HashsetHits implements AutopsyVisitableItem { */ private class HashsetResults extends Observable { // maps hashset name to list of artifacts for that set - private Map> hashSetHitsMap = new LinkedHashMap<>(); + private final Map> hashSetHitsMap = new LinkedHashMap<>(); HashsetResults() { update(); @@ -94,6 +95,11 @@ public class HashsetHits implements AutopsyVisitableItem { final void update() { hashSetHitsMap.clear(); + + if (skCase == null) { + return; + } + ResultSet rs = null; try { int setNameId = ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(); @@ -191,6 +197,13 @@ public class HashsetHits implements AutopsyVisitableItem { || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) { hashsetResults.update(); } + else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { + // case was closed. Remove listeners so that we don't get called with a stale case handle + if (evt.getNewValue() == null) { + removeNotify(); + skCase = null; + } + } } }; @@ -198,6 +211,7 @@ public class HashsetHits implements AutopsyVisitableItem { protected void addNotify() { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); + Case.addPropertyChangeListener(pcl); hashsetResults.update(); hashsetResults.addObserver(this); } @@ -206,6 +220,7 @@ public class HashsetHits implements AutopsyVisitableItem { protected void removeNotify() { IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestModuleEventListener(pcl); + Case.removePropertyChangeListener(pcl); hashsetResults.deleteObserver(this); } @@ -309,6 +324,10 @@ public class HashsetHits implements AutopsyVisitableItem { @Override protected Node createNodeForKey(Long id) { + if (skCase == null) { + return null; + } + try { BlackboardArtifact art = skCase.getBlackboardArtifact(id); return new BlackboardArtifactNode(art); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java index 6da877d2bd..6338698dc2 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java @@ -42,6 +42,7 @@ import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.Exceptions; import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -57,7 +58,7 @@ public class InterestingHits implements AutopsyVisitableItem { private static final String DISPLAY_NAME = NbBundle.getMessage(InterestingHits.class, "InterestingHits.displayName.text"); private static final Logger logger = Logger.getLogger(InterestingHits.class.getName()); private SleuthkitCase skCase; - private InterestingResults interestingResults = new InterestingResults(); + private final InterestingResults interestingResults = new InterestingResults(); public InterestingHits(SleuthkitCase skCase) { this.skCase = skCase; @@ -65,7 +66,7 @@ public class InterestingHits implements AutopsyVisitableItem { } private class InterestingResults extends Observable { - private Map> interestingItemsMap = new LinkedHashMap<>(); + private final Map> interestingItemsMap = new LinkedHashMap<>(); public List getSetNames() { List setNames = new ArrayList<>(interestingItemsMap.keySet()); @@ -89,6 +90,10 @@ public class InterestingHits implements AutopsyVisitableItem { * Reads the artifacts of specified type, grouped by Set, and loads into the interestingItemsMap */ private void loadArtifacts(BlackboardArtifact.ARTIFACT_TYPE artType) { + if (skCase == null) { + return; + } + ResultSet rs = null; try { int setNameId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(); @@ -188,6 +193,13 @@ public class InterestingHits implements AutopsyVisitableItem { || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) { interestingResults.update(); } + else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { + // case was closed. Remove listeners so that we don't get called with a stale case handle + if (evt.getNewValue() == null) { + removeNotify(); + skCase = null; + } + } } }; @@ -195,6 +207,7 @@ public class InterestingHits implements AutopsyVisitableItem { protected void addNotify() { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); + Case.addPropertyChangeListener(pcl); interestingResults.update(); interestingResults.addObserver(this); } @@ -203,6 +216,7 @@ public class InterestingHits implements AutopsyVisitableItem { protected void removeNotify() { IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestModuleEventListener(pcl); + Case.removePropertyChangeListener(pcl); interestingResults.deleteObserver(this); } @@ -290,6 +304,9 @@ public class InterestingHits implements AutopsyVisitableItem { @Override protected Node createNodeForKey(Long l) { + if (skCase == null) { + return null; + } try { return new BlackboardArtifactNode(skCase.getBlackboardArtifact(l)); } catch (TskCoreException ex) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java index 53c43e0490..d2dcadc733 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java @@ -39,6 +39,7 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -61,7 +62,7 @@ public class KeywordHits implements AutopsyVisitableItem { .getMessage(KeywordHits.class, "KeywordHits.simpleLiteralSearch.text"); public static final String SIMPLE_REGEX_SEARCH = NbBundle .getMessage(KeywordHits.class, "KeywordHits.singleRegexSearch.text"); - private KeywordResults keywordResults; + private final KeywordResults keywordResults; public KeywordHits(SleuthkitCase skCase) { this.skCase = skCase; @@ -70,7 +71,7 @@ public class KeywordHits implements AutopsyVisitableItem { private final class KeywordResults extends Observable { // Map from listName/Type to Map of keyword to set of artifact Ids - private Map>> topLevelMap; + private final Map>> topLevelMap; KeywordResults() { topLevelMap = new LinkedHashMap<>(); @@ -146,6 +147,10 @@ public class KeywordHits implements AutopsyVisitableItem { public void update() { Map> artifactIds = new LinkedHashMap<>(); + if (skCase == null) { + return; + } + ResultSet rs = null; try { int setId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(); @@ -245,6 +250,13 @@ public class KeywordHits implements AutopsyVisitableItem { || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) { keywordResults.update(); } + else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { + // case was closed. Remove listeners so that we don't get called with a stale case handle + if (evt.getNewValue() == null) { + removeNotify(); + skCase = null; + } + } } }; @@ -252,6 +264,7 @@ public class KeywordHits implements AutopsyVisitableItem { protected void addNotify() { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); + Case.addPropertyChangeListener(pcl); keywordResults.update(); keywordResults.addObserver(this); } @@ -260,6 +273,7 @@ public class KeywordHits implements AutopsyVisitableItem { protected void removeNotify() { IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestModuleEventListener(pcl); + Case.removePropertyChangeListener(pcl); keywordResults.deleteObserver(this); } @@ -462,12 +476,16 @@ public class KeywordHits implements AutopsyVisitableItem { @Override protected Node createNodeForKey(Long artifactId) { + if (skCase == null) { + return null; + } + try { BlackboardArtifact art = skCase.getBlackboardArtifact(artifactId); BlackboardArtifactNode n = new BlackboardArtifactNode(art); AbstractFile file; try { - file = art.getSleuthkitCase().getAbstractFileById(art.getObjectID()); + file = skCase.getAbstractFileById(art.getObjectID()); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren"); //NON-NLS return n; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java b/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java index 0bb54f9c4d..0d6f972475 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java @@ -52,7 +52,7 @@ public class Tags implements AutopsyVisitableItem { // by a CreateAutopsyNodeVisitor dispatched from the AbstractContentChildren // override of Children.Keys.createNodes(). - private TagResults tagResults = new TagResults(); + private final TagResults tagResults = new TagResults(); private final String DISPLAY_NAME = NbBundle.getMessage(RootNode.class, "TagsNode.displayName.text"); private final String ICON_PATH = "org/sleuthkit/autopsy/images/tag-folder-blue-icon-16.png"; //NON-NLS @@ -130,6 +130,12 @@ public class Tags implements AutopsyVisitableItem { refresh(true); tagResults.update(); } + else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { + // case was closed. Remove listeners so that this can be garbage collected + if (evt.getNewValue() == null) { + removeNotify(); + } + } } }; @@ -137,6 +143,7 @@ public class Tags implements AutopsyVisitableItem { protected void addNotify() { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); + Case.addPropertyChangeListener(pcl); tagResults.update(); tagResults.addObserver(this); } @@ -145,6 +152,7 @@ public class Tags implements AutopsyVisitableItem { protected void removeNotify() { IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestModuleEventListener(pcl); + Case.removePropertyChangeListener(pcl); tagResults.deleteObserver(this); } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestJob.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestJob.java index ae2bd50bc1..616059a28d 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestJob.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestJob.java @@ -46,8 +46,8 @@ final class IngestJob { private long estimatedFilesToProcess = 0L; // Guarded by this private long processedFiles = 0L; // Guarded by this private DataSourceIngestPipeline dataSourceIngestPipeline; - private ProgressHandle dataSourceTasksProgress; - private ProgressHandle fileTasksProgress; + private ProgressHandle dataSourceIngestProgress; + private ProgressHandle fileIngestProgress; private volatile boolean cancelled = false; /** @@ -140,11 +140,11 @@ final class IngestJob { final String displayName = NbBundle.getMessage(this.getClass(), "IngestJob.progress.dataSourceIngest.initialDisplayName", dataSource.getName()); - dataSourceTasksProgress = ProgressHandleFactory.createHandle(displayName, new Cancellable() { + dataSourceIngestProgress = ProgressHandleFactory.createHandle(displayName, new Cancellable() { @Override public boolean cancel() { - if (dataSourceTasksProgress != null) { - dataSourceTasksProgress.setDisplayName( + if (dataSourceIngestProgress != null) { + dataSourceIngestProgress.setDisplayName( NbBundle.getMessage(this.getClass(), "IngestJob.progress.cancelling", displayName)); @@ -153,19 +153,19 @@ final class IngestJob { return true; } }); - dataSourceTasksProgress.start(); - dataSourceTasksProgress.switchToIndeterminate(); + dataSourceIngestProgress.start(); + dataSourceIngestProgress.switchToIndeterminate(); } private void startFileIngestProgressBar() { final String displayName = NbBundle.getMessage(this.getClass(), "IngestJob.progress.fileIngest.displayName", dataSource.getName()); - fileTasksProgress = ProgressHandleFactory.createHandle(displayName, new Cancellable() { + fileIngestProgress = ProgressHandleFactory.createHandle(displayName, new Cancellable() { @Override public boolean cancel() { - if (fileTasksProgress != null) { - fileTasksProgress.setDisplayName( + if (fileIngestProgress != null) { + fileIngestProgress.setDisplayName( NbBundle.getMessage(this.getClass(), "IngestJob.progress.cancelling", displayName)); } @@ -174,29 +174,28 @@ final class IngestJob { } }); estimatedFilesToProcess = dataSource.accept(new GetFilesCountVisitor()); - fileTasksProgress.start(); - fileTasksProgress.switchToDeterminate((int) estimatedFilesToProcess); + fileIngestProgress.start(); + fileIngestProgress.switchToDeterminate((int) estimatedFilesToProcess); } void process(DataSourceIngestTask task) throws InterruptedException { if (!isCancelled()) { List errors = new ArrayList<>(); - errors.addAll(dataSourceIngestPipeline.process(task.getDataSource(), dataSourceTasksProgress)); + errors.addAll(dataSourceIngestPipeline.process(task.getDataSource(), dataSourceIngestProgress)); if (!errors.isEmpty()) { logIngestModuleErrors(errors); } } else { taskScheduler.removeTasksForIngestJob(id); } + taskScheduler.notifyDataSourceIngestTaskCompleted(task); - // Because there is only one data source task per job, it is o.k. to - // call ProgressHandle.finish() now that the data source ingest modules - // are through using the progress bar via the DataSourceIngestModuleProgress wrapper. - // Calling ProgressHandle.finish() again in finish() will be harmless. - dataSourceTasksProgress.finish(); - - if (taskScheduler.isLastTaskForIngestJob(task)) { - finish(); + if (!taskScheduler.hasDataSourceIngestTaskForIngestJob(this)) { + finishProgressBar(dataSourceIngestProgress); + if (!taskScheduler.hasFileIngestTaskForIngestJob(this)) { + finishProgressBar(fileIngestProgress); + finish(); + } } } @@ -206,9 +205,9 @@ final class IngestJob { synchronized (this) { ++processedFiles; if (processedFiles <= estimatedFilesToProcess) { - fileTasksProgress.progress(file.getName(), (int) processedFiles); + fileIngestProgress.progress(file.getName(), (int) processedFiles); } else { - fileTasksProgress.progress(file.getName(), (int) estimatedFilesToProcess); + fileIngestProgress.progress(file.getName(), (int) estimatedFilesToProcess); } } FileIngestPipeline pipeline = fileIngestPipelines.take(); @@ -221,9 +220,21 @@ final class IngestJob { } else { taskScheduler.removeTasksForIngestJob(id); } + taskScheduler.notifyFileIngestTaskCompleted(task); - if (taskScheduler.isLastTaskForIngestJob(task)) { - finish(); + if (!taskScheduler.hasFileIngestTaskForIngestJob(this)) { + finishProgressBar(fileIngestProgress); + if (!taskScheduler.hasDataSourceIngestTaskForIngestJob(this)) { + finishProgressBar(dataSourceIngestProgress); + finish(); + } + } + } + + private synchronized void finishProgressBar(ProgressHandle progress) { + if (progress != null) { + progress.finish(); + progress = null; } } @@ -236,8 +247,6 @@ final class IngestJob { if (!errors.isEmpty()) { logIngestModuleErrors(errors); } - dataSourceTasksProgress.finish(); - fileTasksProgress.finish(); ingestJobsById.remove(id); if (!isCancelled()) { IngestManager.getInstance().fireIngestJobCompleted(id); @@ -256,6 +265,8 @@ final class IngestJob { private void cancel() { cancelled = true; + finishProgressBar(dataSourceIngestProgress); + finishProgressBar(fileIngestProgress); IngestManager.getInstance().fireIngestJobCancelled(id); } } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestScheduler.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestScheduler.java index 9c003895d4..2225efc617 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestScheduler.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestScheduler.java @@ -45,7 +45,8 @@ final class IngestScheduler { private final TreeSet rootDirectoryTasks = new TreeSet<>(new RootDirectoryTaskComparator()); // Guarded by this private final List directoryTasks = new ArrayList<>(); // Guarded by this private final LinkedBlockingQueue fileTasks = new LinkedBlockingQueue<>(); // Guarded by this - private final List tasksInProgress = new ArrayList<>(); // Guarded by this + private final List dataSourceTasksInProgress = new ArrayList<>(); // Guarded by this + private final List fileTasksInProgress = new ArrayList<>(); // Guarded by this private final DataSourceIngestTaskQueue dataSourceTaskDispenser = new DataSourceIngestTaskQueue(); private final FileIngestTaskQueue fileTaskDispenser = new FileIngestTaskQueue(); @@ -105,7 +106,7 @@ final class IngestScheduler { } } - updateFileTaskQueues(null); + updateFileTaskQueues(); } void addFileTaskToIngestJob(IngestJob job, AbstractFile file) { @@ -120,7 +121,7 @@ final class IngestScheduler { } } - synchronized void removeTasksForIngestJob(long ingestJobId) { + synchronized void removeTasksForIngestJob(long ingestJobId) { // Remove all tasks for this ingest job that are not in progress. Iterator fileTasksIterator = fileTasks.iterator(); while (fileTasksIterator.hasNext()) { @@ -148,11 +149,7 @@ final class IngestScheduler { } } - private synchronized void updateFileTaskQueues(FileIngestTask taskInProgress) throws InterruptedException { - if (taskInProgress != null) { - tasksInProgress.add(taskInProgress); - } - + private synchronized void updateFileTaskQueues() throws InterruptedException { // we loop because we could have a directory that has all files // that do not get enqueued while (true) { @@ -262,36 +259,52 @@ final class IngestScheduler { return fileTaskDispenser; } - synchronized boolean isLastTaskForIngestJob(IngestTask completedTask) { - tasksInProgress.remove(completedTask); - IngestJob job = completedTask.getIngestJob(); + synchronized void notifyDataSourceIngestTaskCompleted(DataSourceIngestTask task) { + dataSourceTasksInProgress.remove(task); + } + + synchronized void notifyFileIngestTaskCompleted(FileIngestTask task) { + fileTasksInProgress.remove(task); + } + + synchronized boolean hasDataSourceIngestTaskForIngestJob(IngestJob job) { long jobId = job.getId(); - for (IngestTask task : tasksInProgress) { + for (IngestTask task : dataSourceTasksInProgress) { if (task.getIngestJob().getId() == jobId) { - return false; - } - } - for (FileIngestTask task : fileTasks) { - if (task.getIngestJob().getId() == jobId) { - return false; - } - } - for (FileIngestTask task : directoryTasks) { - if (task.getIngestJob().getId() == jobId) { - return false; - } - } - for (FileIngestTask task : rootDirectoryTasks) { - if (task.getIngestJob().getId() == jobId) { - return false; + return true; } } for (DataSourceIngestTask task : dataSourceTasks) { if (task.getIngestJob().getId() == jobId) { - return false; + return true; } } - return true; + return false; + } + + synchronized boolean hasFileIngestTaskForIngestJob(IngestJob job) { + long jobId = job.getId(); + for (IngestTask task : fileTasksInProgress) { + if (task.getIngestJob().getId() == jobId) { + return true; + } + } + for (FileIngestTask task : fileTasks) { + if (task.getIngestJob().getId() == jobId) { + return true; + } + } + for (FileIngestTask task : directoryTasks) { + if (task.getIngestJob().getId() == jobId) { + return true; + } + } + for (FileIngestTask task : rootDirectoryTasks) { + if (task.getIngestJob().getId() == jobId) { + return true; + } + } + return false; } private static class RootDirectoryTaskComparator implements Comparator { @@ -394,7 +407,11 @@ final class IngestScheduler { @Override public IngestTask getNextTask() throws InterruptedException { - return dataSourceTasks.take(); + DataSourceIngestTask task = dataSourceTasks.take(); + synchronized (this) { + dataSourceTasksInProgress.add(task); + } + return task; } } @@ -403,7 +420,10 @@ final class IngestScheduler { @Override public IngestTask getNextTask() throws InterruptedException { FileIngestTask task = fileTasks.take(); - updateFileTaskQueues(task); + synchronized (this) { + fileTasksInProgress.add(task); + } + updateFileTaskQueues(); return task; } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java index 3c747b9ce0..2abb0d91e9 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java @@ -383,7 +383,7 @@ public final class SearchRunner { public boolean cancel() { logger.log(Level.INFO, "Cancelling the searcher by user."); //NON-NLS if (progressGroup != null) { - progressGroup.setDisplayName(displayName + " (" + NbBundle.getMessage(this.getClass(), "SearchRunner.doInBackGround.cancelMsg") + "...)"); + progressGroup.setDisplayName(displayName + " " + NbBundle.getMessage(this.getClass(), "SearchRunner.doInBackGround.cancelMsg")); } return SearchRunner.Searcher.this.cancel(true); }