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 JButton browse; 079 private JLabel dn; 080 081 private GenericDialog browseDlg; 082 private LDAPEntrySelectionPanel browsePanel; 083 084 private CustomSearchResult entryToDuplicate; 085 private String rdnAttribute; 086 087 /** 088 * Default constructor. 089 * 090 */ 091 public DuplicateEntryPanel() 092 { 093 super(); 094 createLayout(); 095 } 096 097 /** {@inheritDoc} */ 098 public Component getPreferredFocusComponent() 099 { 100 return name; 101 } 102 103 /** {@inheritDoc} */ 104 public boolean requiresScroll() 105 { 106 return true; 107 } 108 109 /** {@inheritDoc} */ 110 public void setParent(BasicNode parentNode, BrowserController controller) 111 { 112 throw new IllegalArgumentException("this method must not be called"); 113 } 114 115 /** 116 * Sets the entry to be duplicated. 117 * @param node the node to be duplicated. 118 * @param controller the browser controller. 119 */ 120 public void setEntryToDuplicate(BasicNode node, 121 BrowserController controller) 122 { 123 if (node == null) 124 { 125 throw new IllegalArgumentException("node is null."); 126 } 127 128 displayMessage(INFO_CTRL_PANEL_READING_SUMMARY.get()); 129 setEnabledOK(false); 130 131 entryToDuplicate = null; 132 super.controller = controller; 133 134 DN aParentDN; 135 String aRdn; 136 try 137 { 138 DN nodeDN = DN.valueOf(node.getDN()); 139 if (nodeDN.isRootDN()) 140 { 141 aParentDN = nodeDN; 142 aRdn = "(1)"; 143 } 144 else 145 { 146 aParentDN = nodeDN.parent(); 147 aRdn = nodeDN.rdn().getAttributeValue(0) + "-1"; 148 } 149 } 150 catch (DirectoryException de) 151 { 152 throw new IllegalStateException("Unexpected error decoding dn: '"+ 153 node.getDN()+"' error: "+de, de); 154 } 155 parentDN.setText(aParentDN.toString()); 156 name.setText(aRdn); 157 password.setText(""); 158 confirmPassword.setText(""); 159 160 readEntry(node); 161 } 162 163 /** {@inheritDoc} */ 164 protected LocalizableMessage getProgressDialogTitle() 165 { 166 return INFO_CTRL_PANEL_DUPLICATE_ENTRY_TITLE.get(); 167 } 168 169 /** {@inheritDoc} */ 170 public LocalizableMessage getTitle() 171 { 172 return INFO_CTRL_PANEL_DUPLICATE_ENTRY_TITLE.get(); 173 } 174 175 /** 176 * Creates the layout of the panel (but the contents are not populated here). 177 */ 178 private void createLayout() 179 { 180 GridBagConstraints gbc = new GridBagConstraints(); 181 gbc.gridx = 0; 182 gbc.gridy = 0; 183 184 addErrorPane(gbc); 185 186 gbc.gridy ++; 187 gbc.gridwidth = 1; 188 gbc.weightx = 0.0; 189 gbc.weighty = 0.0; 190 gbc.fill = GridBagConstraints.HORIZONTAL; 191 192 gbc.gridx = 0; 193 gbc.insets.left = 0; 194 lName = Utilities.createPrimaryLabel( 195 INFO_CTRL_PANEL_DUPLICATE_ENTRY_NAME_LABEL.get()); 196 add(lName, gbc); 197 name = Utilities.createTextField("", 30); 198 gbc.weightx = 1.0; 199 gbc.gridwidth = 2; 200 gbc.weighty = 0.0; 201 gbc.insets.left = 10; 202 gbc.gridx = 1; 203 add(name, gbc); 204 205 gbc.gridy ++; 206 gbc.gridx = 0; 207 gbc.insets.top = 10; 208 gbc.insets.left = 0; 209 gbc.gridwidth = 1; 210 gbc.weightx = 0.0; 211 212 gbc.fill = GridBagConstraints.BOTH; 213 lParentDN = Utilities.createPrimaryLabel( 214 INFO_CTRL_PANEL_DUPLICATE_ENTRY_PARENT_DN_LABEL.get()); 215 add(lParentDN, gbc); 216 217 parentDN = Utilities.createTextField("", 30); 218 gbc.weightx = 1.0; 219 gbc.weighty = 0.0; 220 gbc.insets.left = 10; 221 gbc.gridx = 1; 222 add(parentDN, gbc); 223 224 browse = Utilities.createButton( 225 INFO_CTRL_PANEL_BROWSE_BUTTON_LABEL.get()); 226 gbc.weightx = 0.0; 227 gbc.gridx = 2; 228 add(browse, gbc); 229 browse.addActionListener(new ActionListener() 230 { 231 /** {@inheritDoc} */ 232 public void actionPerformed(ActionEvent ev) 233 { 234 browseClicked(); 235 } 236 }); 237 238 gbc.gridwidth = 1; 239 gbc.weightx = 0.0; 240 gbc.gridx = 0; 241 gbc.gridy ++; 242 gbc.insets.left = 0; 243 lPassword = Utilities.createPrimaryLabel( 244 INFO_CTRL_PANEL_DUPLICATE_ENTRY_NEWPASSWORD_LABEL.get()); 245 add(lPassword, gbc); 246 gbc.weightx = 1.0; 247 gbc.gridwidth = 2; 248 gbc.weighty = 0.0; 249 gbc.insets.left = 10; 250 gbc.gridx = 1; 251 add(password, gbc); 252 253 gbc.gridwidth = 1; 254 gbc.weightx = 0.0; 255 gbc.gridx = 0; 256 gbc.gridy ++; 257 gbc.insets.left = 0; 258 lconfirmPassword = Utilities.createPrimaryLabel( 259 INFO_CTRL_PANEL_DUPLICATE_ENTRY_CONFIRMNEWPASSWORD_LABEL.get()); 260 add(lconfirmPassword, gbc); 261 gbc.weightx = 1.0; 262 gbc.gridwidth = 2; 263 gbc.weighty = 0.0; 264 gbc.insets.left = 10; 265 gbc.gridx = 1; 266 add(confirmPassword, gbc); 267 268 gbc.gridx = 0; 269 gbc.gridy ++; 270 gbc.insets.left = 0; 271 lPasswordInfo = Utilities.createInlineHelpLabel( 272 INFO_CTRL_PANEL_DUPLICATE_ENTRY_PASSWORD_INFO.get()); 273 gbc.gridwidth = 3; 274 add(lPasswordInfo, gbc); 275 276 gbc.gridwidth = 1; 277 gbc.gridx = 0; 278 gbc.gridy ++; 279 gbc.insets.left = 0; 280 add(Utilities.createPrimaryLabel(INFO_CTRL_PANEL_DUPLICATE_ENTRY_DN.get()), 281 gbc); 282 dn = Utilities.createDefaultLabel(); 283 284 gbc.gridx = 1; 285 gbc.gridwidth = 2; 286 gbc.insets.left = 10; 287 add(dn, gbc); 288 289 DocumentListener listener = new DocumentListener() 290 { 291 /** {@inheritDoc} */ 292 public void insertUpdate(DocumentEvent ev) 293 { 294 updateDNValue(); 295 } 296 297 /** {@inheritDoc} */ 298 public void changedUpdate(DocumentEvent ev) 299 { 300 insertUpdate(ev); 301 } 302 303 /** {@inheritDoc} */ 304 public void removeUpdate(DocumentEvent ev) 305 { 306 insertUpdate(ev); 307 } 308 }; 309 name.getDocument().addDocumentListener(listener); 310 parentDN.getDocument().addDocumentListener(listener); 311 312 addBottomGlue(gbc); 313 } 314 315 /** {@inheritDoc} */ 316 protected void checkSyntax(ArrayList<LocalizableMessage> errors) 317 { 318 int origSize = errors.size(); 319 String name = this.name.getText().trim(); 320 setPrimaryValid(lName); 321 setPrimaryValid(lParentDN); 322 if (name.length() == 0) 323 { 324 errors.add(ERR_CTRL_PANEL_DUPLICATE_ENTRY_NAME_EMPTY.get()); 325 setPrimaryInvalid(lName); 326 } 327 String parentDN = this.parentDN.getText().trim(); 328 if (!isDN(parentDN)) 329 { 330 errors.add(ERR_CTRL_PANEL_DUPLICATE_ENTRY_PARENT_DN_NOT_VALID.get()); 331 setPrimaryInvalid(lParentDN); 332 } 333 else if (!entryExists(parentDN)) 334 { 335 errors.add(ERR_CTRL_PANEL_DUPLICATE_ENTRY_PARENT_DOES_NOT_EXIST.get()); 336 setPrimaryInvalid(lParentDN); 337 } 338 339 char[] pwd1 = password.getPassword(); 340 char[] pwd2 = confirmPassword.getPassword(); 341 String sPwd1 = new String(pwd1); 342 String sPwd2 = new String(pwd2); 343 if (!sPwd1.equals(sPwd2)) 344 { 345 errors.add(ERR_CTRL_PANEL_PASSWORD_DO_NOT_MATCH.get()); 346 } 347 348 if (errors.size() == origSize) 349 { 350 try 351 { 352 getEntry(); 353 } 354 catch (IOException ioe) 355 { 356 errors.add(ERR_CTRL_PANEL_ERROR_CHECKING_ENTRY.get(ioe)); 357 } 358 catch (LDIFException le) 359 { 360 errors.add(le.getMessageObject()); 361 } 362 } 363 } 364 365 /** {@inheritDoc} */ 366 protected String getLDIF() 367 { 368 String dn = this.dn.getText(); 369 StringBuilder sb = new StringBuilder(); 370 sb.append("dn: ").append(dn); 371 for (String attrName : entryToDuplicate.getAttributeNames()) 372 { 373 List<Object> values = entryToDuplicate.getAttributeValues(attrName); 374 if (attrName.equalsIgnoreCase(ServerConstants.ATTR_USER_PASSWORD)) 375 { 376 sb.append("\n"); 377 String pwd = new String(password.getPassword()); 378 if (!pwd.isEmpty()) 379 { 380 sb.append(attrName).append(": ").append(pwd); 381 } 382 } 383 else if (!attrName.equalsIgnoreCase(rdnAttribute)) 384 { 385 if (!ViewEntryPanel.isEditable(attrName, 386 getInfo().getServerDescriptor().getSchema())) 387 { 388 continue; 389 } 390 for (Object value : values) 391 { 392 sb.append("\n"); 393 if (value instanceof byte[]) 394 { 395 final String base64 = Base64.encode((byte[]) value); 396 sb.append(attrName).append(":: ").append(base64); 397 } 398 else 399 { 400 sb.append(attrName).append(": ").append(value); 401 } 402 } 403 } 404 else 405 { 406 String newValue = null; 407 try 408 { 409 DN theDN = DN.valueOf(dn); 410 newValue = theDN.rdn().getAttributeValue(0).toString(); 411 } 412 catch (DirectoryException de) 413 { 414 throw new IllegalStateException("Unexpected error with dn: '"+dn+ 415 "' "+de, de); 416 } 417 if (values.size() == 1) 418 { 419 sb.append("\n"); 420 sb.append(attrName).append(": ").append(newValue); 421 } 422 else 423 { 424 String oldValue = null; 425 try 426 { 427 DN oldDN = DN.valueOf(entryToDuplicate.getDN()); 428 oldValue = oldDN.rdn().getAttributeValue(0).toString(); 429 } 430 catch (DirectoryException de) 431 { 432 throw new IllegalStateException("Unexpected error with dn: '"+ 433 entryToDuplicate.getDN()+"' "+de, de); 434 } 435 for (Object value : values) 436 { 437 sb.append("\n"); 438 if (oldValue.equals(value)) 439 { 440 sb.append(attrName).append(": ").append(newValue); 441 } 442 else 443 { 444 sb.append(attrName).append(": ").append(value); 445 } 446 } 447 } 448 } 449 } 450 return sb.toString(); 451 } 452 453 private void browseClicked() 454 { 455 if (browseDlg == null) 456 { 457 browsePanel = new LDAPEntrySelectionPanel(); 458 browsePanel.setTitle(INFO_CTRL_PANEL_CHOOSE_PARENT_ENTRY_DN.get()); 459 browsePanel.setFilter( 460 LDAPEntrySelectionPanel.Filter.DEFAULT); 461 browsePanel.setMultipleSelection(false); 462 browsePanel.setInfo(getInfo()); 463 browseDlg = new GenericDialog(Utilities.getFrame(this), 464 browsePanel); 465 Utilities.centerGoldenMean(browseDlg, 466 Utilities.getParentDialog(this)); 467 browseDlg.setModal(true); 468 } 469 browseDlg.setVisible(true); 470 String[] dns = browsePanel.getDNs(); 471 if (dns.length > 0) 472 { 473 for (String dn : dns) 474 { 475 parentDN.setText(dn); 476 } 477 } 478 } 479 480 private void readEntry(final BasicNode node) 481 { 482 final long t1 = System.currentTimeMillis(); 483 BackgroundTask<CustomSearchResult> task = 484 new BackgroundTask<CustomSearchResult>() 485 { 486 public CustomSearchResult processBackgroundTask() throws Throwable 487 { 488 InitialLdapContext ctx = 489 controller.findConnectionForDisplayedEntry(node); 490 LDAPEntryReader reader = new LDAPEntryReader(node.getDN(), ctx); 491 sleepIfRequired(700, t1); 492 return reader.processBackgroundTask(); 493 } 494 495 public void backgroundTaskCompleted(CustomSearchResult sr, 496 Throwable throwable) 497 { 498 if (throwable != null) 499 { 500 LocalizableMessage title = INFO_CTRL_PANEL_ERROR_SEARCHING_ENTRY_TITLE.get(); 501 LocalizableMessage details = 502 ERR_CTRL_PANEL_ERROR_SEARCHING_ENTRY.get(node.getDN(), throwable); 503 displayErrorMessage(title, details); 504 } 505 else 506 { 507 entryToDuplicate = sr; 508 try 509 { 510 DN dn = DN.valueOf(sr.getDN()); 511 rdnAttribute = dn.rdn().getAttributeType(0).getNameOrOID(); 512 513 updateDNValue(); 514 Boolean hasPassword = !sr.getAttributeValues( 515 ServerConstants.ATTR_USER_PASSWORD).isEmpty(); 516 lPassword.setVisible(hasPassword); 517 password.setVisible(hasPassword); 518 lconfirmPassword.setVisible(hasPassword); 519 confirmPassword.setVisible(hasPassword); 520 lPasswordInfo.setVisible(hasPassword); 521 displayMainPanel(); 522 setEnabledOK(true); 523 } 524 catch (DirectoryException de) 525 { 526 displayErrorMessage(INFO_CTRL_PANEL_ERROR_DIALOG_TITLE.get(), 527 de.getMessageObject()); 528 } 529 } 530 } 531 }; 532 task.startBackgroundTask(); 533 } 534 535 private void updateDNValue() 536 { 537 String value = name.getText().trim(); 538 if (value.length() > 0) 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}