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