edu.harvard.deas.hyperenc
Class PersistentMap<K,V>

java.lang.Object
  extended by edu.harvard.deas.hyperenc.PersistentMap<K,V>

public class PersistentMap<K,V>
extends Object

A map that is stored persistently on disk in a database. A persistent map is a sorted map that is stored in a Berkeley database on disk so that it can be used across multiple instances of a program, or transferred between different clients on different machines.

Each persistent map has a name; no two persistent maps may have the same name. Clients request a map with a particular name using getInstance. If a map by that name already exists, the existing map is returned. Otherwise, a new map is constructed and returned.

The Berkeley DB JE Collections API does not support Map.size() because the operation would be prohibitively expensive to perform reliably on the database. As such, this class keeps track of its own size, maintaining the size data when an element is inserted or removed. This means that it is important that there be only one map object per underlying database (although multiple threads may have a reference to the single map, which is thread-safe): getInstance ensures that this is true.

This implementation forces all data to disk (using Environment.sync())) every time the map is changed. This synchronous I/O incurs a performance penalty, but is necessary to ensure security. Consider an example of a problem that could occur with asynchronous I/O: a user encrypts a message using blocks stored in a PersistentMap, and e-mails the encrypted message to a friend. Then, the power fails after the message is sent, but before the database updates are written to disk. The ciphertext is now public, but the fact that certain encryption blocks were used was not recorded to disk, so those blocks will be used again the next time the program is run!

An alternative approach would be to require that the caller sync the Environment before externally-visible I/O (such as an e-mail). The synchronous I/O strategy is less prone to error, so we'll use it unless performance suffers too greatly.

This map does not allow null values.

Thread safety: PersistentMap is unconditionally thread-safe. The keySet and values methods return collections that are not backed by the map, so there is no need to synchronize on the map when iterating over them.


Field Summary
static File DEFAULT_HOME
          The location on disk of the default database.
 
Method Summary
 boolean containsKey(K key)
          Same as Map.containsKey(java.lang.Object).
 K firstKey()
          Returns the first (lowest) key currently in this map.
 V get(K key)
          Same as Map.get(Object).
 V getFirst()
          Returns the element with the first (lowest) key currently in this map.
static
<K,V> PersistentMap<K,V>
getInstance(String name, Class<K> keyClass, Class<V> valueClass)
          Return a map with the given name, creating it if necessary.
static
<K,V> PersistentMap<K,V>
getInstance(String name, Class<K> keyClass, Class<V> valueClass, File environment)
          Return a map with the given name, creating it if necessary.
 boolean isEmpty()
          Returns true iff this map contains no elements.
 Set<K> keySet()
          Returns a Set view of the keys contained in this map.
 K lastKey()
          Returns the last (highest) key currently in this map.
 V put(K key, V value)
          Same as Map.put(Object,Object).
 V remove(K key)
          Same as Map.remove(Object).
 V removeFirst()
          Removes and returns the element with the first (lowest) key currently in this map.
 int size()
          Returns the number of key-value mappings in this map.
 Collection<V> values()
          Returns a Collection view of the values contained in this map.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

DEFAULT_HOME

public static final File DEFAULT_HOME
The location on disk of the default database.

Method Detail

getInstance

public static <K,V> PersistentMap<K,V> getInstance(String name,
                                                   Class<K> keyClass,
                                                   Class<V> valueClass,
                                                   File environment)
Return a map with the given name, creating it if necessary. This method checks if there is already an open map with the given name in the given environment; if so, it is returned. If not, it attempts to find a map with that name stored in the environment, open it, and return it. If there is no such map, a new, empty map with that name is created in the environment and returned.

If the key type or value type is a primitive, then a TupleBinding will be used as the binding, otherwise a SerialBinding will be used. In either case, K and V must be serializable.

Note on type safety: This method will return map with type parameters K and V, regardless of the actual type of the underlying database. (It is somewhat difficult to actually enforce type safety because the types stored in the database are not readily accessible.) Attempts to read entries from the map that were not originally written through a map with the same parameters K and V will likely throw an IllegalArgumentException.

Parameters:
name - The name of the map. If there is another map by this name, it will be returned. Otherwise, a new map will be created.
keyClass - The class of the keys of this map.
valueClass - The class of the values of this map.
environment - The path on disk of the database environment underlying the database backing this map. Must be readable and writable. If it doesn't exist, it will be created. If null, the default environment specified by DEFAULT_HOME is used.
Returns:
The map with name name. If it didn't exist before, it is created and returned.
Throws:
IllegalArgumentException - if the specified environment does not exist and could not be created on disk, or if it exists and could not be opened.
See Also:
TupleBinding.getPrimitiveBinding(Class), Environment

getInstance

public static <K,V> PersistentMap<K,V> getInstance(String name,
                                                   Class<K> keyClass,
                                                   Class<V> valueClass)
Return a map with the given name, creating it if necessary. Equivalent to calling getInstance(name, keyClass, valueClass, null).


size

public int size()
Returns the number of key-value mappings in this map. This size reflects insertions and removals performed using this object's methods (such as put and remove). It does not necessarily reflect the on-disk state of the map.

Specific situations in which this size may not be accurate include:

Returns:
the number of key-value mappings in this map

get

public V get(K key)
Same as Map.get(Object).


keySet

public Set<K> keySet()
Returns a Set view of the keys contained in this map. Same as Map.keySet() except that the returned set is not backed by the map, so changes to the returned set are not reflected in the original map.

Returns:
a set view of the keys contained in this map

values

public Collection<V> values()
Returns a Collection view of the values contained in this map. Same as Map.values() except that the returned set is not backed by the map, so changes to the returned set are not reflected in the original map.

Returns:
a set view of the values contained in this map

put

public V put(K key,
             V value)
Same as Map.put(Object,Object). If value was not already present in the map, increments the size of this map by 1. Otherwise, doesn't change the size of the map.


isEmpty

public boolean isEmpty()
Returns true iff this map contains no elements.

Returns:
this.size() == 0

remove

public V remove(K key)
Same as Map.remove(Object). If value was present in the map, decrements the size of this map by 1. Otherwise, doesn't change the size of the map.


containsKey

public boolean containsKey(K key)
Same as Map.containsKey(java.lang.Object).


firstKey

public K firstKey()
Returns the first (lowest) key currently in this map. Same as SortedMap.firstKey().

Returns:
the first (lowest) key currently in this map
Throws:
NoSuchElementException - if this map is empty

lastKey

public K lastKey()
Returns the last (highest) key currently in this map. Same as SortedMap.lastKey().

Returns:
the last (highest) key currently in this map
Throws:
NoSuchElementException - if this map is empty

getFirst

public V getFirst()
Returns the element with the first (lowest) key currently in this map. Equivalent to this.get(this.firstKey()), but is atomic.

Returns:
the value associated with the lowest key in this map

removeFirst

public V removeFirst()
Removes and returns the element with the first (lowest) key currently in this map. Equivalent to this.get(this.firstKey()), but is atomic.

Returns:
the value associated with the lowest key in this map
Throws:
NoSuchElementException - if the map is empty