allow marking grouos as seen or unseen, update next unseen appropriatly

This commit is contained in:
jmillman 2015-06-11 15:52:15 -04:00
parent bf1481d685
commit efd19fc56f
4 changed files with 152 additions and 150 deletions

View File

@ -56,14 +56,15 @@ public class NextUnseenGroup extends Action {
}); });
controller.getGroupManager().getUnSeenGroups().addListener((Observable observable) -> { controller.getGroupManager().getUnSeenGroups().addListener((Observable observable) -> {
updateButton(); Platform.runLater(this::updateButton);
}); });
setEventHandler((ActionEvent t) -> { setEventHandler((ActionEvent t) -> {
Optional.ofNullable(controller.viewState()) Optional.ofNullable(controller.viewState())
.map(ObjectExpression<GroupViewState>::getValue) .map(ObjectExpression<GroupViewState>::getValue)
.map(GroupViewState::getGroup) .map(GroupViewState::getGroup)
.ifPresent(controller.getGroupManager()::markGroupSeen); .ifPresent(group -> controller.getGroupManager().markGroupSeen(group, true));
if (false == controller.getGroupManager().getUnSeenGroups().isEmpty()) { if (false == controller.getGroupManager().getUnSeenGroups().isEmpty()) {
controller.advance(GroupViewState.tile(controller.getGroupManager().getUnSeenGroups().get(0))); controller.advance(GroupViewState.tile(controller.getGroupManager().getUnSeenGroups().get(0)));

View File

@ -71,51 +71,51 @@ import org.sqlite.SQLiteJDBCLoader;
* the future. see also {@link EventsDB} in the timeline viewer. * the future. see also {@link EventsDB} in the timeline viewer.
*/ */
public final class DrawableDB { public final class DrawableDB {
private static final java.util.logging.Logger LOGGER = Logger.getLogger(DrawableDB.class.getName()); private static final java.util.logging.Logger LOGGER = Logger.getLogger(DrawableDB.class.getName());
//column name constants////////////////////// //column name constants//////////////////////
private static final String ANALYZED = "analyzed"; private static final String ANALYZED = "analyzed";
private static final String OBJ_ID = "obj_id"; private static final String OBJ_ID = "obj_id";
private static final String HASH_SET_NAME = "hash_set_name"; private static final String HASH_SET_NAME = "hash_set_name";
private final PreparedStatement insertHashSetStmt; private final PreparedStatement insertHashSetStmt;
private final PreparedStatement groupSeenQueryStmt; private final PreparedStatement groupSeenQueryStmt;
private final PreparedStatement insertGroupStmt; private final PreparedStatement insertGroupStmt;
private final List<PreparedStatement> preparedStatements = new ArrayList<>(); private final List<PreparedStatement> preparedStatements = new ArrayList<>();
private final PreparedStatement removeFileStmt; private final PreparedStatement removeFileStmt;
private final PreparedStatement updateGroupStmt; private final PreparedStatement updateGroupStmt;
private final PreparedStatement selectHashSetStmt; private final PreparedStatement selectHashSetStmt;
private final PreparedStatement selectHashSetNamesStmt; private final PreparedStatement selectHashSetNamesStmt;
private final PreparedStatement insertHashHitStmt; private final PreparedStatement insertHashHitStmt;
private final PreparedStatement updateFileStmt; private final PreparedStatement updateFileStmt;
private PreparedStatement insertFileStmt; private PreparedStatement insertFileStmt;
private final PreparedStatement pathGroupStmt; private final PreparedStatement pathGroupStmt;
private final PreparedStatement nameGroupStmt; private final PreparedStatement nameGroupStmt;
private final PreparedStatement created_timeGroupStmt; private final PreparedStatement created_timeGroupStmt;
private final PreparedStatement modified_timeGroupStmt; private final PreparedStatement modified_timeGroupStmt;
private final PreparedStatement makeGroupStmt; private final PreparedStatement makeGroupStmt;
private final PreparedStatement modelGroupStmt; private final PreparedStatement modelGroupStmt;
private final PreparedStatement analyzedGroupStmt; private final PreparedStatement analyzedGroupStmt;
private final PreparedStatement hashSetGroupStmt; private final PreparedStatement hashSetGroupStmt;
/** /**
@ -128,13 +128,13 @@ public final class DrawableDB {
* list of observers to be notified if the database changes * list of observers to be notified if the database changes
*/ */
private final HashSet<FileUpdateEvent.FileUpdateListener> updateListeners = new HashSet<>(); private final HashSet<FileUpdateEvent.FileUpdateListener> updateListeners = new HashSet<>();
private GroupManager groupManager; private GroupManager groupManager;
private final Path dbPath; private final Path dbPath;
volatile private Connection con; volatile private Connection con;
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true); //use fairness policy private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true); //use fairness policy
private final Lock DBLock = rwLock.writeLock(); //using exclusing lock for all db ops for now private final Lock DBLock = rwLock.writeLock(); //using exclusing lock for all db ops for now
@ -206,9 +206,9 @@ public final class DrawableDB {
insertFileStmt = prepareStatement( insertFileStmt = prepareStatement(
"INSERT OR IGNORE INTO drawable_files (obj_id , path, name, created_time, modified_time, make, model, analyzed) " "INSERT OR IGNORE INTO drawable_files (obj_id , path, name, created_time, modified_time, make, model, analyzed) "
+ "VALUES (?,?,?,?,?,?,?,?)"); + "VALUES (?,?,?,?,?,?,?,?)");
removeFileStmt = prepareStatement("delete from drawable_files where obj_id = ?"); removeFileStmt = prepareStatement("delete from drawable_files where obj_id = ?");
pathGroupStmt = prepareStatement("select obj_id , analyzed from drawable_files where path = ? ", DrawableAttribute.PATH); pathGroupStmt = prepareStatement("select obj_id , analyzed from drawable_files where path = ? ", DrawableAttribute.PATH);
nameGroupStmt = prepareStatement("select obj_id , analyzed from drawable_files where name = ? ", DrawableAttribute.NAME); nameGroupStmt = prepareStatement("select obj_id , analyzed from drawable_files where name = ? ", DrawableAttribute.NAME);
created_timeGroupStmt = prepareStatement("select obj_id , analyzed from drawable_files where created_time = ? ", DrawableAttribute.CREATED_TIME); created_timeGroupStmt = prepareStatement("select obj_id , analyzed from drawable_files where created_time = ? ", DrawableAttribute.CREATED_TIME);
@ -217,23 +217,23 @@ public final class DrawableDB {
modelGroupStmt = prepareStatement("select obj_id , analyzed from drawable_files where model = ? ", DrawableAttribute.MODEL); modelGroupStmt = prepareStatement("select obj_id , analyzed from drawable_files where model = ? ", DrawableAttribute.MODEL);
analyzedGroupStmt = prepareStatement("Select obj_id , analyzed from drawable_files where analyzed = ?", DrawableAttribute.ANALYZED); analyzedGroupStmt = prepareStatement("Select obj_id , analyzed from drawable_files where analyzed = ?", DrawableAttribute.ANALYZED);
hashSetGroupStmt = prepareStatement("select drawable_files.obj_id as obj_id, analyzed from drawable_files , hash_sets , hash_set_hits where drawable_files.obj_id = hash_set_hits.obj_id and hash_sets.hash_set_id = hash_set_hits.hash_set_id and hash_sets.hash_set_name = ?", DrawableAttribute.HASHSET); hashSetGroupStmt = prepareStatement("select drawable_files.obj_id as obj_id, analyzed from drawable_files , hash_sets , hash_set_hits where drawable_files.obj_id = hash_set_hits.obj_id and hash_sets.hash_set_id = hash_set_hits.hash_set_id and hash_sets.hash_set_name = ?", DrawableAttribute.HASHSET);
updateGroupStmt = prepareStatement("update groups set seen = 1 where value = ? and attribute = ?"); updateGroupStmt = prepareStatement("update groups set seen = ? where value = ? and attribute = ?");
insertGroupStmt = prepareStatement("insert or replace into groups (value, attribute) values (?,?)"); insertGroupStmt = prepareStatement("insert or replace into groups (value, attribute) values (?,?)");
groupSeenQueryStmt = prepareStatement("select seen from groups where value = ? and attribute = ?"); groupSeenQueryStmt = prepareStatement("select seen from groups where value = ? and attribute = ?");
selectHashSetNamesStmt = prepareStatement("SELECT DISTINCT hash_set_name FROM hash_sets"); selectHashSetNamesStmt = prepareStatement("SELECT DISTINCT hash_set_name FROM hash_sets");
insertHashSetStmt = prepareStatement("insert or ignore into hash_sets (hash_set_name) values (?)"); insertHashSetStmt = prepareStatement("insert or ignore into hash_sets (hash_set_name) values (?)");
selectHashSetStmt = prepareStatement("select hash_set_id from hash_sets where hash_set_name = ?"); selectHashSetStmt = prepareStatement("select hash_set_id from hash_sets where hash_set_name = ?");
insertHashHitStmt = prepareStatement("insert or ignore into hash_set_hits (hash_set_id, obj_id) values (?,?)"); insertHashHitStmt = prepareStatement("insert or ignore into hash_set_hits (hash_set_id, obj_id) values (?,?)");
initializeImageList(); initializeImageList();
} else { } else {
throw new ExceptionInInitializerError(); throw new ExceptionInInitializerError();
} }
} }
/** /**
@ -271,7 +271,7 @@ public final class DrawableDB {
if (attr != null) { if (attr != null) {
groupStatementMap.put(attr, prepareStatement); groupStatementMap.put(attr, prepareStatement);
} }
return prepareStatement; return prepareStatement;
} }
@ -284,7 +284,7 @@ public final class DrawableDB {
* @return * @return
*/ */
public static DrawableDB getDrawableDB(Path dbPath, SleuthkitCase tskCase) { public static DrawableDB getDrawableDB(Path dbPath, SleuthkitCase tskCase) {
try { try {
return new DrawableDB(dbPath.resolve("drawable.db"), tskCase); return new DrawableDB(dbPath.resolve("drawable.db"), tskCase);
} catch (SQLException ex) { } catch (SQLException ex) {
@ -295,7 +295,7 @@ public final class DrawableDB {
return null; return null;
} }
} }
private void setPragmas() throws SQLException { private void setPragmas() throws SQLException {
//this should match Sleuthkit db setupt //this should match Sleuthkit db setupt
@ -321,7 +321,7 @@ public final class DrawableDB {
//we never delete anything so... //we never delete anything so...
statement.execute("PRAGMA auto_vacuum = 0"); statement.execute("PRAGMA auto_vacuum = 0");
} }
try { try {
LOGGER.log(Level.INFO, String.format("sqlite-jdbc version %s loaded in %s mode", LOGGER.log(Level.INFO, String.format("sqlite-jdbc version %s loaded in %s mode",
SQLiteJDBCLoader.getVersion(), SQLiteJDBCLoader.isNativeMode() SQLiteJDBCLoader.getVersion(), SQLiteJDBCLoader.isNativeMode()
@ -329,7 +329,7 @@ public final class DrawableDB {
} catch (Exception exception) { } catch (Exception exception) {
LOGGER.log(Level.WARNING, "exception while checking sqlite-jdbc version and mode", exception); LOGGER.log(Level.WARNING, "exception while checking sqlite-jdbc version and mode", exception);
} }
} }
/** /**
@ -344,7 +344,7 @@ public final class DrawableDB {
openDBCon(); openDBCon();
} }
setPragmas(); setPragmas();
} catch (SQLException ex) { } catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "problem accessing database", ex); LOGGER.log(Level.SEVERE, "problem accessing database", ex);
return false; return false;
@ -364,7 +364,7 @@ public final class DrawableDB {
LOGGER.log(Level.SEVERE, "problem creating drawable_files table", ex); LOGGER.log(Level.SEVERE, "problem creating drawable_files table", ex);
return false; return false;
} }
try (Statement stmt = con.createStatement()) { try (Statement stmt = con.createStatement()) {
String sql = "CREATE TABLE if not exists groups " String sql = "CREATE TABLE if not exists groups "
+ "(group_id INTEGER PRIMARY KEY, " + "(group_id INTEGER PRIMARY KEY, "
@ -377,7 +377,7 @@ public final class DrawableDB {
LOGGER.log(Level.SEVERE, "problem creating groups table", ex); LOGGER.log(Level.SEVERE, "problem creating groups table", ex);
return false; return false;
} }
try (Statement stmt = con.createStatement()) { try (Statement stmt = con.createStatement()) {
String sql = "CREATE TABLE if not exists hash_sets " String sql = "CREATE TABLE if not exists hash_sets "
+ "( hash_set_id INTEGER primary key," + "( hash_set_id INTEGER primary key,"
@ -387,7 +387,7 @@ public final class DrawableDB {
LOGGER.log(Level.SEVERE, "problem creating hash_sets table", ex); LOGGER.log(Level.SEVERE, "problem creating hash_sets table", ex);
return false; return false;
} }
try (Statement stmt = con.createStatement()) { try (Statement stmt = con.createStatement()) {
String sql = "CREATE TABLE if not exists hash_set_hits " String sql = "CREATE TABLE if not exists hash_set_hits "
+ "(hash_set_id INTEGER REFERENCES hash_sets(hash_set_id) not null, " + "(hash_set_id INTEGER REFERENCES hash_sets(hash_set_id) not null, "
@ -398,45 +398,45 @@ public final class DrawableDB {
LOGGER.log(Level.SEVERE, "problem creating hash_set_hits table", ex); LOGGER.log(Level.SEVERE, "problem creating hash_set_hits table", ex);
return false; return false;
} }
try (Statement stmt = con.createStatement()) { try (Statement stmt = con.createStatement()) {
String sql = "CREATE INDEX if not exists path_idx ON drawable_files(path)"; String sql = "CREATE INDEX if not exists path_idx ON drawable_files(path)";
stmt.execute(sql); stmt.execute(sql);
} catch (SQLException ex) { } catch (SQLException ex) {
LOGGER.log(Level.WARNING, "problem creating path_idx", ex); LOGGER.log(Level.WARNING, "problem creating path_idx", ex);
} }
try (Statement stmt = con.createStatement()) { try (Statement stmt = con.createStatement()) {
String sql = "CREATE INDEX if not exists name_idx ON drawable_files(name)"; String sql = "CREATE INDEX if not exists name_idx ON drawable_files(name)";
stmt.execute(sql); stmt.execute(sql);
} catch (SQLException ex) { } catch (SQLException ex) {
LOGGER.log(Level.WARNING, "problem creating name_idx", ex); LOGGER.log(Level.WARNING, "problem creating name_idx", ex);
} }
try (Statement stmt = con.createStatement()) { try (Statement stmt = con.createStatement()) {
String sql = "CREATE INDEX if not exists make_idx ON drawable_files(make)"; String sql = "CREATE INDEX if not exists make_idx ON drawable_files(make)";
stmt.execute(sql); stmt.execute(sql);
} catch (SQLException ex) { } catch (SQLException ex) {
LOGGER.log(Level.WARNING, "problem creating make_idx", ex); LOGGER.log(Level.WARNING, "problem creating make_idx", ex);
} }
try (Statement stmt = con.createStatement()) { try (Statement stmt = con.createStatement()) {
String sql = "CREATE INDEX if not exists model_idx ON drawable_files(model)"; String sql = "CREATE INDEX if not exists model_idx ON drawable_files(model)";
stmt.execute(sql); stmt.execute(sql);
} catch (SQLException ex) { } catch (SQLException ex) {
LOGGER.log(Level.WARNING, "problem creating model_idx", ex); LOGGER.log(Level.WARNING, "problem creating model_idx", ex);
} }
try (Statement stmt = con.createStatement()) { try (Statement stmt = con.createStatement()) {
String sql = "CREATE INDEX if not exists analyzed_idx ON drawable_files(analyzed)"; String sql = "CREATE INDEX if not exists analyzed_idx ON drawable_files(analyzed)";
stmt.execute(sql); stmt.execute(sql);
} catch (SQLException ex) { } catch (SQLException ex) {
LOGGER.log(Level.WARNING, "problem creating analyzed_idx", ex); LOGGER.log(Level.WARNING, "problem creating analyzed_idx", ex);
} }
return true; return true;
} }
@Override @Override
public void finalize() throws Throwable { public void finalize() throws Throwable {
try { try {
@ -445,7 +445,7 @@ public final class DrawableDB {
super.finalize(); super.finalize();
} }
} }
public void closeDBCon() { public void closeDBCon() {
if (con != null) { if (con != null) {
try { try {
@ -457,7 +457,7 @@ public final class DrawableDB {
} }
con = null; con = null;
} }
public void openDBCon() { public void openDBCon() {
try { try {
if (con == null || con.isClosed()) { if (con == null || con.isClosed()) {
@ -467,7 +467,7 @@ public final class DrawableDB {
LOGGER.log(Level.WARNING, "Failed to open connection to drawable.db", ex); LOGGER.log(Level.WARNING, "Failed to open connection to drawable.db", ex);
} }
} }
public boolean isClosed() throws SQLException { public boolean isClosed() throws SQLException {
if (con == null) { if (con == null) {
return true; return true;
@ -495,7 +495,7 @@ public final class DrawableDB {
} }
return names; return names;
} }
public boolean isGroupSeen(GroupKey<?> groupKey) { public boolean isGroupSeen(GroupKey<?> groupKey) {
dbReadLock(); dbReadLock();
try { try {
@ -514,14 +514,15 @@ public final class DrawableDB {
} }
return false; return false;
} }
public void markGroupSeen(GroupKey<?> gk) { public void markGroupSeen(GroupKey<?> gk, boolean seen) {
dbWriteLock(); dbWriteLock();
try { try {
//PreparedStatement updateGroup = con.prepareStatement("update groups set seen = 1 where value = ? and attribute = ?"); //PreparedStatement updateGroup = con.prepareStatement("update groups set seen = ? where value = ? and attribute = ?");
updateGroupStmt.clearParameters(); updateGroupStmt.clearParameters();
updateGroupStmt.setString(1, gk.getValueDisplayName()); updateGroupStmt.setBoolean(1, seen);
updateGroupStmt.setString(2, gk.getAttribute().attrName.toString()); updateGroupStmt.setString(2, gk.getValueDisplayName());
updateGroupStmt.setString(3, gk.getAttribute().attrName.toString());
updateGroupStmt.execute(); updateGroupStmt.execute();
} catch (SQLException ex) { } catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "Error marking group as seen", ex); LOGGER.log(Level.SEVERE, "Error marking group as seen", ex);
@ -529,41 +530,41 @@ public final class DrawableDB {
dbWriteUnlock(); dbWriteUnlock();
} }
} }
public boolean removeFile(long id) { public boolean removeFile(long id) {
DrawableTransaction trans = beginTransaction(); DrawableTransaction trans = beginTransaction();
boolean removeFile = removeFile(id, trans); boolean removeFile = removeFile(id, trans);
commitTransaction(trans, true); commitTransaction(trans, true);
return removeFile; return removeFile;
} }
public void updateFile(DrawableFile<?> f) { public void updateFile(DrawableFile<?> f) {
DrawableTransaction trans = beginTransaction(); DrawableTransaction trans = beginTransaction();
updateFile(f, trans); updateFile(f, trans);
commitTransaction(trans, true); commitTransaction(trans, true);
} }
public void insertFile(DrawableFile<?> f) { public void insertFile(DrawableFile<?> f) {
DrawableTransaction trans = beginTransaction(); DrawableTransaction trans = beginTransaction();
insertFile(f, trans); insertFile(f, trans);
commitTransaction(trans, true); commitTransaction(trans, true);
} }
public void insertFile(DrawableFile<?> f, DrawableTransaction tr) { public void insertFile(DrawableFile<?> f, DrawableTransaction tr) {
insertOrUpdateFile(f, tr, insertFileStmt); insertOrUpdateFile(f, tr, insertFileStmt);
} }
public void updateFile(DrawableFile<?> f, DrawableTransaction tr) { public void updateFile(DrawableFile<?> f, DrawableTransaction tr) {
insertOrUpdateFile(f, tr, updateFileStmt); insertOrUpdateFile(f, tr, updateFileStmt);
} }
private void insertOrUpdateFile(DrawableFile<?> f, DrawableTransaction tr, PreparedStatement stmt) { private void insertOrUpdateFile(DrawableFile<?> f, DrawableTransaction tr, PreparedStatement stmt) {
//TODO: implement batch version -jm //TODO: implement batch version -jm
if (tr.isClosed()) { if (tr.isClosed()) {
throw new IllegalArgumentException("can't update database with closed transaction"); throw new IllegalArgumentException("can't update database with closed transaction");
} }
dbWriteLock(); dbWriteLock();
try { try {
// Update the list of file IDs in memory // Update the list of file IDs in memory
@ -579,9 +580,9 @@ public final class DrawableDB {
stmt.setString(7, f.getModel()); stmt.setString(7, f.getModel());
stmt.setBoolean(8, f.isAnalyzed()); stmt.setBoolean(8, f.isAnalyzed());
stmt.executeUpdate(); stmt.executeUpdate();
final Collection<String> hashSetNames = DrawableAttribute.HASHSET.getValue(f); final Collection<String> hashSetNames = DrawableAttribute.HASHSET.getValue(f);
if (hashSetNames.isEmpty() == false) { if (hashSetNames.isEmpty() == false) {
for (String name : hashSetNames) { for (String name : hashSetNames) {
@ -612,9 +613,9 @@ public final class DrawableDB {
insertGroup(val.toString(), attr); insertGroup(val.toString(), attr);
} }
} }
tr.addUpdatedFile(f.getId()); tr.addUpdatedFile(f.getId());
} catch (SQLException | NullPointerException ex) { } catch (SQLException | NullPointerException ex) {
// This is one of the places where we get an error if the case is closed during processing, // This is one of the places where we get an error if the case is closed during processing,
// which doesn't need to be reported here. // which doesn't need to be reported here.
@ -625,38 +626,38 @@ public final class DrawableDB {
dbWriteUnlock(); dbWriteUnlock();
} }
} }
public DrawableTransaction beginTransaction() { public DrawableTransaction beginTransaction() {
return new DrawableTransaction(); return new DrawableTransaction();
} }
public void commitTransaction(DrawableTransaction tr, Boolean notify) { public void commitTransaction(DrawableTransaction tr, Boolean notify) {
if (tr.isClosed()) { if (tr.isClosed()) {
throw new IllegalArgumentException("can't close already closed transaction"); throw new IllegalArgumentException("can't close already closed transaction");
} }
tr.commit(notify); tr.commit(notify);
} }
public void addUpdatedFileListener(FileUpdateEvent.FileUpdateListener l) { public void addUpdatedFileListener(FileUpdateEvent.FileUpdateListener l) {
updateListeners.add(l); updateListeners.add(l);
} }
private void fireUpdatedFiles(Collection<Long> fileIDs) { private void fireUpdatedFiles(Collection<Long> fileIDs) {
for (FileUpdateEvent.FileUpdateListener listener : updateListeners) { for (FileUpdateEvent.FileUpdateListener listener : updateListeners) {
listener.handleFileUpdate(FileUpdateEvent.newUpdateEvent(fileIDs, null)); listener.handleFileUpdate(FileUpdateEvent.newUpdateEvent(fileIDs, null));
} }
} }
private void fireRemovedFiles(Collection<Long> fileIDs) { private void fireRemovedFiles(Collection<Long> fileIDs) {
for (FileUpdateEvent.FileUpdateListener listener : updateListeners) { for (FileUpdateEvent.FileUpdateListener listener : updateListeners) {
listener.handleFileUpdate(FileUpdateEvent.newRemovedEvent(fileIDs)); listener.handleFileUpdate(FileUpdateEvent.newRemovedEvent(fileIDs));
} }
} }
public Boolean isFileAnalyzed(DrawableFile<?> f) { public Boolean isFileAnalyzed(DrawableFile<?> f) {
return isFileAnalyzed(f.getId()); return isFileAnalyzed(f.getId());
} }
public Boolean isFileAnalyzed(long fileId) { public Boolean isFileAnalyzed(long fileId) {
dbReadLock(); dbReadLock();
try (Statement stmt = con.createStatement(); try (Statement stmt = con.createStatement();
@ -669,12 +670,12 @@ public final class DrawableDB {
} finally { } finally {
dbReadUnlock(); dbReadUnlock();
} }
return false; return false;
} }
public Boolean areFilesAnalyzed(Collection<Long> fileIds) { public Boolean areFilesAnalyzed(Collection<Long> fileIds) {
dbReadLock(); dbReadLock();
try (Statement stmt = con.createStatement(); try (Statement stmt = con.createStatement();
//Can't make this a preprared statement because of the IN ( ... ) //Can't make this a preprared statement because of the IN ( ... )
@ -687,15 +688,15 @@ public final class DrawableDB {
} finally { } finally {
dbReadUnlock(); dbReadUnlock();
} }
return false; return false;
} }
public Boolean isGroupAnalyzed(GroupKey<?> gk) { public Boolean isGroupAnalyzed(GroupKey<?> gk) {
dbReadLock(); dbReadLock();
try { try {
List<Long> fileIDsInGroup = getFileIDsInGroup(gk); List<Long> fileIDsInGroup = getFileIDsInGroup(gk);
try { try {
// In testing, this method appears to be a lot faster than doing one large select statement // In testing, this method appears to be a lot faster than doing one large select statement
for (Long fileID : fileIDsInGroup) { for (Long fileID : fileIDsInGroup) {
@ -708,7 +709,7 @@ public final class DrawableDB {
} }
return true; return true;
} }
} catch (SQLException ex) { } catch (SQLException ex) {
LOGGER.log(Level.WARNING, "problem counting analyzed files: ", ex); LOGGER.log(Level.WARNING, "problem counting analyzed files: ", ex);
} }
@ -862,9 +863,9 @@ public final class DrawableDB {
* @return * @return
*/ */
public <A extends Comparable<A>> List<A> findValuesForAttribute(DrawableAttribute<A> groupBy, GroupSortBy sortBy, SortOrder sortOrder) { public <A extends Comparable<A>> List<A> findValuesForAttribute(DrawableAttribute<A> groupBy, GroupSortBy sortBy, SortOrder sortOrder) {
List<A> vals = new ArrayList<>(); List<A> vals = new ArrayList<>();
switch (groupBy.attrName) { switch (groupBy.attrName) {
case ANALYZED: case ANALYZED:
case CATEGORY: case CATEGORY:
@ -876,7 +877,7 @@ public final class DrawableDB {
dbReadLock(); dbReadLock();
//TODO: convert this to prepared statement //TODO: convert this to prepared statement
StringBuilder query = new StringBuilder("select " + groupBy.attrName.toString() + ", count(*) from drawable_files group by " + groupBy.attrName.toString()); StringBuilder query = new StringBuilder("select " + groupBy.attrName.toString() + ", count(*) from drawable_files group by " + groupBy.attrName.toString());
String orderByClause = ""; String orderByClause = "";
switch (sortBy) { switch (sortBy) {
case GROUP_BY_VALUE: case GROUP_BY_VALUE:
@ -889,12 +890,12 @@ public final class DrawableDB {
// case PRIORITY: // case PRIORITY:
break; break;
} }
query.append(orderByClause); query.append(orderByClause);
if (orderByClause.equals("") == false) { if (orderByClause.equals("") == false) {
String sortOrderClause = ""; String sortOrderClause = "";
switch (sortOrder) { switch (sortOrder) {
case DESCENDING: case DESCENDING:
sortOrderClause = " DESC"; sortOrderClause = " DESC";
@ -907,7 +908,7 @@ public final class DrawableDB {
} }
query.append(sortOrderClause); query.append(sortOrderClause);
} }
try (Statement stmt = con.createStatement(); try (Statement stmt = con.createStatement();
ResultSet valsResults = stmt.executeQuery(query.toString())) { ResultSet valsResults = stmt.executeQuery(query.toString())) {
while (valsResults.next()) { while (valsResults.next()) {
@ -919,13 +920,13 @@ public final class DrawableDB {
dbReadUnlock(); dbReadUnlock();
} }
} }
return vals; return vals;
} }
public void insertGroup(final String value, DrawableAttribute<?> groupBy) { public void insertGroup(final String value, DrawableAttribute<?> groupBy) {
dbWriteLock(); dbWriteLock();
try { try {
//PreparedStatement insertGroup = con.prepareStatement("insert or replace into groups (value, attribute, seen) values (?,?,0)"); //PreparedStatement insertGroup = con.prepareStatement("insert or replace into groups (value, attribute, seen) values (?,?,0)");
insertGroupStmt.clearParameters(); insertGroupStmt.clearParameters();
@ -979,9 +980,9 @@ public final class DrawableDB {
return null; return null;
} }
} }
public List<Long> getFileIDsInGroup(GroupKey<?> groupKey) throws TskCoreException { public List<Long> getFileIDsInGroup(GroupKey<?> groupKey) throws TskCoreException {
if (groupKey.getAttribute().isDBColumn) { if (groupKey.getAttribute().isDBColumn) {
switch (groupKey.getAttribute().attrName) { switch (groupKey.getAttribute().attrName) {
case CATEGORY: case CATEGORY:
@ -995,7 +996,7 @@ public final class DrawableDB {
try { try {
PreparedStatement statement = getGroupStatment(groupKey.getAttribute()); PreparedStatement statement = getGroupStatment(groupKey.getAttribute());
statement.setObject(1, groupKey.getValue()); statement.setObject(1, groupKey.getValue());
try (ResultSet valsResults = statement.executeQuery()) { try (ResultSet valsResults = statement.executeQuery()) {
while (valsResults.next()) { while (valsResults.next()) {
files.add(valsResults.getLong(OBJ_ID)); files.add(valsResults.getLong(OBJ_ID));
@ -1006,10 +1007,10 @@ public final class DrawableDB {
} finally { } finally {
dbReadUnlock(); dbReadUnlock();
} }
return files; return files;
} }
public List<DrawableFile<?>> getFilesInGroup(GroupKey<?> key) throws TskCoreException { public List<DrawableFile<?>> getFilesInGroup(GroupKey<?> key) throws TskCoreException {
List<DrawableFile<?>> files = new ArrayList<>(); List<DrawableFile<?>> files = new ArrayList<>();
dbReadLock(); dbReadLock();
@ -1025,9 +1026,9 @@ public final class DrawableDB {
default: default:
statement = getGroupStatment(key.getAttribute()); statement = getGroupStatment(key.getAttribute());
} }
statement.setObject(1, key.getValue()); statement.setObject(1, key.getValue());
try (ResultSet valsResults = statement.executeQuery()) { try (ResultSet valsResults = statement.executeQuery()) {
while (valsResults.next()) { while (valsResults.next()) {
files.add(getFileFromID(valsResults.getLong(OBJ_ID), valsResults.getBoolean(ANALYZED))); files.add(getFileFromID(valsResults.getLong(OBJ_ID), valsResults.getBoolean(ANALYZED)));
@ -1038,16 +1039,16 @@ public final class DrawableDB {
} finally { } finally {
dbReadUnlock(); dbReadUnlock();
} }
return files; return files;
} }
private void closeStatements() throws SQLException { private void closeStatements() throws SQLException {
for (PreparedStatement pStmt : preparedStatements) { for (PreparedStatement pStmt : preparedStatements) {
pStmt.close(); pStmt.close();
} }
} }
public List<DrawableFile<?>> getFilesWithCategory(Category cat) throws TskCoreException, IllegalArgumentException { public List<DrawableFile<?>> getFilesWithCategory(Category cat) throws TskCoreException, IllegalArgumentException {
try { try {
List<DrawableFile<?>> files = new ArrayList<>(); List<DrawableFile<?>> files = new ArrayList<>();
@ -1064,18 +1065,18 @@ public final class DrawableDB {
throw ex; throw ex;
} }
} }
private PreparedStatement getGroupStatment(DrawableAttribute<?> groupBy) { private PreparedStatement getGroupStatment(DrawableAttribute<?> groupBy) {
return groupStatementMap.get(groupBy); return groupStatementMap.get(groupBy);
} }
public int countAllFiles() { public int countAllFiles() {
int result = -1; int result = -1;
dbReadLock(); dbReadLock();
try (ResultSet rs = con.createStatement().executeQuery("select count(*) as COUNT from drawable_files")) { try (ResultSet rs = con.createStatement().executeQuery("select count(*) as COUNT from drawable_files")) {
while (rs.next()) { while (rs.next()) {
result = rs.getInt("COUNT"); result = rs.getInt("COUNT");
break; break;
} }
@ -1100,7 +1101,7 @@ public final class DrawableDB {
} }
int valsResults = 0; int valsResults = 0;
dbWriteLock(); dbWriteLock();
try { try {
// Update the list of file IDs in memory // Update the list of file IDs in memory
removeImageFileFromList(id); removeImageFileFromList(id);
@ -1118,11 +1119,11 @@ public final class DrawableDB {
//indicates succesfull removal of 1 file //indicates succesfull removal of 1 file
return valsResults == 1; return valsResults == 1;
} }
public class MultipleTransactionException extends IllegalStateException { public class MultipleTransactionException extends IllegalStateException {
private static final String CANNOT_HAVE_MORE_THAN_ONE_OPEN_TRANSACTIO = "cannot have more than one open transaction"; private static final String CANNOT_HAVE_MORE_THAN_ONE_OPEN_TRANSACTIO = "cannot have more than one open transaction";
public MultipleTransactionException() { public MultipleTransactionException() {
super(CANNOT_HAVE_MORE_THAN_ONE_OPEN_TRANSACTIO); super(CANNOT_HAVE_MORE_THAN_ONE_OPEN_TRANSACTIO);
} }
@ -1168,31 +1169,31 @@ public final class DrawableDB {
*/ */
@GuardedBy("fileIDlist") @GuardedBy("fileIDlist")
private final Set<Long> fileIDsInDB = new HashSet<>(); private final Set<Long> fileIDsInDB = new HashSet<>();
public boolean isInDB(Long id) { public boolean isInDB(Long id) {
synchronized (fileIDsInDB) { synchronized (fileIDsInDB) {
return fileIDsInDB.contains(id); return fileIDsInDB.contains(id);
} }
} }
private void addImageFileToList(Long id) { private void addImageFileToList(Long id) {
synchronized (fileIDsInDB) { synchronized (fileIDsInDB) {
fileIDsInDB.add(id); fileIDsInDB.add(id);
} }
} }
private void removeImageFileFromList(Long id) { private void removeImageFileFromList(Long id) {
synchronized (fileIDsInDB) { synchronized (fileIDsInDB) {
fileIDsInDB.remove(id); fileIDsInDB.remove(id);
} }
} }
public int getNumberOfImageFilesInList() { public int getNumberOfImageFilesInList() {
synchronized (fileIDsInDB) { synchronized (fileIDsInDB) {
return fileIDsInDB.size(); return fileIDsInDB.size();
} }
} }
private void initializeImageList() { private void initializeImageList() {
synchronized (fileIDsInDB) { synchronized (fileIDsInDB) {
dbReadLock(); dbReadLock();
@ -1214,7 +1215,7 @@ public final class DrawableDB {
* For performance reasons, keep the file type in memory * For performance reasons, keep the file type in memory
*/ */
private final Map<AbstractFile, Boolean> videoFileMap = new ConcurrentHashMap<>(); private final Map<AbstractFile, Boolean> videoFileMap = new ConcurrentHashMap<>();
public boolean isVideoFile(AbstractFile f) { public boolean isVideoFile(AbstractFile f) {
return videoFileMap.computeIfAbsent(f, ImageGalleryModule::isVideoFile); return videoFileMap.computeIfAbsent(f, ImageGalleryModule::isVideoFile);
} }
@ -1241,7 +1242,7 @@ public final class DrawableDB {
.map(Content::getId) .map(Content::getId)
.filter(this::isInDB) .filter(this::isInDB)
.count(); .count();
} catch (IllegalStateException ex) { } catch (IllegalStateException ex) {
LOGGER.log(Level.WARNING, "Case closed while getting files"); LOGGER.log(Level.WARNING, "Case closed while getting files");
} catch (TskCoreException ex1) { } catch (TskCoreException ex1) {
@ -1254,11 +1255,11 @@ public final class DrawableDB {
* inner class that can reference access database connection * inner class that can reference access database connection
*/ */
public class DrawableTransaction { public class DrawableTransaction {
private final Set<Long> updatedFiles; private final Set<Long> updatedFiles;
private final Set<Long> removedFiles; private final Set<Long> removedFiles;
private boolean closed = false; private boolean closed = false;
/** /**
@ -1277,13 +1278,13 @@ public final class DrawableDB {
dbWriteLock(); dbWriteLock();
try { try {
con.setAutoCommit(false); con.setAutoCommit(false);
} catch (SQLException ex) { } catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "failed to set auto-commit to to false", ex); LOGGER.log(Level.SEVERE, "failed to set auto-commit to to false", ex);
} }
} }
synchronized public void rollback() { synchronized public void rollback() {
if (!closed) { if (!closed) {
try { try {
@ -1296,14 +1297,14 @@ public final class DrawableDB {
} }
} }
} }
synchronized private void commit(Boolean notify) { synchronized private void commit(Boolean notify) {
if (!closed) { if (!closed) {
try { try {
con.commit(); con.commit();
// make sure we close before we update, bc they'll need locks // make sure we close before we update, bc they'll need locks
close(); close();
if (notify) { if (notify) {
fireUpdatedFiles(updatedFiles); fireUpdatedFiles(updatedFiles);
fireRemovedFiles(removedFiles); fireRemovedFiles(removedFiles);
@ -1318,7 +1319,7 @@ public final class DrawableDB {
} }
} }
} }
synchronized private void close() { synchronized private void close() {
if (!closed) { if (!closed) {
try { try {
@ -1335,15 +1336,15 @@ public final class DrawableDB {
} }
} }
} }
synchronized public Boolean isClosed() { synchronized public Boolean isClosed() {
return closed; return closed;
} }
synchronized private void addUpdatedFile(Long f) { synchronized private void addUpdatedFile(Long f) {
updatedFiles.add(f); updatedFiles.add(f);
} }
synchronized private void addRemovedFile(long id) { synchronized private void addRemovedFile(long id) {
removedFiles.add(id); removedFiles.add(id);
} }

View File

@ -138,7 +138,6 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
if (fileIDs.contains(f) == false) { if (fileIDs.contains(f) == false) {
fileIDs.add(f); fileIDs.add(f);
seen.set(false); seen.set(false);
} }
} }
@ -155,8 +154,8 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
return this.groupKey.getValueDisplayName().compareTo(other.groupKey.getValueDisplayName()); return this.groupKey.getValueDisplayName().compareTo(other.groupKey.getValueDisplayName());
} }
void setSeen() { void setSeen(boolean isSeen) {
this.seen.set(true); this.seen.set(isSeen);
} }
public ReadOnlyBooleanWrapper seenProperty() { public ReadOnlyBooleanWrapper seenProperty() {

View File

@ -245,6 +245,9 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener {
List<Long> newFiles = files == null ? new ArrayList<>() : files; List<Long> newFiles = files == null ? new ArrayList<>() : files;
DrawableGroup g = new DrawableGroup(groupKey, newFiles); DrawableGroup g = new DrawableGroup(groupKey, newFiles);
g.seenProperty().addListener((observable, oldSeen, newSeen) -> {
markGroupSeen(g, newSeen);
});
synchronized (groupMap) { synchronized (groupMap) {
groupMap.put(groupKey, g); groupMap.put(groupKey, g);
} }
@ -258,11 +261,15 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener {
* @param group the {@link DrawableGroup} to mark as seen * @param group the {@link DrawableGroup} to mark as seen
*/ */
@ThreadConfined(type = ThreadType.JFX) @ThreadConfined(type = ThreadType.JFX)
public void markGroupSeen(DrawableGroup group) { public void markGroupSeen(DrawableGroup group, boolean seen) {
db.markGroupSeen(group.getGroupKey()); db.markGroupSeen(group.getGroupKey(), seen);
group.setSeen(); group.setSeen(seen);
unSeenGroups.removeAll(group); if (seen) {
unSeenGroups.removeAll(group);
} else if (unSeenGroups.contains(group) == false) {
unSeenGroups.add(group);
FXCollections.sort(unSeenGroups, sortBy.getGrpComparator(sortOrder));
}
} }
/** /**
@ -324,19 +331,13 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener {
if (task == null || (task.isCancelled() == false)) { if (task == null || (task.isCancelled() == false)) {
final boolean groupSeen = db.isGroupSeen(g.getGroupKey()); final boolean groupSeen = db.isGroupSeen(g.getGroupKey());
if (groupSeen) {
g.setSeen();
}
Platform.runLater(() -> { Platform.runLater(() -> {
if (analyzedGroups.contains(g) == false) { if (analyzedGroups.contains(g) == false) {
analyzedGroups.add(g); analyzedGroups.add(g);
} }
if (groupSeen) { markGroupSeen(g, groupSeen);
unSeenGroups.removeAll(g);
} else if (unSeenGroups.contains(g) == false) {
unSeenGroups.add(g);
FXCollections.sort(unSeenGroups, sortBy.getGrpComparator(sortOrder));
}
}); });
} }
} }