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 2008-2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.guitools.controlpanel.task;
028
029import static org.opends.messages.AdminToolMessages.*;
030
031import java.util.ArrayList;
032import java.util.Collection;
033import java.util.HashSet;
034import java.util.Iterator;
035import java.util.LinkedHashSet;
036import java.util.Set;
037import java.util.TreeSet;
038
039import javax.naming.directory.BasicAttribute;
040import javax.naming.directory.BasicAttributes;
041import javax.naming.ldap.InitialLdapContext;
042import javax.swing.SwingUtilities;
043import javax.swing.tree.TreePath;
044
045import org.forgerock.i18n.LocalizableMessage;
046import org.forgerock.opendj.ldap.ByteString;
047import org.opends.guitools.controlpanel.browser.BrowserController;
048import org.opends.guitools.controlpanel.datamodel.BackendDescriptor;
049import org.opends.guitools.controlpanel.datamodel.BaseDNDescriptor;
050import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
051import org.opends.guitools.controlpanel.ui.ColorAndFontConstants;
052import org.opends.guitools.controlpanel.ui.ProgressDialog;
053import org.opends.guitools.controlpanel.ui.nodes.BasicNode;
054import org.opends.guitools.controlpanel.ui.nodes.BrowserNodeInfo;
055import org.opends.guitools.controlpanel.util.Utilities;
056import org.opends.server.config.ConfigConstants;
057import org.opends.server.types.DN;
058import org.opends.server.types.Entry;
059
060/** The task launched when we must create an entry. */
061public class NewEntryTask extends Task
062{
063  private Entry newEntry;
064  private String ldif;
065  private Set<String> backendSet;
066  private BasicNode parentNode;
067  private BrowserController controller;
068  private DN dn;
069  private boolean useAdminCtx;
070
071  /**
072   * Constructor of the task.
073   * @param info the control panel information.
074   * @param dlg the progress dialog where the task progress will be displayed.
075   * @param newEntry the entry containing the new values.
076   * @param ldif the LDIF representation of the new entry.
077   * @param controller the BrowserController.
078   * @param parentNode the parent node in the tree of the entry that we want
079   * to create.
080   */
081  public NewEntryTask(ControlPanelInfo info, ProgressDialog dlg,
082      Entry newEntry, String ldif,
083      BasicNode parentNode, BrowserController controller)
084  {
085    super(info, dlg);
086    backendSet = new HashSet<>();
087    this.newEntry = newEntry;
088    this.ldif = ldif;
089    this.parentNode = parentNode;
090    this.controller = controller;
091    dn = newEntry.getName();
092    for (BackendDescriptor backend : info.getServerDescriptor().getBackends())
093    {
094      for (BaseDNDescriptor baseDN : backend.getBaseDns())
095      {
096        if (dn.isDescendantOf(baseDN.getDn()))
097        {
098          backendSet.add(backend.getBackendID());
099        }
100      }
101    }
102  }
103
104  /** {@inheritDoc} */
105  public Type getType()
106  {
107    return Type.NEW_ENTRY;
108  }
109
110  /** {@inheritDoc} */
111  public Set<String> getBackends()
112  {
113    return backendSet;
114  }
115
116  /** {@inheritDoc} */
117  public LocalizableMessage getTaskDescription()
118  {
119    return INFO_CTRL_PANEL_NEW_ENTRY_TASK_DESCRIPTION.get(dn);
120  }
121
122  /** {@inheritDoc} */
123  protected String getCommandLinePath()
124  {
125    return null;
126  }
127
128  /** {@inheritDoc} */
129  protected ArrayList<String> getCommandLineArguments()
130  {
131    return new ArrayList<>();
132  }
133
134  /** {@inheritDoc} */
135  public boolean canLaunch(Task taskToBeLaunched,
136      Collection<LocalizableMessage> incompatibilityReasons)
137  {
138    if (!isServerRunning()
139        && state == State.RUNNING
140        && runningOnSameServer(taskToBeLaunched))
141    {
142      // All the operations are incompatible if they apply to this
143      // backend for safety.  This is a short operation so the limitation
144      // has not a lot of impact.
145      Set<String> backends = new TreeSet<>(taskToBeLaunched.getBackends());
146      backends.retainAll(getBackends());
147      if (!backends.isEmpty())
148      {
149        incompatibilityReasons.add(getIncompatibilityMessage(this, taskToBeLaunched));
150        return false;
151      }
152    }
153    return true;
154  }
155
156  /** {@inheritDoc} */
157  public boolean regenerateDescriptor()
158  {
159    return false;
160  }
161
162  /** {@inheritDoc} */
163  public void runTask()
164  {
165    state = State.RUNNING;
166    lastException = null;
167
168    try
169    {
170      InitialLdapContext ctx;
171
172      if (parentNode != null)
173      {
174        ctx = controller.findConnectionForDisplayedEntry(parentNode);
175        useAdminCtx = controller.isConfigurationNode(parentNode);
176      }
177      else
178      {
179        ctx = getInfo().getDirContext();
180        useAdminCtx = true;
181      }
182      BasicAttributes attrs = new BasicAttributes();
183      BasicAttribute objectclass =
184        new BasicAttribute(ConfigConstants.ATTR_OBJECTCLASS);
185      for (String oc : newEntry.getObjectClasses().values())
186      {
187        objectclass.add(oc);
188      }
189      attrs.put(objectclass);
190      for (org.opends.server.types.Attribute attr : newEntry.getAttributes())
191      {
192        String attrName = attr.getNameWithOptions();
193        Set<ByteString> values = new LinkedHashSet<>();
194        Iterator<ByteString> it = attr.iterator();
195        while (it.hasNext())
196        {
197          values.add(it.next());
198        }
199        BasicAttribute a = new BasicAttribute(attrName);
200        for (ByteString value : values)
201        {
202          a.add(value.toByteArray());
203        }
204        attrs.put(a);
205      }
206
207      SwingUtilities.invokeLater(new Runnable()
208      {
209        public void run()
210        {
211          printEquivalentCommand();
212          getProgressDialog().appendProgressHtml(
213              Utilities.getProgressWithPoints(
214                  INFO_CTRL_PANEL_CREATING_ENTRY.get(dn),
215                  ColorAndFontConstants.progressFont));
216        }
217      });
218
219      ctx.createSubcontext(Utilities.getJNDIName(newEntry.getName().toString()),
220          attrs);
221
222      SwingUtilities.invokeLater(new Runnable()
223      {
224        public void run()
225        {
226          getProgressDialog().appendProgressHtml(
227              Utilities.getProgressDone(ColorAndFontConstants.progressFont));
228          boolean entryInserted = false;
229          if (parentNode != null)
230          {
231            boolean isReallyParentNode = false;
232            try
233            {
234              DN parentDN = DN.valueOf(parentNode.getDN());
235              isReallyParentNode =
236                parentDN.equals(newEntry.getName().parent());
237            }
238            catch (Throwable t)
239            {
240              // Bug
241              t.printStackTrace();
242              isReallyParentNode = false;
243            }
244            if (isReallyParentNode)
245            {
246              insertNode(parentNode, newEntry.getName(),
247                  isBaseDN(newEntry.getName()));
248              entryInserted = true;
249            }
250          }
251          if (!entryInserted)
252          {
253            BasicNode root = (BasicNode)controller.getTreeModel().getRoot();
254            BasicNode realParentNode = findParentNode(newEntry.getName(), root);
255            if (realParentNode != null)
256            {
257              insertNode(realParentNode, newEntry.getName(), false);
258            }
259            else
260            {
261              if (isBaseDN(newEntry.getName()))
262              {
263                int nRootChildren = controller.getTreeModel().getChildCount(
264                  controller.getTreeModel().getRoot());
265                if (nRootChildren > 1)
266                {
267                  // Insert in the root.
268                  insertNode(root, newEntry.getName(), true);
269                }
270              }
271            }
272          }
273        }
274      });
275      state = State.FINISHED_SUCCESSFULLY;
276    }
277    catch (Throwable t)
278    {
279      lastException = t;
280      state = State.FINISHED_WITH_ERROR;
281    }
282  }
283
284  /**
285   * Prints the equivalent command-line in the progress dialog.
286   *
287   */
288  private void printEquivalentCommand()
289  {
290    ArrayList<String> args = new ArrayList<>(getObfuscatedCommandLineArguments(
291        getConnectionCommandLineArguments(useAdminCtx, true)));
292    args.add(getNoPropertiesFileArgument());
293    args.add("--defaultAdd");
294    String equiv = getEquivalentCommandLine(getCommandLinePath("ldapmodify"),
295        args);
296    StringBuilder sb = new StringBuilder();
297    sb.append(INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_CREATE_ENTRY.get()).append("<br><b>");
298    sb.append(equiv);
299    sb.append("<br>");
300    String[] lines = ldif.split("\n");
301    for (String line : lines)
302    {
303      sb.append(obfuscateLDIFLine(line));
304      sb.append("<br>");
305    }
306    sb.append("</b><br>");
307    getProgressDialog().appendProgressHtml(Utilities.applyFont(sb.toString(),
308        ColorAndFontConstants.progressFont));
309  }
310
311  private BasicNode findParentNode(DN dn, BasicNode root)
312  {
313    BasicNode parentNode = null;
314    int nRootChildren = controller.getTreeModel().getChildCount(root);
315    for (int i=0; i<nRootChildren; i++)
316    {
317      BasicNode node =
318        (BasicNode)controller.getTreeModel().getChild(root, i);
319      try
320      {
321        DN nodeDN = DN.valueOf(node.getDN());
322        if (dn.isDescendantOf(nodeDN))
323        {
324          if (dn.size() == nodeDN.size() + 1)
325          {
326            parentNode = node;
327            break;
328          }
329          else
330          {
331            parentNode = findParentNode(dn, node);
332            break;
333          }
334        }
335      }
336      catch (Throwable t)
337      {
338        // Bug
339        throw new RuntimeException("Unexpected error: "+t, t);
340      }
341    }
342    return parentNode;
343  }
344
345  private void insertNode(BasicNode parentNode, DN dn, boolean isSuffix)
346  {
347    TreePath parentPath =
348      new TreePath(controller.getTreeModel().getPathToRoot(parentNode));
349    if (parentPath != null)
350    {
351      BrowserNodeInfo nodeInfo =
352        controller.getNodeInfoFromPath(parentPath);
353      if (nodeInfo != null)
354      {
355        TreePath newPath;
356        if (isSuffix)
357        {
358          newPath = controller.addSuffix(dn.toString(), parentNode.getDN());
359        }
360        else
361        {
362          newPath = controller.notifyEntryAdded(
363            controller.getNodeInfoFromPath(parentPath), dn.toString());
364        }
365        if (newPath != null)
366        {
367          controller.getTree().setSelectionPath(newPath);
368          controller.getTree().scrollPathToVisible(newPath);
369        }
370      }
371    }
372  }
373
374  private boolean isBaseDN(DN dn)
375  {
376    for (BackendDescriptor backend : getInfo().getServerDescriptor().getBackends())
377    {
378      for (BaseDNDescriptor baseDN : backend.getBaseDns())
379      {
380        if (baseDN.getDn().equals(dn))
381        {
382          return true;
383        }
384      }
385    }
386    return false;
387  }
388}