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-2015 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  /** {@inheritDoc} */
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        public Void processBackgroundTask()
103        {
104          try
105          {
106            Thread.sleep(2000);
107          }
108          catch (Throwable t)
109          {
110          }
111          checkSyntax(errors);
112          return null;
113        }
114        public void backgroundTaskCompleted(Void returnValue, Throwable t)
115        {
116          if (t != null)
117          {
118            errors.add(ERR_CTRL_PANEL_UNEXPECTED_DETAILS.get(t));
119          }
120          displayMainPanel();
121          setEnabledCancel(true);
122          setEnabledOK(true);
123          handleErrorsAndLaunchTask(errors);
124        }
125      };
126      displayMessage(INFO_CTRL_PANEL_CHECKING_SUMMARY.get());
127      setEnabledCancel(false);
128      setEnabledOK(false);
129      worker.startBackgroundTask();
130    }
131    else
132    {
133      checkSyntax(errors);
134      handleErrorsAndLaunchTask(errors);
135    }
136  }
137
138  /**
139   * Checks that there are not errors in the list and launches a new entry
140   * task.
141   * @param errors the list of errors.
142   */
143  private void handleErrorsAndLaunchTask(ArrayList<LocalizableMessage> errors)
144  {
145    Entry entry = null;
146    if (errors.isEmpty())
147    {
148      try
149      {
150        entry = getEntry();
151      }
152      catch (Throwable t)
153      {
154        // Unexpected error: getEntry() should work after calling checkSyntax
155        throw new RuntimeException("Unexpected error: "+t, t);
156      }
157      String dn = entry.getName().toString();
158      // Checking for the existence of an entry is fast enough so we can do
159      // it on the event thread.
160      if (entryExists(dn))
161      {
162        errors.add(ERR_CTRL_PANEL_ENTRY_ALREADY_EXISTS.get(dn));
163      }
164    }
165    if (errors.isEmpty())
166    {
167      final ProgressDialog dlg = new ProgressDialog(
168          Utilities.createFrame(), Utilities.getParentDialog(this),
169          getProgressDialogTitle(), getInfo());
170      try
171      {
172        NewEntryTask newTask =
173          new NewEntryTask(getInfo(), dlg, entry, getLDIF(),
174              parentNode, controller);
175        for (Task task : getInfo().getTasks())
176        {
177          task.canLaunch(newTask, errors);
178        }
179        if (errors.isEmpty())
180        {
181          launchOperation(newTask,
182              INFO_CTRL_PANEL_CREATING_NEW_ENTRY_SUMMARY.get(),
183              INFO_CTRL_PANEL_CREATING_NEW_ENTRY_SUCCESSFUL_SUMMARY.get(),
184              INFO_CTRL_PANEL_CREATING_NEW_ENTRY_SUCCESSFUL_DETAILS.get(),
185              ERR_CTRL_PANEL_CREATING_NEW_ENTRY_ERROR_SUMMARY.get(),
186              ERR_CTRL_PANEL_CREATING_NEW_ENTRY_ERROR_DETAILS.get(),
187              null,
188              dlg);
189          dlg.setVisible(true);
190          Utilities.getParentDialog(this).setVisible(false);
191          SwingUtilities.invokeLater(new Runnable()
192          {
193            public void run()
194            {
195              dlg.toFront();
196            }
197          });
198        }
199      }
200      catch (Throwable t)
201      {
202        // Unexpected error: getEntry() should work after calling checkSyntax
203        throw new RuntimeException("Unexpected error: "+t, t);
204      }
205    }
206    if (!errors.isEmpty())
207    {
208      displayErrorDialog(errors);
209    }
210  }
211
212  /** {@inheritDoc} */
213  public void configurationChanged(ConfigurationChangeEvent ev)
214  {
215    updateErrorPaneIfServerRunningAndAuthRequired(ev.getNewDescriptor(),
216        INFO_CTRL_PANEL_NEW_ENTRY_REQUIRES_SERVER_RUNNING.get(),
217        INFO_CTRL_PANEL_NEW_ENTRY_REQUIRES_AUTHENTICATION.get());
218  }
219
220  /**
221   * Returns the entry object representing what the user provided as data.
222   * @return the entry object representing what the user provided as data.
223   * @throws LDIFException if there is an error with the LDIF syntax.
224   * @throws IOException if there is an error creating the internal stream.
225   */
226  protected Entry getEntry() throws LDIFException, IOException
227  {
228    LDIFImportConfig ldifImportConfig = null;
229    try
230    {
231      String ldif = getLDIF();
232      if (ldif.trim().length() == 0)
233      {
234        throw new LDIFException(ERR_LDIF_REPRESENTATION_REQUIRED.get());
235      }
236
237      ldifImportConfig = new LDIFImportConfig(new StringReader(ldif));
238      LDIFReader reader = new LDIFReader(ldifImportConfig);
239      Entry entry = reader.readEntry(checkSchema());
240      if (entry == null)
241      {
242        throw new LDIFException(ERR_LDIF_REPRESENTATION_REQUIRED.get());
243      }
244      if (entry.getObjectClasses().isEmpty())
245      {
246        throw new LDIFException(ERR_OBJECTCLASS_FOR_ENTRY_REQUIRED.get());
247      }
248      return entry;
249    }
250    finally
251    {
252      if (ldifImportConfig != null)
253      {
254        ldifImportConfig.close();
255      }
256    }
257  }
258
259  /**
260   * Returns <CODE>true</CODE> if the schema must be checked and
261   * <CODE>false</CODE> otherwise.
262   * @return <CODE>true</CODE> if the schema must be checked and
263   * <CODE>false</CODE> otherwise.
264   */
265  protected boolean checkSchema()
266  {
267    return getInfo().getServerDescriptor().isSchemaEnabled();
268  }
269}