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-2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027
028package org.opends.guitools.controlpanel.ui;
029
030import static org.opends.messages.AdminToolMessages.*;
031
032import static org.opends.messages.QuickSetupMessages.INFO_NO_LDIF_PATH;
033
034import java.awt.Component;
035import java.awt.GridBagConstraints;
036import java.io.File;
037import java.util.ArrayList;
038import java.util.Collection;
039import java.util.HashSet;
040import java.util.LinkedHashSet;
041import java.util.Set;
042import java.util.TreeSet;
043
044import javax.swing.DefaultComboBoxModel;
045import javax.swing.JButton;
046import javax.swing.JCheckBox;
047import javax.swing.JComboBox;
048import javax.swing.JLabel;
049import javax.swing.JTextField;
050import javax.swing.SwingUtilities;
051import javax.swing.event.ChangeEvent;
052import javax.swing.event.ChangeListener;
053import javax.swing.event.DocumentEvent;
054import javax.swing.event.DocumentListener;
055
056import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
057import org.opends.guitools.controlpanel.datamodel.ScheduleType;
058import org.opends.guitools.controlpanel.datamodel.ServerDescriptor;
059import org.opends.guitools.controlpanel.event.BrowseActionListener;
060import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
061import org.opends.guitools.controlpanel.task.Task;
062import org.opends.guitools.controlpanel.ui.components.ScheduleSummaryPanel;
063import org.opends.guitools.controlpanel.util.Utilities;
064import org.forgerock.i18n.LocalizableMessage;
065import org.opends.server.tools.ExportLDIF;
066
067/**
068 * The panel where the user can export the contents of the server to an LDIF
069 * file.
070 *
071 */
072public class ExportLDIFPanel extends InclusionExclusionPanel
073{
074 private static final long serialVersionUID = 2256902594454214644L;
075  private JComboBox backends;
076  private JTextField file;
077  private JCheckBox overwrite;
078  private JCheckBox compressData;
079  private JCheckBox encryptData;
080  private JCheckBox generateSignedHash;
081  private JCheckBox wrapText;
082  private JTextField wrapColumn;
083  private JButton bBrowse;
084
085  private JLabel lBackend;
086  private JLabel lNoBackendsFound;
087  private JLabel lFile;
088  private JLabel lExportOptions;
089  private JLabel lRemoteFileHelp;
090  private JCheckBox excludeOperationalAttrs;
091
092  private DocumentListener documentListener;
093
094  private ScheduleSummaryPanel schedulePanel;
095
096  /**
097   * Default constructor.
098   *
099   */
100  public ExportLDIFPanel()
101  {
102    super();
103    createLayout();
104  }
105
106  /** {@inheritDoc} */
107  public LocalizableMessage getTitle()
108  {
109    return INFO_CTRL_PANEL_EXPORT_LDIF_TITLE.get();
110  }
111
112  /** {@inheritDoc} */
113  public Component getPreferredFocusComponent()
114  {
115    return file;
116  }
117
118  /** {@inheritDoc} */
119  public void toBeDisplayed(boolean visible)
120  {
121    if (visible)
122    {
123      documentListener.changedUpdate(null);
124    }
125  }
126
127  /**
128   * Creates the layout of the panel (but the contents are not populated here).
129   */
130  private void createLayout()
131  {
132    GridBagConstraints gbc = new GridBagConstraints();
133    gbc.gridx = 0;
134    gbc.gridy = 0;
135    gbc.gridwidth = 4;
136    addErrorPane(gbc);
137
138    gbc.anchor = GridBagConstraints.WEST;
139    gbc.weightx = 0.0;
140    gbc.gridy ++;
141    gbc.gridwidth = 1;
142    gbc.fill = GridBagConstraints.NONE;
143    lBackend = Utilities.createPrimaryLabel(
144        INFO_CTRL_PANEL_BACKEND_LABEL.get());
145    add(lBackend, gbc);
146    gbc.insets.left = 10;
147    gbc.gridx = 1;
148    backends = Utilities.createComboBox();
149    backends.setModel(new DefaultComboBoxModel(new String[]{}));
150    gbc.gridwidth = 3;
151    add(backends, gbc);
152    lNoBackendsFound = Utilities.createDefaultLabel(
153        INFO_CTRL_PANEL_NO_BACKENDS_FOUND_LABEL.get());
154    add(lNoBackendsFound, gbc);
155    lNoBackendsFound.setVisible(false);
156    gbc.insets.top = 10;
157
158    gbc.gridx = 0;
159    gbc.gridy ++;
160    gbc.insets.left = 0;
161    gbc.gridwidth = 1;
162    lFile = Utilities.createPrimaryLabel(
163        INFO_CTRL_PANEL_EXPORT_TO_FILE_LABEL.get());
164    add(lFile, gbc);
165
166    gbc.gridx = 1;
167    gbc.insets.left = 10;
168    gbc.gridwidth = 2;
169    file = Utilities.createTextField();
170    documentListener = new DocumentListener()
171    {
172      /** {@inheritDoc} */
173      public void changedUpdate(DocumentEvent ev)
174      {
175        String text = file.getText().trim();
176        setEnabledOK(text != null && text.length() > 0 && !errorPane.isVisible());
177      }
178      /** {@inheritDoc} */
179      public void removeUpdate(DocumentEvent ev)
180      {
181        changedUpdate(ev);
182      }
183      /** {@inheritDoc} */
184      public void insertUpdate(DocumentEvent ev)
185      {
186        changedUpdate(ev);
187      }
188    };
189    file.getDocument().addDocumentListener(documentListener);
190    gbc.weightx = 1.0;
191    gbc.fill = GridBagConstraints.HORIZONTAL;
192    add(file, gbc);
193    bBrowse = Utilities.createButton(
194        INFO_CTRL_PANEL_BROWSE_BUTTON_LABEL.get());
195    bBrowse.addActionListener(
196        new BrowseActionListener(file,
197            BrowseActionListener.BrowseType.CREATE_LDIF_FILE,  this));
198    gbc.gridx = 3;
199    gbc.gridwidth = 1;
200    gbc.weightx = 0.0;
201    bBrowse.setOpaque(false);
202    add(bBrowse, gbc);
203
204    lRemoteFileHelp = Utilities.createInlineHelpLabel(
205        INFO_CTRL_PANEL_REMOTE_SERVER_PATH.get());
206    gbc.gridx = 1;
207    gbc.insets.top = 3;
208    gbc.insets.left = 10;
209    gbc.gridy ++;
210    gbc.gridwidth = 3;
211    add(lRemoteFileHelp, gbc);
212
213    gbc.gridx = 1;
214    gbc.gridy ++;
215    gbc.insets.left = 30;
216    gbc.insets.top = 5;
217    gbc.gridwidth = 3;
218    overwrite =
219      Utilities.createCheckBox(INFO_CTRL_PANEL_EXPORT_OVERWRITE_LABEL.get());
220    overwrite.setOpaque(false);
221    add(overwrite, gbc);
222
223    gbc.gridx = 0;
224    gbc.gridy ++;
225    gbc.insets.left = 0;
226    gbc.insets.top = 10;
227    gbc.gridwidth = 1;
228    lExportOptions =
229      Utilities.createPrimaryLabel(INFO_CTRL_PANEL_EXPORT_OPTIONS.get());
230    add(lExportOptions, gbc);
231
232    schedulePanel = new ScheduleSummaryPanel(
233        INFO_CTRL_PANEL_EXPORT_LDIF_TITLE.get().toString());
234    schedulePanel.setSchedule(ScheduleType.createLaunchNow());
235
236    gbc.insets.left = 10;
237    gbc.gridx = 1;
238    gbc.gridwidth = 3;
239    add(schedulePanel, gbc);
240
241    compressData = Utilities.createCheckBox(
242        INFO_CTRL_PANEL_COMPRESS_DATA_LABEL.get());
243    compressData.setSelected(false);
244
245    gbc.gridy ++;
246    gbc.insets.top = 5;
247    add(compressData, gbc);
248
249    encryptData = Utilities.createCheckBox(
250        INFO_CTRL_PANEL_ENCRYPT_DATA_LABEL.get());
251
252    /*
253    gbc.gridy ++;
254    gbc.insets.top = 5;
255    add(encryptData, gbc);
256*/
257    generateSignedHash = Utilities.createCheckBox(
258        INFO_CTRL_PANEL_EXPORT_GENERATE_SIGNED_HASH.get());
259
260    encryptData.addChangeListener(new ChangeListener()
261    {
262      /** {@inheritDoc} */
263      public void stateChanged(ChangeEvent ev)
264      {
265        generateSignedHash.setEnabled(encryptData.isSelected());
266      }
267    });
268    encryptData.setSelected(false);
269    generateSignedHash.setEnabled(false);
270
271    /*
272    gbc.gridy ++;
273    gbc.insets.left = 30;
274    add(generateSignedHash, gbc);
275*/
276    wrapText = Utilities.createCheckBox(INFO_CTRL_PANEL_EXPORT_WRAP_TEXT.get());
277    wrapText.setOpaque(false);
278    gbc.insets.left = 10;
279    gbc.insets.top = 10;
280    gbc.gridy ++;
281    gbc.gridwidth = 1;
282    add(wrapText, gbc);
283
284    gbc.insets.left = 5;
285    gbc.gridx = 2;
286    wrapColumn = Utilities.createTextField("80", 4);
287    gbc.fill = GridBagConstraints.NONE;
288    add(wrapColumn, gbc);
289    gbc.fill = GridBagConstraints.HORIZONTAL;
290
291    wrapText.addChangeListener(new ChangeListener()
292    {
293      /** {@inheritDoc} */
294      public void stateChanged(ChangeEvent ev)
295      {
296        wrapColumn.setEnabled(wrapText.isSelected());
297      }
298    });
299    wrapColumn.setEnabled(false);
300    wrapText.setSelected(false);
301
302    gbc.insets.top = 10;
303    gbc.insets.left = 0;
304    gbc.gridy ++;
305    gbc.gridx = 0;
306    gbc.gridwidth = 4;
307    gbc.fill = GridBagConstraints.HORIZONTAL;
308    excludeOperationalAttrs = Utilities.createCheckBox(
309        INFO_CTRL_PANEL_EXCLUDE_OPERATIONAL_ATTRIBUTES.get());
310    excludeOperationalAttrs.setOpaque(false);
311    add(createDataExclusionOptions(new JLabel[]{null},
312        new Component[]{excludeOperationalAttrs}), gbc);
313    gbc.gridy ++;
314    gbc.insets.top = 15;
315    add(createDataInclusionOptions(new JLabel[]{}, new Component[]{}), gbc);
316    addBottomGlue(gbc);
317  }
318
319  /** {@inheritDoc} */
320  public void configurationChanged(ConfigurationChangeEvent ev)
321  {
322    ServerDescriptor desc = ev.getNewDescriptor();
323    updateSimpleBackendComboBoxModel(backends, lNoBackendsFound,
324        ev.getNewDescriptor());
325
326    updateErrorPaneAndOKButtonIfAuthRequired(desc,
327       isLocal() ? INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_FOR_EXPORT.get() :
328      INFO_CTRL_PANEL_CANNOT_CONNECT_TO_REMOTE_DETAILS.get(desc.getHostname()));
329
330    SwingUtilities.invokeLater(new Runnable()
331    {
332      public void run()
333      {
334        lRemoteFileHelp.setVisible(!isLocal());
335        bBrowse.setVisible(isLocal());
336      }
337    });
338  }
339
340  /** {@inheritDoc} */
341  protected void checkOKButtonEnable()
342  {
343    documentListener.changedUpdate(null);
344  }
345
346  /** {@inheritDoc} */
347  public void okClicked()
348  {
349    setPrimaryValid(lBackend);
350    setPrimaryValid(lFile);
351    setPrimaryValid(lExportOptions);
352    final LinkedHashSet<LocalizableMessage> errors = new LinkedHashSet<>();
353
354    String backendName = (String)backends.getSelectedItem();
355    if (backendName == null)
356    {
357      errors.add(ERR_CTRL_PANEL_NO_BACKEND_SELECTED.get());
358      setPrimaryInvalid(lBackend);
359    }
360
361    String ldifPath = file.getText();
362    if (ldifPath == null || ldifPath.trim().equals(""))
363    {
364      errors.add(INFO_NO_LDIF_PATH.get());
365      setPrimaryInvalid(lFile);
366    }
367    else if (isLocal())
368    {
369      File f = new File(ldifPath);
370      if (f.isDirectory())
371      {
372        errors.add(ERR_CTRL_PANEL_EXPORT_DIRECTORY_PROVIDED.get(ldifPath));
373      }
374    }
375
376    addScheduleErrors(getSchedule(), errors, lExportOptions);
377    if (wrapText.isSelected())
378    {
379      final String cols = wrapColumn.getText();
380      final int minValue = 1;
381      final int maxValue = 1000;
382      final LocalizableMessage errMsg = ERR_CTRL_PANEL_INVALID_WRAP_COLUMN.get(minValue, maxValue);
383      if (!checkIntValue(errors, cols, minValue, maxValue, errMsg))
384      {
385        setPrimaryInvalid(lExportOptions);
386      }
387    }
388
389    updateIncludeExclude(errors, backendName);
390
391    if (errors.isEmpty())
392    {
393      ProgressDialog progressDialog = new ProgressDialog(
394          Utilities.createFrame(),
395          Utilities.getParentDialog(this), getTitle(), getInfo());
396      ExportTask newTask = new ExportTask(getInfo(), progressDialog);
397      for (Task task : getInfo().getTasks())
398      {
399        task.canLaunch(newTask, errors);
400      }
401      boolean confirmed = true;
402      if (errors.isEmpty())
403      {
404        File f = new File(ldifPath);
405        if (overwrite.isSelected() && f.exists())
406        {
407          confirmed = displayConfirmationDialog(
408              INFO_CTRL_PANEL_CONFIRMATION_REQUIRED_SUMMARY.get(),
409              INFO_CTRL_PANEL_CONFIRMATION_EXPORT_LDIF_DETAILS.get(ldifPath));
410        }
411      }
412      if (errors.isEmpty() && confirmed)
413      {
414        launchOperation(newTask,
415            INFO_CTRL_PANEL_EXPORTING_LDIF_SUMMARY.get(backends.getSelectedItem()),
416            INFO_CTRL_PANEL_EXPORTING_LDIF_SUCCESSFUL_SUMMARY.get(),
417            INFO_CTRL_PANEL_EXPORTING_LDIF_SUCCESSFUL_DETAILS.get(),
418            ERR_CTRL_PANEL_EXPORTING_LDIF_ERROR_SUMMARY.get(),
419            null,
420            ERR_CTRL_PANEL_EXPORTING_LDIF_ERROR_DETAILS,
421            progressDialog);
422        progressDialog.setVisible(true);
423        Utilities.getParentDialog(this).setVisible(false);
424      }
425    }
426    if (!errors.isEmpty())
427    {
428      displayErrorDialog(errors);
429    }
430  }
431
432  /** {@inheritDoc} */
433  public void cancelClicked()
434  {
435    setPrimaryValid(lBackend);
436    setPrimaryValid(lFile);
437    setPrimaryValid(lExportOptions);
438    super.cancelClicked();
439  }
440
441  private ScheduleType getSchedule()
442  {
443    return schedulePanel.getSchedule();
444  }
445
446  /**
447   * The class that performs the export.
448   *
449   */
450  protected class ExportTask extends InclusionExclusionTask
451  {
452    private Set<String> backendSet;
453    private String fileName;
454    /**
455     * The constructor of the task.
456     * @param info the control panel info.
457     * @param dlg the progress dialog that shows the progress of the task.
458     */
459    public ExportTask(ControlPanelInfo info, ProgressDialog dlg)
460    {
461      super(info, dlg);
462      backendSet = new HashSet<>();
463      backendSet.add((String)backends.getSelectedItem());
464      fileName = file.getText();
465    }
466
467    /** {@inheritDoc} */
468    public Type getType()
469    {
470      return Type.EXPORT_LDIF;
471    }
472
473    /** {@inheritDoc} */
474    public LocalizableMessage getTaskDescription()
475    {
476      return INFO_CTRL_PANEL_EXPORT_TASK_DESCRIPTION.get(
477          backendSet.iterator().next(), fileName);
478    }
479
480    /** {@inheritDoc} */
481    public boolean canLaunch(Task taskToBeLaunched,
482        Collection<LocalizableMessage> incompatibilityReasons)
483    {
484      boolean canLaunch = true;
485      if (state == State.RUNNING && runningOnSameServer(taskToBeLaunched))
486      {
487        // All the operations are incompatible if they apply to this backend.
488        Set<String> backends = new TreeSet<>(taskToBeLaunched.getBackends());
489        backends.retainAll(getBackends());
490        if (!backends.isEmpty())
491        {
492          incompatibilityReasons.add(getIncompatibilityMessage(this, taskToBeLaunched));
493          canLaunch = false;
494        }
495      }
496      return canLaunch;
497    }
498
499    /** {@inheritDoc} */
500    public void runTask()
501    {
502      state = State.RUNNING;
503      lastException = null;
504      try
505      {
506        ArrayList<String> arguments = getCommandLineArguments();
507
508        String[] args = new String[arguments.size()];
509
510        arguments.toArray(args);
511        if (isServerRunning())
512        {
513          returnCode = ExportLDIF.mainExportLDIF(args, false, outPrintStream,
514              errorPrintStream);
515        }
516        else
517        {
518          returnCode = executeCommandLine(getCommandLinePath(), args);
519        }
520        if (returnCode != 0)
521        {
522          state = State.FINISHED_WITH_ERROR;
523        }
524        else
525        {
526          state = State.FINISHED_SUCCESSFULLY;
527        }
528      }
529      catch (Throwable t)
530      {
531        lastException = t;
532        state = State.FINISHED_WITH_ERROR;
533      }
534    }
535
536    /** {@inheritDoc} */
537    public Set<String> getBackends()
538    {
539      return backendSet;
540    }
541
542    /** {@inheritDoc} */
543    protected ArrayList<String> getCommandLineArguments()
544    {
545      ArrayList<String> args = new ArrayList<>();
546      args.add("--ldifFile");
547      args.add(fileName);
548      args.add("--backendID");
549      args.add(backendSet.iterator().next());
550
551      if (!overwrite.isSelected())
552      {
553        args.add("--appendToLDIF");
554      }
555
556      if (compressData.isSelected())
557      {
558        args.add("--compress");
559      }
560      if (wrapText.isSelected())
561      {
562        args.add("--wrapColumn");
563        args.add(wrapColumn.getText().trim());
564      }
565      if (excludeOperationalAttrs.isSelected())
566      {
567        args.add("--excludeOperational");
568      }
569
570      args.addAll(super.getCommandLineArguments());
571
572      args.addAll(getScheduleArgs(getSchedule()));
573
574      if (isServerRunning())
575      {
576        args.addAll(getConfigCommandLineArguments());
577      }
578      args.add(getNoPropertiesFileArgument());
579
580      return args;
581    }
582
583    /** {@inheritDoc} */
584    protected String getCommandLinePath()
585    {
586      return getCommandLinePath("export-ldif");
587    }
588  }
589}