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 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2007-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.core; 028 029 030 031 import java.lang.reflect.Method; 032 import java.util.*; 033 import java.util.concurrent.ConcurrentHashMap; 034 035 import org.opends.messages.Message; 036 import org.opends.server.admin.ClassPropertyDefinition; 037 import org.opends.server.admin.server.ConfigurationAddListener; 038 import org.opends.server.admin.server.ConfigurationChangeListener; 039 import org.opends.server.admin.server.ConfigurationDeleteListener; 040 import org.opends.server.admin.server.ServerManagementContext; 041 import org.opends.server.admin.std.meta.GroupImplementationCfgDefn; 042 import org.opends.server.admin.std.server.GroupImplementationCfg; 043 import org.opends.server.admin.std.server.RootCfg; 044 import org.opends.server.api.Backend; 045 import org.opends.server.api.BackendInitializationListener; 046 import org.opends.server.api.ChangeNotificationListener; 047 import org.opends.server.api.Group; 048 import org.opends.server.config.ConfigException; 049 import org.opends.server.loggers.debug.DebugTracer; 050 import org.opends.server.protocols.internal.InternalClientConnection; 051 import org.opends.server.protocols.internal.InternalSearchOperation; 052 import org.opends.server.types.ConfigChangeResult; 053 import org.opends.server.types.Control; 054 import org.opends.server.types.DebugLogLevel; 055 import org.opends.server.types.DereferencePolicy; 056 import org.opends.server.types.DN; 057 import org.opends.server.types.Entry; 058 import org.opends.server.types.InitializationException; 059 import org.opends.server.types.ResultCode; 060 import org.opends.server.types.SearchResultEntry; 061 import org.opends.server.types.SearchScope; 062 import org.opends.server.types.SearchFilter; 063 import org.opends.server.types.operation.PostResponseAddOperation; 064 import org.opends.server.types.operation.PostResponseDeleteOperation; 065 import org.opends.server.types.operation.PostResponseModifyOperation; 066 import org.opends.server.types.operation.PostResponseModifyDNOperation; 067 import org.opends.server.workflowelement.localbackend. 068 LocalBackendSearchOperation; 069 070 import static org.opends.messages.ConfigMessages.*; 071 import static org.opends.messages.CoreMessages.*; 072 import static org.opends.server.loggers.debug.DebugLogger.*; 073 import static org.opends.server.loggers.ErrorLogger.*; 074 import static org.opends.server.util.ServerConstants.*; 075 import static org.opends.server.util.StaticUtils.*; 076 077 078 079 /** 080 * This class provides a mechanism for interacting with all groups defined in 081 * the Directory Server. It will handle all necessary processing at server 082 * startup to identify and load all group implementations, as well as to find 083 * all group instances within the server. 084 * <BR><BR> 085 * FIXME: At the present time, it assumes that all of the necessary 086 * information about all of the groups defined in the server can be held in 087 * memory. If it is determined that this approach is not workable in all cases, 088 * then we will need an alternate strategy. 089 */ 090 public class GroupManager 091 implements ConfigurationChangeListener<GroupImplementationCfg>, 092 ConfigurationAddListener<GroupImplementationCfg>, 093 ConfigurationDeleteListener<GroupImplementationCfg>, 094 BackendInitializationListener, 095 ChangeNotificationListener 096 { 097 /** 098 * The tracer object for the debug logger. 099 */ 100 private static final DebugTracer TRACER = getTracer(); 101 102 103 //Used by group instances to determine if new groups have been 104 //registered or groups deleted. 105 private long refreshToken=0; 106 107 108 // A mapping between the DNs of the config entries and the associated 109 // group implementations. 110 private ConcurrentHashMap<DN,Group> groupImplementations; 111 112 // A mapping between the DNs of all group entries and the corresponding 113 // group instances. 114 private ConcurrentHashMap<DN,Group> groupInstances; 115 116 117 118 /** 119 * Creates a new instance of this group manager. 120 */ 121 public GroupManager() 122 { 123 groupImplementations = new ConcurrentHashMap<DN,Group>(); 124 groupInstances = new ConcurrentHashMap<DN,Group>(); 125 126 DirectoryServer.registerBackendInitializationListener(this); 127 DirectoryServer.registerChangeNotificationListener(this); 128 } 129 130 131 132 /** 133 * Initializes all group implementations currently defined in the Directory 134 * Server configuration. This should only be called at Directory Server 135 * startup. 136 * 137 * @throws ConfigException If a configuration problem causes the group 138 * implementation initialization process to fail. 139 * 140 * @throws InitializationException If a problem occurs while initializing 141 * the group implementations that is not 142 * related to the server configuration. 143 */ 144 public void initializeGroupImplementations() 145 throws ConfigException, InitializationException 146 { 147 // Get the root configuration object. 148 ServerManagementContext managementContext = 149 ServerManagementContext.getInstance(); 150 RootCfg rootConfiguration = 151 managementContext.getRootConfiguration(); 152 153 154 // Register as an add and delete listener with the root configuration so we 155 // can be notified if any group implementation entries are added or removed. 156 rootConfiguration.addGroupImplementationAddListener(this); 157 rootConfiguration.addGroupImplementationDeleteListener(this); 158 159 160 //Initialize the existing group implementations. 161 for (String name : rootConfiguration.listGroupImplementations()) 162 { 163 GroupImplementationCfg groupConfiguration = 164 rootConfiguration.getGroupImplementation(name); 165 groupConfiguration.addChangeListener(this); 166 167 if (groupConfiguration.isEnabled()) 168 { 169 String className = groupConfiguration.getJavaClass(); 170 try 171 { 172 Group group = loadGroup(className, groupConfiguration, true); 173 groupImplementations.put(groupConfiguration.dn(), group); 174 } 175 catch (InitializationException ie) 176 { 177 logError(ie.getMessageObject()); 178 continue; 179 } 180 } 181 } 182 } 183 184 185 186 /** 187 * {@inheritDoc} 188 */ 189 public boolean isConfigurationAddAcceptable( 190 GroupImplementationCfg configuration, 191 List<Message> unacceptableReasons) 192 { 193 if (configuration.isEnabled()) 194 { 195 // Get the name of the class and make sure we can instantiate it as a 196 // group implementation. 197 String className = configuration.getJavaClass(); 198 try 199 { 200 loadGroup(className, configuration, false); 201 } 202 catch (InitializationException ie) 203 { 204 unacceptableReasons.add(ie.getMessageObject()); 205 return false; 206 } 207 } 208 209 // If we've gotten here, then it's fine. 210 return true; 211 } 212 213 214 215 /** 216 * {@inheritDoc} 217 */ 218 public ConfigChangeResult applyConfigurationAdd( 219 GroupImplementationCfg configuration) 220 { 221 ResultCode resultCode = ResultCode.SUCCESS; 222 boolean adminActionRequired = false; 223 ArrayList<Message> messages = new ArrayList<Message>(); 224 225 configuration.addChangeListener(this); 226 227 if (! configuration.isEnabled()) 228 { 229 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 230 } 231 232 Group group = null; 233 234 // Get the name of the class and make sure we can instantiate it as a group 235 // implementation. 236 String className = configuration.getJavaClass(); 237 try 238 { 239 group = loadGroup(className, configuration, true); 240 } 241 catch (InitializationException ie) 242 { 243 if (resultCode == ResultCode.SUCCESS) 244 { 245 resultCode = DirectoryServer.getServerErrorResultCode(); 246 } 247 248 messages.add(ie.getMessageObject()); 249 } 250 251 if (resultCode == ResultCode.SUCCESS) 252 { 253 groupImplementations.put(configuration.dn(), group); 254 } 255 256 // FIXME -- We need to make sure to find all groups of this type in the 257 // server before returning. 258 259 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 260 } 261 262 263 264 /** 265 * {@inheritDoc} 266 */ 267 public boolean isConfigurationDeleteAcceptable( 268 GroupImplementationCfg configuration, 269 List<Message> unacceptableReasons) 270 { 271 // FIXME -- We should try to perform some check to determine whether the 272 // group implementation is in use. 273 return true; 274 } 275 276 277 278 /** 279 * {@inheritDoc} 280 */ 281 public ConfigChangeResult applyConfigurationDelete( 282 GroupImplementationCfg configuration) 283 { 284 ResultCode resultCode = ResultCode.SUCCESS; 285 boolean adminActionRequired = false; 286 ArrayList<Message> messages = new ArrayList<Message>(); 287 288 Group group = groupImplementations.remove(configuration.dn()); 289 if (group != null) 290 { 291 Iterator<Group> iterator = groupInstances.values().iterator(); 292 while (iterator.hasNext()) 293 { 294 Group g = iterator.next(); 295 if (g.getClass().getName().equals(group.getClass().getName())) 296 { 297 iterator.remove(); 298 } 299 } 300 301 group.finalizeGroupImplementation(); 302 } 303 304 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 305 } 306 307 308 309 /** 310 * {@inheritDoc} 311 */ 312 public boolean isConfigurationChangeAcceptable( 313 GroupImplementationCfg configuration, 314 List<Message> unacceptableReasons) 315 { 316 if (configuration.isEnabled()) 317 { 318 // Get the name of the class and make sure we can instantiate it as a 319 // group implementation. 320 String className = configuration.getJavaClass(); 321 try 322 { 323 loadGroup(className, configuration, false); 324 } 325 catch (InitializationException ie) 326 { 327 unacceptableReasons.add(ie.getMessageObject()); 328 return false; 329 } 330 } 331 332 // If we've gotten here, then it's fine. 333 return true; 334 } 335 336 337 338 /** 339 * {@inheritDoc} 340 */ 341 public ConfigChangeResult applyConfigurationChange( 342 GroupImplementationCfg configuration) 343 { 344 ResultCode resultCode = ResultCode.SUCCESS; 345 boolean adminActionRequired = false; 346 ArrayList<Message> messages = new ArrayList<Message>(); 347 348 349 // Get the existing group implementation if it's already enabled. 350 Group existingGroup = groupImplementations.get(configuration.dn()); 351 352 353 // If the new configuration has the group implementation disabled, then 354 // disable it if it is enabled, or do nothing if it's already disabled. 355 if (! configuration.isEnabled()) 356 { 357 if (existingGroup != null) 358 { 359 Group group = groupImplementations.remove(configuration.dn()); 360 if (group != null) 361 { 362 Iterator<Group> iterator = groupInstances.values().iterator(); 363 while (iterator.hasNext()) 364 { 365 Group g = iterator.next(); 366 if (g.getClass().getName().equals(group.getClass().getName())) 367 { 368 iterator.remove(); 369 } 370 } 371 372 group.finalizeGroupImplementation(); 373 } 374 } 375 376 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 377 } 378 379 380 // Get the class for the group implementation. If the group is already 381 // enabled, then we shouldn't do anything with it although if the class has 382 // changed then we'll at least need to indicate that administrative action 383 // is required. If the group implementation is disabled, then instantiate 384 // the class and initialize and register it as a group implementation. 385 String className = configuration.getJavaClass(); 386 if (existingGroup != null) 387 { 388 if (! className.equals(existingGroup.getClass().getName())) 389 { 390 adminActionRequired = true; 391 } 392 393 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 394 } 395 396 Group group = null; 397 try 398 { 399 group = loadGroup(className, configuration, true); 400 } 401 catch (InitializationException ie) 402 { 403 if (resultCode == ResultCode.SUCCESS) 404 { 405 resultCode = DirectoryServer.getServerErrorResultCode(); 406 } 407 408 messages.add(ie.getMessageObject()); 409 } 410 411 if (resultCode == ResultCode.SUCCESS) 412 { 413 groupImplementations.put(configuration.dn(), group); 414 } 415 416 // FIXME -- We need to make sure to find all groups of this type in the 417 // server before returning. 418 419 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 420 } 421 422 423 424 /** 425 * Loads the specified class, instantiates it as a group implementation, and 426 * optionally initializes that instance. 427 * 428 * @param className The fully-qualified name of the group implementation 429 * class to load, instantiate, and initialize. 430 * @param configuration The configuration to use to initialize the group 431 * implementation. It must not be {@code null}. 432 * @param initialize Indicates whether the group implementation instance 433 * should be initialized. 434 * 435 * @return The possibly initialized group implementation. 436 * 437 * @throws InitializationException If a problem occurred while attempting to 438 * initialize the group implementation. 439 */ 440 private Group loadGroup(String className, 441 GroupImplementationCfg configuration, 442 boolean initialize) 443 throws InitializationException 444 { 445 try 446 { 447 GroupImplementationCfgDefn definition = 448 GroupImplementationCfgDefn.getInstance(); 449 ClassPropertyDefinition propertyDefinition = 450 definition.getJavaClassPropertyDefinition(); 451 Class<? extends Group> groupClass = 452 propertyDefinition.loadClass(className, Group.class); 453 Group group = groupClass.newInstance(); 454 455 if (initialize) 456 { 457 Method method = group.getClass() 458 .getMethod("initializeGroupImplementation", 459 configuration.configurationClass()); 460 method.invoke(group, configuration); 461 } 462 else 463 { 464 Method method = group.getClass().getMethod("isConfigurationAcceptable", 465 GroupImplementationCfg.class, 466 List.class); 467 468 List<Message> unacceptableReasons = new ArrayList<Message>(); 469 Boolean acceptable = (Boolean) method.invoke(group, configuration, 470 unacceptableReasons); 471 if (! acceptable) 472 { 473 StringBuilder buffer = new StringBuilder(); 474 if (! unacceptableReasons.isEmpty()) 475 { 476 Iterator<Message> iterator = unacceptableReasons.iterator(); 477 buffer.append(iterator.next()); 478 while (iterator.hasNext()) 479 { 480 buffer.append(". "); 481 buffer.append(iterator.next()); 482 } 483 } 484 485 Message message = ERR_CONFIG_GROUP_CONFIG_NOT_ACCEPTABLE.get( 486 String.valueOf(configuration.dn()), buffer.toString()); 487 throw new InitializationException(message); 488 } 489 } 490 491 return group; 492 } 493 catch (Exception e) 494 { 495 Message message = ERR_CONFIG_GROUP_INITIALIZATION_FAILED. 496 get(className, String.valueOf(configuration.dn()), 497 stackTraceToSingleLineString(e)); 498 throw new InitializationException(message, e); 499 } 500 } 501 502 503 504 /** 505 * Performs any cleanup work that may be needed when the server is shutting 506 * down. 507 */ 508 public void finalizeGroupManager() 509 { 510 deregisterAllGroups(); 511 512 for (Group groupImplementation : groupImplementations.values()) 513 { 514 groupImplementation.finalizeGroupImplementation(); 515 } 516 517 groupImplementations.clear(); 518 } 519 520 521 522 /** 523 * Retrieves an {@code Iterable} object that may be used to cursor across the 524 * group implementations defined in the server. 525 * 526 * @return An {@code Iterable} object that may be used to cursor across the 527 * group implementations defined in the server. 528 */ 529 public Iterable<Group> getGroupImplementations() 530 { 531 return groupImplementations.values(); 532 } 533 534 535 536 /** 537 * Retrieves an {@code Iterable} object that may be used to cursor across the 538 * group instances defined in the server. 539 * 540 * @return An {@code Iterable} object that may be used to cursor across the 541 * group instances defined in the server. 542 */ 543 public Iterable<Group> getGroupInstances() 544 { 545 return groupInstances.values(); 546 } 547 548 549 550 /** 551 * Retrieves the group instance defined in the entry with the specified DN. 552 * 553 * @param entryDN The DN of the entry containing the definition of the group 554 * instance to retrieve. 555 * 556 * @return The group instance defined in the entry with the specified DN, or 557 * {@code null} if no such group is currently defined. 558 */ 559 public Group getGroupInstance(DN entryDN) 560 { 561 Group group = groupInstances.get(entryDN); 562 if (group == null) 563 { 564 // FIXME -- Should we try to retrieve the corresponding entry and see if 565 // it is a group? 566 } 567 568 return group; 569 } 570 571 572 573 /** 574 * {@inheritDoc} In this case, the server will search the backend to find 575 * all group instances that it may contain and register them with this group 576 * manager. 577 */ 578 public void performBackendInitializationProcessing(Backend backend) 579 { 580 InternalClientConnection conn = 581 InternalClientConnection.getRootConnection(); 582 583 LinkedList<Control> requestControls = new LinkedList<Control>(); 584 requestControls.add(new Control(OID_INTERNAL_GROUP_MEMBERSHIP_UPDATE, 585 false)); 586 for (DN configEntryDN : groupImplementations.keySet()) 587 { 588 SearchFilter filter; 589 Group groupImplementation = groupImplementations.get(configEntryDN); 590 try 591 { 592 filter = groupImplementation.getGroupDefinitionFilter(); 593 if (! backend.isIndexed(filter)) 594 { 595 logError(WARN_GROUP_FILTER_NOT_INDEXED.get(String.valueOf(filter), 596 String.valueOf(configEntryDN), backend.getBackendID())); 597 } 598 } 599 catch (Exception e) 600 { 601 if (debugEnabled()) 602 { 603 TRACER.debugCaught(DebugLogLevel.ERROR, e); 604 } 605 606 // FIXME -- Is there anything that we need to do here? 607 continue; 608 } 609 610 611 for (DN baseDN : backend.getBaseDNs()) 612 { 613 try 614 { 615 if (! backend.entryExists(baseDN)) 616 { 617 continue; 618 } 619 } 620 catch (Exception e) 621 { 622 if (debugEnabled()) 623 { 624 TRACER.debugCaught(DebugLogLevel.ERROR, e); 625 } 626 627 // FIXME -- Is there anything that we need to do here? 628 continue; 629 } 630 631 632 InternalSearchOperation internalSearch = 633 new InternalSearchOperation(conn, conn.nextOperationID(), 634 conn.nextMessageID(), requestControls, 635 baseDN, 636 SearchScope.WHOLE_SUBTREE, 637 DereferencePolicy.NEVER_DEREF_ALIASES, 638 0, 0, false, filter, null, null); 639 LocalBackendSearchOperation localSearch = 640 new LocalBackendSearchOperation(internalSearch); 641 try 642 { 643 backend.search(localSearch); 644 } 645 catch (Exception e) 646 { 647 if (debugEnabled()) 648 { 649 TRACER.debugCaught(DebugLogLevel.ERROR, e); 650 } 651 652 // FIXME -- Is there anything that we need to do here? 653 continue; 654 } 655 656 for (SearchResultEntry entry : internalSearch.getSearchEntries()) 657 { 658 try 659 { 660 Group groupInstance = groupImplementation.newInstance(entry); 661 groupInstances.put(entry.getDN(), groupInstance); 662 refreshToken++; 663 } 664 catch (Exception e) 665 { 666 if (debugEnabled()) 667 { 668 TRACER.debugCaught(DebugLogLevel.ERROR, e); 669 } 670 671 // FIXME -- Handle this. 672 continue; 673 } 674 } 675 } 676 } 677 } 678 679 680 681 /** 682 * {@inheritDoc} In this case, the server will de-register all group 683 * instances associated with entries in the provided backend. 684 */ 685 public void performBackendFinalizationProcessing(Backend backend) 686 { 687 Iterator<Map.Entry<DN,Group>> iterator = 688 groupInstances.entrySet().iterator(); 689 while (iterator.hasNext()) 690 { 691 Map.Entry<DN,Group> mapEntry = iterator.next(); 692 DN groupEntryDN = mapEntry.getKey(); 693 if (backend.handlesEntry(groupEntryDN)) 694 { 695 iterator.remove(); 696 } 697 } 698 } 699 700 701 702 /** 703 * {@inheritDoc} In this case, each entry is checked to see if it contains 704 * a group definition, and if so it will be instantiated and registered with 705 * this group manager. 706 */ 707 public void handleAddOperation(PostResponseAddOperation addOperation, 708 Entry entry) 709 { 710 List<Control> requestControls = addOperation.getRequestControls(); 711 if (requestControls != null) 712 { 713 for (Control c : requestControls) 714 { 715 if (c.getOID().equals(OID_INTERNAL_GROUP_MEMBERSHIP_UPDATE)) 716 { 717 return; 718 } 719 } 720 } 721 synchronized (groupInstances) 722 { 723 createAndRegisterGroup(entry); 724 refreshToken++; 725 } 726 } 727 728 729 730 /** 731 * {@inheritDoc} In this case, if the entry is associated with a registered 732 * group instance, then that group instance will be deregistered. 733 */ 734 public void handleDeleteOperation(PostResponseDeleteOperation deleteOperation, 735 Entry entry) 736 { 737 List<Control> requestControls = deleteOperation.getRequestControls(); 738 if (requestControls != null) 739 { 740 for (Control c : requestControls) 741 { 742 if (c.getOID().equals(OID_INTERNAL_GROUP_MEMBERSHIP_UPDATE)) 743 { 744 return; 745 } 746 } 747 } 748 synchronized (groupInstances) 749 { 750 groupInstances.remove(entry.getDN()); 751 refreshToken++; 752 } 753 } 754 755 756 757 /** 758 * {@inheritDoc} In this case, if the entry is associated with a registered 759 * group instance, then that instance will be recreated from the contents of 760 * the provided entry and re-registered with the group manager. 761 */ 762 public void handleModifyOperation(PostResponseModifyOperation modifyOperation, 763 Entry oldEntry, Entry newEntry) 764 { 765 List<Control> requestControls = modifyOperation.getRequestControls(); 766 if (requestControls != null) 767 { 768 for (Control c : requestControls) 769 { 770 if (c.getOID().equals(OID_INTERNAL_GROUP_MEMBERSHIP_UPDATE)) 771 { 772 return; 773 } 774 } 775 } 776 777 778 if (groupInstances.containsKey(oldEntry.getDN())) 779 { 780 synchronized (groupInstances) 781 { 782 if (! oldEntry.getDN().equals(newEntry.getDN())) 783 { 784 // This should never happen, but check for it anyway. 785 groupInstances.remove(oldEntry.getDN()); 786 } 787 788 createAndRegisterGroup(newEntry); 789 refreshToken++; 790 } 791 } 792 } 793 794 795 796 /** 797 * {@inheritDoc} In this case, if the entry is associated with a registered 798 * group instance, then that instance will be recreated from the contents of 799 * the provided entry and re-registered with the group manager under the new 800 * DN, and the old instance will be deregistered. 801 */ 802 public void handleModifyDNOperation( 803 PostResponseModifyDNOperation modifyDNOperation, 804 Entry oldEntry, Entry newEntry) 805 { 806 List<Control> requestControls = modifyDNOperation.getRequestControls(); 807 if (requestControls != null) 808 { 809 for (Control c : requestControls) 810 { 811 if (c.getOID().equals(OID_INTERNAL_GROUP_MEMBERSHIP_UPDATE)) 812 { 813 return; 814 } 815 } 816 } 817 818 if (groupInstances.containsKey(oldEntry.getDN())) 819 { 820 synchronized (groupInstances) 821 { 822 createAndRegisterGroup(newEntry); 823 groupInstances.remove(oldEntry.getDN()); 824 refreshToken++; 825 } 826 } 827 } 828 829 830 831 /** 832 * Attempts to create a group instance from the provided entry, and if that is 833 * successful then register it with the server, overwriting any existing 834 * group instance that may be registered with the same DN. 835 * 836 * @param entry The entry containing the potential group definition. 837 */ 838 private void createAndRegisterGroup(Entry entry) 839 { 840 for (Group groupImplementation : groupImplementations.values()) 841 { 842 try 843 { 844 if (groupImplementation.isGroupDefinition(entry)) 845 { 846 Group groupInstance = groupImplementation.newInstance(entry); 847 groupInstances.put(entry.getDN(), groupInstance); 848 } 849 } 850 catch (Exception e) 851 { 852 if (debugEnabled()) 853 { 854 TRACER.debugCaught(DebugLogLevel.ERROR, e); 855 } 856 857 // FIXME -- Do we need to do anything else? 858 } 859 } 860 } 861 862 863 864 /** 865 * Removes all group instances that might happen to be registered with the 866 * group manager. This method is only intended for testing purposes and 867 * should not be called by any other code. 868 */ 869 void deregisterAllGroups() 870 { 871 groupInstances.clear(); 872 } 873 874 875 /** 876 * Compare the specified token against the current group manager 877 * token value. Can be used to reload cached group instances if there has 878 * been a group instance change. 879 * 880 * @param token The current token that the group class holds. 881 * 882 * @return {@code true} if the group class should reload its nested groups, 883 * or {@code false} if it shouldn't. 884 */ 885 public boolean hasInstancesChanged(long token) { 886 return token != this.refreshToken; 887 } 888 889 /** 890 * Return the current refresh token value. Can be used to 891 * reload cached group instances if there has been a group instance change. 892 * 893 * @return The current token value. 894 */ 895 public long refreshToken() { 896 return this.refreshToken; 897 } 898 } 899