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