blob: 4b3334243075a9550d94d7d13519742fca325f1e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.internal.p2.touchpoint.natives;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.zip.*;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.equinox.internal.p2.core.helpers.FileUtils;
import org.eclipse.osgi.util.NLS;
public class BackupFiles {
private static final String ZIP_SUFFIX = ".zip"; //$NON-NLS-1$
private static final String PROPERTIES_SUFFIX = ".properties"; //$NON-NLS-1$
private final File backupDir;
private boolean doBackup;
/**
* Save or restore backups of files in backupDir
*/
public BackupFiles(File backupDir) {
this.doBackup = true;
this.backupDir = backupDir;
this.backupDir.mkdirs();
}
// /**
// * If doBackup is set to false, files are deleted on uninstall, instead of restored.
// */
// public void setDoBackup(boolean doBackup) {
// this.doBackup = doBackup;
// }
/**
* Restore all backups made in this dir.
*/
public void restore(IProgressMonitor monitor) throws IOException {
// find backup properties files, do in reverse order
List propsFiles = new LinkedList();
for (int i = 0;; i += 1) {
File propsFile = getBackupProperties(i);
if (!propsFile.exists()) {
break;
}
propsFiles.add(0, propsFile);
}
IProgressMonitor[] pm = Util.splitProgressMonitor(monitor, propsFiles.size());
int j = 0;
for (Iterator i = propsFiles.iterator(); i.hasNext();) {
File propsFile = (File) i.next();
restoreFilesFromBackup(propsFile, pm[j++]);
}
if (!this.backupDir.delete()) {
//not empty? log a warning?
} else {
// delete the parent if empty
this.backupDir.getParentFile().delete();
}
monitor.done();
}
/**
* Find files under outputDir that will be overwritten in unzipURL
* and save under backupDir, and delete.
* Include properties file to indicate files to delete or restore on rolled back.
* The progress monitor is used only to display sub-tasks; we don't update it otherwise.
*/
public void backupFilesInZip(String identifier, URL zipURL, File outputDir, IProgressMonitor monitor) throws IOException {
BackupProperties backupProps = new BackupProperties(identifier, outputDir);
ZipOutputStream zos = null;
String prevDir = null;
try {
ZipInputStream in = new ZipInputStream(zipURL.openStream());
ZipEntry ze;
while ((ze = in.getNextEntry()) != null) {
String name = ze.getName();
int i = name.lastIndexOf('/');
if (i != -1) {
String dir = name.substring(0, i);
if (this.doBackup && !dir.equals(prevDir)) {
monitor.subTask(name.substring(0, i));
prevDir = dir;
}
}
if (!ze.isDirectory()) {
File origFile = new File(outputDir, name);
if (this.doBackup && origFile.exists()) {
if (zos == null) {
File zipFile = backupProps.getArchive();
zos = new ZipOutputStream(new FileOutputStream(zipFile));
}
ZipEntry zipEntry = new ZipEntry(name);
zipEntry.setTime(origFile.lastModified());
zos.putNextEntry(zipEntry);
FileUtils.copyStream(new FileInputStream(origFile), true, zos, false);
zos.closeEntry();
} else {
backupProps.addFileToDelete(name);
}
origFile.delete();
}
in.closeEntry();
}
in.close();
} finally {
backupProps.store();
if (zos != null) {
zos.close();
}
}
}
private void restoreFilesFromBackup(File propsFile, IProgressMonitor monitor) throws IOException {
BackupProperties backupProps = new BackupProperties(propsFile);
monitor.beginTask(NLS.bind(Messages.restoring, propsFile.toString()), 3);
monitor.subTask(""); //$NON-NLS-1$
for (Iterator i = backupProps.getFilesToDelete().iterator(); i.hasNext();) {
String name = (String) i.next();
File full = new File(backupProps.getRootDir(), name);
full.delete();
}
monitor.worked(1);
File zipFile = backupProps.getArchive();
if (zipFile.exists()) { // only exists if files were saved
SubProgressMonitor sub = new SubProgressMonitor(monitor, 1, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
FileUtils.unzipFile(zipFile, backupProps.getRootDir(), "", sub); //$NON-NLS-1$
zipFile.delete();
} else {
monitor.worked(1);
}
for (Iterator i = backupProps.getDirsToDelete().iterator(); i.hasNext();) {
String name = (String) i.next();
File full = new File(backupProps.getRootDir(), name);
FileUtils.deleteEmptyDirs(full);
}
propsFile.delete();
monitor.worked(1);
monitor.done();
}
// Backup files are just 0.properties, 1.properties, etc.
// Get the next unused one.
File getBackupProperties() {
for (int i = 0;; i += 1) {
File result = getBackupProperties(i);
if (!result.exists())
return result;
}
}
private File getBackupProperties(int i) {
return new File(BackupFiles.this.backupDir, Integer.toString(i) + PROPERTIES_SUFFIX);
}
private class BackupProperties extends Properties {
private static final long serialVersionUID = 2268313492348533029L;
private static final char FILE_KIND = 'f';
private static final char DIR_KIND = 'd';
private static final String ROOT_DIR = "rootDir"; //$NON-NLS-1$
private static final String ARTIFACT_KEY = "artifactKey"; //$NON-NLS-1$
// private static final String ARTIFACT_USER = "artifactUser"; //$NON-NLS-1$
private int n = 0; // number of properties
private File file; // file to store properties in
private List keys = new LinkedList(); // keys, in order they were added or read
private final File rootDir; // root of where files are going
private Set dirsToCreate = new TreeSet(); // set of dirs we will create
// create properties based on file we are backing up to
public BackupProperties(String identifier, File rootDir) {
this.file = BackupFiles.this.getBackupProperties();
this.rootDir = rootDir;
setProperty(ROOT_DIR, rootDir.getPath().replace('\\', '/'));
setProperty(ARTIFACT_KEY, (identifier != null ? identifier : rootDir.getAbsolutePath()));
// setProperty(ARTIFACT_USER, artifact.toUserString());
// make sure rootDir is deleted if appropriate
addDir("./"); //$NON-NLS-1$
}
// create backup properties from a previously saved BackupProperties
public BackupProperties(File file) throws IOException {
this.file = file;
FileInputStream stream = new FileInputStream(file);
try {
load(stream);
} finally {
stream.close();
}
this.rootDir = new File(getProperty(ROOT_DIR));
}
// public String getArtifactKey() {
// return getProperty(ARTIFACT_KEY);
// }
// public String getArtifactUserString() {
// String result = getProperty(ARTIFACT_USER);
// if (result != null) {
// return result;
// } else {
// // return something if the key wasn't saved
// result = getArtifactKey();
// result = result.replaceFirst(",native,", ","); //$NON-NLS-1$ //$NON-NLS-2$
// return result.replace(',', ' ').trim();
// }
// }
public File getRootDir() {
return this.rootDir;
}
// We are backing up files for this artifact.
// Create a backup zip based on the artifact key (as a hint).
public File getArchive() {
String path = this.file.getPath();
if (path.endsWith(PROPERTIES_SUFFIX)) {
path = path.substring(0, path.length() - PROPERTIES_SUFFIX.length());
}
return new File(path + ZIP_SUFFIX);
}
public List getFilesToDelete() {
return getMatchingProperties(FILE_KIND);
}
public List getDirsToDelete() {
return getMatchingProperties(DIR_KIND);
}
private List getMatchingProperties(char c) {
List result = new LinkedList();
for (Enumeration e = propertyNames(); e.hasMoreElements();) {
String key = (String) e.nextElement();
if (key.equals(BackupProperties.ROOT_DIR)) {
continue;
}
if (key.charAt(0) == c) {
result.add(getProperty(key));
}
}
return result;
}
public void addFileToDelete(String name) {
add(FILE_KIND, name);
addDir(name);
}
public void store() throws IOException {
// add the directories -- at end because we want them all in order
for (Iterator i = this.dirsToCreate.iterator(); i.hasNext();) {
String name = (String) i.next();
add(DIR_KIND, name);
}
FileOutputStream stream = new FileOutputStream(this.file);
try {
store(stream, /*header*/null);
} finally {
stream.close();
}
}
public Object put(Object key, Object value) {
if (!(key instanceof String))
throw new AssertionError("expected String: " + key); //$NON-NLS-1$
if (!(value instanceof String))
throw new AssertionError("expected String: " + value); //$NON-NLS-1$
this.keys.add(key);
return super.put(key, value);
}
// return keys in the order they were added
public synchronized Enumeration keys() {
final Iterator iterator = this.keys.iterator();
return new Enumeration() {
public boolean hasMoreElements() {
return iterator.hasNext();
}
public Object nextElement() {
return iterator.next();
}
};
}
private void add(char kind, String name) {
StringBuffer key = new StringBuffer(4);
key.append(kind).append(n++);
setProperty(key.toString(), name.replace('\\', '/'));
}
// if we're going to create this dir, remember that so we delete it
private void addDir(String name) {
int slash = name.lastIndexOf('/');
if (slash == -1)
return; // no dir
String dirName = name.substring(0, slash);
if (this.dirsToCreate.contains(dirName))
return; // already have it
if (new File(this.rootDir, dirName).exists())
return; // already exists
this.dirsToCreate.add(dirName);
}
}
}