diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java
index 4ebbe9223d2eb1cdb5b73995ff4de8801af8d8cf..3ffae2bd0f528a14e01a076fe30f66327812fe6f 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java
@@ -87,10 +87,10 @@
 import org.sleuthkit.autopsy.casemodule.events.HostsRemovedEvent;
 import org.sleuthkit.autopsy.casemodule.events.OsAccountAddedEvent;
 import org.sleuthkit.autopsy.casemodule.events.OsAccountChangedEvent;
-import org.sleuthkit.autopsy.casemodule.events.OsAccountRemovedEvent;
+import org.sleuthkit.autopsy.casemodule.events.OsAccountDeletedEvent;
 import org.sleuthkit.autopsy.casemodule.events.PersonsAddedEvent;
 import org.sleuthkit.autopsy.casemodule.events.PersonsChangedEvent;
-import org.sleuthkit.autopsy.casemodule.events.PersonsRemovedEvent;
+import org.sleuthkit.autopsy.casemodule.events.PersonsDeletedEvent;
 import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent;
 import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException;
 import org.sleuthkit.autopsy.casemodule.multiusercases.CoordinationServiceUtils;
@@ -140,24 +140,16 @@
 import org.sleuthkit.datamodel.DataSource;
 import org.sleuthkit.datamodel.FileSystem;
 import org.sleuthkit.datamodel.Host;
-import org.sleuthkit.datamodel.HostManager.HostsCreationEvent;
-import org.sleuthkit.datamodel.HostManager.HostsUpdateEvent;
-import org.sleuthkit.datamodel.HostManager.HostsDeletionEvent;
 import org.sleuthkit.datamodel.Image;
 import org.sleuthkit.datamodel.OsAccount;
-import org.sleuthkit.datamodel.OsAccountManager.OsAccountsCreationEvent;
-import org.sleuthkit.datamodel.OsAccountManager.OsAccountsDeleteEvent;
-import org.sleuthkit.datamodel.OsAccountManager.OsAccountsUpdateEvent;
 import org.sleuthkit.datamodel.Person;
-import org.sleuthkit.datamodel.PersonManager.PersonsCreationEvent;
-import org.sleuthkit.datamodel.PersonManager.PersonsUpdateEvent;
-import org.sleuthkit.datamodel.PersonManager.PersonsDeletionEvent;
 import org.sleuthkit.datamodel.Report;
 import org.sleuthkit.datamodel.SleuthkitCase;
 import org.sleuthkit.datamodel.TimelineManager;
 import org.sleuthkit.datamodel.SleuthkitCaseAdminUtil;
 import org.sleuthkit.datamodel.TskCoreException;
 import org.sleuthkit.datamodel.TskDataException;
+import org.sleuthkit.datamodel.TskEvent;
 import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException;
 
 /**
@@ -504,36 +496,36 @@ public void publishArtifactsPostedEvent(Blackboard.ArtifactsPostedEvent event) {
                         event.getArtifacts(artifactType)));
             }
         }
-
-        @Subscribe
-        public void publishOsAccountAddedEvent(OsAccountsCreationEvent event) {
-            for (OsAccount account : event.getOsAcounts()) {
+        
+        @Subscribe 
+        public void publishOsAccountAddedEvent(TskEvent.OsAccountsAddedTskEvent event) {
+            for(OsAccount account: event.getOsAcounts()) {
                 eventPublisher.publish(new OsAccountAddedEvent(account));
             }
         }
-
-        @Subscribe
-        public void publishOsAccountChangedEvent(OsAccountsUpdateEvent event) {
-            for (OsAccount account : event.getOsAcounts()) {
+        
+        @Subscribe 
+        public void publishOsAccountChangedEvent(TskEvent.OsAccountsChangedTskEvent event) {
+            for(OsAccount account: event.getOsAcounts()) {
                 eventPublisher.publish(new OsAccountChangedEvent(account));
             }
         }
-
-        @Subscribe
-        public void publishOsAccountDeletedEvent(OsAccountsDeleteEvent event) {
-            for (Long accountId : event.getOsAcountObjectIds()) {
-                eventPublisher.publish(new OsAccountRemovedEvent(accountId));
+        
+        @Subscribe 
+        public void publishOsAccountDeletedEvent(TskEvent.OsAccountsDeletedTskEvent event) {
+            for(Long accountId: event.getOsAcountObjectIds()) {
+                eventPublisher.publish(new OsAccountDeletedEvent(accountId));
             }
         }
 
         /**
-         * Publishes an autopsy event from the sleuthkit HostCreationEvent
+         * Publishes an autopsy event from the sleuthkit HostAddedEvent 
          * indicating that hosts have been created.
          *
          * @param event The sleuthkit event for the creation of hosts.
          */
-        @Subscribe
-        public void publishHostsAddedEvent(HostsCreationEvent event) {
+        @Subscribe 
+        public void publishHostsAddedEvent(TskEvent.HostsAddedTskEvent event) {
             eventPublisher.publish(new HostsAddedEvent(
                     event == null ? Collections.emptyList() : event.getHosts()));
         }
@@ -543,9 +535,9 @@ public void publishHostsAddedEvent(HostsCreationEvent event) {
          * indicating that hosts have been updated.
          *
          * @param event The sleuthkit event for the updating of hosts.
-         */
-        @Subscribe
-        public void publishHostsChangedEvent(HostsUpdateEvent event) {
+         */        
+        @Subscribe 
+        public void publishHostsChangedEvent(TskEvent.HostsChangedTskEvent event) {
             eventPublisher.publish(new HostsChangedEvent(
                     event == null ? Collections.emptyList() : event.getHosts()));
         }
@@ -555,33 +547,33 @@ public void publishHostsChangedEvent(HostsUpdateEvent event) {
          * indicating that hosts have been deleted.
          *
          * @param event The sleuthkit event for the deleting of hosts.
-         */
-        @Subscribe
-        public void publishHostsDeletedEvent(HostsDeletionEvent event) {
+         */    
+        @Subscribe 
+        public void publishHostsDeletedEvent(TskEvent.HostsDeletedTskEvent event) {
             eventPublisher.publish(new HostsRemovedEvent(
                     event == null ? Collections.emptyList() : event.getHosts()));
         }
 
         /**
-         * Publishes an autopsy event from the sleuthkit PersonCreationEvent
+         * Publishes an autopsy event from the sleuthkit PersonAddedEvent 
          * indicating that persons have been created.
          *
          * @param event The sleuthkit event for the creation of persons.
          */
-        @Subscribe
-        public void publishPersonsAddedEvent(PersonsCreationEvent event) {
+        @Subscribe 
+        public void publishPersonsAddedEvent(TskEvent.PersonsAddedTskEvent event) {
             eventPublisher.publish(new PersonsAddedEvent(
                     event == null ? Collections.emptyList() : event.getPersons()));
         }
 
         /**
-         * Publishes an autopsy event from the sleuthkit PersonUpdateEvent
+         * Publishes an autopsy event from the sleuthkit PersonChangedEvent 
          * indicating that persons have been updated.
          *
          * @param event The sleuthkit event for the updating of persons.
-         */
-        @Subscribe
-        public void publishPersonsChangedEvent(PersonsUpdateEvent event) {
+         */        
+        @Subscribe 
+        public void publishPersonsChangedEvent(TskEvent.PersonsChangedTskEvent event) {
             eventPublisher.publish(new PersonsChangedEvent(
                     event == null ? Collections.emptyList() : event.getPersons()));
         }
@@ -591,10 +583,10 @@ public void publishPersonsChangedEvent(PersonsUpdateEvent event) {
          * indicating that persons have been deleted.
          *
          * @param event The sleuthkit event for the deleting of persons.
-         */
-        @Subscribe
-        public void publishPersonsDeletedEvent(PersonsDeletionEvent event) {
-            eventPublisher.publish(new PersonsRemovedEvent(
+         */    
+        @Subscribe 
+        public void publishPersonsDeletedEvent(TskEvent.PersonsDeletedTskEvent event) {
+            eventPublisher.publish(new PersonsDeletedEvent(
                     event == null ? Collections.emptyList() : event.getPersons()));
         }
     }
@@ -1796,7 +1788,7 @@ public void notifyOsAccountChanged(OsAccount account) {
     }
 
     public void notifyOsAccountRemoved(Long osAccountObjectId) {
-        eventPublisher.publish(new OsAccountRemovedEvent(osAccountObjectId));
+        eventPublisher.publish(new OsAccountDeletedEvent(osAccountObjectId));
     }
 
     /**
@@ -1850,7 +1842,7 @@ public void notifyPersonChanged(Person newValue) {
      * @param person The person that has been deleted.
      */
     public void notifyPersonDeleted(Person person) {
-        eventPublisher.publish(new PersonsRemovedEvent(Collections.singletonList(person)));
+        eventPublisher.publish(new PersonsDeletedEvent(Collections.singletonList(person)));
     }
 
     /**
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsEvent.java
index efa60c8b4796c62652a31dfc7d9e20871d42a176..7c9a31f01e1b3aba2f41794a7704871e22a6521e 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsEvent.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsEvent.java
@@ -33,6 +33,8 @@
  */
 public class HostsEvent extends TskDataModelChangeEvent<Host> {
 
+    private static final long serialVersionUID = 1L;
+
     /**
      * Retrieves a list of ids from a list of hosts.
      *
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountRemovedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountDeletedEvent.java
similarity index 90%
rename from Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountRemovedEvent.java
rename to Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountDeletedEvent.java
index 4bf03c90d98db1369cc5e368976fa622989c93fa..adc726fca85018a24f5f30e49a7deefb4bf4fe15 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountRemovedEvent.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountDeletedEvent.java
@@ -27,11 +27,11 @@
  * oldValue will contain the objectId of the account that was removed. newValue
  * will be null.
  */
-public final class OsAccountRemovedEvent extends AutopsyEvent {
+public final class OsAccountDeletedEvent extends AutopsyEvent {
 
     private static final long serialVersionUID = 1L;
     
-    public OsAccountRemovedEvent(Long osAccountObjectId) {
+    public OsAccountDeletedEvent(Long osAccountObjectId) {
         super(Case.Events.OS_ACCOUNT_REMOVED.toString(), osAccountObjectId, null);
     }  
 }
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsRemovedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsDeletedEvent.java
similarity index 90%
rename from Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsRemovedEvent.java
rename to Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsDeletedEvent.java
index 522c841461d03e3b82fa311dffd87c26fcc9e42e..a63fef32cbfe074f5489caafee76d3c128f6dfba 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsRemovedEvent.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsDeletedEvent.java
@@ -25,7 +25,7 @@
 /**
  * Event fired when persons are removed.
  */
-public class PersonsRemovedEvent extends PersonsEvent {
+public class PersonsDeletedEvent extends PersonsEvent {
     
     private static final long serialVersionUID = 1L;
     
@@ -33,7 +33,7 @@ public class PersonsRemovedEvent extends PersonsEvent {
      * Main constructor.
      * @param dataModelObjects The list of persons that have been deleted.
      */
-    public PersonsRemovedEvent(List<Person> dataModelObjects) {
+    public PersonsDeletedEvent(List<Person> dataModelObjects) {
         super(Case.Events.PERSONS_DELETED.name(), dataModelObjects);
     }
 }
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepository.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepository.java
index 842c8e3f0488f30d31e6d0a681985114b02f4562..289dfa476f85ea0a1363174632936ba9dfdd443c 100755
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepository.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepository.java
@@ -21,6 +21,7 @@
 import java.sql.SQLException;
 import java.util.Collection;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 import org.sleuthkit.datamodel.TskData;
 import org.sleuthkit.autopsy.casemodule.Case;
@@ -860,10 +861,10 @@ CorrelationAttributeInstance getCorrelationAttributeInstance(CorrelationAttribut
      * Get account type by type name.
      *
      * @param accountTypeName account type name to look for
-     * @return CR account type
+     * @return CR account type (if found)
      * @throws CentralRepoException
      */
-    CentralRepoAccountType getAccountTypeByName(String accountTypeName) throws CentralRepoException;
+    Optional<CentralRepoAccountType> getAccountTypeByName(String accountTypeName) throws CentralRepoException;
 
     /**
      * Gets all account types.
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeUtil.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeUtil.java
index 4867b4eb0a82f116f8b935eab9f6325922f6ea9a..2d0315ef7b21320258c309d27226c090d9b70b5d 100755
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeUtil.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeUtil.java
@@ -22,6 +22,7 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 import java.util.logging.Level;
 import org.openide.util.NbBundle.Messages;
@@ -308,8 +309,12 @@ private static void makeCorrAttrFromAcctArtifact(List<CorrelationAttributeInstan
         if (Account.Type.DEVICE.getTypeName().equalsIgnoreCase(accountTypeStr) == false && predefinedAccountType != null) {
 
             // Get the corresponding CentralRepoAccountType from the database.
-            CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
-
+            Optional<CentralRepoAccountType> optCrAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
+            if (!optCrAccountType.isPresent()) {
+                return;
+            }
+            CentralRepoAccountType crAccountType = optCrAccountType.get();
+            
             int corrTypeId = crAccountType.getCorrelationTypeId();
             CorrelationAttributeInstance.Type corrType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId);
 
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java
index afbb2fe4b5dd73ff37bae3b4e18fb7389ac1eb67..59b0ebe62728b484d711e8f0b023d77ff70bb6d6 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java
@@ -26,8 +26,10 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
 import org.apache.commons.lang3.StringUtils;
 import org.sleuthkit.datamodel.Account;
+import org.sleuthkit.datamodel.TskCoreException;
 
 /**
  * This class represents an association between a Persona and an Account.
@@ -206,10 +208,15 @@ public void process(ResultSet rs) throws CentralRepoException, SQLException {
                 );
 
                 // create account
-                CentralRepoAccount.CentralRepoAccountType crAccountType = getCRInstance().getAccountTypeByName(rs.getString("type_name"));
+                String accountTypeName = rs.getString("type_name");
+                Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = getCRInstance().getAccountTypeByName(accountTypeName);
+                if (! optCrAccountType.isPresent()) {
+                    // The CR account can not be null, so throw an exception
+                    throw new CentralRepoException("Account type with name '" + accountTypeName + "' not found in Central Repository");
+                }
                 CentralRepoAccount account = new CentralRepoAccount(
                         rs.getInt("account_id"),
-                        crAccountType,
+                        optCrAccountType.get(),
                         rs.getString("account_unique_identifier"));
 
                 // create persona account
@@ -389,10 +396,15 @@ public void process(ResultSet rs) throws CentralRepoException, SQLException {
             while (rs.next()) {
 
                 // create account
-                CentralRepoAccount.CentralRepoAccountType crAccountType = getCRInstance().getAccountTypeByName(rs.getString("type_name"));
+                String accountTypeName = rs.getString("type_name");
+                Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = getCRInstance().getAccountTypeByName(accountTypeName);
+                if (! optCrAccountType.isPresent()) {
+                    // The CR account can not be null, so throw an exception
+                    throw new CentralRepoException("Account type with name '" + accountTypeName + "' not found in Central Repository");
+                }
                 CentralRepoAccount account = new CentralRepoAccount(
                         rs.getInt("account_id"),
-                        crAccountType,
+                        optCrAccountType.get(),
                         rs.getString("account_unique_identifier"));
 
                 accountsList.add(account);
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/RdbmsCentralRepo.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/RdbmsCentralRepo.java
index b48797e3fc458b79a64e35088f2284fb77d492be..1b4ce08c18f08f38b7df2a54ecd285097686178d 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/RdbmsCentralRepo.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/RdbmsCentralRepo.java
@@ -37,6 +37,7 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
@@ -78,7 +79,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
     private static final int CASE_CACHE_TIMEOUT = 5;
     private static final int DATA_SOURCE_CACHE_TIMEOUT = 5;
     private static final int ACCOUNTS_CACHE_TIMEOUT = 5;
-    private static final Cache<String, CentralRepoAccountType> accountTypesCache = CacheBuilder.newBuilder().build();
+    private static final Cache<String, Optional<CentralRepoAccountType>> accountTypesCache = CacheBuilder.newBuilder().build();
     private static final Cache<Pair<CentralRepoAccountType, String>, CentralRepoAccount> accountsCache = CacheBuilder.newBuilder()
             .expireAfterWrite(ACCOUNTS_CACHE_TIMEOUT, TimeUnit.MINUTES).
             build();
@@ -1115,7 +1116,7 @@ public CentralRepoAccount getOrCreateAccount(CentralRepoAccountType crAccountTyp
     }
 
     @Override
-    public CentralRepoAccountType getAccountTypeByName(String accountTypeName) throws CentralRepoException {
+    public Optional<CentralRepoAccountType> getAccountTypeByName(String accountTypeName) throws CentralRepoException {
         try {
             return accountTypesCache.get(accountTypeName, () -> getCRAccountTypeFromDb(accountTypeName));
         } catch (CacheLoader.InvalidCacheLoadException | ExecutionException ex) {
@@ -1155,7 +1156,7 @@ public Collection<CentralRepoAccountType> getAllAccountTypes() throws CentralRep
      *
      * @throws CentralRepoException
      */
-    private CentralRepoAccountType getCRAccountTypeFromDb(String accountTypeName) throws CentralRepoException {
+    private Optional<CentralRepoAccountType> getCRAccountTypeFromDb(String accountTypeName) throws CentralRepoException {
 
         String sql = "SELECT * FROM account_types WHERE type_name = ?";
         try (Connection conn = connect();
@@ -1166,10 +1167,11 @@ private CentralRepoAccountType getCRAccountTypeFromDb(String accountTypeName) th
                 if (resultSet.next()) {
                     Account.Type acctType = new Account.Type(accountTypeName, resultSet.getString("display_name"));
                     CentralRepoAccountType crAccountType = new CentralRepoAccountType(resultSet.getInt("id"), acctType, resultSet.getInt("correlation_type_id"));
-                    accountTypesCache.put(accountTypeName, crAccountType);
-                    return crAccountType;
+                    accountTypesCache.put(accountTypeName, Optional.of(crAccountType));
+                    return Optional.of(crAccountType);
                 } else {
-                    throw new CentralRepoException("Failed to find entry for account type = " + accountTypeName);
+                    accountTypesCache.put(accountTypeName, Optional.empty());
+                    return Optional.empty();
                 }
             }
         } catch (SQLException ex) {
diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/CorrelationCaseChildNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/CorrelationCaseChildNodeFactory.java
index 588e474303b8738612d3b9206636c3c228f5902e..1e2b3506694225937bee2c5ca0e5d2e2fe42d5bb 100755
--- a/Core/src/org/sleuthkit/autopsy/communications/relationships/CorrelationCaseChildNodeFactory.java
+++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/CorrelationCaseChildNodeFactory.java
@@ -21,6 +21,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.logging.Level;
 import org.openide.nodes.AbstractNode;
@@ -75,9 +76,9 @@ protected boolean createKeys(List<CorrelationCase> list) {
 
         accounts.forEach((account) -> {
             try {
-                CorrelationAttributeInstance.Type correlationType = getCorrelationType(account.getAccountType());
-                if (correlationType != null) {
-                    List<CorrelationAttributeInstance> correlationInstances = dbInstance.getArtifactInstancesByTypeValue(correlationType, account.getTypeSpecificID());
+                Optional<CorrelationAttributeInstance.Type> optCorrelationType = getCorrelationType(account.getAccountType());
+                if (optCorrelationType.isPresent()) {
+                    List<CorrelationAttributeInstance> correlationInstances = dbInstance.getArtifactInstancesByTypeValue(optCorrelationType.get(), account.getTypeSpecificID());
                     correlationInstances.forEach((correlationInstance) -> {
                         CorrelationCase correlationCase = correlationInstance.getCorrelationCase();
                         uniqueCaseMap.put(correlationCase.getCaseUUID(), correlationCase);
@@ -103,20 +104,22 @@ protected Node createNodeForKey(CorrelationCase correlationCase) {
      *
      * @param accountType Account type
      *
-     * @return CorrelationAttributeInstance.Type for given account or null if
+     * @return CorrelationAttributeInstance.Type for given account or empty if
      *         there is no match
      *
      * @throws CentralRepoException
      */
-    private CorrelationAttributeInstance.Type getCorrelationType(Account.Type accountType) throws CentralRepoException {        
+    private Optional<CorrelationAttributeInstance.Type> getCorrelationType(Account.Type accountType) throws CentralRepoException { 
+
         String accountTypeStr = accountType.getTypeName();
         if (Account.Type.DEVICE.getTypeName().equalsIgnoreCase(accountTypeStr) == false) {
-            CentralRepoAccount.CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
-            int corrTypeId = crAccountType.getCorrelationTypeId();
-            return CentralRepository.getInstance().getCorrelationTypeById(corrTypeId);
+            Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
+            if (optCrAccountType.isPresent()) {
+                int corrTypeId = optCrAccountType.get().getCorrelationTypeId();
+                return Optional.of(CentralRepository.getInstance().getCorrelationTypeById(corrTypeId));
+            }
         }
-       
-        return null;
+        return Optional.empty();
     }
 
     /**
diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPanelWorker.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPanelWorker.java
index ddc3282fe0ad214b769e4f7ab9e89dd1883fc22a..e18f3883d9c59e10d7cbe7feebbee2349e0aabbf 100755
--- a/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPanelWorker.java
+++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPanelWorker.java
@@ -21,10 +21,12 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Optional;
 import java.util.logging.Level;
 import javax.swing.SwingWorker;
 import org.sleuthkit.autopsy.casemodule.Case;
 import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount;
+import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
 import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
 import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
 import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
@@ -76,12 +78,15 @@ protected SummaryWorkerResults doInBackground() throws Exception {
                 personaList.add(pAccount.getPersona());
             }
 
-            try {
-                crAccount = CentralRepository.getInstance().getAccount(CentralRepository.getInstance().getAccountTypeByName(account.getAccountType().getTypeName()), account.getTypeSpecificID());
-            } catch (InvalidAccountIDException unused) {
-                // This was probably caused to a phone number not making
-                // threw the normalization.
-                logger.log(Level.WARNING, String.format("Exception thrown from CR getAccount for account %s (%d)", account.getTypeSpecificID(), account.getAccountID()));
+            Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = CentralRepository.getInstance().getAccountTypeByName(account.getAccountType().getTypeName());
+            if (optCrAccountType.isPresent()) {
+                try {
+                    crAccount = CentralRepository.getInstance().getAccount(optCrAccountType.get(), account.getTypeSpecificID());
+                } catch (InvalidAccountIDException unused) {
+                    // This was probably caused to a phone number not making
+                    // threw the normalization.
+                    logger.log(Level.WARNING, String.format("Exception thrown from CR getAccount for account %s (%d)", account.getTypeSpecificID(), account.getAccountID()));
+                }
             }
         }
 
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/ContactArtifactViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/ContactArtifactViewer.java
index 2bd3d9087518072bff83fd1845f43cb5d0a9e660..b17263a26de02d0336700d020a00502abb15f134 100644
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/ContactArtifactViewer.java
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/ContactArtifactViewer.java
@@ -32,6 +32,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.ExecutionException;
 import java.util.logging.Level;
@@ -630,11 +631,13 @@ protected Map<Persona, ArrayList<CentralRepoAccount>> doInBackground() throws Ex
 
                 // make a list of all unique accounts for this contact
                 if (!account.getAccountType().equals(Account.Type.DEVICE)) {
-                    CentralRepoAccount.CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(account.getAccountType().getTypeName());
-                    CentralRepoAccount crAccount = CentralRepository.getInstance().getAccount(crAccountType, account.getTypeSpecificID());
+                    Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = CentralRepository.getInstance().getAccountTypeByName(account.getAccountType().getTypeName());
+                    if (optCrAccountType.isPresent()) {
+                        CentralRepoAccount crAccount = CentralRepository.getInstance().getAccount(optCrAccountType.get(), account.getTypeSpecificID());
 
-                    if (crAccount != null && uniqueAccountsList.contains(crAccount) == false) {
-                        uniqueAccountsList.add(crAccount);
+                        if (crAccount != null && uniqueAccountsList.contains(crAccount) == false) {
+                            uniqueAccountsList.add(crAccount);
+                        }
                     }
                 }
                 
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/osaccount/OsAccountDataPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/osaccount/OsAccountDataPanel.java
old mode 100755
new mode 100644
index 9c5ae67fa1fcdfd7cbf3acc22e10c662950362d7..2c88660cb03a80992a3b3f822f47225e6264ca71
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/osaccount/OsAccountDataPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/osaccount/OsAccountDataPanel.java
@@ -485,7 +485,7 @@ protected void done() {
 //
 //                    data.add(instanceSection);
 //                }
-
+                
                 addDataComponents(data);
 
                 revalidate();
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form
index 4c42c44668901c0b3acd9ddfe53d295097f264dc..d47982f2a774c0a94f5b1f9b18891ce56f1089eb 100644
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form
+++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form
@@ -244,7 +244,7 @@
                                       <Group type="102" alignment="0" attributes="0">
                                           <EmptySpace min="-2" pref="23" max="-2" attributes="0"/>
                                           <Component id="memFieldValidationLabel" min="-2" pref="478" max="-2" attributes="0"/>
-                                          <EmptySpace max="32767" attributes="0"/>
+                                          <EmptySpace pref="12" max="32767" attributes="0"/>
                                       </Group>
                                       <Group type="102" alignment="0" attributes="0">
                                           <EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
@@ -255,7 +255,24 @@
                                       </Group>
                                   </Group>
                               </Group>
-                              <Component id="restartNecessaryWarning" alignment="0" min="-2" pref="615" max="-2" attributes="0"/>
+                              <Group type="102" attributes="0">
+                                  <Group type="103" groupAlignment="0" attributes="0">
+                                      <Component id="restartNecessaryWarning" alignment="0" min="-2" pref="615" max="-2" attributes="0"/>
+                                      <Group type="102" alignment="0" attributes="0">
+                                          <Component id="heapFileLabel" min="-2" max="-2" attributes="0"/>
+                                          <EmptySpace max="-2" attributes="0"/>
+                                          <Group type="103" groupAlignment="0" attributes="0">
+                                              <Component id="heapFieldValidationLabel" min="-2" pref="478" max="-2" attributes="0"/>
+                                              <Group type="102" attributes="0">
+                                                  <Component id="heapDumpFileField" min="-2" pref="415" max="-2" attributes="0"/>
+                                                  <EmptySpace max="-2" attributes="0"/>
+                                                  <Component id="heapDumpBrowseButton" min="-2" max="-2" attributes="0"/>
+                                              </Group>
+                                          </Group>
+                                      </Group>
+                                  </Group>
+                                  <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
+                              </Group>
                           </Group>
                       </Group>
                   </Group>
@@ -293,7 +310,15 @@
                               <Component id="maxLogFileCount" alignment="3" min="-2" max="-2" attributes="0"/>
                               <Component id="logFileCount" alignment="3" min="-2" max="-2" attributes="0"/>
                           </Group>
-                          <EmptySpace type="unrelated" max="-2" attributes="0"/>
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Group type="103" groupAlignment="3" attributes="0">
+                              <Component id="heapFileLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                              <Component id="heapDumpFileField" alignment="3" min="-2" max="-2" attributes="0"/>
+                              <Component id="heapDumpBrowseButton" alignment="3" min="-2" max="-2" attributes="0"/>
+                          </Group>
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Component id="heapFieldValidationLabel" min="-2" pref="16" max="-2" attributes="0"/>
+                          <EmptySpace max="-2" attributes="0"/>
                           <Component id="restartNecessaryWarning" min="-2" max="-2" attributes="0"/>
                           <EmptySpace max="-2" attributes="0"/>
                       </Group>
@@ -414,6 +439,40 @@
                     </Property>
                   </Properties>
                 </Component>
+                <Component class="javax.swing.JLabel" name="heapFileLabel">
+                  <Properties>
+                    <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                      <ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.heapFileLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                    </Property>
+                  </Properties>
+                </Component>
+                <Component class="javax.swing.JTextField" name="heapDumpFileField">
+                  <Properties>
+                    <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                      <ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.heapDumpFileField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                    </Property>
+                  </Properties>
+                </Component>
+                <Component class="javax.swing.JButton" name="heapDumpBrowseButton">
+                  <Properties>
+                    <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                      <ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.heapDumpBrowseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                    </Property>
+                  </Properties>
+                  <Events>
+                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="heapDumpBrowseButtonActionPerformed"/>
+                  </Events>
+                </Component>
+                <Component class="javax.swing.JLabel" name="heapFieldValidationLabel">
+                  <Properties>
+                    <Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+                      <Color blue="0" green="0" red="ff" type="rgb"/>
+                    </Property>
+                    <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                      <ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.heapFieldValidationLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                    </Property>
+                  </Properties>
+                </Component>
               </SubComponents>
             </Container>
             <Container class="javax.swing.JPanel" name="tempDirectoryPanel">
@@ -462,7 +521,7 @@
                                   </Group>
                               </Group>
                           </Group>
-                          <EmptySpace pref="158" max="32767" attributes="0"/>
+                          <EmptySpace pref="164" max="32767" attributes="0"/>
                       </Group>
                   </Group>
                 </DimensionLayout>
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java
index 193b1836e9bef8fb130ed6dbfc4ea71eb0bf8c23..edbef370b2ed5a828299b24c4d402268c743c6b3 100644
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java
@@ -29,8 +29,11 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
-import java.util.StringJoiner;
 import java.util.logging.Level;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import javax.imageio.ImageIO;
 import javax.swing.ImageIcon;
 import javax.swing.JFileChooser;
@@ -38,7 +41,9 @@
 import javax.swing.SwingUtilities;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
+import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.tools.ant.types.Commandline;
 import org.netbeans.spi.options.OptionsPanelController;
 import org.openide.util.NbBundle;
 import org.sleuthkit.autopsy.coreutils.Logger;
@@ -76,6 +81,7 @@
 final class AutopsyOptionsPanel extends javax.swing.JPanel {
 
     private static final long serialVersionUID = 1L;
+    private static final String DEFAULT_HEAP_DUMP_FILE_FIELD = "";
     private final JFileChooser logoFileChooser;
     private final JFileChooser tempDirChooser;
     private static final String ETC_FOLDER_NAME = "etc";
@@ -88,6 +94,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
     private String initialMemValue = Long.toString(Runtime.getRuntime().maxMemory() / ONE_BILLION);
     
     private final ReportBranding reportBranding;
+    private final JFileChooser heapFileChooser;
 
     /**
      * Instantiate the Autopsy options panel.
@@ -103,13 +110,19 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
         tempDirChooser = new JFileChooser();
         tempDirChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
         tempDirChooser.setMultiSelectionEnabled(false);
+        
+        heapFileChooser = new JFileChooser();
+        heapFileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+        heapFileChooser.setMultiSelectionEnabled(false);
 
-        if (!PlatformUtil.is64BitJVM() || Version.getBuildType() == Version.Type.DEVELOPMENT) {
+        if (!isJVMHeapSettingsCapable()) {
             //32 bit JVM has a max heap size of 1.4 gb to 4 gb depending on OS
             //So disabling the setting of heap size when the JVM is not 64 bit 
             //Is the safest course of action
             //And the file won't exist in the install folder when running through netbeans
             memField.setEnabled(false);
+            heapDumpFileField.setEnabled(false);
+            heapDumpBrowseButton.setEnabled(false);
             solrMaxHeapSpinner.setEnabled(false);
         }
         systemMemoryTotal.setText(Long.toString(getSystemMemoryInGB()));
@@ -118,13 +131,21 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
         solrMaxHeapSpinner.setModel(new javax.swing.SpinnerNumberModel(UserPreferences.getMaxSolrVMSize(),
                 JVM_MEMORY_STEP_SIZE_MB, ((int) getSystemMemoryInGB()) * MEGA_IN_GIGA, JVM_MEMORY_STEP_SIZE_MB));
 
-        TextFieldListener textFieldListener = new TextFieldListener();
-        agencyLogoPathField.getDocument().addDocumentListener(textFieldListener);
-        tempCustomField.getDocument().addDocumentListener(new TempCustomTextListener());
+        agencyLogoPathField.getDocument().addDocumentListener(new TextFieldListener(null));
+        heapDumpFileField.getDocument().addDocumentListener(new TextFieldListener(this::isHeapPathValid));
+        tempCustomField.getDocument().addDocumentListener(new TextFieldListener(this::evaluateTempDirState));
         logFileCount.setText(String.valueOf(UserPreferences.getLogFileCount()));
         
         reportBranding = new ReportBranding();
     }
+    
+    /**
+     * Returns whether or not the jvm runtime heap settings can effectively be changed.
+     * @return Whether or not the jvm runtime heap settings can effectively be changed.
+     */
+    private static boolean isJVMHeapSettingsCapable() {
+        return PlatformUtil.is64BitJVM() && Version.getBuildType() != Version.Type.DEVELOPMENT;
+    }
 
     /**
      * Get the total system memory in gigabytes which exists on the machine
@@ -140,18 +161,23 @@ private long getSystemMemoryInGB() {
 
     /**
      * Gets the currently saved max java heap space in gigabytes.
+     * @param The conf file memory value (i.e. 4G).
      *
-     * @return @throws IOException when unable to get a valid setting
+     * @return The value in gigabytes.
+     * @throws IOException when unable to get a valid setting
      */
-    private long getCurrentJvmMaxMemoryInGB() throws IOException {
-        String currentXmx = getCurrentXmxValue();
+    private long getCurrentJvmMaxMemoryInGB(String confFileMemValue) throws IOException {
         char units = '-';
         Long value = 0L;
-        if (currentXmx.length() > 1) {
-            units = currentXmx.charAt(currentXmx.length() - 1);
-            value = Long.parseLong(currentXmx.substring(0, currentXmx.length() - 1));
+        if (confFileMemValue.length() > 1) {
+            units = confFileMemValue.charAt(confFileMemValue.length() - 1);
+            try {
+                value = Long.parseLong(confFileMemValue.substring(0, confFileMemValue.length() - 1));
+            } catch (NumberFormatException ex) {
+                throw new IOException("Unable to properly parse memory number.", ex);
+            }
         } else {
-            throw new IOException("No memory setting found in String: " + currentXmx);
+            throw new IOException("No memory setting found in String: " + confFileMemValue);
         }
         //some older .conf files might have the units as megabytes instead of gigabytes
         switch (units) {
@@ -166,33 +192,6 @@ private long getCurrentJvmMaxMemoryInGB() throws IOException {
         }
     }
 
-    /*
-     * The value currently saved in the conf file as the max java heap space
-     * available to this application. Form will be an integer followed by a
-     * character indicating units. Helper method for
-     * getCurrentJvmMaxMemoryInGB()
-     *
-     * @return the saved value for the max java heap space
-     *
-     * @throws IOException if the conf file does not exist in either the user
-     * directory or the install directory
-     */
-    private String getCurrentXmxValue() throws IOException {
-        String[] settings;
-        String currentSetting = "";
-        File userConfFile = getUserFolderConfFile();
-        if (!userConfFile.exists()) {
-            settings = getDefaultsFromFileContents(readConfFile(getInstallFolderConfFile()));
-        } else {
-            settings = getDefaultsFromFileContents(readConfFile(userConfFile));
-        }
-        for (String option : settings) {
-            if (option.startsWith("-J-Xmx")) {
-                currentSetting = option.replace("-J-Xmx", "").trim();
-            }
-        }
-        return currentSetting;
-    }
 
     /**
      * Get the conf file from the install directory which stores the default
@@ -229,7 +228,61 @@ private static File getUserFolderConfFile() {
         }
         return new File(userEtcFolder, confFileName);
     }
-
+    
+    private static final String JVM_SETTINGS_REGEX_PARAM = "options";
+    private static final String JVM_SETTINGS_REGEX_STR = "^\\s*default_options\\s*=\\s*\"?(?<" + JVM_SETTINGS_REGEX_PARAM + ">.+?)\"?\\s*$";
+    private static final Pattern JVM_SETTINGS_REGEX = Pattern.compile(JVM_SETTINGS_REGEX_STR);
+    private static final String XMX_REGEX_PARAM = "mem";
+    private static final String XMX_REGEX_STR = "^\\s*\\-J\\-Xmx(?<" + XMX_REGEX_PARAM + ">.+?)\\s*$";
+    private static final Pattern XMX_REGEX = Pattern.compile(XMX_REGEX_STR);
+    private static final String HEAP_DUMP_REGEX_PARAM = "path";
+    private static final String HEAP_DUMP_REGEX_STR = "^\\s*\\-J\\-XX:HeapDumpPath=(\\\")?\\s*(?<" + HEAP_DUMP_REGEX_PARAM + ">.+?)\\s*(\\\")?$";
+    private static final Pattern HEAP_DUMP_REGEX = Pattern.compile(HEAP_DUMP_REGEX_STR);
+    
+    /**
+     * Parse the autopsy conf file line.  If the line is the default_options line, 
+     * then replaces with current memory and heap path value.  Otherwise, returns 
+     * the line provided as parameter.
+     * 
+     * @param line The line.
+     * @param memText The text to add as an argument to be used as memory with -J-Xmx.
+     * @param heapText The text to add as an argument to be used as the heap dump path with
+     * -J-XX:HeapDumpPath.
+     * @return The line modified to contain memory and heap arguments.
+     */
+    private static String updateConfLine(String line, String memText, String heapText) {
+        Matcher match = JVM_SETTINGS_REGEX.matcher(line);
+        if (match.find()) {
+            // split on command line arguments
+            String[] parsedArgs = Commandline.translateCommandline(match.group(JVM_SETTINGS_REGEX_PARAM));
+            
+            String memString = "-J-Xmx" + memText.replaceAll("[^\\d]", "") + "g";
+            
+            // only add in heap path argument if a heap path is specified
+            String heapString = null;
+            if (StringUtils.isNotBlank(heapText)) {
+                while (heapText.endsWith("\\") && heapText.length() > 0) {
+                    heapText = heapText.substring(0, heapText.length() - 1);
+                }
+                
+                heapString = String.format("-J-XX:HeapDumpPath=\"%s\"", heapText);
+            }
+            
+            Stream<String> argsNoMemHeap = Stream.of(parsedArgs)
+                    // remove saved version of memory and heap dump path
+                    .filter(s -> !s.matches(XMX_REGEX_STR) && !s.matches(HEAP_DUMP_REGEX_STR));
+                    
+            String newArgs = Stream.concat(argsNoMemHeap, Stream.of(memString, heapString))
+                    .filter(s -> s != null)
+                    .collect(Collectors.joining(" "));
+            
+            return String.format("default_options=\"%s\"", newArgs);
+        };
+            
+        return line;
+    }
+    
+    
     /**
      * Take the conf file in the install directory and save a copy of it to the
      * user directory. The copy will be modified to include the current memory
@@ -239,25 +292,124 @@ private static File getUserFolderConfFile() {
      *                     install folders conf file
      */
     private void writeEtcConfFile() throws IOException {
-        StringBuilder content = new StringBuilder();
-        List<String> confFile = readConfFile(getInstallFolderConfFile());
-        for (String line : confFile) {
-            if (line.contains("-J-Xmx")) {
-                String[] splitLine = line.split(" ");
-                StringJoiner modifiedLine = new StringJoiner(" ");
-                for (String piece : splitLine) {
-                    if (piece.contains("-J-Xmx")) {
-                        piece = "-J-Xmx" + memField.getText() + "g";
-                    }
-                    modifiedLine.add(piece);
-                }
-                content.append(modifiedLine.toString());
-            } else {
-                content.append(line);
+        String fileText = readConfFile(getInstallFolderConfFile()).stream()
+                .map((line) -> updateConfLine(line, memField.getText(), heapDumpFileField.getText()))
+                .collect(Collectors.joining("\n"));
+        
+        FileUtils.writeStringToFile(getUserFolderConfFile(), fileText, "UTF-8");
+    }
+    
+    
+    /**
+     * Values for configuration located in the /etc/*.conf file.
+     */
+    private static class ConfValues {
+        private final String XmxVal;
+        private final String heapDumpPath;
+
+        /**
+         * Main constructor.
+         * @param XmxVal The heap memory size.
+         * @param heapDumpPath The heap dump path.
+         */
+        ConfValues(String XmxVal, String heapDumpPath) {
+            this.XmxVal = XmxVal;
+            this.heapDumpPath = heapDumpPath;
+        }
+
+        /**
+         * Returns the heap memory value specified in the conf file.
+         * @return The heap memory value specified in the conf file.
+         */
+        String getXmxVal() {
+            return XmxVal;
+        }
+
+        /**
+         * Returns path to the heap dump specified in the conf file.
+         * @return Path to the heap dump specified in the conf file.
+         */
+        String getHeapDumpPath() {
+            return heapDumpPath;
+        }
+    }
+    
+    /**
+     * Retrieve the /etc/*.conf file values pertinent to settings.
+     * @return The conf file values.
+     * @throws IOException 
+     */
+    private ConfValues getEtcConfValues() throws IOException {
+        File userConfFile = getUserFolderConfFile();
+        String[] args = userConfFile.exists() ? 
+                getDefaultsFromFileContents(readConfFile(userConfFile)) : 
+                getDefaultsFromFileContents(readConfFile(getInstallFolderConfFile()));
+        
+        String heapFile = "";
+        String memSize = "";
+
+        for (String arg : args) {
+            Matcher memMatch = XMX_REGEX.matcher(arg);
+            if (memMatch.find()) {
+                memSize = memMatch.group(XMX_REGEX_PARAM);
+                continue;
+            }
+
+            Matcher heapFileMatch = HEAP_DUMP_REGEX.matcher(arg);
+            if (heapFileMatch.find()) {
+                heapFile = heapFileMatch.group(HEAP_DUMP_REGEX_PARAM);
+                continue;
+            }
+        }
+        
+        return new ConfValues(memSize, heapFile);
+    }
+    
+    
+    
+    /**
+     * Checks current heap path value to see if it is valid, and displays an error message if invalid.
+     * @return True if the heap path is valid.
+     */
+    @Messages({
+        "AutopsyOptionsPanel_isHeapPathValid_selectValidDirectory=Please select an existing directory.",
+        "AutopsyOptionsPanel_isHeapPathValid_developerMode=Cannot change heap dump path while in developer mode.",
+        "AutopsyOptionsPanel_isHeapPathValid_not64BitMachine=Changing heap dump path settings only enabled for 64 bit version.",
+        "AutopsyOPtionsPanel_isHeapPathValid_illegalCharacters=Please select a path with no quotes."
+    })
+    private boolean isHeapPathValid() {
+        if (Version.getBuildType() == Version.Type.DEVELOPMENT) {
+            heapFieldValidationLabel.setVisible(true);
+            heapFieldValidationLabel.setText(Bundle.AutopsyOptionsPanel_isHeapPathValid_developerMode());
+            return true;
+        } 
+        
+        if (!PlatformUtil.is64BitJVM()) {
+            heapFieldValidationLabel.setVisible(true);
+            heapFieldValidationLabel.setText(Bundle.AutopsyOptionsPanel_isHeapPathValid_not64BitMachine());
+            return true;
+        }
+        
+        //allow blank field as the default will be used
+        if (StringUtils.isNotBlank(heapDumpFileField.getText())) { 
+            String heapText = heapDumpFileField.getText().trim();
+            if (heapText.contains("\"") || heapText.contains("'")) {
+                heapFieldValidationLabel.setVisible(true);
+                heapFieldValidationLabel.setText(Bundle.AutopsyOPtionsPanel_isHeapPathValid_illegalCharacters());
+                return false;
+            }
+            
+            File curHeapFile = new File(heapText);
+            if (!curHeapFile.exists() || !curHeapFile.isDirectory()) {
+                heapFieldValidationLabel.setVisible(true);
+                heapFieldValidationLabel.setText(Bundle.AutopsyOptionsPanel_isHeapPathValid_selectValidDirectory());
+                return false;
             }
-            content.append("\n");
         }
-        Files.write(getUserFolderConfFile().toPath(), content.toString().getBytes());
+            
+        heapFieldValidationLabel.setVisible(false);
+        heapFieldValidationLabel.setText("");
+        return true;
     }
 
     /**
@@ -295,11 +447,17 @@ private static List<String> readConfFile(File configFile) {
      *         options is not present.
      */
     private static String[] getDefaultsFromFileContents(List<String> list) {
-        Optional<String> defaultSettings = list.stream().filter(line -> line.startsWith("default_options=")).findFirst();
+        Optional<String> defaultSettings = list.stream()
+                .filter(line -> line.matches(JVM_SETTINGS_REGEX_STR))
+                .findFirst();
 
         if (defaultSettings.isPresent()) {
-            return defaultSettings.get().replace("default_options=", "").replaceAll("\"", "").split(" ");
+            Matcher match = JVM_SETTINGS_REGEX.matcher(defaultSettings.get());
+            if (match.find()) {
+                return Commandline.translateCommandline(match.group(JVM_SETTINGS_REGEX_PARAM));
+            }
         }
+        
         return new String[]{};
     }
     
@@ -347,15 +505,25 @@ void load() {
         } catch (IOException ex) {
             logger.log(Level.WARNING, "Error loading image from previously saved agency logo path", ex);
         }
-        if (memField.isEnabled()) {
+        
+        boolean confLoaded = false;
+        if (isJVMHeapSettingsCapable()) {
             try {
-                initialMemValue = Long.toString(getCurrentJvmMaxMemoryInGB());
+                ConfValues confValues = getEtcConfValues();
+                heapDumpFileField.setText(confValues.getHeapDumpPath());
+                initialMemValue = Long.toString(getCurrentJvmMaxMemoryInGB(confValues.getXmxVal()));
+                confLoaded = true;
             } catch (IOException ex) {
                 logger.log(Level.SEVERE, "Can't read current Jvm max memory setting from file", ex);
                 memField.setEnabled(false);
+                heapDumpFileField.setText(DEFAULT_HEAP_DUMP_FILE_FIELD);
             }
             memField.setText(initialMemValue);
         }
+        
+        heapDumpBrowseButton.setEnabled(confLoaded);
+        heapDumpFileField.setEnabled(confLoaded);
+        
         setTempDirEnabled();
         valid(); //ensure the error messages are up to date
     }
@@ -459,7 +627,7 @@ void store() {
             reportBranding.setAgencyLogoPath("");
         }
         UserPreferences.setMaxSolrVMSize((int) solrMaxHeapSpinner.getValue());
-        if (memField.isEnabled()) {  //if the field could of been changed we need to try and save it
+        if (isJVMHeapSettingsCapable()) {  //if the field could of been changed we need to try and save it
             try {
                 writeEtcConfFile();
             } catch (IOException ex) {
@@ -474,18 +642,12 @@ void store() {
      * @return True if valid; false otherwise.
      */
     boolean valid() {
-        boolean valid = true;
-        if (!isAgencyLogoPathValid()) {
-            valid = false;
-        }
-        if (!isMemFieldValid()) {
-            valid = false;
-        }
-        if (!isLogNumFieldValid()) {
-            valid = false;
-        }
-
-        return valid;
+        boolean agencyValid = isAgencyLogoPathValid();
+        boolean memFieldValid = isMemFieldValid();
+        boolean logNumValid = isLogNumFieldValid();
+        boolean heapPathValid = isHeapPathValid();
+        
+        return agencyValid && memFieldValid && logNumValid && heapPathValid;
     }
 
     /**
@@ -589,47 +751,41 @@ private boolean isLogNumFieldValid() {
 
     /**
      * Listens for registered text fields that have changed and fires a
-     * PropertyChangeEvent accordingly.
+     * PropertyChangeEvent accordingly as well as firing an optional additional listener.
      */
     private class TextFieldListener implements DocumentListener {
+        private final Runnable onChange;
 
-        @Override
-        public void insertUpdate(DocumentEvent e) {
-            firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
-        }
-
-        @Override
-        public void removeUpdate(DocumentEvent e) {
-            firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
+        
+        /**
+         * Main constructor.
+         * @param onChange Additional listener for change events.
+         */
+        TextFieldListener(Runnable onChange) {
+            this.onChange = onChange;
         }
-
-        @Override
-        public void changedUpdate(DocumentEvent e) {
+        
+        private void baseOnChange() {
+            if (onChange != null) {
+                onChange.run();    
+            }
+            
             firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
         }
-    }
-    
-    /**
-     * Listens for changes in the temp directory custom directory text field.
-     */
-    private class TempCustomTextListener extends TextFieldListener {
-
+        
         @Override
         public void changedUpdate(DocumentEvent e) {
-            evaluateTempDirState();
-            super.changedUpdate(e);
+            baseOnChange();
         }
 
         @Override
         public void removeUpdate(DocumentEvent e) {
-            evaluateTempDirState();
-            super.changedUpdate(e);
+            baseOnChange();
         }
 
         @Override
         public void insertUpdate(DocumentEvent e) {
-            evaluateTempDirState();
-            super.changedUpdate(e);
+            baseOnChange();
         }
         
         
@@ -673,6 +829,10 @@ private void initComponents() {
         maxMemoryUnitsLabel2 = new javax.swing.JLabel();
         solrMaxHeapSpinner = new javax.swing.JSpinner();
         solrJVMHeapWarning = new javax.swing.JLabel();
+        heapFileLabel = new javax.swing.JLabel();
+        heapDumpFileField = new javax.swing.JTextField();
+        heapDumpBrowseButton = new javax.swing.JButton();
+        heapFieldValidationLabel = new javax.swing.JLabel();
         tempDirectoryPanel = new javax.swing.JPanel();
         tempCustomField = new javax.swing.JTextField();
         tempDirectoryBrowseButton = new javax.swing.JButton();
@@ -823,6 +983,20 @@ public void stateChanged(javax.swing.event.ChangeEvent evt) {
 
         org.openide.awt.Mnemonics.setLocalizedText(solrJVMHeapWarning, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.solrJVMHeapWarning.text")); // NOI18N
 
+        org.openide.awt.Mnemonics.setLocalizedText(heapFileLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.heapFileLabel.text")); // NOI18N
+
+        heapDumpFileField.setText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.heapDumpFileField.text")); // NOI18N
+
+        org.openide.awt.Mnemonics.setLocalizedText(heapDumpBrowseButton, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.heapDumpBrowseButton.text")); // NOI18N
+        heapDumpBrowseButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                heapDumpBrowseButtonActionPerformed(evt);
+            }
+        });
+
+        heapFieldValidationLabel.setForeground(new java.awt.Color(255, 0, 0));
+        org.openide.awt.Mnemonics.setLocalizedText(heapFieldValidationLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.heapFieldValidationLabel.text")); // NOI18N
+
         javax.swing.GroupLayout runtimePanelLayout = new javax.swing.GroupLayout(runtimePanel);
         runtimePanel.setLayout(runtimePanelLayout);
         runtimePanelLayout.setHorizontalGroup(
@@ -851,14 +1025,26 @@ public void stateChanged(javax.swing.event.ChangeEvent evt) {
                             .addGroup(runtimePanelLayout.createSequentialGroup()
                                 .addGap(23, 23, 23)
                                 .addComponent(memFieldValidationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 478, javax.swing.GroupLayout.PREFERRED_SIZE)
-                                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                                .addContainerGap(12, Short.MAX_VALUE))
                             .addGroup(runtimePanelLayout.createSequentialGroup()
                                 .addGap(18, 18, 18)
                                 .addComponent(solrJVMHeapWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 331, javax.swing.GroupLayout.PREFERRED_SIZE)
                                 .addGap(44, 44, 44)
                                 .addComponent(logNumAlert)
                                 .addContainerGap())))
-                    .addComponent(restartNecessaryWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 615, javax.swing.GroupLayout.PREFERRED_SIZE)))
+                    .addGroup(runtimePanelLayout.createSequentialGroup()
+                        .addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                            .addComponent(restartNecessaryWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 615, javax.swing.GroupLayout.PREFERRED_SIZE)
+                            .addGroup(runtimePanelLayout.createSequentialGroup()
+                                .addComponent(heapFileLabel)
+                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                                .addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                                    .addComponent(heapFieldValidationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 478, javax.swing.GroupLayout.PREFERRED_SIZE)
+                                    .addGroup(runtimePanelLayout.createSequentialGroup()
+                                        .addComponent(heapDumpFileField, javax.swing.GroupLayout.PREFERRED_SIZE, 415, javax.swing.GroupLayout.PREFERRED_SIZE)
+                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                                        .addComponent(heapDumpBrowseButton)))))
+                        .addGap(0, 0, Short.MAX_VALUE))))
         );
 
         runtimePanelLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {maxLogFileCount, maxMemoryLabel, totalMemoryLabel});
@@ -892,7 +1078,14 @@ public void stateChanged(javax.swing.event.ChangeEvent evt) {
                 .addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                     .addComponent(maxLogFileCount)
                     .addComponent(logFileCount, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
-                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(heapFileLabel)
+                    .addComponent(heapDumpFileField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(heapDumpBrowseButton))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(heapFieldValidationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                 .addComponent(restartNecessaryWarning)
                 .addContainerGap())
         );
@@ -965,7 +1158,7 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
                                 .addComponent(tempCustomField, javax.swing.GroupLayout.PREFERRED_SIZE, 459, javax.swing.GroupLayout.PREFERRED_SIZE)
                                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                 .addComponent(tempDirectoryBrowseButton)))))
-                .addContainerGap(158, Short.MAX_VALUE))
+                .addContainerGap(164, Short.MAX_VALUE))
         );
         tempDirectoryPanelLayout.setVerticalGroup(
             tempDirectoryPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@@ -1162,6 +1355,24 @@ private void tempCustomRadioActionPerformed(java.awt.event.ActionEvent evt) {//G
         evaluateTempDirState();
     }//GEN-LAST:event_tempCustomRadioActionPerformed
 
+    @Messages({
+        "AutopsyOptionsPanel_heapDumpBrowseButtonActionPerformed_fileAlreadyExistsTitle=File Already Exists",
+        "AutopsyOptionsPanel_heapDumpBrowseButtonActionPerformed_fileAlreadyExistsMessage=File already exists.  Please select a new location."
+    })
+    private void heapDumpBrowseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_heapDumpBrowseButtonActionPerformed
+        String oldHeapPath = heapDumpFileField.getText();
+        if (!StringUtils.isBlank(oldHeapPath)) {
+            heapFileChooser.setCurrentDirectory(new File(oldHeapPath));
+        }
+        
+        int returnState = heapFileChooser.showOpenDialog(this);
+        if (returnState == JFileChooser.APPROVE_OPTION) {
+            File selectedDirectory = heapFileChooser.getSelectedFile();
+            heapDumpFileField.setText(selectedDirectory.getAbsolutePath());
+            firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
+        }
+    }//GEN-LAST:event_heapDumpBrowseButtonActionPerformed
+
     // Variables declaration - do not modify//GEN-BEGIN:variables
     private javax.swing.JTextField agencyLogoPathField;
     private javax.swing.JLabel agencyLogoPathFieldValidationLabel;
@@ -1170,6 +1381,10 @@ private void tempCustomRadioActionPerformed(java.awt.event.ActionEvent evt) {//G
     private javax.swing.JRadioButton defaultLogoRB;
     private javax.swing.ButtonGroup displayTimesButtonGroup;
     private javax.swing.ButtonGroup fileSelectionButtonGroup;
+    private javax.swing.JButton heapDumpBrowseButton;
+    private javax.swing.JTextField heapDumpFileField;
+    private javax.swing.JLabel heapFieldValidationLabel;
+    private javax.swing.JLabel heapFileLabel;
     private javax.swing.JScrollPane jScrollPane1;
     private javax.swing.JTextField logFileCount;
     private javax.swing.JTextField logNumAlert;
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties
index bad8f8ff838c96e508cf242752c70542469a8704..2deaab0d3b835dc7732e03ed492c5d7835a9b571 100644
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties
@@ -251,3 +251,7 @@ AutopsyOptionsPanel.tempCaseRadio.text=Temp folder in case directory
 AutopsyOptionsPanel.tempCustomRadio.text=Custom
 AutopsyOptionsPanel.tempCustomField.text=
 AutopsyOptionsPanel.tempOnCustomNoPath.text=Please select a path for the custom root temp directory.
+AutopsyOptionsPanel.heapDumpFileField.text=
+AutopsyOptionsPanel.heapDumpBrowseButton.text=Browse
+AutopsyOptionsPanel.heapFileLabel.text=Custom Heap Dump Location:
+AutopsyOptionsPanel.heapFieldValidationLabel.text=
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED
index bcf95a19a9a53f3ea4fd8f2875ebca2314375e7d..16fa8d0e5fada523bc62d1a7af91626adb6dedac 100755
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED
+++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED
@@ -12,6 +12,12 @@ AutopsyOptionsPanel.memFieldValidationLabel.noValueEntered.text=No value entered
 AutopsyOptionsPanel.memFieldValidationLabel.overMaxMemory.text=Value must be less than the total system memory of {0}GB
 # {0} - minimumMemory
 AutopsyOptionsPanel.memFieldValidationLabel.underMinMemory.text=Value must be at least {0}GB
+AutopsyOptionsPanel_heapDumpBrowseButtonActionPerformed_fileAlreadyExistsMessage=File already exists.  Please select a new location.
+AutopsyOptionsPanel_heapDumpBrowseButtonActionPerformed_fileAlreadyExistsTitle=File Already Exists
+AutopsyOptionsPanel_isHeapPathValid_developerMode=Cannot change heap dump path while in developer mode.
+AutopsyOPtionsPanel_isHeapPathValid_illegalCharacters=Please select a path with no quotes.
+AutopsyOptionsPanel_isHeapPathValid_not64BitMachine=Changing heap dump path settings only enabled for 64 bit version.
+AutopsyOptionsPanel_isHeapPathValid_selectValidDirectory=Please select an existing directory.
 AutopsyOptionsPanel_storeTempDir_onChoiceError_description=There was an error updating temporary directory choice selection.
 AutopsyOptionsPanel_storeTempDir_onChoiceError_title=Error Saving Temporary Directory Choice
 # {0} - path
@@ -311,3 +317,7 @@ AutopsyOptionsPanel.tempCaseRadio.text=Temp folder in case directory
 AutopsyOptionsPanel.tempCustomRadio.text=Custom
 AutopsyOptionsPanel.tempCustomField.text=
 AutopsyOptionsPanel.tempOnCustomNoPath.text=Please select a path for the custom root temp directory.
+AutopsyOptionsPanel.heapDumpFileField.text=
+AutopsyOptionsPanel.heapDumpBrowseButton.text=Browse
+AutopsyOptionsPanel.heapFileLabel.text=Custom Heap Dump Location:
+AutopsyOptionsPanel.heapFieldValidationLabel.text=
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/HostNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/HostNode.java
index 433a98972c6e2631d7c03c87d36ada0ccedc663e..a98bdb61ddd0733e682c6025857b26f691a1d232 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/HostNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/HostNode.java
@@ -78,13 +78,16 @@ private static class HostGroupingChildren extends ChildFactory.Detachable<DataSo
         }
 
         /**
-         * Listener for handling DATA_SOURCE_ADDED events.
+         * Listener for handling DATA_SOURCE_ADDED / HOST_DELETED events.
+         * A host may have been deleted as part of a merge, which means its data sources could
+         * have moved to a different host requiring a refresh.
          */
         private final PropertyChangeListener dataSourceAddedPcl = new PropertyChangeListener() {
             @Override
             public void propertyChange(PropertyChangeEvent evt) {
                 String eventType = evt.getPropertyName();
-                if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
+                if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())
+                        || eventType.equals(Case.Events.HOSTS_DELETED.toString())) {
                     refresh(true);
                 }
             }
@@ -92,12 +95,12 @@ public void propertyChange(PropertyChangeEvent evt) {
 
         @Override
         protected void addNotify() {
-            Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), dataSourceAddedPcl);
+            Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.HOSTS_DELETED), dataSourceAddedPcl);
         }
 
         @Override
         protected void removeNotify() {
-            Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), dataSourceAddedPcl);
+            Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.HOSTS_DELETED), dataSourceAddedPcl);
         }
 
         @Override
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java
index 4b2588c6bb7524705b5a6e1a9939e471139f8948..9b7802bdbc60508fc7151d40b10759395e1fb889 100755
--- a/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java
@@ -255,8 +255,7 @@ protected Sheet createSheet() {
                     Bundle.OsAccounts_loginNameProperty_displayName(),
                     Bundle.OsAccounts_loginNameProperty_desc(),
                     optional.isPresent() ? optional.get() : ""));
-
-            // TODO - load realm on background thread
+           // TODO - load realm on background thread
             String realmName = "";
             //String realmName = account.getRealm().getRealmNames().isEmpty() ? "" :  account.getRealm().getRealmNames().get(0);
             propertiesSet.put(new NodeProperty<>(
diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/ObjectDetectedFilterPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/ObjectDetectedFilterPanel.java
index b4a9a0182a001e9855f124d94358a5c7a1ed9caa..cd50214b84a81ebef69bea1230d473a7d96c5cd8 100644
--- a/Core/src/org/sleuthkit/autopsy/discovery/ui/ObjectDetectedFilterPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/ObjectDetectedFilterPanel.java
@@ -60,7 +60,7 @@ private void setUpObjectFilter() {
             objectsList.clearList();
             List<String> setNames = DiscoveryUiUtils.getSetNames(BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION);
             for (String name : setNames) {
-                objectsList.addElement(name, null, null);
+                objectsList.addElement(name, null, name);
             }
         } catch (TskCoreException ex) {
             logger.log(Level.SEVERE, "Error loading object detected set names", ex);
diff --git a/Core/src/org/sleuthkit/autopsy/machinesettings/UserMachinePreferences.java b/Core/src/org/sleuthkit/autopsy/machinesettings/UserMachinePreferences.java
index ef7b138ef057b1d2b730a0c20425b651d85cb32d..f0aa9b06d57101ae2076ce2046bbe32073bc7cd0 100644
--- a/Core/src/org/sleuthkit/autopsy/machinesettings/UserMachinePreferences.java
+++ b/Core/src/org/sleuthkit/autopsy/machinesettings/UserMachinePreferences.java
@@ -21,6 +21,7 @@
 import java.io.File;
 import java.nio.file.Paths;
 import java.util.Optional;
+import java.util.logging.Level;
 import java.util.prefs.Preferences;
 import java.util.stream.Stream;
 import org.apache.commons.lang3.StringUtils;
@@ -30,12 +31,15 @@
 import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
 import org.sleuthkit.autopsy.core.UserPreferences;
 import org.sleuthkit.autopsy.coreutils.FileUtil;
+import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.autopsy.coreutils.NetworkUtils;
 
 /**
  * Provides case-specific settings like the user-specified temp folder.
  */
 public final class UserMachinePreferences {
 
+    private static final Logger logger = Logger.getLogger(UserMachinePreferences.class.getName());
     private static final Preferences preferences = NbPreferences.forModule(UserMachinePreferences.class);
 
     /**
@@ -81,6 +85,17 @@ static Optional<TempDirChoice> getValue(String val) {
 
     private static final TempDirChoice DEFAULT_CHOICE = TempDirChoice.SYSTEM;
 
+    /**
+     * Returns the name of this computer's host name to be used as a directory
+     * in some instances.
+     *
+     * @return The name of this computer's host name to be used as a directory
+     * in some instances.
+     */
+    private static String getHostName() {
+        return NetworkUtils.getLocalHostName();
+    }
+
     /**
      * @return A subdirectory of java.io.tmpdir.
      */
@@ -94,8 +109,16 @@ private static File getSystemTempDirFile() {
      */
     private static File getCaseTempDirFile() {
         try {
-            String caseDirStr = Case.getCurrentCaseThrows().getCaseDirectory();
-            return Paths.get(caseDirStr, CASE_SUBDIR).toFile();
+            Case autCase = Case.getCurrentCaseThrows();
+            String caseDirStr = autCase.getCaseDirectory();
+            switch (autCase.getCaseType()) {
+                case MULTI_USER_CASE: return Paths.get(caseDirStr, getHostName(), CASE_SUBDIR).toFile();
+                case SINGLE_USER_CASE: return Paths.get(caseDirStr, CASE_SUBDIR).toFile();
+                default: 
+                    logger.log(Level.SEVERE, "Unknown case type: " + autCase.getCaseType());
+                    return getSystemTempDirFile();
+            }
+            
         } catch (NoCurrentCaseException ex) {
             return getSystemTempDirFile();
         }
@@ -111,7 +134,7 @@ private static File getCaseTempDirFile() {
     private static File getCustomTempDirFile() {
         String customDirectory = getCustomTempDirectory();
         return (StringUtils.isBlank(customDirectory))
-                ? getSystemTempDirFile() : Paths.get(customDirectory, AUTOPSY_SUBDIR).toFile();
+                ? getSystemTempDirFile() : Paths.get(customDirectory, AUTOPSY_SUBDIR, getHostName()).toFile();
     }
 
     /**
@@ -214,8 +237,9 @@ public static TempDirChoice getTempDirChoice() {
 
     /**
      * Sets the temp directory choice (i.e. system, case, custom).
+     *
      * @param tempDirChoice The choice (must be non-null).
-     * @throws UserMachinePreferencesException 
+     * @throws UserMachinePreferencesException
      */
     public static void setTempDirChoice(TempDirChoice tempDirChoice) throws UserMachinePreferencesException {
         if (tempDirChoice == null) {
diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/WebCategoriesDataModel.java b/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/WebCategoriesDataModel.java
index 5b2932b7186ec1233cc99d0a2915110b768cf881..bb5c2abbae641ef84290d2b045a5329e5f692219 100644
--- a/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/WebCategoriesDataModel.java
+++ b/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/WebCategoriesDataModel.java
@@ -26,6 +26,7 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 import java.io.File;
 import java.io.IOException;
+import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.sql.Connection;
 import java.sql.DriverManager;
@@ -44,7 +45,7 @@
 import java.util.stream.Stream;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
-import org.openide.modules.InstalledFileLocator;
+import org.sleuthkit.autopsy.coreutils.PlatformUtil;
 import org.sleuthkit.autopsy.url.analytics.DomainCategory;
 
 /**
@@ -136,13 +137,19 @@ static int getMaxCategoryLength() {
      * @return The path or null if the path cannot be reconciled.
      */
     private static File getDefaultPath() {
-        File dir = InstalledFileLocator.getDefault().locate(ROOT_FOLDER, WebCategoriesDataModel.class.getPackage().getName(), false);
-        if (dir == null || !dir.exists()) {
-            logger.log(Level.WARNING, String.format("Unable to find file %s with InstalledFileLocator", ROOT_FOLDER));
+        String configDir = PlatformUtil.getUserConfigDirectory();
+        if (configDir == null || !new File(configDir).exists()) {
+            logger.log(Level.WARNING, "Unable to find UserConfigDirectory");
             return null;
         }
 
-        return Paths.get(dir.getAbsolutePath(), FILE_REL_PATH).toFile();
+        Path subDirPath = Paths.get(configDir, ROOT_FOLDER);
+        File subDir = subDirPath.toFile();
+        if (!subDir.exists() && !subDir.mkdirs()) {
+                logger.log(Level.WARNING, "There was an issue creating custom domain config at: {0}", subDirPath.toString());
+        }
+
+        return Paths.get(configDir, ROOT_FOLDER, FILE_REL_PATH).toFile();
     }
 
     /**
@@ -530,7 +537,7 @@ private List<String> getSuffixes(String host) {
     public synchronized void close() throws SQLException {
         if (dbConn != null) {
             dbConn.close();
-            dbConn = null;   
+            dbConn = null;
         }
     }
 }
diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoAccountsTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoAccountsTest.java
index a87c2959badf359285f1e25271cc84660c47f092..537835bc01c25dd2cdf0fa9e5f1bcd10f9480502 100755
--- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoAccountsTest.java
+++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoAccountsTest.java
@@ -22,6 +22,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Optional;
 import junit.framework.Assert;
 import junit.framework.TestCase;
 import junit.framework.Test;
@@ -104,10 +105,11 @@ public void testPredefinedAccountTypes() {
             if(expectedAccountType == Account.Type.DEVICE) continue;
             
             try {
-                CentralRepoAccountType crAccountType = CentralRepository.getInstance()
+                Optional<CentralRepoAccountType> optCrAccountType = CentralRepository.getInstance()
                         .getAccountTypeByName(expectedAccountType.getTypeName());
+                Assert.assertTrue(optCrAccountType.isPresent());
                 
-                Account.Type actualAccountType = crAccountType.getAcctType();
+                Account.Type actualAccountType = optCrAccountType.get().getAcctType();
                 Assert.assertEquals(expectedAccountType, actualAccountType);
             } catch (CentralRepoException ex) {
                 Assert.fail("Didn't expect an exception here. Exception: " + ex);
@@ -118,35 +120,34 @@ public void testPredefinedAccountTypes() {
     public void testRejectionOfDeviceAccountType() {
         try {
             Account.Type deviceAccount = Account.Type.DEVICE;
-            CentralRepository.getInstance()
+            Optional<CentralRepoAccountType> optType = CentralRepository.getInstance()
                     .getAccountTypeByName(deviceAccount.getTypeName());
-            Assert.fail("Expected an exception from getAccountTypeByName() when"
-                    + " querying the device account type");
+            Assert.assertFalse(optType.isPresent());
         } catch (CentralRepoException ex) {
-            // Pass
+            Assert.fail("Didn't expect an exception here. Exception: " + ex);
         }
     }
 
     public void testNonExistentAccountType() {
         try {
-            CentralRepository.getInstance()
+            Optional<CentralRepoAccountType> optType = CentralRepository.getInstance()
                     .getAccountTypeByName("NotARealAccountType");
-            Assert.fail("Expected an exception from getAccountTypeByName()"
-                    + " when querying a non-existent account type");
+            Assert.assertFalse(optType.isPresent());
         } catch (CentralRepoException ex) {
-            // Pass
+            Assert.fail("Didn't expect an exception here. Exception: " + ex);
         }
     }
     
     public void testCreatingAccount() {
         try {
             Account.Type facebookAccountType = Account.Type.FACEBOOK;
-            CentralRepoAccountType expectedAccountType = CentralRepository.getInstance()
+            Optional<CentralRepoAccountType> optExpectedAccountType = CentralRepository.getInstance()
                     .getAccountTypeByName(facebookAccountType.getTypeName());
+            assertTrue(optExpectedAccountType.isPresent());
             
             // Create the account
             CentralRepository.getInstance()
-                    .getOrCreateAccount(expectedAccountType, "+1 401-231-2552");
+                    .getOrCreateAccount(optExpectedAccountType.get(), "+1 401-231-2552");
         } catch (InvalidAccountIDException | CentralRepoException ex) {
              Assert.fail("Didn't expect an exception here. Exception: " + ex);
         }
@@ -155,19 +156,20 @@ public void testCreatingAccount() {
     public void testRetreivingAnAccount() {
         try {
             Account.Type facebookAccountType = Account.Type.FACEBOOK;
-            CentralRepoAccountType expectedAccountType = CentralRepository
+            Optional<CentralRepoAccountType> optExpectedAccountType = CentralRepository
                     .getInstance()
                     .getAccountTypeByName(facebookAccountType.getTypeName());
+            assertTrue(optExpectedAccountType.isPresent());
             
             // Create the account
             CentralRepository.getInstance()
-                    .getOrCreateAccount(expectedAccountType, "+1 441-231-2552");
+                    .getOrCreateAccount(optExpectedAccountType.get(), "+1 441-231-2552");
             
             // Retrieve the account
             CentralRepoAccount actualAccount = CentralRepository.getInstance()
-                    .getOrCreateAccount(expectedAccountType, "+1 441-231-2552");
+                    .getOrCreateAccount(optExpectedAccountType.get(), "+1 441-231-2552");
             
-            Assert.assertEquals(expectedAccountType, actualAccount.getAccountType());
+            Assert.assertEquals(optExpectedAccountType.get(), actualAccount.getAccountType());
             Assert.assertEquals("+1 441-231-2552", actualAccount.getIdentifier());
         } catch (InvalidAccountIDException | CentralRepoException ex) {
              Assert.fail("Didn't expect an exception here. Exception: " + ex);
diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoPersonasTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoPersonasTest.java
index 199a48b8451089e82a4711ea1d3c8f72750f3300..47372fcc07826e3e654963a894c63827a3a975f4 100644
--- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoPersonasTest.java
+++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoPersonasTest.java
@@ -24,6 +24,7 @@
 import java.nio.file.Paths;
 import java.time.Instant;
 import java.util.Collection;
+import java.util.Optional;
 import junit.framework.Assert;
 import static junit.framework.Assert.assertTrue;
 import junit.framework.TestCase;
@@ -222,27 +223,39 @@ public void setUp() throws CentralRepoException, IOException {
             org2 = CentralRepository.getInstance().newOrganization(org2);
 
             // get some correltion types for different account types, for later use
-            phoneAccountType = CentralRepository.getInstance().getAccountTypeByName( Account.Type.PHONE.getTypeName());
+            Optional<CentralRepoAccount.CentralRepoAccountType> optType = CentralRepository.getInstance().getAccountTypeByName( Account.Type.PHONE.getTypeName());
+            assertTrue(optType.isPresent());
+            phoneAccountType = optType.get();
             phoneInstanceType = CentralRepository.getInstance().getCorrelationTypeById(phoneAccountType.getCorrelationTypeId());
             assertTrue("getCorrelationTypeById(PHONE) returned null", phoneInstanceType != null);
             
-            emailAccountType = CentralRepository.getInstance().getAccountTypeByName( Account.Type.EMAIL.getTypeName());
+            optType = CentralRepository.getInstance().getAccountTypeByName( Account.Type.EMAIL.getTypeName());
+            assertTrue(optType.isPresent());
+            emailAccountType = optType.get();
             emailInstanceType = CentralRepository.getInstance().getCorrelationTypeById(emailAccountType.getCorrelationTypeId());
             assertTrue("getCorrelationTypeById(EMAIL) returned null", emailInstanceType != null);
             
-            facebookAccountType = CentralRepository.getInstance().getAccountTypeByName( Account.Type.FACEBOOK.getTypeName());
+            optType = CentralRepository.getInstance().getAccountTypeByName( Account.Type.FACEBOOK.getTypeName());
+            assertTrue(optType.isPresent());
+            facebookAccountType = optType.get();
             facebookInstanceType = CentralRepository.getInstance().getCorrelationTypeById(facebookAccountType.getCorrelationTypeId());
             assertTrue("getCorrelationTypeById(FACEBOOK) returned null", facebookInstanceType != null);
             
-            textnowAccountType = CentralRepository.getInstance().getAccountTypeByName( Account.Type.TEXTNOW.getTypeName());
+            optType = CentralRepository.getInstance().getAccountTypeByName( Account.Type.TEXTNOW.getTypeName());
+            assertTrue(optType.isPresent());
+            textnowAccountType = optType.get();
             textnowInstanceType = CentralRepository.getInstance().getCorrelationTypeById(textnowAccountType.getCorrelationTypeId());
             assertTrue("getCorrelationTypeById(TEXTNOW) returned null", textnowInstanceType != null);
             
-            whatsAppAccountType = CentralRepository.getInstance().getAccountTypeByName( Account.Type.WHATSAPP.getTypeName());
+            optType = CentralRepository.getInstance().getAccountTypeByName( Account.Type.WHATSAPP.getTypeName());
+            assertTrue(optType.isPresent());
+            whatsAppAccountType = optType.get();
             whatsAppInstanceType = CentralRepository.getInstance().getCorrelationTypeById(whatsAppAccountType.getCorrelationTypeId());
             assertTrue("getCorrelationTypeById(WHATSAPP) returned null", whatsAppInstanceType != null);
             
-            skypeAccountType = CentralRepository.getInstance().getAccountTypeByName( Account.Type.SKYPE.getTypeName());
+            optType = CentralRepository.getInstance().getAccountTypeByName( Account.Type.SKYPE.getTypeName());
+            assertTrue(optType.isPresent());
+            skypeAccountType = optType.get();
             skypeInstanceType = CentralRepository.getInstance().getCorrelationTypeById(skypeAccountType.getCorrelationTypeId());
             assertTrue("getCorrelationTypeById(SKYPE) returned null", skypeInstanceType != null);
             
diff --git a/RecentActivity/nbproject/project.properties b/RecentActivity/nbproject/project.properties
index 9736070e535c751845709a9d241ec4b2a13a5faa..aab9fa2a60e077c0fb1c1e3ba57f7241c5d30e64 100644
--- a/RecentActivity/nbproject/project.properties
+++ b/RecentActivity/nbproject/project.properties
@@ -1,3 +1,4 @@
+file.reference.Rejistry-1.1-SNAPSHOT.jar=release/modules/ext/Rejistry-1.1-SNAPSHOT.jar
 javac.source=1.8
 javac.compilerargs=-Xlint -Xlint:-serial
 license.file=../LICENSE-2.0.txt
diff --git a/RecentActivity/nbproject/project.xml b/RecentActivity/nbproject/project.xml
index af8c2edace68d6a0b8b52f08a71e4978cafa5baf..8fc5e13b534f82e9b645a481875399df3d77f1e3 100644
--- a/RecentActivity/nbproject/project.xml
+++ b/RecentActivity/nbproject/project.xml
@@ -74,6 +74,10 @@
                 </dependency>
             </module-dependencies>
             <public-packages/>
+            <class-path-extension>
+                <runtime-relative-path>ext/Rejistry-1.1-SNAPSHOT.jar</runtime-relative-path>
+                <binary-origin>release/modules/ext/Rejistry-1.1-SNAPSHOT.jar</binary-origin>
+            </class-path-extension>
         </data>
     </configuration>
 </project>
diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED
index 26181755d016e4d90d483658503293e81ef0ffb7..4538c2836cdbded63b1f4245fc5c69996d6501ce 100755
--- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED
+++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED
@@ -15,6 +15,7 @@ DataSourceUsage_DJU_Drone_DAT=DJI Internal SD Card
 DataSourceUsage_FlashDrive=Flash Drive
 DataSourceUsageAnalyzer.customVolume.label=OS Drive ({0})
 DataSourceUsageAnalyzer.parentModuleName=Recent Activity
+DefaultPriorityDomainCategorizer_searchEngineCategory=Search Engine
 DomainCategoryRunner_moduleName_text=DomainCategoryRunner
 DomainCategoryRunner_parentModuleName=Recent Activity
 DomainCategoryRunner_Progress_Message_Domain_Types=Finding Domain Types
diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/DefaultDomainCategorizer.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/DefaultDomainCategorizer.java
index 0d64661f6c0cd544e1a7ee5431bb8bdc44edaff2..9519a37bb14d5aeb8d1570e64a713ae67047be8c 100644
--- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/DefaultDomainCategorizer.java
+++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/DefaultDomainCategorizer.java
@@ -128,18 +128,33 @@ private static void addItem(Map<String, String> mapping, String line, int lineNu
     private Map<String, String> mapping = null;
 
     @Override
-    public void initialize() throws DomainCategorizerException {
-        if (this.mapping == null) {
-            try {
-                this.mapping = loadMapping();
-            } catch (IOException ex) {
-                throw new DomainCategorizerException("Unable to load domain type csv for domain category analysis", ex);
-            }
+    public synchronized void initialize() throws DomainCategorizerException {
+        if (isInitialized()) {
+            return;
+        }
+
+        try {
+            this.mapping = loadMapping();
+        } catch (IOException ex) {
+            throw new DomainCategorizerException("Unable to load domain type csv for domain category analysis", ex);
         }
     }
 
+    /**
+     * Returns true if this categorizer is properly initialized.
+     *
+     * @return True if this categorizer is properly initialized.
+     */
+    private synchronized boolean isInitialized() {
+        return this.mapping != null;
+    }
+
     @Override
-    public DomainCategory getCategory(String domain, String host) throws DomainCategorizerException {
+    public synchronized DomainCategory getCategory(String domain, String host) throws DomainCategorizerException {
+        if (!isInitialized()) {
+            initialize();
+        }
+
         // use host; use domain as fallback if no host provided
         String hostToUse = StringUtils.isBlank(host) ? domain : host;
 
@@ -162,7 +177,7 @@ public DomainCategory getCategory(String domain, String host) throws DomainCateg
     }
 
     @Override
-    public void close() throws Exception {
+    public synchronized void close() throws Exception {
         // clear out the mapping to release resources
         mapping = null;
     }
diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/DefaultPriorityDomainCategorizer.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/DefaultPriorityDomainCategorizer.java
new file mode 100644
index 0000000000000000000000000000000000000000..da84660cc282a24db6c10c9925a2761384588019
--- /dev/null
+++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/DefaultPriorityDomainCategorizer.java
@@ -0,0 +1,104 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2021 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.recentactivity;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.commons.lang.StringUtils;
+import org.openide.util.NbBundle.Messages;
+import org.sleuthkit.autopsy.url.analytics.DomainCategorizer;
+import org.sleuthkit.autopsy.url.analytics.DomainCategorizerException;
+import org.sleuthkit.autopsy.url.analytics.DomainCategory;
+
+/**
+ * The autopsy provided domain category provider that overrides all domain
+ * category providers except the custom web domain categorizations.
+ */
+@Messages({
+    "DefaultPriorityDomainCategorizer_searchEngineCategory=Search Engine"
+})
+public class DefaultPriorityDomainCategorizer implements DomainCategorizer {
+
+    // taken from https://www.google.com/supported_domains
+    private static final List<String> GOOGLE_DOMAINS = Arrays.asList("google.com", "google.ad", "google.ae", "google.com.af", "google.com.ag", "google.com.ai", "google.al", "google.am", "google.co.ao", "google.com.ar", "google.as", "google.at", "google.com.au", "google.az", "google.ba", "google.com.bd", "google.be", "google.bf", "google.bg", "google.com.bh", "google.bi", "google.bj", "google.com.bn", "google.com.bo", "google.com.br", "google.bs", "google.bt", "google.co.bw", "google.by", "google.com.bz", "google.ca", "google.cd", "google.cf", "google.cg", "google.ch", "google.ci", "google.co.ck", "google.cl", "google.cm", "google.cn", "google.com.co", "google.co.cr", "google.com.cu", "google.cv", "google.com.cy", "google.cz", "google.de", "google.dj", "google.dk", "google.dm", "google.com.do", "google.dz", "google.com.ec", "google.ee", "google.com.eg", "google.es", "google.com.et", "google.fi", "google.com.fj", "google.fm", "google.fr", "google.ga", "google.ge", "google.gg", "google.com.gh", "google.com.gi", "google.gl", "google.gm", "google.gr", "google.com.gt", "google.gy", "google.com.hk", "google.hn", "google.hr", "google.ht", "google.hu", "google.co.id", "google.ie", "google.co.il", "google.im", "google.co.in", "google.iq", "google.is", "google.it", "google.je", "google.com.jm", "google.jo", "google.co.jp", "google.co.ke", "google.com.kh", "google.ki", "google.kg", "google.co.kr", "google.com.kw", "google.kz", "google.la", "google.com.lb", "google.li", "google.lk", "google.co.ls", "google.lt", "google.lu", "google.lv", "google.com.ly", "google.co.ma", "google.md", "google.me", "google.mg", "google.mk", "google.ml", "google.com.mm", "google.mn", "google.ms", "google.com.mt", "google.mu", "google.mv", "google.mw", "google.com.mx", "google.com.my", "google.co.mz", "google.com.na", "google.com.ng", "google.com.ni", "google.ne", "google.nl", "google.no", "google.com.np", "google.nr", "google.nu", "google.co.nz", "google.com.om", "google.com.pa", "google.com.pe", "google.com.pg", "google.com.ph", "google.com.pk", "google.pl", "google.pn", "google.com.pr", "google.ps", "google.pt", "google.com.py", "google.com.qa", "google.ro", "google.ru", "google.rw", "google.com.sa", "google.com.sb", "google.sc", "google.se", "google.com.sg", "google.sh", "google.si", "google.sk", "google.com.sl", "google.sn", "google.so", "google.sm", "google.sr", "google.st", "google.com.sv", "google.td", "google.tg", "google.co.th", "google.com.tj", "google.tl", "google.tm", "google.tn", "google.to", "google.com.tr", "google.tt", "google.com.tw", "google.co.tz", "google.com.ua", "google.co.ug", "google.co.uk", "google.com.uy", "google.co.uz", "google.com.vc", "google.co.ve", "google.vg", "google.co.vi", "google.com.vn", "google.vu", "google.ws", "google.rs", "google.co.za", "google.co.zm", "google.co.zw", "google.cat");
+
+    // taken from https://www.yahoo.com/everything/world
+    private static final List<String> YAHOO_DOMAINS = Arrays.asList("espanol.yahoo.com", "au.yahoo.com", "be.yahoo.com", "fr-be.yahoo.com", "br.yahoo.com", "ca.yahoo.com", "espanol.yahoo.com", "espanol.yahoo.com", "de.yahoo.com", "es.yahoo.com", "espanol.yahoo.com", "fr.yahoo.com", "in.yahoo.com", "id.yahoo.com", "ie.yahoo.com", "it.yahoo.com", "en-maktoob.yahoo.com", "malaysia.yahoo.com", "espanol.yahoo.com", "nz.yahoo.com", "espanol.yahoo.com", "ph.yahoo.com", "qc.yahoo.com", "ro.yahoo.com", "sg.yahoo.com", "za.yahoo.com", "se.yahoo.com", "uk.yahoo.com", "yahoo.com", "espanol.yahoo.com", "vn.yahoo.com", "gr.yahoo.com", "maktoob.yahoo.com", "yahoo.com", "hk.yahoo.com", "tw.yahoo.com", "yahoo.co.jp");
+
+    private static final List<String> OTHER_SEARCH_ENGINES = Arrays.asList(
+            "bing.com",
+            "baidu.com",
+            "sogou.com",
+            "soso.com",
+            "duckduckgo.com",
+            "swisscows.com",
+            "gibiru.com",
+            "cutestat.com",
+            "youdao.com",
+            "biglobe.ne.jp",
+            "givewater.com",
+            "ekoru.org",
+            "ecosia.org",
+            // according to https://en.wikipedia.org/wiki/Yandex
+            "yandex.ru",
+            "yandex.com"
+    );
+
+    private static final String WWW_PREFIX = "www";
+
+    private static final Map<String, String> DOMAIN_LOOKUP
+            = Stream.of(GOOGLE_DOMAINS, YAHOO_DOMAINS, OTHER_SEARCH_ENGINES)
+                    .flatMap((lst) -> lst.stream())
+                    .collect(Collectors.toMap((k) -> k, (k) -> Bundle.DefaultPriorityDomainCategorizer_searchEngineCategory(), (v1, v2) -> v1));
+
+    @Override
+    public void initialize() throws DomainCategorizerException {
+    }
+
+    @Override
+    public DomainCategory getCategory(String domain, String host) throws DomainCategorizerException {
+
+        String hostToUse = StringUtils.isBlank(host) ? domain : host;
+
+        if (StringUtils.isBlank(hostToUse)) {
+            return null;
+        }
+
+        List<String> domainWords = Stream.of(hostToUse.toLowerCase().split("\\."))
+                .filter(StringUtils::isNotBlank)
+                .map(String::trim)
+                .collect(Collectors.toList());
+
+        String sanitizedDomain = domainWords.stream()
+                // skip first word segment if 'www'
+                .skip(domainWords.size() > 0 && WWW_PREFIX.equals(domainWords.get(0)) ? 1 : 0)
+                .collect(Collectors.joining("."));
+
+        String category = DOMAIN_LOOKUP.get(sanitizedDomain);
+        return category == null ? null : new DomainCategory(sanitizedDomain, category);
+    }
+
+    @Override
+    public void close() throws IOException {
+    }
+}
diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/DomainCategoryRunner.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/DomainCategoryRunner.java
index 0102f6e868640417ccc5cca149efb6f639737122..d24a031a48414d8ce32239a231bd7890a8e50518 100644
--- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/DomainCategoryRunner.java
+++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/DomainCategoryRunner.java
@@ -20,6 +20,7 @@
 
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -453,33 +454,45 @@ public void process(Content dataSource, IngestJobContext context, DataSourceInge
     @Override
     void configExtractor() throws IngestModule.IngestModuleException {
         // lookup all providers, filter null providers, and sort providers
-        Collection<? extends DomainCategorizer> lookupList = Lookup.getDefault().lookupAll(DomainCategorizer.class);
-        if (lookupList == null) {
-            lookupList = Collections.emptyList();
-        }
-
-        List<DomainCategorizer> foundProviders = lookupList.stream()
-                .filter(provider -> provider != null)
-                .sorted((a, b) -> {
-                    boolean aIsCustom = a.getClass().getName().contains(CUSTOM_CATEGORIZER_PATH);
-                    boolean bIsCustom = b.getClass().getName().contains(CUSTOM_CATEGORIZER_PATH);
-                    if (aIsCustom != bIsCustom) {
-                        // push custom categorizer to top
-                        return -Boolean.compare(aIsCustom, bIsCustom);
-                    }
-
-                    return a.getClass().getName().compareToIgnoreCase(b.getClass().getName());
+        Collection<? extends DomainCategorizer> lookupCollection = Lookup.getDefault().lookupAll(DomainCategorizer.class);
+        Collection<? extends DomainCategorizer> lookupList = (lookupCollection == null) ? 
+                Collections.emptyList() :
+                lookupCollection;
+        
+        // this will be the class instance of the foundProviders
+        List<DomainCategorizer> foundProviders = new ArrayList<>();
+        
+        // find the custom domain categories provider if present and add it first to the list
+        lookupList.stream()
+                .filter(categorizer -> categorizer.getClass().getName().contains(CUSTOM_CATEGORIZER_PATH))
+                .findFirst()
+                .ifPresent((provider) -> foundProviders.add(provider));
+                
+        // add the default priority categorizer
+        foundProviders.add(new DefaultPriorityDomainCategorizer());
+        
+        // add all others except for the custom web domain categorizer, the default priority 
+        // categorizer and the default categorizer
+        lookupList.stream()
+                .filter(categorizer -> categorizer != null)
+                .filter(categorizer -> {
+                    String className = categorizer.getClass().getName();
+                    return !className.contains(CUSTOM_CATEGORIZER_PATH) &&
+                            !className.equals(DefaultPriorityDomainCategorizer.class.getName()) &&
+                            !className.equals(DefaultDomainCategorizer.class.getName());
                 })
-                .collect(Collectors.toList());
-
-        // add the default categorizer last as a last resort
+                .sorted((a, b) -> a.getClass().getName().compareToIgnoreCase(b.getClass().getName()))
+                .forEach(foundProviders::add);
+        
+        // add the default categorizer last
         foundProviders.add(new DefaultDomainCategorizer());
-
+        
         for (DomainCategorizer provider : foundProviders) {
             try {
                 provider.initialize();
             } catch (DomainCategorizerException ex) {
-                throw new IngestModule.IngestModuleException("There was an error instantiating the provider: " + provider.getClass().getSimpleName(), ex);
+                throw new IngestModule.IngestModuleException("There was an error instantiating the provider: " + 
+                        provider.getClass().getSimpleName(), ex);
             }
         }
 
diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractPrefetch.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractPrefetch.java
index c7f17d6f7696ac91a06da77d59c90a22af1dd52a..32312c2ee1ea28550c188c82fb4dc83dc7522e6b 100644
--- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractPrefetch.java
+++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractPrefetch.java
@@ -33,9 +33,13 @@
 import java.util.List;
 import java.util.Set;
 import java.util.logging.Level;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.commons.io.FilenameUtils;
 import org.openide.modules.InstalledFileLocator;
 import org.openide.util.NbBundle.Messages;
 import org.sleuthkit.autopsy.casemodule.Case;
+import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
 import org.sleuthkit.autopsy.casemodule.services.FileManager;
 import org.sleuthkit.autopsy.coreutils.ExecUtil;
 import org.sleuthkit.autopsy.coreutils.Logger;
@@ -62,8 +66,6 @@ final class ExtractPrefetch extends Extract {
 
     private IngestJobContext context;
 
-    private static final String MODULE_NAME = "extractPREFETCH"; //NON-NLS
-
     private static final String PREFETCH_TSK_COMMENT = "Prefetch File";
     private static final String PREFETCH_FILE_LOCATION = "/windows/prefetch";
     private static final String PREFETCH_TOOL_FOLDER = "markmckinnon"; //NON-NLS
@@ -147,8 +149,13 @@ void extractPrefetchFiles(Content dataSource) {
                 return;
             }
 
-            String prefetchFile = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), dataSource.getName() + "-" + PREFETCH_DIR_NAME) + File.separator + pFile.getName();
-            if (pFile.getParentPath().toLowerCase().contains(PREFETCH_FILE_LOCATION.toLowerCase())) {
+            if (pFile.getParentPath().toLowerCase().contains(PREFETCH_FILE_LOCATION.toLowerCase()) && pFile.getSize() > 0) {
+                String origFileName = pFile.getName();
+                String ext = FilenameUtils.getExtension(origFileName);
+                String baseName = FilenameUtils.getBaseName(origFileName);
+                String fileName = String.format("%s_%d.%s", baseName, pFile.getId(), ext);
+                String baseRaTempPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), dataSource.getName() + "-" + PREFETCH_DIR_NAME);
+                String prefetchFile =  Paths.get(baseRaTempPath, fileName).toString();
                 try {
                     ContentUtils.writeToFile(pFile, new File(prefetchFile));
                 } catch (IOException ex) {
@@ -259,10 +266,32 @@ private void createAppExecArtifacts(String prefetchDb, Content dataSource) {
                 String timesProgramRun = resultSet.getString("Number_time_file_run");
                 String filePath = resultSet.getString("file_path");
 
-                AbstractFile pfAbstractFile = getAbstractFile(prefetchFileName, PREFETCH_FILE_LOCATION, dataSource);
-
                 Set<Long> prefetchExecutionTimes = findNonZeroExecutionTimes(executionTimes);
 
+                String baseName = FilenameUtils.getBaseName(prefetchFileName);
+                Matcher match = Pattern.compile("_(?<objId>\\d*)\\s*$").matcher(baseName);
+                if (!match.find()) {
+                    logger.log(Level.WARNING, "Invalid format for PF file: " + prefetchFileName);//NON-NLS
+                    continue;
+                }
+                
+                
+                /**
+                 * A prefetch file is created when a program is run and the superfetch service collected data about the first 10 
+                 * seconds of the run, the trace data is then written to a new prefetch file or merged with an existing prefetch file.  
+                 * If the prefetch file gets deleted for some reason then a new one will be created.  See 7500 in JIRA for more 
+                 * information.
+                 */
+                AbstractFile pfAbstractFile = null;
+                try {
+                    Content c = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(Long.parseLong(match.group("objId")));
+                    if (c instanceof AbstractFile) {
+                        pfAbstractFile = (AbstractFile) c;
+                    }
+                } catch (NoCurrentCaseException | TskCoreException | NumberFormatException ex ) {
+                    logger.log(Level.SEVERE, "Unable to find content for: " + prefetchFileName, ex);
+                }
+                        
                 if (pfAbstractFile != null) {
                     for (Long executionTime : prefetchExecutionTimes) {
 
@@ -305,26 +334,7 @@ BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT, getName(), Integer.valueOf(timesPr
             postArtifacts(blkBrdArtList);
         }
     }
-
-    /**
-     * Cycle thru the execution times list and only return a new list of times
-     * that are greater than zero.
-     *
-     * @param executionTimes - list of prefetch execution times 8 possible
-     *                       timestamps
-     *
-     * @return List of timestamps that are greater than zero
-     */
-    private Set<Long> findNonZeroExecutionTimes(List<Long> executionTimes) {
-        Set<Long> prefetchExecutionTimes = new HashSet<>();
-        for (Long executionTime : executionTimes) {                        // only add prefetch file entries that have an actual date associated with them
-            if (executionTime > 0) {
-                prefetchExecutionTimes.add(executionTime);
-            }
-        }
-        return prefetchExecutionTimes;
-    }
-
+    
     /**
      * Create associated artifacts using file path name and the artifact it
      * associates with
@@ -339,7 +349,7 @@ private Set<Long> findNonZeroExecutionTimes(List<Long> executionTimes) {
     private BlackboardArtifact createAssociatedArtifact(String fileName, String filePathName, BlackboardArtifact bba, Content dataSource) throws TskCoreException {
         AbstractFile sourceFile = getAbstractFile(fileName, filePathName, dataSource);
         if (sourceFile != null) {
-            return  createAssociatedArtifact(sourceFile, bba);         
+            return createAssociatedArtifact(sourceFile, bba);         
         }
         return null;
     }
@@ -368,7 +378,6 @@ AbstractFile getAbstractFile(String fileName, String filePath, Content dataSourc
         }
 
         for (AbstractFile pFile : files) {
-
             if (pFile.getParentPath().toLowerCase().endsWith(filePath.toLowerCase() + '/')) {
                 return pFile;
             }
@@ -376,6 +385,24 @@ AbstractFile getAbstractFile(String fileName, String filePath, Content dataSourc
 
         return null;
 
-    }
+    }    
 
+    /**
+     * Cycle thru the execution times list and only return a new list of times
+     * that are greater than zero.
+     *
+     * @param executionTimes - list of prefetch execution times 8 possible
+     *                       timestamps
+     *
+     * @return List of timestamps that are greater than zero
+     */
+    private Set<Long> findNonZeroExecutionTimes(List<Long> executionTimes) {
+        Set<Long> prefetchExecutionTimes = new HashSet<>();
+        for (Long executionTime : executionTimes) {                        // only add prefetch file entries that have an actual date associated with them
+            if (executionTime > 0) {
+                prefetchExecutionTimes.add(executionTime);
+            }
+        }
+        return prefetchExecutionTimes;
+    }
 }
diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java
index 3330e6181a623a6f98017ef56378c428fc5e20a4..85c622cc621dd1650cf51cc1aa492a9a9e3d6bbf 100644
--- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java
+++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java
@@ -175,6 +175,9 @@ class ExtractRegistry extends Extract {
     private IngestJobContext context;
     private Map<String, String> userNameMap;
 
+    private String hostName = null;
+    private String domainName = null;
+    
     private static final String SHELLBAG_ARTIFACT_NAME = "RA_SHELL_BAG"; //NON-NLS
     private static final String SHELLBAG_ATTRIBUTE_LAST_WRITE = "RA_SHELL_BAG_LAST_WRITE"; //NON-NLS
     private static final String SHELLBAG_ATTRIBUTE_KEY = "RA_SHELL_BAG_KEY"; //NON-NLS
@@ -1050,6 +1053,8 @@ private void addBlueToothAttribute(String line, Collection<BlackboardAttribute>
      * @return true if successful, false if parsing failed at some point
      */
     private boolean parseSamPluginOutput(String regFilePath, AbstractFile regAbstractFile) {
+        parseSystemHostDomain();
+        
         File regfile = new File(regFilePath);
         List<BlackboardArtifact> newArtifacts = new ArrayList<>();
         try (BufferedReader bufferedReader = new BufferedReader(new FileReader(regfile))) {
@@ -1098,7 +1103,7 @@ private boolean parseSamPluginOutput(String regFilePath, AbstractFile regAbstrac
             
             //add remaining userinfos as accounts;
             for (Map<String, String> userInfo : userInfoMap.values()) {
-                OsAccount osAccount = accountMgr.newWindowsOsAccount(userInfo.get(SID_KEY), null, null, host, OsAccountRealm.RealmScope.UNKNOWN);
+                OsAccount osAccount = accountMgr.newWindowsOsAccount(userInfo.get(SID_KEY), null, domainName, host, domainName != null || !domainName.isEmpty() ? OsAccountRealm.RealmScope.DOMAIN : OsAccountRealm.RealmScope.UNKNOWN);
                 accountMgr.newOsAccountInstance(osAccount, (DataSource)dataSource, OsAccountInstance.OsAccountInstanceType.LAUNCHED);
                 updateOsAccount(osAccount, userInfo, groupMap.get(userInfo.get(SID_KEY)), regAbstractFile);
             }
@@ -1131,6 +1136,53 @@ private boolean parseSamPluginOutput(String regFilePath, AbstractFile regAbstrac
         return false;
     }
 
+    
+    /**
+     *  Finds the Host and Domain information from the registry.
+     */
+    private void parseSystemHostDomain() {
+        List<AbstractFile> regFiles = findRegistryFiles();
+
+        for (AbstractFile systemHive: regFiles) {
+            if (systemHive.getName().toLowerCase().equals("system")) {
+                
+                String systemFileNameLocal = RAImageIngestModule.getRATempPath(currentCase, "reg") + File.separator + systemHive.getName();
+                File systemFileNameLocalFile = new File(systemFileNameLocal);
+        
+                if (!systemFileNameLocalFile.exists()) {
+                    try {
+                        ContentUtils.writeToFile(systemHive, systemFileNameLocalFile, context::dataSourceIngestIsCancelled);
+                    } catch (ReadContentInputStreamException ex) {
+                        logger.log(Level.WARNING, String.format("Error reading registry file '%s' (id=%d).",
+                                systemHive.getName(), systemHive.getId()), ex); //NON-NLS
+                        this.addErrorMessage(
+                                NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.errMsg.errWritingTemp",
+                                this.getName(), systemHive.getName()));
+                        continue;
+                    } catch (IOException ex) {
+                        logger.log(Level.SEVERE, String.format("Error writing temp registry file '%s' for registry file '%s' (id=%d).",
+                                systemFileNameLocal, systemHive.getName(), systemHive.getId()), ex); //NON-NLS
+                        this.addErrorMessage(
+                                NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.errMsg.errWritingTemp",
+                                        this.getName(), systemHive.getName()));
+                        continue;
+                    }                
+                }
+					
+                try {
+                    ParseRegistryHive systemRegFile = new ParseRegistryHive(systemFileNameLocalFile);
+                    hostName = systemRegFile.getRegistryKeyValue("ControlSet001/Services/Tcpip/Parameters", "hostname");
+                    domainName = systemRegFile.getRegistryKeyValue("ControlSet001/Services/Tcpip/Parameters", "domain");
+                    break;
+                } catch (IOException ex) {
+		    logger.log(Level.SEVERE, String.format("Error reading registry file '%s' for registry file '%s' (id=%d).",
+                            systemFileNameLocal, systemHive.getName(), systemHive.getId()), ex); //NON-NLS
+                    this.addErrorMessage(NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.errMsg.errWritingTemp",
+                                    this.getName(), systemHive.getName()));                }
+            }
+        }
+    }
+    
     /**
      * Read the User Information section of the SAM regripper plugin's output
      * and collect user account information from the file.
@@ -1970,7 +2022,7 @@ private void createOrUpdateOsAccount(AbstractFile file, String sid, String userN
         Optional<OsAccount> optional = accountMgr.getWindowsOsAccount(sid, null, null, host);
         OsAccount osAccount;
         if (!optional.isPresent()) {
-            osAccount = accountMgr.newWindowsOsAccount(sid, userName != null && userName.isEmpty() ? null : userName, null, host, OsAccountRealm.RealmScope.UNKNOWN);
+            osAccount = accountMgr.newWindowsOsAccount(sid, userName != null && userName.isEmpty() ? null : userName, domainName, host, domainName != null || !domainName.isEmpty()? OsAccountRealm.RealmScope.DOMAIN : OsAccountRealm.RealmScope.UNKNOWN);
             accountMgr.newOsAccountInstance(osAccount, (DataSource)dataSource, OsAccountInstance.OsAccountInstanceType.LAUNCHED);
         } else {
             osAccount = optional.get();
diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java
index 0954081f3007920fd36de7db9d28f4ece48741f5..c4a472a350d103ff703d899023432985c733c67d 100644
--- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java
+++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java
@@ -600,7 +600,6 @@ private void getDownloadPreVersion24() {
             }
             j++;
             dbFile.delete();
-            break;
         }
 
         if(!context.dataSourceIngestIsCancelled()) {
@@ -733,7 +732,6 @@ private void getDownloadVersion24() {
             }
             j++;
             dbFile.delete();
-            break;
         }
 
         if(!context.dataSourceIngestIsCancelled()) {
diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ParseRegistryHive.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ParseRegistryHive.java
new file mode 100644
index 0000000000000000000000000000000000000000..2048313c43f1b514d8b87cb49dc495c122321c4b
--- /dev/null
+++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ParseRegistryHive.java
@@ -0,0 +1,124 @@
+/*
+ *
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2021 Basis Technology Corp.
+ *
+ * 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.recentactivity;
+
+import com.williballenthin.rejistry.RegistryHiveFile;
+import com.williballenthin.rejistry.RegistryKey;
+import com.williballenthin.rejistry.RegistryParseException;
+import com.williballenthin.rejistry.RegistryValue;
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Calendar;
+import java.util.List;
+import java.util.logging.Level;
+import org.sleuthkit.autopsy.coreutils.Logger;
+
+/**
+ *
+ * Parsers registry keys from a registry hive
+ */
+public class ParseRegistryHive {
+   
+    private final File registryHiveFile;
+    private final RegistryHiveFile registryHive;
+    final private static Logger logger = Logger.getLogger(ParseRegistryHive.class.getName());
+    
+    ParseRegistryHive(File registryHiveFile) throws IOException {
+        this.registryHiveFile = registryHiveFile;
+        registryHive = new RegistryHiveFile(this.registryHiveFile);
+    }
+    
+    /**
+     * 
+     * @param registryKey Registry key to get the value for
+     * @param registryValue Value of the registry key to get the data for
+     * @return  String data from the registry key/value pair
+     * 
+     */
+    public String getRegistryKeyValue(String registryKey, String registryValue) {
+        
+        RegistryKey currentKey = findRegistryKey(registryHive, registryKey);
+        
+        if (currentKey == null) {
+            return null;
+        }
+    
+        try {
+            List<RegistryValue> parameterList = currentKey.getValueList();
+                for (RegistryValue parameter : parameterList) {
+                    if (parameter.getName().toLowerCase().equals(registryValue)) {
+                        return parameter.getValue().getAsString();
+                    }
+                }
+        } catch (RegistryParseException ex) {
+            logger.log(Level.WARNING, String.format("Error reading registry file '%s'", registryHiveFile.getName()), ex); //NON-NLS
+
+        } catch (UnsupportedEncodingException ex) {
+            logger.log(Level.WARNING, String.format("Unsupported Encoding Error for registry key '%s' and registry value '%s'", 
+                        registryKey, registryValue), ex); //NON-NLS            
+        }
+    
+        return null;
+    
+    }
+    
+    /**
+     *  Gets the time that the registry key was written.
+     *  @param registryKey Registry key to get the timestamp for
+     *  @return  date/time that the key was written
+     * 
+     */
+   public Calendar getRegistryKeyTime(String registryKey) {
+        
+        RegistryKey currentKey = findRegistryKey(registryHive, registryKey);
+        
+        if (currentKey == null) {
+            return null;
+        }
+    
+        return currentKey.getTimestamp();
+    
+    }
+    
+    /**
+     * Gets the timestamp of the Registry key
+     * 
+     * @param registryHiveFile Hive to parse
+     * @param registryKey registry key to find in hive
+     * @return registry key or null if it cannot be found
+     */
+    private RegistryKey findRegistryKey(RegistryHiveFile registryHiveFile, String registryKey) {
+        
+        RegistryKey currentKey;
+        try {
+            RegistryKey rootKey = registryHiveFile.getRoot();
+            String regKeyList[] = registryKey.split("/");
+            currentKey = rootKey;
+            for (String key : regKeyList) {
+                currentKey = currentKey.getSubkey(key);
+            }
+        } catch (RegistryParseException ex) {
+            return null;
+        }
+        return currentKey;   
+
+    }
+    
+}
\ No newline at end of file