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