/* * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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. */ package org.opends.guitools.uninstaller; import org.opends.server.admin.client.cli.DsFrameworkCliReturnCode; import org.opends.server.admin.client.cli.SecureConnectionCliArgs; import org.opends.admin.ads.ADSContext; import org.opends.admin.ads.ServerDescriptor; import org.opends.admin.ads.TopologyCache; import org.opends.admin.ads.TopologyCacheException; import org.opends.admin.ads.util.ApplicationTrustManager; import org.opends.guitools.controlpanel.datamodel.ConnectionProtocolPolicy; import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo; import org.opends.messages.Message; import org.opends.messages.MessageBuilder; import static org.opends.messages.AdminToolMessages.*; import static org.opends.messages.QuickSetupMessages.*; import org.opends.quicksetup.*; import org.opends.quicksetup.event.ProgressUpdateEvent; import org.opends.quicksetup.event.ProgressUpdateListener; import org.opends.quicksetup.util.PlainTextProgressMessageFormatter; import org.opends.quicksetup.util.ServerController; import org.opends.quicksetup.util.Utils; import org.opends.server.tools.ClientException; import org.opends.server.tools.ToolConstants; import org.opends.server.tools.dsconfig.LDAPManagementContextFactory; import org.opends.server.util.args.ArgumentException; import org.opends.server.util.cli.CLIException; import org.opends.server.util.cli.ConsoleApplication; import org.opends.server.util.cli.LDAPConnectionConsoleInteraction; import org.opends.server.util.cli.Menu; import org.opends.server.util.cli.MenuBuilder; import org.opends.server.util.cli.MenuResult; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; import java.util.Collections; import java.util.logging.Level; import java.util.logging.Logger; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.net.URI; import javax.naming.NamingException; import javax.naming.ldap.InitialLdapContext; import javax.net.ssl.TrustManager; /** * The class used to provide some CLI interface in the uninstall. * * This class basically is in charge of parsing the data provided by the user * in the command line and displaying messages asking the user for information. * * Once the user has provided all the required information it calls Uninstaller * and launches it. * */ public class UninstallCliHelper extends ConsoleApplication { static private final Logger LOG = Logger.getLogger(UninstallCliHelper.class.getName()); private UninstallerArgumentParser parser; private boolean forceNonInteractive; private LDAPConnectionConsoleInteraction ci = null; private ControlPanelInfo info; // This CLI is always using the administration connector with SSL private final boolean alwaysSSL = true; private boolean useSSL = true; private boolean useStartTLS = false; /** * Default constructor. */ public UninstallCliHelper() { super(System.in, System.out, System.err); } /** * Creates a UserData based in the arguments provided. It asks * user for additional information if what is provided in the arguments is not * enough. * @param args the ArgumentParser with the allowed arguments of the command * line. The code assumes that the arguments have already been parsed. * @param rawArguments the arguments provided in the command line. * @return the UserData object with what the user wants to uninstall * and null if the user cancels the uninstallation. * @throws UserDataException if there is an error with the data * in the arguments. * @throws ApplicationException if there is an error processing data in * non-interactive mode and an error must be thrown (not in force on error * mode). */ public UninstallUserData createUserData(UninstallerArgumentParser args, String[] rawArguments) throws UserDataException, ApplicationException { parser = args; UninstallUserData userData = new UninstallUserData(); try { boolean isInteractive; boolean isQuiet; boolean isVerbose; boolean isCanceled = false; /* Step 1: analyze the arguments. */ isInteractive = args.isInteractive(); isQuiet = args.isQuiet(); isVerbose = args.isVerbose(); userData.setQuiet(isQuiet); userData.setVerbose(isVerbose); userData.setForceOnError(args.isForceOnError()); userData.setTrustManager(args.getTrustManager()); userData.setConnectTimeout(getConnectTimeout()); /* * Step 2: check that the provided parameters are compatible. */ MessageBuilder buf = new MessageBuilder(); int v = args.validateGlobalOptions(buf); if (v != DsFrameworkCliReturnCode.SUCCESSFUL_NOP.getReturnCode()) { throw new UserDataException(null, buf.toMessage()); } /* Step 3: If this is an interactive uninstall ask for confirmation to * delete the different parts of the installation if the user did not * specify anything to delete. If we are not in interactive mode * check that the user specified something to be deleted. */ Set<String> outsideDbs; Set<String> outsideLogs; Configuration config = Installation.getLocal().getCurrentConfiguration(); try { outsideDbs = config.getOutsideDbs(); } catch (IOException ioe) { outsideDbs = Collections.emptySet(); LOG.log(Level.INFO, "error determining outside databases", ioe); } try { outsideLogs = config.getOutsideLogs(); } catch (IOException ioe) { outsideLogs = Collections.emptySet(); LOG.log(Level.INFO, "error determining outside logs", ioe); } boolean somethingSpecifiedToDelete = args.removeAll() || args.removeBackupFiles() || args.removeDatabases() || args.removeLDIFFiles() || args.removeConfigurationFiles() || args.removeLogFiles() || args.removeServerLibraries(); if (somethingSpecifiedToDelete) { userData.setRemoveBackups(args.removeAll() || args.removeBackupFiles()); userData.setRemoveConfigurationAndSchema(args.removeAll() || args.removeConfigurationFiles()); userData.setRemoveDatabases(args.removeAll() || args.removeDatabases()); userData.setRemoveLDIFs(args.removeAll() || args.removeLDIFFiles()); userData.setRemoveLibrariesAndTools(args.removeAll() || args.removeServerLibraries()); userData.setRemoveLogs(args.removeAll() || args.removeLogFiles()); userData.setExternalDbsToRemove(outsideDbs); userData.setExternalLogsToRemove(outsideLogs); } else { if (!isInteractive) { throw new UserDataException(null, ERR_CLI_UNINSTALL_NOTHING_TO_BE_UNINSTALLED_NON_INTERACTIVE.get()); } else { isCanceled = askWhatToDelete(userData, outsideDbs, outsideLogs); } } String adminUid = args.getAdministratorUID(); if ((adminUid == null) && !args.isInteractive()) { adminUid = args.getDefaultAdministratorUID(); } userData.setAdminUID(adminUid); userData.setAdminPwd(args.getBindPassword()); String referencedHostName = args.getReferencedHostName(); if ((referencedHostName == null) && !args.isInteractive()) { referencedHostName = args.getDefaultReferencedHostName(); } try { UninstallData d = new UninstallData(Installation.getLocal()); userData.setReplicationServer( referencedHostName+":"+d.getReplicationServerPort()); } catch (Throwable t) { LOG.log(Level.SEVERE, "Could not create UninstallData: "+t, t); userData.setReplicationServer( referencedHostName+":8989"); } info = ControlPanelInfo.getInstance(); info.setTrustManager(userData.getTrustManager()); info.setConnectTimeout(getConnectTimeout()); info.regenerateDescriptor(); info.setConnectionPolicy(ConnectionProtocolPolicy.USE_ADMIN); String adminConnectorUrl = info.getAdminConnectorURL(); if (adminConnectorUrl == null) { LOG.log(Level.WARNING, "Error retrieving a valid LDAP URL in conf file."); if (!parser.isInteractive()) { Message msg = ERR_COULD_NOT_FIND_VALID_LDAPURL.get(); throw new ApplicationException(ReturnCode.APPLICATION_ERROR, msg, null); } } userData.setLocalServerUrl(adminConnectorUrl); userData.setReferencedHostName(referencedHostName); /* * Step 4: check if server is running. Depending if it is running and the * OS we are running, ask for authentication information. */ if (!isCanceled) { isCanceled = checkServerState(userData); } if (isCanceled && !userData.isForceOnError()) { LOG.log(Level.INFO, "User cancelled uninstall."); userData = null; } if ((userData != null) && !args.isQuiet()) { println(); } } catch (Throwable t) { LOG.log(Level.WARNING, "Exception: "+t, t); if (t instanceof UserDataException) { throw (UserDataException)t; } else if (t instanceof ApplicationException) { throw (ApplicationException)t; } else { throw new IllegalStateException("Unexpected error: "+t, t); } } LOG.log(Level.INFO, "Successfully created user data"); return userData; } /** * Commodity method used to ask the user to confirm the deletion of certain * parts of the server. It updates the provided UserData object * accordingly. Returns <CODE>true</CODE> if the user cancels and <CODE> * false</CODE> otherwise. * @param userData the UserData object to be updated. * @param outsideDbs the set of relative paths of databases located outside * the installation path of the server. * @param outsideLogs the set of relative paths of log files located outside * the installation path of the server. * @return <CODE>true</CODE> if the user cancels and <CODE>false</CODE> * otherwise. */ private boolean askWhatToDelete(UninstallUserData userData, Set<String> outsideDbs, Set<String> outsideLogs) throws UserDataException { boolean cancelled = false; final int REMOVE_ALL = 1; final int SPECIFY_TO_REMOVE = 2; int[] indexes = {REMOVE_ALL, SPECIFY_TO_REMOVE}; Message[] msgs = new Message[] { INFO_CLI_UNINSTALL_REMOVE_ALL.get(), INFO_CLI_UNINSTALL_SPECIFY_WHAT_REMOVE.get() }; MenuBuilder<Integer> builder = new MenuBuilder<Integer>(this); builder.setPrompt(INFO_CLI_UNINSTALL_WHAT_TO_DELETE.get()); for (int i=0; i<indexes.length; i++) { builder.addNumberedOption(msgs[i], MenuResult.success(indexes[i])); } builder.addQuitOption(); builder.setDefault(Message.raw(String.valueOf(REMOVE_ALL)), MenuResult.success(REMOVE_ALL)); builder.setMaxTries(CONFIRMATION_MAX_TRIES); Menu<Integer> menu = builder.toMenu(); int choice; try { MenuResult<Integer> m = menu.run(); if (m.isSuccess()) { choice = m.getValue(); } else if (m.isQuit()) { choice = REMOVE_ALL; cancelled = true; } else { // Should never happen. throw new RuntimeException(); } } catch (CLIException ce) { LOG.log(Level.WARNING, "Error reading input: "+ce, ce); throw new UserDataException(null, ce.getMessageObject(), ce); } if (cancelled) { // Nothing to do } else if (choice == REMOVE_ALL) { userData.setRemoveBackups(true); userData.setRemoveConfigurationAndSchema(true); userData.setRemoveDatabases(true); userData.setRemoveLDIFs(true); userData.setRemoveLibrariesAndTools(true); userData.setRemoveLogs(true); userData.setExternalDbsToRemove(outsideDbs); userData.setExternalLogsToRemove(outsideLogs); } else { boolean somethingSelected = false; while (!somethingSelected && !cancelled) { println(); // Ask for confirmation for the different items msgs = new Message [] { INFO_CLI_UNINSTALL_CONFIRM_LIBRARIES_BINARIES.get(), INFO_CLI_UNINSTALL_CONFIRM_DATABASES.get(), INFO_CLI_UNINSTALL_CONFIRM_LOGS.get(), INFO_CLI_UNINSTALL_CONFIRM_CONFIGURATION_SCHEMA.get(), INFO_CLI_UNINSTALL_CONFIRM_BACKUPS.get(), INFO_CLI_UNINSTALL_CONFIRM_LDIFS.get(), INFO_CLI_UNINSTALL_CONFIRM_OUTSIDEDBS.get( Utils.getStringFromCollection(outsideDbs, Constants.LINE_SEPARATOR)), INFO_CLI_UNINSTALL_CONFIRM_OUTSIDELOGS.get( Utils.getStringFromCollection(outsideLogs, Constants.LINE_SEPARATOR) ) }; boolean[] answers = new boolean[msgs.length]; try { for (int i=0; i<msgs.length; i++) { boolean ignore = ((i == 6) && (outsideDbs.size() == 0)) || ((i == 7) && (outsideLogs.size() == 0)); if (!ignore) { answers[i] = askConfirmation(msgs[i], true, LOG); } else { answers[i] = false; } } } catch (CLIException ce) { throw new UserDataException(null, ce.getMessageObject(), ce); } if (!cancelled) { for (int i=0; i<answers.length; i++) { switch (i) { case 0: userData.setRemoveLibrariesAndTools(answers[i]); break; case 1: userData.setRemoveDatabases(answers[i]); break; case 2: userData.setRemoveLogs(answers[i]); break; case 3: userData.setRemoveConfigurationAndSchema(answers[i]); break; case 4: userData.setRemoveBackups(answers[i]); break; case 5: userData.setRemoveLDIFs(answers[i]); break; case 6: if (answers[i]) { userData.setExternalDbsToRemove(outsideDbs); } break; case 7: if (answers[i]) { userData.setExternalLogsToRemove(outsideLogs); } break; } } if ((userData.getExternalDbsToRemove().size() == 0) && (userData.getExternalLogsToRemove().size() == 0) && !userData.getRemoveLibrariesAndTools() && !userData.getRemoveDatabases() && !userData.getRemoveConfigurationAndSchema() && !userData.getRemoveBackups() && !userData.getRemoveLDIFs() && !userData.getRemoveLogs()) { somethingSelected = false; println(); printErrorMessage( ERR_CLI_UNINSTALL_NOTHING_TO_BE_UNINSTALLED.get()); } else { somethingSelected = true; } } } } return cancelled; } /** * Commodity method used to ask the user (when necessary) if the server must * be stopped or not. It also prompts (if required) for authentication. * @param userData the UserData object to be updated with the * authentication of the user. * @return <CODE>true</CODE> if the user wants to continue with uninstall and * <CODE>false</CODE> otherwise. * @throws UserDataException if there is a problem with the data * provided by the user (in the particular case where we are on * non-interactive uninstall and some data is missing or not valid). * @throws ApplicationException if there is an error processing data in * non-interactive mode and an error must be thrown (not in force on error * mode). */ private boolean checkServerState(UninstallUserData userData) throws UserDataException, ApplicationException { boolean cancelled = false; boolean interactive = parser.isInteractive(); boolean forceOnError = parser.isForceOnError(); UninstallData conf = null; try { conf = new UninstallData(Installation.getLocal()); } catch (Throwable t) { LOG.log(Level.WARNING, "Error processing task: "+t, t); throw new UserDataException(Step.CONFIRM_UNINSTALL, Utils.getThrowableMsg(INFO_BUG_MSG.get(), t)); } LOG.log(Level.INFO, "interactive: "+interactive); LOG.log(Level.INFO, "forceOnError: "+forceOnError); LOG.log(Level.INFO, "conf.isADS(): "+conf.isADS()); LOG.log(Level.INFO, "conf.isReplicationServer(): "+ conf.isReplicationServer()); LOG.log(Level.INFO, "conf.isServerRunning(): "+conf.isServerRunning()); if (conf.isADS() && conf.isReplicationServer()) { if (conf.isServerRunning()) { if (interactive) { try { if (confirmToUpdateRemote()) { println(); cancelled = !askForAuthenticationIfNeeded(userData); if (cancelled) { /* Ask for confirmation to stop server */ println(); cancelled = !confirmToStopServer(); } else { cancelled = !updateUserUninstallDataWithRemoteServers(userData); if (cancelled) { println(); /* Ask for confirmation to stop server */ cancelled = !confirmToStopServer(); } } } else { println(); /* Ask for confirmation to stop server */ cancelled = !confirmToStopServer(); } } catch (CLIException ce) { throw new UserDataException(null, ce.getMessageObject(), ce); } } else { boolean errorWithRemote = !updateUserUninstallDataWithRemoteServers(userData); cancelled = errorWithRemote && !parser.isForceOnError(); LOG.log(Level.INFO, "Non interactive mode. errorWithRemote: "+ errorWithRemote); } } else { if (interactive) { println(); try { if (confirmToUpdateRemoteAndStart()) { boolean startWorked = startServer(userData.isQuiet()); // Ask for authentication if needed, etc. if (startWorked) { cancelled = !askForAuthenticationIfNeeded(userData); if (cancelled) { println(); /* Ask for confirmation to stop server */ cancelled = !confirmToStopServer(); } else { cancelled = !updateUserUninstallDataWithRemoteServers(userData); if (cancelled) { println(); /* Ask for confirmation to stop server */ cancelled = !confirmToStopServer(); } } userData.setStopServer(true); } else { userData.setStopServer(false); println(); /* Ask for confirmation to delete files */ cancelled = !confirmDeleteFiles(); } } else { println(); /* Ask for confirmation to delete files */ cancelled = !confirmDeleteFiles(); } } catch (CLIException ce) { throw new UserDataException(null, ce.getMessageObject(), ce); } } else { boolean startWorked = startServer(userData.isQuiet()); // Ask for authentication if needed, etc. if (startWorked) { userData.setStopServer(true); boolean errorWithRemote = !updateUserUninstallDataWithRemoteServers(userData); cancelled = errorWithRemote && !parser.isForceOnError(); } else { cancelled = !forceOnError; userData.setStopServer(false); } } } if (!cancelled || parser.isForceOnError()) { /* During all the confirmations, the server might be stopped. */ userData.setStopServer( Installation.getLocal().getStatus().isServerRunning()); LOG.log(Level.INFO, "Must stop the server after confirmations? "+ userData.getStopServer()); } } else { if (conf.isServerRunning()) { try { if (interactive) { println(); /* Ask for confirmation to stop server */ cancelled = !confirmToStopServer(); } if (!cancelled) { /* During all the confirmations, the server might be stopped. */ userData.setStopServer( Installation.getLocal().getStatus().isServerRunning()); LOG.log(Level.INFO, "Must stop the server after confirmations? "+ userData.getStopServer()); } } catch (CLIException ce) { throw new UserDataException(null, ce.getMessageObject(), ce); } } else { userData.setStopServer(false); if (interactive) { println(); /* Ask for confirmation to delete files */ try { cancelled = !confirmDeleteFiles(); } catch (CLIException ce) { throw new UserDataException(null, ce.getMessageObject(), ce); } } } } LOG.log(Level.INFO, "cancelled: "+cancelled); return cancelled; } /** * Ask for confirmation to stop server. * @return <CODE>true</CODE> if the user wants to continue and stop the * server. <CODE>false</CODE> otherwise. * @throws CLIException if the user reached the confirmation limit. */ private boolean confirmToStopServer() throws CLIException { return askConfirmation(INFO_CLI_UNINSTALL_CONFIRM_STOP.get(), true, LOG); } /** * Ask for confirmation to delete files. * @return <CODE>true</CODE> if the user wants to continue and delete the * files. <CODE>false</CODE> otherwise. * @throws CLIException if the user reached the confirmation limit. */ private boolean confirmDeleteFiles() throws CLIException { return askConfirmation(INFO_CLI_UNINSTALL_CONFIRM_DELETE_FILES.get(), true, LOG); } /** * Ask for confirmation to update configuration on remote servers. * @return <CODE>true</CODE> if the user wants to continue and stop the * server. <CODE>false</CODE> otherwise. * @throws CLIException if the user reached the confirmation limit. */ private boolean confirmToUpdateRemote() throws CLIException { return askConfirmation(INFO_CLI_UNINSTALL_CONFIRM_UPDATE_REMOTE.get(), true, LOG); } /** * Ask for confirmation to update configuration on remote servers. * @return <CODE>true</CODE> if the user wants to continue and stop the * server. <CODE>false</CODE> otherwise. * @throws CLIException if the user reached the confirmation limit. */ private boolean confirmToUpdateRemoteAndStart() throws CLIException { return askConfirmation( INFO_CLI_UNINSTALL_CONFIRM_UPDATE_REMOTE_AND_START.get(), true, LOG); } /** * Ask for confirmation to provide again authentication. * @return <CODE>true</CODE> if the user wants to provide authentication * again. <CODE>false</CODE> otherwise. * @throws CLIException if the user reached the confirmation limit. */ private boolean promptToProvideAuthenticationAgain() throws CLIException { return askConfirmation( INFO_UNINSTALL_CONFIRM_PROVIDE_AUTHENTICATION_AGAIN.get(), true, LOG); } /** * Ask for data required to update configuration on remote servers. If * all the data is provided and validated, we assume that the user wants * to update the remote servers. * @return <CODE>true</CODE> if the user wants to continue and update the * remote servers. <CODE>false</CODE> otherwise. * @throws UserDataException if there is a problem with the information * provided by the user. * @throws ApplicationException if there is an error processing data. */ private boolean askForAuthenticationIfNeeded(UninstallUserData userData) throws UserDataException, ApplicationException { boolean accepted = true; String uid = userData.getAdminUID(); String pwd = userData.getAdminPwd(); boolean couldConnect = false; while (!couldConnect && accepted) { // This is done because we do not need to ask the user about these // parameters. If we force their presence the class // LDAPConnectionConsoleInteraction will not prompt the user for // them. SecureConnectionCliArgs secureArgsList = parser.getSecureArgsList(); secureArgsList.hostNameArg.setPresent(true); secureArgsList.portArg.setPresent(true); secureArgsList.hostNameArg.clearValues(); secureArgsList.hostNameArg.addValue( secureArgsList.hostNameArg.getDefaultValue()); secureArgsList.portArg.clearValues(); secureArgsList.portArg.addValue( secureArgsList.portArg.getDefaultValue()); secureArgsList.bindDnArg.clearValues(); if (uid != null) { secureArgsList.bindDnArg.addValue(ADSContext.getAdministratorDN(uid)); secureArgsList.bindDnArg.setPresent(true); } else { secureArgsList.bindDnArg.setPresent(false); } secureArgsList.bindPasswordArg.clearValues(); if (pwd != null) { secureArgsList.bindPasswordArg.addValue(pwd); secureArgsList.bindPasswordArg.setPresent(true); } else { secureArgsList.bindPasswordArg.setPresent(false); } if (ci == null) { ci = new LDAPConnectionConsoleInteraction(this, parser.getSecureArgsList()); ci.setDisplayLdapIfSecureParameters(true); } InitialLdapContext ctx = null; try { ci.run(true, false); userData.setAdminUID(ci.getAdministratorUID()); userData.setAdminPwd(ci.getBindPassword()); info.setConnectionPolicy(ConnectionProtocolPolicy.USE_ADMIN); String adminConnectorUrl = info.getAdminConnectorURL(); if (adminConnectorUrl == null) { LOG.log(Level.WARNING, "Error retrieving a valid Administration Connector URL in conf file."); Message msg = ERR_COULD_NOT_FIND_VALID_LDAPURL.get(); throw new ApplicationException(ReturnCode.APPLICATION_ERROR, msg, null); } try { URI uri = new URI(adminConnectorUrl); int port = uri.getPort(); secureArgsList.portArg.clearValues(); secureArgsList.portArg.addValue(String.valueOf(port)); ci.setPortNumber(port); } catch (Throwable t) { LOG.log(Level.SEVERE, "Error parsing url: "+adminConnectorUrl); } LDAPManagementContextFactory factory = new LDAPManagementContextFactory(alwaysSSL); factory.getManagementContext(this, ci); updateTrustManager(userData, ci); info.setConnectionPolicy(ConnectionProtocolPolicy.USE_ADMIN); adminConnectorUrl = info.getAdminConnectorURL(); if (adminConnectorUrl == null) { LOG.log(Level.WARNING, "Error retrieving a valid Administration Connector URL in conf file."); Message msg = ERR_COULD_NOT_FIND_VALID_LDAPURL.get(); throw new ApplicationException(ReturnCode.APPLICATION_ERROR, msg, null); } userData.setLocalServerUrl(adminConnectorUrl); couldConnect = true; } catch (ArgumentException e) { printErrorMessage(e.getMessageObject()); println(); } catch (ClientException e) { printErrorMessage(e.getMessageObject()); println(); } finally { if (ctx != null) { try { ctx.close(); } catch (Throwable t) { LOG.log(Level.INFO, "Error closing connection: "+t, t); } } } if (!couldConnect) { try { accepted = promptToProvideAuthenticationAgain(); if (accepted) { uid = null; pwd = null; } } catch (CLIException ce) { throw new UserDataException(null, ce.getMessageObject(), ce); } } } if (accepted) { String referencedHostName = parser.getReferencedHostName(); while (referencedHostName == null) { println(); referencedHostName = askForReferencedHostName(userData.getHostName()); } try { UninstallData d = new UninstallData(Installation.getLocal()); userData.setReplicationServer( referencedHostName+":"+d.getReplicationServerPort()); userData.setReferencedHostName(referencedHostName); } catch (Throwable t) { LOG.log(Level.SEVERE, "Could not create UninstallData: "+t, t); } } userData.setUpdateRemoteReplication(accepted); return accepted; } private String askForReferencedHostName(String defaultHostName) { String s = defaultHostName; try { s = readInput(INFO_UNINSTALL_CLI_REFERENCED_HOSTNAME_PROMPT.get(), defaultHostName); } catch (CLIException ce) { LOG.log(Level.WARNING, "Error reading input: "+ce, ce); } return s; } private boolean startServer(boolean supressOutput) { LOG.log(Level.INFO, "startServer, supressOutput: "+supressOutput); boolean serverStarted = false; Application application = new Application() { /** * {@inheritDoc} */ public String getInstallationPath() { return Installation.getLocal().getRootDirectory().getAbsolutePath(); } /** * {@inheritDoc} */ public String getInstancePath() { String installPath = getInstallationPath(); // look for <installPath>/lib/resource.loc String instancePathFileName = installPath + File.separator + "lib" + File.separator + "resource.loc"; File f = new File(instancePathFileName); if (! f.exists()) { return installPath; } BufferedReader reader; try { reader = new BufferedReader(new FileReader(instancePathFileName)); } catch (Exception e) { return installPath; } // Read the first line and close the file. String line; try { line = reader.readLine(); return new File(line).getAbsolutePath(); } catch (Exception e) { return installPath; } finally { try { reader.close(); } catch (Exception e) {} } } /** * {@inheritDoc} */ public ProgressStep getCurrentProgressStep() { return UninstallProgressStep.NOT_STARTED; } /** * {@inheritDoc} */ public Integer getRatio(ProgressStep step) { return 0; } /** * {@inheritDoc} */ public Message getSummary(ProgressStep step) { return null; } /** * {@inheritDoc} */ public boolean isFinished() { return false; } /** * {@inheritDoc} */ public boolean isCancellable() { return false; } /** * {@inheritDoc} */ public void cancel() { } /** * {@inheritDoc} */ public void run() { } }; application.setProgressMessageFormatter( new PlainTextProgressMessageFormatter()); if (!supressOutput) { application.addProgressUpdateListener( new ProgressUpdateListener() { public void progressUpdate(ProgressUpdateEvent ev) { System.out.print(ev.getNewLogs().toString()); System.out.flush(); } }); } ServerController controller = new ServerController(application, Installation.getLocal()); try { if (!supressOutput) { printlnProgress(); } controller.startServer(supressOutput); if (!supressOutput) { printlnProgress(); } serverStarted = Installation.getLocal().getStatus().isServerRunning(); LOG.log(Level.INFO, "server started successfully. serverStarted: "+ serverStarted); } catch (ApplicationException ae) { LOG.log(Level.WARNING, "ApplicationException: "+ae, ae); if (!supressOutput) { printErrorMessage(ae.getMessageObject()); } } catch (Throwable t) { LOG.log(Level.SEVERE, "Unexpected error: "+t, t); throw new IllegalStateException("Unexpected error: "+t, t); } return serverStarted; } /** * Updates the contents of the UninstallUserData while trying to connect * to the remote servers. It returns <CODE>true</CODE> if we could connect * to the remote servers and all the presented certificates were accepted and * <CODE>false</CODE> otherwise. * continue if * @param userData the user data to be updated. * @return <CODE>true</CODE> if we could connect * to the remote servers and all the presented certificates were accepted and * <CODE>false</CODE> otherwise. * @throws UserDataException if were are not in interactive mode and not in * force on error mode and the operation must be stopped. * @throws ApplicationException if there is an error processing data in * non-interactive mode and an error must be thrown (not in force on error * mode). */ private boolean updateUserUninstallDataWithRemoteServers( UninstallUserData userData) throws UserDataException, ApplicationException { boolean accepted = false; boolean interactive = parser.isInteractive(); boolean forceOnError = parser.isForceOnError(); boolean exceptionOccurred = true; Message exceptionMsg = null; LOG.log(Level.INFO, "Updating user data with remote servers."); InitialLdapContext ctx = null; try { info.setTrustManager(userData.getTrustManager()); info.setConnectTimeout(getConnectTimeout()); String host = "localhost"; int port = 389; String adminUid = userData.getAdminUID(); String pwd = userData.getAdminPwd(); String dn = ADSContext.getAdministratorDN(adminUid); info.setConnectionPolicy(ConnectionProtocolPolicy.USE_ADMIN); String adminConnectorUrl = info.getAdminConnectorURL(); try { URI uri = new URI(adminConnectorUrl); host = uri.getHost(); port = uri.getPort(); } catch (Throwable t) { LOG.log(Level.SEVERE, "Error parsing url: "+adminConnectorUrl); } ctx = createAdministrativeContext(host, port, useSSL, useStartTLS, dn, pwd, getConnectTimeout(), userData.getTrustManager()); ADSContext adsContext = new ADSContext(ctx); if (interactive && (userData.getTrustManager() == null)) { // This is required when the user did connect to the server using SSL // or Start TLS in interactive mode. In this case // LDAPConnectionInteraction.run does not initialize the keystore and // the trust manager is null. forceTrustManagerInitialization(); updateTrustManager(userData, ci); } LOG.log(Level.INFO, "Reloading topology"); TopologyCache cache = new TopologyCache(adsContext, userData.getTrustManager(), getConnectTimeout()); cache.getFilter().setSearchMonitoringInformation(false); cache.reloadTopology(); accepted = handleTopologyCache(cache, userData); exceptionOccurred = false; } catch (NamingException ne) { LOG.log(Level.WARNING, "Error connecting to server: "+ne, ne); if (Utils.isCertificateException(ne)) { String details = ne.getMessage() != null ? ne.getMessage() : ne.toString(); exceptionMsg = INFO_ERROR_READING_CONFIG_LDAP_CERTIFICATE.get(details); } else { exceptionMsg = Utils.getThrowableMsg( INFO_ERROR_CONNECTING_TO_LOCAL.get(), ne); } } catch (TopologyCacheException te) { LOG.log(Level.WARNING, "Error connecting to server: "+te, te); exceptionMsg = Utils.getMessage(te); } catch (ApplicationException ae) { throw ae; } catch (Throwable t) { LOG.log(Level.WARNING, "Error connecting to server: "+t, t); exceptionMsg = Utils.getThrowableMsg(INFO_BUG_MSG.get(), t); } finally { if (ctx != null) { try { ctx.close(); } catch (Throwable t) { LOG.log(Level.INFO, "Error closing connection: "+t, t); } } } if (exceptionOccurred) { if (!interactive) { if (forceOnError) { println(); printErrorMessage(ERR_UNINSTALL_ERROR_UPDATING_REMOTE_FORCE.get( "--"+parser.getSecureArgsList().adminUidArg.getLongIdentifier(), "--"+ToolConstants.OPTION_LONG_BINDPWD, "--"+ToolConstants.OPTION_LONG_BINDPWD_FILE, String.valueOf(exceptionMsg))); } else { println(); throw new UserDataException(null, ERR_UNINSTALL_ERROR_UPDATING_REMOTE_NO_FORCE.get( "--"+ parser.getSecureArgsList().adminUidArg.getLongIdentifier(), "--"+ToolConstants.OPTION_LONG_BINDPWD, "--"+ToolConstants.OPTION_LONG_BINDPWD_FILE, "--"+parser.forceOnErrorArg.getLongIdentifier(), String.valueOf(exceptionMsg))); } } else { try { accepted = askConfirmation( ERR_UNINSTALL_NOT_UPDATE_REMOTE_PROMPT.get(), false, LOG); } catch (CLIException ce) { throw new UserDataException(null, ce.getMessageObject(), ce); } } } userData.setUpdateRemoteReplication(accepted); LOG.log(Level.INFO, "accepted: "+accepted); return accepted; } /** * 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. * Returns <CODE>true</CODE> if the user accepts all the problems encountered * and <CODE>false</CODE> otherwise. * @param userData the user data. * @throws UserDataException if there is an error with the information * provided by the user when we are in non-interactive mode. * @throws ApplicationException if there is an error processing data in * non-interactive mode and an error must be thrown (not in force on error * mode). */ private boolean handleTopologyCache(TopologyCache cache, UninstallUserData userData) throws UserDataException, ApplicationException { boolean returnValue; boolean stopProcessing = false; boolean reloadTopologyCache = false; boolean interactive = parser.isInteractive(); LOG.log(Level.INFO, "Handle topology cache."); Set<TopologyCacheException> exceptions = new HashSet<TopologyCacheException>(); /* 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. */ Set<ServerDescriptor> servers = cache.getServers(); userData.setRemoteServers(servers); for (ServerDescriptor server : servers) { TopologyCacheException e = server.getLastException(); if (e != null) { exceptions.add(e); } } Set<Message> exceptionMsgs = new LinkedHashSet<Message>(); /* Check the exceptions and see if we throw them or not. */ for (TopologyCacheException e : exceptions) { LOG.log(Level.INFO, "Analyzing exception: "+e, e); if (stopProcessing) { break; } switch (e.getType()) { case NOT_GLOBAL_ADMINISTRATOR: println(); printErrorMessage(INFO_NOT_GLOBAL_ADMINISTRATOR_PROVIDED.get()); stopProcessing = true; break; case GENERIC_CREATING_CONNECTION: if ((e.getCause() != null) && Utils.isCertificateException(e.getCause())) { if (interactive) { println(); if (ci.promptForCertificateConfirmation(e.getCause(), e.getTrustManager(), e.getLdapUrl(), true, LOG)) { stopProcessing = true; reloadTopologyCache = true; updateTrustManager(userData, ci); } else { stopProcessing = true; } } else { exceptionMsgs.add( INFO_ERROR_READING_CONFIG_LDAP_CERTIFICATE_SERVER.get( e.getHostPort(), e.getCause().getMessage())); } } else { exceptionMsgs.add(Utils.getMessage(e)); } break; default: exceptionMsgs.add(Utils.getMessage(e)); } } if (interactive) { if (!stopProcessing && (exceptionMsgs.size() > 0)) { println(); try { returnValue = askConfirmation( ERR_UNINSTALL_READING_REGISTERED_SERVERS_CONFIRM_UPDATE_REMOTE.get( Utils.getMessageFromCollection(exceptionMsgs, Constants.LINE_SEPARATOR).toString()), true, LOG); } catch (CLIException ce) { throw new UserDataException(null, ce.getMessageObject(), ce); } } else if (reloadTopologyCache) { returnValue = updateUserUninstallDataWithRemoteServers(userData); } else { returnValue = !stopProcessing; } } else { LOG.log(Level.INFO, "exceptionMsgs: "+exceptionMsgs); if (exceptionMsgs.size() > 0) { if (parser.isForceOnError()) { Message msg = Utils.getMessageFromCollection(exceptionMsgs, Constants.LINE_SEPARATOR); println(); printErrorMessage(msg); returnValue = false; } else { Message msg = ERR_UNINSTALL_ERROR_UPDATING_REMOTE_NO_FORCE.get( "--"+ parser.getSecureArgsList().adminUidArg.getLongIdentifier(), "--"+ToolConstants.OPTION_LONG_BINDPWD, "--"+ToolConstants.OPTION_LONG_BINDPWD_FILE, "--"+parser.forceOnErrorArg.getLongIdentifier(), Utils.getMessageFromCollection(exceptionMsgs, Constants.LINE_SEPARATOR).toString()); throw new ApplicationException(ReturnCode.APPLICATION_ERROR, msg, null); } } else { returnValue = true; } } LOG.log(Level.INFO, "Return value: "+returnValue); return returnValue; } /** * {@inheritDoc} */ public boolean isAdvancedMode() { return false; } /** * {@inheritDoc} */ public boolean isInteractive() { if (forceNonInteractive) { return false; } else { return parser.isInteractive(); } } /** * {@inheritDoc} */ @Override public boolean isMenuDrivenMode() { return true; } /** * {@inheritDoc} */ public boolean isQuiet() { return false; } /** * {@inheritDoc} */ public boolean isScriptFriendly() { return false; } /** * {@inheritDoc} */ public boolean isVerbose() { return true; } /** * Commodity method to update the user data with the trust manager in the * LDAPConnectionConsoleInteraction object. * @param userData the user data to be updated. * @param ci the LDAPConnectionConsoleInteraction object to be used to update * the user data object. */ private void updateTrustManager(UninstallUserData userData, LDAPConnectionConsoleInteraction ci) { ApplicationTrustManager trust = null; TrustManager t = ci.getTrustManager(); if (t != null) { if (t instanceof ApplicationTrustManager) { trust = (ApplicationTrustManager)t; } else { trust = new ApplicationTrustManager(ci.getKeyStore()); } } userData.setTrustManager(trust); } /** * Forces the initialization of the trust manager in the * LDAPConnectionInteraction object. */ private void forceTrustManagerInitialization() { forceNonInteractive = true; try { ci.initializeTrustManagerIfRequired(); } catch (ArgumentException ae) { LOG.log(Level.WARNING, "Error initializing trust store: "+ae, ae); } forceNonInteractive = false; } private void printErrorMessage(Message msg) { super.println(msg); LOG.log(Level.WARNING, msg.toString()); } /** * Returns the timeout to be used to connect in milliseconds. The method * must be called after parsing the arguments. * @return the timeout to be used to connect in milliseconds. Returns * {@code 0} if there is no timeout. * @throw {@code IllegalStateException} if the method is called before * parsing the arguments. */ private int getConnectTimeout() { try { return parser.getSecureArgsList().connectTimeoutArg.getIntValue(); } catch (ArgumentException ae) { throw new IllegalStateException("Argument parser is not parsed: "+ae, ae); } } }