/* * The contents of this file are subject to the terms of the Common Development and * Distribution License (the License). You may not use this file except in compliance with the * License. * * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the * specific language governing permission and limitations under the License. * * When distributing Covered Software, include this CDDL Header Notice in each file and include * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions Copyright [year] [name of copyright owner]". * * Copyright 2008-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2016 ForgeRock AS. */ package org.opends.guitools.controlpanel.util; import static com.forgerock.opendj.cli.Utils.*; import static com.forgerock.opendj.util.OperatingSystem.*; import static org.forgerock.opendj.ldap.DereferenceAliasesPolicy.*; import static org.forgerock.opendj.ldap.SearchScope.*; import static org.forgerock.opendj.ldap.requests.Requests.*; import static org.opends.admin.ads.util.PreferredConnection.Type.*; import static org.opends.messages.AdminToolMessages.*; import static org.opends.quicksetup.Installation.*; import static org.opends.server.schema.SchemaConstants.*; import static org.opends.server.util.SchemaUtils.*; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.Image; import java.awt.Point; import java.awt.Toolkit; import java.awt.Window; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.text.CharacterIterator; import java.text.StringCharacterIterator; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.logging.Logger; import java.util.regex.Pattern; import javax.swing.BorderFactory; import javax.swing.DefaultComboBoxModel; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPasswordField; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import javax.swing.border.EtchedBorder; import javax.swing.border.TitledBorder; import javax.swing.table.JTableHeader; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.slf4j.LocalizedLogger; import org.forgerock.opendj.config.ConfigurationFramework; import org.forgerock.opendj.config.server.ConfigException; import org.forgerock.opendj.ldap.Attribute; import org.forgerock.opendj.ldap.AttributeDescription; import org.forgerock.opendj.ldap.ByteString; import org.forgerock.opendj.ldap.DN; import org.forgerock.opendj.ldap.Entry; import org.forgerock.opendj.ldap.requests.SearchRequest; import org.forgerock.opendj.ldap.responses.SearchResultEntry; import org.forgerock.opendj.ldap.schema.AttributeType; import org.forgerock.opendj.ldap.schema.MatchingRule; import org.forgerock.opendj.ldap.schema.ObjectClass; import org.forgerock.opendj.ldap.schema.SchemaBuilder; import org.forgerock.opendj.ldap.schema.SchemaElement; import org.forgerock.opendj.ldap.schema.Syntax; import org.opends.admin.ads.util.ConnectionWrapper; import org.opends.admin.ads.util.PreferredConnection.Type; import org.opends.guitools.controlpanel.ControlPanel; import org.opends.guitools.controlpanel.browser.IconPool; import org.opends.guitools.controlpanel.datamodel.CategorizedComboBoxElement; import org.opends.guitools.controlpanel.datamodel.ConfigReadException; import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo; import org.opends.guitools.controlpanel.datamodel.MonitoringAttributes; import org.opends.guitools.controlpanel.datamodel.SortableTableModel; import org.opends.guitools.controlpanel.datamodel.VLVIndexDescriptor; import org.opends.guitools.controlpanel.event.ClickTooltipDisplayer; import org.opends.guitools.controlpanel.event.ComboKeySelectionManager; import org.opends.guitools.controlpanel.event.TextComponentFocusListener; import org.opends.guitools.controlpanel.ui.ColorAndFontConstants; import org.opends.guitools.controlpanel.ui.components.LabelWithHelpIcon; import org.opends.guitools.controlpanel.ui.components.SelectableLabelWithHelpIcon; import org.opends.guitools.controlpanel.ui.renderer.AccessibleTableHeaderRenderer; import org.opends.quicksetup.Installation; import org.opends.quicksetup.ui.UIFactory; import org.opends.quicksetup.util.Utils; import org.opends.server.config.ConfigConstants; import org.opends.server.config.ConfigurationHandler; import org.opends.server.core.LockFileManager; import org.opends.server.core.ServerContext; import org.opends.server.schema.SchemaConstants; import org.opends.server.types.HostPort; import org.opends.server.types.OpenDsException; import org.opends.server.types.Schema; import org.opends.server.util.SchemaUtils; import org.opends.server.util.SchemaUtils.PasswordType; import org.opends.server.util.ServerConstants; import org.opends.server.util.StaticUtils; /** A static class that provides miscellaneous functions. */ public class Utilities { private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); private static File rootDirectory; private static File instanceRootDirectory; private static final String HTML_SPACE = " "; private static final String[] attrsToObfuscate = { ServerConstants.ATTR_USER_PASSWORD }; private static final List binarySyntaxOIDs = Arrays.asList( SchemaConstants.SYNTAX_BINARY_OID, SchemaConstants.SYNTAX_JPEG_OID, SchemaConstants.SYNTAX_CERTIFICATE_OID, SchemaConstants.SYNTAX_OCTET_STRING_OID ); private static ImageIcon warningIcon; private static ImageIcon requiredIcon; private static final LocalizableMessage NO_VALUE_SET = INFO_CTRL_PANEL_NO_MONITORING_VALUE.get(); private static final LocalizableMessage NOT_IMPLEMENTED = INFO_CTRL_PANEL_NOT_IMPLEMENTED.get(); /** * Creates a combo box. * * @param * The combo box data type. * @return a combo box. */ public static JComboBox createComboBox() { JComboBox combo = new JComboBox<>(); if (isMacOS()) { combo.setOpaque(false); } combo.setKeySelectionManager(new ComboKeySelectionManager(combo)); return combo; } /** * Creates a frame. * @return a frame. */ public static JFrame createFrame() { JFrame frame = new JFrame(); frame.setResizable(true); org.opends.quicksetup.ui.Utilities.setFrameIcon(frame); return frame; } /** * Returns whether an attribute value must be obfuscated because * it contains sensitive information (like passwords). * * @param attrName the attribute name. * @param schema the schema of the server. * @return {@code true} if an attribute value must be obfuscated because * it contains sensitive information (like passwords) and {@code false} * otherwise. */ public static boolean mustObfuscate(String attrName, Schema schema) { if (schema != null) { return hasPasswordSyntax(attrName, schema); } for (String attr : attrsToObfuscate) { if (attr.equalsIgnoreCase(attrName)) { return true; } } return false; } /** * Derives a color by adding the specified offsets to the base color's * hue, saturation, and brightness values. The resulting hue, saturation, * and brightness values will be constrained to be between 0 and 1. * @param base the color to which the HSV offsets will be added * @param dH the offset for hue * @param dS the offset for saturation * @param dB the offset for brightness * @return Color with modified HSV values */ public static Color deriveColorHSB(Color base, float dH, float dS, float dB) { float hsb[] = Color.RGBtoHSB( base.getRed(), base.getGreen(), base.getBlue(), null); hsb[0] += dH; hsb[1] += dS; hsb[2] += dB; return Color.getHSBColor( hsb[0] < 0? 0 : (hsb[0] > 1? 1 : hsb[0]), hsb[1] < 0? 0 : (hsb[1] > 1? 1 : hsb[1]), hsb[2] < 0? 0 : (hsb[2] > 1? 1 : hsb[2])); } /** * Displays an error dialog that contains a set of error messages. * @param parentComponent the parent component relative to which the dialog * will be displayed. * @param errors the set of error messages that the dialog must display. */ public static void displayErrorDialog(Component parentComponent, Collection errors) { /* ErrorPanel panel = new ErrorPanel("Error", errors); GenericDialog dlg = new GenericDialog(null, panel); dlg.setModal(true); Utilities.centerGoldenMean(dlg, Utilities.getParentDialog(this)); dlg.setVisible(true); */ ArrayList stringErrors = new ArrayList<>(); for (LocalizableMessage err : errors) { stringErrors.add(err.toString()); } String msg = getStringFromCollection(stringErrors, "
"); String plainText = msg.replaceAll("
", ServerConstants.EOL); String wrappedText = wrapText(plainText, 70); wrappedText = wrappedText.replaceAll(ServerConstants.EOL, "
"); JOptionPane.showMessageDialog( parentComponent, ""+wrappedText, INFO_CTRL_PANEL_ERROR_DIALOG_TITLE.get().toString(), JOptionPane.ERROR_MESSAGE); } /** * Displays a confirmation dialog. * * @param parentComponent the parent component relative to which the dialog * will be displayed. * @param title the title of the dialog. * @param msg the message to be displayed. * @return {@code true} if the user accepts the message, {@code false} otherwise. */ public static boolean displayConfirmationDialog(Component parentComponent, LocalizableMessage title, LocalizableMessage msg) { String plainText = msg.toString().replaceAll("
", ServerConstants.EOL); String wrappedText = wrapText(plainText, 70); wrappedText = wrappedText.replaceAll(ServerConstants.EOL, "
"); return JOptionPane.YES_OPTION == JOptionPane.showOptionDialog( parentComponent, ""+wrappedText, title.toString(), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, // don't use a custom Icon null, // the titles of buttons null); // default button title } /** * Displays a warning dialog. * @param parentComponent the parent component relative to which the dialog * will be displayed. * @param title the title of the dialog. * @param msg the message to be displayed. */ public static void displayWarningDialog(Component parentComponent, LocalizableMessage title, LocalizableMessage msg) { String plainText = msg.toString().replaceAll("
", ServerConstants.EOL); String wrappedText = wrapText(plainText, 70); wrappedText = wrappedText.replaceAll(ServerConstants.EOL, "
"); JOptionPane.showMessageDialog( parentComponent, ""+wrappedText, title.toString(), JOptionPane.WARNING_MESSAGE); } /** * Creates a JEditorPane that displays a message. * @param text the message of the editor pane in HTML format. * @param font the font to be used in the message. * @return a JEditorPane that displays a message. */ public static JEditorPane makeHtmlPane(CharSequence text, Font font) { JEditorPane pane = new JEditorPane(); pane.setContentType("text/html"); pane.setFont(font); if (text != null) { pane.setText(applyFont(text, font)); } pane.setEditable(false); pane.setBorder(new EmptyBorder(0, 0, 0, 0)); pane.setOpaque(false); pane.setFocusCycleRoot(false); return pane; } /** * Creates a JEditorPane that displays a message. * @param text the message of the editor pane in plain text format. * @param font the font to be used in the message. * @return a JEditorPane that displays a message. */ public static JEditorPane makePlainTextPane(String text, Font font) { JEditorPane pane = new JEditorPane(); pane.setContentType("text/plain"); if (text != null) { pane.setText(text); } pane.setFont(font); pane.setEditable(false); pane.setBorder(new EmptyBorder(0, 0, 0, 0)); pane.setOpaque(false); pane.setFocusCycleRoot(false); return pane; } /** * Returns the HTML style representation for the given font. * @param font the font for which we want to get an HTML style representation. * @return the HTML style representation for the given font. */ private static String getFontStyle(Font font) { StringBuilder buf = new StringBuilder(); buf.append("font-family:").append(font.getName()) .append(";font-size:").append(font.getSize()).append("pt"); if (font.isItalic()) { buf.append(";font-style:italic"); } if (font.isBold()) { buf.append(";font-weight:bold;"); } return buf.toString(); } /** * Creates a titled border. * @param msg the message to be displayed in the titled border. * @return the created titled border. */ public static Border makeTitledBorder(LocalizableMessage msg) { TitledBorder border = new TitledBorder(new EtchedBorder(), " "+msg+" "); border.setTitleFont(ColorAndFontConstants.titleFont); border.setTitleColor(ColorAndFontConstants.foreground); return border; } /** * Returns a JScrollPane that contains the provided component. The scroll * pane will not contain any border. * @param comp the component contained in the scroll pane. * @return a JScrollPane that contains the provided component. The scroll * pane will not contain any border. */ public static JScrollPane createBorderLessScrollBar(Component comp) { JScrollPane scroll = new JScrollPane(comp); scroll.setBorder(new EmptyBorder(0, 0, 0, 0)); scroll.setViewportBorder(new EmptyBorder(0, 0, 0, 0)); scroll.setOpaque(false); scroll.getViewport().setOpaque(false); scroll.getViewport().setBackground(ColorAndFontConstants.background); scroll.setBackground(ColorAndFontConstants.background); UIFactory.setScrollIncrementUnit(scroll); return scroll; } /** * Returns a JScrollPane that contains the provided component. * @param comp the component contained in the scroll pane. * @return a JScrollPane that contains the provided component. */ public static JScrollPane createScrollPane(Component comp) { JScrollPane scroll = new JScrollPane(comp); scroll.getViewport().setOpaque(false); scroll.setOpaque(false); scroll.getViewport().setBackground(ColorAndFontConstants.background); scroll.setBackground(ColorAndFontConstants.background); UIFactory.setScrollIncrementUnit(scroll); return scroll; } /** * Creates a button. * @param text the message to be displayed by the button. * @return the created button. */ public static JButton createButton(LocalizableMessage text) { JButton button = new JButton(text.toString()); button.setOpaque(false); button.setForeground(ColorAndFontConstants.buttonForeground); button.getAccessibleContext().setAccessibleName(text.toString()); return button; } /** * Creates a radio button. * @param text the message to be displayed by the radio button. * @return the created radio button. */ public static JRadioButton createRadioButton(LocalizableMessage text) { JRadioButton button = new JRadioButton(text.toString()); button.setOpaque(false); button.setForeground(ColorAndFontConstants.buttonForeground); button.getAccessibleContext().setAccessibleName(text.toString()); return button; } /** * Creates a check box. * @param text the message to be displayed by the check box. * @return the created check box. */ public static JCheckBox createCheckBox(LocalizableMessage text) { JCheckBox cb = new JCheckBox(text.toString()); cb.setOpaque(false); cb.setForeground(ColorAndFontConstants.buttonForeground); cb.getAccessibleContext().setAccessibleName(text.toString()); return cb; } /** * Creates a menu item with the provided text. * @param msg the text. * @return a menu item with the provided text. */ public static JMenuItem createMenuItem(LocalizableMessage msg) { return new JMenuItem(msg.toString()); } /** * Creates a menu with the provided text. * @param msg the text. * @param description the accessible description. * @return a menu with the provided text. */ public static JMenu createMenu(LocalizableMessage msg, LocalizableMessage description) { JMenu menu = new JMenu(msg.toString()); menu.getAccessibleContext().setAccessibleDescription(description.toString()); return menu; } /** * Creates a label of type 'primary' (with bigger font than usual) with no text. * * @return the label of type 'primary' (with bigger font than usual) with no text. */ public static JLabel createPrimaryLabel() { return createPrimaryLabel(LocalizableMessage.EMPTY); } /** * Creates a label of type 'primary' (with bigger font than usual). * @param text the message to be displayed by the label. * @return the label of type 'primary' (with bigger font than usual). */ public static JLabel createPrimaryLabel(LocalizableMessage text) { JLabel label = new JLabel(text.toString()); label.setFont(ColorAndFontConstants.primaryFont); label.setForeground(ColorAndFontConstants.foreground); return label; } /** * Creates a label of type 'inline help' (with smaller font). * @param text the message to be displayed by the label. * @return the label of type 'inline help' (with smaller font). */ public static JLabel createInlineHelpLabel(LocalizableMessage text) { JLabel label = new JLabel(text.toString()); label.setFont(ColorAndFontConstants.inlineHelpFont); label.setForeground(ColorAndFontConstants.foreground); return label; } /** * Creates a label of type 'title' (with bigger font). * @param text the message to be displayed by the label. * @return the label of type 'title' (with bigger font). */ public static JLabel createTitleLabel(LocalizableMessage text) { JLabel label = new JLabel(text.toString()); label.setFont(ColorAndFontConstants.titleFont); label.setForeground(ColorAndFontConstants.foreground); return label; } /** * Creates a label (with default font) with no text. * @return the label (with default font) with no text. */ public static JLabel createDefaultLabel() { return createDefaultLabel(LocalizableMessage.EMPTY); } /** * Creates a label (with default font). * @param text the message to be displayed by the label. * @return the label (with default font). */ public static JLabel createDefaultLabel(LocalizableMessage text) { JLabel label = new JLabel(text.toString()); label.setFont(ColorAndFontConstants.defaultFont); label.setForeground(ColorAndFontConstants.foreground); return label; } /** * Returns a table created with the provided model and renderers. * @param tableModel the table model. * @param renderer the cell renderer. * @return a table created with the provided model and renderers. */ public static JTable createSortableTable(final SortableTableModel tableModel, TableCellRenderer renderer) { final JTable table = new JTable(tableModel); table.setShowGrid(true); table.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS); table.setGridColor(ColorAndFontConstants.gridColor); if (isMacOS()) { table.getTableHeader().setBorder( BorderFactory.createMatteBorder(1, 1, 0, 0, ColorAndFontConstants.gridColor)); } else if (isWindows()) { table.getTableHeader().setBorder( BorderFactory.createMatteBorder(1, 1, 0, 1, ColorAndFontConstants.gridColor)); } table.getTableHeader().setDefaultRenderer( new AccessibleTableHeaderRenderer( table.getTableHeader().getDefaultRenderer())); for (int i=0; i width2 ? JTable.AUTO_RESIZE_OFF : JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS); } /** * Updates the size of the table rows according to the size of the * rendered component. * @param table the table to handle. */ public static void updateTableSizes(JTable table) { updateTableSizes(table, -1); } /** * Updates the size of the table rows according to the size of the * rendered component. * @param table the table to handle. * @param rows the maximum rows to be displayed (-1 for unlimited) */ public static void updateTableSizes(JTable table, int rows) { int horizontalMargin = table.getIntercellSpacing().width; int verticalMargin = table.getIntercellSpacing().height; Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); int headerMaxHeight = 5; int headerMaxWidth = 0; JTableHeader header = table.getTableHeader(); if (header != null && header.isVisible()) { for (int col=0; col screenSize.height) { // There are some issues on Mac OS and sometimes the preferred size // is too big. colHeight = 0; } headerMaxHeight = Math.max(headerMaxHeight, colHeight); } } for (int col=0; col screenSize.width) { colMaxWidth = 8; } for (int row=0; row screenSize.height) { colHeight = 0; } maxRow = Math.max(maxRow, colHeight); } } if (maxRow > table.getRowHeight()) { table.setRowHeight(maxRow); } Dimension d1; if (rows == -1) { d1 = table.getPreferredSize(); } else { d1 = new Dimension(table.getPreferredSize().width, rows * maxRow); } table.setPreferredScrollableViewportSize(d1); } /** * Returns a String that contains the html passed as parameter with a span * applied. The span style corresponds to the Font specified as parameter. * The goal of this method is to be able to specify a font for an HTML string. * * @param html the original html text. * @param font the font to be used to generate the new HTML. * @return a string that represents the original HTML with the font specified * as parameter. */ public static String applyFont(CharSequence html, Font font) { return "" + html + ""; } /** * Returns an ImageIcon or {@code null} if the path was invalid. * @param path the path of the image. * @param loader the class loader to use to load the image. If * {@code null} this class class loader will be used. * @return an ImageIcon or {@code null} if the path was invalid. */ public static ImageIcon createImageIcon(String path, ClassLoader loader) { if (loader == null) { loader = ControlPanel.class.getClassLoader(); } java.net.URL imgURL = loader.getResource(path); return imgURL != null ? new ImageIcon(imgURL) : null; } /** * Returns an ImageIcon or {@code null} if the path was invalid. * @param path the path of the image. * @return an ImageIcon or {@code null} if the path was invalid. */ public static ImageIcon createImageIcon(String path) { return createImageIcon(path, null); } /** * Creates an image icon using an array of bytes that contain the image and * specifying the maximum height of the image. * @param bytes the byte array. * @param maxHeight the maximum height of the image. * @param description the description of the image. * @param useFast whether a fast algorithm must be used to transform the image * or an algorithm with a better result. * @return an image icon using an array of bytes that contain the image and * specifying the maximum height of the image. */ public static ImageIcon createImageIcon(byte[] bytes, int maxHeight, LocalizableMessage description, boolean useFast) { ImageIcon icon = new ImageIcon(bytes, description.toString()); if (maxHeight > icon.getIconHeight() || icon.getIconHeight() <= 0) { return icon; } int newHeight = maxHeight; int newWidth = (newHeight * icon.getIconWidth()) / icon.getIconHeight(); int algo = useFast ? Image.SCALE_FAST : Image.SCALE_SMOOTH; Image scaledImage = icon.getImage().getScaledInstance(newWidth, newHeight, algo); return new ImageIcon(scaledImage); } /** * Updates the preferred size of an editor pane. * @param pane the panel to be updated. * @param nCols the number of columns that the panel must have. * @param plainText the text to be displayed (plain text). * @param font the font to be used. * @param applyBackground whether an error/warning background must be applied * to the text or not. */ public static void updatePreferredSize(JEditorPane pane, int nCols, String plainText, Font font, boolean applyBackground) { String wrappedText = wrapText(plainText, nCols); wrappedText = wrappedText.replaceAll(ServerConstants.EOL, "
"); if (applyBackground) { wrappedText = UIFactory.applyErrorBackgroundToHtml( Utilities.applyFont(wrappedText, font)); } JEditorPane pane2 = makeHtmlPane(wrappedText, font); pane.setPreferredSize(pane2.getPreferredSize()); JFrame frame = getFrame(pane); if (frame != null && frame.isVisible()) { frame.getRootPane().revalidate(); frame.getRootPane().repaint(); } } /** * Strips any potential HTML markup from a given string. * @param s string to strip * @return resulting string */ public static String stripHtmlToSingleLine(String s) { String o = null; if (s != null) { s = s.replaceAll("
", " "); // This is not a comprehensive solution but addresses // the few tags that we have in Resources.properties // at the moment. Note that the following might strip // out more than is intended for non-tags like // '' or for funky tags like // ''. See test class for cases that // might cause problems. o = s.replaceAll("\\<.*?\\>",""); } return o; } /** * Wraps the contents of the provided message using the specified number of * columns. * @param msg the message to be wrapped. * @param nCols the number of columns. * @return the wrapped message. */ public static LocalizableMessage wrapHTML(LocalizableMessage msg, int nCols) { String s = msg.toString(); StringBuilder sb = new StringBuilder(); StringBuilder lastLine = new StringBuilder(); int lastOpenTag = -1; boolean inTag = false; int lastSpace = -1; int lastLineLengthInLastSpace = 0; int lastLineLength = 0; for (int i=0; i') { if (lastOpenTag != -1) { inTag = false; String tag = s.substring(lastOpenTag, i+1); lastOpenTag = -1; lastLine.append(c); if (isLineBreakTag(tag)) { sb.append(lastLine); lastLine.delete(0, lastLine.length()); lastLineLength = 0; lastSpace = -1; lastLineLengthInLastSpace = 0; } } else { isNormalChar = true; } } else if (inTag) { lastLine.append(c); } else if (c == HTML_SPACE.charAt(0)) { if (s.length() >= i + HTML_SPACE.length()) { if (HTML_SPACE.equalsIgnoreCase(s.substring(i, i + HTML_SPACE.length()))) { if (lastLineLength < nCols) { // Only count as 1 space lastLine.append(HTML_SPACE); lastSpace = lastLine.length() - HTML_SPACE.length(); lastLineLength ++; lastLineLengthInLastSpace = lastLineLength; i += HTML_SPACE.length() - 1; } else { // Insert a line break sb.append(lastLine); sb.append("
"); lastLine.delete(0, lastLine.length()); lastLineLength = 0; lastSpace = -1; lastLineLengthInLastSpace = 0; i += HTML_SPACE.length() - 1; } } else { isNormalChar = true; } } else { isNormalChar = true; } } else if (c == ' ') { if (lastLineLength < nCols) { // Only count as 1 space lastLine.append(c); lastSpace = lastLine.length() - 1; lastLineLength ++; lastLineLengthInLastSpace = lastLineLength; } else { // Insert a line break sb.append(lastLine); sb.append("
"); lastLine.delete(0, lastLine.length()); lastLineLength = 0; lastSpace = -1; lastLineLengthInLastSpace = 0; } } else { isNormalChar = true; } if (isNormalChar) { if (lastLineLength < nCols) { lastLine.append(c); lastLineLength ++; } else { // Check where to insert a line break if (lastSpace != -1) { sb.append(lastLine, 0, lastSpace); sb.append("
"); lastLine.delete(0, lastSpace + 1); lastLine.append(c); lastLineLength = lastLineLength - lastLineLengthInLastSpace + 1; lastLineLengthInLastSpace = 0; lastSpace = -1; } else { // Force the line break. sb.append(lastLine); sb.append("
"); lastLine.delete(0, lastLine.length()); lastLine.append(c); lastLineLength = 1; } } } } if (lastLine.length() > 0) { sb.append(lastLine); } return LocalizableMessage.raw(sb.toString()); } private static boolean isLineBreakTag(String tag) { return "
".equalsIgnoreCase(tag) || "
".equalsIgnoreCase(tag) || "".equalsIgnoreCase(tag) || "

".equalsIgnoreCase(tag) || "

".equalsIgnoreCase(tag); } /** * Center the component location based on its preferred size. The code * considers the particular case of 2 screens and puts the component on the * center of the left screen * * @param comp the component to be centered. */ public static void centerOnScreen(Component comp) { Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); int width = comp.getPreferredSize().width; int height = comp.getPreferredSize().height; boolean multipleScreen = screenSize.width / screenSize.height >= 2; if (multipleScreen) { comp.setLocation(screenSize.width / 4 - width / 2, (screenSize.height - height) / 2); } else { comp.setLocation((screenSize.width - width) / 2, (screenSize.height - height) / 2); } } /** * Center the component location of the ref component. * * @param comp the component to be centered. * @param ref the component to be used as reference. */ public static void centerGoldenMean(Window comp, Component ref) { comp.setLocationRelativeTo(ref); // Apply the golden mean if (ref != null && ref.isVisible()) { int refY = ref.getY(); int refHeight = ref.getHeight(); int compHeight = comp.getPreferredSize().height; int newY = refY + (int) (refHeight * 0.3819 - compHeight * 0.5); // Check that the new window will be fully visible Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); if (newY > 0 && screenSize.height > newY + compHeight) { comp.setLocation(comp.getX(), newY); } } } /** * Returns the parent frame of a component. {@code null} if this * component is not contained in any frame. * @param comp the component. * @return the parent frame of a component. {@code null} if this * component is not contained in any frame. */ public static JFrame getFrame(Component comp) { Component parent = comp; while (parent != null && !(parent instanceof JFrame)) { parent = parent.getParent(); } return parent != null ? (JFrame) parent : null; } /** * Returns the parent dialog of a component. {@code null} if this * component is not contained in any dialog. * @param comp the component. * @return the parent dialog of a component. {@code null} if this * component is not contained in any dialog. */ public static Window getParentDialog(Component comp) { Component parent = comp; while (parent != null) { if (parent instanceof JDialog || parent instanceof JFrame) { return (Window)parent; } parent = parent.getParent(); } return null; } /** * Unescapes UTF-8 text and generates a String from it. * @param v the string in UTF-8 format. * @return the string with unescaped characters. */ public static String unescapeUtf8(String v) { try { byte[] stringBytes = v.getBytes("UTF-8"); byte[] decodedBytes = new byte[stringBytes.length]; int pos = 0; for (int i = 0; i < stringBytes.length; i++) { if (stringBytes[i] == '\\' && i + 2 < stringBytes.length && StaticUtils.isHexDigit(stringBytes[i+1]) && StaticUtils.isHexDigit(stringBytes[i+2])) { decodedBytes[pos++] = convertHexEncodedUtf8To16BitChars(stringBytes, i); i += 2; } else { decodedBytes[pos++] = stringBytes[i]; } } return new String(decodedBytes, 0, pos, "UTF-8"); } catch (UnsupportedEncodingException unexpected) { // This is a bug, UTF-8 should be supported always by the JVM throw new RuntimeException("UTF-8 encoding not supported", unexpected); } } /** Convert hex-encoded UTF-8 to 16-bit chars. */ private static byte convertHexEncodedUtf8To16BitChars(byte[] bytes, int i) { byte b; byte escapedByte1 = bytes[i + 1]; switch (escapedByte1) { case '0': b = (byte) 0x00; break; case '1': b = (byte) 0x10; break; case '2': b = (byte) 0x20; break; case '3': b = (byte) 0x30; break; case '4': b = (byte) 0x40; break; case '5': b = (byte) 0x50; break; case '6': b = (byte) 0x60; break; case '7': b = (byte) 0x70; break; case '8': b = (byte) 0x80; break; case '9': b = (byte) 0x90; break; case 'a': case 'A': b = (byte) 0xA0; break; case 'b': case 'B': b = (byte) 0xB0; break; case 'c': case 'C': b = (byte) 0xC0; break; case 'd': case 'D': b = (byte) 0xD0; break; case 'e': case 'E': b = (byte) 0xE0; break; case 'f': case 'F': b = (byte) 0xF0; break; default: throw new RuntimeException("Unexpected byte: " + escapedByte1); } byte escapedByte2 = bytes[i + 2]; switch (escapedByte2) { case '0': break; case '1': b |= 0x01; break; case '2': b |= 0x02; break; case '3': b |= 0x03; break; case '4': b |= 0x04; break; case '5': b |= 0x05; break; case '6': b |= 0x06; break; case '7': b |= 0x07; break; case '8': b |= 0x08; break; case '9': b |= 0x09; break; case 'a': case 'A': b |= 0x0A; break; case 'b': case 'B': b |= 0x0B; break; case 'c': case 'C': b |= 0x0C; break; case 'd': case 'D': b |= 0x0D; break; case 'e': case 'E': b |= 0x0E; break; case 'f': case 'F': b |= 0x0F; break; default: throw new RuntimeException("Unexpected byte: " + escapedByte2); } return b; } /** * Returns the attribute name with no options (or subtypes). *

* Note: normal code should use AttributeDescription.valueOf(attrName).getNameOrOID() *

* However this method is used by UI code which sometimes receives strings which are not valid * attribute descriptions. * * @param attrDesc * the complete attribute name. * @return the attribute name with no options (or subtypes). */ public static String getAttributeNameWithoutOptions(String attrDesc) { int index = attrDesc.indexOf(";"); if (index != -1) { return attrDesc.substring(0, index); } return attrDesc; } /** * Strings any potential "separator" from a given string. * @param s string to strip * @param separator the separator string to remove * @return resulting string */ private static String stripStringToSingleLine(String s, String separator) { return (s != null) ? s.replaceAll(separator, "") : null; } /** The pattern for control characters. */ private static final Pattern cntrl_pattern = Pattern.compile("\\p{Cntrl}", Pattern.MULTILINE); /** * Checks if a string contains control characters. * @param s : the string to check * @return true if s contains control characters, false otherwise */ public static boolean hasControlCharaters(String s) { return cntrl_pattern.matcher(s).find(); } /** * This is a helper method that gets a String representation of the elements * in the Collection. The String will display the different elements separated * by the separator String. * * @param col * the collection containing the String. * @param separator * the separator String to be used. * @return the String representation for the collection. */ public static String getStringFromCollection(Collection col, String separator) { StringBuilder msg = new StringBuilder(); for (String m : col) { if (msg.length() > 0) { msg.append(separator); } msg.append(stripStringToSingleLine(m, separator)); } return msg.toString(); } /** * Returns the HTML representation of the 'Done' string. * @param progressFont the font to be used. * @return the HTML representation of the 'Done' string. */ public static String getProgressDone(Font progressFont) { return applyFont(INFO_CTRL_PANEL_PROGRESS_DONE.get(), progressFont.deriveFont(Font.BOLD)); } /** * Returns the HTML representation of a message to which some points have * been appended. * @param plainText the plain text. * @param progressFont the font to be used. * @return the HTML representation of a message to which some points have * been appended. */ public static String getProgressWithPoints(LocalizableMessage plainText, Font progressFont) { return applyFont(plainText.toString(), progressFont)+ applyFont(" ..... ", progressFont.deriveFont(Font.BOLD)); } /** * Returns the HTML representation of an error for a given text. * @param title the title. * @param titleFont the font for the title. * @param details the details. * @param detailsFont the font to be used for the details. * @return the HTML representation of an error for the given text. */ public static String getFormattedError(LocalizableMessage title, Font titleFont, LocalizableMessage details, Font detailsFont) { StringBuilder buf = new StringBuilder(); buf.append(UIFactory.getIconHtml(UIFactory.IconType.ERROR_LARGE)) .append(HTML_SPACE).append(HTML_SPACE) .append(applyFont(title.toString(), titleFont)); if (details != null) { buf.append("

") .append(applyFont(details.toString(), detailsFont)); } return "

"+UIFactory.applyErrorBackgroundToHtml(buf.toString())+ "
"; } /** * Returns the HTML representation of a success for a given text. * @param title the title. * @param titleFont the font for the title. * @param details the details. * @param detailsFont the font to be used for the details. * @return the HTML representation of a success for the given text. */ public static String getFormattedSuccess(LocalizableMessage title, Font titleFont, LocalizableMessage details, Font detailsFont) { StringBuilder buf = new StringBuilder(); buf.append(UIFactory.getIconHtml(UIFactory.IconType.INFORMATION_LARGE)) .append(HTML_SPACE).append(HTML_SPACE) .append(applyFont(title.toString(), titleFont)); if (details != null) { buf.append("

") .append(applyFont(details.toString(), detailsFont)); } return "
"+UIFactory.applyErrorBackgroundToHtml(buf.toString())+ "
"; } /** * Returns the HTML representation of a confirmation for a given text. * @param title the title. * @param titleFont the font for the title. * @param details the details. * @param detailsFont the font to be used for the details. * @return the HTML representation of a confirmation for the given text. */ public static String getFormattedConfirmation(LocalizableMessage title, Font titleFont, LocalizableMessage details, Font detailsFont) { StringBuilder buf = new StringBuilder(); buf.append(UIFactory.getIconHtml(UIFactory.IconType.WARNING_LARGE)) .append(HTML_SPACE).append(HTML_SPACE) .append(applyFont(title.toString(), titleFont)); if (details != null) { buf.append("

") .append(applyFont(details.toString(), detailsFont)); } return "
" + buf + "
"; } /** * Returns the HTML representation of a warning for a given text. * @param title the title. * @param titleFont the font for the title. * @param details the details. * @param detailsFont the font to be used for the details. * @return the HTML representation of a success for the given text. */ public static String getFormattedWarning(LocalizableMessage title, Font titleFont, LocalizableMessage details, Font detailsFont) { StringBuilder buf = new StringBuilder(); buf.append(UIFactory.getIconHtml(UIFactory.IconType.WARNING_LARGE)) .append(HTML_SPACE).append(HTML_SPACE) .append(applyFont(title.toString(), titleFont)); if (details != null) { buf.append("

") .append(applyFont(details.toString(), detailsFont)); } return "
"+UIFactory.applyErrorBackgroundToHtml(buf.toString())+ "
"; } /** * Sets the not available text to a label and associates a help icon and * a tooltip explaining that the data is not available because the server is * down. * @param l the label. */ public static void setNotAvailableBecauseServerIsDown(LabelWithHelpIcon l) { l.setText(INFO_CTRL_PANEL_NOT_AVAILABLE_LONG_LABEL.get().toString()); l.setHelpIconVisible(true); l.setHelpTooltip(INFO_NOT_AVAILABLE_SERVER_DOWN_TOOLTIP.get().toString()); } /** * Sets the not available text to a label and associates a help icon and * a tooltip explaining that the data is not available because authentication * is required. * @param l the label. */ public static void setNotAvailableBecauseAuthenticationIsRequired( LabelWithHelpIcon l) { l.setText(INFO_CTRL_PANEL_NOT_AVAILABLE_LONG_LABEL.get().toString()); l.setHelpIconVisible(true); l.setHelpTooltip(INFO_NOT_AVAILABLE_AUTHENTICATION_REQUIRED_TOOLTIP.get().toString()); } /** * Sets the not available text to a label and associates a help icon and * a tooltip explaining that the data is not available because the server is * down. * @param l the label. */ public static void setNotAvailableBecauseServerIsDown( SelectableLabelWithHelpIcon l) { l.setText(INFO_CTRL_PANEL_NOT_AVAILABLE_LONG_LABEL.get().toString()); l.setHelpIconVisible(true); l.setHelpTooltip(INFO_NOT_AVAILABLE_SERVER_DOWN_TOOLTIP.get().toString()); } /** * Sets the not available text to a label and associates a help icon and * a tooltip explaining that the data is not available because authentication * is required. * @param l the label. */ public static void setNotAvailableBecauseAuthenticationIsRequired( SelectableLabelWithHelpIcon l) { l.setText(INFO_CTRL_PANEL_NOT_AVAILABLE_LONG_LABEL.get().toString()); l.setHelpIconVisible(true); l.setHelpTooltip(INFO_NOT_AVAILABLE_AUTHENTICATION_REQUIRED_TOOLTIP.get().toString()); } /** * Updates a label by setting a warning icon and a text. * @param l the label to be updated. * @param text the text to be set on the label. */ public static void setWarningLabel(JLabel l, LocalizableMessage text) { l.setText(text.toString()); if (warningIcon == null) { warningIcon = createImageIcon("org/opends/quicksetup/images/warning_medium.gif"); warningIcon.setDescription( INFO_WARNING_ICON_ACCESSIBLE_DESCRIPTION.get().toString()); warningIcon.getAccessibleContext().setAccessibleName( INFO_WARNING_ICON_ACCESSIBLE_DESCRIPTION.get().toString()); } l.setIcon(warningIcon); l.setToolTipText(text.toString()); l.setHorizontalTextPosition(SwingConstants.RIGHT); } /** * Sets the not available text to a label with no icon nor tooltip. * @param l the label. */ public static void setNotAvailable(LabelWithHelpIcon l) { l.setText(INFO_CTRL_PANEL_NOT_AVAILABLE_LONG_LABEL.get().toString()); l.setHelpIconVisible(false); l.setHelpTooltip(null); } /** * Sets the a text to a label with no icon nor tooltip. * @param l the label. * @param text the text. */ public static void setTextValue(LabelWithHelpIcon l, String text) { l.setText(text); l.setHelpIconVisible(false); l.setHelpTooltip(null); } /** * Sets the not available text to a label with no icon nor tooltip. * @param l the label. */ public static void setNotAvailable(SelectableLabelWithHelpIcon l) { l.setText(INFO_CTRL_PANEL_NOT_AVAILABLE_LONG_LABEL.get().toString()); l.setHelpIconVisible(false); l.setHelpTooltip(null); } /** * Sets the a text to a label with no icon nor tooltip. * @param l the label. * @param text the text. */ public static void setTextValue(SelectableLabelWithHelpIcon l, String text) { l.setText(text); l.setHelpIconVisible(false); l.setHelpTooltip(null); } /** * Returns the server root directory (the path where the server is installed). *

* Note: this method is called by SNMP code. * * @return the server root directory (the path where the server is installed). */ public static File getServerRootDirectory() { if (rootDirectory == null) { // This allows testing of configuration components when the OpenDJ.jar // in the classpath does not necessarily point to the server's String installRoot = System.getProperty("org.opends.quicksetup.Root"); if (installRoot == null) { installRoot = getInstallPathFromClasspath(); } rootDirectory = new File(installRoot); } return rootDirectory; } /** * Returns the instance root directory (the path where the instance is * installed). * @param installPath The installRoot path. * @return the instance root directory (the path where the instance is * installed). */ public static File getInstanceRootDirectory(String installPath) { if (instanceRootDirectory == null) { instanceRootDirectory = new File( Utils.getInstancePathFromInstallPath(installPath)); } return instanceRootDirectory; } /** * Returns the path of the installation of the directory server. Note that * this method assumes that this code is being run locally. * @return the path of the installation of the directory server. */ public static String getInstallPathFromClasspath() { String installPath = null; /* Get the install path from the Class Path */ String sep = System.getProperty("path.separator"); String[] classPaths = System.getProperty("java.class.path").split(sep); String path = getInstallPath(classPaths); if (path != null) { File f = new File(path).getAbsoluteFile(); File librariesDir = f.getParentFile(); /* * Do a best effort to avoid having a relative representation (for * instance to avoid having ../../../). */ try { installPath = librariesDir.getParentFile().getCanonicalPath(); } catch (IOException ioe) { // Best effort installPath = librariesDir.getParent(); } } return installPath; } private static String getInstallPath(String[] classPaths) { for (String classPath : classPaths) { final String normPath = classPath.replace(File.separatorChar, '/'); if (normPath.endsWith(OPENDJ_BOOTSTRAP_CLIENT_JAR_RELATIVE_PATH) || normPath.endsWith(OPENDJ_BOOTSTRAP_JAR_RELATIVE_PATH)) { return classPath; } } return null; } /** * Returns whether the server located in the provided path is running. * * @param serverRootDirectory the path where the server is installed. * @return {@code true} if the server located in the provided path is running, * {@code false} otherwise. */ public static boolean isServerRunning(File serverRootDirectory) { String lockFileName = ServerConstants.SERVER_LOCK_FILE_NAME + ServerConstants.LOCK_FILE_SUFFIX; String lockPathRelative = Installation.LOCKS_PATH_RELATIVE; File locksPath = new File(serverRootDirectory, lockPathRelative); String lockFile = new File(locksPath, lockFileName).getAbsolutePath(); StringBuilder failureReason = new StringBuilder(); try { if (LockFileManager.acquireExclusiveLock(lockFile, failureReason)) { LockFileManager.releaseLock(lockFile, failureReason); return false; } return true; } catch (Throwable t) { // Assume that if we cannot acquire the lock file the // server is running. return true; } } private static final String VALID_SCHEMA_SYNTAX = "abcdefghijklmnopqrstuvwxyz0123456789-"; /** * Returns whether the provided string can be used as objectclass name. * * @param s the string to be analyzed. * @return {@code true} if the provided string can be used as objectclass name, * {@code false} otherwise. */ private static boolean isValidObjectclassName(String s) { if (s == null || s.length() == 0) { return false; } final StringCharacterIterator iter = new StringCharacterIterator(s, 0); char c = iter.first(); while (c != CharacterIterator.DONE) { if (VALID_SCHEMA_SYNTAX.indexOf(Character.toLowerCase(c)) == -1) { return false; } c = iter.next(); } return true; } /** * Returns whether the provided string can be used as attribute name. * * @param s the string to be analyzed. * @return {@code true} if the provided string can be used as attribute name, * {@code false} otherwise. */ public static boolean isValidAttributeName(String s) { return isValidObjectclassName(s); } /** * Returns the representation of the VLV index as it must be used in the * command-line. * @param index the VLV index. * @return the representation of the VLV index as it must be used in the * command-line. */ public static String getVLVNameInCommandLine(VLVIndexDescriptor index) { return "vlv."+index.getName(); } /** * Returns a string representing the VLV index in a cell. * @param index the VLV index to be represented. * @return the string representing the VLV index in a cell. */ public static String getVLVNameInCellRenderer(VLVIndexDescriptor index) { return INFO_CTRL_PANEL_VLV_INDEX_CELL.get(index.getName()).toString(); } private static final List standardSchemaFileNames = Arrays.asList( "00-core.ldif", "01-pwpolicy.ldif", "03-changelog.ldif", "03-uddiv3.ldif", "05-solaris.ldif" ); private static final List configurationSchemaOrigins = Arrays.asList( "OpenDJ Directory Server", "OpenDS Directory Server", "Sun Directory Server", "Microsoft Active Directory" ); private static final List standardSchemaOrigins = Arrays.asList( "Sun Java System Directory Server", "Solaris Specific", "X.501" ); private static final List configurationSchemaFileNames = Arrays.asList( "02-config.ldif", "06-compat.ldif" ); /** * Returns whether the provided schema element is part of the standard. * * @param fileElement the schema element. * @return {@code true} if the provided schema element is part of the standard, * {@code false} otherwise. */ public static boolean isStandard(SchemaElement fileElement) { final String fileName = getElementSchemaFile(fileElement); if (fileName != null) { return standardSchemaFileNames.contains(fileName) || fileName.toLowerCase().contains("-rfc"); } String xOrigin = getElementOrigin(fileElement); if (xOrigin != null) { return standardSchemaOrigins.contains(xOrigin) || xOrigin.startsWith("RFC ") || xOrigin.startsWith("draft-"); } return false; } /** * Returns whether the provided schema element is part of the configuration. * * @param fileElement the schema element. * @return {@code true} if the provided schema element is part of the configuration, * {@code false} otherwise. */ public static boolean isConfiguration(SchemaElement fileElement) { String fileName = getElementSchemaFile(fileElement); if (fileName != null) { return configurationSchemaFileNames.contains(fileName); } String xOrigin = getElementOrigin(fileElement); return xOrigin != null && configurationSchemaOrigins.contains(xOrigin); } /** * Returns the string representation of an attribute syntax. * @param syntax the attribute syntax. * @return the string representation of an attribute syntax. */ public static String getSyntaxText(Syntax syntax) { String syntaxName = syntax.getName(); String syntaxOID = syntax.getOID(); if (syntaxName != null) { return syntaxName + " - " + syntaxOID; } return syntaxOID; } /** * Returns whether the provided attribute has image syntax. * * @param attrName the name of the attribute. * @param schema the schema. * @return {@code true} if the provided attribute has image syntax, {@code false} otherwise. */ public static boolean hasImageSyntax(String attrName, Schema schema) { if ("photo".equals(AttributeDescription.valueOf(attrName).getNameOrOID())) { return true; } // Check all the attributes that we consider binaries. if (schema != null) { AttributeType attrType = AttributeDescription.valueOf(attrName, schema.getSchemaNG()).getAttributeType(); if (!attrType.isPlaceHolder()) { String syntaxOID = attrType.getSyntax().getOID(); return SchemaConstants.SYNTAX_JPEG_OID.equals(syntaxOID); } } return false; } /** * Returns whether the provided attribute has binary syntax. * * @param attrName the name of the attribute. * @param schema the schema. * @return {@code true} if the provided attribute has binary syntax, {@code false} otherwise. */ public static boolean hasBinarySyntax(String attrName, Schema schema) { return attrName.toLowerCase().contains(";binary") || hasAnySyntax(attrName, schema, binarySyntaxOIDs); } /** * Returns whether the provided attribute has password syntax. * * @param attrName the name of the attribute. * @param schema the schema. * @return {@code true} if the provided attribute has password syntax, {@code false} otherwise. */ public static boolean hasPasswordSyntax(String attrName, Schema schema) { if (schema != null) { AttributeType attrType = AttributeDescription.valueOf(attrName, schema.getSchemaNG()).getAttributeType(); if (!attrType.isPlaceHolder()) { PasswordType passwordType = SchemaUtils.checkPasswordType(attrType); return passwordType.equals(PasswordType.USER_PASSWORD); } } return false; } private static boolean hasAnySyntax(String attrName, Schema schema, List oids) { if (schema != null) { AttributeType attrType = AttributeDescription.valueOf(attrName, schema.getSchemaNG()).getAttributeType(); if (!attrType.isPlaceHolder()) { return oids.contains(attrType.getSyntax().getOID()); } } return false; } /** * Returns the string representation of a matching rule. * @param matchingRule the matching rule. * @return the string representation of a matching rule. */ public static String getMatchingRuleText(MatchingRule matchingRule) { String nameOrOID = matchingRule.getNameOrOID(); String oid = matchingRule.getOID(); if (!nameOrOID.equals(oid)) { // This is the name only return nameOrOID + " - " + oid; } return oid; } /** * Returns the connection to connect to the administration connector * of the server using the information in the ControlCenterInfo object (which * provides the host and administration connector port to be used) and some * LDAP credentials. * It also tests that the provided credentials have enough rights to read the * configuration. * @param controlInfo the object which provides the connection parameters. * @param bindDN the base DN to be used to bind. * @param pwd the password to be used to bind. * @return the connection to the server. * @throws IOException if there was a problem connecting to the server * or the provided credentials do not have enough rights. * @throws ConfigReadException if there is an error reading the configuration. */ public static ConnectionWrapper getAdminDirContext(ControlPanelInfo controlInfo, DN bindDN, String pwd) throws IOException, ConfigReadException { return createConnection(controlInfo.getAdminConnectorHostPort(), LDAPS, bindDN, pwd, controlInfo); } /** * Returns the connection to connect to the server using the * information in the ControlCenterInfo object (which provides the host, port * and protocol to be used) and some LDAP credentials. It also tests that * the provided credentials have enough rights to read the configuration. * @param controlInfo the object which provides the connection parameters. * @param bindDN the base DN to be used to bind. * @param pwd the password to be used to bind. * @return the connection to the server. * @throws IOException if there was a problem connecting to the server * or the provided credentials do not have enough rights. * @throws ConfigReadException if there is an error reading the configuration. */ public static ConnectionWrapper getUserDataDirContext(ControlPanelInfo controlInfo, DN bindDN, String pwd) throws IOException, ConfigReadException { if (controlInfo.connectUsingStartTLS()) { return createConnection(controlInfo.getStartTlsHostPort(), START_TLS, bindDN, pwd, controlInfo); } else if (controlInfo.connectUsingLDAPS()) { return createConnection(controlInfo.getLdapsHostPort(), LDAPS, bindDN, pwd, controlInfo); } else { return createConnection(controlInfo.getLdapHostPort(), LDAP, bindDN, pwd, controlInfo); } } private static ConnectionWrapper createConnection(HostPort hostPort, Type connectionType, DN bindDN, String bindPwd, ControlPanelInfo controlInfo) throws IOException, ConfigReadException { if (hostPort == null) { throw new ConfigReadException(ERR_COULD_NOT_FIND_VALID_LDAPURL.get()); } ConnectionWrapper conn = new ConnectionWrapper(hostPort, connectionType, bindDN, bindPwd, controlInfo.getConnectTimeout(), controlInfo.getTrustManager()); checkCanReadConfig(conn); return conn; } /** * Checks that the provided connection can read cn=config. * * @param conn * the connection to be tested. * @throws IOException * if an error occurs while reading cn=config. */ private static void checkCanReadConfig(ConnectionWrapper conn) throws IOException { // Search for the config to check that it is the directory manager. SearchRequest request = newSearchRequest("cn=config", BASE_OBJECT, "objectclass=*", NO_ATTRIBUTES); // rely on exception being thrown if we cannot read conn.getConnection().searchSingleEntry(request); } /** * Ping the specified connection. This method sends a search request on the root entry of the DIT * and forward the corresponding exception (if any). * * @param connWrapper * the connection to be "pinged". */ public static void ping(ConnectionWrapper connWrapper) { SearchRequest request = newSearchRequest("", BASE_OBJECT, "objectClass=*", NO_ATTRIBUTES) .setSizeLimit(0) .setTimeLimit(0) .setDereferenceAliasesPolicy(NEVER); connWrapper.getConnection().search(request).close(); } /** * Deletes a configuration subtree using the provided configuration handler. * * @param confHandler * the configuration handler to be used to delete the subtree. * @param dn * the DN of the subtree to be deleted. * @throws OpenDsException * if an error occurs. * @throws ConfigException * if an error occurs. */ public static void deleteConfigSubtree(ConfigurationHandler confHandler, DN dn) throws OpenDsException, ConfigException { Entry confEntry = confHandler.getEntry(dn); if (confEntry != null) { // Copy the values to avoid problems with this recursive method. for (DN childDN : new ArrayList<>(confHandler.getChildren(dn))) { deleteConfigSubtree(confHandler, childDN); } confHandler.deleteEntry(dn); } } /** * Sets the required icon to the provided label. * @param label the label to be updated. */ public static void setRequiredIcon(JLabel label) { if (requiredIcon == null) { requiredIcon = createImageIcon(IconPool.IMAGE_PATH+"/required.gif"); requiredIcon.setDescription( INFO_REQUIRED_ICON_ACCESSIBLE_DESCRIPTION.get().toString()); requiredIcon.getAccessibleContext().setAccessibleName( INFO_REQUIRED_ICON_ACCESSIBLE_DESCRIPTION.get().toString()); } label.setIcon(requiredIcon); label.setHorizontalTextPosition(SwingConstants.LEADING); } /** * Updates the scrolls with the provided points. * This method uses SwingUtilities.invokeLater so it can be also called * outside the event thread. * @param pos the scroll and points. */ public static void updateViewPositions(final ViewPositions pos) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { for (int i=0; i it = attribute.iterator(); if (it.hasNext()) { try { return attribute.parse().asLong(); } catch (Throwable t1) { ByteString v = it.next(); try { return Double.parseDouble(v.toString()); } catch (Throwable t2) { // Cannot convert it, just return it return v; } } } return null; } /** * Returns the first value as a String for a given attribute in the provided * entry. * * @param sr * the entry. It may be {@code null}. * @param attrName * the attribute name. * @return the first value as a String for a given attribute in the provided * entry. */ public static String getFirstValueAsString(Entry sr, String attrName) { if (sr != null) { final Attribute attr = sr.getAttribute(attrName); if (attr != null && !attr.isEmpty()) { final ByteString v = attr.iterator().next(); if (v != null) { return v.toString(); } } } return null; } /** * 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, SearchResultEntry monitoringEntry) { Attribute monitoringAttr = monitoringEntry.getAttribute(attr.getAttributeName()); if (monitoringAttr == null) { return NO_VALUE_SET.toString(); } String monitoringValue = monitoringAttr.firstValueAsString(); if (isNotImplemented(attr, monitoringEntry)) { return NOT_IMPLEMENTED.toString(); } else if (attr.isNumericDate()) { if ("0".equals(monitoringValue)) { return NO_VALUE_SET.toString(); } Long l = Long.parseLong(monitoringValue); Date date = new Date(l); return ConfigFromDirContext.formatter.format(date); } else if (attr.isTime()) { if ("-1".equals(monitoringValue)) { return NO_VALUE_SET.toString(); } return monitoringValue; } else if (attr.isGMTDate()) { try { Date date = ConfigFromDirContext.utcParser.parse(monitoringValue); return ConfigFromDirContext.formatter.format(date); } catch (Throwable t) { return monitoringValue; } } else if (attr.isValueInBytes()) { Long l = Long.parseLong(monitoringValue); long mb = l / (1024 * 1024); long kbs = (l - mb * 1024 * 1024) / 1024; return INFO_CTRL_PANEL_MEMORY_VALUE.get(mb, kbs).toString(); } return monitoringValue; } /** * Returns whether the provided monitoring value represents the non implemented label. * * @param attr the attribute to analyze. * @param monitoringEntry the monitoring entry. * @return {@code true} if the provided monitoring value represents the non implemented label, * {@code false} otherwise. */ private static boolean isNotImplemented(MonitoringAttributes attr, SearchResultEntry monitoringEntry) { Attribute monitoringValue = monitoringEntry.getAttribute(attr.getAttributeName()); if (attr.isNumeric() && monitoringValue != null) { try { monitoringValue.parse().asLong(); return false; } catch (Throwable t) { return true; } } return false; } /** * Adds a click tool tip listener to the provided component. * @param comp the component. */ public static void addClickTooltipListener(JComponent comp) { comp.addMouseListener(new ClickTooltipDisplayer()); } /** * Updates a combo box model with a number of items. * The method assumes that is being called from the event thread. * @param newElements the new items for the combo box model. * @param model the combo box model to be updated. */ public static 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 being called from 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}, the equals method will be used. */ public static void updateComboBoxModel(Collection newElements, DefaultComboBoxModel model, Comparator comparator) { boolean changed = newElements.size() != model.getSize(); if (!changed) { int i = 0; for (Object newElement : newElements) { if (comparator != null) { changed = comparator.compare(newElement, model.getElementAt(i)) != 0; } else { 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 possibleResults, Collection attrNames) { for (String attrName : attrNames) { possibleResults.add(compareForAttribute(monitor1, monitor2, attrName)); } } private static int compareForAttribute(SearchResultEntry monitor1, SearchResultEntry monitor2, String attrName) { if (monitor1 == null) { return monitor2 == null ? 0 : -1; } else if (monitor2 == null) { return 1; } else { Object v1 = getFirstMonitoringValue(monitor1.getAttribute(attrName)); Object v2 = getFirstMonitoringValue(monitor2.getAttribute(attrName)); if (v1 == null) { return v2 == null ? 0 : -1; } else if (v2 == null) { return 1; } else if (v1 instanceof Number) { if (v2 instanceof Number) { if (v1 instanceof Double || v2 instanceof Double) { double n1 = ((Number) v1).doubleValue(); double n2 = ((Number) v2).doubleValue(); if (n1 > n2) { return 1; } else if (n1 < n2) { return -1; } else { return 0; } } else { long n1 = ((Number) v1).longValue(); long n2 = ((Number) v2).longValue(); if (n1 > n2) { return 1; } else if (n1 < n2) { return -1; } else { return 0; } } } else { return 1; } } else if (v2 instanceof Number) { return -1; } else { return v1.toString().compareTo(v2.toString()); } } } /** * Throw the first exception of the list (if any). * * @param * The exception type * @param exceptions * A list of exceptions. * @throws E * The first element of the provided list (if the list is not empty). */ public static void throwFirstFrom(List exceptions) throws E { if (!exceptions.isEmpty()) { throw exceptions.get(0); } } /** Initialize the configuration framework. */ public static void initializeConfigurationFramework() { if (!ConfigurationFramework.getInstance().isInitialized()) { try { // Initialize configuration framework without logging anything. final Logger configFrameworkLogger = Logger.getLogger("com.forgerock.opendj.ldap.config.config"); configFrameworkLogger.setUseParentHandlers(false); ConfigurationFramework.getInstance().initialize(); configFrameworkLogger.setUseParentHandlers(true); } catch (ConfigException e) { final LocalizableMessage message = ERROR_CTRL_PANEL_INITIALIZE_CONFIG_OFFLINE.get(e.getLocalizedMessage()); logger.error(message); throw new RuntimeException(message.toString(), e); } } } /** * Test whether provided schema element is an attribute type. * * @param element * Element to check. * @return {@code true} iff element is an attribute type. */ public static boolean isAttributeType(SchemaElement element) { return element instanceof AttributeType; } /** * Returns the name of configuration attribute corresponding to the provided element. * * @param element * Either an attribute type or an object class. * Using any other schema element will return invalid result. * @return Either "attributeTypes" or "objectClasses" */ public static String getAttributeConfigName(SchemaElement element) { return isAttributeType(element) ? ConfigConstants.ATTR_ATTRIBUTE_TYPES : ConfigConstants.ATTR_OBJECTCLASSES; } /** * Returns the name or OID of provided element. * * @param element * Either an attribute type or an object class. * Using any other schema element will yield an exception. * @return Either "attributeTypes" or "objectClasses" */ public static String getElementNameOrOID(SchemaElement element) { if (element instanceof AttributeType) { return ((AttributeType) element).getNameOrOID(); } else if (element instanceof ObjectClass) { return ((ObjectClass) element).getNameOrOID(); } throw new RuntimeException("getElementNameOrOID() not implemented for element of type " + element.getClass()); } /** * Returns the OID of provided element. * * @param element * Either an attribute type or an object class. * Using any other schema element will yield an exception. * @return Either "attributeTypes" or "objectClasses" */ public static String getElementOID(SchemaElement element) { if (element instanceof AttributeType) { return ((AttributeType) element).getOID(); } else if (element instanceof ObjectClass) { return ((ObjectClass) element).getOID(); } throw new RuntimeException("getElementOID() not implemented for element of type " + element.getClass()); } /** * Return a new attribute type with the provided new superior type. * * @param attributeType * Initial attribute type. * @param newSuperiorType * new superior type to use. * @return the new attribute type */ public static AttributeType getNewAttributeTypeWithNewSuperiorType(AttributeType attributeType, AttributeType newSuperiorType) { String superiorTypeOID = newSuperiorType != null ? newSuperiorType.getNameOrOID() : null; return new SchemaBuilder() .buildAttributeType(attributeType) .superiorType(superiorTypeOID) .addToSchemaOverwrite() .toSchema() .getAttributeType(attributeType.getNameOrOID()); } /** * Updates an extra property of provided schema element with a single value. * * @param serverContext * the server context * @param element * Either an attribute type or an object class. * Using any other schema element will yield an exception. * @param property * the property to set * @param value * the value to set * @return the updated schema element */ public static SchemaElement updateSchemaElementExtraPropertySingleValue(ServerContext serverContext, SchemaElement element, String property, String value) { List values = value != null ? Arrays.asList(value) : null; return updateSchemaElementExtraPropertyMultiplesValues(serverContext, element, property, values); } /** * Updates an extra property of provided schema element with several values. * * @param serverContext * the server context * @param element * Either an attribute type or an object class. * Using any other schema element will yield an exception. * @param property * the property to set * @param values * the list of values to set * @return the updated schema element */ public static SchemaElement updateSchemaElementExtraPropertyMultiplesValues(ServerContext serverContext, SchemaElement element, String property, List values) { org.forgerock.opendj.ldap.schema.Schema schemaNG = serverContext != null ? serverContext.getSchemaNG() : org.forgerock.opendj.ldap.schema.Schema.getDefaultSchema(); SchemaBuilder schemaBuilder = new SchemaBuilder(schemaNG); if (element instanceof AttributeType) { AttributeType attr = (AttributeType) element; AttributeType.Builder builder = schemaBuilder.buildAttributeType(attr).removeExtraProperty(property, (String) null); if (values != null && !values.isEmpty()) { builder.extraProperties(property, values); } return builder.addToSchemaOverwrite().toSchema().getAttributeType(attr.getNameOrOID()); } else if (element instanceof ObjectClass) { ObjectClass oc = (ObjectClass) element; ObjectClass.Builder builder = schemaBuilder.buildObjectClass(oc).removeExtraProperty(property, (String) null); if (values != null && !values.isEmpty()) { builder.extraProperties(property, values); } return builder.addToSchemaOverwrite().toSchema().getObjectClass(oc.getNameOrOID()); } throw new RuntimeException("updateSchemaElementExtraPropertyMultiplesValues() not implemented for element of type " + element.getClass()); } }