001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2008-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS 026 */ 027package org.opends.guitools.controlpanel.ui; 028 029import static org.opends.messages.AdminToolMessages.*; 030import static org.opends.messages.QuickSetupMessages.*; 031 032import static com.forgerock.opendj.cli.Utils.*; 033 034import java.awt.Component; 035import java.awt.GridBagConstraints; 036import java.awt.GridBagLayout; 037import java.awt.Insets; 038import java.awt.Window; 039import java.awt.event.ActionEvent; 040import java.awt.event.ActionListener; 041import java.awt.event.ItemEvent; 042import java.awt.event.ItemListener; 043import java.awt.event.KeyAdapter; 044import java.awt.event.KeyEvent; 045import java.net.URI; 046import java.security.cert.X509Certificate; 047import java.util.ArrayList; 048import java.util.Enumeration; 049import java.util.HashMap; 050import java.util.LinkedHashSet; 051import java.util.List; 052import java.util.Map; 053import java.util.Set; 054import java.util.SortedSet; 055import java.util.TreeSet; 056 057import javax.naming.NamingException; 058import javax.naming.ldap.InitialLdapContext; 059import javax.swing.BorderFactory; 060import javax.swing.Box; 061import javax.swing.DefaultComboBoxModel; 062import javax.swing.JButton; 063import javax.swing.JComboBox; 064import javax.swing.JComponent; 065import javax.swing.JLabel; 066import javax.swing.JList; 067import javax.swing.JPanel; 068import javax.swing.JSeparator; 069import javax.swing.JTree; 070import javax.swing.SwingConstants; 071import javax.swing.SwingUtilities; 072import javax.swing.border.EmptyBorder; 073import javax.swing.event.TreeModelEvent; 074import javax.swing.event.TreeModelListener; 075import javax.swing.tree.DefaultMutableTreeNode; 076import javax.swing.tree.DefaultTreeModel; 077import javax.swing.tree.TreeNode; 078import javax.swing.tree.TreePath; 079 080import org.forgerock.i18n.LocalizableMessage; 081import org.forgerock.i18n.LocalizableMessageBuilder; 082import org.forgerock.i18n.slf4j.LocalizedLogger; 083import org.forgerock.opendj.ldap.ByteString; 084import org.opends.admin.ads.util.ApplicationTrustManager; 085import org.opends.admin.ads.util.ConnectionUtils; 086import org.opends.guitools.controlpanel.browser.BrowserController; 087import org.opends.guitools.controlpanel.datamodel.BackendDescriptor; 088import org.opends.guitools.controlpanel.datamodel.BaseDNDescriptor; 089import org.opends.guitools.controlpanel.datamodel.CategorizedComboBoxElement; 090import org.opends.guitools.controlpanel.datamodel.ConfigReadException; 091import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo; 092import org.opends.guitools.controlpanel.datamodel.IndexDescriptor; 093import org.opends.guitools.controlpanel.datamodel.ServerDescriptor; 094import org.opends.guitools.controlpanel.event.BackendPopulatedEvent; 095import org.opends.guitools.controlpanel.event.BackendPopulatedListener; 096import org.opends.guitools.controlpanel.event.BrowserEvent; 097import org.opends.guitools.controlpanel.event.BrowserEventListener; 098import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent; 099import org.opends.guitools.controlpanel.ui.components.FilterTextField; 100import org.opends.guitools.controlpanel.ui.components.TreePanel; 101import org.opends.guitools.controlpanel.ui.nodes.BasicNode; 102import org.opends.guitools.controlpanel.ui.renderer.CustomListCellRenderer; 103import org.opends.guitools.controlpanel.util.Utilities; 104import org.opends.quicksetup.UserDataCertificateException; 105import org.opends.quicksetup.ui.CertificateDialog; 106import org.opends.quicksetup.util.UIKeyStore; 107import org.opends.server.protocols.ldap.LDAPFilter; 108import org.opends.server.types.AttributeType; 109import org.opends.server.types.DN; 110import org.opends.server.types.DirectoryException; 111import org.opends.server.types.LDAPException; 112import org.opends.server.types.SearchFilter; 113import org.opends.server.util.ServerConstants; 114 115/** 116 * The abstract class used to refactor some code. The classes that extend this 117 * class are the 'Browse Entries' panel and the panel of the dialog we display 118 * when the user can choose a set of entries (for instance when the user adds a 119 * member to a group in the 'New Group' dialog). 120 */ 121public abstract class AbstractBrowseEntriesPanel extends StatusGenericPanel implements BackendPopulatedListener 122{ 123 private static final long serialVersionUID = -6063927039968115236L; 124 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 125 126 /** LDAP filter message. */ 127 protected static final LocalizableMessage LDAP_FILTER = INFO_CTRL_PANEL_LDAP_FILTER.get(); 128 /** User filter message. */ 129 protected static final LocalizableMessage USER_FILTER = INFO_CTRL_PANEL_USERS_FILTER.get(); 130 /** Group filter message. */ 131 protected static final LocalizableMessage GROUP_FILTER = INFO_CTRL_PANEL_GROUPS_FILTER.get(); 132 private static final LocalizableMessage OTHER_BASE_DN = INFO_CTRL_PANEL_OTHER_BASE_DN.get(); 133 134 private static final String ALL_BASE_DNS = "All Base DNs"; 135 private static final int MAX_NUMBER_ENTRIES = 5000; 136 private static final int MAX_NUMBER_OTHER_BASE_DNS = 10; 137 private static final String[] CONTAINER_CLASSES = { "organization", "organizationalUnit" }; 138 private static final String[] SYSTEM_INDEXES = 139 { "aci", "dn2id", "ds-sync-hist", "entryUUID", "id2children", "id2subtree" }; 140 141 142 private JComboBox<String> baseDNs; 143 144 /** The combo box containing the different filter types. */ 145 protected JComboBox<CharSequence> filterAttribute; 146 /** The text field of the filter. */ 147 protected FilterTextField filter; 148 149 private JButton applyButton; 150 private JButton okButton; 151 private JButton cancelButton; 152 private JButton closeButton; 153 154 private JLabel lBaseDN; 155 private JLabel lFilter; 156 private JLabel lLimit; 157 private JLabel lNumberOfEntries; 158 private JLabel lNoMatchFound; 159 160 private InitialLdapContext createdUserDataCtx; 161 /** The tree pane contained in this panel. */ 162 protected TreePanel treePane; 163 /** The browser controller used to update the LDAP entry tree. */ 164 protected BrowserController controller; 165 private NumberOfEntriesUpdater numberEntriesUpdater; 166 private BaseDNPanel otherBaseDNPanel; 167 private GenericDialog otherBaseDNDlg; 168 private boolean firstTimeDisplayed = true; 169 private Object lastSelectedBaseDN; 170 private boolean ignoreBaseDNEvents; 171 172 private List<DN> otherBaseDns = new ArrayList<>(); 173 174 /** 175 * Default constructor. 176 */ 177 public AbstractBrowseEntriesPanel() 178 { 179 super(); 180 createLayout(); 181 } 182 183 @Override 184 public boolean requiresBorder() 185 { 186 return false; 187 } 188 189 @Override 190 public boolean requiresScroll() 191 { 192 return false; 193 } 194 195 @Override 196 public boolean callConfigurationChangedInBackground() 197 { 198 return true; 199 } 200 201 @Override 202 public void setInfo(ControlPanelInfo info) 203 { 204 if (controller == null) 205 { 206 createBrowserController(info); 207 } 208 super.setInfo(info); 209 treePane.setInfo(info); 210 info.addBackendPopulatedListener(this); 211 } 212 213 @Override 214 public final GenericDialog.ButtonType getButtonType() 215 { 216 return GenericDialog.ButtonType.NO_BUTTON; 217 } 218 219 /** 220 * Since these panel has a special layout, we cannot use the layout of the 221 * GenericDialog and we return ButtonType.NO_BUTTON in the method 222 * getButtonType. We use this method to be able to add some progress 223 * information to the left of the buttons. 224 * 225 * @return the button type of the panel. 226 */ 227 protected abstract GenericDialog.ButtonType getBrowseButtonType(); 228 229 @Override 230 public void toBeDisplayed(boolean visible) 231 { 232 super.toBeDisplayed(visible); 233 Window w = Utilities.getParentDialog(this); 234 if (w instanceof GenericDialog) 235 { 236 ((GenericDialog) w).getRootPane().setDefaultButton(null); 237 } 238 else if (w instanceof GenericFrame) 239 { 240 ((GenericFrame) w).getRootPane().setDefaultButton(null); 241 } 242 } 243 244 @Override 245 protected void setEnabledOK(boolean enable) 246 { 247 okButton.setEnabled(enable); 248 } 249 250 @Override 251 protected void setEnabledCancel(boolean enable) 252 { 253 cancelButton.setEnabled(enable); 254 } 255 256 /** Creates the layout of the panel (but the contents are not populated here). */ 257 @SuppressWarnings("unchecked") 258 private void createLayout() 259 { 260 setBackground(ColorAndFontConstants.greyBackground); 261 GridBagConstraints gbc = new GridBagConstraints(); 262 gbc.anchor = GridBagConstraints.WEST; 263 gbc.gridx = 0; 264 gbc.gridy = 0; 265 gbc.gridwidth = 7; 266 addErrorPane(gbc); 267 LocalizableMessage title = INFO_CTRL_PANEL_SERVER_NOT_RUNNING_SUMMARY.get(); 268 LocalizableMessageBuilder mb = new LocalizableMessageBuilder(); 269 mb.append(INFO_CTRL_PANEL_SERVER_NOT_RUNNING_DETAILS.get()); 270 mb.append("<br><br>"); 271 mb.append(getStartServerHTML()); 272 LocalizableMessage details = mb.toMessage(); 273 updateErrorPane(errorPane, title, ColorAndFontConstants.errorTitleFont, details, ColorAndFontConstants.defaultFont); 274 errorPane.setVisible(true); 275 errorPane.setFocusable(true); 276 277 gbc.insets = new Insets(10, 10, 0, 10); 278 gbc.gridy++; 279 gbc.gridwidth = 1; 280 gbc.weightx = 0; 281 gbc.fill = GridBagConstraints.NONE; 282 lBaseDN = Utilities.createPrimaryLabel(INFO_CTRL_PANEL_BASE_DN_LABEL.get()); 283 gbc.gridx = 0; 284 gbc.fill = GridBagConstraints.HORIZONTAL; 285 gbc.insets.right = 0; 286 add(lBaseDN, gbc); 287 gbc.insets.left = 5; 288 baseDNs = Utilities.createComboBox(); 289 290 DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>(); 291 model.addElement("dc=dn to be displayed"); 292 baseDNs.setModel(model); 293 baseDNs.setRenderer(new CustomComboBoxCellRenderer(baseDNs)); 294 baseDNs.addItemListener(new ItemListener() 295 { 296 @SuppressWarnings("rawtypes") 297 @Override 298 public void itemStateChanged(ItemEvent ev) 299 { 300 if (ignoreBaseDNEvents || ev.getStateChange() != ItemEvent.SELECTED) 301 { 302 return; 303 } 304 Object o = baseDNs.getSelectedItem(); 305 if (isCategory(o)) 306 { 307 if (lastSelectedBaseDN == null) 308 { 309 // Look for the first element that is not a category 310 for (int i = 0; i < baseDNs.getModel().getSize(); i++) 311 { 312 Object item = baseDNs.getModel().getElementAt(i); 313 if (item instanceof CategorizedComboBoxElement && !isCategory(item)) 314 { 315 lastSelectedBaseDN = item; 316 break; 317 } 318 } 319 if (lastSelectedBaseDN != null) 320 { 321 baseDNs.setSelectedItem(lastSelectedBaseDN); 322 } 323 } 324 else 325 { 326 ignoreBaseDNEvents = true; 327 baseDNs.setSelectedItem(lastSelectedBaseDN); 328 ignoreBaseDNEvents = false; 329 } 330 } 331 else if (COMBO_SEPARATOR.equals(o)) 332 { 333 ignoreBaseDNEvents = true; 334 baseDNs.setSelectedItem(lastSelectedBaseDN); 335 ignoreBaseDNEvents = false; 336 } 337 else if (!OTHER_BASE_DN.equals(o)) 338 { 339 lastSelectedBaseDN = o; 340 if (lastSelectedBaseDN != null) 341 { 342 applyButtonClicked(); 343 } 344 } 345 else 346 { 347 if (otherBaseDNDlg == null) 348 { 349 otherBaseDNPanel = new BaseDNPanel(); 350 otherBaseDNDlg = new GenericDialog(Utilities.getFrame(AbstractBrowseEntriesPanel.this), otherBaseDNPanel); 351 otherBaseDNDlg.setModal(true); 352 Utilities.centerGoldenMean(otherBaseDNDlg, Utilities.getParentDialog(AbstractBrowseEntriesPanel.this)); 353 } 354 otherBaseDNDlg.setVisible(true); 355 String newBaseDn = otherBaseDNPanel.getBaseDn(); 356 DefaultComboBoxModel model = (DefaultComboBoxModel) baseDNs.getModel(); 357 if (newBaseDn != null) 358 { 359 CategorizedComboBoxElement newElement = null; 360 361 try 362 { 363 DN dn = DN.valueOf(newBaseDn); 364 newElement = 365 new CategorizedComboBoxElement(Utilities.unescapeUtf8(dn.toString()), 366 CategorizedComboBoxElement.Type.REGULAR); 367 if (!otherBaseDns.contains(dn)) 368 { 369 otherBaseDns.add(0, dn); 370 371 if (otherBaseDns.size() > MAX_NUMBER_OTHER_BASE_DNS) 372 { 373 ignoreBaseDNEvents = true; 374 for (int i = otherBaseDns.size() - 1; i >= MAX_NUMBER_OTHER_BASE_DNS; i--) 375 { 376 DN dnToRemove = otherBaseDns.get(i); 377 otherBaseDns.remove(i); 378 Object elementToRemove = 379 new CategorizedComboBoxElement(Utilities.unescapeUtf8(dnToRemove.toString()), 380 CategorizedComboBoxElement.Type.REGULAR); 381 model.removeElement(elementToRemove); 382 } 383 ignoreBaseDNEvents = false; 384 } 385 } 386 if (model.getIndexOf(newElement) == -1) 387 { 388 int index = model.getIndexOf(COMBO_SEPARATOR); 389 model.insertElementAt(newElement, index + 1); 390 if (otherBaseDns.size() == 1) 391 { 392 model.insertElementAt(COMBO_SEPARATOR, index + 2); 393 } 394 } 395 } 396 catch (Throwable t) 397 { 398 throw new RuntimeException("Unexpected error decoding dn " + newBaseDn, t); 399 } 400 401 model.setSelectedItem(newElement); 402 } 403 else if (lastSelectedBaseDN != null) 404 { 405 ignoreBaseDNEvents = true; 406 model.setSelectedItem(lastSelectedBaseDN); 407 ignoreBaseDNEvents = false; 408 } 409 } 410 } 411 }); 412 gbc.gridx++; 413 add(baseDNs, gbc); 414 415 gbc.gridx++; 416 gbc.fill = GridBagConstraints.VERTICAL; 417 gbc.insets.left = 10; 418 add(new JSeparator(SwingConstants.VERTICAL), gbc); 419 gbc.fill = GridBagConstraints.HORIZONTAL; 420 lFilter = Utilities.createPrimaryLabel(INFO_CTRL_PANEL_FILTER_LABEL.get()); 421 gbc.gridx++; 422 add(lFilter, gbc); 423 424 filterAttribute = Utilities.createComboBox(); 425 filterAttribute.setModel(new DefaultComboBoxModel<CharSequence>(new CharSequence[] { 426 USER_FILTER, GROUP_FILTER, COMBO_SEPARATOR, "attributetobedisplayed", COMBO_SEPARATOR, LDAP_FILTER })); 427 filterAttribute.setRenderer(new CustomListCellRenderer(filterAttribute)); 428 filterAttribute.addItemListener(new IgnoreItemListener(filterAttribute)); 429 gbc.gridx++; 430 gbc.insets.left = 5; 431 add(filterAttribute, gbc); 432 433 filter = new FilterTextField(); 434 filter.setToolTipText(INFO_CTRL_PANEL_SUBSTRING_SEARCH_INLINE_HELP.get().toString()); 435 filter.addKeyListener(new KeyAdapter() 436 { 437 @Override 438 public void keyReleased(KeyEvent e) 439 { 440 if (e.getKeyCode() == KeyEvent.VK_ENTER && applyButton.isEnabled()) 441 { 442 filter.displayRefreshIcon(true); 443 applyButtonClicked(); 444 } 445 } 446 }); 447 filter.addActionListener(new ActionListener() 448 { 449 @Override 450 public void actionPerformed(ActionEvent ev) 451 { 452 filter.displayRefreshIcon(true); 453 applyButtonClicked(); 454 } 455 }); 456 457 gbc.weightx = 1.0; 458 gbc.gridx++; 459 add(filter, gbc); 460 461 gbc.insets.top = 10; 462 applyButton = Utilities.createButton(INFO_CTRL_PANEL_APPLY_BUTTON_LABEL.get()); 463 gbc.insets.right = 10; 464 gbc.gridx++; 465 gbc.weightx = 0.0; 466 add(applyButton, gbc); 467 applyButton.addActionListener(new ActionListener() 468 { 469 @Override 470 public void actionPerformed(ActionEvent ev) 471 { 472 applyButtonClicked(); 473 } 474 }); 475 gbc.insets = new Insets(10, 0, 0, 0); 476 gbc.gridx = 0; 477 gbc.gridy++; 478 gbc.weightx = 1.0; 479 gbc.weighty = 1.0; 480 gbc.fill = GridBagConstraints.BOTH; 481 gbc.gridwidth = 7; 482 add(createMainPanel(), gbc); 483 484 // The button panel 485 gbc.gridy++; 486 gbc.weighty = 0.0; 487 gbc.insets = new Insets(0, 0, 0, 0); 488 add(createButtonsPanel(), gbc); 489 } 490 491 /** 492 * Returns the panel that contains the buttons of type OK, CANCEL, etc. 493 * 494 * @return the panel that contains the buttons of type OK, CANCEL, etc. 495 */ 496 private JPanel createButtonsPanel() 497 { 498 JPanel buttonsPanel = new JPanel(new GridBagLayout()); 499 GridBagConstraints gbc = new GridBagConstraints(); 500 gbc.gridx = 0; 501 gbc.gridy = 0; 502 gbc.anchor = GridBagConstraints.WEST; 503 gbc.fill = GridBagConstraints.HORIZONTAL; 504 gbc.gridwidth = 1; 505 gbc.gridy = 0; 506 lLimit = Utilities.createDefaultLabel(); 507 Utilities.setWarningLabel(lLimit, INFO_CTRL_PANEL_MAXIMUM_CHILDREN_DISPLAYED.get(MAX_NUMBER_ENTRIES)); 508 gbc.weighty = 0.0; 509 gbc.gridy++; 510 lLimit.setVisible(false); 511 lNumberOfEntries = Utilities.createDefaultLabel(); 512 gbc.insets = new Insets(10, 10, 10, 10); 513 buttonsPanel.add(lNumberOfEntries, gbc); 514 buttonsPanel.add(lLimit, gbc); 515 gbc.weightx = 1.0; 516 gbc.gridx++; 517 buttonsPanel.add(Box.createHorizontalGlue(), gbc); 518 buttonsPanel.setOpaque(true); 519 buttonsPanel.setBackground(ColorAndFontConstants.greyBackground); 520 gbc.gridx++; 521 gbc.weightx = 0.0; 522 if (getBrowseButtonType() == GenericDialog.ButtonType.CLOSE) 523 { 524 closeButton = Utilities.createButton(INFO_CTRL_PANEL_CLOSE_BUTTON_LABEL.get()); 525 closeButton.setOpaque(false); 526 buttonsPanel.add(closeButton, gbc); 527 closeButton.addActionListener(new ActionListener() 528 { 529 @Override 530 public void actionPerformed(ActionEvent ev) 531 { 532 closeClicked(); 533 } 534 }); 535 } 536 else if (getBrowseButtonType() == GenericDialog.ButtonType.OK) 537 { 538 okButton = Utilities.createButton(INFO_CTRL_PANEL_OK_BUTTON_LABEL.get()); 539 okButton.setOpaque(false); 540 buttonsPanel.add(okButton, gbc); 541 okButton.addActionListener(new ActionListener() 542 { 543 @Override 544 public void actionPerformed(ActionEvent ev) 545 { 546 okClicked(); 547 } 548 }); 549 } 550 if (getBrowseButtonType() == GenericDialog.ButtonType.OK_CANCEL) 551 { 552 okButton = Utilities.createButton(INFO_CTRL_PANEL_OK_BUTTON_LABEL.get()); 553 okButton.setOpaque(false); 554 gbc.insets.right = 0; 555 buttonsPanel.add(okButton, gbc); 556 okButton.addActionListener(new ActionListener() 557 { 558 @Override 559 public void actionPerformed(ActionEvent ev) 560 { 561 okClicked(); 562 } 563 }); 564 cancelButton = Utilities.createButton(INFO_CTRL_PANEL_CANCEL_BUTTON_LABEL.get()); 565 cancelButton.setOpaque(false); 566 gbc.insets.right = 10; 567 gbc.insets.left = 5; 568 gbc.gridx++; 569 buttonsPanel.add(cancelButton, gbc); 570 cancelButton.addActionListener(new ActionListener() 571 { 572 @Override 573 public void actionPerformed(ActionEvent ev) 574 { 575 cancelClicked(); 576 } 577 }); 578 } 579 580 buttonsPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, ColorAndFontConstants.defaultBorderColor)); 581 582 return buttonsPanel; 583 } 584 585 /** {@inheritDoc} */ 586 @Override 587 public Component getPreferredFocusComponent() 588 { 589 return baseDNs; 590 } 591 592 /** {@inheritDoc} */ 593 @Override 594 public void cancelClicked() 595 { 596 setPrimaryValid(lBaseDN); 597 setSecondaryValid(lFilter); 598 super.cancelClicked(); 599 } 600 601 /** 602 * The method that is called when the user clicks on Apply. Basically it will 603 * update the BrowserController with the new base DN and filter specified by 604 * the user. The method assumes that is being called from the event thread. 605 */ 606 protected void applyButtonClicked() 607 { 608 List<LocalizableMessage> errors = new ArrayList<>(); 609 setPrimaryValid(lFilter); 610 String s = getBaseDN(); 611 boolean displayAll = false; 612 DN theDN = null; 613 if (s != null) 614 { 615 displayAll = ALL_BASE_DNS.equals(s); 616 if (!displayAll) 617 { 618 try 619 { 620 theDN = DN.valueOf(s); 621 } 622 catch (Throwable t) 623 { 624 errors.add(INFO_CTRL_PANEL_INVALID_DN_DETAILS.get(s, t)); 625 } 626 } 627 } 628 else 629 { 630 errors.add(INFO_CTRL_PANEL_NO_BASE_DN_SELECTED.get()); 631 } 632 String filterValue = getFilter(); 633 try 634 { 635 LDAPFilter.decode(filterValue); 636 } 637 catch (LDAPException le) 638 { 639 errors.add(INFO_CTRL_PANEL_INVALID_FILTER_DETAILS.get(le.getMessageObject())); 640 setPrimaryInvalid(lFilter); 641 } 642 if (errors.isEmpty()) 643 { 644 lLimit.setVisible(false); 645 lNumberOfEntries.setVisible(true); 646 controller.removeAllUnderRoot(); 647 controller.setFilter(filterValue); 648 controller.setAutomaticExpand(!BrowserController.ALL_OBJECTS_FILTER.equals(filterValue)); 649 SortedSet<String> allSuffixes = new TreeSet<>(); 650 if (controller.getConfigurationConnection() != null) 651 { 652 treePane.getTree().setRootVisible(displayAll); 653 treePane.getTree().setShowsRootHandles(!displayAll); 654 boolean added = false; 655 for (BackendDescriptor backend : getInfo().getServerDescriptor().getBackends()) 656 { 657 for (BaseDNDescriptor baseDN : backend.getBaseDns()) 658 { 659 boolean isBaseDN = baseDN.getDn().equals(theDN); 660 String dn = Utilities.unescapeUtf8(baseDN.getDn().toString()); 661 if (displayAll) 662 { 663 allSuffixes.add(dn); 664 } 665 else if (isBaseDN) 666 { 667 controller.addSuffix(dn, null); 668 added = true; 669 } 670 } 671 } 672 if (displayAll) 673 { 674 allSuffixes.add(ServerConstants.DN_EXTERNAL_CHANGELOG_ROOT); 675 for (String dn : allSuffixes) 676 { 677 controller.addSuffix(dn, null); 678 } 679 } 680 else if (!added && !displayAll) 681 { 682 if (isChangeLog(theDN)) 683 { 684 // Consider it a suffix 685 controller.addSuffix(s, null); 686 } 687 else 688 { 689 BasicNode rootNode = (BasicNode) controller.getTree().getModel().getRoot(); 690 if (controller.findChildNode(rootNode, s) == -1) 691 { 692 controller.addNodeUnderRoot(s); 693 } 694 } 695 } 696 } 697 else 698 { 699 controller.getTree().setRootVisible(false); 700 controller.removeAllUnderRoot(); 701 } 702 } 703 else 704 { 705 displayErrorDialog(errors); 706 } 707 } 708 709 private boolean isChangeLog(DN theDN) 710 { 711 try 712 { 713 return theDN.equals(DN.valueOf(ServerConstants.DN_EXTERNAL_CHANGELOG_ROOT)); 714 } 715 catch (Throwable t) 716 { 717 // Bug 718 t.printStackTrace(); 719 return false; 720 } 721 } 722 723 /** 724 * Returns the LDAP filter built based in the parameters provided by the user. 725 * 726 * @return the LDAP filter built based in the parameters provided by the user. 727 */ 728 private String getFilter() 729 { 730 String filterText = filter.getText(); 731 if (filterText.length() == 0) 732 { 733 return BrowserController.ALL_OBJECTS_FILTER; 734 } 735 736 Object attr = filterAttribute.getSelectedItem(); 737 if (LDAP_FILTER.equals(attr)) 738 { 739 filterText = filterText.trim(); 740 if (filterText.length() == 0) 741 { 742 return BrowserController.ALL_OBJECTS_FILTER; 743 } 744 745 return filterText; 746 } 747 else if (USER_FILTER.equals(attr)) 748 { 749 if ("*".equals(filterText)) 750 { 751 return "(objectClass=person)"; 752 } 753 754 return "(&(objectClass=person)(|" + "(cn=" + filterText + ")(sn=" + filterText + ")(uid=" + filterText + ")))"; 755 } 756 else if (GROUP_FILTER.equals(attr)) 757 { 758 if ("*".equals(filterText)) 759 { 760 return "(|(objectClass=groupOfUniqueNames)(objectClass=groupOfURLs))"; 761 } 762 763 return "(&(|(objectClass=groupOfUniqueNames)(objectClass=groupOfURLs))" + "(cn=" + filterText + "))"; 764 } 765 else if (attr != null) 766 { 767 try 768 { 769 return new LDAPFilter(SearchFilter.createFilterFromString("(" + attr + "=" + filterText + ")")).toString(); 770 } 771 catch (DirectoryException de) 772 { 773 // Try this alternative: 774 AttributeType attrType = 775 getInfo().getServerDescriptor().getSchema().getAttributeType(attr.toString().toLowerCase()); 776 return new LDAPFilter(SearchFilter.createEqualityFilter(attrType, ByteString.valueOf(filterText))).toString(); 777 } 778 } 779 else 780 { 781 return BrowserController.ALL_OBJECTS_FILTER; 782 } 783 } 784 785 /** 786 * Returns the component that will be displayed between the filtering options 787 * and the buttons panel. This component must contain the tree panel. 788 * 789 * @return the component that will be displayed between the filtering options 790 * and the buttons panel. 791 */ 792 protected abstract Component createMainPanel(); 793 794 /** {@inheritDoc} */ 795 @Override 796 public void backendPopulated(BackendPopulatedEvent ev) 797 { 798 if (controller.getConfigurationConnection() != null) 799 { 800 boolean displayAll = false; 801 boolean errorOccurred = false; 802 DN theDN = null; 803 String s = getBaseDN(); 804 if (s != null) 805 { 806 displayAll = ALL_BASE_DNS.equals(s); 807 if (!displayAll) 808 { 809 try 810 { 811 theDN = DN.valueOf(s); 812 } 813 catch (Throwable t) 814 { 815 errorOccurred = true; 816 } 817 } 818 } 819 else 820 { 821 errorOccurred = true; 822 } 823 if (!errorOccurred) 824 { 825 treePane.getTree().setRootVisible(displayAll); 826 treePane.getTree().setShowsRootHandles(!displayAll); 827 BasicNode rootNode = (BasicNode) controller.getTree().getModel().getRoot(); 828 boolean isSubordinate = false; 829 for (BackendDescriptor backend : ev.getBackends()) 830 { 831 for (BaseDNDescriptor baseDN : backend.getBaseDns()) 832 { 833 boolean isBaseDN = false; 834 if (baseDN.getDn().equals(theDN)) 835 { 836 isBaseDN = true; 837 } 838 else if (baseDN.getDn().isAncestorOf(theDN)) 839 { 840 isSubordinate = true; 841 } 842 String dn = Utilities.unescapeUtf8(baseDN.getDn().toString()); 843 if (displayAll || isBaseDN) 844 { 845 try 846 { 847 if (!controller.hasSuffix(dn)) 848 { 849 controller.addSuffix(dn, null); 850 } 851 else 852 { 853 int index = controller.findChildNode(rootNode, dn); 854 if (index >= 0) 855 { 856 TreeNode node = rootNode.getChildAt(index); 857 if (node != null) 858 { 859 TreePath path = new TreePath(controller.getTreeModel().getPathToRoot(node)); 860 controller.startRefresh(controller.getNodeInfoFromPath(path)); 861 } 862 } 863 } 864 } 865 catch (IllegalArgumentException iae) 866 { 867 // The suffix node exists but is not a suffix node. Simply log a message. 868 logger.warn( 869 LocalizableMessage.raw("Suffix: " + dn + " added as a non suffix node. Exception: " + iae, iae)); 870 } 871 } 872 } 873 } 874 if (isSubordinate && controller.findChildNode(rootNode, s) == -1) 875 { 876 controller.addNodeUnderRoot(s); 877 } 878 } 879 } 880 } 881 882 @Override 883 public void configurationChanged(ConfigurationChangeEvent ev) 884 { 885 final ServerDescriptor desc = ev.getNewDescriptor(); 886 887 updateCombos(desc); 888 updateBrowserControllerAndErrorPane(desc); 889 } 890 891 /** 892 * Creates and returns the tree panel. 893 * 894 * @return the tree panel. 895 */ 896 protected JComponent createTreePane() 897 { 898 treePane = new TreePanel(); 899 900 lNoMatchFound = Utilities.createDefaultLabel(INFO_CTRL_PANEL_NO_MATCHES_FOUND_LABEL.get()); 901 lNoMatchFound.setVisible(false); 902 903 // Calculate default size 904 JTree tree = treePane.getTree(); 905 DefaultMutableTreeNode root = new DefaultMutableTreeNode("myserver.mydomain.com:389"); 906 DefaultTreeModel model = new DefaultTreeModel(root); 907 tree.setModel(model); 908 tree.setShowsRootHandles(false); 909 tree.expandPath(new TreePath(root)); 910 JPanel p = new JPanel(new GridBagLayout()); 911 p.setBackground(ColorAndFontConstants.background); 912 GridBagConstraints gbc = new GridBagConstraints(); 913 gbc.gridx = 0; 914 gbc.gridy = 0; 915 gbc.gridwidth = 1; 916 gbc.anchor = GridBagConstraints.NORTHWEST; 917 gbc.fill = GridBagConstraints.BOTH; 918 gbc.weightx = 1.0; 919 gbc.weighty = 1.0; 920 Utilities.setBorder(treePane, new EmptyBorder(10, 0, 10, 0)); 921 p.add(treePane, gbc); 922 gbc.fill = GridBagConstraints.HORIZONTAL; 923 Utilities.setBorder(lNoMatchFound, new EmptyBorder(15, 15, 15, 15)); 924 p.add(lNoMatchFound, gbc); 925 926 if (getInfo() != null && controller == null) 927 { 928 createBrowserController(getInfo()); 929 } 930 numberEntriesUpdater = new NumberOfEntriesUpdater(); 931 numberEntriesUpdater.start(); 932 933 return p; 934 } 935 936 /** 937 * Creates the browser controller object. 938 * 939 * @param info 940 * the ControlPanelInfo to be used to create the browser controller. 941 */ 942 protected void createBrowserController(ControlPanelInfo info) 943 { 944 controller = new BrowserController(treePane.getTree(), info.getConnectionPool(), info.getIconPool()); 945 controller.setContainerClasses(CONTAINER_CLASSES); 946 controller.setShowContainerOnly(false); 947 controller.setMaxChildren(MAX_NUMBER_ENTRIES); 948 controller.addBrowserEventListener(new BrowserEventListener() 949 { 950 /** {@inheritDoc} */ 951 @Override 952 public void processBrowserEvent(BrowserEvent ev) 953 { 954 if (ev.getType() == BrowserEvent.Type.SIZE_LIMIT_REACHED) 955 { 956 lLimit.setVisible(true); 957 lNumberOfEntries.setVisible(false); 958 } 959 } 960 }); 961 controller.getTreeModel().addTreeModelListener(new TreeModelListener() 962 { 963 @Override 964 public void treeNodesChanged(TreeModelEvent e) 965 { 966 } 967 968 @Override 969 public void treeNodesInserted(TreeModelEvent e) 970 { 971 checkRootNode(); 972 } 973 974 @Override 975 public void treeNodesRemoved(TreeModelEvent e) 976 { 977 checkRootNode(); 978 } 979 980 @Override 981 public void treeStructureChanged(TreeModelEvent e) 982 { 983 checkRootNode(); 984 } 985 }); 986 } 987 988 989 private static boolean displayIndex(String name) 990 { 991 for (String systemIndex : SYSTEM_INDEXES) 992 { 993 if (systemIndex.equalsIgnoreCase(name)) 994 { 995 return false; 996 } 997 } 998 return true; 999 } 1000 1001 /** 1002 * Updates the contents of the combo boxes with the provided ServerDescriptor. 1003 * 1004 * @param desc 1005 * the server descriptor to be used to update the combo boxes. 1006 */ 1007 @SuppressWarnings("rawtypes") 1008 private void updateCombos(ServerDescriptor desc) 1009 { 1010 final SortedSet<String> newElements = new TreeSet<>(); 1011 for (BackendDescriptor backend : desc.getBackends()) 1012 { 1013 for (IndexDescriptor index : backend.getIndexes()) 1014 { 1015 String indexName = index.getName(); 1016 if (displayIndex(indexName)) 1017 { 1018 newElements.add(indexName); 1019 } 1020 } 1021 } 1022 1023 @SuppressWarnings("unchecked") 1024 final DefaultComboBoxModel<CharSequence> model = (DefaultComboBoxModel<CharSequence>) filterAttribute.getModel(); 1025 if (hasChanged(newElements, model)) 1026 { 1027 SwingUtilities.invokeLater(new Runnable() 1028 { 1029 @Override 1030 public void run() 1031 { 1032 Object selected = filterAttribute.getSelectedItem(); 1033 model.removeAllElements(); 1034 model.addElement(USER_FILTER); 1035 model.addElement(GROUP_FILTER); 1036 model.addElement(COMBO_SEPARATOR); 1037 for (String newElement : newElements) 1038 { 1039 model.addElement(newElement); 1040 } 1041 // If there are not backends, we get no indexes to set. 1042 if (!newElements.isEmpty()) 1043 { 1044 model.addElement(COMBO_SEPARATOR); 1045 } 1046 model.addElement(LDAP_FILTER); 1047 if (selected != null) 1048 { 1049 if (model.getIndexOf(selected) != -1) 1050 { 1051 model.setSelectedItem(selected); 1052 } 1053 else 1054 { 1055 model.setSelectedItem(model.getElementAt(0)); 1056 } 1057 } 1058 } 1059 }); 1060 } 1061 1062 Set<Object> baseDNNewElements = new LinkedHashSet<>(); 1063 SortedSet<String> backendIDs = new TreeSet<>(); 1064 Map<String, SortedSet<String>> hmBaseDNs = new HashMap<>(); 1065 1066 Map<String, BaseDNDescriptor> hmBaseDNWithEntries = new HashMap<>(); 1067 1068 BaseDNDescriptor baseDNWithEntries = null; 1069 for (BackendDescriptor backend : desc.getBackends()) 1070 { 1071 if (displayBackend(backend)) 1072 { 1073 String backendID = backend.getBackendID(); 1074 backendIDs.add(backendID); 1075 SortedSet<String> someBaseDNs = new TreeSet<>(); 1076 for (BaseDNDescriptor baseDN : backend.getBaseDns()) 1077 { 1078 try 1079 { 1080 someBaseDNs.add(Utilities.unescapeUtf8(baseDN.getDn().toString())); 1081 } 1082 catch (Throwable t) 1083 { 1084 throw new RuntimeException("Unexpected error: " + t, t); 1085 } 1086 if (baseDN.getEntries() > 0) 1087 { 1088 hmBaseDNWithEntries.put(Utilities.unescapeUtf8(baseDN.getDn().toString()), baseDN); 1089 } 1090 } 1091 hmBaseDNs.put(backendID, someBaseDNs); 1092 if ("userRoot".equalsIgnoreCase(backendID)) 1093 { 1094 for (String baseDN : someBaseDNs) 1095 { 1096 baseDNWithEntries = hmBaseDNWithEntries.get(baseDN); 1097 if (baseDNWithEntries != null) 1098 { 1099 break; 1100 } 1101 } 1102 } 1103 } 1104 } 1105 1106 baseDNNewElements.add(new CategorizedComboBoxElement(ALL_BASE_DNS, CategorizedComboBoxElement.Type.REGULAR)); 1107 for (String backendID : backendIDs) 1108 { 1109 baseDNNewElements.add(new CategorizedComboBoxElement(backendID, CategorizedComboBoxElement.Type.CATEGORY)); 1110 SortedSet<String> someBaseDNs = hmBaseDNs.get(backendID); 1111 for (String baseDN : someBaseDNs) 1112 { 1113 baseDNNewElements.add(new CategorizedComboBoxElement(baseDN, CategorizedComboBoxElement.Type.REGULAR)); 1114 if (baseDNWithEntries == null) 1115 { 1116 baseDNWithEntries = hmBaseDNWithEntries.get(baseDN); 1117 } 1118 } 1119 } 1120 for (DN dn : otherBaseDns) 1121 { 1122 baseDNNewElements.add(COMBO_SEPARATOR); 1123 baseDNNewElements.add(new CategorizedComboBoxElement( 1124 Utilities.unescapeUtf8(dn.toString()), CategorizedComboBoxElement.Type.REGULAR)); 1125 } 1126 baseDNNewElements.add(COMBO_SEPARATOR); 1127 baseDNNewElements.add(OTHER_BASE_DN); 1128 1129 if (firstTimeDisplayed && baseDNWithEntries != null) 1130 { 1131 ignoreBaseDNEvents = true; 1132 } 1133 updateComboBoxModel(baseDNNewElements, (DefaultComboBoxModel) baseDNs.getModel()); 1134 // Select the element in the combo box. 1135 if (firstTimeDisplayed && baseDNWithEntries != null) 1136 { 1137 final Object toSelect = new CategorizedComboBoxElement( 1138 Utilities.unescapeUtf8(baseDNWithEntries.getDn().toString()), CategorizedComboBoxElement.Type.REGULAR); 1139 SwingUtilities.invokeLater(new Runnable() 1140 { 1141 @Override 1142 public void run() 1143 { 1144 // After this updateBrowseController is called. 1145 ignoreBaseDNEvents = true; 1146 baseDNs.setSelectedItem(toSelect); 1147 ignoreBaseDNEvents = false; 1148 } 1149 }); 1150 } 1151 if (getInfo().getServerDescriptor().isAuthenticated()) 1152 { 1153 firstTimeDisplayed = false; 1154 } 1155 } 1156 1157 private boolean hasChanged(final SortedSet<String> newElements, final DefaultComboBoxModel<CharSequence> model) 1158 { 1159 if (newElements.size() != model.getSize() - 2) 1160 { 1161 return true; 1162 } 1163 1164 int i = 0; 1165 for (String newElement : newElements) 1166 { 1167 if (!newElement.equals(model.getElementAt(i))) 1168 { 1169 return true; 1170 } 1171 i++; 1172 } 1173 return false; 1174 } 1175 1176 /** 1177 * Updates the contents of the error pane and the browser controller with the 1178 * provided ServerDescriptor. It checks that the server is running and that we 1179 * are authenticated, that the connection to the server has not changed, etc. 1180 * 1181 * @param desc 1182 * the server descriptor to be used to update the error pane and browser controller. 1183 */ 1184 private void updateBrowserControllerAndErrorPane(ServerDescriptor desc) 1185 { 1186 boolean displayNodes = false; 1187 boolean displayErrorPane = false; 1188 LocalizableMessage errorTitle = LocalizableMessage.EMPTY; 1189 LocalizableMessage errorDetails = LocalizableMessage.EMPTY; 1190 ServerDescriptor.ServerStatus status = desc.getStatus(); 1191 if (status == ServerDescriptor.ServerStatus.STARTED) 1192 { 1193 if (!desc.isAuthenticated()) 1194 { 1195 LocalizableMessageBuilder mb = new LocalizableMessageBuilder(); 1196 mb.append(INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_TO_BROWSE_SUMMARY.get()); 1197 mb.append("<br><br>").append(getAuthenticateHTML()); 1198 errorDetails = mb.toMessage(); 1199 errorTitle = INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_SUMMARY.get(); 1200 1201 displayErrorPane = true; 1202 } 1203 else 1204 { 1205 try 1206 { 1207 InitialLdapContext ctx = getInfo().getDirContext(); 1208 InitialLdapContext ctx1 = controller.getConfigurationConnection(); 1209 boolean setConnection = ctx != ctx1; 1210 updateNumSubordinateHacker(desc); 1211 if (setConnection) 1212 { 1213 if (getInfo().getUserDataDirContext() == null) 1214 { 1215 InitialLdapContext ctxUserData = 1216 createUserDataDirContext(ConnectionUtils.getBindDN(ctx), ConnectionUtils.getBindPassword(ctx)); 1217 getInfo().setUserDataDirContext(ctxUserData); 1218 } 1219 final NamingException[] fNe = { null }; 1220 Runnable runnable = new Runnable() 1221 { 1222 @Override 1223 public void run() 1224 { 1225 try 1226 { 1227 controller.setConnections( 1228 getInfo().getServerDescriptor(), getInfo().getDirContext(), getInfo().getUserDataDirContext()); 1229 applyButtonClicked(); 1230 } 1231 catch (NamingException ne) 1232 { 1233 fNe[0] = ne; 1234 } 1235 } 1236 }; 1237 if (!SwingUtilities.isEventDispatchThread()) 1238 { 1239 try 1240 { 1241 SwingUtilities.invokeAndWait(runnable); 1242 } 1243 catch (Throwable t) {} 1244 } 1245 else 1246 { 1247 runnable.run(); 1248 } 1249 1250 if (fNe[0] != null) 1251 { 1252 throw fNe[0]; 1253 } 1254 } 1255 displayNodes = true; 1256 } 1257 catch (NamingException ne) 1258 { 1259 errorTitle = INFO_CTRL_PANEL_ERROR_CONNECT_BROWSE_DETAILS.get(); 1260 errorDetails = INFO_CTRL_PANEL_ERROR_CONNECT_BROWSE_SUMMARY.get(ne); 1261 displayErrorPane = true; 1262 } 1263 catch (ConfigReadException cre) 1264 { 1265 errorTitle = INFO_CTRL_PANEL_ERROR_CONNECT_BROWSE_DETAILS.get(); 1266 errorDetails = INFO_CTRL_PANEL_ERROR_CONNECT_BROWSE_SUMMARY.get(cre.getMessageObject()); 1267 displayErrorPane = true; 1268 } 1269 } 1270 } 1271 else if (status == ServerDescriptor.ServerStatus.NOT_CONNECTED_TO_REMOTE) 1272 { 1273 LocalizableMessageBuilder mb = new LocalizableMessageBuilder(); 1274 mb.append(INFO_CTRL_PANEL_CANNOT_CONNECT_TO_REMOTE_DETAILS.get(desc.getHostname())); 1275 mb.append("<br><br>").append(getAuthenticateHTML()); 1276 errorDetails = mb.toMessage(); 1277 errorTitle = INFO_CTRL_PANEL_CANNOT_CONNECT_TO_REMOTE_SUMMARY.get(); 1278 displayErrorPane = true; 1279 } 1280 else 1281 { 1282 errorTitle = INFO_CTRL_PANEL_SERVER_NOT_RUNNING_SUMMARY.get(); 1283 LocalizableMessageBuilder mb = new LocalizableMessageBuilder(); 1284 mb.append(INFO_CTRL_PANEL_AUTHENTICATION_SERVER_MUST_RUN_TO_BROWSE_SUMMARY.get()); 1285 mb.append("<br><br>"); 1286 mb.append(getStartServerHTML()); 1287 errorDetails = mb.toMessage(); 1288 displayErrorPane = true; 1289 } 1290 1291 final boolean fDisplayNodes = displayNodes; 1292 final boolean fDisplayErrorPane = displayErrorPane; 1293 final LocalizableMessage fErrorTitle = errorTitle; 1294 final LocalizableMessage fErrorDetails = errorDetails; 1295 SwingUtilities.invokeLater(new Runnable() 1296 { 1297 @Override 1298 public void run() 1299 { 1300 applyButton.setEnabled(!fDisplayErrorPane); 1301 errorPane.setVisible(fDisplayErrorPane); 1302 if (fDisplayErrorPane) 1303 { 1304 updateErrorPane(errorPane, fErrorTitle, 1305 ColorAndFontConstants.errorTitleFont, fErrorDetails, ColorAndFontConstants.defaultFont); 1306 } 1307 else if (fDisplayNodes) 1308 { 1309 // Update the browser controller with the potential new suffixes. 1310 String s = getBaseDN(); 1311 DN theDN = null; 1312 boolean displayAll = false; 1313 if (s != null) 1314 { 1315 displayAll = ALL_BASE_DNS.equals(s); 1316 if (!displayAll) 1317 { 1318 try 1319 { 1320 theDN = DN.valueOf(s); 1321 } 1322 catch (Throwable t) 1323 { 1324 s = null; 1325 } 1326 } 1327 } 1328 treePane.getTree().setRootVisible(displayAll); 1329 treePane.getTree().setShowsRootHandles(!displayAll); 1330 if (s != null) 1331 { 1332 boolean added = false; 1333 for (BackendDescriptor backend : getInfo().getServerDescriptor().getBackends()) 1334 { 1335 for (BaseDNDescriptor baseDN : backend.getBaseDns()) 1336 { 1337 boolean isBaseDN = false; 1338 String dn = Utilities.unescapeUtf8(baseDN.getDn().toString()); 1339 if (theDN != null && baseDN.getDn().equals(theDN)) 1340 { 1341 isBaseDN = true; 1342 } 1343 if (baseDN.getEntries() > 0) 1344 { 1345 try 1346 { 1347 if ((displayAll || isBaseDN) && !controller.hasSuffix(dn)) 1348 { 1349 controller.addSuffix(dn, null); 1350 added = true; 1351 } 1352 } 1353 catch (IllegalArgumentException iae) 1354 { 1355 // The suffix node exists but is not a suffix node. Simply log a message. 1356 logger.warn(LocalizableMessage.raw( 1357 "Suffix: " + dn + " added as a non suffix node. Exception: " + iae, iae)); 1358 } 1359 } 1360 } 1361 if (!added && !displayAll) 1362 { 1363 BasicNode rootNode = (BasicNode) controller.getTree().getModel().getRoot(); 1364 if (controller.findChildNode(rootNode, s) == -1) 1365 { 1366 controller.addNodeUnderRoot(s); 1367 } 1368 } 1369 } 1370 } 1371 } 1372 1373 if (!fDisplayNodes) 1374 { 1375 controller.removeAllUnderRoot(); 1376 treePane.getTree().setRootVisible(false); 1377 } 1378 } 1379 }); 1380 } 1381 1382 /** 1383 * Returns the base DN specified by the user. 1384 * 1385 * @return the base DN specified by the user. 1386 */ 1387 private String getBaseDN() 1388 { 1389 String dn = getBaseDN0(); 1390 if (dn != null && dn.trim().length() == 0) 1391 { 1392 dn = ALL_BASE_DNS; 1393 } 1394 return dn; 1395 } 1396 1397 private String getBaseDN0() 1398 { 1399 Object o = baseDNs.getSelectedItem(); 1400 if (o instanceof String) 1401 { 1402 return (String) o; 1403 } 1404 else if (o instanceof CategorizedComboBoxElement) 1405 { 1406 return ((CategorizedComboBoxElement) o).getValue().toString(); 1407 } 1408 else 1409 { 1410 return null; 1411 } 1412 } 1413 1414 /** 1415 * Creates the context to be used to retrieve user data for some given 1416 * credentials. 1417 * 1418 * @param bindDN 1419 * the bind DN. 1420 * @param bindPassword 1421 * the bind password. 1422 * @return the context to be used to retrieve user data for some given 1423 * credentials. 1424 * @throws NamingException 1425 * if an error occurs connecting to the server. 1426 * @throws ConfigReadException 1427 * if an error occurs reading the configuration. 1428 */ 1429 private InitialLdapContext createUserDataDirContext(final String bindDN, final String bindPassword) 1430 throws NamingException, ConfigReadException 1431 { 1432 createdUserDataCtx = null; 1433 try 1434 { 1435 createdUserDataCtx = Utilities.getUserDataDirContext(getInfo(), bindDN, bindPassword); 1436 } 1437 catch (NamingException ne) 1438 { 1439 if (!isCertificateException(ne)) 1440 { 1441 throw ne; 1442 } 1443 1444 ApplicationTrustManager.Cause cause = getInfo().getTrustManager().getLastRefusedCause(); 1445 1446 logger.info(LocalizableMessage.raw("Certificate exception cause: " + cause)); 1447 UserDataCertificateException.Type excType = null; 1448 if (cause == ApplicationTrustManager.Cause.NOT_TRUSTED) 1449 { 1450 excType = UserDataCertificateException.Type.NOT_TRUSTED; 1451 } 1452 else if (cause == ApplicationTrustManager.Cause.HOST_NAME_MISMATCH) 1453 { 1454 excType = UserDataCertificateException.Type.HOST_NAME_MISMATCH; 1455 } 1456 1457 if (excType != null) 1458 { 1459 String h; 1460 int p; 1461 try 1462 { 1463 URI uri = new URI(getInfo().getAdminConnectorURL()); 1464 h = uri.getHost(); 1465 p = uri.getPort(); 1466 } 1467 catch (Throwable t) 1468 { 1469 logger.warn(LocalizableMessage.raw("Error parsing ldap url of ldap url.", t)); 1470 h = INFO_NOT_AVAILABLE_LABEL.get().toString(); 1471 p = -1; 1472 } 1473 final UserDataCertificateException udce = new UserDataCertificateException( 1474 null, INFO_CERTIFICATE_EXCEPTION.get(h, p), ne, h, p, getInfo().getTrustManager().getLastRefusedChain(), 1475 getInfo().getTrustManager().getLastRefusedAuthType(), excType); 1476 1477 if (SwingUtilities.isEventDispatchThread()) 1478 { 1479 handleCertificateException(udce, bindDN, bindPassword); 1480 } 1481 else 1482 { 1483 final ConfigReadException[] fcre = { null }; 1484 final NamingException[] fne = { null }; 1485 try 1486 { 1487 SwingUtilities.invokeAndWait(new Runnable() 1488 { 1489 @Override 1490 public void run() 1491 { 1492 try 1493 { 1494 handleCertificateException(udce, bindDN, bindPassword); 1495 } 1496 catch (ConfigReadException cre) 1497 { 1498 fcre[0] = cre; 1499 } 1500 catch (NamingException ne) 1501 { 1502 fne[0] = ne; 1503 } 1504 } 1505 }); 1506 } 1507 catch (Exception e) 1508 { 1509 throw new IllegalArgumentException("Unexpected error: " + e, e); 1510 } 1511 if (fcre[0] != null) 1512 { 1513 throw fcre[0]; 1514 } 1515 if (fne[0] != null) 1516 { 1517 throw fne[0]; 1518 } 1519 } 1520 } 1521 } 1522 return createdUserDataCtx; 1523 } 1524 1525 /** 1526 * Displays a dialog asking the user to accept a certificate if the user 1527 * accepts it, we update the trust manager and simulate a click on "OK" to 1528 * re-check the authentication. This method assumes that we are being called 1529 * from the event thread. 1530 * 1531 * @param bindDN 1532 * the bind DN. 1533 * @param bindPassword 1534 * the bind password. 1535 */ 1536 private void handleCertificateException(UserDataCertificateException ce, String bindDN, String bindPassword) 1537 throws NamingException, ConfigReadException 1538 { 1539 CertificateDialog dlg = new CertificateDialog(null, ce); 1540 dlg.pack(); 1541 Utilities.centerGoldenMean(dlg, Utilities.getParentDialog(this)); 1542 dlg.setVisible(true); 1543 if (dlg.getUserAnswer() != CertificateDialog.ReturnType.NOT_ACCEPTED) 1544 { 1545 X509Certificate[] chain = ce.getChain(); 1546 String authType = ce.getAuthType(); 1547 String host = ce.getHost(); 1548 1549 if (chain != null && authType != null && host != null) 1550 { 1551 logger.info(LocalizableMessage.raw("Accepting certificate presented by host " + host)); 1552 getInfo().getTrustManager().acceptCertificate(chain, authType, host); 1553 createdUserDataCtx = createUserDataDirContext(bindDN, bindPassword); 1554 } 1555 else 1556 { 1557 if (chain == null) 1558 { 1559 logger.warn(LocalizableMessage.raw("The chain is null for the UserDataCertificateException")); 1560 } 1561 if (authType == null) 1562 { 1563 logger.warn(LocalizableMessage.raw("The auth type is null for the UserDataCertificateException")); 1564 } 1565 if (host == null) 1566 { 1567 logger.warn(LocalizableMessage.raw("The host is null for the UserDataCertificateException")); 1568 } 1569 } 1570 } 1571 if (dlg.getUserAnswer() == CertificateDialog.ReturnType.ACCEPTED_PERMANENTLY) 1572 { 1573 X509Certificate[] chain = ce.getChain(); 1574 if (chain != null) 1575 { 1576 try 1577 { 1578 UIKeyStore.acceptCertificate(chain); 1579 } 1580 catch (Throwable t) 1581 { 1582 logger.warn(LocalizableMessage.raw("Error accepting certificate: " + t, t)); 1583 } 1584 } 1585 } 1586 } 1587 1588 /** 1589 * This class is used simply to avoid an inset on the left for the 'All Base 1590 * DNs' item. Since this item is a CategorizedComboBoxElement of type 1591 * CategorizedComboBoxElement.Type.REGULAR, it has by default an inset on the 1592 * left. The class simply handles this particular case to not to have that 1593 * inset for the 'All Base DNs' item. 1594 */ 1595 class CustomComboBoxCellRenderer extends CustomListCellRenderer 1596 { 1597 private LocalizableMessage ALL_BASE_DNS_STRING = INFO_CTRL_PANEL_ALL_BASE_DNS.get(); 1598 1599 /** 1600 * The constructor. 1601 * 1602 * @param combo 1603 * the combo box to be rendered. 1604 */ 1605 CustomComboBoxCellRenderer(JComboBox<?> combo) 1606 { 1607 super(combo); 1608 } 1609 1610 @Override 1611 @SuppressWarnings("rawtypes") 1612 public Component getListCellRendererComponent( 1613 JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) 1614 { 1615 Component comp = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 1616 if (value instanceof CategorizedComboBoxElement) 1617 { 1618 CategorizedComboBoxElement element = (CategorizedComboBoxElement) value; 1619 String name = getStringValue(element); 1620 if (ALL_BASE_DNS.equals(name)) 1621 { 1622 ((JLabel) comp).setText(ALL_BASE_DNS_STRING.toString()); 1623 } 1624 } 1625 comp.setFont(defaultFont); 1626 return comp; 1627 } 1628 } 1629 1630 /** 1631 * Checks that the root node has some children. It it has no children the 1632 * message 'No Match Found' is displayed instead of the tree panel. 1633 */ 1634 private void checkRootNode() 1635 { 1636 DefaultMutableTreeNode root = (DefaultMutableTreeNode) controller.getTreeModel().getRoot(); 1637 boolean visible = root.getChildCount() > 0; 1638 if (visible != treePane.isVisible()) 1639 { 1640 treePane.setVisible(visible); 1641 lNoMatchFound.setVisible(!visible); 1642 lNumberOfEntries.setVisible(visible); 1643 } 1644 numberEntriesUpdater.recalculate(); 1645 } 1646 1647 /** 1648 * Updates the NumsubordinateHacker of the browser controller with the 1649 * provided server descriptor. 1650 * 1651 * @param server 1652 * the server descriptor. 1653 */ 1654 private void updateNumSubordinateHacker(ServerDescriptor server) 1655 { 1656 String serverHost = server.getHostname(); 1657 int serverPort = server.getAdminConnector().getPort(); 1658 1659 List<DN> allSuffixes = new ArrayList<>(); 1660 for (BackendDescriptor backend : server.getBackends()) 1661 { 1662 for (BaseDNDescriptor baseDN : backend.getBaseDns()) 1663 { 1664 allSuffixes.add(baseDN.getDn()); 1665 } 1666 } 1667 1668 List<DN> rootSuffixes = new ArrayList<>(); 1669 for (DN dn : allSuffixes) 1670 { 1671 if (isRootSuffix(allSuffixes, dn)) 1672 { 1673 rootSuffixes.add(dn); 1674 } 1675 } 1676 controller.getNumSubordinateHacker().update(allSuffixes, rootSuffixes, serverHost, serverPort); 1677 } 1678 1679 private boolean isRootSuffix(List<DN> allSuffixes, DN dn) 1680 { 1681 for (DN suffix : allSuffixes) 1682 { 1683 if (suffix.isAncestorOf(dn) && !suffix.equals(dn)) 1684 { 1685 return false; 1686 } 1687 } 1688 return true; 1689 } 1690 1691 /** 1692 * This is a class that simply checks the number of entries that the browser 1693 * contains and updates a counter with the new number of entries. It is 1694 * basically a thread that sleeps and checks whether some calculation must be 1695 * made: when we know that something is updated in the browser the method 1696 * recalculate() is called. We could use a more sophisticated code (like use a 1697 * wait() call that would get notified when recalculate() is called) but this 1698 * is not required and it might have an impact on the reactivity of the UI if 1699 * recalculate gets called too often. We can afford to wait 400 miliseconds 1700 * before updating the number of entries and with this approach there is 1701 * hardly no impact on the reactivity of the UI. 1702 */ 1703 protected class NumberOfEntriesUpdater extends Thread 1704 { 1705 private boolean recalculate; 1706 1707 /** Notifies that the number of entries in the browser has changed. */ 1708 public void recalculate() 1709 { 1710 recalculate = true; 1711 } 1712 1713 /** Executes the updater. */ 1714 @Override 1715 public void run() 1716 { 1717 while (true) 1718 { 1719 try 1720 { 1721 Thread.sleep(400); 1722 } 1723 catch (Throwable t) 1724 { 1725 } 1726 if (recalculate) 1727 { 1728 recalculate = false; 1729 SwingUtilities.invokeLater(new Runnable() 1730 { 1731 @Override 1732 public void run() 1733 { 1734 int nEntries = 0; 1735 // This recursive algorithm is fast enough to use it on the 1736 // event thread. Running it here we avoid issues with concurrent 1737 // access to the node children 1738 if (controller.getTree().isRootVisible()) 1739 { 1740 nEntries++; 1741 } 1742 DefaultMutableTreeNode root = (DefaultMutableTreeNode) controller.getTreeModel().getRoot(); 1743 1744 nEntries += getChildren(root); 1745 lNumberOfEntries.setText(INFO_CTRL_BROWSER_NUMBER_OF_ENTRIES.get(nEntries).toString()); 1746 } 1747 }); 1748 } 1749 if (controller != null) 1750 { 1751 final boolean mustDisplayRefreshIcon = controller.getQueueSize() > 0; 1752 if (mustDisplayRefreshIcon != filter.isRefreshIconDisplayed()) 1753 { 1754 SwingUtilities.invokeLater(new Runnable() 1755 { 1756 @Override 1757 public void run() 1758 { 1759 filter.displayRefreshIcon(mustDisplayRefreshIcon); 1760 } 1761 }); 1762 } 1763 } 1764 } 1765 } 1766 1767 /** 1768 * Returns the number of children for a given node. 1769 * 1770 * @param node 1771 * the node. 1772 * @return the number of children for the node. 1773 */ 1774 private int getChildren(DefaultMutableTreeNode node) 1775 { 1776 int nEntries = 0; 1777 1778 if (!node.isLeaf()) 1779 { 1780 Enumeration<?> en = node.children(); 1781 while (en.hasMoreElements()) 1782 { 1783 nEntries++; 1784 nEntries += getChildren((DefaultMutableTreeNode) en.nextElement()); 1785 } 1786 } 1787 return nEntries; 1788 } 1789 } 1790}