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