diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index afcf23ed57..55c0db7724 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -85,6 +85,7 @@ import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferencesException; import org.sleuthkit.autopsy.corecomponentinterfaces.CoreComponentControl; import org.sleuthkit.autopsy.coreutils.DriveUtils; +import org.sleuthkit.autopsy.coreutils.ExecUtil; import org.sleuthkit.autopsy.coreutils.FileUtil; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @@ -1645,7 +1646,7 @@ public class Case { } else { future.cancel(true); } - Case.shutDownTaskExecutor(caseLockingExecutor); + ExecUtil.shutDownTaskExecutor(caseLockingExecutor); } catch (CancellationException discarded) { /* * The create/open task has been cancelled. Wait for it to finish, @@ -1654,7 +1655,7 @@ public class Case { * will have been closed and the case directory lock released will * have been released. */ - Case.shutDownTaskExecutor(caseLockingExecutor); + ExecUtil.shutDownTaskExecutor(caseLockingExecutor); throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser()); } catch (ExecutionException ex) { /* @@ -1664,7 +1665,7 @@ public class Case { * case will have been closed and the case directory lock released * will have been released. */ - Case.shutDownTaskExecutor(caseLockingExecutor); + ExecUtil.shutDownTaskExecutor(caseLockingExecutor); throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getLocalizedMessage()), ex); } finally { progressIndicator.finish(); @@ -1992,7 +1993,7 @@ public class Case { * would be possible to start the next task before the current * task responded to a cancellation request. */ - shutDownTaskExecutor(executor); + ExecUtil.shutDownTaskExecutor(executor); progressIndicator.finish(); } @@ -2063,7 +2064,7 @@ public class Case { } catch (ExecutionException ex) { throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getMessage()), ex); } finally { - shutDownTaskExecutor(caseLockingExecutor); + ExecUtil.shutDownTaskExecutor(caseLockingExecutor); progressIndicator.finish(); } } @@ -2174,7 +2175,7 @@ public class Case { Bundle.Case_servicesException_serviceResourcesCloseError(service.getServiceName(), ex.getLocalizedMessage()))); } } finally { - shutDownTaskExecutor(executor); + ExecUtil.shutDownTaskExecutor(executor); progressIndicator.finish(); } } @@ -2231,41 +2232,6 @@ public class Case { } - /** - * Shuts down a task executor service, waiting until all tasks are - * terminated. The current policy is to wait for the tasks to finish so that - * the case for which the executor is running can be left in a consistent - * state. - * - * @param executor The executor. - */ - private static void shutDownTaskExecutor(ExecutorService executor) { - executor.shutdown(); - boolean taskCompleted = false; - while (!taskCompleted) { - try { - taskCompleted = executor.awaitTermination(EXECUTOR_AWAIT_TIMEOUT_SECS, TimeUnit.SECONDS); - } catch (InterruptedException ignored) { - /* - * The current policy is to wait for the task to finish so that - * the case can be left in a consistent state. - * - * For a specific example of the motivation for this policy, - * note that a application service (Solr search service) - * experienced an error condition when opening case resources - * that left the service blocked uninterruptibly on a socket - * read. This eventually led to a mysterious "freeze" as the - * user-cancelled service task continued to run holdiong a lock - * that a UI thread soon tried to acquire. Thus it has been - * deemed better to make the "freeze" happen in a more - * informative way, i.e., with the progress indicator for the - * unfinished task on the screen, if a similar error condition - * arises again. - */ - } - } - } - /** * A case operation Cancel button listener for use with a * ModalDialogProgressIndicator when running with a GUI. diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java index 7f4f97d08a..0f8c8512af 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java @@ -18,9 +18,12 @@ */ package org.sleuthkit.autopsy.centralrepository.eventlisteners; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.logging.Level; import java.util.stream.Collectors; import org.openide.util.NbBundle.Messages; @@ -39,6 +42,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.autopsy.centralrepository.datamodel.EamOrganization; +import org.sleuthkit.autopsy.coreutils.ExecUtil; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifactTag; @@ -53,9 +57,19 @@ import org.sleuthkit.datamodel.TskDataException; * accordingly */ @Messages({"caseeventlistener.evidencetag=Evidence"}) -public class CaseEventListener implements PropertyChangeListener { +final class CaseEventListener implements PropertyChangeListener { private static final Logger LOGGER = Logger.getLogger(CaseEventListener.class.getName()); + private final ExecutorService jobProcessingExecutor; + private static final String CASE_EVENT_THREAD_NAME = "Case-Event-Listener-%d"; + + CaseEventListener() { + jobProcessingExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(CASE_EVENT_THREAD_NAME).build()); + } + + void shutdown() { + ExecUtil.shutDownTaskExecutor(jobProcessingExecutor); + } @Override public void propertyChange(PropertyChangeEvent evt) { @@ -69,250 +83,24 @@ public class CaseEventListener implements PropertyChangeListener { switch (Case.Events.valueOf(evt.getPropertyName())) { case CONTENT_TAG_ADDED: case CONTENT_TAG_DELETED: { - if (!EamDb.isEnabled()) { - return; - } - - AbstractFile af; - TskData.FileKnown knownStatus; - String comment; - if (Case.Events.valueOf(evt.getPropertyName()) == Case.Events.CONTENT_TAG_ADDED) { - // For added tags, we want to change the known status to BAD if the - // tag that was just added is in the list of central repo tags. - final ContentTagAddedEvent tagAddedEvent = (ContentTagAddedEvent) evt; - final ContentTag tagAdded = tagAddedEvent.getAddedTag(); - - if (dbManager.getBadTags().contains(tagAdded.getName().getDisplayName())) { - if (tagAdded.getContent() instanceof AbstractFile) { - af = (AbstractFile) tagAdded.getContent(); - knownStatus = TskData.FileKnown.BAD; - comment = tagAdded.getComment(); - } else { - LOGGER.log(Level.WARNING, "Error updating non-file object"); - return; - } - } else { - // The added tag isn't flagged as bad in central repo, so do nothing - return; - } - } else { // CONTENT_TAG_DELETED - // For deleted tags, we want to set the file status to UNKNOWN if: - // - The tag that was just removed is notable in central repo - // - There are no remaining tags that are notable - final ContentTagDeletedEvent tagDeletedEvent = (ContentTagDeletedEvent) evt; - long contentID = tagDeletedEvent.getDeletedTagInfo().getContentID(); - - String tagName = tagDeletedEvent.getDeletedTagInfo().getName().getDisplayName(); - if (!dbManager.getBadTags().contains(tagName)) { - // If the tag that got removed isn't on the list of central repo tags, do nothing - return; - } - - try { - // Get the remaining tags on the content object - Content content = Case.getCurrentCase().getSleuthkitCase().getContentById(contentID); - TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); - List tags = tagsManager.getContentTagsByContent(content); - - if (tags.stream() - .map(tag -> tag.getName().getDisplayName()) - .filter(dbManager.getBadTags()::contains) - .collect(Collectors.toList()) - .isEmpty()) { - - // There are no more bad tags on the object - if (content instanceof AbstractFile) { - af = (AbstractFile) content; - knownStatus = TskData.FileKnown.UNKNOWN; - comment = ""; - } else { - LOGGER.log(Level.WARNING, "Error updating non-file object"); - return; - } - } else { - // There's still at least one bad tag, so leave the known status as is - return; - } - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Failed to find content", ex); - return; - } - } - - final CorrelationAttribute eamArtifact = EamArtifactUtil.getEamArtifactFromContent(af, - knownStatus, comment); - - if (eamArtifact != null) { - // send update to Central Repository db - Runnable r = new KnownStatusChangeRunner(eamArtifact, knownStatus); - // TODO: send r into a thread pool instead - Thread t = new Thread(r); - t.start(); - } - } // CONTENT_TAG_ADDED, CONTENT_TAG_DELETED + jobProcessingExecutor.submit(new ContentTagTask(dbManager, evt)); + } break; case BLACKBOARD_ARTIFACT_TAG_DELETED: case BLACKBOARD_ARTIFACT_TAG_ADDED: { - if (!EamDb.isEnabled()) { - return; - } - - Content content; - BlackboardArtifact bbArtifact; - TskData.FileKnown knownStatus; - String comment; - if (Case.Events.valueOf(evt.getPropertyName()) == Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED) { - // For added tags, we want to change the known status to BAD if the - // tag that was just added is in the list of central repo tags. - final BlackBoardArtifactTagAddedEvent tagAddedEvent = (BlackBoardArtifactTagAddedEvent) evt; - final BlackboardArtifactTag tagAdded = tagAddedEvent.getAddedTag(); - - if (dbManager.getBadTags().contains(tagAdded.getName().getDisplayName())) { - content = tagAdded.getContent(); - bbArtifact = tagAdded.getArtifact(); - knownStatus = TskData.FileKnown.BAD; - comment = tagAdded.getComment(); - } else { - // The added tag isn't flagged as bad in central repo, so do nothing - return; - } - } else { //BLACKBOARD_ARTIFACT_TAG_DELETED - // For deleted tags, we want to set the file status to UNKNOWN if: - // - The tag that was just removed is notable in central repo - // - There are no remaining tags that are notable - final BlackBoardArtifactTagDeletedEvent tagDeletedEvent = (BlackBoardArtifactTagDeletedEvent) evt; - long contentID = tagDeletedEvent.getDeletedTagInfo().getContentID(); - long artifactID = tagDeletedEvent.getDeletedTagInfo().getArtifactID(); - - String tagName = tagDeletedEvent.getDeletedTagInfo().getName().getDisplayName(); - if (!dbManager.getBadTags().contains(tagName)) { - // If the tag that got removed isn't on the list of central repo tags, do nothing - return; - } - - try { - // Get the remaining tags on the artifact - content = Case.getCurrentCase().getSleuthkitCase().getContentById(contentID); - bbArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(artifactID); - TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); - List tags = tagsManager.getBlackboardArtifactTagsByArtifact(bbArtifact); - - if (tags.stream() - .map(tag -> tag.getName().getDisplayName()) - .filter(dbManager.getBadTags()::contains) - .collect(Collectors.toList()) - .isEmpty()) { - - // There are no more bad tags on the object - knownStatus = TskData.FileKnown.UNKNOWN; - comment = ""; - - } else { - // There's still at least one bad tag, so leave the known status as is - return; - } - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Failed to find content", ex); - return; - } - } - - if ((content instanceof AbstractFile) && (((AbstractFile) content).getKnown() == TskData.FileKnown.KNOWN)) { - return; - } - - List convertedArtifacts = EamArtifactUtil.getCorrelationAttributeFromBlackboardArtifact(bbArtifact, true, true); - for (CorrelationAttribute eamArtifact : convertedArtifacts) { - eamArtifact.getInstances().get(0).setComment(comment); - Runnable r = new KnownStatusChangeRunner(eamArtifact, knownStatus); - // TODO: send r into a thread pool instead - Thread t = new Thread(r); - t.start(); - } - - } // BLACKBOARD_ARTIFACT_TAG_ADDED, BLACKBOARD_ARTIFACT_TAG_DELETED + jobProcessingExecutor.submit(new BlackboardTagTask(dbManager, evt)); + } break; case DATA_SOURCE_ADDED: { - if (!EamDb.isEnabled()) { - break; - } - - final DataSourceAddedEvent dataSourceAddedEvent = (DataSourceAddedEvent) evt; - Content newDataSource = dataSourceAddedEvent.getDataSource(); - - try { - String deviceId = Case.getCurrentCase().getSleuthkitCase().getDataSource(newDataSource.getId()).getDeviceId(); - CorrelationCase correlationCase = dbManager.getCaseByUUID(Case.getCurrentCase().getName()); - if (null == correlationCase) { - dbManager.newCase(Case.getCurrentCase()); - correlationCase = dbManager.getCaseByUUID(Case.getCurrentCase().getName()); - } - if (null == dbManager.getDataSourceDetails(correlationCase, deviceId)) { - dbManager.newDataSource(CorrelationDataSource.fromTSKDataSource(correlationCase, newDataSource)); - } - } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database.", ex); //NON-NLS - } catch (TskCoreException | TskDataException ex) { - LOGGER.log(Level.SEVERE, "Error getting data source from DATA_SOURCE_ADDED event content.", ex); //NON-NLS - } - } // DATA_SOURCE_ADDED + jobProcessingExecutor.submit(new DataSourceAddedTask(dbManager, evt)); + } break; case CURRENT_CASE: { - /* - * A case has been opened if evt.getOldValue() is null and - * evt.getNewValue() is a valid Case. - */ - if ((null == evt.getOldValue()) && (evt.getNewValue() instanceof Case)) { - Case curCase = (Case) evt.getNewValue(); - IngestEventsListener.resetCeModuleInstanceCount(); - try { - // only add default evidence tag if case is open and it doesn't already exist in the tags list. - if (Case.isCaseOpen() - && Case.getCurrentCase().getServices().getTagsManager().getAllTagNames().stream() - .map(tag -> tag.getDisplayName()) - .filter(tagName -> Bundle.caseeventlistener_evidencetag().equals(tagName)) - .collect(Collectors.toList()) - .isEmpty()) { - curCase.getServices().getTagsManager().addTagName(Bundle.caseeventlistener_evidencetag()); - } - } catch (TagsManager.TagNameAlreadyExistsException ex) { - LOGGER.info("Evidence tag already exists"); // NON-NLS - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error adding tag.", ex); // NON-NLS - } - - CorrelationCase curCeCase = new CorrelationCase( - -1, - curCase.getName(), // unique case ID - EamOrganization.getDefault(), - curCase.getDisplayName(), - curCase.getCreatedDate(), - curCase.getNumber(), - curCase.getExaminer(), - null, - null, - null); - - if (!EamDb.isEnabled()) { - break; - } - - try { - // NOTE: Cannot determine if the opened case is a new case or a reopened case, - // so check for existing name in DB and insert if missing. - CorrelationCase existingCase = dbManager.getCaseByUUID(curCeCase.getCaseUUID()); - - if (null == existingCase) { - dbManager.newCase(curCeCase); - } - } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database.", ex); //NON-NLS - } - } - } // CURRENT_CASE + jobProcessingExecutor.submit(new CurrentCaseTask(dbManager, evt)); + } break; case NAME: { @@ -340,4 +128,296 @@ public class CaseEventListener implements PropertyChangeListener { break; } } + + private final class ContentTagTask implements Runnable { + + private final EamDb dbManager; + private final PropertyChangeEvent event; + + private ContentTagTask(EamDb db, PropertyChangeEvent evt) { + dbManager = db; + event = evt; + } + + @Override + public void run() { + if (!EamDb.isEnabled()) { + return; + } + + AbstractFile af; + TskData.FileKnown knownStatus; + String comment; + if (Case.Events.valueOf(event.getPropertyName()) == Case.Events.CONTENT_TAG_ADDED) { + // For added tags, we want to change the known status to BAD if the + // tag that was just added is in the list of central repo tags. + final ContentTagAddedEvent tagAddedEvent = (ContentTagAddedEvent) event; + final ContentTag tagAdded = tagAddedEvent.getAddedTag(); + + if (dbManager.getBadTags().contains(tagAdded.getName().getDisplayName())) { + if (tagAdded.getContent() instanceof AbstractFile) { + af = (AbstractFile) tagAdded.getContent(); + knownStatus = TskData.FileKnown.BAD; + comment = tagAdded.getComment(); + } else { + LOGGER.log(Level.WARNING, "Error updating non-file object"); + return; + } + } else { + // The added tag isn't flagged as bad in central repo, so do nothing + return; + } + } else { // CONTENT_TAG_DELETED + // For deleted tags, we want to set the file status to UNKNOWN if: + // - The tag that was just removed is notable in central repo + // - There are no remaining tags that are notable + final ContentTagDeletedEvent tagDeletedEvent = (ContentTagDeletedEvent) event; + long contentID = tagDeletedEvent.getDeletedTagInfo().getContentID(); + + String tagName = tagDeletedEvent.getDeletedTagInfo().getName().getDisplayName(); + if (!dbManager.getBadTags().contains(tagName)) { + // If the tag that got removed isn't on the list of central repo tags, do nothing + return; + } + + try { + // Get the remaining tags on the content object + Content content = Case.getCurrentCase().getSleuthkitCase().getContentById(contentID); + TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); + List tags = tagsManager.getContentTagsByContent(content); + + if (tags.stream() + .map(tag -> tag.getName().getDisplayName()) + .filter(dbManager.getBadTags()::contains) + .collect(Collectors.toList()) + .isEmpty()) { + + // There are no more bad tags on the object + if (content instanceof AbstractFile) { + af = (AbstractFile) content; + knownStatus = TskData.FileKnown.UNKNOWN; + comment = ""; + } else { + LOGGER.log(Level.WARNING, "Error updating non-file object"); + return; + } + } else { + // There's still at least one bad tag, so leave the known status as is + return; + } + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Failed to find content", ex); + return; + } + } + + final CorrelationAttribute eamArtifact = EamArtifactUtil.getEamArtifactFromContent(af, + knownStatus, comment); + + if (eamArtifact != null) { + // send update to Central Repository db + try { + dbManager.setArtifactInstanceKnownStatus(eamArtifact, knownStatus); + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database while setting artifact known status.", ex); //NON-NLS + } + } + } // CONTENT_TAG_ADDED, CONTENT_TAG_DELETED + } + + private final class BlackboardTagTask implements Runnable { + + private final EamDb dbManager; + private final PropertyChangeEvent event; + + private BlackboardTagTask(EamDb db, PropertyChangeEvent evt) { + dbManager = db; + event = evt; + } + + @Override + public void run() { + if (!EamDb.isEnabled()) { + return; + } + + Content content; + BlackboardArtifact bbArtifact; + TskData.FileKnown knownStatus; + String comment; + if (Case.Events.valueOf(event.getPropertyName()) == Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED) { + // For added tags, we want to change the known status to BAD if the + // tag that was just added is in the list of central repo tags. + final BlackBoardArtifactTagAddedEvent tagAddedEvent = (BlackBoardArtifactTagAddedEvent) event; + final BlackboardArtifactTag tagAdded = tagAddedEvent.getAddedTag(); + + if (dbManager.getBadTags().contains(tagAdded.getName().getDisplayName())) { + content = tagAdded.getContent(); + bbArtifact = tagAdded.getArtifact(); + knownStatus = TskData.FileKnown.BAD; + comment = tagAdded.getComment(); + } else { + // The added tag isn't flagged as bad in central repo, so do nothing + return; + } + } else { //BLACKBOARD_ARTIFACT_TAG_DELETED + // For deleted tags, we want to set the file status to UNKNOWN if: + // - The tag that was just removed is notable in central repo + // - There are no remaining tags that are notable + final BlackBoardArtifactTagDeletedEvent tagDeletedEvent = (BlackBoardArtifactTagDeletedEvent) event; + long contentID = tagDeletedEvent.getDeletedTagInfo().getContentID(); + long artifactID = tagDeletedEvent.getDeletedTagInfo().getArtifactID(); + + String tagName = tagDeletedEvent.getDeletedTagInfo().getName().getDisplayName(); + if (!dbManager.getBadTags().contains(tagName)) { + // If the tag that got removed isn't on the list of central repo tags, do nothing + return; + } + + try { + // Get the remaining tags on the artifact + content = Case.getCurrentCase().getSleuthkitCase().getContentById(contentID); + bbArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(artifactID); + TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); + List tags = tagsManager.getBlackboardArtifactTagsByArtifact(bbArtifact); + + if (tags.stream() + .map(tag -> tag.getName().getDisplayName()) + .filter(dbManager.getBadTags()::contains) + .collect(Collectors.toList()) + .isEmpty()) { + + // There are no more bad tags on the object + knownStatus = TskData.FileKnown.UNKNOWN; + comment = ""; + + } else { + // There's still at least one bad tag, so leave the known status as is + return; + } + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Failed to find content", ex); + return; + } + } + + if ((content instanceof AbstractFile) && (((AbstractFile) content).getKnown() == TskData.FileKnown.KNOWN)) { + return; + } + + List convertedArtifacts = EamArtifactUtil.getCorrelationAttributeFromBlackboardArtifact(bbArtifact, true, true); + for (CorrelationAttribute eamArtifact : convertedArtifacts) { + eamArtifact.getInstances().get(0).setComment(comment); + try { + dbManager.setArtifactInstanceKnownStatus(eamArtifact, knownStatus); + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database while setting artifact known status.", ex); //NON-NLS + } + } + } // BLACKBOARD_ARTIFACT_TAG_ADDED, BLACKBOARD_ARTIFACT_TAG_DELETED + + } + + private final class DataSourceAddedTask implements Runnable { + + private final EamDb dbManager; + private final PropertyChangeEvent event; + + private DataSourceAddedTask(EamDb db, PropertyChangeEvent evt) { + dbManager = db; + event = evt; + } + + @Override + public void run() { + if (!EamDb.isEnabled()) { + return; + } + + final DataSourceAddedEvent dataSourceAddedEvent = (DataSourceAddedEvent) event; + Content newDataSource = dataSourceAddedEvent.getDataSource(); + + try { + String deviceId = Case.getCurrentCase().getSleuthkitCase().getDataSource(newDataSource.getId()).getDeviceId(); + CorrelationCase correlationCase = dbManager.getCaseByUUID(Case.getCurrentCase().getName()); + if (null == correlationCase) { + dbManager.newCase(Case.getCurrentCase()); + correlationCase = dbManager.getCaseByUUID(Case.getCurrentCase().getName()); + } + if (null == dbManager.getDataSourceDetails(correlationCase, deviceId)) { + dbManager.newDataSource(CorrelationDataSource.fromTSKDataSource(correlationCase, newDataSource)); + } + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database.", ex); //NON-NLS + } catch (TskCoreException | TskDataException ex) { + LOGGER.log(Level.SEVERE, "Error getting data source from DATA_SOURCE_ADDED event content.", ex); //NON-NLS + } + } // DATA_SOURCE_ADDED + } + + private final class CurrentCaseTask implements Runnable { + + private final EamDb dbManager; + private final PropertyChangeEvent event; + + private CurrentCaseTask(EamDb db, PropertyChangeEvent evt) { + dbManager = db; + event = evt; + } + + @Override + public void run() { + /* + * A case has been opened if evt.getOldValue() is null and + * evt.getNewValue() is a valid Case. + */ + if ((null == event.getOldValue()) && (event.getNewValue() instanceof Case)) { + Case curCase = (Case) event.getNewValue(); + IngestEventsListener.resetCeModuleInstanceCount(); + try { + // only add default evidence tag if case is open and it doesn't already exist in the tags list. + if (Case.isCaseOpen() + && Case.getCurrentCase().getServices().getTagsManager().getAllTagNames().stream() + .map(tag -> tag.getDisplayName()) + .filter(tagName -> Bundle.caseeventlistener_evidencetag().equals(tagName)) + .collect(Collectors.toList()) + .isEmpty()) { + curCase.getServices().getTagsManager().addTagName(Bundle.caseeventlistener_evidencetag()); + } + } catch (TagsManager.TagNameAlreadyExistsException ex) { + LOGGER.info("Evidence tag already exists"); // NON-NLS + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error adding tag.", ex); // NON-NLS + } + + CorrelationCase curCeCase = new CorrelationCase( + -1, + curCase.getName(), // unique case ID + EamOrganization.getDefault(), + curCase.getDisplayName(), + curCase.getCreatedDate(), + curCase.getNumber(), + curCase.getExaminer(), + null, + null, + null); + + if (!EamDb.isEnabled()) { + return; + } + + try { + // NOTE: Cannot determine if the opened case is a new case or a reopened case, + // so check for existing name in DB and insert if missing. + CorrelationCase existingCase = dbManager.getCaseByUUID(curCeCase.getCaseUUID()); + + if (null == existingCase) { + dbManager.newCase(curCeCase); + } + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database.", ex); //NON-NLS + } + } + } // CURRENT_CASE + } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/Installer.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/Installer.java index d2b1beebbb..29fdd331ca 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/Installer.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/Installer.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.centralrepository.eventlisteners; -import java.beans.PropertyChangeListener; import org.openide.modules.ModuleInstall; import org.openide.util.actions.CallableSystemAction; import org.sleuthkit.autopsy.casemodule.Case; @@ -32,7 +31,7 @@ public class Installer extends ModuleInstall { private static final Logger LOGGER = Logger.getLogger(Installer.class.getName()); private static final long serialVersionUID = 1L; - private final PropertyChangeListener pcl = new CaseEventListener(); + private final CaseEventListener pcl = new CaseEventListener(); private final IngestEventsListener ieListener = new IngestEventsListener(); private static Installer instance; @@ -71,6 +70,7 @@ public class Installer extends ModuleInstall { //module is being unloaded Case.removePropertyChangeListener(pcl); + pcl.shutdown(); ieListener.uninstallListeners(); // TODO: remove thread pool diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/KnownStatusChangeRunner.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/KnownStatusChangeRunner.java deleted file mode 100755 index 4761f77370..0000000000 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/KnownStatusChangeRunner.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Central Repository - * - * Copyright 2015-2017 Basis Technology Corp. - * Contact: carrier sleuthkit 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.centralrepository.eventlisteners; - -import java.util.logging.Level; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; -import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; -import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; -import org.sleuthkit.datamodel.TskData.FileKnown; - -/** - * Thread to send info to remote DB that tags a file as known, unknown, or notable. - */ -public class KnownStatusChangeRunner implements Runnable { - - private static final Logger LOGGER = Logger.getLogger(KnownStatusChangeRunner.class.getName()); - private static final long serialVersionUID = 1L; - - private final CorrelationAttribute artifact; - private final FileKnown knownStatus; - - public KnownStatusChangeRunner(CorrelationAttribute artifact, FileKnown knownStatus) { - this.artifact = artifact; - this.knownStatus = knownStatus; - } - - @Override - public void run() { - if (!EamDb.isEnabled()) { - LOGGER.log(Level.WARNING, "Central Repository database not configured"); // NON-NLS - return; - } - - try { - EamDb dbManager = EamDb.getInstance(); - dbManager.setArtifactInstanceKnownStatus(this.artifact, this.knownStatus); - } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database.", ex); //NON-NLS - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ExecUtil.java b/Core/src/org/sleuthkit/autopsy/coreutils/ExecUtil.java index 753ccd598a..51163e502c 100755 --- a/Core/src/org/sleuthkit/autopsy/coreutils/ExecUtil.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/ExecUtil.java @@ -26,6 +26,7 @@ import java.io.InputStreamReader; import java.io.Writer; import java.util.Date; import java.util.List; +import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import org.sleuthkit.autopsy.core.UserPreferences; @@ -212,6 +213,40 @@ public final class ExecUtil { } } + /** + * Shuts down a task executor service, waiting until all tasks are + * terminated. The current policy is to wait for the tasks to finish so that + * the case for which the executor is running can be left in a consistent + * state. + * + * @param executor The executor. + */ + public static void shutDownTaskExecutor(ExecutorService executor) { + executor.shutdown(); + boolean taskCompleted = false; + while (!taskCompleted) { + try { + taskCompleted = executor.awaitTermination(DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_UNITS); + } catch (InterruptedException ignored) { + /* + * The current policy is to wait for the task to finish so that + * the case can be left in a consistent state. + * + * For a specific example of the motivation for this policy, + * note that a application service (Solr search service) + * experienced an error condition when opening case resources + * that left the service blocked uninterruptibly on a socket + * read. This eventually led to a mysterious "freeze" as the + * user-cancelled service task continued to run holdiong a lock + * that a UI thread soon tried to acquire. Thus it has been + * deemed better to make the "freeze" happen in a more + * informative way, i.e., with the progress indicator for the + * unfinished task on the screen, if a similar error condition + * arises again. + */ + } + } + } /** * EVERYTHING FOLLOWING THIS LINE IS DEPRECATED AND SLATED FOR REMOVAL */