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