001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2010 Sun Microsystems, Inc. 015 * Portions Copyright 2011-2016 ForgeRock AS. 016 */ 017package org.opends.server.api; 018 019import java.util.AbstractMap; 020import java.util.AbstractSet; 021import java.util.Collection; 022import java.util.HashMap; 023import java.util.Iterator; 024import java.util.Map; 025import java.util.NoSuchElementException; 026import java.util.Set; 027 028import org.forgerock.opendj.ldap.DN; 029 030/** 031 * The DITCacheMap class implements custom Map for structural storage of 032 * arbitrary objects in Directory Information Tree (DIT) like structure. 033 * <p> 034 * This Map intended usage is for caching various server objects which can be 035 * subject to subtree operations like retrieval or removal of all objects under 036 * a specific DN. While using a regular Map it would require the entire Map 037 * iteration to achieve, this Map implementation maintains such internal 038 * structure that subtree operations are more efficient and do not require 039 * iterations over the entire map, instead additional subtree operations methods 040 * are provided by this Map to do just that. 041 * <p> 042 * API wise it behaves exactly like a regular Map implementation except for 043 * providing additional subtree methods. All required linkage and structuring is 044 * performed within this Map implementation itself and not exposed via the API 045 * in any way. For example, putting these key/value pairs: 046 * 047 * <pre> 048 * cn=Object1,ou=Objects,dc=example,dc=com : object1 049 * cn=Object2,ou=Objects,dc=example,dc=com : object2 050 * cn=Object3,ou=Objects,dc=example,dc=com : object3 051 * </pre> 052 * 053 * then invoking a subtree method on this Map with any of these keys: 054 * 055 * <pre> 056 * ou=Objects,dc=example,dc=com 057 * dc=example,dc=com 058 * dc=com 059 * </pre> 060 * 061 * would bring all three objects previously stored in this map into subtree 062 * operation scope. Standard Map API methods can only work with the objects 063 * previously stored in this map explicitly. 064 * <p> 065 * Note that this Map implementation is not synchronized. 066 * 067 * @param <T> 068 * arbitrary object type. 069 */ 070public final class DITCacheMap<T> extends AbstractMap<DN,T> 071{ 072 /** 073 * Node class for object storage and 074 * linking to any subordinate nodes. 075 * @param <T> arbitrary storage object. 076 */ 077 private static final class Node<T> 078 { 079 /** Node DN. */ 080 DN dn; 081 /** 082 * Storage object or null if this node exist 083 * only to support the DIT like structuring. 084 */ 085 T element; 086 /** Parent. */ 087 Node<T> parent; 088 /** First child. */ 089 Node<T> child; 090 /** Next sibling. */ 091 Node<T> next; 092 /** Previous sibling. */ 093 Node<T> previous; 094 095 /** {@inheritDoc} */ 096 @Override 097 public String toString() 098 { 099 if (element != null) 100 { 101 return "node(" + element + ")"; 102 } 103 else 104 { 105 return "glue"; 106 } 107 } 108 } 109 110 /** Map size reflecting only nodes containing non empty elements. */ 111 private int size; 112 113 /** Backing Map implementation. */ 114 private final Map<DN,Node<T>> ditCacheMap = new HashMap<>(); 115 116 /** Default constructor. */ 117 public DITCacheMap() 118 { 119 } 120 121 /** 122 * Constructs a new DITCacheMap from a given Map. 123 * @param m existing Map to construct new 124 * DITCacheMap from. 125 */ 126 public DITCacheMap(Map<? extends DN, ? extends T> m) 127 { 128 this.putAll(m); 129 } 130 131 /** {@inheritDoc} */ 132 @Override 133 public int size() 134 { 135 return size; 136 } 137 138 /** {@inheritDoc} */ 139 @Override 140 public boolean isEmpty() 141 { 142 return ditCacheMap.isEmpty(); 143 } 144 145 /** {@inheritDoc} */ 146 @Override 147 public boolean containsKey(Object key) 148 { 149 return get(key) != null; 150 } 151 152 /** {@inheritDoc} */ 153 @Override 154 public boolean containsValue(Object value) 155 { 156 for (Node<T> node : ditCacheMap.values()) 157 { 158 if (node.element != null && node.element.equals(value)) 159 { 160 return true; 161 } 162 } 163 return false; 164 } 165 166 /** {@inheritDoc} */ 167 @Override 168 public T get(Object key) 169 { 170 Node<T> node = ditCacheMap.get(key); 171 return node != null ? node.element : null; 172 } 173 174 /** 175 * Returns a set of stored objects subordinate to subtree DN. 176 * @param key subtree DN. 177 * @return collection of stored objects subordinate to subtree DN. 178 */ 179 public Collection<T> getSubtree(DN key) 180 { 181 return new DITSubtreeSet(key); 182 } 183 184 /** {@inheritDoc} */ 185 @Override 186 public T put(DN key, T value) 187 { 188 final Node<T> existingNode = ditCacheMap.get(key); 189 if (existingNode != null) 190 { 191 final T returnValue = existingNode.element; 192 existingNode.element = value; 193 return returnValue; 194 } 195 196 Node<T> node = new Node<>(); 197 node.dn = key; 198 node.element = value; 199 node.parent = null; 200 node.child = null; 201 node.next = null; 202 node.previous = null; 203 204 ditCacheMap.put(key, node); 205 size++; 206 207 // Update parent hierarchy. 208 for (DN parentDN = key.parent(); 209 parentDN != null; 210 parentDN = parentDN.parent()) 211 { 212 final Node<T> parentNode = ditCacheMap.get(parentDN); 213 214 if (parentNode == null) 215 { 216 // Add glue node. 217 final Node<T> newParentNode = new Node<>(); 218 newParentNode.dn = parentDN; 219 newParentNode.element = null; 220 newParentNode.parent = null; 221 newParentNode.child = node; 222 newParentNode.next = null; 223 newParentNode.previous = null; 224 ditCacheMap.put(parentDN, newParentNode); 225 node.parent = newParentNode; 226 node = newParentNode; 227 } 228 else 229 { 230 if (parentNode.child != null) 231 { 232 Node<T> lastNode = parentNode.child; 233 while (lastNode.next != null) 234 { 235 lastNode = lastNode.next; 236 } 237 node.previous = lastNode; 238 lastNode.next = node; 239 } 240 else 241 { 242 parentNode.child = node; 243 } 244 node.parent = parentNode; 245 break; 246 } 247 } 248 return null; 249 } 250 251 /** {@inheritDoc} */ 252 @Override 253 public T remove(Object key) 254 { 255 final DN dn = (DN) key; 256 final Node<T> node = ditCacheMap.get(dn); 257 if (node == null) 258 { 259 return null; 260 } 261 262 final T returnValue = node.element; 263 if (returnValue == null) 264 { 265 return null; 266 } 267 268 // Remove element from DIT. 269 size--; 270 node.element = null; 271 272 if (node.child == null) 273 { 274 // This node is now glue, so remove it completely and fix up 275 // any other nodes which reference it. 276 ditCacheMap.remove(dn); 277 fixNodeReferences(node); 278 } 279 280 return returnValue; 281 } 282 283 284 285 /** 286 * Remove references to a node after it has been removed. 287 * @param node The node which has just been removed. 288 */ 289 private void fixNodeReferences(Node<T> node) 290 { 291 while (true) 292 { 293 // Fix siblings. 294 final Node<T> nextNode = node.next; 295 final Node<T> previousNode = node.previous; 296 297 if (nextNode != null) 298 { 299 nextNode.previous = previousNode; 300 } 301 302 if (previousNode != null) 303 { 304 previousNode.next = nextNode; 305 } 306 307 // Fix parent, if it exists. 308 final Node<T> parentNode = node.parent; 309 if (parentNode == null) 310 { 311 // Reached the top of the DIT, so no parents to fix. 312 break; 313 } 314 315 if (parentNode.child != node) 316 { 317 // The parent references another sibling and does not need fixing. 318 break; 319 } 320 321 if (nextNode != null) 322 { 323 // Update child pointer and return. 324 parentNode.child = nextNode; 325 break; 326 } 327 328 if (parentNode.element != null) 329 { 330 // Parent node is still needed as it contains data. 331 break; 332 } 333 334 // The parent node is glue so remove it. 335 ditCacheMap.remove(parentNode.dn); 336 337 // Smash pointers. 338 node.parent = null; 339 node.previous = null; 340 node.next = null; 341 342 // Continue up tree. 343 node = parentNode; 344 } 345 } 346 347 348 349 /** 350 * Returns {@code true} if there are stored objects subordinate to subtree DN. 351 * @param key subtree DN. 352 * @return {@code true} if there are stored objects subordinate to subtree DN. 353 */ 354 public boolean containsSubtree(DN key) 355 { 356 return ditCacheMap.containsKey(key); 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 @Override 668 public boolean hasNext() 669 { 670 if (rootNode != null) 671 { 672 if (node == rootNode && rootNode.element != null) 673 { 674 return true; 675 } 676 while (node != null) 677 { 678 if (node.element != null) 679 { 680 return true; 681 } 682 if (node.child != null) 683 { 684 node = node.child; 685 } 686 else 687 { 688 while (node.next == null && node.parent != rootNode) 689 { 690 node = node.parent; 691 } 692 node = node.next; 693 } 694 } 695 } 696 return false; 697 } 698 699 @Override 700 public T next() 701 { 702 T element = null; 703 704 if (rootNode != null) 705 { 706 if (node == rootNode) 707 { 708 node = rootNode.child; 709 if (rootNode.element != null) 710 { 711 return rootNode.element; 712 } 713 } 714 while (node != null) 715 { 716 element = node.element; 717 if (node.child != null) 718 { 719 node = node.child; 720 } 721 else 722 { 723 while (node.next == null && node.parent != rootNode) 724 { 725 node = node.parent; 726 } 727 node = node.next; 728 } 729 if (element != null) 730 { 731 return element; 732 } 733 } 734 } 735 throw new NoSuchElementException(); 736 } 737 738 @Override 739 public void remove() 740 { 741 throw new UnsupportedOperationException(); 742 } 743 } 744 745 @Override 746 public Iterator<T> iterator() 747 { 748 return new SubtreeSetIterator(); 749 } 750 751 @Override 752 public boolean isEmpty() 753 { 754 return !(new SubtreeSetIterator(this.key).hasNext()); 755 } 756 757 @Override 758 public int size() 759 { 760 int size = 0; 761 762 Iterator<T> iterator = new SubtreeSetIterator(this.key); 763 while (iterator.hasNext()) 764 { 765 iterator.next(); 766 size++; 767 } 768 769 return size; 770 } 771 } 772 773 /** 774 * Returns the size of the internal map. Used for testing purposes only. 775 * 776 * @return The size of the internal map. 777 */ 778 int getMapSize() 779 { 780 return ditCacheMap.size(); 781 } 782}