diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 19f4e373bc..a4e477fada 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -86,12 +86,13 @@ import org.sleuthkit.datamodel.TskException; * open at a time. Use getCurrentCase() to retrieve the object for the current * case. */ -public class Case { +public class Case implements SleuthkitCase.ErrorObserver { private static final String autopsyVer = Version.getVersion(); // current version of autopsy. Change it when the version is changed private static final String EVENT_CHANNEL_NAME = "%s-Case-Events"; private static String appName = null; - private static IntervalErrorReportData tskErrorReporter = null; + volatile private IntervalErrorReportData tskErrorReporter = null; + private static final int MIN_SECONDS_BETWEEN_ERROR_REPORTS = 60; // No less than 60 seconds between warnings for errors private static final int MAX_SANITIZED_NAME_LENGTH = 47; /** @@ -258,7 +259,6 @@ public class Case { private static final Logger logger = Logger.getLogger(Case.class.getName()); static final String CASE_EXTENSION = "aut"; //NON-NLS static final String CASE_DOT_EXTENSION = "." + CASE_EXTENSION; - private static String HostName; private final static String CACHE_FOLDER = "Cache"; //NON-NLS private final static String EXPORT_FOLDER = "Export"; //NON-NLS private final static String LOG_FOLDER = "Log"; //NON-NLS @@ -317,13 +317,15 @@ public class Case { * */ private static void changeCase(Case newCase) { - // force static initialization of error reporter - tskErrorReporter = IntervalErrorReportData.getInstance(); // close the existing case Case oldCase = Case.currentCase; Case.currentCase = null; if (oldCase != null) { - doCaseChange(null); //closes windows, etc + doCaseChange(null); //closes windows, etc + if (null != oldCase.tskErrorReporter) { + oldCase.tskErrorReporter.shutdown(); // stop listening for TSK errors for the old case + oldCase.tskErrorReporter = null; + } eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), oldCase, null)); if (CaseType.MULTI_USER_CASE == oldCase.getCaseType()) { if (null != oldCase.collaborationMonitor) { @@ -336,6 +338,13 @@ public class Case { if (newCase != null) { currentCase = newCase; Logger.setLogDirectory(currentCase.getLogDirectoryPath()); + // sanity check + if (null != currentCase.tskErrorReporter) { + currentCase.tskErrorReporter.shutdown(); + } + // start listening for TSK errors for the new case + currentCase.tskErrorReporter = new IntervalErrorReportData(currentCase, MIN_SECONDS_BETWEEN_ERROR_REPORTS, + NbBundle.getMessage(Case.class, "IntervalErrorReport.ErrorText")); doCaseChange(currentCase); SwingUtilities.invokeLater(() -> { RecentCases.getInstance().addRecentCase(currentCase.name, currentCase.configFilePath); // update the recent cases @@ -361,6 +370,17 @@ public class Case { Logger.setLogDirectory(PlatformUtil.getLogDirectory()); } } + + @Override + public void receiveError(String context, String errorMessage) { + /* NOTE: We are accessing tskErrorReporter from two different threads. + * This is ok as long as we only read the value of tskErrorReporter + * because tskErrorReporter is declared as volatile. + */ + if (null != tskErrorReporter) { + tskErrorReporter.addProblems(context, errorMessage); + } + } AddImageProcess makeAddImageProcess(String timezone, boolean processUnallocSpace, boolean noFatOrphans) { return this.db.makeAddImageProcess(timezone, processUnallocSpace, noFatOrphans); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/IntervalErrorReportData.java b/Core/src/org/sleuthkit/autopsy/casemodule/IntervalErrorReportData.java index c45d90ceed..52ea056333 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/IntervalErrorReportData.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/IntervalErrorReportData.java @@ -20,18 +20,17 @@ package org.sleuthkit.autopsy.casemodule; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; -import org.sleuthkit.datamodel.SleuthkitCase; /** * This class enables capturing errors and batching them for reporting on a - * no-more-than-x number of seconds basis. When created, you specify what type - * of error it will be batching, and the minimum time between user - * notifications. When the time between notifications has expired, the next - * error encountered will cause a report to be shown to the user. + * no-more-than-x number of seconds basis. When created, you specify the minimum + * time between user notifications. When the time between notifications has + * expired, the next error encountered will cause a report to be shown to the + * user. */ -class IntervalErrorReportData implements SleuthkitCase.ErrorObserver { +class IntervalErrorReportData { - private static volatile IntervalErrorReportData instance; + private final Case currentCase; private long newProblems; private long totalProblems; private long lastReportedDate; @@ -39,37 +38,31 @@ class IntervalErrorReportData implements SleuthkitCase.ErrorObserver { private final String message; /** - * Create a new IntervalErrorReprotData instance. + * Create a new IntervalErrorReprotData instance and subscribe for TSK error + * notifications for the current case. * + * @param currentCase Case for which TSK errors should be tracked + * and displayed. * @param secondsBetweenReports Minimum number of seconds between reports. * It will not warn more frequently than this. * @param message The message that will be shown when warning * the user */ - private IntervalErrorReportData(int secondsBetweenReports, String message) { + IntervalErrorReportData(Case currentCase, int secondsBetweenReports, String message) { this.newProblems = 0; this.totalProblems = 0; this.lastReportedDate = 0; // arm the first warning by choosing zero this.milliSecondsBetweenReports = secondsBetweenReports * 1000; // convert to milliseconds this.message = message; - SleuthkitCase.addErrorObserver(this); + this.currentCase = currentCase; + this.currentCase.getSleuthkitCase().addErrorObserver(this.currentCase); } - + /** - * Returns the singleton instance of this object - * - * @return the singleton instance of this object + * Un-subscribe from TSK error notifications for current case. */ - public static IntervalErrorReportData getInstance() { - if (instance == null) { - synchronized (IntervalErrorReportData.class) { - if (instance == null) { - instance = new IntervalErrorReportData(60, // No less than 60 seconds between warnings for errors - NbBundle.getMessage(Case.class, "IntervalErrorReport.ErrorText")); - } - } - } - return instance; + void shutdown() { + this.currentCase.getSleuthkitCase().removeErrorObserver(this.currentCase); } /** @@ -77,17 +70,17 @@ class IntervalErrorReportData implements SleuthkitCase.ErrorObserver { * (or if this is the first problem encountered), a warning will be shown to * the user. * - * @param newProblems the newProblems to set - * @param ex the exception for this error + * @param context The context in which the error occurred. + * @param errorMessage A description of the error that occurred. */ - private void addProblems(long newProblems, Exception ex) { - this.newProblems += newProblems; + void addProblems(String context, String errorMessage) { + this.newProblems += 1; this.totalProblems += newProblems; long currentTimeStamp = System.currentTimeMillis(); if ((currentTimeStamp - lastReportedDate) > milliSecondsBetweenReports) { this.lastReportedDate = currentTimeStamp; - MessageNotifyUtil.Notify.error(message, ex.getMessage() + " " + MessageNotifyUtil.Notify.error(message, context + ", " + errorMessage + " " + this.newProblems + " " + NbBundle.getMessage(IntervalErrorReportData.class, "IntervalErrorReport.NewIssues") + " " + this.totalProblems + " " @@ -96,9 +89,4 @@ class IntervalErrorReportData implements SleuthkitCase.ErrorObserver { this.newProblems = 0; } } - - @Override - public void receiveError(Exception ex) { - addProblems(1, ex); - } } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties b/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties index 3fa835fdfd..16be66440e 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties @@ -107,3 +107,4 @@ IngestJobSettingsPanel.processUnallocCheckbox.text=Process Unallocated Space IngestManager.cancellingIngest.msgDlg.text=Cancelling all currently running ingest jobs IngestManager.serviceIsDown.msgDlg.text={0} is down RunIngestSubMenu.menuItem.empty=-Empty- +RunIngestModulesMenu.getName.text=Run Ingest Modules diff --git a/Core/src/org/sleuthkit/autopsy/ingest/RunIngestAction.java b/Core/src/org/sleuthkit/autopsy/ingest/RunIngestAction.java index 0bcd23b8b9..151548a9b8 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/RunIngestAction.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/RunIngestAction.java @@ -34,17 +34,20 @@ import org.openide.util.actions.Presenter; id = "org.sleuthkit.autopsy.ingest.RunIngestAction" ) @ActionRegistration( - displayName = "#CTL_RunIngestAction" + displayName = "#CTL_RunIngestAction", + lazy = false ) @Messages("CTL_RunIngestAction=Run Ingest") public final class RunIngestAction extends CallableSystemAction implements Presenter.Menu, ActionListener { + private static final long serialVersionUID = 1L; + static public RunIngestAction getInstance() { return new RunIngestAction(); } /** - * Call getMenuPresenters to create images sublist + * @inheritDoc */ @Override public void performAction() { @@ -52,9 +55,7 @@ public final class RunIngestAction extends CallableSystemAction implements Prese } /** - * Gets the name of this action. This may be presented as an item in a menu. - * - * @return actionName + * @inheritDoc */ @Override public String getName() { @@ -62,20 +63,15 @@ public final class RunIngestAction extends CallableSystemAction implements Prese } /** - * Gets the HelpCtx associated with implementing object - * - * @return HelpCtx or HelpCtx.DEFAULT_HELP + * @inheritDoc */ @Override public HelpCtx getHelpCtx() { return HelpCtx.DEFAULT_HELP; } - + /** - * Create a sublist of images updated by RunIngestSubMenu - * Each has an action to perform Ingest Modules on it. - * - * @return the images sublist created. + * @inheritDoc */ @Override public JMenuItem getMenuPresenter() { @@ -83,11 +79,12 @@ public final class RunIngestAction extends CallableSystemAction implements Prese sublist.setVisible(true); return sublist; } - + /** - * This method does nothing, use performAction instead. + * @inheritDoc */ @Override public void actionPerformed(ActionEvent e) { + performAction(); } } 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 ce158ac62b..a41dd17420 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 -#Mon, 05 Oct 2015 17:56:58 -0400 +#Tue, 13 Oct 2015 17:00:33 -0400 LBL_splash_window_title=Starting Autopsy SPLASH_HEIGHT=314 SPLASH_WIDTH=538 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 65ddfc6e3b..5dd3fa12e1 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,4 +1,4 @@ #Updated by build script -#Mon, 05 Oct 2015 17:56:58 -0400 +#Tue, 13 Oct 2015 17:00:33 -0400 CTL_MainWindow_Title=Autopsy 3.1.3 CTL_MainWindow_Title_No_Project=Autopsy 3.1.3