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