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}