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 2006-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2011-2015 ForgeRock AS
026 */
027package org.opends.quicksetup.installer;
028
029import static org.forgerock.util.Utils.*;
030import static org.opends.admin.ads.ServerDescriptor.*;
031import static org.opends.admin.ads.ServerDescriptor.ServerProperty.*;
032import static org.opends.admin.ads.util.ConnectionUtils.*;
033import static org.opends.messages.QuickSetupMessages.*;
034import static org.opends.quicksetup.Step.*;
035import static org.opends.quicksetup.installer.DataReplicationOptions.Type.*;
036import static org.opends.quicksetup.installer.InstallProgressStep.*;
037import static org.opends.quicksetup.util.Utils.*;
038
039import static com.forgerock.opendj.cli.ArgumentConstants.*;
040import static com.forgerock.opendj.cli.Utils.*;
041
042import java.awt.event.WindowEvent;
043import java.io.BufferedWriter;
044import java.io.File;
045import java.io.FileWriter;
046import java.io.IOException;
047import java.net.URI;
048import java.util.ArrayList;
049import java.util.Collection;
050import java.util.Collections;
051import java.util.HashMap;
052import java.util.HashSet;
053import java.util.LinkedHashSet;
054import java.util.LinkedList;
055import java.util.List;
056import java.util.Map;
057import java.util.Set;
058
059import javax.naming.NameAlreadyBoundException;
060import javax.naming.NameNotFoundException;
061import javax.naming.NamingEnumeration;
062import javax.naming.NamingException;
063import javax.naming.NamingSecurityException;
064import javax.naming.directory.Attribute;
065import javax.naming.directory.BasicAttribute;
066import javax.naming.directory.BasicAttributes;
067import javax.naming.directory.DirContext;
068import javax.naming.directory.SearchControls;
069import javax.naming.directory.SearchResult;
070import javax.naming.ldap.InitialLdapContext;
071import javax.naming.ldap.Rdn;
072import javax.swing.JPanel;
073
074import org.forgerock.i18n.LocalizableMessage;
075import org.forgerock.i18n.LocalizableMessageBuilder;
076import org.forgerock.i18n.LocalizableMessageDescriptor.Arg0;
077import org.forgerock.i18n.slf4j.LocalizedLogger;
078import org.forgerock.opendj.config.ManagedObjectDefinition;
079import org.forgerock.opendj.server.config.client.BackendCfgClient;
080import org.forgerock.opendj.server.config.server.BackendCfg;
081import org.opends.admin.ads.ADSContext;
082import org.opends.admin.ads.ADSContextException;
083import org.opends.admin.ads.ReplicaDescriptor;
084import org.opends.admin.ads.ServerDescriptor;
085import org.opends.admin.ads.SuffixDescriptor;
086import org.opends.admin.ads.TopologyCache;
087import org.opends.admin.ads.TopologyCacheException;
088import org.opends.admin.ads.TopologyCacheFilter;
089import org.opends.admin.ads.util.ApplicationTrustManager;
090import org.opends.admin.ads.util.ConnectionUtils;
091import org.opends.admin.ads.util.PreferredConnection;
092import org.opends.quicksetup.ApplicationException;
093import org.opends.quicksetup.ButtonName;
094import org.opends.quicksetup.Constants;
095import org.opends.quicksetup.Installation;
096import org.opends.quicksetup.JavaArguments;
097import org.opends.quicksetup.LicenseFile;
098import org.opends.quicksetup.ProgressStep;
099import org.opends.quicksetup.QuickSetupLog;
100import org.opends.quicksetup.ReturnCode;
101import org.opends.quicksetup.SecurityOptions;
102import org.opends.quicksetup.Step;
103import org.opends.quicksetup.UserData;
104import org.opends.quicksetup.UserDataCertificateException;
105import org.opends.quicksetup.UserDataConfirmationException;
106import org.opends.quicksetup.UserDataException;
107import org.opends.quicksetup.WizardStep;
108import org.opends.quicksetup.event.ButtonActionListener;
109import org.opends.quicksetup.event.ButtonEvent;
110import org.opends.quicksetup.installer.ui.DataOptionsPanel;
111import org.opends.quicksetup.installer.ui.DataReplicationPanel;
112import org.opends.quicksetup.installer.ui.GlobalAdministratorPanel;
113import org.opends.quicksetup.installer.ui.InstallLicensePanel;
114import org.opends.quicksetup.installer.ui.InstallReviewPanel;
115import org.opends.quicksetup.installer.ui.InstallWelcomePanel;
116import org.opends.quicksetup.installer.ui.RemoteReplicationPortsPanel;
117import org.opends.quicksetup.installer.ui.RuntimeOptionsPanel;
118import org.opends.quicksetup.installer.ui.ServerSettingsPanel;
119import org.opends.quicksetup.installer.ui.SuffixesToReplicatePanel;
120import org.opends.quicksetup.ui.FieldName;
121import org.opends.quicksetup.ui.FinishedPanel;
122import org.opends.quicksetup.ui.GuiApplication;
123import org.opends.quicksetup.ui.ProgressPanel;
124import org.opends.quicksetup.ui.QuickSetup;
125import org.opends.quicksetup.ui.QuickSetupDialog;
126import org.opends.quicksetup.ui.QuickSetupErrorPanel;
127import org.opends.quicksetup.ui.QuickSetupStepPanel;
128import org.opends.quicksetup.ui.UIFactory;
129import org.opends.quicksetup.util.FileManager;
130import org.opends.quicksetup.util.IncompatibleVersionException;
131import org.opends.quicksetup.util.Utils;
132import org.opends.server.tools.BackendTypeHelper;
133import org.opends.server.tools.BackendTypeHelper.BackendTypeUIAdapter;
134import org.opends.server.util.CertificateManager;
135import org.opends.server.util.DynamicConstants;
136import org.opends.server.util.SetupUtils;
137import org.opends.server.util.StaticUtils;
138
139import com.forgerock.opendj.util.OperatingSystem;
140
141/**
142 * This is an abstract class that is in charge of actually performing the
143 * installation.
144 *
145 * It just takes a UserData object and based on that installs OpenDJ.
146 *
147 * When there is an update during the installation it will notify the
148 * ProgressUpdateListener objects that have been added to it.  The
149 * notification will send a ProgressUpdateEvent.
150 *
151 * This class is supposed to be fully independent of the graphical layout.
152 *
153 * Note that we can use freely the class org.opends.server.util.SetupUtils as
154 * it is included in quicksetup.jar.
155 */
156public abstract class Installer extends GuiApplication
157{
158  /** The minimum integer value that can be used for a port. */
159  public static final int MIN_PORT_VALUE = 1;
160  /** The maximum integer value that can be used for a port. */
161  public static final int MAX_PORT_VALUE = 65535;
162
163  /** The name of the backend created on setup. */
164  public static final String ROOT_BACKEND_NAME = "userRoot";
165
166  /** Constants used to do checks. */
167  private static final int MIN_DIRECTORY_MANAGER_PWD = 1;
168
169  private static final int MIN_NUMBER_ENTRIES = 1;
170  private static final int MAX_NUMBER_ENTRIES = 10000000;
171
172  /**
173   * If the user decides to import more than this number of entries, the import
174   * process of automatically generated data will be verbose.
175   */
176  private static final int THRESHOLD_AUTOMATIC_DATA_VERBOSE = 20000;
177
178  /**
179   * If the user decides to import a number of entries higher than this
180   * threshold, the start process will be verbose.
181   */
182  private static final int THRESHOLD_VERBOSE_START = 100000;
183
184  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
185
186  private TopologyCache lastLoadedCache;
187
188  /** Indicates that we've detected that there is something installed. */
189  boolean forceToDisplaySetup;
190
191  /** When true indicates that the user has canceled this operation. */
192  protected boolean canceled;
193
194  private boolean javaVersionCheckFailed;
195
196  /** Map containing information about what has been configured remotely. */
197  private final Map<ServerDescriptor, ConfiguredReplication> hmConfiguredRemoteReplication = new HashMap<>();
198
199  /** Set of progress steps that have been completed. */
200  protected Set<InstallProgressStep> completedProgress = new HashSet<>();
201
202  private final List<WizardStep> lstSteps = new ArrayList<>();
203
204  private final Set<WizardStep> SUBSTEPS = new HashSet<>();
205  {
206    SUBSTEPS.add(Step.CREATE_GLOBAL_ADMINISTRATOR);
207    SUBSTEPS.add(Step.SUFFIXES_OPTIONS);
208    SUBSTEPS.add(Step.NEW_SUFFIX_OPTIONS);
209    SUBSTEPS.add(Step.REMOTE_REPLICATION_PORTS);
210  }
211
212  private final Map<WizardStep, WizardStep> hmPreviousSteps = new HashMap<>();
213
214  private char[] selfSignedCertPw;
215
216  private boolean registeredNewServerOnRemote;
217  private boolean createdAdministrator;
218  private boolean createdRemoteAds;
219  private String lastImportProgress;
220
221  /** A static String that contains the class name of ConfigFileHandler. */
222  protected static final String DEFAULT_CONFIG_CLASS_NAME = "org.opends.server.extensions.ConfigFileHandler";
223
224  /** Alias of a self-signed certificate. */
225  protected static final String SELF_SIGNED_CERT_ALIAS = SecurityOptions.SELF_SIGNED_CERT_ALIAS;
226
227  /**
228   * The threshold in minutes used to know whether we must display a warning
229   * informing that there is a server clock difference between two servers whose
230   * contents are being replicated.
231   */
232  public static final int THRESHOLD_CLOCK_DIFFERENCE_WARNING = 5;
233
234  /** Creates a default instance. */
235  public Installer()
236  {
237    addStepsInOrder(lstSteps, LicenseFile.exists());
238    try
239    {
240      if (!QuickSetupLog.isInitialized())
241      {
242        QuickSetupLog.initLogFileHandler(File.createTempFile(Constants.LOG_FILE_PREFIX, Constants.LOG_FILE_SUFFIX));
243      }
244    }
245    catch (IOException e)
246    {
247      System.err.println("Failed to initialize log");
248    }
249  }
250
251  @Override
252  public boolean isCancellable()
253  {
254    return true;
255  }
256
257  @Override
258  public UserData createUserData()
259  {
260    UserData ud = new UserData();
261    ud.setServerLocation(getDefaultServerLocation());
262    initializeUserDataWithUserArguments(ud, getUserArguments());
263    return ud;
264  }
265
266  private void initializeUserDataWithUserArguments(UserData ud, String[] userArguments)
267  {
268    for (int i = 0; i < userArguments.length; i++)
269    {
270      if ("--connectTimeout".equalsIgnoreCase(userArguments[i]))
271      {
272        if (i < userArguments.length - 1)
273        {
274          String sTimeout = userArguments[i + 1];
275          try
276          {
277            ud.setConnectTimeout(Integer.valueOf(sTimeout));
278          }
279          catch (Throwable t)
280          {
281            logger.warn(LocalizableMessage.raw("Error getting connect timeout: " + t, t));
282          }
283        }
284        break;
285      }
286    }
287  }
288
289  @Override
290  public void forceToDisplay()
291  {
292    forceToDisplaySetup = true;
293  }
294
295  @Override
296  public boolean canGoBack(WizardStep step)
297  {
298    return step != WELCOME && step != PROGRESS && step != FINISHED;
299  }
300
301  @Override
302  public boolean canGoForward(WizardStep step)
303  {
304    return step != REVIEW && step != PROGRESS && step != FINISHED;
305  }
306
307  @Override
308  public boolean canFinish(WizardStep step)
309  {
310    return step == REVIEW;
311  }
312
313    @Override
314  public boolean isSubStep(WizardStep step)
315  {
316    return SUBSTEPS.contains(step);
317  }
318
319  @Override
320  public boolean isVisible(WizardStep step, UserData userData)
321  {
322    if (step == CREATE_GLOBAL_ADMINISTRATOR)
323    {
324      return userData.mustCreateAdministrator();
325    }
326    else if (step == NEW_SUFFIX_OPTIONS)
327    {
328      SuffixesToReplicateOptions suf = userData.getSuffixesToReplicateOptions();
329      return suf != null && suf.getType() != SuffixesToReplicateOptions.Type.REPLICATE_WITH_EXISTING_SUFFIXES;
330    }
331    else if (step == SUFFIXES_OPTIONS)
332    {
333      DataReplicationOptions repl = userData.getReplicationOptions();
334      return repl != null && repl.getType() != DataReplicationOptions.Type.STANDALONE
335          && repl.getType() != DataReplicationOptions.Type.FIRST_IN_TOPOLOGY;
336    }
337    else if (step == REMOTE_REPLICATION_PORTS)
338    {
339      return isVisible(SUFFIXES_OPTIONS, userData)
340          && !userData.getRemoteWithNoReplicationPort().isEmpty()
341          && userData.getSuffixesToReplicateOptions().getType() ==
342              SuffixesToReplicateOptions.Type.REPLICATE_WITH_EXISTING_SUFFIXES;
343    }
344    return true;
345  }
346
347  @Override
348  public boolean isVisible(WizardStep step, QuickSetup qs)
349  {
350    return isVisible(step, getUserData());
351  }
352
353  @Override
354  public boolean finishClicked(final WizardStep cStep, final QuickSetup qs)
355  {
356    if (cStep != Step.REVIEW)
357    {
358      throw new IllegalStateException("Cannot click on finish when we are not in the Review window");
359    }
360
361    updateUserDataForReviewPanel(qs);
362    qs.launch();
363    qs.setCurrentStep(Step.PROGRESS);
364    // Installer responsible for updating the user data and launching
365    return false;
366  }
367
368    @Override
369  public void nextClicked(WizardStep cStep, QuickSetup qs)
370  {
371    if (cStep == PROGRESS)
372    {
373      throw new IllegalStateException("Cannot click on next from progress step");
374    }
375    else if (cStep == REVIEW)
376    {
377      throw new IllegalStateException("Cannot click on next from review step");
378    }
379    else if (cStep == FINISHED)
380    {
381      throw new IllegalStateException("Cannot click on next from finished step");
382    }
383  }
384
385  @Override
386  public void closeClicked(WizardStep cStep, QuickSetup qs)
387  {
388    if (cStep == PROGRESS)
389    {
390      if (isFinished()
391          || qs.displayConfirmation(INFO_CONFIRM_CLOSE_INSTALL_MSG.get(), INFO_CONFIRM_CLOSE_INSTALL_TITLE.get()))
392      {
393        qs.quit();
394      }
395    }
396    else if (cStep == FINISHED)
397    {
398      qs.quit();
399    }
400    else
401    {
402      throw new IllegalStateException("Close only can be clicked on PROGRESS step");
403    }
404  }
405
406  @Override
407  public boolean isFinished()
408  {
409    return getCurrentProgressStep() == InstallProgressStep.FINISHED_SUCCESSFULLY
410        || getCurrentProgressStep() == InstallProgressStep.FINISHED_CANCELED
411        || getCurrentProgressStep() == InstallProgressStep.FINISHED_WITH_ERROR;
412  }
413
414  @Override
415  public void cancel()
416  {
417    setCurrentProgressStep(InstallProgressStep.WAITING_TO_CANCEL);
418    notifyListeners(null);
419    this.canceled = true;
420  }
421
422  @Override
423  public void quitClicked(WizardStep cStep, QuickSetup qs)
424  {
425    if (cStep == FINISHED)
426    {
427      qs.quit();
428    }
429    else if (cStep == PROGRESS)
430    {
431      throw new IllegalStateException("Cannot click on quit from progress step");
432    }
433    else if (installStatus.isInstalled())
434    {
435      qs.quit();
436    }
437    else if (javaVersionCheckFailed)
438    {
439      qs.quit();
440    }
441    else if (qs.displayConfirmation(INFO_CONFIRM_QUIT_INSTALL_MSG.get(), INFO_CONFIRM_QUIT_INSTALL_TITLE.get()))
442    {
443      qs.quit();
444    }
445  }
446
447  @Override
448  public ButtonName getInitialFocusButtonName()
449  {
450    if (!installStatus.isInstalled() || forceToDisplaySetup)
451    {
452      return ButtonName.NEXT;
453    }
454    else if (installStatus.canOverwriteCurrentInstall())
455    {
456      return ButtonName.CONTINUE_INSTALL;
457    }
458    else
459    {
460      return ButtonName.QUIT;
461    }
462  }
463
464  @Override
465  public JPanel createFramePanel(QuickSetupDialog dlg)
466  {
467    JPanel p;
468    javaVersionCheckFailed = true;
469    try
470    {
471      Utils.checkJavaVersion();
472      javaVersionCheckFailed = false;
473      if (installStatus.isInstalled() && !forceToDisplaySetup)
474      {
475        p = dlg.getInstalledPanel();
476      }
477      else
478      {
479        p = super.createFramePanel(dlg);
480      }
481    }
482    catch (IncompatibleVersionException ijv)
483    {
484      LocalizableMessageBuilder sb = new LocalizableMessageBuilder();
485      sb.append(Utils.breakHtmlString(Utils.getHtml(ijv.getMessageObject().toString()),
486          Constants.MAX_CHARS_PER_LINE_IN_DIALOG));
487      QuickSetupErrorPanel errPanel = new QuickSetupErrorPanel(this, sb.toMessage());
488      final QuickSetupDialog fDlg = dlg;
489      errPanel.addButtonActionListener(new ButtonActionListener()
490      {
491        /**
492         * ButtonActionListener implementation. It assumes that we are called in
493         * the event thread.
494         *
495         * @param ev
496         *          the ButtonEvent we receive.
497         */
498        @Override
499        public void buttonActionPerformed(ButtonEvent ev)
500        {
501          // Simulate a close button event
502          fDlg.notifyButtonEvent(ButtonName.QUIT);
503        }
504      });
505      p = errPanel;
506    }
507    return p;
508  }
509
510  @Override
511  public Set<? extends WizardStep> getWizardSteps()
512  {
513    return Collections.unmodifiableSet(new HashSet<WizardStep>(lstSteps));
514  }
515
516  @Override
517  public QuickSetupStepPanel createWizardStepPanel(WizardStep step)
518  {
519    if (step instanceof Step)
520    {
521      switch ((Step) step)
522      {
523      case WELCOME:
524        return new InstallWelcomePanel(this);
525      case LICENSE:
526        return new InstallLicensePanel(this);
527      case SERVER_SETTINGS:
528        return new ServerSettingsPanel(this);
529      case REPLICATION_OPTIONS:
530        return new DataReplicationPanel(this);
531      case CREATE_GLOBAL_ADMINISTRATOR:
532        return new GlobalAdministratorPanel(this);
533      case SUFFIXES_OPTIONS:
534        return new SuffixesToReplicatePanel(this);
535      case REMOTE_REPLICATION_PORTS:
536        return new RemoteReplicationPortsPanel(this);
537      case NEW_SUFFIX_OPTIONS:
538        return new DataOptionsPanel(this);
539      case RUNTIME_OPTIONS:
540        return new RuntimeOptionsPanel(this);
541      case REVIEW:
542        return new InstallReviewPanel(this);
543      case PROGRESS:
544        return new ProgressPanel(this);
545      case FINISHED:
546        return new FinishedPanel(this);
547      }
548    }
549    return null;
550  }
551
552  @Override
553  public void windowClosing(QuickSetupDialog dlg, WindowEvent evt)
554  {
555    if (installStatus.isInstalled() && forceToDisplaySetup)
556    {
557      // Simulate a close button event
558      dlg.notifyButtonEvent(ButtonName.QUIT);
559    }
560    else if (dlg.getDisplayedStep() == Step.PROGRESS)
561    {
562      // Simulate a close button event
563      dlg.notifyButtonEvent(ButtonName.CLOSE);
564    }
565    else
566    {
567      // Simulate a quit button event
568      dlg.notifyButtonEvent(ButtonName.QUIT);
569    }
570  }
571
572  @Override
573  public LocalizableMessage getCloseButtonToolTip()
574  {
575    return INFO_CLOSE_BUTTON_INSTALL_TOOLTIP.get();
576  }
577
578  @Override
579  public LocalizableMessage getQuitButtonToolTip()
580  {
581    return INFO_QUIT_BUTTON_INSTALL_TOOLTIP.get();
582  }
583
584  @Override
585  public LocalizableMessage getFinishButtonToolTip()
586  {
587    return INFO_FINISH_BUTTON_INSTALL_TOOLTIP.get();
588  }
589
590  @Override
591  public int getExtraDialogHeight()
592  {
593    return UIFactory.EXTRA_DIALOG_HEIGHT;
594  }
595
596  @Override
597  public void previousClicked(WizardStep cStep, QuickSetup qs)
598  {
599    if (cStep == WELCOME)
600    {
601      throw new IllegalStateException("Cannot click on previous from progress step");
602    }
603    else if (cStep == PROGRESS)
604    {
605      throw new IllegalStateException("Cannot click on previous from progress step");
606    }
607    else if (cStep == FINISHED)
608    {
609      throw new IllegalStateException("Cannot click on previous from finished step");
610    }
611  }
612
613  @Override
614  public LocalizableMessage getFrameTitle()
615  {
616    return Utils.getCustomizedObject("INFO_FRAME_INSTALL_TITLE", INFO_FRAME_INSTALL_TITLE
617        .get(DynamicConstants.PRODUCT_NAME), LocalizableMessage.class);
618  }
619
620  /** Indicates the current progress step. */
621  private InstallProgressStep currentProgressStep = InstallProgressStep.NOT_STARTED;
622
623  @Override
624  public void setWizardDialogState(QuickSetupDialog dlg, UserData userData, WizardStep step)
625  {
626    if (!installStatus.isInstalled() || forceToDisplaySetup)
627    {
628      // Set the default button for the frame
629      if (step == REVIEW)
630      {
631        dlg.setFocusOnButton(ButtonName.FINISH);
632        dlg.setDefaultButton(ButtonName.FINISH);
633      }
634      else if (step == WELCOME)
635      {
636        dlg.setDefaultButton(ButtonName.NEXT);
637        dlg.setFocusOnButton(ButtonName.NEXT);
638      }
639      else if (step == PROGRESS || step == FINISHED)
640      {
641        dlg.setDefaultButton(ButtonName.CLOSE);
642        dlg.setFocusOnButton(ButtonName.CLOSE);
643      }
644      else
645      {
646        dlg.setDefaultButton(ButtonName.NEXT);
647      }
648    }
649  }
650
651  @Override
652  public ProgressStep getCurrentProgressStep()
653  {
654    return currentProgressStep;
655  }
656
657  @Override
658  public WizardStep getFirstWizardStep()
659  {
660    return WELCOME;
661  }
662
663  @Override
664  public WizardStep getNextWizardStep(WizardStep step)
665  {
666    WizardStep next = getNextWizardStep0(step);
667    if (next != null)
668    {
669      hmPreviousSteps.put(next, step);
670    }
671    return next;
672  }
673
674  private WizardStep getNextWizardStep0(WizardStep step)
675  {
676    if (step == Step.REPLICATION_OPTIONS)
677    {
678      if (getUserData().mustCreateAdministrator())
679      {
680        return Step.CREATE_GLOBAL_ADMINISTRATOR;
681      }
682
683      switch (getUserData().getReplicationOptions().getType())
684      {
685      case FIRST_IN_TOPOLOGY:
686      case STANDALONE:
687        return Step.NEW_SUFFIX_OPTIONS;
688      default:
689        return Step.SUFFIXES_OPTIONS;
690      }
691    }
692    else if (step == Step.SUFFIXES_OPTIONS)
693    {
694      switch (getUserData().getSuffixesToReplicateOptions().getType())
695      {
696      case REPLICATE_WITH_EXISTING_SUFFIXES:
697        if (!getUserData().getRemoteWithNoReplicationPort().isEmpty())
698        {
699          return Step.REMOTE_REPLICATION_PORTS;
700        }
701        return Step.RUNTIME_OPTIONS;
702      default:
703        return Step.NEW_SUFFIX_OPTIONS;
704      }
705    }
706    else if (step == Step.REMOTE_REPLICATION_PORTS)
707    {
708      return Step.RUNTIME_OPTIONS;
709    }
710    else
711    {
712      int i = lstSteps.indexOf(step);
713      if (i != -1 && i + 1 < lstSteps.size())
714      {
715        return lstSteps.get(i + 1);
716      }
717    }
718    return null;
719  }
720
721  @Override
722  public LinkedHashSet<WizardStep> getOrderedSteps()
723  {
724    LinkedHashSet<WizardStep> orderedSteps = new LinkedHashSet<>();
725    addStepsInOrder(orderedSteps, lstSteps.contains(LICENSE));
726    return orderedSteps;
727  }
728
729  private void addStepsInOrder(Collection<WizardStep> steps, boolean licenseExists)
730  {
731    steps.add(WELCOME);
732    if (licenseExists)
733    {
734      steps.add(LICENSE);
735    }
736    steps.add(SERVER_SETTINGS);
737    steps.add(REPLICATION_OPTIONS);
738    steps.add(CREATE_GLOBAL_ADMINISTRATOR);
739    steps.add(SUFFIXES_OPTIONS);
740    steps.add(REMOTE_REPLICATION_PORTS);
741    steps.add(NEW_SUFFIX_OPTIONS);
742    steps.add(RUNTIME_OPTIONS);
743    steps.add(REVIEW);
744    steps.add(PROGRESS);
745    steps.add(FINISHED);
746  }
747
748  @Override
749  public WizardStep getPreviousWizardStep(WizardStep step)
750  {
751    //  Try with the steps calculated in method getNextWizardStep.
752    WizardStep prev = hmPreviousSteps.get(step);
753
754    if (prev == null)
755    {
756      int i = lstSteps.indexOf(step);
757      if (i != -1 && i > 0)
758      {
759        prev = lstSteps.get(i - 1);
760      }
761    }
762    return prev;
763  }
764
765  @Override
766  public WizardStep getFinishedStep()
767  {
768    return Step.FINISHED;
769  }
770
771  /**
772   * Uninstalls installed services. This is to be used when the user has elected
773   * to cancel an installation.
774   */
775  protected void uninstallServices()
776  {
777    if (completedProgress.contains(InstallProgressStep.ENABLING_WINDOWS_SERVICE))
778    {
779      try
780      {
781        new InstallerHelper().disableWindowsService();
782      }
783      catch (ApplicationException ae)
784      {
785        logger.info(LocalizableMessage.raw("Error disabling Windows service", ae));
786      }
787    }
788
789    unconfigureRemote();
790  }
791
792  /**
793   * Creates the template files based in the contents of the UserData object.
794   * These templates files are used to generate automatically data. To generate
795   * the template file the code will basically take into account the value of
796   * the base dn and the number of entries to be generated.
797   *
798   * @return a list of file objects pointing to the create template files.
799   * @throws ApplicationException
800   *           if an error occurs.
801   */
802  private File createTemplateFile() throws ApplicationException
803  {
804    try
805    {
806      Set<String> baseDNs = new LinkedHashSet<>(getUserData().getNewSuffixOptions().getBaseDns());
807      int nEntries = getUserData().getNewSuffixOptions().getNumberEntries();
808      return SetupUtils.createTemplateFile(baseDNs, nEntries);
809    }
810    catch (IOException ioe)
811    {
812      LocalizableMessage failedMsg = getThrowableMsg(INFO_ERROR_CREATING_TEMP_FILE.get(), ioe);
813      throw new ApplicationException(ReturnCode.FILE_SYSTEM_ACCESS_ERROR, failedMsg, ioe);
814    }
815  }
816
817  /**
818   * This methods configures the server based on the contents of the UserData
819   * object provided in the constructor.
820   *
821   * @throws ApplicationException
822   *           if something goes wrong.
823   */
824  protected void configureServer() throws ApplicationException
825  {
826    notifyListeners(getFormattedWithPoints(INFO_PROGRESS_CONFIGURING.get()));
827
828    if (Utils.isWebStart())
829    {
830      String installDir = getUserData().getServerLocation();
831      setInstallation(new Installation(installDir, installDir));
832    }
833
834    copyTemplateInstance();
835    writeOpenDSJavaHome();
836    writeHostName();
837    checkAbort();
838
839    List<String> argList = new ArrayList<>();
840    argList.add("-C");
841    argList.add(getConfigurationClassName());
842
843    argList.add("-c");
844    argList.add(getConfigurationFile());
845    argList.add("-h");
846    argList.add(getUserData().getHostName());
847    argList.add("-p");
848    argList.add(String.valueOf(getUserData().getServerPort()));
849    argList.add("--adminConnectorPort");
850    argList.add(String.valueOf(getUserData().getAdminConnectorPort()));
851
852    SecurityOptions sec = getUserData().getSecurityOptions();
853    // TODO: even if the user does not configure SSL maybe we should choose
854    // a secure port that is not being used and that we can actually use.
855    if (sec.getEnableSSL())
856    {
857      argList.add("-P");
858      argList.add(String.valueOf(sec.getSslPort()));
859    }
860
861    if (sec.getEnableStartTLS())
862    {
863      argList.add("-q");
864    }
865
866    addCertificateArguments(sec, argList);
867    // For the moment do not enable JMX
868    if (getUserData().getServerJMXPort() > 0)
869    {
870      argList.add("-x");
871      argList.add(String.valueOf(getUserData().getServerJMXPort()));
872    }
873
874    argList.add("-D");
875    argList.add(getUserData().getDirectoryManagerDn());
876
877    argList.add("-w");
878    argList.add(getUserData().getDirectoryManagerPwd());
879
880    final ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg> backendType =
881        getUserData().getBackendType();
882    if (backendType != null)
883    {
884      argList.add("--" + OPTION_LONG_BACKEND_TYPE);
885      argList.add(BackendTypeHelper.filterSchemaBackendName(backendType.getName()));
886    }
887
888    if (createNotReplicatedSuffix())
889    {
890      for (String baseDn : getUserData().getNewSuffixOptions().getBaseDns())
891      {
892        argList.add("-b");
893        argList.add(baseDn);
894      }
895    }
896
897    argList.add("-R");
898    argList.add(getInstallation().getRootDirectory().getAbsolutePath());
899
900    final String[] args = new String[argList.size()];
901    argList.toArray(args);
902    StringBuilder cmd = new StringBuilder();
903    boolean nextPassword = false;
904    for (String s : argList)
905    {
906      if (cmd.length() > 0)
907      {
908        cmd.append(" ");
909      }
910      if (nextPassword)
911      {
912        cmd.append("{rootUserPassword}");
913      }
914      else
915      {
916        cmd.append(s);
917      }
918      nextPassword = "-w".equals(s);
919    }
920    logger.info(LocalizableMessage.raw("configure DS cmd: " + cmd));
921    final InstallerHelper helper = new InstallerHelper();
922    setNotifyListeners(false);
923    InvokeThread thread = new InvokeThread()
924    {
925      @Override
926      public void run()
927      {
928        try
929        {
930          if (helper.invokeConfigureServer(args) != 0)
931          {
932            ae = new ApplicationException(ReturnCode.CONFIGURATION_ERROR, INFO_ERROR_CONFIGURING.get(), null);
933          }
934          else if (getUserData().getNewSuffixOptions().getBaseDns().isEmpty())
935          {
936            helper.deleteBackend(ROOT_BACKEND_NAME);
937          }
938        }
939        catch (ApplicationException aex)
940        {
941          ae = aex;
942        }
943        catch (Throwable t)
944        {
945          ae = new ApplicationException(
946              ReturnCode.CONFIGURATION_ERROR, getThrowableMsg(INFO_ERROR_CONFIGURING.get(), t), t);
947        }
948        finally
949        {
950          setNotifyListeners(true);
951        }
952        isOver = true;
953      }
954
955      @Override
956      public void abort()
957      {
958        // TODO: implement the abort
959      }
960    };
961    invokeLongOperation(thread);
962    notifyListeners(getFormattedDoneWithLineBreak());
963    checkAbort();
964    configureCertificate(sec);
965  }
966
967  private void configureCertificate(SecurityOptions sec) throws ApplicationException
968  {
969    try
970    {
971      SecurityOptions.CertificateType certType = sec.getCertificateType();
972      if (certType != SecurityOptions.CertificateType.NO_CERTIFICATE)
973      {
974        notifyListeners(getFormattedWithPoints(INFO_PROGRESS_UPDATING_CERTIFICATES.get()));
975      }
976
977      switch (certType)
978      {
979      case NO_CERTIFICATE:
980        // Nothing to do
981        break;
982      case SELF_SIGNED_CERTIFICATE:
983        String pwd = getSelfSignedCertificatePwd();
984        final CertificateManager certManager =
985            new CertificateManager(getSelfSignedKeystorePath(), CertificateManager.KEY_STORE_TYPE_JKS, pwd);
986        certManager.generateSelfSignedCertificate(SELF_SIGNED_CERT_ALIAS, getSelfSignedCertificateSubjectDN(),
987            getSelfSignedCertificateValidity());
988        SetupUtils.exportCertificate(certManager, SELF_SIGNED_CERT_ALIAS, getTemporaryCertificatePath());
989        configureTrustStore(CertificateManager.KEY_STORE_TYPE_JKS, SELF_SIGNED_CERT_ALIAS, pwd);
990        break;
991
992      case JKS:
993        configureKeyAndTrustStore(sec.getKeystorePath(), CertificateManager.KEY_STORE_TYPE_JKS,
994            CertificateManager.KEY_STORE_TYPE_JKS, sec);
995        break;
996
997      case JCEKS:
998        configureKeyAndTrustStore(sec.getKeystorePath(), CertificateManager.KEY_STORE_TYPE_JCEKS,
999            CertificateManager.KEY_STORE_TYPE_JCEKS, sec);
1000        break;
1001
1002      case PKCS12:
1003        configureKeyAndTrustStore(sec.getKeystorePath(), CertificateManager.KEY_STORE_TYPE_PKCS12,
1004            CertificateManager.KEY_STORE_TYPE_JKS, sec);
1005        break;
1006
1007      case PKCS11:
1008        configureKeyAndTrustStore(CertificateManager.KEY_STORE_PATH_PKCS11, CertificateManager.KEY_STORE_TYPE_PKCS11,
1009            CertificateManager.KEY_STORE_TYPE_JKS, sec);
1010        break;
1011
1012      default:
1013        throw new IllegalStateException("Unknown certificate type: " + certType);
1014      }
1015
1016      if (certType != SecurityOptions.CertificateType.NO_CERTIFICATE)
1017      {
1018        notifyListeners(getFormattedDoneWithLineBreak());
1019      }
1020    }
1021    catch (Throwable t)
1022    {
1023      logger.error(LocalizableMessage.raw("Error configuring certificate: " + t, t));
1024      throw new ApplicationException(
1025          ReturnCode.CONFIGURATION_ERROR, getThrowableMsg(INFO_ERROR_CONFIGURING_CERTIFICATE.get(), t), t);
1026    }
1027  }
1028
1029  private void configureKeyAndTrustStore(final String keyStorePath, final String keyStoreType,
1030      final String trustStoreType, final SecurityOptions sec) throws Exception
1031  {
1032    final String keystorePassword = sec.getKeystorePassword();
1033    final String keyStoreAlias = sec.getAliasToUse();
1034
1035    CertificateManager certManager = new CertificateManager(keyStorePath, keyStoreType, keystorePassword);
1036    SetupUtils.exportCertificate(certManager, keyStoreAlias, getTemporaryCertificatePath());
1037    configureTrustStore(trustStoreType, keyStoreAlias, keystorePassword);
1038  }
1039
1040  private void configureTrustStore(final String type, final String keyStoreAlias, final String password)
1041      throws Exception
1042  {
1043    final String alias = keyStoreAlias != null ? keyStoreAlias : SELF_SIGNED_CERT_ALIAS;
1044    final CertificateManager trustMgr = new CertificateManager(getTrustManagerPath(), type, password);
1045    trustMgr.addCertificate(alias, new File(getTemporaryCertificatePath()));
1046
1047    createProtectedFile(getKeystorePinPath(), password);
1048    final File f = new File(getTemporaryCertificatePath());
1049    f.delete();
1050  }
1051
1052  private void addCertificateArguments(SecurityOptions sec, List<String> argList)
1053  {
1054    final String aliasInKeyStore = sec.getAliasToUse();
1055
1056    switch (sec.getCertificateType())
1057    {
1058    case SELF_SIGNED_CERTIFICATE:
1059      argList.add("-k");
1060      argList.add("cn=JKS,cn=Key Manager Providers,cn=config");
1061      argList.add("-t");
1062      argList.add("cn=JKS,cn=Trust Manager Providers,cn=config");
1063      break;
1064    case JKS:
1065      addCertificateArguments(argList, sec, aliasInKeyStore, "cn=JKS,cn=Key Manager Providers,cn=config",
1066          "cn=JKS,cn=Trust Manager Providers,cn=config");
1067      break;
1068    case JCEKS:
1069      addCertificateArguments(argList, sec, aliasInKeyStore, "cn=JCEKS,cn=Key Manager Providers,cn=config",
1070          "cn=JCEKS,cn=Trust Manager Providers,cn=config");
1071      break;
1072    case PKCS12:
1073      addCertificateArguments(argList, sec, aliasInKeyStore, "cn=PKCS12,cn=Key Manager Providers,cn=config",
1074          "cn=JKS,cn=Trust Manager Providers,cn=config");
1075      break;
1076    case PKCS11:
1077      addCertificateArguments(argList, null, aliasInKeyStore, "cn=PKCS11,cn=Key Manager Providers,cn=config",
1078          "cn=JKS,cn=Trust Manager Providers,cn=config");
1079      break;
1080    case NO_CERTIFICATE:
1081      // Nothing to do.
1082      break;
1083    default:
1084      throw new IllegalStateException("Unknown certificate type: " + sec.getCertificateType());
1085    }
1086  }
1087
1088  private void addCertificateArguments(List<String> argList, SecurityOptions sec, String aliasInKeyStore,
1089      String keyStoreDN, String trustStoreDN)
1090  {
1091    argList.add("-k");
1092    argList.add(keyStoreDN);
1093    argList.add("-t");
1094    argList.add(trustStoreDN);
1095    if (sec != null)
1096    {
1097      argList.add("-m");
1098      argList.add(sec.getKeystorePath());
1099    }
1100    if (aliasInKeyStore != null)
1101    {
1102      argList.add("-a");
1103      argList.add(aliasInKeyStore);
1104    }
1105  }
1106
1107  /**
1108   * This methods creates the base entry for the suffix based on the contents of
1109   * the UserData object provided in the constructor.
1110   *
1111   * @throws ApplicationException
1112   *           if something goes wrong.
1113   */
1114  private void createBaseEntry() throws ApplicationException
1115  {
1116    LinkedList<String> baseDns = getUserData().getNewSuffixOptions().getBaseDns();
1117    if (baseDns.size() == 1)
1118    {
1119      notifyListeners(getFormattedWithPoints(INFO_PROGRESS_CREATING_BASE_ENTRY.get(baseDns.getFirst())));
1120    }
1121    else
1122    {
1123      notifyListeners(getFormattedWithPoints(INFO_PROGRESS_CREATING_BASE_ENTRIES.get()));
1124    }
1125
1126    final InstallerHelper helper = new InstallerHelper();
1127
1128    LinkedList<File> ldifFiles = new LinkedList<>();
1129
1130    for (String baseDn : baseDns)
1131    {
1132      ldifFiles.add(helper.createBaseEntryTempFile(baseDn));
1133    }
1134    checkAbort();
1135
1136    List<String> argList = new ArrayList<>();
1137    argList.add("-n");
1138    argList.add(ROOT_BACKEND_NAME);
1139    for (File f : ldifFiles)
1140    {
1141      argList.add("-l");
1142      argList.add(f.getAbsolutePath());
1143    }
1144    argList.add("-F");
1145    argList.add("-Q");
1146    argList.add("--noPropertiesFile");
1147
1148    final String[] args = new String[argList.size()];
1149    argList.toArray(args);
1150
1151    setNotifyListeners(false);
1152
1153    InvokeThread thread = new InvokeThread()
1154    {
1155      @Override
1156      public void run()
1157      {
1158        try
1159        {
1160          int result = helper.invokeImportLDIF(Installer.this, args);
1161
1162          if (result != 0)
1163          {
1164            ae = new ApplicationException(ReturnCode.IMPORT_ERROR, INFO_ERROR_CREATING_BASE_ENTRY.get(), null);
1165          }
1166        }
1167        catch (Throwable t)
1168        {
1169          ae =
1170              new ApplicationException(ReturnCode.IMPORT_ERROR,
1171                  getThrowableMsg(INFO_ERROR_CREATING_BASE_ENTRY.get(), t), t);
1172        }
1173        finally
1174        {
1175          setNotifyListeners(true);
1176        }
1177        isOver = true;
1178      }
1179
1180      @Override
1181      public void abort()
1182      {
1183        // TODO: implement the abort
1184      }
1185    };
1186    invokeLongOperation(thread);
1187    notifyListeners(getFormattedDoneWithLineBreak());
1188  }
1189
1190  /**
1191   * This methods imports the contents of an LDIF file based on the contents of
1192   * the UserData object provided in the constructor.
1193   *
1194   * @throws ApplicationException
1195   *           if something goes wrong.
1196   */
1197  private void importLDIF() throws ApplicationException
1198  {
1199    LinkedList<String> ldifPaths = getUserData().getNewSuffixOptions().getLDIFPaths();
1200    LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
1201    if (ldifPaths.size() > 1)
1202    {
1203      if (isVerbose())
1204      {
1205        mb.append(getFormattedProgress(INFO_PROGRESS_IMPORTING_LDIFS.get(joinAsString(", ", ldifPaths))));
1206        mb.append(getLineBreak());
1207      }
1208      else
1209      {
1210        mb.append(getFormattedProgress(INFO_PROGRESS_IMPORTING_LDIFS_NON_VERBOSE.get(joinAsString(", ", ldifPaths))));
1211      }
1212    }
1213    else if (isVerbose())
1214    {
1215      mb.append(getFormattedProgress(INFO_PROGRESS_IMPORTING_LDIF.get(ldifPaths.getFirst())));
1216      mb.append(getLineBreak());
1217    }
1218    else
1219    {
1220      mb.append(getFormattedProgress(INFO_PROGRESS_IMPORTING_LDIF_NON_VERBOSE.get(ldifPaths.getFirst())));
1221    }
1222    notifyListeners(mb.toMessage());
1223
1224    final PointAdder pointAdder = new PointAdder();
1225
1226    if (!isVerbose())
1227    {
1228      setNotifyListeners(false);
1229      pointAdder.start();
1230    }
1231
1232    List<String> argList = new ArrayList<>();
1233    argList.add("-n");
1234    argList.add(ROOT_BACKEND_NAME);
1235    for (String ldifPath : ldifPaths)
1236    {
1237      argList.add("-l");
1238      argList.add(ldifPath);
1239    }
1240    argList.add("-F");
1241    String rejectedFile = getUserData().getNewSuffixOptions().getRejectedFile();
1242    if (rejectedFile != null)
1243    {
1244      argList.add("-R");
1245      argList.add(rejectedFile);
1246    }
1247    String skippedFile = getUserData().getNewSuffixOptions().getSkippedFile();
1248    if (skippedFile != null)
1249    {
1250      argList.add("--skipFile");
1251      argList.add(skippedFile);
1252    }
1253
1254    argList.add("--noPropertiesFile");
1255
1256    final String[] args = new String[argList.size()];
1257    argList.toArray(args);
1258
1259    InvokeThread thread = new InvokeThread()
1260    {
1261      @Override
1262      public void run()
1263      {
1264        try
1265        {
1266          InstallerHelper helper = new InstallerHelper();
1267          int result = helper.invokeImportLDIF(Installer.this, args);
1268
1269          if (result != 0)
1270          {
1271            ae = new ApplicationException(ReturnCode.IMPORT_ERROR, INFO_ERROR_IMPORTING_LDIF.get(), null);
1272          }
1273        }
1274        catch (Throwable t)
1275        {
1276          ae = new ApplicationException(
1277              ReturnCode.IMPORT_ERROR, getThrowableMsg(INFO_ERROR_IMPORTING_LDIF.get(), t), t);
1278        }
1279        finally
1280        {
1281          if (!isVerbose())
1282          {
1283            setNotifyListeners(true);
1284            pointAdder.stop();
1285          }
1286        }
1287        isOver = true;
1288      }
1289
1290      @Override
1291      public void abort()
1292      {
1293        // TODO: implement the abort
1294      }
1295    };
1296    try
1297    {
1298      invokeLongOperation(thread);
1299    }
1300    catch (ApplicationException ae)
1301    {
1302      if (!isVerbose() && lastImportProgress != null)
1303      {
1304        notifyListeners(getFormattedProgress(LocalizableMessage.raw(lastImportProgress)));
1305        notifyListeners(getLineBreak());
1306      }
1307      throw ae;
1308    }
1309    if (!isVerbose())
1310    {
1311      if (lastImportProgress == null)
1312      {
1313        notifyListeners(getFormattedDoneWithLineBreak());
1314      }
1315      else
1316      {
1317        notifyListeners(getFormattedProgress(LocalizableMessage.raw(lastImportProgress)));
1318        notifyListeners(getLineBreak());
1319      }
1320    }
1321  }
1322
1323  /**
1324   * This methods imports automatically generated data based on the contents of
1325   * the UserData object provided in the constructor.
1326   *
1327   * @throws ApplicationException
1328   *           if something goes wrong.
1329   */
1330  private void importAutomaticallyGenerated() throws ApplicationException
1331  {
1332    File templatePath = createTemplateFile();
1333    int nEntries = getUserData().getNewSuffixOptions().getNumberEntries();
1334    LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
1335    if (isVerbose() || nEntries > THRESHOLD_AUTOMATIC_DATA_VERBOSE)
1336    {
1337      mb.append(getFormattedProgress(INFO_PROGRESS_IMPORT_AUTOMATICALLY_GENERATED.get(nEntries)));
1338      mb.append(getLineBreak());
1339    }
1340    else
1341    {
1342      mb.append(getFormattedProgress(INFO_PROGRESS_IMPORT_AUTOMATICALLY_GENERATED_NON_VERBOSE.get(nEntries)));
1343    }
1344    notifyListeners(mb.toMessage());
1345
1346    final PointAdder pointAdder = new PointAdder();
1347    if (!isVerbose())
1348    {
1349      pointAdder.start();
1350    }
1351
1352    if (!isVerbose())
1353    {
1354      setNotifyListeners(false);
1355    }
1356    final List<String> argList = new ArrayList<>();
1357    argList.add("-n");
1358    argList.add(ROOT_BACKEND_NAME);
1359    argList.add("-A");
1360    argList.add(templatePath.getAbsolutePath());
1361    argList.add("-s"); // seed
1362    argList.add("0");
1363    argList.add("-F");
1364    argList.add("--noPropertiesFile");
1365
1366    final String[] args = new String[argList.size()];
1367    argList.toArray(args);
1368
1369    InvokeThread thread = new InvokeThread()
1370    {
1371      @Override
1372      public void run()
1373      {
1374        try
1375        {
1376          InstallerHelper helper = new InstallerHelper();
1377          int result = helper.invokeImportLDIF(Installer.this, args);
1378
1379          if (result != 0)
1380          {
1381            ae = new ApplicationException(
1382                ReturnCode.IMPORT_ERROR, INFO_ERROR_IMPORT_LDIF_TOOL_RETURN_CODE.get(result), null);
1383          }
1384        }
1385        catch (Throwable t)
1386        {
1387          ae = new ApplicationException(ReturnCode.IMPORT_ERROR, getThrowableMsg(
1388                      INFO_ERROR_IMPORT_AUTOMATICALLY_GENERATED.get(joinAsString(" ", argList),
1389                      t.getLocalizedMessage()), t), t);
1390        }
1391        finally
1392        {
1393          if (!isVerbose())
1394          {
1395            setNotifyListeners(true);
1396            if (ae != null)
1397            {
1398              pointAdder.stop();
1399            }
1400          }
1401        }
1402        isOver = true;
1403      }
1404
1405      @Override
1406      public void abort()
1407      {
1408        // TODO: implement the abort
1409      }
1410    };
1411    invokeLongOperation(thread);
1412    if (!isVerbose())
1413    {
1414      pointAdder.stop();
1415      notifyListeners(getFormattedDoneWithLineBreak());
1416    }
1417  }
1418
1419  /**
1420   * This method undoes the modifications made in other servers in terms of
1421   * replication. This method assumes that we are aborting the Installer and
1422   * that is why it does not call checkAbort.
1423   */
1424  private void unconfigureRemote()
1425  {
1426    InitialLdapContext ctx = null;
1427    if (registeredNewServerOnRemote || createdAdministrator || createdRemoteAds)
1428    {
1429      // Try to connect
1430      DataReplicationOptions repl = getUserData().getReplicationOptions();
1431      AuthenticationData auth = repl.getAuthenticationData();
1432      if (isVerbose())
1433      {
1434        notifyListeners(getFormattedWithPoints(INFO_PROGRESS_UNCONFIGURING_ADS_ON_REMOTE.get(getHostDisplay(auth))));
1435      }
1436      try
1437      {
1438        ctx = createInitialLdapContext(auth);
1439
1440        ADSContext adsContext = new ADSContext(ctx);
1441        if (createdRemoteAds)
1442        {
1443          adsContext.removeAdminData(true);
1444        }
1445        else
1446        {
1447          if (registeredNewServerOnRemote)
1448          {
1449            try
1450            {
1451              adsContext.unregisterServer(getNewServerAdsProperties(getUserData()));
1452            }
1453            catch (ADSContextException ace)
1454            {
1455              if (ace.getError() != ADSContextException.ErrorType.NOT_YET_REGISTERED)
1456              {
1457                throw ace;
1458              }
1459              // Else, nothing to do: this may occur if the new server has been
1460              // unregistered on another server and the modification has been
1461              // already propagated by replication.
1462            }
1463          }
1464          if (createdAdministrator)
1465          {
1466            adsContext.deleteAdministrator(getAdministratorProperties(getUserData()));
1467          }
1468        }
1469        if (isVerbose())
1470        {
1471          notifyListeners(getFormattedDoneWithLineBreak());
1472        }
1473      }
1474      catch (Throwable t)
1475      {
1476        notifyListeners(getFormattedError(t, true));
1477      }
1478      finally
1479      {
1480        StaticUtils.close(ctx);
1481      }
1482    }
1483    InstallerHelper helper = new InstallerHelper();
1484    for (ServerDescriptor server : hmConfiguredRemoteReplication.keySet())
1485    {
1486      notifyListeners(getFormattedWithPoints(INFO_PROGRESS_UNCONFIGURING_REPLICATION_REMOTE.get(getHostPort(server))));
1487      try
1488      {
1489        ctx = getRemoteConnection(server, getTrustManager(), getPreferredConnections());
1490        helper.unconfigureReplication(ctx, hmConfiguredRemoteReplication.get(server), ConnectionUtils.getHostPort(ctx));
1491      }
1492      catch (ApplicationException ae)
1493      {
1494        notifyListeners(getFormattedError(ae, true));
1495      }
1496      finally
1497      {
1498        StaticUtils.close(ctx);
1499      }
1500      notifyListeners(getFormattedDoneWithLineBreak());
1501    }
1502  }
1503
1504  /**
1505   * This method configures the backends and suffixes that must be replicated.
1506   * The setup uses the same backend names as in the remote servers. If userRoot
1507   * is not one of the backends defined in the remote servers, it deletes it
1508   * from the configuration. NOTE: this method assumes that the server is
1509   * running.
1510   *
1511   * @throws ApplicationException
1512   *           if something goes wrong.
1513   */
1514  protected void createReplicatedBackendsIfRequired() throws ApplicationException
1515  {
1516    if (FIRST_IN_TOPOLOGY == getUserData().getReplicationOptions().getType())
1517    {
1518      return;
1519    }
1520    notifyListeners(getFormattedWithPoints(INFO_PROGRESS_CREATING_REPLICATED_BACKENDS.get()));
1521
1522    // The keys are the backend IDs and the values the list of base DNs.
1523    final Map<String, Set<String>> hmBackendSuffix = new HashMap<>();
1524    final SuffixesToReplicateOptions suffixData = getUserData().getSuffixesToReplicateOptions();
1525    populateBackendsToCreate(hmBackendSuffix, suffixData.getSuffixes());
1526    createReplicatedBackends(hmBackendSuffix, suffixData.getSuffixBackendTypes());
1527    notifyListeners(getFormattedDoneWithLineBreak());
1528    checkAbort();
1529  }
1530
1531  /**
1532   * The criteria to choose the name of the backend is to try to have the
1533   * configuration of the other server. The algorithm consists on putting the
1534   * remote servers in a list and pick the backend as they appear on the list.
1535   */
1536  private void populateBackendsToCreate(Map<String, Set<String>> hmBackendSuffix, Set<SuffixDescriptor> suffixes)
1537  {
1538    Set<ServerDescriptor> serverList = getServerListFromSuffixes(suffixes);
1539    for (SuffixDescriptor suffix : suffixes)
1540    {
1541      final ReplicaDescriptor replica = retrieveReplicaForSuffix(serverList, suffix);
1542      if (replica != null)
1543      {
1544        final String backendNameKey = getOrAddBackend(hmBackendSuffix, replica.getBackendName());
1545        hmBackendSuffix.get(backendNameKey).add(suffix.getDN());
1546      }
1547    }
1548  }
1549
1550  private Set<ServerDescriptor> getServerListFromSuffixes(Set<SuffixDescriptor> suffixes)
1551  {
1552    Set<ServerDescriptor> serverList = new LinkedHashSet<>();
1553    for (SuffixDescriptor suffix : suffixes)
1554    {
1555      for (ReplicaDescriptor replica : suffix.getReplicas())
1556      {
1557        serverList.add(replica.getServer());
1558      }
1559    }
1560    return serverList;
1561  }
1562
1563  private ReplicaDescriptor retrieveReplicaForSuffix(Set<ServerDescriptor> serverList, SuffixDescriptor suffix)
1564  {
1565    for (ServerDescriptor server : serverList)
1566    {
1567      for (ReplicaDescriptor replica : suffix.getReplicas())
1568      {
1569        if (replica.getServer() == server)
1570        {
1571          return replica;
1572        }
1573      }
1574    }
1575    return null;
1576  }
1577
1578  private String getOrAddBackend(Map<String, Set<String>> hmBackendSuffix, String backendName)
1579  {
1580    for (String storedBackend : hmBackendSuffix.keySet())
1581    {
1582      if (storedBackend.equalsIgnoreCase(backendName))
1583      {
1584        return storedBackend;
1585      }
1586    }
1587    hmBackendSuffix.put(backendName, new HashSet<String>());
1588    return backendName;
1589  }
1590
1591  private void createReplicatedBackends(final Map<String, Set<String>> hmBackendSuffix,
1592      final Map<String, BackendTypeUIAdapter> backendTypes) throws ApplicationException
1593  {
1594    InitialLdapContext ctx = null;
1595    try
1596    {
1597      ctx = createLocalContext();
1598      final InstallerHelper helper = new InstallerHelper();
1599      for (String backendName : hmBackendSuffix.keySet())
1600      {
1601        helper.createBackend(ctx, backendName, hmBackendSuffix.get(backendName), ConnectionUtils.getHostPort(ctx),
1602            backendTypes.get(backendName).getLegacyConfigurationFrameworkBackend());
1603      }
1604    }
1605    catch (NamingException ne)
1606    {
1607      LocalizableMessage failedMsg = getThrowableMsg(INFO_ERROR_CONNECTING_TO_LOCAL.get(), ne);
1608      throw new ApplicationException(ReturnCode.CONFIGURATION_ERROR, failedMsg, ne);
1609    }
1610    finally
1611    {
1612      StaticUtils.close(ctx);
1613    }
1614  }
1615
1616  /**
1617   * This method creates the replication configuration for the suffixes on the
1618   * the local server (and eventually in the remote servers) to synchronize
1619   * things. NOTE: this method assumes that the server is running.
1620   *
1621   * @throws ApplicationException
1622   *           if something goes wrong.
1623   */
1624  protected void configureReplication() throws ApplicationException
1625  {
1626    notifyListeners(getFormattedWithPoints(INFO_PROGRESS_CONFIGURING_REPLICATION.get()));
1627
1628    InstallerHelper helper = new InstallerHelper();
1629    Set<Integer> knownServerIds = new HashSet<>();
1630    Set<Integer> knownReplicationServerIds = new HashSet<>();
1631    if (lastLoadedCache != null)
1632    {
1633      for (SuffixDescriptor suffix : lastLoadedCache.getSuffixes())
1634      {
1635        for (ReplicaDescriptor replica : suffix.getReplicas())
1636        {
1637          knownServerIds.add(replica.getReplicationId());
1638        }
1639      }
1640      for (ServerDescriptor server : lastLoadedCache.getServers())
1641      {
1642        Object v = server.getServerProperties().get(REPLICATION_SERVER_ID);
1643        if (v != null)
1644        {
1645          knownReplicationServerIds.add((Integer) v);
1646        }
1647      }
1648    }
1649    else
1650    {
1651      /* There is no ADS anywhere. Just use the SuffixDescriptors we found */
1652      for (SuffixDescriptor suffix : getUserData().getSuffixesToReplicateOptions().getAvailableSuffixes())
1653      {
1654        for (ReplicaDescriptor replica : suffix.getReplicas())
1655        {
1656          knownServerIds.add(replica.getReplicationId());
1657          Object v = replica.getServer().getServerProperties().get(REPLICATION_SERVER_ID);
1658          if (v != null)
1659          {
1660            knownReplicationServerIds.add((Integer) v);
1661          }
1662        }
1663      }
1664    }
1665
1666    /*
1667     * For each suffix specified by the user, create a map from the suffix DN to
1668     * the set of replication servers. The initial instance in a topology is a
1669     * degenerate case. Also, collect a set of all observed replication servers
1670     * as the set of ADS suffix replicas (all instances hosting the replication
1671     * server also replicate ADS).
1672     */
1673    Map<String, Set<String>> replicationServers = new HashMap<>();
1674    Set<String> adsServers = new HashSet<>();
1675
1676    if (getUserData().getReplicationOptions().getType() == DataReplicationOptions.Type.FIRST_IN_TOPOLOGY)
1677    {
1678      List<String> baseDns = getUserData().getNewSuffixOptions().getBaseDns();
1679      Set<String> h = new HashSet<>();
1680      h.add(getLocalReplicationServer());
1681      adsServers.add(getLocalReplicationServer());
1682      for (String dn : baseDns)
1683      {
1684        replicationServers.put(dn, new HashSet<String>(h));
1685      }
1686    }
1687    else
1688    {
1689      Set<SuffixDescriptor> suffixes = getUserData().getSuffixesToReplicateOptions().getSuffixes();
1690      for (SuffixDescriptor suffix : suffixes)
1691      {
1692        Set<String> h = new HashSet<>(suffix.getReplicationServers());
1693        adsServers.addAll(suffix.getReplicationServers());
1694        h.add(getLocalReplicationServer());
1695        adsServers.add(getLocalReplicationServer());
1696        for (ReplicaDescriptor replica : suffix.getReplicas())
1697        {
1698          ServerDescriptor server = replica.getServer();
1699          AuthenticationData repPort = getUserData().getRemoteWithNoReplicationPort().get(server);
1700          if (repPort != null)
1701          {
1702            h.add(server.getHostName() + ":" + repPort.getPort());
1703            adsServers.add(server.getHostName() + ":" + repPort.getPort());
1704          }
1705        }
1706        replicationServers.put(suffix.getDN(), h);
1707      }
1708    }
1709    replicationServers.put(ADSContext.getAdministrationSuffixDN(), adsServers);
1710    replicationServers.put(Constants.SCHEMA_DN, new HashSet<String>(adsServers));
1711
1712    InitialLdapContext ctx = null;
1713    long localTime = -1;
1714    long localTimeMeasureTime = -1;
1715    String localServerDisplay = null;
1716    try
1717    {
1718      ctx = createLocalContext();
1719      helper.configureReplication(ctx, replicationServers,
1720          getUserData().getReplicationOptions().getReplicationPort(),
1721          getUserData().getReplicationOptions().useSecureReplication(),
1722          getLocalHostPort(),
1723          knownReplicationServerIds, knownServerIds);
1724      localTimeMeasureTime = System.currentTimeMillis();
1725      localTime = Utils.getServerClock(ctx);
1726      localServerDisplay = ConnectionUtils.getHostPort(ctx);
1727    }
1728    catch (NamingException ne)
1729    {
1730      LocalizableMessage failedMsg = getThrowableMsg(INFO_ERROR_CONNECTING_TO_LOCAL.get(), ne);
1731      throw new ApplicationException(ReturnCode.CONFIGURATION_ERROR, failedMsg, ne);
1732    }
1733    finally
1734    {
1735      StaticUtils.close(ctx);
1736    }
1737    notifyListeners(getFormattedDoneWithLineBreak());
1738    checkAbort();
1739
1740    if (getUserData().getReplicationOptions().getType() == DataReplicationOptions.Type.IN_EXISTING_TOPOLOGY)
1741    {
1742      Map<ServerDescriptor, Set<ReplicaDescriptor>> hm = new HashMap<>();
1743      for (SuffixDescriptor suffix : getUserData().getSuffixesToReplicateOptions().getSuffixes())
1744      {
1745        for (ReplicaDescriptor replica : suffix.getReplicas())
1746        {
1747          Set<ReplicaDescriptor> replicas = hm.get(replica.getServer());
1748          if (replicas == null)
1749          {
1750            replicas = new HashSet<>();
1751            hm.put(replica.getServer(), replicas);
1752          }
1753          replicas.add(replica);
1754        }
1755      }
1756      for (ServerDescriptor server : hm.keySet())
1757      {
1758        notifyListeners(getFormattedWithPoints(INFO_PROGRESS_CONFIGURING_REPLICATION_REMOTE.get(getHostPort(server))));
1759        Integer v = (Integer) server.getServerProperties().get(REPLICATION_SERVER_PORT);
1760        int replicationPort;
1761        boolean enableSecureReplication;
1762        if (v != null)
1763        {
1764          replicationPort = v;
1765          enableSecureReplication = false;
1766        }
1767        else
1768        {
1769          AuthenticationData authData = getUserData().getRemoteWithNoReplicationPort().get(server);
1770          if (authData != null)
1771          {
1772            replicationPort = authData.getPort();
1773            enableSecureReplication = authData.useSecureConnection();
1774          }
1775          else
1776          {
1777            replicationPort = Constants.DEFAULT_REPLICATION_PORT;
1778            enableSecureReplication = false;
1779            logger.warn(LocalizableMessage.raw("Could not find replication port for: " + getHostPort(server)));
1780          }
1781        }
1782        Set<String> dns = new HashSet<>();
1783        for (ReplicaDescriptor replica : hm.get(server))
1784        {
1785          dns.add(replica.getSuffix().getDN());
1786        }
1787        dns.add(ADSContext.getAdministrationSuffixDN());
1788        dns.add(Constants.SCHEMA_DN);
1789        Map<String, Set<String>> remoteReplicationServers = new HashMap<>();
1790        for (String dn : dns)
1791        {
1792          Set<String> repServer = replicationServers.get(dn);
1793          if (repServer == null)
1794          {
1795            // Do the comparison manually
1796            for (String dn1 : replicationServers.keySet())
1797            {
1798              if (Utils.areDnsEqual(dn, dn1))
1799              {
1800                repServer = replicationServers.get(dn1);
1801                dn = dn1;
1802                break;
1803              }
1804            }
1805          }
1806          if (repServer != null)
1807          {
1808            remoteReplicationServers.put(dn, repServer);
1809          }
1810          else
1811          {
1812            logger.warn(LocalizableMessage.raw("Could not find replication server for: " + dn));
1813          }
1814        }
1815
1816        ctx = getRemoteConnection(server, getTrustManager(), getPreferredConnections());
1817        ConfiguredReplication repl =
1818            helper.configureReplication(ctx, remoteReplicationServers, replicationPort, enableSecureReplication,
1819                ConnectionUtils.getHostPort(ctx), knownReplicationServerIds, knownServerIds);
1820        long remoteTimeMeasureTime = System.currentTimeMillis();
1821        long remoteTime = Utils.getServerClock(ctx);
1822        if (localTime != -1
1823            && remoteTime != -1
1824            && Math.abs(localTime - remoteTime - localTimeMeasureTime + remoteTimeMeasureTime) >
1825               THRESHOLD_CLOCK_DIFFERENCE_WARNING * 60 * 1000)
1826        {
1827          notifyListeners(getFormattedWarning(INFO_WARNING_SERVERS_CLOCK_DIFFERENCE.get(localServerDisplay,
1828              ConnectionUtils.getHostPort(ctx), THRESHOLD_CLOCK_DIFFERENCE_WARNING)));
1829        }
1830
1831        hmConfiguredRemoteReplication.put(server, repl);
1832
1833        StaticUtils.close(ctx);
1834        notifyListeners(getFormattedDoneWithLineBreak());
1835        checkAbort();
1836      }
1837    }
1838  }
1839
1840  /**
1841   * This methods enables this server as a Windows service.
1842   *
1843   * @throws ApplicationException
1844   *           if something goes wrong.
1845   */
1846  protected void enableWindowsService() throws ApplicationException
1847  {
1848    notifyListeners(getFormattedWithPoints(INFO_PROGRESS_ENABLING_WINDOWS_SERVICE.get()));
1849    InstallerHelper helper = new InstallerHelper();
1850    helper.enableWindowsService();
1851    notifyListeners(getLineBreak());
1852  }
1853
1854  /**
1855   * Updates the contents of the provided map with the localized summary
1856   * strings.
1857   *
1858   * @param hmSummary
1859   *          the Map to be updated.
1860   * @param isCli
1861   *          a boolean to indicate if the install is using CLI or GUI
1862   */
1863  protected void initSummaryMap(Map<ProgressStep, LocalizableMessage> hmSummary, boolean isCli)
1864  {
1865    put(hmSummary, NOT_STARTED, INFO_SUMMARY_INSTALL_NOT_STARTED);
1866    put(hmSummary, DOWNLOADING, INFO_SUMMARY_DOWNLOADING);
1867    put(hmSummary, EXTRACTING, INFO_SUMMARY_EXTRACTING);
1868    put(hmSummary, CONFIGURING_SERVER, INFO_SUMMARY_CONFIGURING);
1869    put(hmSummary, CREATING_BASE_ENTRY, INFO_SUMMARY_CREATING_BASE_ENTRY);
1870    put(hmSummary, IMPORTING_LDIF, INFO_SUMMARY_IMPORTING_LDIF);
1871    put(hmSummary, IMPORTING_AUTOMATICALLY_GENERATED, INFO_SUMMARY_IMPORTING_AUTOMATICALLY_GENERATED);
1872    put(hmSummary, CONFIGURING_REPLICATION, INFO_SUMMARY_CONFIGURING_REPLICATION);
1873    put(hmSummary, STARTING_SERVER, INFO_SUMMARY_STARTING);
1874    put(hmSummary, STOPPING_SERVER, INFO_SUMMARY_STOPPING);
1875    put(hmSummary, CONFIGURING_ADS, INFO_SUMMARY_CONFIGURING_ADS);
1876    put(hmSummary, INITIALIZE_REPLICATED_SUFFIXES, INFO_SUMMARY_INITIALIZE_REPLICATED_SUFFIXES);
1877    put(hmSummary, ENABLING_WINDOWS_SERVICE, INFO_SUMMARY_ENABLING_WINDOWS_SERVICE);
1878    put(hmSummary, WAITING_TO_CANCEL, INFO_SUMMARY_WAITING_TO_CANCEL);
1879    put(hmSummary, CANCELING, INFO_SUMMARY_CANCELING);
1880
1881    Installation installation = getInstallation();
1882    String cmd = Utils.addWordBreaks(getPath(installation.getControlPanelCommandFile()), 60, 5);
1883    if (!isCli)
1884    {
1885      cmd = UIFactory.applyFontToHtml(cmd, UIFactory.INSTRUCTIONS_MONOSPACE_FONT);
1886    }
1887    String formattedPath =
1888        Utils.addWordBreaks(formatter.getFormattedText(LocalizableMessage.raw(getPath(new File(getInstancePath()))))
1889            .toString(), 60, 5);
1890    LocalizableMessage successMessage =
1891        Utils.getCustomizedObject("INFO_SUMMARY_INSTALL_FINISHED_SUCCESSFULLY",
1892            INFO_SUMMARY_INSTALL_FINISHED_SUCCESSFULLY.get(DynamicConstants.PRODUCT_NAME,
1893                DynamicConstants.PRODUCT_NAME, formattedPath, INFO_GENERAL_SERVER_STOPPED.get(),
1894                DynamicConstants.DOC_QUICK_REFERENCE_GUIDE, DynamicConstants.PRODUCT_NAME, cmd),
1895            LocalizableMessage.class);
1896    hmSummary.put(FINISHED_SUCCESSFULLY, getFormattedSuccess(successMessage));
1897    hmSummary.put(FINISHED_CANCELED, getFormattedSuccess(INFO_SUMMARY_INSTALL_FINISHED_CANCELED.get()));
1898    hmSummary.put(FINISHED_WITH_ERROR,
1899        getFormattedError(INFO_SUMMARY_INSTALL_FINISHED_WITH_ERROR.get(INFO_GENERAL_SERVER_STOPPED.get(), cmd)));
1900  }
1901
1902  private void put(Map<ProgressStep, LocalizableMessage> hmSummary, InstallProgressStep step, Arg0 msg)
1903  {
1904    hmSummary.put(step, getFormattedSummary(msg.get()));
1905  }
1906
1907  /**
1908   * Updates the messages in the summary with the state of the server.
1909   *
1910   * @param hmSummary
1911   *          the Map containing the messages.
1912   * @param isCli
1913   *          a boolean to indicate if the install is using CLI or GUI
1914   */
1915  protected void updateSummaryWithServerState(Map<ProgressStep, LocalizableMessage> hmSummary, Boolean isCli)
1916  {
1917    Installation installation = getInstallation();
1918    String cmd = getPath(installation.getControlPanelCommandFile());
1919    if (!isCli)
1920    {
1921      cmd = Utils.addWordBreaks(UIFactory.applyFontToHtml(cmd, UIFactory.INSTRUCTIONS_MONOSPACE_FONT), 60, 5);
1922    }
1923    LocalizableMessage status;
1924    if (installation.getStatus().isServerRunning())
1925    {
1926      status = INFO_GENERAL_SERVER_STARTED.get();
1927    }
1928    else
1929    {
1930      status = INFO_GENERAL_SERVER_STOPPED.get();
1931    }
1932    String formattedPath =
1933        Utils.addWordBreaks(formatter.getFormattedText(LocalizableMessage.raw(getPath(new File(getInstancePath()))))
1934            .toString(), 60, 5);
1935    LocalizableMessage successMessage =
1936        Utils.getCustomizedObject("INFO_SUMMARY_INSTALL_FINISHED_SUCCESSFULLY",
1937            INFO_SUMMARY_INSTALL_FINISHED_SUCCESSFULLY.get(DynamicConstants.PRODUCT_NAME,
1938                DynamicConstants.PRODUCT_NAME, formattedPath, status, DynamicConstants.DOC_QUICK_REFERENCE_GUIDE,
1939                DynamicConstants.PRODUCT_NAME, cmd), LocalizableMessage.class);
1940    hmSummary.put(InstallProgressStep.FINISHED_SUCCESSFULLY, getFormattedSuccess(successMessage));
1941    hmSummary.put(InstallProgressStep.FINISHED_WITH_ERROR, getFormattedError(INFO_SUMMARY_INSTALL_FINISHED_WITH_ERROR
1942        .get(status, cmd)));
1943  }
1944
1945  /**
1946   * Checks the value of <code>canceled</code> field and throws an
1947   * ApplicationException if true. This indicates that the user has canceled
1948   * this operation and the process of aborting should begin as soon as
1949   * possible.
1950   *
1951   * @throws ApplicationException
1952   *           thrown if <code>canceled</code>
1953   */
1954  @Override
1955  public void checkAbort() throws ApplicationException
1956  {
1957    if (canceled)
1958    {
1959      setCurrentProgressStep(InstallProgressStep.CANCELING);
1960      notifyListeners(null);
1961      throw new ApplicationException(ReturnCode.CANCELED, INFO_INSTALL_CANCELED.get(), null);
1962    }
1963  }
1964
1965  /**
1966   * Writes the host name to a file that will be used by the server to generate
1967   * a self-signed certificate.
1968   */
1969  private void writeHostName()
1970  {
1971    BufferedWriter writer = null;
1972    try
1973    {
1974      writer = new BufferedWriter(new FileWriter(getHostNameFile(), false));
1975      writer.append(getUserData().getHostName());
1976    }
1977    catch (IOException ioe)
1978    {
1979      logger.warn(LocalizableMessage.raw("Error writing host name file: " + ioe, ioe));
1980    }
1981    finally
1982    {
1983      StaticUtils.close(writer);
1984    }
1985  }
1986
1987  /**
1988   * Returns the file path where the host name is to be written.
1989   *
1990   * @return the file path where the host name is to be written.
1991   */
1992  private String getHostNameFile()
1993  {
1994    return Utils.getPath(getInstallation().getRootDirectory().getAbsolutePath(), SetupUtils.HOST_NAME_FILE);
1995  }
1996
1997  /**
1998   * Writes the java home that we are using for the setup in a file. This way we
1999   * can use this java home even if the user has not set OPENDJ_JAVA_HOME when
2000   * running the different scripts.
2001   */
2002  private void writeOpenDSJavaHome()
2003  {
2004    try
2005    {
2006      // This isn't likely to happen, and it's not a serious problem even if
2007      // it does.
2008      InstallerHelper helper = new InstallerHelper();
2009      helper.writeSetOpenDSJavaHome(getUserData(), getInstallationPath());
2010    }
2011    catch (Exception e)
2012    {
2013      logger.warn(LocalizableMessage.raw("Error writing OpenDJ Java Home file: " + e, e));
2014    }
2015  }
2016
2017  /**
2018   * These methods validate the data provided by the user in the panels and
2019   * update the userData object according to that content.
2020   *
2021   * @param cStep
2022   *          the current step of the wizard
2023   * @param qs
2024   *          QuickStart controller
2025   * @throws UserDataException
2026   *           if the data provided by the user is not valid.
2027   */
2028  @Override
2029  public void updateUserData(WizardStep cStep, QuickSetup qs) throws UserDataException
2030  {
2031    if (cStep == SERVER_SETTINGS)
2032    {
2033      updateUserDataForServerSettingsPanel(qs);
2034    }
2035    else if (cStep == REPLICATION_OPTIONS)
2036    {
2037      updateUserDataForReplicationOptionsPanel(qs);
2038    }
2039    else if (cStep == CREATE_GLOBAL_ADMINISTRATOR)
2040    {
2041      updateUserDataForCreateAdministratorPanel(qs);
2042    }
2043    else if (cStep == SUFFIXES_OPTIONS)
2044    {
2045      updateUserDataForSuffixesOptionsPanel(qs);
2046    }
2047    else if (cStep == REMOTE_REPLICATION_PORTS)
2048    {
2049      updateUserDataForRemoteReplicationPorts(qs);
2050    }
2051    else if (cStep == NEW_SUFFIX_OPTIONS)
2052    {
2053      updateUserDataForNewSuffixOptionsPanel(qs);
2054    }
2055    else if (cStep == RUNTIME_OPTIONS)
2056    {
2057      updateUserDataForRuntimeOptionsPanel(qs);
2058    }
2059    else if (cStep == REVIEW)
2060    {
2061      updateUserDataForReviewPanel(qs);
2062    }
2063  }
2064
2065  /**
2066   * Sets the current progress step of the installation process.
2067   *
2068   * @param currentProgressStep
2069   *          the current progress step of the installation process.
2070   */
2071  protected void setCurrentProgressStep(InstallProgressStep currentProgressStep)
2072  {
2073    if (currentProgressStep != null)
2074    {
2075      this.completedProgress.add(currentProgressStep);
2076    }
2077    this.currentProgressStep = currentProgressStep;
2078  }
2079
2080  /**
2081   * This methods updates the data on the server based on the contents of the
2082   * UserData object provided in the constructor.
2083   *
2084   * @throws ApplicationException
2085   *           if something goes wrong.
2086   */
2087  protected void createData() throws ApplicationException
2088  {
2089    if (createNotReplicatedSuffix()
2090        && NewSuffixOptions.Type.LEAVE_DATABASE_EMPTY != getUserData().getNewSuffixOptions().getType())
2091    {
2092      currentProgressStep = getUserData().getNewSuffixOptions().getInstallProgressStep();
2093      if (isVerbose())
2094      {
2095        notifyListeners(getTaskSeparator());
2096      }
2097
2098      switch (getUserData().getNewSuffixOptions().getType())
2099      {
2100      case CREATE_BASE_ENTRY:
2101        createBaseEntry();
2102        break;
2103      case IMPORT_FROM_LDIF_FILE:
2104        importLDIF();
2105        break;
2106      case IMPORT_AUTOMATICALLY_GENERATED_DATA:
2107        importAutomaticallyGenerated();
2108        break;
2109      default:
2110        break;
2111      }
2112    }
2113  }
2114
2115  /**
2116   * This method initialize the contents of the synchronized servers with the
2117   * contents of the first server we find.
2118   *
2119   * @throws ApplicationException
2120   *           if something goes wrong.
2121   */
2122  protected void initializeSuffixes() throws ApplicationException
2123  {
2124    InitialLdapContext ctx = null;
2125    try
2126    {
2127      ctx = createLocalContext();
2128    }
2129    catch (Throwable t)
2130    {
2131      LocalizableMessage failedMsg = getThrowableMsg(INFO_ERROR_CONNECTING_TO_LOCAL.get(), t);
2132      StaticUtils.close(ctx);
2133      throw new ApplicationException(ReturnCode.CONFIGURATION_ERROR, failedMsg, t);
2134    }
2135
2136    Set<SuffixDescriptor> suffixes = getUserData().getSuffixesToReplicateOptions().getSuffixes();
2137
2138    /* Initialize local ADS and schema contents using any replica. */
2139    {
2140      ServerDescriptor server = suffixes.iterator().next().getReplicas().iterator().next().getServer();
2141      InitialLdapContext rCtx = null;
2142      try
2143      {
2144        rCtx = getRemoteConnection(server, getTrustManager(), getPreferredConnections());
2145        TopologyCacheFilter filter = new TopologyCacheFilter();
2146        filter.setSearchMonitoringInformation(false);
2147        filter.addBaseDNToSearch(ADSContext.getAdministrationSuffixDN());
2148        filter.addBaseDNToSearch(Constants.SCHEMA_DN);
2149        ServerDescriptor s = createStandalone(rCtx, filter);
2150        for (ReplicaDescriptor replica : s.getReplicas())
2151        {
2152          String dn = replica.getSuffix().getDN();
2153          if (areDnsEqual(dn, ADSContext.getAdministrationSuffixDN()))
2154          {
2155            suffixes.add(replica.getSuffix());
2156          }
2157          else if (areDnsEqual(dn, Constants.SCHEMA_DN))
2158          {
2159            suffixes.add(replica.getSuffix());
2160          }
2161        }
2162      }
2163      catch (NamingException ne)
2164      {
2165        LocalizableMessage msg;
2166        if (isCertificateException(ne))
2167        {
2168          msg = INFO_ERROR_READING_CONFIG_LDAP_CERTIFICATE_SERVER.get(getHostPort(server), ne.toString(true));
2169        }
2170        else
2171        {
2172          msg = INFO_CANNOT_CONNECT_TO_REMOTE_GENERIC.get(getHostPort(server), ne.toString(true));
2173        }
2174        throw new ApplicationException(ReturnCode.CONFIGURATION_ERROR, msg, ne);
2175      }
2176      finally
2177      {
2178        StaticUtils.close(rCtx);
2179      }
2180    }
2181
2182    for (SuffixDescriptor suffix : suffixes)
2183    {
2184      String dn = suffix.getDN();
2185
2186      ReplicaDescriptor replica = suffix.getReplicas().iterator().next();
2187      ServerDescriptor server = replica.getServer();
2188      String hostPort = getHostPort(server);
2189
2190      boolean isADS = areDnsEqual(dn, ADSContext.getAdministrationSuffixDN());
2191      boolean isSchema = areDnsEqual(dn, Constants.SCHEMA_DN);
2192      if (isADS)
2193      {
2194        if (isVerbose())
2195        {
2196          notifyListeners(getFormattedWithPoints(INFO_PROGRESS_INITIALIZING_ADS.get()));
2197        }
2198      }
2199      else if (isSchema)
2200      {
2201        if (isVerbose())
2202        {
2203          notifyListeners(getFormattedWithPoints(INFO_PROGRESS_INITIALIZING_SCHEMA.get()));
2204        }
2205      }
2206      else
2207      {
2208        notifyListeners(getFormattedProgress(INFO_PROGRESS_INITIALIZING_SUFFIX.get(dn, hostPort)));
2209        notifyListeners(getLineBreak());
2210      }
2211      try
2212      {
2213        int replicationId = replica.getReplicationId();
2214        if (replicationId == -1)
2215        {
2216          // This occurs if the remote server had not replication configured.
2217          InitialLdapContext rCtx = null;
2218          try
2219          {
2220            rCtx = getRemoteConnection(server, getTrustManager(), getPreferredConnections());
2221            TopologyCacheFilter filter = new TopologyCacheFilter();
2222            filter.setSearchMonitoringInformation(false);
2223            filter.addBaseDNToSearch(dn);
2224            ServerDescriptor s = createStandalone(rCtx, filter);
2225            for (ReplicaDescriptor r : s.getReplicas())
2226            {
2227              if (areDnsEqual(r.getSuffix().getDN(), dn))
2228              {
2229                replicationId = r.getReplicationId();
2230              }
2231            }
2232          }
2233          catch (NamingException ne)
2234          {
2235            LocalizableMessage msg;
2236            if (isCertificateException(ne))
2237            {
2238              msg = INFO_ERROR_READING_CONFIG_LDAP_CERTIFICATE_SERVER.get(getHostPort(server), ne.toString(true));
2239            }
2240            else
2241            {
2242              msg = INFO_CANNOT_CONNECT_TO_REMOTE_GENERIC.get(getHostPort(server), ne.toString(true));
2243            }
2244            throw new ApplicationException(ReturnCode.CONFIGURATION_ERROR, msg, ne);
2245          }
2246          finally
2247          {
2248            StaticUtils.close(rCtx);
2249          }
2250        }
2251        if (replicationId == -1)
2252        {
2253          throw new ApplicationException(ReturnCode.APPLICATION_ERROR, ERR_COULD_NOT_FIND_REPLICATIONID.get(dn), null);
2254        }
2255        StaticUtils.sleep(3000);
2256        int nTries = 5;
2257        boolean initDone = false;
2258        while (!initDone)
2259        {
2260          try
2261          {
2262            logger.info(LocalizableMessage.raw("Calling initializeSuffix with base DN: " + dn));
2263            logger.info(LocalizableMessage.raw("Try number: " + (6 - nTries)));
2264            logger.info(LocalizableMessage.raw("replicationId of source replica: " + replicationId));
2265            initializeSuffix(ctx, replicationId, dn, !isADS && !isSchema, hostPort);
2266            initDone = true;
2267          }
2268          catch (PeerNotFoundException pnfe)
2269          {
2270            logger.info(LocalizableMessage.raw("Peer could not be found"));
2271            if (nTries == 1)
2272            {
2273              throw new ApplicationException(ReturnCode.APPLICATION_ERROR, pnfe.getMessageObject(), null);
2274            }
2275            StaticUtils.sleep((5 - nTries) * 3000);
2276          }
2277          nTries--;
2278        }
2279      }
2280      catch (ApplicationException ae)
2281      {
2282        StaticUtils.close(ctx);
2283        throw ae;
2284      }
2285      if ((isADS || isSchema) && isVerbose())
2286      {
2287        notifyListeners(getFormattedDone());
2288        notifyListeners(getLineBreak());
2289      }
2290      checkAbort();
2291    }
2292  }
2293
2294  /**
2295   * This method updates the ADS contents (and creates the according suffixes).
2296   * If the user specified an existing topology, the new instance is registered
2297   * with that ADS (the ADS might need to be created), and the local ADS will be
2298   * populated when the local server is added to the remote server's ADS
2299   * replication domain in a subsequent step. Otherwise, an ADS is created on
2300   * the new instance and the server is registered with the new ADS. NOTE: this
2301   * method assumes that the local server and any remote server are running.
2302   *
2303   * @throws ApplicationException
2304   *           if something goes wrong.
2305   */
2306  protected void updateADS() throws ApplicationException
2307  {
2308    DataReplicationOptions repl = getUserData().getReplicationOptions();
2309    boolean isRemoteServer = repl.getType() == DataReplicationOptions.Type.IN_EXISTING_TOPOLOGY;
2310    AuthenticationData auth = isRemoteServer ? repl.getAuthenticationData() : null;
2311    InitialLdapContext remoteCtx = null; // Bound to remote ADS host (if any).
2312    InitialLdapContext localCtx = null; // Bound to local server.
2313    ADSContext adsContext = null; // Bound to ADS host (via one of above).
2314
2315    /*
2316     * Outer try-catch-finally to convert occurrences of NamingException and
2317     * ADSContextException to ApplicationException and clean up JNDI contexts.
2318     */
2319    try
2320    {
2321      if (isRemoteServer)
2322      {
2323        remoteCtx = createInitialLdapContext(auth);
2324        adsContext = new ADSContext(remoteCtx); // adsContext owns remoteCtx
2325
2326        /*
2327         * Check the remote server for ADS. If it does not exist, create the
2328         * initial ADS there and register the server with itself.
2329         */
2330        if (!adsContext.hasAdminData())
2331        {
2332          if (isVerbose())
2333          {
2334            notifyListeners(getFormattedWithPoints(INFO_PROGRESS_CREATING_ADS_ON_REMOTE.get(getHostDisplay(auth))));
2335          }
2336
2337          adsContext.createAdminData(null);
2338          TopologyCacheFilter filter = new TopologyCacheFilter();
2339          filter.setSearchMonitoringInformation(false);
2340          filter.setSearchBaseDNInformation(false);
2341          ServerDescriptor server = createStandalone(remoteCtx, filter);
2342          server.updateAdsPropertiesWithServerProperties();
2343          adsContext.registerServer(server.getAdsProperties());
2344          createdRemoteAds = true;
2345          if (isVerbose())
2346          {
2347            notifyListeners(getFormattedDoneWithLineBreak());
2348          }
2349          checkAbort();
2350        }
2351      }
2352
2353      /* Act on local server depending on if using remote or local ADS */
2354      if (isVerbose())
2355      {
2356        notifyListeners(getFormattedWithPoints(INFO_PROGRESS_CREATING_ADS.get()));
2357      }
2358      localCtx = createLocalContext();
2359      //      if (isRemoteServer)
2360      //      {
2361      //        /* Create an empty ADS suffix on the local server. */
2362      //        ADSContext localAdsContext = new ADSContext(localCtx);
2363      //        localAdsContext.createAdministrationSuffix(null);
2364      //      }
2365      if (!isRemoteServer)
2366      {
2367        /* Configure local server to have an ADS */
2368        adsContext = new ADSContext(localCtx); // adsContext owns localCtx
2369        adsContext.createAdminData(null);
2370      }
2371      /* Register new server in ADS. */
2372      TopologyCacheFilter filter = new TopologyCacheFilter();
2373      filter.setSearchMonitoringInformation(false);
2374      filter.setSearchBaseDNInformation(false);
2375      ServerDescriptor server = createStandalone(localCtx, filter);
2376      server.updateAdsPropertiesWithServerProperties();
2377      if (0 == adsContext.registerOrUpdateServer(server.getAdsProperties()))
2378      {
2379        if (isRemoteServer)
2380        {
2381          registeredNewServerOnRemote = true;
2382        }
2383      }
2384      else
2385      {
2386        logger.warn(LocalizableMessage.raw("Server was already registered. Updating " + "server registration."));
2387      }
2388      if (isRemoteServer)
2389      {
2390        seedAdsTrustStore(localCtx, adsContext.getTrustedCertificates());
2391      }
2392      if (isVerbose())
2393      {
2394        notifyListeners(getFormattedDoneWithLineBreak());
2395      }
2396      checkAbort();
2397
2398      /* Add global administrator if the user specified one. */
2399      if (getUserData().mustCreateAdministrator())
2400      {
2401        try
2402        {
2403          if (isVerbose())
2404          {
2405            notifyListeners(getFormattedWithPoints(INFO_PROGRESS_CREATING_ADMINISTRATOR.get()));
2406          }
2407          adsContext.createAdministrator(getAdministratorProperties(getUserData()));
2408          if (isRemoteServer && !createdRemoteAds)
2409          {
2410            createdAdministrator = true;
2411          }
2412          if (isVerbose())
2413          {
2414            notifyListeners(getFormattedDoneWithLineBreak());
2415          }
2416          checkAbort();
2417        }
2418        catch (ADSContextException ade)
2419        {
2420          if (ade.getError() == ADSContextException.ErrorType.ALREADY_REGISTERED)
2421          {
2422            notifyListeners(getFormattedWarning(INFO_ADMINISTRATOR_ALREADY_REGISTERED.get()));
2423            adsContext.unregisterServer(server.getAdsProperties());
2424            adsContext.registerServer(server.getAdsProperties());
2425          }
2426          else
2427          {
2428            throw ade;
2429          }
2430        }
2431      }
2432    }
2433    catch (NamingException ne)
2434    {
2435      LocalizableMessage msg;
2436      if (isRemoteServer)
2437      {
2438        msg = getMessageForException(ne, getHostDisplay(auth));
2439      }
2440      else
2441      {
2442        msg = Utils.getMessageForException(ne);
2443      }
2444      throw new ApplicationException(ReturnCode.CONFIGURATION_ERROR, msg, ne);
2445    }
2446    catch (ADSContextException ace)
2447    {
2448      throw new ApplicationException(ReturnCode.CONFIGURATION_ERROR, (isRemoteServer ? INFO_REMOTE_ADS_EXCEPTION.get(
2449          getHostDisplay(auth), ace.getMessageObject()) : INFO_ADS_EXCEPTION.get(ace)), ace);
2450    }
2451    finally
2452    {
2453      StaticUtils.close(remoteCtx, localCtx);
2454    }
2455  }
2456
2457  private InitialLdapContext createInitialLdapContext(AuthenticationData auth) throws NamingException
2458  {
2459    String ldapUrl = getLdapUrl(auth);
2460    String dn = auth.getDn();
2461    String pwd = auth.getPwd();
2462
2463    if (auth.useSecureConnection())
2464    {
2465      ApplicationTrustManager trustManager = getTrustManager();
2466      trustManager.setHost(auth.getHostName());
2467      return createLdapsContext(ldapUrl, dn, pwd, getConnectTimeout(), null, trustManager, null);
2468    }
2469    return createLdapContext(ldapUrl, dn, pwd, getConnectTimeout(), null);
2470  }
2471
2472  /**
2473   * Tells whether we must create a suffix that we are not going to replicate
2474   * with other servers or not.
2475   *
2476   * @return <CODE>true</CODE> if we must create a new suffix and
2477   *         <CODE>false</CODE> otherwise.
2478   */
2479  protected boolean createNotReplicatedSuffix()
2480  {
2481    DataReplicationOptions repl = getUserData().getReplicationOptions();
2482
2483    SuffixesToReplicateOptions suf = getUserData().getSuffixesToReplicateOptions();
2484
2485    return repl.getType() == DataReplicationOptions.Type.FIRST_IN_TOPOLOGY
2486        || repl.getType() == DataReplicationOptions.Type.STANDALONE
2487        || suf.getType() == SuffixesToReplicateOptions.Type.NEW_SUFFIX_IN_TOPOLOGY;
2488  }
2489
2490  /**
2491   * Returns <CODE>true</CODE> if we must configure replication and
2492   * <CODE>false</CODE> otherwise.
2493   *
2494   * @return <CODE>true</CODE> if we must configure replication and
2495   *         <CODE>false</CODE> otherwise.
2496   */
2497  protected boolean mustConfigureReplication()
2498  {
2499    return getUserData().getReplicationOptions().getType() != DataReplicationOptions.Type.STANDALONE;
2500  }
2501
2502  /**
2503   * Returns <CODE>true</CODE> if we must create the ADS and <CODE>false</CODE>
2504   * otherwise.
2505   *
2506   * @return <CODE>true</CODE> if we must create the ADS and <CODE>false</CODE>
2507   *         otherwise.
2508   */
2509  protected boolean mustCreateAds()
2510  {
2511    return getUserData().getReplicationOptions().getType() != DataReplicationOptions.Type.STANDALONE;
2512  }
2513
2514  /**
2515   * Returns <CODE>true</CODE> if we must start the server and
2516   * <CODE>false</CODE> otherwise.
2517   *
2518   * @return <CODE>true</CODE> if we must start the server and
2519   *         <CODE>false</CODE> otherwise.
2520   */
2521  protected boolean mustStart()
2522  {
2523    return getUserData().getStartServer() || mustCreateAds();
2524  }
2525
2526  /**
2527   * Returns <CODE>true</CODE> if the start server must be launched in verbose
2528   * mode and <CODE>false</CODE> otherwise. The verbose flag is not enough
2529   * because in the case where many entries have been imported, the startup
2530   * phase can take long.
2531   *
2532   * @return <CODE>true</CODE> if the start server must be launched in verbose
2533   *         mode and <CODE>false</CODE> otherwise.
2534   */
2535  protected boolean isStartVerbose()
2536  {
2537    if (isVerbose())
2538    {
2539      return true;
2540    }
2541    boolean manyEntriesToImport = false;
2542    NewSuffixOptions.Type type = getUserData().getNewSuffixOptions().getType();
2543    if (type == NewSuffixOptions.Type.IMPORT_FROM_LDIF_FILE)
2544    {
2545      long mbTotalSize = 0;
2546      LinkedList<String> ldifPaths = getUserData().getNewSuffixOptions().getLDIFPaths();
2547      for (String ldifPath : ldifPaths)
2548      {
2549        File f = new File(ldifPath);
2550        mbTotalSize += f.length();
2551      }
2552      // Assume entries of 1kb
2553      if (mbTotalSize > THRESHOLD_VERBOSE_START * 1024)
2554      {
2555        manyEntriesToImport = true;
2556      }
2557    }
2558    else if (type == NewSuffixOptions.Type.IMPORT_AUTOMATICALLY_GENERATED_DATA)
2559    {
2560      int nEntries = getUserData().getNewSuffixOptions().getNumberEntries();
2561      if (nEntries > THRESHOLD_VERBOSE_START)
2562      {
2563        manyEntriesToImport = true;
2564      }
2565    }
2566    return manyEntriesToImport;
2567  }
2568
2569  /**
2570   * Returns <CODE>true</CODE> if we must stop the server and <CODE>false</CODE>
2571   * otherwise. The server might be stopped if the user asked not to start it at
2572   * the end of the installation and it was started temporarily to update its
2573   * configuration.
2574   *
2575   * @return <CODE>true</CODE> if we must stop the server and <CODE>false</CODE>
2576   *         otherwise.
2577   */
2578  protected boolean mustStop()
2579  {
2580    return !getUserData().getStartServer() && mustCreateAds();
2581  }
2582
2583  /**
2584   * Returns <CODE>true</CODE> if we must initialize suffixes and
2585   * <CODE>false</CODE> otherwise.
2586   *
2587   * @return <CODE>true</CODE> if we must initialize suffixes and
2588   *         <CODE>false</CODE> otherwise.
2589   */
2590  protected boolean mustInitializeSuffixes()
2591  {
2592    return getUserData().getReplicationOptions().getType() == DataReplicationOptions.Type.IN_EXISTING_TOPOLOGY;
2593  }
2594
2595  /**
2596   * Returns the list of preferred URLs to connect to remote servers. In fact it
2597   * returns only the URL to the remote server specified by the user in the
2598   * replication options panel. The method returns a list for convenience with
2599   * other interfaces.
2600   * <p>
2601   * NOTE: this method assumes that the UserData object has
2602   * already been updated with the host and port of the remote server.
2603   *
2604   * @return the list of preferred URLs to connect to remote servers.
2605   */
2606  private Set<PreferredConnection> getPreferredConnections()
2607  {
2608    Set<PreferredConnection> cnx = new LinkedHashSet<>();
2609    DataReplicationOptions repl = getUserData().getReplicationOptions();
2610    if (repl.getType() == DataReplicationOptions.Type.IN_EXISTING_TOPOLOGY)
2611    {
2612      AuthenticationData auth = repl.getAuthenticationData();
2613      if (auth != null)
2614      {
2615        PreferredConnection.Type type;
2616        if (auth.useSecureConnection())
2617        {
2618          type = PreferredConnection.Type.LDAPS;
2619        }
2620        else
2621        {
2622          type = PreferredConnection.Type.LDAP;
2623        }
2624        cnx.add(new PreferredConnection(getLdapUrl(auth), type));
2625      }
2626    }
2627    return cnx;
2628  }
2629
2630  private String getLdapUrl(AuthenticationData auth)
2631  {
2632    if (auth.useSecureConnection())
2633    {
2634      return "ldaps://" + auth.getHostName() + ":" + auth.getPort();
2635    }
2636    return "ldap://" + auth.getHostName() + ":" + auth.getPort();
2637  }
2638
2639  private String getHostDisplay(AuthenticationData auth)
2640  {
2641    return auth.getHostName() + ":" + auth.getPort();
2642  }
2643
2644  private Map<ADSContext.ServerProperty, Object> getNewServerAdsProperties(UserData userData)
2645  {
2646    Map<ADSContext.ServerProperty, Object> serverProperties = new HashMap<>();
2647    serverProperties.put(ADSContext.ServerProperty.HOST_NAME, userData.getHostName());
2648    serverProperties.put(ADSContext.ServerProperty.LDAP_PORT, String.valueOf(userData.getServerPort()));
2649    serverProperties.put(ADSContext.ServerProperty.LDAP_ENABLED, "true");
2650
2651    // TODO: even if the user does not configure SSL maybe we should choose
2652    // a secure port that is not being used and that we can actually use.
2653    SecurityOptions sec = userData.getSecurityOptions();
2654    if (sec.getEnableSSL())
2655    {
2656      serverProperties.put(ADSContext.ServerProperty.LDAPS_PORT, String.valueOf(sec.getSslPort()));
2657      serverProperties.put(ADSContext.ServerProperty.LDAPS_ENABLED, "true");
2658    }
2659    else
2660    {
2661      serverProperties.put(ADSContext.ServerProperty.LDAPS_PORT, "636");
2662      serverProperties.put(ADSContext.ServerProperty.LDAPS_ENABLED, "false");
2663    }
2664
2665    if (sec.getEnableStartTLS())
2666    {
2667      serverProperties.put(ADSContext.ServerProperty.STARTTLS_ENABLED, "true");
2668    }
2669    else
2670    {
2671      serverProperties.put(ADSContext.ServerProperty.STARTTLS_ENABLED, "false");
2672    }
2673
2674    serverProperties.put(ADSContext.ServerProperty.JMX_PORT, "1689");
2675    serverProperties.put(ADSContext.ServerProperty.JMX_ENABLED, "false");
2676
2677    String path;
2678    if (isWebStart())
2679    {
2680      path = userData.getServerLocation();
2681    }
2682    else
2683    {
2684      path = getInstallPathFromClasspath();
2685    }
2686    serverProperties.put(ADSContext.ServerProperty.INSTANCE_PATH, path);
2687
2688    String serverID = serverProperties.get(ADSContext.ServerProperty.HOST_NAME) + ":" + userData.getServerPort();
2689
2690    /* TODO: do we want to ask this specifically to the user? */
2691    serverProperties.put(ADSContext.ServerProperty.ID, serverID);
2692    serverProperties.put(ADSContext.ServerProperty.HOST_OS, OperatingSystem.getOperatingSystem().toString());
2693
2694    return serverProperties;
2695  }
2696
2697  private Map<ADSContext.AdministratorProperty, Object> getAdministratorProperties(UserData userData)
2698  {
2699    Map<ADSContext.AdministratorProperty, Object> adminProperties = new HashMap<>();
2700    adminProperties.put(ADSContext.AdministratorProperty.UID, userData.getGlobalAdministratorUID());
2701    adminProperties.put(ADSContext.AdministratorProperty.PASSWORD, userData.getGlobalAdministratorPassword());
2702    adminProperties.put(ADSContext.AdministratorProperty.DESCRIPTION,
2703                        INFO_GLOBAL_ADMINISTRATOR_DESCRIPTION.get().toString());
2704    return adminProperties;
2705  }
2706
2707  /**
2708   * Validate the data provided by the user in the server settings panel and
2709   * update the userData object according to that content.
2710   *
2711   * @throws UserDataException
2712   *           if the data provided by the user is not valid.
2713   */
2714  private void updateUserDataForServerSettingsPanel(QuickSetup qs) throws UserDataException
2715  {
2716    List<LocalizableMessage> errorMsgs = new ArrayList<>();
2717    LocalizableMessage confirmationMsg = null;
2718
2719    if (isWebStart())
2720    {
2721      // Check the server location
2722      String serverLocation = qs.getFieldStringValue(FieldName.SERVER_LOCATION);
2723
2724      if (serverLocation == null || "".equals(serverLocation.trim()))
2725      {
2726        errorMsgs.add(INFO_EMPTY_SERVER_LOCATION.get());
2727        qs.displayFieldInvalid(FieldName.SERVER_LOCATION, true);
2728      }
2729      else if (!parentDirectoryExists(serverLocation))
2730      {
2731        String existingParentDirectory = null;
2732        File f = new File(serverLocation);
2733        while (existingParentDirectory == null && f != null)
2734        {
2735          f = f.getParentFile();
2736          if (f != null && f.exists())
2737          {
2738            if (f.isDirectory())
2739            {
2740              existingParentDirectory = f.getAbsolutePath();
2741            }
2742            else
2743            {
2744              // The parent path is a file!
2745              f = null;
2746            }
2747          }
2748        }
2749        if (existingParentDirectory == null)
2750        {
2751          errorMsgs.add(INFO_PARENT_DIRECTORY_COULD_NOT_BE_FOUND.get(serverLocation));
2752          qs.displayFieldInvalid(FieldName.SERVER_LOCATION, true);
2753        }
2754        else if (!canWrite(existingParentDirectory))
2755        {
2756          errorMsgs.add(INFO_DIRECTORY_NOT_WRITABLE.get(existingParentDirectory));
2757          qs.displayFieldInvalid(FieldName.SERVER_LOCATION, true);
2758        }
2759        else if (!hasEnoughSpace(existingParentDirectory, getRequiredInstallSpace()))
2760        {
2761          long requiredInMb = getRequiredInstallSpace() / (1024 * 1024);
2762          errorMsgs.add(INFO_NOT_ENOUGH_DISK_SPACE.get(existingParentDirectory, requiredInMb));
2763          qs.displayFieldInvalid(FieldName.SERVER_LOCATION, true);
2764        }
2765        else
2766        {
2767          confirmationMsg = INFO_PARENT_DIRECTORY_DOES_NOT_EXIST_CONFIRMATION.get(serverLocation);
2768          getUserData().setServerLocation(serverLocation);
2769        }
2770      }
2771      else if (fileExists(serverLocation))
2772      {
2773        errorMsgs.add(INFO_FILE_EXISTS.get(serverLocation));
2774        qs.displayFieldInvalid(FieldName.SERVER_LOCATION, true);
2775      }
2776      else if (directoryExistsAndIsNotEmpty(serverLocation))
2777      {
2778        errorMsgs.add(INFO_DIRECTORY_EXISTS_NOT_EMPTY.get(serverLocation));
2779        qs.displayFieldInvalid(FieldName.SERVER_LOCATION, true);
2780      }
2781      else if (!canWrite(serverLocation))
2782      {
2783        errorMsgs.add(INFO_DIRECTORY_NOT_WRITABLE.get(serverLocation));
2784        qs.displayFieldInvalid(FieldName.SERVER_LOCATION, true);
2785      }
2786      else if (!hasEnoughSpace(serverLocation, getRequiredInstallSpace()))
2787      {
2788        long requiredInMb = getRequiredInstallSpace() / (1024 * 1024);
2789        errorMsgs.add(INFO_NOT_ENOUGH_DISK_SPACE.get(serverLocation, requiredInMb));
2790        qs.displayFieldInvalid(FieldName.SERVER_LOCATION, true);
2791      }
2792      else if (OperatingSystem.isWindows() && serverLocation.contains("%"))
2793      {
2794        errorMsgs.add(INFO_INVALID_CHAR_IN_PATH.get("%"));
2795        qs.displayFieldInvalid(FieldName.SERVER_LOCATION, true);
2796      }
2797      else
2798      {
2799        getUserData().setServerLocation(serverLocation);
2800        qs.displayFieldInvalid(FieldName.SERVER_LOCATION, false);
2801      }
2802    }
2803
2804    // Check the host is not empty.
2805    // TODO: check that the host name is valid...
2806    String hostName = qs.getFieldStringValue(FieldName.HOST_NAME);
2807    if (hostName == null || hostName.trim().length() == 0)
2808    {
2809      errorMsgs.add(INFO_EMPTY_HOST_NAME.get());
2810      qs.displayFieldInvalid(FieldName.HOST_NAME, true);
2811    }
2812    else
2813    {
2814      qs.displayFieldInvalid(FieldName.HOST_NAME, false);
2815      getUserData().setHostName(hostName);
2816    }
2817
2818    // Check the port
2819    String sPort = qs.getFieldStringValue(FieldName.SERVER_PORT);
2820    int port = -1;
2821    try
2822    {
2823      port = Integer.parseInt(sPort);
2824      if (port < MIN_PORT_VALUE || port > MAX_PORT_VALUE)
2825      {
2826        errorMsgs.add(INFO_INVALID_PORT_VALUE_RANGE.get(MIN_PORT_VALUE, MAX_PORT_VALUE));
2827        qs.displayFieldInvalid(FieldName.SERVER_PORT, true);
2828      }
2829      else if (!canUseAsPort(port))
2830      {
2831        errorMsgs.add(getCannotBindErrorMessage(port));
2832        qs.displayFieldInvalid(FieldName.SERVER_PORT, true);
2833      }
2834      else
2835      {
2836        getUserData().setServerPort(port);
2837        qs.displayFieldInvalid(FieldName.SERVER_PORT, false);
2838      }
2839    }
2840    catch (NumberFormatException nfe)
2841    {
2842      errorMsgs.add(INFO_INVALID_PORT_VALUE_RANGE.get(MIN_PORT_VALUE, MAX_PORT_VALUE));
2843      qs.displayFieldInvalid(FieldName.SERVER_PORT, true);
2844    }
2845
2846    //  Check the admin connector port
2847    sPort = qs.getFieldStringValue(FieldName.ADMIN_CONNECTOR_PORT);
2848    int adminConnectorPort = -1;
2849    try
2850    {
2851      adminConnectorPort = Integer.parseInt(sPort);
2852      if (adminConnectorPort < MIN_PORT_VALUE || adminConnectorPort > MAX_PORT_VALUE)
2853      {
2854        errorMsgs.add(INFO_INVALID_PORT_VALUE_RANGE.get(MIN_PORT_VALUE, MAX_PORT_VALUE));
2855        qs.displayFieldInvalid(FieldName.ADMIN_CONNECTOR_PORT, true);
2856      }
2857      else if (!canUseAsPort(adminConnectorPort))
2858      {
2859        errorMsgs.add(getCannotBindErrorMessage(adminConnectorPort));
2860        qs.displayFieldInvalid(FieldName.ADMIN_CONNECTOR_PORT, true);
2861      }
2862      else if (adminConnectorPort == port)
2863      {
2864        errorMsgs.add(INFO_ADMIN_CONNECTOR_VALUE_SEVERAL_TIMES.get());
2865        qs.displayFieldInvalid(FieldName.SERVER_PORT, true);
2866        qs.displayFieldInvalid(FieldName.ADMIN_CONNECTOR_PORT, true);
2867      }
2868      else
2869      {
2870        getUserData().setAdminConnectorPort(adminConnectorPort);
2871        qs.displayFieldInvalid(FieldName.ADMIN_CONNECTOR_PORT, false);
2872      }
2873    }
2874    catch (NumberFormatException nfe)
2875    {
2876      errorMsgs.add(INFO_INVALID_PORT_VALUE_RANGE.get(MIN_PORT_VALUE, MAX_PORT_VALUE));
2877      qs.displayFieldInvalid(FieldName.ADMIN_CONNECTOR_PORT, true);
2878    }
2879
2880    // Check the secure port
2881    SecurityOptions sec = (SecurityOptions) qs.getFieldValue(FieldName.SECURITY_OPTIONS);
2882    int securePort = sec.getSslPort();
2883    if (sec.getEnableSSL())
2884    {
2885      if (securePort < MIN_PORT_VALUE || securePort > MAX_PORT_VALUE)
2886      {
2887        errorMsgs.add(INFO_INVALID_SECURE_PORT_VALUE_RANGE.get(MIN_PORT_VALUE, MAX_PORT_VALUE));
2888        qs.displayFieldInvalid(FieldName.SECURITY_OPTIONS, true);
2889      }
2890      else if (!canUseAsPort(securePort))
2891      {
2892        errorMsgs.add(getCannotBindErrorMessage(securePort));
2893        qs.displayFieldInvalid(FieldName.SECURITY_OPTIONS, true);
2894      }
2895      else if (port == securePort)
2896      {
2897        errorMsgs.add(INFO_EQUAL_PORTS.get());
2898        qs.displayFieldInvalid(FieldName.SECURITY_OPTIONS, true);
2899        qs.displayFieldInvalid(FieldName.SERVER_PORT, true);
2900      }
2901      else if (adminConnectorPort == securePort)
2902      {
2903        errorMsgs.add(INFO_ADMIN_CONNECTOR_VALUE_SEVERAL_TIMES.get());
2904        qs.displayFieldInvalid(FieldName.SECURITY_OPTIONS, true);
2905        qs.displayFieldInvalid(FieldName.ADMIN_CONNECTOR_PORT, true);
2906      }
2907      else
2908      {
2909        getUserData().setSecurityOptions(sec);
2910        qs.displayFieldInvalid(FieldName.SECURITY_OPTIONS, false);
2911      }
2912    }
2913    else
2914    {
2915      getUserData().setSecurityOptions(sec);
2916      qs.displayFieldInvalid(FieldName.SECURITY_OPTIONS, false);
2917    }
2918
2919    // Check the Directory Manager DN
2920    String dmDn = qs.getFieldStringValue(FieldName.DIRECTORY_MANAGER_DN);
2921
2922    if (dmDn == null || dmDn.trim().length() == 0)
2923    {
2924      errorMsgs.add(INFO_EMPTY_DIRECTORY_MANAGER_DN.get());
2925      qs.displayFieldInvalid(FieldName.DIRECTORY_MANAGER_DN, true);
2926    }
2927    else if (!isDN(dmDn))
2928    {
2929      errorMsgs.add(INFO_NOT_A_DIRECTORY_MANAGER_DN.get());
2930      qs.displayFieldInvalid(FieldName.DIRECTORY_MANAGER_DN, true);
2931    }
2932    else if (isConfigurationDn(dmDn))
2933    {
2934      errorMsgs.add(INFO_DIRECTORY_MANAGER_DN_IS_CONFIG_DN.get());
2935      qs.displayFieldInvalid(FieldName.DIRECTORY_MANAGER_DN, true);
2936    }
2937    else
2938    {
2939      getUserData().setDirectoryManagerDn(dmDn);
2940      qs.displayFieldInvalid(FieldName.DIRECTORY_MANAGER_DN, false);
2941    }
2942
2943    // Check the provided passwords
2944    String pwd1 = qs.getFieldStringValue(FieldName.DIRECTORY_MANAGER_PWD);
2945    String pwd2 = qs.getFieldStringValue(FieldName.DIRECTORY_MANAGER_PWD_CONFIRM);
2946    if (pwd1 == null)
2947    {
2948      pwd1 = "";
2949    }
2950
2951    boolean pwdValid = true;
2952    if (!pwd1.equals(pwd2))
2953    {
2954      errorMsgs.add(INFO_NOT_EQUAL_PWD.get());
2955      qs.displayFieldInvalid(FieldName.DIRECTORY_MANAGER_PWD_CONFIRM, true);
2956      pwdValid = false;
2957    }
2958    if (pwd1.length() < MIN_DIRECTORY_MANAGER_PWD)
2959    {
2960      errorMsgs.add(INFO_PWD_TOO_SHORT.get(MIN_DIRECTORY_MANAGER_PWD));
2961      qs.displayFieldInvalid(FieldName.DIRECTORY_MANAGER_PWD, true);
2962      if (pwd2 == null || pwd2.length() < MIN_DIRECTORY_MANAGER_PWD)
2963      {
2964        qs.displayFieldInvalid(FieldName.DIRECTORY_MANAGER_PWD_CONFIRM, true);
2965      }
2966      pwdValid = false;
2967    }
2968
2969    if (pwdValid)
2970    {
2971      getUserData().setDirectoryManagerPwd(pwd1);
2972      qs.displayFieldInvalid(FieldName.DIRECTORY_MANAGER_PWD, false);
2973      qs.displayFieldInvalid(FieldName.DIRECTORY_MANAGER_PWD_CONFIRM, false);
2974    }
2975
2976    // For the moment do not enable JMX
2977    int defaultJMXPort = UserData.getDefaultJMXPort(new int[] { port, securePort });
2978    if (defaultJMXPort != -1)
2979    {
2980      //getUserData().setServerJMXPort(defaultJMXPort);
2981      getUserData().setServerJMXPort(-1);
2982    }
2983
2984    if (!errorMsgs.isEmpty())
2985    {
2986      throw new UserDataException(Step.SERVER_SETTINGS, getMessageFromCollection(errorMsgs, "\n"));
2987    }
2988    if (confirmationMsg != null)
2989    {
2990      throw new UserDataConfirmationException(Step.SERVER_SETTINGS, confirmationMsg);
2991    }
2992  }
2993
2994  private LocalizableMessage getCannotBindErrorMessage(int port)
2995  {
2996    if (isPrivilegedPort(port))
2997    {
2998      return INFO_CANNOT_BIND_PRIVILEDGED_PORT.get(port);
2999    }
3000    return INFO_CANNOT_BIND_PORT.get(port);
3001  }
3002
3003  /**
3004   * Validate the data provided by the user in the data options panel and update
3005   * the userData object according to that content.
3006   *
3007   * @throws UserDataException
3008   *           if the data provided by the user is not valid.
3009   */
3010  private void updateUserDataForReplicationOptionsPanel(QuickSetup qs) throws UserDataException
3011  {
3012    boolean hasGlobalAdministrators = false;
3013    int replicationPort = -1;
3014    boolean secureReplication = false;
3015    Integer port = null;
3016    List<LocalizableMessage> errorMsgs = new ArrayList<>();
3017
3018    DataReplicationOptions.Type type = (DataReplicationOptions.Type) qs.getFieldValue(FieldName.REPLICATION_OPTIONS);
3019    String host = qs.getFieldStringValue(FieldName.REMOTE_SERVER_HOST);
3020    String dn = qs.getFieldStringValue(FieldName.REMOTE_SERVER_DN);
3021    String pwd = qs.getFieldStringValue(FieldName.REMOTE_SERVER_PWD);
3022
3023    if (type != DataReplicationOptions.Type.STANDALONE)
3024    {
3025      // Check replication port
3026      replicationPort = checkReplicationPort(qs, errorMsgs);
3027      secureReplication = (Boolean) qs.getFieldValue(FieldName.REPLICATION_SECURE);
3028    }
3029
3030    UserDataConfirmationException confirmEx = null;
3031    switch (type)
3032    {
3033    case IN_EXISTING_TOPOLOGY:
3034    {
3035      String sPort = qs.getFieldStringValue(FieldName.REMOTE_SERVER_PORT);
3036      checkRemoteHostPortDnAndPwd(host, sPort, dn, pwd, qs, errorMsgs);
3037
3038      if (errorMsgs.isEmpty())
3039      {
3040        port = Integer.parseInt(sPort);
3041        // Try to connect
3042        boolean[] globalAdmin = { hasGlobalAdministrators };
3043        String[] effectiveDn = { dn };
3044        try
3045        {
3046          updateUserDataWithADS(host, port, dn, pwd, qs, errorMsgs, globalAdmin, effectiveDn);
3047        }
3048        catch (UserDataConfirmationException e)
3049        {
3050          confirmEx = e;
3051        }
3052        hasGlobalAdministrators = globalAdmin[0];
3053        dn = effectiveDn[0];
3054      }
3055      break;
3056    }
3057    case STANDALONE:
3058    {
3059      getUserData().setSuffixesToReplicateOptions(
3060          new SuffixesToReplicateOptions(SuffixesToReplicateOptions.Type.NO_SUFFIX_TO_REPLICATE,
3061              new HashSet<SuffixDescriptor>(), new HashSet<SuffixDescriptor>()));
3062      break;
3063    }
3064    case FIRST_IN_TOPOLOGY:
3065    {
3066      getUserData().setSuffixesToReplicateOptions(
3067          new SuffixesToReplicateOptions(SuffixesToReplicateOptions.Type.NEW_SUFFIX_IN_TOPOLOGY,
3068              new HashSet<SuffixDescriptor>(), new HashSet<SuffixDescriptor>()));
3069      break;
3070    }
3071    default:
3072      throw new IllegalStateException("Do not know what to do with type: " + type);
3073    }
3074
3075    if (errorMsgs.isEmpty())
3076    {
3077      AuthenticationData auth = new AuthenticationData();
3078      auth.setHostName(host);
3079      if (port != null)
3080      {
3081        auth.setPort(port);
3082      }
3083      auth.setDn(dn);
3084      auth.setPwd(pwd);
3085      auth.setUseSecureConnection(true);
3086
3087      getUserData().setReplicationOptions(createDataReplicationOptions(replicationPort, secureReplication, type, auth));
3088      getUserData().createAdministrator(
3089          !hasGlobalAdministrators && type == DataReplicationOptions.Type.IN_EXISTING_TOPOLOGY);
3090    }
3091    if (!errorMsgs.isEmpty())
3092    {
3093      throw new UserDataException(Step.REPLICATION_OPTIONS, getMessageFromCollection(errorMsgs, "\n"));
3094    }
3095    if (confirmEx != null)
3096    {
3097      throw confirmEx;
3098    }
3099  }
3100
3101  private DataReplicationOptions createDataReplicationOptions(int replicationPort, boolean secureReplication,
3102      DataReplicationOptions.Type type, AuthenticationData auth)
3103  {
3104    switch (type)
3105    {
3106    case IN_EXISTING_TOPOLOGY:
3107      return DataReplicationOptions.createInExistingTopology(auth, replicationPort, secureReplication);
3108    case STANDALONE:
3109      return DataReplicationOptions.createStandalone();
3110    case FIRST_IN_TOPOLOGY:
3111      return DataReplicationOptions.createFirstInTopology(replicationPort, secureReplication);
3112    default:
3113      throw new IllegalStateException("Do not know what to do with type: " + type);
3114    }
3115  }
3116
3117  private int checkReplicationPort(QuickSetup qs, List<LocalizableMessage> errorMsgs)
3118  {
3119    int replicationPort = -1;
3120    String sPort = qs.getFieldStringValue(FieldName.REPLICATION_PORT);
3121    try
3122    {
3123      replicationPort = Integer.parseInt(sPort);
3124      if (replicationPort < MIN_PORT_VALUE || replicationPort > MAX_PORT_VALUE)
3125      {
3126        errorMsgs.add(INFO_INVALID_REPLICATION_PORT_VALUE_RANGE.get(MIN_PORT_VALUE, MAX_PORT_VALUE));
3127        qs.displayFieldInvalid(FieldName.SERVER_PORT, true);
3128      }
3129      else if (!canUseAsPort(replicationPort))
3130      {
3131        errorMsgs.add(getCannotBindErrorMessage(replicationPort));
3132        qs.displayFieldInvalid(FieldName.REPLICATION_PORT, true);
3133      }
3134      else
3135      {
3136        /* Check that we did not chose this port for another protocol */
3137        SecurityOptions sec = getUserData().getSecurityOptions();
3138        if (replicationPort == getUserData().getServerPort() || replicationPort == getUserData().getServerJMXPort()
3139            || (replicationPort == sec.getSslPort() && sec.getEnableSSL()))
3140        {
3141          errorMsgs.add(INFO_REPLICATION_PORT_ALREADY_CHOSEN_FOR_OTHER_PROTOCOL.get());
3142          qs.displayFieldInvalid(FieldName.REPLICATION_PORT, true);
3143        }
3144        else
3145        {
3146          qs.displayFieldInvalid(FieldName.REPLICATION_PORT, false);
3147        }
3148      }
3149    }
3150    catch (NumberFormatException nfe)
3151    {
3152      errorMsgs.add(INFO_INVALID_REPLICATION_PORT_VALUE_RANGE.get(MIN_PORT_VALUE, MAX_PORT_VALUE));
3153      qs.displayFieldInvalid(FieldName.REPLICATION_PORT, true);
3154    }
3155    return replicationPort;
3156  }
3157
3158  private void checkRemoteHostPortDnAndPwd(String host, String sPort, String dn, String pwd, QuickSetup qs,
3159      List<LocalizableMessage> errorMsgs)
3160  {
3161    // Check host
3162    if (host == null || host.length() == 0)
3163    {
3164      errorMsgs.add(INFO_EMPTY_REMOTE_HOST.get());
3165      qs.displayFieldInvalid(FieldName.REMOTE_SERVER_HOST, true);
3166    }
3167    else
3168    {
3169      qs.displayFieldInvalid(FieldName.REMOTE_SERVER_HOST, false);
3170    }
3171
3172    // Check port
3173    try
3174    {
3175      Integer.parseInt(sPort);
3176      qs.displayFieldInvalid(FieldName.REMOTE_SERVER_PORT, false);
3177    }
3178    catch (Throwable t)
3179    {
3180      errorMsgs.add(INFO_INVALID_REMOTE_PORT.get());
3181      qs.displayFieldInvalid(FieldName.REMOTE_SERVER_PORT, true);
3182    }
3183
3184    // Check dn
3185    if (dn == null || dn.length() == 0)
3186    {
3187      errorMsgs.add(INFO_EMPTY_REMOTE_DN.get());
3188      qs.displayFieldInvalid(FieldName.REMOTE_SERVER_DN, true);
3189    }
3190    else
3191    {
3192      qs.displayFieldInvalid(FieldName.REMOTE_SERVER_DN, false);
3193    }
3194
3195    // Check password
3196    if (pwd == null || pwd.length() == 0)
3197    {
3198      errorMsgs.add(INFO_EMPTY_REMOTE_PWD.get());
3199      qs.displayFieldInvalid(FieldName.REMOTE_SERVER_PWD, true);
3200    }
3201    else
3202    {
3203      qs.displayFieldInvalid(FieldName.REMOTE_SERVER_PWD, false);
3204    }
3205  }
3206
3207  private void updateUserDataWithADS(String host, int port, String dn, String pwd, QuickSetup qs,
3208      List<LocalizableMessage> errorMsgs, boolean[] hasGlobalAdministrators, String[] effectiveDn)
3209      throws UserDataException
3210  {
3211    host = getHostNameForLdapUrl(host);
3212    String ldapUrl = "ldaps://" + host + ":" + port;
3213    InitialLdapContext ctx = null;
3214
3215    ApplicationTrustManager trustManager = getTrustManager();
3216    trustManager.setHost(host);
3217    trustManager.resetLastRefusedItems();
3218    try
3219    {
3220      effectiveDn[0] = dn;
3221      try
3222      {
3223        ctx = createLdapsContext(ldapUrl, dn, pwd, getConnectTimeout(), null, trustManager, null);
3224      }
3225      catch (Throwable t)
3226      {
3227        if (!isCertificateException(t))
3228        {
3229          // Try using a global administrator
3230          dn = ADSContext.getAdministratorDN(dn);
3231          effectiveDn[0] = dn;
3232          ctx = createLdapsContext(ldapUrl, dn, pwd, getConnectTimeout(), null, trustManager, null);
3233        }
3234        else
3235        {
3236          throw t;
3237        }
3238      }
3239
3240      ADSContext adsContext = new ADSContext(ctx);
3241      if (adsContext.hasAdminData())
3242      {
3243        /* Check if there are already global administrators */
3244        Set<?> administrators = adsContext.readAdministratorRegistry();
3245        hasGlobalAdministrators[0] = !administrators.isEmpty();
3246        Set<TopologyCacheException> exceptions = updateUserDataWithSuffixesInADS(adsContext, trustManager);
3247        Set<LocalizableMessage> exceptionMsgs = new LinkedHashSet<>();
3248        /* Check the exceptions and see if we throw them or not. */
3249        for (TopologyCacheException e : exceptions)
3250        {
3251          switch (e.getType())
3252          {
3253          case NOT_GLOBAL_ADMINISTRATOR:
3254            LocalizableMessage errorMsg = INFO_NOT_GLOBAL_ADMINISTRATOR_PROVIDED.get();
3255            throw new UserDataException(Step.REPLICATION_OPTIONS, errorMsg);
3256          case GENERIC_CREATING_CONNECTION:
3257            if (isCertificateException(e.getCause()))
3258            {
3259              UserDataCertificateException.Type excType;
3260              ApplicationTrustManager.Cause cause = null;
3261              if (e.getTrustManager() != null)
3262              {
3263                cause = e.getTrustManager().getLastRefusedCause();
3264              }
3265              logger.info(LocalizableMessage.raw("Certificate exception cause: " + cause));
3266              if (cause == ApplicationTrustManager.Cause.NOT_TRUSTED)
3267              {
3268                excType = UserDataCertificateException.Type.NOT_TRUSTED;
3269              }
3270              else if (cause == ApplicationTrustManager.Cause.HOST_NAME_MISMATCH)
3271              {
3272                excType = UserDataCertificateException.Type.HOST_NAME_MISMATCH;
3273              }
3274              else
3275              {
3276                excType = null;
3277              }
3278              if (excType != null)
3279              {
3280                String h;
3281                int p;
3282                try
3283                {
3284                  URI uri = new URI(e.getLdapUrl());
3285                  h = uri.getHost();
3286                  p = uri.getPort();
3287                }
3288                catch (Throwable t)
3289                {
3290                  logger.warn(LocalizableMessage.raw("Error parsing ldap url of TopologyCacheException.", t));
3291                  h = INFO_NOT_AVAILABLE_LABEL.get().toString();
3292                  p = -1;
3293                }
3294                throw new UserDataCertificateException(Step.REPLICATION_OPTIONS, INFO_CERTIFICATE_EXCEPTION.get(h, p),
3295                    e.getCause(), h, p, e.getTrustManager().getLastRefusedChain(), e.getTrustManager()
3296                        .getLastRefusedAuthType(), excType);
3297              }
3298            }
3299            break;
3300          default:
3301            break;
3302          }
3303          exceptionMsgs.add(getMessage(e));
3304        }
3305        if (!exceptionMsgs.isEmpty())
3306        {
3307          LocalizableMessage confirmationMsg =
3308              INFO_ERROR_READING_REGISTERED_SERVERS_CONFIRM.get(getMessageFromCollection(exceptionMsgs, "\n"));
3309          throw new UserDataConfirmationException(Step.REPLICATION_OPTIONS, confirmationMsg);
3310        }
3311      }
3312      else
3313      {
3314        updateUserDataWithSuffixesInServer(ctx);
3315      }
3316    }
3317    catch (UserDataException ude)
3318    {
3319      throw ude;
3320    }
3321    catch (Throwable t)
3322    {
3323      logger.info(LocalizableMessage.raw("Error connecting to remote server.", t));
3324      if (isCertificateException(t))
3325      {
3326        UserDataCertificateException.Type excType;
3327        ApplicationTrustManager.Cause cause = trustManager.getLastRefusedCause();
3328        logger.info(LocalizableMessage.raw("Certificate exception cause: " + cause));
3329        if (cause == ApplicationTrustManager.Cause.NOT_TRUSTED)
3330        {
3331          excType = UserDataCertificateException.Type.NOT_TRUSTED;
3332        }
3333        else if (cause == ApplicationTrustManager.Cause.HOST_NAME_MISMATCH)
3334        {
3335          excType = UserDataCertificateException.Type.HOST_NAME_MISMATCH;
3336        }
3337        else
3338        {
3339          excType = null;
3340        }
3341
3342        if (excType != null)
3343        {
3344          throw new UserDataCertificateException(Step.REPLICATION_OPTIONS, INFO_CERTIFICATE_EXCEPTION.get(host, port),
3345              t, host, port, trustManager.getLastRefusedChain(), trustManager.getLastRefusedAuthType(), excType);
3346        }
3347        else
3348        {
3349          qs.displayFieldInvalid(FieldName.REMOTE_SERVER_HOST, true);
3350          qs.displayFieldInvalid(FieldName.REMOTE_SERVER_PORT, true);
3351          qs.displayFieldInvalid(FieldName.REMOTE_SERVER_DN, true);
3352          qs.displayFieldInvalid(FieldName.REMOTE_SERVER_PWD, true);
3353          errorMsgs.add(INFO_CANNOT_CONNECT_TO_REMOTE_GENERIC.get(host + ":" + port, t));
3354        }
3355      }
3356      else if (t instanceof NamingException)
3357      {
3358        errorMsgs.add(getMessageForException((NamingException) t, host + ":" + port));
3359        qs.displayFieldInvalid(FieldName.REMOTE_SERVER_DN, true);
3360        qs.displayFieldInvalid(FieldName.REMOTE_SERVER_PWD, true);
3361        if (!(t instanceof NamingSecurityException))
3362        {
3363          qs.displayFieldInvalid(FieldName.REMOTE_SERVER_HOST, true);
3364          qs.displayFieldInvalid(FieldName.REMOTE_SERVER_PORT, true);
3365        }
3366      }
3367      else if (t instanceof ADSContextException)
3368      {
3369        errorMsgs.add(INFO_REMOTE_ADS_EXCEPTION.get(host + ":" + port, t));
3370      }
3371      else
3372      {
3373        throw new UserDataException(Step.REPLICATION_OPTIONS, getThrowableMsg(INFO_BUG_MSG.get(), t));
3374      }
3375    }
3376    finally
3377    {
3378      StaticUtils.close(ctx);
3379    }
3380  }
3381
3382  /**
3383   * Validate the data provided by the user in the create global administrator
3384   * panel and update the UserInstallData object according to that content.
3385   *
3386   * @throws UserDataException
3387   *           if the data provided by the user is not valid.
3388   */
3389  private void updateUserDataForCreateAdministratorPanel(QuickSetup qs) throws UserDataException
3390  {
3391    List<LocalizableMessage> errorMsgs = new ArrayList<>();
3392
3393    // Check the Global Administrator UID
3394    String uid = qs.getFieldStringValue(FieldName.GLOBAL_ADMINISTRATOR_UID);
3395
3396    if (uid == null || uid.trim().length() == 0)
3397    {
3398      errorMsgs.add(INFO_EMPTY_ADMINISTRATOR_UID.get());
3399      qs.displayFieldInvalid(FieldName.GLOBAL_ADMINISTRATOR_UID, true);
3400    }
3401    else
3402    {
3403      getUserData().setGlobalAdministratorUID(uid);
3404      qs.displayFieldInvalid(FieldName.GLOBAL_ADMINISTRATOR_UID, false);
3405    }
3406
3407    // Check the provided passwords
3408    String pwd1 = qs.getFieldStringValue(FieldName.GLOBAL_ADMINISTRATOR_PWD);
3409    String pwd2 = qs.getFieldStringValue(FieldName.GLOBAL_ADMINISTRATOR_PWD_CONFIRM);
3410    if (pwd1 == null)
3411    {
3412      pwd1 = "";
3413    }
3414
3415    boolean pwdValid = true;
3416    if (!pwd1.equals(pwd2))
3417    {
3418      errorMsgs.add(INFO_NOT_EQUAL_PWD.get());
3419      qs.displayFieldInvalid(FieldName.GLOBAL_ADMINISTRATOR_PWD_CONFIRM, true);
3420      pwdValid = false;
3421    }
3422    if (pwd1.length() < MIN_DIRECTORY_MANAGER_PWD)
3423    {
3424      errorMsgs.add(INFO_PWD_TOO_SHORT.get(MIN_DIRECTORY_MANAGER_PWD));
3425      qs.displayFieldInvalid(FieldName.GLOBAL_ADMINISTRATOR_PWD, true);
3426      if (pwd2 == null || pwd2.length() < MIN_DIRECTORY_MANAGER_PWD)
3427      {
3428        qs.displayFieldInvalid(FieldName.GLOBAL_ADMINISTRATOR_PWD_CONFIRM, true);
3429      }
3430      pwdValid = false;
3431    }
3432
3433    if (pwdValid)
3434    {
3435      getUserData().setGlobalAdministratorPassword(pwd1);
3436      qs.displayFieldInvalid(FieldName.GLOBAL_ADMINISTRATOR_PWD, false);
3437      qs.displayFieldInvalid(FieldName.GLOBAL_ADMINISTRATOR_PWD_CONFIRM, false);
3438    }
3439
3440    if (!errorMsgs.isEmpty())
3441    {
3442      throw new UserDataException(Step.CREATE_GLOBAL_ADMINISTRATOR, getMessageFromCollection(errorMsgs, "\n"));
3443    }
3444  }
3445
3446  /**
3447   * Validate the data provided by the user in the replicate suffixes options
3448   * panel and update the UserInstallData object according to that content.
3449   *
3450   * @throws UserDataException
3451   *           if the data provided by the user is not valid.
3452   */
3453  @SuppressWarnings("unchecked")
3454  private void updateUserDataForSuffixesOptionsPanel(QuickSetup qs) throws UserDataException
3455  {
3456    List<LocalizableMessage> errorMsgs = new ArrayList<>();
3457    if (qs.getFieldValue(FieldName.SUFFIXES_TO_REPLICATE_OPTIONS) ==
3458        SuffixesToReplicateOptions.Type.REPLICATE_WITH_EXISTING_SUFFIXES)
3459    {
3460      Set<?> s = (Set<?>) qs.getFieldValue(FieldName.SUFFIXES_TO_REPLICATE);
3461      if (s.isEmpty())
3462      {
3463        errorMsgs.add(INFO_NO_SUFFIXES_CHOSEN_TO_REPLICATE.get());
3464        qs.displayFieldInvalid(FieldName.SUFFIXES_TO_REPLICATE, true);
3465      }
3466      else
3467      {
3468        Set<SuffixDescriptor> chosen = new HashSet<>();
3469        for (Object o : s)
3470        {
3471          chosen.add((SuffixDescriptor) o);
3472        }
3473        qs.displayFieldInvalid(FieldName.SUFFIXES_TO_REPLICATE, false);
3474        Set<SuffixDescriptor> available = getUserData().getSuffixesToReplicateOptions().getAvailableSuffixes();
3475        Map<String, BackendTypeUIAdapter> suffixesBackendTypes =
3476            (Map<String, BackendTypeUIAdapter>) qs.getFieldValue(FieldName.SUFFIXES_TO_REPLICATE_BACKEND_TYPE);
3477        SuffixesToReplicateOptions options = new SuffixesToReplicateOptions(
3478            SuffixesToReplicateOptions.Type.REPLICATE_WITH_EXISTING_SUFFIXES, available, chosen, suffixesBackendTypes);
3479        getUserData().setSuffixesToReplicateOptions(options);
3480      }
3481      getUserData().setRemoteWithNoReplicationPort(getRemoteWithNoReplicationPort(getUserData()));
3482    }
3483    else
3484    {
3485      Set<SuffixDescriptor> available = getUserData().getSuffixesToReplicateOptions().getAvailableSuffixes();
3486      Set<SuffixDescriptor> chosen = getUserData().getSuffixesToReplicateOptions().getSuffixes();
3487      SuffixesToReplicateOptions options =
3488          new SuffixesToReplicateOptions(SuffixesToReplicateOptions.Type.NEW_SUFFIX_IN_TOPOLOGY, available, chosen);
3489      getUserData().setSuffixesToReplicateOptions(options);
3490    }
3491
3492    if (!errorMsgs.isEmpty())
3493    {
3494      throw new UserDataException(Step.SUFFIXES_OPTIONS, getMessageFromCollection(errorMsgs, "\n"));
3495    }
3496  }
3497
3498  /**
3499   * Validate the data provided by the user in the remote server replication
3500   * port panel and update the userData object according to that content.
3501   *
3502   * @throws UserDataException
3503   *           if the data provided by the user is not valid.
3504   */
3505  private void updateUserDataForRemoteReplicationPorts(QuickSetup qs) throws UserDataException
3506  {
3507    List<LocalizableMessage> errorMsgs = new ArrayList<>();
3508    Map<ServerDescriptor, AuthenticationData> servers = getUserData().getRemoteWithNoReplicationPort();
3509    Map<?, ?> hm = (Map<?, ?>) qs.getFieldValue(FieldName.REMOTE_REPLICATION_PORT);
3510    Map<?, ?> hmSecure = (Map<?, ?>) qs.getFieldValue(FieldName.REMOTE_REPLICATION_SECURE);
3511    for (ServerDescriptor server : servers.keySet())
3512    {
3513      String hostName = server.getHostName();
3514      boolean secureReplication = (Boolean) hmSecure.get(server.getId());
3515      String sPort = (String) hm.get(server.getId());
3516      try
3517      {
3518        int replicationPort = Integer.parseInt(sPort);
3519        if (replicationPort < MIN_PORT_VALUE || replicationPort > MAX_PORT_VALUE)
3520        {
3521          errorMsgs.add(INFO_INVALID_REMOTE_REPLICATION_PORT_VALUE_RANGE.get(getHostPort(server), MIN_PORT_VALUE,
3522              MAX_PORT_VALUE));
3523        }
3524        if (hostName.equalsIgnoreCase(getUserData().getHostName()))
3525        {
3526          int securePort = -1;
3527          if (getUserData().getSecurityOptions().getEnableSSL())
3528          {
3529            securePort = getUserData().getSecurityOptions().getSslPort();
3530          }
3531          if (replicationPort == getUserData().getServerPort() || replicationPort == getUserData().getServerJMXPort()
3532              || replicationPort == getUserData().getReplicationOptions().getReplicationPort()
3533              || replicationPort == securePort)
3534          {
3535            errorMsgs.add(INFO_REMOTE_REPLICATION_PORT_ALREADY_CHOSEN_FOR_OTHER_PROTOCOL.get(getHostPort(server)));
3536          }
3537        }
3538        AuthenticationData authData = new AuthenticationData();
3539        authData.setPort(replicationPort);
3540        authData.setUseSecureConnection(secureReplication);
3541        servers.put(server, authData);
3542      }
3543      catch (NumberFormatException nfe)
3544      {
3545        errorMsgs.add(INFO_INVALID_REMOTE_REPLICATION_PORT_VALUE_RANGE.get(hostName, MIN_PORT_VALUE, MAX_PORT_VALUE));
3546      }
3547    }
3548
3549    if (!errorMsgs.isEmpty())
3550    {
3551      qs.displayFieldInvalid(FieldName.REMOTE_REPLICATION_PORT, true);
3552      throw new UserDataException(Step.REMOTE_REPLICATION_PORTS, getMessageFromCollection(errorMsgs, "\n"));
3553    }
3554    else
3555    {
3556      qs.displayFieldInvalid(FieldName.REMOTE_REPLICATION_PORT, false);
3557      getUserData().setRemoteWithNoReplicationPort(servers);
3558    }
3559  }
3560
3561  /**
3562   * Validate the data provided by the user in the new suffix data options panel
3563   * and update the UserInstallData object according to that content.
3564   *
3565   * @throws UserDataException
3566   *           if the data provided by the user is not valid.
3567   */
3568  @SuppressWarnings("unchecked")
3569  private void updateUserDataForNewSuffixOptionsPanel(final QuickSetup ui) throws UserDataException
3570  {
3571    final List<LocalizableMessage> errorMsgs = new ArrayList<>();
3572    // Singleton list with the provided baseDN (if exists and valid)
3573    List<String> baseDn = new LinkedList<>();
3574    boolean validBaseDn = checkProvidedBaseDn(ui, baseDn, errorMsgs);
3575    final NewSuffixOptions dataOptions = checkImportData(ui, baseDn, validBaseDn, errorMsgs);
3576
3577    if (dataOptions != null)
3578    {
3579      getUserData().setBackendType((ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg>)
3580          ui.getFieldValue(FieldName.BACKEND_TYPE));
3581      getUserData().setNewSuffixOptions(dataOptions);
3582    }
3583
3584    if (!errorMsgs.isEmpty())
3585    {
3586      throw new UserDataException(Step.NEW_SUFFIX_OPTIONS,
3587          getMessageFromCollection(errorMsgs, Constants.LINE_SEPARATOR));
3588    }
3589  }
3590
3591  private NewSuffixOptions checkImportData(final QuickSetup ui, final List<String> baseDn, final boolean validBaseDn,
3592      final List<LocalizableMessage> errorMsgs)
3593  {
3594    if (baseDn.isEmpty())
3595    {
3596      return NewSuffixOptions.createEmpty(baseDn);
3597    }
3598
3599    final NewSuffixOptions.Type type = (NewSuffixOptions.Type) ui.getFieldValue(FieldName.DATA_OPTIONS);
3600    switch (type)
3601    {
3602    case IMPORT_FROM_LDIF_FILE:
3603      return checkImportLDIFFile(ui, baseDn, validBaseDn, errorMsgs);
3604
3605    case IMPORT_AUTOMATICALLY_GENERATED_DATA:
3606      return checkImportGeneratedData(ui, baseDn, validBaseDn, errorMsgs);
3607
3608    default:
3609      if (validBaseDn)
3610      {
3611        return type == NewSuffixOptions.Type.CREATE_BASE_ENTRY ? NewSuffixOptions.createBaseEntry(baseDn)
3612            : NewSuffixOptions.createEmpty(baseDn);
3613      }
3614    }
3615
3616    return null;
3617  }
3618
3619  private NewSuffixOptions checkImportGeneratedData(final QuickSetup ui, final List<String> baseDn,
3620      final boolean validBaseDn, final List<LocalizableMessage> errorMsgs)
3621  {
3622    boolean fieldIsValid = true;
3623    final List<LocalizableMessage> localErrorMsgs = new LinkedList<>();
3624    final String nEntries = ui.getFieldStringValue(FieldName.NUMBER_ENTRIES);
3625    if (nEntries == null || "".equals(nEntries.trim()))
3626    {
3627      localErrorMsgs.add(INFO_NO_NUMBER_ENTRIES.get());
3628      fieldIsValid = false;
3629    }
3630    else
3631    {
3632      boolean nEntriesValid = false;
3633      try
3634      {
3635        int n = Integer.parseInt(nEntries);
3636        nEntriesValid = n >= MIN_NUMBER_ENTRIES && n <= MAX_NUMBER_ENTRIES;
3637      }
3638      catch (NumberFormatException nfe)
3639      {
3640        /* do nothing */
3641      }
3642
3643      if (!nEntriesValid)
3644      {
3645        localErrorMsgs.add(INFO_INVALID_NUMBER_ENTRIES_RANGE.get(MIN_NUMBER_ENTRIES, MAX_NUMBER_ENTRIES));
3646        fieldIsValid = false;
3647      }
3648    }
3649
3650    ui.displayFieldInvalid(FieldName.NUMBER_ENTRIES, !fieldIsValid);
3651    if (validBaseDn && localErrorMsgs.isEmpty())
3652    {
3653      return NewSuffixOptions.createAutomaticallyGenerated(baseDn, Integer.parseInt(nEntries));
3654    }
3655    errorMsgs.addAll(localErrorMsgs);
3656
3657    return null;
3658  }
3659
3660  private NewSuffixOptions checkImportLDIFFile(final QuickSetup ui, final List<String> baseDn,
3661      final boolean validBaseDn, final List<LocalizableMessage> errorMsgs)
3662  {
3663    final boolean fieldIsValid = false;
3664    final String ldifPath = ui.getFieldStringValue(FieldName.LDIF_PATH);
3665    if (ldifPath == null || ldifPath.trim().isEmpty())
3666    {
3667      errorMsgs.add(INFO_NO_LDIF_PATH.get());
3668    }
3669    else if (!fileExists(ldifPath))
3670    {
3671      errorMsgs.add(INFO_LDIF_FILE_DOES_NOT_EXIST.get());
3672    }
3673    else if (validBaseDn)
3674    {
3675      return NewSuffixOptions.createImportFromLDIF(baseDn, Collections.singletonList(ldifPath), null, null);
3676    }
3677    ui.displayFieldInvalid(FieldName.LDIF_PATH, !fieldIsValid);
3678
3679    return null;
3680  }
3681
3682  private boolean checkProvidedBaseDn(final QuickSetup ui, final List<String> baseDn,
3683      final List<LocalizableMessage> errorMsgs)
3684  {
3685    boolean validBaseDn = true;
3686    String dn = ui.getFieldStringValue(FieldName.DIRECTORY_BASE_DN);
3687    if (dn == null || dn.trim().length() == 0)
3688    {
3689      // Do nothing, the user does not want to provide a base DN.
3690      dn = "";
3691    }
3692    else if (!isDN(dn))
3693    {
3694      validBaseDn = false;
3695      errorMsgs.add(INFO_NOT_A_BASE_DN.get());
3696    }
3697    else if (isConfigurationDn(dn))
3698    {
3699      validBaseDn = false;
3700      errorMsgs.add(INFO_BASE_DN_IS_CONFIGURATION_DN.get());
3701    }
3702    else
3703    {
3704      baseDn.add(dn);
3705    }
3706    ui.displayFieldInvalid(FieldName.DIRECTORY_BASE_DN, !validBaseDn);
3707
3708    return validBaseDn;
3709  }
3710
3711  /**
3712   * Update the userData object according to the content of the runtime options
3713   * panel.
3714   */
3715  private void updateUserDataForRuntimeOptionsPanel(QuickSetup qs)
3716  {
3717    getUserData().setJavaArguments(UserData.SERVER_SCRIPT_NAME,
3718        (JavaArguments) qs.getFieldValue(FieldName.SERVER_JAVA_ARGUMENTS));
3719    getUserData().setJavaArguments(UserData.IMPORT_SCRIPT_NAME,
3720        (JavaArguments) qs.getFieldValue(FieldName.IMPORT_JAVA_ARGUMENTS));
3721  }
3722
3723  /** Update the userData object according to the content of the review panel. */
3724  private void updateUserDataForReviewPanel(QuickSetup qs)
3725  {
3726    Boolean b = (Boolean) qs.getFieldValue(FieldName.SERVER_START_INSTALLER);
3727    getUserData().setStartServer(b);
3728    b = (Boolean) qs.getFieldValue(FieldName.ENABLE_WINDOWS_SERVICE);
3729    getUserData().setEnableWindowsService(b);
3730  }
3731
3732  /**
3733   * Returns the number of free disk space in bytes required to install Open DS
3734   * For the moment we just return 20 Megabytes. TODO we might want to have
3735   * something dynamic to calculate the required free disk space for the
3736   * installation.
3737   *
3738   * @return the number of free disk space required to install Open DS.
3739   */
3740  private long getRequiredInstallSpace()
3741  {
3742    return 20 * 1024 * 1024;
3743  }
3744
3745  /** Update the UserInstallData with the contents we discover in the ADS. */
3746  private Set<TopologyCacheException> updateUserDataWithSuffixesInADS(ADSContext adsContext,
3747      ApplicationTrustManager trustManager) throws TopologyCacheException
3748  {
3749    Set<TopologyCacheException> exceptions = new HashSet<>();
3750    SuffixesToReplicateOptions suf = getUserData().getSuffixesToReplicateOptions();
3751    SuffixesToReplicateOptions.Type type;
3752
3753    if (suf == null || suf.getType() == SuffixesToReplicateOptions.Type.NO_SUFFIX_TO_REPLICATE)
3754    {
3755      type = SuffixesToReplicateOptions.Type.NO_SUFFIX_TO_REPLICATE;
3756    }
3757    else
3758    {
3759      type = SuffixesToReplicateOptions.Type.NEW_SUFFIX_IN_TOPOLOGY;
3760    }
3761    lastLoadedCache = new TopologyCache(adsContext, trustManager, getConnectTimeout());
3762    LinkedHashSet<PreferredConnection> cnx = new LinkedHashSet<>();
3763    cnx.add(PreferredConnection.getPreferredConnection(adsContext.getDirContext()));
3764    // We cannot use getPreferredConnections since the user data has not been
3765    // updated yet.
3766    lastLoadedCache.setPreferredConnections(cnx);
3767    lastLoadedCache.reloadTopology();
3768    Set<SuffixDescriptor> suffixes = lastLoadedCache.getSuffixes();
3769    Set<SuffixDescriptor> moreSuffixes = null;
3770    if (suf != null)
3771    {
3772      moreSuffixes = suf.getSuffixes();
3773    }
3774    getUserData().setSuffixesToReplicateOptions(new SuffixesToReplicateOptions(type, suffixes, moreSuffixes));
3775
3776    /*
3777     * Analyze if we had any exception while loading servers. For the moment
3778     * only throw the exception found if the user did not provide the
3779     * Administrator DN and this caused a problem authenticating in one server
3780     * or if there is a certificate problem.
3781     */
3782    Set<ServerDescriptor> servers = lastLoadedCache.getServers();
3783    for (ServerDescriptor server : servers)
3784    {
3785      TopologyCacheException e = server.getLastException();
3786      if (e != null)
3787      {
3788        exceptions.add(e);
3789      }
3790    }
3791    return exceptions;
3792  }
3793
3794  /**
3795   * Update the UserInstallData object with the contents of the server to which
3796   * we are connected with the provided InitialLdapContext.
3797   */
3798  private void updateUserDataWithSuffixesInServer(InitialLdapContext ctx) throws NamingException
3799  {
3800    SuffixesToReplicateOptions suf = getUserData().getSuffixesToReplicateOptions();
3801    SuffixesToReplicateOptions.Type type;
3802    Set<SuffixDescriptor> suffixes = new HashSet<>();
3803    if (suf != null)
3804    {
3805      type = suf.getType();
3806    }
3807    else
3808    {
3809      type = SuffixesToReplicateOptions.Type.NEW_SUFFIX_IN_TOPOLOGY;
3810    }
3811
3812    ServerDescriptor s = createStandalone(ctx, new TopologyCacheFilter());
3813    Set<ReplicaDescriptor> replicas = s.getReplicas();
3814    for (ReplicaDescriptor replica : replicas)
3815    {
3816      suffixes.add(replica.getSuffix());
3817    }
3818    Set<SuffixDescriptor> moreSuffixes = null;
3819    if (suf != null)
3820    {
3821      moreSuffixes = suf.getSuffixes();
3822    }
3823    getUserData().setSuffixesToReplicateOptions(new SuffixesToReplicateOptions(type, suffixes, moreSuffixes));
3824  }
3825
3826  /**
3827   * Returns the keystore path to be used for generating a self-signed
3828   * certificate.
3829   *
3830   * @return the keystore path to be used for generating a self-signed
3831   *         certificate.
3832   */
3833  protected String getSelfSignedKeystorePath()
3834  {
3835    return getPath2("keystore");
3836  }
3837
3838  /**
3839   * Returns the trustmanager path to be used for generating a self-signed
3840   * certificate.
3841   *
3842   * @return the trustmanager path to be used for generating a self-signed
3843   *         certificate.
3844   */
3845  private String getTrustManagerPath()
3846  {
3847    return getPath2("truststore");
3848  }
3849
3850  /**
3851   * Returns the path of the self-signed that we export to be able to create a
3852   * truststore.
3853   *
3854   * @return the path of the self-signed that is exported.
3855   */
3856  private String getTemporaryCertificatePath()
3857  {
3858    return getPath2("server-cert.txt");
3859  }
3860
3861  /**
3862   * Returns the path to be used to store the password of the keystore.
3863   *
3864   * @return the path to be used to store the password of the keystore.
3865   */
3866  private String getKeystorePinPath()
3867  {
3868    return getPath2("keystore.pin");
3869  }
3870
3871  private String getPath2(String relativePath)
3872  {
3873    String parentFile = getPath(getInstancePath(), Installation.CONFIG_PATH_RELATIVE);
3874    return getPath(parentFile, relativePath);
3875  }
3876
3877  /**
3878   * Returns the validity period to be used to generate the self-signed
3879   * certificate.
3880   *
3881   * @return the validity period to be used to generate the self-signed
3882   *         certificate.
3883   */
3884  private int getSelfSignedCertificateValidity()
3885  {
3886    return 20 * 365;
3887  }
3888
3889  /**
3890   * Returns the Subject DN to be used to generate the self-signed certificate.
3891   *
3892   * @return the Subject DN to be used to generate the self-signed certificate.
3893   */
3894  private String getSelfSignedCertificateSubjectDN()
3895  {
3896    return "cn=" + Rdn.escapeValue(getUserData().getHostName()) + ",O=OpenDJ Self-Signed Certificate";
3897  }
3898
3899  /**
3900   * Returns the self-signed certificate password used for this session. This
3901   * method calls <code>createSelfSignedCertificatePwd()</code> the first time
3902   * this method is called.
3903   *
3904   * @return the self-signed certificate password used for this session.
3905   */
3906  protected String getSelfSignedCertificatePwd()
3907  {
3908    if (selfSignedCertPw == null)
3909    {
3910      selfSignedCertPw = SetupUtils.createSelfSignedCertificatePwd();
3911    }
3912    return new String(selfSignedCertPw);
3913  }
3914
3915  private Map<ServerDescriptor, AuthenticationData> getRemoteWithNoReplicationPort(UserData userData)
3916  {
3917    Map<ServerDescriptor, AuthenticationData> servers = new HashMap<>();
3918    Set<SuffixDescriptor> suffixes = userData.getSuffixesToReplicateOptions().getSuffixes();
3919    for (SuffixDescriptor suffix : suffixes)
3920    {
3921      for (ReplicaDescriptor replica : suffix.getReplicas())
3922      {
3923        ServerDescriptor server = replica.getServer();
3924        Object v = server.getServerProperties().get(IS_REPLICATION_SERVER);
3925        if (!Boolean.TRUE.equals(v))
3926        {
3927          AuthenticationData authData = new AuthenticationData();
3928          authData.setPort(Constants.DEFAULT_REPLICATION_PORT);
3929          authData.setUseSecureConnection(false);
3930          servers.put(server, authData);
3931        }
3932      }
3933    }
3934    return servers;
3935  }
3936
3937  private InitialLdapContext createLocalContext() throws NamingException
3938  {
3939    String ldapUrl =
3940        "ldaps://" + getHostNameForLdapUrl(getUserData().getHostName()) + ":" + getUserData().getAdminConnectorPort();
3941    String dn = getUserData().getDirectoryManagerDn();
3942    String pwd = getUserData().getDirectoryManagerPwd();
3943    return createLdapsContext(ldapUrl, dn, pwd, getConnectTimeout(), null, null, null);
3944  }
3945
3946  /**
3947   * Gets an InitialLdapContext based on the information that appears on the
3948   * provided ServerDescriptor.
3949   *
3950   * @param server
3951   *          the object describing the server.
3952   * @param trustManager
3953   *          the trust manager to be used to establish the connection.
3954   * @param cnx
3955   *          the list of preferred LDAP URLs to be used to connect to the
3956   *          server.
3957   * @return the InitialLdapContext to the remote server.
3958   * @throws ApplicationException
3959   *           if something goes wrong.
3960   */
3961  private InitialLdapContext getRemoteConnection(ServerDescriptor server, ApplicationTrustManager trustManager,
3962      Set<PreferredConnection> cnx) throws ApplicationException
3963  {
3964    Map<ADSContext.ServerProperty, Object> adsProperties;
3965    AuthenticationData auth = getUserData().getReplicationOptions().getAuthenticationData();
3966    if (!server.isRegistered())
3967    {
3968      /*
3969       * Create adsProperties to be able to use the class ServerLoader to get
3970       * the connection. Just update the connection parameters with what the
3971       * user chose in the Topology Options panel (i.e. even if SSL is enabled
3972       * on the remote server, use standard LDAP to connect to the server if the
3973       * user specified the LDAP port: this avoids having an issue with the
3974       * certificate if it has not been accepted previously by the user).
3975       */
3976      adsProperties = new HashMap<>();
3977      adsProperties.put(ADSContext.ServerProperty.HOST_NAME, server.getHostName());
3978      if (auth.useSecureConnection())
3979      {
3980        adsProperties.put(ADSContext.ServerProperty.LDAPS_PORT, String.valueOf(auth.getPort()));
3981        adsProperties.put(ADSContext.ServerProperty.LDAPS_ENABLED, "true");
3982      }
3983      else
3984      {
3985        adsProperties.put(ADSContext.ServerProperty.LDAP_PORT, String.valueOf(auth.getPort()));
3986        adsProperties.put(ADSContext.ServerProperty.LDAP_ENABLED, "true");
3987      }
3988      server.setAdsProperties(adsProperties);
3989    }
3990    return getRemoteConnection(server, auth.getDn(), auth.getPwd(), trustManager, getConnectTimeout(), cnx);
3991  }
3992
3993  /**
3994   * Initializes a suffix with the contents of a replica that has a given
3995   * replication id.
3996   *
3997   * @param ctx
3998   *          the connection to the server whose suffix we want to initialize.
3999   * @param replicaId
4000   *          the replication ID of the replica we want to use to initialize the
4001   *          contents of the suffix.
4002   * @param suffixDn
4003   *          the dn of the suffix.
4004   * @param displayProgress
4005   *          whether we want to display progress or not.
4006   * @param sourceServerDisplay
4007   *          the string to be used to represent the server that contains the
4008   *          data that will be used to initialize the suffix.
4009   * @throws ApplicationException
4010   *           if an unexpected error occurs.
4011   * @throws PeerNotFoundException
4012   *           if the replication mechanism cannot find a peer.
4013   */
4014  public void initializeSuffix(InitialLdapContext ctx, int replicaId, String suffixDn, boolean displayProgress,
4015      String sourceServerDisplay) throws ApplicationException, PeerNotFoundException
4016  {
4017    boolean taskCreated = false;
4018    int i = 1;
4019    boolean isOver = false;
4020    String dn = null;
4021    BasicAttributes attrs = new BasicAttributes();
4022    Attribute oc = new BasicAttribute("objectclass");
4023    oc.add("top");
4024    oc.add("ds-task");
4025    oc.add("ds-task-initialize-from-remote-replica");
4026    attrs.put(oc);
4027    attrs.put("ds-task-class-name", "org.opends.server.tasks.InitializeTask");
4028    attrs.put("ds-task-initialize-domain-dn", suffixDn);
4029    attrs.put("ds-task-initialize-replica-server-id", String.valueOf(replicaId));
4030    while (!taskCreated)
4031    {
4032      checkAbort();
4033      String id = "quicksetup-initialize" + i;
4034      dn = "ds-task-id=" + id + ",cn=Scheduled Tasks,cn=Tasks";
4035      attrs.put("ds-task-id", id);
4036      try
4037      {
4038        DirContext dirCtx = ctx.createSubcontext(dn, attrs);
4039        taskCreated = true;
4040        logger.info(LocalizableMessage.raw("created task entry: " + attrs));
4041        dirCtx.close();
4042      }
4043      catch (NameAlreadyBoundException x)
4044      {
4045        logger.warn(LocalizableMessage.raw("A task with dn: " + dn + " already existed."));
4046      }
4047      catch (NamingException ne)
4048      {
4049        logger.error(LocalizableMessage.raw("Error creating task " + attrs, ne));
4050        throw new ApplicationException(ReturnCode.APPLICATION_ERROR, getThrowableMsg(
4051            INFO_ERROR_LAUNCHING_INITIALIZATION.get(sourceServerDisplay), ne), ne);
4052      }
4053      i++;
4054    }
4055    // Wait until it is over
4056    SearchControls searchControls = new SearchControls();
4057    searchControls.setSearchScope(SearchControls.OBJECT_SCOPE);
4058    String filter = "objectclass=*";
4059    searchControls.setReturningAttributes(new String[] { "ds-task-unprocessed-entry-count",
4060      "ds-task-processed-entry-count", "ds-task-log-message", "ds-task-state" });
4061    LocalizableMessage lastDisplayedMsg = null;
4062    String lastLogMsg = null;
4063    long lastTimeMsgDisplayed = -1;
4064    long lastTimeMsgLogged = -1;
4065    long totalEntries = 0;
4066    while (!isOver)
4067    {
4068      if (canceled)
4069      {
4070        // TODO: we should try to cleanly abort the initialize.  As we have
4071        // aborted the install, the server will be stopped and the remote
4072        // server will receive a connect error.
4073        checkAbort();
4074      }
4075      StaticUtils.sleep(500);
4076      if (canceled)
4077      {
4078        // TODO: we should try to cleanly abort the initialize.  As we have
4079        // aborted the install, the server will be stopped and the remote
4080        // server will receive a connect error.
4081        checkAbort();
4082      }
4083      try
4084      {
4085        NamingEnumeration<SearchResult> res = ctx.search(dn, filter, searchControls);
4086        SearchResult sr = null;
4087        try
4088        {
4089          while (res.hasMore())
4090          {
4091            sr = res.next();
4092          }
4093        }
4094        finally
4095        {
4096          res.close();
4097        }
4098        // Get the number of entries that have been handled and
4099        // a percentage...
4100        LocalizableMessage msg;
4101        String sProcessed = getFirstValue(sr, "ds-task-processed-entry-count");
4102        String sUnprocessed = getFirstValue(sr, "ds-task-unprocessed-entry-count");
4103        long processed = -1;
4104        long unprocessed = -1;
4105        if (sProcessed != null)
4106        {
4107          processed = Integer.parseInt(sProcessed);
4108        }
4109        if (sUnprocessed != null)
4110        {
4111          unprocessed = Integer.parseInt(sUnprocessed);
4112        }
4113        totalEntries = Math.max(totalEntries, processed + unprocessed);
4114
4115        if (processed != -1 && unprocessed != -1)
4116        {
4117          if (processed + unprocessed > 0)
4118          {
4119            long perc = (100 * processed) / (processed + unprocessed);
4120            msg = INFO_INITIALIZE_PROGRESS_WITH_PERCENTAGE.get(sProcessed, perc);
4121          }
4122          else
4123          {
4124            //msg = INFO_NO_ENTRIES_TO_INITIALIZE.get();
4125            msg = null;
4126          }
4127        }
4128        else if (processed != -1)
4129        {
4130          msg = INFO_INITIALIZE_PROGRESS_WITH_PROCESSED.get(sProcessed);
4131        }
4132        else if (unprocessed != -1)
4133        {
4134          msg = INFO_INITIALIZE_PROGRESS_WITH_UNPROCESSED.get(sUnprocessed);
4135        }
4136        else
4137        {
4138          msg = lastDisplayedMsg;
4139        }
4140
4141        if (msg != null)
4142        {
4143          long currentTime = System.currentTimeMillis();
4144          /* Refresh period: to avoid having too many lines in the log */
4145          long minRefreshPeriod;
4146          if (totalEntries < 100)
4147          {
4148            minRefreshPeriod = 0;
4149          }
4150          else if (totalEntries < 1000)
4151          {
4152            minRefreshPeriod = 1000;
4153          }
4154          else if (totalEntries < 10000)
4155          {
4156            minRefreshPeriod = 5000;
4157          }
4158          else
4159          {
4160            minRefreshPeriod = 10000;
4161          }
4162          if (currentTime - minRefreshPeriod > lastTimeMsgLogged)
4163          {
4164            lastTimeMsgLogged = currentTime;
4165            logger.info(LocalizableMessage.raw("Progress msg: " + msg));
4166          }
4167          if (displayProgress && currentTime - minRefreshPeriod > lastTimeMsgDisplayed && !msg.equals(lastDisplayedMsg))
4168          {
4169            notifyListeners(getFormattedProgress(msg));
4170            lastDisplayedMsg = msg;
4171            notifyListeners(getLineBreak());
4172            lastTimeMsgDisplayed = currentTime;
4173          }
4174        }
4175
4176        String logMsg = getFirstValue(sr, "ds-task-log-message");
4177        if (logMsg != null && !logMsg.equals(lastLogMsg))
4178        {
4179          logger.info(LocalizableMessage.raw(logMsg));
4180          lastLogMsg = logMsg;
4181        }
4182        InstallerHelper helper = new InstallerHelper();
4183        String state = getFirstValue(sr, "ds-task-state");
4184
4185        if (helper.isDone(state) || helper.isStoppedByError(state))
4186        {
4187          isOver = true;
4188          LocalizableMessage errorMsg;
4189          logger.info(LocalizableMessage.raw("Last task entry: " + sr));
4190          if (displayProgress && msg != null && !msg.equals(lastDisplayedMsg))
4191          {
4192            notifyListeners(getFormattedProgress(msg));
4193            lastDisplayedMsg = msg;
4194            notifyListeners(getLineBreak());
4195          }
4196
4197          if (lastLogMsg != null)
4198          {
4199            errorMsg =
4200                INFO_ERROR_DURING_INITIALIZATION_LOG.get(sourceServerDisplay, lastLogMsg, state, sourceServerDisplay);
4201          }
4202          else
4203          {
4204            errorMsg = INFO_ERROR_DURING_INITIALIZATION_NO_LOG.get(sourceServerDisplay, state, sourceServerDisplay);
4205          }
4206
4207          logger.warn(LocalizableMessage.raw("Processed errorMsg: " + errorMsg));
4208          if (helper.isCompletedWithErrors(state))
4209          {
4210            if (displayProgress)
4211            {
4212              notifyListeners(getFormattedWarning(errorMsg));
4213            }
4214          }
4215          else if (!helper.isSuccessful(state) || helper.isStoppedByError(state))
4216          {
4217            ApplicationException ae = new ApplicationException(ReturnCode.APPLICATION_ERROR, errorMsg, null);
4218            if (lastLogMsg == null || helper.isPeersNotFoundError(lastLogMsg))
4219            {
4220              logger.warn(LocalizableMessage.raw("Throwing peer not found error.  " + "Last Log Msg: " + lastLogMsg));
4221              // Assume that this is a peer not found error.
4222              throw new PeerNotFoundException(errorMsg);
4223            }
4224            else
4225            {
4226              logger.error(LocalizableMessage.raw("Throwing ApplicationException."));
4227              throw ae;
4228            }
4229          }
4230          else if (displayProgress)
4231          {
4232            logger.info(LocalizableMessage.raw("Initialization completed successfully."));
4233            notifyListeners(getFormattedProgress(INFO_SUFFIX_INITIALIZED_SUCCESSFULLY.get()));
4234            notifyListeners(getLineBreak());
4235          }
4236        }
4237      }
4238      catch (NameNotFoundException x)
4239      {
4240        isOver = true;
4241        logger.info(LocalizableMessage.raw("Initialization entry not found."));
4242        if (displayProgress)
4243        {
4244          notifyListeners(getFormattedProgress(INFO_SUFFIX_INITIALIZED_SUCCESSFULLY.get()));
4245          notifyListeners(getLineBreak());
4246        }
4247      }
4248      catch (NamingException ne)
4249      {
4250        throw new ApplicationException(ReturnCode.APPLICATION_ERROR, getThrowableMsg(INFO_ERROR_POOLING_INITIALIZATION
4251            .get(sourceServerDisplay), ne), ne);
4252      }
4253    }
4254    resetGenerationId(ctx, suffixDn, sourceServerDisplay);
4255  }
4256
4257  /**
4258   * Returns the configuration file path to be used when invoking the
4259   * command-lines.
4260   *
4261   * @return the configuration file path to be used when invoking the
4262   *         command-lines.
4263   */
4264  private String getConfigurationFile()
4265  {
4266    return getPath(getInstallation().getCurrentConfigurationFile());
4267  }
4268
4269  /**
4270   * Returns the configuration class name to be used when invoking the
4271   * command-lines.
4272   *
4273   * @return the configuration class name to be used when invoking the
4274   *         command-lines.
4275   */
4276  private String getConfigurationClassName()
4277  {
4278    return DEFAULT_CONFIG_CLASS_NAME;
4279  }
4280
4281  private String getLocalReplicationServer()
4282  {
4283    return getUserData().getHostName() + ":" + getUserData().getReplicationOptions().getReplicationPort();
4284  }
4285
4286  private String getLocalHostPort()
4287  {
4288    return getUserData().getHostName() + ":" + getUserData().getServerPort();
4289  }
4290
4291  private void resetGenerationId(InitialLdapContext ctx, String suffixDn, String sourceServerDisplay)
4292      throws ApplicationException
4293  {
4294    boolean taskCreated = false;
4295    int i = 1;
4296    boolean isOver = false;
4297    String dn = null;
4298    BasicAttributes attrs = new BasicAttributes();
4299    Attribute oc = new BasicAttribute("objectclass");
4300    oc.add("top");
4301    oc.add("ds-task");
4302    oc.add("ds-task-reset-generation-id");
4303    attrs.put(oc);
4304    attrs.put("ds-task-class-name", "org.opends.server.tasks.SetGenerationIdTask");
4305    attrs.put("ds-task-reset-generation-id-domain-base-dn", suffixDn);
4306    while (!taskCreated)
4307    {
4308      checkAbort();
4309      String id = "quicksetup-reset-generation-id-" + i;
4310      dn = "ds-task-id=" + id + ",cn=Scheduled Tasks,cn=Tasks";
4311      attrs.put("ds-task-id", id);
4312      try
4313      {
4314        DirContext dirCtx = ctx.createSubcontext(dn, attrs);
4315        taskCreated = true;
4316        logger.info(LocalizableMessage.raw("created task entry: " + attrs));
4317        dirCtx.close();
4318      }
4319      catch (NameAlreadyBoundException x)
4320      {
4321      }
4322      catch (NamingException ne)
4323      {
4324        logger.error(LocalizableMessage.raw("Error creating task " + attrs, ne));
4325        throw new ApplicationException(ReturnCode.APPLICATION_ERROR, getThrowableMsg(
4326            INFO_ERROR_LAUNCHING_INITIALIZATION.get(sourceServerDisplay), ne), ne);
4327      }
4328      i++;
4329    }
4330    // Wait until it is over
4331    SearchControls searchControls = new SearchControls();
4332    searchControls.setSearchScope(SearchControls.OBJECT_SCOPE);
4333    String filter = "objectclass=*";
4334    searchControls.setReturningAttributes(new String[] { "ds-task-log-message", "ds-task-state" });
4335    String lastLogMsg = null;
4336    while (!isOver)
4337    {
4338      StaticUtils.sleep(500);
4339      try
4340      {
4341        NamingEnumeration<SearchResult> res = ctx.search(dn, filter, searchControls);
4342        SearchResult sr = null;
4343        try
4344        {
4345          while (res.hasMore())
4346          {
4347            sr = res.next();
4348          }
4349        }
4350        finally
4351        {
4352          res.close();
4353        }
4354        String logMsg = getFirstValue(sr, "ds-task-log-message");
4355        if (logMsg != null && !logMsg.equals(lastLogMsg))
4356        {
4357          logger.info(LocalizableMessage.raw(logMsg));
4358          lastLogMsg = logMsg;
4359        }
4360        InstallerHelper helper = new InstallerHelper();
4361        String state = getFirstValue(sr, "ds-task-state");
4362
4363        if (helper.isDone(state) || helper.isStoppedByError(state))
4364        {
4365          isOver = true;
4366          LocalizableMessage errorMsg = lastLogMsg != null ?
4367              INFO_ERROR_DURING_INITIALIZATION_LOG.get(sourceServerDisplay, lastLogMsg, state, sourceServerDisplay)
4368            : INFO_ERROR_DURING_INITIALIZATION_NO_LOG.get(sourceServerDisplay, state, sourceServerDisplay);
4369
4370          if (helper.isCompletedWithErrors(state))
4371          {
4372            logger.warn(LocalizableMessage.raw("Completed with error: " + errorMsg));
4373            notifyListeners(getFormattedWarning(errorMsg));
4374          }
4375          else if (!helper.isSuccessful(state) || helper.isStoppedByError(state))
4376          {
4377            logger.warn(LocalizableMessage.raw("Error: " + errorMsg));
4378            throw new ApplicationException(ReturnCode.APPLICATION_ERROR, errorMsg, null);
4379          }
4380        }
4381      }
4382      catch (NameNotFoundException x)
4383      {
4384        isOver = true;
4385      }
4386      catch (NamingException ne)
4387      {
4388        throw new ApplicationException(ReturnCode.APPLICATION_ERROR,
4389            getThrowableMsg(INFO_ERROR_POOLING_INITIALIZATION.get(sourceServerDisplay), ne), ne);
4390      }
4391    }
4392  }
4393
4394  /**
4395   * Invokes a long operation in a separate thread and checks whether the user
4396   * canceled the operation or not.
4397   *
4398   * @param thread
4399   *          the Thread that must be launched.
4400   * @throws ApplicationException
4401   *           if there was an error executing the task or if the user canceled
4402   *           the installer.
4403   */
4404  private void invokeLongOperation(InvokeThread thread) throws ApplicationException
4405  {
4406    try
4407    {
4408      thread.start();
4409      while (!thread.isOver() && thread.isAlive())
4410      {
4411        if (canceled)
4412        {
4413          // Try to abort the thread
4414          try
4415          {
4416            thread.abort();
4417          }
4418          catch (Throwable t)
4419          {
4420            logger.warn(LocalizableMessage.raw("Error cancelling thread: " + t, t));
4421          }
4422        }
4423        else if (thread.getException() != null)
4424        {
4425          throw thread.getException();
4426        }
4427        else
4428        {
4429          StaticUtils.sleep(100);
4430        }
4431      }
4432      if (thread.getException() != null)
4433      {
4434        throw thread.getException();
4435      }
4436      if (canceled)
4437      {
4438        checkAbort();
4439      }
4440    }
4441    catch (ApplicationException e)
4442    {
4443      logger.error(LocalizableMessage.raw("Error: " + e, e));
4444      throw e;
4445    }
4446    catch (Throwable t)
4447    {
4448      logger.error(LocalizableMessage.raw("Error: " + t, t));
4449      throw new ApplicationException(ReturnCode.BUG, getThrowableMsg(INFO_BUG_MSG.get(), t), t);
4450    }
4451  }
4452
4453  /**
4454   * Returns the host port representation of the server to be used in progress
4455   * and error messages. It takes into account the fact the host and port
4456   * provided by the user in the replication options panel. NOTE: the code
4457   * assumes that the user data with the contents of the replication options has
4458   * already been updated.
4459   *
4460   * @param server
4461   *          the ServerDescriptor.
4462   * @return the host port string representation of the provided server.
4463   */
4464  protected String getHostPort(ServerDescriptor server)
4465  {
4466    String hostPort = null;
4467
4468    for (PreferredConnection connection : getPreferredConnections())
4469    {
4470      String url = connection.getLDAPURL();
4471      if (url.equals(server.getLDAPURL()))
4472      {
4473        hostPort = server.getHostPort(false);
4474      }
4475      else if (url.equals(server.getLDAPsURL()))
4476      {
4477        hostPort = server.getHostPort(true);
4478      }
4479    }
4480    if (hostPort == null)
4481    {
4482      hostPort = server.getHostPort(true);
4483    }
4484    return hostPort;
4485  }
4486
4487  @Override
4488  protected void applicationPrintStreamReceived(String message)
4489  {
4490    InstallerHelper helper = new InstallerHelper();
4491    String parsedMessage = helper.getImportProgressMessage(message);
4492    if (parsedMessage != null)
4493    {
4494      lastImportProgress = parsedMessage;
4495    }
4496  }
4497
4498  /**
4499   * Returns the timeout to be used to connect in milliseconds.
4500   *
4501   * @return the timeout to be used to connect in milliseconds. Returns
4502   *         {@code 0} if there is no timeout.
4503   */
4504  protected int getConnectTimeout()
4505  {
4506    return getUserData().getConnectTimeout();
4507  }
4508
4509  /**
4510   * Copies the template instance files into the instance directory.
4511   *
4512   * @throws ApplicationException
4513   *           If an IO error occurred.
4514   */
4515  private void copyTemplateInstance() throws ApplicationException
4516  {
4517    FileManager fileManager = new FileManager();
4518    fileManager.synchronize(getInstallation().getTemplateDirectory(), getInstallation().getInstanceDirectory());
4519  }
4520}
4521
4522/** Class used to be able to cancel long operations. */
4523abstract class InvokeThread extends Thread implements Runnable
4524{
4525  protected boolean isOver;
4526  protected ApplicationException ae;
4527
4528  /**
4529   * Returns <CODE>true</CODE> if the thread is over and <CODE>false</CODE>
4530   * otherwise.
4531   *
4532   * @return <CODE>true</CODE> if the thread is over and <CODE>false</CODE>
4533   *         otherwise.
4534   */
4535  public boolean isOver()
4536  {
4537    return isOver;
4538  }
4539
4540  /**
4541   * Returns the exception that was encountered running the thread.
4542   *
4543   * @return the exception that was encountered running the thread.
4544   */
4545  public ApplicationException getException()
4546  {
4547    return ae;
4548  }
4549
4550  /** Runnable implementation. */
4551  @Override
4552  public abstract void run();
4553
4554  /** Abort this thread. */
4555  public abstract void abort();
4556}