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