1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2000,2008 Oracle. All rights reserved. 5 * 6 * $Id: StoredContainer.java,v 12.10 2008/02/07 17:12:26 mark Exp $ 7 */ 8 9package com.sleepycat.collections; 10 11import java.util.Collection; 12import java.util.Iterator; 13 14import com.sleepycat.compat.DbCompat; 15import com.sleepycat.db.CursorConfig; 16import com.sleepycat.db.DatabaseException; 17import com.sleepycat.db.OperationStatus; 18import com.sleepycat.util.RuntimeExceptionWrapper; 19 20/** 21 * A abstract base class for all stored collections and maps. This class 22 * provides implementations of methods that are common to the {@link 23 * java.util.Collection} and the {@link java.util.Map} interfaces, namely 24 * {@link #clear}, {@link #isEmpty} and {@link #size}. 25 * 26 * <p>In addition, this class provides the following methods for stored 27 * collections only. Note that the use of these methods is not compatible with 28 * the standard Java collections interface.</p> 29 * <ul> 30 * <li>{@link #isWriteAllowed()}</li> 31 * <li>{@link #isSecondary()}</li> 32 * <li>{@link #isOrdered()}</li> 33 * <li>{@link #areKeyRangesAllowed()}</li> 34 * <li>{@link #areDuplicatesAllowed()}</li> 35 * <li>{@link #areDuplicatesOrdered()}</li> 36 * <li>{@link #areKeysRenumbered()}</li> 37 * <li>{@link #getCursorConfig()}</li> 38 * <li>{@link #isTransactional()}</li> 39 * </ul> 40 * 41 * @author Mark Hayes 42 */ 43public abstract class StoredContainer implements Cloneable { 44 45 DataView view; 46 47 StoredContainer(DataView view) { 48 49 this.view = view; 50 } 51 52 /** 53 * Returns true if this is a read-write container or false if this is a 54 * read-only container. 55 * This method does not exist in the standard {@link java.util.Map} or 56 * {@link java.util.Collection} interfaces. 57 * 58 * @return whether write is allowed. 59 */ 60 public final boolean isWriteAllowed() { 61 62 return view.writeAllowed; 63 } 64 65 /** 66 * Returns the cursor configuration that is used for all operations 67 * performed via this container. 68 * For example, if <code>CursorConfig.getReadUncommitted</code> returns 69 * true, data will be read that is modified but not committed. 70 * This method does not exist in the standard {@link java.util.Map} or 71 * {@link java.util.Collection} interfaces. 72 * 73 * @return the cursor configuration, or null if no configuration has been 74 * specified. 75 */ 76 public final CursorConfig getCursorConfig() { 77 78 return DbCompat.cloneCursorConfig(view.cursorConfig); 79 } 80 81 /** 82 * Returns whether read-uncommitted is allowed for this container. 83 * For the JE product, read-uncommitted is always allowed; for the DB 84 * product, read-uncommitted is allowed if it was configured for the 85 * underlying database for this container. 86 * Even when read-uncommitted is allowed it must specifically be enabled by 87 * calling one of the {@link StoredCollections} methods. 88 * This method does not exist in the standard {@link java.util.Map} or 89 * {@link java.util.Collection} interfaces. 90 * 91 * @return whether read-uncommitted is allowed. 92 * 93 * @deprecated This method is deprecated with no replacement in this class. 94 * In the DB product, <code>DatabaseConfig.getReadUncommitted</code> may be 95 * called. 96 */ 97 public final boolean isDirtyReadAllowed() { 98 99 return view.readUncommittedAllowed; 100 } 101 102 /** 103 * @deprecated This method has been replaced by {@link #getCursorConfig}. 104 * <code>CursorConfig.isReadUncommitted</code> may be called to determine 105 * whether dirty-read is enabled. 106 */ 107 public final boolean isDirtyRead() { 108 109 return view.cursorConfig.getReadUncommitted(); 110 } 111 112 /** 113 * Returns whether the databases underlying this container are 114 * transactional. 115 * Even in a transactional environment, a database will be transactional 116 * only if it was opened within a transaction or if the auto-commit option 117 * was specified when it was opened. 118 * This method does not exist in the standard {@link java.util.Map} or 119 * {@link java.util.Collection} interfaces. 120 * 121 * @return whether the database is transactional. 122 */ 123 public final boolean isTransactional() { 124 125 return view.transactional; 126 } 127 128 /** 129 * Clones a container with a specified cursor configuration. 130 */ 131 final StoredContainer configuredClone(CursorConfig config) { 132 133 try { 134 StoredContainer cont = (StoredContainer) clone(); 135 cont.view = cont.view.configuredView(config); 136 cont.initAfterClone(); 137 return cont; 138 } catch (CloneNotSupportedException willNeverOccur) { return null; } 139 } 140 141 /** 142 * Override this method to initialize view-dependent fields. 143 */ 144 void initAfterClone() { 145 } 146 147 /** 148 * Returns whether duplicate keys are allowed in this container. 149 * Duplicates are optionally allowed for HASH and BTREE databases. 150 * This method does not exist in the standard {@link java.util.Map} or 151 * {@link java.util.Collection} interfaces. 152 * 153 * <p>Note that the JE product only supports BTREE databases.</p> 154 * 155 * @return whether duplicates are allowed. 156 */ 157 public final boolean areDuplicatesAllowed() { 158 159 return view.dupsAllowed; 160 } 161 162 /** 163 * Returns whether duplicate keys are allowed and sorted by element value. 164 * Duplicates are optionally sorted for HASH and BTREE databases. 165 * This method does not exist in the standard {@link java.util.Map} or 166 * {@link java.util.Collection} interfaces. 167 * 168 * <p>Note that the JE product only supports BTREE databases, and 169 * duplicates are always sorted.</p> 170 * 171 * @return whether duplicates are ordered. 172 */ 173 public final boolean areDuplicatesOrdered() { 174 175 return view.dupsOrdered; 176 } 177 178 /** 179 * Returns whether keys are renumbered when insertions and deletions occur. 180 * Keys are optionally renumbered for RECNO databases. 181 * This method does not exist in the standard {@link java.util.Map} or 182 * {@link java.util.Collection} interfaces. 183 * 184 * <p>Note that the JE product does not support RECNO databases, and 185 * therefore keys are never renumbered.</p> 186 * 187 * @return whether keys are renumbered. 188 */ 189 public final boolean areKeysRenumbered() { 190 191 return view.keysRenumbered; 192 } 193 194 /** 195 * Returns whether keys are ordered in this container. 196 * Keys are ordered for BTREE, RECNO and QUEUE databases. 197 * This method does not exist in the standard {@link java.util.Map} or 198 * {@link java.util.Collection} interfaces. 199 * 200 * <p>Note that the JE product only support BTREE databases, and 201 * therefore keys are always ordered.</p> 202 * 203 * @return whether keys are ordered. 204 */ 205 public final boolean isOrdered() { 206 207 return view.ordered; 208 } 209 210 /** 211 * Returns whether key ranges are allowed in this container. 212 * Key ranges are allowed only for BTREE databases. 213 * This method does not exist in the standard {@link java.util.Map} or 214 * {@link java.util.Collection} interfaces. 215 * 216 * <p>Note that the JE product only supports BTREE databases, and 217 * therefore key ranges are always allowed.</p> 218 * 219 * @return whether keys are ordered. 220 */ 221 public final boolean areKeyRangesAllowed() { 222 223 return view.keyRangesAllowed; 224 } 225 226 /** 227 * Returns whether this container is a view on a secondary database rather 228 * than directly on a primary database. 229 * This method does not exist in the standard {@link java.util.Map} or 230 * {@link java.util.Collection} interfaces. 231 * 232 * @return whether the view is for a secondary database. 233 */ 234 public final boolean isSecondary() { 235 236 return view.isSecondary(); 237 } 238 239 /** 240 * Returns a non-transactional count of the records in the collection or 241 * map. This method conforms to the {@link java.util.Collection#size} and 242 * {@link java.util.Map#size} interfaces. 243 * 244 * 245 * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown. 246 */ 247 public abstract int size(); 248 249 /** 250 * Returns true if this map or collection contains no mappings or elements. 251 * This method conforms to the {@link java.util.Collection#isEmpty} and 252 * {@link java.util.Map#isEmpty} interfaces. 253 * 254 * @return whether the container is empty. 255 * 256 * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown. 257 */ 258 public boolean isEmpty() { 259 260 try { 261 return view.isEmpty(); 262 } catch (Exception e) { 263 throw convertException(e); 264 } 265 } 266 267 /** 268 * Removes all mappings or elements from this map or collection (optional 269 * operation). 270 * This method conforms to the {@link java.util.Collection#clear} and 271 * {@link java.util.Map#clear} interfaces. 272 * 273 * @throws UnsupportedOperationException if the container is read-only. 274 * 275 * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown. 276 */ 277 public void clear() { 278 279 boolean doAutoCommit = beginAutoCommit(); 280 try { 281 view.clear(); 282 commitAutoCommit(doAutoCommit); 283 } catch (Exception e) { 284 throw handleException(e, doAutoCommit); 285 } 286 } 287 288 Object get(Object key) { 289 290 DataCursor cursor = null; 291 try { 292 cursor = new DataCursor(view, false); 293 if (OperationStatus.SUCCESS == 294 cursor.getSearchKey(key, null, false)) { 295 return cursor.getCurrentValue(); 296 } else { 297 return null; 298 } 299 } catch (Exception e) { 300 throw StoredContainer.convertException(e); 301 } finally { 302 closeCursor(cursor); 303 } 304 } 305 306 Object put(final Object key, final Object value) { 307 308 DataCursor cursor = null; 309 boolean doAutoCommit = beginAutoCommit(); 310 try { 311 cursor = new DataCursor(view, true); 312 Object[] oldValue = new Object[1]; 313 cursor.put(key, value, oldValue, false); 314 closeCursor(cursor); 315 commitAutoCommit(doAutoCommit); 316 return oldValue[0]; 317 } catch (Exception e) { 318 closeCursor(cursor); 319 throw handleException(e, doAutoCommit); 320 } 321 } 322 323 final boolean removeKey(final Object key, final Object[] oldVal) { 324 325 DataCursor cursor = null; 326 boolean doAutoCommit = beginAutoCommit(); 327 try { 328 cursor = new DataCursor(view, true); 329 boolean found = false; 330 OperationStatus status = cursor.getSearchKey(key, null, true); 331 while (status == OperationStatus.SUCCESS) { 332 cursor.delete(); 333 found = true; 334 if (oldVal != null && oldVal[0] == null) { 335 oldVal[0] = cursor.getCurrentValue(); 336 } 337 status = areDuplicatesAllowed() ? 338 cursor.getNextDup(true): OperationStatus.NOTFOUND; 339 } 340 closeCursor(cursor); 341 commitAutoCommit(doAutoCommit); 342 return found; 343 } catch (Exception e) { 344 closeCursor(cursor); 345 throw handleException(e, doAutoCommit); 346 } 347 } 348 349 boolean containsKey(Object key) { 350 351 DataCursor cursor = null; 352 try { 353 cursor = new DataCursor(view, false); 354 return OperationStatus.SUCCESS == 355 cursor.getSearchKey(key, null, false); 356 } catch (Exception e) { 357 throw StoredContainer.convertException(e); 358 } finally { 359 closeCursor(cursor); 360 } 361 } 362 363 final boolean removeValue(Object value) { 364 365 DataCursor cursor = null; 366 boolean doAutoCommit = beginAutoCommit(); 367 try { 368 cursor = new DataCursor(view, true); 369 OperationStatus status = cursor.findValue(value, true); 370 if (status == OperationStatus.SUCCESS) { 371 cursor.delete(); 372 } 373 closeCursor(cursor); 374 commitAutoCommit(doAutoCommit); 375 return (status == OperationStatus.SUCCESS); 376 } catch (Exception e) { 377 closeCursor(cursor); 378 throw handleException(e, doAutoCommit); 379 } 380 } 381 382 boolean containsValue(Object value) { 383 384 DataCursor cursor = null; 385 try { 386 cursor = new DataCursor(view, false); 387 OperationStatus status = cursor.findValue(value, true); 388 return (status == OperationStatus.SUCCESS); 389 } catch (Exception e) { 390 throw StoredContainer.convertException(e); 391 } finally { 392 closeCursor(cursor); 393 } 394 } 395 396 /** 397 * Returns a StoredIterator if the given collection is a StoredCollection, 398 * else returns a regular/external Iterator. The iterator returned should 399 * be closed with the static method StoredIterator.close(Iterator). 400 */ 401 final Iterator storedOrExternalIterator(Collection coll) { 402 403 if (coll instanceof StoredCollection) { 404 return ((StoredCollection) coll).storedIterator(); 405 } else { 406 return coll.iterator(); 407 } 408 } 409 410 final void closeCursor(DataCursor cursor) { 411 412 if (cursor != null) { 413 try { 414 cursor.close(); 415 } catch (Exception e) { 416 throw StoredContainer.convertException(e); 417 } 418 } 419 } 420 421 final boolean beginAutoCommit() { 422 423 if (view.transactional) { 424 CurrentTransaction currentTxn = view.getCurrentTxn(); 425 try { 426 if (currentTxn.isAutoCommitAllowed()) { 427 currentTxn.beginTransaction(null); 428 return true; 429 } 430 } catch (DatabaseException e) { 431 throw new RuntimeExceptionWrapper(e); 432 } 433 } 434 return false; 435 } 436 437 final void commitAutoCommit(boolean doAutoCommit) 438 throws DatabaseException { 439 440 if (doAutoCommit) view.getCurrentTxn().commitTransaction(); 441 } 442 443 final RuntimeException handleException(Exception e, boolean doAutoCommit) { 444 445 if (doAutoCommit) { 446 try { 447 view.getCurrentTxn().abortTransaction(); 448 } catch (DatabaseException ignored) { 449 /* Klockwork - ok */ 450 } 451 } 452 return StoredContainer.convertException(e); 453 } 454 455 static RuntimeException convertException(Exception e) { 456 457 if (e instanceof RuntimeException) { 458 return (RuntimeException) e; 459 } else { 460 return new RuntimeExceptionWrapper(e); 461 } 462 } 463} 464