blob: ff536de271cc0aeed44aef22ff4dc5c55f21951f [file] [log] [blame]
/*-
* 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.Iterator;
import com.sleepycat.compat.DbCompat;
import com.sleepycat.je.CursorConfig;
import com.sleepycat.je.DatabaseException;
/* <!-- begin JE only --> */
import com.sleepycat.je.EnvironmentFailureException; // for javadoc
import com.sleepycat.je.OperationFailureException; // for javadoc
/* <!-- end JE only --> */
import com.sleepycat.je.OperationStatus;
import com.sleepycat.util.RuntimeExceptionWrapper;
/**
* A abstract base class for all stored collections and maps. This class
* provides implementations of methods that are common to the {@link
* java.util.Collection} and the {@link java.util.Map} interfaces, namely
* {@link #clear}, {@link #isEmpty} and {@link #size}.
*
* <p>In addition, this class provides the following methods for stored
* collections only. Note that the use of these methods is not compatible with
* the standard Java collections interface.</p>
* <ul>
* <li>{@link #isWriteAllowed()}</li>
* <li>{@link #isSecondary()}</li>
* <li>{@link #isOrdered()}</li>
* <li>{@link #areKeyRangesAllowed()}</li>
* <li>{@link #areDuplicatesAllowed()}</li>
* <li>{@link #areDuplicatesOrdered()}</li>
* <li>{@link #areKeysRenumbered()}</li>
* <li>{@link #getCursorConfig()}</li>
* <li>{@link #isTransactional()}</li>
* </ul>
*
* @author Mark Hayes
*/
public abstract class StoredContainer implements Cloneable {
DataView view;
StoredContainer(DataView view) {
this.view = view;
}
/**
* Returns true if this is a read-write container or false if this is a
* read-only container.
* This method does not exist in the standard {@link java.util.Map} or
* {@link java.util.Collection} interfaces.
*
* @return whether write is allowed.
*/
public final boolean isWriteAllowed() {
return view.writeAllowed;
}
/**
* Returns the cursor configuration that is used for all operations
* performed via this container.
* For example, if <code>CursorConfig.getReadUncommitted</code> returns
* true, data will be read that is modified but not committed.
* This method does not exist in the standard {@link java.util.Map} or
* {@link java.util.Collection} interfaces.
*
* @return the cursor configuration, or null if no configuration has been
* specified.
*/
public final CursorConfig getCursorConfig() {
return DbCompat.cloneCursorConfig(view.cursorConfig);
}
/**
* Returns whether the databases underlying this container are
* transactional.
* Even in a transactional environment, a database will be transactional
* only if it was opened within a transaction or if the auto-commit option
* was specified when it was opened.
* This method does not exist in the standard {@link java.util.Map} or
* {@link java.util.Collection} interfaces.
*
* @return whether the database is transactional.
*/
public final boolean isTransactional() {
return view.transactional;
}
/**
* Clones a container with a specified cursor configuration.
*/
final StoredContainer configuredClone(CursorConfig config) {
try {
StoredContainer cont = (StoredContainer) clone();
cont.view = cont.view.configuredView(config);
cont.initAfterClone();
return cont;
} catch (CloneNotSupportedException willNeverOccur) { return null; }
}
/**
* Override this method to initialize view-dependent fields.
*/
void initAfterClone() {
}
/**
* Returns whether duplicate keys are allowed in this container.
* Duplicates are optionally allowed for HASH and BTREE databases.
* This method does not exist in the standard {@link java.util.Map} or
* {@link java.util.Collection} interfaces.
*
* <p>Note that the JE product only supports BTREE databases.</p>
*
* @return whether duplicates are allowed.
*/
public final boolean areDuplicatesAllowed() {
return view.dupsAllowed;
}
/**
* Returns whether duplicate keys are allowed and sorted by element value.
* Duplicates are optionally sorted for HASH and BTREE databases.
* This method does not exist in the standard {@link java.util.Map} or
* {@link java.util.Collection} interfaces.
*
* <p>Note that the JE product only supports BTREE databases, and
* duplicates are always sorted.</p>
*
* @return whether duplicates are ordered.
*/
public final boolean areDuplicatesOrdered() {
return view.dupsOrdered;
}
/**
* Returns whether keys are renumbered when insertions and deletions occur.
* Keys are optionally renumbered for RECNO databases.
* This method does not exist in the standard {@link java.util.Map} or
* {@link java.util.Collection} interfaces.
*
* <p>Note that the JE product does not support RECNO databases, and
* therefore keys are never renumbered.</p>
*
* @return whether keys are renumbered.
*/
public final boolean areKeysRenumbered() {
return view.keysRenumbered;
}
/**
* Returns whether keys are ordered in this container.
* Keys are ordered for BTREE, RECNO and QUEUE databases.
* This method does not exist in the standard {@link java.util.Map} or
* {@link java.util.Collection} interfaces.
*
* <p>Note that the JE product only support BTREE databases, and
* therefore keys are always ordered.</p>
*
* @return whether keys are ordered.
*/
public final boolean isOrdered() {
return view.ordered;
}
/**
* Returns whether key ranges are allowed in this container.
* Key ranges are allowed only for BTREE databases.
* This method does not exist in the standard {@link java.util.Map} or
* {@link java.util.Collection} interfaces.
*
* <p>Note that the JE product only supports BTREE databases, and
* therefore key ranges are always allowed.</p>
*
* @return whether keys are ordered.
*/
public final boolean areKeyRangesAllowed() {
return view.keyRangesAllowed;
}
/**
* Returns whether this container is a view on a secondary database rather
* than directly on a primary database.
* This method does not exist in the standard {@link java.util.Map} or
* {@link java.util.Collection} interfaces.
*
* @return whether the view is for a secondary database.
*/
public final boolean isSecondary() {
return view.isSecondary();
}
/**
* Returns a non-transactional count of the records in the collection or
* map. This method conforms to the {@link java.util.Collection#size} and
* {@link java.util.Map#size} interfaces.
*
* <!-- begin JE only -->
* <p>This operation is faster than obtaining a count by scanning the
* collection manually, and will not perturb the current contents of the
* cache. However, the count is not guaranteed to be accurate if there are
* concurrent updates.</p>
* <!-- end JE only -->
*
* @return the number of records.
*
* <!-- begin JE only -->
* @throws OperationFailureException if one of the <a
* href="../je/OperationFailureException.html#readFailures">Read Operation
* Failures</a> occurs.
*
* @throws EnvironmentFailureException if an unexpected, internal or
* environment-wide failure occurs.
* <!-- end JE only -->
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public abstract int size();
/**
* Returns true if this map or collection contains no mappings or elements.
* This method conforms to the {@link java.util.Collection#isEmpty} and
* {@link java.util.Map#isEmpty} interfaces.
*
* @return whether the container is empty.
*
* <!-- begin JE only -->
* @throws OperationFailureException if one of the <a
* href="../je/OperationFailureException.html#readFailures">Read Operation
* Failures</a> occurs.
*
* @throws EnvironmentFailureException if an unexpected, internal or
* environment-wide failure occurs.
* <!-- end JE only -->
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public boolean isEmpty() {
try {
return view.isEmpty();
} catch (Exception e) {
throw StoredContainer.convertException(e);
}
}
/**
* Removes all mappings or elements from this map or collection (optional
* operation).
* This method conforms to the {@link java.util.Collection#clear} and
* {@link java.util.Map#clear} interfaces.
*
* <!-- begin JE only -->
* @throws OperationFailureException if one of the <a
* href="../je/OperationFailureException.html#writeFailures">Write
* Operation Failures</a> occurs.
*
* @throws EnvironmentFailureException if an unexpected, internal or
* environment-wide failure occurs.
* <!-- end JE only -->
*
* @throws UnsupportedOperationException if the container is read-only.
*
* @throws RuntimeExceptionWrapper if a checked exception is thrown,
* including a {@code DatabaseException} on BDB (C edition).
*/
public void clear() {
boolean doAutoCommit = beginAutoCommit();
try {
view.clear();
commitAutoCommit(doAutoCommit);
} catch (Exception e) {
throw handleException(e, doAutoCommit);
}
}
Object getValue(Object key) {
DataCursor cursor = null;
try {
cursor = new DataCursor(view, false);
if (OperationStatus.SUCCESS ==
cursor.getSearchKey(key, null, false)) {
return cursor.getCurrentValue();
} else {
return null;
}
} catch (Exception e) {
throw StoredContainer.convertException(e);
} finally {
closeCursor(cursor);
}
}
Object putKeyValue(final Object key, final Object value) {
DataCursor cursor = null;
boolean doAutoCommit = beginAutoCommit();
try {
cursor = new DataCursor(view, true);
Object[] oldValue = new Object[1];
cursor.put(key, value, oldValue, false);
closeCursor(cursor);
commitAutoCommit(doAutoCommit);
return oldValue[0];
} catch (Exception e) {
closeCursor(cursor);
throw handleException(e, doAutoCommit);
}
}
final boolean removeKey(final Object key, final Object[] oldVal) {
DataCursor cursor = null;
boolean doAutoCommit = beginAutoCommit();
try {
cursor = new DataCursor(view, true);
boolean found = false;
OperationStatus status = cursor.getSearchKey(key, null, true);
while (status == OperationStatus.SUCCESS) {
cursor.delete();
found = true;
if (oldVal != null && oldVal[0] == null) {
oldVal[0] = cursor.getCurrentValue();
}
status = areDuplicatesAllowed() ?
cursor.getNextDup(true): OperationStatus.NOTFOUND;
}
closeCursor(cursor);
commitAutoCommit(doAutoCommit);
return found;
} catch (Exception e) {
closeCursor(cursor);
throw handleException(e, doAutoCommit);
}
}
boolean containsKey(Object key) {
DataCursor cursor = null;
try {
cursor = new DataCursor(view, false);
return (OperationStatus.SUCCESS ==
cursor.getSearchKey(key, null, false));
} catch (Exception e) {
throw StoredContainer.convertException(e);
} finally {
closeCursor(cursor);
}
}
final boolean removeValue(Object value) {
DataCursor cursor = null;
boolean doAutoCommit = beginAutoCommit();
try {
cursor = new DataCursor(view, true);
OperationStatus status = cursor.findValue(value, true);
if (status == OperationStatus.SUCCESS) {
cursor.delete();
}
closeCursor(cursor);
commitAutoCommit(doAutoCommit);
return (status == OperationStatus.SUCCESS);
} catch (Exception e) {
closeCursor(cursor);
throw handleException(e, doAutoCommit);
}
}
boolean containsValue(Object value) {
DataCursor cursor = null;
try {
cursor = new DataCursor(view, false);
OperationStatus status = cursor.findValue(value, true);
return (status == OperationStatus.SUCCESS);
} catch (Exception e) {
throw StoredContainer.convertException(e);
} finally {
closeCursor(cursor);
}
}
/**
* Returns a StoredIterator if the given collection is a StoredCollection,
* else returns a regular/external Iterator. The iterator returned should
* be closed with the static method StoredIterator.close(Iterator).
*/
final Iterator storedOrExternalIterator(Collection coll) {
if (coll instanceof StoredCollection) {
return ((StoredCollection) coll).storedIterator();
} else {
return coll.iterator();
}
}
final void closeCursor(DataCursor cursor) {
if (cursor != null) {
try {
cursor.close();
} catch (Exception e) {
throw StoredContainer.convertException(e);
}
}
}
final boolean beginAutoCommit() {
if (view.transactional) {
final CurrentTransaction currentTxn = view.getCurrentTxn();
try {
if (currentTxn.isAutoCommitAllowed()) {
currentTxn.beginTransaction(null);
return true;
}
} catch (DatabaseException e) {
throw RuntimeExceptionWrapper.wrapIfNeeded(e);
}
}
return false;
}
final void commitAutoCommit(boolean doAutoCommit)
throws DatabaseException {
if (doAutoCommit) {
view.getCurrentTxn().commitTransaction();
}
}
final RuntimeException handleException(Exception e, boolean doAutoCommit) {
if (doAutoCommit) {
try {
view.getCurrentTxn().abortTransaction();
} catch (DatabaseException ignored) {
/* Klockwork - ok */
}
}
return StoredContainer.convertException(e);
}
static RuntimeException convertException(Exception e) {
return RuntimeExceptionWrapper.wrapIfNeeded(e);
}
}