001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2009-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2012-2015 ForgeRock AS 026 */ 027 028package org.opends.guitools.controlpanel.ui; 029 030import static org.opends.messages.AdminToolMessages.*; 031import static com.forgerock.opendj.cli.Utils.isDN; 032 033import java.awt.Component; 034import java.awt.GridBagConstraints; 035import java.awt.event.ActionEvent; 036import java.awt.event.ActionListener; 037import java.io.IOException; 038import java.util.ArrayList; 039import java.util.List; 040 041import javax.naming.ldap.InitialLdapContext; 042import javax.swing.JButton; 043import javax.swing.JLabel; 044import javax.swing.JPasswordField; 045import javax.swing.JTextField; 046import javax.swing.event.DocumentEvent; 047import javax.swing.event.DocumentListener; 048 049import org.opends.guitools.controlpanel.browser.BrowserController; 050import org.opends.guitools.controlpanel.datamodel.CustomSearchResult; 051import org.opends.guitools.controlpanel.ui.nodes.BasicNode; 052import org.opends.guitools.controlpanel.util.BackgroundTask; 053import org.opends.guitools.controlpanel.util.LDAPEntryReader; 054import org.opends.guitools.controlpanel.util.Utilities; 055import org.forgerock.i18n.LocalizableMessage; 056import org.opends.server.types.DN; 057import org.opends.server.types.DirectoryException; 058import org.opends.server.util.Base64; 059import org.opends.server.util.LDIFException; 060import org.opends.server.util.ServerConstants; 061 062/** 063 * The panel used to duplicate an entry. 064 * 065 */ 066public class DuplicateEntryPanel extends AbstractNewEntryPanel 067{ 068 private static final long serialVersionUID = -9879879123123123L; 069 private JLabel lName; 070 private JTextField name; 071 private JLabel lParentDN; 072 private JTextField parentDN; 073 private JLabel lPassword; 074 private JPasswordField password = Utilities.createPasswordField(25); 075 private JLabel lconfirmPassword; 076 private JPasswordField confirmPassword = Utilities.createPasswordField(25); 077 private JLabel lPasswordInfo; 078 private JLabel dn; 079 080 private GenericDialog browseDlg; 081 private LDAPEntrySelectionPanel browsePanel; 082 083 private CustomSearchResult entryToDuplicate; 084 private String rdnAttribute; 085 086 /** 087 * Default constructor. 088 * 089 */ 090 public DuplicateEntryPanel() 091 { 092 super(); 093 createLayout(); 094 } 095 096 /** {@inheritDoc} */ 097 public Component getPreferredFocusComponent() 098 { 099 return name; 100 } 101 102 /** {@inheritDoc} */ 103 public boolean requiresScroll() 104 { 105 return true; 106 } 107 108 /** {@inheritDoc} */ 109 public void setParent(BasicNode parentNode, BrowserController controller) 110 { 111 throw new IllegalArgumentException("this method must not be called"); 112 } 113 114 /** 115 * Sets the entry to be duplicated. 116 * @param node the node to be duplicated. 117 * @param controller the browser controller. 118 */ 119 public void setEntryToDuplicate(BasicNode node, 120 BrowserController controller) 121 { 122 if (node == null) 123 { 124 throw new IllegalArgumentException("node is null."); 125 } 126 127 displayMessage(INFO_CTRL_PANEL_READING_SUMMARY.get()); 128 setEnabledOK(false); 129 130 entryToDuplicate = null; 131 super.controller = controller; 132 133 DN aParentDN; 134 String aRdn; 135 try 136 { 137 DN nodeDN = DN.valueOf(node.getDN()); 138 if (nodeDN.isRootDN()) 139 { 140 aParentDN = nodeDN; 141 aRdn = "(1)"; 142 } 143 else 144 { 145 aParentDN = nodeDN.parent(); 146 aRdn = nodeDN.rdn().getAttributeValue(0) + "-1"; 147 } 148 } 149 catch (DirectoryException de) 150 { 151 throw new IllegalStateException("Unexpected error decoding dn: '"+ 152 node.getDN()+"' error: "+de, de); 153 } 154 parentDN.setText(aParentDN != null ? aParentDN.toString() : ""); 155 name.setText(aRdn); 156 password.setText(""); 157 confirmPassword.setText(""); 158 159 readEntry(node); 160 } 161 162 /** {@inheritDoc} */ 163 protected LocalizableMessage getProgressDialogTitle() 164 { 165 return INFO_CTRL_PANEL_DUPLICATE_ENTRY_TITLE.get(); 166 } 167 168 /** {@inheritDoc} */ 169 public LocalizableMessage getTitle() 170 { 171 return INFO_CTRL_PANEL_DUPLICATE_ENTRY_TITLE.get(); 172 } 173 174 /** 175 * Creates the layout of the panel (but the contents are not populated here). 176 */ 177 private void createLayout() 178 { 179 GridBagConstraints gbc = new GridBagConstraints(); 180 gbc.gridx = 0; 181 gbc.gridy = 0; 182 183 addErrorPane(gbc); 184 185 gbc.gridy ++; 186 gbc.gridwidth = 1; 187 gbc.weightx = 0.0; 188 gbc.weighty = 0.0; 189 gbc.fill = GridBagConstraints.HORIZONTAL; 190 191 gbc.gridx = 0; 192 gbc.insets.left = 0; 193 lName = Utilities.createPrimaryLabel( 194 INFO_CTRL_PANEL_DUPLICATE_ENTRY_NAME_LABEL.get()); 195 add(lName, gbc); 196 name = Utilities.createTextField("", 30); 197 gbc.weightx = 1.0; 198 gbc.gridwidth = 2; 199 gbc.weighty = 0.0; 200 gbc.insets.left = 10; 201 gbc.gridx = 1; 202 add(name, gbc); 203 204 gbc.gridy ++; 205 gbc.gridx = 0; 206 gbc.insets.top = 10; 207 gbc.insets.left = 0; 208 gbc.gridwidth = 1; 209 gbc.weightx = 0.0; 210 211 gbc.fill = GridBagConstraints.BOTH; 212 lParentDN = Utilities.createPrimaryLabel( 213 INFO_CTRL_PANEL_DUPLICATE_ENTRY_PARENT_DN_LABEL.get()); 214 add(lParentDN, gbc); 215 216 parentDN = Utilities.createTextField("", 30); 217 gbc.weightx = 1.0; 218 gbc.weighty = 0.0; 219 gbc.insets.left = 10; 220 gbc.gridx = 1; 221 add(parentDN, gbc); 222 223 JButton browse = Utilities.createButton( 224 INFO_CTRL_PANEL_BROWSE_BUTTON_LABEL.get()); 225 gbc.weightx = 0.0; 226 gbc.gridx = 2; 227 add(browse, gbc); 228 browse.addActionListener(new ActionListener() 229 { 230 /** {@inheritDoc} */ 231 public void actionPerformed(ActionEvent ev) 232 { 233 browseClicked(); 234 } 235 }); 236 237 gbc.gridwidth = 1; 238 gbc.weightx = 0.0; 239 gbc.gridx = 0; 240 gbc.gridy ++; 241 gbc.insets.left = 0; 242 lPassword = Utilities.createPrimaryLabel( 243 INFO_CTRL_PANEL_DUPLICATE_ENTRY_NEWPASSWORD_LABEL.get()); 244 add(lPassword, gbc); 245 gbc.weightx = 1.0; 246 gbc.gridwidth = 2; 247 gbc.weighty = 0.0; 248 gbc.insets.left = 10; 249 gbc.gridx = 1; 250 add(password, gbc); 251 252 gbc.gridwidth = 1; 253 gbc.weightx = 0.0; 254 gbc.gridx = 0; 255 gbc.gridy ++; 256 gbc.insets.left = 0; 257 lconfirmPassword = Utilities.createPrimaryLabel( 258 INFO_CTRL_PANEL_DUPLICATE_ENTRY_CONFIRMNEWPASSWORD_LABEL.get()); 259 add(lconfirmPassword, gbc); 260 gbc.weightx = 1.0; 261 gbc.gridwidth = 2; 262 gbc.weighty = 0.0; 263 gbc.insets.left = 10; 264 gbc.gridx = 1; 265 add(confirmPassword, gbc); 266 267 gbc.gridx = 0; 268 gbc.gridy ++; 269 gbc.insets.left = 0; 270 lPasswordInfo = Utilities.createInlineHelpLabel( 271 INFO_CTRL_PANEL_DUPLICATE_ENTRY_PASSWORD_INFO.get()); 272 gbc.gridwidth = 3; 273 add(lPasswordInfo, gbc); 274 275 gbc.gridwidth = 1; 276 gbc.gridx = 0; 277 gbc.gridy ++; 278 gbc.insets.left = 0; 279 add(Utilities.createPrimaryLabel(INFO_CTRL_PANEL_DUPLICATE_ENTRY_DN.get()), 280 gbc); 281 dn = Utilities.createDefaultLabel(); 282 283 gbc.gridx = 1; 284 gbc.gridwidth = 2; 285 gbc.insets.left = 10; 286 add(dn, gbc); 287 288 DocumentListener listener = new DocumentListener() 289 { 290 /** {@inheritDoc} */ 291 public void insertUpdate(DocumentEvent ev) 292 { 293 updateDNValue(); 294 } 295 296 /** {@inheritDoc} */ 297 public void changedUpdate(DocumentEvent ev) 298 { 299 insertUpdate(ev); 300 } 301 302 /** {@inheritDoc} */ 303 public void removeUpdate(DocumentEvent ev) 304 { 305 insertUpdate(ev); 306 } 307 }; 308 name.getDocument().addDocumentListener(listener); 309 parentDN.getDocument().addDocumentListener(listener); 310 311 addBottomGlue(gbc); 312 } 313 314 /** {@inheritDoc} */ 315 protected void checkSyntax(ArrayList<LocalizableMessage> errors) 316 { 317 int origSize = errors.size(); 318 String name = this.name.getText().trim(); 319 setPrimaryValid(lName); 320 setPrimaryValid(lParentDN); 321 if (name.length() == 0) 322 { 323 errors.add(ERR_CTRL_PANEL_DUPLICATE_ENTRY_NAME_EMPTY.get()); 324 setPrimaryInvalid(lName); 325 } 326 String parentDN = this.parentDN.getText().trim(); 327 if (!isDN(parentDN)) 328 { 329 errors.add(ERR_CTRL_PANEL_DUPLICATE_ENTRY_PARENT_DN_NOT_VALID.get()); 330 setPrimaryInvalid(lParentDN); 331 } 332 else if (!entryExists(parentDN)) 333 { 334 errors.add(ERR_CTRL_PANEL_DUPLICATE_ENTRY_PARENT_DOES_NOT_EXIST.get()); 335 setPrimaryInvalid(lParentDN); 336 } 337 338 char[] pwd1 = password.getPassword(); 339 char[] pwd2 = confirmPassword.getPassword(); 340 String sPwd1 = new String(pwd1); 341 String sPwd2 = new String(pwd2); 342 if (!sPwd1.equals(sPwd2)) 343 { 344 errors.add(ERR_CTRL_PANEL_PASSWORD_DO_NOT_MATCH.get()); 345 } 346 347 if (errors.size() == origSize) 348 { 349 try 350 { 351 getEntry(); 352 } 353 catch (IOException ioe) 354 { 355 errors.add(ERR_CTRL_PANEL_ERROR_CHECKING_ENTRY.get(ioe)); 356 } 357 catch (LDIFException le) 358 { 359 errors.add(le.getMessageObject()); 360 } 361 } 362 } 363 364 /** {@inheritDoc} */ 365 protected String getLDIF() 366 { 367 String dn = this.dn.getText(); 368 StringBuilder sb = new StringBuilder(); 369 sb.append("dn: ").append(dn); 370 for (String attrName : entryToDuplicate.getAttributeNames()) 371 { 372 List<Object> values = entryToDuplicate.getAttributeValues(attrName); 373 if (attrName.equalsIgnoreCase(ServerConstants.ATTR_USER_PASSWORD)) 374 { 375 sb.append("\n"); 376 String pwd = new String(password.getPassword()); 377 if (!pwd.isEmpty()) 378 { 379 sb.append(attrName).append(": ").append(pwd); 380 } 381 } 382 else if (!attrName.equalsIgnoreCase(rdnAttribute)) 383 { 384 if (!ViewEntryPanel.isEditable(attrName, 385 getInfo().getServerDescriptor().getSchema())) 386 { 387 continue; 388 } 389 for (Object value : values) 390 { 391 sb.append("\n"); 392 if (value instanceof byte[]) 393 { 394 final String base64 = Base64.encode((byte[]) value); 395 sb.append(attrName).append(":: ").append(base64); 396 } 397 else 398 { 399 sb.append(attrName).append(": ").append(value); 400 } 401 } 402 } 403 else 404 { 405 String newValue; 406 try 407 { 408 DN theDN = DN.valueOf(dn); 409 newValue = theDN.rdn().getAttributeValue(0).toString(); 410 } 411 catch (DirectoryException de) 412 { 413 throw new IllegalStateException("Unexpected error with dn: '"+dn+ 414 "' "+de, de); 415 } 416 if (values.size() == 1) 417 { 418 sb.append("\n"); 419 sb.append(attrName).append(": ").append(newValue); 420 } 421 else 422 { 423 String oldValue; 424 try 425 { 426 DN oldDN = DN.valueOf(entryToDuplicate.getDN()); 427 oldValue = oldDN.rdn().getAttributeValue(0).toString(); 428 } 429 catch (DirectoryException de) 430 { 431 throw new IllegalStateException("Unexpected error with dn: '"+ 432 entryToDuplicate.getDN()+"' "+de, de); 433 } 434 for (Object value : values) 435 { 436 sb.append("\n"); 437 if (oldValue.equals(value)) 438 { 439 sb.append(attrName).append(": ").append(newValue); 440 } 441 else 442 { 443 sb.append(attrName).append(": ").append(value); 444 } 445 } 446 } 447 } 448 } 449 return sb.toString(); 450 } 451 452 private void browseClicked() 453 { 454 if (browseDlg == null) 455 { 456 browsePanel = new LDAPEntrySelectionPanel(); 457 browsePanel.setTitle(INFO_CTRL_PANEL_CHOOSE_PARENT_ENTRY_DN.get()); 458 browsePanel.setFilter( 459 LDAPEntrySelectionPanel.Filter.DEFAULT); 460 browsePanel.setMultipleSelection(false); 461 browsePanel.setInfo(getInfo()); 462 browseDlg = new GenericDialog(Utilities.getFrame(this), 463 browsePanel); 464 Utilities.centerGoldenMean(browseDlg, 465 Utilities.getParentDialog(this)); 466 browseDlg.setModal(true); 467 } 468 browseDlg.setVisible(true); 469 String[] dns = browsePanel.getDNs(); 470 if (dns.length > 0) 471 { 472 for (String dn : dns) 473 { 474 parentDN.setText(dn); 475 } 476 } 477 } 478 479 private void readEntry(final BasicNode node) 480 { 481 final long t1 = System.currentTimeMillis(); 482 BackgroundTask<CustomSearchResult> task = 483 new BackgroundTask<CustomSearchResult>() 484 { 485 public CustomSearchResult processBackgroundTask() throws Throwable 486 { 487 InitialLdapContext ctx = 488 controller.findConnectionForDisplayedEntry(node); 489 LDAPEntryReader reader = new LDAPEntryReader(node.getDN(), ctx); 490 sleepIfRequired(700, t1); 491 return reader.processBackgroundTask(); 492 } 493 494 public void backgroundTaskCompleted(CustomSearchResult sr, 495 Throwable throwable) 496 { 497 if (throwable != null) 498 { 499 LocalizableMessage title = INFO_CTRL_PANEL_ERROR_SEARCHING_ENTRY_TITLE.get(); 500 LocalizableMessage details = 501 ERR_CTRL_PANEL_ERROR_SEARCHING_ENTRY.get(node.getDN(), throwable); 502 displayErrorMessage(title, details); 503 } 504 else 505 { 506 entryToDuplicate = sr; 507 try 508 { 509 DN dn = DN.valueOf(sr.getDN()); 510 rdnAttribute = dn.rdn().getAttributeType(0).getNameOrOID(); 511 512 updateDNValue(); 513 Boolean hasPassword = !sr.getAttributeValues( 514 ServerConstants.ATTR_USER_PASSWORD).isEmpty(); 515 lPassword.setVisible(hasPassword); 516 password.setVisible(hasPassword); 517 lconfirmPassword.setVisible(hasPassword); 518 confirmPassword.setVisible(hasPassword); 519 lPasswordInfo.setVisible(hasPassword); 520 displayMainPanel(); 521 setEnabledOK(true); 522 } 523 catch (DirectoryException de) 524 { 525 displayErrorMessage(INFO_CTRL_PANEL_ERROR_DIALOG_TITLE.get(), 526 de.getMessageObject()); 527 } 528 } 529 } 530 }; 531 task.startBackgroundTask(); 532 } 533 534 private void updateDNValue() 535 { 536 String value = name.getText().trim(); 537 // If it takes time to read the entry, the rdnAttribute might not be initialized yet. Don't try to use it then. 538 if (value.length() > 0 && rdnAttribute != null) 539 { 540 String rdn = Utilities.getRDNString(rdnAttribute, value); 541 dn.setText(rdn+","+parentDN.getText().trim()); 542 } 543 else 544 { 545 dn.setText(","+parentDN.getText().trim()); 546 } 547 } 548 549 private void sleepIfRequired(long sleepTime, long startTime) 550 { 551 long tSleep = sleepTime - (System.currentTimeMillis() - startTime); 552 if (tSleep > 0) 553 { 554 try 555 { 556 Thread.sleep(tSleep); 557 } 558 catch (Throwable t) 559 { 560 } 561 } 562 } 563}