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 java.awt.GridBagConstraints;
033import java.io.File;
034import java.util.ArrayList;
035import java.util.Collection;
036import java.util.HashSet;
037import java.util.LinkedHashSet;
038import java.util.Set;
039import java.util.TreeSet;
040
041import javax.swing.JLabel;
042import javax.swing.JTextField;
043import javax.swing.SwingUtilities;
044import javax.swing.event.ListSelectionEvent;
045import javax.swing.event.ListSelectionListener;
046
047import org.opends.guitools.controlpanel.datamodel.BackendDescriptor;
048import org.opends.guitools.controlpanel.datamodel.BackupDescriptor;
049import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
050import org.opends.guitools.controlpanel.datamodel.ServerDescriptor;
051import org.opends.guitools.controlpanel.event.BackupCreatedEvent;
052import org.opends.guitools.controlpanel.event.BackupCreatedListener;
053import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
054import org.opends.guitools.controlpanel.task.Task;
055import org.opends.guitools.controlpanel.util.Utilities;
056import org.forgerock.i18n.LocalizableMessage;
057import org.opends.server.tools.RestoreDB;
058
059/**
060 * The panel that appears when the user wants to restore from a backup.
061 *
062 */
063public class RestorePanel extends BackupListPanel
064implements BackupCreatedListener
065{
066  private static final long serialVersionUID = -205585323128518051L;
067  private ListSelectionListener listener;
068  private JLabel lBackupID;
069  private JTextField backupID;
070
071  /**
072   * Constructor of the panel.
073   *
074   */
075  public RestorePanel()
076  {
077    super();
078    createLayout();
079  }
080
081  /** {@inheritDoc} */
082  public LocalizableMessage getTitle()
083  {
084    return INFO_CTRL_PANEL_RESTORE_PANEL_TITLE.get();
085  }
086
087  /** {@inheritDoc} */
088  public void backupCreated(BackupCreatedEvent ev)
089  {
090    boolean refreshList = false;
091    File f = new File(parentDirectory.getText());
092    File fBackup = ev.getBackupDescriptor().getPath();
093    if (fBackup.equals(f))
094    {
095      refreshList = true;
096    }
097    else
098    {
099      f = f.getParentFile();
100      if (f != null)
101      {
102        refreshList = fBackup.equals(f);
103      }
104    }
105    if (refreshList && isVisible())
106    {
107      // If not visible the list will be refreshed next time the dialog is
108      // opened.
109      SwingUtilities.invokeLater(new Runnable()
110      {
111        public void run()
112        {
113          refreshList();
114        }
115      });
116    }
117  }
118
119  /** {@inheritDoc} */
120  public void setInfo(ControlPanelInfo info)
121  {
122    super.setInfo(info);
123    info.addBackupCreatedListener(this);
124  }
125
126  /** {@inheritDoc} */
127  public void toBeDisplayed(boolean visible)
128  {
129    if (visible)
130    {
131      listener.valueChanged(null);
132    }
133  }
134
135  /** {@inheritDoc} */
136  public void configurationChanged(ConfigurationChangeEvent ev)
137  {
138    final ServerDescriptor desc = ev.getNewDescriptor();
139    SwingUtilities.invokeLater(new Runnable()
140    {
141      /** {@inheritDoc} */
142      public void run()
143      {
144        lBackupID.setVisible(!desc.isLocal());
145        backupID.setVisible(!desc.isLocal());
146      }
147    });
148    super.configurationChanged(ev);
149    updateErrorPaneAndOKButtonIfAuthRequired(desc,
150        isLocal() ? INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_FOR_RESTORE.get() :
151      INFO_CTRL_PANEL_CANNOT_CONNECT_TO_REMOTE_DETAILS.get(desc.getHostname()));
152  }
153
154  /** {@inheritDoc} */
155  protected void verifyBackupClicked()
156  {
157    LinkedHashSet<LocalizableMessage> errors = new LinkedHashSet<>();
158    // Launch the task in another progress dialog.
159    ProgressDialog dlg = new ProgressDialog(
160        Utilities.createFrame(),
161        Utilities.getParentDialog(this),
162        INFO_CTRL_PANEL_VERIFY_BACKUP_TITLE.get(), getInfo());
163    RestoreTask newTask = new RestoreTask(getInfo(), dlg, true);
164    for (Task task : getInfo().getTasks())
165    {
166      task.canLaunch(newTask, errors);
167    }
168    if (errors.isEmpty())
169    {
170      BackupDescriptor backup = getSelectedBackup();
171      launchOperation(newTask,
172          INFO_CTRL_PANEL_VERIFYING_BACKUP_SUMMARY.get(backup.getID()),
173          INFO_CTRL_PANEL_VERIFYING_BACKUP_SUCCESSFUL_SUMMARY.get(),
174          INFO_CTRL_PANEL_VERIFYING_BACKUP_SUCCESSFUL_DETAILS.get(),
175          ERR_CTRL_PANEL_VERIFYING_BACKUP_ERROR_SUMMARY.get(),
176          null,
177          ERR_CTRL_PANEL_VERIFYING_BACKUP_ERROR_DETAILS,
178          dlg);
179      dlg.setVisible(true);
180    }
181    else
182    {
183      displayErrorDialog(errors);
184    }
185  }
186
187  /**
188   * Creates the layout of the panel (but the contents are not populated here).
189   */
190  private void createLayout()
191  {
192    GridBagConstraints gbc = new GridBagConstraints();
193    gbc.gridx = 0;
194    gbc.gridy = 0;
195
196    gbc.gridwidth = 3;
197    addErrorPane(gbc);
198
199    super.createLayout(gbc);
200
201    gbc.insets.top = 10;
202    gbc.gridx = 0;
203    gbc.gridy ++;
204    gbc.insets.left = 0;
205    gbc.gridwidth = 1;
206    lBackupID = Utilities.createPrimaryLabel(
207        INFO_CTRL_PANEL_BACKUP_ID_LABEL.get());
208    add(lBackupID, gbc);
209    backupID = Utilities.createMediumTextField();
210    gbc.weightx = 0.0;
211    gbc.gridx = 1;
212    gbc.insets.left = 10;
213    gbc.insets.right = 40;
214    gbc.fill = GridBagConstraints.HORIZONTAL;
215    gbc.anchor = GridBagConstraints.WEST;
216    gbc.gridwidth = 2;
217    add(backupID, gbc);
218
219    listener = new ListSelectionListener()
220    {
221      public void valueChanged(ListSelectionEvent ev)
222      {
223        BackupDescriptor backup = getSelectedBackup();
224        setEnabledOK(backup != null && !errorPane.isVisible());
225      }
226    };
227    backupList.getSelectionModel().addListSelectionListener(listener);
228
229    addBottomGlue(gbc);
230  }
231
232  /** {@inheritDoc} */
233  protected void checkOKButtonEnable()
234  {
235    listener.valueChanged(null);
236  }
237
238  /** {@inheritDoc} */
239  public void okClicked()
240  {
241    setPrimaryValid(lPath);
242    setPrimaryValid(lAvailableBackups);
243    setPrimaryValid(lBackupID);
244
245    final LinkedHashSet<LocalizableMessage> errors = new LinkedHashSet<>();
246
247    BackupDescriptor backup = getSelectedBackup();
248
249    if (isLocal())
250    {
251      boolean selected = backupList.isVisible() && backup != null;
252      if (!selected)
253      {
254        if (backupList.getRowCount() == 0)
255        {
256          setPrimaryInvalid(lPath);
257          errors.add(ERR_CTRL_PANEL_NO_PARENT_BACKUP_TO_VERIFY.get());
258        }
259        else
260        {
261          errors.add(ERR_CTRL_PANEL_REQUIRED_BACKUP_TO_VERIFY.get());
262        }
263        setPrimaryInvalid(lAvailableBackups);
264      }
265    }
266    else
267    {
268      String parentPath = parentDirectory.getText();
269      if (parentPath == null || parentPath.trim().equals(""))
270      {
271        errors.add(ERR_CTRL_PANEL_NO_BACKUP_PATH_PROVIDED.get());
272        setPrimaryInvalid(lPath);
273      }
274
275      String id = backupID.getText();
276      if (id == null || id.trim().equals(""))
277      {
278        errors.add(ERR_CTRL_PANEL_NO_BACKUP_ID_PROVIDED.get());
279        setPrimaryInvalid(lBackupID);
280      }
281    }
282
283    if (errors.isEmpty())
284    {
285      ProgressDialog progressDialog = new ProgressDialog(
286          Utilities.createFrame(), Utilities.getParentDialog(this), getTitle(),
287          getInfo());
288      RestoreTask newTask = new RestoreTask(getInfo(), progressDialog, false);
289      for (Task task : getInfo().getTasks())
290      {
291        task.canLaunch(newTask, errors);
292      }
293//    Ask for confirmation
294      boolean confirmed = true;
295      if (errors.isEmpty())
296      {
297        confirmed = displayConfirmationDialog(
298            INFO_CTRL_PANEL_CONFIRMATION_REQUIRED_SUMMARY.get(),
299            INFO_CTRL_PANEL_CONFIRM_RESTORE_DETAILS.get());
300      }
301
302      if (errors.isEmpty() && confirmed)
303      {
304        launchOperation(newTask,
305            INFO_CTRL_PANEL_RESTORING_SUMMARY.get(backup.getID()),
306            INFO_CTRL_PANEL_RESTORING_SUCCESSFUL_SUMMARY.get(),
307            INFO_CTRL_PANEL_RESTORING_SUCCESSFUL_DETAILS.get(),
308            ERR_CTRL_PANEL_RESTORING_ERROR_SUMMARY.get(),
309            null,
310            ERR_CTRL_PANEL_RESTORING_ERROR_DETAILS,
311            progressDialog);
312        progressDialog.setVisible(true);
313        Utilities.getParentDialog(this).setVisible(false);
314      }
315    }
316    if (!errors.isEmpty())
317    {
318      displayErrorDialog(errors);
319    }
320  }
321
322  /** {@inheritDoc} */
323  public void cancelClicked()
324  {
325    setPrimaryValid(lPath);
326    setPrimaryValid(lAvailableBackups);
327
328    super.cancelClicked();
329  }
330
331  /** The task in charge of restoring or verifying the backup. */
332  protected class RestoreTask extends Task
333  {
334    private Set<String> backendSet;
335    private String dir;
336    private String backupID;
337    private boolean verify;
338
339    /**
340     * The constructor of the task.
341     * @param info the control panel info.
342     * @param dlg the progress dialog that shows the progress of the task.
343     * @param verify whether this is an actual restore or a verify of the
344     * backup.
345     */
346    public RestoreTask(ControlPanelInfo info, ProgressDialog dlg,
347        boolean verify)
348    {
349      super(info, dlg);
350      this.verify = verify;
351      if (isLocal())
352      {
353        BackupDescriptor backup = getSelectedBackup();
354        dir = backup.getPath().getAbsolutePath();
355        backupID = backup.getID();
356      }
357      else
358      {
359        dir = parentDirectory.getText();
360        backupID = RestorePanel.this.backupID.getText();
361      }
362      backendSet = new HashSet<>();
363      for (BackendDescriptor backend : info.getServerDescriptor().getBackends())
364      {
365        if (!backend.isConfigBackend())
366        {
367          backendSet.add(backend.getBackendID());
368        }
369      }
370    }
371
372    /** {@inheritDoc} */
373    public Type getType()
374    {
375      return Type.RESTORE;
376    }
377
378    /** {@inheritDoc} */
379    public LocalizableMessage getTaskDescription()
380    {
381      if (verify)
382      {
383        return INFO_CTRL_PANEL_VERIFY_TASK_DESCRIPTION.get(backupID, dir);
384      }
385      else
386      {
387        return INFO_CTRL_PANEL_RESTORE_TASK_DESCRIPTION.get(backupID, dir);
388      }
389    }
390
391    /** {@inheritDoc} */
392    public boolean canLaunch(Task taskToBeLaunched,
393        Collection<LocalizableMessage> incompatibilityReasons)
394    {
395      boolean canLaunch = true;
396      if (state == State.RUNNING && runningOnSameServer(taskToBeLaunched))
397      {
398        // All the operations are incompatible if they apply to this backend.
399        Set<String> backends = new TreeSet<>(taskToBeLaunched.getBackends());
400        backends.retainAll(getBackends());
401        if (!backends.isEmpty())
402        {
403          incompatibilityReasons.add(getIncompatibilityMessage(this, taskToBeLaunched));
404          canLaunch = false;
405        }
406      }
407      return canLaunch;
408    }
409
410    /** {@inheritDoc} */
411    public void runTask()
412    {
413      state = State.RUNNING;
414      lastException = null;
415      try
416      {
417        ArrayList<String> arguments = getCommandLineArguments();
418
419        String[] args = new String[arguments.size()];
420
421        arguments.toArray(args);
422        if (isServerRunning())
423        {
424          returnCode = RestoreDB.mainRestoreDB(args, false, outPrintStream,
425              errorPrintStream);
426        }
427        else
428        {
429          returnCode = executeCommandLine(getCommandLinePath(), args);
430        }
431        if (returnCode != 0)
432        {
433          state = State.FINISHED_WITH_ERROR;
434        }
435        else
436        {
437          if (!verify)
438          {
439            for (String backend : getBackends())
440            {
441              getInfo().unregisterModifiedIndexesInBackend(backend);
442            }
443          }
444          state = State.FINISHED_SUCCESSFULLY;
445        }
446      }
447      catch (Throwable t)
448      {
449        lastException = t;
450        state = State.FINISHED_WITH_ERROR;
451      }
452      HashSet<BackendDescriptor> backends = new HashSet<>();
453      for (BackendDescriptor backend :
454        getInfo().getServerDescriptor().getBackends())
455      {
456        for (String backendID : getBackends())
457        {
458          if (backendID.equalsIgnoreCase(backend.getBackendID()))
459          {
460            backends.add(backend);
461            break;
462          }
463        }
464      }
465      if (!backends.isEmpty())
466      {
467        getInfo().backendPopulated(backends);
468      }
469    }
470
471    /** {@inheritDoc} */
472    public Set<String> getBackends()
473    {
474      return backendSet;
475    }
476
477    /** {@inheritDoc} */
478    protected ArrayList<String> getCommandLineArguments()
479    {
480      ArrayList<String> args = new ArrayList<>();
481
482      args.add("--backupDirectory");
483      args.add(dir);
484
485      args.add("--backupID");
486      args.add(backupID);
487
488      if (verify)
489      {
490        args.add("--dry-run");
491      }
492
493      args.addAll(getConnectionCommandLineArguments());
494
495      if (isServerRunning())
496      {
497        args.addAll(getConfigCommandLineArguments());
498      }
499      args.add(getNoPropertiesFileArgument());
500
501      return args;
502    }
503
504    /** {@inheritDoc} */
505    protected String getCommandLinePath()
506    {
507      return getCommandLinePath("restore");
508    }
509  }
510}