001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2010 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS 026 */ 027package org.opends.server.api; 028 029import java.util.AbstractMap; 030import java.util.AbstractSet; 031import java.util.Collection; 032import java.util.HashMap; 033import java.util.Iterator; 034import java.util.Map; 035import java.util.NoSuchElementException; 036import java.util.Set; 037 038import org.opends.server.types.DN; 039 040/** 041 * The DITCacheMap class implements custom Map for structural storage of 042 * arbitrary objects in Directory Information Tree (DIT) like structure. 043 * <p> 044 * This Map intended usage is for caching various server objects which can be 045 * subject to subtree operations like retrieval or removal of all objects under 046 * a specific DN. While using a regular Map it would require the entire Map 047 * iteration to achieve, this Map implementation maintains such internal 048 * structure that subtree operations are more efficient and do not require 049 * iterations over the entire map, instead additional subtree operations methods 050 * are provided by this Map to do just that. 051 * <p> 052 * API wise it behaves exactly like a regular Map implementation except for 053 * providing additional subtree methods. All required linkage and structuring is 054 * performed within this Map implementation itself and not exposed via the API 055 * in any way. For example, putting these key/value pairs: 056 * 057 * <pre> 058 * cn=Object1,ou=Objects,dc=example,dc=com : object1 059 * cn=Object2,ou=Objects,dc=example,dc=com : object2 060 * cn=Object3,ou=Objects,dc=example,dc=com : object3 061 * </pre> 062 * 063 * then invoking a subtree method on this Map with any of these keys: 064 * 065 * <pre> 066 * ou=Objects,dc=example,dc=com 067 * dc=example,dc=com 068 * dc=com 069 * </pre> 070 * 071 * would bring all three objects previously stored in this map into subtree 072 * operation scope. Standard Map API methods can only work with the objects 073 * previously stored in this map explicitly. 074 * <p> 075 * Note that this Map implementation is not synchronized. 076 * 077 * @param <T> 078 * arbitrary object type. 079 */ 080public final class DITCacheMap<T> extends AbstractMap<DN,T> 081{ 082 /** 083 * Node class for object storage and 084 * linking to any subordinate nodes. 085 * @param <T> arbitrary storage object. 086 */ 087 private static final class Node<T> 088 { 089 /** Node DN. */ 090 DN dn; 091 /** 092 * Storage object or null if this node exist 093 * only to support the DIT like structuring. 094 */ 095 T element; 096 /** Parent. */ 097 Node<T> parent; 098 /** First child. */ 099 Node<T> child; 100 /** Next sibling. */ 101 Node<T> next; 102 /** Previous sibling. */ 103 Node<T> previous; 104 105 /** {@inheritDoc} */ 106 @Override 107 public String toString() 108 { 109 if (element != null) 110 { 111 return "node(" + element + ")"; 112 } 113 else 114 { 115 return "glue"; 116 } 117 } 118 } 119 120 /** Map size reflecting only nodes containing non empty elements. */ 121 private int size; 122 123 /** Backing Map implementation. */ 124 private final Map<DN,Node<T>> ditCacheMap = new HashMap<>(); 125 126 /** Default constructor. */ 127 public DITCacheMap() 128 { 129 } 130 131 /** 132 * Constructs a new DITCacheMap from a given Map. 133 * @param m existing Map to construct new 134 * DITCacheMap from. 135 */ 136 public DITCacheMap(Map<? extends DN, ? extends T> m) 137 { 138 this.putAll(m); 139 } 140 141 /** {@inheritDoc} */ 142 @Override 143 public int size() 144 { 145 return size; 146 } 147 148 /** {@inheritDoc} */ 149 @Override 150 public boolean isEmpty() 151 { 152 return ditCacheMap.isEmpty(); 153 } 154 155 /** {@inheritDoc} */ 156 @Override 157 public boolean containsKey(Object key) 158 { 159 return get(key) != null; 160 } 161 162 /** {@inheritDoc} */ 163 @Override 164 public boolean containsValue(Object value) 165 { 166 for (Node<T> node : ditCacheMap.values()) 167 { 168 if (node.element != null && node.element.equals(value)) 169 { 170 return true; 171 } 172 } 173 return false; 174 } 175 176 /** {@inheritDoc} */ 177 @Override 178 public T get(Object key) 179 { 180 Node<T> node = ditCacheMap.get(key); 181 return node != null ? node.element : null; 182 } 183 184 /** 185 * Returns a set of stored objects 186 * subordinate to subtree DN. 187 * @param key subtree DN. 188 * @return collection of stored objects 189 * subordinate to subtree DN. 190 */ 191 public Collection<T> getSubtree(DN key) 192 { 193 return new DITSubtreeSet(key); 194 } 195 196 /** {@inheritDoc} */ 197 @Override 198 public T put(DN key, T value) 199 { 200 final Node<T> existingNode = ditCacheMap.get(key); 201 if (existingNode != null) 202 { 203 final T returnValue = existingNode.element; 204 existingNode.element = value; 205 return returnValue; 206 } 207 208 Node<T> node = new Node<>(); 209 node.dn = key; 210 node.element = value; 211 node.parent = null; 212 node.child = null; 213 node.next = null; 214 node.previous = null; 215 216 ditCacheMap.put(key, node); 217 size++; 218 219 // Update parent hierarchy. 220 for (DN parentDN = key.parent(); 221 parentDN != null; 222 parentDN = parentDN.parent()) 223 { 224 final Node<T> parentNode = ditCacheMap.get(parentDN); 225 226 if (parentNode == null) 227 { 228 // Add glue node. 229 final Node<T> newParentNode = new Node<>(); 230 newParentNode.dn = parentDN; 231 newParentNode.element = null; 232 newParentNode.parent = null; 233 newParentNode.child = node; 234 newParentNode.next = null; 235 newParentNode.previous = null; 236 ditCacheMap.put(parentDN, newParentNode); 237 node.parent = newParentNode; 238 node = newParentNode; 239 } 240 else 241 { 242 if (parentNode.child != null) 243 { 244 Node<T> lastNode = parentNode.child; 245 while (lastNode.next != null) 246 { 247 lastNode = lastNode.next; 248 } 249 node.previous = lastNode; 250 lastNode.next = node; 251 } 252 else 253 { 254 parentNode.child = node; 255 } 256 node.parent = parentNode; 257 break; 258 } 259 } 260 return null; 261 } 262 263 /** {@inheritDoc} */ 264 @Override 265 public T remove(Object key) 266 { 267 final DN dn = (DN) key; 268 final Node<T> node = ditCacheMap.get(dn); 269 if (node == null) 270 { 271 return null; 272 } 273 274 final T returnValue = node.element; 275 if (returnValue == null) 276 { 277 return null; 278 } 279 280 // Remove element from DIT. 281 size--; 282 node.element = null; 283 284 if (node.child == null) 285 { 286 // This node is now glue, so remove it completely and fix up 287 // any other nodes which reference it. 288 ditCacheMap.remove(dn); 289 fixNodeReferences(node); 290 } 291 292 return returnValue; 293 } 294 295 296 297 /** 298 * Remove references to a node after it has been removed. 299 * @param node The node which has just been removed. 300 */ 301 private void fixNodeReferences(Node<T> node) 302 { 303 while (true) 304 { 305 // Fix siblings. 306 final Node<T> nextNode = node.next; 307 final Node<T> previousNode = node.previous; 308 309 if (nextNode != null) 310 { 311 nextNode.previous = previousNode; 312 } 313 314 if (previousNode != null) 315 { 316 previousNode.next = nextNode; 317 } 318 319 // Fix parent, if it exists. 320 final Node<T> parentNode = node.parent; 321 if (parentNode == null) 322 { 323 // Reached the top of the DIT, so no parents to fix. 324 break; 325 } 326 327 if (parentNode.child != node) 328 { 329 // The parent references another sibling and does not need fixing. 330 break; 331 } 332 333 if (nextNode != null) 334 { 335 // Update child pointer and return. 336 parentNode.child = nextNode; 337 break; 338 } 339 340 if (parentNode.element != null) 341 { 342 // Parent node is still needed as it contains data. 343 break; 344 } 345 346 // The parent node is glue so remove it. 347 ditCacheMap.remove(parentNode.dn); 348 349 // Smash pointers. 350 node.parent = null; 351 node.previous = null; 352 node.next = null; 353 354 // Continue up tree. 355 node = parentNode; 356 } 357 } 358 359 360 361 /** 362 * Removes a set of stored objects subordinate to subtree DN. 363 * @param key subtree DN. 364 * @param values collection for removed objects subordinate 365 * to subtree DN or <code>null</code>. 366 * @return <code>true</code> on success or <code>false</code> otherwise. 367 */ 368 public boolean removeSubtree(DN key, Collection<? super T> values) 369 { 370 final Node<T> node = ditCacheMap.remove(key); 371 if (node != null) 372 { 373 // Fix up sibling and parent references. 374 fixNodeReferences(node); 375 376 // Collect all elements and update the size. 377 adjustSizeAndCollectElements(node, values); 378 return true; 379 } 380 return false; 381 } 382 383 384 385 /** 386 * Iterate through detached subtree counting and collecting any elements. 387 * 388 * @param node 389 * The node to be collected. 390 * @param values 391 * Collection in which to put elements. 392 */ 393 private void adjustSizeAndCollectElements(final Node<T> node, 394 Collection<? super T> values) 395 { 396 if (node.element != null) 397 { 398 if (values != null) 399 { 400 values.add(node.element); 401 } 402 node.element = null; 403 size--; 404 } 405 406 // Repeat for each child. 407 Node<T> child = node.child; 408 while (child != null) 409 { 410 final Node<T> next = child.next; 411 adjustSizeAndCollectElements(child, values); 412 child = next; 413 } 414 415 // Smash pointers. 416 node.parent = null; 417 node.child = null; 418 node.previous = null; 419 node.next = null; 420 421 // Remove node from map. 422 ditCacheMap.remove(node.dn); 423 } 424 425 426 427 /** {@inheritDoc} */ 428 @Override 429 public void putAll(Map<? extends DN, ? extends T> m) 430 { 431 for (Entry<? extends DN, ? extends T> entry : m.entrySet()) 432 { 433 put(entry.getKey(), entry.getValue()); 434 } 435 } 436 437 /** {@inheritDoc} */ 438 @Override 439 public void clear() 440 { 441 ditCacheMap.clear(); 442 size = 0; 443 } 444 445 /** {@inheritDoc} */ 446 @Override 447 public Set<Entry<DN, T>> entrySet() 448 { 449 return new DITCacheEntrySet(); 450 } 451 452 /** EntrySet class implementation for the DITCacheMap. */ 453 private class DITCacheEntrySet extends AbstractSet<Entry<DN, T>> 454 { 455 /** Iterator class implementation for the DITCacheEntrySet. */ 456 private class EntryIterator implements Iterator<Entry<DN, T>> 457 { 458 private Iterator<Entry<DN, Node<T>>> ditCacheMapIterator; 459 private Entry<DN, Node<T>> currentEntry; 460 private Entry<DN, Node<T>> nextEntry; 461 private boolean hasNext; 462 463 /** Default constructor. */ 464 public EntryIterator() 465 { 466 ditCacheMapIterator = ditCacheMap.entrySet().iterator(); 467 currentEntry = null; 468 nextEntry = null; 469 hasNext = false; 470 } 471 472 /** {@inheritDoc} */ 473 @Override 474 public boolean hasNext() 475 { 476 if (hasNext) 477 { 478 return true; 479 } 480 while (ditCacheMapIterator.hasNext()) 481 { 482 Entry<DN, Node<T>> entry = ditCacheMapIterator.next(); 483 Node<T> node = entry.getValue(); 484 if (node != null && node.element != null) 485 { 486 nextEntry = entry; 487 hasNext = true; 488 return true; 489 } 490 } 491 nextEntry = null; 492 return false; 493 } 494 495 /** {@inheritDoc} */ 496 @Override 497 public Entry<DN, T> next() 498 { 499 if (nextEntry != null) 500 { 501 Node<T> node = nextEntry.getValue(); 502 currentEntry = nextEntry; 503 nextEntry = null; 504 hasNext = false; 505 return new DITCacheMapEntry(node.dn, node.element); 506 } 507 while (ditCacheMapIterator.hasNext()) 508 { 509 Entry<DN, Node<T>> entry = ditCacheMapIterator.next(); 510 Node<T> node = entry.getValue(); 511 if (node != null && node.element != null) 512 { 513 currentEntry = entry; 514 hasNext = false; 515 return new DITCacheMapEntry(node.dn, node.element); 516 } 517 } 518 throw new NoSuchElementException(); 519 } 520 521 /** {@inheritDoc} */ 522 @Override 523 public void remove() 524 { 525 if (currentEntry != null) 526 { 527 Entry<DN, Node<T>> oldIteratorEntry = null; 528 if (hasNext()) 529 { 530 oldIteratorEntry = nextEntry; 531 } 532 if (DITCacheMap.this.remove(currentEntry.getKey()) != null) 533 { 534 ditCacheMapIterator = ditCacheMap.entrySet().iterator(); 535 currentEntry = null; 536 nextEntry = null; 537 hasNext = false; 538 while (hasNext()) 539 { 540 Entry<DN, T> newIteratorEntry = next(); 541 if (oldIteratorEntry != null 542 && oldIteratorEntry.getKey().equals(newIteratorEntry.getKey()) 543 && oldIteratorEntry.getValue().element.equals(newIteratorEntry.getValue())) 544 { 545 nextEntry = currentEntry; 546 hasNext = true; 547 return; 548 } 549 } 550 currentEntry = null; 551 nextEntry = null; 552 hasNext = false; 553 return; 554 } 555 } 556 throw new IllegalStateException(); 557 } 558 } 559 560 /** {@inheritDoc} */ 561 @Override 562 public int size() 563 { 564 return DITCacheMap.this.size(); 565 } 566 567 /** {@inheritDoc} */ 568 @Override 569 public Iterator<Entry<DN, T>> iterator() 570 { 571 return new EntryIterator(); 572 } 573 } 574 575 /** Map.Entry class implementation for the DITCacheMap. */ 576 private class DITCacheMapEntry implements Map.Entry<DN, T> 577 { 578 private DN key; 579 private T value; 580 581 /** 582 * Constructs a new DITCacheMapEntry 583 * with given key and value. 584 * @param key Map.Entry key. 585 * @param value Map.Entry value. 586 */ 587 public DITCacheMapEntry(DN key, T value) 588 { 589 this.key = key; 590 this.value = value; 591 } 592 593 /** {@inheritDoc} */ 594 @Override 595 public DN getKey() 596 { 597 return key; 598 } 599 600 /** {@inheritDoc} */ 601 @Override 602 public T getValue() 603 { 604 return value; 605 } 606 607 /** {@inheritDoc} */ 608 @Override 609 public T setValue(T value) 610 { 611 Node<T> node = ditCacheMap.get(key); 612 T oldValue = this.value; 613 node.element = value; 614 this.value = value; 615 return oldValue; 616 } 617 } 618 619 /** SubtreeSet class implementation. */ 620 private class DITSubtreeSet extends AbstractSet<T> 621 { 622 /** Set key. */ 623 private DN key; 624 625 /** 626 * Keyed constructor. 627 * @param key to construct 628 * this set from. 629 */ 630 public DITSubtreeSet(DN key) 631 { 632 this.key = key; 633 } 634 635 /** Iterator class implementation for SubtreeSet. */ 636 private class SubtreeSetIterator implements Iterator<T> 637 { 638 /** Iterator key. */ 639 private DN key; 640 641 /** Iterator root node. */ 642 private Node<T> rootNode; 643 644 /** Iterator current node. */ 645 private Node<T> node; 646 647 /** Default constructor. */ 648 public SubtreeSetIterator() 649 { 650 this.key = DITSubtreeSet.this.key; 651 rootNode = ditCacheMap.get(this.key); 652 node = rootNode; 653 } 654 655 /** 656 * Keyed constructor. 657 * @param key to cue this 658 * iterator from. 659 */ 660 public SubtreeSetIterator(DN key) 661 { 662 this.key = key; 663 rootNode = ditCacheMap.get(this.key); 664 node = rootNode; 665 } 666 667 /** {@inheritDoc} */ 668 @Override 669 public boolean hasNext() 670 { 671 if (rootNode != null) 672 { 673 if (node == rootNode && rootNode.element != null) 674 { 675 return true; 676 } 677 while (node != null) 678 { 679 if (node.element != null) 680 { 681 return true; 682 } 683 if (node.child != null) 684 { 685 node = node.child; 686 } 687 else 688 { 689 while (node.next == null && node.parent != rootNode) 690 { 691 node = node.parent; 692 } 693 node = node.next; 694 } 695 } 696 } 697 return false; 698 } 699 700 /** {@inheritDoc} */ 701 @Override 702 public T next() 703 { 704 T element = null; 705 706 if (rootNode != null) 707 { 708 if (node == rootNode) 709 { 710 node = rootNode.child; 711 if (rootNode.element != null) 712 { 713 return rootNode.element; 714 } 715 } 716 while (node != null) 717 { 718 element = node.element; 719 if (node.child != null) 720 { 721 node = node.child; 722 } 723 else 724 { 725 while (node.next == null && node.parent != rootNode) 726 { 727 node = node.parent; 728 } 729 node = node.next; 730 } 731 if (element != null) 732 { 733 return element; 734 } 735 } 736 } 737 throw new NoSuchElementException(); 738 } 739 740 /** {@inheritDoc} */ 741 @Override 742 public void remove() 743 { 744 throw new UnsupportedOperationException(); 745 } 746 } 747 748 /** {@inheritDoc} */ 749 @Override 750 public Iterator<T> iterator() 751 { 752 return new SubtreeSetIterator(); 753 } 754 755 /** {@inheritDoc} */ 756 @Override 757 public int size() 758 { 759 int size = 0; 760 761 Iterator<T> iterator = new SubtreeSetIterator(this.key); 762 while (iterator.hasNext()) 763 { 764 iterator.next(); 765 size++; 766 } 767 768 return size; 769 } 770 } 771 772 /** 773 * Returns the size of the internal map. Used for testing purposes only. 774 * 775 * @return The size of the internal map. 776 */ 777 int getMapSize() 778 { 779 return ditCacheMap.size(); 780 } 781}