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