001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2008-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2011-2016 ForgeRock AS. 016 */ 017package org.opends.guitools.controlpanel.util; 018 019import static com.forgerock.opendj.cli.Utils.*; 020import static com.forgerock.opendj.util.OperatingSystem.*; 021 022import static org.opends.admin.ads.util.ConnectionUtils.*; 023import static org.opends.admin.ads.util.PreferredConnection.Type.*; 024import static org.opends.messages.AdminToolMessages.*; 025import static org.opends.quicksetup.Installation.*; 026 027import java.awt.Color; 028import java.awt.Component; 029import java.awt.Container; 030import java.awt.Dimension; 031import java.awt.Font; 032import java.awt.Image; 033import java.awt.Point; 034import java.awt.Toolkit; 035import java.awt.Window; 036import java.awt.event.MouseAdapter; 037import java.awt.event.MouseEvent; 038import java.io.File; 039import java.io.IOException; 040import java.io.UnsupportedEncodingException; 041import java.text.CharacterIterator; 042import java.text.StringCharacterIterator; 043import java.util.ArrayList; 044import java.util.Arrays; 045import java.util.Collection; 046import java.util.Comparator; 047import java.util.Date; 048import java.util.List; 049import java.util.logging.Logger; 050import java.util.regex.Pattern; 051 052import javax.naming.CompositeName; 053import javax.naming.InvalidNameException; 054import javax.naming.Name; 055import javax.naming.NamingEnumeration; 056import javax.naming.NamingException; 057import javax.naming.directory.SearchControls; 058import javax.naming.directory.SearchResult; 059import javax.naming.ldap.InitialLdapContext; 060import javax.naming.ldap.LdapName; 061import javax.swing.BorderFactory; 062import javax.swing.DefaultComboBoxModel; 063import javax.swing.ImageIcon; 064import javax.swing.JButton; 065import javax.swing.JCheckBox; 066import javax.swing.JComboBox; 067import javax.swing.JComponent; 068import javax.swing.JDialog; 069import javax.swing.JEditorPane; 070import javax.swing.JFrame; 071import javax.swing.JLabel; 072import javax.swing.JMenu; 073import javax.swing.JMenuItem; 074import javax.swing.JOptionPane; 075import javax.swing.JPasswordField; 076import javax.swing.JRadioButton; 077import javax.swing.JScrollPane; 078import javax.swing.JTable; 079import javax.swing.JTextArea; 080import javax.swing.JTextField; 081import javax.swing.SwingConstants; 082import javax.swing.SwingUtilities; 083import javax.swing.border.Border; 084import javax.swing.border.EmptyBorder; 085import javax.swing.border.EtchedBorder; 086import javax.swing.border.TitledBorder; 087import javax.swing.table.JTableHeader; 088import javax.swing.table.TableCellRenderer; 089import javax.swing.table.TableColumn; 090import javax.swing.table.TableColumnModel; 091 092import org.forgerock.i18n.LocalizableMessage; 093import org.forgerock.i18n.slf4j.LocalizedLogger; 094import org.forgerock.opendj.config.ConfigurationFramework; 095import org.forgerock.opendj.config.server.ConfigException; 096import org.forgerock.opendj.ldap.AttributeDescription; 097import org.forgerock.opendj.ldap.DN; 098import org.forgerock.opendj.ldap.schema.AttributeType; 099import org.forgerock.opendj.ldap.schema.MatchingRule; 100import org.forgerock.opendj.ldap.schema.Syntax; 101import org.opends.admin.ads.util.ConnectionWrapper; 102import org.opends.guitools.controlpanel.ControlPanel; 103import org.opends.guitools.controlpanel.browser.IconPool; 104import org.opends.guitools.controlpanel.datamodel.CategorizedComboBoxElement; 105import org.opends.guitools.controlpanel.datamodel.ConfigReadException; 106import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo; 107import org.opends.guitools.controlpanel.datamodel.CustomSearchResult; 108import org.opends.guitools.controlpanel.datamodel.MonitoringAttributes; 109import org.opends.guitools.controlpanel.datamodel.SomeSchemaElement; 110import org.opends.guitools.controlpanel.datamodel.SortableTableModel; 111import org.opends.guitools.controlpanel.datamodel.VLVIndexDescriptor; 112import org.opends.guitools.controlpanel.event.ClickTooltipDisplayer; 113import org.opends.guitools.controlpanel.event.ComboKeySelectionManager; 114import org.opends.guitools.controlpanel.event.TextComponentFocusListener; 115import org.opends.guitools.controlpanel.ui.ColorAndFontConstants; 116import org.opends.guitools.controlpanel.ui.components.LabelWithHelpIcon; 117import org.opends.guitools.controlpanel.ui.components.SelectableLabelWithHelpIcon; 118import org.opends.guitools.controlpanel.ui.renderer.AccessibleTableHeaderRenderer; 119import org.opends.quicksetup.Installation; 120import org.opends.quicksetup.ui.UIFactory; 121import org.opends.quicksetup.util.Utils; 122import org.opends.server.config.ConfigurationHandler; 123import org.opends.server.core.LockFileManager; 124import org.opends.server.schema.SchemaConstants; 125import org.opends.server.types.OpenDsException; 126import org.opends.server.types.Schema; 127import org.opends.server.util.SchemaUtils; 128import org.opends.server.util.SchemaUtils.PasswordType; 129import org.opends.server.util.ServerConstants; 130import org.opends.server.util.StaticUtils; 131 132/** A static class that provides miscellaneous functions. */ 133public class Utilities 134{ 135 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 136 137 private static File rootDirectory; 138 private static File instanceRootDirectory; 139 140 private static final String HTML_SPACE = " "; 141 private static final String[] attrsToObfuscate = { ServerConstants.ATTR_USER_PASSWORD }; 142 private static final List<String> binarySyntaxOIDs = Arrays.asList( 143 SchemaConstants.SYNTAX_BINARY_OID, 144 SchemaConstants.SYNTAX_JPEG_OID, 145 SchemaConstants.SYNTAX_CERTIFICATE_OID, 146 SchemaConstants.SYNTAX_OCTET_STRING_OID 147 ); 148 149 private static ImageIcon warningIcon; 150 private static ImageIcon requiredIcon; 151 152 private final static LocalizableMessage NO_VALUE_SET = INFO_CTRL_PANEL_NO_MONITORING_VALUE.get(); 153 private final static LocalizableMessage NOT_IMPLEMENTED = INFO_CTRL_PANEL_NOT_IMPLEMENTED.get(); 154 155 /** 156 * Creates a combo box. 157 * 158 * @param <T> 159 * The combo box data type. 160 * @return a combo box. 161 */ 162 public static <T> JComboBox<T> createComboBox() 163 { 164 JComboBox<T> combo = new JComboBox<>(); 165 if (isMacOS()) 166 { 167 combo.setOpaque(false); 168 } 169 combo.setKeySelectionManager(new ComboKeySelectionManager(combo)); 170 return combo; 171 } 172 173 /** 174 * Creates a frame. 175 * @return a frame. 176 */ 177 public static JFrame createFrame() 178 { 179 JFrame frame = new JFrame(); 180 frame.setResizable(true); 181 org.opends.quicksetup.ui.Utilities.setFrameIcon(frame); 182 return frame; 183 } 184 185 /** 186 * Returns whether an attribute value must be obfuscated because 187 * it contains sensitive information (like passwords). 188 * 189 * @param attrName the attribute name. 190 * @param schema the schema of the server. 191 * @return {@code true} if an attribute value must be obfuscated because 192 * it contains sensitive information (like passwords) and {@code false} 193 * otherwise. 194 */ 195 public static boolean mustObfuscate(String attrName, Schema schema) 196 { 197 if (schema != null) 198 { 199 return hasPasswordSyntax(attrName, schema); 200 } 201 for (String attr : attrsToObfuscate) 202 { 203 if (attr.equalsIgnoreCase(attrName)) 204 { 205 return true; 206 } 207 } 208 return false; 209 } 210 211 /** 212 * Derives a color by adding the specified offsets to the base color's 213 * hue, saturation, and brightness values. The resulting hue, saturation, 214 * and brightness values will be constrained to be between 0 and 1. 215 * @param base the color to which the HSV offsets will be added 216 * @param dH the offset for hue 217 * @param dS the offset for saturation 218 * @param dB the offset for brightness 219 * @return Color with modified HSV values 220 */ 221 public static Color deriveColorHSB(Color base, float dH, float dS, float dB) 222 { 223 float hsb[] = Color.RGBtoHSB( 224 base.getRed(), base.getGreen(), base.getBlue(), null); 225 226 hsb[0] += dH; 227 hsb[1] += dS; 228 hsb[2] += dB; 229 return Color.getHSBColor( 230 hsb[0] < 0? 0 : (hsb[0] > 1? 1 : hsb[0]), 231 hsb[1] < 0? 0 : (hsb[1] > 1? 1 : hsb[1]), 232 hsb[2] < 0? 0 : (hsb[2] > 1? 1 : hsb[2])); 233 234 } 235 236 /** 237 * Displays an error dialog that contains a set of error messages. 238 * @param parentComponent the parent component relative to which the dialog 239 * will be displayed. 240 * @param errors the set of error messages that the dialog must display. 241 */ 242 public static void displayErrorDialog(Component parentComponent, 243 Collection<LocalizableMessage> errors) 244 { 245 /* 246 ErrorPanel panel = new ErrorPanel("Error", errors); 247 GenericDialog dlg = new GenericDialog(null, panel); 248 dlg.setModal(true); 249 Utilities.centerGoldenMean(dlg, Utilities.getParentDialog(this)); 250 dlg.setVisible(true); 251 */ 252 ArrayList<String> stringErrors = new ArrayList<>(); 253 for (LocalizableMessage err : errors) 254 { 255 stringErrors.add(err.toString()); 256 } 257 String msg = getStringFromCollection(stringErrors, "<br>"); 258 String plainText = msg.replaceAll("<br>", ServerConstants.EOL); 259 String wrappedText = wrapText(plainText, 70); 260 wrappedText = wrappedText.replaceAll(ServerConstants.EOL, "<br>"); 261 JOptionPane.showMessageDialog( 262 parentComponent, "<html>"+wrappedText, 263 INFO_CTRL_PANEL_ERROR_DIALOG_TITLE.get().toString(), 264 JOptionPane.ERROR_MESSAGE); 265 } 266 267 /** 268 * Displays a confirmation dialog. 269 * 270 * @param parentComponent the parent component relative to which the dialog 271 * will be displayed. 272 * @param title the title of the dialog. 273 * @param msg the message to be displayed. 274 * @return {@code true} if the user accepts the message, {@code false} otherwise. 275 */ 276 public static boolean displayConfirmationDialog(Component parentComponent, 277 LocalizableMessage title, LocalizableMessage msg) 278 { 279 String plainText = msg.toString().replaceAll("<br>", ServerConstants.EOL); 280 String wrappedText = wrapText(plainText, 70); 281 wrappedText = wrappedText.replaceAll(ServerConstants.EOL, "<br>"); 282 return JOptionPane.YES_OPTION == JOptionPane.showOptionDialog( 283 parentComponent, "<html>"+wrappedText, 284 title.toString(), 285 JOptionPane.YES_NO_OPTION, 286 JOptionPane.QUESTION_MESSAGE, 287 null, // don't use a custom Icon 288 null, // the titles of buttons 289 null); // default button title 290 } 291 292 /** 293 * Displays a warning dialog. 294 * @param parentComponent the parent component relative to which the dialog 295 * will be displayed. 296 * @param title the title of the dialog. 297 * @param msg the message to be displayed. 298 */ 299 public static void displayWarningDialog(Component parentComponent, 300 LocalizableMessage title, LocalizableMessage msg) 301 { 302 String plainText = msg.toString().replaceAll("<br>", ServerConstants.EOL); 303 String wrappedText = wrapText(plainText, 70); 304 wrappedText = wrappedText.replaceAll(ServerConstants.EOL, "<br>"); 305 JOptionPane.showMessageDialog( 306 parentComponent, "<html>"+wrappedText, 307 title.toString(), 308 JOptionPane.WARNING_MESSAGE); 309 } 310 311 312 /** 313 * Creates a JEditorPane that displays a message. 314 * @param text the message of the editor pane in HTML format. 315 * @param font the font to be used in the message. 316 * @return a JEditorPane that displays a message. 317 */ 318 public static JEditorPane makeHtmlPane(CharSequence text, Font font) 319 { 320 JEditorPane pane = new JEditorPane(); 321 pane.setContentType("text/html"); 322 pane.setFont(font); 323 if (text != null) 324 { 325 pane.setText(applyFont(text, font)); 326 } 327 pane.setEditable(false); 328 pane.setBorder(new EmptyBorder(0, 0, 0, 0)); 329 pane.setOpaque(false); 330 pane.setFocusCycleRoot(false); 331 return pane; 332 } 333 334 /** 335 * Creates a JEditorPane that displays a message. 336 * @param text the message of the editor pane in plain text format. 337 * @param font the font to be used in the message. 338 * @return a JEditorPane that displays a message. 339 */ 340 public static JEditorPane makePlainTextPane(String text, Font font) 341 { 342 JEditorPane pane = new JEditorPane(); 343 pane.setContentType("text/plain"); 344 if (text != null) 345 { 346 pane.setText(text); 347 } 348 pane.setFont(font); 349 pane.setEditable(false); 350 pane.setBorder(new EmptyBorder(0, 0, 0, 0)); 351 pane.setOpaque(false); 352 pane.setFocusCycleRoot(false); 353 return pane; 354 } 355 356 /** 357 * Returns the HTML style representation for the given font. 358 * @param font the font for which we want to get an HTML style representation. 359 * @return the HTML style representation for the given font. 360 */ 361 private static String getFontStyle(Font font) 362 { 363 StringBuilder buf = new StringBuilder(); 364 365 buf.append("font-family:").append(font.getName()) 366 .append(";font-size:").append(font.getSize()).append("pt"); 367 368 if (font.isItalic()) 369 { 370 buf.append(";font-style:italic"); 371 } 372 373 if (font.isBold()) 374 { 375 buf.append(";font-weight:bold;"); 376 } 377 378 return buf.toString(); 379 } 380 381 /** 382 * Creates a titled border. 383 * @param msg the message to be displayed in the titled border. 384 * @return the created titled border. 385 */ 386 public static Border makeTitledBorder(LocalizableMessage msg) 387 { 388 TitledBorder border = new TitledBorder(new EtchedBorder(), 389 " "+msg+" "); 390 border.setTitleFont(ColorAndFontConstants.titleFont); 391 border.setTitleColor(ColorAndFontConstants.foreground); 392 return border; 393 } 394 395 /** 396 * Returns a JScrollPane that contains the provided component. The scroll 397 * pane will not contain any border. 398 * @param comp the component contained in the scroll pane. 399 * @return a JScrollPane that contains the provided component. The scroll 400 * pane will not contain any border. 401 */ 402 public static JScrollPane createBorderLessScrollBar(Component comp) 403 { 404 JScrollPane scroll = new JScrollPane(comp); 405 scroll.setBorder(new EmptyBorder(0, 0, 0, 0)); 406 scroll.setViewportBorder(new EmptyBorder(0, 0, 0, 0)); 407 scroll.setOpaque(false); 408 scroll.getViewport().setOpaque(false); 409 scroll.getViewport().setBackground(ColorAndFontConstants.background); 410 scroll.setBackground(ColorAndFontConstants.background); 411 UIFactory.setScrollIncrementUnit(scroll); 412 return scroll; 413 } 414 415 /** 416 * Returns a JScrollPane that contains the provided component. 417 * @param comp the component contained in the scroll pane. 418 * @return a JScrollPane that contains the provided component. 419 */ 420 public static JScrollPane createScrollPane(Component comp) 421 { 422 JScrollPane scroll = new JScrollPane(comp); 423 scroll.getViewport().setOpaque(false); 424 scroll.setOpaque(false); 425 scroll.getViewport().setBackground(ColorAndFontConstants.background); 426 scroll.setBackground(ColorAndFontConstants.background); 427 UIFactory.setScrollIncrementUnit(scroll); 428 return scroll; 429 } 430 431 /** 432 * Creates a button. 433 * @param text the message to be displayed by the button. 434 * @return the created button. 435 */ 436 public static JButton createButton(LocalizableMessage text) 437 { 438 JButton button = new JButton(text.toString()); 439 button.setOpaque(false); 440 button.setForeground(ColorAndFontConstants.buttonForeground); 441 button.getAccessibleContext().setAccessibleName(text.toString()); 442 return button; 443 } 444 445 /** 446 * Creates a radio button. 447 * @param text the message to be displayed by the radio button. 448 * @return the created radio button. 449 */ 450 public static JRadioButton createRadioButton(LocalizableMessage text) 451 { 452 JRadioButton button = new JRadioButton(text.toString()); 453 button.setOpaque(false); 454 button.setForeground(ColorAndFontConstants.buttonForeground); 455 button.getAccessibleContext().setAccessibleName(text.toString()); 456 return button; 457 } 458 459 /** 460 * Creates a check box. 461 * @param text the message to be displayed by the check box. 462 * @return the created check box. 463 */ 464 public static JCheckBox createCheckBox(LocalizableMessage text) 465 { 466 JCheckBox cb = new JCheckBox(text.toString()); 467 cb.setOpaque(false); 468 cb.setForeground(ColorAndFontConstants.buttonForeground); 469 cb.getAccessibleContext().setAccessibleName(text.toString()); 470 return cb; 471 } 472 473 /** 474 * Creates a menu item with the provided text. 475 * @param msg the text. 476 * @return a menu item with the provided text. 477 */ 478 public static JMenuItem createMenuItem(LocalizableMessage msg) 479 { 480 return new JMenuItem(msg.toString()); 481 } 482 483 /** 484 * Creates a menu with the provided text. 485 * @param msg the text. 486 * @param description the accessible description. 487 * @return a menu with the provided text. 488 */ 489 public static JMenu createMenu(LocalizableMessage msg, LocalizableMessage description) 490 { 491 JMenu menu = new JMenu(msg.toString()); 492 menu.getAccessibleContext().setAccessibleDescription( 493 description.toString()); 494 return menu; 495 } 496 497 /** 498 * Creates a label of type 'primary' (with bigger font than usual) with no 499 * text. 500 * @return the label of type 'primary' (with bigger font than usual) with no 501 * text. 502 */ 503 public static JLabel createPrimaryLabel() 504 { 505 return createPrimaryLabel(LocalizableMessage.EMPTY); 506 } 507 508 /** 509 * Creates a label of type 'primary' (with bigger font than usual). 510 * @param text the message to be displayed by the label. 511 * @return the label of type 'primary' (with bigger font than usual). 512 */ 513 public static JLabel createPrimaryLabel(LocalizableMessage text) 514 { 515 JLabel label = new JLabel(text.toString()); 516 label.setFont(ColorAndFontConstants.primaryFont); 517 label.setForeground(ColorAndFontConstants.foreground); 518 return label; 519 } 520 521 /** 522 * Creates a label of type 'inline help' (with smaller font). 523 * @param text the message to be displayed by the label. 524 * @return the label of type 'inline help' (with smaller font). 525 */ 526 public static JLabel createInlineHelpLabel(LocalizableMessage text) 527 { 528 JLabel label = new JLabel(text.toString()); 529 label.setFont(ColorAndFontConstants.inlineHelpFont); 530 label.setForeground(ColorAndFontConstants.foreground); 531 return label; 532 } 533 534 /** 535 * Creates a label of type 'title' (with bigger font). 536 * @param text the message to be displayed by the label. 537 * @return the label of type 'title' (with bigger font). 538 */ 539 public static JLabel createTitleLabel(LocalizableMessage text) 540 { 541 JLabel label = new JLabel(text.toString()); 542 label.setFont(ColorAndFontConstants.titleFont); 543 label.setForeground(ColorAndFontConstants.foreground); 544 return label; 545 } 546 547 /** 548 * Creates a label (with default font) with no text. 549 * @return the label (with default font) with no text. 550 */ 551 public static JLabel createDefaultLabel() 552 { 553 return createDefaultLabel(LocalizableMessage.EMPTY); 554 } 555 556 /** 557 * Creates a label (with default font). 558 * @param text the message to be displayed by the label. 559 * @return the label (with default font). 560 */ 561 public static JLabel createDefaultLabel(LocalizableMessage text) 562 { 563 JLabel label = new JLabel(text.toString()); 564 label.setFont(ColorAndFontConstants.defaultFont); 565 label.setForeground(ColorAndFontConstants.foreground); 566 return label; 567 } 568 569 /** 570 * Returns a table created with the provided model and renderers. 571 * @param tableModel the table model. 572 * @param renderer the cell renderer. 573 * @return a table created with the provided model and renderers. 574 */ 575 public static JTable createSortableTable(final SortableTableModel tableModel, 576 TableCellRenderer renderer) 577 { 578 final JTable table = new JTable(tableModel); 579 table.setShowGrid(true); 580 table.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS); 581 table.setGridColor(ColorAndFontConstants.gridColor); 582 if (isMacOS()) 583 { 584 table.getTableHeader().setBorder( 585 BorderFactory.createMatteBorder(1, 1, 0, 0, 586 ColorAndFontConstants.gridColor)); 587 } 588 if (isWindows()) 589 { 590 table.getTableHeader().setBorder( 591 BorderFactory.createMatteBorder(1, 1, 0, 1, 592 ColorAndFontConstants.gridColor)); 593 } 594 table.getTableHeader().setDefaultRenderer( 595 new AccessibleTableHeaderRenderer( 596 table.getTableHeader().getDefaultRenderer())); 597 598 for (int i=0; i<tableModel.getColumnCount(); i++) 599 { 600 TableColumn col = table.getColumn(table.getColumnName(i)); 601 col.setCellRenderer(renderer); 602 } 603 MouseAdapter listMouseListener = new MouseAdapter() { 604 @Override 605 public void mouseClicked(MouseEvent e) { 606 TableColumnModel columnModel = table.getColumnModel(); 607 int viewColumn = columnModel.getColumnIndexAtX(e.getX()); 608 int sortedBy = table.convertColumnIndexToModel(viewColumn); 609 if (e.getClickCount() == 1 && sortedBy != -1) { 610 tableModel.setSortAscending(!tableModel.isSortAscending()); 611 tableModel.setSortColumn(sortedBy); 612 tableModel.forceResort(); 613 updateTableSizes(table); 614 } 615 } 616 }; 617 table.getTableHeader().addMouseListener(listMouseListener); 618 return table; 619 } 620 621 /** 622 * Creates a text area with borders similar to the ones of a text field. 623 * @param text the text of the text area. 624 * @param rows the rows of the text area. 625 * @param cols the columns of the text area. 626 * @return a text area with borders similar to the ones of a text field. 627 */ 628 public static JTextArea createTextAreaWithBorder(LocalizableMessage text, int rows, 629 int cols) 630 { 631 JTextArea ta = createTextArea(text, rows, cols); 632 if (ColorAndFontConstants.textAreaBorder != null) 633 { 634 setBorder(ta, ColorAndFontConstants.textAreaBorder); 635 } 636 return ta; 637 } 638 639 /** 640 * Creates a non-editable text area. 641 * @param text the text of the text area. 642 * @param rows the rows of the text area. 643 * @param cols the columns of the text area. 644 * @return a non-editable text area. 645 */ 646 public static JTextArea createNonEditableTextArea(LocalizableMessage text, int rows, 647 int cols) 648 { 649 JTextArea ta = createTextArea(text, rows, cols); 650 ta.setEditable(false); 651 ta.setOpaque(false); 652 ta.setForeground(ColorAndFontConstants.foreground); 653 return ta; 654 } 655 656 /** 657 * Creates a text area. 658 * @param text the text of the text area. 659 * @param rows the rows of the text area. 660 * @param cols the columns of the text area. 661 * @return a text area. 662 */ 663 public static JTextArea createTextArea(LocalizableMessage text, int rows, 664 int cols) 665 { 666 JTextArea ta = new JTextArea(text.toString(), rows, cols); 667 ta.setFont(ColorAndFontConstants.defaultFont); 668 return ta; 669 } 670 671 /** 672 * Creates a text field. 673 * @param text the text of the text field. 674 * @param cols the columns of the text field. 675 * @return the created text field. 676 */ 677 public static JTextField createTextField(String text, int cols) 678 { 679 JTextField tf = createTextField(); 680 tf.setText(text); 681 tf.setColumns(cols); 682 return tf; 683 } 684 685 /** 686 * Creates a short text field. 687 * @return the created text field. 688 */ 689 public static JTextField createShortTextField() 690 { 691 JTextField tf = createTextField(); 692 tf.setColumns(10); 693 return tf; 694 } 695 696 /** 697 * Creates a medium sized text field. 698 * @return the created text field. 699 */ 700 public static JTextField createMediumTextField() 701 { 702 JTextField tf = createTextField(); 703 tf.setColumns(20); 704 return tf; 705 } 706 707 /** 708 * Creates a long text field. 709 * @return the created text field. 710 */ 711 public static JTextField createLongTextField() 712 { 713 JTextField tf = createTextField(); 714 tf.setColumns(30); 715 return tf; 716 } 717 718 719 /** 720 * Creates a text field with the default size. 721 * @return the created text field. 722 */ 723 public static JTextField createTextField() 724 { 725 JTextField tf = new JTextField(); 726 tf.addFocusListener(new TextComponentFocusListener(tf)); 727 tf.setFont(ColorAndFontConstants.defaultFont); 728 return tf; 729 } 730 731 /** 732 * Creates a pasword text field. 733 * @return the created password text field. 734 */ 735 public static JPasswordField createPasswordField() 736 { 737 JPasswordField pf = new JPasswordField(); 738 pf.addFocusListener(new TextComponentFocusListener(pf)); 739 pf.setFont(ColorAndFontConstants.defaultFont); 740 return pf; 741 } 742 743 /** 744 * Creates a pasword text field. 745 * @param cols the columns of the password text field. 746 * @return the created password text field. 747 */ 748 public static JPasswordField createPasswordField(int cols) 749 { 750 JPasswordField pf = createPasswordField(); 751 pf.setColumns(cols); 752 return pf; 753 } 754 755 756 /** 757 * Sets the border in a given component. If the component already has a 758 * border, creates a compound border. 759 * @param comp the component. 760 * @param border the border to be set. 761 */ 762 public static void setBorder(JComponent comp, Border border) 763 { 764 if (comp.getBorder() != null) 765 { 766 comp.setBorder(BorderFactory.createCompoundBorder(comp.getBorder(), border)); 767 } 768 else 769 { 770 comp.setBorder(border); 771 } 772 } 773 774 /** 775 * Checks the size of the table and of the scroll bar where it is contained, 776 * and depending on it updates the auto resize mode. 777 * @param scroll the scroll pane containing the table. 778 * @param table the table. 779 */ 780 public static void updateScrollMode(JScrollPane scroll, JTable table) 781 { 782 int width1 = table.getPreferredScrollableViewportSize().width; 783 int width2 = scroll.getViewport().getWidth(); 784 785 if (width1 > width2) 786 { 787 table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 788 } 789 else 790 { 791 table.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS); 792 } 793 } 794 795 /** 796 * Updates the size of the table rows according to the size of the 797 * rendered component. 798 * @param table the table to handle. 799 */ 800 public static void updateTableSizes(JTable table) 801 { 802 updateTableSizes(table, -1); 803 } 804 805 /** 806 * Updates the size of the table rows according to the size of the 807 * rendered component. 808 * @param table the table to handle. 809 * @param rows the maximum rows to be displayed (-1 for unlimited) 810 */ 811 public static void updateTableSizes(JTable table, int rows) 812 { 813 int horizontalMargin = table.getIntercellSpacing().width; 814 int verticalMargin = table.getIntercellSpacing().height; 815 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 816 817 int headerMaxHeight = 5; 818 int headerMaxWidth = 0; 819 820 JTableHeader header = table.getTableHeader(); 821 if (header != null && header.isVisible()) 822 { 823 for (int col=0; col<table.getColumnCount(); col++) 824 { 825 TableColumn tcol = table.getColumnModel().getColumn(col); 826 TableCellRenderer renderer = tcol.getHeaderRenderer(); 827 if (renderer == null) 828 { 829 renderer = table.getTableHeader().getDefaultRenderer(); 830 } 831 Component comp = renderer.getTableCellRendererComponent(table, 832 table.getModel().getColumnName(col), false, false, 0, col); 833 int colHeight = comp.getPreferredSize().height + 2 * verticalMargin; 834 if (colHeight > screenSize.height) 835 { 836 // There are some issues on Mac OS and sometimes the preferred size 837 // is too big. 838 colHeight = 0; 839 } 840 headerMaxHeight = Math.max(headerMaxHeight, colHeight); 841 } 842 } 843 844 for (int col=0; col<table.getColumnCount(); col++) 845 { 846 int colMaxWidth = 8; 847 TableColumn tcol = table.getColumnModel().getColumn(col); 848 TableCellRenderer renderer = tcol.getHeaderRenderer(); 849 850 if (renderer == null && header != null) 851 { 852 renderer = header.getDefaultRenderer(); 853 } 854 855 if (renderer != null) 856 { 857 Component comp = renderer.getTableCellRendererComponent(table, 858 table.getModel().getColumnName(col), false, false, 0, col); 859 colMaxWidth = comp.getPreferredSize().width + 2 * horizontalMargin + 8; 860 } 861 862 if (colMaxWidth > screenSize.width) 863 { 864 colMaxWidth = 8; 865 } 866 867 for (int row=0; row<table.getRowCount(); row++) 868 { 869 renderer = table.getCellRenderer(row, col); 870 Component comp = table.prepareRenderer(renderer, row, col); 871 int colWidth = comp.getPreferredSize().width + 2 * horizontalMargin; 872 colMaxWidth = Math.max(colMaxWidth, colWidth); 873 } 874 tcol.setPreferredWidth(colMaxWidth); 875 headerMaxWidth += colMaxWidth; 876 } 877 878 879 if (header != null && header.isVisible()) 880 { 881 header.setPreferredSize(new Dimension(headerMaxWidth, headerMaxHeight)); 882 } 883 884 885 int maxRow = table.getRowHeight(); 886 for (int row=0; row<table.getRowCount(); row++) 887 { 888 for (int col=0; col<table.getColumnCount(); col++) 889 { 890 TableCellRenderer renderer = table.getCellRenderer(row, col); 891 Component comp = renderer.getTableCellRendererComponent(table, 892 table.getModel().getValueAt(row, col), false, false, row, col); 893 int colHeight = comp.getPreferredSize().height + 2 * verticalMargin; 894 if (colHeight > screenSize.height) 895 { 896 colHeight = 0; 897 } 898 maxRow = Math.max(maxRow, colHeight); 899 } 900 } 901 if (maxRow > table.getRowHeight()) 902 { 903 table.setRowHeight(maxRow); 904 } 905 Dimension d1; 906 if (rows == -1) 907 { 908 d1 = table.getPreferredSize(); 909 } 910 else 911 { 912 d1 = new Dimension(table.getPreferredSize().width, rows * maxRow); 913 } 914 table.setPreferredScrollableViewportSize(d1); 915 } 916 917 /** 918 * Returns a String that contains the html passed as parameter with a span 919 * applied. The span style corresponds to the Font specified as parameter. 920 * The goal of this method is to be able to specify a font for an HTML string. 921 * 922 * @param html the original html text. 923 * @param font the font to be used to generate the new HTML. 924 * @return a string that represents the original HTML with the font specified 925 * as parameter. 926 */ 927 public static String applyFont(CharSequence html, Font font) 928 { 929 return "<span style=\"" + getFontStyle(font) + "\">" + html + "</span>"; 930 } 931 932 933 /** 934 * Returns an ImageIcon or <CODE>null</CODE> if the path was invalid. 935 * @param path the path of the image. 936 * @param loader the class loader to use to load the image. If 937 * <CODE>null</CODE> this class class loader will be used. 938 * @return an ImageIcon or <CODE>null</CODE> if the path was invalid. 939 */ 940 public static ImageIcon createImageIcon(String path, ClassLoader loader) { 941 if (loader == null) 942 { 943 loader = ControlPanel.class.getClassLoader(); 944 } 945 java.net.URL imgURL = loader.getResource(path); 946 return imgURL != null ? new ImageIcon(imgURL) : null; 947 } 948 949 /** 950 * Returns an ImageIcon or <CODE>null</CODE> if the path was invalid. 951 * @param path the path of the image. 952 * @return an ImageIcon or <CODE>null</CODE> if the path was invalid. 953 */ 954 public static ImageIcon createImageIcon(String path) { 955 return createImageIcon(path, null); 956 } 957 958 /** 959 * Creates an image icon using an array of bytes that contain the image and 960 * specifying the maximum height of the image. 961 * @param bytes the byte array. 962 * @param maxHeight the maximum height of the image. 963 * @param description the description of the image. 964 * @param useFast whether a fast algorithm must be used to transform the image 965 * or an algorithm with a better result. 966 * @return an image icon using an array of bytes that contain the image and 967 * specifying the maximum height of the image. 968 */ 969 public static ImageIcon createImageIcon(byte[] bytes, int maxHeight, 970 LocalizableMessage description, boolean useFast) 971 { 972 ImageIcon icon = new ImageIcon(bytes, description.toString()); 973 if (maxHeight > icon.getIconHeight() || icon.getIconHeight() <= 0) 974 { 975 return icon; 976 } 977 int newHeight = maxHeight; 978 int newWidth = (newHeight * icon.getIconWidth()) / icon.getIconHeight(); 979 int algo = useFast ? Image.SCALE_FAST : Image.SCALE_SMOOTH; 980 Image scaledImage = icon.getImage().getScaledInstance(newWidth, newHeight, algo); 981 return new ImageIcon(scaledImage); 982 } 983 984 /** 985 * Updates the preferred size of an editor pane. 986 * @param pane the panel to be updated. 987 * @param nCols the number of columns that the panel must have. 988 * @param plainText the text to be displayed (plain text). 989 * @param font the font to be used. 990 * @param applyBackground whether an error/warning background must be applied 991 * to the text or not. 992 */ 993 public static void updatePreferredSize(JEditorPane pane, int nCols, 994 String plainText, Font font, boolean applyBackground) 995 { 996 String wrappedText = wrapText(plainText, nCols); 997 wrappedText = wrappedText.replaceAll(ServerConstants.EOL, "<br>"); 998 if (applyBackground) 999 { 1000 wrappedText = UIFactory.applyErrorBackgroundToHtml( 1001 Utilities.applyFont(wrappedText, font)); 1002 } 1003 JEditorPane pane2 = makeHtmlPane(wrappedText, font); 1004 pane.setPreferredSize(pane2.getPreferredSize()); 1005 JFrame frame = getFrame(pane); 1006 if (frame != null && frame.isVisible()) 1007 { 1008 frame.getRootPane().revalidate(); 1009 frame.getRootPane().repaint(); 1010 } 1011 } 1012 1013 /** 1014 * Strips any potential HTML markup from a given string. 1015 * @param s string to strip 1016 * @return resulting string 1017 */ 1018 public static String stripHtmlToSingleLine(String s) { 1019 String o = null; 1020 if (s != null) { 1021 s = s.replaceAll("<br>", " "); 1022 // This is not a comprehensive solution but addresses 1023 // the few tags that we have in Resources.properties 1024 // at the moment. Note that the following might strip 1025 // out more than is intended for non-tags like 1026 // '<your name here>' or for funky tags like 1027 // '<tag attr="1 > 0">'. See test class for cases that 1028 // might cause problems. 1029 o = s.replaceAll("\\<.*?\\>",""); 1030 } 1031 return o; 1032 } 1033 1034 /** 1035 * Wraps the contents of the provided message using the specified number of 1036 * columns. 1037 * @param msg the message to be wrapped. 1038 * @param nCols the number of columns. 1039 * @return the wrapped message. 1040 */ 1041 public static LocalizableMessage wrapHTML(LocalizableMessage msg, int nCols) 1042 { 1043 String s = msg.toString(); 1044 StringBuilder sb = new StringBuilder(); 1045 StringBuilder lastLine = new StringBuilder(); 1046 int lastOpenTag = -1; 1047 boolean inTag = false; 1048 int lastSpace = -1; 1049 int lastLineLengthInLastSpace = 0; 1050 int lastLineLength = 0; 1051 for (int i=0; i<s.length() ; i++) 1052 { 1053 boolean isNormalChar = false; 1054 char c = s.charAt(i); 1055 if (c == '<') 1056 { 1057 inTag = true; 1058 lastOpenTag = i; 1059 lastLine.append(c); 1060 } 1061 else if (c == '>') 1062 { 1063 if (lastOpenTag != -1) 1064 { 1065 inTag = false; 1066 String tag = s.substring(lastOpenTag, i+1); 1067 lastOpenTag = -1; 1068 lastLine.append(c); 1069 if (isLineBreakTag(tag)) 1070 { 1071 sb.append(lastLine); 1072 lastLine.delete(0, lastLine.length()); 1073 lastLineLength = 0; 1074 lastSpace = -1; 1075 lastLineLengthInLastSpace = 0; 1076 } 1077 } 1078 else 1079 { 1080 isNormalChar = true; 1081 } 1082 } 1083 else if (inTag) 1084 { 1085 lastLine.append(c); 1086 } 1087 else if (c == HTML_SPACE.charAt(0)) 1088 { 1089 if (s.length() >= i + HTML_SPACE.length()) 1090 { 1091 if (HTML_SPACE.equalsIgnoreCase(s.substring(i, i 1092 + HTML_SPACE.length()))) 1093 { 1094 if (lastLineLength < nCols) 1095 { 1096 // Only count as 1 space 1097 lastLine.append(HTML_SPACE); 1098 lastSpace = lastLine.length() - HTML_SPACE.length(); 1099 lastLineLength ++; 1100 lastLineLengthInLastSpace = lastLineLength; 1101 i += HTML_SPACE.length() - 1; 1102 } 1103 else 1104 { 1105 // Insert a line break 1106 sb.append(lastLine); 1107 sb.append("<br>"); 1108 lastLine.delete(0, lastLine.length()); 1109 lastLineLength = 0; 1110 lastSpace = -1; 1111 lastLineLengthInLastSpace = 0; 1112 i += HTML_SPACE.length() - 1; 1113 } 1114 } 1115 else 1116 { 1117 isNormalChar = true; 1118 } 1119 } 1120 else 1121 { 1122 isNormalChar = true; 1123 } 1124 } 1125 else if (c == ' ') 1126 { 1127 if (lastLineLength < nCols) 1128 { 1129 // Only count as 1 space 1130 lastLine.append(c); 1131 lastSpace = lastLine.length() - 1; 1132 lastLineLength ++; 1133 lastLineLengthInLastSpace = lastLineLength; 1134 } 1135 else 1136 { 1137 // Insert a line break 1138 sb.append(lastLine); 1139 sb.append("<br>"); 1140 lastLine.delete(0, lastLine.length()); 1141 lastLineLength = 0; 1142 lastSpace = -1; 1143 lastLineLengthInLastSpace = 0; 1144 } 1145 } 1146 else 1147 { 1148 isNormalChar = true; 1149 } 1150 1151 if (isNormalChar) 1152 { 1153 if (lastLineLength < nCols) 1154 { 1155 lastLine.append(c); 1156 lastLineLength ++; 1157 } 1158 else 1159 { 1160 // Check where to insert a line break 1161 if (lastSpace != -1) 1162 { 1163 sb.append(lastLine, 0, lastSpace); 1164 sb.append("<br>"); 1165 lastLine.delete(0, lastSpace + 1); 1166 lastLine.append(c); 1167 lastLineLength = lastLineLength - lastLineLengthInLastSpace + 1; 1168 lastLineLengthInLastSpace = 0; 1169 lastSpace = -1; 1170 } 1171 else 1172 { 1173 // Force the line break. 1174 sb.append(lastLine); 1175 sb.append("<br>"); 1176 lastLine.delete(0, lastLine.length()); 1177 lastLine.append(c); 1178 lastLineLength = 1; 1179 } 1180 } 1181 } 1182 } 1183 if (lastLine.length() > 0) 1184 { 1185 sb.append(lastLine); 1186 } 1187 return LocalizableMessage.raw(sb.toString()); 1188 } 1189 1190 private static boolean isLineBreakTag(String tag) 1191 { 1192 return "<br>".equalsIgnoreCase(tag) || 1193 "</br>".equalsIgnoreCase(tag) || 1194 "</div>".equalsIgnoreCase(tag) || 1195 "<p>".equalsIgnoreCase(tag) || 1196 "</p>".equalsIgnoreCase(tag); 1197 } 1198 1199 /** 1200 * Center the component location based on its preferred size. The code 1201 * considers the particular case of 2 screens and puts the component on the 1202 * center of the left screen 1203 * 1204 * @param comp the component to be centered. 1205 */ 1206 public static void centerOnScreen(Component comp) 1207 { 1208 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 1209 1210 int width = comp.getPreferredSize().width; 1211 int height = comp.getPreferredSize().height; 1212 1213 boolean multipleScreen = screenSize.width / screenSize.height >= 2; 1214 1215 if (multipleScreen) 1216 { 1217 comp.setLocation(screenSize.width / 4 - width / 2, 1218 (screenSize.height - height) / 2); 1219 } else 1220 { 1221 comp.setLocation((screenSize.width - width) / 2, 1222 (screenSize.height - height) / 2); 1223 } 1224 } 1225 1226 /** 1227 * Center the component location of the ref component. 1228 * 1229 * @param comp the component to be centered. 1230 * @param ref the component to be used as reference. 1231 */ 1232 public static void centerGoldenMean(Window comp, Component ref) 1233 { 1234 comp.setLocationRelativeTo(ref); 1235 // Apply the golden mean 1236 if (ref != null && ref.isVisible()) 1237 { 1238 int refY = ref.getY(); 1239 int refHeight = ref.getHeight(); 1240 int compHeight = comp.getPreferredSize().height; 1241 1242 int newY = refY + (int) (refHeight * 0.3819 - compHeight * 0.5); 1243 // Check that the new window will be fully visible 1244 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 1245 if (newY > 0 && screenSize.height > newY + compHeight) 1246 { 1247 comp.setLocation(comp.getX(), newY); 1248 } 1249 } 1250 } 1251 1252 /** 1253 * Returns the parent frame of a component. <CODE>null</CODE> if this 1254 * component is not contained in any frame. 1255 * @param comp the component. 1256 * @return the parent frame of a component. <CODE>null</CODE> if this 1257 * component is not contained in any frame. 1258 */ 1259 public static JFrame getFrame(Component comp) 1260 { 1261 Component parent = comp; 1262 while (parent != null && !(parent instanceof JFrame)) 1263 { 1264 parent = parent.getParent(); 1265 } 1266 return parent != null ? (JFrame) parent : null; 1267 } 1268 1269 /** 1270 * Returns the parent dialog of a component. <CODE>null</CODE> if this 1271 * component is not contained in any dialog. 1272 * @param comp the component. 1273 * @return the parent dialog of a component. <CODE>null</CODE> if this 1274 * component is not contained in any dialog. 1275 */ 1276 public static Window getParentDialog(Component comp) 1277 { 1278 Component parent = comp; 1279 while (parent != null) 1280 { 1281 if (parent instanceof JDialog || parent instanceof JFrame) 1282 { 1283 return (Window)parent; 1284 } 1285 parent = parent.getParent(); 1286 } 1287 return null; 1288 } 1289 1290 /** 1291 * Unescapes UTF-8 text and generates a String from it. 1292 * @param v the string in UTF-8 format. 1293 * @return the string with unescaped characters. 1294 */ 1295 public static String unescapeUtf8(String v) 1296 { 1297 try 1298 { 1299 byte[] stringBytes = v.getBytes("UTF-8"); 1300 byte[] decodedBytes = new byte[stringBytes.length]; 1301 int pos = 0; 1302 for (int i = 0; i < stringBytes.length; i++) 1303 { 1304 if (stringBytes[i] == '\\' 1305 && i + 2 < stringBytes.length 1306 && StaticUtils.isHexDigit(stringBytes[i+1]) 1307 && StaticUtils.isHexDigit(stringBytes[i+2])) 1308 { 1309 // Convert hex-encoded UTF-8 to 16-bit chars. 1310 byte b; 1311 1312 byte escapedByte1 = stringBytes[++i]; 1313 switch (escapedByte1) 1314 { 1315 case '0': 1316 b = (byte) 0x00; 1317 break; 1318 case '1': 1319 b = (byte) 0x10; 1320 break; 1321 case '2': 1322 b = (byte) 0x20; 1323 break; 1324 case '3': 1325 b = (byte) 0x30; 1326 break; 1327 case '4': 1328 b = (byte) 0x40; 1329 break; 1330 case '5': 1331 b = (byte) 0x50; 1332 break; 1333 case '6': 1334 b = (byte) 0x60; 1335 break; 1336 case '7': 1337 b = (byte) 0x70; 1338 break; 1339 case '8': 1340 b = (byte) 0x80; 1341 break; 1342 case '9': 1343 b = (byte) 0x90; 1344 break; 1345 case 'a': 1346 case 'A': 1347 b = (byte) 0xA0; 1348 break; 1349 case 'b': 1350 case 'B': 1351 b = (byte) 0xB0; 1352 break; 1353 case 'c': 1354 case 'C': 1355 b = (byte) 0xC0; 1356 break; 1357 case 'd': 1358 case 'D': 1359 b = (byte) 0xD0; 1360 break; 1361 case 'e': 1362 case 'E': 1363 b = (byte) 0xE0; 1364 break; 1365 case 'f': 1366 case 'F': 1367 b = (byte) 0xF0; 1368 break; 1369 default: 1370 throw new RuntimeException("Unexpected byte: "+escapedByte1); 1371 } 1372 1373 byte escapedByte2 = stringBytes[++i]; 1374 switch (escapedByte2) 1375 { 1376 case '0': 1377 break; 1378 case '1': 1379 b |= 0x01; 1380 break; 1381 case '2': 1382 b |= 0x02; 1383 break; 1384 case '3': 1385 b |= 0x03; 1386 break; 1387 case '4': 1388 b |= 0x04; 1389 break; 1390 case '5': 1391 b |= 0x05; 1392 break; 1393 case '6': 1394 b |= 0x06; 1395 break; 1396 case '7': 1397 b |= 0x07; 1398 break; 1399 case '8': 1400 b |= 0x08; 1401 break; 1402 case '9': 1403 b |= 0x09; 1404 break; 1405 case 'a': 1406 case 'A': 1407 b |= 0x0A; 1408 break; 1409 case 'b': 1410 case 'B': 1411 b |= 0x0B; 1412 break; 1413 case 'c': 1414 case 'C': 1415 b |= 0x0C; 1416 break; 1417 case 'd': 1418 case 'D': 1419 b |= 0x0D; 1420 break; 1421 case 'e': 1422 case 'E': 1423 b |= 0x0E; 1424 break; 1425 case 'f': 1426 case 'F': 1427 b |= 0x0F; 1428 break; 1429 default: 1430 throw new RuntimeException("Unexpected byte: "+escapedByte2); 1431 } 1432 1433 decodedBytes[pos++] = b; 1434 } 1435 else { 1436 decodedBytes[pos++] = stringBytes[i]; 1437 } 1438 } 1439 return new String(decodedBytes, 0, pos, "UTF-8"); 1440 } 1441 catch (UnsupportedEncodingException uee) 1442 { 1443// This is a bug, UTF-8 should be supported always by the JVM 1444 throw new RuntimeException("UTF-8 encoding not supported", uee); 1445 } 1446 } 1447 1448 /** 1449 * Returns whether the provided strings represent the same DN. 1450 * 1451 * @param dn1 the first dn to compare. 1452 * @param dn2 the second dn to compare. 1453 * @return {@code true} if the provided strings represent the same DN, {@code false} otherwise. 1454 */ 1455 public static boolean areDnsEqual(String dn1, String dn2) 1456 { 1457 try 1458 { 1459 LdapName name1 = new LdapName(dn1); 1460 LdapName name2 = new LdapName(dn2); 1461 return name1.equals(name2); 1462 } catch (Exception ex) 1463 { 1464 return false; 1465 } 1466 } 1467 1468 /** 1469 * Strings any potential "separator" from a given string. 1470 * @param s string to strip 1471 * @param separator the separator string to remove 1472 * @return resulting string 1473 */ 1474 private static String stripStringToSingleLine(String s, String separator) 1475 { 1476 return (s == null) ? null : s.replaceAll(separator, ""); 1477 } 1478 1479 /** The pattern for control characters. */ 1480 private final static Pattern cntrl_pattern = Pattern.compile("\\p{Cntrl}", Pattern.MULTILINE); 1481 1482 /** 1483 * Checks if a string contains control characters. 1484 * @param s : the string to check 1485 * @return true if s contains control characters, false otherwise 1486 */ 1487 public static boolean hasControlCharaters(String s) 1488 { 1489 return cntrl_pattern.matcher(s).find(); 1490 } 1491 1492 /** 1493 * This is a helper method that gets a String representation of the elements 1494 * in the Collection. The String will display the different elements separated 1495 * by the separator String. 1496 * 1497 * @param col 1498 * the collection containing the String. 1499 * @param separator 1500 * the separator String to be used. 1501 * @return the String representation for the collection. 1502 */ 1503 public static String getStringFromCollection(Collection<String> col, String separator) 1504 { 1505 StringBuilder msg = new StringBuilder(); 1506 for (String m : col) 1507 { 1508 if (msg.length() > 0) 1509 { 1510 msg.append(separator); 1511 } 1512 msg.append(stripStringToSingleLine(m, separator)); 1513 } 1514 return msg.toString(); 1515 } 1516 1517 /** 1518 * Commodity method to get the Name object representing a dn. 1519 * It is preferable to use Name objects when doing JNDI operations to avoid 1520 * problems with the '/' character. 1521 * @param dn the DN as a String. 1522 * @return a Name object representing the DN. 1523 * @throws InvalidNameException if the provided DN value is not valid. 1524 */ 1525 public static Name getJNDIName(String dn) throws InvalidNameException 1526 { 1527 Name name = new CompositeName(); 1528 if (dn != null && dn.length() > 0) { 1529 name.add(dn); 1530 } 1531 return name; 1532 } 1533 1534 /** 1535 * Returns the HTML representation of the 'Done' string. 1536 * @param progressFont the font to be used. 1537 * @return the HTML representation of the 'Done' string. 1538 */ 1539 public static String getProgressDone(Font progressFont) 1540 { 1541 return applyFont(INFO_CTRL_PANEL_PROGRESS_DONE.get(), 1542 progressFont.deriveFont(Font.BOLD)); 1543 } 1544 1545 /** 1546 * Returns the HTML representation of a message to which some points have 1547 * been appended. 1548 * @param plainText the plain text. 1549 * @param progressFont the font to be used. 1550 * @return the HTML representation of a message to which some points have 1551 * been appended. 1552 */ 1553 public static String getProgressWithPoints(LocalizableMessage plainText, 1554 Font progressFont) 1555 { 1556 return applyFont(plainText.toString(), progressFont)+ 1557 applyFont(" ..... ", 1558 progressFont.deriveFont(Font.BOLD)); 1559 } 1560 1561 /** 1562 * Returns the HTML representation of an error for a given text. 1563 * @param title the title. 1564 * @param titleFont the font for the title. 1565 * @param details the details. 1566 * @param detailsFont the font to be used for the details. 1567 * @return the HTML representation of an error for the given text. 1568 */ 1569 public static String getFormattedError(LocalizableMessage title, Font titleFont, 1570 LocalizableMessage details, Font detailsFont) 1571 { 1572 StringBuilder buf = new StringBuilder(); 1573 buf.append(UIFactory.getIconHtml(UIFactory.IconType.ERROR_LARGE)) 1574 .append(HTML_SPACE).append(HTML_SPACE) 1575 .append(applyFont(title.toString(), titleFont)); 1576 if (details != null) 1577 { 1578 buf.append("<br><br>") 1579 .append(applyFont(details.toString(), detailsFont)); 1580 } 1581 return "<form>"+UIFactory.applyErrorBackgroundToHtml(buf.toString())+ 1582 "</form>"; 1583 } 1584 1585 /** 1586 * Returns the HTML representation of a success for a given text. 1587 * @param title the title. 1588 * @param titleFont the font for the title. 1589 * @param details the details. 1590 * @param detailsFont the font to be used for the details. 1591 * @return the HTML representation of a success for the given text. 1592 */ 1593 public static String getFormattedSuccess(LocalizableMessage title, Font titleFont, 1594 LocalizableMessage details, Font detailsFont) 1595 { 1596 StringBuilder buf = new StringBuilder(); 1597 buf.append(UIFactory.getIconHtml(UIFactory.IconType.INFORMATION_LARGE)) 1598 .append(HTML_SPACE).append(HTML_SPACE) 1599 .append(applyFont(title.toString(), titleFont)); 1600 if (details != null) 1601 { 1602 buf.append("<br><br>") 1603 .append(applyFont(details.toString(), detailsFont)); 1604 } 1605 return "<form>"+UIFactory.applyErrorBackgroundToHtml(buf.toString())+ 1606 "</form>"; 1607 } 1608 1609 /** 1610 * Returns the HTML representation of a confirmation for a given text. 1611 * @param title the title. 1612 * @param titleFont the font for the title. 1613 * @param details the details. 1614 * @param detailsFont the font to be used for the details. 1615 * @return the HTML representation of a confirmation for the given text. 1616 */ 1617 public static String getFormattedConfirmation(LocalizableMessage title, Font titleFont, 1618 LocalizableMessage details, Font detailsFont) 1619 { 1620 StringBuilder buf = new StringBuilder(); 1621 buf.append(UIFactory.getIconHtml(UIFactory.IconType.WARNING_LARGE)) 1622 .append(HTML_SPACE).append(HTML_SPACE) 1623 .append(applyFont(title.toString(), titleFont)); 1624 if (details != null) 1625 { 1626 buf.append("<br><br>") 1627 .append(applyFont(details.toString(), detailsFont)); 1628 } 1629 return "<form>" + buf + "</form>"; 1630 } 1631 1632 1633 /** 1634 * Returns the HTML representation of a warning for a given text. 1635 * @param title the title. 1636 * @param titleFont the font for the title. 1637 * @param details the details. 1638 * @param detailsFont the font to be used for the details. 1639 * @return the HTML representation of a success for the given text. 1640 */ 1641 public static String getFormattedWarning(LocalizableMessage title, Font titleFont, 1642 LocalizableMessage details, Font detailsFont) 1643 { 1644 StringBuilder buf = new StringBuilder(); 1645 buf.append(UIFactory.getIconHtml(UIFactory.IconType.WARNING_LARGE)) 1646 .append(HTML_SPACE).append(HTML_SPACE) 1647 .append(applyFont(title.toString(), titleFont)); 1648 if (details != null) 1649 { 1650 buf.append("<br><br>") 1651 .append(applyFont(details.toString(), detailsFont)); 1652 } 1653 return "<form>"+UIFactory.applyErrorBackgroundToHtml(buf.toString())+ 1654 "</form>"; 1655 } 1656 1657 /** 1658 * Sets the not available text to a label and associates a help icon and 1659 * a tooltip explaining that the data is not available because the server is 1660 * down. 1661 * @param l the label. 1662 */ 1663 public static void setNotAvailableBecauseServerIsDown(LabelWithHelpIcon l) 1664 { 1665 l.setText(INFO_CTRL_PANEL_NOT_AVAILABLE_LONG_LABEL.get().toString()); 1666 l.setHelpIconVisible(true); 1667 l.setHelpTooltip(INFO_NOT_AVAILABLE_SERVER_DOWN_TOOLTIP.get().toString()); 1668 } 1669 1670 /** 1671 * Sets the not available text to a label and associates a help icon and 1672 * a tooltip explaining that the data is not available because authentication 1673 * is required. 1674 * @param l the label. 1675 */ 1676 public static void setNotAvailableBecauseAuthenticationIsRequired( 1677 LabelWithHelpIcon l) 1678 { 1679 l.setText(INFO_CTRL_PANEL_NOT_AVAILABLE_LONG_LABEL.get().toString()); 1680 l.setHelpIconVisible(true); 1681 l.setHelpTooltip(INFO_NOT_AVAILABLE_AUTHENTICATION_REQUIRED_TOOLTIP.get().toString()); 1682 } 1683 1684 /** 1685 * Sets the not available text to a label and associates a help icon and 1686 * a tooltip explaining that the data is not available because the server is 1687 * down. 1688 * @param l the label. 1689 */ 1690 public static void setNotAvailableBecauseServerIsDown( 1691 SelectableLabelWithHelpIcon l) 1692 { 1693 l.setText(INFO_CTRL_PANEL_NOT_AVAILABLE_LONG_LABEL.get().toString()); 1694 l.setHelpIconVisible(true); 1695 l.setHelpTooltip(INFO_NOT_AVAILABLE_SERVER_DOWN_TOOLTIP.get().toString()); 1696 } 1697 1698 /** 1699 * Sets the not available text to a label and associates a help icon and 1700 * a tooltip explaining that the data is not available because authentication 1701 * is required. 1702 * @param l the label. 1703 */ 1704 public static void setNotAvailableBecauseAuthenticationIsRequired( 1705 SelectableLabelWithHelpIcon l) 1706 { 1707 l.setText(INFO_CTRL_PANEL_NOT_AVAILABLE_LONG_LABEL.get().toString()); 1708 l.setHelpIconVisible(true); 1709 l.setHelpTooltip(INFO_NOT_AVAILABLE_AUTHENTICATION_REQUIRED_TOOLTIP.get().toString()); 1710 } 1711 1712 /** 1713 * Updates a label by setting a warning icon and a text. 1714 * @param l the label to be updated. 1715 * @param text the text to be set on the label. 1716 */ 1717 public static void setWarningLabel(JLabel l, LocalizableMessage text) 1718 { 1719 l.setText(text.toString()); 1720 if (warningIcon == null) 1721 { 1722 warningIcon = 1723 createImageIcon("org/opends/quicksetup/images/warning_medium.gif"); 1724 warningIcon.setDescription( 1725 INFO_WARNING_ICON_ACCESSIBLE_DESCRIPTION.get().toString()); 1726 warningIcon.getAccessibleContext().setAccessibleName( 1727 INFO_WARNING_ICON_ACCESSIBLE_DESCRIPTION.get().toString()); 1728 } 1729 l.setIcon(warningIcon); 1730 l.setToolTipText(text.toString()); 1731 l.setHorizontalTextPosition(SwingConstants.RIGHT); 1732 } 1733 1734 /** 1735 * Sets the not available text to a label with no icon nor tooltip. 1736 * @param l the label. 1737 */ 1738 public static void setNotAvailable(LabelWithHelpIcon l) 1739 { 1740 l.setText(INFO_CTRL_PANEL_NOT_AVAILABLE_LONG_LABEL.get().toString()); 1741 l.setHelpIconVisible(false); 1742 l.setHelpTooltip(null); 1743 } 1744 1745 /** 1746 * Sets the a text to a label with no icon nor tooltip. 1747 * @param l the label. 1748 * @param text the text. 1749 */ 1750 public static void setTextValue(LabelWithHelpIcon l, String text) 1751 { 1752 l.setText(text); 1753 l.setHelpIconVisible(false); 1754 l.setHelpTooltip(null); 1755 } 1756 1757 /** 1758 * Sets the not available text to a label with no icon nor tooltip. 1759 * @param l the label. 1760 */ 1761 public static void setNotAvailable(SelectableLabelWithHelpIcon l) 1762 { 1763 l.setText(INFO_CTRL_PANEL_NOT_AVAILABLE_LONG_LABEL.get().toString()); 1764 l.setHelpIconVisible(false); 1765 l.setHelpTooltip(null); 1766 } 1767 1768 /** 1769 * Sets the a text to a label with no icon nor tooltip. 1770 * @param l the label. 1771 * @param text the text. 1772 */ 1773 public static void setTextValue(SelectableLabelWithHelpIcon l, String text) 1774 { 1775 l.setText(text); 1776 l.setHelpIconVisible(false); 1777 l.setHelpTooltip(null); 1778 } 1779 1780 /** 1781 * Returns the server root directory (the path where the server is installed). 1782 * <p> 1783 * Note: this method is called by SNMP code. 1784 * 1785 * @return the server root directory (the path where the server is installed). 1786 */ 1787 public static File getServerRootDirectory() 1788 { 1789 if (rootDirectory == null) 1790 { 1791 // This allows testing of configuration components when the OpenDJ.jar 1792 // in the classpath does not necessarily point to the server's 1793 String installRoot = System.getProperty("org.opends.quicksetup.Root"); 1794 1795 if (installRoot == null) { 1796 installRoot = getInstallPathFromClasspath(); 1797 } 1798 rootDirectory = new File(installRoot); 1799 } 1800 return rootDirectory; 1801 } 1802 1803 /** 1804 * Returns the instance root directory (the path where the instance is 1805 * installed). 1806 * @param installPath The installRoot path. 1807 * @return the instance root directory (the path where the instance is 1808 * installed). 1809 */ 1810 public static File getInstanceRootDirectory(String installPath) 1811 { 1812 if (instanceRootDirectory == null) 1813 { 1814 instanceRootDirectory = new File( 1815 Utils.getInstancePathFromInstallPath(installPath)); 1816 } 1817 return instanceRootDirectory; 1818 } 1819 1820 /** 1821 * Returns the path of the installation of the directory server. Note that 1822 * this method assumes that this code is being run locally. 1823 * @return the path of the installation of the directory server. 1824 */ 1825 public static String getInstallPathFromClasspath() 1826 { 1827 String installPath = null; 1828 1829 /* Get the install path from the Class Path */ 1830 String sep = System.getProperty("path.separator"); 1831 String[] classPaths = System.getProperty("java.class.path").split(sep); 1832 String path = getInstallPath(classPaths); 1833 if (path != null) { 1834 File f = new File(path).getAbsoluteFile(); 1835 File librariesDir = f.getParentFile(); 1836 1837 /* 1838 * Do a best effort to avoid having a relative representation (for 1839 * instance to avoid having ../../../). 1840 */ 1841 try 1842 { 1843 installPath = librariesDir.getParentFile().getCanonicalPath(); 1844 } 1845 catch (IOException ioe) 1846 { 1847 // Best effort 1848 installPath = librariesDir.getParent(); 1849 } 1850 } 1851 return installPath; 1852 } 1853 1854 private static String getInstallPath(String[] classPaths) 1855 { 1856 for (String classPath : classPaths) 1857 { 1858 final String normPath = classPath.replace(File.separatorChar, '/'); 1859 if (normPath.endsWith(OPENDJ_BOOTSTRAP_CLIENT_JAR_RELATIVE_PATH) 1860 || normPath.endsWith(OPENDJ_BOOTSTRAP_JAR_RELATIVE_PATH)) 1861 { 1862 return classPath; 1863 } 1864 } 1865 return null; 1866 } 1867 1868 /** 1869 * Returns whether the server located in the provided path is running. 1870 * 1871 * @param serverRootDirectory the path where the server is installed. 1872 * @return {@code true} if the server located in the provided path is running, 1873 * {@code false} otherwise. 1874 */ 1875 public static boolean isServerRunning(File serverRootDirectory) 1876 { 1877 String lockFileName = ServerConstants.SERVER_LOCK_FILE_NAME + ServerConstants.LOCK_FILE_SUFFIX; 1878 String lockPathRelative = Installation.LOCKS_PATH_RELATIVE; 1879 File locksPath = new File(serverRootDirectory, lockPathRelative); 1880 String lockFile = new File(locksPath, lockFileName).getAbsolutePath(); 1881 StringBuilder failureReason = new StringBuilder(); 1882 try { 1883 if (LockFileManager.acquireExclusiveLock(lockFile, failureReason)) 1884 { 1885 LockFileManager.releaseLock(lockFile, failureReason); 1886 return false; 1887 } 1888 return true; 1889 } 1890 catch (Throwable t) { 1891 // Assume that if we cannot acquire the lock file the 1892 // server is running. 1893 return true; 1894 } 1895 } 1896 1897 private static final String VALID_SCHEMA_SYNTAX = 1898 "abcdefghijklmnopqrstuvwxyz0123456789-"; 1899 1900 /** 1901 * Returns whether the provided string can be used as objectclass name. 1902 * 1903 * @param s the string to be analyzed. 1904 * @return {@code true} if the provided string can be used as objectclass name, 1905 * {@code false} otherwise. 1906 */ 1907 private static boolean isValidObjectclassName(String s) 1908 { 1909 if (s == null || s.length() == 0) 1910 { 1911 return false; 1912 } 1913 1914 final StringCharacterIterator iter = new StringCharacterIterator(s, 0); 1915 char c = iter.first(); 1916 while (c != CharacterIterator.DONE) 1917 { 1918 if (VALID_SCHEMA_SYNTAX.indexOf(Character.toLowerCase(c)) == -1) 1919 { 1920 return false; 1921 } 1922 c = iter.next(); 1923 } 1924 return true; 1925 } 1926 1927 /** 1928 * Returns whether the provided string can be used as attribute name. 1929 * 1930 * @param s the string to be analyzed. 1931 * @return {@code true} if the provided string can be used as attribute name, 1932 * {@code false} otherwise. 1933 */ 1934 public static boolean isValidAttributeName(String s) 1935 { 1936 return isValidObjectclassName(s); 1937 } 1938 1939 /** 1940 * Returns the representation of the VLV index as it must be used in the 1941 * command-line. 1942 * @param index the VLV index. 1943 * @return the representation of the VLV index as it must be used in the 1944 * command-line. 1945 */ 1946 public static String getVLVNameInCommandLine(VLVIndexDescriptor index) 1947 { 1948 return "vlv."+index.getName(); 1949 } 1950 1951 /** 1952 * Returns a string representing the VLV index in a cell. 1953 * @param index the VLV index to be represented. 1954 * @return the string representing the VLV index in a cell. 1955 */ 1956 public static String getVLVNameInCellRenderer(VLVIndexDescriptor index) 1957 { 1958 return INFO_CTRL_PANEL_VLV_INDEX_CELL.get(index.getName()).toString(); 1959 } 1960 1961 private static final List<String> standardSchemaFileNames = Arrays.asList( 1962 "00-core.ldif", "01-pwpolicy.ldif", "03-changelog.ldif", 1963 "03-uddiv3.ldif", "05-solaris.ldif" 1964 ); 1965 1966 private static final List<String> configurationSchemaOrigins = Arrays.asList( 1967 "OpenDJ Directory Server", "OpenDS Directory Server", 1968 "Sun Directory Server", "Microsoft Active Directory" 1969 ); 1970 1971 private static final List<String> standardSchemaOrigins = Arrays.asList( 1972 "Sun Java System Directory Server", "Solaris Specific", "X.501" 1973 ); 1974 1975 private static final List<String> configurationSchemaFileNames = Arrays.asList( 1976 "02-config.ldif", "06-compat.ldif" 1977 ); 1978 1979 /** 1980 * Returns whether the provided schema element is part of the standard. 1981 * 1982 * @param fileElement the schema element. 1983 * @return {@code true} if the provided schema element is part of the standard, 1984 * {@code false} otherwise. 1985 */ 1986 public static boolean isStandard(SomeSchemaElement fileElement) 1987 { 1988 final String fileName = fileElement.getSchemaFile(); 1989 if (fileName != null) 1990 { 1991 return standardSchemaFileNames.contains(fileName) || fileName.toLowerCase().contains("-rfc"); 1992 } 1993 String xOrigin = fileElement.getOrigin(); 1994 if (xOrigin != null) 1995 { 1996 return standardSchemaOrigins.contains(xOrigin) || xOrigin.startsWith("RFC ") || xOrigin.startsWith("draft-"); 1997 } 1998 return false; 1999 } 2000 2001 /** 2002 * Returns whether the provided schema element is part of the configuration. 2003 * 2004 * @param fileElement the schema element. 2005 * @return {@code true} if the provided schema element is part of the configuration, 2006 * {@code false} otherwise. 2007 */ 2008 public static boolean isConfiguration(SomeSchemaElement fileElement) 2009 { 2010 String fileName = fileElement.getSchemaFile(); 2011 if (fileName != null) 2012 { 2013 return configurationSchemaFileNames.contains(fileName); 2014 } 2015 String xOrigin = fileElement.getOrigin(); 2016 if (xOrigin != null) 2017 { 2018 return configurationSchemaOrigins.contains(xOrigin); 2019 } 2020 return false; 2021 } 2022 2023 /** 2024 * Returns the string representation of an attribute syntax. 2025 * @param syntax the attribute syntax. 2026 * @return the string representation of an attribute syntax. 2027 */ 2028 public static String getSyntaxText(Syntax syntax) 2029 { 2030 String syntaxName = syntax.getName(); 2031 String syntaxOID = syntax.getOID(); 2032 if (syntaxName != null) 2033 { 2034 return syntaxName + " - " + syntaxOID; 2035 } 2036 return syntaxOID; 2037 } 2038 2039 /** 2040 * Returns whether the provided attribute has image syntax. 2041 * 2042 * @param attrName the name of the attribute. 2043 * @param schema the schema. 2044 * @return {@code true} if the provided attribute has image syntax, {@code false} otherwise. 2045 */ 2046 public static boolean hasImageSyntax(String attrName, Schema schema) 2047 { 2048 if ("photo".equals(AttributeDescription.valueOf(attrName).getNameOrOID())) 2049 { 2050 return true; 2051 } 2052 // Check all the attributes that we consider binaries. 2053 if (schema != null) 2054 { 2055 AttributeType attrType = AttributeDescription.valueOf(attrName, schema.getSchemaNG()).getAttributeType(); 2056 if (!attrType.isPlaceHolder()) 2057 { 2058 String syntaxOID = attrType.getSyntax().getOID(); 2059 return SchemaConstants.SYNTAX_JPEG_OID.equals(syntaxOID); 2060 } 2061 } 2062 return false; 2063 } 2064 2065 /** 2066 * Returns whether the provided attribute has binary syntax. 2067 * 2068 * @param attrName the name of the attribute. 2069 * @param schema the schema. 2070 * @return {@code true} if the provided attribute has binary syntax, {@code false} otherwise. 2071 */ 2072 public static boolean hasBinarySyntax(String attrName, Schema schema) 2073 { 2074 return attrName.toLowerCase().contains(";binary") 2075 || hasAnySyntax(attrName, schema, binarySyntaxOIDs); 2076 } 2077 2078 /** 2079 * Returns whether the provided attribute has password syntax. 2080 * 2081 * @param attrName the name of the attribute. 2082 * @param schema the schema. 2083 * @return {@code true} if the provided attribute has password syntax, {@code false} otherwise. 2084 */ 2085 public static boolean hasPasswordSyntax(String attrName, Schema schema) 2086 { 2087 if (schema != null) 2088 { 2089 AttributeType attrType = AttributeDescription.valueOf(attrName, schema.getSchemaNG()).getAttributeType(); 2090 if (!attrType.isPlaceHolder()) 2091 { 2092 PasswordType passwordType = SchemaUtils.checkPasswordType(attrType); 2093 return passwordType.equals(PasswordType.USER_PASSWORD); 2094 } 2095 } 2096 return false; 2097 } 2098 2099 private static boolean hasAnySyntax(String attrName, Schema schema, List<String> oids) 2100 { 2101 if (schema != null) 2102 { 2103 AttributeType attrType = AttributeDescription.valueOf(attrName, schema.getSchemaNG()).getAttributeType(); 2104 if (!attrType.isPlaceHolder()) 2105 { 2106 return oids.contains(attrType.getSyntax().getOID()); 2107 } 2108 } 2109 return false; 2110 } 2111 2112 /** 2113 * Returns the string representation of a matching rule. 2114 * @param matchingRule the matching rule. 2115 * @return the string representation of a matching rule. 2116 */ 2117 public static String getMatchingRuleText(MatchingRule matchingRule) 2118 { 2119 String nameOrOID = matchingRule.getNameOrOID(); 2120 String oid = matchingRule.getOID(); 2121 if (!nameOrOID.equals(oid)) 2122 { 2123 // This is the name only 2124 return nameOrOID + " - " + oid; 2125 } 2126 return oid; 2127 } 2128 2129 /** 2130 * Returns the InitialLdapContext to connect to the administration connector 2131 * of the server using the information in the ControlCenterInfo object (which 2132 * provides the host and administration connector port to be used) and some 2133 * LDAP credentials. 2134 * It also tests that the provided credentials have enough rights to read the 2135 * configuration. 2136 * @param controlInfo the object which provides the connection parameters. 2137 * @param bindDN the base DN to be used to bind. 2138 * @param pwd the password to be used to bind. 2139 * @return the InitialLdapContext connected to the server. 2140 * @throws NamingException if there was a problem connecting to the server 2141 * or the provided credentials do not have enough rights. 2142 * @throws ConfigReadException if there is an error reading the configuration. 2143 */ 2144 public static ConnectionWrapper getAdminDirContext(ControlPanelInfo controlInfo, String bindDN, String pwd) 2145 throws NamingException, ConfigReadException 2146 { 2147 String usedUrl = controlInfo.getAdminConnectorURL(); 2148 if (usedUrl == null) 2149 { 2150 throw new ConfigReadException( 2151 ERR_COULD_NOT_FIND_VALID_LDAPURL.get()); 2152 } 2153 2154 // Search for the config to check that it is the directory manager. 2155 ConnectionWrapper conn = new ConnectionWrapper( 2156 usedUrl, LDAPS, bindDN, pwd, controlInfo.getConnectTimeout(), controlInfo.getTrustManager()); 2157 checkCanReadConfig(conn.getLdapContext()); 2158 return conn; 2159 } 2160 2161 2162 /** 2163 * Returns the InitialLdapContext to connect to the server using the 2164 * information in the ControlCenterInfo object (which provides the host, port 2165 * and protocol to be used) and some LDAP credentials. It also tests that 2166 * the provided credentials have enough rights to read the configuration. 2167 * @param controlInfo the object which provides the connection parameters. 2168 * @param bindDN the base DN to be used to bind. 2169 * @param pwd the password to be used to bind. 2170 * @return the InitialLdapContext connected to the server. 2171 * @throws NamingException if there was a problem connecting to the server 2172 * or the provided credentials do not have enough rights. 2173 * @throws ConfigReadException if there is an error reading the configuration. 2174 */ 2175 public static InitialLdapContext getUserDataDirContext( 2176 ControlPanelInfo controlInfo, 2177 String bindDN, String pwd) throws NamingException, ConfigReadException 2178 { 2179 InitialLdapContext ctx; 2180 String usedUrl; 2181 if (controlInfo.connectUsingStartTLS()) 2182 { 2183 usedUrl = controlInfo.getStartTLSURL(); 2184 if (usedUrl == null) 2185 { 2186 throw new ConfigReadException( 2187 ERR_COULD_NOT_FIND_VALID_LDAPURL.get()); 2188 } 2189 ctx = Utils.createStartTLSContext(usedUrl, 2190 bindDN, pwd, controlInfo.getConnectTimeout(), null, 2191 controlInfo.getTrustManager(), null); 2192 } 2193 else if (controlInfo.connectUsingLDAPS()) 2194 { 2195 usedUrl = controlInfo.getLDAPSURL(); 2196 if (usedUrl == null) 2197 { 2198 throw new ConfigReadException( 2199 ERR_COULD_NOT_FIND_VALID_LDAPURL.get()); 2200 } 2201 ctx = createLdapsContext(usedUrl, 2202 bindDN, pwd, controlInfo.getConnectTimeout(), null, 2203 controlInfo.getTrustManager(), null); 2204 } 2205 else 2206 { 2207 usedUrl = controlInfo.getLDAPURL(); 2208 if (usedUrl == null) 2209 { 2210 throw new ConfigReadException( 2211 ERR_COULD_NOT_FIND_VALID_LDAPURL.get()); 2212 } 2213 ctx = createLdapContext(usedUrl, 2214 bindDN, pwd, controlInfo.getConnectTimeout(), null); 2215 } 2216 2217 checkCanReadConfig(ctx); 2218 return ctx; 2219 } 2220 2221 /** 2222 * Checks that the provided connection can read cn=config. 2223 * @param ctx the connection to be tested. 2224 * @throws NamingException if an error occurs while reading cn=config. 2225 */ 2226 private static void checkCanReadConfig(InitialLdapContext ctx) 2227 throws NamingException 2228 { 2229 // Search for the config to check that it is the directory manager. 2230 SearchControls searchControls = new SearchControls(); 2231 searchControls.setSearchScope(SearchControls.OBJECT_SCOPE); 2232 searchControls.setReturningAttributes(new String[] { SchemaConstants.NO_ATTRIBUTES }); 2233 NamingEnumeration<SearchResult> sr = 2234 ctx.search("cn=config", "objectclass=*", searchControls); 2235 try 2236 { 2237 while (sr.hasMore()) 2238 { 2239 sr.next(); 2240 } 2241 } 2242 finally 2243 { 2244 sr.close(); 2245 } 2246 } 2247 2248 /** 2249 * Ping the specified InitialLdapContext. 2250 * This method sends a search request on the root entry of the DIT 2251 * and forward the corresponding exception (if any). 2252 * @param ctx the InitialLdapContext to be "pinged". 2253 * @throws NamingException if the ping could not be performed. 2254 */ 2255 public static void pingDirContext(InitialLdapContext ctx) 2256 throws NamingException { 2257 SearchControls sc = new SearchControls( 2258 SearchControls.OBJECT_SCOPE, 2259 0, // count limit 2260 0, // time limit 2261 new String[0], // No attributes 2262 false, // Don't return bound object 2263 false // Don't dereference link 2264 ); 2265 ctx.search("", "objectClass=*", sc); 2266 } 2267 2268 /** 2269 * Deletes a configuration subtree using the provided configuration handler. 2270 * @param confHandler the configuration handler to be used to delete the 2271 * subtree. 2272 * @param dn the DN of the subtree to be deleted. 2273 * @throws OpenDsException if an error occurs. 2274 * @throws ConfigException if an error occurs. 2275 */ 2276 public static void deleteConfigSubtree(ConfigurationHandler confHandler, DN dn) 2277 throws OpenDsException, ConfigException 2278 { 2279 org.forgerock.opendj.ldap.Entry confEntry = confHandler.getEntry(dn); 2280 if (confEntry != null) 2281 { 2282 // Copy the values to avoid problems with this recursive method. 2283 for (DN childDN : new ArrayList<>(confHandler.getChildren(dn))) 2284 { 2285 deleteConfigSubtree(confHandler, childDN); 2286 } 2287 confHandler.deleteEntry(dn); 2288 } 2289 } 2290 2291 2292 /** 2293 * Sets the required icon to the provided label. 2294 * @param label the label to be updated. 2295 */ 2296 public static void setRequiredIcon(JLabel label) 2297 { 2298 if (requiredIcon == null) 2299 { 2300 requiredIcon = 2301 createImageIcon(IconPool.IMAGE_PATH+"/required.gif"); 2302 requiredIcon.setDescription( 2303 INFO_REQUIRED_ICON_ACCESSIBLE_DESCRIPTION.get().toString()); 2304 requiredIcon.getAccessibleContext().setAccessibleName( 2305 INFO_REQUIRED_ICON_ACCESSIBLE_DESCRIPTION.get().toString()); 2306 } 2307 label.setIcon(requiredIcon); 2308 label.setHorizontalTextPosition(SwingConstants.LEADING); 2309 } 2310 2311 2312 /** 2313 * Updates the scrolls with the provided points. 2314 * This method uses SwingUtilities.invokeLater so it can be also called 2315 * outside the event thread. 2316 * @param pos the scroll and points. 2317 */ 2318 public static void updateViewPositions(final ViewPositions pos) 2319 { 2320 SwingUtilities.invokeLater(new Runnable() 2321 { 2322 @Override 2323 public void run() 2324 { 2325 for (int i=0; i<pos.size(); i++) 2326 { 2327 pos.getScrollPane(i).getViewport().setViewPosition(pos.getPoint(i)); 2328 } 2329 } 2330 }); 2331 } 2332 2333 /** 2334 * Gets the view positions object for the provided component. This includes 2335 * all the scroll panes inside the provided component. 2336 * @param comp the component. 2337 * @return the view positions for the provided component. 2338 */ 2339 public static ViewPositions getViewPositions(Component comp) 2340 { 2341 ViewPositions pos = new ViewPositions(); 2342 if (comp instanceof Container) 2343 { 2344 updateContainedViewPositions((Container)comp, pos); 2345 } 2346 return pos; 2347 } 2348 2349 /** 2350 * Returns the scrolpane where the provided component is contained. 2351 * <CODE>null</CODE> if the component is not contained in any scrolpane. 2352 * @param comp the component. 2353 * @return the scrolpane where the provided component is contained. 2354 */ 2355 public static JScrollPane getContainingScroll(Component comp) 2356 { 2357 JScrollPane scroll = null; 2358 Container parent = comp.getParent(); 2359 while (scroll == null && parent != null) 2360 { 2361 if (parent instanceof JScrollPane) 2362 { 2363 scroll = (JScrollPane)parent; 2364 } 2365 else 2366 { 2367 parent = parent.getParent(); 2368 } 2369 } 2370 return scroll; 2371 } 2372 2373 private static void updateContainedViewPositions(Container comp, 2374 ViewPositions pos) 2375 { 2376 if (comp instanceof JScrollPane) 2377 { 2378 JScrollPane scroll = (JScrollPane)comp; 2379 Point p = scroll.getViewport().getViewPosition(); 2380 pos.add(scroll, p); 2381 } 2382 else 2383 { 2384 for (int i=0; i<comp.getComponentCount(); i++) 2385 { 2386 Component child = comp.getComponent(i); 2387 if (child instanceof Container) 2388 { 2389 updateContainedViewPositions((Container)child, pos); 2390 } 2391 } 2392 } 2393 } 2394 2395 private static Object getFirstMonitoringValue(CustomSearchResult sr, String attrName) 2396 { 2397 if (sr != null) 2398 { 2399 List<Object> values = sr.getAttributeValues(attrName); 2400 if (values != null && !values.isEmpty()) 2401 { 2402 Object o = values.iterator().next(); 2403 try 2404 { 2405 return Long.parseLong(o.toString()); 2406 } 2407 catch (Throwable t1) 2408 { 2409 try 2410 { 2411 return Double.parseDouble(o.toString()); 2412 } 2413 catch (Throwable t2) 2414 { 2415 // Cannot convert it, just return it 2416 return o; 2417 } 2418 } 2419 } 2420 } 2421 return null; 2422 } 2423 2424 /** 2425 * Returns the first value as a String for a given attribute in the provided 2426 * entry. 2427 * 2428 * @param sr 2429 * the entry. It may be <CODE>null</CODE>. 2430 * @param attrName 2431 * the attribute name. 2432 * @return the first value as a String for a given attribute in the provided 2433 * entry. 2434 */ 2435 public static String getFirstValueAsString(CustomSearchResult sr, String attrName) 2436 { 2437 if (sr != null) 2438 { 2439 final List<Object> values = sr.getAttributeValues(attrName); 2440 if (values != null && !values.isEmpty()) 2441 { 2442 final Object o = values.get(0); 2443 if (o != null) 2444 { 2445 return String.valueOf(o); 2446 } 2447 } 2448 } 2449 return null; 2450 } 2451 2452 /** 2453 * Returns the monitoring value in a String form to be displayed to the user. 2454 * @param attr the attribute to analyze. 2455 * @param monitoringEntry the monitoring entry. 2456 * @return the monitoring value in a String form to be displayed to the user. 2457 */ 2458 public static String getMonitoringValue(MonitoringAttributes attr, 2459 CustomSearchResult monitoringEntry) 2460 { 2461 String monitoringValue = getFirstValueAsString(monitoringEntry, attr.getAttributeName()); 2462 if (monitoringValue == null) 2463 { 2464 return NO_VALUE_SET.toString(); 2465 } 2466 else if (isNotImplemented(attr, monitoringEntry)) 2467 { 2468 return NOT_IMPLEMENTED.toString(); 2469 } 2470 else if (attr.isNumericDate()) 2471 { 2472 if ("0".equals(monitoringValue)) 2473 { 2474 return NO_VALUE_SET.toString(); 2475 } 2476 Long l = Long.parseLong(monitoringValue); 2477 Date date = new Date(l); 2478 return ConfigFromDirContext.formatter.format(date); 2479 } 2480 else if (attr.isTime()) 2481 { 2482 if ("-1".equals(monitoringValue)) 2483 { 2484 return NO_VALUE_SET.toString(); 2485 } 2486 return monitoringValue; 2487 } 2488 else if (attr.isGMTDate()) 2489 { 2490 try 2491 { 2492 Date date = ConfigFromDirContext.utcParser.parse(monitoringValue); 2493 return ConfigFromDirContext.formatter.format(date); 2494 } 2495 catch (Throwable t) 2496 { 2497 return monitoringValue; 2498 } 2499 } 2500 else if (attr.isValueInBytes()) 2501 { 2502 Long l = Long.parseLong(monitoringValue); 2503 long mb = l / (1024 * 1024); 2504 long kbs = (l - mb * 1024 * 1024) / 1024; 2505 return INFO_CTRL_PANEL_MEMORY_VALUE.get(mb, kbs).toString(); 2506 } 2507 return monitoringValue; 2508 } 2509 2510 /** 2511 * Returns whether the provided monitoring value represents the non implemented label. 2512 * 2513 * @param attr the attribute to analyze. 2514 * @param monitoringEntry the monitoring entry. 2515 * @return {@code true} if the provided monitoring value represents the non implemented label, 2516 * {@code false} otherwise. 2517 */ 2518 private static boolean isNotImplemented(MonitoringAttributes attr, 2519 CustomSearchResult monitoringEntry) 2520 { 2521 String monitoringValue = getFirstValueAsString(monitoringEntry, attr.getAttributeName()); 2522 if (attr.isNumeric() && monitoringValue != null) 2523 { 2524 try 2525 { 2526 Long.parseLong(monitoringValue); 2527 return false; 2528 } 2529 catch (Throwable t) 2530 { 2531 return true; 2532 } 2533 } 2534 return false; 2535 } 2536 2537 /** 2538 * Adds a click tool tip listener to the provided component. 2539 * @param comp the component. 2540 */ 2541 public static void addClickTooltipListener(JComponent comp) 2542 { 2543 comp.addMouseListener(new ClickTooltipDisplayer()); 2544 } 2545 2546 /** 2547 * Updates a combo box model with a number of items. 2548 * The method assumes that is being called from the event thread. 2549 * @param newElements the new items for the combo box model. 2550 * @param model the combo box model to be updated. 2551 */ 2552 public static void updateComboBoxModel(Collection<?> newElements, 2553 DefaultComboBoxModel model) 2554 { 2555 updateComboBoxModel(newElements, model, null); 2556 } 2557 2558 /** 2559 * Updates a combo box model with a number of items. 2560 * The method assumes that is being called from the event thread. 2561 * @param newElements the new items for the combo box model. 2562 * @param model the combo box model to be updated. 2563 * @param comparator the object that will be used to compare the objects in 2564 * the model. If <CODE>null</CODE>, the equals method will be used. 2565 */ 2566 public static void updateComboBoxModel(Collection<?> newElements, 2567 DefaultComboBoxModel model, 2568 Comparator<Object> comparator) 2569 { 2570 boolean changed = newElements.size() != model.getSize(); 2571 if (!changed) 2572 { 2573 int i = 0; 2574 for (Object newElement : newElements) 2575 { 2576 if (comparator != null) 2577 { 2578 changed = 2579 comparator.compare(newElement, model.getElementAt(i)) != 0; 2580 } 2581 else 2582 { 2583 changed = !newElement.equals(model.getElementAt(i)); 2584 } 2585 if (changed) 2586 { 2587 break; 2588 } 2589 i++; 2590 } 2591 } 2592 if (changed) 2593 { 2594 Object selected = model.getSelectedItem(); 2595 model.removeAllElements(); 2596 boolean selectDefault = false; 2597 for (Object newElement : newElements) 2598 { 2599 model.addElement(newElement); 2600 } 2601 if (selected != null) 2602 { 2603 if (model.getIndexOf(selected) != -1) 2604 { 2605 model.setSelectedItem(selected); 2606 } 2607 else 2608 { 2609 selectDefault = true; 2610 } 2611 } 2612 else 2613 { 2614 selectDefault = true; 2615 } 2616 if (selectDefault) 2617 { 2618 for (int i=0; i<model.getSize(); i++) 2619 { 2620 Object o = model.getElementAt(i); 2621 if (o instanceof CategorizedComboBoxElement 2622 && ((CategorizedComboBoxElement)o).getType() == CategorizedComboBoxElement.Type.CATEGORY) 2623 { 2624 continue; 2625 } 2626 model.setSelectedItem(o); 2627 break; 2628 } 2629 } 2630 } 2631 } 2632 2633 /** 2634 * Computes the possible comparison results for monitoring information. 2635 * 2636 * @param monitor1 2637 * the first monitor to compare 2638 * @param monitor2 2639 * the second monitor to compare 2640 * @param possibleResults 2641 * where possible results are output 2642 * @param attrNames 2643 * the names for which to compute possible comparison results 2644 */ 2645 public static void computeMonitoringPossibleResults(CustomSearchResult monitor1, CustomSearchResult monitor2, 2646 ArrayList<Integer> possibleResults, Collection<String> attrNames) 2647 { 2648 for (String attrName : attrNames) 2649 { 2650 int possibleResult; 2651 if (monitor1 == null) 2652 { 2653 if (monitor2 == null) 2654 { 2655 possibleResult = 0; 2656 } 2657 else 2658 { 2659 possibleResult = -1; 2660 } 2661 } 2662 else if (monitor2 == null) 2663 { 2664 possibleResult = 1; 2665 } 2666 else 2667 { 2668 Object v1 = getFirstValue(monitor1, attrName); 2669 Object v2 = getFirstValue(monitor2, attrName); 2670 if (v1 == null) 2671 { 2672 if (v2 == null) 2673 { 2674 possibleResult = 0; 2675 } 2676 else 2677 { 2678 possibleResult = -1; 2679 } 2680 } 2681 else if (v2 == null) 2682 { 2683 possibleResult = 1; 2684 } 2685 else if (v1 instanceof Number) 2686 { 2687 if (v2 instanceof Number) 2688 { 2689 if (v1 instanceof Double || v2 instanceof Double) 2690 { 2691 double n1 = ((Number) v1).doubleValue(); 2692 double n2 = ((Number) v2).doubleValue(); 2693 if (n1 > n2) 2694 { 2695 possibleResult = 1; 2696 } 2697 else if (n1 < n2) 2698 { 2699 possibleResult = -1; 2700 } 2701 else 2702 { 2703 possibleResult = 0; 2704 } 2705 } 2706 else 2707 { 2708 long n1 = ((Number) v1).longValue(); 2709 long n2 = ((Number) v2).longValue(); 2710 if (n1 > n2) 2711 { 2712 possibleResult = 1; 2713 } 2714 else if (n1 < n2) 2715 { 2716 possibleResult = -1; 2717 } 2718 else 2719 { 2720 possibleResult = 0; 2721 } 2722 } 2723 } 2724 else 2725 { 2726 possibleResult = 1; 2727 } 2728 } 2729 else if (v2 instanceof Number) 2730 { 2731 possibleResult = -1; 2732 } 2733 else 2734 { 2735 possibleResult = v1.toString().compareTo(v2.toString()); 2736 } 2737 } 2738 possibleResults.add(possibleResult); 2739 } 2740 } 2741 2742 private static Object getFirstValue(CustomSearchResult monitor, String attrName) 2743 { 2744 for (String attr : monitor.getAttributeNames()) 2745 { 2746 if (attr.equalsIgnoreCase(attrName)) 2747 { 2748 return getFirstMonitoringValue(monitor, attrName); 2749 } 2750 } 2751 return null; 2752 } 2753 2754 /** 2755 * Throw the first exception of the list (if any). 2756 * 2757 * @param <E> 2758 * The exception type 2759 * @param exceptions 2760 * A list of exceptions. 2761 * @throws E 2762 * The first element of the provided list (if the list is not 2763 * empty). 2764 */ 2765 public static <E extends Exception> void throwFirstFrom(List<? extends E> exceptions) throws E 2766 { 2767 if (!exceptions.isEmpty()) 2768 { 2769 throw exceptions.get(0); 2770 } 2771 } 2772 2773 /** Initialize the configuration framework. */ 2774 public static void initializeConfigurationFramework() 2775 { 2776 if (!ConfigurationFramework.getInstance().isInitialized()) 2777 { 2778 try 2779 { 2780 // Initialize configuration framework without logging anything. 2781 final Logger configFrameworkLogger = Logger.getLogger("com.forgerock.opendj.ldap.config.config"); 2782 configFrameworkLogger.setUseParentHandlers(false); 2783 ConfigurationFramework.getInstance().initialize(); 2784 configFrameworkLogger.setUseParentHandlers(true); 2785 } 2786 catch (ConfigException e) 2787 { 2788 final LocalizableMessage message = ERROR_CTRL_PANEL_INITIALIZE_CONFIG_OFFLINE.get(e.getLocalizedMessage()); 2789 logger.error(message); 2790 throw new RuntimeException(message.toString(), e); 2791 } 2792 } 2793 } 2794 2795}