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 Sun Microsystems, Inc. 015 * Portions Copyright 2013-2016 ForgeRock AS. 016 */ 017package org.opends.guitools.controlpanel.task; 018 019import static org.forgerock.opendj.ldap.ModificationType.*; 020import static org.forgerock.util.Utils.*; 021import static org.opends.messages.AdminToolMessages.*; 022 023import java.io.File; 024import java.util.ArrayList; 025import java.util.Collection; 026import java.util.Collections; 027import java.util.HashSet; 028import java.util.Iterator; 029import java.util.LinkedHashMap; 030import java.util.LinkedHashSet; 031import java.util.List; 032import java.util.Map; 033import java.util.Set; 034 035import javax.naming.NamingException; 036import javax.naming.directory.BasicAttribute; 037import javax.naming.directory.DirContext; 038import javax.naming.directory.ModificationItem; 039import javax.swing.SwingUtilities; 040 041import org.forgerock.i18n.LocalizableMessage; 042import org.forgerock.opendj.ldap.schema.AttributeType; 043import org.forgerock.opendj.ldap.schema.MatchingRule; 044import org.forgerock.opendj.ldap.schema.ObjectClass; 045import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo; 046import org.opends.guitools.controlpanel.datamodel.SomeSchemaElement; 047import org.opends.guitools.controlpanel.ui.ColorAndFontConstants; 048import org.opends.guitools.controlpanel.ui.ProgressDialog; 049import org.opends.guitools.controlpanel.util.Utilities; 050import org.opends.server.config.ConfigConstants; 051import org.opends.server.core.DirectoryServer; 052import org.opends.server.types.Attribute; 053import org.opends.server.types.Attributes; 054import org.opends.server.types.DirectoryException; 055import org.opends.server.types.Entry; 056import org.opends.server.types.ExistingFileBehavior; 057import org.opends.server.types.LDIFExportConfig; 058import org.opends.server.types.LDIFImportConfig; 059import org.opends.server.types.Modification; 060import org.opends.server.types.OpenDsException; 061import org.opends.server.util.LDIFReader; 062import org.opends.server.util.LDIFWriter; 063import org.opends.server.util.ServerConstants; 064import org.opends.server.util.StaticUtils; 065 066/** 067 * An abstract class used to re-factor some code between the different tasks 068 * that create elements in the schema. 069 */ 070public class NewSchemaElementsTask extends Task 071{ 072 private final Set<ObjectClass> ocsToAdd = new LinkedHashSet<>(); 073 private final Set<AttributeType> attrsToAdd = new LinkedHashSet<>(); 074 075 /** 076 * Constructor of the task. 077 * 078 * @param info 079 * the control panel information. 080 * @param dlg 081 * the progress dialog where the task progress will be displayed. 082 * @param ocsToAdd 083 * the object classes that must be created in order. 084 * @param attrsToAdd 085 * the attributes that must be created in order. 086 */ 087 public NewSchemaElementsTask( 088 ControlPanelInfo info, ProgressDialog dlg, Set<ObjectClass> ocsToAdd, Set<AttributeType> attrsToAdd) 089 { 090 super(info, dlg); 091 this.ocsToAdd.addAll(ocsToAdd); 092 this.attrsToAdd.addAll(attrsToAdd); 093 } 094 095 @Override 096 public Set<String> getBackends() 097 { 098 return Collections.emptySet(); 099 } 100 101 @Override 102 public boolean canLaunch(Task taskToBeLaunched, Collection<LocalizableMessage> incompatibilityReasons) 103 { 104 Type taskTypeToBeLaunched = taskToBeLaunched.getType(); 105 if (state == State.RUNNING && 106 (taskTypeToBeLaunched == Task.Type.DELETE_SCHEMA_ELEMENT 107 || taskTypeToBeLaunched == Task.Type.MODIFY_SCHEMA_ELEMENT 108 || taskTypeToBeLaunched == Task.Type.NEW_SCHEMA_ELEMENT)) 109 { 110 incompatibilityReasons.add(getIncompatibilityMessage(this, taskToBeLaunched)); 111 return false; 112 } 113 return true; 114 } 115 116 @Override 117 public void runTask() 118 { 119 state = State.RUNNING; 120 lastException = null; 121 122 try 123 { 124 updateSchema(); 125 state = State.FINISHED_SUCCESSFULLY; 126 } 127 catch (Throwable t) 128 { 129 lastException = t; 130 state = State.FINISHED_WITH_ERROR; 131 } 132 } 133 134 @Override 135 public Type getType() 136 { 137 return Type.NEW_SCHEMA_ELEMENT; 138 } 139 140 @Override 141 public LocalizableMessage getTaskDescription() 142 { 143 if (attrsToAdd.size() == 1 && ocsToAdd.isEmpty()) 144 { 145 return INFO_CTRL_PANEL_NEW_ATTRIBUTE_TASK_DESCRIPTION.get(attrsToAdd.iterator().next().getNameOrOID()); 146 } 147 else if (ocsToAdd.size() == 1 && attrsToAdd.isEmpty()) 148 { 149 return INFO_CTRL_PANEL_NEW_OBJECTCLASS_TASK_DESCRIPTION.get(ocsToAdd.iterator().next().getNameOrOID()); 150 } 151 else 152 { 153 final List<String> attrNames = getElementsNameOrOID(attributeTypesToSchemaElements(attrsToAdd)); 154 final List<String> ocNames = getElementsNameOrOID(objectClassesToSchemaElements(ocsToAdd)); 155 String attrNamesStr = joinAsString(", ", attrNames); 156 String ocNamesStr = joinAsString(", ", ocNames); 157 if (ocNames.isEmpty()) 158 { 159 return INFO_CTRL_PANEL_NEW_ATTRIBUTES_TASK_DESCRIPTION.get(attrNamesStr); 160 } 161 else if (attrNames.isEmpty()) 162 { 163 return INFO_CTRL_PANEL_NEW_OBJECTCLASSES_TASK_DESCRIPTION.get(ocNamesStr); 164 } 165 else 166 { 167 return INFO_CTRL_PANEL_NEW_SCHEMA_ELEMENTS_TASK_DESCRIPTION.get(attrNamesStr, ocNamesStr); 168 } 169 } 170 } 171 172 private List<String> getElementsNameOrOID(final Collection<SomeSchemaElement> schemaElements) 173 { 174 final List<String> nameOrOIDs = new ArrayList<>(); 175 for (SomeSchemaElement schemaElement : schemaElements) 176 { 177 nameOrOIDs.add(schemaElement.getNameOrOID()); 178 } 179 return nameOrOIDs; 180 } 181 182 /** 183 * Update the schema. 184 * 185 * @throws OpenDsException 186 * if an error occurs. 187 */ 188 private void updateSchema() throws OpenDsException 189 { 190 if (isServerRunning()) 191 { 192 updateSchemaOnline(); 193 } 194 else 195 { 196 updateSchemaOffline(); 197 } 198 } 199 200 @Override 201 protected String getCommandLinePath() 202 { 203 return null; 204 } 205 206 @Override 207 protected List<String> getCommandLineArguments() 208 { 209 return Collections.emptyList(); 210 } 211 212 /** 213 * Add the schema elements one by one: we are not sure that the server will 214 * handle the adds sequentially if we only send one modification. 215 * 216 * @throws OpenDsException 217 */ 218 private void updateSchemaOnline() throws OpenDsException 219 { 220 for (AttributeType attr : attrsToAdd) 221 { 222 addAttributeOnline(attr); 223 appendNewLinesToProgress(); 224 } 225 226 for (ObjectClass oc : ocsToAdd) 227 { 228 addObjectClassOnline(oc); 229 appendNewLinesToProgress(); 230 } 231 } 232 233 private void appendNewLinesToProgress() 234 { 235 SwingUtilities.invokeLater(new Runnable() 236 { 237 @Override 238 public void run() 239 { 240 getProgressDialog().appendProgressHtml(Utilities.applyFont("<br><br>", ColorAndFontConstants.progressFont)); 241 } 242 }); 243 } 244 245 private void updateSchemaOffline() throws OpenDsException 246 { 247 // Group the changes in the same schema file. 248 final Map<String, List<SomeSchemaElement>> mapAttrs = copy(attributeTypesToSchemaElements(attrsToAdd)); 249 final Map<String, List<SomeSchemaElement>> mapClasses = copy(objectClassesToSchemaElements(ocsToAdd)); 250 final Set<String> allFileNames = new LinkedHashSet<>(mapAttrs.keySet()); 251 allFileNames.addAll(mapClasses.keySet()); 252 253 for (String fileName : allFileNames) 254 { 255 List<AttributeType> attrs = schemaElementsToAttributeTypes(get(mapAttrs, fileName)); 256 List<ObjectClass> ocs = schemaElementsToObjectClasses(get(mapClasses, fileName)); 257 258 if ("".equals(fileName)) 259 { 260 fileName = null; 261 } 262 updateSchemaOffline(fileName, attrs, ocs); 263 appendNewLinesToProgress(); 264 } 265 } 266 267 private List<SomeSchemaElement> get(Map<String, List<SomeSchemaElement>> hmElems, String fileName) 268 { 269 List<SomeSchemaElement> elems = hmElems.get(fileName); 270 return elems != null ? elems : Collections.<SomeSchemaElement> emptyList(); 271 } 272 273 private Map<String, List<SomeSchemaElement>> copy(Set<SomeSchemaElement> elemsToAdd) 274 { 275 Map<String, List<SomeSchemaElement>> hmElems = new LinkedHashMap<>(); 276 for (SomeSchemaElement elem : elemsToAdd) 277 { 278 String fileName = elem.getSchemaFile(); 279 if (fileName == null) 280 { 281 fileName = ""; 282 } 283 List<SomeSchemaElement> elems = hmElems.get(fileName); 284 if (elems == null) 285 { 286 elems = new ArrayList<>(); 287 hmElems.put(fileName, elems); 288 } 289 elems.add(elem); 290 } 291 return hmElems; 292 } 293 294 private void addAttributeOnline(final AttributeType attribute) throws OpenDsException 295 { 296 addSchemaElementOnline(new SomeSchemaElement(attribute), 297 INFO_CTRL_PANEL_CREATING_ATTRIBUTE_PROGRESS.get(attribute.getNameOrOID())); 298 } 299 300 private void addObjectClassOnline(final ObjectClass objectClass) throws OpenDsException 301 { 302 addSchemaElementOnline(new SomeSchemaElement(objectClass), 303 INFO_CTRL_PANEL_CREATING_OBJECTCLASS_PROGRESS.get(objectClass.getNameOrOID())); 304 } 305 306 private void addSchemaElementOnline(final SomeSchemaElement schemaElement, final LocalizableMessage progressMsg) 307 throws OpenDsException 308 { 309 SwingUtilities.invokeLater(new Runnable() 310 { 311 @Override 312 public void run() 313 { 314 printEquivalentCommandLineToAddOnline(schemaElement); 315 getProgressDialog().appendProgressHtml( 316 Utilities.getProgressWithPoints(progressMsg, ColorAndFontConstants.progressFont)); 317 } 318 }); 319 try 320 { 321 final BasicAttribute attr = new BasicAttribute(schemaElement.getAttributeName()); 322 attr.add(getElementDefinition(schemaElement)); 323 final ModificationItem mod = new ModificationItem(DirContext.ADD_ATTRIBUTE, attr); 324 getInfo().getConnection().getLdapContext().modifyAttributes( 325 ConfigConstants.DN_DEFAULT_SCHEMA_ROOT, new ModificationItem[] { mod }); 326 } 327 catch (NamingException ne) 328 { 329 throw new OnlineUpdateException(ERR_CTRL_PANEL_ERROR_UPDATING_SCHEMA.get(ne), ne); 330 } 331 notifyConfigurationElementCreated(schemaElement); 332 SwingUtilities.invokeLater(new Runnable() 333 { 334 @Override 335 public void run() 336 { 337 getProgressDialog().appendProgressHtml(Utilities.getProgressDone(ColorAndFontConstants.progressFont)); 338 } 339 }); 340 } 341 342 private String getValueOffline(SomeSchemaElement element) 343 { 344 final Map<String, List<String>> props = element.getExtraProperties(); 345 List<String> previousValues = props.get(ServerConstants.SCHEMA_PROPERTY_FILENAME); 346 element.setExtraPropertySingleValue(null, ServerConstants.SCHEMA_PROPERTY_FILENAME, null); 347 String attributeWithoutFileDefinition = getElementDefinition(element); 348 349 if (previousValues != null && !previousValues.isEmpty()) 350 { 351 element.setExtraPropertyMultipleValues(null, 352 ServerConstants.SCHEMA_PROPERTY_FILENAME, new ArrayList<String>(previousValues)); 353 } 354 return attributeWithoutFileDefinition; 355 } 356 357 private String getElementDefinition(SomeSchemaElement element) 358 { 359 final List<String> names = new ArrayList<>(); 360 for (final String name : element.getNames()) 361 { 362 names.add(StaticUtils.toLowerCase(name)); 363 } 364 return element.isAttributeType() 365 ? getAttributeTypeDefinition(element.getAttributeType(), names) 366 : getObjectClassDefinition(element.getObjectClass(), names); 367 } 368 369 private String getAttributeTypeDefinition(final AttributeType attributeType, final List<String> names) 370 { 371 final StringBuilder buffer = new StringBuilder(); 372 buffer.append("( ").append(attributeType.getOID()); 373 appendCollection(buffer, "NAME", names); 374 appendDescription(buffer, attributeType.getDescription()); 375 appendIfTrue(buffer, " OBSOLETE", attributeType.isObsolete()); 376 377 final AttributeType superiorType = attributeType.getSuperiorType(); 378 final String superiorTypeOID = superiorType != null ? superiorType.getOID() : null; 379 appendIfNotNull(buffer, " SUP ", superiorTypeOID); 380 addMatchingRuleIfNotNull(buffer, " EQUALITY ", attributeType.getEqualityMatchingRule()); 381 addMatchingRuleIfNotNull(buffer, " ORDERING ", attributeType.getOrderingMatchingRule()); 382 addMatchingRuleIfNotNull(buffer, " SUBSTR ", attributeType.getSubstringMatchingRule()); 383 appendIfNotNull(buffer, " SYNTAX ", attributeType.getSyntax().getOID()); 384 appendIfTrue(buffer, " SINGLE-VALUE", attributeType.isSingleValue()); 385 appendIfTrue(buffer, " COLLECTIVE", attributeType.isCollective()); 386 appendIfTrue(buffer, " NO-USER-MODIFICATION", attributeType.isNoUserModification()); 387 appendIfNotNull(buffer, " USAGE ", attributeType.getUsage()); 388 389 final MatchingRule approximateMatchingRule = attributeType.getApproximateMatchingRule(); 390 if (approximateMatchingRule != null) 391 { 392 buffer.append(" ").append(ServerConstants.SCHEMA_PROPERTY_APPROX_RULE).append(" '") 393 .append(approximateMatchingRule.getOID()).append("'"); 394 } 395 appendExtraProperties(buffer, attributeType.getExtraProperties()); 396 buffer.append(")"); 397 398 return buffer.toString(); 399 } 400 401 private void addMatchingRuleIfNotNull(final StringBuilder buffer, final String label, final MatchingRule matchingRule) 402 { 403 if (matchingRule != null) 404 { 405 append(buffer, label, matchingRule.getOID()); 406 } 407 } 408 409 private String getObjectClassDefinition(final ObjectClass objectClass, final List<String> names) 410 { 411 final StringBuilder buffer = new StringBuilder(); 412 buffer.append("( "); 413 buffer.append(objectClass.getOID()); 414 appendCollection(buffer, "NAME", names); 415 appendDescription(buffer, objectClass.getDescription()); 416 appendIfTrue(buffer, " OBSOLETE", objectClass.isObsolete()); 417 appendOIDs(buffer, "SUP", objectClassesToSchemaElements(objectClass.getSuperiorClasses())); 418 appendIfNotNull(buffer, " ", objectClass.getObjectClassType()); 419 appendOIDs(buffer, "MUST", attributeTypesToSchemaElements(objectClass.getDeclaredRequiredAttributes())); 420 appendOIDs(buffer, "MAY", attributeTypesToSchemaElements(objectClass.getDeclaredOptionalAttributes())); 421 appendExtraProperties(buffer, objectClass.getExtraProperties()); 422 buffer.append(")"); 423 424 return buffer.toString(); 425 } 426 427 private void appendOIDs(final StringBuilder buffer, final String label, 428 final Collection<SomeSchemaElement> schemaElements) 429 { 430 if (!schemaElements.isEmpty()) 431 { 432 buffer.append(" ").append(label).append(" ( "); 433 434 final Iterator<SomeSchemaElement> it = schemaElements.iterator(); 435 buffer.append(it.next().getOID()); 436 while (it.hasNext()) 437 { 438 buffer.append(" $ ").append(it.next().getOID()); 439 } 440 buffer.append(" )"); 441 } 442 } 443 444 private Set<SomeSchemaElement> objectClassesToSchemaElements(final Collection<ObjectClass> classes) 445 { 446 Set<SomeSchemaElement> elements = new HashSet<>(); 447 for (ObjectClass objectClass : classes) 448 { 449 elements.add(new SomeSchemaElement(objectClass)); 450 } 451 return elements; 452 } 453 454 private Set<SomeSchemaElement> attributeTypesToSchemaElements(final Collection<AttributeType> types) 455 { 456 Set<SomeSchemaElement> elements = new HashSet<>(); 457 for (AttributeType type : types) 458 { 459 elements.add(new SomeSchemaElement(type)); 460 } 461 return elements; 462 } 463 464 private List<AttributeType> schemaElementsToAttributeTypes(final Collection<SomeSchemaElement> elements) 465 { 466 List<AttributeType> types = new ArrayList<>(); 467 for (SomeSchemaElement element : elements) 468 { 469 types.add(element.getAttributeType()); 470 } 471 return types; 472 } 473 474 private List<ObjectClass> schemaElementsToObjectClasses(final Collection<SomeSchemaElement> elements) 475 { 476 List<ObjectClass> classes = new ArrayList<>(); 477 for (SomeSchemaElement element : elements) 478 { 479 classes.add(element.getObjectClass()); 480 } 481 return classes; 482 } 483 484 private void appendIfTrue(final StringBuilder buffer, final String label, final boolean labelIsActive) 485 { 486 if (labelIsActive) 487 { 488 buffer.append(label); 489 } 490 } 491 492 private void appendIfNotNull(final StringBuilder buffer, final String label, final Object value) 493 { 494 if (value != null) 495 { 496 append(buffer, label, value.toString()); 497 } 498 } 499 500 private void append(final StringBuilder buffer, final String label, final String value) 501 { 502 buffer.append(label).append(value); 503 } 504 505 private void appendDescription(final StringBuilder buffer, final String description) 506 { 507 if (description != null && !description.isEmpty()) 508 { 509 buffer.append(" DESC '"); 510 buffer.append(description); 511 buffer.append("'"); 512 } 513 } 514 515 private void appendExtraProperties( 516 final StringBuilder buffer, final Map<String, List<String>> extraProperties) 517 { 518 for (final Map.Entry<String, List<String>> e : extraProperties.entrySet()) 519 { 520 appendCollection(buffer, e.getKey(), e.getValue()); 521 } 522 } 523 524 private void appendCollection(final StringBuilder buffer, final String property, final Collection<String> values) 525 { 526 final boolean isMultiValued = values.size() > 1; 527 if (!values.isEmpty()) 528 { 529 buffer.append(" ").append(property); 530 buffer.append(isMultiValued ? " ( '" : " '"); 531 final Iterator<String> it = values.iterator(); 532 buffer.append(it.next()).append("' "); 533 while (it.hasNext()) 534 { 535 buffer.append("'").append(it.next()).append("' "); 536 } 537 if (isMultiValued) 538 { 539 buffer.append(")"); 540 } 541 } 542 } 543 544 private void printEquivalentCommandLineToAddOnline(SomeSchemaElement element) 545 { 546 List<String> args = new ArrayList<>(); 547 args.add("-a"); 548 args.addAll(getObfuscatedCommandLineArguments(getConnectionCommandLineArguments(true, true))); 549 args.add(getNoPropertiesFileArgument()); 550 551 final String equivalentCmdLine = getEquivalentCommandLine(getCommandLinePath("ldapmodify"), args); 552 final StringBuilder sb = new StringBuilder(); 553 final String attName = element.getAttributeName(); 554 final String elementId = element.getNameOrOID(); 555 final LocalizableMessage message = element.isAttributeType() 556 ? INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_ADD_ATTRIBUTE_ONLINE.get(elementId) 557 : INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_ADD_OBJECTCLASS_ONLINE.get(elementId); 558 sb.append(message).append("<br><b>") 559 .append(equivalentCmdLine).append("<br>") 560 .append("dn: cn=schema<br>") 561 .append("changetype: modify<br>") 562 .append("add: ").append(attName).append("<br>") 563 .append(attName).append(": ").append(getElementDefinition(element)).append("</b><br><br>"); 564 getProgressDialog().appendProgressHtml(Utilities.applyFont(sb.toString(), ColorAndFontConstants.progressFont)); 565 } 566 567 private void updateSchemaOffline( 568 String file, final List<AttributeType> attributes, final List<ObjectClass> objectClasses) throws OpenDsException 569 { 570 final List<SomeSchemaElement> schemaElements = 571 new ArrayList<SomeSchemaElement>(attributeTypesToSchemaElements(attributes)); 572 schemaElements.addAll(objectClassesToSchemaElements(objectClasses)); 573 if (file == null) 574 { 575 file = ConfigConstants.FILE_USER_SCHEMA_ELEMENTS; 576 } 577 File f = new File(file); 578 if (!f.isAbsolute()) 579 { 580 f = new File(DirectoryServer.getEnvironmentConfig().getSchemaDirectory(), file); 581 } 582 final String fileName = f.getAbsolutePath(); 583 final boolean isSchemaFileDefined = isSchemaFileDefined(fileName); 584 SwingUtilities.invokeLater(new Runnable() 585 { 586 @Override 587 public void run() 588 { 589 final ProgressDialog progressDialog = getProgressDialog(); 590 final String command = equivalentCommandToAddOffline(fileName, isSchemaFileDefined, schemaElements); 591 progressDialog.appendProgressHtml(Utilities.applyFont(command, ColorAndFontConstants.progressFont)); 592 593 if (attributes.size() == 1 && objectClasses.isEmpty()) 594 { 595 String attributeName = attributes.get(0).getNameOrOID(); 596 progressDialog.appendProgressHtml(Utilities.getProgressWithPoints( 597 INFO_CTRL_PANEL_CREATING_ATTRIBUTE_PROGRESS.get(attributeName), ColorAndFontConstants.progressFont)); 598 } 599 else if (objectClasses.size() == 1 && attributes.isEmpty()) 600 { 601 String ocName = objectClasses.get(0).getNameOrOID(); 602 progressDialog.appendProgressHtml(Utilities.getProgressWithPoints( 603 INFO_CTRL_PANEL_CREATING_OBJECTCLASS_PROGRESS.get(ocName), ColorAndFontConstants.progressFont)); 604 } 605 else 606 { 607 progressDialog.appendProgressHtml(Utilities.getProgressWithPoints( 608 INFO_CTRL_PANEL_UPDATING_SCHEMA_FILE_PROGRESS.get(fileName), ColorAndFontConstants.progressFont)); 609 } 610 } 611 }); 612 613 if (isSchemaFileDefined) 614 { 615 updateSchemaFile(fileName, schemaElements); 616 } 617 else 618 { 619 updateSchemaUndefinedFile(fileName, schemaElements); 620 } 621 622 for (SomeSchemaElement schemaElement : schemaElements) 623 { 624 notifyConfigurationElementCreated(schemaElement); 625 } 626 SwingUtilities.invokeLater(new Runnable() 627 { 628 @Override 629 public void run() 630 { 631 getProgressDialog().appendProgressHtml(Utilities.getProgressDone(ColorAndFontConstants.progressFont)); 632 } 633 }); 634 } 635 636 private String equivalentCommandToAddOffline( 637 String schemaFile, boolean isSchemaFileDefined, List<SomeSchemaElement> schemaElements) 638 { 639 List<String> names = getElementsNameOrOID(schemaElements); 640 641 final String namesString = joinAsString(", ", names); 642 final StringBuilder sb = new StringBuilder(); 643 if (isSchemaFileDefined) 644 { 645 sb.append(INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_ADD_SCHEMA_ELEMENT_OFFLINE.get(namesString, schemaFile)) 646 .append("<br><b>"); 647 } 648 else 649 { 650 sb.append(INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_ADD_SCHEMA_ENTRY_OFFLINE.get(namesString, schemaFile)) 651 .append("<br><b>"); 652 for (String line : getSchemaEntryLines()) 653 { 654 sb.append(line); 655 sb.append("<br>"); 656 } 657 } 658 659 for (SomeSchemaElement schemaElement : schemaElements) 660 { 661 sb.append(schemaElement.getAttributeName()).append(": ").append(getValueOffline(schemaElement)).append("<br>"); 662 } 663 sb.append("</b><br><br>"); 664 665 return sb.toString(); 666 } 667 668 /** 669 * Returns whether the file defined in the schema element exists or not. 670 * 671 * @param schemaFile 672 * the path to the schema file. 673 * @return <CODE>true</CODE> if the schema file is defined and 674 * <CODE>false</CODE> otherwise. 675 */ 676 private boolean isSchemaFileDefined(String schemaFile) 677 { 678 try (LDIFReader reader = new LDIFReader(new LDIFImportConfig(schemaFile))) 679 { 680 return reader.readEntry() != null; 681 } 682 catch (Throwable t) 683 { 684 return false; 685 } 686 } 687 688 /** 689 * Returns the list of LDIF lines that are enough to create the entry 690 * containing only the schema element associated with this task. 691 * 692 * @return the list of LDIF lines that are enough to create the entry 693 * containing only the schema element associated with this task. 694 */ 695 private List<String> getSchemaEntryLines() 696 { 697 List<String> lines = new ArrayList<>(); 698 lines.add("dn: cn=schema"); 699 lines.add("objectClass: top"); 700 lines.add("objectClass: ldapSubentry"); 701 lines.add("objectClass: subschema"); 702 return lines; 703 } 704 705 /** 706 * Updates the contents of the schema file. 707 * 708 * @param schemaFile 709 * the schema file. 710 * @param isSchemaFileDefined 711 * whether the schema is defined or not. 712 * @param attributes 713 * the attributes to add. 714 * @param objectClasses 715 * the object classes to add. 716 * @throws OpenDsException 717 * if an error occurs updating the schema file. 718 */ 719 private void updateSchemaFile(String schemaFile, List<SomeSchemaElement> schemaElements) 720 throws OpenDsException 721 { 722 try (final LDIFExportConfig exportConfig = new LDIFExportConfig(schemaFile, ExistingFileBehavior.OVERWRITE)) 723 { 724 try (final LDIFReader reader = new LDIFReader(new LDIFImportConfig(schemaFile))) 725 { 726 final Entry schemaEntry = reader.readEntry(); 727 addElementsToEntry(schemaElements, schemaEntry); 728 try (final LDIFWriter writer = new LDIFWriter(exportConfig)) 729 { 730 writer.writeEntry(schemaEntry); 731 exportConfig.getWriter().newLine(); 732 } 733 } 734 catch (Throwable t) 735 { 736 throw new OfflineUpdateException(ERR_CTRL_PANEL_ERROR_UPDATING_SCHEMA.get(t), t); 737 } 738 } 739 } 740 741 private void addElementsToEntry(List<SomeSchemaElement> schemaElements, Entry schemaEntry) 742 throws DirectoryException 743 { 744 for (SomeSchemaElement schemaElement : schemaElements) 745 { 746 Attribute attr = Attributes.create(schemaElement.getAttributeName(), getValueOffline(schemaElement)); 747 schemaEntry.applyModification(new Modification(ADD, attr)); 748 } 749 } 750 751 private void updateSchemaUndefinedFile(String schemaFile, List<SomeSchemaElement> schemaElements) 752 throws OfflineUpdateException 753 { 754 try (LDIFExportConfig exportConfig = new LDIFExportConfig(schemaFile, ExistingFileBehavior.FAIL)) 755 { 756 List<String> lines = getSchemaEntryLines(); 757 for (final SomeSchemaElement schemaElement : schemaElements) 758 { 759 lines.add(schemaElement.getAttributeName() + ": " + getValueOffline(schemaElement)); 760 } 761 for (String line : lines) 762 { 763 final boolean wrapLines = exportConfig.getWrapColumn() > 1; 764 LDIFWriter.writeLDIFLine( 765 new StringBuilder(line), exportConfig.getWriter(), wrapLines, exportConfig.getWrapColumn()); 766 } 767 exportConfig.getWriter().newLine(); 768 } 769 catch (Throwable t) 770 { 771 throw new OfflineUpdateException(ERR_CTRL_PANEL_ERROR_UPDATING_SCHEMA.get(t), t); 772 } 773 } 774}