| /******************************************************************************* |
| * Copyright (c) 2004, 2013 Tasktop Technologies and others. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * https://www.eclipse.org/legal/epl-2.0 |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Tasktop Technologies - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.mylyn.internal.tasks.ui; |
| |
| import java.io.File; |
| import java.text.ParseException; |
| import java.util.Calendar; |
| import java.util.Date; |
| import java.util.Locale; |
| import java.util.SortedMap; |
| import java.util.TreeMap; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.jface.util.IPropertyChangeListener; |
| import org.eclipse.jface.util.PropertyChangeEvent; |
| import org.eclipse.mylyn.commons.net.Policy; |
| import org.eclipse.mylyn.internal.tasks.core.ITasksCoreConstants; |
| import org.eclipse.mylyn.internal.tasks.core.TaskActivityUtil; |
| import org.eclipse.mylyn.internal.tasks.ui.util.TaskDataExportOperation; |
| import org.eclipse.mylyn.internal.tasks.ui.util.TaskDataSnapshotOperation; |
| import org.eclipse.mylyn.internal.tasks.ui.util.TasksUiInternal; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.progress.IProgressService; |
| |
| import com.ibm.icu.text.SimpleDateFormat; |
| |
| /** |
| * @author Rob Elves |
| */ |
| public class TaskListBackupManager implements IPropertyChangeListener { |
| |
| private static final String OLD_MYLYN_2_BACKUP_FILE_PREFIX = "mylyndata-"; //$NON-NLS-1$ |
| |
| // Mylyn 3.0 Backup file name |
| private static final String BACKUP_FILE_PREFIX = "mylyn-v3-data-"; //$NON-NLS-1$ |
| |
| private static final Pattern MYLYN_BACKUP_REGEXP = Pattern.compile("^(" + BACKUP_FILE_PREFIX + ")?(" //$NON-NLS-1$ //$NON-NLS-2$ |
| + OLD_MYLYN_2_BACKUP_FILE_PREFIX + ")?"); //$NON-NLS-1$ |
| |
| private static final Pattern DATE_FORMAT_OLD = Pattern.compile("\\d{4}-\\d{2}-\\d{2}"); //$NON-NLS-1$ |
| |
| private static final Pattern DATE_FORMAT = Pattern.compile("\\d{4}-\\d{2}-\\d{2}-\\d{6}"); //$NON-NLS-1$ |
| |
| private static final long SECOND = 1000; |
| |
| private static final long MINUTE = 60 * SECOND; |
| |
| private static final long STANDARD_DELAY = 30 * MINUTE; |
| |
| private String backupFolderPath; |
| |
| private Job runBackup; |
| |
| private static boolean errorDisplayed = false; |
| |
| public TaskListBackupManager(String backupFolderPath) { |
| this.backupFolderPath = backupFolderPath; |
| start(STANDARD_DELAY); |
| } |
| |
| public void start(long delay) { |
| |
| if (runBackup != null) { |
| stop(); |
| } |
| |
| runBackup = new Job("Task Data Snapshot") { //$NON-NLS-1$ |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| try { |
| if (TasksUiPlugin.getTaskList().getAllTasks().size() > 0) { |
| backupNow(false, monitor); |
| } |
| return Status.OK_STATUS; |
| } finally { |
| if (PlatformUI.isWorkbenchRunning()) { |
| schedule(STANDARD_DELAY); |
| } |
| } |
| } |
| }; |
| runBackup.setPriority(Job.BUILD); |
| runBackup.setSystem(true); |
| runBackup.schedule(delay); |
| } |
| |
| public void stop() { |
| if (runBackup != null) { |
| if (!runBackup.cancel()) { |
| try { |
| runBackup.join(); |
| } catch (InterruptedException e) { |
| // ignore |
| } |
| } |
| runBackup = null; |
| } |
| } |
| |
| public static String getBackupFileName() { |
| SimpleDateFormat format = new SimpleDateFormat(ITasksCoreConstants.FILENAME_TIMESTAMP_FORMAT, Locale.ENGLISH); |
| String date = format.format(new Date()); |
| String backupFileName = BACKUP_FILE_PREFIX + date + ".zip"; //$NON-NLS-1$ |
| return backupFileName; |
| } |
| |
| public void backupNow(boolean synchronous) { |
| backupNow(synchronous, null); |
| } |
| |
| public synchronized void backupNow(boolean synchronous, IProgressMonitor monitor) { |
| |
| monitor = Policy.monitorFor(monitor); |
| |
| File backupFolder = new File(backupFolderPath); |
| if (!backupFolder.exists()) { |
| backupFolder.mkdir(); |
| } |
| |
| final TaskDataExportOperation backupJob = new TaskDataSnapshotOperation(backupFolderPath, getBackupFileName()); |
| try { |
| if (!synchronous) { |
| backupJob.run(monitor); |
| removeOldBackups(); |
| } else { |
| IProgressService service = PlatformUI.getWorkbench().getProgressService(); |
| service.run(false, true, backupJob); |
| } |
| } catch (InterruptedException e) { |
| } catch (Throwable e) { |
| if (!errorDisplayed) { |
| final Status status = new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, |
| Messages.TaskListBackupManager_Error_occured_during_scheduled_tasklist_backup, e); |
| errorDisplayed = true; |
| if (Display.getCurrent() != null) { |
| TasksUiInternal.logAndDisplayStatus(Messages.TaskListBackupManager_Scheduled_task_data_backup, |
| status); |
| } else { |
| TasksUiInternal.asyncLogAndDisplayStatus(Messages.TaskListBackupManager_Scheduled_task_data_backup, |
| status); |
| } |
| } |
| |
| // clean up corrupt backup file |
| if (backupJob.getDestinationFile() != null) { |
| backupJob.getDestinationFile().delete(); |
| } |
| } |
| } |
| |
| public SortedMap<Long, File> getBackupFiles() { |
| |
| SortedMap<Long, File> filesMap = new TreeMap<Long, File>(); |
| String destination = backupFolderPath; |
| |
| File backupFolder = new File(destination); |
| if (!backupFolder.exists()) { |
| return filesMap; |
| } |
| |
| File[] files = backupFolder.listFiles(); |
| if (files == null) { |
| return filesMap; |
| } |
| |
| for (File file : files) { |
| Matcher matcher = MYLYN_BACKUP_REGEXP.matcher(file.getName()); |
| if (matcher.find()) { |
| Date date = null; |
| try { |
| SimpleDateFormat format = null; |
| String dateText = null; |
| Matcher dateFormatMatcher = DATE_FORMAT.matcher(file.getName()); |
| if (dateFormatMatcher.find()) { |
| format = new SimpleDateFormat(ITasksCoreConstants.FILENAME_TIMESTAMP_FORMAT, Locale.ENGLISH); |
| dateText = dateFormatMatcher.group(); |
| } else { |
| dateFormatMatcher = DATE_FORMAT_OLD.matcher(file.getName()); |
| if (dateFormatMatcher.find()) { |
| format = new SimpleDateFormat(ITasksCoreConstants.OLD_FILENAME_TIMESTAMP_FORMAT, |
| Locale.ENGLISH); |
| dateText = dateFormatMatcher.group(); |
| } |
| } |
| if (format != null && dateText != null && dateText.length() > 0) { |
| date = format.parse(dateText); |
| } else { |
| continue; |
| } |
| } catch (IndexOutOfBoundsException e) { |
| continue; |
| } catch (ParseException e) { |
| continue; |
| } |
| if (date != null && date.getTime() > 0) { |
| filesMap.put(new Long(date.getTime()), file); |
| } |
| } |
| } |
| |
| return filesMap; |
| } |
| |
| /** public for testing purposes */ |
| public synchronized void removeOldBackups() { |
| |
| SortedMap<Long, File> filesMap = getBackupFiles(); |
| |
| if (filesMap.size() > 0) { |
| |
| Calendar rangeStart = TaskActivityUtil.getCalendar(); |
| rangeStart.setTimeInMillis(filesMap.lastKey()); |
| TaskActivityUtil.snapStartOfHour(rangeStart); |
| int startHour = rangeStart.get(Calendar.HOUR_OF_DAY); |
| Calendar rangeEnd = TaskActivityUtil.getCalendar(); |
| rangeEnd.setTimeInMillis(rangeStart.getTimeInMillis()); |
| rangeEnd.add(Calendar.HOUR_OF_DAY, 1); |
| // Keep one backup for last 8 hours of today |
| for (int x = 0; x <= startHour && x < 9; x++) { |
| SortedMap<Long, File> subMap = filesMap.subMap(rangeStart.getTimeInMillis(), rangeEnd.getTimeInMillis()); |
| if (subMap.size() > 1) { |
| while (subMap.size() > 1) { |
| File toDelete = subMap.remove(subMap.firstKey()); |
| toDelete.delete(); |
| } |
| } |
| rangeStart.add(Calendar.HOUR_OF_DAY, -1); |
| rangeEnd.add(Calendar.HOUR_OF_DAY, -1); |
| } |
| |
| // Keep one backup a day for the past 12 days |
| TaskActivityUtil.snapStartOfDay(rangeEnd); |
| rangeStart.add(Calendar.DAY_OF_YEAR, -1); |
| for (int x = 1; x <= 12; x++) { |
| SortedMap<Long, File> subMap = filesMap.subMap(rangeStart.getTimeInMillis(), rangeEnd.getTimeInMillis()); |
| if (subMap.size() > 1) { |
| while (subMap.size() > 1) { |
| File toDelete = subMap.remove(subMap.firstKey()); |
| toDelete.delete(); |
| } |
| } |
| rangeStart.add(Calendar.DAY_OF_YEAR, -1); |
| rangeEnd.add(Calendar.DAY_OF_YEAR, -1); |
| } |
| |
| // Remove all older backups |
| SortedMap<Long, File> subMap = filesMap.subMap(0l, rangeStart.getTimeInMillis()); |
| if (subMap.size() > 0) { |
| while (subMap.size() > 0) { |
| File toDelete = subMap.remove(subMap.firstKey()); |
| toDelete.delete(); |
| } |
| } |
| } |
| } |
| |
| public void propertyChange(PropertyChangeEvent event) { |
| if (event.getProperty().equals(ITasksUiPreferenceConstants.PREF_DATA_DIR)) { |
| backupFolderPath = TasksUiPlugin.getDefault().getBackupFolderPath(); |
| } |
| } |
| |
| } |