/*
* 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 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.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 = false;
if (requiresAuthenticationToStop())
{
getLoginDialog().pack();
Utils.centerOnComponent(getLoginDialog(), getStatusPanelDialog());
getLoginDialog().setVisible(true);
if (!getLoginDialog().isCancelled())
{
serverStatusPooler.setAuthentication(
getLoginDialog().getDirectoryManagerDn(),
getLoginDialog().getDirectoryManagerPwd());
stopServer = confirmStop();
}
}
else
{
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 = false;
if (requiresAuthenticationToRestart())
{
getLoginDialog().pack();
Utils.centerOnComponent(getLoginDialog(), getStatusPanelDialog());
getLoginDialog().setVisible(true);
if (!getLoginDialog().isCancelled())
{
serverStatusPooler.setAuthentication(
getLoginDialog().getDirectoryManagerDn(),
getLoginDialog().getDirectoryManagerPwd());
restartServer = confirmRestart();
}
}
else
{
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();
if (Utils.isWindows())
{
argList.add(Utils.getPath(getBinariesPath(), "start-ds.bat"));
} else
{
argList.add(Utils.getPath(getBinariesPath(), "start-ds"));
}
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 = Utils.isServerRunning(
Utils.getInstallPathFromClasspath());
}
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();
if (Utils.isWindows())
{
argList.add(Utils.getPath(getBinariesPath(), "stop-ds.bat"));
argList.add("--bindDN");
argList.add(getLoginDialog().getDirectoryManagerDn());
argList.add("--bindPassword");
argList.add(getLoginDialog().getDirectoryManagerPwd());
} else
{
argList.add(Utils.getPath(getBinariesPath(), "stop-ds"));
}
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();
}
private boolean requiresAuthenticationToStop()
{
boolean requireAuthentication;
ServerStatusDescriptor desc = serverStatusPooler.getLastDescriptor();
if (desc == null)
{
requireAuthentication = Utils.isWindows() && !isAuthenticated();
}
else
{
requireAuthentication = Utils.isWindows() &&
(!isAuthenticated() || desc.getErrorMessage() != null);
}
return requireAuthentication;
}
private boolean requiresAuthenticationToRestart()
{
return requiresAuthenticationToStop();
}
}