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