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 ByteString filterBytes = ByteString.valueOfUtf8(filterText); 777 return new LDAPFilter(SearchFilter.createEqualityFilter(attrType, filterBytes)).toString(); 778 } 779 } 780 else 781 { 782 return BrowserController.ALL_OBJECTS_FILTER; 783 } 784 } 785 786 /** 787 * Returns the component that will be displayed between the filtering options 788 * and the buttons panel. This component must contain the tree panel. 789 * 790 * @return the component that will be displayed between the filtering options 791 * and the buttons panel. 792 */ 793 protected abstract Component createMainPanel(); 794 795 /** {@inheritDoc} */ 796 @Override 797 public void backendPopulated(BackendPopulatedEvent ev) 798 { 799 if (controller.getConfigurationConnection() != null) 800 { 801 boolean displayAll = false; 802 boolean errorOccurred = false; 803 DN theDN = null; 804 String s = getBaseDN(); 805 if (s != null) 806 { 807 displayAll = ALL_BASE_DNS.equals(s); 808 if (!displayAll) 809 { 810 try 811 { 812 theDN = DN.valueOf(s); 813 } 814 catch (Throwable t) 815 { 816 errorOccurred = true; 817 } 818 } 819 } 820 else 821 { 822 errorOccurred = true; 823 } 824 if (!errorOccurred) 825 { 826 treePane.getTree().setRootVisible(displayAll); 827 treePane.getTree().setShowsRootHandles(!displayAll); 828 BasicNode rootNode = (BasicNode) controller.getTree().getModel().getRoot(); 829 boolean isSubordinate = false; 830 for (BackendDescriptor backend : ev.getBackends()) 831 { 832 for (BaseDNDescriptor baseDN : backend.getBaseDns()) 833 { 834 boolean isBaseDN = false; 835 if (baseDN.getDn().equals(theDN)) 836 { 837 isBaseDN = true; 838 } 839 else if (baseDN.getDn().isAncestorOf(theDN)) 840 { 841 isSubordinate = true; 842 } 843 String dn = Utilities.unescapeUtf8(baseDN.getDn().toString()); 844 if (displayAll || isBaseDN) 845 { 846 try 847 { 848 if (!controller.hasSuffix(dn)) 849 { 850 controller.addSuffix(dn, null); 851 } 852 else 853 { 854 int index = controller.findChildNode(rootNode, dn); 855 if (index >= 0) 856 { 857 TreeNode node = rootNode.getChildAt(index); 858 if (node != null) 859 { 860 TreePath path = new TreePath(controller.getTreeModel().getPathToRoot(node)); 861 controller.startRefresh(controller.getNodeInfoFromPath(path)); 862 } 863 } 864 } 865 } 866 catch (IllegalArgumentException iae) 867 { 868 // The suffix node exists but is not a suffix node. Simply log a message. 869 logger.warn( 870 LocalizableMessage.raw("Suffix: " + dn + " added as a non suffix node. Exception: " + iae, iae)); 871 } 872 } 873 } 874 } 875 if (isSubordinate && controller.findChildNode(rootNode, s) == -1) 876 { 877 controller.addNodeUnderRoot(s); 878 } 879 } 880 } 881 } 882 883 @Override 884 public void configurationChanged(ConfigurationChangeEvent ev) 885 { 886 final ServerDescriptor desc = ev.getNewDescriptor(); 887 888 updateCombos(desc); 889 updateBrowserControllerAndErrorPane(desc); 890 } 891 892 /** 893 * Creates and returns the tree panel. 894 * 895 * @return the tree panel. 896 */ 897 protected JComponent createTreePane() 898 { 899 treePane = new TreePanel(); 900 901 lNoMatchFound = Utilities.createDefaultLabel(INFO_CTRL_PANEL_NO_MATCHES_FOUND_LABEL.get()); 902 lNoMatchFound.setVisible(false); 903 904 // Calculate default size 905 JTree tree = treePane.getTree(); 906 DefaultMutableTreeNode root = new DefaultMutableTreeNode("myserver.mydomain.com:389"); 907 DefaultTreeModel model = new DefaultTreeModel(root); 908 tree.setModel(model); 909 tree.setShowsRootHandles(false); 910 tree.expandPath(new TreePath(root)); 911 JPanel p = new JPanel(new GridBagLayout()); 912 p.setBackground(ColorAndFontConstants.background); 913 GridBagConstraints gbc = new GridBagConstraints(); 914 gbc.gridx = 0; 915 gbc.gridy = 0; 916 gbc.gridwidth = 1; 917 gbc.anchor = GridBagConstraints.NORTHWEST; 918 gbc.fill = GridBagConstraints.BOTH; 919 gbc.weightx = 1.0; 920 gbc.weighty = 1.0; 921 Utilities.setBorder(treePane, new EmptyBorder(10, 0, 10, 0)); 922 p.add(treePane, gbc); 923 gbc.fill = GridBagConstraints.HORIZONTAL; 924 Utilities.setBorder(lNoMatchFound, new EmptyBorder(15, 15, 15, 15)); 925 p.add(lNoMatchFound, gbc); 926 927 if (getInfo() != null && controller == null) 928 { 929 createBrowserController(getInfo()); 930 } 931 numberEntriesUpdater = new NumberOfEntriesUpdater(); 932 numberEntriesUpdater.start(); 933 934 return p; 935 } 936 937 /** 938 * Creates the browser controller object. 939 * 940 * @param info 941 * the ControlPanelInfo to be used to create the browser controller. 942 */ 943 protected void createBrowserController(ControlPanelInfo info) 944 { 945 controller = new BrowserController(treePane.getTree(), info.getConnectionPool(), info.getIconPool()); 946 controller.setContainerClasses(CONTAINER_CLASSES); 947 controller.setShowContainerOnly(false); 948 controller.setMaxChildren(MAX_NUMBER_ENTRIES); 949 controller.addBrowserEventListener(new BrowserEventListener() 950 { 951 /** {@inheritDoc} */ 952 @Override 953 public void processBrowserEvent(BrowserEvent ev) 954 { 955 if (ev.getType() == BrowserEvent.Type.SIZE_LIMIT_REACHED) 956 { 957 lLimit.setVisible(true); 958 lNumberOfEntries.setVisible(false); 959 } 960 } 961 }); 962 controller.getTreeModel().addTreeModelListener(new TreeModelListener() 963 { 964 @Override 965 public void treeNodesChanged(TreeModelEvent e) 966 { 967 } 968 969 @Override 970 public void treeNodesInserted(TreeModelEvent e) 971 { 972 checkRootNode(); 973 } 974 975 @Override 976 public void treeNodesRemoved(TreeModelEvent e) 977 { 978 checkRootNode(); 979 } 980 981 @Override 982 public void treeStructureChanged(TreeModelEvent e) 983 { 984 checkRootNode(); 985 } 986 }); 987 } 988 989 990 private static boolean displayIndex(String name) 991 { 992 for (String systemIndex : SYSTEM_INDEXES) 993 { 994 if (systemIndex.equalsIgnoreCase(name)) 995 { 996 return false; 997 } 998 } 999 return true; 1000 } 1001 1002 /** 1003 * Updates the contents of the combo boxes with the provided ServerDescriptor. 1004 * 1005 * @param desc 1006 * the server descriptor to be used to update the combo boxes. 1007 */ 1008 @SuppressWarnings("rawtypes") 1009 private void updateCombos(ServerDescriptor desc) 1010 { 1011 final SortedSet<String> newElements = new TreeSet<>(); 1012 for (BackendDescriptor backend : desc.getBackends()) 1013 { 1014 for (IndexDescriptor index : backend.getIndexes()) 1015 { 1016 String indexName = index.getName(); 1017 if (displayIndex(indexName)) 1018 { 1019 newElements.add(indexName); 1020 } 1021 } 1022 } 1023 1024 @SuppressWarnings("unchecked") 1025 final DefaultComboBoxModel<CharSequence> model = (DefaultComboBoxModel<CharSequence>) filterAttribute.getModel(); 1026 if (hasChanged(newElements, model)) 1027 { 1028 SwingUtilities.invokeLater(new Runnable() 1029 { 1030 @Override 1031 public void run() 1032 { 1033 Object selected = filterAttribute.getSelectedItem(); 1034 model.removeAllElements(); 1035 model.addElement(USER_FILTER); 1036 model.addElement(GROUP_FILTER); 1037 model.addElement(COMBO_SEPARATOR); 1038 for (String newElement : newElements) 1039 { 1040 model.addElement(newElement); 1041 } 1042 // If there are not backends, we get no indexes to set. 1043 if (!newElements.isEmpty()) 1044 { 1045 model.addElement(COMBO_SEPARATOR); 1046 } 1047 model.addElement(LDAP_FILTER); 1048 if (selected != null) 1049 { 1050 if (model.getIndexOf(selected) != -1) 1051 { 1052 model.setSelectedItem(selected); 1053 } 1054 else 1055 { 1056 model.setSelectedItem(model.getElementAt(0)); 1057 } 1058 } 1059 } 1060 }); 1061 } 1062 1063 Set<Object> baseDNNewElements = new LinkedHashSet<>(); 1064 SortedSet<String> backendIDs = new TreeSet<>(); 1065 Map<String, SortedSet<String>> hmBaseDNs = new HashMap<>(); 1066 1067 Map<String, BaseDNDescriptor> hmBaseDNWithEntries = new HashMap<>(); 1068 1069 BaseDNDescriptor baseDNWithEntries = null; 1070 for (BackendDescriptor backend : desc.getBackends()) 1071 { 1072 if (displayBackend(backend)) 1073 { 1074 String backendID = backend.getBackendID(); 1075 backendIDs.add(backendID); 1076 SortedSet<String> someBaseDNs = new TreeSet<>(); 1077 for (BaseDNDescriptor baseDN : backend.getBaseDns()) 1078 { 1079 try 1080 { 1081 someBaseDNs.add(Utilities.unescapeUtf8(baseDN.getDn().toString())); 1082 } 1083 catch (Throwable t) 1084 { 1085 throw new RuntimeException("Unexpected error: " + t, t); 1086 } 1087 if (baseDN.getEntries() > 0) 1088 { 1089 hmBaseDNWithEntries.put(Utilities.unescapeUtf8(baseDN.getDn().toString()), baseDN); 1090 } 1091 } 1092 hmBaseDNs.put(backendID, someBaseDNs); 1093 if ("userRoot".equalsIgnoreCase(backendID)) 1094 { 1095 for (String baseDN : someBaseDNs) 1096 { 1097 baseDNWithEntries = hmBaseDNWithEntries.get(baseDN); 1098 if (baseDNWithEntries != null) 1099 { 1100 break; 1101 } 1102 } 1103 } 1104 } 1105 } 1106 1107 baseDNNewElements.add(new CategorizedComboBoxElement(ALL_BASE_DNS, CategorizedComboBoxElement.Type.REGULAR)); 1108 for (String backendID : backendIDs) 1109 { 1110 baseDNNewElements.add(new CategorizedComboBoxElement(backendID, CategorizedComboBoxElement.Type.CATEGORY)); 1111 SortedSet<String> someBaseDNs = hmBaseDNs.get(backendID); 1112 for (String baseDN : someBaseDNs) 1113 { 1114 baseDNNewElements.add(new CategorizedComboBoxElement(baseDN, CategorizedComboBoxElement.Type.REGULAR)); 1115 if (baseDNWithEntries == null) 1116 { 1117 baseDNWithEntries = hmBaseDNWithEntries.get(baseDN); 1118 } 1119 } 1120 } 1121 for (DN dn : otherBaseDns) 1122 { 1123 baseDNNewElements.add(COMBO_SEPARATOR); 1124 baseDNNewElements.add(new CategorizedComboBoxElement( 1125 Utilities.unescapeUtf8(dn.toString()), CategorizedComboBoxElement.Type.REGULAR)); 1126 } 1127 baseDNNewElements.add(COMBO_SEPARATOR); 1128 baseDNNewElements.add(OTHER_BASE_DN); 1129 1130 if (firstTimeDisplayed && baseDNWithEntries != null) 1131 { 1132 ignoreBaseDNEvents = true; 1133 } 1134 updateComboBoxModel(baseDNNewElements, (DefaultComboBoxModel) baseDNs.getModel()); 1135 // Select the element in the combo box. 1136 if (firstTimeDisplayed && baseDNWithEntries != null) 1137 { 1138 final Object toSelect = new CategorizedComboBoxElement( 1139 Utilities.unescapeUtf8(baseDNWithEntries.getDn().toString()), CategorizedComboBoxElement.Type.REGULAR); 1140 SwingUtilities.invokeLater(new Runnable() 1141 { 1142 @Override 1143 public void run() 1144 { 1145 // After this updateBrowseController is called. 1146 ignoreBaseDNEvents = true; 1147 baseDNs.setSelectedItem(toSelect); 1148 ignoreBaseDNEvents = false; 1149 } 1150 }); 1151 } 1152 if (getInfo().getServerDescriptor().isAuthenticated()) 1153 { 1154 firstTimeDisplayed = false; 1155 } 1156 } 1157 1158 private boolean hasChanged(final SortedSet<String> newElements, final DefaultComboBoxModel<CharSequence> model) 1159 { 1160 if (newElements.size() != model.getSize() - 2) 1161 { 1162 return true; 1163 } 1164 1165 int i = 0; 1166 for (String newElement : newElements) 1167 { 1168 if (!newElement.equals(model.getElementAt(i))) 1169 { 1170 return true; 1171 } 1172 i++; 1173 } 1174 return false; 1175 } 1176 1177 /** 1178 * Updates the contents of the error pane and the browser controller with the 1179 * provided ServerDescriptor. It checks that the server is running and that we 1180 * are authenticated, that the connection to the server has not changed, etc. 1181 * 1182 * @param desc 1183 * the server descriptor to be used to update the error pane and browser controller. 1184 */ 1185 private void updateBrowserControllerAndErrorPane(ServerDescriptor desc) 1186 { 1187 boolean displayNodes = false; 1188 boolean displayErrorPane = false; 1189 LocalizableMessage errorTitle = LocalizableMessage.EMPTY; 1190 LocalizableMessage errorDetails = LocalizableMessage.EMPTY; 1191 ServerDescriptor.ServerStatus status = desc.getStatus(); 1192 if (status == ServerDescriptor.ServerStatus.STARTED) 1193 { 1194 if (!desc.isAuthenticated()) 1195 { 1196 LocalizableMessageBuilder mb = new LocalizableMessageBuilder(); 1197 mb.append(INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_TO_BROWSE_SUMMARY.get()); 1198 mb.append("<br><br>").append(getAuthenticateHTML()); 1199 errorDetails = mb.toMessage(); 1200 errorTitle = INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_SUMMARY.get(); 1201 1202 displayErrorPane = true; 1203 } 1204 else 1205 { 1206 try 1207 { 1208 InitialLdapContext ctx = getInfo().getDirContext(); 1209 InitialLdapContext ctx1 = controller.getConfigurationConnection(); 1210 boolean setConnection = ctx != ctx1; 1211 updateNumSubordinateHacker(desc); 1212 if (setConnection) 1213 { 1214 if (getInfo().getUserDataDirContext() == null) 1215 { 1216 InitialLdapContext ctxUserData = 1217 createUserDataDirContext(ConnectionUtils.getBindDN(ctx), ConnectionUtils.getBindPassword(ctx)); 1218 getInfo().setUserDataDirContext(ctxUserData); 1219 } 1220 final NamingException[] fNe = { null }; 1221 Runnable runnable = new Runnable() 1222 { 1223 @Override 1224 public void run() 1225 { 1226 try 1227 { 1228 controller.setConnections( 1229 getInfo().getServerDescriptor(), getInfo().getDirContext(), getInfo().getUserDataDirContext()); 1230 applyButtonClicked(); 1231 } 1232 catch (NamingException ne) 1233 { 1234 fNe[0] = ne; 1235 } 1236 } 1237 }; 1238 if (!SwingUtilities.isEventDispatchThread()) 1239 { 1240 try 1241 { 1242 SwingUtilities.invokeAndWait(runnable); 1243 } 1244 catch (Throwable t) {} 1245 } 1246 else 1247 { 1248 runnable.run(); 1249 } 1250 1251 if (fNe[0] != null) 1252 { 1253 throw fNe[0]; 1254 } 1255 } 1256 displayNodes = true; 1257 } 1258 catch (NamingException ne) 1259 { 1260 errorTitle = INFO_CTRL_PANEL_ERROR_CONNECT_BROWSE_DETAILS.get(); 1261 errorDetails = INFO_CTRL_PANEL_ERROR_CONNECT_BROWSE_SUMMARY.get(ne); 1262 displayErrorPane = true; 1263 } 1264 catch (ConfigReadException cre) 1265 { 1266 errorTitle = INFO_CTRL_PANEL_ERROR_CONNECT_BROWSE_DETAILS.get(); 1267 errorDetails = INFO_CTRL_PANEL_ERROR_CONNECT_BROWSE_SUMMARY.get(cre.getMessageObject()); 1268 displayErrorPane = true; 1269 } 1270 } 1271 } 1272 else if (status == ServerDescriptor.ServerStatus.NOT_CONNECTED_TO_REMOTE) 1273 { 1274 LocalizableMessageBuilder mb = new LocalizableMessageBuilder(); 1275 mb.append(INFO_CTRL_PANEL_CANNOT_CONNECT_TO_REMOTE_DETAILS.get(desc.getHostname())); 1276 mb.append("<br><br>").append(getAuthenticateHTML()); 1277 errorDetails = mb.toMessage(); 1278 errorTitle = INFO_CTRL_PANEL_CANNOT_CONNECT_TO_REMOTE_SUMMARY.get(); 1279 displayErrorPane = true; 1280 } 1281 else 1282 { 1283 errorTitle = INFO_CTRL_PANEL_SERVER_NOT_RUNNING_SUMMARY.get(); 1284 LocalizableMessageBuilder mb = new LocalizableMessageBuilder(); 1285 mb.append(INFO_CTRL_PANEL_AUTHENTICATION_SERVER_MUST_RUN_TO_BROWSE_SUMMARY.get()); 1286 mb.append("<br><br>"); 1287 mb.append(getStartServerHTML()); 1288 errorDetails = mb.toMessage(); 1289 displayErrorPane = true; 1290 } 1291 1292 final boolean fDisplayNodes = displayNodes; 1293 final boolean fDisplayErrorPane = displayErrorPane; 1294 final LocalizableMessage fErrorTitle = errorTitle; 1295 final LocalizableMessage fErrorDetails = errorDetails; 1296 SwingUtilities.invokeLater(new Runnable() 1297 { 1298 @Override 1299 public void run() 1300 { 1301 applyButton.setEnabled(!fDisplayErrorPane); 1302 errorPane.setVisible(fDisplayErrorPane); 1303 if (fDisplayErrorPane) 1304 { 1305 updateErrorPane(errorPane, fErrorTitle, 1306 ColorAndFontConstants.errorTitleFont, fErrorDetails, ColorAndFontConstants.defaultFont); 1307 } 1308 else if (fDisplayNodes) 1309 { 1310 // Update the browser controller with the potential new suffixes. 1311 String s = getBaseDN(); 1312 DN theDN = null; 1313 boolean displayAll = false; 1314 if (s != null) 1315 { 1316 displayAll = ALL_BASE_DNS.equals(s); 1317 if (!displayAll) 1318 { 1319 try 1320 { 1321 theDN = DN.valueOf(s); 1322 } 1323 catch (Throwable t) 1324 { 1325 s = null; 1326 } 1327 } 1328 } 1329 treePane.getTree().setRootVisible(displayAll); 1330 treePane.getTree().setShowsRootHandles(!displayAll); 1331 if (s != null) 1332 { 1333 boolean added = false; 1334 for (BackendDescriptor backend : getInfo().getServerDescriptor().getBackends()) 1335 { 1336 for (BaseDNDescriptor baseDN : backend.getBaseDns()) 1337 { 1338 boolean isBaseDN = false; 1339 String dn = Utilities.unescapeUtf8(baseDN.getDn().toString()); 1340 if (theDN != null && baseDN.getDn().equals(theDN)) 1341 { 1342 isBaseDN = true; 1343 } 1344 if (baseDN.getEntries() > 0) 1345 { 1346 try 1347 { 1348 if ((displayAll || isBaseDN) && !controller.hasSuffix(dn)) 1349 { 1350 controller.addSuffix(dn, null); 1351 added = true; 1352 } 1353 } 1354 catch (IllegalArgumentException iae) 1355 { 1356 // The suffix node exists but is not a suffix node. Simply log a message. 1357 logger.warn(LocalizableMessage.raw( 1358 "Suffix: " + dn + " added as a non suffix node. Exception: " + iae, iae)); 1359 } 1360 } 1361 } 1362 if (!added && !displayAll) 1363 { 1364 BasicNode rootNode = (BasicNode) controller.getTree().getModel().getRoot(); 1365 if (controller.findChildNode(rootNode, s) == -1) 1366 { 1367 controller.addNodeUnderRoot(s); 1368 } 1369 } 1370 } 1371 } 1372 } 1373 1374 if (!fDisplayNodes) 1375 { 1376 controller.removeAllUnderRoot(); 1377 treePane.getTree().setRootVisible(false); 1378 } 1379 } 1380 }); 1381 } 1382 1383 /** 1384 * Returns the base DN specified by the user. 1385 * 1386 * @return the base DN specified by the user. 1387 */ 1388 private String getBaseDN() 1389 { 1390 String dn = getBaseDN0(); 1391 if (dn != null && dn.trim().length() == 0) 1392 { 1393 dn = ALL_BASE_DNS; 1394 } 1395 return dn; 1396 } 1397 1398 private String getBaseDN0() 1399 { 1400 Object o = baseDNs.getSelectedItem(); 1401 if (o instanceof String) 1402 { 1403 return (String) o; 1404 } 1405 else if (o instanceof CategorizedComboBoxElement) 1406 { 1407 return ((CategorizedComboBoxElement) o).getValue().toString(); 1408 } 1409 else 1410 { 1411 return null; 1412 } 1413 } 1414 1415 /** 1416 * Creates the context to be used to retrieve user data for some given 1417 * credentials. 1418 * 1419 * @param bindDN 1420 * the bind DN. 1421 * @param bindPassword 1422 * the bind password. 1423 * @return the context to be used to retrieve user data for some given 1424 * credentials. 1425 * @throws NamingException 1426 * if an error occurs connecting to the server. 1427 * @throws ConfigReadException 1428 * if an error occurs reading the configuration. 1429 */ 1430 private InitialLdapContext createUserDataDirContext(final String bindDN, final String bindPassword) 1431 throws NamingException, ConfigReadException 1432 { 1433 createdUserDataCtx = null; 1434 try 1435 { 1436 createdUserDataCtx = Utilities.getUserDataDirContext(getInfo(), bindDN, bindPassword); 1437 } 1438 catch (NamingException ne) 1439 { 1440 if (!isCertificateException(ne)) 1441 { 1442 throw ne; 1443 } 1444 1445 ApplicationTrustManager.Cause cause = getInfo().getTrustManager().getLastRefusedCause(); 1446 1447 logger.info(LocalizableMessage.raw("Certificate exception cause: " + cause)); 1448 UserDataCertificateException.Type excType = null; 1449 if (cause == ApplicationTrustManager.Cause.NOT_TRUSTED) 1450 { 1451 excType = UserDataCertificateException.Type.NOT_TRUSTED; 1452 } 1453 else if (cause == ApplicationTrustManager.Cause.HOST_NAME_MISMATCH) 1454 { 1455 excType = UserDataCertificateException.Type.HOST_NAME_MISMATCH; 1456 } 1457 1458 if (excType != null) 1459 { 1460 String h; 1461 int p; 1462 try 1463 { 1464 URI uri = new URI(getInfo().getAdminConnectorURL()); 1465 h = uri.getHost(); 1466 p = uri.getPort(); 1467 } 1468 catch (Throwable t) 1469 { 1470 logger.warn(LocalizableMessage.raw("Error parsing ldap url of ldap url.", t)); 1471 h = INFO_NOT_AVAILABLE_LABEL.get().toString(); 1472 p = -1; 1473 } 1474 final UserDataCertificateException udce = new UserDataCertificateException( 1475 null, INFO_CERTIFICATE_EXCEPTION.get(h, p), ne, h, p, getInfo().getTrustManager().getLastRefusedChain(), 1476 getInfo().getTrustManager().getLastRefusedAuthType(), excType); 1477 1478 if (SwingUtilities.isEventDispatchThread()) 1479 { 1480 handleCertificateException(udce, bindDN, bindPassword); 1481 } 1482 else 1483 { 1484 final ConfigReadException[] fcre = { null }; 1485 final NamingException[] fne = { null }; 1486 try 1487 { 1488 SwingUtilities.invokeAndWait(new Runnable() 1489 { 1490 @Override 1491 public void run() 1492 { 1493 try 1494 { 1495 handleCertificateException(udce, bindDN, bindPassword); 1496 } 1497 catch (ConfigReadException cre) 1498 { 1499 fcre[0] = cre; 1500 } 1501 catch (NamingException ne) 1502 { 1503 fne[0] = ne; 1504 } 1505 } 1506 }); 1507 } 1508 catch (Exception e) 1509 { 1510 throw new IllegalArgumentException("Unexpected error: " + e, e); 1511 } 1512 if (fcre[0] != null) 1513 { 1514 throw fcre[0]; 1515 } 1516 if (fne[0] != null) 1517 { 1518 throw fne[0]; 1519 } 1520 } 1521 } 1522 } 1523 return createdUserDataCtx; 1524 } 1525 1526 /** 1527 * Displays a dialog asking the user to accept a certificate if the user 1528 * accepts it, we update the trust manager and simulate a click on "OK" to 1529 * re-check the authentication. This method assumes that we are being called 1530 * from the event thread. 1531 * 1532 * @param bindDN 1533 * the bind DN. 1534 * @param bindPassword 1535 * the bind password. 1536 */ 1537 private void handleCertificateException(UserDataCertificateException ce, String bindDN, String bindPassword) 1538 throws NamingException, ConfigReadException 1539 { 1540 CertificateDialog dlg = new CertificateDialog(null, ce); 1541 dlg.pack(); 1542 Utilities.centerGoldenMean(dlg, Utilities.getParentDialog(this)); 1543 dlg.setVisible(true); 1544 if (dlg.getUserAnswer() != CertificateDialog.ReturnType.NOT_ACCEPTED) 1545 { 1546 X509Certificate[] chain = ce.getChain(); 1547 String authType = ce.getAuthType(); 1548 String host = ce.getHost(); 1549 1550 if (chain != null && authType != null && host != null) 1551 { 1552 logger.info(LocalizableMessage.raw("Accepting certificate presented by host " + host)); 1553 getInfo().getTrustManager().acceptCertificate(chain, authType, host); 1554 createdUserDataCtx = createUserDataDirContext(bindDN, bindPassword); 1555 } 1556 else 1557 { 1558 if (chain == null) 1559 { 1560 logger.warn(LocalizableMessage.raw("The chain is null for the UserDataCertificateException")); 1561 } 1562 if (authType == null) 1563 { 1564 logger.warn(LocalizableMessage.raw("The auth type is null for the UserDataCertificateException")); 1565 } 1566 if (host == null) 1567 { 1568 logger.warn(LocalizableMessage.raw("The host is null for the UserDataCertificateException")); 1569 } 1570 } 1571 } 1572 if (dlg.getUserAnswer() == CertificateDialog.ReturnType.ACCEPTED_PERMANENTLY) 1573 { 1574 X509Certificate[] chain = ce.getChain(); 1575 if (chain != null) 1576 { 1577 try 1578 { 1579 UIKeyStore.acceptCertificate(chain); 1580 } 1581 catch (Throwable t) 1582 { 1583 logger.warn(LocalizableMessage.raw("Error accepting certificate: " + t, t)); 1584 } 1585 } 1586 } 1587 } 1588 1589 /** 1590 * This class is used simply to avoid an inset on the left for the 'All Base 1591 * DNs' item. Since this item is a CategorizedComboBoxElement of type 1592 * CategorizedComboBoxElement.Type.REGULAR, it has by default an inset on the 1593 * left. The class simply handles this particular case to not to have that 1594 * inset for the 'All Base DNs' item. 1595 */ 1596 class CustomComboBoxCellRenderer extends CustomListCellRenderer 1597 { 1598 private LocalizableMessage ALL_BASE_DNS_STRING = INFO_CTRL_PANEL_ALL_BASE_DNS.get(); 1599 1600 /** 1601 * The constructor. 1602 * 1603 * @param combo 1604 * the combo box to be rendered. 1605 */ 1606 CustomComboBoxCellRenderer(JComboBox<?> combo) 1607 { 1608 super(combo); 1609 } 1610 1611 @Override 1612 @SuppressWarnings("rawtypes") 1613 public Component getListCellRendererComponent( 1614 JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) 1615 { 1616 Component comp = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 1617 if (value instanceof CategorizedComboBoxElement) 1618 { 1619 CategorizedComboBoxElement element = (CategorizedComboBoxElement) value; 1620 String name = getStringValue(element); 1621 if (ALL_BASE_DNS.equals(name)) 1622 { 1623 ((JLabel) comp).setText(ALL_BASE_DNS_STRING.toString()); 1624 } 1625 } 1626 comp.setFont(defaultFont); 1627 return comp; 1628 } 1629 } 1630 1631 /** 1632 * Checks that the root node has some children. It it has no children the 1633 * message 'No Match Found' is displayed instead of the tree panel. 1634 */ 1635 private void checkRootNode() 1636 { 1637 DefaultMutableTreeNode root = (DefaultMutableTreeNode) controller.getTreeModel().getRoot(); 1638 boolean visible = root.getChildCount() > 0; 1639 if (visible != treePane.isVisible()) 1640 { 1641 treePane.setVisible(visible); 1642 lNoMatchFound.setVisible(!visible); 1643 lNumberOfEntries.setVisible(visible); 1644 } 1645 numberEntriesUpdater.recalculate(); 1646 } 1647 1648 /** 1649 * Updates the NumsubordinateHacker of the browser controller with the 1650 * provided server descriptor. 1651 * 1652 * @param server 1653 * the server descriptor. 1654 */ 1655 private void updateNumSubordinateHacker(ServerDescriptor server) 1656 { 1657 String serverHost = server.getHostname(); 1658 int serverPort = server.getAdminConnector().getPort(); 1659 1660 List<DN> allSuffixes = new ArrayList<>(); 1661 for (BackendDescriptor backend : server.getBackends()) 1662 { 1663 for (BaseDNDescriptor baseDN : backend.getBaseDns()) 1664 { 1665 allSuffixes.add(baseDN.getDn()); 1666 } 1667 } 1668 1669 List<DN> rootSuffixes = new ArrayList<>(); 1670 for (DN dn : allSuffixes) 1671 { 1672 if (isRootSuffix(allSuffixes, dn)) 1673 { 1674 rootSuffixes.add(dn); 1675 } 1676 } 1677 controller.getNumSubordinateHacker().update(allSuffixes, rootSuffixes, serverHost, serverPort); 1678 } 1679 1680 private boolean isRootSuffix(List<DN> allSuffixes, DN dn) 1681 { 1682 for (DN suffix : allSuffixes) 1683 { 1684 if (suffix.isAncestorOf(dn) && !suffix.equals(dn)) 1685 { 1686 return false; 1687 } 1688 } 1689 return true; 1690 } 1691 1692 /** 1693 * This is a class that simply checks the number of entries that the browser 1694 * contains and updates a counter with the new number of entries. It is 1695 * basically a thread that sleeps and checks whether some calculation must be 1696 * made: when we know that something is updated in the browser the method 1697 * recalculate() is called. We could use a more sophisticated code (like use a 1698 * wait() call that would get notified when recalculate() is called) but this 1699 * is not required and it might have an impact on the reactivity of the UI if 1700 * recalculate gets called too often. We can afford to wait 400 miliseconds 1701 * before updating the number of entries and with this approach there is 1702 * hardly no impact on the reactivity of the UI. 1703 */ 1704 protected class NumberOfEntriesUpdater extends Thread 1705 { 1706 private boolean recalculate; 1707 1708 /** Notifies that the number of entries in the browser has changed. */ 1709 public void recalculate() 1710 { 1711 recalculate = true; 1712 } 1713 1714 /** Executes the updater. */ 1715 @Override 1716 public void run() 1717 { 1718 while (true) 1719 { 1720 try 1721 { 1722 Thread.sleep(400); 1723 } 1724 catch (Throwable t) 1725 { 1726 } 1727 if (recalculate) 1728 { 1729 recalculate = false; 1730 SwingUtilities.invokeLater(new Runnable() 1731 { 1732 @Override 1733 public void run() 1734 { 1735 int nEntries = 0; 1736 // This recursive algorithm is fast enough to use it on the 1737 // event thread. Running it here we avoid issues with concurrent 1738 // access to the node children 1739 if (controller.getTree().isRootVisible()) 1740 { 1741 nEntries++; 1742 } 1743 DefaultMutableTreeNode root = (DefaultMutableTreeNode) controller.getTreeModel().getRoot(); 1744 1745 nEntries += getChildren(root); 1746 lNumberOfEntries.setText(INFO_CTRL_BROWSER_NUMBER_OF_ENTRIES.get(nEntries).toString()); 1747 } 1748 }); 1749 } 1750 if (controller != null) 1751 { 1752 final boolean mustDisplayRefreshIcon = controller.getQueueSize() > 0; 1753 if (mustDisplayRefreshIcon != filter.isRefreshIconDisplayed()) 1754 { 1755 SwingUtilities.invokeLater(new Runnable() 1756 { 1757 @Override 1758 public void run() 1759 { 1760 filter.displayRefreshIcon(mustDisplayRefreshIcon); 1761 } 1762 }); 1763 } 1764 } 1765 } 1766 } 1767 1768 /** 1769 * Returns the number of children for a given node. 1770 * 1771 * @param node 1772 * the node. 1773 * @return the number of children for the node. 1774 */ 1775 private int getChildren(DefaultMutableTreeNode node) 1776 { 1777 int nEntries = 0; 1778 1779 if (!node.isLeaf()) 1780 { 1781 Enumeration<?> en = node.children(); 1782 while (en.hasMoreElements()) 1783 { 1784 nEntries++; 1785 nEntries += getChildren((DefaultMutableTreeNode) en.nextElement()); 1786 } 1787 } 1788 return nEntries; 1789 } 1790 } 1791}