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