diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index 0e8ae6bc76..7f52ec6b25 100644 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -188,6 +188,7 @@ org.sleuthkit.autopsy.casemodule.services org.sleuthkit.autopsy.contentviewers org.sleuthkit.autopsy.core + org.sleuthkit.autopsy.core.events org.sleuthkit.autopsy.corecomponentinterfaces org.sleuthkit.autopsy.corecomponents org.sleuthkit.autopsy.coreutils diff --git a/Core/src/org/sleuthkit/autopsy/actions/AddBlackboardArtifactTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/AddBlackboardArtifactTagAction.java index 2568ba2936..91dc25193c 100755 --- a/Core/src/org/sleuthkit/autopsy/actions/AddBlackboardArtifactTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/AddBlackboardArtifactTagAction.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.actions; import java.util.Collection; import java.util.logging.Level; import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; import org.openide.util.NbBundle; import org.openide.util.Utilities; @@ -61,21 +62,26 @@ public class AddBlackboardArtifactTagAction extends AddTagAction { @Override protected void addTag(TagName tagName, String comment) { - Collection selectedArtifacts = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class); - for (BlackboardArtifact artifact : selectedArtifacts) { - try { - Case.getCurrentCase().getServices().getTagsManager().addBlackboardArtifactTag(artifact, tagName, comment); - } - catch (TskCoreException ex) { - Logger.getLogger(AddBlackboardArtifactTagAction.class.getName()).log(Level.SEVERE, "Error tagging result", ex); //NON-NLS - JOptionPane.showMessageDialog(null, - NbBundle.getMessage(this.getClass(), - "AddBlackboardArtifactTagAction.unableToTag.msg", - artifact.getDisplayName()), - NbBundle.getMessage(this.getClass(), - "AddBlackboardArtifactTagAction.taggingErr"), - JOptionPane.ERROR_MESSAGE); - } - } + final Collection selectedArtifacts = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class); + + new Thread(() -> { + for (BlackboardArtifact artifact : selectedArtifacts) { + try { + Case.getCurrentCase().getServices().getTagsManager().addBlackboardArtifactTag(artifact, tagName, comment); + } + catch (TskCoreException ex) { + Logger.getLogger(AddBlackboardArtifactTagAction.class.getName()).log(Level.SEVERE, "Error tagging result", ex); //NON-NLS + SwingUtilities.invokeLater(() -> { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(this.getClass(), + "AddBlackboardArtifactTagAction.unableToTag.msg", + artifact.getDisplayName()), + NbBundle.getMessage(this.getClass(), + "AddBlackboardArtifactTagAction.taggingErr"), + JOptionPane.ERROR_MESSAGE); + }); + } + } + }).start(); } } diff --git a/Core/src/org/sleuthkit/autopsy/actions/AddContentTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/AddContentTagAction.java index cff35af7c1..6f1b493ecf 100755 --- a/Core/src/org/sleuthkit/autopsy/actions/AddContentTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/AddContentTagAction.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.List; import java.util.logging.Level; import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; import org.openide.util.NbBundle; import org.openide.util.Utilities; @@ -64,81 +65,98 @@ public class AddContentTagAction extends AddTagAction { @Override protected void addTag(TagName tagName, String comment) { - Collection selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class); - for (AbstractFile file : selectedFiles) { - try { - // Handle the special cases of current (".") and parent ("..") directory entries. - if (file.getName().equals(".")) { - Content parentFile = file.getParent(); - if (parentFile instanceof AbstractFile) { - file = (AbstractFile)parentFile; - } - else { - JOptionPane.showMessageDialog(null, - NbBundle.getMessage(this.getClass(), - "AddContentTagAction.unableToTag.msg", - parentFile.getName()), - NbBundle.getMessage(this.getClass(), - "AddContentTagAction.cannotApplyTagErr"), - JOptionPane.WARNING_MESSAGE); - continue; - } - } - else if (file.getName().equals("..")) { - Content parentFile = file.getParent(); - if (parentFile instanceof AbstractFile) { - parentFile = (AbstractFile)((AbstractFile)parentFile).getParent(); + final Collection selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class); + + new Thread(() -> { + for (AbstractFile file : selectedFiles) { + try { + // Handle the special cases of current (".") and parent ("..") directory entries. + if (file.getName().equals(".")) { + Content parentFile = file.getParent(); if (parentFile instanceof AbstractFile) { file = (AbstractFile)parentFile; } else { - JOptionPane.showMessageDialog(null, - NbBundle.getMessage(this.getClass(), - "AddContentTagAction.unableToTag.msg", - parentFile.getName()), - NbBundle.getMessage(this.getClass(), - "AddContentTagAction.cannotApplyTagErr"), - JOptionPane.WARNING_MESSAGE); + SwingUtilities.invokeLater(() -> { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(this.getClass(), + "AddContentTagAction.unableToTag.msg", + parentFile.getName()), + NbBundle.getMessage(this.getClass(), + "AddContentTagAction.cannotApplyTagErr"), + JOptionPane.WARNING_MESSAGE); + }); continue; } } - else { - JOptionPane.showMessageDialog(null, - NbBundle.getMessage(this.getClass(), - "AddContentTagAction.unableToTag.msg", - parentFile.getName()), - NbBundle.getMessage(this.getClass(), - "AddContentTagAction.cannotApplyTagErr"), - JOptionPane.WARNING_MESSAGE); - continue; - } - } - // check if the same tag is being added for the same abstract file. - TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); - List contentTagList = tagsManager.getContentTagsByContent(file); - for (ContentTag contentTag : contentTagList) { - if (contentTag.getName().getDisplayName().equals(tagName.getDisplayName())) { - JOptionPane.showMessageDialog(null, - NbBundle.getMessage(this.getClass(), - "AddContentTagAction.tagExists", - file.getName(), tagName.getDisplayName()), - NbBundle.getMessage(this.getClass(), - "AddContentTagAction.cannotApplyTagErr"), - JOptionPane.WARNING_MESSAGE); - return; + else if (file.getName().equals("..")) { + Content parentFile = file.getParent(); + if (parentFile instanceof AbstractFile) { + parentFile = (AbstractFile)((AbstractFile)parentFile).getParent(); + if (parentFile instanceof AbstractFile) { + file = (AbstractFile)parentFile; + } + else { + final Content parentFileCopy = parentFile; + SwingUtilities.invokeLater(() -> { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(this.getClass(), + "AddContentTagAction.unableToTag.msg", + parentFileCopy.getName()), + NbBundle.getMessage(this.getClass(), + "AddContentTagAction.cannotApplyTagErr"), + JOptionPane.WARNING_MESSAGE); + }); + continue; + } + } + else { + final Content parentFileCopy = parentFile; + SwingUtilities.invokeLater(() -> { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(this.getClass(), + "AddContentTagAction.unableToTag.msg", + parentFileCopy.getName()), + NbBundle.getMessage(this.getClass(), + "AddContentTagAction.cannotApplyTagErr"), + JOptionPane.WARNING_MESSAGE); + }); + continue; + } } + // check if the same tag is being added for the same abstract file. + TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); + List contentTagList = tagsManager.getContentTagsByContent(file); + for (ContentTag contentTag : contentTagList) { + if (contentTag.getName().getDisplayName().equals(tagName.getDisplayName())) { + AbstractFile fileCopy = file; + SwingUtilities.invokeLater(() -> { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(this.getClass(), + "AddContentTagAction.tagExists", + fileCopy.getName(), tagName.getDisplayName()), + NbBundle.getMessage(this.getClass(), + "AddContentTagAction.cannotApplyTagErr"), + JOptionPane.WARNING_MESSAGE); + }); + return; + } + } + tagsManager.addContentTag(file, tagName, comment); } - tagsManager.addContentTag(file, tagName, comment); - } - catch (TskCoreException ex) { - Logger.getLogger(AddContentTagAction.class.getName()).log(Level.SEVERE, "Error tagging result", ex); //NON-NLS - JOptionPane.showMessageDialog(null, - NbBundle.getMessage(this.getClass(), - "AddContentTagAction.unableToTag.msg2", - file.getName()), - NbBundle.getMessage(this.getClass(), "AddContentTagAction.taggingErr"), - JOptionPane.ERROR_MESSAGE); - } - } - } + catch (TskCoreException ex) { + Logger.getLogger(AddContentTagAction.class.getName()).log(Level.SEVERE, "Error tagging result", ex); //NON-NLS + AbstractFile fileCopy = file; + SwingUtilities.invokeLater(() -> { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(this.getClass(), + "AddContentTagAction.unableToTag.msg2", + fileCopy.getName()), + NbBundle.getMessage(this.getClass(), "AddContentTagAction.taggingErr"), + JOptionPane.ERROR_MESSAGE); + }); + } + } + }).start(); + } } \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteBlackboardArtifactTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteBlackboardArtifactTagAction.java index ac9e29fe59..51482c0928 100755 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteBlackboardArtifactTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteBlackboardArtifactTagAction.java @@ -23,6 +23,7 @@ import java.util.Collection; import java.util.logging.Level; import javax.swing.AbstractAction; import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; import org.openide.util.NbBundle; import org.openide.util.Utilities; import org.sleuthkit.autopsy.casemodule.Case; @@ -55,22 +56,26 @@ public class DeleteBlackboardArtifactTagAction extends AbstractAction { @Override public void actionPerformed(ActionEvent event) { - Collection selectedTags = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifactTag.class); - for (BlackboardArtifactTag tag : selectedTags) { - try { - Case.getCurrentCase().getServices().getTagsManager().deleteBlackboardArtifactTag(tag); - } - catch (TskCoreException ex) { - Logger.getLogger(AddContentTagAction.class.getName()).log(Level.SEVERE, "Error deleting tag", ex); //NON-NLS - JOptionPane.showMessageDialog(null, - NbBundle.getMessage(this.getClass(), - "DeleteBlackboardArtifactTagAction.unableToDelTag.msg", - tag.getName()), - NbBundle.getMessage(this.getClass(), - "DeleteBlackboardArtifactTagAction.tagDelErr"), - JOptionPane.ERROR_MESSAGE); - } - } + final Collection selectedTags = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifactTag.class); + new Thread(() -> { + for (BlackboardArtifactTag tag : selectedTags) { + try { + Case.getCurrentCase().getServices().getTagsManager().deleteBlackboardArtifactTag(tag); + } + catch (TskCoreException ex) { + Logger.getLogger(AddContentTagAction.class.getName()).log(Level.SEVERE, "Error deleting tag", ex); //NON-NLS + SwingUtilities.invokeLater(() -> { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(this.getClass(), + "DeleteBlackboardArtifactTagAction.unableToDelTag.msg", + tag.getName()), + NbBundle.getMessage(this.getClass(), + "DeleteBlackboardArtifactTagAction.tagDelErr"), + JOptionPane.ERROR_MESSAGE); + }); + } + } + }).start(); } } diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteContentTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteContentTagAction.java index c716311eea..f215df8a40 100755 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteContentTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteContentTagAction.java @@ -23,6 +23,7 @@ import java.util.Collection; import java.util.logging.Level; import javax.swing.AbstractAction; import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; import org.openide.util.NbBundle; import org.openide.util.Utilities; import org.sleuthkit.autopsy.casemodule.Case; @@ -55,20 +56,24 @@ public class DeleteContentTagAction extends AbstractAction { @Override public void actionPerformed(ActionEvent e) { - Collection selectedTags = Utilities.actionsGlobalContext().lookupAll(ContentTag.class); - for (ContentTag tag : selectedTags) { - try { - Case.getCurrentCase().getServices().getTagsManager().deleteContentTag(tag); - } - catch (TskCoreException ex) { - Logger.getLogger(AddContentTagAction.class.getName()).log(Level.SEVERE, "Error deleting tag", ex); //NON-NLS - JOptionPane.showMessageDialog(null, - NbBundle.getMessage(this.getClass(), - "DeleteContentTagAction.unableToDelTag.msg", - tag.getName()), - NbBundle.getMessage(this.getClass(), "DeleteContentTagAction.tagDelErr"), - JOptionPane.ERROR_MESSAGE); - } - } + final Collection selectedTags = Utilities.actionsGlobalContext().lookupAll(ContentTag.class); + new Thread(() -> { + for (ContentTag tag : selectedTags) { + try { + Case.getCurrentCase().getServices().getTagsManager().deleteContentTag(tag); + } + catch (TskCoreException ex) { + Logger.getLogger(AddContentTagAction.class.getName()).log(Level.SEVERE, "Error deleting tag", ex); //NON-NLS + SwingUtilities.invokeLater(() -> { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(this.getClass(), + "DeleteContentTagAction.unableToDelTag.msg", + tag.getName()), + NbBundle.getMessage(this.getClass(), "DeleteContentTagAction.tagDelErr"), + JOptionPane.ERROR_MESSAGE); + }); + } + } + }).start(); } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java index d9479c5a6d..2633d0f74e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java @@ -238,7 +238,9 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel { + Case.getCurrentCase().notifyAddingNewDataSource(dataSourceId); + }).start(); DataSourceProcessorCallback cbObj = new DataSourceProcessorCallback () { @Override public void doneEDT(DataSourceProcessorCallback.DataSourceProcessorResult result, List errList, List contents) { @@ -258,8 +260,10 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel { + Case.getCurrentCase().notifyFailedAddingNewDataSource(dataSourceId); + }).start(); + dsProcessor.cancel(); } /* @@ -307,11 +311,13 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel { + if (!newContents.isEmpty()) { + Case.getCurrentCase().notifyNewDataSource(newContents.get(0), dataSourceId); + } else { + Case.getCurrentCase().notifyFailedAddingNewDataSource(dataSourceId); + } + }).start(); // Start ingest if we can diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties index 784d7c4d8a..9c16f20263 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties @@ -242,14 +242,6 @@ LocalFilesPanel.errorLabel.text=Error Label NewCaseVisualPanel1.errorLabel.text=Error Label CollaborationMonitor.addingDataSourceStatus.msg={0} adding data source CollaborationMonitor.analyzingDataSourceStatus.msg={0} analyzing {1} -CollaborationMonitor.failedService.notify.title=Collaboration Service Failed -CollaborationMonitor.failedDbService.notify.msg=Lost connection to database server -CollaborationMonitor.failedSolrService.notify.msg=Lost connection to keyword search server -CollaborationMonitor.failedMessageService.notify.msg=Lost connection to messaging server -CollaborationMonitor.restoredService.notify.title=Collaboration Service Restored -CollaborationMonitor.restoredDbService.notify.msg=Connection to database server restored -CollaborationMonitor.restoredSolrService.notify.msg=Connection to keyword search server restored -CollaborationMonitor.restoredMessageService.notify.msg=Connection to messaging server restored MissingImageDialog.lbWarning.text= MissingImageDialog.lbWarning.toolTipText= CaseConverter.AlreadyMultiUser=Case is already multi-user! @@ -264,4 +256,4 @@ CaseConverter.PotentiallyNonUniqueDatabaseName=Unclear if database name unique. CaseConverter.ConvertedToMultiUser=This case was converted to a Multi-user collaborative case on CaseConverter.UnableToCopySourceImages=Unable to copy source images CaseConverter.ConversionSuccessful=. Conversion successful: -CaseConverter.DeletingCase=Deleting original case folder \ No newline at end of file +CaseConverter.DeletingCase=Deleting original case folder diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index b67c7d0436..af785d02e2 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -44,6 +44,7 @@ import java.util.logging.Level; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; import org.openide.util.NbBundle; import org.openide.util.actions.CallableSystemAction; import org.openide.util.actions.SystemAction; @@ -329,7 +330,9 @@ public class Case { currentCase = newCase; Logger.setLogDirectory(currentCase.getLogDirectoryPath()); doCaseChange(currentCase); - RecentCases.getInstance().addRecentCase(currentCase.name, currentCase.configFilePath); // update the recent cases + SwingUtilities.invokeLater(() -> { + RecentCases.getInstance().addRecentCase(currentCase.name, currentCase.configFilePath); // update the recent cases + }); if (CaseType.MULTI_USER_CASE == newCase.getCaseType()) { try { /** @@ -524,20 +527,24 @@ public class Case { String dbPath = caseDir + File.separator + "autopsy.db"; //NON-NLS db = SleuthkitCase.openCase(dbPath); if (null != db.getBackupDatabasePath()) { - JOptionPane.showMessageDialog(null, - NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.msg", - db.getBackupDatabasePath()), - NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.title"), - JOptionPane.INFORMATION_MESSAGE); + SwingUtilities.invokeLater(() -> { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.msg", + db.getBackupDatabasePath()), + NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.title"), + JOptionPane.INFORMATION_MESSAGE); + }); } } else { db = SleuthkitCase.openCase(xmlcm.getDatabaseName(), UserPreferences.getDatabaseConnectionInfo(), caseDir); if (null != db.getBackupDatabasePath()) { - JOptionPane.showMessageDialog(null, - NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.msg", - db.getBackupDatabasePath()), - NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.title"), - JOptionPane.INFORMATION_MESSAGE); + SwingUtilities.invokeLater(() -> { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.msg", + db.getBackupDatabasePath()), + NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.title"), + JOptionPane.INFORMATION_MESSAGE); + }); } } @@ -646,6 +653,8 @@ public class Case { /** * Notifies case event subscribers (property change listeners) that a data * source is being added to the case database. + * + * This should not be called from the event dispatch thread (EDT) * * @param dataSourceId A unique identifier for the data source. This UUID * should be used to call notifyNewDataSource() after the @@ -658,6 +667,8 @@ public class Case { /** * Notifies case event subscribers (property change listeners) that a data * source failed to be added to the case database. + * + * This should not be called from the event dispatch thread (EDT) * * @param dataSourceId A unique identifier for the data source. */ @@ -668,6 +679,8 @@ public class Case { /** * Notifies case event subscribers (property change listeners) that a data * source is being added to the case database. + * + * This should not be called from the event dispatch thread (EDT) * * @param newDataSource New data source added. * @param dataSourceId A unique identifier for the data source. Should be @@ -680,6 +693,8 @@ public class Case { /** * Notifies the UI that a new ContentTag has been added. + * + * This should not be called from the event dispatch thread (EDT) * * @param newTag new ContentTag added */ @@ -689,6 +704,8 @@ public class Case { /** * Notifies the UI that a ContentTag has been deleted. + * + * This should not be called from the event dispatch thread (EDT) * * @param deletedTag ContentTag deleted */ @@ -698,6 +715,8 @@ public class Case { /** * Notifies the UI that a new BlackboardArtifactTag has been added. + * + * This should not be called from the event dispatch thread (EDT) * * @param newTag new BlackboardArtifactTag added */ @@ -706,7 +725,9 @@ public class Case { } /** - * Notifies the UI that a BlackboardArtifactTag has been. + * Notifies the UI that a BlackboardArtifactTag has been deleted. + * + * This should not be called from the event dispatch thread (EDT) * * @param deletedTag BlackboardArtifactTag deleted */ @@ -775,6 +796,8 @@ public class Case { /** * Updates the case name. + * + * This should not be called from the EDT. * * @param oldCaseName the old case name that wants to be updated * @param oldPath the old path that wants to be updated @@ -785,9 +808,15 @@ public class Case { try { xmlcm.setCaseName(newCaseName); // set the case name = newCaseName; // change the local value - RecentCases.getInstance().updateRecentCase(oldCaseName, oldPath, newCaseName, newPath); // update the recent case eventPublisher.publish(new AutopsyEvent(Events.NAME.toString(), oldCaseName, newCaseName)); - updateMainWindowTitle(newCaseName); + SwingUtilities.invokeLater(() -> { + try{ + RecentCases.getInstance().updateRecentCase(oldCaseName, oldPath, newCaseName, newPath); // update the recent case + updateMainWindowTitle(newCaseName); + } catch (Exception e) { + Logger.getLogger(CasePropertiesForm.class.getName()).log(Level.WARNING, "Error: problem updating case name.", e); //NON-NLS + } + }); } catch (Exception e) { throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.updateCaseName.exception.msg"), e); } @@ -795,6 +824,8 @@ public class Case { /** * Updates the case examiner + * + * This should not be called from the EDT. * * @param oldExaminer the old examiner * @param newExaminer the new examiner @@ -811,6 +842,8 @@ public class Case { /** * Updates the case number + * + * This should not be called from the EDT. * * @param oldCaseNumber the old case number * @param newCaseNumber the new case number @@ -1468,44 +1501,61 @@ public class Case { if (IngestManager.getInstance().isRunningInteractively()) { // enable these menus - CallableSystemAction.get(AddImageAction.class).setEnabled(true); - CallableSystemAction.get(CaseCloseAction.class).setEnabled(true); - CallableSystemAction.get(CasePropertiesAction.class).setEnabled(true); - CallableSystemAction.get(CaseDeleteAction.class).setEnabled(true); // Delete Case menu + SwingUtilities.invokeLater(() -> { + CallableSystemAction.get(AddImageAction.class).setEnabled(true); + CallableSystemAction.get(CaseCloseAction.class).setEnabled(true); + CallableSystemAction.get(CasePropertiesAction.class).setEnabled(true); + CallableSystemAction.get(CaseDeleteAction.class).setEnabled(true); // Delete Case menu + }); if (toChangeTo.hasData()) { // open all top components - CoreComponentControl.openCoreWindows(); + SwingUtilities.invokeLater(() -> { + CoreComponentControl.openCoreWindows(); + }); } else { // close all top components - CoreComponentControl.closeCoreWindows(); + SwingUtilities.invokeLater(() -> { + CoreComponentControl.closeCoreWindows(); + }); } } if (IngestManager.getInstance().isRunningInteractively()) { - updateMainWindowTitle(currentCase.name); + SwingUtilities.invokeLater(() -> { + updateMainWindowTitle(currentCase.name); + }); } else { - Frame f = WindowManager.getDefault().getMainWindow(); - f.setTitle(Case.getAppName()); // set the window name to just application name + SwingUtilities.invokeLater(() -> { + Frame f = WindowManager.getDefault().getMainWindow(); + f.setTitle(Case.getAppName()); // set the window name to just application name + }); } } else { // case is closed if (IngestManager.getInstance().isRunningInteractively()) { - // close all top components first - CoreComponentControl.closeCoreWindows(); - - // disable these menus - CallableSystemAction.get(AddImageAction.class).setEnabled(false); // Add Image menu - CallableSystemAction.get(CaseCloseAction.class).setEnabled(false); // Case Close menu - CallableSystemAction.get(CasePropertiesAction.class).setEnabled(false); // Case Properties menu - CallableSystemAction.get(CaseDeleteAction.class).setEnabled(false); // Delete Case menu + + SwingUtilities.invokeLater(() -> { + // close all top components first + CoreComponentControl.closeCoreWindows(); + + // disable these menus + CallableSystemAction.get(AddImageAction.class).setEnabled(false); // Add Image menu + CallableSystemAction.get(CaseCloseAction.class).setEnabled(false); // Case Close menu + CallableSystemAction.get(CasePropertiesAction.class).setEnabled(false); // Case Properties menu + CallableSystemAction.get(CaseDeleteAction.class).setEnabled(false); // Delete Case menu + }); } //clear pending notifications - MessageNotifyUtil.Notify.clear(); + SwingUtilities.invokeLater(() -> { + MessageNotifyUtil.Notify.clear(); + }); - Frame f = WindowManager.getDefault().getMainWindow(); - f.setTitle(Case.getAppName()); // set the window name to just application name + SwingUtilities.invokeLater(() -> { + Frame f = WindowManager.getDefault().getMainWindow(); + f.setTitle(Case.getAppName()); // set the window name to just application name + }); //try to force gc to happen System.gc(); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseCloseAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseCloseAction.java index ad0b4cc952..702162b987 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseCloseAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseCloseAction.java @@ -27,6 +27,7 @@ import java.util.logging.Level;import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.Action; import javax.swing.ImageIcon; import javax.swing.JButton; +import javax.swing.SwingWorker; import org.openide.util.HelpCtx; import org.openide.util.NbBundle; import org.openide.util.actions.CallableSystemAction; @@ -70,19 +71,24 @@ import org.openide.util.actions.Presenter; return; } - Case result = Case.getCurrentCase(); - try { - result.closeCase(); - } catch (Exception ex) { - Logger.getLogger(CaseCloseAction.class.getName()).log(Level.SEVERE, "Error closing case.", ex); //NON-NLS - } - - EventQueue.invokeLater(new Runnable() { + new SwingWorker() { + @Override - public void run() { + protected Void doInBackground() throws Exception { + try{ + Case result = Case.getCurrentCase(); + result.closeCase(); + } catch (CaseActionException | IllegalStateException ex){ + Logger.getLogger(CaseCloseAction.class.getName()).log(Level.SEVERE, "Error closing case.", ex); //NON-NLS + } + return null; + } + + @Override + protected void done() { StartupWindowProvider.getInstance().open(); } - }); + }.execute(); } /** diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenAction.java index f767efcf09..5bae094395 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenAction.java @@ -23,9 +23,12 @@ import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; +import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.JFileChooser; import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileNameExtensionFilter; import org.openide.util.NbBundle; @@ -76,7 +79,7 @@ public final class CaseOpenAction implements ActionListener { int retval = fc.showOpenDialog(WindowManager.getDefault().getMainWindow()); if (retval == JFileChooser.APPROVE_OPTION) { - String path = fc.getSelectedFile().getPath(); + final String path = fc.getSelectedFile().getPath(); String dirPath = fc.getSelectedFile().getParent(); ModuleSettings.setConfigSetting(ModuleSettings.MAIN_SETTINGS, PROP_BASECASE, dirPath.substring(0, dirPath.lastIndexOf(File.separator))); // check if the file exists @@ -96,20 +99,27 @@ public final class CaseOpenAction implements ActionListener { // no need to show the error message to the user. logger.log(Level.WARNING, "Error closing startup window.", ex); //NON-NLS } - try { - Case.open(path); // open the case - } catch (CaseActionException ex) { - JOptionPane.showMessageDialog(null, - NbBundle.getMessage(this.getClass(), - "CaseOpenAction.msgDlg.cantOpenCase.msg", path, - ex.getMessage()), - NbBundle.getMessage(this.getClass(), - "CaseOpenAction.msgDlg.cantOpenCase.title"), - JOptionPane.ERROR_MESSAGE); - logger.log(Level.WARNING, "Error opening case in folder " + path, ex); //NON-NLS + + new Thread(() -> { + // Create case. + try{ + Case.open(path); + } catch (CaseActionException ex) { + SwingUtilities.invokeLater(() -> { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(this.getClass(), + "CaseOpenAction.msgDlg.cantOpenCase.msg", path, + ex.getMessage()), + NbBundle.getMessage(this.getClass(), + "CaseOpenAction.msgDlg.cantOpenCase.title"), + JOptionPane.ERROR_MESSAGE); - StartupWindowProvider.getInstance().open(); - } + + StartupWindowProvider.getInstance().open(); + }); + logger.log(Level.WARNING, "Error opening case in folder " + path, ex); //NON-NLS + } + }).start(); } } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CollaborationMonitor.java b/Core/src/org/sleuthkit/autopsy/casemodule/CollaborationMonitor.java index 16dfde423d..b75213bcc8 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CollaborationMonitor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CollaborationMonitor.java @@ -22,8 +22,6 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.Serializable; -import java.net.URISyntaxException; -import java.net.UnknownHostException; import java.time.Duration; import java.time.Instant; import java.util.Arrays; @@ -36,46 +34,36 @@ import java.util.UUID; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.logging.Level; -import javax.jms.Connection; -import javax.jms.JMSException; -import org.apache.activemq.ActiveMQConnectionFactory; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandleFactory; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.events.AddingDataSourceEvent; import org.sleuthkit.autopsy.casemodule.events.AddingDataSourceFailedEvent; import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent; -import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.NetworkUtils; import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.events.AutopsyEventException; import org.sleuthkit.autopsy.events.AutopsyEventPublisher; -import org.sleuthkit.autopsy.events.MessageServiceConnectionInfo; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent; import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisStartedEvent; -import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; -import org.sleuthkit.datamodel.CaseDbConnectionInfo; /** * A collaboration monitor listens to local events and translates them into - * collaboration tasks that are broadcast to collaborating nodes, informs the - * user of collaboration tasks on other nodes using progress bars, and monitors - * the health of key collaboration services. + * collaboration tasks that are broadcast to collaborating nodes and informs the + * user of collaboration tasks on other nodes using progress bars. */ final class CollaborationMonitor { private static final String EVENT_CHANNEL_NAME = "%s-Collaboration-Monitor-Events"; private static final String COLLABORATION_MONITOR_EVENT = "COLLABORATION_MONITOR_EVENT"; private static final Set CASE_EVENTS_OF_INTEREST = new HashSet<>(Arrays.asList(new String[]{Case.Events.ADDING_DATA_SOURCE.toString(), Case.Events.DATA_SOURCE_ADDED.toString(), Case.Events.ADDING_DATA_SOURCE_FAILED.toString()})); - private static final int NUMBER_OF_PERIODIC_TASK_THREADS = 3; + private static final int NUMBER_OF_PERIODIC_TASK_THREADS = 2; private static final String PERIODIC_TASK_THREAD_NAME = "collab-monitor-periodic-tasks-%d"; private static final long HEARTBEAT_INTERVAL_MINUTES = 1; private static final long MAX_MISSED_HEARTBEATS = 5; private static final long STALE_TASKS_DETECTION_INTERVAL_MINUTES = 2; - private static final long CRASH_DETECTION_INTERVAL_MINUTES = 2; private static final long EXECUTOR_TERMINATION_WAIT_SECS = 30; private static final Logger logger = Logger.getLogger(CollaborationMonitor.class.getName()); private final String hostName; @@ -130,12 +118,10 @@ final class CollaborationMonitor { * * 1. Send heartbeats to collaboration monitors on other nodes.
* 2. Check for stale remote tasks.
- * 3. Check the availability of key collaboration services.
*/ periodicTasksExecutor = new ScheduledThreadPoolExecutor(NUMBER_OF_PERIODIC_TASK_THREADS, new ThreadFactoryBuilder().setNameFormat(PERIODIC_TASK_THREAD_NAME).build()); periodicTasksExecutor.scheduleAtFixedRate(new HeartbeatTask(), HEARTBEAT_INTERVAL_MINUTES, HEARTBEAT_INTERVAL_MINUTES, TimeUnit.MINUTES); periodicTasksExecutor.scheduleAtFixedRate(new StaleTaskDetectionTask(), STALE_TASKS_DETECTION_INTERVAL_MINUTES, STALE_TASKS_DETECTION_INTERVAL_MINUTES, TimeUnit.MINUTES); - periodicTasksExecutor.scheduleAtFixedRate(new CrashDetectionTask(), CRASH_DETECTION_INTERVAL_MINUTES, CRASH_DETECTION_INTERVAL_MINUTES, TimeUnit.MINUTES); } /** @@ -497,80 +483,6 @@ final class CollaborationMonitor { } } - /** - * A Runnable task that periodically checks the availability of - * collaboration resources (PostgreSQL server, Solr server, Active MQ - * message broker) and reports status to the user in case of a gap in - * service. - */ - private final static class CrashDetectionTask implements Runnable { - - private static boolean dbServerIsRunning = true; - private static boolean solrServerIsRunning = true; - private static boolean messageServerIsRunning = true; - private static final Object lock = new Object(); - - /** - * Monitor the availability of collaboration resources - */ - @Override - public void run() { - synchronized (lock) { - CaseDbConnectionInfo dbInfo = UserPreferences.getDatabaseConnectionInfo(); - if (dbInfo.canConnect()) { - if (!dbServerIsRunning) { - dbServerIsRunning = true; - logger.log(Level.INFO, "Connection to PostgreSQL server restored"); //NON-NLS - MessageNotifyUtil.Notify.info(NbBundle.getMessage(CollaborationMonitor.class, "CollaborationMonitor.restoredService.notify.title"), NbBundle.getMessage(CollaborationMonitor.class, "CollaborationMonitor.restoredDbService.notify.msg")); - } - } else { - if (dbServerIsRunning) { - dbServerIsRunning = false; - logger.log(Level.SEVERE, "Failed to connect to PostgreSQL server"); //NON-NLS - MessageNotifyUtil.Notify.error(NbBundle.getMessage(CollaborationMonitor.class, "CollaborationMonitor.failedService.notify.title"), NbBundle.getMessage(CollaborationMonitor.class, "CollaborationMonitor.failedDbService.notify.msg")); - } - } - - KeywordSearchService kwsService = Case.getCurrentCase().getServices().getKeywordSearchService(); - - if (kwsService.canConnectToRemoteSolrServer()) { - if (!solrServerIsRunning) { - solrServerIsRunning = true; - logger.log(Level.INFO, "Connection to Solr server restored"); //NON-NLS - MessageNotifyUtil.Notify.info(NbBundle.getMessage(CollaborationMonitor.class, "CollaborationMonitor.restoredService.notify.title"), NbBundle.getMessage(CollaborationMonitor.class, "CollaborationMonitor.restoredSolrService.notify.msg")); - } - } - else { - if (solrServerIsRunning) { - solrServerIsRunning = false; - logger.log(Level.SEVERE, "Failed to connect to Solr server"); //NON-NLS - MessageNotifyUtil.Notify.error(NbBundle.getMessage(CollaborationMonitor.class, "CollaborationMonitor.failedService.notify.title"), NbBundle.getMessage(CollaborationMonitor.class, "CollaborationMonitor.failedSolrService.notify.msg")); - } - } - - MessageServiceConnectionInfo msgInfo = UserPreferences.getMessageServiceConnectionInfo(); - try { - ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(msgInfo.getUserName(), msgInfo.getPassword(), msgInfo.getURI()); - Connection connection = connectionFactory.createConnection(); - connection.start(); - connection.close(); - if (!messageServerIsRunning) { - messageServerIsRunning = true; - logger.log(Level.INFO, "Connection to ActiveMQ server restored"); //NON-NLS - MessageNotifyUtil.Notify.info(NbBundle.getMessage(CollaborationMonitor.class, "CollaborationMonitor.restoredService.notify.title"), NbBundle.getMessage(CollaborationMonitor.class, "CollaborationMonitor.restoredMessageService.notify.msg")); - } - } catch (URISyntaxException | JMSException ex) { - if (messageServerIsRunning) { - messageServerIsRunning = false; - logger.log(Level.SEVERE, "Failed to connect to ActiveMQ server", ex); //NON-NLS - MessageNotifyUtil.Notify.error(NbBundle.getMessage(CollaborationMonitor.class, "CollaborationMonitor.failedService.notify.title"), NbBundle.getMessage(CollaborationMonitor.class, "CollaborationMonitor.failedMessageService.notify.msg")); - } - } - } - } - - } - /** * An Autopsy event to be sent in event messages to the collaboration * monitors on other Autopsy nodes. diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardAction.java index bc9cdea275..aad75a51c0 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardAction.java @@ -24,7 +24,10 @@ import java.awt.Dialog; import java.io.File; import java.text.MessageFormat; import java.util.logging.Level; +import java.util.concurrent.ExecutionException; import javax.swing.JComponent; +import javax.swing.SwingWorker; +import javax.swing.SwingUtilities; import org.openide.DialogDescriptor; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; @@ -81,7 +84,7 @@ import org.sleuthkit.datamodel.TskData.DbType; * The method to perform new case creation */ private void newCaseAction() { - WizardDescriptor wizardDescriptor = new WizardDescriptor(getPanels()); + final WizardDescriptor wizardDescriptor = new WizardDescriptor(getPanels()); // {0} will be replaced by WizardDesriptor.Panel.getComponent().getName() wizardDescriptor.setTitleFormat(new MessageFormat("{0}")); wizardDescriptor.setTitle(NbBundle.getMessage(this.getClass(), "NewCaseWizardAction.newCase.windowTitle.text")); @@ -89,44 +92,71 @@ import org.sleuthkit.datamodel.TskData.DbType; dialog.setVisible(true); dialog.toFront(); + if(wizardDescriptor.getValue() == WizardDescriptor.FINISH_OPTION){ + new SwingWorker() { - boolean finished = wizardDescriptor.getValue() == WizardDescriptor.FINISH_OPTION; // check if it finishes (it's not cancelled) - boolean isCancelled = wizardDescriptor.getValue() == WizardDescriptor.CANCEL_OPTION; // check if the "Cancel" button is pressed + @Override + protected Void doInBackground() throws Exception { + // Create case. + + String caseNumber = (String) wizardDescriptor.getProperty("caseNumber"); //NON-NLS + String examiner = (String) wizardDescriptor.getProperty("caseExaminer"); //NON-NLS + final String caseName = (String) wizardDescriptor.getProperty("caseName"); //NON-NLS + String createdDirectory = (String) wizardDescriptor.getProperty("createdDirectory"); //NON-NLS + CaseType caseType = CaseType.values()[(int)wizardDescriptor.getProperty("caseType")]; //NON-NLS - // if the finish button is pressed (not cancelled) - if (finished) { - // now start the 'Add Image' wizard - //TODO fix for local - CaseType currentCaseType = CaseType.fromString(ModuleSettings.getConfigSetting(ModuleSettings.MAIN_SETTINGS, ModuleSettings.CURRENT_CASE_TYPE)); - CaseDbConnectionInfo info = UserPreferences.getDatabaseConnectionInfo(); - if ((currentCaseType==CaseType.SINGLE_USER_CASE) || ((info.getDbType() != DbType.SQLITE) && info.canConnect())) { - AddImageAction addImageAction = SystemAction.get(AddImageAction.class); - addImageAction.actionPerformed(null); - } else { - JOptionPane.showMessageDialog(null, - NbBundle.getMessage(this.getClass(), "NewCaseWizardAction.databaseProblem1.text"), - NbBundle.getMessage(this.getClass(), "NewCaseWizardAction.databaseProblem2.text"), - JOptionPane.ERROR_MESSAGE); - isCancelled = true; - } + Case.create(createdDirectory, caseName, caseNumber, examiner, caseType); + return null; + } + + @Override + protected void done() { + try { + get(); + CaseType currentCaseType = CaseType.values()[(int)wizardDescriptor.getProperty("caseType")]; //NON-NLS + CaseDbConnectionInfo info = UserPreferences.getDatabaseConnectionInfo(); + if ((currentCaseType==CaseType.SINGLE_USER_CASE) || ((info.getDbType() != DbType.SQLITE) && info.canConnect())) { + AddImageAction addImageAction = SystemAction.get(AddImageAction.class); + addImageAction.actionPerformed(null); + } else { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(this.getClass(), "NewCaseWizardAction.databaseProblem1.text"), + NbBundle.getMessage(this.getClass(), "NewCaseWizardAction.databaseProblem2.text"), + JOptionPane.ERROR_MESSAGE); + doFailedCaseCleanup(wizardDescriptor); + } + + + } catch (Exception ex) { + final String caseName = (String) wizardDescriptor.getProperty("caseName"); //NON-NLS + SwingUtilities.invokeLater(() -> { + JOptionPane.showMessageDialog(null, NbBundle.getMessage(this.getClass(), + "CaseCreateAction.msgDlg.cantCreateCase.msg")+" "+caseName, + NbBundle.getMessage(this.getClass(), + "CaseOpenAction.msgDlg.cantOpenCase.title"), + JOptionPane.ERROR_MESSAGE); + }); + doFailedCaseCleanup(wizardDescriptor); + } + } + }.execute(); + + + } else { + new Thread(() -> { + doFailedCaseCleanup(wizardDescriptor); + }).start(); } - - // if Cancel button is pressed - if (isCancelled) { - String createdDirectory = (String) wizardDescriptor.getProperty("createdDirectory"); //NON-NLS - // if there's case opened, close the case - if (Case.existsCurrentCase()) { - // close the previous case if there's any - CaseCloseAction closeCase = SystemAction.get(CaseCloseAction.class); - closeCase.actionPerformed(null); - } - if (createdDirectory != null) { - logger.log(Level.INFO, "Deleting a created case directory due to isCancelled set, dir: " + createdDirectory); //NON-NLS - Case.deleteCaseDirectory(new File(createdDirectory)); - } - } - panels = null; // reset the panel } + + private void doFailedCaseCleanup(WizardDescriptor wizardDescriptor){ + String createdDirectory = (String) wizardDescriptor.getProperty("createdDirectory"); //NON-NLS + + if (createdDirectory != null) { + logger.log(Level.INFO, "Deleting a created case directory due to an error, dir: " + createdDirectory); //NON-NLS + Case.deleteCaseDirectory(new File(createdDirectory)); + } + } /** * Initialize panels representing individual wizard's steps and sets diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardPanel2.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardPanel2.java index 98a972a5af..f3edbe0370 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardPanel2.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardPanel2.java @@ -171,33 +171,12 @@ class NewCaseWizardPanel2 implements WizardDescriptor.ValidatingPanel { + // Create case. + try{ + Case.open(casePath); + } catch (CaseActionException ex) { + SwingUtilities.invokeLater(() -> { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(this.getClass(), "CaseOpenAction.msgDlg.cantOpenCase.msg", caseName, ex.getMessage()), NbBundle.getMessage(this.getClass(), "CaseOpenAction.msgDlg.cantOpenCase.title"), JOptionPane.ERROR_MESSAGE); - logger.log(Level.WARNING, "Error: couldn't open case: " + caseName, ex); //NON-NLS + }); + logger.log(Level.WARNING, "Error: couldn't open case: " + caseName, ex); //NON-NLS + } + }).start(); } } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/RecentItems.java b/Core/src/org/sleuthkit/autopsy/casemodule/RecentItems.java index 43de7bbb7f..763dfe59d2 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/RecentItems.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/RecentItems.java @@ -23,9 +23,12 @@ import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; +import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; @@ -35,8 +38,8 @@ import org.sleuthkit.autopsy.coreutils.Logger; */ class RecentItems implements ActionListener { - String caseName; - String casePath; + final String caseName; + final String casePath; private JPanel caller; // for error handling /** the constructor */ @@ -76,15 +79,20 @@ class RecentItems implements ActionListener { } } else { - try { - Case.open(casePath); // open the case - } catch (CaseActionException ex) { - JOptionPane.showMessageDialog(null, + new Thread(() -> { + // Create case. + try{ + Case.open(casePath); + } catch (CaseActionException ex) { + SwingUtilities.invokeLater(() -> { + JOptionPane.showMessageDialog(null, NbBundle.getMessage(this.getClass(), "CaseOpenAction.msgDlg.cantOpenCase.msg", casePath, ex.getMessage()), NbBundle.getMessage(this.getClass(), "CaseOpenAction.msgDlg.cantOpenCase.title"), JOptionPane.ERROR_MESSAGE); - Logger.getLogger(RecentItems.class.getName()).log(Level.WARNING, "Error: Couldn't open recent case at " + casePath, ex); //NON-NLS - } + }); + Logger.getLogger(RecentItems.class.getName()).log(Level.WARNING, "Error: Couldn't open recent case at " + casePath, ex); //NON-NLS + } + }).start(); } } } diff --git a/Core/src/org/sleuthkit/autopsy/core/Bundle.properties b/Core/src/org/sleuthkit/autopsy/core/Bundle.properties index ff08a90d89..8d0d3087ce 100644 --- a/Core/src/org/sleuthkit/autopsy/core/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/core/Bundle.properties @@ -12,3 +12,12 @@ org_sleuthkit_autopsy_core_update_center=http://sleuthkit.org/autopsy/updates.xm Services/AutoupdateType/org_sleuthkit_autopsy_core_update_center.settings=Autopsy Update Center Installer.errorInitJavafx.msg=Error initializing JavaFX. Installer.errorInitJavafx.details=\ Some features will not be available. Check that you have the right JRE installed (Oracle JRE > 1.7.10). +ServicesMonitor.failedService.notify.title=Service Failed +ServicesMonitor.failedService.notify.msg=Lost connection to {0} +ServicesMonitor.restoredService.notify.title=Service Restored +ServicesMonitor.restoredService.notify.msg=Connection to {0} restored +ServicesMonitor.statusChange.notify.title=Service Status Update +ServicesMonitor.statusChange.notify.msg=Status for {0} is {1} +ServicesMonitor.nullServiceName.excepton.txt=Requested service name is null +ServicesMonitor.nullStatusOrDetails.excepton.txt=Status or details string is null +ServicesMonitor.unknownServiceName.excepton.txt=Requested service name {0} is unknown \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/core/ServicesMonitor.java b/Core/src/org/sleuthkit/autopsy/core/ServicesMonitor.java new file mode 100644 index 0000000000..9fb7389fb5 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/core/ServicesMonitor.java @@ -0,0 +1,383 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013-2015 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.core; + +import org.sleuthkit.autopsy.core.events.ServiceEvent; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.beans.PropertyChangeListener; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.events.AutopsyEventPublisher; +import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; + +/** + * This class periodically checks availability of collaboration resources - + * remote database, remote keyword search server, messaging service - and + * reports status updates to the user in case of a gap in service. + */ +public class ServicesMonitor { + + private AutopsyEventPublisher eventPublisher; + private static final Logger logger = Logger.getLogger(ServicesMonitor.class.getName()); + private final ScheduledThreadPoolExecutor periodicTasksExecutor; + + private static final String PERIODIC_TASK_THREAD_NAME = "services-monitor-periodic-task-%d"; + private static final int NUMBER_OF_PERIODIC_TASK_THREADS = 1; + private static final long CRASH_DETECTION_INTERVAL_MINUTES = 2; + + private static final Set servicesList = Stream.of(ServicesMonitor.Service.values()) + .map(Service::toString) + .collect(Collectors.toSet()); + + /** + * The service monitor maintains a mapping of each service to it's last + * status update. + */ + private final ConcurrentHashMap statusByService; + + /** + * Call constructor on start-up so that the first check of services is done + * as soon as possible. + */ + private static ServicesMonitor instance = new ServicesMonitor(); + + /** + * List of services that are being monitored. The service names should be + * representative of the service functionality and readable as they get + * logged when service outage occurs. + */ + public enum Service { + + /** + * Property change event fired when remote case database service status + * changes. New value is set to updated ServiceStatus, old value is + * null. + */ + REMOTE_CASE_DATABASE("Multi-user case database service"), + /** + * Property change event fired when remote keyword search service status + * changes. New value is set to updated ServiceStatus, old value is + * null. + */ + REMOTE_KEYWORD_SEARCH("Multi-user keyword search service"), + /** + * Property change event fired when messaging service status changes. + * New value is set to updated ServiceStatus, old value is null. + */ + MESSAGING("Messaging service"); + + private final String displayName; + + private Service(String name) { + this.displayName = name; + } + + public String getDisplayName() { + return displayName; + } + }; + + /** + * List of possible service statuses. + */ + public enum ServiceStatus { + + /** + * Service is currently up. + */ + UP, + /** + * Service is currently down. + */ + DOWN + }; + + public synchronized static ServicesMonitor getInstance() { + if (instance == null) { + instance = new ServicesMonitor(); + } + return instance; + } + + private ServicesMonitor() { + + this.eventPublisher = new AutopsyEventPublisher(); + this.statusByService = new ConcurrentHashMap<>(); + + // First check is triggered immediately on current thread. + checkAllServices(); + + /** + * Start periodic task that check the availability of key collaboration + * services. + */ + periodicTasksExecutor = new ScheduledThreadPoolExecutor(NUMBER_OF_PERIODIC_TASK_THREADS, new ThreadFactoryBuilder().setNameFormat(PERIODIC_TASK_THREAD_NAME).build()); + periodicTasksExecutor.scheduleAtFixedRate(new CrashDetectionTask(), CRASH_DETECTION_INTERVAL_MINUTES, CRASH_DETECTION_INTERVAL_MINUTES, TimeUnit.MINUTES); + } + + /** + * Updates service status and publishes the service status update if it is + * different from previous status. Event is published locally. Logs status + * changes. + * + * @param service Name of the service. + * @param status Updated status for the service. + * @param details Details of the event. + * @throws + * org.sleuthkit.autopsy.core.ServicesMonitor.ServicesMonitorException + * Thrown if either of input parameters is null. + */ + public void setServiceStatus(String service, String status, String details) throws ServicesMonitorException { + + if (service == null) { + logger.log(Level.SEVERE, "Call to setServiceStatus() with null service name"); //NON-NLS + throw new ServicesMonitorException(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.nullServiceName.excepton.txt")); + } + + if (status == null || details == null) { + logger.log(Level.SEVERE, "Call to setServiceStatus() with null status or details"); //NON-NLS + throw new ServicesMonitorException(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.nullStatusOrDetails.excepton.txt")); + } + + // if the status update is for an existing service who's status hasn't changed - do nothing. + if (statusByService.containsKey(service) && status.equals(statusByService.get(service))) { + return; + } + + // new service or status has changed - identify service's display name + String serviceDisplayName; + try { + serviceDisplayName = ServicesMonitor.Service.valueOf(service).getDisplayName(); + } catch (IllegalArgumentException ignore) { + // custom service that is not listed in ServicesMonitor.Service enum. Use service name as display name. + serviceDisplayName = service; + } + + if (status.equals(ServiceStatus.UP.toString())) { + logger.log(Level.INFO, "Connection to {0} restored", serviceDisplayName); //NON-NLS + MessageNotifyUtil.Notify.info(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredService.notify.title"), + NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredService.notify.msg", serviceDisplayName)); + } else if (status.equals(ServiceStatus.DOWN.toString())) { + logger.log(Level.SEVERE, "Failed to connect to {0}", serviceDisplayName); //NON-NLS + MessageNotifyUtil.Notify.error(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedService.notify.title"), + NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedService.notify.msg", serviceDisplayName)); + } else { + logger.log(Level.INFO, "Status for {0} is {1}", new Object[]{serviceDisplayName, status}); //NON-NLS + MessageNotifyUtil.Notify.info(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.statusChange.notify.title"), + NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.statusChange.notify.msg", new Object[]{serviceDisplayName, status})); + } + + // update and publish new status + statusByService.put(service, status); + eventPublisher.publishLocally(new ServiceEvent(service, status, details)); + } + + /** + * Get last status update for a service. + * + * @param service Name of the service. + * @return ServiceStatus Status for the service. + * @throws + * org.sleuthkit.autopsy.core.ServicesMonitor.ServicesMonitorException + * Thrown if service name is null or service doesn't exist. + */ + public String getServiceStatus(String service) throws ServicesMonitorException { + + if (service == null) { + throw new ServicesMonitorException(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.nullServiceName.excepton.txt")); + } + + String status = statusByService.get(service); + if (status == null) { + // no such service + throw new ServicesMonitorException(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.unknownServiceName.excepton.txt", service)); + } + return status; + } + + /** + * Performs service availability status check. + * + * @param service Name of the service. + */ + private void checkServiceStatus(String service) { + if (service.equals(Service.REMOTE_CASE_DATABASE.toString())) { + checkDatabaseConnectionStatus(); + } else if (service.equals(Service.REMOTE_KEYWORD_SEARCH.toString())) { + checkKeywordSearchServerConnectionStatus(); + } else if (service.equals(Service.MESSAGING.toString())) { + checkMessagingServerConnectionStatus(); + } + } + + /** + * Performs case database service availability status check. + */ + private void checkDatabaseConnectionStatus() { + try { + if (UserPreferences.getDatabaseConnectionInfo().canConnect()) { + setServiceStatus(Service.REMOTE_CASE_DATABASE.toString(), ServiceStatus.UP.toString(), ""); + } else { + setServiceStatus(Service.REMOTE_CASE_DATABASE.toString(), ServiceStatus.DOWN.toString(), ""); + } + } catch (Exception ex) { + logger.log(Level.SEVERE, "Exception while checking database connection status", ex); //NON-NLS + } + } + + /** + * Performs keyword search service availability status check. + */ + private void checkKeywordSearchServerConnectionStatus() { + try { + KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class); + if (kwsService != null && kwsService.canConnectToRemoteSolrServer()) { + setServiceStatus(Service.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.UP.toString(), ""); + } else { + setServiceStatus(Service.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.DOWN.toString(), ""); + } + } catch (Exception ex) { + logger.log(Level.SEVERE, "Exception while checking keyword search server connection status", ex); //NON-NLS + } + } + + /** + * Performs messaging service availability status check. + */ + private void checkMessagingServerConnectionStatus() { + try { + if (UserPreferences.getMessageServiceConnectionInfo().canConnect()) { + setServiceStatus(Service.MESSAGING.toString(), ServiceStatus.UP.toString(), ""); + } else { + setServiceStatus(Service.MESSAGING.toString(), ServiceStatus.DOWN.toString(), ""); + } + } catch (Exception ex) { + logger.log(Level.SEVERE, "Exception while checking messaging server connection status", ex); //NON-NLS + } + } + + /** + * Adds an event subscriber to this publisher. Subscriber will be subscribed + * to all events from this publisher. + * + * @param subscriber The subscriber to add. + */ + public void addSubscriber(PropertyChangeListener subscriber) { + eventPublisher.addSubscriber(servicesList, subscriber); + } + + /** + * Adds an event subscriber to this publisher. + * + * @param eventNames The events the subscriber is interested in. + * @param subscriber The subscriber to add. + */ + public void addSubscriber(Set eventNames, PropertyChangeListener subscriber) { + eventPublisher.addSubscriber(eventNames, subscriber); + } + + /** + * Adds an event subscriber to this publisher. + * + * @param eventName The event the subscriber is interested in. + * @param subscriber The subscriber to add. + */ + public void addSubscriber(String eventName, PropertyChangeListener subscriber) { + eventPublisher.addSubscriber(eventName, subscriber); + } + + /** + * Removes an event subscriber from this publisher. + * + * @param eventNames The events the subscriber is no longer interested in. + * @param subscriber The subscriber to remove. + */ + public void removeSubscriber(Set eventNames, PropertyChangeListener subscriber) { + eventPublisher.removeSubscriber(eventNames, subscriber); + } + + /** + * Removes an event subscriber from this publisher. + * + * @param eventName The event the subscriber is no longer interested in. + * @param subscriber The subscriber to remove. + */ + public void removeSubscriber(String eventName, PropertyChangeListener subscriber) { + eventPublisher.removeSubscriber(eventName, subscriber); + } + + /** + * Removes an event subscriber to this publisher. Subscriber will be removed + * from all event notifications from this publisher. + * + * @param subscriber The subscriber to remove. + */ + public void removeSubscriber(PropertyChangeListener subscriber) { + eventPublisher.removeSubscriber(servicesList, subscriber); + } + + /** + * Verifies connectivity to all services. + */ + private void checkAllServices() { + for (String service : servicesList) { + checkServiceStatus(service); + } + } + + /** + * A Runnable task that periodically checks the availability of + * collaboration resources (remote database, remote keyword search service, + * message broker) and reports status to the user in case of a gap in + * service. + */ + private final class CrashDetectionTask implements Runnable { + + /** + * Monitor the availability of collaboration resources + */ + @Override + public void run() { + checkAllServices(); + } + } + + /** + * Exception thrown when service status query results in an error. + */ + public class ServicesMonitorException extends Exception { + + public ServicesMonitorException(String message) { + super(message); + } + + public ServicesMonitorException(String message, Throwable cause) { + super(message, cause); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/core/events/ServiceEvent.java b/Core/src/org/sleuthkit/autopsy/core/events/ServiceEvent.java new file mode 100644 index 0000000000..057d9cd563 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/core/events/ServiceEvent.java @@ -0,0 +1,49 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013-2015 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.core.events; + +import java.io.Serializable; +import org.sleuthkit.autopsy.events.AutopsyEvent; + +/** + * A class for events to be published to registered subscribers of Service + * Monitor on this Autopsy node. The class extends PropertyChangeEvent (via + * AutopsyEvent) to integrate with legacy use of JavaBeans PropertyChangeEvents + * and PropertyChangeListeners as an application event system, and implements + * Serializable to allow it to be published over a network in serialized form. + */ +public final class ServiceEvent extends AutopsyEvent implements Serializable { + + private static final long serialVersionUID = 1L; + private final String details; + + public ServiceEvent(String serviceName, String status, String details) { + super(serviceName, null, status); + this.details = details; + } + + /** + * Gets details string passed as input to ServiceEvent constructor. + * + * @return String Details of the event. + */ + public String getDetails() { + return details; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Installer.java b/Core/src/org/sleuthkit/autopsy/corecomponents/Installer.java index d38e58525e..14da528efc 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Installer.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Installer.java @@ -24,10 +24,12 @@ import java.util.Collection; import java.util.Map; import java.util.TreeMap; import java.util.logging.Level; +import java.util.concurrent.ExecutionException; import javax.swing.BorderFactory; import javax.swing.UIManager; import javax.swing.UIManager.LookAndFeelInfo; import javax.swing.UnsupportedLookAndFeelException; +import javax.swing.SwingWorker; import org.netbeans.spi.sendopts.OptionProcessor; import org.netbeans.swing.tabcontrol.plaf.DefaultTabbedContainerUI; import org.openide.modules.ModuleInstall; @@ -74,13 +76,18 @@ public class Installer extends ModuleInstall { for (OptionProcessor processor : processors) { if (processor instanceof OpenFromArguments) { OpenFromArguments argsProcessor = (OpenFromArguments) processor; - String caseFile = argsProcessor.getDefaultArg(); + final String caseFile = argsProcessor.getDefaultArg(); if (caseFile != null && !caseFile.equals("") && caseFile.endsWith(".aut") && new File(caseFile).exists()) { //NON-NLS - try { - Case.open(caseFile); - return; - } catch (Exception e) { - } + + new Thread(() -> { + // Create case. + try{ + Case.open(caseFile); + } catch(Exception ex){ + logger.log(Level.WARNING, "Error opening case. ", ex); //NON-NLS + } + }).start(); + return; } } } @@ -99,13 +106,15 @@ public class Installer extends ModuleInstall { @Override public void close() { - try { - if (Case.isCaseOpen()) - Case.getCurrentCase().closeCase(); - } - catch (CaseActionException ex) { - logger.log(Level.WARNING, "Error closing case. ", ex); //NON-NLS - } + new Thread(() -> { + try { + if (Case.isCaseOpen()) + Case.getCurrentCase().closeCase(); + } + catch (CaseActionException | IllegalStateException ex) { + logger.log(Level.WARNING, "Error closing case. ", ex); //NON-NLS + } + }).start(); } private void setupLAF() { diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index 791272d57b..d93f3d1ba8 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -567,8 +567,10 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat */ try { Case.getCurrentCase(); - CoreComponentControl.openCoreWindows(); - SwingUtilities.invokeLater(this::componentOpened); + SwingUtilities.invokeLater(() -> { + CoreComponentControl.openCoreWindows(); + componentOpened(); + }); } catch (IllegalStateException notUsed) { /** * Case is closed, do nothing. diff --git a/Core/src/org/sleuthkit/autopsy/events/AutopsyEvent.java b/Core/src/org/sleuthkit/autopsy/events/AutopsyEvent.java index fb011a1484..d099fa2a65 100644 --- a/Core/src/org/sleuthkit/autopsy/events/AutopsyEvent.java +++ b/Core/src/org/sleuthkit/autopsy/events/AutopsyEvent.java @@ -62,7 +62,7 @@ public class AutopsyEvent extends PropertyChangeEvent implements Serializable { /** * Gets the source type (local or remote). * - * @param sourceType The source type of the event, local or remote. + * @return SourceType The source type of the event, local or remote. */ public SourceType getSourceType() { return sourceType; diff --git a/Core/src/org/sleuthkit/autopsy/events/AutopsyEventPublisher.java b/Core/src/org/sleuthkit/autopsy/events/AutopsyEventPublisher.java index df2b21674a..eca2f8dfb6 100644 --- a/Core/src/org/sleuthkit/autopsy/events/AutopsyEventPublisher.java +++ b/Core/src/org/sleuthkit/autopsy/events/AutopsyEventPublisher.java @@ -120,7 +120,7 @@ public final class AutopsyEventPublisher { /** * Removes an event subscriber from this publisher. * - * @param eventNames The event the subscriber is no longer interested in. + * @param eventName The event the subscriber is no longer interested in. * @param subscriber The subscriber to remove. */ public void removeSubscriber(String eventName, PropertyChangeListener subscriber) { diff --git a/Core/src/org/sleuthkit/autopsy/events/MessageServiceConnectionInfo.java b/Core/src/org/sleuthkit/autopsy/events/MessageServiceConnectionInfo.java index 8bebf16f0d..2e26d3a04c 100644 --- a/Core/src/org/sleuthkit/autopsy/events/MessageServiceConnectionInfo.java +++ b/Core/src/org/sleuthkit/autopsy/events/MessageServiceConnectionInfo.java @@ -21,6 +21,10 @@ package org.sleuthkit.autopsy.events; import java.net.URI; import java.net.URISyntaxException; import javax.annotation.concurrent.Immutable; +import javax.jms.Connection; +import javax.jms.JMSException; +import org.apache.activemq.ActiveMQConnectionFactory; +import org.sleuthkit.autopsy.core.UserPreferences; /** * Connection info for a Java Message Service (JMS) provider. Thread-safe. @@ -99,4 +103,20 @@ public final class MessageServiceConnectionInfo { return new URI(String.format(MESSAGE_SERVICE_URI, host, port)); } + /** + * Verifies connection to messaging service. + * + * @return True if connection can be established, false otherwise. + */ + public boolean canConnect() { + try { + ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(getUserName(), getPassword(), getURI()); + Connection connection = connectionFactory.createConnection(); + connection.start(); + connection.close(); + return true; + } catch (URISyntaxException | JMSException ex) { + return false; + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/events/RemoteEventPublisher.java b/Core/src/org/sleuthkit/autopsy/events/RemoteEventPublisher.java index dde382e397..01e47342ae 100644 --- a/Core/src/org/sleuthkit/autopsy/events/RemoteEventPublisher.java +++ b/Core/src/org/sleuthkit/autopsy/events/RemoteEventPublisher.java @@ -169,7 +169,9 @@ final class RemoteEventPublisher { if (object instanceof AutopsyEvent) { AutopsyEvent event = (AutopsyEvent) object; event.setSourceType(AutopsyEvent.SourceType.REMOTE); - localPublisher.publish(event); + new Thread(() -> { + localPublisher.publish(event); + }).start(); } } } catch (Exception ex) { diff --git a/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties b/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties index cfea3bd790..9cf8cdc91c 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties @@ -103,3 +103,5 @@ IngestJobSettingsPanel.jButtonSelectAll.text=Select All IngestJobSettingsPanel.jButtonDeselectAll.text=Deselect All 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 +IngestManager.cancellingIngest.msgDlg.text=Cancelling all currently running ingest jobs +IngestManager.serviceIsDown.msgDlg.text={0} is down diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java index 3ee3267d8a..68d8863b17 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -44,6 +45,7 @@ import org.netbeans.api.progress.ProgressHandleFactory; import org.openide.util.Cancellable; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.core.ServicesMonitor; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.coreutils.Logger; @@ -150,6 +152,12 @@ public class IngestManager { * is the default. */ private volatile boolean runInteractively; + + /** + * Ingest manager subscribes to service outage notifications. If key services are down, + * ingest manager cancels all ingest jobs in progress. + */ + private final ServicesMonitor servicesMonitor; /** * Ingest job events. @@ -264,6 +272,9 @@ public class IngestManager { this.nextThreadId = new AtomicLong(0L); this.jobsById = new ConcurrentHashMap<>(); this.ingestJobStarters = new ConcurrentHashMap<>(); + + this.servicesMonitor = ServicesMonitor.getInstance(); + subscribeToServiceMonitorEvents(); this.startDataSourceIngestThread(); @@ -313,6 +324,56 @@ public class IngestManager { } }); } + + /** + * Subscribe ingest manager to service monitor events. Cancels ingest + * if one of services it's subscribed to goes down. + */ + private void subscribeToServiceMonitorEvents() { + PropertyChangeListener propChangeListener = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getNewValue().equals(ServicesMonitor.ServiceStatus.DOWN.toString())) { + + // check whether a milti-user case is currently being processed + try { + if (!Case.isCaseOpen() || Case.getCurrentCase().getCaseType() != Case.CaseType.MULTI_USER_CASE) { + return; + } + } catch (IllegalStateException ignore) { + // thorown by Case.getCurrentCase() when no case is open + return; + } + + // one of the services we subscribed to went down + String serviceDisplayName = ServicesMonitor.Service.valueOf(evt.getPropertyName()).getDisplayName(); + logger.log(Level.SEVERE, "Service {0} is down! Cancelling all running ingest jobs", serviceDisplayName); //NON-NLS + + // display notification if running interactively + if (isIngestRunning() && isRunningInteractively()) { + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(this.getClass(), "IngestManager.cancellingIngest.msgDlg.text"), + NbBundle.getMessage(this.getClass(), "IngestManager.serviceIsDown.msgDlg.text", serviceDisplayName), + JOptionPane.ERROR_MESSAGE); + } + }); + } + + // cancel ingest if running + cancelAllIngestJobs(); + } + } + }; + + // subscribe to services of interest + Set servicesList = new HashSet<>(); + servicesList.add(ServicesMonitor.Service.REMOTE_CASE_DATABASE.toString()); + servicesList.add(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH.toString()); + this.servicesMonitor.addSubscriber(servicesList, propChangeListener); + } synchronized void handleCaseOpened() { this.jobCreationIsEnabled = true; @@ -361,6 +422,7 @@ public class IngestManager { * The ingest manager can be directed to forgo use of message boxes, the * ingest message box, NetBeans progress handles, etc. Running interactively * is the default. + * @return true if running interactively, false otherwise. */ public boolean isRunningInteractively() { return this.runInteractively; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java index c047e67009..2f492f46a2 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java @@ -53,9 +53,13 @@ public class SolrSearchService implements KeywordSearchService { if (artifactId > 0) return; - Case currentCase = Case.getCurrentCase(); - if (currentCase == null) + Case currentCase; + try { + currentCase = Case.getCurrentCase(); + } catch (IllegalStateException ignore) { + // thorown by Case.getCurrentCase() if currentCase is null return; + } SleuthkitCase sleuthkitCase = currentCase.getSleuthkitCase(); if (sleuthkitCase == null) @@ -153,6 +157,9 @@ public class SolrSearchService implements KeywordSearchService { try { String host = UserPreferences.getIndexingServerHost(); String port = UserPreferences.getIndexingServerPort(); + if (host.isEmpty() || port.isEmpty()){ + return false; + } HttpSolrServer solrServer = new HttpSolrServer("http://" + host + ":" + port + "/solr"); //NON-NLS; KeywordSearch.getServer().connectToSolrServer(solrServer); } diff --git a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties index 54a8353a13..84e42eab37 100644 --- a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties +++ b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Thu, 25 Jun 2015 13:09:21 -0400 +#Thu, 23 Jul 2015 09:29:40 -0400 LBL_splash_window_title=Starting Autopsy SPLASH_HEIGHT=314 SPLASH_WIDTH=538 @@ -8,4 +8,4 @@ SplashRunningTextBounds=0,289,538,18 SplashRunningTextColor=0x0 SplashRunningTextFontSize=19 -currentVersion=Autopsy 3.1.2 +currentVersion=Autopsy 3.1.3 diff --git a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties index 3b8f688ddc..122b976d9e 100644 --- a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties +++ b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Thu, 25 Jun 2015 13:09:21 -0400 +#Thu, 23 Jul 2015 09:29:40 -0400 -CTL_MainWindow_Title=Autopsy 3.1.2 -CTL_MainWindow_Title_No_Project=Autopsy 3.1.2 +CTL_MainWindow_Title=Autopsy 3.1.3 +CTL_MainWindow_Title_No_Project=Autopsy 3.1.3