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