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