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-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.guitools.controlpanel.datamodel;
028
029import static org.opends.admin.ads.util.ConnectionUtils.*;
030import static org.opends.guitools.controlpanel.util.Utilities.*;
031import static org.opends.server.tools.ConfigureWindowsService.*;
032
033import static com.forgerock.opendj.cli.Utils.*;
034import static com.forgerock.opendj.util.OperatingSystem.*;
035
036import java.io.File;
037import java.net.InetAddress;
038import java.util.Collection;
039import java.util.Collections;
040import java.util.HashSet;
041import java.util.LinkedHashSet;
042import java.util.Objects;
043import java.util.Set;
044import java.util.SortedSet;
045
046import javax.naming.NamingException;
047import javax.naming.ldap.InitialLdapContext;
048
049import org.forgerock.i18n.LocalizableMessage;
050import org.forgerock.i18n.slf4j.LocalizedLogger;
051import org.forgerock.opendj.config.ConfigurationFramework;
052import org.forgerock.opendj.config.server.ConfigException;
053import org.opends.admin.ads.util.ApplicationTrustManager;
054import org.opends.admin.ads.util.ConnectionUtils;
055import org.opends.guitools.controlpanel.browser.IconPool;
056import org.opends.guitools.controlpanel.browser.LDAPConnectionPool;
057import org.opends.guitools.controlpanel.datamodel.ServerDescriptor.ServerStatus;
058import org.opends.guitools.controlpanel.event.BackendPopulatedEvent;
059import org.opends.guitools.controlpanel.event.BackendPopulatedListener;
060import org.opends.guitools.controlpanel.event.BackupCreatedEvent;
061import org.opends.guitools.controlpanel.event.BackupCreatedListener;
062import org.opends.guitools.controlpanel.event.ConfigChangeListener;
063import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
064import org.opends.guitools.controlpanel.event.IndexModifiedEvent;
065import org.opends.guitools.controlpanel.event.IndexModifiedListener;
066import org.opends.guitools.controlpanel.task.Task;
067import org.opends.guitools.controlpanel.task.Task.State;
068import org.opends.guitools.controlpanel.task.Task.Type;
069import org.opends.guitools.controlpanel.util.ConfigFromDirContext;
070import org.opends.guitools.controlpanel.util.ConfigFromFile;
071import org.opends.guitools.controlpanel.util.ConfigReader;
072import org.opends.guitools.controlpanel.util.Utilities;
073import org.opends.quicksetup.util.UIKeyStore;
074import org.opends.quicksetup.util.Utils;
075import org.opends.server.util.StaticUtils;
076
077import com.forgerock.opendj.cli.CliConstants;
078
079/**
080 * This is the classes that is shared among all the different places in the
081 * Control Panel.  It contains information about the server status and
082 * configuration and some objects that are shared everywhere.
083 */
084public class ControlPanelInfo
085{
086  private long poolingPeriod = 20000;
087
088  private ServerDescriptor serverDesc;
089  private Set<Task> tasks = new HashSet<>();
090  private InitialLdapContext ctx;
091  private InitialLdapContext userDataCtx;
092  private final LDAPConnectionPool connectionPool = new LDAPConnectionPool();
093  /** Used by the browsers. */
094  private final IconPool iconPool = new IconPool();
095  private Thread poolingThread;
096  private boolean stopPooling;
097  private boolean pooling;
098  private ApplicationTrustManager trustManager;
099  private int connectTimeout = CliConstants.DEFAULT_LDAP_CONNECT_TIMEOUT;
100  private ConnectionProtocolPolicy connectionPolicy =
101    ConnectionProtocolPolicy.USE_MOST_SECURE_AVAILABLE;
102  private String ldapURL;
103  private String startTLSURL;
104  private String ldapsURL;
105  private String adminConnectorURL;
106  private String localAdminConnectorURL;
107  private String lastWorkingBindDN;
108  private String lastWorkingBindPwd;
109  private String lastRemoteHostName;
110  private String lastRemoteAdministrationURL;
111
112  private static boolean mustDeregisterConfig;
113
114  private boolean isLocal = true;
115
116  private Set<AbstractIndexDescriptor> modifiedIndexes = new HashSet<>();
117  private LinkedHashSet<ConfigChangeListener> configListeners = new LinkedHashSet<>();
118  private LinkedHashSet<BackupCreatedListener> backupListeners = new LinkedHashSet<>();
119  private LinkedHashSet<BackendPopulatedListener> backendPopulatedListeners = new LinkedHashSet<>();
120  private LinkedHashSet<IndexModifiedListener> indexListeners = new LinkedHashSet<>();
121
122  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
123
124  private static ControlPanelInfo instance;
125
126  /** Default constructor. */
127  protected ControlPanelInfo()
128  {
129  }
130
131  /**
132   * Returns a singleton for this instance.
133   * @return the control panel info.
134   */
135  public static ControlPanelInfo getInstance()
136  {
137    if (instance == null)
138    {
139      instance = new ControlPanelInfo();
140      try
141      {
142        instance.setTrustManager(
143            new ApplicationTrustManager(UIKeyStore.getInstance()));
144      }
145      catch (Throwable t)
146      {
147        logger.warn(LocalizableMessage.raw("Error retrieving UI key store: "+t, t));
148        instance.setTrustManager(new ApplicationTrustManager(null));
149      }
150    }
151    return instance;
152  }
153
154  /**
155   * Returns the last ServerDescriptor that has been retrieved.
156   * @return the last ServerDescriptor that has been retrieved.
157   */
158  public ServerDescriptor getServerDescriptor()
159  {
160    return serverDesc;
161  }
162
163  /**
164   * Returns the list of tasks.
165   * @return the list of tasks.
166   */
167  public Set<Task> getTasks()
168  {
169    return Collections.unmodifiableSet(tasks);
170  }
171
172  /**
173   * Registers a task.  The Control Panel creates a task every time an operation
174   * is made and they are stored here.
175   * @param task the task to be registered.
176   */
177  public void registerTask(Task task)
178  {
179    tasks.add(task);
180  }
181
182  /**
183   * Unregisters a task.
184   * @param task the task to be unregistered.
185   */
186  public void unregisterTask(Task task)
187  {
188    tasks.remove(task);
189  }
190
191  /**
192   * Tells whether an index must be reindexed or not.
193   * @param index the index.
194   * @return <CODE>true</CODE> if the index must be reindexed and
195   * <CODE>false</CODE> otherwise.
196   */
197  public boolean mustReindex(AbstractIndexDescriptor index)
198  {
199    boolean mustReindex = false;
200    for (AbstractIndexDescriptor i : modifiedIndexes)
201    {
202      if (i.getName().equals(index.getName()) &&
203          i.getBackend().getBackendID().equals(
204              index.getBackend().getBackendID()))
205      {
206        mustReindex = true;
207        break;
208      }
209    }
210    return mustReindex;
211  }
212
213  /**
214   * Registers an index as modified.  This is used by the panels to be able
215   * to inform the user that a rebuild of the index is required.
216   * @param index the index.
217   */
218  public void registerModifiedIndex(AbstractIndexDescriptor index)
219  {
220    modifiedIndexes.add(index);
221    indexModified(index);
222  }
223
224  /**
225   * Unregisters a modified index.
226   * @param index the index.
227   * @return <CODE>true</CODE> if the index is found in the list of modified
228   * indexes and <CODE>false</CODE> otherwise.
229   */
230  public boolean unregisterModifiedIndex(AbstractIndexDescriptor index)
231  {
232    // We might have stored indexes whose configuration has changed, just remove
233    // them if they have the same name, are of the same type and are defined in
234    // the same backend.
235    Set<AbstractIndexDescriptor> toRemove = new HashSet<>();
236    for (AbstractIndexDescriptor i : modifiedIndexes)
237    {
238      if (i.getName().equalsIgnoreCase(index.getName()) &&
239          i.getBackend().getBackendID().equalsIgnoreCase(
240              index.getBackend().getBackendID()) &&
241          i.getClass().equals(index.getClass()))
242      {
243        toRemove.add(i);
244      }
245    }
246
247    if (!toRemove.isEmpty())
248    {
249      boolean returnValue = modifiedIndexes.removeAll(toRemove);
250      indexModified(toRemove.iterator().next());
251      return returnValue;
252    }
253    return false;
254  }
255
256  /**
257   * Unregisters all the modified indexes on a given backend.
258   * @param backendName the name of the backend.
259   */
260  public void unregisterModifiedIndexesInBackend(String backendName)
261  {
262    HashSet<AbstractIndexDescriptor> toDelete = new HashSet<>();
263    for (AbstractIndexDescriptor index : modifiedIndexes)
264    {
265      // Compare only the Backend ID, since the backend object attached to
266      // the registered index might have changed (for instance the number of
267      // entries).  Relying on the backend ID to identify the backend is
268      // safe.
269      if (index.getBackend().getBackendID().equalsIgnoreCase(backendName))
270      {
271        toDelete.add(index);
272      }
273    }
274    modifiedIndexes.removeAll(toDelete);
275    for (BackendDescriptor backend : getServerDescriptor().getBackends())
276    {
277      if (backend.getBackendID().equals(backendName))
278      {
279        IndexModifiedEvent ev = new IndexModifiedEvent(backend);
280        for (IndexModifiedListener listener : indexListeners)
281        {
282          listener.backendIndexesModified(ev);
283        }
284        break;
285      }
286    }
287  }
288
289  /**
290   * Returns a collection with all the modified indexes.
291   * @return a collection with all the modified indexes.
292   */
293  public Collection<AbstractIndexDescriptor> getModifiedIndexes()
294  {
295    return Collections.unmodifiableCollection(modifiedIndexes);
296  }
297
298  /**
299   * Sets the dir context to be used by the ControlPanelInfo to retrieve
300   * monitoring and configuration information.
301   * @param ctx the connection.
302   */
303  public void setDirContext(InitialLdapContext ctx)
304  {
305    this.ctx = ctx;
306    if (ctx != null)
307    {
308      lastWorkingBindDN = ConnectionUtils.getBindDN(ctx);
309      lastWorkingBindPwd = ConnectionUtils.getBindPassword(ctx);
310      lastRemoteHostName = ConnectionUtils.getHostName(ctx);
311      lastRemoteAdministrationURL = ConnectionUtils.getLdapUrl(ctx);
312    }
313  }
314
315  /**
316   * Returns the dir context to be used by the ControlPanelInfo to retrieve
317   * monitoring and configuration information.
318   * @return the dir context to be used by the ControlPanelInfo to retrieve
319   * monitoring and configuration information.
320   */
321  public InitialLdapContext getDirContext()
322  {
323    return ctx;
324  }
325
326  /**
327   * Sets the dir context to be used by the ControlPanelInfo to retrieve
328   * user data.
329   * @param ctx the connection.
330   * @throws NamingException if there is a problem updating the connection pool.
331   */
332  public void setUserDataDirContext(InitialLdapContext ctx)
333  throws NamingException
334  {
335    if (userDataCtx != null)
336    {
337      unregisterConnection(connectionPool, ctx);
338    }
339    this.userDataCtx = ctx;
340    if (ctx != null)
341    {
342      InitialLdapContext cloneLdc =
343        ConnectionUtils.cloneInitialLdapContext(userDataCtx,
344            getConnectTimeout(),
345            getTrustManager(), null);
346      connectionPool.registerConnection(cloneLdc);
347    }
348  }
349
350  /**
351   * Returns the dir context to be used by the ControlPanelInfo to retrieve
352   * user data.
353   * @return the dir context to be used by the ControlPanelInfo to retrieve
354   * user data.
355   */
356  public InitialLdapContext getUserDataDirContext()
357  {
358    return userDataCtx;
359  }
360
361  /**
362   * Informs that a backup has been created.  The method will notify to all
363   * the backup listeners that a backup has been created.
364   * @param newBackup the new created backup.
365   */
366  public void backupCreated(BackupDescriptor newBackup)
367  {
368    BackupCreatedEvent ev = new BackupCreatedEvent(newBackup);
369    for (BackupCreatedListener listener : backupListeners)
370    {
371      listener.backupCreated(ev);
372    }
373  }
374
375  /**
376   * Informs that a set of backends have been populated.  The method will notify
377   * to all the backend populated listeners.
378   * @param backends the populated backends.
379   */
380  public void backendPopulated(Set<BackendDescriptor> backends)
381  {
382    BackendPopulatedEvent ev = new BackendPopulatedEvent(backends);
383    for (BackendPopulatedListener listener : backendPopulatedListeners)
384    {
385      listener.backendPopulated(ev);
386    }
387  }
388
389  /**
390   * Informs that an index has been modified.  The method will notify to all
391   * the index listeners that an index has been modified.
392   * @param modifiedIndex the modified index.
393   */
394  public void indexModified(AbstractIndexDescriptor modifiedIndex)
395  {
396    IndexModifiedEvent ev = new IndexModifiedEvent(modifiedIndex);
397    for (IndexModifiedListener listener : indexListeners)
398    {
399      listener.indexModified(ev);
400    }
401  }
402
403  /**
404   * Returns an empty new server descriptor instance.
405   * @return an empty new server descriptor instance.
406   */
407  protected ServerDescriptor createNewServerDescriptorInstance()
408  {
409    return new ServerDescriptor();
410  }
411
412  /**
413   * Returns a reader that will read the configuration from a file.
414   * @return a reader that will read the configuration from a file.
415   */
416  protected ConfigFromFile createNewConfigFromFileReader()
417  {
418    return new ConfigFromFile();
419  }
420
421  /**
422   * Returns a reader that will read the configuration from a dir context.
423   * @return a reader that will read the configuration from a dir context.
424   */
425  protected ConfigFromDirContext createNewConfigFromDirContextReader()
426  {
427    ConfigFromDirContext configFromDirContext = new ConfigFromDirContext();
428    configFromDirContext.setIsLocal(isLocal());
429    return configFromDirContext;
430  }
431
432  /**
433   * Updates the contents of the server descriptor with the provider reader.
434   * @param reader the configuration reader.
435   * @param desc the server descriptor.
436   */
437  protected void updateServerDescriptor(ConfigReader reader,
438      ServerDescriptor desc)
439  {
440    desc.setExceptions(reader.getExceptions());
441    desc.setAdministrativeUsers(reader.getAdministrativeUsers());
442    desc.setBackends(reader.getBackends());
443    desc.setConnectionHandlers(reader.getConnectionHandlers());
444    desc.setAdminConnector(reader.getAdminConnector());
445    desc.setSchema(reader.getSchema());
446    desc.setSchemaEnabled(reader.isSchemaEnabled());
447  }
448
449  /** Regenerates the last found ServerDescriptor object. */
450  public synchronized void regenerateDescriptor()
451  {
452    boolean isLocal = isLocal();
453
454    ServerDescriptor desc = createNewServerDescriptorInstance();
455    desc.setIsLocal(isLocal);
456    InitialLdapContext ctx = getDirContext();
457    if (isLocal)
458    {
459      desc.setOpenDSVersion(
460        org.opends.server.util.DynamicConstants.FULL_VERSION_STRING);
461      String installPath = Utilities.getInstallPathFromClasspath();
462      desc.setInstallPath(installPath);
463      desc.setInstancePath(Utils.getInstancePathFromInstallPath(installPath));
464      desc.setWindowsServiceEnabled(isWindows() && serviceState() == SERVICE_STATE_ENABLED);
465    }
466    else if (lastRemoteHostName != null)
467    {
468      desc.setHostname(lastRemoteHostName);
469    }
470    ConfigReader reader;
471
472    ServerStatus status = getStatus(desc);
473    if (status != null)
474    {
475      desc.setStatus(status);
476      if (status == ServerStatus.STOPPING)
477      {
478        StaticUtils.close(ctx);
479        this.ctx = null;
480        if (userDataCtx != null)
481        {
482          unregisterConnection(connectionPool, ctx);
483          StaticUtils.close(userDataCtx);
484          userDataCtx = null;
485        }
486      }
487      if (isLocal)
488      {
489        reader = createNewConfigFromFileReader();
490        ((ConfigFromFile)reader).readConfiguration();
491      }
492      else
493      {
494        reader = null;
495      }
496      desc.setAuthenticated(false);
497    }
498    else if (!isLocal ||
499        Utilities.isServerRunning(new File(desc.getInstancePath())))
500    {
501      desc.setStatus(ServerStatus.STARTED);
502
503      if (ctx == null && lastWorkingBindDN != null)
504      {
505        // Try with previous credentials.
506        try
507        {
508          if (isLocal)
509          {
510            ctx = Utilities.getAdminDirContext(this, lastWorkingBindDN,
511              lastWorkingBindPwd);
512          }
513          else if (lastRemoteAdministrationURL != null)
514          {
515            ctx = createLdapsContext(lastRemoteAdministrationURL,
516                lastWorkingBindDN,
517                lastWorkingBindPwd,
518                getConnectTimeout(), null,
519                getTrustManager(), null);
520          }
521        }
522        catch (ConfigReadException | NamingException cre)
523        {
524          // Ignore: we will ask the user for credentials.
525        }
526        if (ctx != null)
527        {
528          this.ctx = ctx;
529        }
530      }
531
532      if (isLocal && ctx == null)
533      {
534        reader = createNewConfigFromFileReader();
535        ((ConfigFromFile)reader).readConfiguration();
536      }
537      else if (!isLocal && ctx == null)
538      {
539        desc.setStatus(ServerStatus.NOT_CONNECTED_TO_REMOTE);
540        reader = null;
541      }
542      else
543      {
544        Utilities.initializeLegacyConfigurationFramework();
545        reader = createNewConfigFromDirContextReader();
546        ((ConfigFromDirContext) reader).readConfiguration(ctx);
547
548        boolean connectionWorks = checkConnections(ctx, userDataCtx);
549        if (!connectionWorks)
550        {
551          if (isLocal)
552          {
553            // Try with off-line info
554            reader = createNewConfigFromFileReader();
555            ((ConfigFromFile) reader).readConfiguration();
556          }
557          else
558          {
559            desc.setStatus(ServerStatus.NOT_CONNECTED_TO_REMOTE);
560            reader = null;
561          }
562          StaticUtils.close(ctx);
563          this.ctx = null;
564          unregisterConnection(connectionPool, ctx);
565          StaticUtils.close(userDataCtx);
566          userDataCtx = null;
567        }
568      }
569
570      if (reader != null)
571      {
572        desc.setAuthenticated(reader instanceof ConfigFromDirContext);
573        desc.setJavaVersion(reader.getJavaVersion());
574        desc.setOpenConnections(reader.getOpenConnections());
575        desc.setTaskEntries(reader.getTaskEntries());
576        if (reader instanceof ConfigFromDirContext)
577        {
578          ConfigFromDirContext rCtx = (ConfigFromDirContext)reader;
579          desc.setRootMonitor(rCtx.getRootMonitor());
580          desc.setEntryCachesMonitor(rCtx.getEntryCaches());
581          desc.setJvmMemoryUsageMonitor(rCtx.getJvmMemoryUsage());
582          desc.setSystemInformationMonitor(rCtx.getSystemInformation());
583          desc.setWorkQueueMonitor(rCtx.getWorkQueue());
584          desc.setOpenDSVersion(getFirstValueAsString(rCtx.getVersionMonitor(), "fullVersion"));
585          String installPath = getFirstValueAsString(rCtx.getSystemInformation(), "installPath");
586          if (installPath != null)
587          {
588            desc.setInstallPath(installPath);
589          }
590          String instancePath = getFirstValueAsString(rCtx.getSystemInformation(), "instancePath");
591          if (instancePath != null)
592          {
593            desc.setInstancePath(instancePath);
594          }
595        }
596      }
597    }
598    else
599    {
600      desc.setStatus(ServerStatus.STOPPED);
601      desc.setAuthenticated(false);
602      reader = createNewConfigFromFileReader();
603      ((ConfigFromFile)reader).readConfiguration();
604    }
605    if (reader != null)
606    {
607      updateServerDescriptor(reader, desc);
608    }
609
610    if (serverDesc == null || !serverDesc.equals(desc))
611    {
612      serverDesc = desc;
613      ldapURL = getURL(serverDesc, ConnectionHandlerDescriptor.Protocol.LDAP);
614      ldapsURL = getURL(serverDesc, ConnectionHandlerDescriptor.Protocol.LDAPS);
615      adminConnectorURL = getAdminConnectorURL(serverDesc);
616      if (serverDesc.isLocal())
617      {
618        localAdminConnectorURL = adminConnectorURL;
619      }
620      startTLSURL = getURL(serverDesc,
621          ConnectionHandlerDescriptor.Protocol.LDAP_STARTTLS);
622      ConfigurationChangeEvent ev = new ConfigurationChangeEvent(this, desc);
623      for (ConfigChangeListener listener : configListeners)
624      {
625        listener.configurationChanged(ev);
626      }
627    }
628  }
629
630  private ServerStatus getStatus(ServerDescriptor desc)
631  {
632    ServerStatus status = null;
633    for (Task task : getTasks())
634    {
635      if (task.getType() == Type.START_SERVER
636          && task.getState() == State.RUNNING
637          && isRunningOnServer(desc, task))
638      {
639        status = ServerStatus.STARTING;
640      }
641      else if (task.getType() == Type.STOP_SERVER && task.getState() == State.RUNNING
642          && isRunningOnServer(desc, task))
643      {
644        status = ServerStatus.STOPPING;
645      }
646    }
647    return status;
648  }
649
650  private void unregisterConnection(LDAPConnectionPool connectionPool, InitialLdapContext userDataCtx)
651  {
652    if (connectionPool.isConnectionRegistered(userDataCtx))
653    {
654      try
655      {
656        connectionPool.unregisterConnection(userDataCtx);
657      }
658      catch (Throwable t)
659      {
660      }
661    }
662  }
663
664  /**
665   * Adds a configuration change listener.
666   * @param listener the listener.
667   */
668  public void addConfigChangeListener(ConfigChangeListener listener)
669  {
670    configListeners.add(listener);
671  }
672
673  /**
674   * Removes a configuration change listener.
675   * @param listener the listener.
676   * @return <CODE>true</CODE> if the listener is found and <CODE>false</CODE>
677   * otherwise.
678   */
679  public boolean removeConfigChangeListener(ConfigChangeListener listener)
680  {
681    return configListeners.remove(listener);
682  }
683
684  /**
685   * Adds a backup creation listener.
686   * @param listener the listener.
687   */
688  public void addBackupCreatedListener(BackupCreatedListener listener)
689  {
690    backupListeners.add(listener);
691  }
692
693  /**
694   * Removes a backup creation listener.
695   * @param listener the listener.
696   * @return <CODE>true</CODE> if the listener is found and <CODE>false</CODE>
697   * otherwise.
698   */
699  public boolean removeBackupCreatedListener(BackupCreatedListener listener)
700  {
701    return backupListeners.remove(listener);
702  }
703
704  /**
705   * Adds a backend populated listener.
706   * @param listener the listener.
707   */
708  public void addBackendPopulatedListener(BackendPopulatedListener listener)
709  {
710    backendPopulatedListeners.add(listener);
711  }
712
713  /**
714   * Removes a backend populated listener.
715   * @param listener the listener.
716   * @return <CODE>true</CODE> if the listener is found and <CODE>false</CODE>
717   * otherwise.
718   */
719  public boolean removeBackendPopulatedListener(
720      BackendPopulatedListener listener)
721  {
722    return backendPopulatedListeners.remove(listener);
723  }
724
725  /**
726   * Adds an index modification listener.
727   * @param listener the listener.
728   */
729  public void addIndexModifiedListener(IndexModifiedListener listener)
730  {
731    indexListeners.add(listener);
732  }
733
734  /**
735   * Removes an index modification listener.
736   * @param listener the listener.
737   * @return <CODE>true</CODE> if the listener is found and <CODE>false</CODE>
738   * otherwise.
739   */
740  public boolean removeIndexModifiedListener(IndexModifiedListener listener)
741  {
742    return indexListeners.remove(listener);
743  }
744
745  /**
746   * Starts pooling the server configuration.  The period of the pooling is
747   * specified as a parameter.  This method is asynchronous and it will start
748   * the pooling in another thread.
749   */
750  public synchronized void startPooling()
751  {
752    if (poolingThread != null)
753    {
754      return;
755    }
756    pooling = true;
757    stopPooling = false;
758
759    poolingThread = new Thread(new Runnable()
760    {
761      @Override
762      public void run()
763      {
764        try
765        {
766          while (!stopPooling)
767          {
768            cleanupTasks();
769            regenerateDescriptor();
770            Thread.sleep(poolingPeriod);
771          }
772        }
773        catch (Throwable t)
774        {
775        }
776        pooling = false;
777      }
778    });
779    poolingThread.start();
780  }
781
782  /**
783   * Stops pooling the server.  This method is synchronous, it does not return
784   * until the pooling is actually stopped.
785   */
786  public synchronized void stopPooling()
787  {
788    stopPooling = true;
789    while (poolingThread != null && pooling)
790    {
791      try
792      {
793        poolingThread.interrupt();
794        Thread.sleep(100);
795      }
796      catch (Throwable t)
797      {
798        // do nothing;
799      }
800    }
801    poolingThread = null;
802    pooling = false;
803  }
804
805  /**
806   * Returns the trust manager to be used by this ControlPanelInfo (and in
807   * general by the control panel).
808   * @return the trust manager to be used by this ControlPanelInfo.
809   */
810  public ApplicationTrustManager getTrustManager()
811  {
812    return trustManager;
813  }
814
815  /**
816   * Sets the trust manager to be used by this ControlPanelInfo (and in
817   * general by the control panel).
818   * @param trustManager the trust manager to be used by this ControlPanelInfo.
819   */
820  public void setTrustManager(ApplicationTrustManager trustManager)
821  {
822    this.trustManager = trustManager;
823    connectionPool.setTrustManager(trustManager);
824  }
825
826  /**
827   * Returns the timeout to establish the connection in milliseconds.
828   * @return the timeout to establish the connection in milliseconds.
829   */
830  public int getConnectTimeout()
831  {
832    return connectTimeout;
833  }
834
835  /**
836   * Sets the timeout to establish the connection in milliseconds.
837   * Use {@code 0} to express no timeout.
838   * @param connectTimeout the timeout to establish the connection in
839   * milliseconds.
840   * Use {@code 0} to express no timeout.
841   */
842  public void setConnectTimeout(int connectTimeout)
843  {
844    this.connectTimeout = connectTimeout;
845    connectionPool.setConnectTimeout(connectTimeout);
846  }
847
848  /**
849   * Returns the connection policy to be used by this ControlPanelInfo (and in
850   * general by the control panel).
851   * @return the connection policy to be used by this ControlPanelInfo.
852   */
853  public ConnectionProtocolPolicy getConnectionPolicy()
854  {
855    return connectionPolicy;
856  }
857
858  /**
859   * Sets the connection policy to be used by this ControlPanelInfo (and in
860   * general by the control panel).
861   * @param connectionPolicy the connection policy to be used by this
862   * ControlPanelInfo.
863   */
864  public void setConnectionPolicy(ConnectionProtocolPolicy connectionPolicy)
865  {
866    this.connectionPolicy = connectionPolicy;
867  }
868
869  /**
870   * Gets the LDAPS URL based in what is read in the configuration. It
871   * returns <CODE>null</CODE> if no LDAPS URL was found.
872   * @return the LDAPS URL to be used to connect to the server.
873   */
874  public String getLDAPSURL()
875  {
876    return ldapsURL;
877  }
878
879  /**
880   * Gets the Administration Connector URL based in what is read in the
881   * configuration. It returns <CODE>null</CODE> if no Administration
882   * Connector URL was found.
883   * @return the Administration Connector URL to be used to connect
884   * to the server.
885   */
886  public String getAdminConnectorURL()
887  {
888    if (isLocal)
889    {
890      // If the user set isLocal to true, we want to return the
891      // localAdminConnectorURL (in particular if regenerateDescriptor has not
892      // been called).
893      return localAdminConnectorURL;
894    }
895    return adminConnectorURL;
896  }
897
898  /**
899   * Gets the Administration Connector URL based in what is read in the local
900   * configuration. It returns <CODE>null</CODE> if no Administration
901   * Connector URL was found.
902   * @return the Administration Connector URL to be used to connect
903   * to the local server.
904   */
905  public String getLocalAdminConnectorURL()
906  {
907    return localAdminConnectorURL;
908  }
909
910  /**
911   * Gets the LDAP URL based in what is read in the configuration. It
912   * returns <CODE>null</CODE> if no LDAP URL was found.
913   * @return the LDAP URL to be used to connect to the server.
914   */
915  public String getLDAPURL()
916  {
917    return ldapURL;
918  }
919
920  /**
921   * Gets the Start TLS URL based in what is read in the configuration. It
922   * returns <CODE>null</CODE> if no Start TLS URL is found.
923   * @return the Start TLS URL to be used to connect to the server.
924   */
925  public String getStartTLSURL()
926  {
927    return startTLSURL;
928  }
929
930  /**
931   * Returns the LDAP URL to be used to connect to a given ServerDescriptor
932   * using a certain protocol. It returns <CODE>null</CODE> if URL for the
933   * protocol is not found.
934   * @param server the server descriptor.
935   * @param protocol the protocol to be used.
936   * @return the LDAP URL to be used to connect to a given ServerDescriptor
937   * using a certain protocol.
938   */
939  private static String getURL(ServerDescriptor server,
940      ConnectionHandlerDescriptor.Protocol protocol)
941  {
942    String sProtocol = toString(protocol);
943
944    String url = null;
945    for (ConnectionHandlerDescriptor desc : server.getConnectionHandlers())
946    {
947      if (desc.getState() == ConnectionHandlerDescriptor.State.ENABLED
948          && desc.getProtocol() == protocol)
949      {
950        int port = desc.getPort();
951        if (port > 0)
952        {
953          if (server.isLocal())
954          {
955            SortedSet<InetAddress> addresses = desc.getAddresses();
956            if (addresses.isEmpty())
957            {
958              url = sProtocol +"://localhost:"+port;
959            }
960            else
961            {
962              InetAddress address = addresses.first();
963              url = sProtocol + "://" + getHostNameForLdapUrl(address.getHostAddress()) + ":" + port;
964            }
965          }
966          else
967          {
968            url = sProtocol + "://" + getHostNameForLdapUrl(server.getHostname()) + ":" + port;
969          }
970        }
971      }
972    }
973    return url;
974  }
975
976  private static String toString(ConnectionHandlerDescriptor.Protocol protocol)
977  {
978    switch (protocol)
979    {
980    case LDAP:
981      return "ldap";
982    case LDAPS:
983      return "ldaps";
984    case LDAP_STARTTLS:
985      return "ldap";
986    case JMX:
987      return "jmx";
988    case JMXS:
989      return "jmxs";
990    default:
991      return null;
992    }
993  }
994
995  /**
996   * Returns the Administration Connector URL.
997   * It returns <CODE>null</CODE> if URL for the
998   * protocol is not found.
999   * @param server the server descriptor.
1000   * @return the Administration Connector URL.
1001   */
1002  private static String getAdminConnectorURL(ServerDescriptor server) {
1003    ConnectionHandlerDescriptor desc = server.getAdminConnector();
1004    if (desc != null)
1005    {
1006      int port = desc.getPort();
1007      if (port > 0) {
1008        SortedSet<InetAddress> addresses = desc.getAddresses();
1009        if (!addresses.isEmpty())
1010        {
1011          String hostAddr = addresses.first().getHostAddress();
1012          return getLDAPUrl(hostAddr, port, true);
1013        }
1014        else
1015        {
1016          return getLDAPUrl("localhost", port, true);
1017        }
1018      }
1019    }
1020    return null;
1021  }
1022
1023  /**
1024   * Tells whether we must connect to the server using Start TLS.
1025   * @return <CODE>true</CODE> if we must connect to the server using Start TLS
1026   * and <CODE>false</CODE> otherwise.
1027   */
1028  public boolean connectUsingStartTLS()
1029  {
1030    if (getStartTLSURL() != null)
1031    {
1032      return getStartTLSURL().equals(getURLToConnect());
1033    }
1034    return false;
1035  }
1036
1037  /**
1038   * Tells whether we must connect to the server using LDAPS.
1039   * @return <CODE>true</CODE> if we must connect to the server using LDAPS
1040   * and <CODE>false</CODE> otherwise.
1041   */
1042  public boolean connectUsingLDAPS()
1043  {
1044    if (getLDAPSURL() != null)
1045    {
1046      return getLDAPSURL().equals(getURLToConnect());
1047    }
1048    return false;
1049  }
1050
1051  /**
1052   * Returns the URL that must be used to connect to the server based on the
1053   * available enabled connection handlers in the server and the connection
1054   * policy.
1055   * @return the URL that must be used to connect to the server.
1056   */
1057  public String getURLToConnect()
1058  {
1059    String url;
1060    switch (getConnectionPolicy())
1061    {
1062    case USE_STARTTLS:
1063      return getStartTLSURL();
1064    case USE_LDAP:
1065      return getLDAPURL();
1066    case USE_LDAPS:
1067      return getLDAPSURL();
1068    case USE_ADMIN:
1069      return getAdminConnectorURL();
1070    case USE_MOST_SECURE_AVAILABLE:
1071      url = getLDAPSURL();
1072      if (url == null)
1073      {
1074        url = getStartTLSURL();
1075      }
1076      if (url == null)
1077      {
1078        url = getLDAPURL();
1079      }
1080      return url;
1081    case USE_LESS_SECURE_AVAILABLE:
1082      url = getLDAPURL();
1083      if (url == null)
1084      {
1085        url = getStartTLSURL();
1086      }
1087      if (url == null)
1088      {
1089        url = getLDAPSURL();
1090      }
1091      return url;
1092    default:
1093      throw new RuntimeException("Unknown policy: "+getConnectionPolicy());
1094    }
1095  }
1096
1097  /**
1098   * Returns <CODE>true</CODE> if the configuration must be deregistered and
1099   * <CODE>false</CODE> otherwise.
1100   * This is required when we use the ConfigFileHandler to update the
1101   * configuration, in these cases cn=config must the deregistered from the
1102   * ConfigFileHandler and after that register again.
1103   * @return <CODE>true</CODE> if the configuration must be deregistered and
1104   * <CODE>false</CODE> otherwise.
1105   */
1106  public boolean mustDeregisterConfig()
1107  {
1108    return mustDeregisterConfig;
1109  }
1110
1111  /**
1112   * Sets whether the configuration must be deregistered or not.
1113   * @param mustDeregisterConfig whether the configuration must be deregistered
1114   * or not.
1115   */
1116  public void setMustDeregisterConfig(boolean mustDeregisterConfig)
1117  {
1118    ControlPanelInfo.mustDeregisterConfig = mustDeregisterConfig;
1119  }
1120
1121  /**
1122   * Sets whether the server is local or not.
1123   * @param isLocal whether the server is local or not.
1124   */
1125  public void setIsLocal(boolean isLocal)
1126  {
1127    this.isLocal = isLocal;
1128  }
1129
1130  /**
1131   * Returns <CODE>true</CODE> if we are trying to manage the local host and
1132   * <CODE>false</CODE> otherwise.
1133   * @return <CODE>true</CODE> if we are trying to manage the local host and
1134   * <CODE>false</CODE> otherwise.
1135   */
1136  public boolean isLocal()
1137  {
1138    return isLocal;
1139  }
1140
1141  /**
1142   * Returns the connection pool to be used by the LDAP entry browsers.
1143   * @return the connection pool to be used by the LDAP entry browsers.
1144   */
1145  public LDAPConnectionPool getConnectionPool()
1146  {
1147    return connectionPool;
1148  }
1149
1150  /**
1151   * Returns the icon pool to be used by the LDAP entry browsers.
1152   * @return the icon pool to be used by the LDAP entry browsers.
1153   */
1154  public IconPool getIconPool()
1155  {
1156    return iconPool;
1157  }
1158
1159  /**
1160   * Returns the pooling period in miliseconds.
1161   * @return the pooling period in miliseconds.
1162   */
1163  public long getPoolingPeriod()
1164  {
1165    return poolingPeriod;
1166  }
1167
1168  /**
1169   * Sets the pooling period in miliseconds.
1170   * @param poolingPeriod the pooling time in miliseconds.
1171   */
1172  public void setPoolingPeriod(long poolingPeriod)
1173  {
1174    this.poolingPeriod = poolingPeriod;
1175  }
1176
1177  /** Cleans the tasks that are over. */
1178  private void cleanupTasks()
1179  {
1180    Set<Task> toClean = new HashSet<>();
1181    for (Task task : tasks)
1182    {
1183      if (task.getState() == Task.State.FINISHED_SUCCESSFULLY ||
1184          task.getState() == Task.State.FINISHED_WITH_ERROR)
1185      {
1186        toClean.add(task);
1187      }
1188    }
1189    for (Task task : toClean)
1190    {
1191      unregisterTask(task);
1192    }
1193  }
1194
1195  /**
1196   * Returns whether the provided task is running on the provided server or not.
1197   * The code takes into account that the server object might not be fully
1198   * initialized (but at least it contains the host name and the instance
1199   * path if it is local).
1200   * @param server the server.
1201   * @param task the task to be analyzed.
1202   * @return <CODE>true</CODE> if the provided task is running on the provided
1203   * server and <CODE>false</CODE> otherwise.
1204   */
1205  private boolean isRunningOnServer(ServerDescriptor server, Task task)
1206  {
1207    if (server.isLocal() && task.getServer().isLocal())
1208    {
1209      return true;
1210    }
1211
1212    String host1 = server.getHostname();
1213    String host2 = task.getServer().getHostname();
1214    boolean isRunningOnServer = host1 != null ? host1.equalsIgnoreCase(host2) : host2 == null;
1215    if (!isRunningOnServer)
1216    {
1217      return false;
1218    }
1219
1220    if (server.isLocal())
1221    {
1222      // Compare paths
1223      String path1 = server.getInstancePath();
1224      String path2 = task.getServer().getInstancePath();
1225      return Objects.equals(path1, path2);
1226    }
1227
1228    // At this point we only have connection information about the new server.
1229    // Use the dir context which corresponds to the server to compare things.
1230
1231    // Compare administration port;
1232    int adminPort1 = -1;
1233    int adminPort2 = -1;
1234    if (server.getAdminConnector() != null)
1235    {
1236      adminPort1 = server.getAdminConnector().getPort();
1237    }
1238
1239    if (getDirContext() != null)
1240    {
1241      adminPort2 = ConnectionUtils.getPort(getDirContext());
1242    }
1243    return adminPort1 == adminPort2;
1244  }
1245
1246  private boolean checkConnections(InitialLdapContext ctx, InitialLdapContext userCtx)
1247  {
1248    // Check the connection
1249    int nMaxErrors = 5;
1250    for (int i=0; i< nMaxErrors; i++)
1251    {
1252      try
1253      {
1254        Utilities.pingDirContext(ctx);
1255        if (userCtx != null)
1256        {
1257          Utilities.pingDirContext(userCtx);
1258        }
1259        return true;
1260      }
1261      catch (NamingException ne)
1262      {
1263        try
1264        {
1265          Thread.sleep(400);
1266        }
1267        catch (Throwable t)
1268        {
1269        }
1270      }
1271    }
1272    return false;
1273  }
1274
1275  /**
1276   * Initialize the new configuration framework if needed.
1277   *
1278   * @throws org.opends.server.config.ConfigException
1279   *           If error occurred during the initialization
1280   */
1281  public void initializeConfigurationFramework() throws org.opends.server.config.ConfigException
1282  {
1283    if (!ConfigurationFramework.getInstance().isInitialized())
1284    {
1285      try
1286      {
1287        ConfigurationFramework.getInstance().initialize();
1288      }
1289      catch (ConfigException ce)
1290      {
1291        throw new org.opends.server.config.ConfigException(ce.getMessageObject(), ce);
1292      }
1293    }
1294  }
1295}