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 2014-2016 ForgeRock AS.
016 */
017package org.opends.guitools.controlpanel.task;
018
019import static org.opends.messages.AdminToolMessages.*;
020import static org.opends.server.util.CollectionUtils.*;
021import static org.opends.server.util.SchemaUtils.*;
022
023import java.util.ArrayList;
024import java.util.Collection;
025import java.util.Collections;
026import java.util.HashSet;
027import java.util.LinkedHashSet;
028import java.util.List;
029import java.util.Map;
030import java.util.Set;
031
032import javax.swing.SwingUtilities;
033
034import org.forgerock.i18n.LocalizableMessage;
035import org.forgerock.opendj.ldap.schema.AttributeType;
036import org.forgerock.opendj.ldap.schema.ObjectClass;
037import org.forgerock.opendj.ldap.schema.SchemaBuilder;
038import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
039import org.opends.guitools.controlpanel.datamodel.SomeSchemaElement;
040import org.opends.guitools.controlpanel.ui.ColorAndFontConstants;
041import org.opends.guitools.controlpanel.ui.ProgressDialog;
042import org.opends.guitools.controlpanel.util.Utilities;
043import org.opends.server.types.OpenDsException;
044import org.opends.server.types.Schema;
045
046/**
047 * The task that is in charge of modifying an attribute definition (and all
048 * the references to this attribute).
049 */
050public class ModifyAttributeTask extends Task
051{
052  private AttributeType oldAttribute;
053  private AttributeType newAttribute;
054
055  /**
056   * The constructor of the task.
057   * @param info the control panel info.
058   * @param dlg the progress dialog that shows the progress of the task.
059   * @param oldAttribute the old attribute definition.
060   * @param newAttribute the new attribute definition.
061   */
062  public ModifyAttributeTask(ControlPanelInfo info, ProgressDialog dlg,
063      AttributeType oldAttribute, AttributeType newAttribute)
064  {
065    super(info, dlg);
066    if (oldAttribute == null)
067    {
068      throw new IllegalArgumentException("oldAttribute cannot be null.");
069    }
070    if (newAttribute == null)
071    {
072      throw new IllegalArgumentException("newAttribute cannot be null.");
073    }
074    this.oldAttribute = oldAttribute;
075    this.newAttribute = newAttribute;
076  }
077
078  @Override
079  public Type getType()
080  {
081    return Type.MODIFY_SCHEMA_ELEMENT;
082  }
083
084  @Override
085  public LocalizableMessage getTaskDescription()
086  {
087    return INFO_CTRL_PANEL_MODIFY_ATTRIBUTE_TASK_DESCRIPTION.get(
088        oldAttribute.getNameOrOID());
089  }
090
091  @Override
092  public boolean canLaunch(Task taskToBeLaunched,
093      Collection<LocalizableMessage> incompatibilityReasons)
094  {
095    boolean canLaunch = true;
096    if (state == State.RUNNING &&
097        (taskToBeLaunched.getType() == Task.Type.DELETE_SCHEMA_ELEMENT ||
098         taskToBeLaunched.getType() == Task.Type.MODIFY_SCHEMA_ELEMENT ||
099         taskToBeLaunched.getType() == Task.Type.NEW_SCHEMA_ELEMENT))
100    {
101      incompatibilityReasons.add(getIncompatibilityMessage(this,
102            taskToBeLaunched));
103      canLaunch = false;
104    }
105    return canLaunch;
106  }
107
108  @Override
109  public Set<String> getBackends()
110  {
111    return Collections.emptySet();
112  }
113
114  @Override
115  protected List<String> getCommandLineArguments()
116  {
117    return Collections.emptyList();
118  }
119
120  @Override
121  protected String getCommandLinePath()
122  {
123    return null;
124  }
125
126  @Override
127  public void runTask()
128  {
129    try
130    {
131      updateSchema();
132      state = State.FINISHED_SUCCESSFULLY;
133    }
134    catch (Throwable t)
135    {
136      // TODO
137      //revertChanges();
138      lastException = t;
139      state = State.FINISHED_WITH_ERROR;
140    }
141  }
142
143  private AttributeType getAttributeToAdd(AttributeType attrToDelete)
144  {
145    if (attrToDelete.equals(oldAttribute))
146    {
147      return newAttribute;
148    }
149    else if (oldAttribute.equals(attrToDelete.getSuperiorType()))
150    {
151      // get a new attribute with the new superior type
152      return SomeSchemaElement.changeSuperiorType(attrToDelete, newAttribute);
153    }
154    else
155    {
156      // Nothing to be changed in the definition of the attribute itself.
157      return attrToDelete;
158    }
159  }
160
161  private ObjectClass getObjectClassToAdd(ObjectClass ocToDelete)
162  {
163    boolean containsAttribute =
164      ocToDelete.getRequiredAttributes().contains(oldAttribute) ||
165      ocToDelete.getOptionalAttributes().contains(oldAttribute);
166    if (containsAttribute)
167    {
168      Map<String, List<String>> extraProperties =
169        DeleteSchemaElementsTask.cloneExtraProperties(ocToDelete);
170      Set<AttributeType> required = new HashSet<>(ocToDelete.getDeclaredRequiredAttributes());
171      Set<AttributeType> optional = new HashSet<>(ocToDelete.getDeclaredOptionalAttributes());
172      if (required.contains(oldAttribute))
173      {
174        required.remove(oldAttribute);
175        required.add(newAttribute);
176      }
177      else if (optional.contains(oldAttribute))
178      {
179        optional.remove(oldAttribute);
180        optional.add(newAttribute);
181      }
182
183      Schema schema = getInfo().getServerDescriptor().getSchema();
184      String oid = ocToDelete.getOID();
185      return new SchemaBuilder(schema.getSchemaNG()).buildObjectClass(oid)
186          .names(ocToDelete.getNames())
187          .description(ocToDelete.getDescription())
188          .superiorObjectClasses(getNameOrOIDsForOCs(ocToDelete.getSuperiorClasses()))
189          .requiredAttributes(getNameOrOIDsForATs(required))
190          .optionalAttributes(getNameOrOIDsForATs(optional))
191          .type(ocToDelete.getObjectClassType())
192          .obsolete(ocToDelete.isObsolete())
193          .extraProperties(extraProperties)
194          .addToSchema()
195          .toSchema()
196          .getObjectClass(oid);
197    }
198    else
199    {
200      // Nothing to be changed in the definition of the object class itself.
201      return ocToDelete;
202    }
203  }
204
205  /**
206   * Updates the schema.
207   * @throws OpenDsException if an error occurs.
208   */
209  private void updateSchema() throws OpenDsException
210  {
211    Schema schema = getInfo().getServerDescriptor().getSchema();
212    ArrayList<AttributeType> attrs = newArrayList(oldAttribute);
213    LinkedHashSet<AttributeType> attrsToDelete =
214      DeleteSchemaElementsTask.getOrderedAttributesToDelete(attrs, schema);
215    LinkedHashSet<ObjectClass> ocsToDelete =
216      DeleteSchemaElementsTask.getOrderedObjectClassesToDeleteFromAttrs(
217          attrsToDelete, schema);
218
219    LinkedHashSet<AttributeType> attrsToAdd = new LinkedHashSet<>();
220    ArrayList<AttributeType> lAttrsToDelete = new ArrayList<>(attrsToDelete);
221    for (int i = lAttrsToDelete.size() - 1; i >= 0; i--)
222    {
223      AttributeType attrToAdd = getAttributeToAdd(lAttrsToDelete.get(i));
224      if (attrToAdd != null)
225      {
226        attrsToAdd.add(attrToAdd);
227      }
228    }
229
230    ArrayList<ObjectClass> lOcsToDelete = new ArrayList<>(ocsToDelete);
231    LinkedHashSet<ObjectClass> ocsToAdd = new LinkedHashSet<>();
232    for (int i = lOcsToDelete.size() - 1; i >= 0; i--)
233    {
234      ocsToAdd.add(getObjectClassToAdd(lOcsToDelete.get(i)));
235    }
236
237    SwingUtilities.invokeLater(new Runnable()
238    {
239      @Override
240      public void run()
241      {
242        getProgressDialog().appendProgressHtml(Utilities.applyFont(
243            INFO_CTRL_PANEL_EXPLANATION_TO_MODIFY_ATTRIBUTE.get(
244                oldAttribute.getNameOrOID())+"<br><br>",
245                ColorAndFontConstants.progressFont));
246      }
247    });
248
249    DeleteSchemaElementsTask deleteTask =
250      new DeleteSchemaElementsTask(getInfo(), getProgressDialog(), ocsToDelete,
251          attrsToDelete);
252    deleteTask.runTask();
253
254    SwingUtilities.invokeLater(new Runnable()
255    {
256      @Override
257      public void run()
258      {
259        getProgressDialog().appendProgressHtml(Utilities.applyFont("<br><br>",
260                ColorAndFontConstants.progressFont));
261      }
262    });
263
264    NewSchemaElementsTask createTask =
265      new NewSchemaElementsTask(getInfo(), getProgressDialog(), ocsToAdd,
266          attrsToAdd);
267    createTask.runTask();
268
269    notifyConfigurationElementCreated(newAttribute);
270  }
271}