/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2008-2010 Sun Microsystems, Inc. * Portions Copyright 2011 ForgeRock AS */ package org.opends.guitools.controlpanel.ui; import static org.opends.messages.AdminToolMessages.*; import static org.opends.messages.CoreMessages.*; import static org.opends.messages.ToolMessages.*; import java.awt.Component; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.util.ArrayList; import java.util.GregorianCalendar; import java.util.LinkedHashSet; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.ListSelectionModel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.TableColumn; import org.opends.guitools.controlpanel.datamodel.BackupDescriptor; import org.opends.guitools.controlpanel.datamodel.BackupTableModel; import org.opends.guitools.controlpanel.datamodel.ServerDescriptor; import org.opends.guitools.controlpanel.event.BrowseActionListener; import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent; 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.messages.Message; 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. * */ public abstract class BackupListPanel extends StatusGenericPanel { private static final long serialVersionUID = -4804555239922795163L; /** * The text field containing the parent directory. */ protected JTextField parentDirectory; /** * The refreshing list message, displayed when the list of backups is * refreshed. */ protected final Message REFRESHING_LIST = INFO_CTRL_PANEL_REFRESHING_LIST_SUMMARY.get(); /** * The message informing that no backups where found. */ protected final Message NO_BACKUPS_FOUND = INFO_CTRL_PANEL_NO_BACKUPS_FOUND.get(); /** * Label for the path field. */ protected JLabel lPath; /** * Label for the list. */ protected JLabel lAvailableBackups; /** * Refreshing list label (displayed instead of the list when this one is * being refreshed). */ protected JLabel lRefreshingList; /** * Refresh list button. */ protected JButton refreshList; /** * Verify backup button. */ protected JButton verifyBackup; /** * Browse button. */ protected JButton browse; /** * The scroll that contains the list of backups (actually is a table). */ protected JScrollPane tableScroll; /** * The list of backups. */ protected JTable backupList; private JLabel lRemoteFileHelp; /** * Whether the backup parent directory has been initialized with a value or * not. */ private boolean backupDirectoryInitialized; private BackupTableCellRenderer renderer; private static final Logger LOG = Logger.getLogger(BackupListPanel.class.getName()); /** * Default constructor. * */ protected BackupListPanel() { super(); } /** * {@inheritDoc} */ public Component getPreferredFocusComponent() { return parentDirectory; } /** * Returns the selected backup in the list. * @return the selected backup in the list. */ protected BackupDescriptor getSelectedBackup() { BackupDescriptor backup = null; int row = backupList.getSelectedRow(); if (row != -1) { 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. * */ protected abstract void verifyBackupClicked(); /** * Creates the components and lays them in the panel. * @param gbc the grid bag constraints to be used. */ protected void createLayout(GridBagConstraints gbc) { 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()); add(lPath, gbc); gbc.gridx = 1; gbc.insets.left = 10; parentDirectory = Utilities.createLongTextField(); gbc.weightx = 1.0; gbc.fill = GridBagConstraints.HORIZONTAL; add(parentDirectory, gbc); browse = Utilities.createButton(INFO_CTRL_PANEL_BROWSE_BUTTON_LABEL.get()); browse.setOpaque(false); browse.addActionListener( 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()); gbc.gridx = 1; gbc.gridwidth = 2; gbc.insets.top = 3; gbc.insets.left = 10; gbc.gridy ++; add(lRemoteFileHelp, gbc); gbc.gridx = 0; gbc.gridy ++; gbc.insets.top = 10; gbc.insets.left = 0; lAvailableBackups = Utilities.createPrimaryLabel( INFO_CTRL_PANEL_AVAILABLE_BACKUPS_LABEL.get()); gbc.anchor = GridBagConstraints.NORTHWEST; gbc.fill = GridBagConstraints.NONE; gbc.gridwidth = 1; add(lAvailableBackups, gbc); gbc.gridx = 1; gbc.gridwidth = 2; gbc.weightx = 1.0; gbc.weighty = 1.0; gbc.fill = GridBagConstraints.BOTH; gbc.insets.left = 10; lRefreshingList = Utilities.createDefaultLabel(REFRESHING_LIST); lRefreshingList.setHorizontalAlignment(SwingConstants.CENTER); gbc.anchor = GridBagConstraints.CENTER; add(lRefreshingList, gbc); backupList = new JTable(); // Done to provide a good size to the table. BackupTableModel model = new BackupTableModel(); for (BackupDescriptor backup : createDummyBackupList()) { model.add(backup); } backupList.setModel(model); 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++) { TableColumn col = backupList.getColumn(model.getColumnName(i)); col.setCellRenderer(renderer); } backupList.setTableHeader(null); Utilities.updateTableSizes(backupList); tableScroll = Utilities.createScrollPane(backupList); tableScroll.setColumnHeaderView(null); tableScroll.setPreferredSize(backupList.getPreferredSize()); gbc.anchor = GridBagConstraints.NORTHWEST; add(tableScroll, gbc); lRefreshingList.setPreferredSize(tableScroll.getPreferredSize()); gbc.gridy ++; gbc.anchor = GridBagConstraints.EAST; gbc.weightx = 0.0; gbc.weighty = 0.0; gbc.insets.top = 5; JPanel buttonPanel = new JPanel(new GridBagLayout()); buttonPanel.setOpaque(false); add(buttonPanel, gbc); GridBagConstraints gbc2 = new GridBagConstraints(); gbc2.gridx = 0; gbc2.gridy = 0; gbc2.gridwidth = 1; gbc2.anchor = GridBagConstraints.EAST; 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.setOpaque(false); refreshList.addActionListener(new ActionListener() { /** * {@inheritDoc} */ public void actionPerformed(ActionEvent ev) { refreshList(); } }); gbc2.weightx = 0.0; gbc2.gridx ++; buttonPanel.add(refreshList, gbc2); gbc2.gridx ++; gbc2.insets.left = 5; verifyBackup = Utilities.createButton( INFO_CTRL_PANEL_VERIFY_BACKUP_BUTTON_LABEL.get()); verifyBackup.setOpaque(false); verifyBackup.addActionListener(new ActionListener() { /** * {@inheritDoc} */ public void actionPerformed(ActionEvent ev) { verifyBackupClicked(); } }); ListSelectionListener listener = new ListSelectionListener() { /** * {@inheritDoc} */ public void valueChanged(ListSelectionEvent ev) { BackupDescriptor backup = getSelectedBackup(); verifyBackup.setEnabled(backup != null); } }; backupList.getSelectionModel().addListSelectionListener(listener); listener.valueChanged(null); buttonPanel.add(verifyBackup, gbc2); } /** * 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 String parentPath = parentDirectory.getText(); BackgroundTask<Set<BackupInfo>> worker = new BackgroundTask<Set<BackupInfo>>() { /** * {@inheritDoc} */ public Set<BackupInfo> processBackgroundTask() throws Throwable { // Open the backup directory and make sure it is valid. LinkedHashSet<BackupInfo> backups = new LinkedHashSet<BackupInfo>(); Throwable firstThrowable = null; try { BackupDirectory backupDir = BackupDirectory.readBackupDirectoryDescriptor(parentPath); backups.addAll(backupDir.getBackups().values()); } catch (Throwable t) { firstThrowable = t; } // 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). if (f.isDirectory()) { File[] children = f.listFiles(); for (int i=0; i<children.length; i++) { if (children[i].isDirectory()) { try { BackupDirectory backupDir = BackupDirectory.readBackupDirectoryDescriptor( children[i].getAbsolutePath()); backups.addAll(backupDir.getBackups().values()); } catch (Throwable t2) { if (!children[i].getName().equals("tasks") && (firstThrowable != null)) { LOG.log(Level.WARNING, "Error searching backup: "+t2, t2); } } } } } if ((backups.size() == 0) && (firstThrowable != null)) { throw firstThrowable; } return backups; } /** * {@inheritDoc} */ public void backgroundTaskCompleted(Set<BackupInfo> returnValue, Throwable t) { BackupTableModel model = (BackupTableModel)backupList.getModel(); model.clear(); renderer.setParentPath(new File(parentPath)); if (t == null) { if (returnValue.size() > 0) { 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())); } else { model.fireTableDataChanged(); boolean displayError = true; if (t instanceof OpenDsException) { OpenDsException e = (OpenDsException)t; if (e.getMessageObject().getDescriptor().equals( ERR_BACKUPDIRECTORY_NO_DESCRIPTOR_FILE)) { displayError = false; } } if (displayError) { Message 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()); } refreshList.setEnabled(refreshEnabled); verifyBackup.setEnabled(getSelectedBackup() != null); if ((lastSelectedRow != -1) && (lastSelectedRow < backupList.getRowCount())) { backupList.setRowSelectionInterval(lastSelectedRow, lastSelectedRow); } else if (backupList.getRowCount() > 0) { backupList.setRowSelectionInterval(0, 0); } } }; 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. * @return a list with bogus backup descriptors. */ private ArrayList<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")); return list; } /** * {@inheritDoc} */ public void configurationChanged(ConfigurationChangeEvent ev) { ServerDescriptor desc = ev.getNewDescriptor(); if (!backupDirectoryInitialized && (parentDirectory.getText().length() == 0)) { String path; if (desc.isLocal() || (desc.isWindows() == Utilities.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() { public void run() { parentDirectory.setText(fPath); refreshList(); backupDirectoryInitialized = true; } }); } SwingUtilities.invokeLater(new Runnable() { public void run() { lRemoteFileHelp.setVisible(!isLocal()); browse.setVisible(isLocal()); lAvailableBackups.setVisible(isLocal()); tableScroll.setVisible(isLocal()); refreshList.setVisible(isLocal()); verifyBackup.setVisible(isLocal()); } }); } /** * {@inheritDoc} */ public void toBeDisplayed(boolean visible) { if (visible && backupDirectoryInitialized) { refreshList(); } } }