com.sleepycat.collections.StoredMap Maven / Gradle / Ivy
/*-
* Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
*
* This file was distributed by Oracle as part of a version of Oracle Berkeley
* DB Java Edition made available at:
*
* http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
*
* Please see the LICENSE file included in the top-level directory of the
* appropriate version of Oracle Berkeley DB Java Edition for a copy of the
* license and additional information.
*/
package com.sleepycat.collections;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import com.sleepycat.bind.EntityBinding;
import com.sleepycat.bind.EntryBinding;
import com.sleepycat.je.Database;
/* */
import com.sleepycat.je.EnvironmentFailureException; // for javadoc
import com.sleepycat.je.OperationFailureException; // for javadoc
/* */
import com.sleepycat.je.OperationStatus;
import com.sleepycat.util.RuntimeExceptionWrapper;
import com.sleepycat.util.keyrange.KeyRangeException;
/**
* A Map view of a {@link Database}.
*
* In addition to the standard Map methods, this class provides the
* following methods for stored maps only. Note that the use of these methods
* is not compatible with the standard Java collections interface.
*
* - {@link #duplicates}
* - {@link #duplicatesMap}
* - {@link #append}
*
*
* @author Mark Hayes
*/
public class StoredMap extends StoredContainer
implements ConcurrentMap {
private StoredKeySet keySet;
private StoredEntrySet entrySet;
private StoredValueSet valueSet;
/**
* Creates a map view of a {@link Database}.
*
* @param database is the Database underlying the new collection.
*
* @param keyBinding is the binding used to translate between key buffers
* and key objects.
*
* @param valueBinding is the binding used to translate between value
* buffers and value objects.
*
* @param writeAllowed is true to create a read-write collection or false
* to create a read-only collection.
*
* @throws IllegalArgumentException if formats are not consistently
* defined or a parameter is invalid.
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public StoredMap(Database database,
EntryBinding keyBinding,
EntryBinding valueBinding,
boolean writeAllowed) {
super(new DataView(database, keyBinding, valueBinding, null,
writeAllowed, null));
initView();
}
/**
* Creates a map view of a {@link Database} with a {@link
* PrimaryKeyAssigner}. Writing is allowed for the created map.
*
* @param database is the Database underlying the new collection.
*
* @param keyBinding is the binding used to translate between key buffers
* and key objects.
*
* @param valueBinding is the binding used to translate between value
* buffers and value objects.
*
* @param keyAssigner is used by the {@link #append} method to assign
* primary keys.
*
* @throws IllegalArgumentException if formats are not consistently
* defined or a parameter is invalid.
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public StoredMap(Database database,
EntryBinding keyBinding,
EntryBinding valueBinding,
PrimaryKeyAssigner keyAssigner) {
super(new DataView(database, keyBinding, valueBinding, null,
true, keyAssigner));
initView();
}
/**
* Creates a map entity view of a {@link Database}.
*
* @param database is the Database underlying the new collection.
*
* @param keyBinding is the binding used to translate between key buffers
* and key objects.
*
* @param valueEntityBinding is the binding used to translate between
* key/value buffers and entity value objects.
*
* @param writeAllowed is true to create a read-write collection or false
* to create a read-only collection.
*
* @throws IllegalArgumentException if formats are not consistently
* defined or a parameter is invalid.
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public StoredMap(Database database,
EntryBinding keyBinding,
EntityBinding valueEntityBinding,
boolean writeAllowed) {
super(new DataView(database, keyBinding, null, valueEntityBinding,
writeAllowed, null));
initView();
}
/**
* Creates a map entity view of a {@link Database} with a {@link
* PrimaryKeyAssigner}. Writing is allowed for the created map.
*
* @param database is the Database underlying the new collection.
*
* @param keyBinding is the binding used to translate between key buffers
* and key objects.
*
* @param valueEntityBinding is the binding used to translate between
* key/value buffers and entity value objects.
*
* @param keyAssigner is used by the {@link #append} method to assign
* primary keys.
*
* @throws IllegalArgumentException if formats are not consistently
* defined or a parameter is invalid.
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public StoredMap(Database database,
EntryBinding keyBinding,
EntityBinding valueEntityBinding,
PrimaryKeyAssigner keyAssigner) {
super(new DataView(database, keyBinding, null, valueEntityBinding,
true, keyAssigner));
initView();
}
StoredMap(DataView view) {
super(view);
initView();
}
/**
* Override this method to initialize view-dependent fields.
*/
void initAfterClone() {
initView();
}
/**
* The keySet, entrySet and valueSet are created during Map construction
* rather than lazily when requested (as done with the java.util.Map
* implementations). This is done to avoid synchronization every time they
* are requested. Since they are requested often but a StoredMap is
* created infrequently, this gives the best performance. The additional
* views are small objects and are cheap to construct.
*/
private void initView() {
/* entrySet */
if (areKeyRangesAllowed()) {
entrySet = new StoredSortedEntrySet(view);
} else {
entrySet = new StoredEntrySet(view);
}
/* keySet */
DataView newView = view.keySetView();
if (areKeyRangesAllowed()) {
keySet = new StoredSortedKeySet(newView);
} else {
keySet = new StoredKeySet(newView);
}
/* valueSet */
newView = view.valueSetView();
if (areKeyRangesAllowed() && newView.canDeriveKeyFromValue()) {
valueSet = new StoredSortedValueSet(newView);
} else {
valueSet = new StoredValueSet(newView);
}
}
/**
* Returns the value to which this map maps the specified key. If
* duplicates are allowed, this method returns the first duplicate, in the
* order in which duplicates are configured, that maps to the specified
* key.
*
* This method conforms to the {@link Map#get} interface.
*
*
* @throws OperationFailureException if one of the Read Operation
* Failures occurs.
*
* @throws EnvironmentFailureException if an unexpected, internal or
* environment-wide failure occurs.
*
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public V get(Object key) {
return (V) getValue(key);
}
/**
* Associates the specified value with the specified key in this map
* (optional operation). If duplicates are allowed and the specified key
* is already mapped to a value, this method appends the new duplicate
* after the existing duplicates. This method conforms to the {@link
* Map#put} interface.
*
* The key parameter may be null if an entity binding is used and the
* key will be derived from the value (entity) parameter. If an entity
* binding is used and the key parameter is non-null, then the key
* parameter must be equal to the key derived from the value parameter.
*
* @return the previous value associated with specified key, or null if
* there was no mapping for the key or if duplicates are allowed.
*
*
* @throws OperationFailureException if one of the Write
* Operation Failures occurs.
*
* @throws EnvironmentFailureException if an unexpected, internal or
* environment-wide failure occurs.
*
*
* @throws UnsupportedOperationException if the collection is indexed, or
* if the collection is read-only.
*
* @throws IllegalArgumentException if an entity value binding is used and
* the primary key of the value given is different than the existing stored
* primary key.
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public V put(K key, V value) {
return (V) putKeyValue(key, value);
}
/**
* Appends a given value returning the newly assigned key. If a {@link
* PrimaryKeyAssigner} is associated with Store for this map, it will be
* used to assigned the returned key. Otherwise the Store must be a QUEUE
* or RECNO database and the next available record number is assigned as
* the key. This method does not exist in the standard {@link Map}
* interface.
*
* Note that for the JE product, QUEUE and RECNO databases are not
* supported, and therefore a PrimaryKeyAssigner must be associated with
* the map in order to call this method.
*
* @param value the value to be appended.
*
* @return the assigned key.
*
*
* @throws OperationFailureException if one of the Write
* Operation Failures occurs.
*
* @throws EnvironmentFailureException if an unexpected, internal or
* environment-wide failure occurs.
*
*
* @throws UnsupportedOperationException if the collection is indexed, or
* if the collection is read-only, or if the Store has no {@link
* PrimaryKeyAssigner} and is not a QUEUE or RECNO database.
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public K append(V value) {
boolean doAutoCommit = beginAutoCommit();
try {
Object[] key = new Object[1];
view.append(value, key, null);
commitAutoCommit(doAutoCommit);
return (K) key[0];
} catch (Exception e) {
throw handleException(e, doAutoCommit);
}
}
/**
* Removes the mapping for this key from this map if present (optional
* operation). If duplicates are allowed, this method removes all
* duplicates for the given key. This method conforms to the {@link
* Map#remove} interface.
*
*
* @throws OperationFailureException if one of the Write
* Operation Failures occurs.
*
* @throws EnvironmentFailureException if an unexpected, internal or
* environment-wide failure occurs.
*
*
* @throws UnsupportedOperationException if the collection is read-only.
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public V remove(Object key) {
Object[] oldVal = new Object[1];
removeKey(key, oldVal);
return (V) oldVal[0];
}
/**
* If the specified key is not already associated with a value, associate
* it with the given value. This method conforms to the {@link
* ConcurrentMap#putIfAbsent} interface.
*
*
* @throws OperationFailureException if one of the Write
* Operation Failures occurs.
*
* @throws EnvironmentFailureException if an unexpected, internal or
* environment-wide failure occurs.
*
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public V putIfAbsent(K key, V value) {
DataCursor cursor = null;
boolean doAutoCommit = beginAutoCommit();
try {
cursor = new DataCursor(view, true);
V oldValue;
while (true) {
OperationStatus status =
cursor.putNoOverwrite(key, value, false /*useCurrentKey*/);
if (status == OperationStatus.SUCCESS) {
/* We inserted the key. Return null. */
oldValue = null;
break;
} else {
status = cursor.getSearchKey(key, null /*value*/,
false /*lockForWrite*/);
if (status == OperationStatus.SUCCESS) {
/* The key is present. Return the current value. */
oldValue = (V) cursor.getCurrentValue();
break;
} else {
/*
* If Serializable isolation is not configured, another
* thread can delete the record after our attempt to
* insert it failed above. Loop back and try again.
*/
continue;
}
}
}
closeCursor(cursor);
commitAutoCommit(doAutoCommit);
return oldValue;
} catch (Exception e) {
closeCursor(cursor);
throw handleException(e, doAutoCommit);
}
}
/**
* Remove entry for key only if currently mapped to given value. This
* method conforms to the {@link ConcurrentMap#remove(Object,Object)}
* interface.
*
*
* @throws OperationFailureException if one of the Write
* Operation Failures occurs.
*
* @throws EnvironmentFailureException if an unexpected, internal or
* environment-wide failure occurs.
*
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public boolean remove(Object key, Object value) {
DataCursor cursor = null;
boolean doAutoCommit = beginAutoCommit();
try {
cursor = new DataCursor(view, true, key);
OperationStatus status = cursor.getFirst(true /*lockForWrite*/);
boolean removed;
if (status == OperationStatus.SUCCESS &&
cursor.getCurrentValue().equals(value)) {
cursor.delete();
removed = true;
} else {
removed = false;
}
closeCursor(cursor);
commitAutoCommit(doAutoCommit);
return removed;
} catch (Exception e) {
closeCursor(cursor);
throw handleException(e, doAutoCommit);
}
}
/**
* Replace entry for key only if currently mapped to some value. This
* method conforms to the {@link ConcurrentMap#replace(Object,Object)}
* interface.
*
*
* @throws OperationFailureException if one of the Write
* Operation Failures occurs.
*
* @throws EnvironmentFailureException if an unexpected, internal or
* environment-wide failure occurs.
*
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public V replace(K key, V value) {
DataCursor cursor = null;
boolean doAutoCommit = beginAutoCommit();
try {
cursor = new DataCursor(view, true, key);
OperationStatus status = cursor.getFirst(true /*lockForWrite*/);
V oldValue;
if (status == OperationStatus.SUCCESS) {
oldValue = (V) cursor.getCurrentValue();
cursor.putCurrent(value);
} else {
oldValue = null;
}
closeCursor(cursor);
commitAutoCommit(doAutoCommit);
return oldValue;
} catch (Exception e) {
closeCursor(cursor);
throw handleException(e, doAutoCommit);
}
}
/**
* Replace entry for key only if currently mapped to given value. This
* method conforms to the {@link
* ConcurrentMap#replace(Object,Object,Object)} interface.
*
*
* @throws OperationFailureException if one of the Write
* Operation Failures occurs.
*
* @throws EnvironmentFailureException if an unexpected, internal or
* environment-wide failure occurs.
*
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public boolean replace(K key, V oldValue, V newValue) {
DataCursor cursor = null;
boolean doAutoCommit = beginAutoCommit();
try {
cursor = new DataCursor(view, true, key);
OperationStatus status = cursor.getFirst(true /*lockForWrite*/);
boolean replaced;
if (status == OperationStatus.SUCCESS &&
cursor.getCurrentValue().equals(oldValue)) {
cursor.putCurrent(newValue);
replaced = true;
} else {
replaced = false;
}
closeCursor(cursor);
commitAutoCommit(doAutoCommit);
return replaced;
} catch (Exception e) {
closeCursor(cursor);
throw handleException(e, doAutoCommit);
}
}
/**
* Returns true if this map contains the specified key. This method
* conforms to the {@link Map#containsKey} interface.
*
*
* @throws OperationFailureException if one of the Read Operation
* Failures occurs.
*
* @throws EnvironmentFailureException if an unexpected, internal or
* environment-wide failure occurs.
*
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public boolean containsKey(Object key) {
return super.containsKey(key);
}
/**
* Returns true if this map contains the specified value. When an entity
* binding is used, this method returns whether the map contains the
* primary key and value mapping of the entity. This method conforms to
* the {@link Map#containsValue} interface.
*
*
* @throws OperationFailureException if one of the Read Operation
* Failures occurs.
*
* @throws EnvironmentFailureException if an unexpected, internal or
* environment-wide failure occurs.
*
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public boolean containsValue(Object value) {
return super.containsValue(value);
}
/**
* Copies all of the mappings from the specified map to this map (optional
* operation). When duplicates are allowed, the mappings in the specified
* map are effectively appended to the existing mappings in this map, that
* is no previously existing mappings in this map are replaced. This
* method conforms to the {@link Map#putAll} interface.
*
*
* @throws OperationFailureException if one of the Write
* Operation Failures occurs.
*
* @throws EnvironmentFailureException if an unexpected, internal or
* environment-wide failure occurs.
*
*
* @throws UnsupportedOperationException if the collection is read-only, or
* if the collection is indexed.
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public void putAll(Map extends K, ? extends V> map) {
boolean doAutoCommit = beginAutoCommit();
Iterator i = null;
try {
Collection coll = map.entrySet();
i = storedOrExternalIterator(coll);
while (i.hasNext()) {
Map.Entry entry = (Map.Entry) i.next();
putKeyValue(entry.getKey(), entry.getValue());
}
StoredIterator.close(i);
commitAutoCommit(doAutoCommit);
} catch (Exception e) {
StoredIterator.close(i);
throw handleException(e, doAutoCommit);
}
}
/**
* Returns a set view of the keys contained in this map. A {@link
* java.util.SortedSet} is returned if the map supports key ranges. The
* returned collection will be read-only if the map is read-only. This
* method conforms to the {@link Map#keySet()} interface.
*
* Note that the return value is a StoredCollection and must be treated
* as such; for example, its iterators must be explicitly closed.
*
* @return a {@link StoredKeySet} or a {@link StoredSortedKeySet} for this
* map.
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*
* @see #areKeyRangesAllowed
* @see #isWriteAllowed
*/
public Set keySet() {
return keySet;
}
/**
* Returns a set view of the mappings contained in this map. A {@link
* java.util.SortedSet} is returned if the map supports key ranges. The
* returned collection will be read-only if the map is read-only. This
* method conforms to the {@link Map#entrySet()} interface.
*
* Note that the return value is a StoredCollection and must be treated
* as such; for example, its iterators must be explicitly closed.
*
* @return a {@link StoredEntrySet} or a {@link StoredSortedEntrySet} for
* this map.
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*
* @see #areKeyRangesAllowed
* @see #isWriteAllowed
*/
public Set> entrySet() {
return entrySet;
}
/**
* Returns a collection view of the values contained in this map. A {@link
* java.util.SortedSet} is returned if the map supports key ranges and the
* value/entity binding can be used to derive the map's key from its
* value/entity object. The returned collection will be read-only if the
* map is read-only. This method conforms to the {@link Map#values()}
* interface.
*
* Note that the return value is a StoredCollection and must be treated
* as such; for example, its iterators must be explicitly closed.
*
* @return a {@link StoredValueSet} or a {@link StoredSortedValueSet} for
* this map.
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*
* @see #areKeyRangesAllowed
* @see #isWriteAllowed
*/
public Collection values() {
return valueSet;
}
/**
* Returns a new collection containing the values mapped to the given key
* in this map. This collection's iterator() method is particularly useful
* for iterating over the duplicates for a given key, since this is not
* supported by the standard Map interface. This method does not exist in
* the standard {@link Map} interface.
*
* If no mapping for the given key is present, an empty collection is
* returned. If duplicates are not allowed, at most a single value will be
* in the collection returned. If duplicates are allowed, the returned
* collection's add() method may be used to add values for the given
* key.
*
* @param key is the key for which values are to be returned.
*
* @return the new collection.
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public Collection duplicates(K key) {
try {
DataView newView = view.valueSetView(key);
return new StoredValueSet(newView);
} catch (KeyRangeException e) {
return Collections.EMPTY_SET;
} catch (Exception e) {
throw StoredContainer.convertException(e);
}
}
/**
* Returns a new map from primary key to value for the subset of records
* having a given secondary key (duplicates). This method does not exist
* in the standard {@link Map} interface.
*
* If no mapping for the given key is present, an empty collection is
* returned. If duplicates are not allowed, at most a single value will be
* in the collection returned. If duplicates are allowed, the returned
* collection's add() method may be used to add values for the given
* key.
*
* @param secondaryKey is the secondary key for which duplicates values
* will be represented by the returned map.
*
* @param primaryKeyBinding is the binding used for keys in the returned
* map.
*
* @param the primary key class.
*
* @return the new map.
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public Map duplicatesMap(K secondaryKey,
EntryBinding primaryKeyBinding) {
try {
DataView newView =
view.duplicatesView(secondaryKey, primaryKeyBinding);
if (isOrdered()) {
return new StoredSortedMap(newView);
} else {
return new StoredMap(newView);
}
} catch (Exception e) {
throw StoredContainer.convertException(e);
}
}
/**
* Compares the specified object with this map for equality. A value
* comparison is performed by this method and the stored values are
* compared rather than calling the equals() method of each element. This
* method conforms to the {@link Map#equals} interface.
*
*
* @throws OperationFailureException if one of the Read Operation
* Failures occurs.
*
* @throws EnvironmentFailureException if an unexpected, internal or
* environment-wide failure occurs.
*
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public boolean equals(Object other) {
if (other instanceof Map) {
return entrySet().equals(((Map) other).entrySet());
} else {
return false;
}
}
/*
* Add this in to keep FindBugs from whining at us about implementing
* equals(), but not hashCode().
*/
public int hashCode() {
return super.hashCode();
}
// Inherit javadoc
public int size() {
return values().size();
}
/**
* Converts the map to a string representation for debugging. WARNING: All
* mappings will be converted to strings and returned and therefore the
* returned string may be very large.
*
* @return the string representation.
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public String toString() {
return entrySet().toString();
}
}