blob: 37d251245a8c85e9e1e6b43ce517bb00449143fb [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.update.internal.core;
import java.io.*;
import java.net.*;
import java.util.*;
import org.eclipse.core.runtime.*;
import org.eclipse.update.core.*;
import org.eclipse.update.configurator.*;
/**
* singleton pattern.
* manages the error/recover log file
*/
public class ErrorRecoveryLog {
public static final boolean RECOVERY_ON = false;
private static final String ERROR_RECOVERY_LOG = "error_recovery.log"; //$NON-NLS-1$
private static final String LOG_ENTRY_KEY = "LogEntry."; //$NON-NLS-1$
private static final String RETURN_CARRIAGE = "\r\n"; //$NON-NLS-1$
private static final String END_OF_FILE = "eof=eof"; //$NON-NLS-1$
//
public static final String START_INSTALL_LOG = "START_INSTALL_LOG"; //$NON-NLS-1$
public static final String PLUGIN_ENTRY = "PLUGIN"; //$NON-NLS-1$
public static final String FRAGMENT_ENTRY = "FRAGMENT"; //$NON-NLS-1$
public static final String BUNDLE_MANIFEST_ENTRY = "BUNDLE_MANIFEST"; //$NON-NLS-1$
public static final String BUNDLE_JAR_ENTRY = "BUNDLE"; //$NON-NLS-1$
public static final String FEATURE_ENTRY = "FEATURE"; //$NON-NLS-1$
public static final String ALL_INSTALLED = "ALL_FEATURES_INSTALLED"; //$NON-NLS-1$
public static final String RENAME_ENTRY = "RENAME"; //$NON-NLS-1$
public static final String END_INSTALL_LOG = "END_INSTALL_LOG"; //$NON-NLS-1$
public static final String START_REMOVE_LOG = "REMOVE_LOG"; //$NON-NLS-1$
public static final String END_ABOUT_REMOVE = "END_ABOUT_TO_REMOVE"; //$NON-NLS-1$
public static final String DELETE_ENTRY = "DELETE"; //$NON-NLS-1$
public static final String END_REMOVE_LOG = "END_REMOVE_LOG"; //$NON-NLS-1$
public static boolean forceRemove = false;
private static ErrorRecoveryLog inst;
private FileWriter out;
private int index;
private List paths;
private boolean open = false;
private int nbOfOpen = 0;
/**
* Constructor for ErrorRecoveryLog.
*/
private ErrorRecoveryLog() {
super();
}
/**
* Singleton
*/
public static ErrorRecoveryLog getLog() {
if (inst == null){
inst = new ErrorRecoveryLog();
}
return inst;
}
/**
* get a unique identifer for the file, ensure uniqueness up to now
*/
public static String getLocalRandomIdentifier(String path) {
if (path==null) return null;
// verify if it will be a directory without creating the file
// as it doesn't exist yet
if (path.endsWith(File.separator) || path.endsWith("/")) //$NON-NLS-1$
return path;
File file = new File(path);
String newName =
UpdateManagerUtils.getLocalRandomIdentifier(file.getName(), new Date());
while (new File(newName).exists()) {
newName =
UpdateManagerUtils.getLocalRandomIdentifier(file.getName(), new Date());
}
File newFile = new File(file.getParentFile(),newName);
return newFile.getAbsolutePath();
}
/**
* returns the log file
* We do not check if the file exists
*/
public File getRecoveryLogFile() {
IPlatformConfiguration configuration =
ConfiguratorUtils.getCurrentPlatformConfiguration();
URL location = configuration.getConfigurationLocation();
String locationString = location.getFile();
File platformConfiguration = new File(locationString);
if (!platformConfiguration.isDirectory()) platformConfiguration = platformConfiguration.getParentFile();
return new File(platformConfiguration, ERROR_RECOVERY_LOG);
}
/**
* Open the log
*/
public void open(String logEntry) throws CoreException {
if (open) {
nbOfOpen++;
UpdateCore.warn("Open nested Error/Recovery log #"+nbOfOpen+":"+logEntry); //$NON-NLS-1$ //$NON-NLS-2$
return;
}
File logFile = null;
try {
logFile = getRecoveryLogFile();
out = new FileWriter(logFile);
index = 0;
paths=null;
open=true;
nbOfOpen=0;
UpdateCore.warn("Start new Error/Recovery log #"+nbOfOpen+":"+logEntry); //$NON-NLS-1$ //$NON-NLS-2$
} catch (IOException e) {
throw Utilities.newCoreException(
Policy.bind("UpdateManagerUtils.UnableToLog", new Object[] { logFile }), //$NON-NLS-1$
e);
}
append(logEntry);
}
/**
* Append the string to the log and flush
*/
public void append(String logEntry) throws CoreException {
File logFile = null;
try {
if (!open) {
UpdateCore.warn("Internal Error: The Error/Recovery log is not open:"+logEntry); //$NON-NLS-1$
return;
}
StringBuffer buffer = new StringBuffer(LOG_ENTRY_KEY);
buffer.append(index);
buffer.append("="); //$NON-NLS-1$
buffer.append(logEntry);
buffer.append(RETURN_CARRIAGE);
out.write(buffer.toString());
out.flush();
index++;
} catch (IOException e) {
throw Utilities.newCoreException(
Policy.bind("UpdateManagerUtils.UnableToLog", new Object[] { logFile }), //$NON-NLS-1$
e);
}
}
/**
* Append the string to the log and flush
*/
public void appendPath(String logEntry, String path) throws CoreException {
if (path == null)
return;
StringBuffer buffer = new StringBuffer(logEntry);
buffer.append(" "); //$NON-NLS-1$
buffer.append(path);
append(buffer.toString());
addPath(path);
}
/**
* Close any open recovery log
*/
public void close(String logEntry) throws CoreException {
if (nbOfOpen>0){
UpdateCore.warn("Close nested Error/Recovery log #"+nbOfOpen+":"+logEntry); //$NON-NLS-1$ //$NON-NLS-2$
nbOfOpen--;
return;
}
UpdateCore.warn("Close Error/Recovery log #"+nbOfOpen+":"+logEntry); //$NON-NLS-1$ //$NON-NLS-2$
append(logEntry);
if (out != null) {
try {
out.write(END_OF_FILE);
out.flush();
out.close();
} catch (IOException e) { //eat the exception
} finally {
out = null;
open=false;
}
}
}
/**
* Delete the file from the file system
*/
public void delete() {
//File logFile = getRecoveryLogFile();
getRecoveryLogFile();
//if (logFile.exists())
//logFile.delete();
}
/**
*
*/
private void addPath(String path){
if (paths==null) paths = new ArrayList();
paths.add(path);
}
/**
* recover an install or remove that didn't finish
* Delete file for an unfinished delete
* Delete file for an unfinshed install if not all the files were installed
* Rename XML files for an install if all the files were installed but not renamed
*/
public IStatus recover(){
IStatus mainStatus = createStatus(IStatus.OK,Policy.bind("ErrorRecoveryLog.recoveringStatus"),null); //$NON-NLS-1$
MultiStatus multi = new MultiStatus(mainStatus.getPlugin(),mainStatus.getCode(),mainStatus.getMessage(),null);
//check if recovery is on
if (!RECOVERY_ON){
UpdateCore.warn("Recovering is turned off. Abort recovery"); //$NON-NLS-1$
return multi;
}
File logFile = getRecoveryLogFile();
if (!logFile.exists()){
multi.add(createStatus(IStatus.ERROR,Policy.bind("ErrorRecoveryLog.cannotFindLogFile")+logFile,null)); //$NON-NLS-1$
return multi;
}
InputStream in = null;
Properties prop = null;
try {
in = new FileInputStream(logFile);
prop = new Properties();
prop.load(in);
} catch (IOException e){
UpdateCore.warn("Unable to read:"+logFile,e); //$NON-NLS-1$
multi.add(createStatus(IStatus.ERROR,Policy.bind("ErrorRecoveryLog.noPropertyFile")+logFile,e)); //$NON-NLS-1$
return multi;
} finally {
if (in != null)
try {
in.close();
} catch (IOException e1) {
}
}
String eof = prop.getProperty("eof"); //$NON-NLS-1$
if(eof!=null && eof.equals("eof")){ //$NON-NLS-1$
// all is good
delete();
UpdateCore.warn("Found log file. Log file contains end-of-file. No need to process"); //$NON-NLS-1$
multi.add(createStatus(IStatus.OK,null,null));
return multi;
}
String recovery = prop.getProperty(LOG_ENTRY_KEY+"0"); //$NON-NLS-1$
if (recovery==null){
multi.add(createStatus(IStatus.ERROR,Policy.bind("ErrorRecoveryLog.noLogEntry")+logFile,null)); //$NON-NLS-1$
return multi;
}
if(recovery.equalsIgnoreCase(START_INSTALL_LOG)){
multi.addAll(processRecoverInstall(prop));
return multi;
}
if(recovery.equalsIgnoreCase(START_REMOVE_LOG)){
multi.addAll(processRecoverRemove(prop));
return multi;
}
multi.add(createStatus(IStatus.ERROR,Policy.bind("ErrorRecoveryLog.noRecoveryToExecute")+logFile,null)); //$NON-NLS-1$
return multi;
}
/*
* creates a Status
*/
private IStatus createStatus(int statusSeverity, String msg, Exception e){
String id =
UpdateCore.getPlugin().getBundle().getSymbolicName();
StringBuffer completeString = new StringBuffer(""); //$NON-NLS-1$
if (msg!=null)
completeString.append(msg);
if (e!=null){
completeString.append("\r\n["); //$NON-NLS-1$
completeString.append(e.toString());
completeString.append("]\r\n"); //$NON-NLS-1$
}
return new Status(statusSeverity, id, IStatus.OK, completeString.toString(), e);
}
/*
*
*/
private IStatus processRecoverInstall(Properties prop){
IStatus mainStatus = createStatus(IStatus.OK,"",null); //$NON-NLS-1$
MultiStatus multi = new MultiStatus(mainStatus.getPlugin(),mainStatus.getCode(),"",null); //$NON-NLS-1$
Collection values = prop.values();
if(values.contains(END_INSTALL_LOG)){
// all is good
delete();
UpdateCore.warn("Found log file. Log file contains END_INSTALL_LOG. No need to process rename"); //$NON-NLS-1$
multi.add(createStatus(IStatus.OK,null,null));
return multi;
}
if (values.contains(ALL_INSTALLED) && !forceRemove){
// finish install by renaming
int index = 0;
boolean found = false;
String val = prop.getProperty(LOG_ENTRY_KEY+index);
while(val!=null && !found){
if(val.equalsIgnoreCase(ALL_INSTALLED)) found = true;
IStatus renameStatus = processRename(val);
UpdateCore.log(renameStatus);
if(renameStatus.getSeverity()!=IStatus.OK){
multi.add(renameStatus);
}
index++;
val = prop.getProperty(LOG_ENTRY_KEY+index);
}
if (val==null){
UpdateCore.warn("Unable to find value for :"+LOG_ENTRY_KEY+index); //$NON-NLS-1$
multi.add(createStatus(IStatus.ERROR,Policy.bind("ErrorRecoveryLog.wrongLogFile")+LOG_ENTRY_KEY+index,null)); //$NON-NLS-1$
return multi;
}
// process recovery finished
delete();
UpdateCore.warn("Found log file. Successfully recovered by renaming. Feature is installed."); //$NON-NLS-1$
multi.add(createStatus(IStatus.OK,null,null));
} else {
// remove all because install did not lay out all the files
// or recovery is not allowed
int index = 0;
String val = prop.getProperty(LOG_ENTRY_KEY+index);
while(val!=null){
IStatus removeStatus = processRemove(val);
UpdateCore.log(removeStatus);
if(removeStatus.getSeverity()!=IStatus.OK){
multi.addAll(removeStatus);
}
index++;
val = prop.getProperty(LOG_ENTRY_KEY+index);
}
// process recovery finished
delete();
UpdateCore.warn("Found log file. Successfully recovered by removing. Feature is removed."); //$NON-NLS-1$
multi.add(createStatus(IStatus.OK,null,null));
}
return multi;
}
/*
*
*/
private IStatus processRename(String val){
// get the path
int index = -1;
String newFileName = null;
if (val.startsWith(PLUGIN_ENTRY)){
index = PLUGIN_ENTRY.length();
newFileName= "plugin.xml"; //$NON-NLS-1$
} else if (val.startsWith(BUNDLE_MANIFEST_ENTRY)){
index = BUNDLE_MANIFEST_ENTRY.length();
newFileName= "META-INF/MANIFEST.MF"; //$NON-NLS-1$
}else if (val.startsWith(FRAGMENT_ENTRY)){
index = FRAGMENT_ENTRY.length();
newFileName= "fragment.xml"; //$NON-NLS-1$
} else if (val.startsWith(FEATURE_ENTRY)){
index = FEATURE_ENTRY.length();
newFileName= "feature.xml"; //$NON-NLS-1$
} else if (val.startsWith(BUNDLE_JAR_ENTRY)){
index = BUNDLE_JAR_ENTRY.length();
}
if (index==-1){
return createStatus(IStatus.ERROR,Policy.bind("ErrorRecoveryLog.noAction")+val,null); //$NON-NLS-1$
}
String oldName = val.substring(index+1);
// oldname is com.pid/plugin#####.xml
// or oldname is com.pid/pid_pver.jar######.tmp
File oldFile = new File(oldName);
File newFile;
if(val.startsWith(BUNDLE_JAR_ENTRY)){
newFile = new File(oldFile.getAbsolutePath().substring(0, oldFile.getAbsolutePath().lastIndexOf(".jar")+".jar".length())); //$NON-NLS-1$ //$NON-NLS-2$
}else{
newFile = new File(oldFile.getParentFile(),newFileName);
}
if (!oldFile.exists()){
if (newFile.exists()){
// ok the file has been renamed apparently
return createStatus(IStatus.OK,Policy.bind("ErrorRecoveryLog.fileAlreadyRenamed")+newFile,null); //$NON-NLS-1$
} else {
// the file doesn't exist, log as problem, and force the removal of the feature
return createStatus(IStatus.ERROR,Policy.bind("ErrorRecoveryLog.cannotFindFile")+oldFile,null); //$NON-NLS-1$
}
}
boolean sucess = false;
if (newFile.exists()) {
UpdateManagerUtils.removeFromFileSystem(newFile);
UpdateCore.warn("Removing already existing file:"+newFile); //$NON-NLS-1$
}
sucess = oldFile.renameTo(newFile);
if(!sucess){
String msg =(Policy.bind("ErrorRecoveryLog.oldToNew")+oldFile+newFile); //$NON-NLS-1$
return createStatus(IStatus.ERROR,msg,null);
}
return createStatus(IStatus.OK,Policy.bind("ErrorRecoveryLog.renamed")+oldFile+Policy.bind("ErrorRecoveryLog.to")+newFile,null); //$NON-NLS-1$ //$NON-NLS-2$
}
/*
*
*/
private IStatus processRemove(String val){
IStatus mainStatus = createStatus(IStatus.OK,"",null); //$NON-NLS-1$
MultiStatus multi = new MultiStatus(mainStatus.getPlugin(),mainStatus.getCode(),"",null); //$NON-NLS-1$
// get the path
int index = -1;
if (val.startsWith(BUNDLE_JAR_ENTRY)){
index = BUNDLE_JAR_ENTRY.length();
}
if (index==-1){
return createStatus(IStatus.ERROR,Policy.bind("ErrorRecoveryLog.noAction")+val,null); //$NON-NLS-1$
}
String oldName = val.substring(index+1);
File oldFile = new File(oldName);
if (!oldFile.exists()){
// the jar or directory doesn't exist, log as problem, and force the removal of the feature
multi.add(createStatus(IStatus.ERROR,Policy.bind("ErrorRecoveryLog.cannotFindFile")+oldFile,null)); //$NON-NLS-1$
return multi;
}
multi.addAll(removeFromFileSystem(oldFile));
return multi;
}
/**
* return a multi status,
* the children are the file that couldn't be removed
*/
public IStatus removeFromFileSystem(File file) {
IStatus mainStatus = createStatus(IStatus.OK,"",null); //$NON-NLS-1$
MultiStatus multi = new MultiStatus(mainStatus.getPlugin(),mainStatus.getCode(),"",null); //$NON-NLS-1$
if (!file.exists()){
multi.add(createStatus(IStatus.ERROR,Policy.bind("ErrorRecoveryLog.noFiletoRemove")+file,null)); //$NON-NLS-1$
return multi;
}
if (file.isDirectory()) {
String[] files = file.list();
if (files != null) // be careful since file.list() can return null
for (int i = 0; i < files.length; ++i){
multi.addAll(removeFromFileSystem(new File(file, files[i])));
}
}
if (!file.delete()) {
String msg = "Unable to remove file" +file.getAbsolutePath(); //$NON-NLS-1$
multi.add(createStatus(IStatus.ERROR,msg,null));
}
return multi;
}
/*
*
*/
private IStatus processRecoverRemove(Properties prop){
IStatus mainStatus = createStatus(IStatus.OK,"",null); //$NON-NLS-1$
MultiStatus multi = new MultiStatus(mainStatus.getPlugin(),mainStatus.getCode(),"",null); //$NON-NLS-1$
Collection values = prop.values();
if(values.contains(END_REMOVE_LOG)){
// all is good
delete();
UpdateCore.warn("Found log file. Log file contains END_REMOVE_LOG. No need to process rename"); //$NON-NLS-1$
multi.add(createStatus(IStatus.OK,null,null));
return multi;
}
if (!values.contains(END_ABOUT_REMOVE)){
// finish install by renaming
multi.add(createStatus(IStatus.ERROR,Policy.bind("ErrorRecoveryLog.removeFeature"),null)); //$NON-NLS-1$
return multi;
} else {
// finish install by renaming
int index = 0;
boolean found = false;
String val = prop.getProperty(LOG_ENTRY_KEY+index);
while(val!=null && !found){
if(val.equalsIgnoreCase(END_ABOUT_REMOVE)) found = true;
IStatus renameStatus = processRemove(val);
UpdateCore.log(renameStatus);
if(renameStatus.getSeverity()!=IStatus.OK){
multi.add(renameStatus);
}
index++;
val = prop.getProperty(LOG_ENTRY_KEY+index);
}
if (val==null){
UpdateCore.warn("Unable to find value for :"+LOG_ENTRY_KEY+index); //$NON-NLS-1$
multi.add(createStatus(IStatus.ERROR,Policy.bind("ErrorRecoveryLog.wrongLogFile")+LOG_ENTRY_KEY+index,null)); //$NON-NLS-1$
return multi;
}
// process recovery finished
delete();
UpdateCore.warn("Found log file. Successfully recovered by deleting. Feature is removed."); //$NON-NLS-1$
multi.add(createStatus(IStatus.OK,null,null));
}
return multi;
}
}