/* * 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 2006-2007 Sun Microsystems, Inc. */ package org.opends.statuspanel; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Map; import javax.swing.SwingUtilities; import org.opends.server.core.DirectoryServer; import org.opends.quicksetup.CurrentInstallStatus; import org.opends.quicksetup.Installation; import org.opends.quicksetup.ui.UIFactory; import org.opends.quicksetup.util.BackgroundTask; import org.opends.quicksetup.util.HtmlProgressMessageFormatter; import org.opends.quicksetup.util.Utils; import org.opends.statuspanel.event.ServerStatusChangeEvent; import org.opends.statuspanel.event.ServerStatusChangeListener; import org.opends.statuspanel.event.StatusPanelButtonListener; import org.opends.statuspanel.i18n.ResourceProvider; import org.opends.statuspanel.ui.LoginDialog; import org.opends.statuspanel.ui.ProgressDialog; import org.opends.statuspanel.ui.StatusPanelDialog; /** * This is the main class of the status panel. * * It is in charge of all the logic behind the displaying of the dialogs and * the one that initializes everything. This is also the class that ultimately * listens to user events (notably button clicks) and executes the associated * operations with these user events. * */ public class StatusPanelController implements ServerStatusChangeListener, StatusPanelButtonListener { private LoginDialog loginDialog; private StatusPanelDialog controlPanelDialog; private ProgressDialog progressDialog; private ServerStatusPooler serverStatusPooler; private HtmlProgressMessageFormatter formatter = new HtmlProgressMessageFormatter(); private boolean isStarting; private boolean isStopping; private boolean isRestarting; private boolean mustDisplayAuthenticateDialog; private ServerStatusDescriptor desc; private String lastDetail; private String lastSummary; private Thread progressUpdater; //Update period of the progress dialog. private static final int UPDATE_PERIOD = 500; /** * This method creates the control panel dialogs and to check the current * install status. This method must be called outside the event thread because * it can perform long operations which can make the user think that the UI is * blocked. * * Note that there is no synchronization code between threads. The * synchronization code is not required as all the start/stop/restart actions * are launched in the event thread so just using booleans that are updated * in the event thread is enough to guarantee that there are no multiple * operations launched at the same time. * * @param args for the moment this parameter is not used but we keep it in * order to (in case of need) pass parameters through the command line. */ public void initialize(String[] args) { DirectoryServer.bootstrapClient(); initLookAndFeel(); /* Call this methods to create the dialogs (the control panel dialog * is generated when we call getLoginDialog()). */ getLoginDialog(); getProgressDialog(); serverStatusPooler = new ServerStatusPooler(); serverStatusPooler.addServerStatusChangeListener(this); serverStatusPooler.startPooling(); desc = serverStatusPooler.getLastDescriptor(); while (desc == null) { try { Thread.sleep(100); } catch (Exception ex) { } desc = serverStatusPooler.getLastDescriptor(); } } /** * This method displays the required dialog. This method must be called from * the event thread. */ public void display() { getStatusPanelDialog().packAndShow(); if (!isAuthenticated()) { if (desc.getStatus() == ServerStatusDescriptor.ServerStatus.STARTED) { authenticateClicked(); } } } /** * ServerStatusChangeListener implementation. This method is called when * a new ServerStatusDescriptor has been generated by the ServerStatusPooler. * @param ev the event we receive. */ public void statusChanged(final ServerStatusChangeEvent ev) { /* Here we assume that this events are not very frequent (not frequent * at least to generate flickering). This is acceptable since the * ServerStatusPooler does pooling every second. */ if (SwingUtilities.isEventDispatchThread()) { getStatusPanelDialog().updateContents(ev.getStatusDescriptor()); } else { SwingUtilities.invokeLater(new Runnable() { public void run() { getStatusPanelDialog().updateContents(ev.getStatusDescriptor()); } }); } } /** * Method called when user clicks on Quit button. * StatusPanelButtonListener implementation. */ public void quitClicked() { System.exit(0); } /** * Method called when user clicks on Start button. * StatusPanelButtonListener implementation. */ public void startClicked() { if (isStarting) { if (!getProgressDialog().isVisible()) { getProgressDialog().setVisible(true); } } else if (isStopping) { /* Should not happen */ Thread.dumpStack(); } else if (isRestarting) { /* Should not happen */ Thread.dumpStack(); } else { isStarting = true; lastDetail = null; getProgressDialog().setSummary( getFormattedSummary(getMsg("summary-starting"))); getProgressDialog().setDetails(""); serverStatusPooler.beginServerStart(); getProgressDialog().setCloseButtonEnabled(false); getStatusPanelDialog().setStartButtonEnabled(false); getStatusPanelDialog().setStopButtonEnabled(false); getStatusPanelDialog().setRestartButtonEnabled(false); getStatusPanelDialog().setAuthenticateButtonEnabled(false); if (!getProgressDialog().isVisible()) { getProgressDialog().pack(); Utils.centerOnComponent(getProgressDialog(), getStatusPanelDialog()); getProgressDialog().setVisible(true); } BackgroundTask task = new BackgroundTask() { public Object processBackgroundTask() { if (progressUpdater == null) { runProgressUpdater(); } mustDisplayAuthenticateDialog = startServer() && !isAuthenticated(); serverStatusPooler.endServerStart(); return null; } public void backgroundTaskCompleted(Object value, Throwable t) { if (t != null) { // Bug t.printStackTrace(); } getStatusPanelDialog().setStartButtonEnabled(true); getStatusPanelDialog().setStopButtonEnabled(true); getStatusPanelDialog().setRestartButtonEnabled(true); getStatusPanelDialog().setAuthenticateButtonEnabled( !isAuthenticated()); getProgressDialog().setCloseButtonEnabled(true); isStarting = false; } }; task.startBackgroundTask(); } } /** * Method called when user clicks on Stop button. * StatusPanelButtonListener implementation. */ public void stopClicked() { if (isStopping) { if (!getProgressDialog().isVisible()) { getProgressDialog().setVisible(true); } } else if (isStarting) { /* Should not happen */ Thread.dumpStack(); } else if (isRestarting) { /* Should not happen */ Thread.dumpStack(); } else { boolean stopServer = confirmStop(); if (stopServer) { isStopping = true; lastDetail = null; getProgressDialog().setSummary( getFormattedSummary(getMsg("summary-stopping"))); getProgressDialog().setDetails(""); serverStatusPooler.beginServerStop(); getProgressDialog().setCloseButtonEnabled(false); getStatusPanelDialog().setStartButtonEnabled(false); getStatusPanelDialog().setStopButtonEnabled(false); getStatusPanelDialog().setRestartButtonEnabled(false); getStatusPanelDialog().setAuthenticateButtonEnabled(false); if (!getProgressDialog().isVisible()) { getProgressDialog().pack(); Utils.centerOnComponent(getProgressDialog(), getStatusPanelDialog()); getProgressDialog().setVisible(true); } BackgroundTask task = new BackgroundTask() { public Object processBackgroundTask() { if (progressUpdater == null) { runProgressUpdater(); } stopServer(); serverStatusPooler.endServerStop(); mustDisplayAuthenticateDialog = false; return null; } public void backgroundTaskCompleted(Object value, Throwable t) { if (t != null) { // Bug t.printStackTrace(); } getStatusPanelDialog().setStartButtonEnabled(true); getStatusPanelDialog().setStopButtonEnabled(true); getStatusPanelDialog().setRestartButtonEnabled(true); getStatusPanelDialog().setAuthenticateButtonEnabled(false); getProgressDialog().setCloseButtonEnabled(true); isStopping = false; } }; task.startBackgroundTask(); } } } /** * Method called when user clicks on Restart button. * StatusPanelButtonListener implementation. */ public void restartClicked() { if (isRestarting) { if (!getProgressDialog().isVisible()) { getProgressDialog().setVisible(true); } } else if (isStopping) { /* Should not happen */ Thread.dumpStack(); } else if (isStarting) { /* Should not happen */ Thread.dumpStack(); } else { boolean restartServer = confirmRestart(); if (restartServer) { isRestarting = true; lastDetail = null; getProgressDialog().setSummary( getFormattedSummary(getMsg("summary-stopping"))); getProgressDialog().setDetails(""); serverStatusPooler.beginServerStop(); getProgressDialog().setCloseButtonEnabled(false); getStatusPanelDialog().setStartButtonEnabled(false); getStatusPanelDialog().setStopButtonEnabled(false); getStatusPanelDialog().setRestartButtonEnabled(false); getStatusPanelDialog().setAuthenticateButtonEnabled(false); if (!getProgressDialog().isVisible()) { getProgressDialog().pack(); Utils.centerOnComponent(getProgressDialog(), getStatusPanelDialog()); getProgressDialog().setVisible(true); } BackgroundTask task = new BackgroundTask() { public Object processBackgroundTask() { if (progressUpdater == null) { runProgressUpdater(); } if (stopServer()) { serverStatusPooler.endServerStop(); serverStatusPooler.beginServerStart(); mustDisplayAuthenticateDialog = startServer() && !isAuthenticated(); serverStatusPooler.endServerStart(); } else { serverStatusPooler.endServerStop(); mustDisplayAuthenticateDialog = false; } return null; } public void backgroundTaskCompleted(Object value, Throwable t) { if (t != null) { // Bug t.printStackTrace(); } getStatusPanelDialog().setStartButtonEnabled(true); getStatusPanelDialog().setStopButtonEnabled(true); getStatusPanelDialog().setRestartButtonEnabled(true); getStatusPanelDialog().setAuthenticateButtonEnabled( !isAuthenticated()); getProgressDialog().setCloseButtonEnabled(true); isRestarting = false; } }; task.startBackgroundTask(); } } } /** * Method called when user clicks on Authenticate button. * StatusPanelButtonListener implementation. */ public void authenticateClicked() { getLoginDialog().pack(); Utils.centerOnComponent(getLoginDialog(), getStatusPanelDialog()); getLoginDialog().setVisible(true); if (!getLoginDialog().isCancelled()) { serverStatusPooler.setAuthentication( getLoginDialog().getDirectoryManagerDn(), getLoginDialog().getDirectoryManagerPwd()); } } /** * A method to initialize the look and feel. * */ private void initLookAndFeel() { UIFactory.initialize(); } /** * Returns the login dialog and creates it if it does not exist. * @return the login dialog. */ private LoginDialog getLoginDialog() { if (loginDialog == null) { loginDialog = new LoginDialog(getStatusPanelDialog()); loginDialog.setModal(true); } return loginDialog; } /** * Returns the progress dialog and creates it if it does not exist. * As we only can run an operation at a time (considering the nature of the * operations we provide today) having a single progress dialog is enough. * @return the progress dialog. */ private ProgressDialog getProgressDialog() { if (progressDialog == null) { progressDialog = new ProgressDialog(getStatusPanelDialog()); progressDialog.addWindowListener(new WindowAdapter() { public void windowClosed(WindowEvent ev) { if (mustDisplayAuthenticateDialog) { mustDisplayAuthenticateDialog = false; authenticateClicked(); } } }); } return progressDialog; } /** * Returns the status panel dialog and creates it if it does not exist. This * is the dialog that displays the status information to the user. * @return the status panel dialog. */ private StatusPanelDialog getStatusPanelDialog() { if (controlPanelDialog == null) { controlPanelDialog = new StatusPanelDialog(); controlPanelDialog.addButtonListener(this); } return controlPanelDialog; } /** * This methods starts the server and updates the progress with the start * messages. * @return true if the server started successfully and * false otherwise. */ protected boolean startServer() { boolean started = false; if (isRestarting) { updateProgress( getFormattedSummary(getMsg("summary-starting")), getTaskSeparator()); } updateProgress( getFormattedSummary(getMsg("summary-starting")), getFormattedProgress(getMsg("progress-starting")) + getLineBreak()); ArrayList argList = new ArrayList(); Installation installation = new Installation(Utils.getInstallPathFromClasspath()); argList.add(Utils.getPath(installation.getServerStartCommandFile())); 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 installer * JVM to start 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 ProgressReader(err, true, true); new ProgressReader(out, false, true); int returnValue = process.waitFor(); if (returnValue == 0) { /* * 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 running = false; for (int i=0; i<5 && !running; i++) { running = CurrentInstallStatus.isServerRunning(); } if (!running) { try { Thread.sleep(1000); } catch (Throwable t) { } } if (!running) { updateProgress(getFormattedError(getMsg("summary-start-error")), getFormattedError(getMsg("error-starting-server-generic"), true)); } else { updateProgress( getFormattedSuccess(getMsg("summary-start-success")), ""); started = true; } } else { String[] arg = {String.valueOf(returnValue)}; String msg = getMsg("error-starting-server-code", arg); /* * The return code is not the one expected, assume the server could * not be started. */ updateProgress( getFormattedError(getMsg("summary-start-error")), msg); } } catch (IOException ioe) { String msg = getThrowableMsg("error-starting-server", ioe); updateProgress( getFormattedError(getMsg("summary-start-error")), msg); } catch (InterruptedException ie) { String msg = getThrowableMsg("error-starting-server", ie); updateProgress( getFormattedError(getMsg("summary-start-error")), msg); } return started; } /** * This methods stops the server and updates the progress with the stop * messages. * @return true if the server stopped successfully and * false otherwise. */ private boolean stopServer() { boolean stopped = false; updateProgress( getFormattedSummary(getMsg("summary-stopping")), getFormattedProgress(getMsg("progress-stopping")) + getLineBreak()); ArrayList argList = new ArrayList(); Installation installation = new Installation(Utils.getInstallPathFromClasspath()); 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 ProgressReader(err, true, false); new ProgressReader(out, false, 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; for (int i=0; itrue if the server confirms that (s)he wants to stop * the server and false otherwise. */ private boolean confirmStop() { return Utils.displayConfirmation(getStatusPanelDialog(), getMsg("confirm-stop-message"), getMsg("confirm-stop-title")); } /** * Displays a confirmation dialog asking the user whether to restart the * server or not. * @return true if the server confirms that (s)he wants to * restart the server and false otherwise. */ private boolean confirmRestart() { return Utils.displayConfirmation(getStatusPanelDialog(), getMsg("confirm-restart-message"), getMsg("confirm-restart-title")); } /** * Returns whether the user provided LDAP authentication or not. * @return true if the server provided LDAP authentication and * false otherwise. */ private boolean isAuthenticated() { return serverStatusPooler.isAuthenticated(); } }