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