/* * 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 Sun Microsystems, Inc. */ package org.opends.guitools.controlpanel.ui; import static org.opends.messages.AdminToolMessages.*; 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.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; 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.JDialog; import javax.swing.JEditorPane; import javax.swing.JLabel; import javax.swing.JMenuBar; import javax.swing.JPanel; import javax.swing.SwingUtilities; 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.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.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.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 { /** * 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 mainPanel; private JLabel message; private GenericDialog loginDialog; /** * The error pane. */ protected JEditorPane errorPane; /** * The last displayed message in the error pane. */ protected String lastDisplayedError = null; private ArrayList confListeners = new ArrayList(); private boolean sizeSet = false; private boolean focusSet = false; /** * 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 true if this panel requires some bordering (in general * an EmptyBorder with some insets) and false otherwise. * @return true if this panel requires some bordering (in general * an EmptyBorder with some insets) and false otherwise. */ public boolean requiresBorder() { return true; } /** * Returns the menu bar that the panel might have. Returns * null 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 * true and the operation will be performed in the background * while a message of type 'Loading...' is displayed on the panel. * @return true if changes should be loaded in the background and * false 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 true if this panel should be contained in a scroll * pane and false otherwise. */ public boolean requiresScroll() { return true; } /** * Constructor. * */ protected StatusGenericPanel() { super(new GridBagLayout()); setBackground(ColorAndFontConstants.background); mainPanel = new JPanel(new GridBagLayout()); mainPanel.setOpaque(false); message = Utilities.createDefaultLabel(); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.fill = GridBagConstraints.BOTH; gbc.weightx = 1.0; gbc.weighty = 1.0; super.add(mainPanel, gbc); gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.CENTER; super.add(message, gbc); message.setVisible(false); } /** * 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); } 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); p.add(errorPane, gbc); } /** * 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 parent = Utilities.getParentDialog(this); if ((parent != null) && (parent instanceof GenericDialog)) { ((GenericDialog)parent).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) { JDialog parent = (JDialog)Utilities.getParentDialog(this); if ((parent != null) && (parent instanceof GenericDialog)) { ((GenericDialog)parent).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) { confListeners.add(listener); } /** * Removes a configuration element created listener. * @param listener the listener. */ public void removeConfigurationElementCreatedListener( ConfigurationElementCreatedListener listener) { confListeners.remove(listener); } /** * Notifies the configuraton element created listener that a new object has * been created. * @param configObject the created object. */ protected void notifyConfigurationElementCreated(Object configObject) { for (ConfigurationElementCreatedListener listener : confListeners) { listener.elementCreated( new ConfigurationElementCreatedEvent(this, configObject)); } } /** * 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 true if the dialog should be disposed when the user * closes it or true otherwise. */ public boolean isDisposeOnClose() { return disposeOnClose; } /** * Sets whether the dialog should be disposed when the user closes it or not. * @param disposeOnClose true if the dialog should be disposed * when the user closes it or true 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 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 true if the user confirms and false * 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 { 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)); } if (rebuildIndexes) { SortedSet indexes = new TreeSet(); indexes.add(index); SortedSet baseDNs = new TreeSet(); 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 errors = new ArrayList(); for (Task task : getInfo().getTasks()) { task.canLaunch(newTask, errors); } if (errors.size() == 0) { progressDialog.appendProgressHtml("

"); 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); progressDialog.toFront(); progressDialog.setVisible(true); } 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. */ 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"; } /** * 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 ""; } /** * 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("

"+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 true if the server is running and the user did not * provide authentication and false otherwise. * @param desc the server descriptor. * @return true if the server is running and the user did not * provide authentication and false otherwise. */ protected boolean authenticationRequired(ServerDescriptor desc) { boolean returnValue; ServerDescriptor.ServerStatus status = desc.getStatus(); if ((status == ServerDescriptor.ServerStatus.STARTED) && !desc.isAuthenticated()) { 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("

"+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) { Message title = INFO_CTRL_PANEL_SERVER_NOT_RUNNING_SUMMARY.get(); MessageBuilder mb = new MessageBuilder(); mb.append(detailsServerNotRunning); mb.append("

"+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("

"+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 true if the provided object is a category object in * a combo box. * @param o the item in the combo box. * @return true 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 worker = new BackgroundTask() { 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 { configurationChanged(new ConfigurationChangeEvent( this.info, this.info.getServerDescriptor())); } } } /** * Displays the main panel. * */ protected void displayMainPanel() { mainPanel.setVisible(true); message.setVisible(false); } /** * Displays a message and hides the main panel. * @param msg the message to be displayed. */ protected void displayMessage(Message msg) { message.setText(msg.toString()); mainPanel.setVisible(false); message.setVisible(true); } /** * 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 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 panel that are handled. * */ private enum PanelType { ERROR, CONFIRMATION, INFORMATION }; /** * 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(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; default: text = Utilities.getFormattedSuccess(title, titleFont, details, detailsFont); break; } if (!text.equals(lastDisplayedError)) { JEditorPane pane1 = Utilities.makeHtmlPane(null, pane.getFont()); String text1; switch (type) { case ERROR: text1 = Utilities.getFormattedError(title, titleFont, null, detailsFont); break; default: text1 = Utilities.getFormattedSuccess(title, titleFont, null, detailsFont); break; } pane1.setText(text1); Dimension d1 = pane1.getPreferredSize(); JEditorPane pane2 = Utilities.makeHtmlPane(null, pane.getFont()); pane2.setText(details.toString()); String plainText = details.toString().replaceAll("
", ServerConstants.EOL); Utilities.updatePreferredSize(pane2, 100, plainText, detailsFont, true); Dimension d2 = pane2.getPreferredSize(); pane.setPreferredSize(new Dimension(Math.max(d1.width, d2.width), d1.height + d2.height)); lastDisplayedError = text; pane.setText(text); } } /** * 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 newElements = new TreeSet(); 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 true if the backend must be displayed and * false 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 newElements = new LinkedHashSet(); SortedSet backendIDs = new TreeSet(); HashMap> hmBaseDNs = new HashMap>(); for (BackendDescriptor backend : desc.getBackends()) { if (displayBackend(backend)) { String backendID = backend.getBackendID(); backendIDs.add(backendID); SortedSet baseDNs = new TreeSet(); for (BaseDNDescriptor baseDN : backend.getBaseDns()) { try { baseDNs.add(Utilities.unescapeUtf8(baseDN.getDn().toString())); } catch (Throwable t) { throw new IllegalStateException("Unexpected error: "+t, t); } } hmBaseDNs.put(backendID, baseDNs); } } for (String backendID : backendIDs) { newElements.add(new CategorizedComboBoxElement(backendID, CategorizedComboBoxElement.Type.CATEGORY)); SortedSet 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(final Collection newElements, final DefaultComboBoxModel model) { SwingUtilities.invokeLater(new Runnable() { public void run() { boolean changed = newElements.size() != model.getSize(); if (!changed) { int i = 0; for (Object newElement : newElements) { changed = !newElement.equals(model.getElementAt(i)); if (changed) { break; } i++; } } if (changed) { Object selected = model.getSelectedItem(); model.removeAllElements(); boolean selectDefault = false; for (Object newElement : newElements) { model.addElement(newElement); } if (selected != null) { if (model.getIndexOf(selected) != -1) { model.setSelectedItem(selected); } else { selectDefault = true; } } else { selectDefault = true; } if (selectDefault) { for (int i=0; i> hmIndexes) { synchronized (hmIndexes) { HashSet dns = new HashSet(); 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 IllegalStateException("Unexpected error: "+t, t); } dns.add(dn); SortedSet indexes = new TreeSet(); indexes.addAll(backend.getIndexes()); indexes.addAll(backend.getVLVIndexes()); SortedSet 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(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> hmIndexes, CategorizedComboBoxElement selectedItem, AddRemovePanel addRemove) { synchronized (hmIndexes) { String selectedDn = null; if (selectedItem != null) { selectedDn = (String)selectedItem.getValue(); } if (selectedDn != null) { SortedSet indexes = hmIndexes.get(selectedDn); if (indexes != null) { boolean availableChanged = false; boolean selectedChanged = false; SortedSet availableIndexes = addRemove.getAvailableListModel().getData(); SortedSet 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 true if the cancel button is enabled and * false otherwise. * @return true if the cancel button is enabled and * false otherwise. */ public boolean isEnableCancel() { return enableCancel; } /** * Returns true if the close button is enabled and * false otherwise. * @return true if the close button is enabled and * false otherwise. */ public boolean isEnableClose() { return enableClose; } /** * Returns true if the ok button is enabled and * false otherwise. * @return true if the ok button is enabled and * false otherwise. */ public boolean isEnableOK() { return enableOK; } /** * Returns true if the server is running and * false otherwise. * @return true if the server is running and * false otherwise. */ protected boolean isServerRunning() { return getInfo().getServerDescriptor().getStatus() == ServerDescriptor.ServerStatus.STARTED; } /** * 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 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 errorDetailCode, final ProgressDialog dialog, boolean resetLogs) { 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()+"
"+cmdLine+ "

", ColorAndFontConstants.progressFont)); } dialog.setEnabledClose(false); dialog.setSummary(Message.raw( Utilities.applyFont(initialSummary.toString(), ColorAndFontConstants.defaultFont))); dialog.getProgressBar().setVisible(true); BackgroundTask worker = new BackgroundTask() { /** * {@inheritDoc} */ public Task processBackgroundTask() throws Throwable { task.runTask(); if (task.regenerateDescriptor()) { getInfo().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) { if ((task.getReturnCode() != null) && (errorDetailCode != null)) { MessageBuilder mb = new MessageBuilder(); mb.append(errorDetailCode.get(task.getReturnCode())); mb.append( ". "+INFO_CTRL_PANEL_DETAILS_THROWABLE.get(t.toString())); 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(); } }; getInfo().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 errors, String stringValue, int minValue, int maxValue, Message errMsg) { try { int n = Integer.parseInt(stringValue); if ((n > maxValue) || (n < minValue)) { throw new IllegalStateException("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 errors = new LinkedHashSet(); ProgressDialog progressDialog = new ProgressDialog( 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 errors = new LinkedHashSet(); ProgressDialog progressDialog = new ProgressDialog( 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 errors = new LinkedHashSet(); ProgressDialog progressDialog = new ProgressDialog( 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 (loginDialog == null) { LoginPanel loginPanel = new LoginPanel(); loginDialog = new GenericDialog(Utilities.getFrame(this), loginPanel); loginPanel.setInfo(getInfo()); Utilities.centerGoldenMean(loginDialog, Utilities.getFrame(this)); loginDialog.setModal(true); } return loginDialog; } /** * 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 true if the entry with the provided DN could be found * and false otherwise. */ protected boolean entryExists(String dn) { boolean entryExists = false; try { SearchControls ctls = new SearchControls(); ctls.setSearchScope(SearchControls.OBJECT_SCOPE); ctls.setReturningAttributes( new String[] { "dn" }); String filter = BrowserController.ALL_OBJECTS_FILTER; NamingEnumeration result = getInfo().getDirContext().search(Utilities.getJNDIName(dn), filter, ctls); while (result.hasMore()) { SearchResult sr = result.next(); entryExists = sr != null; } } 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 true if the entry exists and contains the specified * object class and false 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 result = getInfo().getDirContext().search(Utilities.getJNDIName(dn), filter, ctls); while (result.hasMore()) { SearchResult sr = result.next(); Set values = ConnectionUtils.getValues(sr, ServerConstants.OBJECTCLASS_ATTRIBUTE_TYPE_NAME); if (values != null) { for (String s : values) { if (s.equalsIgnoreCase(objectClass)) { hasObjectClass = true; break; } } } } } catch (Throwable t) { } return hasObjectClass; } }