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-2009 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 java.awt.Component; 023import java.awt.GridBagConstraints; 024import java.awt.GridBagLayout; 025import java.awt.event.ItemEvent; 026import java.awt.event.ItemListener; 027import java.io.File; 028import java.io.IOException; 029import java.util.ArrayList; 030import java.util.Collection; 031import java.util.Collections; 032import java.util.Enumeration; 033import java.util.HashSet; 034import java.util.LinkedHashSet; 035import java.util.LinkedList; 036import java.util.List; 037import java.util.Set; 038import java.util.SortedSet; 039import java.util.TreeSet; 040 041import javax.swing.AbstractButton; 042import javax.swing.Box; 043import javax.swing.ButtonGroup; 044import javax.swing.DefaultComboBoxModel; 045import javax.swing.JButton; 046import javax.swing.JComboBox; 047import javax.swing.JComponent; 048import javax.swing.JLabel; 049import javax.swing.JPanel; 050import javax.swing.JRadioButton; 051import javax.swing.JTextField; 052import javax.swing.SwingUtilities; 053import javax.swing.event.ChangeEvent; 054import javax.swing.event.ChangeListener; 055import javax.swing.event.DocumentEvent; 056import javax.swing.event.DocumentListener; 057 058import org.forgerock.i18n.LocalizableMessage; 059import org.forgerock.i18n.LocalizedIllegalArgumentException; 060import org.forgerock.opendj.config.client.ManagementContext; 061import org.forgerock.opendj.config.client.ldap.LDAPManagementContext; 062import org.forgerock.opendj.ldap.DN; 063import org.forgerock.opendj.ldap.LdapException; 064import org.forgerock.opendj.server.config.client.BackendCfgClient; 065import org.forgerock.opendj.server.config.client.BackendIndexCfgClient; 066import org.forgerock.opendj.server.config.client.PluggableBackendCfgClient; 067import org.forgerock.opendj.server.config.client.RootCfgClient; 068import org.forgerock.opendj.server.config.meta.BackendIndexCfgDefn; 069import org.forgerock.opendj.server.config.meta.BackendIndexCfgDefn.IndexType; 070import org.opends.guitools.controlpanel.datamodel.BackendDescriptor; 071import org.opends.guitools.controlpanel.datamodel.BaseDNDescriptor; 072import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo; 073import org.opends.guitools.controlpanel.datamodel.ServerDescriptor; 074import org.opends.guitools.controlpanel.event.BrowseActionListener; 075import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent; 076import org.opends.guitools.controlpanel.task.OfflineUpdateException; 077import org.opends.guitools.controlpanel.task.OnlineUpdateException; 078import org.opends.guitools.controlpanel.task.Task; 079import org.opends.guitools.controlpanel.ui.renderer.CustomListCellRenderer; 080import org.opends.guitools.controlpanel.util.Utilities; 081import org.opends.quicksetup.Installation; 082import org.opends.quicksetup.installer.InstallerHelper; 083import org.opends.quicksetup.util.Utils; 084import org.opends.server.tools.BackendCreationHelper; 085import org.opends.server.tools.BackendCreationHelper.DefaultIndex; 086import org.opends.server.tools.BackendTypeHelper; 087import org.opends.server.tools.BackendTypeHelper.BackendTypeUIAdapter; 088import org.opends.server.tools.ImportLDIF; 089import org.opends.server.tools.LDAPModify; 090import org.opends.server.tools.makeldif.MakeLDIF; 091import org.opends.server.types.OpenDsException; 092import org.opends.server.util.SetupUtils; 093 094import com.forgerock.opendj.cli.CommandBuilder; 095 096/** The class that appears when the user clicks on 'New Base DN'. */ 097public class NewBaseDNPanel extends StatusGenericPanel 098{ 099 private static final int MAX_ENTRIES_NUMBER_GENERATED = 1000; 100 private static final int MAX_ENTRIES_NUMBER_GENERATED_LOCAL = 20000; 101 private static final long serialVersionUID = -2680821576362341119L; 102 private static final LocalizableMessage NEW_BACKEND_TEXT = INFO_CTRL_PANEL_NEW_BACKEND_LABEL.get(); 103 104 private JComboBox<?> backends; 105 private JComboBox<BackendTypeUIAdapter> backendTypes; 106 private JTextField newBackend; 107 private JTextField baseDN; 108 private JRadioButton onlyCreateBaseEntry; 109 private JRadioButton leaveDatabaseEmpty; 110 private JRadioButton importDataFromLDIF; 111 private JRadioButton importAutomaticallyGenerated; 112 private JTextField path; 113 private JTextField numberOfEntries; 114 private JLabel lRemoteFileHelp; 115 private JButton browseImportPath; 116 117 private JLabel lBackend; 118 private JLabel lDirectoryBaseDN; 119 private JLabel lPath; 120 private JLabel lNumberOfEntries; 121 private JLabel lDirectoryData; 122 private JLabel lNewBackendType; 123 124 private DocumentListener documentListener; 125 126 /** Default constructor. */ 127 public NewBaseDNPanel() 128 { 129 super(); 130 createLayout(); 131 } 132 133 @Override 134 public LocalizableMessage getTitle() 135 { 136 return INFO_CTRL_PANEL_NEW_BASE_DN_TITLE.get(); 137 } 138 139 @Override 140 public Component getPreferredFocusComponent() 141 { 142 return baseDN; 143 } 144 145 @Override 146 public void toBeDisplayed(boolean visible) 147 { 148 if (visible) 149 { 150 documentListener.changedUpdate(null); 151 } 152 } 153 154 /** Creates the layout of the panel (but the contents are not populated here). */ 155 private void createLayout() 156 { 157 GridBagConstraints gbc = new GridBagConstraints(); 158 addErrorPanel(gbc); 159 addBackendLabel(gbc); 160 addBackendNamesComboBox(gbc); 161 addNewBackendName(gbc); 162 addNewBackendTypeLabel(gbc); 163 addNewBackendTypeComboBox(gbc); 164 addBaseDNLabel(gbc); 165 addBaseDNTextField(gbc); 166 addBaseDNInlineHelp(gbc); 167 addDirectoryDataLabel(gbc); 168 addImportDataChoiceSection(gbc); 169 addBottomGlue(gbc); 170 } 171 172 private void addErrorPanel(GridBagConstraints gbc) 173 { 174 gbc.gridx = 0; 175 gbc.gridy = 0; 176 gbc.gridwidth = 3; 177 addErrorPane(gbc); 178 } 179 180 private void addBackendLabel(GridBagConstraints gbc) 181 { 182 gbc.anchor = GridBagConstraints.WEST; 183 gbc.weightx = 0.0; 184 gbc.gridwidth = 1; 185 gbc.gridy++; 186 gbc.fill = GridBagConstraints.NONE; 187 lBackend = Utilities.createPrimaryLabel(INFO_CTRL_PANEL_BACKEND_LABEL.get()); 188 add(lBackend, gbc); 189 } 190 191 @SuppressWarnings({ "unchecked", "rawtypes" }) 192 private void addBackendNamesComboBox(GridBagConstraints gbc) 193 { 194 gbc.insets.left = 10; 195 gbc.gridx = 1; 196 backends = Utilities.createComboBox(); 197 backends.setModel(new DefaultComboBoxModel(new Object[] { "bogus", NEW_BACKEND_TEXT })); 198 backends.setRenderer(new CustomListCellRenderer(backends)); 199 backends.addItemListener(new IgnoreItemListener(backends)); 200 gbc.gridwidth = 1; 201 add(backends, gbc); 202 } 203 204 private void addNewBackendTypeLabel(GridBagConstraints gbc) 205 { 206 gbc.insets.top = 10; 207 gbc.gridx = 0; 208 gbc.gridy++; 209 gbc.insets.left = 0; 210 gbc.gridwidth = 1; 211 lNewBackendType = Utilities.createPrimaryLabel(INFO_CTRL_PANEL_BASE_DN_NEW_BACKEND_TYPE_LABEL.get()); 212 add(lNewBackendType, gbc); 213 addBackendNameChangeListener(lNewBackendType); 214 } 215 216 @SuppressWarnings("unchecked") 217 private void addNewBackendTypeComboBox(GridBagConstraints gbc) 218 { 219 gbc.insets.left = 10; 220 gbc.gridx = 1; 221 gbc.gridwidth = 1; 222 final BackendTypeHelper backendTypeHelper = new BackendTypeHelper(); 223 backendTypes = Utilities.createComboBox(); 224 backendTypes.setModel(new DefaultComboBoxModel<>(backendTypeHelper.getBackendTypeUIAdaptors())); 225 backendTypes.setRenderer(new CustomListCellRenderer(backendTypes)); 226 backendTypes.addItemListener(new IgnoreItemListener(backendTypes)); 227 add(backendTypes, gbc); 228 addBackendNameChangeListener(backendTypes); 229 } 230 231 private void addNewBackendName(GridBagConstraints gbc) 232 { 233 gbc.gridx = 2; 234 newBackend = Utilities.createTextField(); 235 newBackend.setColumns(18); 236 add(newBackend, gbc); 237 addBackendNameChangeListener(newBackend); 238 } 239 240 private void addBackendNameChangeListener(final JComponent component) 241 { 242 ItemListener comboListener = new ItemListener() 243 { 244 @Override 245 public void itemStateChanged(ItemEvent ev) 246 { 247 Object o = backends.getSelectedItem(); 248 component.setVisible(NEW_BACKEND_TEXT.equals(o)); 249 } 250 }; 251 backends.addItemListener(comboListener); 252 comboListener.itemStateChanged(null); 253 } 254 255 private void addBaseDNLabel(GridBagConstraints gbc) 256 { 257 gbc.insets.top = 10; 258 gbc.gridx = 0; 259 gbc.gridy++; 260 gbc.insets.left = 0; 261 gbc.gridwidth = 1; 262 lDirectoryBaseDN = Utilities.createPrimaryLabel(INFO_CTRL_PANEL_BASE_DN_LABEL.get()); 263 add(lDirectoryBaseDN, gbc); 264 } 265 266 private void addBaseDNTextField(GridBagConstraints gbc) 267 { 268 gbc.gridx = 1; 269 gbc.insets.left = 10; 270 gbc.gridwidth = 2; 271 baseDN = Utilities.createTextField(); 272 documentListener = new DocumentListener() 273 { 274 @Override 275 public void changedUpdate(DocumentEvent ev) 276 { 277 String text = baseDN.getText().trim(); 278 setEnabledOK(text != null && text.length() > 0 && !errorPane.isVisible()); 279 } 280 281 @Override 282 public void removeUpdate(DocumentEvent ev) 283 { 284 changedUpdate(ev); 285 } 286 287 @Override 288 public void insertUpdate(DocumentEvent ev) 289 { 290 changedUpdate(ev); 291 } 292 }; 293 baseDN.getDocument().addDocumentListener(documentListener); 294 gbc.weightx = 1.0; 295 gbc.fill = GridBagConstraints.HORIZONTAL; 296 add(baseDN, gbc); 297 } 298 299 private void addBaseDNInlineHelp(GridBagConstraints gbc) 300 { 301 gbc.gridy++; 302 gbc.anchor = GridBagConstraints.EAST; 303 gbc.insets.top = 3; 304 JLabel inlineHelp = Utilities.createInlineHelpLabel(INFO_CTRL_PANEL_BASE_DN_EXAMPLE.get()); 305 add(inlineHelp, gbc); 306 } 307 308 private void addDirectoryDataLabel(GridBagConstraints gbc) 309 { 310 gbc.gridx = 0; 311 gbc.gridy++; 312 gbc.insets.left = 0; 313 gbc.insets.top = 10; 314 gbc.gridwidth = 1; 315 gbc.weightx = 0.0; 316 lDirectoryData = Utilities.createPrimaryLabel(INFO_CTRL_PANEL_DIRECTORY_DATA_LABEL.get()); 317 add(lDirectoryData, gbc); 318 } 319 320 private void addImportDataChoiceSection(GridBagConstraints gbc) 321 { 322 onlyCreateBaseEntry = Utilities.createRadioButton(INFO_CTRL_PANEL_ONLY_CREATE_BASE_ENTRY_LABEL.get()); 323 onlyCreateBaseEntry.setSelected(false); 324 325 gbc.insets.left = 10; 326 gbc.gridx = 1; 327 gbc.gridwidth = 2; 328 add(onlyCreateBaseEntry, gbc); 329 330 leaveDatabaseEmpty = Utilities.createRadioButton(INFO_CTRL_PANEL_LEAVE_DATABASE_EMPTY_LABEL.get()); 331 leaveDatabaseEmpty.setSelected(false); 332 333 gbc.gridy++; 334 gbc.gridwidth = 2; 335 gbc.insets.top = 5; 336 add(leaveDatabaseEmpty, gbc); 337 338 importDataFromLDIF = Utilities.createRadioButton(INFO_CTRL_PANEL_IMPORT_FROM_LDIF_LABEL.get()); 339 importDataFromLDIF.setSelected(false); 340 341 gbc.gridy++; 342 gbc.gridwidth = 2; 343 add(importDataFromLDIF, gbc); 344 345 gbc.gridy++; 346 gbc.gridwidth = 2; 347 gbc.insets.left = 30; 348 add(createPathPanel(), gbc); 349 350 importAutomaticallyGenerated = 351 Utilities.createRadioButton(INFO_CTRL_PANEL_IMPORT_AUTOMATICALLY_GENERATED_LABEL.get()); 352 importAutomaticallyGenerated.setOpaque(false); 353 importAutomaticallyGenerated.setSelected(false); 354 355 gbc.gridy++; 356 gbc.gridwidth = 2; 357 gbc.insets.left = 10; 358 add(importAutomaticallyGenerated, gbc); 359 360 gbc.gridy++; 361 gbc.gridwidth = 2; 362 gbc.insets.left = 30; 363 add(createNumberOfUsersPanel(), gbc); 364 365 ButtonGroup group = new ButtonGroup(); 366 group.add(onlyCreateBaseEntry); 367 group.add(leaveDatabaseEmpty); 368 group.add(importDataFromLDIF); 369 group.add(importAutomaticallyGenerated); 370 371 ChangeListener listener = new ChangeListener() 372 { 373 @Override 374 public void stateChanged(ChangeEvent ev) 375 { 376 browseImportPath.setEnabled(importDataFromLDIF.isSelected()); 377 lPath.setEnabled(importDataFromLDIF.isSelected()); 378 lRemoteFileHelp.setEnabled(importDataFromLDIF.isSelected()); 379 numberOfEntries.setEnabled(importAutomaticallyGenerated.isSelected()); 380 lNumberOfEntries.setEnabled(importAutomaticallyGenerated.isSelected()); 381 } 382 }; 383 384 Enumeration<AbstractButton> buttons = group.getElements(); 385 while (buttons.hasMoreElements()) 386 { 387 buttons.nextElement().addChangeListener(listener); 388 } 389 onlyCreateBaseEntry.setSelected(true); 390 listener.stateChanged(null); 391 } 392 393 @Override 394 public void configurationChanged(ConfigurationChangeEvent ev) 395 { 396 ServerDescriptor desc = ev.getNewDescriptor(); 397 final SortedSet<String> sortedBackends = new TreeSet<>(); 398 for (BackendDescriptor backend : desc.getBackends()) 399 { 400 if (!backend.isConfigBackend()) 401 { 402 sortedBackends.add(backend.getBackendID()); 403 } 404 } 405 406 List<Object> newElements = new ArrayList<Object>(sortedBackends); 407 if (!sortedBackends.isEmpty()) 408 { 409 newElements.add(COMBO_SEPARATOR); 410 } 411 newElements.add(NEW_BACKEND_TEXT); 412 super.updateComboBoxModel(newElements, (DefaultComboBoxModel<?>) backends.getModel()); 413 updateErrorPaneAndOKButtonIfAuthRequired(desc, 414 isLocal() ? INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_FOR_CREATE_BASE_DN.get() 415 : INFO_CTRL_PANEL_CANNOT_CONNECT_TO_REMOTE_DETAILS.get(desc.getHostname())); 416 SwingUtilities.invokeLater(new Runnable() 417 { 418 @Override 419 public void run() 420 { 421 lRemoteFileHelp.setVisible(!isLocal()); 422 browseImportPath.setVisible(isLocal()); 423 } 424 }); 425 } 426 427 private JPanel createPathPanel() 428 { 429 JPanel panel = new JPanel(new GridBagLayout()); 430 panel.setOpaque(false); 431 GridBagConstraints gbc = new GridBagConstraints(); 432 gbc.gridwidth = 1; 433 gbc.gridy = 0; 434 gbc.gridx = 0; 435 lPath = Utilities.createDefaultLabel(INFO_CTRL_PANEL_IMPORT_LDIF_PATH_LABEL.get()); 436 panel.add(lPath, gbc); 437 438 gbc.gridx = 1; 439 gbc.insets.left = 10; 440 path = Utilities.createTextField(); 441 gbc.weightx = 1.0; 442 gbc.fill = GridBagConstraints.HORIZONTAL; 443 panel.add(path, gbc); 444 browseImportPath = Utilities.createButton(INFO_CTRL_PANEL_BROWSE_BUTTON_LABEL.get()); 445 browseImportPath.addActionListener( 446 new BrowseActionListener(path, BrowseActionListener.BrowseType.OPEN_LDIF_FILE, this)); 447 gbc.gridx = 2; 448 gbc.weightx = 0.0; 449 panel.add(browseImportPath, gbc); 450 451 gbc.gridy++; 452 gbc.gridx = 1; 453 lRemoteFileHelp = Utilities.createInlineHelpLabel(INFO_CTRL_PANEL_REMOTE_SERVER_PATH.get()); 454 gbc.insets.top = 3; 455 gbc.insets.left = 10; 456 panel.add(lRemoteFileHelp, gbc); 457 458 return panel; 459 } 460 461 private JPanel createNumberOfUsersPanel() 462 { 463 JPanel panel = new JPanel(new GridBagLayout()); 464 panel.setOpaque(false); 465 GridBagConstraints gbc = new GridBagConstraints(); 466 gbc.weightx = 0.0; 467 gbc.fill = GridBagConstraints.HORIZONTAL; 468 lNumberOfEntries = Utilities.createDefaultLabel(INFO_CTRL_PANEL_NUMBER_OF_USER_ENTRIES_LABEL.get()); 469 panel.add(lNumberOfEntries, gbc); 470 471 gbc.gridx = 1; 472 gbc.insets.left = 10; 473 numberOfEntries = Utilities.createTextField("2000", 6); 474 panel.add(numberOfEntries, gbc); 475 476 gbc.gridx = 2; 477 gbc.insets.left = 0; 478 gbc.weightx = 1.0; 479 panel.add(Box.createHorizontalGlue(), gbc); 480 481 return panel; 482 } 483 484 @Override 485 public void cancelClicked() 486 { 487 resetLabelAsValid(); 488 super.cancelClicked(); 489 } 490 491 private void resetLabelAsValid() 492 { 493 setPrimaryValid(lBackend); 494 setPrimaryValid(lDirectoryBaseDN); 495 setPrimaryValid(lDirectoryData); 496 setSecondaryValid(lPath); 497 setSecondaryValid(lNumberOfEntries); 498 } 499 500 @Override 501 protected void checkOKButtonEnable() 502 { 503 documentListener.changedUpdate(null); 504 } 505 506 @Override 507 public void okClicked() 508 { 509 resetLabelAsValid(); 510 511 final Set<LocalizableMessage> errors = new LinkedHashSet<>(); 512 final ServerDescriptor desc = getInfo().getServerDescriptor(); 513 final Set<BackendDescriptor> existingBackends = desc.getBackends(); 514 515 final String backendName = validateBackendName(existingBackends, errors); 516 final String dn = validateBaseDN(backendName, existingBackends, errors); 517 validateImportLDIFFilePath(errors); 518 validateAutomaticallyGenerated(errors); 519 520 if (errors.isEmpty()) 521 { 522 final ProgressDialog progressDialog = new ProgressDialog( 523 Utilities.createFrame(), Utilities.getParentDialog(this), getTitle(), getInfo()); 524 final NewBaseDNTask newTask = new NewBaseDNTask(getInfo(), progressDialog); 525 for (Task task : getInfo().getTasks()) 526 { 527 task.canLaunch(newTask, errors); 528 } 529 530 if (errors.isEmpty()) 531 { 532 launchOperation(newTask, 533 INFO_CTRL_PANEL_CREATING_BASE_DN_SUMMARY.get(dn), 534 INFO_CTRL_PANEL_CREATING_BASE_DN_COMPLETE.get(), 535 INFO_CTRL_PANEL_CREATING_BASE_DN_SUCCESSFUL.get(dn), 536 ERR_CTRL_PANEL_CREATING_BASE_DN_ERROR_SUMMARY.get(dn), 537 null, 538 ERR_CTRL_PANEL_CREATING_BASE_DN_ERROR_DETAILS, 539 progressDialog); 540 progressDialog.setVisible(true); 541 baseDN.setText(""); 542 baseDN.grabFocus(); 543 Utilities.getParentDialog(this).setVisible(false); 544 } 545 } 546 547 if (!errors.isEmpty()) 548 { 549 displayErrorDialog(errors); 550 } 551 } 552 553 /** Returns the existing or the new backend name, once user have clicked on 'OK' button. */ 554 private String validateBackendName( 555 final Set<BackendDescriptor> existingBackends, final Set<LocalizableMessage> errors) 556 { 557 final Object selectedItem = backends.getSelectedItem(); 558 if (!selectedItem.equals(NEW_BACKEND_TEXT)) 559 { 560 return selectedItem.toString(); 561 } 562 563 final String backendName = newBackend.getText().trim(); 564 if (backendName.length() == 0) 565 { 566 errors.add(ERR_NEW_BACKEND_NAME_REQUIRED.get()); 567 setPrimaryInvalid(lBackend); 568 return backendName; 569 } 570 571 // Check that the backend is not already defined. 572 for (BackendDescriptor backend : existingBackends) 573 { 574 if (backendName.equalsIgnoreCase(backend.getBackendID())) 575 { 576 errors.add(ERR_BACKEND_ALREADY_EXISTS.get(backendName)); 577 setPrimaryInvalid(lBackend); 578 } 579 } 580 581 return backendName; 582 } 583 584 private String validateBaseDN(final String backendName, final Set<BackendDescriptor> existingBackends, 585 final Set<LocalizableMessage> errors) 586 { 587 String dn = baseDN.getText(); 588 if (dn.trim().length() == 0) 589 { 590 errors.add(ERR_NEW_BASE_DN_VALUE_REQUIRED.get()); 591 setPrimaryInvalid(lDirectoryBaseDN); 592 return dn; 593 } 594 595 try 596 { 597 final DN theDN = DN.valueOf(dn); 598 for (final BackendDescriptor backend : existingBackends) 599 { 600 for (final BaseDNDescriptor baseDN : backend.getBaseDns()) 601 { 602 if (baseDN.getDn().equals(theDN)) 603 { 604 return invalidBaseDNValue(dn, ERR_BASE_DN_ALREADY_EXISTS.get(dn), errors); 605 } 606 else if (baseDN.getDn().isSuperiorOrEqualTo(theDN) && backendName.equalsIgnoreCase(backend.getBackendID())) 607 { 608 return invalidBaseDNValue(dn, ERR_BASE_DN_ANCESTOR_EXISTS.get(baseDN.getDn()), errors); 609 } 610 else if (theDN.isSuperiorOrEqualTo(baseDN.getDn()) && backendName.equalsIgnoreCase(backend.getBackendID())) 611 { 612 return invalidBaseDNValue(dn, ERR_BASE_DN_DN_IS_ANCESTOR_OF.get(baseDN.getDn()), errors); 613 } 614 } 615 } 616 } 617 catch (LocalizedIllegalArgumentException e) 618 { 619 errors.add(INFO_CTRL_PANEL_INVALID_DN_DETAILS.get(dn, e.getMessageObject())); 620 setPrimaryInvalid(lDirectoryBaseDN); 621 } 622 623 return dn; 624 } 625 626 /** Mark the provided base DN as invalid with the provided reason and return it. */ 627 private String invalidBaseDNValue(final String dn, final LocalizableMessage errorMsg, 628 final Set<LocalizableMessage> errors) 629 { 630 errors.add(errorMsg); 631 setPrimaryInvalid(lDirectoryBaseDN); 632 return dn; 633 } 634 635 private void validateImportLDIFFilePath(final Set<LocalizableMessage> errors) 636 { 637 // TODO: what happens with sub-suffixes? 638 if (importDataFromLDIF.isSelected()) 639 { 640 String ldifPath = path.getText(); 641 if (ldifPath == null || "".equals(ldifPath.trim())) 642 { 643 errors.add(INFO_NO_LDIF_PATH.get()); 644 setSecondaryInvalid(lPath); 645 } 646 else if (isLocal() && !Utils.fileExists(ldifPath)) 647 { 648 errors.add(INFO_LDIF_FILE_DOES_NOT_EXIST.get()); 649 setSecondaryInvalid(lPath); 650 } 651 } 652 } 653 654 private void validateAutomaticallyGenerated(final Set<LocalizableMessage> errors) 655 { 656 if (importAutomaticallyGenerated.isSelected()) 657 { 658 final int minValue = 1; 659 final int maxValue = isLocal() ? MAX_ENTRIES_NUMBER_GENERATED_LOCAL : MAX_ENTRIES_NUMBER_GENERATED; 660 final LocalizableMessage errorMsg = ERR_NUMBER_OF_ENTRIES_INVALID.get(minValue, maxValue); 661 if (!checkIntValue(errors, numberOfEntries.getText(), minValue, maxValue, errorMsg)) 662 { 663 setSecondaryInvalid(lNumberOfEntries); 664 } 665 } 666 } 667 668 private String getBackendName() 669 { 670 Object backendName = backends.getSelectedItem(); 671 if (NEW_BACKEND_TEXT.equals(backendName)) 672 { 673 return newBackend.getText().trim(); 674 } 675 else if (backendName != null) 676 { 677 return backendName.toString(); 678 } 679 680 return null; 681 } 682 683 private BackendTypeUIAdapter getSelectedBackendType() 684 { 685 return (BackendTypeUIAdapter) backendTypes.getSelectedItem(); 686 } 687 688 private boolean isNewBackend() 689 { 690 return NEW_BACKEND_TEXT.equals(backends.getSelectedItem()); 691 } 692 693 /** The task in charge of creating the base DN (and if required, the backend). */ 694 private class NewBaseDNTask extends Task 695 { 696 private final Set<String> backendSet; 697 private final String newBaseDN; 698 private int progressAfterConfigurationUpdate = -1; 699 700 /** 701 * The constructor of the task. 702 * 703 * @param info 704 * the control panel info. 705 * @param dlg 706 * the progress dialog that shows the progress of the task. 707 */ 708 private NewBaseDNTask(ControlPanelInfo info, ProgressDialog dlg) 709 { 710 super(info, dlg); 711 backendSet = new HashSet<>(); 712 backendSet.add(getBackendName()); 713 newBaseDN = baseDN.getText(); 714 715 if (onlyCreateBaseEntry.isSelected()) 716 { 717 progressAfterConfigurationUpdate = 40; 718 } 719 else if (leaveDatabaseEmpty.isSelected()) 720 { 721 progressAfterConfigurationUpdate = 90; 722 } 723 else if (importAutomaticallyGenerated.isSelected()) 724 { 725 int nEntries = Integer.parseInt(numberOfEntries.getText().trim()); 726 if (nEntries < 500) 727 { 728 progressAfterConfigurationUpdate = 30; 729 } 730 else if (nEntries < 3000) 731 { 732 progressAfterConfigurationUpdate = 15; 733 } 734 else 735 { 736 progressAfterConfigurationUpdate = 5; 737 } 738 } 739 } 740 741 @Override 742 public Type getType() 743 { 744 return Type.NEW_BASEDN; 745 } 746 747 @Override 748 public LocalizableMessage getTaskDescription() 749 { 750 return INFO_CTRL_PANEL_NEW_BASE_DN_TASK_DESCRIPTION.get(newBaseDN, backendSet.iterator().next()); 751 } 752 753 @Override 754 public boolean canLaunch(Task taskToBeLaunched, Collection<LocalizableMessage> incompatibilityReasons) 755 { 756 boolean canLaunch = true; 757 if (state == State.RUNNING && runningOnSameServer(taskToBeLaunched)) 758 { 759 // All the operations are incompatible if they apply to this backend. 760 Set<String> backends = new TreeSet<>(taskToBeLaunched.getBackends()); 761 backends.retainAll(getBackends()); 762 if (!backends.isEmpty()) 763 { 764 incompatibilityReasons.add(getIncompatibilityMessage(this, taskToBeLaunched)); 765 canLaunch = false; 766 } 767 } 768 return canLaunch; 769 } 770 771 private String getDataCommandLineToDisplay() 772 { 773 StringBuilder sb = new StringBuilder(); 774 sb.append(getDataCommandLineName()); 775 Collection<String> args = getObfuscatedCommandLineArguments(getDataCommandLineArguments(path.getText(), false)); 776 args.removeAll(getConfigCommandLineArguments()); 777 for (String arg : args) 778 { 779 sb.append(" ").append(CommandBuilder.escapeValue(arg)); 780 } 781 return sb.toString(); 782 } 783 784 private String getDataCommandLineName() 785 { 786 if (!leaveDatabaseEmpty.isSelected()) 787 { 788 return getCommandLinePath(isLocal() ? "import-ldif" : "ldapmodify"); 789 } 790 791 return null; 792 } 793 794 /** 795 * Returns the arguments of the command-line that can be used to generate 796 * the data. 797 * 798 * @param ldifFile 799 * the LDIF file. 800 * @param useTemplate 801 * whether to use a template or not. 802 * @return the arguments of the command-line that can be used to generate 803 * the data. 804 */ 805 private List<String> getDataCommandLineArguments(String ldifFile, boolean useTemplate) 806 { 807 List<String> args = new ArrayList<>(); 808 if (!leaveDatabaseEmpty.isSelected()) 809 { 810 if (isLocal()) 811 { 812 if (!useTemplate) 813 { 814 args.add("--ldifFile"); 815 args.add(ldifFile); 816 } 817 else 818 { 819 args.add("--templateFile"); 820 args.add(ldifFile); 821 args.add("--randomSeed"); 822 args.add("0"); 823 } 824 args.add("--backendID"); 825 args.add(getBackendName()); 826 } 827 else 828 { 829 // If we are not local, we use ldapmodify to update the contents. 830 args.add("-a"); 831 args.add("-f"); 832 args.add(ldifFile); 833 } 834 args.addAll(getConnectionCommandLineArguments(true, !isLocal())); 835 836 if (isServerRunning() && isLocal()) 837 { 838 args.addAll(getConfigCommandLineArguments()); 839 } 840 841 args.add(getNoPropertiesFileArgument()); 842 } 843 844 return args; 845 } 846 847 private void updateConfigurationOnline() throws Exception 848 { 849 SwingUtilities.invokeLater(new Runnable() 850 { 851 @Override 852 public void run() 853 { 854 List<String> args = getObfuscatedCommandLineArguments(getDSConfigCommandLineArguments()); 855 args.removeAll(getConfigCommandLineArguments()); 856 printEquivalentCommandLine( 857 getConfigCommandLineFullPath(), args, INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_CREATE_BASE_DN.get()); 858 } 859 }); 860 861 performTask(); 862 printTaskDone(); 863 if (isNewBackend()) 864 { 865 createAdditionalIndexes(); 866 } 867 refreshProgressBar(); 868 } 869 870 private void updateConfigurationOffline() throws Exception 871 { 872 boolean configHandlerUpdated = false; 873 try 874 { 875 stopPoolingAndInitializeConfiguration(); 876 configHandlerUpdated = true; 877 878 performTask(); 879 printTaskDone(); 880 refreshProgressBar(); 881 } 882 finally 883 { 884 if (configHandlerUpdated) 885 { 886 startPoolingAndInitializeConfiguration(); 887 } 888 } 889 } 890 891 private void printCreateNewBackendProgress(final String backendName) throws OpenDsException 892 { 893 SwingUtilities.invokeLater(new Runnable() 894 { 895 @Override 896 public void run() 897 { 898 LocalizableMessage message = INFO_CTRL_PANEL_CREATING_BACKEND_PROGRESS.get(backendName, newBaseDN); 899 getProgressDialog().appendProgressHtml( 900 Utilities.getProgressWithPoints(message, ColorAndFontConstants.progressFont)); 901 } 902 }); 903 } 904 905 private void performTask() throws Exception 906 { 907 final String backendName = getBackendName(); 908 if (isNewBackend()) 909 { 910 printCreateNewBackendProgress(backendName); 911 createBackend(backendName); 912 } 913 else 914 { 915 printCreateNewBaseDNProgress(backendName); 916 addNewBaseDN(backendName); 917 } 918 } 919 920 private void createBackend(String backendName) throws Exception 921 { 922 if (!isServerRunning()) 923 { 924 createBackendOffline(backendName); 925 return; 926 } 927 928 createBackendOnline(backendName); 929 } 930 931 private void createBackendOffline(String backendName) throws OpenDsException 932 { 933 try 934 { 935 Set<DN> baseDNs = Collections.singleton(DN.valueOf(newBaseDN)); 936 BackendCreationHelper.createBackendOffline(backendName, baseDNs, getSelectedBackendType().getBackend()); 937 } 938 catch (Exception e) 939 { 940 throw new OfflineUpdateException(ERROR_CTRL_PANEL_CREATE_NEW_BACKEND.get(backendName, e.getMessage()), e); 941 } 942 } 943 944 private void createBackendOnline(String backendName) throws Exception 945 { 946 Set<DN> baseDNs = Collections.singleton(DN.valueOf(newBaseDN)); 947 BackendCreationHelper.createBackendOffline(backendName, baseDNs, getSelectedBackendType().getBackend()); 948 } 949 950 private RootCfgClient getRootConfigurationClient() throws LdapException 951 { 952 return getInfo().getConnection().getRootConfiguration(); 953 } 954 955 private void addNewBaseDN(String backendName) throws Exception 956 { 957 if (!isServerRunning()) 958 { 959 addNewBaseDNOffline(backendName); 960 return; 961 } 962 963 final BackendCfgClient backend = getRootConfigurationClient().getBackend(backendName); 964 final Set<DN> baseDNs = backend.getBaseDN(); 965 baseDNs.add(DN.valueOf(newBaseDN)); 966 backend.setBaseDN(baseDNs); 967 backend.commit(); 968 } 969 970 private void addNewBaseDNOffline(String backendName) throws OpenDsException 971 { 972 try 973 { 974 getInfo().initializeConfigurationFramework(); 975 final File config = Installation.getLocal().getCurrentConfigurationFile(); 976 try (ManagementContext context = LDAPManagementContext.newLDIFManagementContext(config)) 977 { 978 final BackendCfgClient backend = context.getRootConfiguration().getBackend(backendName); 979 final SortedSet<DN> baseDNs = backend.getBaseDN(); 980 baseDNs.add(DN.valueOf(newBaseDN)); 981 backend.setBaseDN(baseDNs); 982 backend.commit(); 983 } 984 } 985 catch (Exception e) 986 { 987 throw new OfflineUpdateException(LocalizableMessage.raw(e.getMessage()), e); 988 } 989 } 990 991 private void createAdditionalIndexes() throws Exception 992 { 993 final String backendName = getBackendName(); 994 displayCreateAdditionalIndexesDsConfigCmdLine(); 995 final RootCfgClient root = getRootConfigurationClient(); 996 addBackendDefaultIndexes((PluggableBackendCfgClient) root.getBackend(backendName)); 997 displayCreateAdditionalIndexesDone(); 998 } 999 1000 private void addBackendDefaultIndexes(PluggableBackendCfgClient backendCfgClient) throws Exception 1001 { 1002 for (DefaultIndex defaultIndex : BackendCreationHelper.DEFAULT_INDEXES) 1003 { 1004 final BackendIndexCfgClient index = backendCfgClient.createBackendIndex( 1005 BackendIndexCfgDefn.getInstance(), defaultIndex.getName(), null); 1006 1007 final List<IndexType> indexTypes = new LinkedList<>(); 1008 indexTypes.add(IndexType.EQUALITY); 1009 if (defaultIndex.shouldCreateSubstringIndex()) 1010 { 1011 indexTypes.add(IndexType.SUBSTRING); 1012 } 1013 index.setIndexType(indexTypes); 1014 index.commit(); 1015 } 1016 } 1017 1018 private void printCreateNewBaseDNProgress(final String backendName) throws OpenDsException 1019 { 1020 SwingUtilities.invokeLater(new Runnable() 1021 { 1022 @Override 1023 public void run() 1024 { 1025 LocalizableMessage message = INFO_CTRL_PANEL_CREATING_BASE_DN_PROGRESS.get(newBaseDN, backendName); 1026 getProgressDialog().appendProgressHtml( 1027 Utilities.getProgressWithPoints(message, ColorAndFontConstants.progressFont)); 1028 } 1029 }); 1030 } 1031 1032 private void printTaskDone() 1033 { 1034 SwingUtilities.invokeLater(new Runnable() 1035 { 1036 @Override 1037 public void run() 1038 { 1039 getProgressDialog().appendProgressHtml( 1040 Utilities.getProgressDone(ColorAndFontConstants.progressFont) + "<br><br>"); 1041 } 1042 }); 1043 } 1044 1045 private void refreshProgressBar() 1046 { 1047 if (progressAfterConfigurationUpdate > 0) 1048 { 1049 SwingUtilities.invokeLater(new Runnable() 1050 { 1051 @Override 1052 public void run() 1053 { 1054 getProgressDialog().getProgressBar().setIndeterminate(false); 1055 getProgressDialog().getProgressBar().setValue(progressAfterConfigurationUpdate); 1056 } 1057 }); 1058 } 1059 } 1060 1061 private void displayCreateAdditionalIndexesDsConfigCmdLine() 1062 { 1063 final List<List<String>> argsArray = new ArrayList<>(); 1064 for (DefaultIndex defaultIndex : BackendCreationHelper.DEFAULT_INDEXES) 1065 { 1066 argsArray.add(getCreateIndexCommandLineArguments(defaultIndex)); 1067 } 1068 1069 final StringBuilder sb = new StringBuilder(); 1070 for (List<String> args : argsArray) 1071 { 1072 sb.append(getEquivalentCommandLine(getCommandLinePath("dsconfig"), getObfuscatedCommandLineArguments(args))); 1073 sb.append("<br><br>"); 1074 } 1075 1076 SwingUtilities.invokeLater(new Runnable() 1077 { 1078 @Override 1079 public void run() 1080 { 1081 getProgressDialog().appendProgressHtml(Utilities.applyFont( 1082 INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_CREATE_ADDITIONAL_INDEXES.get() 1083 + "<br><br><b>" + sb + "</b>", ColorAndFontConstants.progressFont)); 1084 getProgressDialog().appendProgressHtml(Utilities.getProgressWithPoints( 1085 INFO_CTRL_PANEL_CREATING_ADDITIONAL_INDEXES_PROGRESS.get(), ColorAndFontConstants.progressFont)); 1086 } 1087 }); 1088 } 1089 1090 private List<String> getCreateIndexCommandLineArguments(final DefaultIndex defaultIndex) 1091 { 1092 final List<String> args = new ArrayList<>(); 1093 args.add("create-backend-index"); 1094 args.add("--backend-name"); 1095 args.add(getBackendName()); 1096 args.add("--type"); 1097 args.add("generic"); 1098 args.add("--index-name"); 1099 args.add(defaultIndex.getName()); 1100 args.add("--set"); 1101 args.add("index-type:" + IndexType.EQUALITY); 1102 if (defaultIndex.shouldCreateSubstringIndex()) 1103 { 1104 args.add("--set"); 1105 args.add("index-type:" + IndexType.SUBSTRING); 1106 } 1107 args.addAll(getConnectionCommandLineArguments()); 1108 args.add(getNoPropertiesFileArgument()); 1109 args.add("--no-prompt"); 1110 1111 return args; 1112 } 1113 1114 private void displayCreateAdditionalIndexesDone() 1115 { 1116 SwingUtilities.invokeLater(new Runnable() 1117 { 1118 @Override 1119 public void run() 1120 { 1121 getProgressDialog().appendProgressHtml( 1122 Utilities.getProgressDone(ColorAndFontConstants.progressFont) + "<br><br>"); 1123 } 1124 }); 1125 } 1126 1127 /** 1128 * Creates the data in the new base DN. 1129 * 1130 * @throws OpenDsException 1131 * if there is an error importing contents. 1132 * @throws IOException 1133 * if there is an err 1134 */ 1135 private void updateData() throws OpenDsException, IOException 1136 { 1137 final boolean leaveEmpty = leaveDatabaseEmpty.isSelected(); 1138 final boolean createBaseEntry = onlyCreateBaseEntry.isSelected(); 1139 final boolean importLDIF = importDataFromLDIF.isSelected(); 1140 final boolean generateData = !leaveEmpty && !createBaseEntry && !importLDIF; 1141 final String nEntries = numberOfEntries.getText(); 1142 final String ldif = path.getText(); 1143 1144 if (leaveEmpty) 1145 { 1146 state = State.FINISHED_SUCCESSFULLY; 1147 } 1148 else 1149 { 1150 final ProgressDialog progressDialog = getProgressDialog(); 1151 String ldifFile; 1152 if (importLDIF) 1153 { 1154 ldifFile = ldif; 1155 final String cmdLine = getDataCommandLineToDisplay(); 1156 SwingUtilities.invokeLater(new Runnable() 1157 { 1158 @Override 1159 public void run() 1160 { 1161 progressDialog.appendProgressHtml(Utilities.applyFont("Equivalent command line:<br><b>" + cmdLine 1162 + "</b><br><br>", ColorAndFontConstants.progressFont)); 1163 } 1164 }); 1165 } 1166 else if (createBaseEntry) 1167 { 1168 SwingUtilities.invokeLater(new Runnable() 1169 { 1170 @Override 1171 public void run() 1172 { 1173 progressDialog.appendProgressHtml(Utilities.getProgressWithPoints( 1174 INFO_PROGRESS_CREATING_BASE_ENTRY.get(newBaseDN), ColorAndFontConstants.progressFont)); 1175 } 1176 }); 1177 InstallerHelper helper = new InstallerHelper(); 1178 File f = helper.createBaseEntryTempFile(newBaseDN); 1179 ldifFile = f.getAbsolutePath(); 1180 } 1181 else 1182 { 1183 SwingUtilities.invokeLater(new Runnable() 1184 { 1185 @Override 1186 public void run() 1187 { 1188 if (isLocal()) 1189 { 1190 progressDialog.appendProgressHtml(Utilities.applyFont( 1191 INFO_PROGRESS_IMPORT_AUTOMATICALLY_GENERATED.get(nEntries).toString(), 1192 ColorAndFontConstants.progressFont) + "<br>"); 1193 } 1194 else 1195 { 1196 getProgressDialog().appendProgressHtml(Utilities.getProgressWithPoints( 1197 INFO_PROGRESS_IMPORT_AUTOMATICALLY_GENERATED_REMOTE.get(nEntries), 1198 ColorAndFontConstants.progressFont)); 1199 } 1200 } 1201 }); 1202 1203 File f = SetupUtils.createTemplateFile(newBaseDN, Integer.parseInt(nEntries)); 1204 if (!isLocal()) 1205 { 1206 File tempFile = File.createTempFile("opendj-control-panel", ".ldif"); 1207 tempFile.deleteOnExit(); 1208 ldifFile = tempFile.getAbsolutePath(); 1209 1210 // Create the LDIF file locally using make-ldif 1211 List<String> makeLDIFArgs = new ArrayList<>(); 1212 makeLDIFArgs.add("--templateFile"); 1213 makeLDIFArgs.add(f.getAbsolutePath()); 1214 makeLDIFArgs.add("--ldifFile"); 1215 makeLDIFArgs.add(ldifFile); 1216 makeLDIFArgs.add("--randomSeed"); 1217 makeLDIFArgs.add("0"); 1218 makeLDIFArgs.add("--resourcePath"); 1219 1220 File makeLDIFPath = new File(Installation.getLocal().getConfigurationDirectory(), "MakeLDIF"); 1221 makeLDIFArgs.add(makeLDIFPath.getAbsolutePath()); 1222 makeLDIFArgs.addAll(getConfigCommandLineArguments()); 1223 1224 MakeLDIF makeLDIF = new MakeLDIF(); 1225 String[] array = new String[makeLDIFArgs.size()]; 1226 makeLDIFArgs.toArray(array); 1227 returnCode = makeLDIF.makeLDIFMain(array, false, false, outPrintStream, errorPrintStream); 1228 f.delete(); 1229 1230 if (returnCode != 0) 1231 { 1232 throw new OnlineUpdateException(ERR_CTRL_PANEL_ERROR_CREATING_NEW_DATA_LDIF.get(returnCode), null); 1233 } 1234 } 1235 else 1236 { 1237 ldifFile = f.getAbsolutePath(); 1238 } 1239 } 1240 1241 List<String> arguments = getDataCommandLineArguments(ldifFile, generateData); 1242 String[] args = new String[arguments.size()]; 1243 arguments.toArray(args); 1244 if (createBaseEntry || !isLocal()) 1245 { 1246 outPrintStream.setNotifyListeners(false); 1247 errorPrintStream.setNotifyListeners(false); 1248 } 1249 try 1250 { 1251 if (isServerRunning()) 1252 { 1253 if (isLocal() || importLDIF) 1254 { 1255 returnCode = ImportLDIF.mainImportLDIF(args, false, outPrintStream, errorPrintStream); 1256 } 1257 else 1258 { 1259 returnCode = LDAPModify.mainModify(args, false, outPrintStream, errorPrintStream); 1260 } 1261 } 1262 else 1263 { 1264 returnCode = executeCommandLine(getDataCommandLineName(), args); 1265 } 1266 } 1267 finally 1268 { 1269 outPrintStream.setNotifyListeners(true); 1270 errorPrintStream.setNotifyListeners(true); 1271 } 1272 1273 if (returnCode != 0) 1274 { 1275 state = State.FINISHED_WITH_ERROR; 1276 } 1277 else 1278 { 1279 if (createBaseEntry || (!isLocal() && generateData)) 1280 { 1281 SwingUtilities.invokeLater(new Runnable() 1282 { 1283 @Override 1284 public void run() 1285 { 1286 progressDialog.appendProgressHtml(Utilities.getProgressDone(ColorAndFontConstants.progressFont)); 1287 } 1288 }); 1289 } 1290 state = State.FINISHED_SUCCESSFULLY; 1291 } 1292 } 1293 } 1294 1295 @Override 1296 protected String getCommandLinePath() 1297 { 1298 return null; 1299 } 1300 1301 @Override 1302 protected List<String> getCommandLineArguments() 1303 { 1304 return new ArrayList<>(); 1305 } 1306 1307 private String getConfigCommandLineFullPath() 1308 { 1309 return isServerRunning() ? getCommandLinePath("dsconfig") : null; 1310 } 1311 1312 private List<String> getDSConfigCommandLineArguments() 1313 { 1314 List<String> args = new ArrayList<>(); 1315 if (isServerRunning()) 1316 { 1317 if (isNewBackend()) 1318 { 1319 args.add("create-backend"); 1320 args.add("--backend-name"); 1321 args.add(getBackendName()); 1322 args.add("--set"); 1323 args.add("base-dn:" + newBaseDN); 1324 args.add("--set"); 1325 args.add("enabled:true"); 1326 args.add("--type"); 1327 args.add(BackendTypeHelper.filterSchemaBackendName(getSelectedBackendType().getBackend().getName())); 1328 } 1329 else 1330 { 1331 args.add("set-backend-prop"); 1332 args.add("--backend-name"); 1333 args.add(getBackendName()); 1334 args.add("--add"); 1335 args.add("base-dn:" + newBaseDN); 1336 } 1337 args.addAll(getConnectionCommandLineArguments()); 1338 args.add(getNoPropertiesFileArgument()); 1339 args.add("--no-prompt"); 1340 } 1341 return args; 1342 } 1343 1344 @Override 1345 public void runTask() 1346 { 1347 state = State.RUNNING; 1348 lastException = null; 1349 1350 try 1351 { 1352 if (isServerRunning()) 1353 { 1354 updateConfigurationOnline(); 1355 } 1356 else 1357 { 1358 updateConfigurationOffline(); 1359 } 1360 updateData(); 1361 } 1362 catch (Throwable t) 1363 { 1364 lastException = t; 1365 state = State.FINISHED_WITH_ERROR; 1366 } 1367 } 1368 1369 @Override 1370 public Set<String> getBackends() 1371 { 1372 return backendSet; 1373 } 1374 } 1375}