/* * 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 * * * Portions Copyright 2007 Sun Microsystems, Inc. */ package org.opends.quicksetup.util; import org.opends.quicksetup.*; import javax.naming.NamingException; import java.util.ArrayList; import java.util.Map; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; /** * Class used to manipulate an OpenDS server. */ public class ServerController { private Application application; private Installation installation; /** * Creates a new instance that will operate on application's * installation. * @param application to use for notifications */ public ServerController(Application application) { this(application, application.getInstallation()); } /** * Creates a new instance that will operate on installation * and use application for notifications. * @param application to use for notifications * @param installation representing the server instance to control */ public ServerController(Application application, Installation installation) { if (application == null) { throw new NullPointerException("application cannot be null"); } if (installation == null) { throw new NullPointerException("installation cannot be null"); } this.application = application; this.installation = installation; } /** * This methods stops the server. * * @throws org.opends.quicksetup.ApplicationException if something goes wrong. */ public void stopServer() throws ApplicationException { application.notifyListeners( application.getFormattedProgress( application.getMsg("progress-stopping")) + application.getLineBreak()); ArrayList argList = new ArrayList(); argList.add(Utils.getPath(installation.getServerStopCommandFile())); String[] args = new String[argList.size()]; argList.toArray(args); ProcessBuilder pb = new ProcessBuilder(args); Map env = pb.environment(); env.put("JAVA_HOME", System.getProperty("java.home")); /* Remove JAVA_BIN to be sure that we use the JVM running the uninstaller * JVM to stop the server. */ env.remove("JAVA_BIN"); try { Process process = pb.start(); BufferedReader err = new BufferedReader( new InputStreamReader(process.getErrorStream())); BufferedReader out = new BufferedReader( new InputStreamReader(process.getInputStream())); /* Create these objects to resend the stop process output to the details * area. */ new StopReader(err, true); new StopReader(out, false); int returnValue = process.waitFor(); int clientSideError = org.opends.server.protocols.ldap. LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR; if ((returnValue == clientSideError) || (returnValue == 0)) { if (Utils.isWindows()) { /* * Sometimes the server keeps some locks on the files. * TODO: remove this code once stop-ds returns properly when server * is stopped. */ int nTries = 10; boolean stopped = false; for (int i = 0; i < nTries && !stopped; i++) { stopped = !CurrentInstallStatus.isServerRunning( installation.getLocksDirectory()); if (!stopped) { String msg = application.getFormattedLog( application.getMsg("progress-server-waiting-to-stop")) + application.getLineBreak(); application.notifyListeners(msg); try { Thread.sleep(5000); } catch (Exception ex) { } } } if (!stopped) { returnValue = -1; } } } if (returnValue == clientSideError) { String msg = application.getLineBreak() + application.getFormattedLog( application.getMsg("progress-server-already-stopped")) + application.getLineBreak(); application.notifyListeners(msg); } else if (returnValue != 0) { String[] arg = {String.valueOf(returnValue)}; String msg = application.getMsg("error-stopping-server-code", arg); /* * The return code is not the one expected, assume the server could * not be stopped. */ throw new ApplicationException(ApplicationException.Type.STOP_ERROR, msg, null); } else { String msg = application.getFormattedLog( application.getMsg("progress-server-stopped")); application.notifyListeners(msg); } } catch (IOException ioe) { throw new ApplicationException(ApplicationException.Type.STOP_ERROR, application.getThrowableMsg("error-stopping-server", ioe), ioe); } catch (InterruptedException ie) { throw new ApplicationException(ApplicationException.Type.BUG, application.getThrowableMsg("error-stopping-server", ie), ie); } } /** * Starts the directory server within this process. * @param disableConnectionHandlers boolean that when true starts the * the server mode that is otherwise up and running but will not accept any * connections from external clients (i.e., does not create or initialize the * connection handlers). This could be useful, for example, in an upgrade mode * where it might be helpful to start the server but don't want it to appear * externally as if the server is online without connection handlers * listening. * * @throws org.opends.server.config.ConfigException * If there is a problem with the Directory Server * configuration that prevents a critical component * from being instantiated. * * @throws org.opends.server.types.InitializationException * If some other problem occurs while * attempting to initialize and start the * Directory Server. */ public void startServerInProcess(boolean disableConnectionHandlers) throws org.opends.server.types.InitializationException, org.opends.server.config.ConfigException { System.setProperty( "org.opends.server.DisableConnectionHandlers", disableConnectionHandlers ? "true" : null); startServerInProcess(); } /** * Stops a server that had been running 'in process'. */ public void stopServerInProcess() { org.opends.server.core.DirectoryServer.shutDown( ServerController.class.getName(), "quicksetup requests shutdown"); } /** * This methods starts the server. * @throws org.opends.quicksetup.QuickSetupException if something goes wrong. */ public void startServer() throws QuickSetupException { startServer(true); } /** * This methods starts the server. * @param verify boolean indicating whether this method will attempt to * connect to the server after starting to verify that it is listening. * @throws org.opends.quicksetup.QuickSetupException if something goes wrong. */ private void startServer(boolean verify) throws QuickSetupException { application.notifyListeners( application.getFormattedProgress( application.getMsg("progress-starting")) + application.getLineBreak()); ArrayList argList = new ArrayList(); argList.add(Utils.getPath(installation.getServerStartCommandFile())); String[] args = new String[argList.size()]; argList.toArray(args); ProcessBuilder pb = new ProcessBuilder(args); pb.directory(installation.getBinariesDirectory()); Map env = pb.environment(); env.put("JAVA_HOME", System.getProperty("java.home")); /* Remove JAVA_BIN to be sure that we use the JVM running the installer * JVM to start the server. */ env.remove("JAVA_BIN"); // Upgrader's classpath contains jars located in the temporary // directory that we don't want locked by the directory server // when it starts. Since we're just calling the start-ds script // it will figure out the correct classpath for the server. env.remove("CLASSPATH"); try { String startedId = getStartedId(); Process process = pb.start(); BufferedReader err = new BufferedReader(new InputStreamReader(process.getErrorStream())); BufferedReader out = new BufferedReader(new InputStreamReader(process.getInputStream())); StartReader errReader = new StartReader(err, startedId, true); StartReader outputReader = new StartReader(out, startedId, false); while (!errReader.isFinished() && !outputReader.isFinished()) { try { Thread.sleep(100); } catch (InterruptedException ie) { } } // Check if something wrong occurred reading the starting of the server QuickSetupException ex = errReader.getException(); if (ex == null) { ex = outputReader.getException(); } if (ex != null) { throw ex; } else if (verify) { /* * There are no exceptions from the readers and they are marked as * finished. This means that the server has written in its output the * message id informing that it started. So it seems that everything * went fine. * * However we can have issues with the firewalls or do not have rights * to connect. Just check if we can connect to the server. * Try 5 times with an interval of 1 second between try. */ boolean connected = false; Configuration config = application.getInstallation().getCurrentConfiguration(); int port = config.getPort(); String ldapUrl = "ldap://localhost:" + port; // See if the application has prompted for credentials. If // not we'll just try to connect anonymously. String userDn = application.getUserData().getDirectoryManagerDn(); String userPw = application.getUserData().getDirectoryManagerPwd(); if (userDn == null || userPw == null) { userDn = null; userPw = null; } for (int i=0; i<5 && !connected; i++) { try { Utils.createLdapContext( ldapUrl, userDn, userPw, 3000, null); connected = true; } catch (NamingException ne) { } if (!connected) { try { Thread.sleep(1000); } catch (Throwable t) { } } } if (!connected) { String[] arg = {String.valueOf(port)}; if (Utils.isWindows()) { throw new QuickSetupException(QuickSetupException.Type.START_ERROR, application.getMsg("error-starting-server-in-windows", arg), null); } else { throw new QuickSetupException(QuickSetupException.Type.START_ERROR, application.getMsg("error-starting-server-in-unix", arg), null); } } } } catch (IOException ioe) { throw new QuickSetupException(QuickSetupException.Type.START_ERROR, application.getThrowableMsg("error-starting-server", ioe), ioe); } } /** * Starts the OpenDS server in this process. * * @throws org.opends.server.config.ConfigException * If there is a problem with the Directory Server * configuration that prevents a critical component * from being instantiated. * * @throws org.opends.server.types.InitializationException * If some other problem occurs while * attempting to initialize and start the * Directory Server. */ public void startServerInProcess() throws org.opends.server.types.InitializationException, org.opends.server.config.ConfigException { // Bootstrap and start the Directory Server. org.opends.server.core.DirectoryServer directoryServer = org.opends.server.core.DirectoryServer.getInstance(); directoryServer.bootstrapServer(); String configClass = "org.opends.server.extensions.ConfigFileHandler"; String configPath = Utils.getPath( application.getInstallation().getCurrentConfigurationFile()); directoryServer.initializeConfiguration(configClass, configPath); directoryServer.startServer(); } /** * This class is used to read the standard error and standard output of the * Stop process. *

* When a new log message is found notifies the * UninstallProgressUpdateListeners of it. If an error occurs it also * notifies the listeners. */ private class StopReader { private boolean isFirstLine; /** * The protected constructor. * * @param reader the BufferedReader of the stop process. * @param isError a boolean indicating whether the BufferedReader * corresponds to the standard error or to the standard output. */ public StopReader(final BufferedReader reader, final boolean isError) { final String errorTag = isError ? "error-reading-erroroutput" : "error-reading-output"; isFirstLine = true; Thread t = new Thread(new Runnable() { public void run() { try { String line = reader.readLine(); while (line != null) { StringBuilder buf = new StringBuilder(); if (!isFirstLine) { buf.append(application.getProgressMessageFormatter(). getLineBreak()); } if (isError) { buf.append(application.getFormattedLogError(line)); } else { buf.append(application.getFormattedLog(line)); } application.notifyListeners(buf.toString()); isFirstLine = false; line = reader.readLine(); } } catch (IOException ioe) { String errorMsg = application.getThrowableMsg(errorTag, ioe); application.notifyListeners(errorMsg); } catch (Throwable t) { String errorMsg = application.getThrowableMsg(errorTag, t); application.notifyListeners(errorMsg); } } }); t.start(); } } /** * Returns the Message ID indicating that the server has started. * @return the Message ID indicating that the server has started. */ private String getStartedId() { InstallerHelper helper = new InstallerHelper(); return helper.getStartedId(); } /** * This class is used to read the standard error and standard output of the * Start process. * * When a new log message is found notifies the ProgressUpdateListeners * of it. If an error occurs it also notifies the listeners. * */ private class StartReader { private QuickSetupException ex; private boolean isFinished; private boolean isFirstLine; /** * The protected constructor. * @param reader the BufferedReader of the start process. * @param startedId the message ID that this class can use to know whether * the start is over or not. * @param isError a boolean indicating whether the BufferedReader * corresponds to the standard error or to the standard output. */ public StartReader(final BufferedReader reader, final String startedId, final boolean isError) { final String errorTag = isError ? "error-reading-erroroutput" : "error-reading-output"; isFirstLine = true; Thread t = new Thread(new Runnable() { public void run() { try { String line = reader.readLine(); while (line != null) { StringBuffer buf = new StringBuffer(); if (!isFirstLine) { buf.append(application.getProgressMessageFormatter(). getLineBreak()); } if (isError) { buf.append(application.getFormattedLogError(line)); } else { buf.append(application.getFormattedLog(line)); } application.notifyListeners(buf.toString()); isFirstLine = false; if (line.indexOf("id=" + startedId) != -1) { isFinished = true; } line = reader.readLine(); } } catch (IOException ioe) { String errorMsg = application.getThrowableMsg(errorTag, ioe); ex = new QuickSetupException(QuickSetupException.Type.START_ERROR, errorMsg, ioe); } catch (Throwable t) { String errorMsg = application.getThrowableMsg(errorTag, t); ex = new QuickSetupException(QuickSetupException.Type.START_ERROR, errorMsg, t); } isFinished = true; } }); t.start(); } /** * Returns the QuickSetupException that occurred reading the Start error and * output or null if no exception occurred. * @return the exception that occurred reading or null if * no exception occurred. */ public QuickSetupException getException() { return ex; } /** * Returns true if the server starting process finished * (successfully or not) and false otherwise. * @return true if the server starting process finished * (successfully or not) and false otherwise. */ public boolean isFinished() { return isFinished; } } }