1 28 29 package org.apache.commons.transaction.memory; 30 31 import java.util.ArrayList ; 32 import java.util.Collection ; 33 import java.util.Collections ; 34 import java.util.HashSet ; 35 import java.util.Iterator ; 36 import java.util.Map ; 37 import java.util.Set ; 38 39 import javax.transaction.Status ; 40 41 60 public class TransactionalMapWrapper implements Map , Status { 61 62 63 protected Map wrapped; 64 65 66 protected MapFactory mapFactory; 67 68 protected SetFactory setFactory; 69 70 private ThreadLocal activeTx = new ThreadLocal (); 71 72 78 public TransactionalMapWrapper(Map wrapped) { 79 this(wrapped, new HashMapFactory(), new HashSetFactory()); 80 } 81 82 90 public TransactionalMapWrapper(Map wrapped, MapFactory mapFactory, SetFactory setFactory) { 91 this.wrapped = Collections.synchronizedMap(wrapped); 92 this.mapFactory = mapFactory; 93 this.setFactory = setFactory; 94 } 95 96 102 public boolean isReadOnly() { 103 TxContext txContext = getActiveTx(); 104 105 if (txContext == null) { 106 throw new IllegalStateException ( 107 "Active thread " + Thread.currentThread() + " not associated with a transaction!"); 108 } 109 110 return txContext.readOnly; 111 } 112 113 123 public boolean isTransactionMarkedForRollback() { 124 TxContext txContext = getActiveTx(); 125 126 if (txContext == null) { 127 throw new IllegalStateException ( 128 "Active thread " + Thread.currentThread() + " not associated with a transaction!"); 129 } 130 131 return (txContext.status == Status.STATUS_MARKED_ROLLBACK); 132 } 133 134 139 public void markTransactionForRollback() { 140 TxContext txContext = getActiveTx(); 141 142 if (txContext == null) { 143 throw new IllegalStateException ( 144 "Active thread " + Thread.currentThread() + " not associated with a transaction!"); 145 } 146 147 txContext.status = Status.STATUS_MARKED_ROLLBACK; 148 } 149 150 165 public TxContext suspendTransaction() { 166 TxContext txContext = getActiveTx(); 167 168 if (txContext == null) { 169 throw new IllegalStateException ( 170 "Active thread " + Thread.currentThread() + " not associated with a transaction!"); 171 } 172 173 txContext.suspended = true; 174 setActiveTx(null); 175 return txContext; 176 } 177 178 185 public void resumeTransaction(TxContext suspendedTx) { 186 if (getActiveTx() != null) { 187 throw new IllegalStateException ( 188 "Active thread " + Thread.currentThread() + " already associated with a transaction!"); 189 } 190 191 if (suspendedTx == null) { 192 throw new IllegalStateException ("No transaction to resume!"); 193 } 194 195 if (!suspendedTx.suspended) { 196 throw new IllegalStateException ("Transaction to resume needs to be suspended!"); 197 } 198 199 suspendedTx.suspended = false; 200 setActiveTx(suspendedTx); 201 } 202 203 208 public int getTransactionState() { 209 TxContext txContext = getActiveTx(); 210 211 if (txContext == null) { 212 return STATUS_NO_TRANSACTION; 213 } 214 return txContext.status; 215 } 216 217 229 public void startTransaction() { 230 if (getActiveTx() != null) { 231 throw new IllegalStateException ( 232 "Active thread " + Thread.currentThread() + " already associated with a transaction!"); 233 } 234 setActiveTx(new TxContext()); 235 } 236 237 244 public void rollbackTransaction() { 245 TxContext txContext = getActiveTx(); 246 247 if (txContext == null) { 248 throw new IllegalStateException ( 249 "Active thread " + Thread.currentThread() + " not associated with a transaction!"); 250 } 251 252 txContext.dispose(); 254 setActiveTx(null); 255 } 256 257 264 public void commitTransaction() { 265 TxContext txContext = getActiveTx(); 266 267 if (txContext == null) { 268 throw new IllegalStateException ( 269 "Active thread " + Thread.currentThread() + " not associated with a transaction!"); 270 } 271 272 if (txContext.status == Status.STATUS_MARKED_ROLLBACK) { 273 throw new IllegalStateException ("Active thread " + Thread.currentThread() + " is marked for rollback!"); 274 } 275 276 txContext.merge(); 277 txContext.dispose(); 278 setActiveTx(null); 279 } 280 281 285 288 public void clear() { 289 TxContext txContext = getActiveTx(); 290 if (txContext != null) { 291 txContext.clear(); 292 } else { 293 wrapped.clear(); 294 } 295 } 296 297 300 public int size() { 301 TxContext txContext = getActiveTx(); 302 if (txContext != null) { 303 return txContext.size(); 304 } else { 305 return wrapped.size(); 306 } 307 } 308 309 312 public boolean isEmpty() { 313 TxContext txContext = getActiveTx(); 314 if (txContext == null) { 315 return wrapped.isEmpty(); 316 } else { 317 return txContext.isEmpty(); 318 } 319 } 320 321 324 public boolean containsKey(Object key) { 325 return (get(key) != null); 326 } 327 328 331 public boolean containsValue(Object value) { 332 TxContext txContext = getActiveTx(); 333 334 if (txContext == null) { 335 return wrapped.containsValue(value); 336 } else { 337 return values().contains(value); 338 } 339 } 340 341 344 public Collection values() { 345 346 TxContext txContext = getActiveTx(); 347 348 if (txContext == null) { 349 return wrapped.values(); 350 } else { 351 Collection values = new ArrayList (); 353 for (Iterator it = keySet().iterator(); it.hasNext();) { 354 Object key = it.next(); 355 Object value = get(key); 356 if (value != null) { 358 values.add(value); 359 } 360 } 361 return values; 362 } 363 } 364 365 368 public void putAll(Map map) { 369 TxContext txContext = getActiveTx(); 370 371 if (txContext == null) { 372 wrapped.putAll(map); 373 } else { 374 for (Iterator it = map.entrySet().iterator(); it.hasNext();) { 375 Map.Entry entry = (Map.Entry ) it.next(); 376 txContext.put(entry.getKey(), entry.getValue()); 377 } 378 } 379 } 380 381 384 public Set entrySet() { 385 TxContext txContext = getActiveTx(); 386 if (txContext == null) { 387 return wrapped.entrySet(); 388 } else { 389 Set entrySet = new HashSet (); 390 for (Iterator it = keySet().iterator(); it.hasNext();) { 392 Object key = it.next(); 393 Object value = get(key); 394 if (value != null) { 396 entrySet.add(new HashEntry(key, value)); 397 } 398 } 399 return entrySet; 400 } 401 } 402 403 406 public Set keySet() { 407 TxContext txContext = getActiveTx(); 408 409 if (txContext == null) { 410 return wrapped.keySet(); 411 } else { 412 return txContext.keys(); 413 } 414 } 415 416 419 public Object get(Object key) { 420 TxContext txContext = getActiveTx(); 421 422 if (txContext != null) { 423 return txContext.get(key); 424 } else { 425 return wrapped.get(key); 426 } 427 } 428 429 432 public Object remove(Object key) { 433 TxContext txContext = getActiveTx(); 434 435 if (txContext == null) { 436 return wrapped.remove(key); 437 } else { 438 Object oldValue = get(key); 439 txContext.remove(key); 440 return oldValue; 441 } 442 } 443 444 447 public Object put(Object key, Object value) { 448 TxContext txContext = getActiveTx(); 449 450 if (txContext == null) { 451 return wrapped.put(key, value); 452 } else { 453 Object oldValue = get(key); 454 txContext.put(key, value); 455 return oldValue; 456 } 457 458 } 459 460 protected TxContext getActiveTx() { 461 return (TxContext) activeTx.get(); 462 } 463 464 protected void setActiveTx(TxContext txContext) { 465 activeTx.set(txContext); 466 } 467 468 protected static class HashEntry implements Map.Entry { 470 471 protected Object key; 472 473 protected Object value; 474 475 protected HashEntry(Object key, Object value) { 476 this.key = key; 477 this.value = value; 478 } 479 480 public Object getKey() { 481 return key; 482 } 483 484 public Object getValue() { 485 return value; 486 } 487 488 public Object setValue(Object value) { 489 Object old = this.value; 490 this.value = value; 491 return old; 492 } 493 494 public boolean equals(Object obj) { 495 if (obj == this) { 496 return true; 497 } 498 if (!(obj instanceof Map.Entry )) { 499 return false; 500 } 501 Map.Entry other = (Map.Entry ) obj; 502 return (getKey() == null ? other.getKey() == null : getKey().equals(other.getKey())) 503 && (getValue() == null ? other.getValue() == null : getValue().equals(other.getValue())); 504 } 505 506 public int hashCode() { 507 return (getKey() == null ? 0 : getKey().hashCode()) ^ (getValue() == null ? 0 : getValue().hashCode()); 508 } 509 510 public String toString() { 511 return new StringBuffer ().append(getKey()).append('=').append(getValue()).toString(); 512 } 513 } 514 515 public class TxContext { 516 protected Set deletes; 517 protected Map changes; 518 protected Map adds; 519 protected int status; 520 protected boolean cleared; 521 protected boolean readOnly; 522 protected boolean suspended = false; 523 524 protected TxContext() { 525 deletes = setFactory.createSet(); 526 changes = mapFactory.createMap(); 527 adds = mapFactory.createMap(); 528 status = Status.STATUS_ACTIVE; 529 cleared = false; 530 readOnly = true; 531 } 532 533 protected Set keys() { 534 Set keySet = new HashSet (); 535 if (!cleared) { 536 keySet.addAll(wrapped.keySet()); 537 } 538 keySet.addAll(adds.keySet()); 539 return keySet; 540 } 541 542 protected Object get(Object key) { 543 544 if (deletes.contains(key)) { 545 return null; 547 } 548 549 Object changed = changes.get(key); 550 if (changed != null) { 551 return changed; 552 } 553 554 Object added = adds.get(key); 555 if (added != null) { 556 return added; 557 } 558 559 if (cleared) { 560 return null; 561 } else { 562 return wrapped.get(key); 564 } 565 } 566 567 protected void put(Object key, Object value) { 568 try { 569 readOnly = false; 570 deletes.remove(key); 571 if (wrapped.get(key) != null) { 572 changes.put(key, value); 573 } else { 574 adds.put(key, value); 575 } 576 } catch (RuntimeException e) { 577 status = Status.STATUS_MARKED_ROLLBACK; 578 throw e; 579 } catch (Error e) { 580 status = Status.STATUS_MARKED_ROLLBACK; 581 throw e; 582 } 583 } 584 585 protected void remove(Object key) { 586 587 try { 588 readOnly = false; 589 changes.remove(key); 590 adds.remove(key); 591 if (wrapped.containsKey(key) && !cleared) { 592 deletes.add(key); 593 } 594 } catch (RuntimeException e) { 595 status = Status.STATUS_MARKED_ROLLBACK; 596 throw e; 597 } catch (Error e) { 598 status = Status.STATUS_MARKED_ROLLBACK; 599 throw e; 600 } 601 } 602 603 protected int size() { 604 int size = (cleared ? 0 : wrapped.size()); 605 606 size -= deletes.size(); 607 size += adds.size(); 608 609 return size; 610 } 611 612 protected void clear() { 613 readOnly = false; 614 cleared = true; 615 deletes.clear(); 616 changes.clear(); 617 adds.clear(); 618 } 619 620 protected boolean isEmpty() { 621 return (size() == 0); 622 } 623 624 protected void merge() { 625 if (!readOnly) { 626 627 if (cleared) { 628 wrapped.clear(); 629 } 630 631 wrapped.putAll(changes); 632 wrapped.putAll(adds); 633 634 for (Iterator it = deletes.iterator(); it.hasNext();) { 635 Object key = it.next(); 636 wrapped.remove(key); 637 } 638 } 639 } 640 641 protected void dispose() { 642 setFactory.disposeSet(deletes); 643 deletes = null; 644 mapFactory.disposeMap(changes); 645 changes = null; 646 mapFactory.disposeMap(adds); 647 adds = null; 648 status = Status.STATUS_NO_TRANSACTION; 649 } 650 } 651 } 652 | Popular Tags |