/*
* 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 2008-2010 Sun Microsystems, Inc.
* Portions Copyright 2013 ForgeRock AS
*/
package org.opends.guitools.controlpanel.ui;
import static org.opends.messages.AdminToolMessages.*;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.NamingEnumeration;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.swing.Box;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import org.opends.admin.ads.util.ConnectionUtils;
import org.opends.guitools.controlpanel.browser.BrowserController;
import org.opends.guitools.controlpanel.browser.IconPool;
import org.opends.guitools.controlpanel.datamodel.AbstractIndexDescriptor;
import org.opends.guitools.controlpanel.datamodel.BackendDescriptor;
import org.opends.guitools.controlpanel.datamodel.BaseDNDescriptor;
import org.opends.guitools.controlpanel.datamodel.CategorizedComboBoxElement;
import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
import org.opends.guitools.controlpanel.datamodel.CustomSearchResult;
import org.opends.guitools.controlpanel.datamodel.MonitoringAttributes;
import org.opends.guitools.controlpanel.datamodel.ScheduleType;
import org.opends.guitools.controlpanel.datamodel.ServerDescriptor;
import org.opends.guitools.controlpanel.event.*;
import org.opends.guitools.controlpanel.task.RebuildIndexTask;
import org.opends.guitools.controlpanel.task.RestartServerTask;
import org.opends.guitools.controlpanel.task.StartServerTask;
import org.opends.guitools.controlpanel.task.StopServerTask;
import org.opends.guitools.controlpanel.task.Task;
import org.opends.guitools.controlpanel.ui.components.AddRemovePanel;
import org.opends.guitools.controlpanel.util.BackgroundTask;
import org.opends.guitools.controlpanel.util.LowerCaseComparator;
import org.opends.guitools.controlpanel.util.Utilities;
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
import org.opends.messages.MessageDescriptor;
import org.opends.quicksetup.ui.CustomHTMLEditorKit;
import org.opends.server.schema.SchemaConstants;
import org.opends.server.types.ObjectClass;
import org.opends.server.types.ObjectClassType;
import org.opends.server.types.OpenDsException;
import org.opends.server.util.ServerConstants;
/**
* An abstract class that contains a number of methods that are shared by all
* the inheriting classes. In general a StatusGenericPanel is contained in a
* GenericDialog and specifies the kind of buttons that this dialog has. The
* StatusGenericPanel is also notified when the dialog is displayed (through
* the toBeDisplayed method)
*/
public abstract class StatusGenericPanel extends JPanel
implements ConfigChangeListener
{
private static final long serialVersionUID = -9123358652232556732L;
/**
* The string to be used as combo separator.
*/
public static final String COMBO_SEPARATOR = "----------";
/**
* The not applicable message.
*/
protected final static Message NOT_APPLICABLE =
INFO_NOT_APPLICABLE_LABEL.get();
private Message AUTHENTICATE = INFO_AUTHENTICATE_BUTTON_LABEL.get();
private Message START = INFO_START_BUTTON_LABEL.get();
private ControlPanelInfo info;
private boolean enableClose = true;
private boolean enableCancel = true;
private boolean enableOK = true;
private boolean disposeOnClose = false;
private JPanel cardPanel;
private JPanel mainPanel;
private JEditorPane message;
private CardLayout cardLayout;
private static final String MAIN_PANEL = "mainPanel";
private static final String MESSAGE_PANEL = "messagePanel";
private static final Logger LOG =
Logger.getLogger(StatusGenericPanel.class.getName());
/**
* The error pane.
*/
protected JEditorPane errorPane;
/**
* The last displayed message in the error pane.
*/
protected String lastDisplayedError = null;
private ArrayList<ConfigurationElementCreatedListener> confListeners =
new ArrayList<ConfigurationElementCreatedListener>();
private boolean sizeSet = false;
private boolean focusSet = false;
private static DateFormat taskDateFormat =
new SimpleDateFormat("yyyyMMddHHmmss");
/**
* Returns the title that will be used as title of the dialog.
* @return the title that will be used as title of the dialog.
*/
public abstract Message getTitle();
/**
* Returns the buttons that the dialog where this panel is contained should
* display.
* @return the buttons that the dialog where this panel is contained should
* display.
*/
public GenericDialog.ButtonType getButtonType()
{
return GenericDialog.ButtonType.OK_CANCEL;
}
/**
* Returns the component that should get the focus when the dialog that
* contains this panel is displayed.
* @return the component that should get the focus.
*/
public abstract Component getPreferredFocusComponent();
/**
* Returns <CODE>true</CODE> if this panel requires some bordering (in general
* an EmptyBorder with some insets) and <CODE>false</CODE> otherwise.
* @return <CODE>true</CODE> if this panel requires some bordering (in general
* an EmptyBorder with some insets) and <CODE>false</CODE> otherwise.
*/
public boolean requiresBorder()
{
return true;
}
/**
* Returns the menu bar that the panel might have. Returns
* <CODE>null</CODE> if the panel has no menu bar associated.
* @return the menu bar that the panel might have.
*/
public JMenuBar getMenuBar()
{
return null;
}
/**
* This method is called to indicate that the configuration changes should
* be called in the background. In the case of panels which require some
* time to be updated with the new configuration this method returns
* <CODE>true</CODE> and the operation will be performed in the background
* while a message of type 'Loading...' is displayed on the panel.
* @return <CODE>true</CODE> if changes should be loaded in the background and
* <CODE>false</CODE> otherwise.
*/
public boolean callConfigurationChangedInBackground()
{
return false;
}
/**
* The panel is notified that the dialog is going to be visible or invisible.
* @param visible whether is going to be visible or not.
*/
public void toBeDisplayed(boolean visible)
{
}
/**
* Tells whether this panel should be contained in a scroll pane or not.
* @return <CODE>true</CODE> if this panel should be contained in a scroll
* pane and <CODE>false</CODE> otherwise.
*/
public boolean requiresScroll()
{
return true;
}
/**
* Constructor.
*
*/
protected StatusGenericPanel()
{
super(new GridBagLayout());
setBackground(ColorAndFontConstants.background);
cardLayout = new CardLayout();
cardPanel = new JPanel(cardLayout);
cardPanel.setOpaque(false);
mainPanel = new JPanel(new GridBagLayout());
mainPanel.setOpaque(false);
message = Utilities.makeHtmlPane("", ColorAndFontConstants.progressFont);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
super.add(cardPanel, gbc);
cardPanel.add(mainPanel, MAIN_PANEL);
JPanel messagePanel = new JPanel(new GridBagLayout());
messagePanel.setOpaque(false);
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
messagePanel.add(message, gbc);
cardPanel.add(messagePanel, MESSAGE_PANEL);
cardLayout.show(cardPanel, MAIN_PANEL);
}
/**
* The components are not added directly to the panel but to the main panel.
* This is done to be able to display a message that takes the whole panel
* (of type 'Loading...') when we are doing long operations.
* @param comp the Component to be added.
* @param constraints the constraints.
*/
public void add(Component comp, Object constraints)
{
mainPanel.add(comp, constraints);
}
/**
* Adds a bottom glue to the main panel with the provided constraints.
* @param gbc the constraints.
*/
protected void addBottomGlue(GridBagConstraints gbc)
{
GridBagConstraints gbc2 = (GridBagConstraints)gbc.clone();
gbc2.insets = new Insets(0, 0, 0, 0);
gbc2.gridy ++;
gbc2.gridwidth = GridBagConstraints.REMAINDER;
gbc2.weighty = 1.0;
gbc2.fill = GridBagConstraints.VERTICAL;
add(Box.createVerticalGlue(), gbc2);
gbc.gridy ++;
}
/**
* Returns a label with text 'Required Field' and an icon (used as legend in
* some panels).
* @return a label with text 'Required Field' and an icon (used as legend in
* some panels).
*/
protected JLabel createRequiredLabel()
{
JLabel requiredLabel = Utilities.createInlineHelpLabel(
INFO_CTRL_PANEL_INDICATES_REQUIRED_FIELD_LABEL.get());
requiredLabel.setIcon(
Utilities.createImageIcon(IconPool.IMAGE_PATH+"/required.gif"));
return requiredLabel;
}
/**
* Creates and adds an error pane. Is up to the caller to set the proper
* gridheight, gridwidth, gridx and gridy on the provided GridBagConstraints.
* @param baseGbc the GridBagConstraints to be used.
*/
protected void addErrorPane(GridBagConstraints baseGbc)
{
addErrorPane(this, baseGbc);
}
/**
* Adds an error pane to the provided container.
* Is up to the caller to set the proper gridheight, gridwidth, gridx and
* gridy on the provided GridBagConstraints.
* @param baseGbc the GridBagConstraints to be used.
* @param p the container.
*/
protected void addErrorPane(Container p, GridBagConstraints baseGbc)
{
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = baseGbc.gridx;
gbc.gridy = baseGbc.gridy;
gbc.gridwidth = baseGbc.gridwidth;
gbc.gridheight = baseGbc.gridheight;
gbc.weightx = 1.0;
gbc.fill = GridBagConstraints.BOTH;
if (requiresBorder())
{
gbc.insets = new Insets(0, 0, 10, 0);
}
else
{
gbc.insets = new Insets(20, 20, 0, 20);
}
createErrorPane();
p.add(errorPane, gbc);
}
/**
* Creates the error pane.
*/
protected void createErrorPane()
{
errorPane = Utilities.makeHtmlPane("", ColorAndFontConstants.progressFont);
errorPane.setOpaque(false);
errorPane.setEditable(false);
errorPane.setVisible(false);
CustomHTMLEditorKit htmlEditor = new CustomHTMLEditorKit();
htmlEditor.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ev)
{
if (AUTHENTICATE.toString().equals(ev.getActionCommand()))
{
authenticate();
}
else if (START.toString().equals(ev.getActionCommand()))
{
startServer();
}
}
});
errorPane.setEditorKit(htmlEditor);
}
/**
* Commodity method used to add lines, where each line contains a label, a
* component and an inline help label.
* @param labels the labels.
* @param comps the components.
* @param inlineHelp the inline help labels.
* @param panel the panel where we will add the lines.
* @param gbc the grid bag constraints.
*/
protected void add(JLabel[] labels, Component[] comps, JLabel[] inlineHelp,
Container panel, GridBagConstraints gbc)
{
int i = 0;
for (Component comp : comps)
{
gbc.insets.left = 0;
gbc.weightx = 0.0;
gbc.gridx = 0;
if (labels[i] != null)
{
panel.add(labels[i], gbc);
}
gbc.insets.left = 10;
gbc.weightx = 1.0;
gbc.gridx = 1;
panel.add(comp, gbc);
if (inlineHelp[i] != null)
{
gbc.insets.top = 3;
gbc.gridy ++;
panel.add(inlineHelp[i], gbc);
}
gbc.insets.top = 10;
gbc.gridy ++;
i++;
}
}
/**
* Enables the OK button in the parent dialog.
* @param enable whether to enable or disable the button.
*/
protected void setEnabledOK(boolean enable)
{
Window w = Utilities.getParentDialog(this);
if (w instanceof GenericDialog)
{
((GenericDialog)w).setEnabledOK(enable);
}
else if (w instanceof GenericFrame)
{
((GenericFrame)w).setEnabledOK(enable);
}
enableOK = enable;
}
/**
* Enables the Cancel button in the parent dialog.
* @param enable whether to enable or disable the button.
*/
protected void setEnabledCancel(boolean enable)
{
Window w = Utilities.getParentDialog(this);
if (w instanceof GenericDialog)
{
((GenericDialog)w).setEnabledCancel(enable);
}
else if (w instanceof GenericFrame)
{
((GenericFrame)w).setEnabledCancel(enable);
}
enableCancel = enable;
}
/**
* Updates the font type and color of the component to be invalid and
* primary.
* @param comp the component to update.
*/
protected void setPrimaryInvalid(JComponent comp)
{
comp.setFont(ColorAndFontConstants.primaryInvalidFont);
comp.setForeground(ColorAndFontConstants.invalidFontColor);
}
/**
* Updates the font type and color of the component to be valid and
* primary.
* @param comp the component to update.
*/
protected void setPrimaryValid(JComponent comp)
{
comp.setForeground(ColorAndFontConstants.validFontColor);
comp.setFont(ColorAndFontConstants.primaryFont);
}
/**
* Updates the font type and color of the component to be invalid and
* secondary.
* @param comp the component to update.
*/
protected void setSecondaryInvalid(JComponent comp)
{
comp.setForeground(ColorAndFontConstants.invalidFontColor);
comp.setFont(ColorAndFontConstants.invalidFont);
}
/**
* Updates the font type and color of the component to be valid and
* secondary.
* @param comp the component to update.
*/
protected void setSecondaryValid(JComponent comp)
{
comp.setForeground(ColorAndFontConstants.validFontColor);
comp.setFont(ColorAndFontConstants.defaultFont);
}
/**
* Packs the parent dialog.
*
*/
protected void packParentDialog()
{
Window dlg = Utilities.getParentDialog(this);
if (dlg != null)
{
invalidate();
dlg.invalidate();
dlg.pack();
if (!SwingUtilities.isEventDispatchThread())
{
Thread.dumpStack();
}
}
}
/**
* Notification that the ok button has been clicked, the panel is in charge
* of doing whatever is required (close the dialog, launch a task, etc.).
*
*/
abstract public void okClicked();
/**
* Adds a configuration element created listener.
* @param listener the listener.
*/
public void addConfigurationElementCreatedListener(
ConfigurationElementCreatedListener listener)
{
getConfigurationElementCreatedListeners().add(listener);
}
/**
* Removes a configuration element created listener.
* @param listener the listener.
*/
public void removeConfigurationElementCreatedListener(
ConfigurationElementCreatedListener listener)
{
getConfigurationElementCreatedListeners().remove(listener);
}
/**
* Notifies the configuration element created listener that a new object has
* been created.
* @param configObject the created object.
*/
protected void notifyConfigurationElementCreated(Object configObject)
{
for (ConfigurationElementCreatedListener listener :
getConfigurationElementCreatedListeners())
{
listener.elementCreated(
new ConfigurationElementCreatedEvent(this, configObject));
}
}
/**
* Returns the list of configuration listeners.
* @return the list of configuration listeners.
*/
protected List<ConfigurationElementCreatedListener>
getConfigurationElementCreatedListeners()
{
return confListeners;
}
/**
* Notification that cancel was clicked, the panel is in charge
* of doing whatever is required (close the dialog, etc.).
*
*/
public void cancelClicked()
{
// Default implementation
Utilities.getParentDialog(this).setVisible(false);
if (isDisposeOnClose())
{
Utilities.getParentDialog(this).dispose();
}
}
/**
* Whether the dialog should be disposed when the user closes it.
* @return <CODE>true</CODE> if the dialog should be disposed when the user
* closes it or <CODE>true</CODE> otherwise.
*/
public boolean isDisposeOnClose()
{
return disposeOnClose;
}
/**
* Sets whether the dialog should be disposed when the user closes it or not.
* @param disposeOnClose <CODE>true</CODE> if the dialog should be disposed
* when the user closes it or <CODE>true</CODE> otherwise.
*/
public void setDisposeOnClose(boolean disposeOnClose)
{
this.disposeOnClose = disposeOnClose;
}
/**
* Notification that close was clicked, the panel is in charge
* of doing whatever is required (close the dialog, etc.).
*
*/
public void closeClicked()
{
// Default implementation
Utilities.getParentDialog(this).setVisible(false);
if (isDisposeOnClose())
{
Utilities.getParentDialog(this).dispose();
}
}
/**
* Displays a dialog with the provided list of error messages.
* @param errors the error messages.
*/
protected void displayErrorDialog(Collection<Message> errors)
{
Utilities.displayErrorDialog(Utilities.getParentDialog(this), errors);
}
/**
* Displays a confirmation message.
* @param title the title/summary of the message.
* @param msg the description of the confirmation.
* @return <CODE>true</CODE> if the user confirms and <CODE>false</CODE>
* otherwise.
*/
protected boolean displayConfirmationDialog(Message title, Message msg)
{
return Utilities.displayConfirmationDialog(Utilities.getParentDialog(this),
title, msg);
}
/**
* If the index must be rebuilt, asks the user for confirmation. If the user
* confirms launches a task that will rebuild the indexes. The progress will
* be displayed in the provided progress dialog.
* @param index the index.
* @param progressDialog the progress dialog.
*/
protected void rebuildIndexIfNecessary(AbstractIndexDescriptor index,
ProgressDialog progressDialog)
{
progressDialog.setTaskIsOver(false);
boolean rebuildIndexes;
String backendName = index.getBackend().getBackendID();
if (!isServerRunning())
{
rebuildIndexes = Utilities.displayConfirmationDialog(progressDialog,
INFO_CTRL_PANEL_INDEX_REBUILD_REQUIRED_SUMMARY.get(),
INFO_CTRL_PANEL_INDEX_REBUILD_REQUIRED_OFFLINE_DETAILS.get(
index.getName(), backendName));
}
else
{
if (isLocal())
{
rebuildIndexes = Utilities.displayConfirmationDialog(progressDialog,
INFO_CTRL_PANEL_INDEX_REBUILD_REQUIRED_SUMMARY.get(),
INFO_CTRL_PANEL_INDEX_REBUILD_REQUIRED_ONLINE_DETAILS.get(
index.getName(), backendName, backendName));
}
else
{
Utilities.displayWarningDialog(progressDialog,
INFO_CTRL_PANEL_INDEX_REBUILD_REQUIRED_SUMMARY.get(),
INFO_CTRL_PANEL_INDEX_REBUILD_REQUIRED_REMOTE_DETAILS.get(
index.getName(), backendName));
rebuildIndexes = false;
}
}
if (rebuildIndexes)
{
SortedSet<AbstractIndexDescriptor> indexes =
new TreeSet<AbstractIndexDescriptor>();
indexes.add(index);
SortedSet<String> baseDNs = new TreeSet<String>();
for (BaseDNDescriptor b : index.getBackend().getBaseDns())
{
String baseDN = Utilities.unescapeUtf8(b.getDn().toString());
baseDNs.add(baseDN);
}
RebuildIndexTask newTask = new RebuildIndexTask(getInfo(),
progressDialog, baseDNs, indexes);
ArrayList<Message> errors = new ArrayList<Message>();
for (Task task : getInfo().getTasks())
{
task.canLaunch(newTask, errors);
}
if (errors.size() == 0)
{
progressDialog.appendProgressHtml("<br><br>");
launchOperation(newTask,
INFO_CTRL_PANEL_REBUILDING_INDEXES_SUMMARY.get(backendName),
INFO_CTRL_PANEL_REBUILDING_INDEXES_SUCCESSFUL_SUMMARY.get(),
INFO_CTRL_PANEL_REBUILDING_INDEXES_SUCCESSFUL_DETAILS.get(),
ERR_CTRL_PANEL_REBUILDING_INDEXES_ERROR_SUMMARY.get(),
null,
ERR_CTRL_PANEL_REBUILDING_INDEXES_ERROR_DETAILS,
progressDialog, false);
if (progressDialog.isModal())
{
progressDialog.toFront();
}
progressDialog.setVisible(true);
if (!progressDialog.isModal())
{
progressDialog.toFront();
}
}
if (errors.size() > 0)
{
displayErrorDialog(errors);
}
}
else
{
progressDialog.setTaskIsOver(true);
if (progressDialog.isVisible())
{
progressDialog.toFront();
}
}
}
/**
* A class used to avoid the possibility a certain type of objects in a combo
* box. This is used for instance in the combo box that contains base DNs
* where the base DNs are separated in backends, so the combo box displays
* both the backends (~ categories) and base DNs (~ values) and we do not
* allow to select the backends (~ categories).
*
*/
protected class IgnoreItemListener implements ItemListener
{
private Object selectedItem;
private JComboBox combo;
/**
* Constructor.
* @param combo the combo box.
*/
public IgnoreItemListener(JComboBox combo)
{
this.combo = combo;
selectedItem = combo.getSelectedItem();
if (isCategory(selectedItem))
{
selectedItem = null;
}
}
/**
* {@inheritDoc}
*/
public void itemStateChanged(ItemEvent ev)
{
Object o = combo.getSelectedItem();
if (isCategory(o))
{
if (selectedItem == null)
{
// Look for the first element that is not a category
for (int i=0; i<combo.getModel().getSize(); i++)
{
Object item = combo.getModel().getElementAt(i);
if (item instanceof CategorizedComboBoxElement)
{
if (!isCategory(item))
{
selectedItem = item;
break;
}
}
}
}
if (selectedItem != null)
{
combo.setSelectedItem(selectedItem);
}
}
else if (COMBO_SEPARATOR.equals(o))
{
combo.setSelectedItem(selectedItem);
}
else
{
selectedItem = o;
}
}
}
/**
* Returns the HTML required to render an Authenticate button in HTML.
* @return the HTML required to render an Authenticate button in HTML.
*/
protected String getAuthenticateHTML()
{
return "<INPUT type=\"submit\" value=\""+AUTHENTICATE+"\"></INPUT>";
}
/**
* Returns the HTML required to render an Start button in HTML.
* @return the HTML required to render an Start button in HTML.
*/
protected String getStartServerHTML()
{
return "<INPUT type=\"submit\" value=\""+START+"\"></INPUT>";
}
/**
* Updates the error panel and enables/disables the OK button depending on
* the status of the server.
* @param desc the Server Descriptor.
* @param details the message to be displayed if authentication has not been
* provided and the server is running.
*/
protected void updateErrorPaneAndOKButtonIfAuthRequired(ServerDescriptor desc,
Message details)
{
if (authenticationRequired(desc))
{
MessageBuilder mb = new MessageBuilder();
mb.append(details);
mb.append("<br><br>"+getAuthenticateHTML());
Message title = INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_SUMMARY.get();
updateErrorPane(errorPane, title, ColorAndFontConstants.errorTitleFont,
mb.toMessage(), ColorAndFontConstants.defaultFont);
SwingUtilities.invokeLater(new Runnable()
{
/**
* {@inheritDoc}
*/
public void run()
{
errorPane.setVisible(true);
packParentDialog();
setEnabledOK(false);
}
});
}
else
{
SwingUtilities.invokeLater(new Runnable()
{
/**
* {@inheritDoc}
*/
public void run()
{
errorPane.setVisible(false);
checkOKButtonEnable();
}
});
}
}
/**
* Returns <CODE>true</CODE> if the server is running and the user did not
* provide authentication and <CODE>false</CODE> otherwise.
* @param desc the server descriptor.
* @return <CODE>true</CODE> if the server is running and the user did not
* provide authentication and <CODE>false</CODE> otherwise.
*/
protected boolean authenticationRequired(ServerDescriptor desc)
{
boolean returnValue;
ServerDescriptor.ServerStatus status = desc.getStatus();
if (((status == ServerDescriptor.ServerStatus.STARTED) &&
!desc.isAuthenticated()) ||
(status == ServerDescriptor.ServerStatus.NOT_CONNECTED_TO_REMOTE))
{
returnValue = true;
}
else
{
returnValue = false;
}
return returnValue;
}
/**
* Updates the error panel depending on the status of the server.
* @param desc the Server Descriptor.
* @param details the message to be displayed if authentication has not been
* provided and the server is running.
*/
protected void updateErrorPaneIfAuthRequired(ServerDescriptor desc,
Message details)
{
if (authenticationRequired(desc))
{
Message title = INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_SUMMARY.get();
MessageBuilder mb = new MessageBuilder();
mb.append(details);
mb.append("<br><br>"+getAuthenticateHTML());
updateErrorPane(errorPane, title, ColorAndFontConstants.errorTitleFont,
mb.toMessage(), ColorAndFontConstants.defaultFont);
SwingUtilities.invokeLater(new Runnable()
{
/**
* {@inheritDoc}
*/
public void run()
{
errorPane.setVisible(true);
packParentDialog();
}
});
}
else
{
SwingUtilities.invokeLater(new Runnable()
{
/**
* {@inheritDoc}
*/
public void run()
{
errorPane.setVisible(false);
}
});
}
}
/**
* Updates the error panel depending on the status of the server. This
* method will display an error message in the error pane if the server is not
* running and another message if the server is running but authentication
* has not been provided.
* @param desc the Server Descriptor.
* @param detailsServerNotRunning the message to be displayed if the server is
* not running.
* @param authRequired the message to be displayed if authentication has not
* been provided and the server is running.
*/
protected void updateErrorPaneIfServerRunningAndAuthRequired(
ServerDescriptor desc, Message detailsServerNotRunning,
Message authRequired)
{
ServerDescriptor.ServerStatus status = desc.getStatus();
if ((status != ServerDescriptor.ServerStatus.STARTED) &&
(status != ServerDescriptor.ServerStatus.NOT_CONNECTED_TO_REMOTE))
{
Message title = INFO_CTRL_PANEL_SERVER_NOT_RUNNING_SUMMARY.get();
MessageBuilder mb = new MessageBuilder();
mb.append(detailsServerNotRunning);
mb.append("<br><br>"+getStartServerHTML());
updateErrorPane(errorPane, title, ColorAndFontConstants.errorTitleFont,
mb.toMessage(),
ColorAndFontConstants.defaultFont);
SwingUtilities.invokeLater(new Runnable()
{
/**
* {@inheritDoc}
*/
public void run()
{
errorPane.setVisible(true);
packParentDialog();
}
});
}
else if (authenticationRequired(desc))
{
Message title = INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_SUMMARY.get();
MessageBuilder mb = new MessageBuilder();
mb.append(authRequired);
mb.append("<br><br>"+getAuthenticateHTML());
updateErrorPane(errorPane, title, ColorAndFontConstants.errorTitleFont,
mb.toMessage(), ColorAndFontConstants.defaultFont);
SwingUtilities.invokeLater(new Runnable()
{
/**
* {@inheritDoc}
*/
public void run()
{
errorPane.setVisible(true);
packParentDialog();
}
});
}
else
{
SwingUtilities.invokeLater(new Runnable()
{
/**
* {@inheritDoc}
*/
public void run()
{
errorPane.setVisible(false);
}
});
}
}
/**
* Updates the enabling/disabling of the OK button. The code assumes that
* the error pane has already been updated.
*
*/
protected void checkOKButtonEnable()
{
setEnabledOK(!errorPane.isVisible());
}
/**
* Returns <CODE>true</CODE> if the provided object is a category object in
* a combo box.
* @param o the item in the combo box.
* @return <CODE>true</CODE> if the provided object is a category object in
* a combo box.
*/
protected boolean isCategory(Object o)
{
boolean isCategory = false;
if (o instanceof CategorizedComboBoxElement)
{
CategorizedComboBoxElement desc = (CategorizedComboBoxElement)o;
isCategory = desc.getType() == CategorizedComboBoxElement.Type.CATEGORY;
}
return isCategory;
}
/**
* Returns the control panel info object.
* @return the control panel info object.
*/
public ControlPanelInfo getInfo()
{
return info;
}
/**
* Sets the control panel info object.
* @param info the control panel info object.
*/
public void setInfo(ControlPanelInfo info)
{
if (!info.equals(this.info))
{
if (this.info != null)
{
this.info.removeConfigChangeListener(this);
}
this.info = info;
this.info.addConfigChangeListener(this);
if (SwingUtilities.isEventDispatchThread() &&
callConfigurationChangedInBackground())
{
final Color savedBackground = getBackground();
setBackground(ColorAndFontConstants.background);
if (!sizeSet)
{
setPreferredSize(mainPanel.getPreferredSize());
sizeSet = true;
}
// Do it outside the event thread if the panel requires it.
BackgroundTask<Void> worker = new BackgroundTask<Void>()
{
public Void processBackgroundTask() throws Throwable
{
try
{
Thread.sleep(1000);
}
catch (Throwable t)
{
}
configurationChanged(new ConfigurationChangeEvent(
StatusGenericPanel.this.info,
StatusGenericPanel.this.info.getServerDescriptor()));
return null;
}
public void backgroundTaskCompleted(Void returnValue,
Throwable t)
{
setBackground(savedBackground);
displayMainPanel();
if (!focusSet)
{
focusSet = true;
Component comp = getPreferredFocusComponent();
if (comp != null)
{
comp.requestFocusInWindow();
}
}
}
};
displayMessage(INFO_CTRL_PANEL_LOADING_PANEL_SUMMARY.get());
worker.startBackgroundTask();
}
else if (info.getServerDescriptor() != null)
{
configurationChanged(new ConfigurationChangeEvent(
this.info, this.info.getServerDescriptor()));
}
}
}
/**
* Displays the main panel.
*
*/
protected void displayMainPanel()
{
cardLayout.show(cardPanel, MAIN_PANEL);
}
/**
* Returns whether the main panel is visible or not.
* @return whether the main panel is visible or not.
*/
protected boolean isMainPanelVisible()
{
return mainPanel.isVisible();
}
/**
* Displays a message and hides the main panel.
* @param msg the message to be displayed.
*/
protected void displayMessage(Message msg)
{
message.setText(Utilities.applyFont(msg.toString(),
ColorAndFontConstants.defaultFont));
cardLayout.show(cardPanel, MESSAGE_PANEL);
message.requestFocusInWindow();
}
/**
* Displays an error message and hides the main panel.
* @param title the title of the message to be displayed.
* @param msg the message to be displayed.
*/
protected void displayErrorMessage(Message title, Message msg)
{
updateErrorPane(message, title, ColorAndFontConstants.errorTitleFont,
msg, ColorAndFontConstants.defaultFont);
cardLayout.show(cardPanel, MESSAGE_PANEL);
message.requestFocusInWindow();
}
/**
* Returns whether the message is visible or not.
* @return whether the message is visible or not.
*/
protected boolean isMessageVisible()
{
return message.isVisible();
}
/**
* Updates the contents of an editor pane using the error format.
* @param pane the editor pane to be updated.
* @param title the title.
* @param titleFont the font to be used for the title.
* @param details the details message.
* @param detailsFont the font to be used for the details.
*/
protected void updateErrorPane(JEditorPane pane, Message title,
Font titleFont, Message details, Font detailsFont)
{
updatePane(pane, title, titleFont, details, detailsFont, PanelType.ERROR);
}
/**
* Updates the contents of an editor pane using the warning format.
* @param pane the editor pane to be updated.
* @param title the title.
* @param titleFont the font to be used for the title.
* @param details the details message.
* @param detailsFont the font to be used for the details.
*/
protected void updateWarningPane(JEditorPane pane, Message title,
Font titleFont, Message details, Font detailsFont)
{
updatePane(pane, title, titleFont, details, detailsFont, PanelType.WARNING);
}
/**
* Updates the contents of an editor pane using the confirmation format.
* @param pane the editor pane to be updated.
* @param title the title.
* @param titleFont the font to be used for the title.
* @param details the details message.
* @param detailsFont the font to be used for the details.
*/
protected void updateConfirmationPane(JEditorPane pane, Message title,
Font titleFont, Message details, Font detailsFont)
{
updatePane(pane, title, titleFont, details, detailsFont,
PanelType.CONFIRMATION);
}
/**
* The different types of error panels that are handled.
*
*/
protected enum PanelType
{
/**
* The message in the panel is an error.
*/
ERROR,
/**
* The message in the panel is a confirmation.
*/
CONFIRMATION,
/**
* The message in the panel is an information message.
*/
INFORMATION,
/**
* The message in the panel is a warning message.
*/
WARNING
};
/**
* Updates the contents of an editor pane using the provided format.
* @param pane the editor pane to be updated.
* @param title the title.
* @param titleFont the font to be used for the title.
* @param details the details message.
* @param detailsFont the font to be used for the details.
* @param type the type of panel.
*/
private void updatePane(final JEditorPane pane, Message title,
Font titleFont, Message details, Font detailsFont, PanelType type)
{
String text;
switch (type)
{
case ERROR:
text = Utilities.getFormattedError(title, titleFont, details,
detailsFont);
break;
case CONFIRMATION:
text = Utilities.getFormattedConfirmation(title, titleFont, details,
detailsFont);
break;
case WARNING:
text = Utilities.getFormattedWarning(title, titleFont, details,
detailsFont);
break;
default:
text = Utilities.getFormattedSuccess(title, titleFont, details,
detailsFont);
break;
}
if (!text.equals(lastDisplayedError))
{
Message wrappedTitle = Utilities.wrapHTML(title, 80);
Message wrappedDetails = Utilities.wrapHTML(details, 90);
JEditorPane wrappedPane = Utilities.makeHtmlPane(null, pane.getFont());
String wrappedText;
switch (type)
{
case ERROR:
wrappedText = Utilities.getFormattedError(wrappedTitle, titleFont,
wrappedDetails, detailsFont);
break;
default:
wrappedText = Utilities.getFormattedSuccess(wrappedTitle, titleFont,
wrappedDetails, detailsFont);
break;
}
wrappedPane.setText(wrappedText);
Dimension d = wrappedPane.getPreferredSize();
pane.setText(text);
pane.setPreferredSize(d);
lastDisplayedError = text;
}
final Window window =
Utilities.getParentDialog(StatusGenericPanel.this);
if (window != null)
{
SwingUtilities.invokeLater(new Runnable()
{
/**
* {@inheritDoc}
*/
public void run()
{
pane.invalidate();
window.validate();
}
});
}
}
/**
* Commodity method used to update the elements of a combo box that contains
* the different user backends. If no backends are found the combo box will
* be made invisible and a label will be made visible. This method does not
* update the label's text nor creates any layout.
* @param combo the combo to be updated.
* @param lNoBackendsFound the label that must be shown if no user backends
* are found.
* @param desc the server descriptor that contains the configuration.
*/
protected void updateSimpleBackendComboBoxModel(final JComboBox combo,
final JLabel lNoBackendsFound, ServerDescriptor desc)
{
final SortedSet<String> newElements =
new TreeSet<String>(new LowerCaseComparator());
for (BackendDescriptor backend : desc.getBackends())
{
if (!backend.isConfigBackend())
{
newElements.add(backend.getBackendID());
}
}
DefaultComboBoxModel model = (DefaultComboBoxModel)combo.getModel();
updateComboBoxModel(newElements, model);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
combo.setVisible(newElements.size() > 0);
lNoBackendsFound.setVisible(newElements.size() == 0);
}
});
}
/**
* Method that says if a backend must be displayed. Only non-config backends
* are displayed.
* @param backend the backend.
* @return <CODE>true</CODE> if the backend must be displayed and
* <CODE>false</CODE> otherwise.
*/
protected boolean displayBackend(BackendDescriptor backend)
{
return !backend.isConfigBackend();
}
/**
* Commodity method to update a combo box model with the backends of a server.
* @param model the combo box model to be updated.
* @param desc the server descriptor containing the configuration.
*/
protected void updateBaseDNComboBoxModel(DefaultComboBoxModel model,
ServerDescriptor desc)
{
LinkedHashSet<CategorizedComboBoxElement> newElements =
new LinkedHashSet<CategorizedComboBoxElement>();
SortedSet<String> backendIDs =
new TreeSet<String>(new LowerCaseComparator());
HashMap<String, SortedSet<String>> hmBaseDNs =
new HashMap<String, SortedSet<String>>();
for (BackendDescriptor backend : desc.getBackends())
{
if (displayBackend(backend))
{
String backendID = backend.getBackendID();
backendIDs.add(backendID);
SortedSet<String> baseDNs =
new TreeSet<String>(new LowerCaseComparator());
for (BaseDNDescriptor baseDN : backend.getBaseDns())
{
try
{
baseDNs.add(Utilities.unescapeUtf8(baseDN.getDn().toString()));
}
catch (Throwable t)
{
throw new RuntimeException("Unexpected error: "+t, t);
}
}
hmBaseDNs.put(backendID, baseDNs);
}
}
for (String backendID : backendIDs)
{
newElements.add(new CategorizedComboBoxElement(backendID,
CategorizedComboBoxElement.Type.CATEGORY));
SortedSet<String> baseDNs = hmBaseDNs.get(backendID);
for (String baseDN : baseDNs)
{
newElements.add(new CategorizedComboBoxElement(baseDN,
CategorizedComboBoxElement.Type.REGULAR));
}
}
updateComboBoxModel(newElements, model);
}
/**
* Updates a combo box model with a number of items.
* @param newElements the new items for the combo box model.
* @param model the combo box model to be updated.
*/
protected void updateComboBoxModel(Collection<?> newElements,
DefaultComboBoxModel model)
{
updateComboBoxModel(newElements, model, null);
}
/**
* Updates a combo box model with a number of items.
* The method assumes that is called outside the event thread.
* @param newElements the new items for the combo box model.
* @param model the combo box model to be updated.
* @param comparator the object that will be used to compare the objects in
* the model. If <CODE>null</CODE>, the equals method will be used.
*/
protected void updateComboBoxModel(final Collection<?> newElements,
final DefaultComboBoxModel model,
final Comparator<Object> comparator)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
Utilities.updateComboBoxModel(newElements, model, comparator);
}
});
}
/**
* Updates a map, so that the keys are the base DN where the indexes are
* defined and the values are a sorted set of indexes.
* @param desc the server descriptor containing the index configuration.
* @param hmIndexes the map to be updated.
*/
protected void updateIndexMap(ServerDescriptor desc,
HashMap<String, SortedSet<AbstractIndexDescriptor>> hmIndexes)
{
synchronized (hmIndexes)
{
HashSet<String> dns = new HashSet<String>();
for (BackendDescriptor backend : desc.getBackends())
{
if (backend.getType() == BackendDescriptor.Type.LOCAL_DB)
{
for (BaseDNDescriptor baseDN : backend.getBaseDns())
{
String dn;
try
{
dn = Utilities.unescapeUtf8(baseDN.getDn().toString());
}
catch (Throwable t)
{
throw new RuntimeException("Unexpected error: "+t, t);
}
dns.add(dn);
SortedSet<AbstractIndexDescriptor> indexes =
new TreeSet<AbstractIndexDescriptor>();
indexes.addAll(backend.getIndexes());
indexes.addAll(backend.getVLVIndexes());
SortedSet<AbstractIndexDescriptor> currentIndexes =
hmIndexes.get(dn);
if (currentIndexes != null)
{
if (!currentIndexes.equals(indexes))
{
hmIndexes.put(dn, indexes);
}
}
else
{
hmIndexes.put(dn, indexes);
}
}
}
}
for (String dn : new HashSet<String>(hmIndexes.keySet()))
{
if (!dns.contains(dn))
{
hmIndexes.remove(dn);
}
}
}
}
/**
* Updates and addremove panel with the contents of the provided item. The
* selected item represents a base DN.
* @param hmIndexes the map that contains the indexes definitions as values
* and the base DNs as keys.
* @param selectedItem the selected item.
* @param addRemove the add remove panel to be updated.
*/
protected void comboBoxSelected(
HashMap<String, SortedSet<AbstractIndexDescriptor>> hmIndexes,
CategorizedComboBoxElement selectedItem,
AddRemovePanel<AbstractIndexDescriptor> addRemove)
{
synchronized (hmIndexes)
{
String selectedDn = null;
if (selectedItem != null)
{
selectedDn = (String)selectedItem.getValue();
}
if (selectedDn != null)
{
SortedSet<AbstractIndexDescriptor> indexes =
hmIndexes.get(selectedDn);
if (indexes != null)
{
boolean availableChanged = false;
boolean selectedChanged = false;
SortedSet<AbstractIndexDescriptor> availableIndexes =
addRemove.getAvailableListModel().getData();
SortedSet<AbstractIndexDescriptor> selectedIndexes =
addRemove.getSelectedListModel().getData();
availableChanged = availableIndexes.retainAll(indexes);
selectedChanged = selectedIndexes.retainAll(indexes);
for (AbstractIndexDescriptor index : indexes)
{
if (!availableIndexes.contains(index) &&
!selectedIndexes.contains(index))
{
availableIndexes.add(index);
availableChanged = true;
}
}
if (availableChanged)
{
addRemove.getAvailableListModel().clear();
addRemove.getAvailableListModel().addAll(availableIndexes);
addRemove.getAvailableListModel().fireContentsChanged(
addRemove.getAvailableListModel(), 0,
addRemove.getAvailableListModel().getSize());
}
if (selectedChanged)
{
addRemove.getSelectedListModel().clear();
addRemove.getSelectedListModel().addAll(selectedIndexes);
addRemove.getSelectedListModel().fireContentsChanged(
addRemove.getSelectedListModel(), 0,
addRemove.getSelectedListModel().getSize());
}
}
}
}
}
/**
* Returns <CODE>true</CODE> if the cancel button is enabled and
* <CODE>false</CODE> otherwise.
* @return <CODE>true</CODE> if the cancel button is enabled and
* <CODE>false</CODE> otherwise.
*/
public boolean isEnableCancel()
{
return enableCancel;
}
/**
* Returns <CODE>true</CODE> if the close button is enabled and
* <CODE>false</CODE> otherwise.
* @return <CODE>true</CODE> if the close button is enabled and
* <CODE>false</CODE> otherwise.
*/
public boolean isEnableClose()
{
return enableClose;
}
/**
* Returns <CODE>true</CODE> if the ok button is enabled and
* <CODE>false</CODE> otherwise.
* @return <CODE>true</CODE> if the ok button is enabled and
* <CODE>false</CODE> otherwise.
*/
public boolean isEnableOK()
{
return enableOK;
}
/**
* Returns <CODE>true</CODE> if the server is running and
* <CODE>false</CODE> otherwise.
* @return <CODE>true</CODE> if the server is running and
* <CODE>false</CODE> otherwise.
*/
protected boolean isServerRunning()
{
return getInfo().getServerDescriptor().getStatus() ==
ServerDescriptor.ServerStatus.STARTED;
}
/**
* Returns <CODE>true</CODE> if the managed server is the local installation
* (where the control panel is installed) <CODE>false</CODE> otherwise.
* @return <CODE>true</CODE> if the managed server is the local installation
* (where the control panel is installed) <CODE>false</CODE> otherwise.
*/
protected boolean isLocal()
{
return getInfo().getServerDescriptor().isLocal();
}
/**
* Launch an task.
* @param task the task to be launched.
* @param initialSummary the initial summary to be displayed in the progress
* dialog.
* @param successSummary the success summary to be displayed in the progress
* dialog if the task is successful.
* @param successDetail the success details to be displayed in the progress
* dialog if the task is successful.
* @param errorSummary the error summary to be displayed in the progress
* dialog if the task ended with error.
* @param errorDetail error details to be displayed in the progress
* dialog if the task ended with error.
* @param errorDetailCode error detail message to be displayed in the progress
* dialog if the task ended with error and we have an exit error code (for
* instance if the error occurred when launching a script we will have an
* error code).
* @param dialog the progress dialog.
*/
protected void launchOperation(final Task task, Message initialSummary,
final Message successSummary, final Message successDetail,
final Message errorSummary,
final Message errorDetail,
final MessageDescriptor.Arg1<Number> errorDetailCode,
final ProgressDialog dialog)
{
launchOperation(task, initialSummary, successSummary, successDetail,
errorSummary, errorDetail, errorDetailCode, dialog, true);
}
/**
* Launch an task.
* @param task the task to be launched.
* @param initialSummary the initial summary to be displayed in the progress
* dialog.
* @param successSummary the success summary to be displayed in the progress
* dialog if the task is successful.
* @param successDetail the success details to be displayed in the progress
* dialog if the task is successful.
* @param errorSummary the error summary to be displayed in the progress
* dialog if the task ended with error.
* @param errorDetail error details to be displayed in the progress
* dialog if the task ended with error.
* @param errorDetailCode error detail message to be displayed in the progress
* dialog if the task ended with error and we have an exit error code (for
* instance if the error occurred when launching a script we will have an
* error code).
* @param dialog the progress dialog.
* @param resetLogs whether the contents of the progress dialog should be
* reset or not.
*/
protected void launchOperation(final Task task, Message initialSummary,
final Message successSummary, final Message successDetail,
final Message errorSummary,
final Message errorDetail,
final MessageDescriptor.Arg1<Number> errorDetailCode,
final ProgressDialog dialog, boolean resetLogs)
{
launchOperation(task, initialSummary, successSummary, successDetail,
errorSummary, errorDetail, errorDetailCode, dialog, resetLogs,
getInfo());
}
/**
* Launch an task.
* @param task the task to be launched.
* @param initialSummary the initial summary to be displayed in the progress
* dialog.
* @param successSummary the success summary to be displayed in the progress
* dialog if the task is successful.
* @param successDetail the success details to be displayed in the progress
* dialog if the task is successful.
* @param errorSummary the error summary to be displayed in the progress
* dialog if the task ended with error.
* @param errorDetail error details to be displayed in the progress
* dialog if the task ended with error.
* @param errorDetailCode error detail message to be displayed in the progress
* dialog if the task ended with error and we have an exit error code (for
* instance if the error occurred when launching a script we will have an
* error code).
* @param dialog the progress dialog.
* @param resetLogs whether the contents of the progress dialog should be
* reset or not.
* @param info the ControlPanelInfo.
*/
public static void launchOperation(final Task task, Message initialSummary,
final Message successSummary, final Message successDetail,
final Message errorSummary,
final Message errorDetail,
final MessageDescriptor.Arg1<Number> errorDetailCode,
final ProgressDialog dialog, boolean resetLogs,
final ControlPanelInfo info)
{
dialog.setTaskIsOver(false);
dialog.getProgressBar().setIndeterminate(true);
dialog.addPrintStreamListeners(task.getOutPrintStream(),
task.getErrorPrintStream());
if (resetLogs)
{
dialog.resetProgressLogs();
}
String cmdLine = task.getCommandLineToDisplay();
if (cmdLine != null)
{
dialog.appendProgressHtml(Utilities.applyFont(
INFO_CTRL_PANEL_EQUIVALENT_COMMAND_LINE.get()+"<br><b>"+cmdLine+
"</b><br><br>",
ColorAndFontConstants.progressFont));
}
dialog.setEnabledClose(false);
dialog.setSummary(Message.raw(
Utilities.applyFont(initialSummary.toString(),
ColorAndFontConstants.defaultFont)));
dialog.getProgressBar().setVisible(true);
BackgroundTask<Task> worker = new BackgroundTask<Task>()
{
/**
* {@inheritDoc}
*/
public Task processBackgroundTask() throws Throwable
{
task.runTask();
if (task.regenerateDescriptor())
{
info.regenerateDescriptor();
}
return task;
}
/**
* {@inheritDoc}
*/
public void backgroundTaskCompleted(Task returnValue, Throwable t)
{
String summaryMsg;
if (task.getState() == Task.State.FINISHED_SUCCESSFULLY)
{
summaryMsg = Utilities.getFormattedSuccess(successSummary,
ColorAndFontConstants.errorTitleFont,
successDetail, ColorAndFontConstants.defaultFont);
}
else
{
if (t == null)
{
t = task.getLastException();
}
if (t != null)
{
LOG.log(Level.WARNING, "Error occurred running task: "+t, t);
if ((task.getReturnCode() != null) &&
(errorDetailCode != null))
{
String sThrowable;
if (t instanceof OpenDsException)
{
sThrowable = ((OpenDsException)t).getMessageObject().toString();
}
else
{
if (t.getMessage() != null)
{
sThrowable = t.getMessage();
}
else
{
sThrowable = t.toString();
}
}
MessageBuilder mb = new MessageBuilder();
mb.append(errorDetailCode.get(task.getReturnCode()));
mb.append(
" "+INFO_CTRL_PANEL_DETAILS_THROWABLE.get(sThrowable));
summaryMsg = Utilities.getFormattedError(errorSummary,
ColorAndFontConstants.errorTitleFont,
mb.toMessage(), ColorAndFontConstants.defaultFont);
}
else if (errorDetail != null)
{
MessageBuilder mb = new MessageBuilder();
mb.append(errorDetail);
mb.append(
" "+INFO_CTRL_PANEL_DETAILS_THROWABLE.get(t.toString()));
summaryMsg = Utilities.getFormattedError(errorSummary,
ColorAndFontConstants.errorTitleFont,
mb.toMessage(), ColorAndFontConstants.defaultFont);
}
else
{
summaryMsg = null;
}
}
else if ((task.getReturnCode() != null) &&
(errorDetailCode != null))
{
summaryMsg = Utilities.getFormattedError(errorSummary,
ColorAndFontConstants.errorTitleFont,
errorDetailCode.get(task.getReturnCode()),
ColorAndFontConstants.defaultFont);
}
else if (errorDetail != null)
{
summaryMsg = Utilities.getFormattedError(errorSummary,
ColorAndFontConstants.errorTitleFont,
errorDetail, ColorAndFontConstants.defaultFont);
}
else
{
summaryMsg = null;
}
}
if (summaryMsg != null)
{
dialog.setSummary(Message.raw(summaryMsg));
}
dialog.setEnabledClose(true);
dialog.getProgressBar().setVisible(false);
if (task.getState() == Task.State.FINISHED_SUCCESSFULLY)
{
dialog.setTaskIsOver(true);
}
task.postOperation();
}
};
info.registerTask(task);
worker.startBackgroundTask();
}
/**
* Checks that the provided string value is a valid integer and if it is not
* updates a list of error messages with an error.
* @param errors the list of error messages to be updated.
* @param stringValue the string value to analyze.
* @param minValue the minimum integer value accepted.
* @param maxValue the maximum integer value accepted.
* @param errMsg the error message to use to update the error list if the
* provided value is not valid.
*/
protected void checkIntValue(Collection<Message> errors, String stringValue,
int minValue, int maxValue, Message errMsg)
{
try
{
int n = Integer.parseInt(stringValue);
if ((n > maxValue) || (n < minValue))
{
throw new RuntimeException("Invalid value");
}
}
catch (Throwable t)
{
errors.add(errMsg);
}
}
/**
* Starts the server. This method will launch a task and open a progress
* dialog that will start the server. This method must be called from the
* event thread.
*
*/
protected void startServer()
{
LinkedHashSet<Message> errors = new LinkedHashSet<Message>();
ProgressDialog progressDialog = new ProgressDialog(
Utilities.createFrame(),
Utilities.getParentDialog(this),
INFO_CTRL_PANEL_START_SERVER_PROGRESS_DLG_TITLE.get(), getInfo());
StartServerTask newTask = new StartServerTask(getInfo(), progressDialog);
for (Task task : getInfo().getTasks())
{
task.canLaunch(newTask, errors);
}
if (errors.size() == 0)
{
launchOperation(newTask,
INFO_CTRL_PANEL_STARTING_SERVER_SUMMARY.get(),
INFO_CTRL_PANEL_STARTING_SERVER_SUCCESSFUL_SUMMARY.get(),
INFO_CTRL_PANEL_STARTING_SERVER_SUCCESSFUL_DETAILS.get(),
ERR_CTRL_PANEL_STARTING_SERVER_ERROR_SUMMARY.get(),
null,
ERR_CTRL_PANEL_STARTING_SERVER_ERROR_DETAILS,
progressDialog);
progressDialog.setVisible(true);
}
else
{
displayErrorDialog(errors);
}
}
/**
* Stops the server. This method will launch a task and open a progress
* dialog that will stop the server. This method must be called from the
* event thread.
*
*/
protected void stopServer()
{
LinkedHashSet<Message> errors = new LinkedHashSet<Message>();
ProgressDialog progressDialog = new ProgressDialog(
Utilities.createFrame(),
Utilities.getParentDialog(this),
INFO_CTRL_PANEL_STOP_SERVER_PROGRESS_DLG_TITLE.get(), getInfo());
StopServerTask newTask = new StopServerTask(getInfo(), progressDialog);
for (Task task : getInfo().getTasks())
{
task.canLaunch(newTask, errors);
}
boolean confirmed = true;
if (errors.size() == 0)
{
confirmed = displayConfirmationDialog(
INFO_CTRL_PANEL_CONFIRMATION_REQUIRED_SUMMARY.get(),
INFO_CTRL_PANEL_CONFIRM_STOP_SERVER_DETAILS.get());
}
if ((errors.size() == 0) && confirmed)
{
launchOperation(newTask,
INFO_CTRL_PANEL_STOPPING_SERVER_SUMMARY.get(),
INFO_CTRL_PANEL_STOPPING_SERVER_SUCCESSFUL_SUMMARY.get(),
INFO_CTRL_PANEL_STOPPING_SERVER_SUCCESSFUL_DETAILS.get(),
ERR_CTRL_PANEL_STOPPING_SERVER_ERROR_SUMMARY.get(),
null,
ERR_CTRL_PANEL_STOPPING_SERVER_ERROR_DETAILS,
progressDialog);
progressDialog.setVisible(true);
}
if (errors.size() > 0)
{
displayErrorDialog(errors);
}
}
/**
* Restarts the server. This method will launch a task and open a progress
* dialog that will restart the server. This method must be called from the
* event thread.
*
*/
protected void restartServer()
{
LinkedHashSet<Message> errors = new LinkedHashSet<Message>();
ProgressDialog progressDialog = new ProgressDialog(
Utilities.createFrame(),
Utilities.getParentDialog(this),
INFO_CTRL_PANEL_RESTART_SERVER_PROGRESS_DLG_TITLE.get(), getInfo());
RestartServerTask newTask = new RestartServerTask(getInfo(),
progressDialog);
for (Task task : getInfo().getTasks())
{
task.canLaunch(newTask, errors);
}
boolean confirmed = true;
if (errors.size() == 0)
{
confirmed = displayConfirmationDialog(
INFO_CTRL_PANEL_CONFIRMATION_REQUIRED_SUMMARY.get(),
INFO_CTRL_PANEL_CONFIRM_RESTART_SERVER_DETAILS.get());
}
if ((errors.size() == 0) && confirmed)
{
launchOperation(newTask,
INFO_CTRL_PANEL_STOPPING_SERVER_SUMMARY.get(),
INFO_CTRL_PANEL_RESTARTING_SERVER_SUCCESSFUL_SUMMARY.get(),
INFO_CTRL_PANEL_RESTARTING_SERVER_SUCCESSFUL_DETAILS.get(),
ERR_CTRL_PANEL_RESTARTING_SERVER_ERROR_SUMMARY.get(),
null,
ERR_CTRL_PANEL_RESTARTING_SERVER_ERROR_DETAILS,
progressDialog);
progressDialog.setVisible(true);
}
if (errors.size() > 0)
{
displayErrorDialog(errors);
}
}
/**
* Displays a dialog asking for authentication. This method must be called
* from the event thread.
*
*/
protected void authenticate()
{
if (!getLoginDialog().isVisible())
{
getLoginDialog().setVisible(true);
}
getLoginDialog().toFront();
}
/**
* Returns the login dialog that is displayed when the method authenticate
* is called.
* @return the login dialog that is displayed when the method authenticate
* is called.
*/
protected GenericDialog getLoginDialog()
{
if (isLocal())
{
GenericDialog loginDialog =
ControlCenterMainPane.getLocalServerLoginDialog(getInfo());
Utilities.centerGoldenMean(loginDialog, Utilities.getFrame(this));
loginDialog.setModal(true);
return loginDialog;
}
else
{
GenericDialog localOrRemoteDialog =
ControlCenterMainPane.getLocalOrRemoteDialog(getInfo());
Utilities.centerGoldenMean(localOrRemoteDialog, Utilities.getFrame(this));
localOrRemoteDialog.setModal(true);
return localOrRemoteDialog;
}
}
/**
* Tells whether an entry exists or not. Actually it tells if we could find
* a given entry or not.
* @param dn the DN of the entry to look for.
* @return <CODE>true</CODE> if the entry with the provided DN could be found
* and <CODE>false</CODE> otherwise.
*/
protected boolean entryExists(String dn)
{
boolean entryExists = false;
try
{
SearchControls ctls = new SearchControls();
ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
ctls.setReturningAttributes(
new String[] { SchemaConstants.NO_ATTRIBUTES });
String filter = BrowserController.ALL_OBJECTS_FILTER;
NamingEnumeration<SearchResult> result =
getInfo().getDirContext().search(Utilities.getJNDIName(dn),
filter, ctls);
try
{
while (result.hasMore())
{
SearchResult sr = result.next();
entryExists = sr != null;
}
}
finally
{
result.close();
}
}
catch (Throwable t)
{
}
return entryExists;
}
/**
* Tells whether a given entry exists and contains the specified object class.
* @param dn the DN of the entry.
* @param objectClass the object class.
* @return <CODE>true</CODE> if the entry exists and contains the specified
* object class and <CODE>false</CODE> otherwise.
*/
protected boolean hasObjectClass(String dn, String objectClass)
{
boolean hasObjectClass = false;
try
{
SearchControls ctls = new SearchControls();
ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
ctls.setReturningAttributes(
new String[] {
ServerConstants.OBJECTCLASS_ATTRIBUTE_TYPE_NAME
});
String filter = BrowserController.ALL_OBJECTS_FILTER;
NamingEnumeration<SearchResult> result =
getInfo().getDirContext().search(Utilities.getJNDIName(dn),
filter, ctls);
try
{
while (result.hasMore())
{
SearchResult sr = result.next();
Set<String> values = ConnectionUtils.getValues(sr,
ServerConstants.OBJECTCLASS_ATTRIBUTE_TYPE_NAME);
if (values != null)
{
for (String s : values)
{
if (s.equalsIgnoreCase(objectClass))
{
hasObjectClass = true;
break;
}
}
}
}
}
finally
{
result.close();
}
}
catch (Throwable t)
{
}
return hasObjectClass;
}
/**
* Returns the border to be used in the right panel of the dialog with a tree
* on the left (for instance the schema browser, entry browser and index
* browser).
* @return the border to be used in the right panel.
*/
protected Border getRightPanelBorder()
{
return ColorAndFontConstants.textAreaBorder;
}
/**
* Returns the monitoring value in a String form to be displayed to the user.
* @param attr the attribute to analyze.
* @param monitoringEntry the monitoring entry.
* @return the monitoring value in a String form to be displayed to the user.
*/
public static String getMonitoringValue(MonitoringAttributes attr,
CustomSearchResult monitoringEntry)
{
return Utilities.getMonitoringValue(attr, monitoringEntry);
}
/**
* Updates the monitoring information writing it to a list of labels.
* @param monitoringAttrs the monitoring operations whose information we want
* to update.
* @param monitoringLabels the monitoring labels to be updated.
* @param monitoringEntry the monitoring entry containing the information to
* be displayed.
*/
protected void updateMonitoringInfo(
List<MonitoringAttributes> monitoringAttrs,
List<JLabel> monitoringLabels, CustomSearchResult monitoringEntry)
{
for (int i=0 ; i<monitoringAttrs.size(); i++)
{
String value =
getMonitoringValue(monitoringAttrs.get(i), monitoringEntry);
JLabel l = monitoringLabels.get(i);
l.setText(value);
}
}
/**
* Returns the first value for a given attribute in the provided entry.
* @param sr the entry. It may be <CODE>null</CODE>.
* @param attrName the attribute name.
* @return the first value for a given attribute in the provided entry.
*/
protected Object getFirstMonitoringValue(CustomSearchResult sr,
String attrName)
{
return Utilities.getFirstMonitoringValue(sr, attrName);
}
/**
* Returns the label to be used in panels (with ':') based on the definition
* of the monitoring attribute.
* @param attr the monitoring attribute.
* @return the label to be used in panels (with ':') based on the definition
* of the monitoring attribute.
*/
protected static Message getLabel(MonitoringAttributes attr)
{
return INFO_CTRL_PANEL_OPERATION_NAME_AS_LABEL.get(
attr.getMessage().toString());
}
/**
* Returns the command-line arguments associated with the provided schedule.
* @param schedule the schedule.
* @return the command-line arguments associated with the provided schedule.
*/
protected List<String> getScheduleArgs(ScheduleType schedule)
{
List<String> args = new ArrayList<String>(2);
switch (schedule.getType())
{
case LAUNCH_LATER:
args.add("--start");
args.add(getStartTimeForTask(schedule.getLaunchLaterDate()));
break;
case LAUNCH_PERIODICALLY:
args.add("--recurringTask");
args.add(schedule.getCronValue());
break;
}
return args;
}
/**
* Checks whether the server is running or not and depending on the schedule
* updates the list of errors with the errors found.
* @param schedule the schedule.
* @param errors the list of errors.
* @param label the label to be marked as invalid if errors where encountered.
*/
protected void addScheduleErrors(ScheduleType schedule,
Collection<Message> errors, JLabel label)
{
if (!isServerRunning())
{
ScheduleType.Type type = schedule.getType();
if (type == ScheduleType.Type.LAUNCH_LATER)
{
errors.add(ERR_CTRL_PANEL_LAUNCH_LATER_REQUIRES_SERVER_RUNNING.get());
setPrimaryInvalid(label);
}
else if (type == ScheduleType.Type.LAUNCH_PERIODICALLY)
{
errors.add(
ERR_CTRL_PANEL_LAUNCH_SCHEDULE_REQUIRES_SERVER_RUNNING.get());
setPrimaryInvalid(label);
}
}
}
private String getStartTimeForTask(Date date)
{
return taskDateFormat.format(date);
}
/**
* Checks whether the provided superior object classes are compatible with
* the provided object class type. If not, the method updates the provided
* list of error messages with a message describing the incompatibility.
* @param objectClassSuperiors the superior object classes.
* @param objectClassType the object class type.
* @param errors the list of error messages.
*/
protected void checkCompatibleSuperiors(Set<ObjectClass> objectClassSuperiors,
ObjectClassType objectClassType, List<Message> errors)
{
SortedSet<String> notCompatibleClasses =
new TreeSet<String>(new LowerCaseComparator());
for (ObjectClass oc : objectClassSuperiors)
{
if (oc.getObjectClassType() == ObjectClassType.ABSTRACT)
{
// Nothing to do.
}
else if (oc.getObjectClassType() != objectClassType)
{
notCompatibleClasses.add(oc.getNameOrOID());
}
}
if (!notCompatibleClasses.isEmpty())
{
String arg = Utilities.getStringFromCollection(notCompatibleClasses,
", ");
if (objectClassType == ObjectClassType.STRUCTURAL)
{
errors.add(
ERR_CTRL_PANEL_INCOMPATIBLE_SUPERIORS_WITH_STRUCTURAL.get(arg));
}
else if (objectClassType == ObjectClassType.AUXILIARY)
{
errors.add(
ERR_CTRL_PANEL_INCOMPATIBLE_SUPERIORS_WITH_AUXILIARY.get(arg));
}
else if (objectClassType == ObjectClassType.ABSTRACT)
{
errors.add(
ERR_CTRL_PANEL_INCOMPATIBLE_SUPERIORS_WITH_ABSTRACT.get(arg));
}
}
}
}