mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Gaetan Boismal
15.50.2015 666d84b1f01d1dbbf709ed5a4bbb961777d9ce0e
OPENDJ-1991 Code cleanup

* org.opends.server.types.BackupDirectory
** Autorefactor'ed file
** Reformat
** Moves some constants to static
** Use Collection interfaces instead of implementation as declarative type
** Inline some message local variables
*** getLatestBackup()
*** BackupDirectory(...)
**** factorize code
** Simplify code of last loop in readBackupDirectoryDescriptor(String)
** Extract some methods to improve code lisibilty

* org.opends.server.types.BackupInfo
** Consequence of the change done in BackupDirectory

* org.opends.guitools.controlpanel.ui.BackupListPanel
** Autorefactor'ed file
** Reformat
** Moves some constants to static
** Use List instead of ArrayList as declarative type
** Extracted methods:
*** updateUI(boolean, BackupTableModel)
*** performSuccessActions(Set<BackupInfo>, BackupTableModel)
*** performErrorActions(Throwable t, BackupTableModel model)
*** getBackupPath(ServerDescriptor desc)
**** Consequence of this extract in configurationChanged(ConfigurationChangeEvent)
3 files modified
965 ■■■■■ changed files
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/guitools/controlpanel/ui/BackupListPanel.java 400 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/types/BackupDirectory.java 559 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/types/BackupInfo.java 6 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/guitools/controlpanel/ui/BackupListPanel.java
@@ -30,7 +30,8 @@
import static org.opends.messages.AdminToolMessages.*;
import static org.opends.messages.CoreMessages.*;
import static org.opends.messages.ToolMessages.*;
import static com.forgerock.opendj.util.OperatingSystem.isWindows;
import static com.forgerock.opendj.util.OperatingSystem.*;
import java.awt.Component;
import java.awt.Dimension;
@@ -42,11 +43,9 @@
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JLabel;
@@ -61,6 +60,8 @@
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableColumn;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.opends.guitools.controlpanel.datamodel.BackupDescriptor;
import org.opends.guitools.controlpanel.datamodel.BackupTableModel;
import org.opends.guitools.controlpanel.datamodel.ServerDescriptor;
@@ -69,94 +70,68 @@
import org.opends.guitools.controlpanel.ui.renderer.BackupTableCellRenderer;
import org.opends.guitools.controlpanel.util.BackgroundTask;
import org.opends.guitools.controlpanel.util.Utilities;
import org.opends.quicksetup.Installation;
import org.opends.server.types.BackupDirectory;
import org.opends.server.types.BackupInfo;
import org.opends.server.types.OpenDsException;
import org.opends.server.util.StaticUtils;
/**
 * Abstract class used to refactor code in panels that contain a backup list on
 * it.
 *
 */
/** Abstract class used to refactor code in panels that contain a backup list on it. */
public abstract class BackupListPanel extends StatusGenericPanel
{
  private static final long serialVersionUID = -4804555239922795163L;
  /**
   * The text field containing the parent directory.
   */
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  /** The refreshing list message, displayed when the list of backups is refreshed. */
  protected static final LocalizableMessage REFRESHING_LIST = INFO_CTRL_PANEL_REFRESHING_LIST_SUMMARY.get();
  /** The message informing that no backups where found. */
  protected static final LocalizableMessage NO_BACKUPS_FOUND = INFO_CTRL_PANEL_NO_BACKUPS_FOUND.get();
  private static final String DUMMY_PARENT_PATH = "/local/OpenDJ-X.X.X/bak";
  /** The text field containing the parent directory. */
  protected JTextField parentDirectory;
  /**
   * The refreshing list message, displayed when the list of backups is
   * refreshed.
   */
  protected final LocalizableMessage REFRESHING_LIST =
    INFO_CTRL_PANEL_REFRESHING_LIST_SUMMARY.get();
  /**
   * The message informing that no backups where found.
   */
  protected final LocalizableMessage NO_BACKUPS_FOUND =
    INFO_CTRL_PANEL_NO_BACKUPS_FOUND.get();
  /**
   * Label for the path field.
   */
  /** Label for the path field. */
  protected JLabel lPath;
  /**
   * Label for the list.
   */
  /** Label for the list. */
  protected JLabel lAvailableBackups;
  /**
   * Refreshing list label (displayed instead of the list when this one is
   * being refreshed).
   */
  /** Refreshing list label (displayed instead of the list when this one is being refreshed). */
  protected JLabel lRefreshingList;
  /**
   * Refresh list button.
   */
  /** Refresh list button. */
  protected JButton refreshList;
  /**
   * Verify backup button.
   */
  /** Verify backup button. */
  protected JButton verifyBackup;
  /**
   * Browse button.
   */
  /** Browse button. */
  protected JButton browse;
  /**
   * The scroll that contains the list of backups (actually is a table).
   */
  /** The scroll that contains the list of backups (actually is a table). */
  protected JScrollPane tableScroll;
  /**
   * The list of backups.
   */
  /** The list of backups. */
  protected JTable backupList;
  private JLabel lRemoteFileHelp;
  /**
   * Whether the backup parent directory has been initialized with a value or
   * not.
   */
  /** Whether the backup parent directory has been initialized with a value. */
  private boolean backupDirectoryInitialized;
  private BackupTableCellRenderer renderer;
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  /**
   * Default constructor.
   *
   */
  /** Default constructor. */
  protected BackupListPanel()
  {
    super();
  }
  /** {@inheritDoc} */
  @Override
  public Component getPreferredFocusComponent()
  {
    return parentDirectory;
@@ -164,6 +139,7 @@
  /**
   * Returns the selected backup in the list.
   *
   * @return the selected backup in the list.
   */
  protected BackupDescriptor getSelectedBackup()
@@ -172,33 +148,33 @@
    int row = backupList.getSelectedRow();
    if (row != -1)
    {
      BackupTableModel model = (BackupTableModel)backupList.getModel();
      BackupTableModel model = (BackupTableModel) backupList.getModel();
      backup = model.get(row);
    }
    return backup;
  }
  /**
   * Notification that the verify button was clicked.  Whatever is required
   * to be done must be done in this method.
   *
   * Notification that the verify button was clicked. Whatever is required to be
   * done must be done in this method.
   */
  protected abstract void verifyBackupClicked();
  /**
   * Creates the components and lays them in the panel.
   * @param gbc the grid bag constraints to be used.
   *
   * @param gbc
   *          the grid bag constraints to be used.
   */
  protected void createLayout(GridBagConstraints gbc)
  {
    gbc.gridy ++;
    gbc.gridy++;
    gbc.anchor = GridBagConstraints.WEST;
    gbc.weightx = 0.0;
    gbc.fill = GridBagConstraints.NONE;
    gbc.gridwidth = 1;
    gbc.insets.left = 0;
    lPath = Utilities.createPrimaryLabel(
        INFO_CTRL_PANEL_BACKUP_PATH_LABEL.get());
    lPath = Utilities.createPrimaryLabel(INFO_CTRL_PANEL_BACKUP_PATH_LABEL.get());
    add(lPath, gbc);
    gbc.gridx = 1;
@@ -210,27 +186,24 @@
    browse = Utilities.createButton(INFO_CTRL_PANEL_BROWSE_BUTTON_LABEL.get());
    browse.setOpaque(false);
    browse.addActionListener(
        new BrowseActionListener(parentDirectory,
            BrowseActionListener.BrowseType.LOCATION_DIRECTORY,  this));
        new BrowseActionListener(parentDirectory, BrowseActionListener.BrowseType.LOCATION_DIRECTORY, this));
    gbc.gridx = 2;
    gbc.weightx = 0.0;
    add(browse, gbc);
    lRemoteFileHelp = Utilities.createInlineHelpLabel(
        INFO_CTRL_PANEL_REMOTE_SERVER_PATH.get());
    lRemoteFileHelp = Utilities.createInlineHelpLabel(INFO_CTRL_PANEL_REMOTE_SERVER_PATH.get());
    gbc.gridx = 1;
    gbc.gridwidth = 2;
    gbc.insets.top = 3;
    gbc.insets.left = 10;
    gbc.gridy ++;
    gbc.gridy++;
    add(lRemoteFileHelp, gbc);
    gbc.gridx = 0;
    gbc.gridy ++;
    gbc.gridy++;
    gbc.insets.top = 10;
    gbc.insets.left = 0;
    lAvailableBackups = Utilities.createPrimaryLabel(
        INFO_CTRL_PANEL_AVAILABLE_BACKUPS_LABEL.get());
    lAvailableBackups = Utilities.createPrimaryLabel(INFO_CTRL_PANEL_AVAILABLE_BACKUPS_LABEL.get());
    gbc.anchor = GridBagConstraints.NORTHWEST;
    gbc.fill = GridBagConstraints.NONE;
    gbc.gridwidth = 1;
@@ -255,13 +228,12 @@
      model.add(backup);
    }
    backupList.setModel(model);
    backupList.getSelectionModel().setSelectionMode(
        ListSelectionModel.SINGLE_SELECTION);
    backupList.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    backupList.setShowGrid(false);
    backupList.setIntercellSpacing(new Dimension(0, 0));
    renderer = new BackupTableCellRenderer();
    renderer.setParentPath(new File(DUMMY_PARENT_PATH));
    for (int i=0; i<model.getColumnCount(); i++)
    for (int i = 0; i < model.getColumnCount(); i++)
    {
      TableColumn col = backupList.getColumn(model.getColumnName(i));
      col.setCellRenderer(renderer);
@@ -275,7 +247,7 @@
    add(tableScroll, gbc);
    lRefreshingList.setPreferredSize(tableScroll.getPreferredSize());
    gbc.gridy ++;
    gbc.gridy++;
    gbc.anchor = GridBagConstraints.EAST;
    gbc.weightx = 0.0;
    gbc.weighty = 0.0;
@@ -291,28 +263,26 @@
    gbc2.fill = GridBagConstraints.HORIZONTAL;
    gbc2.weightx = 1.0;
    buttonPanel.add(Box.createHorizontalGlue(), gbc2);
    refreshList = Utilities.createButton(
        INFO_CTRL_PANEL_REFRESH_LIST_BUTTON_LABEL.get());
    refreshList = Utilities.createButton(INFO_CTRL_PANEL_REFRESH_LIST_BUTTON_LABEL.get());
    refreshList.setOpaque(false);
    refreshList.addActionListener(new ActionListener()
    {
      /** {@inheritDoc} */
      @Override
      public void actionPerformed(ActionEvent ev)
      {
        refreshList();
      }
    });
    gbc2.weightx = 0.0;
    gbc2.gridx ++;
    gbc2.gridx++;
    buttonPanel.add(refreshList, gbc2);
    gbc2.gridx ++;
    gbc2.gridx++;
    gbc2.insets.left = 5;
    verifyBackup = Utilities.createButton(
        INFO_CTRL_PANEL_VERIFY_BACKUP_BUTTON_LABEL.get());
    verifyBackup = Utilities.createButton(INFO_CTRL_PANEL_VERIFY_BACKUP_BUTTON_LABEL.get());
    verifyBackup.setOpaque(false);
    verifyBackup.addActionListener(new ActionListener()
    {
      /** {@inheritDoc} */
      @Override
      public void actionPerformed(ActionEvent ev)
      {
        verifyBackupClicked();
@@ -320,7 +290,7 @@
    });
    ListSelectionListener listener = new ListSelectionListener()
    {
      /** {@inheritDoc} */
      @Override
      public void valueChanged(ListSelectionEvent ev)
      {
        BackupDescriptor backup = getSelectedBackup();
@@ -335,33 +305,30 @@
  /**
   * Refresh the list of backups by looking in the backups defined under the
   * provided parent backup directory.
   *
   */
  protected void refreshList()
  {
    final boolean refreshEnabled = refreshList.isEnabled();
    refreshList.setEnabled(false);
    verifyBackup.setEnabled(false);
    tableScroll.setVisible(false);
    lRefreshingList.setText(REFRESHING_LIST.toString());
    lRefreshingList.setVisible(isLocal());
    final int lastSelectedRow = backupList.getSelectedRow();
    final int lastSelectedRow = backupList.getSelectedRow();
    final String parentPath = parentDirectory.getText();
    BackgroundTask<Set<BackupInfo>> worker =
      new BackgroundTask<Set<BackupInfo>>()
    BackgroundTask<Set<BackupInfo>> worker = new BackgroundTask<Set<BackupInfo>>()
    {
      /** {@inheritDoc} */
      @Override
      public Set<BackupInfo> processBackgroundTask() throws Throwable
      {
        // Open the backup directory and make sure it is valid.
        LinkedHashSet<BackupInfo> backups = new LinkedHashSet<BackupInfo>();
        Set<BackupInfo> backups = new LinkedHashSet<>();
        Throwable firstThrowable = null;
        try
        {
          BackupDirectory backupDir =
            BackupDirectory.readBackupDirectoryDescriptor(parentPath);
          BackupDirectory backupDir = BackupDirectory.readBackupDirectoryDescriptor(parentPath);
          backups.addAll(backupDir.getBackups().values());
        }
        catch (Throwable t)
@@ -371,30 +338,27 @@
        // Check the subdirectories
        File f = new File(parentPath);
        // Check the first level of directories (we might have done a backup
        // of one backend and then a backup of several backends under the
        // same directory).
        // Check the first level of directories (we might have done
        // a backup of one backend and then a backup of several backends under the same directory).
        if (f.isDirectory())
        {
          File[] children = f.listFiles();
          for (int i=0; i<children.length; i++)
          for (int i = 0; i < children.length; i++)
          {
            if (children[i].isDirectory())
            {
              try
              {
                BackupDirectory backupDir =
                  BackupDirectory.readBackupDirectoryDescriptor(
                      children[i].getAbsolutePath());
                    BackupDirectory.readBackupDirectoryDescriptor(children[i].getAbsolutePath());
                backups.addAll(backupDir.getBackups().values());
              }
              catch (Throwable t2)
              {
                if (!children[i].getName().equals("tasks") &&
                    (firstThrowable != null))
                if (!children[i].getName().equals("tasks") && firstThrowable != null)
                {
                  logger.warn(LocalizableMessage.raw("Error searching backup: "+t2, t2));
                  logger.warn(LocalizableMessage.raw("Error searching backup: " + t2, t2));
                }
              }
            }
@@ -407,79 +371,24 @@
        return backups;
      }
      /** {@inheritDoc} */
      public void backgroundTaskCompleted(Set<BackupInfo> returnValue,
          Throwable t)
      @Override
      public void backgroundTaskCompleted(Set<BackupInfo> returnValue, Throwable t)
      {
        BackupTableModel model = (BackupTableModel)backupList.getModel();
        BackupTableModel model = (BackupTableModel) backupList.getModel();
        model.clear();
        renderer.setParentPath(new File(parentPath));
        if (t == null)
        {
          if (!returnValue.isEmpty())
          {
            for (BackupInfo backup : returnValue)
            {
              model.add(new BackupDescriptor(backup));
            }
            model.fireTableDataChanged();
            Utilities.updateTableSizes(backupList);
            tableScroll.setVisible(true);
            lRefreshingList.setVisible(false);
          }
          else
          {
            model.fireTableDataChanged();
            lRefreshingList.setText(NO_BACKUPS_FOUND.toString());
            lRefreshingList.setVisible(isLocal());
          }
          errorPane.setVisible(false);
          // This is done to perform checks against whether we require to
          // display an error message or not.
          configurationChanged(new ConfigurationChangeEvent(null,
              getInfo().getServerDescriptor()));
          performSuccessActions(returnValue, model);
        }
        else
        {
          model.fireTableDataChanged();
          boolean displayError = true;
          if (t instanceof OpenDsException)
          {
            OpenDsException e = (OpenDsException)t;
            if (StaticUtils.hasDescriptor(e.getMessageObject(),
                ERR_BACKUPDIRECTORY_NO_DESCRIPTOR_FILE))
            {
              displayError = false;
            }
          }
          if (displayError)
          {
            LocalizableMessage details = ERR_RESTOREDB_CANNOT_READ_BACKUP_DIRECTORY.get(
              parentDirectory.getText(), StaticUtils.getExceptionMessage(t));
            updateErrorPane(errorPane,
                ERR_ERROR_SEARCHING_BACKUPS_SUMMARY.get(),
                ColorAndFontConstants.errorTitleFont,
                details,
                errorPane.getFont());
            packParentDialog();
          }
          errorPane.setVisible(displayError);
          if (!displayError)
          {
            // This is done to perform checks against whether we require to
            // display an error message or not.
            configurationChanged(new ConfigurationChangeEvent(null,
                getInfo().getServerDescriptor()));
          }
          lRefreshingList.setText(NO_BACKUPS_FOUND.toString());
          performErrorActions(t, model);
        }
        refreshList.setEnabled(refreshEnabled);
        verifyBackup.setEnabled(getSelectedBackup() != null);
        if ((lastSelectedRow != -1) &&
            (lastSelectedRow < backupList.getRowCount()))
        if (lastSelectedRow != -1 && lastSelectedRow < backupList.getRowCount())
        {
          backupList.setRowSelectionInterval(lastSelectedRow, lastSelectedRow);
        }
@@ -488,77 +397,111 @@
          backupList.setRowSelectionInterval(0, 0);
        }
      }
      private void performSuccessActions(Set<BackupInfo> returnValue, BackupTableModel model)
      {
        if (!returnValue.isEmpty())
        {
          for (BackupInfo backup : returnValue)
          {
            model.add(new BackupDescriptor(backup));
          }
          Utilities.updateTableSizes(backupList);
          tableScroll.setVisible(true);
          lRefreshingList.setVisible(false);
        }
        else
        {
          lRefreshingList.setText(NO_BACKUPS_FOUND.toString());
          lRefreshingList.setVisible(isLocal());
        }
        updateUI(true, model);
      }
      private void performErrorActions(Throwable t, BackupTableModel model)
      {
        boolean displayError = true;
        if (t instanceof OpenDsException)
        {
          OpenDsException e = (OpenDsException) t;
          if (StaticUtils.hasDescriptor(e.getMessageObject(), ERR_BACKUPDIRECTORY_NO_DESCRIPTOR_FILE))
          {
            displayError = false;
          }
        }
        if (displayError)
        {
          LocalizableMessage details = ERR_RESTOREDB_CANNOT_READ_BACKUP_DIRECTORY.get(
              parentDirectory.getText(), StaticUtils.getExceptionMessage(t));
          updateErrorPane(errorPane,
                          ERR_ERROR_SEARCHING_BACKUPS_SUMMARY.get(),
                          ColorAndFontConstants.errorTitleFont,
                          details,
                          errorPane.getFont());
          packParentDialog();
        }
        updateUI(false, model);
     }
      private void updateUI(boolean isSuccess, BackupTableModel model)
      {
        model.fireTableDataChanged();
        errorPane.setVisible(!isSuccess);
        if (isSuccess)
        {
          // This is done to perform checks against whether we require to display an error message.
          configurationChanged(new ConfigurationChangeEvent(null, getInfo().getServerDescriptor()));
        }
        else
        {
          lRefreshingList.setText(NO_BACKUPS_FOUND.toString());
        }
      }
    };
    worker.startBackgroundTask();
  }
  private final String DUMMY_PARENT_PATH = "/local/OpenDJ-X.X.X/bak";
  /**
   * Creates a list with backup descriptor.  This is done simply to have a good
   * initial size for the table.
   * Creates a list with backup descriptor.
   * This is done simply to have a good initial size for the table.
   *
   * @return a list with bogus backup descriptors.
   */
  private ArrayList<BackupDescriptor> createDummyBackupList()
  private List<BackupDescriptor> createDummyBackupList()
  {
    ArrayList<BackupDescriptor> list = new ArrayList<BackupDescriptor>();
    list.add(new BackupDescriptor(
        new File(DUMMY_PARENT_PATH+"/200704201567Z"),
        new GregorianCalendar(2007, 5, 20, 8, 10).getTime(),
        BackupDescriptor.Type.FULL, "id"));
    list.add(new BackupDescriptor(
        new File(DUMMY_PARENT_PATH+"/200704201567Z"),
        new GregorianCalendar(2007, 5, 22, 8, 10).getTime(),
        BackupDescriptor.Type.INCREMENTAL, "id"));
    list.add(new BackupDescriptor(
        new File(DUMMY_PARENT_PATH+"/200704221567Z"),
        new GregorianCalendar(2007, 5, 25, 8, 10).getTime(),
        BackupDescriptor.Type.INCREMENTAL, "id"));
    List<BackupDescriptor> list = new ArrayList<>();
    list.add(new BackupDescriptor(new File(DUMMY_PARENT_PATH + "/200704201567Z"),
             new GregorianCalendar(2007, 5, 20, 8, 10).getTime(), BackupDescriptor.Type.FULL, "id"));
    list.add(new BackupDescriptor(new File(DUMMY_PARENT_PATH + "/200704201567Z"),
             new GregorianCalendar(2007, 5, 22, 8, 10).getTime(), BackupDescriptor.Type.INCREMENTAL, "id"));
    list.add(new BackupDescriptor(new File(DUMMY_PARENT_PATH + "/200704221567Z"),
             new GregorianCalendar(2007, 5, 25, 8, 10).getTime(), BackupDescriptor.Type.INCREMENTAL, "id"));
    return list;
  }
  /** {@inheritDoc} */
  public void configurationChanged(ConfigurationChangeEvent ev)
  @Override
  public void configurationChanged(final ConfigurationChangeEvent ev)
  {
    ServerDescriptor desc = ev.getNewDescriptor();
    if (!backupDirectoryInitialized &&
        (parentDirectory.getText().length() == 0))
    if (!backupDirectoryInitialized && parentDirectory.getText().length() == 0)
    {
      String path;
      if (desc.isLocal() || (desc.isWindows() == isWindows()))
      {
        File f = new File(desc.getInstancePath(),
            org.opends.quicksetup.Installation.BACKUPS_PATH_RELATIVE);
        try
        {
          path = f.getCanonicalPath();
        }
        catch (Throwable t)
        {
          path = f.getAbsolutePath();
        }
      }
      else
      {
        String separator = desc.isWindows() ? "\\" : "/";
        path = desc.getInstancePath() + separator +
        org.opends.quicksetup.Installation.BACKUPS_PATH_RELATIVE;
      }
      final String fPath = path;
      SwingUtilities.invokeLater(new Runnable()
      {
        @Override
        public void run()
        {
          parentDirectory.setText(fPath);
          parentDirectory.setText(getBackupPath(ev.getNewDescriptor()));
          refreshList();
          backupDirectoryInitialized = true;
        }
      });
    }
    SwingUtilities.invokeLater(new Runnable()
    {
      @Override
      public void run()
      {
        lRemoteFileHelp.setVisible(!isLocal());
@@ -571,7 +514,28 @@
    });
  }
  /** {@inheritDoc} */
  private String getBackupPath(ServerDescriptor desc)
  {
    if (desc.isLocal() || desc.isWindows() == isWindows())
    {
      File f = new File(desc.getInstancePath(), Installation.BACKUPS_PATH_RELATIVE);
      try
      {
        return f.getCanonicalPath();
      }
      catch (Throwable t)
      {
        return f.getAbsolutePath();
      }
    }
    else
    {
      String separator = desc.isWindows() ? "\\" : "/";
      return desc.getInstancePath() + separator + Installation.BACKUPS_PATH_RELATIVE;
    }
  }
  @Override
  public void toBeDisplayed(boolean visible)
  {
    if (visible && backupDirectoryInitialized)
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/types/BackupDirectory.java
@@ -28,8 +28,6 @@
import org.forgerock.i18n.LocalizableMessage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
@@ -38,291 +36,284 @@
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.forgerock.opendj.config.server.ConfigException;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import static org.opends.messages.CoreMessages.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
/**
 * This class defines a data structure for holding information about a
 * filesystem directory that contains data for one or more backups
 * associated with a backend.  Only backups for a single backend may
 * be placed in any given directory.
 * filesystem directory that contains data for one or more backups associated
 * with a backend. Only backups for a single backend may be placed in any given
 * directory.
 */
@org.opends.server.types.PublicAPI(
     stability=org.opends.server.types.StabilityLevel.VOLATILE,
     mayInstantiate=true,
     mayExtend=false,
     mayInvoke=true)
    stability = org.opends.server.types.StabilityLevel.VOLATILE,
    mayInstantiate = true,
    mayExtend = false,
    mayInvoke = true)
public final class BackupDirectory
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  /**
   * The name of the property that will be used to provide the DN of
   * the configuration entry for the backend associated with the
   * backups in this directory.
   */
  public static final String PROPERTY_BACKEND_CONFIG_DN =
       "backend_dn";
  public static final String PROPERTY_BACKEND_CONFIG_DN = "backend_dn";
  /**
   * The DN of the configuration entry for the backend with which this
   * backup directory is associated.
   */
  private DN configEntryDN;
  private final DN configEntryDN;
  /**
   * The set of backups in the specified directory.  The iteration
   * order will be the order in which the backups were created.
   */
  private LinkedHashMap<String,BackupInfo> backups;
  private final Map<String, BackupInfo> backups;
  /** The filesystem path to the backup directory. */
  private String path;
  private final String path;
  /**
   * Creates a new backup directory object with the provided
   * information.
   * Creates a new backup directory object with the provided information.
   *
   * @param  path           The path to the directory containing the
   *                        backup file(s).
   * @param  configEntryDN  The DN of the configuration entry for the
   *                        backend with which this backup directory
   *                        is associated.
   * @param path
   *          The path to the directory containing the backup file(s).
   * @param configEntryDN
   *          The DN of the configuration entry for the backend with which this
   *          backup directory is associated.
   */
  public BackupDirectory(String path, DN configEntryDN)
  {
    this.path          = path;
    this.configEntryDN = configEntryDN;
    backups = new LinkedHashMap<String,BackupInfo>();
    this(path, configEntryDN, null);
  }
  /**
   * Creates a new backup directory object with the provided
   * information.
   * Creates a new backup directory object with the provided information.
   *
   * @param  path           The path to the directory containing the
   *                        backup file(s).
   * @param  configEntryDN  The DN of the configuration entry for the
   *                        backend with which this backup directory
   *                        is associated.
   * @param  backups        Information about the set of backups
   *                        available within the specified directory.
   * @param path
   *          The path to the directory containing the backup file(s).
   * @param configEntryDN
   *          The DN of the configuration entry for the backend with which this
   *          backup directory is associated.
   * @param backups
   *          Information about the set of backups available within the
   *          specified directory.
   */
  public BackupDirectory(String path, DN configEntryDN,
                         LinkedHashMap<String,BackupInfo> backups)
  public BackupDirectory(String path, DN configEntryDN, LinkedHashMap<String, BackupInfo> backups)
  {
    this.path          = path;
    this.path = path;
    this.configEntryDN = configEntryDN;
    if (backups == null)
    {
      this.backups = new LinkedHashMap<String,BackupInfo>();
    }
    else
    if (backups != null)
    {
      this.backups = backups;
    }
    else
    {
      this.backups = new LinkedHashMap<>();
    }
  }
  /**
   * Retrieves the path to the directory containing the backup
   * file(s).
   * Retrieves the path to the directory containing the backup file(s).
   *
   * @return  The path to the directory containing the backup file(s).
   * @return The path to the directory containing the backup file(s).
   */
  public String getPath()
  {
    return path;
  }
  /**
   * Retrieves the DN of the configuration entry for the backend with
   * which this backup directory is associated.
   * Retrieves the DN of the configuration entry for the backend with which this
   * backup directory is associated.
   *
   * @return  The DN of the configuration entry for the backend with
   *          which this backup directory is associated.
   * @return The DN of the configuration entry for the backend with which this
   *         backup directory is associated.
   */
  public DN getConfigEntryDN()
  {
    return configEntryDN;
  }
  /**
   * Retrieves the set of backups in this backup directory, as a
   * mapping between the backup ID and the associated backup info.
   * The iteration order for the map will be the order in which the
   * backups were created.
   * Retrieves the set of backups in this backup directory, as a mapping between
   * the backup ID and the associated backup info. The iteration order for the
   * map will be the order in which the backups were created.
   *
   * @return  The set of backups in this backup directory.
   * @return The set of backups in this backup directory.
   */
  public LinkedHashMap<String,BackupInfo> getBackups()
  public Map<String, BackupInfo> getBackups()
  {
    return backups;
  }
  /**
   * Retrieves the backup info structure for the backup with the
   * specified ID.
   * Retrieves the backup info structure for the backup with the specified ID.
   *
   * @param  backupID  The backup ID for the structure to retrieve.
   *
   * @return  The requested backup info structure, or
   *          <CODE>null</CODE> if no such structure exists.
   * @param backupID
   *          The backup ID for the structure to retrieve.
   * @return The requested backup info structure, or <CODE>null</CODE> if no such
   *         structure exists.
   */
  public BackupInfo getBackupInfo(String backupID)
  {
    return backups.get(backupID);
  }
  /**
   * Retrieves the most recent backup for this backup directory,
   * according to the backup date.
   * Retrieves the most recent backup for this backup directory, according to
   * the backup date.
   *
   * @return  The most recent backup for this backup directory,
   *          according to the backup date, or <CODE>null</CODE> if
   *          there are no backups in the backup directory.
   * @return The most recent backup for this backup directory, according to the
   *         backup date, or <CODE>null</CODE> if there are no backups in the
   *         backup directory.
   */
  public BackupInfo getLatestBackup()
  {
    BackupInfo latestBackup = null;
    for (BackupInfo backup : backups.values())
    {
      if (latestBackup == null)
      if (latestBackup == null
          || backup.getBackupDate().getTime() > latestBackup.getBackupDate().getTime())
      {
        latestBackup = backup;
      }
      else
      {
        if (backup.getBackupDate().getTime() >
            latestBackup.getBackupDate().getTime())
        {
          latestBackup = backup;
        }
      }
    }
    return latestBackup;
  }
  /**
   * Adds information about the provided backup to this backup
   * directory.
   * Adds information about the provided backup to this backup directory.
   *
   * @param  backupInfo  The backup info structure for the backup to
   *                     be added.
   *
   * @throws  ConfigException  If another backup already exists with
   *                           the same backup ID.
   * @param backupInfo
   *          The backup info structure for the backup to be added.
   * @throws ConfigException
   *           If another backup already exists with the same backup ID.
   */
  public void addBackup(BackupInfo backupInfo)
         throws ConfigException
  public void addBackup(BackupInfo backupInfo) throws ConfigException
  {
    String backupID = backupInfo.getBackupID();
    if (backups.containsKey(backupID))
    {
      LocalizableMessage message =
          ERR_BACKUPDIRECTORY_ADD_DUPLICATE_ID.get(backupID, path);
      throw new ConfigException(message);
      throw new ConfigException(ERR_BACKUPDIRECTORY_ADD_DUPLICATE_ID.get(backupID, path));
    }
    backups.put(backupID, backupInfo);
  }
  /**
   * Removes the backup with the specified backup ID from this backup
   * directory.
   * Removes the backup with the specified backup ID from this backup directory.
   *
   * @param  backupID  The backup ID for the backup to remove from
   *                   this backup directory.
   *
   * @throws  ConfigException  If it is not possible to remove the
   *                           requested backup for some reason (e.g.,
   *                           no such backup exists, or another
   *                           backup is dependent on it).
   * @param backupID
   *          The backup ID for the backup to remove from this backup directory.
   * @throws ConfigException
   *           If it is not possible to remove the requested backup for some
   *           reason (e.g., no such backup exists, or another backup is
   *           dependent on it).
   */
  public void removeBackup(String backupID)
         throws ConfigException
  public void removeBackup(String backupID) throws ConfigException
  {
    if (! backups.containsKey(backupID))
    if (!backups.containsKey(backupID))
    {
      LocalizableMessage message =
          ERR_BACKUPDIRECTORY_NO_SUCH_BACKUP.get(backupID, path);
      throw new ConfigException(message);
      throw new ConfigException(ERR_BACKUPDIRECTORY_NO_SUCH_BACKUP.get(backupID, path));
    }
    for (BackupInfo backup : backups.values())
    {
      if (backup.dependsOn(backupID))
      {
        LocalizableMessage message = ERR_BACKUPDIRECTORY_UNRESOLVED_DEPENDENCY.
            get(backupID, path, backup.getBackupID());
        throw new ConfigException(message);
        throw new ConfigException(ERR_BACKUPDIRECTORY_UNRESOLVED_DEPENDENCY.get(backupID, path, backup.getBackupID()));
      }
    }
    backups.remove(backupID);
  }
  /**
   * Retrieves a path to the backup descriptor file that should be
   * used for this backup directory.
   * Retrieves a path to the backup descriptor file that should be used for this
   * backup directory.
   *
   * @return  A path to the backup descriptor file that should be used
   *          for this backup directory.
   * @return A path to the backup descriptor file that should be used for this
   *         backup directory.
   */
  public String getDescriptorPath()
  {
    return path + File.separator + BACKUP_DIRECTORY_DESCRIPTOR_FILE;
  }
  /**
   * Writes the descriptor with the information contained in this
   * structure to disk in the appropriate directory.
   * Writes the descriptor with the information contained in this structure to
   * disk in the appropriate directory.
   *
   * @throws  IOException  If a problem occurs while writing to disk.
   * @throws IOException
   *           If a problem occurs while writing to disk.
   */
  public void writeBackupDirectoryDescriptor()
         throws IOException
  public void writeBackupDirectoryDescriptor() throws IOException
  {
    // First make sure that the target directory exists.  If it
    // doesn't, then try to create it.
    // First make sure that the target directory exists.  If it doesn't, then try to create it.
    createDirectoryIfNotExists();
    // We'll write to a temporary file so that we won't destroy the live copy if a problem occurs.
    String newDescriptorFilePath = path + File.separator + BACKUP_DIRECTORY_DESCRIPTOR_FILE + ".new";
    File newDescriptorFile = new File(newDescriptorFilePath);
    try (BufferedWriter writer = new BufferedWriter(new FileWriter(newDescriptorFile, false)))
    {
      // The first line in the file will only contain the DN of the configuration entry for the associated backend.
      writer.write(PROPERTY_BACKEND_CONFIG_DN + "=" + configEntryDN);
      writer.newLine();
      writer.newLine();
      // Iterate through all of the backups and add them to the file.
      for (BackupInfo backup : backups.values())
      {
        List<String> backupLines = backup.encode();
        for (String line : backupLines)
        {
          writer.write(line);
          writer.newLine();
        }
        writer.newLine();
      }
      // At this point, the file should be complete so flush and close it.
      writer.flush();
    }
    // If previous backup descriptor file exists, then rename it.
    String descriptorFilePath = path + File.separator + BACKUP_DIRECTORY_DESCRIPTOR_FILE;
    File descriptorFile = new File(descriptorFilePath);
    renameOldBackupDescriptorFile(descriptorFile, descriptorFilePath);
    // Rename the new descriptor file to match the previous one.
    try
    {
      newDescriptorFile.renameTo(descriptorFile);
    }
    catch (Exception e)
    {
      logger.traceException(e);
      LocalizableMessage message = ERR_BACKUPDIRECTORY_CANNOT_RENAME_NEW_DESCRIPTOR.get(
          newDescriptorFilePath, descriptorFilePath, getExceptionMessage(e));
      throw new IOException(message.toString());
    }
  }
  private void createDirectoryIfNotExists() throws IOException
  {
    File dir = new File(path);
    if (! dir.exists())
    if (!dir.exists())
    {
      try
      {
@@ -331,61 +322,18 @@
      catch (Exception e)
      {
        logger.traceException(e);
        LocalizableMessage message = ERR_BACKUPDIRECTORY_CANNOT_CREATE_DIRECTORY.
            get(path, getExceptionMessage(e));
        LocalizableMessage message = ERR_BACKUPDIRECTORY_CANNOT_CREATE_DIRECTORY.get(path, getExceptionMessage(e));
        throw new IOException(message.toString());
      }
    }
    else if (! dir.isDirectory())
    else if (!dir.isDirectory())
    {
      LocalizableMessage message = ERR_BACKUPDIRECTORY_NOT_DIRECTORY.get(path);
      throw new IOException(message.toString());
      throw new IOException(ERR_BACKUPDIRECTORY_NOT_DIRECTORY.get(path).toString());
    }
  }
    // We'll write to a temporary file so that we won't destroy the
    // live copy if a problem occurs.
    String newDescriptorFilePath = path + File.separator +
                                   BACKUP_DIRECTORY_DESCRIPTOR_FILE +
                                   ".new";
    File newDescriptorFile = new File(newDescriptorFilePath);
    BufferedWriter writer =
         new BufferedWriter(new FileWriter(newDescriptorFile, false));
    // The first line in the file will only contain the DN of the
    // configuration entry for the associated backend.
    writer.write(PROPERTY_BACKEND_CONFIG_DN + "=" + configEntryDN);
    writer.newLine();
    writer.newLine();
    // Iterate through all of the backups and add them to the file.
    for (BackupInfo backup : backups.values())
    {
      LinkedList<String> backupLines = backup.encode();
      for (String line : backupLines)
      {
        writer.write(line);
        writer.newLine();
      }
      writer.newLine();
    }
    // At this point, the file should be complete so flush and close
    // it.
    writer.flush();
    writer.close();
    // If previous backup descriptor file exists, then rename it.
    String descriptorFilePath = path + File.separator +
                                BACKUP_DIRECTORY_DESCRIPTOR_FILE;
    File descriptorFile = new File(descriptorFilePath);
  private void renameOldBackupDescriptorFile(File descriptorFile, String descriptorFilePath) throws IOException
  {
    if (descriptorFile.exists())
    {
      String savedDescriptorFilePath = descriptorFilePath + ".save";
@@ -399,11 +347,8 @@
        catch (Exception e)
        {
          logger.traceException(e);
          LocalizableMessage message =
              ERR_BACKUPDIRECTORY_CANNOT_DELETE_SAVED_DESCRIPTOR.
                get(savedDescriptorFilePath, getExceptionMessage(e),
                    newDescriptorFilePath, descriptorFilePath);
          LocalizableMessage message = ERR_BACKUPDIRECTORY_CANNOT_DELETE_SAVED_DESCRIPTOR.get(
              savedDescriptorFilePath, getExceptionMessage(e), descriptorFilePath, descriptorFilePath);
          throw new IOException(message.toString());
        }
      }
@@ -415,162 +360,100 @@
      catch (Exception e)
      {
        logger.traceException(e);
        LocalizableMessage message =
            ERR_BACKUPDIRECTORY_CANNOT_RENAME_CURRENT_DESCRIPTOR.
              get(descriptorFilePath, savedDescriptorFilePath,
                  getExceptionMessage(e), newDescriptorFilePath,
                  descriptorFilePath);
        LocalizableMessage message = ERR_BACKUPDIRECTORY_CANNOT_RENAME_CURRENT_DESCRIPTOR.get(descriptorFilePath,
            savedDescriptorFilePath, getExceptionMessage(e), descriptorFilePath, descriptorFilePath);
        throw new IOException(message.toString());
      }
    }
    // Rename the new descriptor file to match the previous one.
    try
    {
      newDescriptorFile.renameTo(descriptorFile);
    }
    catch (Exception e)
    {
      logger.traceException(e);
      LocalizableMessage message =
        ERR_BACKUPDIRECTORY_CANNOT_RENAME_NEW_DESCRIPTOR.
            get(newDescriptorFilePath, descriptorFilePath,
                getExceptionMessage(e));
      throw new IOException(message.toString());
    }
  }
  /**
   * Reads the backup descriptor file in the specified path and uses
   * the information it contains to create a new backup directory
   * structure.
   * Reads the backup descriptor file in the specified path and uses the
   * information it contains to create a new backup directory structure.
   *
   * @param  path  The path to the directory containing the backup
   *               descriptor file to read.
   *
   * @return  The backup directory structure created from the contents
   *          of the descriptor file.
   *
   * @throws  IOException  If a problem occurs while trying to read
   *                       the contents of the descriptor file.
   *
   * @throws  ConfigException  If the contents of the descriptor file
   *                           cannot be parsed to create a backup
   *                           directory structure.
   * @param path
   *          The path to the directory containing the backup descriptor file to
   *          read.
   * @return The backup directory structure created from the contents of the
   *         descriptor file.
   * @throws IOException
   *           If a problem occurs while trying to read the contents of the
   *           descriptor file.
   * @throws ConfigException
   *           If the contents of the descriptor file cannot be parsed to create
   *           a backup directory structure.
   */
  public static BackupDirectory
                     readBackupDirectoryDescriptor(String path)
         throws IOException, ConfigException
  public static BackupDirectory readBackupDirectoryDescriptor(String path) throws IOException, ConfigException
  {
    // Make sure that the descriptor file exists.
    String descriptorFilePath = path + File.separator +
                                BACKUP_DIRECTORY_DESCRIPTOR_FILE;
    File descriptorFile = new File(descriptorFilePath);
    if (! descriptorFile.exists())
    String descriptorFilePath = path + File.separator + BACKUP_DIRECTORY_DESCRIPTOR_FILE;
    if (!new File(descriptorFilePath).exists())
    {
      LocalizableMessage message = ERR_BACKUPDIRECTORY_NO_DESCRIPTOR_FILE.get(
          descriptorFilePath);
      throw new ConfigException(message);
      throw new ConfigException(ERR_BACKUPDIRECTORY_NO_DESCRIPTOR_FILE.get(descriptorFilePath));
    }
    // Open the file for reading.  The first line should be the DN of
    // the associated configuration entry.
    BufferedReader reader =
         new BufferedReader(new FileReader(descriptorFile));
    String line = reader.readLine();
    if ((line == null) || (line.length() == 0))
    // Open the file for reading.
    // The first line should be the DN of the associated configuration entry.
    try (BufferedReader reader = new BufferedReader(new FileReader(descriptorFilePath)))
    {
      LocalizableMessage message =
        ERR_BACKUPDIRECTORY_CANNOT_READ_CONFIG_ENTRY_DN.
            get(descriptorFilePath);
      throw new ConfigException(message);
    }
    else if (! line.startsWith(PROPERTY_BACKEND_CONFIG_DN))
    {
      LocalizableMessage message = ERR_BACKUPDIRECTORY_FIRST_LINE_NOT_DN.get(
          descriptorFilePath, line);
      throw new ConfigException(message);
    }
    String dnString =
         line.substring(PROPERTY_BACKEND_CONFIG_DN.length() + 1);
    DN configEntryDN;
    try
    {
      configEntryDN = DN.valueOf(dnString);
    }
    catch (DirectoryException de)
    {
      LocalizableMessage message = ERR_BACKUPDIRECTORY_CANNOT_DECODE_DN.get(
          dnString, descriptorFilePath, de.getMessageObject());
      throw new ConfigException(message, de);
    }
    catch (Exception e)
    {
      LocalizableMessage message = ERR_BACKUPDIRECTORY_CANNOT_DECODE_DN.get(
          dnString, descriptorFilePath, getExceptionMessage(e));
      throw new ConfigException(message, e);
    }
    // Create the backup directory structure from what we know so far.
    BackupDirectory backupDirectory =
         new BackupDirectory(path, configEntryDN);
    // Iterate through the rest of the file and create the backup info
    // structures.  Blank lines will be considered delimiters.
    LinkedList<String> lines = new LinkedList<String>();
    while (true)
    {
      line = reader.readLine();
      if ((line == null) || (line.length() == 0))
      String line = reader.readLine();
      if (line == null || line.length() == 0)
      {
        // It's a blank line or the end of the file.  If we have lines
        // to process then do so.  Otherwise, move on.
        if (lines.isEmpty())
        throw new ConfigException(ERR_BACKUPDIRECTORY_CANNOT_READ_CONFIG_ENTRY_DN.get(descriptorFilePath));
      }
      else if (!line.startsWith(PROPERTY_BACKEND_CONFIG_DN))
      {
        throw new ConfigException(ERR_BACKUPDIRECTORY_FIRST_LINE_NOT_DN.get(descriptorFilePath, line));
      }
      String dnString = line.substring(PROPERTY_BACKEND_CONFIG_DN.length() + 1);
      DN configEntryDN;
      try
      {
        configEntryDN = DN.valueOf(dnString);
      }
      catch (DirectoryException de)
      {
        LocalizableMessage message = ERR_BACKUPDIRECTORY_CANNOT_DECODE_DN.get(
            dnString, descriptorFilePath, de.getMessageObject());
        throw new ConfigException(message, de);
      }
      catch (Exception e)
      {
        LocalizableMessage message = ERR_BACKUPDIRECTORY_CANNOT_DECODE_DN.get(
            dnString, descriptorFilePath, getExceptionMessage(e));
        throw new ConfigException(message, e);
      }
      // Create the backup directory structure from what we know so far.
      BackupDirectory backupDirectory = new BackupDirectory(path, configEntryDN);
      // Iterate through the rest of the file and create the backup info structures.
      // Blank lines will be considered delimiters.
      List<String> lines = new LinkedList<>();
      while ((line = reader.readLine()) != null)
      {
        if (!line.isEmpty())
        {
          if (line == null)
          {
            break;
          }
          else
          {
            continue;
          }
          lines.add(line);
          continue;
        }
        // Parse the lines that we read and add the backup info to the
        // directory structure.
        BackupInfo backupInfo = BackupInfo.decode(backupDirectory,
                                                  lines);
        backupDirectory.addBackup(backupInfo);
        lines.clear();
        // If it was the end of the file, then break out of the loop.
        if (line == null)
        {
          break;
        }
        // We are on a delimiter blank line.
        readBackupFromLines(backupDirectory, lines);
      }
      else
      {
        lines.add(line);
      }
      readBackupFromLines(backupDirectory, lines);
      return backupDirectory;
    }
  }
    // Close the reader and return the backup directory structure.
    reader.close();
    return backupDirectory;
  private static void readBackupFromLines(BackupDirectory backupDirectory, List<String> lines) throws ConfigException
  {
    if (!lines.isEmpty())
    {
      backupDirectory.addBackup(BackupInfo.decode(backupDirectory, lines));
      lines.clear();
    }
  }
}
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/types/BackupInfo.java
@@ -30,17 +30,19 @@
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.TimeZone;
import org.forgerock.opendj.config.server.ConfigException;
import org.opends.server.util.Base64;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import static org.opends.messages.CoreMessages.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
@@ -501,7 +503,7 @@
   *                           decode the backup info data.
   */
  public static BackupInfo decode(BackupDirectory backupDirectory,
                                  LinkedList<String> encodedInfo)
                                  List<String> encodedInfo)
         throws ConfigException
  {
    String                 backupID         = null;