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