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