001 package data.ooimpl; 002 003 import java.util.*; 004 005 import java.io.Serializable; 006 007 import data.events.*; 008 import data.*; 009 010 import log.*; 011 012 import util.*; 013 014 /** 015 * Pure Java implementation of the {@link DataBasket} interface. 016 * 017 * <p>This DataBasket implementation can be used together with the {@link CatalogImpl Catalog} and 018 * {@link StockImpl Stock implementations} that come with the framework as well as with any other data 019 * container that manages all its internal structure on its own, instead of delegating them, e.g. to a data 020 * base. For a data base backing you should use other implementations of {@link Stock}, {@link Catalog} and 021 * {@link DataBasket}, but these are not yet part of the framework.</p> 022 * 023 * @author Steffen Zschaler 024 * @version 2.0 14/06/1999 025 * @since v2.0 026 */ 027 public class DataBasketImpl extends Object implements ListenableDataBasket { 028 029 /** 030 * Internal helper class used by {@link DataBasketImpl}, representing a subbasket of a {@link DataBasket}. 031 * 032 * <p>This class has been made protected so that framework users be able to subclass it should the need 033 * arise.</p> 034 * 035 * @author Steffen Zschaler 036 * @version 2.0 14/06/1999 037 * @since v2.0 038 */ 039 protected static class SubDataBasket implements Serializable { 040 041 /** 042 * The entries contained in this subbasket. 043 * 044 * <p>This is a map of maps of lists of {@link DataBasketEntry DataBasketEntries}.</p> 045 * 046 * @serial 047 */ 048 private Map m_mpmpldbeCategories = new HashMap(); 049 050 /** 051 * The owner of this subbasket. 052 * 053 * @serial 054 */ 055 private DataBasketImpl m_dbiOwner; 056 057 /** 058 * Create a new subbasket. 059 * 060 * @param dbiOwner the DataBasketImpl instance owning this subbasket. 061 */ 062 public SubDataBasket(DataBasketImpl dbiOwner) { 063 super(); 064 065 m_dbiOwner = dbiOwner; 066 } 067 068 /** 069 * Check whether the given Object is equal to this subbasket. 070 * 071 * <p>This is overridden to mean identity, because for subbaskets equality and identity are really the 072 * same.</p> 073 * 074 * @override Never 075 */ 076 public boolean equals(Object o) { 077 return this == o; 078 } 079 080 /** 081 * Commit all items in this subbasket that match the condition. 082 * 083 * @param dbc the condition that must be matched. 084 * 085 * @see DataBasketEntry#commit 086 * 087 * @override Never 088 */ 089 public void commit(DataBasketCondition dbc) { 090 DataBasketEntry dbe = null; 091 092 for (Iterator i = iterator(dbc, true, true); i.hasNext(); ) { 093 try { 094 dbe = (DataBasketEntry)i.next(); 095 096 if (!dbe.isHandled()) { 097 dbe.commit(); 098 } 099 100 i.remove(); 101 } 102 catch (Throwable t) { 103 System.err.println("Exception during commit of <" + dbe + ">:"); 104 t.printStackTrace(); 105 System.err.println("Continuing committing any other DataBasketEntries."); 106 } 107 } 108 } 109 110 /** 111 * Rollback all entries in this subbasket that match the condition. 112 * 113 * @param dbc the condition to be matched. 114 * 115 * @see DataBasketEntry#rollback 116 * 117 * @override Never 118 */ 119 public void rollback(DataBasketCondition dbc) { 120 DataBasketEntry dbe = null; 121 122 for (Iterator i = iterator(dbc, true, true); i.hasNext(); ) { 123 try { 124 dbe = (DataBasketEntry)i.next(); 125 126 if (!dbe.isHandled()) { 127 dbe.rollback(); 128 } 129 130 i.remove(); 131 } 132 catch (Throwable t) { 133 System.err.println("Exception during rollback of <" + dbe + ">:"); 134 t.printStackTrace(); 135 System.err.println("Continuing rolling back any other DataBasketEntries."); 136 } 137 } 138 } 139 140 /** 141 * Iterate all entries in the subbasket that match the given condition. 142 * 143 * <p>The condition applies to the returned iterator only, it will not be influenced by any future calls 144 * to <code>iterator()</code>.</p> 145 * 146 * @param dbc the condition returned items will have to match. <code>null</code> means match all. 147 * @param fAllowRemove if true,the returned iterator's {@link java.util.Iterator#remove remove()} method * will be enabled. An iterator with its <code>remove()</code> method enabled must never be made publicly 148 * accessible outside of the {@link DataBasketImpl DataBasket}. 149 * @param fShowHandled if true, the iterator will include items that return true from their 150 * {@link DataBasketEntry#isHandled} method. Such an iterator must never be made publicly accessible 151 * outside of the {@link DataBasketImpl DataBasket}. 152 * 153 * @return an iterator that will iterate over all entries in this subbasket that match the given 154 * condition. The iterator will support the <code>remove()</code> method, if <code>fAllowRemove</code> is 155 * true. 156 * 157 * @override Never 158 */ 159 public Iterator iterator(final DataBasketCondition dbc, final boolean fAllowRemove, 160 final boolean fShowHandled) { 161 162 // The iterator to be returned 163 class I implements Iterator { 164 165 private Map m_mpmpldbeCategories; 166 private Iterator m_iCategories; 167 private Map m_mpldbeSubCategories; 168 private Iterator m_iSubCategories; 169 private Iterator m_iItems; 170 171 private DataBasketEntry m_dbeCurrent = null; 172 private DataBasketEntry m_dbeNext = null; 173 174 public I(Map mpmpldbeCategories) { 175 super(); 176 177 m_mpmpldbeCategories = mpmpldbeCategories; 178 179 // Use a TreeSet to sort the main keys alphabetically. 180 m_iCategories = new TreeSet(m_mpmpldbeCategories.keySet()).iterator(); 181 } 182 183 public boolean hasNext() { 184 return findNext(false); 185 } 186 187 public Object next() { 188 if (!findNext(true)) { 189 throw new NoSuchElementException(); 190 } 191 192 return m_dbeCurrent; 193 } 194 195 public void remove() { 196 if (!fAllowRemove) { 197 throw new UnsupportedOperationException(); 198 } else { 199 if (m_iItems == null) { 200 throw new IllegalStateException(); 201 } 202 203 m_iItems.remove(); 204 m_dbeCurrent.setOwner(null); 205 m_dbiOwner.fireDBERemoved(m_dbeCurrent); 206 } 207 } 208 209 private boolean findNext(boolean fGet) { 210 // traverse the hierarchy to find the next item that applies 211 // if fGet == true, put the next valid item into m_dbeCurrent 212 do { 213 if (m_iSubCategories != null) { 214 //look if current Category (__MAINKEY:_STOCKITEM_IMPL or __MAINKEY:_CATALOGITEM_IMPL) 215 //contains DataBasketEntries in one of its SubCategories, if so, we're done 216 if (checkSubCategories(fGet)) { 217 return true; 218 } 219 } else { 220 //dummy iterator to prevent possible NullPointerException at the end of the 221 //do-while statement 222 m_iSubCategories = new Iterator() { 223 public boolean hasNext() { 224 return false; 225 } 226 227 public Object next() { 228 return null; 229 } 230 231 public void remove() {} 232 }; 233 } 234 //Current Category did not contain DBEs, switch to next Category 235 while ((m_iCategories.hasNext()) && (!m_iSubCategories.hasNext())) { 236 String sCategoryID = (String)m_iCategories.next(); 237 238 if (dbc != null) { 239 if ((dbc.getMainKey() == null) || (dbc.getMainKey().equals(sCategoryID))) { 240 m_mpldbeSubCategories = (Map)m_mpmpldbeCategories.get(sCategoryID); 241 m_iSubCategories = m_mpldbeSubCategories.keySet().iterator(); 242 } 243 } else { 244 m_mpldbeSubCategories = (Map)m_mpmpldbeCategories.get(sCategoryID); 245 m_iSubCategories = m_mpldbeSubCategories.keySet().iterator(); 246 } 247 } 248 } 249 while (m_iSubCategories.hasNext()); 250 251 return false; 252 } 253 254 /** 255 * 256 * @param fGet 257 * @return 258 */ 259 private boolean checkSubCategories(boolean fGet) { 260 do { 261 if (m_iItems != null) { 262 //look if current SubCategory contains more DataBasketEntries, if so, we're done 263 if (checkItems(fGet)) { 264 return true; 265 } 266 } else { 267 //dummy iterator to prevent possible NullPointerException at the end of the 268 //do-while statement 269 m_iItems = new Iterator() { 270 public boolean hasNext() { 271 return false; 272 } 273 274 public Object next() { 275 return null; 276 } 277 278 public void remove() {} 279 }; 280 } 281 //Current SubCategory did not contain DBEs, switch to next SubCategory 282 while ((m_iSubCategories.hasNext()) && (!m_iItems.hasNext())) { 283 String sSubCategoryID = (String)m_iSubCategories.next(); 284 285 if (dbc != null) { 286 if ((dbc.getSecondaryKey() == null) || 287 (dbc.getSecondaryKey().equals(sSubCategoryID))) { 288 List ldbeSubCategory = (List)m_mpldbeSubCategories.get(sSubCategoryID); 289 m_iItems = ldbeSubCategory.iterator(); 290 } 291 } else { 292 List ldbeSubCategory = (List)m_mpldbeSubCategories.get(sSubCategoryID); 293 m_iItems = ldbeSubCategory.iterator(); 294 } 295 } 296 } 297 while (m_iItems.hasNext()); 298 299 return false; 300 } 301 302 /** 303 * Iterate over m_iItems, until a DataBasketEntry that matches the DataBasketCondition is found. 304 * DataBasketEntries of m_iItems have both the same main and secondary key. 305 * @param fGet if true, the found DataBasketEntry is assigned to m_dbeCurrent, otherwise to 306 * m_dbeNext. 307 * @return true, if a DataBasketEntry that matches the DataBasketCondition is found. 308 */ 309 private boolean checkItems(boolean fGet) { 310 if (m_dbeNext != null) { 311 if (fGet) { 312 m_dbeCurrent = m_dbeNext; 313 m_dbeNext = null; 314 } 315 316 return true; 317 } 318 319 while (m_iItems.hasNext()) { 320 DataBasketEntry dbe = (DataBasketEntry)m_iItems.next(); 321 322 if ((dbe.isHandled()) && (!fShowHandled)) { 323 continue; 324 } 325 326 if (dbc != null) { 327 if ((dbc.getSource() != null) && (dbc.getSource() != dbe.getSource())) { 328 continue; 329 } 330 331 if ((dbc.getDestination() != null) && (dbc.getDestination() != dbe.getDestination())) { 332 continue; 333 } 334 335 if (((dbc.getValue() != null) && (dbc.getValue() == dbe.getValue())) || 336 ((dbc.getValue() == null) && (dbc.match(dbe)))) { 337 338 if (!fGet) { 339 m_dbeNext = dbe; 340 } else { 341 m_dbeCurrent = dbe; 342 } 343 344 return true; 345 } 346 } else { 347 if (!fGet) { 348 m_dbeNext = dbe; 349 } else { 350 m_dbeCurrent = dbe; 351 } 352 353 return true; 354 } 355 } 356 357 return false; 358 } 359 } 360 361 return new I(m_mpmpldbeCategories); 362 } 363 364 /** 365 * Sum up the values of all entries in this subbasket that match the condition. 366 * 367 * @param dbc the condition to be matched. 368 * @param bev an helper object used to determine the value of each matching DataBasketEntry. 369 * @param vInit the value that is to be used for adding up. All adding is performed by calling 370 * {@link Value#addAccumulating} on this object. 371 * 372 * @return the sum in <code>vInit</code>. 373 * 374 * @override Never 375 */ 376 public Value sumSubBasket(DataBasketCondition dbc, BasketEntryValue bev, Value vInit) { 377 for (Iterator i = iterator(dbc, false, false); i.hasNext(); ) { 378 vInit.addAccumulating((Value)bev.getEntryValue((DataBasketEntry)i.next()).clone()); 379 } 380 381 return vInit; 382 } 383 384 /** 385 * Put a {@link DataBasketEntry} into the subbasket. 386 * 387 * @param dbe the entry to be put 388 * 389 * @see DataBasketImpl#put 390 * 391 * @override Never 392 */ 393 public void put(DataBasketEntryImpl dbe) { 394 Map mpldbeCategory = (Map)m_mpmpldbeCategories.get(dbe.getMainKey()); 395 396 if (mpldbeCategory == null) { 397 mpldbeCategory = new HashMap(); 398 m_mpmpldbeCategories.put(dbe.getMainKey(), mpldbeCategory); 399 } 400 401 List ldbeSubCategory = (List)mpldbeCategory.get(dbe.getSecondaryKey()); 402 403 if (ldbeSubCategory == null) { 404 ldbeSubCategory = new LinkedList(); 405 mpldbeCategory.put(dbe.getSecondaryKey(), ldbeSubCategory); 406 } 407 408 ldbeSubCategory.add(dbe); 409 410 dbe.setOwner(m_dbiOwner); 411 412 m_dbiOwner.log(PUT_ACTION, dbe); 413 m_dbiOwner.fireDBEAdded(dbe); 414 } 415 416 /** 417 * Get the first entry in this subbasket that matches the condition, if any. 418 * 419 * @param dbc the condition to be matched 420 * 421 * @return the matching entry, if any. 422 * 423 * @see DataBasketImpl#get 424 * 425 * @override Never 426 */ 427 public DataBasketEntry get(DataBasketCondition dbc) { 428 Iterator i = iterator(dbc, false, false); 429 430 if (i.hasNext()) { 431 return (DataBasketEntry)i.next(); 432 } else { 433 return null; 434 } 435 } 436 } 437 438 /** 439 * The subbaskets of this DataBasket. 440 * 441 * @serial 442 */ 443 protected Map m_mpsdbChildren = new HashMap(); 444 445 /** 446 * The monitor used to synchronize access to the list of children. 447 */ 448 private transient Object m_oChildrenLock; 449 450 /** 451 * Return the monitor used to synchronize access to the list of children. 452 * 453 * @override Never 454 */ 455 protected final Object getChildrenLock() { 456 if (m_oChildrenLock == null) { 457 m_oChildrenLock = new Object(); 458 } 459 460 return m_oChildrenLock; 461 } 462 463 /** 464 * The current subbasket. 465 * 466 * @serial 467 */ 468 private SubDataBasket m_sdbCurrent; 469 470 /** 471 * The monitor used to synchronize access to the current subbasket. 472 */ 473 private transient Object m_oCurrentLock; 474 475 /** 476 * Return the monitor used to synchronize access to the current subbasket. 477 * 478 * @override Never 479 */ 480 private final Object getCurrentLock() { 481 if (m_oCurrentLock == null) { 482 m_oCurrentLock = new Object(); 483 } 484 485 return m_oCurrentLock; 486 } 487 488 /** 489 * The current log context. 490 * 491 * @serial 492 */ 493 private LogContext m_lcLog; 494 495 /** 496 * The current log mode. 497 * 498 * @serial 499 */ 500 private int m_nLogMode = LOG_MODE_NONE; 501 502 /** 503 * The monitor synchronizing access to the log related attributes. 504 */ 505 private transient Object m_oLogLock; 506 507 /** 508 * Get the monitor synchronizing access to the log related attributes. 509 * 510 * @override Never 511 */ 512 private final Object getLogLock() { 513 if (m_oLogLock == null) { 514 m_oLogLock = new Object(); 515 } 516 517 return m_oLogLock; 518 } 519 520 /** 521 * The listeners currently listening for events from this DataBasket. 522 * 523 * @serial 524 */ 525 protected ListenerHelper m_lhListeners = new ListenerHelper(); 526 527 /** 528 * Create a new DataBasketImpl. 529 */ 530 public DataBasketImpl() { 531 super(); 532 533 setCurrentSubBasket(DEFAULTSUBBASKET_NAME); 534 } 535 536 /** 537 * Rollback the contents of all subbaskets of this DataBasket. 538 * 539 * @override Never 540 */ 541 public void rollback() { 542 rollback((DataBasketCondition)null); 543 } 544 545 /** 546 * Commit the contents of all subbaskets of this DataBasket. 547 * 548 * @override Never 549 */ 550 public void commit() { 551 commit((DataBasketCondition)null); 552 } 553 554 /** 555 * Rollback all items in all subbaskets that do match the given condition. 556 * 557 * @param dbc the condition to be matched. <code>null</code> means rollback 558 * unconditionally. 559 * 560 * @override Never 561 */ 562 public void rollback(DataBasketCondition dbc) { 563 synchronized (getChildrenLock()) { 564 synchronized (getLogLock()) { 565 for (Iterator i = m_mpsdbChildren.values().iterator(); i.hasNext(); ) { 566 ((SubDataBasket)i.next()).rollback(dbc); 567 } 568 569 clean(); 570 } 571 } 572 } 573 574 /** 575 * Commit all items in all subbaskets that do match the given condition. 576 * 577 * @param dbc the condition to be matched. <code>null</code> means commit 578 * unconditionally. 579 * 580 * @override Never 581 */ 582 public void commit(DataBasketCondition dbc) { 583 synchronized (getChildrenLock()) { 584 synchronized (getLogLock()) { 585 for (Iterator i = m_mpsdbChildren.values().iterator(); i.hasNext(); ) { 586 ((SubDataBasket)i.next()).commit(dbc); 587 } 588 589 clean(); 590 } 591 } 592 } 593 594 /** 595 * Rollback all entries in a given subbasket. 596 * 597 * @param sName the name of the subbasket. 598 * 599 * @override Never 600 */ 601 public void rollbackSubBasket(String sName) { 602 SubDataBasket sdb = null; 603 604 synchronized (getChildrenLock()) { 605 sdb = (SubDataBasket)m_mpsdbChildren.get(sName); 606 607 synchronized (getLogLock()) { 608 if (sdb != null) { 609 sdb.rollback(null); 610 } 611 612 clean(); 613 } 614 } 615 } 616 617 /** 618 * Rollback the current subbasket's contents. 619 * 620 * @override Never 621 */ 622 public void rollbackCurrentSubBasket() { 623 SubDataBasket sdbCurrent = null; 624 625 synchronized (getCurrentLock()) { 626 sdbCurrent = m_sdbCurrent; 627 } 628 629 if (sdbCurrent != null) { 630 synchronized (getChildrenLock()) { 631 synchronized (getLogLock()) { 632 sdbCurrent.rollback(null); 633 634 clean(); 635 } 636 } 637 } 638 } 639 640 /** 641 * Commit all entries in a given subbasket. 642 * 643 * @param sName the name of the subbasket. 644 * 645 * @override Never 646 */ 647 public void commitSubBasket(String sName) { 648 SubDataBasket sdb = null; 649 650 synchronized (getChildrenLock()) { 651 sdb = (SubDataBasket)m_mpsdbChildren.get(sName); 652 653 synchronized (getLogLock()) { 654 if (sdb != null) { 655 sdb.commit(null); 656 } 657 658 clean(); 659 } 660 } 661 } 662 663 /** 664 * Commit the current subbasket's contents. 665 * 666 * @override Never 667 */ 668 public void commitCurrentSubBasket() { 669 SubDataBasket sdbCurrent = null; 670 671 synchronized (getCurrentLock()) { 672 sdbCurrent = m_sdbCurrent; 673 } 674 675 if (sdbCurrent != null) { 676 synchronized (getChildrenLock()) { 677 synchronized (getLogLock()) { 678 sdbCurrent.commit(null); 679 clean(); 680 } 681 } 682 } 683 } 684 685 /** 686 * Set the current subbasket. 687 * 688 * @param sName the name of the new current subbasket. If the subbasket does not yet exist, it 689 * is created prior to being made the current subbasket. 690 * 691 * @override Never 692 */ 693 public void setCurrentSubBasket(String sName) { 694 synchronized (getChildrenLock()) { 695 if (!m_mpsdbChildren.containsKey(sName)) { 696 SubDataBasket sdb = new SubDataBasket(this); 697 m_mpsdbChildren.put(sName, sdb); 698 } 699 700 synchronized (getCurrentLock()) { 701 m_sdbCurrent = (SubDataBasket)m_mpsdbChildren.get(sName); 702 } 703 } 704 } 705 706 /** 707 * Iterate all entries in all subbaskets that match the condition. 708 * 709 * @param dbc the condition to be matched. 710 * 711 * @return an iterator that iterates all the entries in the DataBasket that match the condition. The 712 * iterator will not support the {@link java.util.Iterator#remove remove()} method. 713 * 714 * @override Never 715 */ 716 public Iterator iterator(final DataBasketCondition dbc) { 717 class I implements Iterator { 718 private Iterator m_iChildKeys; 719 private Iterator m_iSubBasketItems; 720 private DataBasket m_dbSource; 721 722 public I(Iterator iChildKeys, DataBasket dbSource) { 723 super(); 724 725 m_iChildKeys = iChildKeys; 726 m_dbSource = dbSource; 727 } 728 729 public boolean hasNext() { 730 return findNextItem(); 731 } 732 733 public Object next() { 734 if (!findNextItem()) { 735 throw new NoSuchElementException(); 736 } 737 738 return m_iSubBasketItems.next(); 739 } 740 741 public void remove() { 742 throw new UnsupportedOperationException(); 743 } 744 745 private boolean findNextItem() { 746 while (((m_iSubBasketItems == null) || 747 (!m_iSubBasketItems.hasNext())) && (m_iChildKeys.hasNext())) { 748 // try next subbasket 749 String sNextBasket = (String)m_iChildKeys.next(); 750 m_iSubBasketItems = m_dbSource.subBasketIterator(sNextBasket, dbc); 751 } 752 753 if ((m_iSubBasketItems == null) || (!m_iSubBasketItems.hasNext())) { 754 // did not find valid next subbasket 755 return false; 756 } 757 758 return true; 759 } 760 } 761 762 return new I(m_mpsdbChildren.keySet().iterator(), this); 763 } 764 765 /** 766 * Iterate all entries in a given subbasket that match the given condition. 767 * 768 * @param dbc the condition to be matched. 769 * 770 * @return an iterator that iterates all the entries in the given subbasket that match the condition. The 771 * iterator will not support the {@link java.util.Iterator#remove remove()} method. 772 * 773 * @override Never 774 */ 775 public Iterator subBasketIterator(String sName, DataBasketCondition dbc) { 776 synchronized (getChildrenLock()) { 777 SubDataBasket sdb = (SubDataBasket)m_mpsdbChildren.get(sName); 778 779 return sdb.iterator(dbc, false, false); 780 } 781 } 782 783 /** 784 * Sum up all entries in the DataBasket that match the given condition. 785 * 786 * @param dbc the condition to be matched. 787 * @param bev an object used for determining the value of a {@link DataBasketEntry}. 788 * @param vInit the value that is to be used for adding up. All adding is performed by calling 789 * {@link Value#addAccumulating} on this object. 790 * 791 * @return the sum in <code>vInit</code>. 792 * 793 * @override Never 794 */ 795 public Value sumBasket(DataBasketCondition dbc, BasketEntryValue bev, Value vInit) { 796 synchronized (getChildrenLock()) { 797 for (Iterator i = m_mpsdbChildren.values().iterator(); i.hasNext(); ) { 798 SubDataBasket sdb = (SubDataBasket)i.next(); 799 sdb.sumSubBasket(dbc, bev, vInit); 800 } 801 } 802 803 return vInit; 804 } 805 806 /** 807 * Sum up all entries in a given subbasket that match the given condition. 808 * 809 * @param sName the name of the subbasket that is to be summed up. 810 * @param dbc the condition to be matched. 811 * @param bev an object used for determining the value of a {@link DataBasketEntry}. 812 * @param vInit the value that is to be used for adding up. All adding is performed by calling 813 * {@link Value#addAccumulating} on this object. 814 * 815 * @return the sum in <code>vInit</code>. 816 * 817 * @override Never 818 */ 819 public Value sumSubBasket(String sName, DataBasketCondition dbc, BasketEntryValue bev, Value vInit) { 820 SubDataBasket sdb = null; 821 822 synchronized (getChildrenLock()) { 823 sdb = (SubDataBasket)m_mpsdbChildren.get(sName); 824 825 return sdb.sumSubBasket(dbc, bev, vInit); 826 } 827 } 828 829 /** 830 * Sum up all entries in the current subbasket that match the given condition. 831 * 832 * @param dbc the condition to be matched. 833 * @param bev an object used for determining the value of a {@link DataBasketEntry}. 834 * @param vInit the value that is to be used for adding up. All adding is performed by calling 835 * {@link Value#addAccumulating} on this object. 836 * 837 * @return the sum in <code>vInit</code>. 838 * 839 * @override Never 840 */ 841 public Value sumCurrentSubBasket(DataBasketCondition dbc, BasketEntryValue bev, Value vInit) { 842 synchronized (getCurrentLock()) { 843 return m_sdbCurrent.sumSubBasket(dbc, bev, vInit); 844 } 845 } 846 847 /** 848 * Put a DataBasketEntry into the current subbasket. DataBasketEntries that are to be put into a 849 * DataBasketImpl must be instances of, or of subclasses of, {@link DataBasketEntryImpl}. 850 * 851 * @param dbe the entry to be put in. 852 * 853 * @exception ClassCastException if <code>! (dbe instanceof {@link DataBasketEntryImpl})</code>. 854 * 855 * @override Never 856 */ 857 public void put(DataBasketEntry dbe) { 858 synchronized (getCurrentLock()) { 859 synchronized (getChildrenLock()) { 860 synchronized (getLogLock()) { 861 m_sdbCurrent.put((DataBasketEntryImpl)dbe); 862 } 863 } 864 } 865 } 866 867 /** 868 * Exchange a DataBasketEntry with another. 869 * 870 * @param dbeOrg the original DataBasketEntry, to be replaced. 871 * @param dbeNew the replacement. 872 * 873 * @override Never 874 */ 875 public void exchange(final DataBasketEntry dbeOrg, DataBasketEntry dbeNew) { 876 DataBasketCondition dbc = new DataBasketConditionImpl(dbeOrg.getMainKey(), dbeOrg.getSecondaryKey(), 877 dbeOrg.getSource(), dbeOrg.getDestination(), null) { 878 public boolean match(DataBasketEntry dbe) { 879 return (dbe == dbeOrg); 880 } 881 }; 882 883 synchronized (getChildrenLock()) { 884 synchronized (getLogLock()) { 885 for (Iterator i = m_mpsdbChildren.values().iterator(); i.hasNext(); ) { 886 SubDataBasket sdb = (SubDataBasket)i.next(); 887 888 Iterator i1 = sdb.iterator(dbc, true, false); 889 if (i1.hasNext()) { 890 DataBasketEntryImpl dbe = (DataBasketEntryImpl)i1.next(); 891 892 i1.remove(); 893 894 log(EXCHANGE_REMOVE_ACTION, dbe); 895 sdb.put((DataBasketEntryImpl)dbeNew); 896 897 return; 898 } 899 } 900 901 put(dbeNew); 902 } 903 } 904 } 905 906 /** 907 * Get the first entry in the DataBasket that matches the condition. 908 * 909 * @param dbc the condition 910 * 911 * @override Never 912 */ 913 public DataBasketEntry get(DataBasketCondition dbc) { 914 synchronized (getChildrenLock()) { 915 Iterator i = iterator(dbc); 916 if (i.hasNext()) { 917 return (DataBasketEntry)i.next(); 918 } else { 919 return null; 920 } 921 } 922 } 923 924 /** 925 * Check whether any entries matching a particular condition are contained in the DataBasket. 926 * 927 * @param dbc the condition 928 * 929 * @return true if an entry that matches the condition could be found. 930 * 931 * @override Never 932 */ 933 public boolean contains(DataBasketCondition dbc) { 934 synchronized (getChildrenLock()) { 935 return (iterator(dbc).hasNext()); 936 } 937 } 938 939 /** 940 * Set the log context for this DataBasket. 941 * 942 * <p>All operations as defined through {@link #setLogMode} will be logged using the given log context. If 943 * the current log context is <code>null</code> no logging of any kind will occur.</p> 944 * 945 * <p>The actual LogEntries written are defined by the {@link DataBasketEntryImpl DataBasketEntries} that 946 * participate in the activities that are logged. The DataBasket will wrap those LogEntries into LogEntries 947 * that give the type of operation performed.</p> 948 * 949 * @param lcNew the new log context 950 * 951 * @return the previous log context, if any. 952 * 953 * @see #log 954 * 955 * @override Never 956 */ 957 public LogContext setLogContext(LogContext lcNew) { 958 synchronized (getLogLock()) { 959 LogContext lc = m_lcLog; 960 961 m_lcLog = lcNew; 962 963 return lc; 964 } 965 } 966 967 /** 968 * Set the log mode for this DataBasket. 969 * 970 * <p>The current log mode decides what operations on the DataBasket are being logged. The default value is 971 * {@link DataBasket#LOG_MODE_NONE}, indicating that no logging occurs. Other possibilities are:</p> 972 * 973 * <ul> 974 * <li><strong>{@link DataBasket#LOG_MODE_ALL}</strong> All operations on the DataBasket are being logged. 975 * </li> 976 * <li><strong>{@link DataBasket#LOG_MODE_COMMITS_ONLY}</strong> Only commits are being logged. There will 977 * be one entry for each single step in the commit process.</li> 978 * <li><strong>{@link DataBasket#LOG_MODE_ROLLBACKS_ONLY}</strong> Only rollbacks are being logged. There 979 * will be one entry for each single step in the rollback process.</li> 980 * </ul> 981 * 982 * <p>The actual LogEntries written are defined by the {@link DataBasketEntryImpl DataBasketEntries} that 983 * participate in the activities that are logged. The DataBasket will wrap those LogEntries into LogEntries 984 * that give the type of operation performed.</p> 985 * 986 * @param nLogMode the new log mode. 987 * 988 * @return the previous log mode. 989 * 990 * @see #log 991 * 992 * @override Never 993 */ 994 public int setLogMode(int nLogMode) { 995 synchronized (getLogLock()) { 996 int n = m_nLogMode; 997 998 m_nLogMode = nLogMode; 999 1000 return n; 1001 } 1002 } 1003 1004 /** 1005 * Return the current log mode of the DataBasket. For information on the possible values and their meaning, 1006 * please refer to {@link #setLogMode}. 1007 * 1008 * @override Never 1009 */ 1010 public int getLogMode() { 1011 synchronized (getLogLock()) { 1012 return m_nLogMode; 1013 } 1014 } 1015 1016 /** 1017 * Action constant for {@link #log}. 1018 */ 1019 public static final int PUT_ACTION = 0; 1020 1021 /** 1022 * Action constant for {@link #log}. 1023 */ 1024 public static final int EXCHANGE_REMOVE_ACTION = 1; 1025 1026 /** 1027 * Action constant for {@link #log}. 1028 */ 1029 public static final int COMMIT_ACTION = 2; 1030 1031 /** 1032 * Action constant for {@link #log}. 1033 */ 1034 public static final int ROLLBACK_ACTION = 3; 1035 1036 /** 1037 * A LogEntryFilter that will accept only LogEntries that were produced by a DataBasketImpl. 1038 */ 1039 public static final LogEntryFilter LOGENTRYFILTER_DATABASKETIMPLACTIONS = new LogEntryFilter() { 1040 public boolean accept(LogEntry le) { 1041 return (le instanceof DataBasketImplLogEntry); 1042 } 1043 }; 1044 1045 /** 1046 * A LogEntryFilter that will accept only LogEntries that were not produced by a DataBasketImpl. 1047 */ 1048 public static final LogEntryFilter LOGENTRYFILTER_NO_DATABASKETIMPLACTIONS = new LogEntryFilter() { 1049 public boolean accept(LogEntry le) { 1050 return!(le instanceof DataBasketImplLogEntry); 1051 } 1052 }; 1053 1054 /** 1055 * A LogEntryFilter that will accept only LogEntries that were produced by a DataBasketImpl and that 1056 * describe a <code>put</code> action. 1057 */ 1058 public static final LogEntryFilter LOGENTRYFILTER_PUT_ACTIONS = new LogEntryFilter() { 1059 public boolean accept(LogEntry le) { 1060 return ((le instanceof DataBasketImplLogEntry) && 1061 (((DataBasketImplLogEntry)le).getAction() == PUT_ACTION)); 1062 } 1063 }; 1064 1065 /** 1066 * A LogEntryFilter that will accept only LogEntries that were produced by a DataBasketImpl and that 1067 * describe a <code>exchange_remove</code> action. 1068 */ 1069 public static final LogEntryFilter LOGENTRYFILTER_EXCHANGE_REMOVE_ACTIONS = new LogEntryFilter() { 1070 public boolean accept(LogEntry le) { 1071 return ((le instanceof DataBasketImplLogEntry) && 1072 (((DataBasketImplLogEntry)le).getAction() == EXCHANGE_REMOVE_ACTION)); 1073 } 1074 }; 1075 1076 /** 1077 * A LogEntryFilter that will accept only LogEntries that were produced by a DataBasketImpl and that 1078 * describe a <code>commit</code> action. 1079 */ 1080 public static final LogEntryFilter LOGENTRYFILTER_COMMIT_ACTIONS = new LogEntryFilter() { 1081 public boolean accept(LogEntry le) { 1082 return ((le instanceof DataBasketImplLogEntry) && 1083 (((DataBasketImplLogEntry)le).getAction() == COMMIT_ACTION)); 1084 } 1085 }; 1086 1087 /** 1088 * A LogEntryFilter that will accept only LogEntries that were produced by a DataBasketImpl and that 1089 * describe a <code>rollback</code> action. 1090 */ 1091 public static final LogEntryFilter LOGENTRYFILTER_ROLLBACK_ACTIONS = new LogEntryFilter() { 1092 public boolean accept(LogEntry le) { 1093 return ((le instanceof DataBasketImplLogEntry) && 1094 (((DataBasketImplLogEntry)le).getAction() == ROLLBACK_ACTION)); 1095 } 1096 }; 1097 1098 /** 1099 * A LogEntry that describes an action on a DataBasket. 1100 * 1101 * @see DataBasketImpl#log 1102 * 1103 * @author Steffen Zschaler 1104 * @version 2.0 14/07/1999 1105 * @since v2.0 1106 */ 1107 public static class DataBasketImplLogEntry extends LogEntry { 1108 1109 /** 1110 * The log entry describing the actual action. 1111 * 1112 * @serial 1113 */ 1114 private LogEntry m_leData; 1115 1116 /** 1117 * The action code. One of PUT_ACTION, EXCHANGE_REMOVE_ACTION, COMMIT_ACTION, ROLLBACK_ACTION. 1118 * 1119 * @serial 1120 */ 1121 private int m_nAction; 1122 1123 /** 1124 * Helper array for converting action codes into Strings. 1125 */ 1126 private static String[] s_asActionNames = { 1127 "PUT_ACTION", "EXCHANGE_REMOVE_ACTION", "COMMIT_ACTION", "ROLLBACK_ACTION"}; 1128 1129 /** 1130 * Create a new DataBasketImplLogEntry. 1131 * 1132 * @param The action code. One of {@link DataBasketImpl#PUT_ACTION}, 1133 * {@link DataBasketImpl#EXCHANGE_REMOVE_ACTION}, {@link DataBasketImpl#COMMIT_ACTION}, 1134 * {@link DataBasketImpl#ROLLBACK_ACTION}. 1135 * @param leData The log entry describing the actual action. 1136 */ 1137 public DataBasketImplLogEntry(int nAction, LogEntry leData) { 1138 super(); 1139 1140 m_nAction = nAction; 1141 m_leData = leData; 1142 } 1143 1144 /** 1145 * Get the action code. 1146 * 1147 * @override Never 1148 */ 1149 public int getAction() { 1150 return m_nAction; 1151 } 1152 1153 /** 1154 * Get the name of the action. 1155 * 1156 * @override Never 1157 */ 1158 public String getActionName() { 1159 return s_asActionNames[getAction()]; 1160 } 1161 1162 /** 1163 * Get the log entry describing the actual action. 1164 * 1165 * @override Never 1166 */ 1167 public LogEntry getData() { 1168 return m_leData; 1169 } 1170 1171 /** 1172 * A short descriptive text of the log entry. 1173 * 1174 * @override Never 1175 */ 1176 public String toString() { 1177 return getActionName() + " performed on " + getData(); 1178 } 1179 } 1180 1181 /** 1182 * Log the given event wrapped in a LogEntry that describes the action. 1183 * 1184 * <p>If the action needs to be logged in the current log mode, a new {@link DataBasketImplLogEntry} is 1185 * created that wraps the LogEntry produced by the given Loggable. This LogEntry is then logged using the 1186 * current LogContext.</p> 1187 * 1188 * @see #setLogContext 1189 * @see #setLogMode 1190 * 1191 * @override Never 1192 */ 1193 public void log(int nAction, Loggable la) { 1194 synchronized (getLogLock()) { 1195 if (m_lcLog != null) { 1196 switch (m_nLogMode) { 1197 case LOG_MODE_NONE: 1198 return; 1199 case LOG_MODE_COMMITS_ONLY: 1200 if (nAction != COMMIT_ACTION) { 1201 return; 1202 } 1203 1204 break; 1205 case LOG_MODE_ROLLBACKS_ONLY: 1206 if (nAction != ROLLBACK_ACTION) { 1207 return; 1208 } 1209 } 1210 1211 final DataBasketImplLogEntry dbile = new DataBasketImplLogEntry(nAction, la.getLogData()); 1212 1213 try { 1214 m_lcLog.log(new Loggable() { 1215 public LogEntry getLogData() { 1216 return dbile; 1217 } 1218 }); 1219 } 1220 catch (java.io.IOException ioe) { 1221 ioe.printStackTrace(); 1222 } 1223 } 1224 } 1225 } 1226 1227 /** 1228 * Return the monitor used to synchronize access to the internal data structures of the DataBasket. Whenever 1229 * a container wants to access a DataBasket implemented in this way, it must <i>first</i> synchronize over 1230 * the DataBasket's monitor and <i>then</i> synchronize over its own monitor(s). 1231 * 1232 * @override Never 1233 */ 1234 public Object getDBIMonitor() { 1235 return getChildrenLock(); 1236 } 1237 1238 /** 1239 * Remove any items that have already been {@link DataBasketEntry#isHandled handled}, but so far have not 1240 * been removed from the DataBasket. 1241 * 1242 * <p>This method is usually called by the framework only. It assumes, that it is called from within a 1243 * <code>synchronized</code> block synchronizing on the monitor returned by {@link #getDBIMonitor}.</p> 1244 * 1245 * @override Never 1246 */ 1247 protected void clean() { 1248 // Remove any items that where handled because of dependencies 1249 for (Iterator i = m_mpsdbChildren.values().iterator(); i.hasNext(); ) { 1250 Iterator j = ((SubDataBasket)i.next()).iterator(null, true, true); 1251 1252 while (j.hasNext()) { 1253 if (((DataBasketEntry)j.next()).isHandled()) { 1254 j.remove(); 1255 } 1256 } 1257 } 1258 } 1259 1260 // ListenableDataBasket interface methods 1261 1262 /** 1263 * Add a listener that will receive events when the DataBasket's contents change. 1264 * 1265 * @override Never 1266 */ 1267 public void addDataBasketListener(DataBasketListener dbl) { 1268 m_lhListeners.add(DataBasketListener.class, dbl); 1269 } 1270 1271 /** 1272 * Remove a listener that received events when the DataBasket's contents changed. 1273 * 1274 * @override Never 1275 */ 1276 public void removeDataBasketListener(DataBasketListener dbl) { 1277 m_lhListeners.remove(DataBasketListener.class, dbl); 1278 } 1279 1280 /** 1281 * Fire an event to all listeners listening to this DataBasket. 1282 * 1283 * @override Never 1284 */ 1285 public void fireDataBasketChanged() { 1286 Object[] listeners = m_lhListeners.getListenerList(); 1287 DataBasketEvent e = null; 1288 1289 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1290 if (listeners[i] == DataBasketListener.class) { 1291 if (e == null) { 1292 e = new DataBasketEvent(this, null); 1293 } 1294 1295 ((DataBasketListener)listeners[i + 1]).dataBasketChanged(e); 1296 } 1297 } 1298 } 1299 1300 /** 1301 * Fire an event to all listeners listening to this DataBasket. 1302 * 1303 * @override Never 1304 */ 1305 protected void fireDBEAdded(DataBasketEntry dbe) { 1306 Object[] listeners = m_lhListeners.getListenerList(); 1307 DataBasketEvent e = null; 1308 1309 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1310 if (listeners[i] == DataBasketListener.class) { 1311 if (e == null) { 1312 e = new DataBasketEvent(this, dbe); 1313 } 1314 1315 ((DataBasketListener)listeners[i + 1]).addedDBE(e); 1316 } 1317 } 1318 } 1319 1320 /** 1321 * Fire an event to all listeners listening to this DataBasket. 1322 * 1323 * @override Never 1324 */ 1325 protected void fireDBERemoved(DataBasketEntry dbe) { 1326 Object[] listeners = m_lhListeners.getListenerList(); 1327 DataBasketEvent e = null; 1328 1329 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1330 if (listeners[i] == DataBasketListener.class) { 1331 if (e == null) { 1332 e = new DataBasketEvent(this, dbe); 1333 } 1334 1335 ((DataBasketListener)listeners[i + 1]).removedDBE(e); 1336 } 1337 } 1338 } 1339 }