From d028db66dd1e687590e20d56acf05932e3e82fcc Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Wed, 29 Jul 2020 14:46:52 -0400 Subject: [PATCH] Modified cvt account cache to be more efficent --- .../autopsy/communications/ContactCache.java | 92 ++++++++++++++----- 1 file changed, 69 insertions(+), 23 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java b/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java index 1f34b3cf62..5ee6e6a563 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java +++ b/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java @@ -22,9 +22,13 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import java.beans.PropertyChangeListener; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.ArrayList; import java.util.EnumSet; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.logging.Level; @@ -36,21 +40,22 @@ import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; /** - * A singleton cache of the Contact artifacts for accounts. This list of - * TSK_CONTACT artifacts for a given Account retrieved on first access and - * evicted from the ache after 10 minutes. + * A singleton cache of the Contact artifacts for accounts. The map of account + * unique ids to list of contact artifacts is stored in a LoadingCache which + * expires after 10 of non-use. * */ final class ContactCache { private static final Logger logger = Logger.getLogger(ContactCache.class.getName()); - private final LoadingCache> accountMap; - private static ContactCache instance; + + private final LoadingCache>> accountMap; /** * Returns the list of Contacts for the given Account. @@ -63,7 +68,7 @@ final class ContactCache { * @throws ExecutionException */ static synchronized List getContacts(Account account) throws ExecutionException { - return getInstance().accountMap.get(account); + return getInstance().accountMap.get("realMap").get(account.getTypeSpecificID()); } /** @@ -77,18 +82,17 @@ final class ContactCache { * Construct a new instance. */ private ContactCache() { + accountMap = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build( - new CacheLoader>() { + new CacheLoader>>() { @Override - public List load(Account key) { + public Map> load(String key) { try { - List contactList = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT); - return findContactForAccount(contactList, key); - - } catch (TskCoreException ex) { - logger.log(Level.WARNING, String.format("Failed to load contacts for account %d", key.getAccountID()), ex); - } - return new ArrayList<>(); + return buildMap(); + } catch (SQLException | TskCoreException ex) { + logger.log(Level.WARNING, "Failed to build account to contact map", ex); + } + return new HashMap<>(); // Return an empty map if there is an exception to avoid NPE and continual trying. } }); @@ -117,23 +121,43 @@ final class ContactCache { return instance; } + + /** + * Builds the map of account IDs to contacts that reference them. + * + * @return A map of account IDs to contact artifacts. + * + * @throws TskCoreException + * @throws SQLException + */ + private Map> buildMap() throws TskCoreException, SQLException { + Map> acctMap = new HashMap<>(); + List accountIdList = getAccountList(); + List contactList = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT); + + for(String id: accountIdList) { + acctMap.put(id, findContactForAccount(contactList, id)); + } + + return acctMap; + } /** * Returns a list of TSK_CONTACT artifacts that reference the given account. * * @param allContactList List of existing TSK_CONTACT artifacts. - * @param account Account reference. + * @param account String account unique id. * * @return A list of TSK_CONTACT artifact that reference the given account * or empty list of none were found. * * @throws TskCoreException */ - private List findContactForAccount(List allContactList, Account account) throws TskCoreException { + private List findContactForAccount(List allContactList, String accountId) throws TskCoreException { List accountContacts = new ArrayList<>(); for (BlackboardArtifact contact : allContactList) { - if (isAccountInAttributeList(contact.getAttributes(), account)) { + if (isAccountRelatedToArtifact(contact, accountId)) { accountContacts.add(contact); } } @@ -146,13 +170,14 @@ final class ContactCache { * given account. * * @param contactAttributes List of attributes. - * @param account Account object. + * @param account String account uniqueID. * * @return True if one of the attributes in the list reference the account. */ - private boolean isAccountInAttributeList(List contactAttributes, Account account) { + private boolean isAccountRelatedToArtifact(BlackboardArtifact artifact, String accountId) throws TskCoreException { + List contactAttributes = artifact.getAttributes(); for (BlackboardAttribute attribute : contactAttributes) { - if (isAccountInAttribute(attribute, account)) { + if (isAccountInAttribute(attribute, accountId)) { return true; } } @@ -167,14 +192,35 @@ final class ContactCache { * * @return True if the attribute references the account. */ - private boolean isAccountInAttribute(BlackboardAttribute attribute, Account account) { + private boolean isAccountInAttribute(BlackboardAttribute attribute, String accountId) { String typeName = attribute.getAttributeType().getTypeName(); return (typeName.startsWith("TSK_EMAIL") || typeName.startsWith("TSK_PHONE") || typeName.startsWith("TSK_NAME") || typeName.startsWith("TSK_ID")) - && attribute.getValueString().equals(account.getTypeSpecificID()); + && attribute.getValueString().equals(accountId); + } + + /** + * Gets a list of all accounts unique IDs from the db. + * + * @return A list of unique account ids or empty list if no accounts were found. + * + * @throws TskCoreException + * @throws SQLException + */ + private List getAccountList() throws TskCoreException, SQLException { + List uniqueIdList = new ArrayList<>(); + + CaseDbQuery caseDbQuery = Case.getCurrentCase().getSleuthkitCase().executeQuery("SELECT account_unique_indenifier FROM accounts"); + ResultSet resultSet = caseDbQuery.getResultSet(); + + while(resultSet.next()) { + uniqueIdList.add(resultSet.getString(1)); + } + + return uniqueIdList; } }