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