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  @Override
095  public Type getType()
096  {
097    return Type.NEW_ENTRY;
098  }
099
100  @Override
101  public Set<String> getBackends()
102  {
103    return backendSet;
104  }
105
106  @Override
107  public LocalizableMessage getTaskDescription()
108  {
109    return INFO_CTRL_PANEL_NEW_ENTRY_TASK_DESCRIPTION.get(dn);
110  }
111
112  @Override
113  protected String getCommandLinePath()
114  {
115    return null;
116  }
117
118  @Override
119  protected ArrayList<String> getCommandLineArguments()
120  {
121    return new ArrayList<>();
122  }
123
124  @Override
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  @Override
147  public boolean regenerateDescriptor()
148  {
149    return false;
150  }
151
152  @Override
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().getConnection().getLdapContext();
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        Set<ByteString> values = new LinkedHashSet<>();
183        Iterator<ByteString> it = attr.iterator();
184        while (it.hasNext())
185        {
186          values.add(it.next());
187        }
188        BasicAttribute a = new BasicAttribute(attr.getAttributeDescription().toString());
189        for (ByteString value : values)
190        {
191          a.add(value.toByteArray());
192        }
193        attrs.put(a);
194      }
195
196      SwingUtilities.invokeLater(new Runnable()
197      {
198        @Override
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        @Override
215        public void run()
216        {
217          getProgressDialog().appendProgressHtml(
218              Utilities.getProgressDone(ColorAndFontConstants.progressFont));
219          boolean entryInserted = false;
220          if (parentNode != null)
221          {
222            boolean isReallyParentNode = false;
223            try
224            {
225              DN parentDN = DN.valueOf(parentNode.getDN());
226              isReallyParentNode =
227                parentDN.equals(newEntry.getName().parent());
228            }
229            catch (Throwable t)
230            {
231              // Bug
232              t.printStackTrace();
233              isReallyParentNode = false;
234            }
235            if (isReallyParentNode)
236            {
237              insertNode(parentNode, newEntry.getName(),
238                  isBaseDN(newEntry.getName()));
239              entryInserted = true;
240            }
241          }
242          if (!entryInserted)
243          {
244            BasicNode root = (BasicNode)controller.getTreeModel().getRoot();
245            BasicNode realParentNode = findParentNode(newEntry.getName(), root);
246            if (realParentNode != null)
247            {
248              insertNode(realParentNode, newEntry.getName(), false);
249            }
250            else
251            {
252              if (isBaseDN(newEntry.getName()))
253              {
254                int nRootChildren = controller.getTreeModel().getChildCount(
255                  controller.getTreeModel().getRoot());
256                if (nRootChildren > 1)
257                {
258                  // Insert in the root.
259                  insertNode(root, newEntry.getName(), true);
260                }
261              }
262            }
263          }
264        }
265      });
266      state = State.FINISHED_SUCCESSFULLY;
267    }
268    catch (Throwable t)
269    {
270      lastException = t;
271      state = State.FINISHED_WITH_ERROR;
272    }
273  }
274
275  /** Prints the equivalent command-line in the progress dialog. */
276  private void printEquivalentCommand()
277  {
278    ArrayList<String> args = new ArrayList<>(getObfuscatedCommandLineArguments(
279        getConnectionCommandLineArguments(useAdminCtx, true)));
280    args.add(getNoPropertiesFileArgument());
281    args.add("--defaultAdd");
282    String equiv = getEquivalentCommandLine(getCommandLinePath("ldapmodify"),
283        args);
284    StringBuilder sb = new StringBuilder();
285    sb.append(INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_CREATE_ENTRY.get()).append("<br><b>");
286    sb.append(equiv);
287    sb.append("<br>");
288    String[] lines = ldif.split("\n");
289    for (String line : lines)
290    {
291      sb.append(obfuscateLDIFLine(line));
292      sb.append("<br>");
293    }
294    sb.append("</b><br>");
295    getProgressDialog().appendProgressHtml(Utilities.applyFont(sb.toString(),
296        ColorAndFontConstants.progressFont));
297  }
298
299  private BasicNode findParentNode(DN dn, BasicNode root)
300  {
301    BasicNode parentNode = null;
302    int nRootChildren = controller.getTreeModel().getChildCount(root);
303    for (int i=0; i<nRootChildren; i++)
304    {
305      BasicNode node =
306        (BasicNode)controller.getTreeModel().getChild(root, i);
307      try
308      {
309        DN nodeDN = DN.valueOf(node.getDN());
310        if (dn.isSubordinateOrEqualTo(nodeDN))
311        {
312          if (dn.size() == nodeDN.size() + 1)
313          {
314            parentNode = node;
315            break;
316          }
317          else
318          {
319            parentNode = findParentNode(dn, node);
320            break;
321          }
322        }
323      }
324      catch (Throwable t)
325      {
326        // Bug
327        throw new RuntimeException("Unexpected error: "+t, t);
328      }
329    }
330    return parentNode;
331  }
332
333  private void insertNode(BasicNode parentNode, DN dn, boolean isSuffix)
334  {
335    TreePath parentPath =
336      new TreePath(controller.getTreeModel().getPathToRoot(parentNode));
337    if (parentPath != null)
338    {
339      BrowserNodeInfo nodeInfo =
340        controller.getNodeInfoFromPath(parentPath);
341      if (nodeInfo != null)
342      {
343        TreePath newPath;
344        if (isSuffix)
345        {
346          newPath = controller.addSuffix(dn.toString(), parentNode.getDN());
347        }
348        else
349        {
350          newPath = controller.notifyEntryAdded(
351            controller.getNodeInfoFromPath(parentPath), dn.toString());
352        }
353        if (newPath != null)
354        {
355          controller.getTree().setSelectionPath(newPath);
356          controller.getTree().scrollPathToVisible(newPath);
357        }
358      }
359    }
360  }
361
362  private boolean isBaseDN(DN dn)
363  {
364    for (BackendDescriptor backend : getInfo().getServerDescriptor().getBackends())
365    {
366      for (BaseDNDescriptor baseDN : backend.getBaseDns())
367      {
368        if (baseDN.getDn().equals(dn))
369        {
370          return true;
371        }
372      }
373    }
374    return false;
375  }
376}