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