diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/NextUnseenGroup.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/NextUnseenGroup.java index fe12dfe010..fc7e5020ec 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/NextUnseenGroup.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/NextUnseenGroup.java @@ -69,7 +69,7 @@ public class NextUnseenGroup extends Action { Optional.ofNullable(controller.viewState()) .map(ObjectExpression::getValue) .map(GroupViewState::getGroup) - .ifPresent(group -> groupManager.markGroupSeen(group, true)); + .ifPresent(group -> groupManager.saveGroupSeen(group, true)); if (unSeenGroups.isEmpty() == false) { controller.advance(GroupViewState.tile(unSeenGroups.get(0)), true); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java index e9fe337d4b..7e33766ea5 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java @@ -80,7 +80,7 @@ import org.sqlite.SQLiteJDBCLoader; */ public final class DrawableDB { - private static final org.sleuthkit.autopsy.coreutils.Logger LOGGER = Logger.getLogger(DrawableDB.class.getName()); + private static final org.sleuthkit.autopsy.coreutils.Logger logger = Logger.getLogger(DrawableDB.class.getName()); //column name constants////////////////////// private static final String ANALYZED = "analyzed"; //NON-NLS @@ -147,7 +147,7 @@ public final class DrawableDB { try { Class.forName("org.sqlite.JDBC"); } catch (ClassNotFoundException ex) { - LOGGER.log(Level.SEVERE, "Failed to load sqlite JDBC driver", ex); //NON-NLS + logger.log(Level.SEVERE, "Failed to load sqlite JDBC driver", ex); //NON-NLS } } private final SleuthkitCase tskCase; @@ -259,7 +259,7 @@ public final class DrawableDB { try { caseDbTransaction.rollback(); } catch (TskCoreException ex2) { - LOGGER.log(Level.SEVERE, "Error in trying to rollback transaction", ex2); + logger.log(Level.SEVERE, "Error in trying to rollback transaction", ex2); } } throw new ExceptionInInitializerError(ex); @@ -356,10 +356,10 @@ public final class DrawableDB { try { return new DrawableDB(dbPath.resolve("drawable.db"), controller); //NON-NLS } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "sql error creating database connection", ex); //NON-NLS + logger.log(Level.SEVERE, "sql error creating database connection", ex); //NON-NLS return null; } catch (ExceptionInInitializerError | IOException ex) { - LOGGER.log(Level.SEVERE, "error creating database connection", ex); //NON-NLS + logger.log(Level.SEVERE, "error creating database connection", ex); //NON-NLS return null; } } @@ -391,11 +391,11 @@ public final class DrawableDB { } try { - LOGGER.log(Level.INFO, String.format("sqlite-jdbc version %s loaded in %s mode", //NON-NLS + logger.log(Level.INFO, String.format("sqlite-jdbc version %s loaded in %s mode", //NON-NLS SQLiteJDBCLoader.getVersion(), SQLiteJDBCLoader.isNativeMode() ? "native" : "pure-java")); //NON-NLS } catch (Exception exception) { - LOGGER.log(Level.WARNING, "exception while checking sqlite-jdbc version and mode", exception); //NON-NLS + logger.log(Level.WARNING, "exception while checking sqlite-jdbc version and mode", exception); //NON-NLS } } @@ -414,7 +414,7 @@ public final class DrawableDB { setPragmas(); } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "problem accessing database", ex); //NON-NLS + logger.log(Level.SEVERE, "problem accessing database", ex); //NON-NLS return false; } @@ -425,7 +425,7 @@ public final class DrawableDB { + " drawable_db_build_status VARCHAR(128) )"; //NON-NLS stmt.execute(sql); } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "problem creating datasources table", ex); //NON-NLS + logger.log(Level.SEVERE, "problem creating datasources table", ex); //NON-NLS return false; } @@ -442,7 +442,7 @@ public final class DrawableDB { + " analyzed integer DEFAULT 0)"; //NON-NLS stmt.execute(sql); } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "problem creating drawable_files table", ex); //NON-NLS + logger.log(Level.SEVERE, "problem creating drawable_files table", ex); //NON-NLS return false; } @@ -459,7 +459,7 @@ public final class DrawableDB { tskCase.getCaseDbAccessManager().createTable(GROUPS_TABLENAME, tableSchema); } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "problem creating groups table", ex); //NON-NLS + logger.log(Level.SEVERE, "problem creating groups table", ex); //NON-NLS return false; } @@ -469,7 +469,7 @@ public final class DrawableDB { + " hash_set_name VARCHAR(255) UNIQUE NOT NULL)"; //NON-NLS stmt.execute(sql); } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "problem creating hash_sets table", ex); //NON-NLS + logger.log(Level.SEVERE, "problem creating hash_sets table", ex); //NON-NLS return false; } @@ -480,7 +480,7 @@ public final class DrawableDB { + " PRIMARY KEY (hash_set_id, obj_id))"; //NON-NLS stmt.execute(sql); } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "problem creating hash_set_hits table", ex); //NON-NLS + logger.log(Level.SEVERE, "problem creating hash_set_hits table", ex); //NON-NLS return false; } @@ -488,35 +488,35 @@ public final class DrawableDB { String sql = "CREATE INDEX if not exists path_idx ON drawable_files(path)"; //NON-NLS stmt.execute(sql); } catch (SQLException ex) { - LOGGER.log(Level.WARNING, "problem creating path_idx", ex); //NON-NLS + logger.log(Level.WARNING, "problem creating path_idx", ex); //NON-NLS } try (Statement stmt = con.createStatement()) { String sql = "CREATE INDEX if not exists name_idx ON drawable_files(name)"; //NON-NLS stmt.execute(sql); } catch (SQLException ex) { - LOGGER.log(Level.WARNING, "problem creating name_idx", ex); //NON-NLS + logger.log(Level.WARNING, "problem creating name_idx", ex); //NON-NLS } try (Statement stmt = con.createStatement()) { String sql = "CREATE INDEX if not exists make_idx ON drawable_files(make)"; //NON-NLS stmt.execute(sql); } catch (SQLException ex) { - LOGGER.log(Level.WARNING, "problem creating make_idx", ex); //NON-NLS + logger.log(Level.WARNING, "problem creating make_idx", ex); //NON-NLS } try (Statement stmt = con.createStatement()) { String sql = "CREATE INDEX if not exists model_idx ON drawable_files(model)"; //NON-NLS stmt.execute(sql); } catch (SQLException ex) { - LOGGER.log(Level.WARNING, "problem creating model_idx", ex); //NON-NLS + logger.log(Level.WARNING, "problem creating model_idx", ex); //NON-NLS } try (Statement stmt = con.createStatement()) { String sql = "CREATE INDEX if not exists analyzed_idx ON drawable_files(analyzed)"; //NON-NLS stmt.execute(sql); } catch (SQLException ex) { - LOGGER.log(Level.WARNING, "problem creating analyzed_idx", ex); //NON-NLS + logger.log(Level.WARNING, "problem creating analyzed_idx", ex); //NON-NLS } return true; @@ -537,7 +537,7 @@ public final class DrawableDB { closeStatements(); con.close(); } catch (SQLException ex) { - LOGGER.log(Level.WARNING, "Failed to close connection to drawable.db", ex); //NON-NLS + logger.log(Level.WARNING, "Failed to close connection to drawable.db", ex); //NON-NLS } } con = null; @@ -549,7 +549,7 @@ public final class DrawableDB { con = DriverManager.getConnection("jdbc:sqlite:" + dbPath.toString()); //NON-NLS } } catch (SQLException ex) { - LOGGER.log(Level.WARNING, "Failed to open connection to drawable.db", ex); //NON-NLS + logger.log(Level.WARNING, "Failed to open connection to drawable.db", ex); //NON-NLS } } @@ -600,7 +600,7 @@ public final class DrawableDB { names.add(rs.getString(HASH_SET_NAME)); } } catch (SQLException sQLException) { - LOGGER.log(Level.WARNING, "failed to get hash set names", sQLException); //NON-NLS + logger.log(Level.WARNING, "failed to get hash set names", sQLException); //NON-NLS } finally { dbReadUnlock(); } @@ -628,7 +628,7 @@ public final class DrawableDB { } } } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "failed to get group seen", ex); //NON-NLS + logger.log(Level.SEVERE, "failed to get group seen", ex); //NON-NLS } } } @@ -648,26 +648,22 @@ public final class DrawableDB { return queryResultProcessor.getGroupSeen(); } catch (TskCoreException ex) { String msg = String.format("Failed to get is group seen for group key %s", groupKey.getValueDisplayName()); //NON-NLS - LOGGER.log(Level.WARNING, msg, ex); + logger.log(Level.WARNING, msg, ex); } return false; } - public void markGroupSeen(GroupKey gk, boolean seen) { - try { - String updateSQL; - if (gk.getDataSource().isPresent()) { - updateSQL = String.format("SET seen = %d WHERE VALUE = \'%s\' AND attribute = \'%s\' AND data_source_obj_id = %d", seen ? 1 : 0, - gk.getValueDisplayName(), gk.getAttribute().attrName.toString(), gk.getDataSourceObjId()); - } else { - updateSQL = String.format("SET seen = %d WHERE VALUE = \'%s\' AND attribute = \'%s\'", seen ? 1 : 0, - gk.getValueDisplayName(), gk.getAttribute().attrName.toString()); - } - tskCase.getCaseDbAccessManager().update(GROUPS_TABLENAME, updateSQL); - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error marking group as seen", ex); //NON-NLS + public void markGroupSeen(GroupKey gk, boolean seen) throws TskCoreException { + String updateSQL; + if (gk.getDataSource().isPresent()) { + updateSQL = String.format("SET seen = %d WHERE VALUE = \'%s\' AND attribute = \'%s\' AND data_source_obj_id = %d", seen ? 1 : 0, + gk.getValueDisplayName(), gk.getAttribute().attrName.toString(), gk.getDataSourceObjId()); + } else { + updateSQL = String.format("SET seen = %d WHERE VALUE = \'%s\' AND attribute = \'%s\'", seen ? 1 : 0, + gk.getValueDisplayName(), gk.getAttribute().attrName.toString()); } + tskCase.getCaseDbAccessManager().update(GROUPS_TABLENAME, updateSQL); } public boolean removeFile(long id) { @@ -695,10 +691,10 @@ public final class DrawableDB { try { caseDbTransaction.rollback(); } catch (TskCoreException ex2) { - LOGGER.log(Level.SEVERE, "Error in trying to rollback transaction", ex2); //NON-NLS + logger.log(Level.SEVERE, "Error in trying to rollback transaction", ex2); //NON-NLS } } - LOGGER.log(Level.SEVERE, "Error updating file", ex); //NON-NLS + logger.log(Level.SEVERE, "Error updating file", ex); //NON-NLS } } @@ -720,10 +716,10 @@ public final class DrawableDB { try { caseDbTransaction.rollback(); } catch (TskCoreException ex2) { - LOGGER.log(Level.SEVERE, "Error in trying to rollback transaction", ex2); //NON-NLS + logger.log(Level.SEVERE, "Error in trying to rollback transaction", ex2); //NON-NLS } } - LOGGER.log(Level.SEVERE, "Error inserting file", ex); //NON-NLS + logger.log(Level.SEVERE, "Error inserting file", ex); //NON-NLS } } @@ -792,7 +788,7 @@ public final class DrawableDB { } } } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "failed to insert/update hash hits for file" + f.getContentPathSafe(), ex); //NON-NLS + logger.log(Level.SEVERE, "failed to insert/update hash hits for file" + f.getContentPathSafe(), ex); //NON-NLS } //and update all groups this file is in @@ -818,7 +814,7 @@ public final class DrawableDB { * closed during processing, which doesn't need to be reported here. */ if (Case.isCaseOpen()) { - LOGGER.log(Level.SEVERE, "failed to insert/update file" + f.getContentPathSafe(), ex); //NON-NLS + logger.log(Level.SEVERE, "failed to insert/update file" + f.getContentPathSafe(), ex); //NON-NLS } } finally { @@ -850,14 +846,14 @@ public final class DrawableDB { try { rs.close(); } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "Error closing resultset", ex); //NON-NLS + logger.log(Level.SEVERE, "Error closing resultset", ex); //NON-NLS } } if (statement != null) { try { statement.close(); } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "Error closing statement ", ex); //NON-NLS + logger.log(Level.SEVERE, "Error closing statement ", ex); //NON-NLS } } dbReadUnlock(); @@ -882,7 +878,7 @@ public final class DrawableDB { updateDataSourceStmt.executeUpdate(); } catch (SQLException | NullPointerException ex) { - LOGGER.log(Level.SEVERE, "failed to insert/update datasources table", ex); //NON-NLS + logger.log(Level.SEVERE, "failed to insert/update datasources table", ex); //NON-NLS } finally { dbWriteUnlock(); } @@ -919,7 +915,7 @@ public final class DrawableDB { } } catch (SQLException ex) { String msg = String.format("Failed to determine if file %s is finalized", String.valueOf(fileId)); //NON-NLS - LOGGER.log(Level.WARNING, msg, ex); + logger.log(Level.WARNING, msg, ex); } finally { dbReadUnlock(); } @@ -937,7 +933,7 @@ public final class DrawableDB { return analyzedQuery.getInt(ANALYZED) == fileIds.size(); } } catch (SQLException ex) { - LOGGER.log(Level.WARNING, "problem counting analyzed files: ", ex); //NON-NLS + logger.log(Level.WARNING, "problem counting analyzed files: ", ex); //NON-NLS } finally { dbReadUnlock(); } @@ -964,10 +960,10 @@ public final class DrawableDB { } } catch (SQLException ex) { - LOGGER.log(Level.WARNING, "problem counting analyzed files: ", ex); //NON-NLS + logger.log(Level.WARNING, "problem counting analyzed files: ", ex); //NON-NLS } } catch (TskCoreException tskCoreException) { - LOGGER.log(Level.WARNING, "problem counting analyzed files: ", tskCoreException); //NON-NLS + logger.log(Level.WARNING, "problem counting analyzed files: ", tskCoreException); //NON-NLS } finally { dbReadUnlock(); } @@ -1004,14 +1000,14 @@ public final class DrawableDB { try { rs.close(); } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "Error closing result set after executing findAllFileIdsWhere", ex); //NON-NLS + logger.log(Level.SEVERE, "Error closing result set after executing findAllFileIdsWhere", ex); //NON-NLS } } if (statement != null) { try { statement.close(); } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "Error closing statement after executing findAllFileIdsWhere", ex); //NON-NLS + logger.log(Level.SEVERE, "Error closing statement after executing findAllFileIdsWhere", ex); //NON-NLS } } dbReadUnlock(); @@ -1045,14 +1041,14 @@ public final class DrawableDB { try { rs.close(); } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "Error closing result set after executing countFilesWhere", ex); //NON-NLS + logger.log(Level.SEVERE, "Error closing result set after executing countFilesWhere", ex); //NON-NLS } } if (statement != null) { try { statement.close(); } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "Error closing statement after executing countFilesWhere", ex); //NON-NLS + logger.log(Level.SEVERE, "Error closing statement after executing countFilesWhere", ex); //NON-NLS } } dbReadUnlock(); @@ -1132,7 +1128,7 @@ public final class DrawableDB { vals.add(value); } } catch (SQLException ex) { - LOGGER.log(Level.WARNING, "Unable to get values for attribute", ex); //NON-NLS + logger.log(Level.WARNING, "Unable to get values for attribute", ex); //NON-NLS } finally { dbReadUnlock(); } @@ -1173,7 +1169,7 @@ public final class DrawableDB { } catch (TskCoreException ex) { // Don't need to report it if the case was closed if (Case.isCaseOpen()) { - LOGGER.log(Level.SEVERE, "Unable to insert group", ex); //NON-NLS + logger.log(Level.SEVERE, "Unable to insert group", ex); //NON-NLS } } @@ -1193,7 +1189,7 @@ public final class DrawableDB { return DrawableFile.create(f, areFilesAnalyzed(Collections.singleton(id)), isVideoFile(f)); } catch (IllegalStateException ex) { - LOGGER.log(Level.SEVERE, "there is no case open; failed to load file with id: {0}", id); //NON-NLS + logger.log(Level.SEVERE, "there is no case open; failed to load file with id: {0}", id); //NON-NLS return null; } } @@ -1222,7 +1218,7 @@ public final class DrawableDB { } } } catch (SQLException ex) { - LOGGER.log(Level.WARNING, "failed to get file for group:" + groupKey.getAttribute() + " == " + groupKey.getValue(), ex); //NON-NLS + logger.log(Level.WARNING, "failed to get file for group:" + groupKey.getAttribute() + " == " + groupKey.getValue(), ex); //NON-NLS } finally { dbReadUnlock(); } @@ -1282,7 +1278,7 @@ public final class DrawableDB { //TODO: delete from hash_set_hits table also... } catch (SQLException ex) { - LOGGER.log(Level.WARNING, "failed to delete row for obj_id = " + id, ex); //NON-NLS + logger.log(Level.WARNING, "failed to delete row for obj_id = " + id, ex); //NON-NLS } finally { dbWriteUnlock(); } @@ -1343,7 +1339,7 @@ public final class DrawableDB { addImageFileToList(analyzedQuery.getLong(OBJ_ID)); } } catch (SQLException ex) { - LOGGER.log(Level.WARNING, "problem loading file IDs: ", ex); //NON-NLS + logger.log(Level.WARNING, "problem loading file IDs: ", ex); //NON-NLS } finally { dbReadUnlock(); } @@ -1394,9 +1390,9 @@ public final class DrawableDB { .count(); } } catch (IllegalStateException ex) { - LOGGER.log(Level.WARNING, "Case closed while getting files"); //NON-NLS + logger.log(Level.WARNING, "Case closed while getting files"); //NON-NLS } catch (TskCoreException ex1) { - LOGGER.log(Level.SEVERE, "Failed to get content tags by tag name.", ex1); //NON-NLS + logger.log(Level.SEVERE, "Failed to get content tags by tag name.", ex1); //NON-NLS } return -1; @@ -1445,7 +1441,7 @@ public final class DrawableDB { return resultSet.getLong("obj_count"); //NON-NLS } } catch (SQLException | TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error getting category count.", ex); //NON-NLS + logger.log(Level.SEVERE, "Error getting category count.", ex); //NON-NLS } return -1; } @@ -1479,7 +1475,7 @@ public final class DrawableDB { con.setAutoCommit(false); } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "failed to set auto-commit to to false", ex); //NON-NLS + logger.log(Level.SEVERE, "failed to set auto-commit to to false", ex); //NON-NLS } } @@ -1490,7 +1486,7 @@ public final class DrawableDB { con.rollback(); updatedFiles.clear(); } catch (SQLException ex1) { - LOGGER.log(Level.SEVERE, "Exception while attempting to rollback!!", ex1); //NON-NLS + logger.log(Level.SEVERE, "Exception while attempting to rollback!!", ex1); //NON-NLS } finally { close(); } @@ -1512,9 +1508,9 @@ public final class DrawableDB { } } catch (SQLException ex) { if (Case.isCaseOpen()) { - LOGGER.log(Level.SEVERE, "Error commiting drawable.db.", ex); //NON-NLS + logger.log(Level.SEVERE, "Error commiting drawable.db.", ex); //NON-NLS } else { - LOGGER.log(Level.WARNING, "Error commiting drawable.db - case is closed."); //NON-NLS + logger.log(Level.WARNING, "Error commiting drawable.db - case is closed."); //NON-NLS } rollback(); } @@ -1527,9 +1523,9 @@ public final class DrawableDB { con.setAutoCommit(true); } catch (SQLException ex) { if (Case.isCaseOpen()) { - LOGGER.log(Level.SEVERE, "Error setting auto-commit to true.", ex); //NON-NLS + logger.log(Level.SEVERE, "Error setting auto-commit to true.", ex); //NON-NLS } else { - LOGGER.log(Level.SEVERE, "Error setting auto-commit to true - case is closed"); //NON-NLS + logger.log(Level.SEVERE, "Error setting auto-commit to true - case is closed"); //NON-NLS } } finally { closed = true; diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/DrawableGroup.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/DrawableGroup.java index 3ef949c9c9..0b4940255f 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/DrawableGroup.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/DrawableGroup.java @@ -174,6 +174,7 @@ public class DrawableGroup implements Comparable { } } + synchronized void addFile(Long f) { if (fileIDs.contains(f) == false) { fileIDs.add(f); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java index 37617acbc8..146f613ccf 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java @@ -19,6 +19,11 @@ package org.sleuthkit.autopsy.imagegallery.datamodel.grouping; import com.google.common.eventbus.Subscribe; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; @@ -31,9 +36,12 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Level; @@ -86,13 +94,17 @@ import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData.DbType; /** - * Provides an abstraction layer on top of {@link DrawableDB} ( and to some - * extent {@link SleuthkitCase} ) to facilitate creation, retrieval, updating, - * and sorting of {@link DrawableGroup}s. + * Provides an abstraction layer on top of DrawableDB ( and to some extent + * SleuthkitCase ) to facilitate creation, retrieval, updating, and sorting of + * DrawableGroups. */ public class GroupManager { - private static final Logger LOGGER = Logger.getLogger(GroupManager.class.getName()); + private static final Logger logger = Logger.getLogger(GroupManager.class.getName()); + + /** An executor to submit async UI related background tasks to. */ + private final ListeningExecutorService exec = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor( + new BasicThreadFactory.Builder().namingPattern("GUI Task -%d").build())); //NON-NLS private final ImageGalleryController controller; @@ -265,25 +277,40 @@ public class GroupManager { } /** - * 'mark' the given group as seen. This removes it from the queue of groups - * to review, and is persisted in the drawable db. + * 'Save' the given group as seen in the drawable db. * * @param group The DrawableGroup to mark as seen. - * @param seen The seen stastus to set. + * @param seen The seen state to set for the given group. + * + * @return A ListenableFuture that encapsulates saving the seen state to the + * DB. */ - @ThreadConfined(type = ThreadType.JFX) - public void markGroupSeen(DrawableGroup group, boolean seen) { - DrawableDB db = getDB(); - if (nonNull(db)) { - db.markGroupSeen(group.getGroupKey(), seen); - group.setSeen(seen); - if (seen) { - unSeenGroups.removeAll(group); - } else if (unSeenGroups.contains(group) == false) { - unSeenGroups.add(group); + public ListenableFuture saveGroupSeen(DrawableGroup group, boolean seen) { + synchronized (controller) { + DrawableDB db = getDB(); + if (nonNull(db)) { + return exec.submit(() -> { + try { + db.markGroupSeen(group.getGroupKey(), seen); + group.setSeen(seen); + + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error marking group as seen", ex); //NON-NLS + } + }); } - FXCollections.sort(unSeenGroups, applySortOrder(sortOrder, sortBy)); } + return Futures.immediateFuture(null); + } + + @ThreadConfined(type = ThreadType.JFX) + private void updateUnSeenGroups(DrawableGroup group, boolean seen) { + if (seen) { + unSeenGroups.removeAll(group); + } else if (unSeenGroups.contains(group) == false) { + unSeenGroups.add(group); + } + FXCollections.sort(unSeenGroups, applySortOrder(sortOrder, sortBy)); } /** @@ -387,7 +414,7 @@ public class GroupManager { .collect(Collectors.toSet()); } } catch (TskCoreException ex) { - LOGGER.log(Level.WARNING, "TSK error getting files in Category:" + category.getDisplayName(), ex); //NON-NLS + logger.log(Level.WARNING, "TSK error getting files in Category:" + category.getDisplayName(), ex); //NON-NLS throw ex; } } @@ -406,7 +433,7 @@ public class GroupManager { } return files; } catch (TskCoreException ex) { - LOGGER.log(Level.WARNING, "TSK error getting files with Tag:" + tagName.getDisplayName(), ex); //NON-NLS + logger.log(Level.WARNING, "TSK error getting files with Tag:" + tagName.getDisplayName(), ex); //NON-NLS throw ex; } } @@ -495,7 +522,7 @@ public class GroupManager { groupByTask = new ReGroupTask<>(dataSource, groupBy, sortBy, sortOrder); Platform.runLater(() -> regroupProgress.bind(groupByTask.progressProperty())); - regroupExecutor.submit(groupByTask); + exec.submit(groupByTask); } else { // resort the list of groups setSortBy(sortBy); @@ -507,11 +534,6 @@ public class GroupManager { } } - /** - * an executor to submit async ui related background tasks to. - */ - final ExecutorService regroupExecutor = Executors.newSingleThreadExecutor(new BasicThreadFactory.Builder().namingPattern("ui task -%d").build()); //NON-NLS - public ReadOnlyDoubleProperty regroupProgress() { return regroupProgress.getReadOnlyProperty(); } @@ -579,10 +601,9 @@ public class GroupManager { } /** - * handle {@link FileUpdateEvent} sent from Db when files are - * inserted/updated + * Handle notificationsS sent from Db when files are inserted/updated * - * @param evt + * @param updatedFileIDs The ID of the inserted/updated files. */ @Subscribe synchronized public void handleFileUpdate(Collection updatedFileIDs) { @@ -608,26 +629,22 @@ public class GroupManager { } private DrawableGroup popuplateIfAnalyzed(GroupKey groupKey, ReGroupTask task) { - - if (Objects.nonNull(task) && (task.isCancelled())) { - /* - * if this method call is part of a ReGroupTask and that task is - * cancelled, no-op - * - * this allows us to stop if a regroup task has been cancelled (e.g. - * the user picked a different group by attribute, while the current - * task was still running) - */ - - } else { // no task or un-cancelled task + /* + * If this method call is part of a ReGroupTask and that task is + * cancelled, no-op. + * + * This allows us to stop if a regroup task has been cancelled (e.g. the + * user picked a different group by attribute, while the current task + * was still running) + */ + if (isNull(task) || task.isCancelled() == false) { DrawableDB db = getDB(); + /* + * For attributes other than path we can't be sure a group is fully + * analyzed because we don't know all the files that will be a part + * of that group. just show them no matter what. + */ if (nonNull(db) && ((groupKey.getAttribute() != DrawableAttribute.PATH) || db.isGroupAnalyzed(groupKey))) { - /* - * for attributes other than path we can't be sure a group is - * fully analyzed because we don't know all the files that will - * be a part of that group. just show them no matter what. - */ - try { Set fileIDs = getFileIDsInGroup(groupKey); if (Objects.nonNull(fileIDs)) { @@ -636,34 +653,38 @@ public class GroupManager { synchronized (groupMap) { if (groupMap.containsKey(groupKey)) { group = groupMap.get(groupKey); - group.setFiles(ObjectUtils.defaultIfNull(fileIDs, Collections.emptySet())); + group.setSeen(groupSeen); } else { group = new DrawableGroup(groupKey, fileIDs, groupSeen); controller.getCategoryManager().registerListener(group); group.seenProperty().addListener((o, oldSeen, newSeen) - -> Platform.runLater(() -> markGroupSeen(group, newSeen)) - ); + -> saveGroupSeen(group, newSeen) + .addListener(() -> updateUnSeenGroups(group, newSeen), + Platform::runLater)); + groupMap.put(groupKey, group); } } + Platform.runLater(() -> { if (analyzedGroups.contains(group) == false) { analyzedGroups.add(group); - if (Objects.isNull(task)) { + if (isNull(task)) { FXCollections.sort(analyzedGroups, applySortOrder(sortOrder, sortBy)); } } - markGroupSeen(group, groupSeen); + updateUnSeenGroups(group, groupSeen); }); return group; } } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "failed to get files for group: " + groupKey.getAttribute().attrName.toString() + " = " + groupKey.getValue(), ex); //NON-NLS + logger.log(Level.SEVERE, "failed to get files for group: " + groupKey.getAttribute().attrName.toString() + " = " + groupKey.getValue(), ex); //NON-NLS } } } + return null; } @@ -687,6 +708,7 @@ public class GroupManager { } catch (Exception ex) { Exceptions.printStackTrace(ex); throw new TskCoreException("Failed to get file ids with mime type " + mimeType, ex); + } } @@ -842,7 +864,7 @@ public class GroupManager { return values; } catch (TskCoreException ex) { - LOGGER.log(Level.WARNING, "TSK error getting list of type {0}", groupBy.getDisplayName()); //NON-NLS + logger.log(Level.WARNING, "TSK error getting list of type {0}", groupBy.getDisplayName()); //NON-NLS return Collections.emptyList(); } }