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 2011-2016 ForgeRock AS.
016 */
017package org.opends.guitools.controlpanel.ui;
018
019import static org.opends.messages.AdminToolMessages.*;
020import static org.opends.messages.QuickSetupMessages.*;
021
022import java.awt.Component;
023import java.awt.GridBagConstraints;
024import java.awt.GridBagLayout;
025import java.awt.event.ItemEvent;
026import java.awt.event.ItemListener;
027import java.io.File;
028import java.io.IOException;
029import java.util.ArrayList;
030import java.util.Collection;
031import java.util.Collections;
032import java.util.Enumeration;
033import java.util.HashSet;
034import java.util.LinkedHashSet;
035import java.util.LinkedList;
036import java.util.List;
037import java.util.Set;
038import java.util.SortedSet;
039import java.util.TreeSet;
040
041import javax.swing.AbstractButton;
042import javax.swing.Box;
043import javax.swing.ButtonGroup;
044import javax.swing.DefaultComboBoxModel;
045import javax.swing.JButton;
046import javax.swing.JComboBox;
047import javax.swing.JComponent;
048import javax.swing.JLabel;
049import javax.swing.JPanel;
050import javax.swing.JRadioButton;
051import javax.swing.JTextField;
052import javax.swing.SwingUtilities;
053import javax.swing.event.ChangeEvent;
054import javax.swing.event.ChangeListener;
055import javax.swing.event.DocumentEvent;
056import javax.swing.event.DocumentListener;
057
058import org.forgerock.i18n.LocalizableMessage;
059import org.forgerock.i18n.LocalizedIllegalArgumentException;
060import org.forgerock.opendj.config.client.ManagementContext;
061import org.forgerock.opendj.config.client.ldap.LDAPManagementContext;
062import org.forgerock.opendj.ldap.DN;
063import org.forgerock.opendj.ldap.LdapException;
064import org.forgerock.opendj.server.config.client.BackendCfgClient;
065import org.forgerock.opendj.server.config.client.BackendIndexCfgClient;
066import org.forgerock.opendj.server.config.client.PluggableBackendCfgClient;
067import org.forgerock.opendj.server.config.client.RootCfgClient;
068import org.forgerock.opendj.server.config.meta.BackendIndexCfgDefn;
069import org.forgerock.opendj.server.config.meta.BackendIndexCfgDefn.IndexType;
070import org.opends.guitools.controlpanel.datamodel.BackendDescriptor;
071import org.opends.guitools.controlpanel.datamodel.BaseDNDescriptor;
072import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
073import org.opends.guitools.controlpanel.datamodel.ServerDescriptor;
074import org.opends.guitools.controlpanel.event.BrowseActionListener;
075import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
076import org.opends.guitools.controlpanel.task.OfflineUpdateException;
077import org.opends.guitools.controlpanel.task.OnlineUpdateException;
078import org.opends.guitools.controlpanel.task.Task;
079import org.opends.guitools.controlpanel.ui.renderer.CustomListCellRenderer;
080import org.opends.guitools.controlpanel.util.Utilities;
081import org.opends.quicksetup.Installation;
082import org.opends.quicksetup.installer.InstallerHelper;
083import org.opends.quicksetup.util.Utils;
084import org.opends.server.tools.BackendCreationHelper;
085import org.opends.server.tools.BackendCreationHelper.DefaultIndex;
086import org.opends.server.tools.BackendTypeHelper;
087import org.opends.server.tools.BackendTypeHelper.BackendTypeUIAdapter;
088import org.opends.server.tools.ImportLDIF;
089import org.opends.server.tools.LDAPModify;
090import org.opends.server.tools.makeldif.MakeLDIF;
091import org.opends.server.types.OpenDsException;
092import org.opends.server.util.SetupUtils;
093
094import com.forgerock.opendj.cli.CommandBuilder;
095
096/** The class that appears when the user clicks on 'New Base DN'. */
097public class NewBaseDNPanel extends StatusGenericPanel
098{
099  private static final int MAX_ENTRIES_NUMBER_GENERATED = 1000;
100  private static final int MAX_ENTRIES_NUMBER_GENERATED_LOCAL = 20000;
101  private static final long serialVersionUID = -2680821576362341119L;
102  private static final LocalizableMessage NEW_BACKEND_TEXT = INFO_CTRL_PANEL_NEW_BACKEND_LABEL.get();
103
104  private JComboBox<?> backends;
105  private JComboBox<BackendTypeUIAdapter> backendTypes;
106  private JTextField newBackend;
107  private JTextField baseDN;
108  private JRadioButton onlyCreateBaseEntry;
109  private JRadioButton leaveDatabaseEmpty;
110  private JRadioButton importDataFromLDIF;
111  private JRadioButton importAutomaticallyGenerated;
112  private JTextField path;
113  private JTextField numberOfEntries;
114  private JLabel lRemoteFileHelp;
115  private JButton browseImportPath;
116
117  private JLabel lBackend;
118  private JLabel lDirectoryBaseDN;
119  private JLabel lPath;
120  private JLabel lNumberOfEntries;
121  private JLabel lDirectoryData;
122  private JLabel lNewBackendType;
123
124  private DocumentListener documentListener;
125
126  /** Default constructor. */
127  public NewBaseDNPanel()
128  {
129    super();
130    createLayout();
131  }
132
133  @Override
134  public LocalizableMessage getTitle()
135  {
136    return INFO_CTRL_PANEL_NEW_BASE_DN_TITLE.get();
137  }
138
139  @Override
140  public Component getPreferredFocusComponent()
141  {
142    return baseDN;
143  }
144
145  @Override
146  public void toBeDisplayed(boolean visible)
147  {
148    if (visible)
149    {
150      documentListener.changedUpdate(null);
151    }
152  }
153
154  /** Creates the layout of the panel (but the contents are not populated here). */
155  private void createLayout()
156  {
157    GridBagConstraints gbc = new GridBagConstraints();
158    addErrorPanel(gbc);
159    addBackendLabel(gbc);
160    addBackendNamesComboBox(gbc);
161    addNewBackendName(gbc);
162    addNewBackendTypeLabel(gbc);
163    addNewBackendTypeComboBox(gbc);
164    addBaseDNLabel(gbc);
165    addBaseDNTextField(gbc);
166    addBaseDNInlineHelp(gbc);
167    addDirectoryDataLabel(gbc);
168    addImportDataChoiceSection(gbc);
169    addBottomGlue(gbc);
170  }
171
172  private void addErrorPanel(GridBagConstraints gbc)
173  {
174    gbc.gridx = 0;
175    gbc.gridy = 0;
176    gbc.gridwidth = 3;
177    addErrorPane(gbc);
178  }
179
180  private void addBackendLabel(GridBagConstraints gbc)
181  {
182    gbc.anchor = GridBagConstraints.WEST;
183    gbc.weightx = 0.0;
184    gbc.gridwidth = 1;
185    gbc.gridy++;
186    gbc.fill = GridBagConstraints.NONE;
187    lBackend = Utilities.createPrimaryLabel(INFO_CTRL_PANEL_BACKEND_LABEL.get());
188    add(lBackend, gbc);
189  }
190
191  @SuppressWarnings({ "unchecked", "rawtypes" })
192  private void addBackendNamesComboBox(GridBagConstraints gbc)
193  {
194    gbc.insets.left = 10;
195    gbc.gridx = 1;
196    backends = Utilities.createComboBox();
197    backends.setModel(new DefaultComboBoxModel(new Object[] { "bogus", NEW_BACKEND_TEXT }));
198    backends.setRenderer(new CustomListCellRenderer(backends));
199    backends.addItemListener(new IgnoreItemListener(backends));
200    gbc.gridwidth = 1;
201    add(backends, gbc);
202  }
203
204  private void addNewBackendTypeLabel(GridBagConstraints gbc)
205  {
206    gbc.insets.top = 10;
207    gbc.gridx = 0;
208    gbc.gridy++;
209    gbc.insets.left = 0;
210    gbc.gridwidth = 1;
211    lNewBackendType = Utilities.createPrimaryLabel(INFO_CTRL_PANEL_BASE_DN_NEW_BACKEND_TYPE_LABEL.get());
212    add(lNewBackendType, gbc);
213    addBackendNameChangeListener(lNewBackendType);
214  }
215
216  @SuppressWarnings("unchecked")
217  private void addNewBackendTypeComboBox(GridBagConstraints gbc)
218  {
219    gbc.insets.left = 10;
220    gbc.gridx = 1;
221    gbc.gridwidth = 1;
222    final BackendTypeHelper backendTypeHelper = new BackendTypeHelper();
223    backendTypes = Utilities.createComboBox();
224    backendTypes.setModel(new DefaultComboBoxModel<>(backendTypeHelper.getBackendTypeUIAdaptors()));
225    backendTypes.setRenderer(new CustomListCellRenderer(backendTypes));
226    backendTypes.addItemListener(new IgnoreItemListener(backendTypes));
227    add(backendTypes, gbc);
228    addBackendNameChangeListener(backendTypes);
229  }
230
231  private void addNewBackendName(GridBagConstraints gbc)
232  {
233    gbc.gridx = 2;
234    newBackend = Utilities.createTextField();
235    newBackend.setColumns(18);
236    add(newBackend, gbc);
237    addBackendNameChangeListener(newBackend);
238  }
239
240  private void addBackendNameChangeListener(final JComponent component)
241  {
242    ItemListener comboListener = new ItemListener()
243    {
244      @Override
245      public void itemStateChanged(ItemEvent ev)
246      {
247        Object o = backends.getSelectedItem();
248        component.setVisible(NEW_BACKEND_TEXT.equals(o));
249      }
250    };
251    backends.addItemListener(comboListener);
252    comboListener.itemStateChanged(null);
253  }
254
255  private void addBaseDNLabel(GridBagConstraints gbc)
256  {
257    gbc.insets.top = 10;
258    gbc.gridx = 0;
259    gbc.gridy++;
260    gbc.insets.left = 0;
261    gbc.gridwidth = 1;
262    lDirectoryBaseDN = Utilities.createPrimaryLabel(INFO_CTRL_PANEL_BASE_DN_LABEL.get());
263    add(lDirectoryBaseDN, gbc);
264  }
265
266  private void addBaseDNTextField(GridBagConstraints gbc)
267  {
268    gbc.gridx = 1;
269    gbc.insets.left = 10;
270    gbc.gridwidth = 2;
271    baseDN = Utilities.createTextField();
272    documentListener = new DocumentListener()
273    {
274      @Override
275      public void changedUpdate(DocumentEvent ev)
276      {
277        String text = baseDN.getText().trim();
278        setEnabledOK(text != null && text.length() > 0 && !errorPane.isVisible());
279      }
280
281      @Override
282      public void removeUpdate(DocumentEvent ev)
283      {
284        changedUpdate(ev);
285      }
286
287      @Override
288      public void insertUpdate(DocumentEvent ev)
289      {
290        changedUpdate(ev);
291      }
292    };
293    baseDN.getDocument().addDocumentListener(documentListener);
294    gbc.weightx = 1.0;
295    gbc.fill = GridBagConstraints.HORIZONTAL;
296    add(baseDN, gbc);
297  }
298
299  private void addBaseDNInlineHelp(GridBagConstraints gbc)
300  {
301    gbc.gridy++;
302    gbc.anchor = GridBagConstraints.EAST;
303    gbc.insets.top = 3;
304    JLabel inlineHelp = Utilities.createInlineHelpLabel(INFO_CTRL_PANEL_BASE_DN_EXAMPLE.get());
305    add(inlineHelp, gbc);
306  }
307
308  private void addDirectoryDataLabel(GridBagConstraints gbc)
309  {
310    gbc.gridx = 0;
311    gbc.gridy++;
312    gbc.insets.left = 0;
313    gbc.insets.top = 10;
314    gbc.gridwidth = 1;
315    gbc.weightx = 0.0;
316    lDirectoryData = Utilities.createPrimaryLabel(INFO_CTRL_PANEL_DIRECTORY_DATA_LABEL.get());
317    add(lDirectoryData, gbc);
318  }
319
320  private void addImportDataChoiceSection(GridBagConstraints gbc)
321  {
322    onlyCreateBaseEntry = Utilities.createRadioButton(INFO_CTRL_PANEL_ONLY_CREATE_BASE_ENTRY_LABEL.get());
323    onlyCreateBaseEntry.setSelected(false);
324
325    gbc.insets.left = 10;
326    gbc.gridx = 1;
327    gbc.gridwidth = 2;
328    add(onlyCreateBaseEntry, gbc);
329
330    leaveDatabaseEmpty = Utilities.createRadioButton(INFO_CTRL_PANEL_LEAVE_DATABASE_EMPTY_LABEL.get());
331    leaveDatabaseEmpty.setSelected(false);
332
333    gbc.gridy++;
334    gbc.gridwidth = 2;
335    gbc.insets.top = 5;
336    add(leaveDatabaseEmpty, gbc);
337
338    importDataFromLDIF = Utilities.createRadioButton(INFO_CTRL_PANEL_IMPORT_FROM_LDIF_LABEL.get());
339    importDataFromLDIF.setSelected(false);
340
341    gbc.gridy++;
342    gbc.gridwidth = 2;
343    add(importDataFromLDIF, gbc);
344
345    gbc.gridy++;
346    gbc.gridwidth = 2;
347    gbc.insets.left = 30;
348    add(createPathPanel(), gbc);
349
350    importAutomaticallyGenerated =
351        Utilities.createRadioButton(INFO_CTRL_PANEL_IMPORT_AUTOMATICALLY_GENERATED_LABEL.get());
352    importAutomaticallyGenerated.setOpaque(false);
353    importAutomaticallyGenerated.setSelected(false);
354
355    gbc.gridy++;
356    gbc.gridwidth = 2;
357    gbc.insets.left = 10;
358    add(importAutomaticallyGenerated, gbc);
359
360    gbc.gridy++;
361    gbc.gridwidth = 2;
362    gbc.insets.left = 30;
363    add(createNumberOfUsersPanel(), gbc);
364
365    ButtonGroup group = new ButtonGroup();
366    group.add(onlyCreateBaseEntry);
367    group.add(leaveDatabaseEmpty);
368    group.add(importDataFromLDIF);
369    group.add(importAutomaticallyGenerated);
370
371    ChangeListener listener = new ChangeListener()
372    {
373      @Override
374      public void stateChanged(ChangeEvent ev)
375      {
376        browseImportPath.setEnabled(importDataFromLDIF.isSelected());
377        lPath.setEnabled(importDataFromLDIF.isSelected());
378        lRemoteFileHelp.setEnabled(importDataFromLDIF.isSelected());
379        numberOfEntries.setEnabled(importAutomaticallyGenerated.isSelected());
380        lNumberOfEntries.setEnabled(importAutomaticallyGenerated.isSelected());
381      }
382    };
383
384    Enumeration<AbstractButton> buttons = group.getElements();
385    while (buttons.hasMoreElements())
386    {
387      buttons.nextElement().addChangeListener(listener);
388    }
389    onlyCreateBaseEntry.setSelected(true);
390    listener.stateChanged(null);
391  }
392
393  @Override
394  public void configurationChanged(ConfigurationChangeEvent ev)
395  {
396    ServerDescriptor desc = ev.getNewDescriptor();
397    final SortedSet<String> sortedBackends = new TreeSet<>();
398    for (BackendDescriptor backend : desc.getBackends())
399    {
400      if (!backend.isConfigBackend())
401      {
402        sortedBackends.add(backend.getBackendID());
403      }
404    }
405
406    List<Object> newElements = new ArrayList<Object>(sortedBackends);
407    if (!sortedBackends.isEmpty())
408    {
409      newElements.add(COMBO_SEPARATOR);
410    }
411    newElements.add(NEW_BACKEND_TEXT);
412    super.updateComboBoxModel(newElements, (DefaultComboBoxModel<?>) backends.getModel());
413    updateErrorPaneAndOKButtonIfAuthRequired(desc,
414        isLocal() ? INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_FOR_CREATE_BASE_DN.get()
415                  : INFO_CTRL_PANEL_CANNOT_CONNECT_TO_REMOTE_DETAILS.get(desc.getHostname()));
416    SwingUtilities.invokeLater(new Runnable()
417    {
418      @Override
419      public void run()
420      {
421        lRemoteFileHelp.setVisible(!isLocal());
422        browseImportPath.setVisible(isLocal());
423      }
424    });
425  }
426
427  private JPanel createPathPanel()
428  {
429    JPanel panel = new JPanel(new GridBagLayout());
430    panel.setOpaque(false);
431    GridBagConstraints gbc = new GridBagConstraints();
432    gbc.gridwidth = 1;
433    gbc.gridy = 0;
434    gbc.gridx = 0;
435    lPath = Utilities.createDefaultLabel(INFO_CTRL_PANEL_IMPORT_LDIF_PATH_LABEL.get());
436    panel.add(lPath, gbc);
437
438    gbc.gridx = 1;
439    gbc.insets.left = 10;
440    path = Utilities.createTextField();
441    gbc.weightx = 1.0;
442    gbc.fill = GridBagConstraints.HORIZONTAL;
443    panel.add(path, gbc);
444    browseImportPath = Utilities.createButton(INFO_CTRL_PANEL_BROWSE_BUTTON_LABEL.get());
445    browseImportPath.addActionListener(
446        new BrowseActionListener(path, BrowseActionListener.BrowseType.OPEN_LDIF_FILE, this));
447    gbc.gridx = 2;
448    gbc.weightx = 0.0;
449    panel.add(browseImportPath, gbc);
450
451    gbc.gridy++;
452    gbc.gridx = 1;
453    lRemoteFileHelp = Utilities.createInlineHelpLabel(INFO_CTRL_PANEL_REMOTE_SERVER_PATH.get());
454    gbc.insets.top = 3;
455    gbc.insets.left = 10;
456    panel.add(lRemoteFileHelp, gbc);
457
458    return panel;
459  }
460
461  private JPanel createNumberOfUsersPanel()
462  {
463    JPanel panel = new JPanel(new GridBagLayout());
464    panel.setOpaque(false);
465    GridBagConstraints gbc = new GridBagConstraints();
466    gbc.weightx = 0.0;
467    gbc.fill = GridBagConstraints.HORIZONTAL;
468    lNumberOfEntries = Utilities.createDefaultLabel(INFO_CTRL_PANEL_NUMBER_OF_USER_ENTRIES_LABEL.get());
469    panel.add(lNumberOfEntries, gbc);
470
471    gbc.gridx = 1;
472    gbc.insets.left = 10;
473    numberOfEntries = Utilities.createTextField("2000", 6);
474    panel.add(numberOfEntries, gbc);
475
476    gbc.gridx = 2;
477    gbc.insets.left = 0;
478    gbc.weightx = 1.0;
479    panel.add(Box.createHorizontalGlue(), gbc);
480
481    return panel;
482  }
483
484  @Override
485  public void cancelClicked()
486  {
487    resetLabelAsValid();
488    super.cancelClicked();
489  }
490
491  private void resetLabelAsValid()
492  {
493    setPrimaryValid(lBackend);
494    setPrimaryValid(lDirectoryBaseDN);
495    setPrimaryValid(lDirectoryData);
496    setSecondaryValid(lPath);
497    setSecondaryValid(lNumberOfEntries);
498  }
499
500  @Override
501  protected void checkOKButtonEnable()
502  {
503    documentListener.changedUpdate(null);
504  }
505
506  @Override
507  public void okClicked()
508  {
509    resetLabelAsValid();
510
511    final Set<LocalizableMessage> errors = new LinkedHashSet<>();
512    final ServerDescriptor desc = getInfo().getServerDescriptor();
513    final Set<BackendDescriptor> existingBackends = desc.getBackends();
514
515    final String backendName = validateBackendName(existingBackends, errors);
516    final String dn = validateBaseDN(backendName, existingBackends, errors);
517    validateImportLDIFFilePath(errors);
518    validateAutomaticallyGenerated(errors);
519
520    if (errors.isEmpty())
521    {
522      final ProgressDialog progressDialog = new ProgressDialog(
523          Utilities.createFrame(), Utilities.getParentDialog(this), getTitle(), getInfo());
524      final NewBaseDNTask newTask = new NewBaseDNTask(getInfo(), progressDialog);
525      for (Task task : getInfo().getTasks())
526      {
527        task.canLaunch(newTask, errors);
528      }
529
530      if (errors.isEmpty())
531      {
532        launchOperation(newTask,
533            INFO_CTRL_PANEL_CREATING_BASE_DN_SUMMARY.get(dn),
534            INFO_CTRL_PANEL_CREATING_BASE_DN_COMPLETE.get(),
535            INFO_CTRL_PANEL_CREATING_BASE_DN_SUCCESSFUL.get(dn),
536            ERR_CTRL_PANEL_CREATING_BASE_DN_ERROR_SUMMARY.get(dn),
537            null,
538            ERR_CTRL_PANEL_CREATING_BASE_DN_ERROR_DETAILS,
539            progressDialog);
540        progressDialog.setVisible(true);
541        baseDN.setText("");
542        baseDN.grabFocus();
543        Utilities.getParentDialog(this).setVisible(false);
544      }
545    }
546
547    if (!errors.isEmpty())
548    {
549      displayErrorDialog(errors);
550    }
551  }
552
553  /** Returns the existing or the new backend name, once user have clicked on 'OK' button. */
554  private String validateBackendName(
555      final Set<BackendDescriptor> existingBackends, final Set<LocalizableMessage> errors)
556  {
557    final Object selectedItem = backends.getSelectedItem();
558    if (!selectedItem.equals(NEW_BACKEND_TEXT))
559    {
560      return selectedItem.toString();
561    }
562
563    final String backendName = newBackend.getText().trim();
564    if (backendName.length() == 0)
565    {
566      errors.add(ERR_NEW_BACKEND_NAME_REQUIRED.get());
567      setPrimaryInvalid(lBackend);
568      return backendName;
569    }
570
571    // Check that the backend is not already defined.
572    for (BackendDescriptor backend : existingBackends)
573    {
574      if (backendName.equalsIgnoreCase(backend.getBackendID()))
575      {
576        errors.add(ERR_BACKEND_ALREADY_EXISTS.get(backendName));
577        setPrimaryInvalid(lBackend);
578      }
579    }
580
581    return backendName;
582  }
583
584  private String validateBaseDN(final String backendName, final Set<BackendDescriptor> existingBackends,
585      final Set<LocalizableMessage> errors)
586  {
587    String dn = baseDN.getText();
588    if (dn.trim().length() == 0)
589    {
590      errors.add(ERR_NEW_BASE_DN_VALUE_REQUIRED.get());
591      setPrimaryInvalid(lDirectoryBaseDN);
592      return dn;
593    }
594
595    try
596    {
597      final DN theDN = DN.valueOf(dn);
598      for (final BackendDescriptor backend : existingBackends)
599      {
600        for (final BaseDNDescriptor baseDN : backend.getBaseDns())
601        {
602          if (baseDN.getDn().equals(theDN))
603          {
604            return invalidBaseDNValue(dn, ERR_BASE_DN_ALREADY_EXISTS.get(dn), errors);
605          }
606          else if (baseDN.getDn().isSuperiorOrEqualTo(theDN) && backendName.equalsIgnoreCase(backend.getBackendID()))
607          {
608            return invalidBaseDNValue(dn, ERR_BASE_DN_ANCESTOR_EXISTS.get(baseDN.getDn()), errors);
609          }
610          else if (theDN.isSuperiorOrEqualTo(baseDN.getDn()) && backendName.equalsIgnoreCase(backend.getBackendID()))
611          {
612            return invalidBaseDNValue(dn, ERR_BASE_DN_DN_IS_ANCESTOR_OF.get(baseDN.getDn()), errors);
613          }
614        }
615      }
616    }
617    catch (LocalizedIllegalArgumentException e)
618    {
619      errors.add(INFO_CTRL_PANEL_INVALID_DN_DETAILS.get(dn, e.getMessageObject()));
620      setPrimaryInvalid(lDirectoryBaseDN);
621    }
622
623    return dn;
624  }
625
626  /** Mark the provided base DN as invalid with the provided reason and return it. */
627  private String invalidBaseDNValue(final String dn, final LocalizableMessage errorMsg,
628      final Set<LocalizableMessage> errors)
629  {
630    errors.add(errorMsg);
631    setPrimaryInvalid(lDirectoryBaseDN);
632    return dn;
633  }
634
635  private void validateImportLDIFFilePath(final Set<LocalizableMessage> errors)
636  {
637    // TODO: what happens with sub-suffixes?
638    if (importDataFromLDIF.isSelected())
639    {
640      String ldifPath = path.getText();
641      if (ldifPath == null || "".equals(ldifPath.trim()))
642      {
643        errors.add(INFO_NO_LDIF_PATH.get());
644        setSecondaryInvalid(lPath);
645      }
646      else if (isLocal() && !Utils.fileExists(ldifPath))
647      {
648        errors.add(INFO_LDIF_FILE_DOES_NOT_EXIST.get());
649        setSecondaryInvalid(lPath);
650      }
651    }
652  }
653
654  private void validateAutomaticallyGenerated(final Set<LocalizableMessage> errors)
655  {
656    if (importAutomaticallyGenerated.isSelected())
657    {
658      final int minValue = 1;
659      final int maxValue = isLocal() ? MAX_ENTRIES_NUMBER_GENERATED_LOCAL : MAX_ENTRIES_NUMBER_GENERATED;
660      final LocalizableMessage errorMsg = ERR_NUMBER_OF_ENTRIES_INVALID.get(minValue, maxValue);
661      if (!checkIntValue(errors, numberOfEntries.getText(), minValue, maxValue, errorMsg))
662      {
663        setSecondaryInvalid(lNumberOfEntries);
664      }
665    }
666  }
667
668  private String getBackendName()
669  {
670    Object backendName = backends.getSelectedItem();
671    if (NEW_BACKEND_TEXT.equals(backendName))
672    {
673      return newBackend.getText().trim();
674    }
675    else if (backendName != null)
676    {
677      return backendName.toString();
678    }
679
680    return null;
681  }
682
683  private BackendTypeUIAdapter getSelectedBackendType()
684  {
685    return (BackendTypeUIAdapter) backendTypes.getSelectedItem();
686  }
687
688  private boolean isNewBackend()
689  {
690    return NEW_BACKEND_TEXT.equals(backends.getSelectedItem());
691  }
692
693  /** The task in charge of creating the base DN (and if required, the backend). */
694  private class NewBaseDNTask extends Task
695  {
696    private final Set<String> backendSet;
697    private final String newBaseDN;
698    private int progressAfterConfigurationUpdate = -1;
699
700    /**
701     * The constructor of the task.
702     *
703     * @param info
704     *          the control panel info.
705     * @param dlg
706     *          the progress dialog that shows the progress of the task.
707     */
708    private NewBaseDNTask(ControlPanelInfo info, ProgressDialog dlg)
709    {
710      super(info, dlg);
711      backendSet = new HashSet<>();
712      backendSet.add(getBackendName());
713      newBaseDN = baseDN.getText();
714
715      if (onlyCreateBaseEntry.isSelected())
716      {
717        progressAfterConfigurationUpdate = 40;
718      }
719      else if (leaveDatabaseEmpty.isSelected())
720      {
721        progressAfterConfigurationUpdate = 90;
722      }
723      else if (importAutomaticallyGenerated.isSelected())
724      {
725        int nEntries = Integer.parseInt(numberOfEntries.getText().trim());
726        if (nEntries < 500)
727        {
728          progressAfterConfigurationUpdate = 30;
729        }
730        else if (nEntries < 3000)
731        {
732          progressAfterConfigurationUpdate = 15;
733        }
734        else
735        {
736          progressAfterConfigurationUpdate = 5;
737        }
738      }
739    }
740
741    @Override
742    public Type getType()
743    {
744      return Type.NEW_BASEDN;
745    }
746
747    @Override
748    public LocalizableMessage getTaskDescription()
749    {
750      return INFO_CTRL_PANEL_NEW_BASE_DN_TASK_DESCRIPTION.get(newBaseDN, backendSet.iterator().next());
751    }
752
753    @Override
754    public boolean canLaunch(Task taskToBeLaunched, Collection<LocalizableMessage> incompatibilityReasons)
755    {
756      boolean canLaunch = true;
757      if (state == State.RUNNING && runningOnSameServer(taskToBeLaunched))
758      {
759        // All the operations are incompatible if they apply to this backend.
760        Set<String> backends = new TreeSet<>(taskToBeLaunched.getBackends());
761        backends.retainAll(getBackends());
762        if (!backends.isEmpty())
763        {
764          incompatibilityReasons.add(getIncompatibilityMessage(this, taskToBeLaunched));
765          canLaunch = false;
766        }
767      }
768      return canLaunch;
769    }
770
771    private String getDataCommandLineToDisplay()
772    {
773      StringBuilder sb = new StringBuilder();
774      sb.append(getDataCommandLineName());
775      Collection<String> args = getObfuscatedCommandLineArguments(getDataCommandLineArguments(path.getText(), false));
776      args.removeAll(getConfigCommandLineArguments());
777      for (String arg : args)
778      {
779        sb.append(" ").append(CommandBuilder.escapeValue(arg));
780      }
781      return sb.toString();
782    }
783
784    private String getDataCommandLineName()
785    {
786      if (!leaveDatabaseEmpty.isSelected())
787      {
788        return getCommandLinePath(isLocal() ? "import-ldif" : "ldapmodify");
789      }
790
791      return null;
792    }
793
794    /**
795     * Returns the arguments of the command-line that can be used to generate
796     * the data.
797     *
798     * @param ldifFile
799     *          the LDIF file.
800     * @param useTemplate
801     *          whether to use a template or not.
802     * @return the arguments of the command-line that can be used to generate
803     *         the data.
804     */
805    private List<String> getDataCommandLineArguments(String ldifFile, boolean useTemplate)
806    {
807      List<String> args = new ArrayList<>();
808      if (!leaveDatabaseEmpty.isSelected())
809      {
810        if (isLocal())
811        {
812          if (!useTemplate)
813          {
814            args.add("--ldifFile");
815            args.add(ldifFile);
816          }
817          else
818          {
819            args.add("--templateFile");
820            args.add(ldifFile);
821            args.add("--randomSeed");
822            args.add("0");
823          }
824          args.add("--backendID");
825          args.add(getBackendName());
826        }
827        else
828        {
829          // If we are not local, we use ldapmodify to update the contents.
830          args.add("-a");
831          args.add("-f");
832          args.add(ldifFile);
833        }
834        args.addAll(getConnectionCommandLineArguments(true, !isLocal()));
835
836        if (isServerRunning() && isLocal())
837        {
838          args.addAll(getConfigCommandLineArguments());
839        }
840
841        args.add(getNoPropertiesFileArgument());
842      }
843
844      return args;
845    }
846
847    private void updateConfigurationOnline() throws Exception
848    {
849      SwingUtilities.invokeLater(new Runnable()
850      {
851        @Override
852        public void run()
853        {
854          List<String> args = getObfuscatedCommandLineArguments(getDSConfigCommandLineArguments());
855          args.removeAll(getConfigCommandLineArguments());
856          printEquivalentCommandLine(
857              getConfigCommandLineFullPath(), args, INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_CREATE_BASE_DN.get());
858        }
859      });
860
861      performTask();
862      printTaskDone();
863      if (isNewBackend())
864      {
865        createAdditionalIndexes();
866      }
867      refreshProgressBar();
868    }
869
870    private void updateConfigurationOffline() throws Exception
871    {
872      boolean configHandlerUpdated = false;
873      try
874      {
875        stopPoolingAndInitializeConfiguration();
876        configHandlerUpdated = true;
877
878        performTask();
879        printTaskDone();
880        refreshProgressBar();
881      }
882      finally
883      {
884        if (configHandlerUpdated)
885        {
886          startPoolingAndInitializeConfiguration();
887        }
888      }
889    }
890
891    private void printCreateNewBackendProgress(final String backendName) throws OpenDsException
892    {
893      SwingUtilities.invokeLater(new Runnable()
894      {
895        @Override
896        public void run()
897        {
898          LocalizableMessage message = INFO_CTRL_PANEL_CREATING_BACKEND_PROGRESS.get(backendName, newBaseDN);
899          getProgressDialog().appendProgressHtml(
900              Utilities.getProgressWithPoints(message, ColorAndFontConstants.progressFont));
901        }
902      });
903    }
904
905    private void performTask() throws Exception
906    {
907      final String backendName = getBackendName();
908      if (isNewBackend())
909      {
910        printCreateNewBackendProgress(backendName);
911        createBackend(backendName);
912      }
913      else
914      {
915        printCreateNewBaseDNProgress(backendName);
916        addNewBaseDN(backendName);
917      }
918    }
919
920    private void createBackend(String backendName) throws Exception
921    {
922      if (!isServerRunning())
923      {
924        createBackendOffline(backendName);
925        return;
926      }
927
928      createBackendOnline(backendName);
929    }
930
931    private void createBackendOffline(String backendName) throws OpenDsException
932    {
933      try
934      {
935        Set<DN> baseDNs = Collections.singleton(DN.valueOf(newBaseDN));
936        BackendCreationHelper.createBackendOffline(backendName, baseDNs, getSelectedBackendType().getBackend());
937      }
938      catch (Exception e)
939      {
940        throw new OfflineUpdateException(ERROR_CTRL_PANEL_CREATE_NEW_BACKEND.get(backendName, e.getMessage()), e);
941      }
942    }
943
944    private void createBackendOnline(String backendName) throws Exception
945    {
946      Set<DN> baseDNs = Collections.singleton(DN.valueOf(newBaseDN));
947      BackendCreationHelper.createBackendOffline(backendName, baseDNs, getSelectedBackendType().getBackend());
948    }
949
950    private RootCfgClient getRootConfigurationClient() throws LdapException
951    {
952      return getInfo().getConnection().getRootConfiguration();
953    }
954
955    private void addNewBaseDN(String backendName) throws Exception
956    {
957      if (!isServerRunning())
958      {
959        addNewBaseDNOffline(backendName);
960        return;
961      }
962
963      final BackendCfgClient backend = getRootConfigurationClient().getBackend(backendName);
964      final Set<DN> baseDNs = backend.getBaseDN();
965      baseDNs.add(DN.valueOf(newBaseDN));
966      backend.setBaseDN(baseDNs);
967      backend.commit();
968    }
969
970    private void addNewBaseDNOffline(String backendName) throws OpenDsException
971    {
972      try
973      {
974        getInfo().initializeConfigurationFramework();
975        final File config = Installation.getLocal().getCurrentConfigurationFile();
976        try (ManagementContext context = LDAPManagementContext.newLDIFManagementContext(config))
977        {
978          final BackendCfgClient backend = context.getRootConfiguration().getBackend(backendName);
979          final SortedSet<DN> baseDNs = backend.getBaseDN();
980          baseDNs.add(DN.valueOf(newBaseDN));
981          backend.setBaseDN(baseDNs);
982          backend.commit();
983        }
984      }
985      catch (Exception e)
986      {
987        throw new OfflineUpdateException(LocalizableMessage.raw(e.getMessage()), e);
988      }
989    }
990
991    private void createAdditionalIndexes() throws Exception
992    {
993      final String backendName = getBackendName();
994      displayCreateAdditionalIndexesDsConfigCmdLine();
995      final RootCfgClient root = getRootConfigurationClient();
996      addBackendDefaultIndexes((PluggableBackendCfgClient) root.getBackend(backendName));
997      displayCreateAdditionalIndexesDone();
998    }
999
1000    private void addBackendDefaultIndexes(PluggableBackendCfgClient backendCfgClient) throws Exception
1001    {
1002      for (DefaultIndex defaultIndex : BackendCreationHelper.DEFAULT_INDEXES)
1003      {
1004        final BackendIndexCfgClient index = backendCfgClient.createBackendIndex(
1005            BackendIndexCfgDefn.getInstance(), defaultIndex.getName(), null);
1006
1007        final List<IndexType> indexTypes = new LinkedList<>();
1008        indexTypes.add(IndexType.EQUALITY);
1009        if (defaultIndex.shouldCreateSubstringIndex())
1010        {
1011          indexTypes.add(IndexType.SUBSTRING);
1012        }
1013        index.setIndexType(indexTypes);
1014        index.commit();
1015      }
1016    }
1017
1018    private void printCreateNewBaseDNProgress(final String backendName) throws OpenDsException
1019    {
1020      SwingUtilities.invokeLater(new Runnable()
1021      {
1022        @Override
1023        public void run()
1024        {
1025          LocalizableMessage message = INFO_CTRL_PANEL_CREATING_BASE_DN_PROGRESS.get(newBaseDN, backendName);
1026          getProgressDialog().appendProgressHtml(
1027              Utilities.getProgressWithPoints(message, ColorAndFontConstants.progressFont));
1028        }
1029      });
1030    }
1031
1032    private void printTaskDone()
1033    {
1034      SwingUtilities.invokeLater(new Runnable()
1035      {
1036        @Override
1037        public void run()
1038        {
1039          getProgressDialog().appendProgressHtml(
1040              Utilities.getProgressDone(ColorAndFontConstants.progressFont) + "<br><br>");
1041        }
1042      });
1043    }
1044
1045    private void refreshProgressBar()
1046    {
1047      if (progressAfterConfigurationUpdate > 0)
1048      {
1049        SwingUtilities.invokeLater(new Runnable()
1050        {
1051          @Override
1052          public void run()
1053          {
1054            getProgressDialog().getProgressBar().setIndeterminate(false);
1055            getProgressDialog().getProgressBar().setValue(progressAfterConfigurationUpdate);
1056          }
1057        });
1058      }
1059    }
1060
1061    private void displayCreateAdditionalIndexesDsConfigCmdLine()
1062    {
1063      final List<List<String>> argsArray = new ArrayList<>();
1064      for (DefaultIndex defaultIndex : BackendCreationHelper.DEFAULT_INDEXES)
1065      {
1066        argsArray.add(getCreateIndexCommandLineArguments(defaultIndex));
1067      }
1068
1069      final StringBuilder sb = new StringBuilder();
1070      for (List<String> args : argsArray)
1071      {
1072        sb.append(getEquivalentCommandLine(getCommandLinePath("dsconfig"), getObfuscatedCommandLineArguments(args)));
1073        sb.append("<br><br>");
1074      }
1075
1076      SwingUtilities.invokeLater(new Runnable()
1077      {
1078        @Override
1079        public void run()
1080        {
1081          getProgressDialog().appendProgressHtml(Utilities.applyFont(
1082              INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_CREATE_ADDITIONAL_INDEXES.get()
1083              + "<br><br><b>" + sb + "</b>", ColorAndFontConstants.progressFont));
1084          getProgressDialog().appendProgressHtml(Utilities.getProgressWithPoints(
1085              INFO_CTRL_PANEL_CREATING_ADDITIONAL_INDEXES_PROGRESS.get(), ColorAndFontConstants.progressFont));
1086        }
1087      });
1088    }
1089
1090    private List<String> getCreateIndexCommandLineArguments(final DefaultIndex defaultIndex)
1091    {
1092      final List<String> args = new ArrayList<>();
1093      args.add("create-backend-index");
1094      args.add("--backend-name");
1095      args.add(getBackendName());
1096      args.add("--type");
1097      args.add("generic");
1098      args.add("--index-name");
1099      args.add(defaultIndex.getName());
1100      args.add("--set");
1101      args.add("index-type:" + IndexType.EQUALITY);
1102      if (defaultIndex.shouldCreateSubstringIndex())
1103      {
1104        args.add("--set");
1105        args.add("index-type:" + IndexType.SUBSTRING);
1106      }
1107      args.addAll(getConnectionCommandLineArguments());
1108      args.add(getNoPropertiesFileArgument());
1109      args.add("--no-prompt");
1110
1111      return args;
1112    }
1113
1114    private void displayCreateAdditionalIndexesDone()
1115    {
1116      SwingUtilities.invokeLater(new Runnable()
1117      {
1118        @Override
1119        public void run()
1120        {
1121          getProgressDialog().appendProgressHtml(
1122              Utilities.getProgressDone(ColorAndFontConstants.progressFont) + "<br><br>");
1123        }
1124      });
1125    }
1126
1127    /**
1128     * Creates the data in the new base DN.
1129     *
1130     * @throws OpenDsException
1131     *           if there is an error importing contents.
1132     * @throws IOException
1133     *           if there is an err
1134     */
1135    private void updateData() throws OpenDsException, IOException
1136    {
1137      final boolean leaveEmpty = leaveDatabaseEmpty.isSelected();
1138      final boolean createBaseEntry = onlyCreateBaseEntry.isSelected();
1139      final boolean importLDIF = importDataFromLDIF.isSelected();
1140      final boolean generateData = !leaveEmpty && !createBaseEntry && !importLDIF;
1141      final String nEntries = numberOfEntries.getText();
1142      final String ldif = path.getText();
1143
1144      if (leaveEmpty)
1145      {
1146        state = State.FINISHED_SUCCESSFULLY;
1147      }
1148      else
1149      {
1150        final ProgressDialog progressDialog = getProgressDialog();
1151        String ldifFile;
1152        if (importLDIF)
1153        {
1154          ldifFile = ldif;
1155          final String cmdLine = getDataCommandLineToDisplay();
1156          SwingUtilities.invokeLater(new Runnable()
1157          {
1158            @Override
1159            public void run()
1160            {
1161              progressDialog.appendProgressHtml(Utilities.applyFont("Equivalent command line:<br><b>" + cmdLine
1162                  + "</b><br><br>", ColorAndFontConstants.progressFont));
1163            }
1164          });
1165        }
1166        else if (createBaseEntry)
1167        {
1168          SwingUtilities.invokeLater(new Runnable()
1169          {
1170            @Override
1171            public void run()
1172            {
1173              progressDialog.appendProgressHtml(Utilities.getProgressWithPoints(
1174                  INFO_PROGRESS_CREATING_BASE_ENTRY.get(newBaseDN), ColorAndFontConstants.progressFont));
1175            }
1176          });
1177          InstallerHelper helper = new InstallerHelper();
1178          File f = helper.createBaseEntryTempFile(newBaseDN);
1179          ldifFile = f.getAbsolutePath();
1180        }
1181        else
1182        {
1183          SwingUtilities.invokeLater(new Runnable()
1184          {
1185            @Override
1186            public void run()
1187            {
1188              if (isLocal())
1189              {
1190                progressDialog.appendProgressHtml(Utilities.applyFont(
1191                    INFO_PROGRESS_IMPORT_AUTOMATICALLY_GENERATED.get(nEntries).toString(),
1192                    ColorAndFontConstants.progressFont) + "<br>");
1193              }
1194              else
1195              {
1196                getProgressDialog().appendProgressHtml(Utilities.getProgressWithPoints(
1197                    INFO_PROGRESS_IMPORT_AUTOMATICALLY_GENERATED_REMOTE.get(nEntries),
1198                    ColorAndFontConstants.progressFont));
1199              }
1200            }
1201          });
1202
1203          File f = SetupUtils.createTemplateFile(newBaseDN, Integer.parseInt(nEntries));
1204          if (!isLocal())
1205          {
1206            File tempFile = File.createTempFile("opendj-control-panel", ".ldif");
1207            tempFile.deleteOnExit();
1208            ldifFile = tempFile.getAbsolutePath();
1209
1210            // Create the LDIF file locally using make-ldif
1211            List<String> makeLDIFArgs = new ArrayList<>();
1212            makeLDIFArgs.add("--templateFile");
1213            makeLDIFArgs.add(f.getAbsolutePath());
1214            makeLDIFArgs.add("--ldifFile");
1215            makeLDIFArgs.add(ldifFile);
1216            makeLDIFArgs.add("--randomSeed");
1217            makeLDIFArgs.add("0");
1218            makeLDIFArgs.add("--resourcePath");
1219
1220            File makeLDIFPath = new File(Installation.getLocal().getConfigurationDirectory(), "MakeLDIF");
1221            makeLDIFArgs.add(makeLDIFPath.getAbsolutePath());
1222            makeLDIFArgs.addAll(getConfigCommandLineArguments());
1223
1224            MakeLDIF makeLDIF = new MakeLDIF();
1225            String[] array = new String[makeLDIFArgs.size()];
1226            makeLDIFArgs.toArray(array);
1227            returnCode = makeLDIF.makeLDIFMain(array, false, false, outPrintStream, errorPrintStream);
1228            f.delete();
1229
1230            if (returnCode != 0)
1231            {
1232              throw new OnlineUpdateException(ERR_CTRL_PANEL_ERROR_CREATING_NEW_DATA_LDIF.get(returnCode), null);
1233            }
1234          }
1235          else
1236          {
1237            ldifFile = f.getAbsolutePath();
1238          }
1239        }
1240
1241        List<String> arguments = getDataCommandLineArguments(ldifFile, generateData);
1242        String[] args = new String[arguments.size()];
1243        arguments.toArray(args);
1244        if (createBaseEntry || !isLocal())
1245        {
1246          outPrintStream.setNotifyListeners(false);
1247          errorPrintStream.setNotifyListeners(false);
1248        }
1249        try
1250        {
1251          if (isServerRunning())
1252          {
1253            if (isLocal() || importLDIF)
1254            {
1255              returnCode = ImportLDIF.mainImportLDIF(args, false, outPrintStream, errorPrintStream);
1256            }
1257            else
1258            {
1259              returnCode = LDAPModify.mainModify(args, false, outPrintStream, errorPrintStream);
1260            }
1261          }
1262          else
1263          {
1264            returnCode = executeCommandLine(getDataCommandLineName(), args);
1265          }
1266        }
1267        finally
1268        {
1269          outPrintStream.setNotifyListeners(true);
1270          errorPrintStream.setNotifyListeners(true);
1271        }
1272
1273        if (returnCode != 0)
1274        {
1275          state = State.FINISHED_WITH_ERROR;
1276        }
1277        else
1278        {
1279          if (createBaseEntry || (!isLocal() && generateData))
1280          {
1281            SwingUtilities.invokeLater(new Runnable()
1282            {
1283              @Override
1284              public void run()
1285              {
1286                progressDialog.appendProgressHtml(Utilities.getProgressDone(ColorAndFontConstants.progressFont));
1287              }
1288            });
1289          }
1290          state = State.FINISHED_SUCCESSFULLY;
1291        }
1292      }
1293    }
1294
1295    @Override
1296    protected String getCommandLinePath()
1297    {
1298      return null;
1299    }
1300
1301    @Override
1302    protected List<String> getCommandLineArguments()
1303    {
1304      return new ArrayList<>();
1305    }
1306
1307    private String getConfigCommandLineFullPath()
1308    {
1309      return isServerRunning() ? getCommandLinePath("dsconfig") : null;
1310    }
1311
1312    private List<String> getDSConfigCommandLineArguments()
1313    {
1314      List<String> args = new ArrayList<>();
1315      if (isServerRunning())
1316      {
1317        if (isNewBackend())
1318        {
1319          args.add("create-backend");
1320          args.add("--backend-name");
1321          args.add(getBackendName());
1322          args.add("--set");
1323          args.add("base-dn:" + newBaseDN);
1324          args.add("--set");
1325          args.add("enabled:true");
1326          args.add("--type");
1327          args.add(BackendTypeHelper.filterSchemaBackendName(getSelectedBackendType().getBackend().getName()));
1328        }
1329        else
1330        {
1331          args.add("set-backend-prop");
1332          args.add("--backend-name");
1333          args.add(getBackendName());
1334          args.add("--add");
1335          args.add("base-dn:" + newBaseDN);
1336        }
1337        args.addAll(getConnectionCommandLineArguments());
1338        args.add(getNoPropertiesFileArgument());
1339        args.add("--no-prompt");
1340      }
1341      return args;
1342    }
1343
1344    @Override
1345    public void runTask()
1346    {
1347      state = State.RUNNING;
1348      lastException = null;
1349
1350      try
1351      {
1352        if (isServerRunning())
1353        {
1354          updateConfigurationOnline();
1355        }
1356        else
1357        {
1358          updateConfigurationOffline();
1359        }
1360        updateData();
1361      }
1362      catch (Throwable t)
1363      {
1364        lastException = t;
1365        state = State.FINISHED_WITH_ERROR;
1366      }
1367    }
1368
1369    @Override
1370    public Set<String> getBackends()
1371    {
1372      return backendSet;
1373    }
1374  }
1375}