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-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.guitools.controlpanel.ui;
028
029import static org.opends.messages.AdminToolMessages.*;
030
031import java.io.IOException;
032import java.io.StringReader;
033import java.util.ArrayList;
034
035import javax.swing.SwingUtilities;
036
037import org.forgerock.i18n.LocalizableMessage;
038import org.opends.guitools.controlpanel.browser.BrowserController;
039import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
040import org.opends.guitools.controlpanel.task.NewEntryTask;
041import org.opends.guitools.controlpanel.task.Task;
042import org.opends.guitools.controlpanel.ui.nodes.BasicNode;
043import org.opends.guitools.controlpanel.util.BackgroundTask;
044import org.opends.guitools.controlpanel.util.Utilities;
045import org.opends.server.types.Entry;
046import org.opends.server.types.LDIFImportConfig;
047import org.opends.server.util.LDIFException;
048import org.opends.server.util.LDIFReader;
049
050/**
051 * Abstract class used to re-factor some code among the different panels that
052 * are used to create a new entry.
053 */
054public abstract class AbstractNewEntryPanel extends StatusGenericPanel
055{
056  private static final long serialVersionUID = 6894546787832469213L;
057
058  /** The parent node that was selected when the user clicked on the new entry action. */
059  protected BasicNode parentNode;
060  /** The browser controller. */
061  protected BrowserController controller;
062
063  /**
064   * Sets the parent and the browser controller for this panel.
065   * @param parentNode the selected parent node (or <CODE>null</CODE> if no
066   * parent node was selected).
067   * @param controller the browser controller.
068   */
069  public void setParent(BasicNode parentNode, BrowserController controller)
070  {
071    this.parentNode = parentNode;
072    this.controller = controller;
073  }
074
075  /**
076   * Returns the title for the progress dialog.
077   * @return the title for the progress dialog.
078   */
079  protected abstract LocalizableMessage getProgressDialogTitle();
080  /**
081   * Returns the LDIF representation of the new entry.
082   * @return the LDIF representation of the new entry.
083   */
084  protected abstract String getLDIF();
085
086  /**
087   * Updates the list of errors by checking the syntax of the entry.
088   * @param errors the list of errors that must be updated.
089   */
090  protected abstract void checkSyntax(ArrayList<LocalizableMessage> errors);
091
092  /**
093   * Returns <CODE>true</CODE> if the syntax of the entry must be checked in
094   * the background and <CODE>false</CODE> otherwise.
095   * @return <CODE>true</CODE> if the syntax of the entry must be checked in
096   * the background and <CODE>false</CODE> otherwise.
097   */
098  protected boolean checkSyntaxBackground()
099  {
100    return false;
101  }
102
103  /** {@inheritDoc} */
104  public void okClicked()
105  {
106    final ArrayList<LocalizableMessage> errors = new ArrayList<>();
107
108    if (checkSyntaxBackground())
109    {
110      BackgroundTask<Void> worker = new BackgroundTask<Void>()
111      {
112        public Void processBackgroundTask()
113        {
114          try
115          {
116            Thread.sleep(2000);
117          }
118          catch (Throwable t)
119          {
120          }
121          checkSyntax(errors);
122          return null;
123        }
124        public void backgroundTaskCompleted(Void returnValue, Throwable t)
125        {
126          if (t != null)
127          {
128            errors.add(ERR_CTRL_PANEL_UNEXPECTED_DETAILS.get(t));
129          }
130          displayMainPanel();
131          setEnabledCancel(true);
132          setEnabledOK(true);
133          handleErrorsAndLaunchTask(errors);
134        }
135      };
136      displayMessage(INFO_CTRL_PANEL_CHECKING_SUMMARY.get());
137      setEnabledCancel(false);
138      setEnabledOK(false);
139      worker.startBackgroundTask();
140    }
141    else
142    {
143      checkSyntax(errors);
144      handleErrorsAndLaunchTask(errors);
145    }
146  }
147
148  /**
149   * Checks that there are not errors in the list and launches a new entry
150   * task.
151   * @param errors the list of errors.
152   */
153  private void handleErrorsAndLaunchTask(ArrayList<LocalizableMessage> errors)
154  {
155    Entry entry = null;
156    if (errors.isEmpty())
157    {
158      try
159      {
160        entry = getEntry();
161      }
162      catch (Throwable t)
163      {
164        // Unexpected error: getEntry() should work after calling checkSyntax
165        throw new RuntimeException("Unexpected error: "+t, t);
166      }
167      String dn = entry.getName().toString();
168      // Checking for the existence of an entry is fast enough so we can do
169      // it on the event thread.
170      if (entryExists(dn))
171      {
172        errors.add(ERR_CTRL_PANEL_ENTRY_ALREADY_EXISTS.get(dn));
173      }
174    }
175    if (errors.isEmpty())
176    {
177      final ProgressDialog dlg = new ProgressDialog(
178          Utilities.createFrame(), Utilities.getParentDialog(this),
179          getProgressDialogTitle(), getInfo());
180      try
181      {
182        NewEntryTask newTask =
183          new NewEntryTask(getInfo(), dlg, entry, getLDIF(),
184              parentNode, controller);
185        for (Task task : getInfo().getTasks())
186        {
187          task.canLaunch(newTask, errors);
188        }
189        if (errors.isEmpty())
190        {
191          launchOperation(newTask,
192              INFO_CTRL_PANEL_CREATING_NEW_ENTRY_SUMMARY.get(),
193              INFO_CTRL_PANEL_CREATING_NEW_ENTRY_SUCCESSFUL_SUMMARY.get(),
194              INFO_CTRL_PANEL_CREATING_NEW_ENTRY_SUCCESSFUL_DETAILS.get(),
195              ERR_CTRL_PANEL_CREATING_NEW_ENTRY_ERROR_SUMMARY.get(),
196              ERR_CTRL_PANEL_CREATING_NEW_ENTRY_ERROR_DETAILS.get(),
197              null,
198              dlg);
199          dlg.setVisible(true);
200          Utilities.getParentDialog(this).setVisible(false);
201          SwingUtilities.invokeLater(new Runnable()
202          {
203            public void run()
204            {
205              dlg.toFront();
206            }
207          });
208        }
209      }
210      catch (Throwable t)
211      {
212        // Unexpected error: getEntry() should work after calling checkSyntax
213        throw new RuntimeException("Unexpected error: "+t, t);
214      }
215    }
216    if (!errors.isEmpty())
217    {
218      displayErrorDialog(errors);
219    }
220  }
221
222  /** {@inheritDoc} */
223  public void configurationChanged(ConfigurationChangeEvent ev)
224  {
225    updateErrorPaneIfServerRunningAndAuthRequired(ev.getNewDescriptor(),
226        INFO_CTRL_PANEL_NEW_ENTRY_REQUIRES_SERVER_RUNNING.get(),
227        INFO_CTRL_PANEL_NEW_ENTRY_REQUIRES_AUTHENTICATION.get());
228  }
229
230  /**
231   * Returns the entry object representing what the user provided as data.
232   * @return the entry object representing what the user provided as data.
233   * @throws LDIFException if there is an error with the LDIF syntax.
234   * @throws IOException if there is an error creating the internal stream.
235   */
236  protected Entry getEntry() throws LDIFException, IOException
237  {
238    LDIFImportConfig ldifImportConfig = null;
239    try
240    {
241      String ldif = getLDIF();
242      if (ldif.trim().length() == 0)
243      {
244        throw new LDIFException(ERR_LDIF_REPRESENTATION_REQUIRED.get());
245      }
246
247      ldifImportConfig = new LDIFImportConfig(new StringReader(ldif));
248      LDIFReader reader = new LDIFReader(ldifImportConfig);
249      Entry entry = reader.readEntry(checkSchema());
250      if (entry == null)
251      {
252        throw new LDIFException(ERR_LDIF_REPRESENTATION_REQUIRED.get());
253      }
254      if (entry.getObjectClasses().isEmpty())
255      {
256        throw new LDIFException(ERR_OBJECTCLASS_FOR_ENTRY_REQUIRED.get());
257      }
258      return entry;
259    }
260    finally
261    {
262      if (ldifImportConfig != null)
263      {
264        ldifImportConfig.close();
265      }
266    }
267  }
268
269  /**
270   * Returns <CODE>true</CODE> if the schema must be checked and
271   * <CODE>false</CODE> otherwise.
272   * @return <CODE>true</CODE> if the schema must be checked and
273   * <CODE>false</CODE> otherwise.
274   */
275  protected boolean checkSchema()
276  {
277    return getInfo().getServerDescriptor().isSchemaEnabled();
278  }
279}