001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2006-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.guitools.uninstaller;
028
029import java.awt.event.WindowEvent;
030import java.io.File;
031import java.io.FileFilter;
032import java.io.PrintStream;
033import java.net.InetAddress;
034import java.net.URI;
035import java.security.cert.X509Certificate;
036import java.util.*;
037
038import javax.naming.Context;
039import javax.naming.NamingException;
040import javax.naming.ldap.InitialLdapContext;
041import javax.swing.JFrame;
042import javax.swing.SwingUtilities;
043
044import org.forgerock.i18n.LocalizableMessage;
045import org.forgerock.i18n.LocalizableMessageBuilder;
046import org.forgerock.i18n.slf4j.LocalizedLogger;
047import org.opends.admin.ads.ADSContext;
048import org.opends.admin.ads.ADSContextException;
049import org.opends.admin.ads.ReplicaDescriptor;
050import org.opends.admin.ads.ServerDescriptor;
051import org.opends.admin.ads.TopologyCache;
052import org.opends.admin.ads.TopologyCacheException;
053import org.opends.admin.ads.util.ApplicationTrustManager;
054import org.opends.admin.ads.util.ConnectionUtils;
055import org.opends.admin.ads.util.PreferredConnection;
056import org.opends.guitools.uninstaller.ui.ConfirmUninstallPanel;
057import org.opends.guitools.uninstaller.ui.LoginDialog;
058import org.opends.quicksetup.*;
059import org.opends.quicksetup.ui.*;
060import org.opends.quicksetup.util.BackgroundTask;
061import org.opends.quicksetup.util.ServerController;
062import org.opends.quicksetup.util.UIKeyStore;
063import org.opends.quicksetup.util.Utils;
064import org.opends.server.admin.AttributeTypePropertyDefinition;
065import org.opends.server.admin.ClassLoaderProvider;
066import org.opends.server.admin.ClassPropertyDefinition;
067import org.opends.server.admin.ManagedObjectNotFoundException;
068import org.opends.server.admin.client.ManagementContext;
069import org.opends.server.admin.client.ldap.JNDIDirContextAdaptor;
070import org.opends.server.admin.client.ldap.LDAPManagementContext;
071import org.opends.server.admin.std.client.ReplicationDomainCfgClient;
072import org.opends.server.admin.std.client.ReplicationServerCfgClient;
073import org.opends.server.admin.std.client.ReplicationSynchronizationProviderCfgClient;
074import org.opends.server.admin.std.client.RootCfgClient;
075import org.opends.server.core.DirectoryServer;
076import org.opends.server.util.DynamicConstants;
077import org.opends.server.util.StaticUtils;
078
079import com.forgerock.opendj.cli.ClientException;
080
081import static com.forgerock.opendj.cli.ArgumentConstants.*;
082import static com.forgerock.opendj.cli.Utils.*;
083import static com.forgerock.opendj.util.OperatingSystem.*;
084
085import static org.forgerock.util.Utils.*;
086import static org.opends.messages.AdminToolMessages.*;
087import static org.opends.messages.QuickSetupMessages.*;
088import static org.opends.quicksetup.Step.*;
089import static org.opends.quicksetup.util.Utils.*;
090import static org.opends.server.tools.ConfigureWindowsService.*;
091
092/**
093 * This class is in charge of performing the uninstallation of Open DS.
094 */
095public class Uninstaller extends GuiApplication implements CliApplication {
096
097  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
098  private ProgressStep status = UninstallProgressStep.NOT_STARTED;
099  private boolean runStarted;
100  private boolean errorOnRemoteOccurred;
101  private boolean errorDeletingOccurred;
102
103  private UninstallerArgumentParser parser;
104
105  private Map<ProgressStep, Integer> hmRatio = new HashMap<>();
106  private Map<ProgressStep, LocalizableMessage> hmSummary = new HashMap<>();
107
108  private ApplicationException ue;
109  private Boolean isWindowsServiceEnabled;
110  private UninstallCliHelper cliHelper = new UninstallCliHelper();
111
112  private LoginDialog loginDialog;
113  private ProgressDialog startProgressDlg;
114  private LocalizableMessageBuilder startProgressDetails = new LocalizableMessageBuilder();
115  private UninstallData conf;
116
117  /** Default constructor. */
118  public Uninstaller()
119  {
120    super();
121
122    /* Do some initialization required to use the administration framework
123     * classes.  Note that this is not done in the uninstaller code because
124     * when the basic configuration of the server is performed (using
125     * ConfigureDS) this initialization is done.
126     */
127    DirectoryServer.bootstrapClient();
128    //  Bootstrap definition classes.
129    try
130    {
131      if (!ClassLoaderProvider.getInstance().isEnabled())
132      {
133        ClassLoaderProvider.getInstance().enable();
134      }
135    }
136    catch (Throwable t)
137    {
138      logger.warn(LocalizableMessage.raw("Error enabling admin framework class loader: "+t,
139          t));
140    }
141
142    // Switch off class name validation in client.
143    ClassPropertyDefinition.setAllowClassValidation(false);
144
145    // Switch off attribute type name validation in client.
146    AttributeTypePropertyDefinition.setCheckSchema(false);
147
148    logger.info(LocalizableMessage.raw("Uninstaller is created."));
149  }
150  /** {@inheritDoc} */
151  @Override
152  public LocalizableMessage getFrameTitle() {
153    LocalizableMessage defaultVal = INFO_FRAME_UNINSTALL_TITLE.get(DynamicConstants.PRODUCT_NAME);
154    return Utils.getCustomizedObject("INFO_FRAME_UNINSTALL_TITLE", defaultVal, LocalizableMessage.class);
155  }
156
157  /** {@inheritDoc} */
158  @Override
159  public UserData createUserData() {
160    UninstallUserData data = new UninstallUserData();
161    data.setTrustManager(super.getTrustManager());
162    return data;
163  }
164
165  /** {@inheritDoc} */
166  @Override
167  public WizardStep getFirstWizardStep() {
168    return Step.CONFIRM_UNINSTALL;
169  }
170
171  /** {@inheritDoc} */
172  @Override
173  public WizardStep getNextWizardStep(WizardStep step) {
174    Step nextStep = null;
175    if (step != null && step.equals(Step.CONFIRM_UNINSTALL)) {
176      nextStep = Step.PROGRESS;
177    }
178    else if (Step.PROGRESS.equals(step))
179    {
180      nextStep = Step.FINISHED;
181    }
182    return nextStep;
183  }
184
185  /** {@inheritDoc} */
186  @Override
187  public WizardStep getPreviousWizardStep(WizardStep step) {
188    Step prevStep = null;
189    if (step != null && step.equals(Step.PROGRESS)) {
190      prevStep = Step.CONFIRM_UNINSTALL;
191    }
192    else if (Step.FINISHED.equals(step))
193    {
194      prevStep = Step.PROGRESS;
195    }
196    return prevStep;
197  }
198
199  /** {@inheritDoc} */
200  @Override
201  public WizardStep getFinishedStep() {
202    return Step.FINISHED;
203  }
204
205  /** {@inheritDoc} */
206  @Override
207  public boolean finishOnLeft()
208  {
209    return false;
210  }
211
212  /** {@inheritDoc} */
213  @Override
214  public boolean canGoBack(WizardStep step) {
215    return false;
216  }
217
218  /** {@inheritDoc} */
219  @Override
220  public boolean canGoForward(WizardStep step) {
221    return false;
222  }
223
224  /** {@inheritDoc} */
225  @Override
226  public boolean canFinish(WizardStep step) {
227    return step == Step.CONFIRM_UNINSTALL;
228  }
229
230  /**
231   * Whether the provided wizard step allow to quit.
232   *
233   * @param step the wizard step
234   * @return true if the provided wizard step allow to quit, false otherwise
235   */
236  public boolean canQuit(WizardStep step) {
237    return step == Step.CONFIRM_UNINSTALL;
238  }
239
240  /** {@inheritDoc} */
241  @Override
242  public void nextClicked(WizardStep cStep, QuickSetup qs) {
243    if (cStep == PROGRESS) {
244      throw new IllegalStateException("Cannot click on next from progress step");
245    } else if (cStep == REVIEW) {
246      throw new IllegalStateException("Cannot click on next from review step");
247    } else if (cStep == FINISHED) {
248      throw new IllegalStateException("Cannot click on next from finished step");
249    }
250  }
251
252  /** {@inheritDoc} */
253  @Override
254  public void closeClicked(WizardStep cStep, QuickSetup qs) {
255    if (cStep == PROGRESS) {
256        if (isFinished()
257            || qs.displayConfirmation(INFO_CONFIRM_CLOSE_UNINSTALL_MSG.get(),
258                INFO_CONFIRM_CLOSE_UNINSTALL_TITLE.get()))
259        {
260          qs.quit();
261        }
262    }
263    else if (cStep == FINISHED)
264    {
265      qs.quit();
266    } else {
267      throw new IllegalStateException(
268          "Close only can be clicked on PROGRESS step");
269    }
270  }
271
272  /**
273   * Update the UserData object according to the content of the review
274   * panel.
275   */
276  private void updateUserUninstallDataForConfirmUninstallPanel(QuickSetup qs)
277          throws UserDataException {
278    UninstallUserData uud = getUninstallUserData();
279    uud.setRemoveLibrariesAndTools(
280            (Boolean) qs.getFieldValue(FieldName.REMOVE_LIBRARIES_AND_TOOLS));
281    uud.setRemoveDatabases(
282            (Boolean) qs.getFieldValue(FieldName.REMOVE_DATABASES));
283    uud.setRemoveConfigurationAndSchema(
284            (Boolean) qs.getFieldValue(
285                    FieldName.REMOVE_CONFIGURATION_AND_SCHEMA));
286    uud.setRemoveBackups(
287            (Boolean) qs.getFieldValue(FieldName.REMOVE_BACKUPS));
288    uud.setRemoveLDIFs(
289            (Boolean) qs.getFieldValue(FieldName.REMOVE_LDIFS));
290    uud.setRemoveLogs(
291            (Boolean) qs.getFieldValue(FieldName.REMOVE_LOGS));
292    // This is updated on the method handleTopologyCache
293    uud.setUpdateRemoteReplication(false);
294
295    Set<String> dbs = new HashSet<>();
296    Set<?> s = (Set<?>) qs.getFieldValue(FieldName.EXTERNAL_DB_DIRECTORIES);
297    for (Object v : s) {
298      dbs.add((String) v);
299    }
300
301    Set<String> logs = new HashSet<>();
302    s = (Set<?>) qs.getFieldValue(FieldName.EXTERNAL_LOG_FILES);
303    for (Object v : s) {
304      logs.add((String) v);
305    }
306
307    uud.setExternalDbsToRemove(dbs);
308    uud.setExternalLogsToRemove(logs);
309
310    if (dbs.isEmpty() &&
311            logs.isEmpty() &&
312            !uud.getRemoveLibrariesAndTools() &&
313            !uud.getRemoveDatabases() &&
314            !uud.getRemoveConfigurationAndSchema() &&
315            !uud.getRemoveBackups() &&
316            !uud.getRemoveLDIFs() &&
317            !uud.getRemoveLogs()) {
318      throw new UserDataException(Step.CONFIRM_UNINSTALL,
319              INFO_NOTHING_SELECTED_TO_UNINSTALL.get());
320    }
321  }
322
323
324  /** {@inheritDoc} */
325  @Override
326  public void quitClicked(WizardStep step, QuickSetup qs) {
327    if (step == Step.PROGRESS) {
328      throw new IllegalStateException(
329              "Cannot click on quit from progress step");
330    }
331    else if (step == Step.FINISHED) {
332      throw new IllegalStateException(
333      "Cannot click on quit from finished step");
334    }
335    qs.quit();
336  }
337
338  /** {@inheritDoc} */
339  @Override
340  public LocalizableMessage getCloseButtonToolTip() {
341    return INFO_CLOSE_BUTTON_UNINSTALL_TOOLTIP.get();
342  }
343
344  /** {@inheritDoc} */
345  @Override
346  public LocalizableMessage getFinishButtonToolTip() {
347    return INFO_FINISH_BUTTON_UNINSTALL_TOOLTIP.get();
348  }
349
350  /** {@inheritDoc} */
351  @Override
352  public LocalizableMessage getFinishButtonLabel() {
353    return INFO_FINISH_BUTTON_UNINSTALL_LABEL.get();
354  }
355
356  /** {@inheritDoc} */
357  @Override
358  public void previousClicked(WizardStep cStep, QuickSetup qs) {
359    if (cStep == PROGRESS) {
360      throw new IllegalStateException(
361              "Cannot click on previous from progress step");
362    }
363    else if (cStep == FINISHED) {
364      throw new IllegalStateException(
365        "Cannot click on previous from finished step");
366    }
367  }
368
369  /** {@inheritDoc} */
370  @Override
371  public void notifyListeners(Integer ratio, LocalizableMessage currentPhaseSummary,
372      final LocalizableMessage newLogDetail)
373  {
374    if (runStarted)
375    {
376      super.notifyListeners(ratio, currentPhaseSummary, newLogDetail);
377    }
378    else
379    {
380      SwingUtilities.invokeLater(new Runnable()
381      {
382        @Override
383        public void run()
384        {
385          if (startProgressDlg != null && newLogDetail != null)
386          {
387            startProgressDetails.append(newLogDetail);
388            startProgressDlg.setDetails(startProgressDetails.toMessage());
389          }
390        }
391      });
392    }
393  }
394
395  /** {@inheritDoc} */
396  @Override
397  public boolean finishClicked(final WizardStep cStep, final QuickSetup qs) {
398    if (cStep == Step.CONFIRM_UNINSTALL) {
399      BackgroundTask<UninstallData> worker =
400        new BackgroundTask<UninstallData>() {
401        @Override
402        public UninstallData processBackgroundTask() throws UserDataException {
403          try {
404            updateUserUninstallDataForConfirmUninstallPanel(qs);
405            return new UninstallData(Installation.getLocal());
406          }
407          catch (UserDataException uude) {
408            throw uude;
409          } catch (Throwable t) {
410            logger.warn(LocalizableMessage.raw("Error processing task: "+t, t));
411            throw new UserDataException(Step.CONFIRM_UNINSTALL,
412                    getThrowableMsg(INFO_BUG_MSG.get(), t));
413          }
414        }
415
416        @Override
417        public void backgroundTaskCompleted(UninstallData returnValue,
418                                            Throwable throwable) {
419          qs.getDialog().workerFinished();
420          if (throwable != null) {
421            if (throwable instanceof UserDataException)
422            {
423              qs.displayError(LocalizableMessage.raw(throwable.getLocalizedMessage()),
424                    INFO_ERROR_TITLE.get());
425            }
426            else
427            {
428              logger.warn(LocalizableMessage.raw("Error processing task: "+throwable,
429                  throwable));
430              qs.displayError(LocalizableMessage.raw(throwable.toString()),
431                      INFO_ERROR_TITLE.get());
432            }
433          } else {
434            conf = returnValue;
435            if (conf.isADS() && conf.isReplicationServer())
436            {
437              if (conf.isServerRunning())
438              {
439                if (qs.displayConfirmation(
440                    INFO_CONFIRM_UNINSTALL_REPLICATION_SERVER_RUNNING_MSG.get(),
441                    INFO_CONFIRM_UNINSTALL_REPLICATION_SERVER_RUNNING_TITLE
442                            .get()))
443                {
444                  askForAuthenticationAndLaunch(qs);
445                }
446                else if (qs.displayConfirmation(
447                        INFO_CONFIRM_UNINSTALL_SERVER_RUNNING_MSG.get(),
448                        INFO_CONFIRM_UNINSTALL_SERVER_RUNNING_TITLE.get()))
449                {
450                  getUserData().setStopServer(true);
451                  qs.launch();
452                  qs.setCurrentStep(getNextWizardStep(Step.CONFIRM_UNINSTALL));
453                } else {
454                  getUserData().setStopServer(false);
455                }
456              }
457              else if (qs.displayConfirmation(
458                  INFO_CONFIRM_UNINSTALL_REPLICATION_SERVER_NOT_RUNNING_MSG.get(),
459                  INFO_CONFIRM_UNINSTALL_REPLICATION_SERVER_NOT_RUNNING_TITLE.get()))
460              {
461                boolean startWorked = startServer(qs.getDialog().getFrame());
462                if (startWorked)
463                {
464                  askForAuthenticationAndLaunch(qs);
465                }
466                else
467                {
468                  getUserData().setStopServer(false);
469                  if (qs.displayConfirmation(
470                      INFO_CONFIRM_UNINSTALL_SERVER_NOT_RUNNING_MSG.get(),
471                      INFO_CONFIRM_UNINSTALL_SERVER_NOT_RUNNING_TITLE.get()))
472                  {
473                    qs.launch();
474                    qs.setCurrentStep(
475                        getNextWizardStep(Step.CONFIRM_UNINSTALL));
476                  }
477                }
478              }
479              else
480              {
481                getUserData().setStopServer(false);
482                if (qs.displayConfirmation(
483                    INFO_CONFIRM_UNINSTALL_SERVER_NOT_RUNNING_MSG.get(),
484                    INFO_CONFIRM_UNINSTALL_SERVER_NOT_RUNNING_TITLE.get()))
485                {
486                  qs.launch();
487                  qs.setCurrentStep(
488                      getNextWizardStep(Step.CONFIRM_UNINSTALL));
489                }
490              }
491            }
492            else if (!conf.isServerRunning())
493            {
494              getUserData().setStopServer(false);
495              if (qs.displayConfirmation(
496                      INFO_CONFIRM_UNINSTALL_SERVER_NOT_RUNNING_MSG.get(),
497                      INFO_CONFIRM_UNINSTALL_SERVER_NOT_RUNNING_TITLE.get()))
498              {
499                qs.launch();
500                qs.setCurrentStep(getNextWizardStep(Step.CONFIRM_UNINSTALL));
501              }
502            }
503            else if (qs.displayConfirmation(
504                    INFO_CONFIRM_UNINSTALL_SERVER_RUNNING_MSG.get(),
505                    INFO_CONFIRM_UNINSTALL_SERVER_RUNNING_TITLE.get())) {
506              getUserData().setStopServer(true);
507              qs.launch();
508              qs.setCurrentStep(getNextWizardStep(Step.CONFIRM_UNINSTALL));
509            } else {
510              getUserData().setStopServer(false);
511            }
512          }
513        }
514      };
515      qs.getDialog().workerStarted();
516      worker.startBackgroundTask();
517    }
518    // Uninstaller is responsible for updating user data and launching
519    return false;
520  }
521
522  /** {@inheritDoc} */
523  @Override
524  public void updateUserData(WizardStep step, QuickSetup qs) {
525    // do nothing;
526  }
527
528  /** {@inheritDoc} */
529  @Override
530  public void setWizardDialogState(QuickSetupDialog dlg,
531                                      UserData userData,
532                                      WizardStep step) {
533    if (step == Step.CONFIRM_UNINSTALL) {
534      dlg.setDefaultButton(ButtonName.FINISH);
535      dlg.setFocusOnButton(ButtonName.FINISH);
536    } else if (step == PROGRESS || step == FINISHED) {
537      dlg.setDefaultButton(ButtonName.CLOSE);
538      dlg.setFocusOnButton(ButtonName.CLOSE);
539      dlg.setButtonEnabled(ButtonName.CLOSE, false);
540    }
541  }
542
543  /** {@inheritDoc} */
544  @Override
545  public UserData createUserData(Launcher launcher) throws UserDataException,
546      ApplicationException, ClientException
547  {
548    parser = (UninstallerArgumentParser) launcher.getArgumentParser();
549    return cliHelper.createUserData(parser, launcher.getArguments());
550  }
551
552  /** {@inheritDoc} */
553  @Override
554  public String getInstallationPath() {
555    return getInstallPathFromClasspath();
556  }
557
558  /** {@inheritDoc} */
559  @Override
560  public String getInstancePath() {
561    return getInstancePathFromInstallPath(getInstallPathFromClasspath());
562  }
563
564  /**
565   * Returns the ApplicationException that might occur during installation or
566   * <CODE>null</CODE> if no exception occurred.
567   *
568   * @return the ApplicationException that might occur during installation or
569   *         <CODE>null</CODE> if no exception occurred.
570   */
571  @Override
572  public ApplicationException getRunError() {
573    return ue;
574  }
575
576  /** {@inheritDoc} */
577  @Override
578  public ReturnCode getReturnCode() {
579    return null;
580  }
581
582  /**
583   * Initialize the different map used in this class.
584   */
585  private void initMaps() {
586    hmSummary.put(UninstallProgressStep.NOT_STARTED,
587            getFormattedSummary(INFO_SUMMARY_UNINSTALL_NOT_STARTED.get()));
588    hmSummary.put(UninstallProgressStep.STOPPING_SERVER,
589            getFormattedSummary(INFO_SUMMARY_STOPPING.get()));
590    hmSummary.put(UninstallProgressStep.UNCONFIGURING_REPLICATION,
591            getFormattedSummary(INFO_SUMMARY_UNCONFIGURING_REPLICATION.get()));
592    hmSummary.put(UninstallProgressStep.DISABLING_WINDOWS_SERVICE,
593            getFormattedSummary(INFO_SUMMARY_DISABLING_WINDOWS_SERVICE.get()));
594    hmSummary.put(UninstallProgressStep.DELETING_EXTERNAL_DATABASE_FILES,
595            getFormattedSummary(INFO_SUMMARY_DELETING_EXTERNAL_DB_FILES.get()));
596    hmSummary.put(UninstallProgressStep.DELETING_EXTERNAL_LOG_FILES,
597            getFormattedSummary(
598                    INFO_SUMMARY_DELETING_EXTERNAL_LOG_FILES.get()));
599    hmSummary.put(UninstallProgressStep.REMOVING_EXTERNAL_REFERENCES,
600            getFormattedSummary(
601                    INFO_SUMMARY_DELETING_EXTERNAL_REFERENCES.get()));
602    hmSummary.put(UninstallProgressStep.DELETING_INSTALLATION_FILES,
603            getFormattedSummary(
604                    INFO_SUMMARY_DELETING_INSTALLATION_FILES.get()));
605
606    LocalizableMessage successMsg;
607    Installation installation = getInstallation();
608    String libPath = getPath(installation.getLibrariesDirectory());
609    String resourcesPath = getPath(installation.getResourcesDirectory());
610    String classesPath = getPath(installation.getClassesDirectory());
611    boolean resourcesDefined =
612     Utils.directoryExistsAndIsNotEmpty(resourcesPath);
613    boolean classesDefined =
614      Utils.directoryExistsAndIsNotEmpty(classesPath);
615    ArrayList<String> paths = new ArrayList<>();
616    paths.add(libPath);
617    if (resourcesDefined)
618    {
619      paths.add(resourcesPath);
620    }
621    if (classesDefined)
622    {
623      paths.add(classesPath);
624    }
625    if (isCli()) {
626      if (getUninstallUserData().getRemoveLibrariesAndTools()) {
627        String arg;
628        if (isWindows()) {
629          arg = installation.getUninstallBatFile() + getLineBreak().toString() +
630                  getTab() + joinAsString(getLineBreak().toString(), paths);
631        } else {
632          arg = joinAsString(getLineBreak().toString(), paths);
633        }
634        successMsg = INFO_SUMMARY_UNINSTALL_FINISHED_SUCCESSFULLY_REMOVE_JARFILES_CLI.get(arg);
635      } else {
636        successMsg = INFO_SUMMARY_UNINSTALL_FINISHED_SUCCESSFULLY_CLI.get();
637      }
638    } else if (getUninstallUserData().getRemoveLibrariesAndTools()) {
639      String formattedPath =
640          addWordBreaks(joinAsString(getLineBreak().toString(), paths), 60, 5);
641      successMsg = INFO_SUMMARY_UNINSTALL_FINISHED_SUCCESSFULLY_REMOVE_JARFILES.get(formattedPath);
642    } else {
643      successMsg = INFO_SUMMARY_UNINSTALL_FINISHED_SUCCESSFULLY.get();
644    }
645    hmSummary.put(UninstallProgressStep.FINISHED_SUCCESSFULLY,
646            getFormattedSuccess(successMsg));
647
648    LocalizableMessage nonCriticalMsg;
649    if (!isCli())
650    {
651      nonCriticalMsg =
652        INFO_SUMMARY_UNINSTALL_FINISHED_WITH_ERROR_ON_REMOTE.get();
653    }
654    else
655    {
656      nonCriticalMsg =
657        INFO_SUMMARY_UNINSTALL_FINISHED_WITH_ERROR_ON_REMOTE_CLI.get();
658    }
659    hmSummary.put(UninstallProgressStep.FINISHED_WITH_ERROR_ON_REMOTE,
660            getFormattedWarning(nonCriticalMsg));
661    if (!isCli())
662    {
663      nonCriticalMsg =
664        INFO_SUMMARY_UNINSTALL_FINISHED_WITH_ERROR_DELETING.get();
665    }
666    else
667    {
668      nonCriticalMsg =
669        INFO_SUMMARY_UNINSTALL_FINISHED_WITH_ERROR_DELETING_CLI.get();
670    }
671    hmSummary.put(UninstallProgressStep.FINISHED_WITH_ERROR_DELETING,
672        getFormattedWarning(nonCriticalMsg));
673    hmSummary.put(UninstallProgressStep.FINISHED_WITH_ERROR,
674            getFormattedError(
675                    INFO_SUMMARY_UNINSTALL_FINISHED_WITH_ERROR.get()));
676
677    /*
678    * hmTime contains the relative time that takes for each task to be
679    * accomplished. For instance if stopping takes twice the time of
680    * deleting files, the value for downloading will be the double of the
681    * value for extracting.
682    */
683    Map<UninstallProgressStep, Integer> hmTime = new HashMap<>();
684    hmTime.put(UninstallProgressStep.UNCONFIGURING_REPLICATION, 5);
685    hmTime.put(UninstallProgressStep.STOPPING_SERVER, 15);
686    hmTime.put(UninstallProgressStep.DISABLING_WINDOWS_SERVICE, 5);
687    hmTime.put(UninstallProgressStep.DELETING_EXTERNAL_DATABASE_FILES, 30);
688    hmTime.put(UninstallProgressStep.DELETING_EXTERNAL_LOG_FILES, 5);
689    hmTime.put(UninstallProgressStep.REMOVING_EXTERNAL_REFERENCES, 5);
690    hmTime.put(UninstallProgressStep.DELETING_INSTALLATION_FILES, 10);
691
692    int totalTime = 0;
693    List<UninstallProgressStep> steps = new ArrayList<>();
694    if (getUninstallUserData().getUpdateRemoteReplication()) {
695      totalTime += hmTime.get(UninstallProgressStep.UNCONFIGURING_REPLICATION);
696      steps.add(UninstallProgressStep.UNCONFIGURING_REPLICATION);
697    }
698    if (getUserData().getStopServer()) {
699      totalTime += hmTime.get(UninstallProgressStep.STOPPING_SERVER);
700      steps.add(UninstallProgressStep.STOPPING_SERVER);
701    }
702    if (isWindowsServiceEnabled()) {
703      totalTime += hmTime.get(UninstallProgressStep.DISABLING_WINDOWS_SERVICE);
704      steps.add(UninstallProgressStep.DISABLING_WINDOWS_SERVICE);
705    }
706    totalTime += hmTime.get(UninstallProgressStep.DELETING_INSTALLATION_FILES);
707    steps.add(UninstallProgressStep.DELETING_INSTALLATION_FILES);
708
709    if (getUninstallUserData().getExternalDbsToRemove().size() > 0) {
710      totalTime += hmTime.get(
711              UninstallProgressStep.DELETING_EXTERNAL_DATABASE_FILES);
712      steps.add(UninstallProgressStep.DELETING_EXTERNAL_DATABASE_FILES);
713    }
714
715    if (getUninstallUserData().getExternalLogsToRemove().size() > 0) {
716      totalTime += hmTime.get(
717              UninstallProgressStep.DELETING_EXTERNAL_LOG_FILES);
718      steps.add(UninstallProgressStep.DELETING_EXTERNAL_LOG_FILES);
719    }
720
721    int cumulatedTime = 0;
722    for (UninstallProgressStep s : steps) {
723      Integer statusTime = hmTime.get(s);
724      hmRatio.put(s, (100 * cumulatedTime) / totalTime);
725      if (statusTime != null) {
726        cumulatedTime += statusTime;
727      }
728    }
729
730    hmRatio.put(UninstallProgressStep.FINISHED_SUCCESSFULLY, 100);
731    hmRatio.put(UninstallProgressStep.FINISHED_WITH_ERROR_ON_REMOTE, 100);
732    hmRatio.put(UninstallProgressStep.FINISHED_WITH_ERROR, 100);
733  }
734
735  /**
736   * Actually performs the uninstall in this thread.  The thread is blocked.
737   */
738  @Override
739  public void run() {
740    runStarted = true;
741    logger.info(LocalizableMessage.raw("run of the Uninstaller started"));
742
743    initMaps();
744    PrintStream origErr = System.err;
745    PrintStream origOut = System.out;
746    try {
747      PrintStream err = new ErrorPrintStream();
748      PrintStream out = new OutputPrintStream();
749      if (!isCli()) {
750        System.setErr(err);
751        System.setOut(out);
752      }
753
754      boolean displaySeparator = false;
755
756      logger.info(LocalizableMessage.raw("Update remote replication? "+
757          getUninstallUserData().getUpdateRemoteReplication()));
758      if (getUninstallUserData().getUpdateRemoteReplication())
759      {
760        status = UninstallProgressStep.UNCONFIGURING_REPLICATION;
761        removeRemoteServerReferences();
762        displaySeparator = true;
763      }
764
765      logger.info(LocalizableMessage.raw("Stop server? "+getUserData().getStopServer()));
766      if (getUserData().getStopServer()) {
767        status = UninstallProgressStep.STOPPING_SERVER;
768        if (displaySeparator && isVerbose()) {
769          notifyListeners(getTaskSeparator());
770        }
771        if (!isVerbose())
772        {
773          notifyListeners(getFormattedWithPoints(
774              INFO_PROGRESS_STOPPING_NON_VERBOSE.get()));
775        }
776        // In case of uninstall, the server stop has to run locally.
777        // In order to bypass the tools.properties mechanism, if any,
778        // we systematically add the --noPropertiesFile flag
779        // when we run the stop-ds command. This is done
780        // by setting the parameter "noPropertiesFile" to 'true'
781        // in the following call.
782        new ServerController(this).stopServer(!isVerbose(),true);
783        if (!isVerbose())
784        {
785          notifyListeners(getFormattedDoneWithLineBreak());
786        }
787        displaySeparator = true;
788      }
789      logger.info(LocalizableMessage.raw("Is Windows Service Enabled? "+
790          isWindowsServiceEnabled()));
791      if (isWindowsServiceEnabled()) {
792        status = UninstallProgressStep.DISABLING_WINDOWS_SERVICE;
793        if (displaySeparator && isVerbose()) {
794          notifyListeners(getTaskSeparator());
795        }
796        disableWindowsService();
797        displaySeparator = true;
798      }
799
800      Set<String> dbsToDelete = getUninstallUserData().getExternalDbsToRemove();
801      if (!dbsToDelete.isEmpty()) {
802        status = UninstallProgressStep.DELETING_EXTERNAL_DATABASE_FILES;
803        if (displaySeparator && isVerbose()) {
804          notifyListeners(getTaskSeparator());
805        }
806
807        try
808        {
809          deleteExternalDatabaseFiles(dbsToDelete);
810          displaySeparator = true;
811        }
812        catch (ApplicationException ae)
813        {
814          if (ae.getType() == ReturnCode.FILE_SYSTEM_ACCESS_ERROR)
815          {
816            errorDeletingOccurred = true;
817            LocalizableMessage msg = getFormattedWarning(ae.getMessageObject());
818            notifyListeners(msg);
819          }
820          else
821          {
822            throw ae;
823          }
824        }
825      }
826
827      Set<String> logsToDelete = getUninstallUserData().getExternalLogsToRemove();
828      if (!logsToDelete.isEmpty()) {
829        status = UninstallProgressStep.DELETING_EXTERNAL_LOG_FILES;
830
831        if (displaySeparator && isVerbose()) {
832          notifyListeners(getTaskSeparator());
833        }
834
835        try
836        {
837          deleteExternalLogFiles(logsToDelete);
838          displaySeparator = true;
839        }
840        catch (ApplicationException ae)
841        {
842          if (ae.getType() == ReturnCode.FILE_SYSTEM_ACCESS_ERROR)
843          {
844            errorDeletingOccurred = true;
845            LocalizableMessage msg = getFormattedWarning(ae.getMessageObject());
846            notifyListeners(msg);
847          }
848          else
849          {
850            throw ae;
851          }
852        }
853      }
854
855      UninstallUserData userData = getUninstallUserData();
856      boolean somethingToDelete = userData.getRemoveBackups() ||
857              userData.getRemoveConfigurationAndSchema() ||
858              userData.getRemoveDatabases() ||
859              userData.getRemoveLDIFs() ||
860              userData.getRemoveLibrariesAndTools() ||
861              userData.getRemoveLogs();
862      if (displaySeparator && somethingToDelete && isVerbose()) {
863        notifyListeners(getTaskSeparator());
864      }
865
866      if (somethingToDelete) {
867        status = UninstallProgressStep.DELETING_INSTALLATION_FILES;
868        try
869        {
870          deleteInstallationFiles(getRatio(status),
871                getRatio(UninstallProgressStep.FINISHED_SUCCESSFULLY));
872        }
873        catch (ApplicationException ae)
874        {
875          if (ae.getType() == ReturnCode.FILE_SYSTEM_ACCESS_ERROR)
876          {
877            errorDeletingOccurred = true;
878            LocalizableMessage msg = getFormattedWarning(ae.getMessageObject());
879            notifyListeners(msg);
880          }
881          else
882          {
883            throw ae;
884          }
885        }
886      }
887      if (errorOnRemoteOccurred)
888      {
889        status = UninstallProgressStep.FINISHED_WITH_ERROR_ON_REMOTE;
890      }
891      else if (errorDeletingOccurred)
892      {
893        status = UninstallProgressStep.FINISHED_WITH_ERROR_DELETING;
894      }
895      else
896      {
897        status = UninstallProgressStep.FINISHED_SUCCESSFULLY;
898      }
899      if (isCli()) {
900        notifyListeners(new LocalizableMessageBuilder(getLineBreak())
901                .append(getLineBreak()).append(getSummary(status))
902                .toMessage());
903      } else {
904        notifyListeners(null);
905      }
906
907    } catch (ApplicationException ex) {
908      logger.error(LocalizableMessage.raw("Error: "+ex, ex));
909      ue = ex;
910      status = UninstallProgressStep.FINISHED_WITH_ERROR;
911      LocalizableMessage msg = getFormattedError(ex, true);
912      notifyListeners(msg);
913    }
914    catch (Throwable t) {
915      logger.error(LocalizableMessage.raw("Error: "+t, t));
916      ue = new ApplicationException(
917              ReturnCode.BUG,
918              getThrowableMsg(INFO_BUG_MSG.get(), t), t);
919      status = UninstallProgressStep.FINISHED_WITH_ERROR;
920      LocalizableMessage msg = getFormattedError(ue, true);
921      notifyListeners(msg);
922    }
923    if (!isCli()) {
924      System.setErr(origErr);
925      System.setOut(origOut);
926    }
927  }
928
929  /** {@inheritDoc} */
930  @Override
931  public ProgressStep getCurrentProgressStep() {
932    return status;
933  }
934
935  /**
936   * Returns an integer that specifies which percentage of the whole
937   * installation has been completed.
938   *
939   * @param step the UninstallProgressStep for which we want to get the ratio.
940   * @return an integer that specifies which percentage of the whole
941   *         uninstallation has been completed.
942   */
943  @Override
944  public Integer getRatio(ProgressStep step) {
945    return hmRatio.get(step);
946  }
947
948  /**
949   * Returns an formatted representation of the summary for the specified
950   * UninstallProgressStep.
951   *
952   * @param step the UninstallProgressStep for which we want to get the summary.
953   * @return an formatted representation of the summary for the specified
954   *         UninstallProgressStep.
955   */
956  @Override
957  public LocalizableMessage getSummary(ProgressStep step) {
958    return hmSummary.get(step);
959  }
960
961  /** {@inheritDoc} */
962  @Override
963  public boolean isFinished() {
964    return getCurrentProgressStep() ==
965            UninstallProgressStep.FINISHED_SUCCESSFULLY
966    || getCurrentProgressStep() ==
967            UninstallProgressStep.FINISHED_WITH_ERROR
968    || getCurrentProgressStep() ==
969            UninstallProgressStep.FINISHED_WITH_ERROR_ON_REMOTE
970    || getCurrentProgressStep() ==
971            UninstallProgressStep.FINISHED_WITH_ERROR_DELETING;
972  }
973
974  /** {@inheritDoc} */
975  @Override
976  public boolean isCancellable() {
977    return false;
978  }
979
980  /** {@inheritDoc} */
981  @Override
982  public void cancel() {
983    // do nothing; not cancellable
984  }
985
986  /** {@inheritDoc} */
987  @Override
988  public void windowClosing(QuickSetupDialog dlg, WindowEvent evt) {
989    if (dlg.getDisplayedStep() == PROGRESS ||
990        dlg.getDisplayedStep() == FINISHED) {
991      // Simulate a close button event
992      dlg.notifyButtonEvent(ButtonName.CLOSE);
993    } else {
994      // Simulate a quit button event
995      dlg.notifyButtonEvent(ButtonName.QUIT);
996    }
997  }
998
999  /** {@inheritDoc} */
1000  @Override
1001  public ButtonName getInitialFocusButtonName() {
1002    return ButtonName.FINISH;
1003  }
1004
1005  /** {@inheritDoc} */
1006  @Override
1007  public Set<? extends WizardStep> getWizardSteps() {
1008    Set<WizardStep> setSteps = new HashSet<>();
1009    setSteps.add(Step.CONFIRM_UNINSTALL);
1010    setSteps.add(Step.PROGRESS);
1011    setSteps.add(Step.FINISHED);
1012    return Collections.unmodifiableSet(setSteps);
1013  }
1014
1015  /** {@inheritDoc} */
1016  @Override
1017  public QuickSetupStepPanel createWizardStepPanel(WizardStep step) {
1018    if (step == Step.CONFIRM_UNINSTALL) {
1019      return new ConfirmUninstallPanel(this, installStatus);
1020    } else if (step == Step.PROGRESS) {
1021      return new ProgressPanel(this);
1022    } else if (step == Step.FINISHED) {
1023      return new FinishedPanel(this);
1024    }
1025    return null;
1026  }
1027
1028  /**
1029   * Deletes the external database files specified in the provided Set.
1030   *
1031   * @param dbFiles the database directories to be deleted.
1032   * @throws ApplicationException if something goes wrong.
1033   */
1034  private void deleteExternalDatabaseFiles(Set<String> dbFiles)
1035          throws ApplicationException {
1036    if (isVerbose())
1037    {
1038      notifyListeners(getFormattedProgressWithLineBreak(
1039            INFO_PROGRESS_DELETING_EXTERNAL_DB_FILES.get()));
1040    }
1041    else
1042    {
1043      notifyListeners(getFormattedWithPoints(
1044          INFO_PROGRESS_DELETING_EXTERNAL_DB_FILES_NON_VERBOSE.get()));
1045    }
1046    for (String path : dbFiles) {
1047      deleteRecursively(new File(path));
1048    }
1049    if (!isVerbose())
1050    {
1051      notifyListeners(getFormattedDoneWithLineBreak());
1052    }
1053  }
1054
1055  /**
1056   * Deletes the external database files specified in the provided Set.
1057   *
1058   * @param logFiles the log files to be deleted.
1059   * @throws ApplicationException if something goes wrong.
1060   */
1061  private void deleteExternalLogFiles(Set<String> logFiles)
1062          throws ApplicationException {
1063    if (isVerbose())
1064    {
1065      notifyListeners(getFormattedProgressWithLineBreak(
1066          INFO_PROGRESS_DELETING_EXTERNAL_LOG_FILES.get()));
1067    }
1068    else
1069    {
1070      notifyListeners(getFormattedWithPoints(
1071          INFO_PROGRESS_DELETING_EXTERNAL_LOG_FILES_NON_VERBOSE.get()));
1072    }
1073    for (String path : logFiles) {
1074      deleteRecursively(new File(path));
1075    }
1076    if (!isVerbose())
1077    {
1078      notifyListeners(getFormattedDoneWithLineBreak());
1079    }
1080  }
1081
1082  /**
1083   * Deletes the files under the installation path.
1084   *
1085   * @throws ApplicationException if something goes wrong.
1086   */
1087  private void deleteInstallationFiles(int minRatio, int maxRatio)
1088          throws ApplicationException {
1089    if (isVerbose())
1090    {
1091      notifyListeners(getFormattedProgressWithLineBreak(
1092          INFO_PROGRESS_DELETING_INSTALLATION_FILES.get()));
1093    }
1094    else
1095    {
1096      notifyListeners(getFormattedWithPoints(
1097          INFO_PROGRESS_DELETING_INSTALLATION_FILES_NON_VERBOSE.get()));
1098    }
1099
1100    String installPath = getInstallPathFromClasspath();
1101    File installFile = new File(installPath);
1102    try
1103    {
1104      installPath = installFile.getCanonicalPath();
1105    }
1106    catch(Exception e)
1107    {
1108      installPath = getInstallPathFromClasspath();
1109    }
1110
1111    String instancePath =
1112      Utils.getInstancePathFromInstallPath(installFile.getAbsolutePath());
1113    File instanceFile = new File(instancePath);
1114    try
1115    {
1116      instancePath = instanceFile.getCanonicalPath();
1117    } catch (Exception e)
1118    {
1119      instancePath =
1120        Utils.getInstancePathFromInstallPath(installFile.getAbsolutePath());
1121    }
1122
1123    InstallationFilesToDeleteFilter filter =
1124            new InstallationFilesToDeleteFilter();
1125
1126    File[] installFiles  = installFile.listFiles();
1127    File[] instanceFiles  = null ;
1128    if (! installPath.equals(instancePath))
1129    {
1130      instanceFiles = new File(instancePath).listFiles();
1131    }
1132
1133    File[] rootFiles = null;
1134
1135    if (installFiles == null)
1136    {
1137      rootFiles = new File(instancePath).listFiles();
1138    }
1139    else
1140    if (instanceFiles == null)
1141    {
1142      rootFiles = installFiles;
1143    }
1144    else
1145    {
1146      // both installFiles and instanceFiles are not null
1147      rootFiles = new File[installFiles.length + instanceFiles.length];
1148      System.arraycopy(installFiles,  0, rootFiles, 0, installFiles.length);
1149      System.arraycopy(instanceFiles, 0, rootFiles, installFiles.length,
1150          instanceFiles.length);
1151    }
1152
1153    if (rootFiles != null) {
1154      /* The following is done to have a moving progress bar when we delete
1155       * the installation files.
1156       */
1157      int totalRatio = 0;
1158      ArrayList<Integer> cumulatedRatio = new ArrayList<>();
1159      for (File f : rootFiles) {
1160        if (filter.accept(f)) {
1161          Installation installation = getInstallation();
1162          int relativeRatio;
1163          if (equalsOrDescendant(f, installation.getLibrariesDirectory())) {
1164            relativeRatio = 10;
1165          } else
1166          if (equalsOrDescendant(f, installation.getBinariesDirectory())) {
1167            relativeRatio = 5;
1168          } else
1169          if (equalsOrDescendant(f, installation.getConfigurationDirectory())) {
1170            relativeRatio = 5;
1171          } else
1172          if (equalsOrDescendant(f, installation.getBackupDirectory())) {
1173            relativeRatio = 20;
1174          } else
1175          if (equalsOrDescendant(f, installation.getLdifDirectory())) {
1176            relativeRatio = 20;
1177          } else if (equalsOrDescendant(f, installation.getDatabasesDirectory())) {
1178            relativeRatio = 50;
1179          } else
1180          if (equalsOrDescendant(f, installation.getLogsDirectory())) {
1181            relativeRatio = 30;
1182          } else {
1183            relativeRatio = 2;
1184          }
1185          cumulatedRatio.add(totalRatio);
1186          totalRatio += relativeRatio;
1187        } else {
1188          cumulatedRatio.add(totalRatio);
1189        }
1190      }
1191      Iterator<Integer> it = cumulatedRatio.iterator();
1192      for (File rootFile : rootFiles)
1193      {
1194        int beforeRatio = minRatio +
1195                (it.next() * (maxRatio - minRatio)) / totalRatio;
1196        hmRatio.put(UninstallProgressStep.DELETING_INSTALLATION_FILES, beforeRatio);
1197        deleteRecursively(rootFile, filter);
1198      }
1199      hmRatio.put(UninstallProgressStep.DELETING_INSTALLATION_FILES, maxRatio);
1200    }
1201    if (!isVerbose())
1202    {
1203      notifyListeners(getFormattedDone());
1204    }
1205  }
1206
1207  /**
1208   * Deletes everything below the specified file.
1209   *
1210   * @param file the path to be deleted.
1211   * @throws ApplicationException if something goes wrong.
1212   */
1213  private void deleteRecursively(File file) throws ApplicationException {
1214    deleteRecursively(file, null);
1215  }
1216
1217  /**
1218   * Deletes everything below the specified file.
1219   *
1220   * @param file   the path to be deleted.
1221   * @param filter the filter of the files to know if the file can be deleted
1222   *               directly or not.
1223   * @throws ApplicationException if something goes wrong.
1224   */
1225  private void deleteRecursively(File file, FileFilter filter)
1226          throws ApplicationException {
1227    File cfile ;
1228    try
1229    {
1230      cfile = file.getCanonicalFile();
1231    }
1232    catch (Exception e)
1233    {
1234      cfile = file ;
1235    }
1236    if (cfile.exists()) {
1237      if (cfile.isFile()) {
1238        if (filter != null) {
1239          if (filter.accept(cfile)) {
1240            delete(cfile);
1241          }
1242        } else {
1243          delete(cfile);
1244        }
1245      } else {
1246        File[] children = cfile.listFiles();
1247        if (children != null) {
1248          for (File element : children)
1249          {
1250            deleteRecursively(element, filter);
1251          }
1252        }
1253        if (filter != null) {
1254          if (filter.accept(cfile)) {
1255            delete(cfile);
1256          }
1257        } else {
1258          delete(cfile);
1259        }
1260      }
1261    } else {
1262      // Just tell that the file/directory does not exist.
1263      notifyListeners(getFormattedWarning(
1264          INFO_PROGRESS_DELETING_FILE_DOES_NOT_EXIST.get(cfile)));
1265    }
1266  }
1267
1268  /**
1269   * Deletes the specified file.
1270   *
1271   * @param file the file to be deleted.
1272   * @throws ApplicationException if something goes wrong.
1273   */
1274  private void delete(File file) throws ApplicationException {
1275    boolean isFile = file.isFile();
1276
1277    if (isVerbose())
1278    {
1279      if (isFile) {
1280        notifyListeners(getFormattedWithPoints(
1281            INFO_PROGRESS_DELETING_FILE.get(file.getAbsolutePath())));
1282      } else {
1283        notifyListeners(getFormattedWithPoints(
1284            INFO_PROGRESS_DELETING_DIRECTORY.get(file.getAbsolutePath())));
1285      }
1286    }
1287
1288    boolean delete = false;
1289    /*
1290     * Sometimes the server keeps some locks on the files.
1291     * This is dependent on the OS so there is no much we can do here.
1292     */
1293    int nTries = 5;
1294    for (int i = 0; i < nTries && !delete; i++) {
1295      delete = file.delete();
1296      if (!delete) {
1297        try {
1298          Thread.sleep(1000);
1299        }
1300        catch (Exception ex) {
1301        }
1302      }
1303    }
1304
1305    if (!delete) {
1306      LocalizableMessage errMsg;
1307      if (isFile) {
1308        errMsg = INFO_ERROR_DELETING_FILE.get(file.getAbsolutePath());
1309      } else {
1310        errMsg = INFO_ERROR_DELETING_DIRECTORY.get(file.getAbsolutePath());
1311      }
1312      throw new ApplicationException(
1313          ReturnCode.FILE_SYSTEM_ACCESS_ERROR,
1314          errMsg, null);
1315    }
1316
1317    if (isVerbose())
1318    {
1319      notifyListeners(getFormattedDoneWithLineBreak());
1320    }
1321  }
1322
1323  private boolean equalsOrDescendant(File file, File directory) {
1324    return file.equals(directory) || isDescendant(file, directory);
1325  }
1326
1327  /**
1328   * This class is used to get the files that are not binaries.  This is
1329   * required to know which are the files that can be deleted directly and which
1330   * not.
1331   */
1332  private class InstallationFilesToDeleteFilter implements FileFilter {
1333    private Installation installation = getInstallation();
1334    private File quicksetupFile = installation.getQuicksetupJarFile();
1335    private File openDSFile = installation.getOpenDSJarFile();
1336    private File librariesFile = installation.getLibrariesDirectory();
1337    private File resourcesDir = installation.getResourcesDirectory();
1338    private File classesDir = installation.getClassesDirectory();
1339    private File uninstallBatFile = installation.getUninstallBatFile();
1340
1341    private boolean canDeleteResourcesDir =
1342      !Utils.directoryExistsAndIsNotEmpty(resourcesDir.getAbsolutePath());
1343    private boolean canDeleteClassesDir =
1344      !Utils.directoryExistsAndIsNotEmpty(classesDir.getAbsolutePath());
1345
1346
1347    private File installationPath = installation.getRootDirectory();
1348
1349    /** {@inheritDoc} */
1350    @Override
1351    public boolean accept(File file) {
1352      UninstallUserData userData = getUninstallUserData();
1353      boolean[] uData = {
1354              userData.getRemoveLibrariesAndTools(),
1355              userData.getRemoveLibrariesAndTools(),
1356              userData.getRemoveLibrariesAndTools(),
1357              userData.getRemoveLibrariesAndTools(),
1358              userData.getRemoveDatabases(),
1359              userData.getRemoveLogs(),
1360              userData.getRemoveConfigurationAndSchema(),
1361              userData.getRemoveBackups(),
1362              userData.getRemoveLDIFs()
1363      };
1364
1365      Installation installation = getInstallation();
1366      File[] parentFiles  ;
1367      try {
1368        File[] tmp  = {
1369              installation.getLibrariesDirectory().getCanonicalFile(),
1370              installation.getBinariesDirectory().getCanonicalFile(),
1371              installation.getResourcesDirectory().getCanonicalFile(),
1372              installation.getClassesDirectory().getCanonicalFile(),
1373              installation.getDatabasesDirectory().getCanonicalFile(),
1374              installation.getLogsDirectory().getCanonicalFile(),
1375              installation.getConfigurationDirectory().getCanonicalFile(),
1376              installation.getBackupDirectory().getCanonicalFile(),
1377              installation.getLdifDirectory().getCanonicalFile()
1378      };
1379        parentFiles = tmp ;
1380      }
1381      catch (Exception e)
1382      {
1383        return true;
1384      }
1385
1386      boolean accept =
1387        !installationPath.equals(file)
1388        && !equalsOrDescendant(file, librariesFile)
1389        && (canDeleteClassesDir  || !equalsOrDescendant(file, classesDir))
1390        && (canDeleteResourcesDir || !equalsOrDescendant(file, resourcesDir))
1391        && !quicksetupFile.equals(file)
1392        && !openDSFile.equals(file);
1393
1394      if (accept && isWindows() && isCli()) {
1395        accept = !uninstallBatFile.equals(file);
1396      }
1397
1398      for (int i = 0; i < uData.length && accept; i++) {
1399        File parent = parentFiles[i];
1400        accept &= uData[i] ||
1401                !equalsOrDescendant(file, parent);
1402      }
1403
1404      logger.info(LocalizableMessage.raw("accept for :"+file+" is: "+accept));
1405      return accept;
1406    }
1407  }
1408
1409  private boolean isWindowsServiceEnabled() {
1410    if (isWindowsServiceEnabled == null) {
1411      isWindowsServiceEnabled = serviceState() == SERVICE_STATE_ENABLED;
1412    }
1413    return isWindowsServiceEnabled.booleanValue();
1414  }
1415
1416  /** {@inheritDoc} */
1417  @Override
1418  public ApplicationTrustManager getTrustManager()
1419  {
1420    return getUninstallUserData().getTrustManager();
1421  }
1422
1423  /**
1424   * This methods disables this server as a Windows service.
1425   *
1426   * @throws ApplicationException if something goes wrong.
1427   */
1428  protected void disableWindowsService() throws ApplicationException {
1429    notifyListeners(getFormattedWithPoints(
1430            INFO_PROGRESS_DISABLING_WINDOWS_SERVICE.get()));
1431    int code = disableService(System.out, System.err);
1432
1433    LocalizableMessage errorMessage = INFO_ERROR_DISABLING_WINDOWS_SERVICE.get(
1434            getInstallationPath());
1435
1436    switch (code) {
1437      case SERVICE_DISABLE_SUCCESS:
1438        break;
1439      case SERVICE_ALREADY_DISABLED:
1440        break;
1441      default:
1442        throw new ApplicationException(ReturnCode.WINDOWS_SERVICE_ERROR, errorMessage, null);
1443    }
1444    notifyListeners(getLineBreak());
1445  }
1446
1447  private UninstallUserData getUninstallUserData() {
1448    return (UninstallUserData) getUserData();
1449  }
1450
1451  /**
1452   * Tries to start the server and launches a progress dialog.  This method
1453   * assumes that is being called from the event thread.
1454   * @return <CODE>true</CODE> if the server could be started and <CODE>
1455   * false</CODE> otherwise.
1456   * @param frame the JFrame to be used as parent of the progress dialog.
1457   */
1458  private boolean startServer(JFrame frame)
1459  {
1460    startProgressDetails = new LocalizableMessageBuilder();
1461    startProgressDlg = new ProgressDialog(frame);
1462    startProgressDlg.setSummary(
1463        getFormattedSummary(INFO_SUMMARY_STARTING.get()));
1464    startProgressDlg.setDetails(LocalizableMessage.EMPTY);
1465    startProgressDlg.setCloseButtonEnabled(false);
1466    final Boolean[] returnValue = new Boolean[] {Boolean.FALSE};
1467    Thread t = new Thread(new Runnable()
1468    {
1469      @Override
1470      public void run()
1471      {
1472        try
1473        {
1474          new ServerController(Uninstaller.this).startServer();
1475          final boolean isServerRunning =
1476            Installation.getLocal().getStatus().isServerRunning();
1477          returnValue[0] = isServerRunning;
1478          SwingUtilities.invokeLater(new Runnable()
1479          {
1480            @Override
1481            public void run()
1482            {
1483              if (isServerRunning)
1484              {
1485                startProgressDlg.setSummary(getFormattedSuccess(
1486                    INFO_SUMMARY_START_SUCCESS.get()));
1487              }
1488              else
1489              {
1490               startProgressDlg.setSummary(getFormattedError(
1491                       INFO_SUMMARY_START_ERROR.get()));
1492              }
1493              startProgressDlg.setCloseButtonEnabled(true);
1494            }
1495          });
1496        }
1497        catch (Throwable t)
1498        {
1499          LocalizableMessage msg = getFormattedError(t, true);
1500          notifyListeners(msg);
1501        }
1502      }
1503    });
1504    t.start();
1505    startProgressDlg.pack();
1506    Utilities.centerOnComponent(startProgressDlg, frame);
1507    startProgressDlg.setModal(true);
1508    startProgressDlg.setVisible(true);
1509    startProgressDlg = null;
1510    return returnValue[0];
1511  }
1512
1513  /**
1514   * This method displays a login dialog message, asking the user to provide
1515   * authentication to retrieve information from the ADS and update the
1516   * remote servers.  Then it tries to connect to the remote servers.
1517   *
1518   * @param qs the QuickSetup object.
1519   */
1520  private void askForAuthenticationAndLaunch(final QuickSetup qs)
1521  {
1522    if (loginDialog == null)
1523    {
1524      loginDialog = new LoginDialog(qs.getDialog().getFrame(),
1525          getTrustManager(), getConnectTimeout());
1526      loginDialog.pack();
1527    }
1528    Utilities.centerOnComponent(loginDialog, qs.getDialog().getFrame());
1529    loginDialog.setModal(true);
1530    loginDialog.setVisible(true);
1531    if (!loginDialog.isCanceled())
1532    {
1533      getUninstallUserData().setAdminUID(loginDialog.getAdministratorUid());
1534      getUninstallUserData().setAdminPwd(loginDialog.getAdministratorPwd());
1535      final InitialLdapContext ctx = loginDialog.getContext();
1536      try
1537      {
1538        getUninstallUserData().setLocalServerUrl(
1539            (String)ctx.getEnvironment().get(Context.PROVIDER_URL));
1540      }
1541      catch (NamingException ne)
1542      {
1543        logger.warn(LocalizableMessage.raw("Could not find local server: "+ne, ne));
1544        getUninstallUserData().setLocalServerUrl("ldap://localhost:389");
1545      }
1546      getUninstallUserData().setReplicationServer(
1547          loginDialog.getHostName() + ":" +
1548          conf.getReplicationServerPort());
1549      getUninstallUserData().setReferencedHostName(loginDialog.getHostName());
1550
1551      BackgroundTask<TopologyCache> worker = new BackgroundTask<TopologyCache>()
1552      {
1553        @Override
1554        public TopologyCache processBackgroundTask() throws Throwable
1555        {
1556          logger.info(LocalizableMessage.raw("Loading Topology Cache in askForAuthentication"));
1557          ADSContext adsContext = new ADSContext(ctx);
1558          TopologyCache cache = new TopologyCache(adsContext,
1559              getTrustManager(), getConnectTimeout());
1560          cache.getFilter().setSearchMonitoringInformation(false);
1561          cache.reloadTopology();
1562          return cache;
1563        }
1564        @Override
1565        public void backgroundTaskCompleted(TopologyCache returnValue,
1566            Throwable throwable) {
1567          qs.getDialog().workerFinished();
1568          if (throwable != null)
1569          {
1570            logger.warn(LocalizableMessage.raw("Throwable: "+throwable, throwable));
1571            if (throwable instanceof TopologyCacheException)
1572            {
1573              qs.displayError(
1574                      getMessage(
1575                              (TopologyCacheException)throwable),
1576                      INFO_ERROR_TITLE.get());
1577            }
1578            else
1579            {
1580              qs.displayError(
1581                  getThrowableMsg(INFO_BUG_MSG.get(), throwable),
1582                  INFO_ERROR_TITLE.get());
1583            }
1584            logger.info(LocalizableMessage.raw("Error was displayed"));
1585          }
1586          else
1587          {
1588            TopologyCache cache = returnValue;
1589            handleTopologyCache(qs, cache);
1590          }
1591        }
1592      };
1593
1594      qs.getDialog().workerStarted();
1595      worker.startBackgroundTask();
1596    }
1597    else if (qs.displayConfirmation(
1598        INFO_CONFIRM_UNINSTALL_SERVER_RUNNING_MSG.get(),
1599        INFO_CONFIRM_UNINSTALL_SERVER_RUNNING_TITLE.get()))
1600    {
1601      getUserData().setStopServer(true);
1602      qs.launch();
1603      qs.setCurrentStep(getNextWizardStep(Step.CONFIRM_UNINSTALL));
1604    } else {
1605      getUserData().setStopServer(false);
1606    }
1607  }
1608
1609  /**
1610   * Method that interacts with the user depending on what errors where
1611   * encountered in the TopologyCache object.  This method assumes that the
1612   * TopologyCache has been reloaded.
1613   * Note: this method assumes that is being called from the event thread.
1614   * @param qs the QuickSetup object for the application.
1615   * @param cache the TopologyCache.
1616   */
1617  private void handleTopologyCache(QuickSetup qs, TopologyCache cache)
1618  {
1619    logger.info(LocalizableMessage.raw("Handling TopologyCache"));
1620    boolean stopProcessing = false;
1621    Set<TopologyCacheException> exceptions = new HashSet<>();
1622    /* Analyze if we had any exception while loading servers.  For the moment
1623     * only throw the exception found if the user did not provide the
1624     * Administrator DN and this caused a problem authenticating in one server
1625     * or if there is a certificate problem.
1626     */
1627    for (ServerDescriptor server : cache.getServers())
1628    {
1629      TopologyCacheException e = server.getLastException();
1630      if (e != null)
1631      {
1632        exceptions.add(e);
1633      }
1634    }
1635    Set<LocalizableMessage> exceptionMsgs = new LinkedHashSet<>();
1636    /* Check the exceptions and see if we throw them or not. */
1637    for (TopologyCacheException e : exceptions)
1638    {
1639      logger.info(LocalizableMessage.raw("Analyzing exception: "+e, e));
1640      if (stopProcessing)
1641      {
1642        break;
1643      }
1644      switch (e.getType())
1645      {
1646      case NOT_GLOBAL_ADMINISTRATOR:
1647        LocalizableMessage errorMsg = INFO_NOT_GLOBAL_ADMINISTRATOR_PROVIDED.get();
1648        qs.displayError(errorMsg, INFO_ERROR_TITLE.get());
1649        stopProcessing = true;
1650        break;
1651      case GENERIC_CREATING_CONNECTION:
1652        if (isCertificateException(e.getCause()))
1653        {
1654          ApplicationTrustManager.Cause cause = null;
1655          if (e.getTrustManager() != null)
1656          {
1657            cause = e.getTrustManager().getLastRefusedCause();
1658          }
1659          logger.info(LocalizableMessage.raw("Certificate exception cause: "+cause));
1660          UserDataCertificateException.Type excType = getCertificateExceptionType(cause);
1661          if (excType != null)
1662          {
1663            String h;
1664            int p;
1665            try
1666            {
1667              URI uri = new URI(e.getLdapUrl());
1668              h = uri.getHost();
1669              p = uri.getPort();
1670            }
1671            catch (Throwable t)
1672            {
1673              logger.warn(LocalizableMessage.raw(
1674                  "Error parsing ldap url of TopologyCacheException.", t));
1675              h = INFO_NOT_AVAILABLE_LABEL.get().toString();
1676              p = -1;
1677            }
1678            UserDataCertificateException exc =
1679              new UserDataCertificateException(Step.REPLICATION_OPTIONS,
1680                INFO_CERTIFICATE_EXCEPTION.get(h, p),
1681                e.getCause(), h, p,
1682                e.getTrustManager().getLastRefusedChain(),
1683                e.getTrustManager().getLastRefusedAuthType(), excType);
1684            handleCertificateException(qs, exc, cache);
1685            stopProcessing = true;
1686          }
1687        }
1688      }
1689      exceptionMsgs.add(getMessage(e));
1690    }
1691    if (!stopProcessing && !exceptionMsgs.isEmpty())
1692    {
1693      LocalizableMessage confirmationMsg =
1694        ERR_UNINSTALL_READING_REGISTERED_SERVERS_CONFIRM_UPDATE_REMOTE.get(
1695                getMessageFromCollection(exceptionMsgs, "\n"));
1696      stopProcessing = !qs.displayConfirmation(confirmationMsg,
1697          INFO_CONFIRMATION_TITLE.get());
1698    }
1699    if (!stopProcessing)
1700    {
1701      stopProcessing = !qs.displayConfirmation(
1702          INFO_CONFIRM_UNINSTALL_SERVER_RUNNING_MSG.get(),
1703          INFO_CONFIRM_UNINSTALL_SERVER_RUNNING_TITLE.get());
1704    }
1705    if (!stopProcessing)
1706    {
1707      // Launch everything
1708      getUninstallUserData().setUpdateRemoteReplication(true);
1709      getUninstallUserData().setRemoteServers(cache.getServers());
1710      getUserData().setStopServer(true);
1711      qs.launch();
1712      qs.setCurrentStep(getNextWizardStep(Step.CONFIRM_UNINSTALL));
1713    }
1714  }
1715
1716  private UserDataCertificateException.Type getCertificateExceptionType(ApplicationTrustManager.Cause cause)
1717  {
1718    if (cause == ApplicationTrustManager.Cause.NOT_TRUSTED)
1719    {
1720      return UserDataCertificateException.Type.NOT_TRUSTED;
1721    }
1722    else if (cause == ApplicationTrustManager.Cause.HOST_NAME_MISMATCH)
1723    {
1724      return UserDataCertificateException.Type.HOST_NAME_MISMATCH;
1725    }
1726    else
1727    {
1728      return null;
1729    }
1730  }
1731
1732  /**
1733   * Displays a dialog asking the user to accept a certificate if the user
1734   * accepts it, we update the trust manager and call again to the method that
1735   * handles the action of clicking on "Finish".
1736   * This method assumes that we are being called from the event thread.
1737   */
1738  private void handleCertificateException(final QuickSetup qs,
1739      UserDataCertificateException ce, final TopologyCache cache)
1740  {
1741    CertificateDialog dlg =
1742      new CertificateDialog(qs.getDialog().getFrame(), ce);
1743    dlg.pack();
1744    dlg.setVisible(true);
1745    if (dlg.getUserAnswer() != CertificateDialog.ReturnType.NOT_ACCEPTED)
1746    {
1747      X509Certificate[] chain = ce.getChain();
1748      String authType = ce.getAuthType();
1749      String host = ce.getHost();
1750
1751      if (chain != null && authType != null && host != null)
1752      {
1753        logger.info(LocalizableMessage.raw("Accepting certificate presented by host "+host));
1754        getTrustManager().acceptCertificate(chain, authType, host);
1755        BackgroundTask<TopologyCache> worker =
1756          new BackgroundTask<TopologyCache>()
1757        {
1758          @Override
1759          public TopologyCache processBackgroundTask() throws Throwable
1760          {
1761            logger.info(LocalizableMessage.raw("Reloading topology"));
1762            cache.getFilter().setSearchMonitoringInformation(false);
1763            cache.reloadTopology();
1764            return cache;
1765          }
1766          @Override
1767          public void backgroundTaskCompleted(TopologyCache returnValue,
1768              Throwable throwable) {
1769            qs.getDialog().workerFinished();
1770            if (throwable != null)
1771            {
1772              if (throwable instanceof TopologyCacheException)
1773              {
1774                qs.displayError(getMessage((TopologyCacheException)throwable),
1775                    INFO_ERROR_TITLE.get());
1776              }
1777              else
1778              {
1779                qs.displayError(
1780                    getThrowableMsg(INFO_BUG_MSG.get(), throwable),
1781                    INFO_ERROR_TITLE.get());
1782              }
1783            }
1784            else
1785            {
1786              handleTopologyCache(qs, cache);
1787            }
1788          }
1789        };
1790
1791        qs.getDialog().workerStarted();
1792        worker.startBackgroundTask();
1793      }
1794      else
1795      {
1796        if (chain == null)
1797        {
1798          logger.warn(LocalizableMessage.raw(
1799              "The chain is null for the UserDataCertificateException"));
1800        }
1801        if (authType == null)
1802        {
1803          logger.warn(LocalizableMessage.raw(
1804              "The auth type is null for the UserDataCertificateException"));
1805        }
1806        if (host == null)
1807        {
1808          logger.warn(LocalizableMessage.raw(
1809              "The host is null for the UserDataCertificateException"));
1810        }
1811      }
1812    }
1813    if (dlg.getUserAnswer() ==
1814      CertificateDialog.ReturnType.ACCEPTED_PERMANENTLY)
1815    {
1816      X509Certificate[] chain = ce.getChain();
1817      if (chain != null)
1818      {
1819        try
1820        {
1821          UIKeyStore.acceptCertificate(chain);
1822        }
1823        catch (Throwable t)
1824        {
1825          logger.warn(LocalizableMessage.raw("Error accepting certificate: "+t, t));
1826        }
1827      }
1828    }
1829  }
1830
1831  /**
1832   * This method updates the replication in the remote servers.  It does
1833   * throw ApplicationException if we are working on the force on error mode.
1834   * It also tries to delete the server registration entry from the remote ADS
1835   * servers.
1836   * @throws ApplicationException if we are not working on force on error mode
1837   * and there is an error.
1838   */
1839  private void removeRemoteServerReferences() throws ApplicationException
1840  {
1841    Set<ServerDescriptor> servers = getUninstallUserData().getRemoteServers();
1842    Map<ADSContext.ServerProperty, Object> serverADSProperties = null;
1843    for (ServerDescriptor server : servers)
1844    {
1845      if (isServerToUninstall(server))
1846      {
1847        serverADSProperties = server.getAdsProperties();
1848        break;
1849      }
1850    }
1851    if (serverADSProperties == null)
1852    {
1853      logger.warn(LocalizableMessage.raw("The server ADS properties for the server to "+
1854          "uninstall could not be found."));
1855    }
1856
1857    for (ServerDescriptor server : servers)
1858    {
1859      if (server.getAdsProperties() != serverADSProperties)
1860      {
1861        removeReferences(server, serverADSProperties);
1862      }
1863    }
1864  }
1865
1866  /**
1867   * This method updates the replication in the remote server represented by
1868   * a given ServerProperty object.
1869   * It also tries to delete the server registration entry from the remote ADS
1870   * servers if the serverADSProperties object passed is not null.
1871   * @param server the ServerDescriptor object representing the server where
1872   * we want to remove references to the server that we are trying to uninstall.
1873   * @param serverADSProperties the Map with the ADS properties of the server
1874   * that we are trying to uninstall.
1875   * @throws ApplicationException if we are not working on force on error mode
1876   * and there is an error.
1877   */
1878  private void removeReferences(ServerDescriptor server,
1879      Map<ADSContext.ServerProperty, Object> serverADSProperties)
1880  throws ApplicationException
1881  {
1882    /* First check if the server must be updated based in the contents of the
1883     * ServerDescriptor object. */
1884    boolean hasReferences = false;
1885
1886    Object v = server.getServerProperties().get(
1887        ServerDescriptor.ServerProperty.IS_REPLICATION_SERVER);
1888    if (Boolean.TRUE.equals(v))
1889    {
1890      Set<?> replicationServers = (Set<?>)server.getServerProperties().get(
1891          ServerDescriptor.ServerProperty.EXTERNAL_REPLICATION_SERVERS);
1892      if (replicationServers != null)
1893      {
1894        for (Object o : replicationServers)
1895        {
1896          if (getUninstallUserData().getReplicationServer().equalsIgnoreCase(
1897              (String)o))
1898          {
1899            hasReferences = true;
1900            break;
1901          }
1902        }
1903      }
1904    }
1905
1906    if (!hasReferences)
1907    {
1908      for (ReplicaDescriptor replica : server.getReplicas())
1909      {
1910        if (replica.isReplicated())
1911        {
1912          for (Object o : replica.getReplicationServers())
1913          {
1914            if (getUninstallUserData().getReplicationServer().equalsIgnoreCase(
1915                (String)o))
1916            {
1917              hasReferences = true;
1918              break;
1919            }
1920          }
1921        }
1922        if (hasReferences)
1923        {
1924          break;
1925        }
1926      }
1927    }
1928
1929    if (!hasReferences)
1930    {
1931      logger.info(LocalizableMessage.raw("No references in: "+ server.getHostPort(true)));
1932    }
1933    if (hasReferences)
1934    {
1935      logger.info(LocalizableMessage.raw("Updating references in: "+ server.getHostPort(true)));
1936      notifyListeners(getFormattedWithPoints(
1937          INFO_PROGRESS_REMOVING_REFERENCES.get(server.getHostPort(true))));
1938      InitialLdapContext ctx = null;
1939      try
1940      {
1941        String dn = ADSContext.getAdministratorDN(
1942            getUninstallUserData().getAdminUID());
1943        String pwd = getUninstallUserData().getAdminPwd();
1944        ctx = getRemoteConnection(server, dn, pwd, getTrustManager(),
1945            getConnectTimeout(),
1946            new LinkedHashSet<PreferredConnection>());
1947
1948        // Update replication servers and domains.  If the domain
1949        // is an ADS, then remove it from there.
1950        removeReferences(ctx, server.getHostPort(true), serverADSProperties);
1951
1952        notifyListeners(getFormattedDoneWithLineBreak());
1953      }
1954      catch (ApplicationException ae)
1955      {
1956        errorOnRemoteOccurred = true;
1957        logger.info(LocalizableMessage.raw("Error updating replication references in: "+
1958            server.getHostPort(true), ae));
1959
1960        if (!getUninstallUserData().isForceOnError())
1961        {
1962          LocalizableMessage msg =
1963            ERR_UNINSTALL_ERROR_UPDATING_REMOTE_NO_FORCE.get(
1964              "--"+
1965              parser.getSecureArgsList().adminUidArg.getLongIdentifier(),
1966              "--"+OPTION_LONG_BINDPWD,
1967              "--"+OPTION_LONG_BINDPWD_FILE,
1968              "--"+parser.forceOnErrorArg.getLongIdentifier(),
1969              ae.getMessageObject());
1970          throw new ApplicationException(ae.getType(), msg, ae);
1971        }
1972        else
1973        {
1974          LocalizableMessage html = getFormattedError(ae, true);
1975          notifyListeners(html);
1976        }
1977      }
1978      finally
1979      {
1980        StaticUtils.close(ctx);
1981      }
1982    }
1983  }
1984
1985  /**
1986   * This method updates the replication in the remote server using the
1987   * provided InitialLdapContext.
1988   * It also tries to delete the server registration entry from the remote ADS
1989   * servers if the serverADSProperties object passed is not null.
1990   * @param ctx the connection to the remote server where we want to remove
1991   * references to the server that we are trying to uninstall.
1992   * @param serverDisplay an String representation that is used to identify
1993   * the remote server in the log messages we present to the user.
1994   * @param serverADSProperties the Map with the ADS properties of the server
1995   * that we are trying to uninstall.
1996   * @throws ApplicationException if an error occurs while updating the remote
1997   * OpenDS server configuration.
1998   */
1999  private void removeReferences(InitialLdapContext ctx, String serverDisplay,
2000      Map<ADSContext.ServerProperty, Object> serverADSProperties)
2001  throws ApplicationException
2002  {
2003    try
2004    {
2005      ManagementContext mCtx = LDAPManagementContext.createFromContext(
2006          JNDIDirContextAdaptor.adapt(ctx));
2007      RootCfgClient root = mCtx.getRootConfiguration();
2008      ReplicationSynchronizationProviderCfgClient sync =
2009        (ReplicationSynchronizationProviderCfgClient)
2010        root.getSynchronizationProvider("Multimaster Synchronization");
2011      if (sync.hasReplicationServer())
2012      {
2013        ReplicationServerCfgClient replicationServer =
2014          sync.getReplicationServer();
2015        Set<String> replServers = replicationServer.getReplicationServer();
2016        if (replServers != null)
2017        {
2018          String replServer = null;
2019          for (String o : replServers)
2020          {
2021            if (getUninstallUserData().getReplicationServer().equalsIgnoreCase(
2022                o))
2023            {
2024              replServer = o;
2025              break;
2026            }
2027          }
2028          if (replServer != null)
2029          {
2030            logger.info(LocalizableMessage.raw("Updating references in replication server on "+
2031                serverDisplay+"."));
2032            replServers.remove(replServer);
2033            if (!replServers.isEmpty())
2034            {
2035              replicationServer.setReplicationServer(replServers);
2036              replicationServer.commit();
2037            }
2038            else
2039            {
2040              sync.removeReplicationServer();
2041              sync.commit();
2042            }
2043          }
2044        }
2045      }
2046      String[] domainNames = sync.listReplicationDomains();
2047      if (domainNames != null)
2048      {
2049        for (String domainName : domainNames)
2050        {
2051          ReplicationDomainCfgClient domain =
2052            sync.getReplicationDomain(domainName);
2053          Set<String> replServers = domain.getReplicationServer();
2054          if (replServers != null)
2055          {
2056            String replServer = null;
2057            for (String o : replServers)
2058            {
2059              if (getUninstallUserData().getReplicationServer().
2060                  equalsIgnoreCase(o))
2061              {
2062                replServer = o;
2063                break;
2064              }
2065            }
2066            if (replServer != null)
2067            {
2068              logger.info(LocalizableMessage.raw("Updating references in domain " +
2069                  domain.getBaseDN()+" on " + serverDisplay + "."));
2070              replServers.remove(replServer);
2071              if (!replServers.isEmpty())
2072              {
2073                domain.setReplicationServer(replServers);
2074                domain.commit();
2075              }
2076              else
2077              {
2078                sync.removeReplicationDomain(domainName);
2079                sync.commit();
2080              }
2081            }
2082          }
2083        }
2084      }
2085    }
2086    catch (ManagedObjectNotFoundException monfe)
2087    {
2088      // It does not exist.
2089      logger.info(LocalizableMessage.raw("No synchronization found on "+ serverDisplay+".",
2090          monfe));
2091    }
2092    catch (Throwable t)
2093    {
2094      logger.warn(LocalizableMessage.raw(
2095          "Error removing references in replication server on "+
2096          serverDisplay+": "+t, t));
2097      LocalizableMessage errorMessage = INFO_ERROR_CONFIGURING_REMOTE_GENERIC.get(
2098              serverDisplay, t);
2099      throw new ApplicationException(
2100          ReturnCode.CONFIGURATION_ERROR, errorMessage, t);
2101    }
2102    ADSContext adsContext = new ADSContext(ctx);
2103
2104    try
2105    {
2106      if (adsContext.hasAdminData() && serverADSProperties != null)
2107      {
2108        logger.info(LocalizableMessage.raw("Unregistering server on ADS of server "+
2109            ConnectionUtils.getHostPort(ctx)+".  Properties: "+
2110            serverADSProperties));
2111        adsContext.unregisterServer(serverADSProperties);
2112      }
2113    }
2114    catch (ADSContextException ace)
2115    {
2116      if (ace.getError() !=
2117        ADSContextException.ErrorType.NOT_YET_REGISTERED)
2118      {
2119        throw new ApplicationException(
2120            ReturnCode.CONFIGURATION_ERROR,
2121            INFO_REMOTE_ADS_EXCEPTION.get(serverDisplay, ace),
2122            ace);
2123      }
2124      else
2125      {
2126        // Nothing to do: this may occur if the new server has been
2127        // unregistered on another server and the modification has
2128        // been already propagated by replication.
2129      }
2130    }
2131  }
2132
2133  /**
2134   * Tells whether this ServerDescriptor object represents the server that we
2135   * are trying to uninstall or not.
2136   * @param server the ServerDescriptor object to analyze.
2137   * @return <CODE>true</CODE> if the ServerDescriptor object represents the
2138   * server that we are trying to uninstall and <CODE>false</CODE> otherwise.
2139   */
2140  private boolean isServerToUninstall(ServerDescriptor server)
2141  {
2142    boolean isServerToUninstall = false;
2143    String path = (String)server.getAdsProperties().get(
2144        ADSContext.ServerProperty.INSTANCE_PATH);
2145    if (path == null)
2146    {
2147      // Compare the port of the URL we used.
2148      try
2149      {
2150        String usedUrl = getUninstallUserData().getLocalServerUrl();
2151        boolean isSecure = usedUrl.toLowerCase().startsWith("ldaps");
2152        URI uri = new URI(usedUrl);
2153        int port = uri.getPort();
2154        ServerDescriptor.ServerProperty property;
2155        if (isSecure)
2156        {
2157          property = ServerDescriptor.ServerProperty.ADMIN_PORT;
2158        }
2159        else
2160        {
2161          property = ServerDescriptor.ServerProperty.LDAP_PORT;
2162        }
2163        ArrayList<?> ports =
2164          (ArrayList<?>)server.getServerProperties().get(property);
2165        if (ports != null)
2166        {
2167          isServerToUninstall = ports.contains(port);
2168        }
2169        else
2170        {
2171          // This occurs if the instance could not be loaded.
2172          ADSContext.ServerProperty adsProperty;
2173          if (isSecure)
2174          {
2175            adsProperty = ADSContext.ServerProperty.ADMIN_PORT;
2176          }
2177          else
2178          {
2179            adsProperty = ADSContext.ServerProperty.LDAP_PORT;
2180          }
2181          String v = (String)server.getAdsProperties().get(adsProperty);
2182          if (v != null)
2183          {
2184            isServerToUninstall = v.equals(String.valueOf(port));
2185          }
2186        }
2187      }
2188      catch (Throwable t)
2189      {
2190        logger.warn(LocalizableMessage.raw("Failing checking the port: "+t, t));
2191      }
2192    }
2193    else
2194    {
2195      File f = new File(path);
2196      isServerToUninstall =
2197        f.equals(Installation.getLocal().getRootDirectory());
2198    }
2199
2200    if (isServerToUninstall)
2201    {
2202      // TODO: the host name comparison made here does not necessarily work in
2203      // all environments...
2204      String hostName = server.getHostName();
2205      boolean hostNameEquals =
2206        getUninstallUserData().getReferencedHostName().equals(hostName);
2207      try
2208      {
2209        InetAddress localAddress = InetAddress.getLocalHost();
2210        InetAddress[] addresses = InetAddress.getAllByName(hostName);
2211        for (int i=0; i<addresses.length && !hostNameEquals; i++)
2212        {
2213          hostNameEquals = localAddress.equals(addresses[i]);
2214        }
2215        if (!hostNameEquals)
2216        {
2217          hostNameEquals =
2218            localAddress.getHostName().equalsIgnoreCase(hostName) ||
2219            localAddress.getCanonicalHostName().equalsIgnoreCase(hostName);
2220        }
2221      }
2222      catch (Throwable t)
2223      {
2224        logger.warn(LocalizableMessage.raw("Failing checking host names: "+t, t));
2225      }
2226      isServerToUninstall = hostNameEquals;
2227    }
2228    return isServerToUninstall;
2229  }
2230
2231  /**
2232   * Returns the timeout to be used to connect in milliseconds.
2233   * @return the timeout to be used to connect in milliseconds.  Returns
2234   * {@code 0} if there is no timeout.
2235   */
2236  private int getConnectTimeout()
2237  {
2238    return getUserData().getConnectTimeout();
2239  }
2240}
2241