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