/** * */ package com.grendelscan.data.database.collections; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; import com.grendelscan.data.database.CommandJob; import com.grendelscan.data.database.DataNotFoundException; import com.grendelscan.data.database.Database; import com.grendelscan.data.database.DatabaseUser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.grendelscan.scan.Scan; /** * @author david * */ public class DatabaseBackedMap<K, V> extends DatabaseBackedCollection implements DatabaseUser, Map<K, V> { private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseBackedMap.class); private Database database; private Map<K, V> cache; public DatabaseBackedMap(String uniqueName) { super(uniqueName + "map"); cache = new HashMap<K, V>(); database = Scan.getInstance().getTestData().getDatabase(); initializeDatabase(uniqueName); } @SuppressWarnings("unchecked") private void initializeDatabase(String uniqueName) { LOGGER.debug("Initializing database for database-backed map: " + collectionName); try { if (!database.tableExists(collectionName)) { String tableQuery = "CREATE TABLE " + collectionName + " (name blob, value blob, PRIMARY KEY (name))"; String indexQuery = "CREATE INDEX IDX_DEFAULT_" + collectionName + " ON " + collectionName + " (name)"; database.execute(tableQuery); database.execute(indexQuery); } else { Object[][] content = database.selectAll("SELECT name, value FROM " + collectionName, new Object[0]); for (Object[] row: content) { cache.put((K)row[0], (V)row[1]); } } } catch (DataNotFoundException e) { LOGGER.info("No data found in " + collectionName + " collection. This may not be a problem.", e); } catch (Throwable e) { LOGGER.error("Problem with creating database for " + uniqueName + " database backed map: " + e.toString(), e); System.exit(1); } } @Override public Set<K> keySet() { synchronized(this) { return cache.keySet(); } } private V privatePut(K name, V value) { V oldValue = cache.get(name); cache.put(name, value); removeFromDb(name); CommandJob job = new CommandJob("INSERT INTO " + collectionName + " (name, value) VALUES (?, ?)", new Object[] { name, value }); try { database.execute(job); } catch (Throwable e) { LOGGER.error("Problem saving string: " + e.toString(), e); } return oldValue; } @Override public V put(K name, V value) { if (name == null) { throw new IllegalArgumentException("Null is not a legal name"); } if (value == null) { throw new IllegalArgumentException("Null is not a legal value"); } synchronized(this) { return privatePut(name, value); } } private void removeFromDb(Object name) { CommandJob job = new CommandJob("DELETE FROM " + collectionName + " WHERE name = ?", new Object[] { name }); try { database.execute(job); } catch (Throwable e) { LOGGER.error("Problem deleting from " + collectionName + ": " + e.toString(), e); } } @Override public void shutdown(boolean gracefully) throws InterruptedException { LOGGER.debug("Shutting down transaction record"); database.stop(gracefully).join(); } @Override public int size() { synchronized(this) { return cache.size(); } } @Override public Collection<V> values() { synchronized(this) { return cache.values(); } } /* (non-Javadoc) * @see java.util.Map#isEmpty() */ @Override public boolean isEmpty() { synchronized(this) { return cache.size() > 0; } } @Override public boolean containsKey(Object key) { synchronized(this) { return cache.containsKey(key); } } @Override public V get(Object key) { synchronized(this) { return cache.get(key); } } /* (non-Javadoc) * @see java.util.Map#containsValue(java.lang.Object) */ @Override public boolean containsValue(Object value) { synchronized(this) { return cache.containsValue(value); } } /* (non-Javadoc) * @see java.util.Map#putAll(java.util.Map) */ @Override public void putAll(Map<? extends K, ? extends V> m) { synchronized (this) { for (K key : m.keySet()) { privatePut(key, m.get(key)); } } } /* (non-Javadoc) * @see java.util.Map#clear() */ @Override public void clear() { synchronized (this) { cache.clear(); CommandJob job = new CommandJob("DELETE FROM " + collectionName, new Object[0]); try { database.execute(job); } catch (Throwable e) { LOGGER.error("Problem deleting from " + collectionName + ": " + e.toString(), e); } } } /* (non-Javadoc) * @see java.util.Map#entrySet() */ @Override public Set<java.util.Map.Entry<K, V>> entrySet() { synchronized (this) { return cache.entrySet(); } } /* (non-Javadoc) * @see java.util.Map#remove(java.lang.Object) */ @Override public V remove(Object key) { synchronized (this) { V old = cache.get(key); cache.remove(key); removeFromDb(key); return old; } } }