001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2000,2008 Oracle. All rights reserved.
005: *
006: * $Id: StoredContainer.java,v 1.51.2.3 2008/01/07 15:14:06 cwl Exp $
007: */
008:
009: package com.sleepycat.collections;
010:
011: import java.util.Collection;
012: import java.util.Iterator;
013:
014: import com.sleepycat.compat.DbCompat;
015: import com.sleepycat.je.CursorConfig;
016: import com.sleepycat.je.DatabaseException;
017: import com.sleepycat.je.OperationStatus;
018: import com.sleepycat.util.RuntimeExceptionWrapper;
019:
020: /**
021: * A abstract base class for all stored collections and maps. This class
022: * provides implementations of methods that are common to the {@link
023: * java.util.Collection} and the {@link java.util.Map} interfaces, namely
024: * {@link #clear}, {@link #isEmpty} and {@link #size}.
025: *
026: * <p>In addition, this class provides the following methods for stored
027: * collections only. Note that the use of these methods is not compatible with
028: * the standard Java collections interface.</p>
029: * <ul>
030: * <li>{@link #isWriteAllowed()}</li>
031: * <li>{@link #isSecondary()}</li>
032: * <li>{@link #isOrdered()}</li>
033: * <li>{@link #areDuplicatesAllowed()}</li>
034: * <li>{@link #areDuplicatesOrdered()}</li>
035: * <li>{@link #areKeysRenumbered()}</li>
036: * <li>{@link #getCursorConfig()}</li>
037: * <li>{@link #isTransactional()}</li>
038: * </ul>
039: *
040: * @author Mark Hayes
041: */
042: public abstract class StoredContainer implements Cloneable {
043:
044: DataView view;
045:
046: StoredContainer(DataView view) {
047:
048: this .view = view;
049: }
050:
051: /**
052: * Returns true if this is a read-write container or false if this is a
053: * read-only container.
054: * This method does not exist in the standard {@link java.util.Map} or
055: * {@link java.util.Collection} interfaces.
056: *
057: * @return whether write is allowed.
058: */
059: public final boolean isWriteAllowed() {
060:
061: return view.writeAllowed;
062: }
063:
064: /**
065: * Returns the cursor configuration that is used for all operations
066: * performed via this container.
067: * For example, if <code>CursorConfig.getReadUncommitted</code> returns
068: * true, data will be read that is modified but not committed.
069: * This method does not exist in the standard {@link java.util.Map} or
070: * {@link java.util.Collection} interfaces.
071: *
072: * @return the cursor configuration, or null if no configuration has been
073: * specified.
074: */
075: public final CursorConfig getCursorConfig() {
076:
077: return DbCompat.cloneCursorConfig(view.cursorConfig);
078: }
079:
080: /**
081: * Returns whether read-uncommitted is allowed for this container.
082: * For the JE product, read-uncommitted is always allowed; for the DB
083: * product, read-uncommitted is allowed if it was configured for the
084: * underlying database for this container.
085: * Even when read-uncommitted is allowed it must specifically be enabled by
086: * calling one of the {@link StoredCollections} methods.
087: * This method does not exist in the standard {@link java.util.Map} or
088: * {@link java.util.Collection} interfaces.
089: *
090: * @return whether read-uncommitted is allowed.
091: *
092: * @deprecated This method is deprecated with no replacement in this class.
093: * In the DB product, <code>DatabaseConfig.getReadUncommitted</code> may be
094: * called.
095: */
096: public final boolean isDirtyReadAllowed() {
097:
098: return view.readUncommittedAllowed;
099: }
100:
101: /**
102: * @deprecated This method has been replaced by {@link #getCursorConfig}.
103: * <code>CursorConfig.isReadUncommitted</code> may be called to determine
104: * whether dirty-read is enabled.
105: */
106: public final boolean isDirtyRead() {
107:
108: return view.cursorConfig.getReadUncommitted();
109: }
110:
111: /**
112: * Returns whether the databases underlying this container are
113: * transactional.
114: * Even in a transactional environment, a database will be transactional
115: * only if it was opened within a transaction or if the auto-commit option
116: * was specified when it was opened.
117: * This method does not exist in the standard {@link java.util.Map} or
118: * {@link java.util.Collection} interfaces.
119: *
120: * @return whether the database is transactional.
121: */
122: public final boolean isTransactional() {
123:
124: return view.transactional;
125: }
126:
127: /**
128: * Clones a container with a specified cursor configuration.
129: */
130: final StoredContainer configuredClone(CursorConfig config) {
131:
132: try {
133: StoredContainer cont = (StoredContainer) clone();
134: cont.view = cont.view.configuredView(config);
135: cont.initAfterClone();
136: return cont;
137: } catch (CloneNotSupportedException willNeverOccur) {
138: return null;
139: }
140: }
141:
142: /**
143: * Override this method to initialize view-dependent fields.
144: */
145: void initAfterClone() {
146: }
147:
148: /**
149: * Returns whether duplicate keys are allowed in this container.
150: * Duplicates are optionally allowed for HASH and BTREE databases.
151: * This method does not exist in the standard {@link java.util.Map} or
152: * {@link java.util.Collection} interfaces.
153: *
154: * <p>Note that the JE product only supports BTREE databases.</p>
155: *
156: * @return whether duplicates are allowed.
157: */
158: public final boolean areDuplicatesAllowed() {
159:
160: return view.dupsAllowed;
161: }
162:
163: /**
164: * Returns whether duplicate keys are allowed and sorted by element value.
165: * Duplicates are optionally sorted for HASH and BTREE databases.
166: * This method does not exist in the standard {@link java.util.Map} or
167: * {@link java.util.Collection} interfaces.
168: *
169: * <p>Note that the JE product only supports BTREE databases, and
170: * duplicates are always sorted.</p>
171: *
172: * @return whether duplicates are ordered.
173: */
174: public final boolean areDuplicatesOrdered() {
175:
176: return view.dupsOrdered;
177: }
178:
179: /**
180: * Returns whether keys are renumbered when insertions and deletions occur.
181: * Keys are optionally renumbered for RECNO databases.
182: * This method does not exist in the standard {@link java.util.Map} or
183: * {@link java.util.Collection} interfaces.
184: *
185: * <p>Note that the JE product does not support RECNO databases, and
186: * therefore keys are never renumbered.</p>
187: *
188: * @return whether keys are renumbered.
189: */
190: public final boolean areKeysRenumbered() {
191:
192: return view.keysRenumbered;
193: }
194:
195: /**
196: * Returns whether keys are ordered in this container.
197: * Keys are ordered for BTREE, RECNO and QUEUE database.
198: * This method does not exist in the standard {@link java.util.Map} or
199: * {@link java.util.Collection} interfaces.
200: *
201: * <p>Note that the JE product only support BTREE databases, and
202: * therefore keys are always ordered.</p>
203: *
204: * @return whether keys are ordered.
205: */
206: public final boolean isOrdered() {
207:
208: return view.ordered;
209: }
210:
211: /**
212: * Returns whether this container is a view on a secondary database rather
213: * than directly on a primary database.
214: * This method does not exist in the standard {@link java.util.Map} or
215: * {@link java.util.Collection} interfaces.
216: *
217: * @return whether the view is for a secondary database.
218: */
219: public final boolean isSecondary() {
220:
221: return view.isSecondary();
222: }
223:
224: /**
225: * Returns a non-transactional count of the records in the collection or
226: * map. This method conforms to the {@link java.util.Collection#size} and
227: * {@link java.util.Map#size} interfaces.
228: *
229: * <!-- begin JE only -->
230: * <p>This operation is faster than obtaining a count by scanning the
231: * collection manually, and will not perturb the current contents of the
232: * cache. However, the count is not guaranteed to be accurate if there are
233: * concurrent updates.</p>
234: * <!-- end JE only -->
235: *
236: * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown.
237: */
238: public abstract int size();
239:
240: /**
241: * Returns true if this map or collection contains no mappings or elements.
242: * This method conforms to the {@link java.util.Collection#isEmpty} and
243: * {@link java.util.Map#isEmpty} interfaces.
244: *
245: * @return whether the container is empty.
246: *
247: * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown.
248: */
249: public boolean isEmpty() {
250:
251: try {
252: return view.isEmpty();
253: } catch (Exception e) {
254: throw convertException(e);
255: }
256: }
257:
258: /**
259: * Removes all mappings or elements from this map or collection (optional
260: * operation).
261: * This method conforms to the {@link java.util.Collection#clear} and
262: * {@link java.util.Map#clear} interfaces.
263: *
264: * @throws UnsupportedOperationException if the container is read-only.
265: *
266: * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown.
267: */
268: public void clear() {
269:
270: boolean doAutoCommit = beginAutoCommit();
271: try {
272: view.clear();
273: commitAutoCommit(doAutoCommit);
274: } catch (Exception e) {
275: throw handleException(e, doAutoCommit);
276: }
277: }
278:
279: Object get(Object key) {
280:
281: DataCursor cursor = null;
282: try {
283: cursor = new DataCursor(view, false);
284: if (OperationStatus.SUCCESS == cursor.getSearchKey(key,
285: null, false)) {
286: return cursor.getCurrentValue();
287: } else {
288: return null;
289: }
290: } catch (Exception e) {
291: throw StoredContainer.convertException(e);
292: } finally {
293: closeCursor(cursor);
294: }
295: }
296:
297: Object put(final Object key, final Object value) {
298:
299: DataCursor cursor = null;
300: boolean doAutoCommit = beginAutoCommit();
301: try {
302: cursor = new DataCursor(view, true);
303: Object[] oldValue = new Object[1];
304: cursor.put(key, value, oldValue, false);
305: closeCursor(cursor);
306: commitAutoCommit(doAutoCommit);
307: return oldValue[0];
308: } catch (Exception e) {
309: closeCursor(cursor);
310: throw handleException(e, doAutoCommit);
311: }
312: }
313:
314: final boolean removeKey(final Object key, final Object[] oldVal) {
315:
316: DataCursor cursor = null;
317: boolean doAutoCommit = beginAutoCommit();
318: try {
319: cursor = new DataCursor(view, true);
320: boolean found = false;
321: OperationStatus status = cursor.getSearchKey(key, null,
322: true);
323: while (status == OperationStatus.SUCCESS) {
324: cursor.delete();
325: found = true;
326: if (oldVal != null && oldVal[0] == null) {
327: oldVal[0] = cursor.getCurrentValue();
328: }
329: status = areDuplicatesAllowed() ? cursor
330: .getNextDup(true) : OperationStatus.NOTFOUND;
331: }
332: closeCursor(cursor);
333: commitAutoCommit(doAutoCommit);
334: return found;
335: } catch (Exception e) {
336: closeCursor(cursor);
337: throw handleException(e, doAutoCommit);
338: }
339: }
340:
341: boolean containsKey(Object key) {
342:
343: DataCursor cursor = null;
344: try {
345: cursor = new DataCursor(view, false);
346: return OperationStatus.SUCCESS == cursor.getSearchKey(key,
347: null, false);
348: } catch (Exception e) {
349: throw StoredContainer.convertException(e);
350: } finally {
351: closeCursor(cursor);
352: }
353: }
354:
355: final boolean removeValue(Object value) {
356:
357: DataCursor cursor = null;
358: boolean doAutoCommit = beginAutoCommit();
359: try {
360: cursor = new DataCursor(view, true);
361: OperationStatus status = cursor.findValue(value, true);
362: if (status == OperationStatus.SUCCESS) {
363: cursor.delete();
364: }
365: closeCursor(cursor);
366: commitAutoCommit(doAutoCommit);
367: return (status == OperationStatus.SUCCESS);
368: } catch (Exception e) {
369: closeCursor(cursor);
370: throw handleException(e, doAutoCommit);
371: }
372: }
373:
374: boolean containsValue(Object value) {
375:
376: DataCursor cursor = null;
377: try {
378: cursor = new DataCursor(view, false);
379: OperationStatus status = cursor.findValue(value, true);
380: return (status == OperationStatus.SUCCESS);
381: } catch (Exception e) {
382: throw StoredContainer.convertException(e);
383: } finally {
384: closeCursor(cursor);
385: }
386: }
387:
388: /**
389: * Returns a StoredIterator if the given collection is a StoredCollection,
390: * else returns a regular/external Iterator. The iterator returned should
391: * be closed with the static method StoredIterator.close(Iterator).
392: */
393: final Iterator storedOrExternalIterator(Collection coll) {
394:
395: if (coll instanceof StoredCollection) {
396: return ((StoredCollection) coll).storedIterator();
397: } else {
398: return coll.iterator();
399: }
400: }
401:
402: final void closeCursor(DataCursor cursor) {
403:
404: if (cursor != null) {
405: try {
406: cursor.close();
407: } catch (Exception e) {
408: throw StoredContainer.convertException(e);
409: }
410: }
411: }
412:
413: final boolean beginAutoCommit() {
414:
415: if (view.transactional) {
416: CurrentTransaction currentTxn = view.getCurrentTxn();
417: try {
418: if (currentTxn.isAutoCommitAllowed()) {
419: currentTxn.beginTransaction(null);
420: return true;
421: }
422: } catch (DatabaseException e) {
423: throw new RuntimeExceptionWrapper(e);
424: }
425: }
426: return false;
427: }
428:
429: final void commitAutoCommit(boolean doAutoCommit)
430: throws DatabaseException {
431:
432: if (doAutoCommit)
433: view.getCurrentTxn().commitTransaction();
434: }
435:
436: final RuntimeException handleException(Exception e,
437: boolean doAutoCommit) {
438:
439: if (doAutoCommit) {
440: try {
441: view.getCurrentTxn().abortTransaction();
442: } catch (DatabaseException ignored) {
443: /* Klockwork - ok */
444: }
445: }
446: return StoredContainer.convertException(e);
447: }
448:
449: static RuntimeException convertException(Exception e) {
450:
451: if (e instanceof RuntimeException) {
452: return (RuntimeException) e;
453: } else {
454: return new RuntimeExceptionWrapper(e);
455: }
456: }
457: }
|