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 2006-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2012-2016 ForgeRock AS. 016 */ 017package org.opends.server.backends; 018 019import static org.forgerock.util.Reject.*; 020import static org.opends.messages.BackendMessages.*; 021import static org.opends.messages.ConfigMessages.*; 022import static org.opends.server.config.ConfigConstants.*; 023import static org.opends.server.util.CollectionUtils.*; 024import static org.opends.server.util.ServerConstants.*; 025import static org.opends.server.util.StaticUtils.*; 026 027import java.io.IOException; 028import java.util.ArrayList; 029import java.util.Collection; 030import java.util.Collections; 031import java.util.HashMap; 032import java.util.LinkedHashMap; 033import java.util.List; 034import java.util.Map; 035import java.util.NavigableMap; 036import java.util.Set; 037import java.util.TreeMap; 038 039import org.forgerock.i18n.LocalizableMessage; 040import org.forgerock.i18n.slf4j.LocalizedLogger; 041import org.forgerock.opendj.config.server.ConfigChangeResult; 042import org.forgerock.opendj.config.server.ConfigException; 043import org.forgerock.opendj.config.server.ConfigurationChangeListener; 044import org.forgerock.opendj.ldap.AVA; 045import org.forgerock.opendj.ldap.ByteString; 046import org.forgerock.opendj.ldap.ConditionResult; 047import org.forgerock.opendj.ldap.DN; 048import org.forgerock.opendj.ldap.RDN; 049import org.forgerock.opendj.ldap.ResultCode; 050import org.forgerock.opendj.ldap.SearchScope; 051import org.forgerock.opendj.ldap.schema.AttributeType; 052import org.forgerock.opendj.ldap.schema.CoreSchema; 053import org.forgerock.opendj.ldap.schema.ObjectClass; 054import org.forgerock.opendj.server.config.server.MonitorBackendCfg; 055import org.forgerock.util.Reject; 056import org.opends.server.api.Backend; 057import org.opends.server.api.MonitorData; 058import org.opends.server.api.MonitorProvider; 059import org.opends.server.core.AddOperation; 060import org.opends.server.core.DeleteOperation; 061import org.opends.server.core.DirectoryServer; 062import org.opends.server.core.ModifyDNOperation; 063import org.opends.server.core.ModifyOperation; 064import org.opends.server.core.SearchOperation; 065import org.opends.server.core.ServerContext; 066import org.opends.server.types.Attribute; 067import org.opends.server.types.Attributes; 068import org.opends.server.types.BackupConfig; 069import org.opends.server.types.BackupDirectory; 070import org.opends.server.types.DirectoryException; 071import org.opends.server.types.Entry; 072import org.opends.server.types.IndexType; 073import org.opends.server.types.InitializationException; 074import org.opends.server.types.LDIFExportConfig; 075import org.opends.server.types.LDIFImportConfig; 076import org.opends.server.types.LDIFImportResult; 077import org.opends.server.types.RestoreConfig; 078import org.opends.server.types.SearchFilter; 079import org.opends.server.util.DynamicConstants; 080import org.opends.server.util.LDIFWriter; 081import org.opends.server.util.TimeThread; 082 083/** 084 * This class defines a backend to hold Directory Server monitor entries. It 085 * will not actually store anything, but upon request will retrieve the 086 * requested monitor and dynamically generate the associated entry. It will also 087 * construct a base monitor entry with some useful server-wide data. 088 */ 089public class MonitorBackend extends Backend<MonitorBackendCfg> implements 090 ConfigurationChangeListener<MonitorBackendCfg> 091{ 092 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 093 094 /** The set of user-defined attributes that will be included in the base monitor entry. */ 095 private ArrayList<Attribute> userDefinedAttributes; 096 /** The set of objectclasses that will be used in monitor entries. */ 097 private final HashMap<ObjectClass, String> monitorObjectClasses = new LinkedHashMap<>(2); 098 /** The DN of the configuration entry for this backend. */ 099 private DN configEntryDN; 100 /** The current configuration state. */ 101 private MonitorBackendCfg currentConfig; 102 /** The DN for the base monitor entry. */ 103 private DN baseMonitorDN; 104 /** The set of base DNs for this backend. */ 105 private Set<DN> baseDNs; 106 107 /** 108 * Creates a new backend with the provided information. All backend 109 * implementations must implement a default constructor that use 110 * <CODE>super()</CODE> to invoke this constructor. 111 */ 112 public MonitorBackend() 113 { 114 super(); 115 } 116 117 @Override 118 public void addEntry(final Entry entry, final AddOperation addOperation) 119 throws DirectoryException 120 { 121 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 122 ERR_BACKEND_ADD_NOT_SUPPORTED.get(entry.getName(), getBackendID())); 123 } 124 125 @Override 126 public ConfigChangeResult applyConfigurationChange( 127 final MonitorBackendCfg backendCfg) 128 { 129 final ConfigChangeResult ccr = new ConfigChangeResult(); 130 131 // Check to see if there is a new set of user-defined attributes. 132 final ArrayList<Attribute> userAttrs = new ArrayList<>(); 133 try 134 { 135 final Entry configEntry = DirectoryServer.getConfigEntry(configEntryDN); 136 addAllNonMonitorConfigAttributes(userAttrs, configEntry.getUserAttributes().values()); 137 addAllNonMonitorConfigAttributes(userAttrs, configEntry.getOperationalAttributes().values()); 138 } 139 catch (final Exception e) 140 { 141 logger.traceException(e); 142 143 ccr.addMessage(ERR_CONFIG_BACKEND_ERROR_INTERACTING_WITH_BACKEND_ENTRY.get( 144 configEntryDN, stackTraceToSingleLineString(e))); 145 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 146 } 147 148 userDefinedAttributes = userAttrs; 149 150 ccr.addMessage(INFO_MONITOR_USING_NEW_USER_ATTRS.get()); 151 152 currentConfig = backendCfg; 153 return ccr; 154 } 155 156 private void addAllNonMonitorConfigAttributes(final List<Attribute> userAttrs, Collection<List<Attribute>> attrbutes) 157 { 158 for (final List<Attribute> attrs : attrbutes) 159 { 160 for (final Attribute a : attrs) 161 { 162 if (!isMonitorConfigAttribute(a)) 163 { 164 userAttrs.add(a); 165 } 166 } 167 } 168 } 169 170 @Override 171 public void configureBackend(final MonitorBackendCfg config, ServerContext serverContext) 172 throws ConfigException 173 { 174 Reject.ifNull(config); 175 176 final MonitorBackendCfg cfg = config; 177 final Entry configEntry = DirectoryServer.getConfigEntry(cfg.dn()); 178 179 // Make sure that a configuration entry was provided. If not, then we will 180 // not be able to complete initialization. 181 if (configEntry == null) 182 { 183 final LocalizableMessage message = ERR_MONITOR_CONFIG_ENTRY_NULL.get(); 184 throw new ConfigException(message); 185 } 186 187 configEntryDN = configEntry.getName(); 188 189 // Get the set of user-defined attributes for the configuration entry. Any 190 // attributes that we don't recognize will be included directly in the base 191 // monitor entry. 192 userDefinedAttributes = new ArrayList<>(); 193 addAll(userDefinedAttributes, configEntry.getUserAttributes().values()); 194 addAll(userDefinedAttributes, configEntry.getOperationalAttributes().values()); 195 196 // Construct the set of objectclasses to include in the base monitor entry. 197 monitorObjectClasses.put(CoreSchema.getTopObjectClass(), OC_TOP); 198 monitorObjectClasses.put(DirectoryServer.getSchema().getObjectClass(OC_MONITOR_ENTRY), OC_MONITOR_ENTRY); 199 200 // Create the set of base DNs that we will handle. In this case, it's just 201 // the DN of the base monitor entry. 202 try 203 { 204 baseMonitorDN = DN.valueOf(DN_MONITOR_ROOT); 205 } 206 catch (final Exception e) 207 { 208 logger.traceException(e); 209 210 final LocalizableMessage message = ERR_MONITOR_CANNOT_DECODE_MONITOR_ROOT_DN 211 .get(getExceptionMessage(e)); 212 throw new ConfigException(message, e); 213 } 214 215 this.baseDNs = Collections.singleton(baseMonitorDN); 216 217 currentConfig = cfg; 218 } 219 220 private void addAll(ArrayList<Attribute> attributes, Collection<List<Attribute>> attributesToAdd) 221 { 222 addAllNonMonitorConfigAttributes(attributes, attributesToAdd); 223 } 224 225 @Override 226 public void createBackup(final BackupConfig backupConfig) 227 throws DirectoryException 228 { 229 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 230 ERR_BACKEND_BACKUP_AND_RESTORE_NOT_SUPPORTED.get(getBackendID())); 231 } 232 233 @Override 234 public void deleteEntry(final DN entryDN, 235 final DeleteOperation deleteOperation) throws DirectoryException 236 { 237 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 238 ERR_BACKEND_DELETE_NOT_SUPPORTED.get(entryDN, getBackendID())); 239 } 240 241 @Override 242 public boolean entryExists(final DN entryDN) throws DirectoryException 243 { 244 return getDIT().containsKey(entryDN); 245 } 246 247 @Override 248 public void exportLDIF(final LDIFExportConfig exportConfig) 249 throws DirectoryException 250 { 251 // TODO export-ldif reports nonsense for upTime etc. 252 try (LDIFWriter ldifWriter = newLDIFWriter(exportConfig)) 253 { 254 // Write the base monitor entry to the LDIF. 255 try 256 { 257 ldifWriter.writeEntry(getBaseMonitorEntry()); 258 } 259 catch (final Exception e) 260 { 261 logger.traceException(e); 262 final LocalizableMessage message = ERR_MONITOR_UNABLE_TO_EXPORT_BASE.get(stackTraceToSingleLineString(e)); 263 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message); 264 } 265 266 // Get all the monitor providers, convert them to entries, and write them to LDIF. 267 for (final MonitorProvider<?> monitorProvider : DirectoryServer.getMonitorProviders().values()) 268 { 269 try 270 { 271 // TODO implementation of export is incomplete 272 } 273 catch (final Exception e) 274 { 275 logger.traceException(e); 276 final LocalizableMessage message = 277 ERR_MONITOR_UNABLE_TO_EXPORT_PROVIDER_ENTRY.get(monitorProvider.getMonitorInstanceName(), 278 stackTraceToSingleLineString(e)); 279 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message); 280 } 281 } 282 } 283 catch (IOException ignoreOnClose) 284 { 285 logger.traceException(ignoreOnClose); 286 } 287 } 288 289 private LDIFWriter newLDIFWriter(final LDIFExportConfig exportConfig) throws DirectoryException 290 { 291 try 292 { 293 return new LDIFWriter(exportConfig); 294 } 295 catch (final Exception e) 296 { 297 logger.traceException(e); 298 299 final LocalizableMessage message = ERR_ROOTDSE_UNABLE_TO_CREATE_LDIF_WRITER.get(stackTraceToSingleLineString(e)); 300 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message); 301 } 302 } 303 304 @Override 305 public void closeBackend() 306 { 307 currentConfig.removeMonitorChangeListener(this); 308 try 309 { 310 DirectoryServer.deregisterBaseDN(baseMonitorDN); 311 } 312 catch (final Exception e) 313 { 314 logger.traceException(e); 315 } 316 } 317 318 @Override 319 public Set<DN> getBaseDNs() 320 { 321 return baseDNs; 322 } 323 324 @Override 325 public Entry getEntry(final DN entryDN) throws DirectoryException 326 { 327 // If the requested entry was null, then throw an exception. 328 if (entryDN == null) 329 { 330 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 331 ERR_BACKEND_GET_ENTRY_NULL.get(getBackendID())); 332 } 333 334 // If the requested entry was the monitor base entry, then retrieve it 335 // without constructing the DIT. 336 if (entryDN.equals(baseMonitorDN)) 337 { 338 return getBaseMonitorEntry(); 339 } 340 341 // From now on we'll need the DIT. 342 final Map<DN, MonitorProvider<?>> dit = getDIT(); 343 if (!dit.containsKey(entryDN)) 344 { 345 throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, 346 ERR_MONITOR_INVALID_BASE.get(entryDN, baseMonitorDN)); 347 } 348 349 // The DN is associated with a valid monitor/glue entry. 350 return getEntry(entryDN, dit); 351 } 352 353 @Override 354 public long getEntryCount() 355 { 356 return getDIT().size(); 357 } 358 359 @Override 360 public Set<String> getSupportedControls() 361 { 362 return Collections.emptySet(); 363 } 364 365 @Override 366 public Set<String> getSupportedFeatures() 367 { 368 return Collections.emptySet(); 369 } 370 371 @Override 372 public ConditionResult hasSubordinates(final DN entryDN) 373 throws DirectoryException 374 { 375 final NavigableMap<DN, MonitorProvider<?>> dit = getDIT(); 376 if (dit.containsKey(entryDN)) 377 { 378 final DN nextDN = dit.higherKey(entryDN); 379 return ConditionResult.valueOf(nextDN != null && nextDN.isSubordinateOrEqualTo(entryDN)); 380 } 381 return ConditionResult.UNDEFINED; 382 } 383 384 @Override 385 public LDIFImportResult importLDIF(final LDIFImportConfig importConfig, ServerContext serverContext) 386 throws DirectoryException 387 { 388 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 389 ERR_BACKEND_IMPORT_NOT_SUPPORTED.get(getBackendID())); 390 } 391 392 @Override 393 public void openBackend() throws ConfigException, InitializationException 394 { 395 // Register with the Directory Server as a configurable component. 396 currentConfig.addMonitorChangeListener(this); 397 398 // Register the monitor base as a private suffix. 399 try 400 { 401 DirectoryServer.registerBaseDN(baseMonitorDN, this, true); 402 } 403 catch (final Exception e) 404 { 405 logger.traceException(e); 406 407 final LocalizableMessage message = ERR_BACKEND_CANNOT_REGISTER_BASEDN.get( 408 baseMonitorDN, getExceptionMessage(e)); 409 throw new InitializationException(message, e); 410 } 411 } 412 413 @Override 414 public boolean isConfigurationChangeAcceptable( 415 final MonitorBackendCfg backendCfg, 416 final List<LocalizableMessage> unacceptableReasons) 417 { 418 // We'll pretty much accept anything here as long as it isn't one of our 419 // private attributes. 420 return true; 421 } 422 423 @Override 424 public boolean isIndexed(final AttributeType attributeType, 425 final IndexType indexType) 426 { 427 // All searches in this backend will always be considered indexed. 428 return true; 429 } 430 431 @Override 432 public long getNumberOfEntriesInBaseDN(final DN baseDN) throws DirectoryException { 433 checkNotNull(baseDN, "baseDN must not be null"); 434 return getNumberOfSubordinates(baseDN, true) + 1; 435 } 436 437 @Override 438 public long getNumberOfChildren(final DN parentDN) throws DirectoryException { 439 checkNotNull(parentDN, "parentDN must not be null"); 440 return getNumberOfSubordinates(parentDN, false); 441 } 442 443 private long getNumberOfSubordinates(final DN entryDN, final boolean includeSubtree) throws DirectoryException 444 { 445 final NavigableMap<DN, MonitorProvider<?>> dit = getDIT(); 446 if (!dit.containsKey(entryDN)) 447 { 448 return -1L; 449 } 450 long count = 0; 451 final int childDNSize = entryDN.size() + 1; 452 for (final DN dn : dit.tailMap(entryDN, false).navigableKeySet()) 453 { 454 if (!dn.isSubordinateOrEqualTo(entryDN)) 455 { 456 break; 457 } 458 else if (includeSubtree || dn.size() == childDNSize) 459 { 460 count++; 461 } 462 } 463 return count; 464 } 465 466 @Override 467 public void removeBackup(final BackupDirectory backupDirectory, 468 final String backupID) throws DirectoryException 469 { 470 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 471 ERR_BACKEND_BACKUP_AND_RESTORE_NOT_SUPPORTED.get(getBackendID())); 472 } 473 474 @Override 475 public void renameEntry(final DN currentDN, final Entry entry, 476 final ModifyDNOperation modifyDNOperation) throws DirectoryException 477 { 478 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 479 ERR_BACKEND_MODIFY_DN_NOT_SUPPORTED.get(currentDN, getBackendID())); 480 } 481 482 @Override 483 public void replaceEntry(final Entry oldEntry, final Entry newEntry, 484 final ModifyOperation modifyOperation) throws DirectoryException 485 { 486 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 487 ERR_MONITOR_MODIFY_NOT_SUPPORTED.get(newEntry.getName(), configEntryDN)); 488 } 489 490 @Override 491 public void restoreBackup(final RestoreConfig restoreConfig) 492 throws DirectoryException 493 { 494 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 495 ERR_BACKEND_BACKUP_AND_RESTORE_NOT_SUPPORTED.get(getBackendID())); 496 } 497 498 @Override 499 public void search(final SearchOperation searchOperation) 500 throws DirectoryException 501 { 502 // Get the base DN, scope, and filter for the search. 503 final DN baseDN = searchOperation.getBaseDN(); 504 final SearchScope scope = searchOperation.getScope(); 505 final SearchFilter filter = searchOperation.getFilter(); 506 507 // Compute the current monitor DIT. 508 final NavigableMap<DN, MonitorProvider<?>> dit = getDIT(); 509 510 // Resolve the base entry and return no such object if it does not exist. 511 if (!dit.containsKey(baseDN)) 512 { 513 // Not found, so find the nearest match. 514 DN matchedDN = baseDN.parent(); 515 while (matchedDN != null) 516 { 517 if (dit.containsKey(matchedDN)) 518 { 519 break; 520 } 521 matchedDN = matchedDN.parent(); 522 } 523 final LocalizableMessage message = ERR_BACKEND_ENTRY_DOESNT_EXIST.get(baseDN, getBackendID()); 524 throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message, 525 matchedDN, null); 526 } 527 528 // Walk through all entries and send the ones that match. 529 for (final Map.Entry<DN, MonitorProvider<?>> e : dit.tailMap(baseDN).entrySet()) 530 { 531 final DN dn = e.getKey(); 532 if (dn.isInScopeOf(baseDN, scope)) 533 { 534 final Entry entry = getEntry(dn, dit); 535 if (filter.matchesEntry(entry)) 536 { 537 searchOperation.returnEntry(entry, null); 538 } 539 } 540 else if (scope == SearchScope.BASE_OBJECT || !dn.isSubordinateOrEqualTo(baseDN)) 541 { 542 // No more entries will be in scope. 543 break; 544 } 545 } 546 } 547 548 @Override 549 public boolean supports(BackendOperation backendOperation) 550 { 551 // We can export all the monitor entries as a point-in-time snapshot. 552 // TODO implementation of export is incomplete 553 // TODO export-ldif reports nonsense for upTime etc. 554 return false; 555 } 556 557 /** 558 * Retrieves the base monitor entry for the Directory Server. 559 * 560 * @return The base monitor entry for the Directory Server. 561 */ 562 private Entry getBaseMonitorEntry() 563 { 564 final ObjectClass extensibleObjectOC = CoreSchema.getExtensibleObjectObjectClass(); 565 final HashMap<ObjectClass, String> monitorClasses = newObjectClasses(extensibleObjectOC, OC_EXTENSIBLE_OBJECT); 566 567 final HashMap<AttributeType, List<Attribute>> monitorUserAttrs = new LinkedHashMap<>(); 568 final HashMap<AttributeType, List<Attribute>> monitorOperationalAttrs = new LinkedHashMap<>(); 569 570 put(monitorUserAttrs, Attributes.create(ATTR_COMMON_NAME, "monitor")); 571 put(monitorUserAttrs, Attributes.create(ATTR_PRODUCT_NAME, DynamicConstants.PRODUCT_NAME)); 572 put(monitorUserAttrs, Attributes.create(ATTR_VENDOR_NAME, SERVER_VENDOR_NAME)); 573 put(monitorUserAttrs, Attributes.create(ATTR_VENDOR_VERSION, DirectoryServer.getVersionString())); 574 put(monitorUserAttrs, Attributes.create(ATTR_START_TIME, DirectoryServer.getStartTimeUTC())); 575 put(monitorUserAttrs, Attributes.create(ATTR_CURRENT_TIME, TimeThread.getGMTTime())); 576 put(monitorUserAttrs, Attributes.create(ATTR_UP_TIME, getHumanReadableUpTime())); 577 578 // Add the number of connections currently established. 579 final long currentConns = DirectoryServer.getCurrentConnections(); 580 put(monitorUserAttrs, Attributes.create(ATTR_CURRENT_CONNS, String.valueOf(currentConns))); 581 582 // Add the maximum number of connections established at one time. 583 final long maxConns = DirectoryServer.getMaxConnections(); 584 put(monitorUserAttrs, Attributes.create(ATTR_MAX_CONNS, String.valueOf(maxConns))); 585 586 // Add the total number of connections the server has accepted. 587 final long totalConns = DirectoryServer.getTotalConnections(); 588 put(monitorUserAttrs, Attributes.create(ATTR_TOTAL_CONNS, String.valueOf(totalConns))); 589 590 // Add all the user-defined attributes. 591 for (final Attribute a : userDefinedAttributes) 592 { 593 final AttributeType type = a.getAttributeDescription().getAttributeType(); 594 595 final HashMap<AttributeType, List<Attribute>> attrsMap = 596 type.isOperational() ? monitorOperationalAttrs : monitorUserAttrs; 597 List<Attribute> attrs = attrsMap.get(type); 598 if (attrs == null) 599 { 600 attrs = new ArrayList<>(); 601 attrsMap.put(type, attrs); 602 } 603 attrs.add(a); 604 } 605 606 return newEntry(baseMonitorDN, monitorClasses, monitorUserAttrs, monitorOperationalAttrs); 607 } 608 609 private String getHumanReadableUpTime() 610 { 611 long upSeconds = (System.currentTimeMillis() - DirectoryServer.getStartTime()) / 1000; 612 final long upDays = upSeconds / 86400; 613 upSeconds %= 86400; 614 final long upHours = upSeconds / 3600; 615 upSeconds %= 3600; 616 final long upMinutes = upSeconds / 60; 617 upSeconds %= 60; 618 return INFO_MONITOR_UPTIME.get(upDays, upHours, upMinutes, upSeconds).toString(); 619 } 620 621 private void put(final HashMap<AttributeType, List<Attribute>> attrsMap, final Attribute attr) 622 { 623 attrsMap.put(attr.getAttributeDescription().getAttributeType(), newArrayList(attr)); 624 } 625 626 /** 627 * Retrieves the branch monitor entry for the Directory Server. 628 * 629 * @param dn 630 * to get. 631 * @return The branch monitor entry for the Directory Server. 632 */ 633 private Entry getBranchMonitorEntry(final DN dn) 634 { 635 final ObjectClass monitorOC = DirectoryServer.getSchema().getObjectClass(OC_MONITOR_BRANCH); 636 final HashMap<ObjectClass, String> monitorClasses = newObjectClasses(monitorOC, OC_MONITOR_BRANCH); 637 638 final HashMap<AttributeType, List<Attribute>> monitorUserAttrs = new LinkedHashMap<>(); 639 640 final RDN rdn = dn.rdn(); 641 if (rdn != null) 642 { 643 for (AVA ava : rdn) 644 { 645 final AttributeType attributeType = ava.getAttributeType(); 646 final ByteString value = ava.getAttributeValue(); 647 monitorUserAttrs.put(attributeType, Attributes.createAsList(attributeType, value)); 648 } 649 } 650 651 return newEntry(dn, monitorClasses, monitorUserAttrs, null); 652 } 653 654 /** 655 * Returns a map containing records for each DN in the monitor backend's DIT. 656 * Each record maps the entry DN to the associated monitor provider, or 657 * {@code null} if the entry is a glue (branch) entry. 658 * 659 * @return A map containing records for each DN in the monitor backend's DIT. 660 */ 661 private NavigableMap<DN, MonitorProvider<?>> getDIT() 662 { 663 final NavigableMap<DN, MonitorProvider<?>> dit = new TreeMap<>(); 664 for (final MonitorProvider<?> monitorProvider : DirectoryServer.getMonitorProviders().values()) 665 { 666 DN dn = DirectoryServer.getMonitorProviderDN(monitorProvider); 667 dit.put(dn, monitorProvider); 668 669 // Added glue records. 670 for (dn = dn.parent(); dn != null; dn = dn.parent()) 671 { 672 if (dit.containsKey(dn)) 673 { 674 break; 675 } 676 dit.put(dn, null); 677 } 678 } 679 return dit; 680 } 681 682 /** 683 * Creates the monitor entry having the specified DN. 684 * 685 * @param entryDN 686 * The name of the monitor entry. 687 * @param dit 688 * The monitor DIT. 689 * @return Returns the monitor entry having the specified DN. 690 */ 691 private Entry getEntry(final DN entryDN, 692 final Map<DN, MonitorProvider<?>> dit) 693 { 694 // Get the monitor provider. 695 final MonitorProvider<?> monitorProvider = dit.get(entryDN); 696 if (monitorProvider != null) 697 { 698 return getMonitorEntry(entryDN, monitorProvider); 699 } 700 else if (entryDN.equals(baseMonitorDN)) 701 { 702 // The monitor base entry needs special treatment. 703 return getBaseMonitorEntry(); 704 } 705 else 706 { 707 // Create a generic glue branch entry. 708 return getBranchMonitorEntry(entryDN); 709 } 710 } 711 712 /** 713 * Generates and returns a monitor entry based on the contents of the provided 714 * monitor provider. 715 * 716 * @param entryDN 717 * The DN to use for the entry. 718 * @param monitorProvider 719 * The monitor provider to use to obtain the information for the 720 * entry. 721 * @return The monitor entry generated from the information in the provided 722 * monitor provider. 723 */ 724 private Entry getMonitorEntry(final DN entryDN, 725 final MonitorProvider<?> monitorProvider) 726 { 727 final ObjectClass monitorOC = monitorProvider.getMonitorObjectClass(); 728 final HashMap<ObjectClass, String> monitorClasses = newObjectClasses(monitorOC, monitorOC.getNameOrOID()); 729 730 final MonitorData monitorAttrs = monitorProvider.getMonitorData(); 731 final Map<AttributeType, List<Attribute>> attrMap = asMap(monitorAttrs); 732 733 // Make sure to include the RDN attribute. 734 final AVA ava = entryDN.rdn().getFirstAVA(); 735 final AttributeType rdnType = ava.getAttributeType(); 736 final ByteString rdnValue = ava.getAttributeValue(); 737 attrMap.put(rdnType, Attributes.createAsList(rdnType, rdnValue)); 738 739 return newEntry(entryDN, monitorClasses, attrMap, new HashMap<AttributeType, List<Attribute>>(0)); 740 } 741 742 private Map<AttributeType, List<Attribute>> asMap(MonitorData monitorAttrs) 743 { 744 final Map<AttributeType, List<Attribute>> results = new LinkedHashMap<>(monitorAttrs.size() + 1); 745 for (final Attribute a : monitorAttrs) 746 { 747 final AttributeType type = a.getAttributeDescription().getAttributeType(); 748 749 List<Attribute> attrs = results.get(type); 750 if (attrs == null) 751 { 752 attrs = new ArrayList<>(); 753 results.put(type, attrs); 754 } 755 attrs.add(a); 756 } 757 return results; 758 } 759 760 private HashMap<ObjectClass, String> newObjectClasses(ObjectClass objectClass, String objectClassName) 761 { 762 final HashMap<ObjectClass, String> monitorClasses = new LinkedHashMap<>(monitorObjectClasses.size() + 1); 763 monitorClasses.putAll(monitorObjectClasses); 764 monitorClasses.put(objectClass, objectClassName); 765 return monitorClasses; 766 } 767 768 private Entry newEntry(final DN dn, final Map<ObjectClass, String> objectClasses, 769 final Map<AttributeType, List<Attribute>> userAttrs, Map<AttributeType, List<Attribute>> opAttrs) 770 { 771 final Entry e = new Entry(dn, objectClasses, userAttrs, opAttrs); 772 e.processVirtualAttributes(); 773 return e; 774 } 775 776 /** 777 * Indicates whether the provided attribute is one that is used in the 778 * configuration of this backend. 779 * 780 * @param attribute 781 * The attribute for which to make the determination. 782 * @return <CODE>true</CODE> if the provided attribute is one that is used in 783 * the configuration of this backend, <CODE>false</CODE> if not. 784 */ 785 private boolean isMonitorConfigAttribute(final Attribute attribute) 786 { 787 final AttributeType attrType = attribute.getAttributeDescription().getAttributeType(); 788 return attrType.hasName(ATTR_COMMON_NAME) 789 || attrType.hasName(ATTR_BACKEND_ENABLED) 790 || attrType.hasName(ATTR_BACKEND_CLASS) 791 || attrType.hasName(ATTR_BACKEND_BASE_DN) 792 || attrType.hasName(ATTR_BACKEND_ID) 793 || attrType.hasName(ATTR_BACKEND_WRITABILITY_MODE); 794 } 795}