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