diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java b/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java
index 373c5b5fed7d34abf8a7d1a76f383c272097caf5..57db059411edb14c5a45ba2eac3d05c3c24949a4 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java
@@ -246,7 +246,7 @@ static class TypeFactory extends ChildFactory.Detachable<TypeNodeKey> implements
         @SuppressWarnings("deprecation")
         private static TypeNodeKey getTypeKey(BlackboardArtifact.Type type, SleuthkitCase skCase, long dsObjId) {
 
-            // ELTODO
+            // Get the custom TSK_MALWARE artifact type from case database
             if (MALWARE_ARTIFACT_TYPE == null) {
                 try {
                     MALWARE_ARTIFACT_TYPE = skCase.getArtifactType(MALWARE_HITS);
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/MalwareHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/MalwareHits.java
index 6c3a9f74d63005ce0aa0ba440f6eb8aa89cf56a0..784da11ba5c2a7db824012c00136ef29f4a9d32f 100755
--- a/Core/src/org/sleuthkit/autopsy/datamodel/MalwareHits.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/MalwareHits.java
@@ -27,14 +27,12 @@
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Observable;
 import java.util.Observer;
 import java.util.Set;
 import java.util.logging.Level;
-import org.openide.nodes.ChildFactory;
 import org.openide.nodes.Children;
 import org.openide.nodes.Node;
 import org.openide.nodes.Sheet;
@@ -57,13 +55,11 @@
 /**
  * Malware hits node support. Inner classes have all of the nodes in the tree.
  */
-@NbBundle.Messages({
-    "MalwareHits_malwareTypeDisplayName=Malware",})
 public class MalwareHits implements AutopsyVisitableItem {
 
     private static final String MALWARE_HITS = "TSK_MALWARE";
     private static BlackboardArtifact.Type MALWARE_ARTIFACT_TYPE = null;
-    private static final String DISPLAY_NAME = Bundle.MalwareHits_malwareTypeDisplayName(); // ELTODO get from database
+    private static String DISPLAY_NAME;
     private static final Logger logger = Logger.getLogger(MalwareHits.class.getName());
     private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
     private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED);
@@ -103,13 +99,14 @@ public <T> T accept(AutopsyItemVisitor<T> visitor) {
      * Stores all of the malware results in a single class that is observable
      * for the child nodes
      */
-    private class MalwareResults extends Observable {
+    private class MalwareResults extends Observable implements Observer {
 
         // list of artifacts
         // NOTE: the list can be accessed by multiple worker threads and needs to be synchronized
         private final Set<Long> malwareHits = new HashSet<>();
 
         MalwareResults() {
+            addNotify();
             update();
         }
 
@@ -128,16 +125,18 @@ final void update() {
             if (skCase == null) {
                 return;
             }
-            
+
+            // Get the custom TSK_MALWARE artifact type from case database
             if (MALWARE_ARTIFACT_TYPE == null) {
                 try {
                     MALWARE_ARTIFACT_TYPE = skCase.getArtifactType(MALWARE_HITS);
+                    DISPLAY_NAME = MALWARE_ARTIFACT_TYPE.getDisplayName();
                 } catch (TskCoreException ex) {
                     logger.log(Level.WARNING, "Unable to get TSK_MALWARE artifact type from database : ", ex); //NON-NLS
                     return;
                 }
             }
-            
+
             String query = "SELECT blackboard_artifacts.artifact_obj_id " //NON-NLS
                     + "FROM blackboard_artifacts,tsk_analysis_results WHERE " //NON-NLS
                     + "blackboard_artifacts.artifact_type_id=" + MALWARE_ARTIFACT_TYPE.getTypeID()
@@ -163,6 +162,84 @@ final void update() {
             setChanged();
             notifyObservers();
         }
+
+        private final PropertyChangeListener pcl = new PropertyChangeListener() {
+            @Override
+            public void propertyChange(PropertyChangeEvent evt) {
+                String eventType = evt.getPropertyName();
+                if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
+                    /**
+                     * Checking for a current case is a stop gap measure until a
+                     * different way of handling the closing of cases is worked
+                     * out. Currently, remote events may be received for a case
+                     * that is already closed.
+                     */
+                    try {
+                        Case.getCurrentCaseThrows();
+                        /**
+                         * Due to some unresolved issues with how cases are
+                         * closed, it is possible for the event to have a null
+                         * oldValue if the event is a remote event.
+                         */
+                        ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
+                        if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == MALWARE_ARTIFACT_TYPE.getTypeID()) {
+                            malwareResults.update();
+                        }
+                    } catch (NoCurrentCaseException notUsed) {
+                        /**
+                         * Case is closed, do nothing.
+                         */
+                    }
+                } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
+                        || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
+                    /**
+                     * Checking for a current case is a stop gap measure until a
+                     * different way of handling the closing of cases is worked
+                     * out. Currently, remote events may be received for a case
+                     * that is already closed.
+                     */
+                    try {
+                        Case.getCurrentCaseThrows();
+                        malwareResults.update();
+                    } catch (NoCurrentCaseException notUsed) {
+                        /**
+                         * Case is closed, do nothing.
+                         */
+                    }
+                } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
+                    // case was closed. Remove listeners so that we don't get called with a stale case handle
+                    if (evt.getNewValue() == null) {
+                        removeNotify();
+                        skCase = null;
+                    }
+                }
+            }
+        };
+
+        private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
+
+        protected void addNotify() {
+            IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl);
+            IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakPcl);
+            Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
+        }
+        
+        protected void removeNotify() {
+            IngestManager.getInstance().removeIngestJobEventListener(weakPcl);
+            IngestManager.getInstance().removeIngestModuleEventListener(weakPcl);
+            Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);            
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            super.finalize();
+            removeNotify();
+        }
+
+        @Override
+        public void update(Observable o, Object arg) {
+            update();
+        }
     }
 
     /**
@@ -212,68 +289,16 @@ protected Sheet createSheet() {
         public String getItemType() {
             return getClass().getName();
         }
-    }
-
-    /**
-     * Node for a hash set name
-     
-    public class HashsetNameNode extends DisplayableItemNode implements Observer {
-
-        private final String hashSetName;
-
-        public HashsetNameNode(String hashSetName) {
-            super(Children.create(new HitFactory(hashSetName), true), Lookups.singleton(hashSetName));
-            super.setName(hashSetName);
-            this.hashSetName = hashSetName;
-            updateDisplayName();
-            this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/hashset_hits.png"); // ELTODO
-            malwareResults.addObserver(this);
-        }
-
-        // Update the count in the display name
-        private void updateDisplayName() {
-            super.setDisplayName(hashSetName + " (" + malwareResults.getArtifactIds(hashSetName).size() + ")");
-        }
-
-        @Override
-        public boolean isLeafTypeNode() {
-            return true;
-        }
-
-        @Override
-        protected Sheet createSheet() {
-            Sheet sheet = super.createSheet();
-            Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
-            if (sheetSet == null) {
-                sheetSet = Sheet.createPropertiesSet();
-                sheet.put(sheetSet);
-            }
-
-            sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "MalwareHits.createSheet.name.name"),
-                    NbBundle.getMessage(this.getClass(), "MalwareHits.createSheet.name.displayName"),
-                    NbBundle.getMessage(this.getClass(), "MalwareHits.createSheet.name.desc"),
-                    getName()));
-
-            return sheet;
-        }
-
-        @Override
-        public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
-            return visitor.visit(this);
-        }
-
-        @Override
-        public void update(Observable o, Object arg) {
-            updateDisplayName();
-        }
 
+        /**
+         * When this method is called, the count to be displayed will be
+         * updated.
+         */
         @Override
-        public String getItemType() {
-            // For custom settings for each hash set, return
-             *getClass().getName() + hashSetName instead.
-            return getClass().getName();
+        void updateDisplayName() {
+            super.setDisplayName(DISPLAY_NAME + " (" + malwareResults.getArtifactIds().size() + ")");
         }
-    }*/
+    }
 
     /**
      * Creates the nodes for the malware hits.