Uninstaller.java revision e79461c53adce17f83f30954f8a03d67bb761a1f
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2006-2010 Sun Microsystems, Inc.
* Portions Copyright 2014-2015 ForgeRock AS
*/
/**
* This class is in charge of performing the uninstallation of Open DS.
*/
private boolean runStarted;
private boolean errorOnRemoteOccurred;
private boolean errorDeletingOccurred;
private UninstallerArgumentParser parser;
private ApplicationException ue;
private Boolean isWindowsServiceEnabled;
private LoginDialog loginDialog;
private ProgressDialog startProgressDlg;
private UninstallData conf;
/** Default constructor. */
public Uninstaller()
{
super();
/* Do some initialization required to use the administration framework
* classes. Note that this is not done in the uninstaller code because
* when the basic configuration of the server is performed (using
* ConfigureDS) this initialization is done.
*/
// Bootstrap definition classes.
try
{
{
}
}
catch (Throwable t)
{
t));
}
// Switch off class name validation in client.
// Switch off attribute type name validation in client.
}
/** {@inheritDoc} */
public LocalizableMessage getFrameTitle() {
return Utils.getCustomizedObject("INFO_FRAME_UNINSTALL_TITLE", defaultVal, LocalizableMessage.class);
}
/** {@inheritDoc} */
public UserData createUserData() {
return data;
}
/** {@inheritDoc} */
public WizardStep getFirstWizardStep() {
return Step.CONFIRM_UNINSTALL;
}
/** {@inheritDoc} */
}
{
}
return nextStep;
}
/** {@inheritDoc} */
}
{
}
return prevStep;
}
/** {@inheritDoc} */
public WizardStep getFinishedStep() {
}
/** {@inheritDoc} */
public boolean finishOnLeft()
{
return false;
}
/** {@inheritDoc} */
return false;
}
/** {@inheritDoc} */
return false;
}
/** {@inheritDoc} */
}
/**
* Whether the provided wizard step allow to quit.
*
* @param step the wizard step
* @return true if the provided wizard step allow to quit, false otherwise
*/
}
/** {@inheritDoc} */
throw new IllegalStateException("Cannot click on next from progress step");
throw new IllegalStateException("Cannot click on next from review step");
throw new IllegalStateException("Cannot click on next from finished step");
}
}
/** {@inheritDoc} */
if (isFinished()
{
}
}
{
} else {
throw new IllegalStateException(
"Close only can be clicked on PROGRESS step");
}
}
/**
* Update the UserData object according to the content of the review
* panel.
*/
throws UserDataException {
// This is updated on the method handleTopologyCache
uud.setUpdateRemoteReplication(false);
for (Object v : s) {
}
for (Object v : s) {
}
!uud.getRemoveDatabases() &&
!uud.getRemoveBackups() &&
!uud.getRemoveLDIFs() &&
!uud.getRemoveLogs()) {
}
}
/** {@inheritDoc} */
throw new IllegalStateException(
"Cannot click on quit from progress step");
}
throw new IllegalStateException(
"Cannot click on quit from finished step");
}
}
/** {@inheritDoc} */
public LocalizableMessage getCloseButtonToolTip() {
return INFO_CLOSE_BUTTON_UNINSTALL_TOOLTIP.get();
}
/** {@inheritDoc} */
public LocalizableMessage getFinishButtonToolTip() {
return INFO_FINISH_BUTTON_UNINSTALL_TOOLTIP.get();
}
/** {@inheritDoc} */
public LocalizableMessage getFinishButtonLabel() {
return INFO_FINISH_BUTTON_UNINSTALL_LABEL.get();
}
/** {@inheritDoc} */
throw new IllegalStateException(
"Cannot click on previous from progress step");
}
throw new IllegalStateException(
"Cannot click on previous from finished step");
}
}
/** {@inheritDoc} */
final LocalizableMessage newLogDetail)
{
if (runStarted)
{
}
else
{
{
public void run()
{
{
}
}
});
}
}
/** {@inheritDoc} */
new BackgroundTask<UninstallData>() {
try {
}
catch (UserDataException uude) {
throw uude;
} catch (Throwable t) {
}
}
if (throwable instanceof UserDataException)
{
INFO_ERROR_TITLE.get());
}
else
{
throwable));
INFO_ERROR_TITLE.get());
}
} else {
conf = returnValue;
{
if (conf.isServerRunning())
{
if (qs.displayConfirmation(
.get()))
{
}
else if (qs.displayConfirmation(
{
getUserData().setStopServer(true);
} else {
getUserData().setStopServer(false);
}
}
else if (qs.displayConfirmation(
{
if (startWorked)
{
}
else
{
getUserData().setStopServer(false);
if (qs.displayConfirmation(
{
}
}
}
else
{
getUserData().setStopServer(false);
if (qs.displayConfirmation(
{
}
}
}
else if (!conf.isServerRunning())
{
getUserData().setStopServer(false);
if (qs.displayConfirmation(
{
}
}
else if (qs.displayConfirmation(
getUserData().setStopServer(true);
} else {
getUserData().setStopServer(false);
}
}
}
};
}
// Uninstaller is responsible for updating user data and launching
return false;
}
/** {@inheritDoc} */
// do nothing;
}
/** {@inheritDoc} */
WizardStep step) {
}
}
/** {@inheritDoc} */
{
}
/** {@inheritDoc} */
public String getInstallationPath() {
return getInstallPathFromClasspath();
}
/** {@inheritDoc} */
public String getInstancePath() {
}
/**
* Returns the ApplicationException that might occur during installation or
* <CODE>null</CODE> if no exception occurred.
*
* @return the ApplicationException that might occur during installation or
* <CODE>null</CODE> if no exception occurred.
*/
public ApplicationException getRunError() {
return ue;
}
/** {@inheritDoc} */
public ReturnCode getReturnCode() {
return null;
}
/**
* Initialize the different map used in this class.
*/
private void initMaps() {
boolean resourcesDefined =
boolean classesDefined =
if (resourcesDefined)
{
}
if (classesDefined)
{
}
if (isCli()) {
if (getUninstallUserData().getRemoveLibrariesAndTools()) {
if (isWindows()) {
} else {
}
} else {
}
} else if (getUninstallUserData().getRemoveLibrariesAndTools()) {
} else {
}
if (!isCli())
{
}
else
{
}
if (!isCli())
{
}
else
{
}
/*
* hmTime contains the relative time that takes for each task to be
* accomplished. For instance if stopping takes twice the time of
* deleting files, the value for downloading will be the double of the
* value for extracting.
*/
int totalTime = 0;
if (getUninstallUserData().getUpdateRemoteReplication()) {
}
if (getUserData().getStopServer()) {
}
if (isWindowsServiceEnabled()) {
}
}
}
int cumulatedTime = 0;
for (UninstallProgressStep s : steps) {
if (statusTime != null) {
}
}
}
/**
* Actually performs the uninstall in this thread. The thread is blocked.
*/
public void run() {
runStarted = true;
initMaps();
try {
if (!isCli()) {
}
boolean displaySeparator = false;
{
displaySeparator = true;
}
if (getUserData().getStopServer()) {
if (displaySeparator && isVerbose()) {
}
if (!isVerbose())
{
}
// In case of uninstall, the server stop has to run locally.
// In order to bypass the tools.properties mechanism, if any,
// we systematically add the --noPropertiesFile flag
// when we run the stop-ds command. This is done
// by setting the parameter "noPropertiesFile" to 'true'
// in the following call.
if (!isVerbose())
{
}
displaySeparator = true;
}
if (isWindowsServiceEnabled()) {
if (displaySeparator && isVerbose()) {
}
displaySeparator = true;
}
if (displaySeparator && isVerbose()) {
}
try
{
displaySeparator = true;
}
catch (ApplicationException ae)
{
{
errorDeletingOccurred = true;
}
else
{
throw ae;
}
}
}
if (displaySeparator && isVerbose()) {
}
try
{
displaySeparator = true;
}
catch (ApplicationException ae)
{
{
errorDeletingOccurred = true;
}
else
{
throw ae;
}
}
}
userData.getRemoveLDIFs() ||
}
if (somethingToDelete) {
try
{
}
catch (ApplicationException ae)
{
{
errorDeletingOccurred = true;
}
else
{
throw ae;
}
}
}
{
}
else if (errorDeletingOccurred)
{
}
else
{
}
if (isCli()) {
.toMessage());
} else {
}
} catch (ApplicationException ex) {
}
catch (Throwable t) {
ue = new ApplicationException(
}
if (!isCli()) {
}
}
/** {@inheritDoc} */
public ProgressStep getCurrentProgressStep() {
return status;
}
/**
* Returns an integer that specifies which percentage of the whole
* installation has been completed.
*
* @param step the UninstallProgressStep for which we want to get the ratio.
* @return an integer that specifies which percentage of the whole
* uninstallation has been completed.
*/
}
/**
* Returns an formatted representation of the summary for the specified
* UninstallProgressStep.
*
* @param step the UninstallProgressStep for which we want to get the summary.
* @return an formatted representation of the summary for the specified
* UninstallProgressStep.
*/
}
/** {@inheritDoc} */
public boolean isFinished() {
return getCurrentProgressStep() ==
|| getCurrentProgressStep() ==
|| getCurrentProgressStep() ==
|| getCurrentProgressStep() ==
}
/** {@inheritDoc} */
public boolean isCancellable() {
return false;
}
/** {@inheritDoc} */
public void cancel() {
// do nothing; not cancellable
}
/** {@inheritDoc} */
// Simulate a close button event
} else {
// Simulate a quit button event
}
}
/** {@inheritDoc} */
public ButtonName getInitialFocusButtonName() {
return ButtonName.FINISH;
}
/** {@inheritDoc} */
}
/** {@inheritDoc} */
return new ConfirmUninstallPanel(this, installStatus);
return new ProgressPanel(this);
return new FinishedPanel(this);
}
return null;
}
/**
* Deletes the external database files specified in the provided Set.
*
* @param dbFiles the database directories to be deleted.
* @throws ApplicationException if something goes wrong.
*/
throws ApplicationException {
if (isVerbose())
{
}
else
{
}
}
if (!isVerbose())
{
}
}
/**
* Deletes the external database files specified in the provided Set.
*
* @param logFiles the log files to be deleted.
* @throws ApplicationException if something goes wrong.
*/
throws ApplicationException {
if (isVerbose())
{
}
else
{
}
}
if (!isVerbose())
{
}
}
/**
* Deletes the files under the installation path.
*
* @throws ApplicationException if something goes wrong.
*/
throws ApplicationException {
if (isVerbose())
{
}
else
{
}
try
{
}
catch(Exception e)
{
}
try
{
} catch (Exception e)
{
}
{
}
if (installFiles == null)
{
}
else
if (instanceFiles == null)
{
}
else
{
// both installFiles and instanceFiles are not null
}
/* The following is done to have a moving progress bar when we delete
* the installation files.
*/
int totalRatio = 0;
int relativeRatio;
relativeRatio = 10;
} else
relativeRatio = 5;
} else
relativeRatio = 5;
} else
relativeRatio = 20;
} else
relativeRatio = 20;
relativeRatio = 50;
} else
relativeRatio = 30;
} else {
relativeRatio = 2;
}
} else {
}
}
{
int beforeRatio = minRatio +
}
}
if (!isVerbose())
{
}
}
/**
* Deletes everything below the specified file.
*
* @param file the path to be deleted.
* @throws ApplicationException if something goes wrong.
*/
}
/**
* Deletes everything below the specified file.
*
* @param file the path to be deleted.
* @param filter the filter of the files to know if the file can be deleted
* directly or not.
* @throws ApplicationException if something goes wrong.
*/
throws ApplicationException {
try
{
}
catch (Exception e)
{
}
}
} else {
}
} else {
{
}
}
}
} else {
}
}
} else {
}
}
/**
* Deletes the specified file.
*
* @param file the file to be deleted.
* @throws ApplicationException if something goes wrong.
*/
if (isVerbose())
{
if (isFile) {
} else {
}
}
boolean delete = false;
/*
* Sometimes the server keeps some locks on the files.
* This is dependent on the OS so there is no much we can do here.
*/
int nTries = 5;
if (!delete) {
try {
}
}
}
}
if (!delete) {
if (isFile) {
} else {
}
throw new ApplicationException(
}
if (isVerbose())
{
}
}
}
/**
* This class is used to get the files that are not binaries. This is
* required to know which are the files that can be deleted directly and which
* not.
*/
private class InstallationFilesToDeleteFilter implements FileFilter {
private boolean canDeleteResourcesDir =
private boolean canDeleteClassesDir =
/** {@inheritDoc} */
boolean[] uData = {
};
File[] parentFiles ;
try {
};
parentFiles = tmp ;
}
catch (Exception e)
{
return true;
}
boolean accept =
}
}
return accept;
}
}
private boolean isWindowsServiceEnabled() {
if (isWindowsServiceEnabled == null) {
}
return isWindowsServiceEnabled.booleanValue();
}
/** {@inheritDoc} */
public ApplicationTrustManager getTrustManager()
{
return getUninstallUserData().getTrustManager();
}
/**
* This methods disables this server as a Windows service.
*
* @throws ApplicationException if something goes wrong.
*/
protected void disableWindowsService() throws ApplicationException {
switch (code) {
case SERVICE_DISABLE_SUCCESS:
break;
case SERVICE_ALREADY_DISABLED:
break;
default:
}
}
private UninstallUserData getUninstallUserData() {
return (UninstallUserData) getUserData();
}
/**
* Tries to start the server and launches a progress dialog. This method
* assumes that is being called from the event thread.
* @return <CODE>true</CODE> if the server could be started and <CODE>
* false</CODE> otherwise.
* @param frame the JFrame to be used as parent of the progress dialog.
*/
{
{
public void run()
{
try
{
final boolean isServerRunning =
{
public void run()
{
if (isServerRunning)
{
}
else
{
}
}
});
}
catch (Throwable t)
{
}
}
});
t.start();
startProgressDlg.setModal(true);
startProgressDlg.setVisible(true);
return returnValue[0];
}
/**
* This method displays a login dialog message, asking the user to provide
* authentication to retrieve information from the ADS and update the
* remote servers. Then it tries to connect to the remote servers.
*
* @param qs the QuickSetup object.
*/
{
if (loginDialog == null)
{
getTrustManager(), getConnectTimeout());
loginDialog.pack();
}
loginDialog.setModal(true);
loginDialog.setVisible(true);
if (!loginDialog.isCanceled())
{
try
{
}
catch (NamingException ne)
{
}
{
{
getTrustManager(), getConnectTimeout());
return cache;
}
{
if (throwable instanceof TopologyCacheException)
{
INFO_ERROR_TITLE.get());
}
else
{
INFO_ERROR_TITLE.get());
}
}
else
{
}
}
};
}
else if (qs.displayConfirmation(
{
getUserData().setStopServer(true);
} else {
getUserData().setStopServer(false);
}
}
/**
* Method that interacts with the user depending on what errors where
* encountered in the TopologyCache object. This method assumes that the
* TopologyCache has been reloaded.
* Note: this method assumes that is being called from the event thread.
* @param qs the QuickSetup object for the application.
* @param cache the TopologyCache.
*/
{
boolean stopProcessing = false;
/* Analyze if we had any exception while loading servers. For the moment
* only throw the exception found if the user did not provide the
* Administrator DN and this caused a problem authenticating in one server
* or if there is a certificate problem.
*/
{
if (e != null)
{
exceptions.add(e);
}
}
/* Check the exceptions and see if we throw them or not. */
for (TopologyCacheException e : exceptions)
{
if (stopProcessing)
{
break;
}
switch (e.getType())
{
case NOT_GLOBAL_ADMINISTRATOR:
stopProcessing = true;
break;
if (isCertificateException(e.getCause()))
{
if (e.getTrustManager() != null)
{
}
{
String h;
int p;
try
{
}
catch (Throwable t)
{
"Error parsing ldap url of TopologyCacheException.", t));
p = -1;
}
INFO_CERTIFICATE_EXCEPTION.get(h, p),
e.getCause(), h, p,
stopProcessing = true;
}
}
}
}
{
}
if (!stopProcessing)
{
}
if (!stopProcessing)
{
// Launch everything
getUserData().setStopServer(true);
}
}
private UserDataCertificateException.Type getCertificateExceptionType(ApplicationTrustManager.Cause cause)
{
{
}
{
}
else
{
return null;
}
}
/**
* Displays a dialog asking the user to accept a certificate if the user
* accepts it, we update the trust manager and call again to the method that
* handles the action of clicking on "Finish".
* This method assumes that we are being called from the event thread.
*/
{
dlg.setVisible(true);
{
{
new BackgroundTask<TopologyCache>()
{
{
return cache;
}
{
if (throwable instanceof TopologyCacheException)
{
INFO_ERROR_TITLE.get());
}
else
{
INFO_ERROR_TITLE.get());
}
}
else
{
}
}
};
}
else
{
{
"The chain is null for the UserDataCertificateException"));
}
{
"The auth type is null for the UserDataCertificateException"));
}
{
"The host is null for the UserDataCertificateException"));
}
}
}
if (dlg.getUserAnswer() ==
{
{
try
{
}
catch (Throwable t)
{
}
}
}
}
/**
* This method updates the replication in the remote servers. It does
* throw ApplicationException if we are working on the force on error mode.
* It also tries to delete the server registration entry from the remote ADS
* servers.
* @throws ApplicationException if we are not working on force on error mode
* and there is an error.
*/
private void removeRemoteServerReferences() throws ApplicationException
{
{
if (isServerToUninstall(server))
{
break;
}
}
if (serverADSProperties == null)
{
"uninstall could not be found."));
}
{
{
}
}
}
/**
* This method updates the replication in the remote server represented by
* a given ServerProperty object.
* It also tries to delete the server registration entry from the remote ADS
* servers if the serverADSProperties object passed is not null.
* @param server the ServerDescriptor object representing the server where
* we want to remove references to the server that we are trying to uninstall.
* @param serverADSProperties the Map with the ADS properties of the server
* that we are trying to uninstall.
* @throws ApplicationException if we are not working on force on error mode
* and there is an error.
*/
throws ApplicationException
{
/* First check if the server must be updated based in the contents of the
* ServerDescriptor object. */
boolean hasReferences = false;
{
if (replicationServers != null)
{
for (Object o : replicationServers)
{
(String)o))
{
hasReferences = true;
break;
}
}
}
}
if (!hasReferences)
{
{
if (replica.isReplicated())
{
{
(String)o))
{
hasReferences = true;
break;
}
}
}
if (hasReferences)
{
break;
}
}
}
if (!hasReferences)
{
}
if (hasReferences)
{
try
{
new LinkedHashSet<PreferredConnection>());
// Update replication servers and domains. If the domain
// is an ADS, then remove it from there.
}
catch (ApplicationException ae)
{
errorOnRemoteOccurred = true;
if (!getUninstallUserData().isForceOnError())
{
"--"+
"--"+OPTION_LONG_BINDPWD,
"--"+OPTION_LONG_BINDPWD_FILE,
ae.getMessageObject());
}
else
{
}
}
finally
{
}
}
}
/**
* This method updates the replication in the remote server using the
* provided InitialLdapContext.
* It also tries to delete the server registration entry from the remote ADS
* servers if the serverADSProperties object passed is not null.
* @param ctx the connection to the remote server where we want to remove
* references to the server that we are trying to uninstall.
* @param serverDisplay an String representation that is used to identify
* the remote server in the log messages we present to the user.
* @param serverADSProperties the Map with the ADS properties of the server
* that we are trying to uninstall.
* @throws ApplicationException if an error occurs while updating the remote
* OpenDS server configuration.
*/
throws ApplicationException
{
try
{
if (sync.hasReplicationServer())
{
if (replServers != null)
{
for (String o : replServers)
{
o))
{
replServer = o;
break;
}
}
if (replServer != null)
{
serverDisplay+"."));
{
}
else
{
}
}
}
}
if (domainNames != null)
{
{
if (replServers != null)
{
for (String o : replServers)
{
if (getUninstallUserData().getReplicationServer().
equalsIgnoreCase(o))
{
replServer = o;
break;
}
}
if (replServer != null)
{
{
}
else
{
}
}
}
}
}
}
catch (ManagedObjectNotFoundException monfe)
{
// It does not exist.
monfe));
}
catch (Throwable t)
{
"Error removing references in replication server on "+
serverDisplay+": "+t, t));
serverDisplay, t);
throw new ApplicationException(
}
try
{
{
}
}
catch (ADSContextException ace)
{
{
throw new ApplicationException(
ace);
}
else
{
// Nothing to do: this may occur if the new server has been
// unregistered on another server and the modification has
// been already propagated by replication.
}
}
}
/**
* Tells whether this ServerDescriptor object represents the server that we
* are trying to uninstall or not.
* @param server the ServerDescriptor object to analyze.
* @return <CODE>true</CODE> if the ServerDescriptor object represents the
* server that we are trying to uninstall and <CODE>false</CODE> otherwise.
*/
{
boolean isServerToUninstall = false;
{
// Compare the port of the URL we used.
try
{
if (isSecure)
{
}
else
{
}
{
}
else
{
// This occurs if the instance could not be loaded.
if (isSecure)
{
}
else
{
}
if (v != null)
{
}
}
}
catch (Throwable t)
{
}
}
else
{
}
if (isServerToUninstall)
{
// TODO: the host name comparison made here does not necessarily work in
// all environments...
boolean hostNameEquals =
try
{
{
}
if (!hostNameEquals)
{
}
}
catch (Throwable t)
{
}
}
return isServerToUninstall;
}
/**
* Returns the timeout to be used to connect in milliseconds.
* @return the timeout to be used to connect in milliseconds. Returns
* {@code 0} if there is no timeout.
*/
private int getConnectTimeout()
{
return getUserData().getConnectTimeout();
}
}