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 2008-2011 Sun Microsystems, Inc.
015 * Portions Copyright 2013-2016 ForgeRock AS.
016 */
017package org.opends.guitools.controlpanel.util;
018
019import static org.opends.messages.AdminToolMessages.*;
020import static org.opends.server.backends.pluggable.SuffixContainer.*;
021
022import java.net.InetAddress;
023import java.text.DateFormat;
024import java.text.SimpleDateFormat;
025import java.util.ArrayList;
026import java.util.Collection;
027import java.util.Collections;
028import java.util.HashMap;
029import java.util.HashSet;
030import java.util.List;
031import java.util.Map;
032import java.util.Set;
033import java.util.SortedSet;
034import java.util.TimeZone;
035import java.util.TreeSet;
036
037import javax.naming.NamingEnumeration;
038import javax.naming.NamingException;
039import javax.naming.directory.SearchControls;
040import javax.naming.directory.SearchResult;
041import javax.naming.ldap.InitialLdapContext;
042import javax.naming.ldap.LdapName;
043
044import org.forgerock.i18n.LocalizableMessage;
045import org.forgerock.i18n.slf4j.LocalizedLogger;
046import org.forgerock.opendj.config.server.ConfigException;
047import org.forgerock.opendj.ldap.DN;
048import org.forgerock.opendj.server.config.client.AdministrationConnectorCfgClient;
049import org.forgerock.opendj.server.config.client.BackendCfgClient;
050import org.forgerock.opendj.server.config.client.BackendIndexCfgClient;
051import org.forgerock.opendj.server.config.client.BackendVLVIndexCfgClient;
052import org.forgerock.opendj.server.config.client.BackupBackendCfgClient;
053import org.forgerock.opendj.server.config.client.ConnectionHandlerCfgClient;
054import org.forgerock.opendj.server.config.client.HTTPConnectionHandlerCfgClient;
055import org.forgerock.opendj.server.config.client.JMXConnectionHandlerCfgClient;
056import org.forgerock.opendj.server.config.client.LDAPConnectionHandlerCfgClient;
057import org.forgerock.opendj.server.config.client.LDIFBackendCfgClient;
058import org.forgerock.opendj.server.config.client.LDIFConnectionHandlerCfgClient;
059import org.forgerock.opendj.server.config.client.MemoryBackendCfgClient;
060import org.forgerock.opendj.server.config.client.MonitorBackendCfgClient;
061import org.forgerock.opendj.server.config.client.PluggableBackendCfgClient;
062import org.forgerock.opendj.server.config.client.ReplicationDomainCfgClient;
063import org.forgerock.opendj.server.config.client.ReplicationServerCfgClient;
064import org.forgerock.opendj.server.config.client.ReplicationSynchronizationProviderCfgClient;
065import org.forgerock.opendj.server.config.client.RootCfgClient;
066import org.forgerock.opendj.server.config.client.RootDNCfgClient;
067import org.forgerock.opendj.server.config.client.RootDNUserCfgClient;
068import org.forgerock.opendj.server.config.client.SNMPConnectionHandlerCfgClient;
069import org.forgerock.opendj.server.config.client.TaskBackendCfgClient;
070import org.opends.admin.ads.util.ConnectionUtils;
071import org.opends.admin.ads.util.ConnectionWrapper;
072import org.opends.guitools.controlpanel.datamodel.AbstractIndexDescriptor;
073import org.opends.guitools.controlpanel.datamodel.BackendDescriptor;
074import org.opends.guitools.controlpanel.datamodel.BaseDNDescriptor;
075import org.opends.guitools.controlpanel.datamodel.ConnectionHandlerDescriptor;
076import org.opends.guitools.controlpanel.datamodel.CustomSearchResult;
077import org.opends.guitools.controlpanel.datamodel.IndexDescriptor;
078import org.opends.guitools.controlpanel.datamodel.VLVIndexDescriptor;
079import org.opends.guitools.controlpanel.datamodel.VLVSortOrder;
080import org.opends.guitools.controlpanel.task.OnlineUpdateException;
081import org.opends.server.config.ConfigConstants;
082import org.opends.server.core.DirectoryServer;
083import org.opends.server.tools.tasks.TaskEntry;
084import org.opends.server.types.OpenDsException;
085import org.opends.server.util.ServerConstants;
086
087/**
088 * A class that reads the configuration and monitoring information using a
089 * DirContext through LDAP.
090 */
091public class ConfigFromDirContext extends ConfigReader
092{
093  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
094
095  private static final String DATABASE_JE_MONITORING_ENTRY_SUFFIX = " JE Database";
096  private static final String DATABASE_PDB_MONITORING_ENTRY_SUFFIX = " PDB Database";
097  private static final String SYNC_PROVIDER_NAME = "Multimaster Synchronization";
098
099  private CustomSearchResult rootMonitor;
100  private CustomSearchResult jvmMemoryUsage;
101  private CustomSearchResult systemInformation;
102  private CustomSearchResult entryCaches;
103  private CustomSearchResult workQueue;
104  private CustomSearchResult versionMonitor;
105
106  private boolean isLocal = true;
107
108  private final Map<String, CustomSearchResult> hmConnectionHandlersMonitor = new HashMap<>();
109
110  /** The monitor root entry DN. */
111  private DN monitorDN = DN.rootDN();
112  /** The JVM memory usage monitoring entry DN. */
113  private DN jvmMemoryUsageDN = DN.rootDN();
114  /** The system information monitoring entry DN. */
115  private DN systemInformationDN = DN.rootDN();
116  /**The entry cache monitoring entry DN. */
117  private DN entryCachesDN = DN.rootDN();
118  /** The work queue monitoring entry DN. */
119  private DN workQueueDN = DN.rootDN();
120  /** The version monitoring entry DN. */
121  private DN versionDN = DN.rootDN();
122
123  {
124    try
125    {
126      monitorDN = DN.valueOf("cn=monitor");
127      jvmMemoryUsageDN = DN.valueOf("cn=JVM Memory Usage,cn=monitor");
128      systemInformationDN = DN.valueOf("cn=System Information,cn=monitor");
129      entryCachesDN = DN.valueOf("cn=Entry Caches,cn=monitor");
130      workQueueDN = DN.valueOf("cn=Work Queue,cn=monitor");
131      versionDN = DN.valueOf("cn=Version,cn=monitor");
132    }
133    catch (Throwable t)
134    {
135      throw new RuntimeException("Could not decode DNs: "+t, t);
136    }
137  }
138
139  /** The date formatter to be used to parse GMT dates. */
140  public static final SimpleDateFormat utcParser = new SimpleDateFormat(ServerConstants.DATE_FORMAT_GMT_TIME);
141  {
142    utcParser.setTimeZone(TimeZone.getTimeZone("UTC"));
143  }
144
145  /** The date formatter to be used to format dates. */
146  public static final DateFormat formatter = DateFormat.getDateTimeInstance();
147
148  /**
149   * Returns the monitoring entry for the entry caches.
150   *
151   * @return the monitoring entry for the entry caches.
152   */
153  public CustomSearchResult getEntryCaches()
154  {
155    return entryCaches;
156  }
157
158  /**
159   * Returns the monitoring entry for the JVM memory usage.
160   *
161   * @return the monitoring entry for the JVM memory usage.
162   */
163  public CustomSearchResult getJvmMemoryUsage()
164  {
165    return jvmMemoryUsage;
166  }
167
168  /**
169   * Returns the root entry of the monitoring tree.
170   *
171   * @return the root entry of the monitoring tree.
172   */
173  public CustomSearchResult getRootMonitor()
174  {
175    return rootMonitor;
176  }
177
178  /**
179   * Returns the version entry of the monitoring tree.
180   *
181   * @return the version entry of the monitoring tree.
182   */
183  public CustomSearchResult getVersionMonitor()
184  {
185    return versionMonitor;
186  }
187
188  /**
189   * Returns the monitoring entry for the system information.
190   *
191   * @return the monitoring entry for the system information.
192   */
193  public CustomSearchResult getSystemInformation()
194  {
195    return systemInformation;
196  }
197
198  /**
199   * Returns the monitoring entry for the work queue.
200   *
201   * @return the monitoring entry for the work queue.
202   */
203  public CustomSearchResult getWorkQueue()
204  {
205    return workQueue;
206  }
207
208  /**
209   * Sets whether this server represents the local instance or a remote server.
210   *
211   * @param isLocal
212   *          whether this server represents the local instance or a remote
213   *          server (in another machine or in another installation on the same
214   *          machine).
215   */
216  public void setIsLocal(boolean isLocal)
217  {
218    this.isLocal = isLocal;
219  }
220
221  /**
222   * Returns <CODE>true</CODE> if we are trying to manage the local host and
223   * <CODE>false</CODE> otherwise.
224   *
225   * @return <CODE>true</CODE> if we are trying to manage the local host and
226   *         <CODE>false</CODE> otherwise.
227   */
228  public boolean isLocal()
229  {
230    return isLocal;
231  }
232
233  /**
234   * Reads configuration and monitoring information using the provided
235   * connection.
236   *
237   * @param connWrapper
238   *          the connection to be used to read the information.
239   */
240  public void readConfiguration(final ConnectionWrapper connWrapper)
241  {
242    final List<Exception> errors = new ArrayList<>();
243    final Set<ConnectionHandlerDescriptor> connectionHandlers = new HashSet<>();
244    final Set<BackendDescriptor> backendDescriptors = new HashSet<>();
245    final Set<DN> as = new HashSet<>();
246    final Set<TaskEntry> tasks = new HashSet<>();
247
248    rootMonitor = null;
249    jvmMemoryUsage = null;
250    systemInformation = null;
251    entryCaches = null;
252    workQueue = null;
253    versionMonitor = null;
254
255    hmConnectionHandlersMonitor.clear();
256
257    readSchemaIfNeeded(connWrapper.getLdapContext(), errors);
258
259    try
260    {
261      readConfig(connWrapper, connectionHandlers, backendDescriptors, as, errors);
262    }
263    catch (final Throwable t)
264    {
265      errors.add(new OnlineUpdateException(ERR_READING_CONFIG_LDAP.get(t), t));
266    }
267
268    for (Exception oe : errors)
269    {
270      logger.warn(LocalizableMessage.raw("Error reading configuration: " + oe, oe));
271    }
272    administrativeUsers = Collections.unmodifiableSet(as);
273    listeners = Collections.unmodifiableSet(connectionHandlers);
274    backends = Collections.unmodifiableSet(backendDescriptors);
275    try
276    {
277      updateMonitorInformation(connWrapper.getLdapContext(), errors);
278    }
279    catch (Throwable t)
280    {
281      logger.warn(LocalizableMessage.raw("Error reading monitoring: " + t, t));
282      errors.add(new OnlineUpdateException(ERR_READING_CONFIG_LDAP.get(t), t));
283    }
284
285    try
286    {
287      updateTaskInformation(connWrapper.getLdapContext(), errors, tasks);
288    }
289    catch (Throwable t)
290    {
291      logger.warn(LocalizableMessage.raw("Error reading task information: " + t, t));
292      errors.add(new OnlineUpdateException(ERR_READING_CONFIG_LDAP.get(t), t));
293    }
294
295    taskEntries = Collections.unmodifiableSet(tasks);
296    for (ConnectionHandlerDescriptor ch : getConnectionHandlers())
297    {
298      ch.setMonitoringEntries(getMonitoringEntries(ch));
299    }
300
301    if (adminConnector != null)
302    {
303      adminConnector.setMonitoringEntries(getMonitoringEntries(adminConnector));
304    }
305    exceptions = Collections.unmodifiableList(errors);
306  }
307
308  private void readSchemaIfNeeded(final InitialLdapContext context, final List<Exception> errors)
309  {
310    if (mustReadSchema())
311    {
312      try
313      {
314        readSchema(context);
315        if (getSchema() != null)
316        {
317          // Update the schema: so that when we call the server code the
318          // latest schema read on the server we are managing is used.
319          DirectoryServer.setSchema(getSchema());
320        }
321      }
322      catch (OpenDsException oe)
323      {
324        errors.add(oe);
325      }
326    }
327  }
328
329  private void readConfig(final ConnectionWrapper connWrapper,
330      final Set<ConnectionHandlerDescriptor> connectionHandlers, final Set<BackendDescriptor> backendDescriptors,
331      final Set<DN> alternateBindDNs, final List<Exception> errors) throws Exception
332  {
333    final RootCfgClient root = connWrapper.getRootConfiguration();
334
335    readAdminConnector(root, errors);
336    readConnectionHandlers(connectionHandlers, root, errors);
337    isSchemaEnabled = root.getGlobalConfiguration().isCheckSchema();
338
339    readBackendConfiguration(backendDescriptors, root, errors);
340
341    boolean isReplicationSecure = readIfReplicationIsSecure(root, errors);
342
343    final ReplicationSynchronizationProviderCfgClient sync = readSyncProviderIfExists(root);
344    if (sync != null)
345    {
346      readReplicationConfig(connectionHandlers, backendDescriptors, sync, isReplicationSecure, errors);
347    }
348
349    readAlternateBindDNs(alternateBindDNs, root, errors);
350  }
351
352  private void readAdminConnector(final RootCfgClient root, final List<Exception> errors)
353  {
354    try
355    {
356      AdministrationConnectorCfgClient adminConnector = root.getAdministrationConnector();
357      this.adminConnector = getConnectionHandler(adminConnector);
358    }
359    catch (Exception oe)
360    {
361      errors.add(oe);
362    }
363  }
364
365  private void readConnectionHandlers(final Set<ConnectionHandlerDescriptor> connectionHandlers,
366      RootCfgClient root, final List<Exception> errors)
367  {
368    try
369    {
370      for (String connHandler : root.listConnectionHandlers())
371      {
372        try
373        {
374          ConnectionHandlerCfgClient connectionHandler = root.getConnectionHandler(connHandler);
375          connectionHandlers.add(getConnectionHandler(connectionHandler, connHandler));
376        }
377        catch (Exception oe)
378        {
379          errors.add(oe);
380        }
381      }
382    }
383    catch (Exception oe)
384    {
385      errors.add(oe);
386    }
387  }
388
389  private void readBackendConfiguration(final Set<BackendDescriptor> backendDescriptors,
390      final RootCfgClient root, final List<Exception> errors) throws Exception
391  {
392    for (final String backendName : root.listBackends())
393    {
394      try
395      {
396        BackendCfgClient backend = root.getBackend(backendName);
397        Set<BaseDNDescriptor> baseDNs = new HashSet<>();
398        for (DN dn : backend.getBaseDN())
399        {
400          BaseDNDescriptor baseDN = new BaseDNDescriptor(BaseDNDescriptor.Type.NOT_REPLICATED, dn, null, -1, -1, -1);
401          baseDNs.add(baseDN);
402        }
403        Set<IndexDescriptor> indexes = new HashSet<>();
404        Set<VLVIndexDescriptor> vlvIndexes = new HashSet<>();
405        BackendDescriptor.Type type = getBackendType(backend);
406        if (type == BackendDescriptor.Type.PLUGGABLE)
407        {
408          refreshBackendConfig(indexes, vlvIndexes, backend, errors);
409        }
410
411        BackendDescriptor desc = new BackendDescriptor(
412            backend.getBackendId(), baseDNs, indexes, vlvIndexes, -1, backend.isEnabled(), type);
413        for (AbstractIndexDescriptor index: indexes)
414        {
415          index.setBackend(desc);
416        }
417        for (AbstractIndexDescriptor index: vlvIndexes)
418        {
419          index.setBackend(desc);
420        }
421        for (BaseDNDescriptor baseDN : baseDNs)
422        {
423          baseDN.setBackend(desc);
424        }
425        backendDescriptors.add(desc);
426      }
427      catch (Exception oe)
428      {
429        errors.add(oe);
430      }
431    }
432  }
433
434  private BackendDescriptor.Type getBackendType(BackendCfgClient backend)
435  {
436    if (backend instanceof PluggableBackendCfgClient)
437    {
438      return BackendDescriptor.Type.PLUGGABLE;
439    }
440    else if (backend instanceof LDIFBackendCfgClient)
441    {
442      return BackendDescriptor.Type.LDIF;
443    }
444    else if (backend instanceof MemoryBackendCfgClient)
445    {
446      return BackendDescriptor.Type.MEMORY;
447    }
448    else if (backend instanceof BackupBackendCfgClient)
449    {
450      return BackendDescriptor.Type.BACKUP;
451    }
452    else if (backend instanceof MonitorBackendCfgClient)
453    {
454      return BackendDescriptor.Type.MONITOR;
455    }
456    else if (backend instanceof TaskBackendCfgClient)
457    {
458      return BackendDescriptor.Type.TASK;
459    }
460    else
461    {
462      return BackendDescriptor.Type.OTHER;
463    }
464  }
465
466  private void refreshBackendConfig(final Set<IndexDescriptor> indexes,
467      final Set<VLVIndexDescriptor> vlvIndexes, final BackendCfgClient backend, final List<Exception> errors)
468  {
469    final PluggableBackendCfgClient db = (PluggableBackendCfgClient) backend;
470    readBackendIndexes(indexes, errors, db);
471    readBackendVLVIndexes(vlvIndexes, errors, db);
472  }
473
474  private void readBackendIndexes(final Set<IndexDescriptor> indexes, final List<Exception> errors,
475      final PluggableBackendCfgClient db)
476  {
477    indexes.add(new IndexDescriptor(DN2ID_INDEX_NAME));
478    indexes.add(new IndexDescriptor(ID2CHILDREN_COUNT_NAME));
479    try
480    {
481      for (final String indexName : db.listBackendIndexes())
482      {
483        final BackendIndexCfgClient index = db.getBackendIndex(indexName);
484        indexes.add(new IndexDescriptor(
485            index.getAttribute().getNameOrOID(), index.getAttribute(),
486            null, index.getIndexType(), index.getIndexEntryLimit()));
487      }
488    }
489    catch (Exception oe)
490    {
491      errors.add(oe);
492    }
493  }
494
495  private void readBackendVLVIndexes(final Set<VLVIndexDescriptor> vlvIndexes,
496      final List<Exception> errors, final PluggableBackendCfgClient db)
497  {
498    try
499    {
500      for (final String vlvIndexName : db.listBackendVLVIndexes())
501      {
502        final BackendVLVIndexCfgClient index = db.getBackendVLVIndex(vlvIndexName);
503        final List<VLVSortOrder> sortOrder = getVLVSortOrder(index.getSortOrder());
504        vlvIndexes.add(new VLVIndexDescriptor(
505            index.getName(), null, index.getBaseDN(), VLVIndexDescriptor.toSearchScope(index.getScope()),
506            index.getFilter(), sortOrder));
507      }
508    }
509    catch (Exception oe)
510    {
511      errors.add(oe);
512    }
513  }
514
515  private boolean readIfReplicationIsSecure(final RootCfgClient root, final List<Exception> errors)
516  {
517    try
518    {
519      return root.getCryptoManager().isSSLEncryption();
520    }
521    catch (Exception oe)
522    {
523      errors.add(oe);
524      return false;
525    }
526  }
527
528  private ReplicationSynchronizationProviderCfgClient readSyncProviderIfExists(final RootCfgClient root)
529  {
530    try
531    {
532      return (ReplicationSynchronizationProviderCfgClient) root.getSynchronizationProvider(SYNC_PROVIDER_NAME);
533    }
534    catch (Exception oe)
535    {
536      return null;
537    }
538  }
539
540  private void readReplicationConfig(final Set<ConnectionHandlerDescriptor> connectionHandlers,
541      final Set<BackendDescriptor> backendDescriptors, final ReplicationSynchronizationProviderCfgClient sync,
542      boolean isReplicationSecure, final List<Exception> errors)
543  {
544    replicationPort = -1;
545    try
546    {
547      if (sync.isEnabled() && sync.hasReplicationServer())
548      {
549        ReplicationServerCfgClient replicationServer = sync.getReplicationServer();
550        if (replicationServer != null)
551        {
552          replicationPort = replicationServer.getReplicationPort();
553          ConnectionHandlerDescriptor.Protocol protocol =
554            isReplicationSecure ? ConnectionHandlerDescriptor.Protocol.REPLICATION_SECURE
555                                : ConnectionHandlerDescriptor.Protocol.REPLICATION;
556          Set<CustomSearchResult> emptySet = Collections.emptySet();
557          ConnectionHandlerDescriptor connHandler = new ConnectionHandlerDescriptor(
558              new HashSet<InetAddress>(), replicationPort, protocol, ConnectionHandlerDescriptor.State.ENABLED,
559                SYNC_PROVIDER_NAME, emptySet);
560          connectionHandlers.add(connHandler);
561        }
562      }
563
564      String[] domains = sync.listReplicationDomains();
565      if (domains != null)
566      {
567        for (String domain2 : domains)
568        {
569          ReplicationDomainCfgClient domain = sync.getReplicationDomain(domain2);
570          DN dn = domain.getBaseDN();
571          for (BackendDescriptor backend : backendDescriptors)
572          {
573            for (BaseDNDescriptor baseDN : backend.getBaseDns())
574            {
575              if (baseDN.getDn().equals(dn))
576              {
577                baseDN.setType(sync.isEnabled() ? BaseDNDescriptor.Type.REPLICATED
578                                                : BaseDNDescriptor.Type.DISABLED);
579                baseDN.setReplicaID(domain.getServerId());
580              }
581            }
582          }
583        }
584      }
585    }
586    catch (Exception oe)
587    {
588      errors.add(oe);
589    }
590  }
591
592  private void readAlternateBindDNs(final Set<DN> alternateBindDNs, final RootCfgClient root,
593      final List<Exception> errors)
594  {
595    try
596    {
597      RootDNCfgClient rootDN = root.getRootDN();
598      String[] rootUsers = rootDN.listRootDNUsers();
599      if (rootUsers != null)
600      {
601        for (String rootUser2 : rootUsers)
602        {
603          RootDNUserCfgClient rootUser = rootDN.getRootDNUser(rootUser2);
604          alternateBindDNs.addAll(rootUser.getAlternateBindDN());
605        }
606      }
607    }
608    catch (Exception oe)
609    {
610      errors.add(oe);
611    }
612  }
613
614  /**
615   * Returns an array of monitoring attributes to be returned in the request.
616   *
617   * @return an array of monitoring attributes to be returned in the request.
618   */
619  private String[] getMonitoringAttributes()
620  {
621    return new String[] {"*"};
622  }
623
624  /**
625   * Reads the schema from the files.
626   *
627   * @param ctx
628   *          the connection to be used to load the schema.
629   * @throws OpenDsException
630   *           if an error occurs reading the schema.
631   */
632  private void readSchema(InitialLdapContext ctx) throws OpenDsException
633  {
634    try
635    {
636      if (isLocal)
637      {
638        super.readSchema();
639      }
640      else
641      {
642        RemoteSchemaLoader loader = new RemoteSchemaLoader();
643        loader.readSchema(ctx);
644        schema = loader.getSchema();
645      }
646    }
647    catch (NamingException ne)
648    {
649      throw new OnlineUpdateException(ERR_READING_SCHEMA_LDAP.get(ne), ne);
650    }
651    catch (ConfigException ce)
652    {
653      throw new org.opends.server.config.ConfigException(ce.getMessageObject(), ce);
654    }
655  }
656
657  /**
658   * Takes the provided search result and updates the monitoring information
659   * accordingly.
660   *
661   * @param sr
662   *          the search result.
663   * @param searchBaseDN
664   *          the base search.
665   * @throws NamingException
666   *           if there is an error retrieving the values of the search result.
667   */
668  private void handleMonitoringSearchResult(SearchResult sr, String searchBaseDN) throws NamingException
669  {
670    if (javaVersion == null)
671    {
672      javaVersion = ConnectionUtils.getFirstValue(sr, "javaVersion");
673    }
674
675    if (numberConnections == -1)
676    {
677      String v = ConnectionUtils.getFirstValue(sr, "currentConnections");
678      if (v != null)
679      {
680        numberConnections = Integer.parseInt(v);
681      }
682    }
683
684    String dn = ConnectionUtils.getFirstValue(sr, "domain-name");
685    String replicaId = ConnectionUtils.getFirstValue(sr, "server-id");
686    String missingChanges = ConnectionUtils.getFirstValue(sr, "missing-changes");
687
688    if (dn != null  && replicaId != null && missingChanges != null)
689    {
690      for (BackendDescriptor backend : backends)
691      {
692        for (BaseDNDescriptor baseDN : backend.getBaseDns())
693        {
694          try
695          {
696            if (baseDN.getDn().equals(DN.valueOf(dn)) &&
697                Integer.toString(baseDN.getReplicaID()).equals(replicaId))
698            {
699              try
700              {
701                baseDN.setAgeOfOldestMissingChange(
702                    Long.valueOf(ConnectionUtils.getFirstValue(sr, "approx-older-change-not-synchronized-millis")));
703              }
704              catch (Throwable ignored)
705              {
706              }
707              try
708              {
709                baseDN.setMissingChanges(Integer.valueOf(missingChanges));
710              }
711              catch (Throwable ignored)
712              {
713              }
714            }
715          }
716          catch (Throwable ignored)
717          {
718          }
719        }
720      }
721    }
722    else
723    {
724      CustomSearchResult csr = new CustomSearchResult(sr, searchBaseDN);
725      String backendID = ConnectionUtils.getFirstValue(sr, "ds-backend-id");
726      String entryCount = ConnectionUtils.getFirstValue(sr, "ds-backend-entry-count");
727      Set<String> baseDnEntries = ConnectionUtils.getValues(sr, "ds-base-dn-entry-count");
728      if (backendID != null && (entryCount != null || baseDnEntries != null))
729      {
730        for (BackendDescriptor backend : backends)
731        {
732          if (backend.getBackendID().equalsIgnoreCase(backendID))
733          {
734            if (entryCount != null)
735            {
736              backend.setEntries(Integer.parseInt(entryCount));
737            }
738            if (baseDnEntries != null)
739            {
740              for (String s : baseDnEntries)
741              {
742                int index = s.indexOf(" ");
743                if (index != -1)
744                {
745                  for (BaseDNDescriptor baseDN : backend.getBaseDns())
746                  {
747                    dn = s.substring(index +1);
748
749                    if (Utilities.areDnsEqual(dn,
750                        baseDN.getDn().toString()))
751                    {
752                      try
753                      {
754                        baseDN.setEntries(
755                            Integer.parseInt(s.substring(0, index)));
756                      }
757                      catch (Throwable t)
758                      {
759                        /* Ignore */
760                      }
761                      break;
762                    }
763                  }
764                }
765              }
766            }
767          }
768        }
769      }
770      else
771      {
772        // Check if it is the DB monitor entry
773        String cn = ConnectionUtils.getFirstValue(sr, "cn");
774        String monitorBackendID = null;
775        BackendDescriptor.PluggableType pluggableType = BackendDescriptor.PluggableType.UNKNOWN;
776        if (cn != null && cn.endsWith(DATABASE_JE_MONITORING_ENTRY_SUFFIX))
777        {
778          pluggableType = BackendDescriptor.PluggableType.JE;
779          monitorBackendID = cn.substring(0, cn.length() - DATABASE_JE_MONITORING_ENTRY_SUFFIX.length());
780        }
781        if (cn != null && cn.endsWith(DATABASE_PDB_MONITORING_ENTRY_SUFFIX))
782        {
783          pluggableType = BackendDescriptor.PluggableType.PDB;
784          monitorBackendID = cn.substring(0, cn.length() - DATABASE_PDB_MONITORING_ENTRY_SUFFIX.length());
785        }
786        if (monitorBackendID != null)
787        {
788          for (BackendDescriptor backend : backends)
789          {
790            if (backend.getBackendID().equalsIgnoreCase(monitorBackendID))
791            {
792              backend.setPluggableType(pluggableType);
793              backend.setMonitoringEntry(csr);
794            }
795          }
796        }
797      }
798      try
799      {
800        if (rootMonitor == null && isRootMonitor(csr))
801        {
802          rootMonitor = csr;
803        }
804        else if (entryCaches == null && isEntryCaches(csr))
805        {
806          entryCaches = csr;
807        }
808        else if (workQueue == null && isWorkQueue(csr))
809        {
810          workQueue = csr;
811        }
812        else if (jvmMemoryUsage == null && isJvmMemoryUsage(csr))
813        {
814          jvmMemoryUsage = csr;
815        }
816        else if (systemInformation == null && isSystemInformation(csr))
817        {
818          systemInformation = csr;
819        }
820        else if (versionMonitor == null && isVersionMonitor(csr))
821        {
822          versionMonitor = csr;
823        }
824        else if (isConnectionHandler(csr))
825        {
826          String statistics = " Statistics";
827          String cn = ConnectionUtils.getFirstValue(sr, "cn");
828          if (cn.endsWith(statistics))
829          {
830            // Assume it is a connection handler
831            String name = cn.substring(0, cn.length() - statistics.length());
832            hmConnectionHandlersMonitor.put(getKey(name), csr);
833          }
834        }
835      }
836      catch (OpenDsException ode)
837      {
838        exceptions.add(ode);
839      }
840    }
841  }
842
843  /**
844   * Takes the provided search result and updates the task information
845   * accordingly.
846   *
847   * @param sr
848   *          the search result.
849   * @param searchBaseDN
850   *          the base search.
851   * @param taskEntries
852   *          the collection of TaskEntries to be updated.
853   * @param ex
854   *          the list of exceptions to be updated if an error occurs.
855   * @throws NamingException
856   *           if there is an error retrieving the values of the search result.
857   */
858  private void handleTaskSearchResult(SearchResult sr, String searchBaseDN, Collection<TaskEntry> taskEntries,
859      List<Exception> ex) throws NamingException
860  {
861    CustomSearchResult csr = new CustomSearchResult(sr, searchBaseDN);
862    try
863    {
864      if (isTaskEntry(csr))
865      {
866        taskEntries.add(new TaskEntry(csr.getEntry()));
867      }
868    }
869    catch (OpenDsException ode)
870    {
871      ex.add(ode);
872    }
873  }
874
875  private void updateMonitorInformation(InitialLdapContext ctx,
876      List<Exception> ex)
877  {
878    // Read monitoring information: since it is computed, it is faster
879    // to get everything in just one request.
880    SearchControls ctls = new SearchControls();
881    ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
882    ctls.setReturningAttributes(getMonitoringAttributes());
883    String filter = "(objectclass=*)";
884
885    try
886    {
887      LdapName jndiName = new LdapName("cn=monitor");
888      NamingEnumeration<SearchResult> monitorEntries = ctx.search(jndiName, filter, ctls);
889      javaVersion = null;
890      numberConnections = -1;
891
892      try
893      {
894        while (monitorEntries.hasMore())
895        {
896          SearchResult sr = monitorEntries.next();
897          handleMonitoringSearchResult(sr, "cn=monitor");
898        }
899      }
900      finally
901      {
902        monitorEntries.close();
903      }
904    }
905    catch (NamingException ne)
906    {
907      ex.add(new OnlineUpdateException(ERR_READING_CONFIG_LDAP.get(ne.getMessage()), ne));
908    }
909  }
910
911  /**
912   * Updates the provided list of TaskEntry with the task entries found in a
913   * server.
914   *
915   * @param ctx
916   *          the connection to the server.
917   * @param ex
918   *          the list of exceptions encountered while retrieving the task
919   *          entries.
920   * @param ts
921   *          the list of task entries to be updated.
922   */
923  public void updateTaskInformation(InitialLdapContext ctx, List<Exception> ex, Collection<TaskEntry> ts)
924  {
925    // Read monitoring information: since it is computed, it is faster
926    // to get everything in just one request.
927    SearchControls ctls = new SearchControls();
928    ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
929    ctls.setReturningAttributes(getMonitoringAttributes());
930    String filter = "(objectclass=ds-task)";
931
932    try
933    {
934      LdapName jndiName = new LdapName(ConfigConstants.DN_TASK_ROOT);
935      NamingEnumeration<SearchResult> taskEntries = ctx.search(jndiName, filter, ctls);
936      try
937      {
938        while (taskEntries.hasMore())
939        {
940          SearchResult sr = taskEntries.next();
941          handleTaskSearchResult(sr, ConfigConstants.DN_TASK_ROOT, ts, ex);
942        }
943      }
944      finally
945      {
946        taskEntries.close();
947      }
948    }
949    catch (NamingException ne)
950    {
951      ex.add(new OnlineUpdateException(ERR_READING_CONFIG_LDAP.get(ne.getMessage()), ne));
952    }
953  }
954
955  private ConnectionHandlerDescriptor getConnectionHandler(ConnectionHandlerCfgClient connHandler, String name)
956  throws OpenDsException
957  {
958    SortedSet<InetAddress> addresses = new TreeSet<>(getInetAddressComparator());
959    ConnectionHandlerDescriptor.State state = connHandler.isEnabled() ? ConnectionHandlerDescriptor.State.ENABLED
960                                                                      : ConnectionHandlerDescriptor.State.DISABLED;
961
962    ConnectionHandlerDescriptor.Protocol protocol;
963    int port;
964    if (connHandler instanceof LDAPConnectionHandlerCfgClient)
965    {
966      LDAPConnectionHandlerCfgClient ldap = (LDAPConnectionHandlerCfgClient)connHandler;
967      if (ldap.isUseSSL())
968      {
969        protocol = ConnectionHandlerDescriptor.Protocol.LDAPS;
970      }
971      else if (ldap.isAllowStartTLS())
972      {
973        protocol = ConnectionHandlerDescriptor.Protocol.LDAP_STARTTLS;
974      }
975      else
976      {
977        protocol = ConnectionHandlerDescriptor.Protocol.LDAP;
978      }
979      addAll(addresses, ldap.getListenAddress());
980      port = ldap.getListenPort();
981    }
982    else if (connHandler instanceof HTTPConnectionHandlerCfgClient)
983    {
984      HTTPConnectionHandlerCfgClient http = (HTTPConnectionHandlerCfgClient) connHandler;
985      if (http.isUseSSL())
986      {
987        protocol = ConnectionHandlerDescriptor.Protocol.HTTPS;
988      }
989      else
990      {
991        protocol = ConnectionHandlerDescriptor.Protocol.HTTP;
992      }
993      addAll(addresses, http.getListenAddress());
994      port = http.getListenPort();
995    }
996    else if (connHandler instanceof JMXConnectionHandlerCfgClient)
997    {
998      JMXConnectionHandlerCfgClient jmx = (JMXConnectionHandlerCfgClient)connHandler;
999      if (jmx.isUseSSL())
1000      {
1001        protocol = ConnectionHandlerDescriptor.Protocol.JMXS;
1002      }
1003      else
1004      {
1005        protocol = ConnectionHandlerDescriptor.Protocol.JMX;
1006      }
1007      addresses.add(jmx.getListenAddress());
1008      port = jmx.getListenPort();
1009    }
1010    else if (connHandler instanceof LDIFConnectionHandlerCfgClient)
1011    {
1012      protocol = ConnectionHandlerDescriptor.Protocol.LDIF;
1013      port = -1;
1014    }
1015    else if (connHandler instanceof SNMPConnectionHandlerCfgClient)
1016    {
1017      protocol = ConnectionHandlerDescriptor.Protocol.SNMP;
1018      SNMPConnectionHandlerCfgClient snmp = (SNMPConnectionHandlerCfgClient)connHandler;
1019      addAll(addresses, snmp.getListenAddress());
1020      port = snmp.getListenPort();
1021    }
1022    else
1023    {
1024      protocol = ConnectionHandlerDescriptor.Protocol.OTHER;
1025      port = -1;
1026    }
1027    Set<CustomSearchResult> emptySet = Collections.emptySet();
1028    return new ConnectionHandlerDescriptor(addresses, port, protocol, state, name, emptySet);
1029  }
1030
1031  private <T> void addAll(Collection<T> target, Collection<T> source)
1032  {
1033    if (source != null)
1034    {
1035      target.addAll(source);
1036    }
1037  }
1038
1039  private ConnectionHandlerDescriptor getConnectionHandler(AdministrationConnectorCfgClient adminConnector)
1040      throws OpenDsException
1041  {
1042    SortedSet<InetAddress> addresses = new TreeSet<>(getInetAddressComparator());
1043
1044    ConnectionHandlerDescriptor.Protocol protocol = ConnectionHandlerDescriptor.Protocol.ADMINISTRATION_CONNECTOR;
1045    ConnectionHandlerDescriptor.State state = ConnectionHandlerDescriptor.State.ENABLED;
1046
1047    addAll(addresses, adminConnector.getListenAddress());
1048    int port = adminConnector.getListenPort();
1049
1050    Set<CustomSearchResult> emptySet = Collections.emptySet();
1051    return new ConnectionHandlerDescriptor(
1052        addresses, port, protocol, state, INFO_CTRL_PANEL_CONN_HANDLER_ADMINISTRATION.get().toString(), emptySet);
1053  }
1054
1055  private boolean isRootMonitor(CustomSearchResult csr) throws OpenDsException
1056  {
1057    return monitorDN.equals(DN.valueOf(csr.getDN()));
1058  }
1059
1060  private boolean isVersionMonitor(CustomSearchResult csr) throws OpenDsException
1061  {
1062    return versionDN.equals(DN.valueOf(csr.getDN()));
1063  }
1064
1065  private boolean isSystemInformation(CustomSearchResult csr) throws OpenDsException
1066  {
1067    return systemInformationDN.equals(DN.valueOf(csr.getDN()));
1068  }
1069
1070  private boolean isJvmMemoryUsage(CustomSearchResult csr) throws OpenDsException
1071  {
1072    return jvmMemoryUsageDN.equals(DN.valueOf(csr.getDN()));
1073  }
1074
1075  private boolean isWorkQueue(CustomSearchResult csr) throws OpenDsException
1076  {
1077    return workQueueDN.equals(DN.valueOf(csr.getDN()));
1078  }
1079
1080  private boolean isEntryCaches(CustomSearchResult csr) throws OpenDsException
1081  {
1082    return entryCachesDN.equals(DN.valueOf(csr.getDN()));
1083  }
1084
1085  private boolean isConnectionHandler(CustomSearchResult csr) throws OpenDsException
1086  {
1087    DN dn = DN.valueOf(csr.getDN());
1088    DN parent = dn.parent();
1089    if (parent != null && parent.equals(monitorDN))
1090    {
1091      List<?> vs = csr.getAttributeValues("cn");
1092      if (vs != null && !vs.isEmpty())
1093      {
1094        String cn = (String) vs.iterator().next();
1095        String statistics = " Statistics";
1096        if (cn.endsWith(statistics))
1097        {
1098          return true;
1099        }
1100      }
1101    }
1102    return false;
1103  }
1104
1105  private static boolean isTaskEntry(CustomSearchResult csr) throws OpenDsException
1106  {
1107    List<Object> vs = csr.getAttributeValues("objectclass");
1108    if (vs != null && !vs.isEmpty())
1109    {
1110      for (Object oc : vs)
1111      {
1112        if (oc.toString().equalsIgnoreCase("ds-task"))
1113        {
1114          return true;
1115        }
1116      }
1117    }
1118    return false;
1119  }
1120
1121  /**
1122   * Commodity method to get the string representation to be used in the hash
1123   * maps as key.
1124   *
1125   * @param value
1126   *          the value to be transformed into a key for a hash map.
1127   * @return the string representation to be used in the hash maps as key.
1128   */
1129  private String getKey(String value)
1130  {
1131    return value.toLowerCase();
1132  }
1133
1134  private Set<CustomSearchResult>getMonitoringEntries(ConnectionHandlerDescriptor ch)
1135  {
1136    Set<CustomSearchResult> monitorEntries = new HashSet<>();
1137    if (ch.getState() == ConnectionHandlerDescriptor.State.ENABLED)
1138    {
1139      for (String key : hmConnectionHandlersMonitor.keySet())
1140      {
1141        // The name of the connection handler does not appear necessarily in the
1142        // key (which is based on the DN of the monitoring entry).  In general
1143        // the DN contains the String specified in
1144        // LDAPConnectionHandler.DEFAULT_FRIENDLY_NAME, so we have to check that
1145        // this connection handler is the right one.
1146        // See org.opends.server.protocols.ldap.LDAPConnectionHandler to see
1147        // how the DN of the monitoring entry is generated.
1148        if (key.contains(getKey("port " + ch.getPort()))
1149            && hasAllAddresses(ch, key))
1150        {
1151          monitorEntries.add(hmConnectionHandlersMonitor.get(key));
1152        }
1153      }
1154    }
1155
1156    return monitorEntries;
1157  }
1158
1159  private boolean hasAllAddresses(ConnectionHandlerDescriptor ch, String key)
1160  {
1161    for (InetAddress a : ch.getAddresses())
1162    {
1163      if (!key.contains(getKey(a.getHostAddress())))
1164      {
1165        return false;
1166      }
1167    }
1168    return true;
1169  }
1170}