Add local/remote event support for Case events

This commit is contained in:
Richard Cordovano 2015-04-28 13:58:08 -04:00
parent c1a505b9f1
commit 23344ce98b
8 changed files with 211 additions and 226 deletions

View File

@ -45,6 +45,7 @@ import org.openide.util.NbBundle;
import org.openide.util.actions.CallableSystemAction; import org.openide.util.actions.CallableSystemAction;
import org.openide.util.actions.SystemAction; import org.openide.util.actions.SystemAction;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent;
import org.sleuthkit.autopsy.casemodule.services.Services; import org.sleuthkit.autopsy.casemodule.services.Services;
import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.corecomponentinterfaces.CoreComponentControl; import org.sleuthkit.autopsy.corecomponentinterfaces.CoreComponentControl;
@ -53,6 +54,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.coreutils.Version; import org.sleuthkit.autopsy.coreutils.Version;
import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.autopsy.events.AutopsyEventException; import org.sleuthkit.autopsy.events.AutopsyEventException;
import org.sleuthkit.autopsy.events.AutopsyEventPublisher; import org.sleuthkit.autopsy.events.AutopsyEventPublisher;
import org.sleuthkit.datamodel.*; import org.sleuthkit.datamodel.*;
@ -75,11 +77,6 @@ public class Case implements SleuthkitCase.ErrorObserver {
*/ */
public static final String propStartup = "LBL_StartupDialog"; //NON-NLS public static final String propStartup = "LBL_StartupDialog"; //NON-NLS
// RJCTODO: Replace PropertyChangeSupport with AutopsyEventPublisher.
private static final PropertyChangeSupport pcs = new PropertyChangeSupport(Case.class);
private static final Set<String> eventNames = Stream.of(Events.values())
.map(Events::toString)
.collect(Collectors.toSet());
private static final AutopsyEventPublisher eventPublisher = new AutopsyEventPublisher(); private static final AutopsyEventPublisher eventPublisher = new AutopsyEventPublisher();
/** /**
@ -90,12 +87,9 @@ public class Case implements SleuthkitCase.ErrorObserver {
/** /**
* Property name that indicates the name of the current case has * Property name that indicates the name of the current case has
* changed. When a case is opened, "old name" is empty string and "new * changed. The old value is the old case name, the new value is the new
* name" is the name. When a case is closed, "old name" is the case name * case name.
* and "new name" is empty string. When a case is renamed, "old name"
* has the original name and "new name" has the new name.
*/ */
// @@@ BC: I propose that this is no longer called for case open/close.
NAME, NAME,
/** /**
* Property name that indicates the number of the current case has * Property name that indicates the number of the current case has
@ -205,16 +199,6 @@ public class Case implements SleuthkitCase.ErrorObserver {
this.caseType = type; this.caseType = type;
this.db = db; this.db = db;
this.services = new Services(db); this.services = new Services(db);
if (CaseType.MULTI_USER_CASE == this.caseType) {
try {
eventPublisher.openRemoteEventChannel(name);
} catch (AutopsyEventException ex) {
logger.log(Level.SEVERE, "Failed to start remote event publisher", ex);
MessageNotifyUtil.Message.error(NbBundle.getMessage(Case.class,
"Case.OpenEventChannel.ErrMsg",
name));
}
}
} }
/** /**
@ -261,58 +245,30 @@ public class Case implements SleuthkitCase.ErrorObserver {
Case oldCase = Case.currentCase; Case oldCase = Case.currentCase;
Case.currentCase = null; Case.currentCase = null;
if (oldCase != null) { if (oldCase != null) {
doCaseChange(null); //closes windows, etc doCaseChange(null); //closes windows, etc
eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), oldCase, null));
try { if (CaseType.MULTI_USER_CASE == oldCase.getCaseType()) {
pcs.firePropertyChange(Events.CURRENT_CASE.toString(), oldCase, null); eventPublisher.closeRemoteEventChannel();
} catch (Exception e) { }
logger.log(Level.SEVERE, "Case listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(NbBundle.getMessage(Case.class, "Case.moduleErr"),
NbBundle.getMessage(Case.class,
"Case.changeCase.errListenToCaseUpdates.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
doCaseNameChange("");
try {
pcs.firePropertyChange(Events.NAME.toString(), oldCase.name, "");
} catch (Exception e) {
logger.log(Level.SEVERE, "Case listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(NbBundle.getMessage(Case.class, "Case.moduleErr"),
NbBundle.getMessage(Case.class,
"Case.changeCase.errListenToCaseUpdates.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
} }
if (newCase != null) { if (newCase != null) {
currentCase = newCase; currentCase = newCase;
Logger.setLogDirectory(currentCase.getLogDirectoryPath()); Logger.setLogDirectory(currentCase.getLogDirectoryPath());
try {
pcs.firePropertyChange(Events.CURRENT_CASE.toString(), null, currentCase);
} catch (Exception e) {
logger.log(Level.SEVERE, "Case listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(NbBundle.getMessage(Case.class, "Case.moduleErr"),
NbBundle.getMessage(Case.class,
"Case.changeCase.errListenToCaseUpdates.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
doCaseChange(currentCase); doCaseChange(currentCase);
updateMainWindowTitle(currentCase.name);
try {
pcs.firePropertyChange(Events.NAME.toString(), "", currentCase.name);
} catch (Exception e) {
logger.log(Level.SEVERE, "Case threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(NbBundle.getMessage(Case.class, "Case.moduleErr"),
NbBundle.getMessage(Case.class,
"Case.changeCase.errListenToCaseUpdates.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
doCaseNameChange(currentCase.name);
RecentCases.getInstance().addRecentCase(currentCase.name, currentCase.configFilePath); // update the recent cases RecentCases.getInstance().addRecentCase(currentCase.name, currentCase.configFilePath); // update the recent cases
if (CaseType.MULTI_USER_CASE == newCase.getCaseType()) {
try {
eventPublisher.openRemoteEventChannel(newCase.getName());
} catch (AutopsyEventException ex) {
logger.log(Level.SEVERE, "Failed to start remote event publisher", ex);
MessageNotifyUtil.Message.error(NbBundle.getMessage(Case.class,
"Case.OpenEventChannel.ErrMsg",
newCase.getName()));
}
}
eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), null, currentCase));
} else { } else {
Logger.setLogDirectory(PlatformUtil.getLogDirectory()); Logger.setLogDirectory(PlatformUtil.getLogDirectory());
} }
@ -525,25 +481,14 @@ public class Case implements SleuthkitCase.ErrorObserver {
* @param imgPaths the paths of the image that being added * @param imgPaths the paths of the image that being added
* @param imgId the ID of the image that being added * @param imgId the ID of the image that being added
* @param timeZone the timeZone of the image where it's added * @param timeZone the timeZone of the image where it's added
* @deprecated Use notifyNewDataSource() instead.
*/ */
@Deprecated @Deprecated
public Image addImage(String imgPath, long imgId, String timeZone) throws CaseActionException { public Image addImage(String imgPath, long imgId, String timeZone) throws CaseActionException {
logger.log(Level.INFO, "Adding image to Case. imgPath: {0} ID: {1} TimeZone: {2}", new Object[]{imgPath, imgId, timeZone}); //NON-NLS
try { try {
Image newImage = db.getImageById(imgId); Image newDataSource = db.getImageById(imgId);
notifyNewDataSource(newDataSource);
try { return newDataSource;
pcs.firePropertyChange(Events.DATA_SOURCE_ADDED.toString(), null, newImage); // the new value is the instance of the image
} catch (Exception e) {
logger.log(Level.SEVERE, "Case listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "Case.moduleErr"),
NbBundle.getMessage(this.getClass(),
"Case.changeCase.errListenToCaseUpdates.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
CoreComponentControl.openCoreWindows();
return newImage;
} catch (Exception ex) { } catch (Exception ex) {
throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.addImg.exception.msg"), ex); throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.addImg.exception.msg"), ex);
} }
@ -554,6 +499,7 @@ public class Case implements SleuthkitCase.ErrorObserver {
* reopens windows if needed. * reopens windows if needed.
* *
* @param newDataSource new data source added * @param newDataSource new data source added
* @deprecated Use notifyNewDataSource() instead.
*/ */
@Deprecated @Deprecated
void addLocalDataSource(Content newDataSource) { void addLocalDataSource(Content newDataSource) {
@ -563,20 +509,10 @@ public class Case implements SleuthkitCase.ErrorObserver {
/** /**
* Notifies the UI that a new data source has been added. * Notifies the UI that a new data source has been added.
* *
*
* @param newDataSource new data source added * @param newDataSource new data source added
*/ */
void notifyNewDataSource(Content newDataSource) { void notifyNewDataSource(Content newDataSource) {
eventPublisher.publish(new DataSourceAddedEvent(newDataSource));
try {
pcs.firePropertyChange(Events.DATA_SOURCE_ADDED.toString(), null, newDataSource);
} catch (Exception e) {
logger.log(Level.SEVERE, "Case threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "Case.moduleErr"),
NbBundle.getMessage(this.getClass(),
"Case.changeCase.errListenToCaseUpdates.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
CoreComponentControl.openCoreWindows(); CoreComponentControl.openCoreWindows();
} }
@ -603,9 +539,6 @@ public class Case implements SleuthkitCase.ErrorObserver {
public void closeCase() throws CaseActionException { public void closeCase() throws CaseActionException {
changeCase(null); changeCase(null);
try { try {
if (CaseType.MULTI_USER_CASE == this.caseType) {
eventPublisher.closeRemoteEventChannel();
}
services.close(); services.close();
this.xmlcm.close(); // close the xmlcm this.xmlcm.close(); // close the xmlcm
this.db.close(); this.db.close();
@ -655,17 +588,8 @@ public class Case implements SleuthkitCase.ErrorObserver {
xmlcm.setCaseName(newCaseName); // set the case xmlcm.setCaseName(newCaseName); // set the case
name = newCaseName; // change the local value name = newCaseName; // change the local value
RecentCases.getInstance().updateRecentCase(oldCaseName, oldPath, newCaseName, newPath); // update the recent case RecentCases.getInstance().updateRecentCase(oldCaseName, oldPath, newCaseName, newPath); // update the recent case
try { eventPublisher.publish(new AutopsyEvent(Events.NAME.toString(), oldCaseName, newCaseName));
pcs.firePropertyChange(Events.NAME.toString(), oldCaseName, newCaseName); updateMainWindowTitle(newCaseName);
} catch (Exception e) {
logger.log(Level.SEVERE, "Case listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "Case.moduleErr"),
NbBundle.getMessage(this.getClass(),
"Case.changeCase.errListenToCaseUpdates.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
doCaseNameChange(newCaseName);
} catch (Exception e) { } catch (Exception e) {
throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.updateCaseName.exception.msg"), e); throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.updateCaseName.exception.msg"), e);
} }
@ -681,15 +605,7 @@ public class Case implements SleuthkitCase.ErrorObserver {
try { try {
xmlcm.setCaseExaminer(newExaminer); // set the examiner xmlcm.setCaseExaminer(newExaminer); // set the examiner
examiner = newExaminer; examiner = newExaminer;
try { eventPublisher.publish(new AutopsyEvent(Events.EXAMINER.toString(), oldExaminer, newExaminer));
pcs.firePropertyChange(Events.EXAMINER.toString(), oldExaminer, newExaminer);
} catch (Exception e) {
logger.log(Level.SEVERE, "Case listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "Case.moduleErr"),
NbBundle.getMessage(this.getClass(),
"Case.changeCase.errListenToCaseUpdates.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
} catch (Exception e) { } catch (Exception e) {
throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.updateExaminer.exception.msg"), e); throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.updateExaminer.exception.msg"), e);
} }
@ -705,16 +621,7 @@ public class Case implements SleuthkitCase.ErrorObserver {
try { try {
xmlcm.setCaseNumber(newCaseNumber); // set the case number xmlcm.setCaseNumber(newCaseNumber); // set the case number
number = newCaseNumber; number = newCaseNumber;
eventPublisher.publish(new AutopsyEvent(Events.NUMBER.toString(), oldCaseNumber, newCaseNumber));
try {
pcs.firePropertyChange(Events.NUMBER.toString(), oldCaseNumber, newCaseNumber);
} catch (Exception e) {
logger.log(Level.SEVERE, "Case listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "Case.moduleErr"),
NbBundle.getMessage(this.getClass(),
"Case.changeCase.errListenToCaseUpdates.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
} catch (Exception e) { } catch (Exception e) {
throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.updateCaseNum.exception.msg"), e); throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.updateCaseNum.exception.msg"), e);
} }
@ -917,12 +824,14 @@ public class Case implements SleuthkitCase.ErrorObserver {
} }
/** /**
* get the PropertyChangeSupport of this class * Gets a new PropertyChangeSupport object.
* *
* @return PropertyChangeSupport * @return A new PropertyChangeSupport object with source set to null.
* @deprecated Do not use.
*/ */
@Deprecated
public static PropertyChangeSupport getPropertyChangeSupport() { public static PropertyChangeSupport getPropertyChangeSupport() {
return pcs; return new PropertyChangeSupport(null);
} }
/** /**
@ -958,20 +867,35 @@ public class Case implements SleuthkitCase.ErrorObserver {
return timezones; return timezones;
} }
// RJCTODO: Deprecate in favor of addEventSubscriber() and remove use of PropertyChangeSupport /**
* Adds a subscriber to all case events from this Autopsy node and other
* Autopsy nodes. To subscribe to only specific events, use one of the
* overloads of addEventSubscriber().
*
* @param listener The subscriber to add.
*/
public static synchronized void addPropertyChangeListener(PropertyChangeListener listener) { public static synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener); addEventSubscriber(Stream.of(Events.values())
// eventPublisher.addSubscriber(eventNames, listener); .map(Events::toString)
} .collect(Collectors.toSet()), listener);
// RJCTODO: Deprecate in favor of removeEventSubscriber() and remove use of PropertyChangeSupport
public static synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
// eventPublisher.removeSubscriber(eventNames, listener);
} }
/** /**
* Adds a subscriber to events from this Autopsy node and other Autopsy nodes. * Removes a subscriber from all case events from this Autopsy node and
* other Autopsy nodes. To remove a subscription to only specific events,
* use one of the overloads of removeEventSubscriber().
*
* @param listener The subscriber to add.
*/
public static synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
removeEventSubscriber(Stream.of(Events.values())
.map(Events::toString)
.collect(Collectors.toSet()), listener);
}
/**
* Adds a subscriber to events from this Autopsy node and other Autopsy
* nodes.
* *
* @param eventNames The events the subscriber is interested in. * @param eventNames The events the subscriber is interested in.
* @param subscriber The subscriber to add. * @param subscriber The subscriber to add.
@ -981,7 +905,8 @@ public class Case implements SleuthkitCase.ErrorObserver {
} }
/** /**
* Adds a subscriber to events from this Autopsy node and other Autopsy nodes. * Adds a subscriber to events from this Autopsy node and other Autopsy
* nodes.
* *
* @param eventNames The event the subscriber is interested in. * @param eventNames The event the subscriber is interested in.
* @param subscriber The subscriber to add. * @param subscriber The subscriber to add.
@ -991,7 +916,8 @@ public class Case implements SleuthkitCase.ErrorObserver {
} }
/** /**
* Adds a subscriber to events from this Autopsy node and other Autopsy nodes. * Adds a subscriber to events from this Autopsy node and other Autopsy
* nodes.
* *
* @param eventName 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 add. * @param subscriber The subscriber to add.
@ -1001,7 +927,8 @@ public class Case implements SleuthkitCase.ErrorObserver {
} }
/** /**
* Removes a subscriber to events from this Autopsy node and other Autopsy nodes. * Removes a subscriber to events from this Autopsy node and other Autopsy
* nodes.
* *
* @param eventNames The event the subscriber is no longer interested in. * @param eventNames The event the subscriber is no longer interested in.
* @param subscriber The subscriber to add. * @param subscriber The subscriber to add.
@ -1301,7 +1228,7 @@ public class Case implements SleuthkitCase.ErrorObserver {
} }
//case name change helper //case name change helper
private static void doCaseNameChange(String newCaseName) { private static void updateMainWindowTitle(String newCaseName) {
// update case name // update case name
if (!newCaseName.equals("")) { if (!newCaseName.equals("")) {
Frame f = WindowManager.getDefault().getMainWindow(); Frame f = WindowManager.getDefault().getMainWindow();
@ -1309,15 +1236,6 @@ public class Case implements SleuthkitCase.ErrorObserver {
} }
} }
//delete image helper
private void doDeleteImage() {
// no more image left in this case
if (currentCase.hasData()) {
// close all top components
CoreComponentControl.closeCoreWindows();
}
}
@Override @Override
public void receiveError(String context, String errorMessage) { public void receiveError(String context, String errorMessage) {
MessageNotifyUtil.Notify.error(context, errorMessage); MessageNotifyUtil.Notify.error(context, errorMessage);
@ -1336,12 +1254,7 @@ public class Case implements SleuthkitCase.ErrorObserver {
*/ */
public void addReport(String localPath, String srcModuleName, String reportName) throws TskCoreException { public void addReport(String localPath, String srcModuleName, String reportName) throws TskCoreException {
Report report = this.db.addReport(localPath, srcModuleName, reportName); Report report = this.db.addReport(localPath, srcModuleName, reportName);
try { eventPublisher.publish(new AutopsyEvent(Events.REPORT_ADDED.toString(), null, report));
Case.pcs.firePropertyChange(Events.REPORT_ADDED.toString(), null, report);
} catch (Exception ex) {
String errorMessage = String.format("A Case %s listener threw an exception", Events.REPORT_ADDED.toString()); //NON-NLS
logger.log(Level.SEVERE, errorMessage, ex);
}
} }
public List<Report> getAllReports() throws TskCoreException { public List<Report> getAllReports() throws TskCoreException {

View File

@ -0,0 +1,82 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.casemodule.events;
import java.io.Serializable;
import java.util.logging.Level;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Event published when a data source is added to a case.
*/
public final class DataSourceAddedEvent extends AutopsyEvent implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(DataSourceAddedEvent.class.getName());
private transient Content dataSource;
/**
* Constructs an event published when a data source is added to a case.
*
* @param dataSource The data source that was added.
*/
public DataSourceAddedEvent(Content dataSource) {
/**
* Putting the object id of the data source into newValue to allow for
* lazy loading of the Content object. This bypasses the issues related
* to the serialization and de-serialization of Content objects when the
* event is published over a network.
*/
super(Case.Events.DATA_SOURCE_ADDED.toString(), null, dataSource.getId());
this.dataSource = dataSource;
}
/**
* Gets the data source that was added.
*
* @return The data source.
*/
@Override
public Object getNewValue() {
/**
* The dataSource field is set in the constructor, but it is transient
* so it will become null when the event is serialized for publication
* over a network. Doing a lazy load of the Content object bypasses the
* issues related to the serialization and de-serialization of Content
* objects and may also save database round trips from other nodes since
* subscribers to this event are often not interested in the event data.
*/
if (null != dataSource) {
return dataSource;
}
try {
long id = (Long) super.getOldValue();
dataSource = Case.getCurrentCase().getSleuthkitCase().getContentById(id);
return dataSource;
} catch (IllegalStateException | TskCoreException ex) {
logger.log(Level.SEVERE, "Error doing lazy load for remote event", ex);
return null;
}
}
}

View File

@ -27,6 +27,7 @@ import java.beans.PropertyVetoException;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
@ -117,7 +118,6 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
subscribeToChangeEvents(); subscribeToChangeEvents();
associateLookup(ExplorerUtils.createLookup(em, getActionMap())); associateLookup(ExplorerUtils.createLookup(em, getActionMap()));
this.pcs = new PropertyChangeSupport(this); this.pcs = new PropertyChangeSupport(this);
// set the back & forward list and also disable the back & forward button // set the back & forward list and also disable the back & forward button
@ -144,7 +144,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
} }
} }
}); });
Case.addPropertyChangeListener(this); Case.addEventSubscriber(new HashSet<>(Arrays.asList(Case.Events.CURRENT_CASE.toString(), Case.Events.DATA_SOURCE_ADDED.toString())), this);
this.em.addPropertyChangeListener(this); this.em.addPropertyChangeListener(this);
IngestManager.getInstance().addIngestJobEventListener(this); IngestManager.getInstance().addIngestJobEventListener(this);
IngestManager.getInstance().addIngestModuleEventListener(this); IngestManager.getInstance().addIngestModuleEventListener(this);
@ -245,9 +245,11 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
forwardList.addLast(currentNodePath); forwardList.addLast(currentNodePath);
forwardButton.setEnabled(true); forwardButton.setEnabled(true);
/* We peek instead of poll because we use its existence /*
* in the list later on so that we do not reset the forward list * We peek instead of poll because we use its existence in the list
* after the selection occurs. */ * later on so that we do not reset the forward list after the selection
* occurs.
*/
String[] newCurrentNodePath = backList.peekLast(); String[] newCurrentNodePath = backList.peekLast();
// enable / disable the back and forward button // enable / disable the back and forward button
@ -388,7 +390,6 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
root = new DirectoryTreeFilterNode(root, true); root = new DirectoryTreeFilterNode(root, true);
em.setRootContext(root); em.setRootContext(root);
em.getRootContext().setName(currentCase.getName()); em.getRootContext().setName(currentCase.getName());
em.getRootContext().setDisplayName(currentCase.getName()); em.getRootContext().setDisplayName(currentCase.getName());
@ -407,7 +408,6 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
tree.expandNode(resultsChilds.findChild(KeywordHits.NAME)); tree.expandNode(resultsChilds.findChild(KeywordHits.NAME));
tree.expandNode(resultsChilds.findChild(ExtractedContent.NAME)); tree.expandNode(resultsChilds.findChild(ExtractedContent.NAME));
Node views = childNodes.findChild(ViewsNode.NAME); Node views = childNodes.findChild(ViewsNode.NAME);
Children viewsChilds = views.getChildren(); Children viewsChilds = views.getChildren();
for (Node n : viewsChilds.getNodes()) { for (Node n : viewsChilds.getNodes()) {
@ -421,7 +421,6 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
dataResult.open(); // open the data result top component as well when the directory tree is opened dataResult.open(); // open the data result top component as well when the directory tree is opened
} }
// select the first image node, if there is one // select the first image node, if there is one
// (this has to happen after dataResult is opened, because the event // (this has to happen after dataResult is opened, because the event
// of changing the selected node fires a handler that tries to make // of changing the selected node fires a handler that tries to make
@ -534,22 +533,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
String changed = evt.getPropertyName(); String changed = evt.getPropertyName();
Object oldValue = evt.getOldValue(); Object oldValue = evt.getOldValue();
Object newValue = evt.getNewValue(); Object newValue = evt.getNewValue();
if (changed.equals(Case.Events.CURRENT_CASE.toString())) { // changed current case
// change in the case name
if (changed.equals(Case.Events.NAME.toString())) {
// set the main title of the window
String oldCaseName = oldValue.toString();
String newCaseName = newValue.toString();
// update the case name
if ((!oldCaseName.equals("")) && (!newCaseName.equals(""))) {
// change the root name and display name
em.getRootContext().setName(newCaseName);
em.getRootContext().setDisplayName(newCaseName);
}
} // changed current case
else if (changed.equals(Case.Events.CURRENT_CASE.toString())) {
// When a case is closed, the old value of this property is the // When a case is closed, the old value of this property is the
// closed Case object and the new value is null. When a case is // closed Case object and the new value is null. When a case is
// opened, the old value is null and the new value is the new Case // opened, the old value is null and the new value is the new Case
@ -563,7 +547,13 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
Node emptyNode = new AbstractNode(Children.LEAF); Node emptyNode = new AbstractNode(Children.LEAF);
em.setRootContext(emptyNode); em.setRootContext(emptyNode);
} else if (newValue != null) { } else if (newValue != null) {
// A new case has been opened. Reset the forward and back // A new case has been opened. Reset the ExplorerManager.
Case newCase = (Case) newValue;
String newCaseName = newCase.getName();
em.getRootContext().setName(newCaseName);
em.getRootContext().setDisplayName(newCaseName);
// Reset the forward and back
// buttons. Note that a call to CoreComponentControl.openCoreWindows() // buttons. Note that a call to CoreComponentControl.openCoreWindows()
// by the new Case object will lead to a componentOpened() call // by the new Case object will lead to a componentOpened() call
// that will repopulate the tree. // that will repopulate the tree.
@ -574,12 +564,10 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
} // if the image is added to the case } // if the image is added to the case
else if (changed.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { else if (changed.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
componentOpened(); componentOpened();
} } // change in node selection
// change in node selection
else if (changed.equals(ExplorerManager.PROP_SELECTED_NODES)) { else if (changed.equals(ExplorerManager.PROP_SELECTED_NODES)) {
respondSelection((Node[]) oldValue, (Node[]) newValue); respondSelection((Node[]) oldValue, (Node[]) newValue);
} } else if (changed.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
else if (changed.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
// nothing to do here. // nothing to do here.
// all nodes should be listening for these events and update accordingly. // all nodes should be listening for these events and update accordingly.
} else if (changed.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) } else if (changed.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
@ -615,7 +603,6 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
return; return;
} }
// Some lock that prevents certain Node operations is set during the // Some lock that prevents certain Node operations is set during the
// ExplorerManager selection-change, so we must handle changes after the // ExplorerManager selection-change, so we must handle changes after the
// selection-change event is processed. // selection-change event is processed.
@ -630,7 +617,6 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
// make sure dataResult is open, redundant? // make sure dataResult is open, redundant?
//dataResult.open(); //dataResult.open();
Node treeNode = DirectoryTreeTopComponent.this.getSelectedNode(); Node treeNode = DirectoryTreeTopComponent.this.getSelectedNode();
if (treeNode != null) { if (treeNode != null) {
DirectoryTreeFilterNode.OriginalNode origin = treeNode.getLookup().lookup(DirectoryTreeFilterNode.OriginalNode.class); DirectoryTreeFilterNode.OriginalNode origin = treeNode.getLookup().lookup(DirectoryTreeFilterNode.OriginalNode.class);
@ -687,10 +673,11 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
Node selectedNode = selectedNodes[0]; Node selectedNode = selectedNodes[0];
String selectedNodeName = selectedNode.getName(); String selectedNodeName = selectedNode.getName();
/* get the previous entry to make sure we don't duplicate it. /*
* Motivation for this is also that if we used the back button, * get the previous entry to make sure we don't duplicate it. Motivation
* then we already added the 'current' node to 'back' and we will * for this is also that if we used the back button, then we already
* detect that and not reset the forward list. * added the 'current' node to 'back' and we will detect that and not
* reset the forward list.
*/ */
String[] currentLast = backList.peekLast(); String[] currentLast = backList.peekLast();
String lastNodeName = null; String lastNodeName = null;
@ -783,7 +770,6 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
//final TreeView tree = getTree(); //final TreeView tree = getTree();
//tree.expandNode(imagesNode); //tree.expandNode(imagesNode);
setSelectedNode(selectedPath, DataSourcesNode.NAME); setSelectedNode(selectedPath, DataSourcesNode.NAME);
} }
@ -838,7 +824,6 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
// //@@@ setSelectedNode(selectedPath, ResultsNode.NAME); // //@@@ setSelectedNode(selectedPath, ResultsNode.NAME);
// //
// } // }
/** /**
* Set the selected node using a path to a previously selected node. * Set the selected node using a path to a previously selected node.
* *
@ -975,11 +960,11 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
} }
treeNode = extractedChilds.findChild(type.getLabel()); treeNode = extractedChilds.findChild(type.getLabel());
} }
if (treeNode == null) { if (treeNode == null) {
return; return;
} }
try { try {
em.setExploredContextAndSelection(treeNode, new Node[]{treeNode}); em.setExploredContextAndSelection(treeNode, new Node[]{treeNode});
} catch (PropertyVetoException ex) { } catch (PropertyVetoException ex) {
@ -1025,7 +1010,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
logger.log(Level.SEVERE, "DirectoryTreeTopComponent listener threw exception", e); //NON-NLS logger.log(Level.SEVERE, "DirectoryTreeTopComponent listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "DirectoryTreeTopComponent.moduleErr"), MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "DirectoryTreeTopComponent.moduleErr"),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"DirectoryTreeTopComponent.moduleErr.msg"), "DirectoryTreeTopComponent.moduleErr.msg"),
MessageNotifyUtil.MessageType.ERROR); MessageNotifyUtil.MessageType.ERROR);
} }
} }

View File

@ -125,18 +125,28 @@ public final class AutopsyEventPublisher {
} }
/** /**
* Publishes an event. * Publishes an event to this Autopsy node and other Autopsy nodes.
* *
* @param event The event to publish. * @param event The event to publish.
* @throws JMSException
*/ */
synchronized public void publish(AutopsyEvent event) throws JMSException { synchronized public void publish(AutopsyEvent event) {
event.setSourceType(AutopsyEvent.SourceType.LOCAL); publishLocally(event);
localPublisher.publish(event);
if (null != remotePublisher) { if (null != remotePublisher) {
event.setSourceType(AutopsyEvent.SourceType.REMOTE); try {
remotePublisher.send(event); remotePublisher.publish(event);
} catch (JMSException ex) {
logger.log(Level.SEVERE, String.format("Failed to publish %s event remotely", event.getPropertyName()), ex); //NON-NLS
}
} }
} }
/**
* Publishes an event to this Autopsy node only.
*
* @param event The event to publish.
*/
synchronized public void publishLocally(AutopsyEvent event) {
localPublisher.publish(event);
}
} }

View File

@ -74,7 +74,7 @@ final class RemoteEventPublisher {
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic(eventChannelName); Topic topic = session.createTopic(eventChannelName);
producer = session.createProducer(topic); producer = session.createProducer(topic);
MessageConsumer consumer = session.createConsumer(topic, "events = '" + ALL_MESSAGE_SELECTOR + "'", true); MessageConsumer consumer = session.createConsumer(topic, "events = '" + ALL_MESSAGE_SELECTOR + "'", true); // RJCTODO: Can I use the empty string?
receiver = new MessageReceiver(); receiver = new MessageReceiver();
consumer.setMessageListener(receiver); consumer.setMessageListener(receiver);
} catch (URISyntaxException | JMSException ex) { } catch (URISyntaxException | JMSException ex) {
@ -102,9 +102,9 @@ final class RemoteEventPublisher {
/** /**
* Sends an event message to the message service. * Sends an event message to the message service.
* *
* @param event The event to send. * @param event The event to publish.
*/ */
synchronized void send(AutopsyEvent event) throws JMSException { synchronized void publish(AutopsyEvent event) throws JMSException {
ObjectMessage message = session.createObjectMessage(); ObjectMessage message = session.createObjectMessage();
message.setStringProperty("events", ALL_MESSAGE_SELECTOR); message.setStringProperty("events", ALL_MESSAGE_SELECTOR);
message.setObject(event); message.setObject(event);
@ -131,6 +131,7 @@ final class RemoteEventPublisher {
Object object = objectMessage.getObject(); Object object = objectMessage.getObject();
if (object instanceof AutopsyEvent) { if (object instanceof AutopsyEvent) {
AutopsyEvent event = (AutopsyEvent) object; AutopsyEvent event = (AutopsyEvent) object;
event.setSourceType(AutopsyEvent.SourceType.REMOTE);
localPublisher.publish(event); localPublisher.publish(event);
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011 Basis Technology Corp. * Copyright 2011-2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -160,7 +160,7 @@ class DateSearchFilter extends AbstractFileSearchFilter<DateSearchPanel> {
private static List<String> createTimeZoneList() { private static List<String> createTimeZoneList() {
List<String> timeZones = new ArrayList<String>(); List<String> timeZones = new ArrayList<>();
if (Case.existsCurrentCase()) { if (Case.existsCurrentCase()) {
// get the latest case // get the latest case
@ -241,7 +241,7 @@ class DateSearchFilter extends AbstractFileSearchFilter<DateSearchPanel> {
Object oldValue = evt.getOldValue(); Object oldValue = evt.getOldValue();
Object newValue = evt.getNewValue(); Object newValue = evt.getNewValue();
if (changed.equals(Case.Events.CURRENT_CASE.toString().toString())) { if (changed.equals(Case.Events.CURRENT_CASE.toString())) {
// create or open a case // create or open a case
if (newValue != null) { if (newValue != null) {
DateSearchFilter.this.updateTimeZoneList(); DateSearchFilter.this.updateTimeZoneList();

View File

@ -38,7 +38,6 @@ import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.jms.JMSException;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory; import org.netbeans.api.progress.ProgressHandleFactory;
@ -283,15 +282,13 @@ public class IngestManager {
* Subscribes this ingest manager to local and remote case-related events. * Subscribes this ingest manager to local and remote case-related events.
*/ */
private void subscribeToCaseEvents() { private void subscribeToCaseEvents() {
Case.addPropertyChangeListener(new PropertyChangeListener() { Case.addEventSubscriber(Case.Events.CURRENT_CASE.toString(), new PropertyChangeListener() {
@Override @Override
public void propertyChange(PropertyChangeEvent event) { public void propertyChange(PropertyChangeEvent event) {
if (event.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) { if (event.getNewValue() != null) {
if (event.getNewValue() != null) { handleCaseOpened();
handleCaseOpened(); } else {
} else { handleCaseClosed();
handleCaseClosed();
}
} }
} }
}); });
@ -309,16 +306,16 @@ public class IngestManager {
} catch (AutopsyEventException ex) { } catch (AutopsyEventException ex) {
logger.log(Level.SEVERE, "Failed to open remote job events channel", ex); //NON-NLS logger.log(Level.SEVERE, "Failed to open remote job events channel", ex); //NON-NLS
MessageNotifyUtil.Message.error(NbBundle.getMessage(IngestManager.class, MessageNotifyUtil.Message.error(NbBundle.getMessage(IngestManager.class,
"IngestManager.OpenEventChannel.ErrMsg", "IngestManager.OpenEventChannel.ErrMsg",
caseName)); caseName));
} }
try { try {
moduleEventPublisher.openRemoteEventChannel(String.format(MODULE_EVENT_CHANNEL_NAME, caseName)); moduleEventPublisher.openRemoteEventChannel(String.format(MODULE_EVENT_CHANNEL_NAME, caseName));
} catch (AutopsyEventException ex) { } catch (AutopsyEventException ex) {
logger.log(Level.SEVERE, "Failed to open remote module events channel", ex); //NON-NLS logger.log(Level.SEVERE, "Failed to open remote module events channel", ex); //NON-NLS
MessageNotifyUtil.Message.error(NbBundle.getMessage(IngestManager.class, MessageNotifyUtil.Message.error(NbBundle.getMessage(IngestManager.class,
"IngestManager.OpenEventChannel.ErrMsg", "IngestManager.OpenEventChannel.ErrMsg",
caseName)); caseName));
} }
} }
} catch (IllegalStateException ex) { } catch (IllegalStateException ex) {
@ -889,11 +886,7 @@ public class IngestManager {
*/ */
@Override @Override
public void run() { public void run() {
try { publisher.publish(event);
publisher.publish(event);
} catch (JMSException ex) {
logger.log(Level.SEVERE, String.format("Failed to publish %s event to remote subscribers", event.getPropertyName()), ex);
}
} }
} }

View File

@ -52,6 +52,7 @@ public final class FileAnalyzedEvent extends AutopsyEvent implements Serializabl
* event is published over a network. * event is published over a network.
*/ */
super(IngestManager.IngestModuleEvent.FILE_DONE.toString(), file.getId(), null); super(IngestManager.IngestModuleEvent.FILE_DONE.toString(), file.getId(), null);
this.file = file;
} }
/** /**