package org.eclipse.core.internal.boot; | |
/* | |
* (c) Copyright IBM Corp. 2000, 2001. | |
* All Rights Reserved. | |
*/ | |
import java.io.File; | |
import java.io.FileInputStream; | |
import java.io.FileOutputStream; | |
import java.io.FilenameFilter; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.PrintWriter; | |
import java.net.MalformedURLException; | |
import java.net.URL; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.Date; | |
import java.util.HashMap; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Properties; | |
import java.util.StringTokenizer; | |
import org.eclipse.core.boot.BootLoader; | |
import org.eclipse.core.boot.IInstallInfo; | |
import org.eclipse.core.boot.IPlatformConfiguration; | |
import org.eclipse.core.internal.boot.update.BootUpdateManager; | |
public class LaunchInfo implements IInstallInfo { | |
private static final String PLATFORM_COMPONENT_ID = "org.eclipse.platform"; | |
private static final String BOOT_PLUGIN_ID = "org.eclipse.core.boot"; | |
private static LaunchInfo profile = null; | |
// temporary flag signalling 2.0 startup. Is set by BootLoader command line processing code. | |
static boolean r2_0 = false; | |
private URL baseUrl; | |
private URL infoUrl; | |
private String id; | |
private String nextId; | |
private String platform; | |
private String app; | |
private String appconfig; | |
private ArrayList configs; | |
private ArrayList configsInact; | |
private ArrayList configsPendingDelete; | |
private ArrayList comps; | |
private ArrayList compsInact; | |
private ArrayList compsDang; | |
private ArrayList compsPendingDelete; | |
private ArrayList plugins; | |
private ArrayList pluginsInact; | |
private ArrayList pluginsUnmgd; | |
private ArrayList pluginsPendingDelete; | |
private ArrayList pluginsOld; | |
private ArrayList fragments; | |
private ArrayList fragmentsInact; | |
private ArrayList fragmentsUnmgd; | |
private ArrayList fragmentsPendingDelete; | |
private ArrayList fragmentsOld; | |
private HashMap infoMap; | |
private ArrayList status; | |
private int historyCount; | |
private ArrayList historyPendingDelete; | |
private boolean newHistory = false; | |
private boolean isUpdateEnabled = false; | |
private FileOutputStream uos = null; | |
private File uf = null; | |
private static final String CONFIGSDIR = "configurations/"; | |
private static final String COMPSDIR = "components/"; | |
private static final String PLUGINSDIR = "plugins/"; | |
private static final String FRAGMENTSDIR = "fragments/"; | |
private static final String PLUGINXML = "plugin.xml"; | |
private static final String FRAGMENTXML = "fragment.xml"; | |
private static final String INSTALLDIR = "install/"; | |
private static final String INSTALLINFODIR = INSTALLDIR + "info/"; | |
private static final String INFO_EXTENSION = ".install"; | |
private static final String LAUNCH_SUMMARY_NAME = "install"; | |
private static final String LAUNCH_SUMMARY_EXT = "properties"; | |
private static final String LAUNCH_SUMMARY = | |
LAUNCH_SUMMARY_NAME + "." + LAUNCH_SUMMARY_EXT; | |
private static final String LAUNCH_PROFILE_NAME = "update"; | |
private static final String LAUNCH_PROFILE_EXT = "cfg"; | |
private static final String LAUNCH_PROFILE = | |
LAUNCH_PROFILE_NAME + "." + LAUNCH_PROFILE_EXT; | |
private static final String LAUNCH_PROFILE_CHKPT = "_" + LAUNCH_PROFILE; | |
private static final String LAUNCH_PROFILE_BAK = "__" + LAUNCH_PROFILE; | |
private static final String UPDATE_MARKER = ".update"; | |
private static final String ID = "id"; | |
private static final String PLATFORM = "runtime"; | |
private static final String APP = "application"; | |
private static final String APP_CONFIG = "application.configuration"; | |
private static final String CONFIG_ACT = "configurations.active"; | |
private static final String CONFIG_INACT = "configurations.inactive"; | |
private static final String CONFIG_PENDDEL = "configurations.delete"; | |
private static final String CONFIG_MAP = "configurations.map"; | |
private static final String COMP_ACT = "components.active"; | |
private static final String COMP_INACT = "components.inactive"; | |
private static final String COMP_DANG = "components.dangling"; | |
private static final String COMP_PENDDEL = "components.delete"; | |
private static final String PLUGIN_ACT = "plugins.active"; | |
private static final String PLUGIN_INACT = "plugins.inactive"; | |
private static final String PLUGIN_UNMGD = "plugins.unmanaged"; | |
private static final String PLUGIN_PENDDEL = "plugins.delete"; | |
private static final String FRAG_ACT = "fragments.active"; | |
private static final String FRAG_INACT = "fragments.inactive"; | |
private static final String FRAG_UNMGD = "fragments.unmanaged"; | |
private static final String FRAG_PENDDEL = "fragments.delete"; | |
private static final String HISTORY_COUNT = "history.count"; | |
private static final String HISTORY_PENDDEL = "history.delete"; | |
private static final String INFO = "info"; | |
private static final String INFO_CONFIGS = "configurations"; | |
private static final String INFO_COMPS = "components"; | |
private static final String EOF = "eof"; | |
private static final String EOF_MARKER = EOF + "=" + EOF; | |
private static final int LIST_SIZE = 10; | |
private static final String DEFAULT_PLATFORM = ""; | |
private static final String DEFAULT_APP = "org.eclipse.ui.workbench"; | |
private static final String DEFAULT_APP_CONFIG = ""; | |
private static final int DEFAULT_HISTORY_COUNT = 5; | |
private static final String URL_FILE = "file"; | |
private static final String URL_VA = "valoader"; | |
private static final String CONFIG_URL = | |
PlatformURLConfigurationConnection.CONFIG_URL_STRING; | |
private static final String COMP_URL = | |
PlatformURLComponentConnection.COMP_URL_STRING; | |
// uninstall | |
private static final String UNINSTALLFLAG = "-uninstall"; | |
// debug tracing | |
private static final String DEBUGFLAG = "-debug"; | |
public static boolean DEBUG = false; | |
public static class History { | |
private URL url; | |
private Date date; | |
public History(URL url, Date date) { | |
this.url = url; | |
this.date = date; | |
} | |
public History(URL url, String id) { | |
this.url = url; | |
this.date = id == null ? null : new Date(Long.parseLong(id)); | |
} | |
public String toString() { | |
if (date == null) | |
return "current"; | |
else | |
return date.toString(); | |
} | |
public URL getLaunchInfoURL() { | |
return url; | |
} | |
public Date getLaunchInfoDate() { | |
/** | |
* Return history profile creation date, or null (if curent profile) | |
*/ | |
return date; | |
} | |
public String getIdentifier() { | |
/** | |
* Return history profile creation date, or null (if curent profile) | |
*/ | |
if (date == null) | |
return null; | |
else | |
return Long.toString(date.getTime()); | |
} | |
public boolean isCurrent() { | |
return date == null; | |
} | |
} | |
public static class Status { | |
private String msg; | |
private Throwable exc; | |
public Status(String msg) { | |
this.msg = msg; | |
this.exc = null; | |
} | |
public Status(String msg, Throwable exc) { | |
this.msg = msg; | |
this.exc = exc; | |
} | |
public String getMessage() { | |
return msg; | |
} | |
public Throwable getException() { | |
return exc; | |
} | |
} | |
public static class VersionedIdentifier { | |
private String id; | |
private String version; | |
public static final String SEPARATOR = "_"; | |
public VersionedIdentifier(String s) { | |
if (s == null || (s = s.trim()).equals("")) { | |
this.id = ""; | |
this.version = ""; | |
} | |
int ix = s.lastIndexOf(SEPARATOR); | |
if (ix > 0) { | |
this.id = s.substring(0, ix); | |
this.version = s.substring(ix + 1); | |
} else { | |
this.id = s; | |
this.version = ""; | |
} | |
} | |
public VersionedIdentifier(String id, String version) { | |
if (id == null || (id = id.trim()).equals("") || version == null) | |
throw new IllegalArgumentException(); | |
this.id = id; | |
this.version = version.trim(); | |
} | |
public String getIdentifier() { | |
return this.id; | |
} | |
public String getVersion() { | |
return this.version; | |
} | |
public String toString() { | |
return this.version.equals("") ? this.id : this.id + SEPARATOR + this.version; | |
} | |
public boolean equals(Object vid) { | |
if (!(vid instanceof VersionedIdentifier)) | |
return false; | |
return equals((VersionedIdentifier) vid); | |
} | |
public boolean equals(VersionedIdentifier vid) { | |
if (!this.id.equals(vid.id)) | |
return false; | |
return this.version.equals(vid.version); | |
} | |
} | |
public static interface ListSelector { | |
public List get(LaunchInfo info); | |
} | |
private LaunchInfo() { | |
setDefaults(); | |
} | |
private LaunchInfo(URL info) throws IOException { | |
this(info, getCurrent() == null ? null : getCurrent().baseUrl); | |
} | |
private LaunchInfo(URL info, URL install) throws IOException { | |
super(); | |
if (install == null) | |
throw new IOException(); | |
infoUrl = info; | |
baseUrl = install; | |
Properties props = new Properties(); | |
InputStream is = null; | |
try { | |
is = infoUrl.openStream(); | |
props.load(is); | |
loadProperties(props); | |
if (!EOF.equals(props.getProperty(EOF))) | |
throw new IOException(); | |
} finally { | |
if (is != null) | |
try { | |
is.close(); | |
} catch (IOException e) { | |
} | |
} | |
} | |
public LaunchInfo(History history) throws IOException { | |
this(history.getLaunchInfoURL()); | |
} | |
public void addStatus(Status[] status) { | |
if (status == null || status.length == 0) | |
return; | |
if (this.status == null) | |
this.status = new ArrayList(); | |
for (int i = 0; i < status.length; i++) | |
this.status.add(status[i]); | |
} | |
public void addStatus(Status status) { | |
if (status == null) | |
return; | |
if (this.status == null) | |
this.status = new ArrayList(); | |
this.status.add(status); | |
} | |
synchronized private boolean checkpoint() { | |
if (!isUpdateEnabled()) | |
return false; | |
File active = new File(infoUrl.getFile().replace('/', File.separatorChar)); | |
File dir = active.getParentFile(); | |
if (dir == null) | |
return false; // cannot save | |
dir.mkdirs(); | |
File chkpt = new File(dir, LAUNCH_PROFILE_CHKPT); | |
// write temp state | |
PrintWriter os = null; | |
try { | |
os = new PrintWriter(new FileOutputStream(chkpt)); | |
write(getIdentifier(), os); | |
} catch (IOException e) { | |
return false; | |
} finally { | |
if (os != null) | |
os.close(); | |
} | |
return true; | |
} | |
private void checkRuntimePath() { | |
if (pluginsOld.size() != (plugins.size() + pluginsUnmgd.size())) { | |
setNewId(); | |
return; | |
} | |
for (int i = 0; i < plugins.size(); i++) { | |
if (!pluginsOld.contains(plugins.get(i))) { | |
setNewId(); | |
return; | |
} | |
} | |
for (int i = 0; i < pluginsUnmgd.size(); i++) { | |
if (!pluginsOld.contains(pluginsUnmgd.get(i))) { | |
setNewId(); | |
return; | |
} | |
} | |
if (fragmentsOld.size() != (fragments.size() + fragmentsUnmgd.size())) { | |
setNewId(); | |
return; | |
} | |
for (int i = 0; i < fragments.size(); i++) { | |
if (!fragmentsOld.contains(fragments.get(i))) { | |
setNewId(); | |
return; | |
} | |
} | |
for (int i = 0; i < fragmentsUnmgd.size(); i++) { | |
if (!fragmentsOld.contains(fragmentsUnmgd.get(i))) { | |
setNewId(); | |
return; | |
} | |
} | |
} | |
private void checkUpdateEnabled() { | |
if (this != LaunchInfo.getCurrent()) | |
// only allow updates via current LaunchInfo | |
return; | |
if (infoUrl == null) // base must be set | |
return; | |
if (!isFileProtocol(infoUrl)) // must be file URL | |
return; | |
// if (!InternalBootLoader.inDevelopmentMode()) { | |
try { // install must be r/w | |
File f = | |
new File( | |
(new URL(infoUrl, UPDATE_MARKER)).getFile().replace('/', File.separatorChar)); | |
uf = f; | |
if (f.exists()) { | |
if (!f.delete()) | |
return; | |
} | |
byte[] b = EOF.getBytes(); | |
uos = new FileOutputStream(f); | |
uos.write(b); | |
} catch (IOException e) { | |
return; | |
} | |
// } | |
isUpdateEnabled = true; | |
} | |
private VersionedIdentifier[] computeDelta( | |
String[] list, | |
List active, | |
List inactive, | |
List pendingDelete) { | |
if (active == null) | |
active = new ArrayList(); | |
if (inactive == null) | |
inactive = new ArrayList(); | |
ArrayList delta = new ArrayList(); | |
VersionedIdentifier vid; | |
for (int i = 0; i < list.length; i++) { | |
try { | |
vid = new VersionedIdentifier(list[i]); | |
if (!active.contains(vid) | |
&& !inactive.contains(vid) | |
&& !pendingDelete.contains(vid)) | |
delta.add(vid); | |
} catch (Exception e) { /* skip bad identifiers */ | |
} | |
} | |
if (delta.size() == 0) | |
return new VersionedIdentifier[0]; | |
VersionedIdentifier[] result = new VersionedIdentifier[delta.size()]; | |
delta.toArray(result); | |
if (DEBUG) | |
for (int i = 0; i < result.length; i++) | |
debug(" new " + result[i].toString()); | |
return result; | |
} | |
private static void debug(String s) { | |
System.out.println("LaunchInfo: " + s); | |
} | |
synchronized public void flush() { | |
if (!isUpdateEnabled()) | |
return; | |
// flush the current state to disk. | |
checkpoint(); | |
// check to see if we need to perform cleanup sweep | |
uninstall(); | |
} | |
synchronized private VersionedIdentifier[] get(List list) { | |
VersionedIdentifier[] result = new VersionedIdentifier[list.size()]; | |
list.toArray(result); | |
return result; | |
} | |
synchronized private VersionedIdentifier[] get(List list1, List list2) { | |
ArrayList temp = new ArrayList(list1.size() + list2.size()); | |
temp.addAll(list1); | |
temp.addAll(list2); | |
VersionedIdentifier[] result = new VersionedIdentifier[temp.size()]; | |
temp.toArray(result); | |
return result; | |
} | |
public String getApplication() { | |
return app; | |
} | |
public String getApplicationConfiguration() { | |
return appconfig; | |
} | |
/** | |
* @see ILaunchInfo#getApplicationConfigurationIdentifier | |
*/ | |
public String getApplicationConfigurationIdentifier() { | |
String appCfig = getApplicationConfiguration(); | |
if (appCfig != null && appCfig.equals("")) | |
return null; | |
else | |
return appCfig; | |
} | |
public URL getBaseURL() { | |
return baseUrl; | |
} | |
/** | |
* @see ILaunchInfo#getComponentInstallURLFor | |
*/ | |
public URL getComponentInstallURLFor(String componentId) { | |
if (componentId == null || componentId.trim().equals("")) | |
throw new IllegalArgumentException(); | |
try { | |
return new URL(COMP_URL + componentId.trim() + "/"); | |
} catch (MalformedURLException e) { | |
throw new IllegalStateException(); | |
} | |
} | |
public VersionedIdentifier[] getComponents() { | |
return get(comps); | |
} | |
/** | |
* @see ILaunchInfo#getConfigurationInstallURLFor | |
*/ | |
public URL getConfigurationInstallURLFor(String configurationId) { | |
if (configurationId == null || configurationId.trim().equals("")) | |
throw new IllegalArgumentException(); | |
try { | |
return new URL(CONFIG_URL + configurationId.trim() + "/"); | |
} catch (MalformedURLException e) { | |
throw new IllegalStateException(); | |
} | |
} | |
public VersionedIdentifier[] getConfigurations() { | |
return get(configs); | |
} | |
public static LaunchInfo getCurrent() { | |
return profile; | |
} | |
public URL[] getFragmentPath() { | |
ArrayList path = new ArrayList(); | |
VersionedIdentifier vid; | |
// temporary code for handling 2.0 startup | |
if (r2_0) { | |
try { | |
path.add(new URL(baseUrl, FRAGMENTSDIR)); | |
// for compatibility w/ R1.0 fragments | |
} catch (MalformedURLException e) { | |
} | |
} else { | |
// include active | |
for (int i = 0; i < fragments.size(); i++) { | |
vid = (VersionedIdentifier) fragments.get(i); | |
try { | |
path.add(new URL(baseUrl, FRAGMENTSDIR + vid.toString() + "/" + FRAGMENTXML)); | |
} catch (MalformedURLException e) { | |
} | |
} | |
// include unmanaged | |
for (int i = 0; i < fragmentsUnmgd.size(); i++) { | |
vid = (VersionedIdentifier) fragmentsUnmgd.get(i); | |
try { | |
path.add(new URL(baseUrl, FRAGMENTSDIR + vid.toString() + "/" + FRAGMENTXML)); | |
} catch (MalformedURLException e) { | |
} | |
} | |
} | |
URL[] result = new URL[path.size()]; | |
path.toArray(result); | |
if (DEBUG) { | |
debug("Fragment-Path:"); | |
for (int i = 0; i < result.length; i++) { | |
debug(" " + result[i].toString()); | |
} | |
} | |
return result; | |
} | |
public VersionedIdentifier[] getFragments() { | |
return get(fragments, fragmentsUnmgd); | |
} | |
/** | |
* returns install profile history, sorted from oldest (least recent) | |
* to youngest (most recent). Typically the most recent entry is | |
* the current profile | |
*/ | |
private static History[] getHistory(URL url) { | |
if (url == null || !isFileProtocol(url)) | |
return new History[] { new History(url, (Date) null)}; | |
File dir = | |
(new File(url.getFile().replace('/', File.separatorChar))).getParentFile(); | |
String[] list = null; | |
if (dir != null) | |
list = dir.list(); | |
if (list == null) | |
return new History[] { new History(url, (Date) null)}; | |
Arrays.sort(list); | |
ArrayList result = new ArrayList(); | |
History current = null; | |
for (int i = 0; i < list.length; i++) { | |
if (list[i].startsWith(LAUNCH_PROFILE_NAME) | |
&& list[i].endsWith(LAUNCH_PROFILE_EXT)) { | |
String time = | |
list[i].substring( | |
LAUNCH_PROFILE_NAME.length(), | |
list[i].length() - LAUNCH_PROFILE_EXT.length() - 1); | |
Date date = null; | |
try { | |
if (time.length() > 0) { | |
time = time.substring(1); | |
date = new Date(Long.parseLong(time)); | |
} | |
URL newurl = new URL(url, list[i]); | |
if (time.length() > 0) | |
result.add(new History(newurl, date)); | |
else | |
current = new History(newurl, (Date) null); | |
} catch (MalformedURLException e) { | |
} catch (NumberFormatException e) { | |
} | |
} | |
} | |
if (current != null) | |
result.add(current); | |
History[] array = new History[result.size()]; | |
result.toArray(array); | |
return array; | |
} | |
public int getHistoryCount() { | |
return historyCount; | |
} | |
public String getIdentifier() { | |
if (r2_0) { | |
IPlatformConfiguration cfig = BootLoader.getCurrentPlatformConfiguration(); | |
if (cfig == null) | |
return ""; | |
else | |
return Long.toString(cfig.getPluginsChangeStamp()); | |
} | |
if (newHistory) | |
return nextId; | |
else | |
return id; | |
} | |
/** | |
* @see ILaunchInfo#getInstalledComponentIdentifiers | |
*/ | |
public String[] getInstalledComponentIdentifiers() { | |
VersionedIdentifier[] c = getComponents(); | |
String[] result = new String[c.length]; | |
for (int i = 0; i < c.length; i++) | |
result[i] = c[i].toString(); | |
return result; | |
} | |
/** | |
* @see ILaunchInfo#getInstalledConfigurationIdentifiers | |
*/ | |
public String[] getInstalledConfigurationIdentifiers() { | |
VersionedIdentifier[] c = getConfigurations(); | |
String[] result = new String[c.length]; | |
for (int i = 0; i < c.length; i++) | |
result[i] = c[i].toString(); | |
return result; | |
} | |
public History[] getLaunchInfoHistory() { | |
return getHistory(infoUrl); | |
} | |
public URL[] getPluginPath() { | |
ArrayList path = new ArrayList(); | |
VersionedIdentifier vid; | |
// temporary code for handling 2.0 startup | |
if (r2_0) { | |
IPlatformConfiguration r2_0_config = | |
BootLoader.getCurrentPlatformConfiguration(); | |
if (r2_0_config != null) { | |
URL[] r2_0_path = r2_0_config.getPluginPath(); | |
path.addAll(Arrays.asList(r2_0_path)); | |
} | |
} else { | |
// include active | |
for (int i = 0; i < plugins.size(); i++) { | |
vid = (VersionedIdentifier) plugins.get(i); | |
try { | |
path.add(new URL(baseUrl, PLUGINSDIR + vid.toString() + "/" + PLUGINXML)); | |
} catch (MalformedURLException e) { | |
} | |
} | |
// include unmanaged | |
for (int i = 0; i < pluginsUnmgd.size(); i++) { | |
vid = (VersionedIdentifier) pluginsUnmgd.get(i); | |
try { | |
path.add(new URL(baseUrl, PLUGINSDIR + vid.toString() + "/" + PLUGINXML)); | |
} catch (MalformedURLException e) { | |
} | |
} | |
} | |
URL[] result = new URL[path.size()]; | |
path.toArray(result); | |
if (DEBUG) { | |
debug("Plugin-Path:"); | |
for (int i = 0; i < result.length; i++) { | |
debug(" " + result[i].toString()); | |
} | |
} | |
return result; | |
} | |
public VersionedIdentifier[] getPlugins() { | |
return get(plugins, pluginsUnmgd); | |
} | |
public String getRuntime() { | |
return platform; | |
} | |
public Status[] getStatus() { | |
if (!hasStatus()) | |
return null; | |
else { | |
Status[] result = new Status[status.size()]; | |
status.toArray(result); | |
return result; | |
} | |
} | |
public boolean hasStatus() { | |
if (status == null || status.size() == 0) | |
return false; | |
else | |
return true; | |
} | |
public boolean installPending( | |
List confList, | |
List compList, | |
List pluginList, | |
List fragList) { | |
if (confList != null) | |
for (int i = 0; i < confList.size(); i++) { | |
if (!configsPendingDelete.contains(confList.get(i))) | |
configsPendingDelete.add(confList.get(i)); | |
} | |
if (compList != null) | |
for (int i = 0; i < compList.size(); i++) { | |
if (!compsPendingDelete.contains(compList.get(i))) | |
compsPendingDelete.add(compList.get(i)); | |
} | |
if (pluginList != null) | |
for (int i = 0; i < pluginList.size(); i++) { | |
if (!pluginsPendingDelete.contains(pluginList.get(i))) | |
pluginsPendingDelete.add(pluginList.get(i)); | |
} | |
if (fragList != null) | |
for (int i = 0; i < fragList.size(); i++) { | |
if (!fragmentsPendingDelete.contains(fragList.get(i))) | |
fragmentsPendingDelete.add(fragList.get(i)); | |
} | |
return checkpoint(); // harden state | |
} | |
public boolean installConfirmed( | |
List confList, | |
List compList, | |
List pluginList, | |
List fragList) { | |
if (confList != null) | |
for (int i = 0; i < confList.size(); i++) { | |
configsPendingDelete.remove(confList.get(i)); | |
} | |
if (compList != null) | |
for (int i = 0; i < compList.size(); i++) { | |
compsPendingDelete.remove(compList.get(i)); | |
} | |
if (pluginList != null) | |
for (int i = 0; i < pluginList.size(); i++) { | |
pluginsPendingDelete.remove(pluginList.get(i)); | |
} | |
if (fragList != null) | |
for (int i = 0; i < fragList.size(); i++) { | |
fragmentsPendingDelete.remove(fragList.get(i)); | |
} | |
return checkpoint(); // harden state | |
} | |
public boolean isDanglingComponent(VersionedIdentifier component) { | |
return compsDang.contains(component); | |
} | |
public void isDanglingComponent( | |
VersionedIdentifier component, | |
boolean isDangling) { | |
if (!comps.contains(component)) { | |
if (!compsInact.contains(component)) | |
return; | |
} | |
if (isDangling) { | |
// check to see if we have to add as dangling | |
if (compsDang.contains(component)) | |
return; | |
compsDang.add(component); | |
} else { | |
// check to see if we have to remove as dangling | |
compsDang.remove(component); | |
} | |
} | |
public boolean isDominantConfiguration(String config) { | |
if (config == null || config.trim().equals("")) | |
return false; | |
if (this.appconfig == null || this.appconfig.equals(DEFAULT_APP_CONFIG)) | |
return false; | |
VersionedIdentifier argId = new VersionedIdentifier(config); | |
VersionedIdentifier appId = new VersionedIdentifier(this.appconfig); | |
return argId.getIdentifier().equals(appId.getIdentifier()); | |
} | |
public boolean isPlatformComponent(String comp) { | |
if (comp == null || comp.trim().equals("")) | |
return false; | |
VersionedIdentifier vid = new VersionedIdentifier(comp); | |
return vid.getIdentifier().equals(PLATFORM_COMPONENT_ID); | |
} | |
public boolean isRuntimePlugin(String plugin) { | |
if (plugin == null || plugin.trim().equals("")) | |
return false; | |
VersionedIdentifier vid = new VersionedIdentifier(plugin); | |
return vid.getIdentifier().equals(BOOT_PLUGIN_ID); | |
} | |
private static boolean isFileProtocol(URL u) { | |
return URL_FILE.equals(u.getProtocol()) || URL_VA.equals(u.getProtocol()); | |
} | |
public boolean isUpdateEnabled() { | |
return isUpdateEnabled; | |
} | |
private ArrayList loadListProperty(Properties props, String name) { | |
return loadListProperty(props, name, VersionedIdentifier.class); | |
} | |
private ArrayList loadListProperty(Properties props, String name, Class type) { | |
ArrayList list = new ArrayList(); | |
String value = (String) props.get(name + ".0"); | |
for (int i = 1; value != null; i++) { | |
loadListPropertyEntry(list, value, type); | |
value = (String) props.get(name + "." + i); | |
} | |
return list; | |
} | |
private void loadListPropertyEntry(List list, String value, Class type) { | |
if (value == null) | |
return; | |
StringTokenizer tokens = new StringTokenizer(value, ","); | |
String token; | |
Object o; | |
while (tokens.hasMoreTokens()) { | |
token = tokens.nextToken().trim(); | |
if (!token.equals("")) { | |
try { | |
if (type.equals(VersionedIdentifier.class)) | |
o = new VersionedIdentifier(token); | |
else | |
o = token; | |
list.add(o); | |
} catch (Exception e) { /* skip bad entry */ | |
} | |
} | |
} | |
return; | |
} | |
private HashMap loadMapProperty(Properties props, String name) { | |
HashMap map = new HashMap(); | |
Iterator i = props.keySet().iterator(); | |
while (i.hasNext()) { | |
String key = (String) i.next(); | |
if (key.startsWith(name)) { | |
String id = key.substring(name.length() + 1); | |
ArrayList mapIds = new ArrayList(); | |
loadListPropertyEntry( | |
mapIds, | |
props.getProperty(key), | |
VersionedIdentifier.class); | |
map.put(id, mapIds); | |
} | |
} | |
return map; | |
} | |
private void loadProperties(Properties props) { | |
id = props.getProperty(ID, ""); | |
if (id.trim().equals("")) | |
id = Long.toString((new java.util.Date()).getTime()); | |
platform = props.getProperty(PLATFORM, DEFAULT_PLATFORM); | |
app = props.getProperty(APP, DEFAULT_APP); | |
if (app.trim().equals("")) | |
app = DEFAULT_APP; | |
appconfig = props.getProperty(APP_CONFIG, DEFAULT_APP_CONFIG); | |
String count = props.getProperty(HISTORY_COUNT, ""); | |
try { | |
historyCount = (new Integer(count)).intValue(); | |
} catch (Exception e) { | |
historyCount = DEFAULT_HISTORY_COUNT; | |
} | |
historyPendingDelete = loadListProperty(props, HISTORY_PENDDEL, String.class); | |
configs = loadListProperty(props, CONFIG_ACT); | |
configsInact = loadListProperty(props, CONFIG_INACT); | |
configsPendingDelete = loadListProperty(props, CONFIG_PENDDEL); | |
comps = loadListProperty(props, COMP_ACT); | |
compsInact = loadListProperty(props, COMP_INACT); | |
compsDang = loadListProperty(props, COMP_DANG); | |
compsPendingDelete = loadListProperty(props, COMP_PENDDEL); | |
plugins = loadListProperty(props, PLUGIN_ACT); | |
pluginsInact = loadListProperty(props, PLUGIN_INACT); | |
pluginsUnmgd = new ArrayList(); | |
pluginsPendingDelete = loadListProperty(props, PLUGIN_PENDDEL); | |
pluginsOld = loadListProperty(props, PLUGIN_UNMGD); | |
pluginsOld.addAll(plugins); | |
fragments = loadListProperty(props, FRAG_ACT); | |
fragmentsInact = loadListProperty(props, FRAG_INACT); | |
fragmentsUnmgd = new ArrayList(); | |
fragmentsPendingDelete = loadListProperty(props, FRAG_PENDDEL); | |
fragmentsOld = loadListProperty(props, FRAG_UNMGD); | |
fragmentsOld.addAll(fragments); | |
infoMap = loadMapProperty(props, INFO); | |
} | |
private static String[] processCommandLine(String[] args) throws Exception { | |
for (int i = 0; i < args.length; i++) { | |
if (args[i].equalsIgnoreCase(DEBUGFLAG)) { | |
DEBUG = true; | |
break; | |
} | |
} | |
return args; | |
} | |
private void processInfoChanges(File dir, String[] list) { | |
// look for new info entries or changes to existing ones | |
if (list == null) | |
return; | |
List infoKeys; | |
Iterator info; | |
for (int i = 0; i < list.length; i++) { | |
infoKeys = new ArrayList(infoMap.keySet()); | |
info = infoKeys.iterator(); | |
boolean found = false; | |
while (info.hasNext() && !found) { | |
String key = (String) info.next(); | |
if (key.startsWith(list[i])) { | |
processInfoChangesExisting(dir, list[i], key); | |
found = true; | |
} | |
} | |
if (!found) { | |
processInfoChangesNew(dir, list[i]); | |
} | |
} | |
// look for deletions and trigger uninstall processing | |
List current = Arrays.asList(list); | |
ArrayList seen = new ArrayList(); | |
infoKeys = new ArrayList(infoMap.keySet()); | |
info = infoKeys.iterator(); | |
String keyLong; | |
String key; | |
int ix; | |
while (info.hasNext()) { | |
key = (String) info.next(); | |
ix = key.lastIndexOf("."); | |
keyLong = ix == -1 ? key : key.substring(0, ix); | |
ix = keyLong.lastIndexOf("."); | |
key = ix == -1 ? keyLong : keyLong.substring(0, ix); | |
if (seen.contains(key)) | |
continue; | |
else | |
seen.add(key); | |
if (!current.contains(key)) | |
processInfoChangesDelete(dir, key, keyLong); | |
} | |
} | |
private void processInfoChangesDelete(File dir, String info, String key) { | |
if (DEBUG) | |
debug(" deleted " + info); | |
// determine old info settings | |
ArrayList oldCfgIds = (ArrayList) infoMap.get(key + "." + INFO_CONFIGS); | |
ArrayList oldCmpIds = (ArrayList) infoMap.get(key + "." + INFO_COMPS); | |
if (oldCfgIds == null) | |
oldCfgIds = new ArrayList(); | |
if (oldCmpIds == null) | |
oldCmpIds = new ArrayList(); | |
VersionedIdentifier[] uninstallCfg = new VersionedIdentifier[oldCfgIds.size()]; | |
oldCfgIds.toArray(uninstallCfg); | |
VersionedIdentifier[] uninstallCmp = new VersionedIdentifier[oldCmpIds.size()]; | |
oldCmpIds.toArray(uninstallCmp); | |
// update state | |
infoMap.remove(key + "." + INFO_CONFIGS); | |
infoMap.remove(key + "." + INFO_COMPS); | |
BootUpdateManager.uninstall(uninstallCfg, uninstallCmp); | |
} | |
private void processInfoChangesExisting(File dir, String info, String key) { | |
File f = new File(dir, info); | |
if (!f.exists()) | |
return; | |
String stamp = Long.toString(f.lastModified()); | |
if (key.startsWith(info + "." + stamp)) | |
return; | |
if (DEBUG) | |
debug(" changed " + info); | |
// determine new info setting | |
Properties props = processInfoChangesProperties(f); | |
ArrayList newCfgIds = new ArrayList(); | |
loadListPropertyEntry( | |
newCfgIds, | |
props.getProperty(INFO_CONFIGS), | |
VersionedIdentifier.class); | |
ArrayList newCmpIds = new ArrayList(); | |
loadListPropertyEntry( | |
newCmpIds, | |
props.getProperty(INFO_COMPS), | |
VersionedIdentifier.class); | |
// determine old info settings | |
int ix = key.lastIndexOf("."); | |
String oldPrefix = ix == -1 ? key : key.substring(0, ix + 1); | |
ArrayList oldCfgIds = (ArrayList) infoMap.get(oldPrefix + INFO_CONFIGS); | |
ArrayList oldCmpIds = (ArrayList) infoMap.get(oldPrefix + INFO_COMPS); | |
// compute delta | |
if (oldCfgIds == null) | |
oldCfgIds = new ArrayList(); | |
else | |
oldCfgIds.removeAll(newCfgIds); | |
if (oldCfgIds == null) | |
oldCmpIds = new ArrayList(); | |
else | |
oldCmpIds.removeAll(newCmpIds); | |
VersionedIdentifier[] uninstallCfg = new VersionedIdentifier[oldCfgIds.size()]; | |
oldCfgIds.toArray(uninstallCfg); | |
VersionedIdentifier[] uninstallCmp = new VersionedIdentifier[oldCmpIds.size()]; | |
oldCmpIds.toArray(uninstallCmp); | |
// update state | |
infoMap.remove(oldPrefix + INFO_CONFIGS); | |
infoMap.remove(oldPrefix + INFO_COMPS); | |
infoMap.put(info + "." + stamp + "." + INFO_CONFIGS, newCfgIds); | |
infoMap.put(info + "." + stamp + "." + INFO_COMPS, newCmpIds); | |
BootUpdateManager.uninstall(uninstallCfg, uninstallCmp); | |
} | |
private void processInfoChangesNew(File dir, String info) { | |
if (DEBUG) | |
debug(" new " + info); | |
File f = new File(dir, info); | |
if (!f.exists()) | |
return; | |
Properties props = processInfoChangesProperties(f); | |
ArrayList cfgIds = new ArrayList(); | |
loadListPropertyEntry( | |
cfgIds, | |
props.getProperty(INFO_CONFIGS), | |
VersionedIdentifier.class); | |
ArrayList cmpIds = new ArrayList(); | |
loadListPropertyEntry( | |
cmpIds, | |
props.getProperty(INFO_COMPS), | |
VersionedIdentifier.class); | |
String stamp = Long.toString(f.lastModified()); | |
String key = info + "." + stamp + "." + INFO_CONFIGS; | |
infoMap.put(key, cfgIds); | |
key = info + "." + stamp + "." + INFO_COMPS; | |
infoMap.put(key, cmpIds); | |
} | |
private Properties processInfoChangesProperties(File f) { | |
FileInputStream is; | |
Properties props = new Properties(); | |
if (f.exists()) { | |
is = null; | |
try { | |
is = new FileInputStream(f); | |
props = new Properties(); | |
props.load(is); | |
} catch (IOException e) { | |
} finally { | |
if (is != null) { | |
try { | |
is.close(); | |
} catch (IOException e) { | |
} | |
} | |
} | |
} | |
return props; | |
} | |
synchronized private void remove( | |
VersionedIdentifier id, | |
List active, | |
List inactive) { | |
if (active.contains(id)) { | |
setNewHistory(); | |
active.remove(id); | |
if (!inactive.contains(id)) | |
inactive.add(id); | |
} | |
} | |
public void removeComponent(VersionedIdentifier component) { | |
remove(component, comps, compsInact); | |
} | |
public void removeConfiguration(VersionedIdentifier configuration) { | |
remove(configuration, configs, configsInact); | |
} | |
public void removeFragment(VersionedIdentifier fragment) { | |
remove(fragment, fragments, fragmentsInact); | |
} | |
public void removePlugin(VersionedIdentifier plugin) { | |
remove(plugin, plugins, pluginsInact); | |
} | |
private static LaunchInfo restoreProfile(URL base) { | |
//make sure we come up using the most accurate information possible | |
LaunchInfo li; | |
// check for install.properties | |
Properties props = new Properties(); | |
InputStream is = null; | |
try { | |
URL summary = new URL(base, INSTALLDIR + LAUNCH_SUMMARY); | |
is = summary.openStream(); | |
props.load(is); | |
} catch (IOException e) { | |
} finally { | |
if (is != null) { | |
try { | |
is.close(); | |
} catch (IOException e) { | |
} | |
} | |
} | |
// check for improper shutdown | |
URL info; | |
try { | |
info = new URL(base, INSTALLDIR + LAUNCH_PROFILE_CHKPT); | |
li = new LaunchInfo(info, base); | |
if (DEBUG) | |
debug("Using temporary profile " + info.toString()); | |
return restoreProfileSummary(li, props); | |
} catch (IOException e) { | |
} | |
// look for profile from last shutdown ... this is the normal case | |
try { | |
info = new URL(base, INSTALLDIR + LAUNCH_PROFILE); | |
li = new LaunchInfo(info, base); | |
if (DEBUG) | |
debug("Using saved profile " + info.toString()); | |
return restoreProfileSummary(li, props); | |
} catch (IOException e) { | |
} | |
// check for backup copy | |
try { | |
info = new URL(base, INSTALLDIR + LAUNCH_PROFILE_BAK); | |
li = new LaunchInfo(info, base); | |
if (DEBUG) | |
debug("Using backup profile " + info.toString()); | |
return restoreProfileSummary(li, props); | |
} catch (IOException e) { | |
} | |
// try history (newest to oldest) | |
try { | |
info = new URL(base, INSTALLDIR + LAUNCH_PROFILE); | |
History[] history = getHistory(info); | |
for (int i = history.length - 1; i >= 0; i--) { | |
try { | |
li = new LaunchInfo(history[i].getLaunchInfoURL(), base); | |
li.setNewId(); | |
if (DEBUG) | |
debug("Using history profile " + history[i].getIdentifier()); | |
return restoreProfileSummary(li, props); | |
} catch (IOException e) { | |
} | |
} | |
} catch (MalformedURLException e) { | |
} | |
// we struck out ... come up with default | |
li = new LaunchInfo(); | |
try { | |
li.baseUrl = base; | |
li.infoUrl = new URL(base, INSTALLDIR + LAUNCH_PROFILE); | |
if (DEBUG) | |
debug("Creating new profile " + li.infoUrl.toString()); | |
} catch (MalformedURLException e) { | |
if (DEBUG) | |
debug("Using default profile"); | |
} | |
return restoreProfileSummary(li, props); | |
} | |
private static LaunchInfo restoreProfileSummary( | |
LaunchInfo info, | |
Properties summary) { | |
String runtime = summary.getProperty(PLATFORM); | |
String config = summary.getProperty(APP_CONFIG); | |
String app = summary.getProperty(APP); | |
if (runtime != null && !runtime.trim().equals("")) | |
info.platform = runtime.trim(); | |
if (config != null && !config.trim().equals("")) | |
info.appconfig = config.trim(); | |
if (app != null && !app.trim().equals("")) | |
info.app = app.trim(); | |
return info; | |
} | |
private void resetInfoURL() { | |
try { | |
URL info = new URL(baseUrl, INSTALLDIR + LAUNCH_PROFILE); | |
infoUrl = info; | |
} catch (MalformedURLException e) { | |
// if we can't construct a good URL leave things asis | |
} | |
} | |
synchronized public void revertTo(History history) { | |
if (history == null) | |
return; | |
if (!isUpdateEnabled()) | |
return; | |
// poof up launch info for specified history; | |
LaunchInfo old = null; | |
try { | |
old = new LaunchInfo(history); | |
} catch (IOException e) { | |
return; | |
} | |
ArrayList newConfigsInact = | |
revertToInactive(configs, configsInact, old.configs, old.configsInact); | |
ArrayList newCompsInact = | |
revertToInactive(comps, compsInact, old.comps, old.compsInact); | |
ArrayList newPluginsInact = | |
revertToInactive(plugins, pluginsInact, old.plugins, old.pluginsInact); | |
ArrayList newFragmentsInact = | |
revertToInactive(fragments, fragmentsInact, old.fragments, old.fragmentsInact); | |
// update current state | |
setNewHistory(); | |
platform = old.platform; | |
app = old.app; | |
appconfig = old.appconfig; | |
// historyCount = historyCount; // keep current historyCount | |
configs = old.configs; | |
configsInact = newConfigsInact; | |
comps = old.comps; | |
compsInact = newCompsInact; | |
// compsDang = compsDang; // keep compsDang from current | |
plugins = old.plugins; | |
pluginsInact = newPluginsInact; | |
// pluginsUnmgd = pluginsUnmgd; // keep pluginsUnmgd from current | |
fragments = old.fragments; | |
fragmentsInact = newFragmentsInact; | |
// fragmentsUnmgd = fragmentsUnmgd; // keep fragmentsUnmgd from current | |
} | |
private ArrayList revertToInactive( | |
List curAct, | |
List curInact, | |
List oldAct, | |
List oldInact) { | |
// start with old inactive list | |
ArrayList inactive = new ArrayList(oldInact); // clone | |
VersionedIdentifier vid; | |
// add current inactive | |
for (int i = 0; i < curInact.size(); i++) { | |
vid = (VersionedIdentifier) curInact.get(i); | |
if (!inactive.contains(vid)) | |
inactive.add(vid); | |
} | |
// add current active that are not active in new state | |
for (int i = 0; i < curAct.size(); i++) { | |
vid = (VersionedIdentifier) curAct.get(i); | |
if (!oldAct.contains(vid)) { | |
if (!inactive.contains(vid)) | |
inactive.add(vid); | |
} | |
} | |
// remove all that are active in new state | |
for (int i = 0; i < oldAct.size(); i++) { | |
vid = (VersionedIdentifier) oldAct.get(i); | |
inactive.remove(vid); | |
} | |
return inactive; | |
} | |
public static Object run( | |
String flag, | |
String value, | |
String location, | |
String[] args) | |
throws Exception { | |
processCommandLine(args); | |
if (DEBUG) { | |
debug(flag + " " + value); | |
} | |
if (flag.equalsIgnoreCase(UNINSTALLFLAG)) { | |
URL cookie = null; | |
URL base = InternalBootLoader.getInstallURL(); | |
try { | |
cookie = new URL("file", null, 0, value); | |
} catch (MalformedURLException e) { | |
return null; | |
} | |
startup(base); | |
BootUpdateManager.uninstall(cookie); | |
} | |
return null; | |
} | |
synchronized private void set( | |
VersionedIdentifier id, | |
List active, | |
List inactive) { | |
// common "set" processing used for components and configurations | |
if (id == null) | |
return; | |
for (int i = 0; i < active.size(); i++) { | |
VersionedIdentifier vid = (VersionedIdentifier) active.get(i); | |
if (vid.getIdentifier().equals(id.getIdentifier())) { | |
if (vid.getVersion().equals(id.getVersion())) | |
return; // same identifier already active ... do nothing | |
active.remove(i); // different version found ... replace it | |
active.add(i, id); | |
if (!inactive.contains(vid)) | |
inactive.add(vid); | |
inactive.remove(id); | |
setNewHistory(); | |
return; | |
} | |
} | |
active.add(id); // did not exist ... add it | |
inactive.remove(id); // remove from inactive (if existed) | |
setNewHistory(); | |
} | |
synchronized private void set( | |
VersionedIdentifier id, | |
List active, | |
List inactive, | |
List unmanaged) { | |
// common "set" processing used for plugins and fragments | |
if (!active.contains(id)) { | |
active.add(id); // plugins and fragments can have multiple active versions | |
inactive.remove(id); | |
unmanaged.remove(id); | |
setNewHistory(); | |
} | |
} | |
public void setApplication(String app) { | |
if (this.app != null && !this.app.equals(app)) { | |
setNewHistory(); | |
if (app != null && !app.equals("")) | |
this.app = app; | |
else | |
this.app = DEFAULT_APP; | |
} | |
} | |
private void setDominantConfiguration(String appconfig) { | |
if (this.appconfig != null && !this.appconfig.equals(appconfig)) { | |
setNewHistory(); | |
if (appconfig != null && !appconfig.equals("")) | |
this.appconfig = appconfig; | |
else | |
this.appconfig = DEFAULT_APP_CONFIG; | |
} | |
} | |
public void setComponent(VersionedIdentifier component) { | |
set(component, comps, compsInact); | |
} | |
public void setConfiguration(VersionedIdentifier config, String application) { | |
set(config, configs, configsInact); | |
if (this.appconfig.equals(DEFAULT_APP_CONFIG) | |
|| isDominantConfiguration(config.toString())) { | |
setDominantConfiguration(config.toString()); | |
setApplication(application); | |
} | |
} | |
/* | |
* called after any new configs, components and plugins are processed | |
*/ | |
private void setDefaultRuntime() { | |
boolean found = false; | |
if (getRuntime().equals(DEFAULT_PLATFORM)) { | |
VersionedIdentifier vid; | |
// check active list for runtime | |
for (int i = 0; i < plugins.size(); i++) { | |
vid = (VersionedIdentifier) plugins.get(i); | |
if (isRuntimePlugin(vid.getIdentifier())) { | |
setRuntime(vid); | |
found = true; | |
break; | |
} | |
} | |
if (!found) { | |
// check unmanaged list for runtime | |
for (int i = 0; i < pluginsUnmgd.size(); i++) { | |
vid = (VersionedIdentifier) pluginsUnmgd.get(i); | |
if (isRuntimePlugin(vid.getIdentifier())) { | |
setRuntime(vid); | |
found = true; | |
break; | |
} | |
} | |
} | |
} | |
} | |
private void setDefaults() { | |
setNewId(); | |
platform = DEFAULT_PLATFORM; | |
app = DEFAULT_APP; | |
appconfig = DEFAULT_APP_CONFIG; | |
historyCount = DEFAULT_HISTORY_COUNT; | |
historyPendingDelete = new ArrayList(); | |
configs = new ArrayList(); | |
configsInact = new ArrayList(); | |
configsPendingDelete = new ArrayList(); | |
comps = new ArrayList(); | |
compsInact = new ArrayList(); | |
compsDang = new ArrayList(); | |
compsPendingDelete = new ArrayList(); | |
plugins = new ArrayList(); | |
pluginsInact = new ArrayList(); | |
pluginsUnmgd = new ArrayList(); | |
pluginsPendingDelete = new ArrayList(); | |
pluginsOld = new ArrayList(); | |
fragments = new ArrayList(); | |
fragmentsInact = new ArrayList(); | |
fragmentsUnmgd = new ArrayList(); | |
fragmentsPendingDelete = new ArrayList(); | |
fragmentsOld = new ArrayList(); | |
infoMap = new HashMap(); | |
} | |
public void setFragment(VersionedIdentifier fragment) { | |
set(fragment, fragments, fragmentsInact, fragmentsUnmgd); | |
} | |
public void setHistoryCount(int count) { | |
if (count <= 0) | |
historyCount = DEFAULT_HISTORY_COUNT; | |
else | |
historyCount = count; | |
} | |
/** | |
* This method is called by the BootUpdateManager with a list of | |
* configurations, components, plugins and fragments that could not | |
* be installed (after they were discovered). If these items | |
* do not exist on an active or inactive list already, they are added | |
* to the corresponding inactive list. | |
*/ | |
synchronized public void setInactive( | |
VersionedIdentifier[] configId, | |
VersionedIdentifier[] compId, | |
VersionedIdentifier[] pluginId, | |
VersionedIdentifier[] fragId) { | |
VersionedIdentifier vid; | |
for (int i = 0; configId != null && i < configId.length; i++) { | |
vid = (VersionedIdentifier) configId[i]; | |
configs.remove(vid); | |
if (!configsInact.contains(vid)) | |
configsInact.add(vid); | |
} | |
for (int i = 0; compId != null && i < compId.length; i++) { | |
vid = (VersionedIdentifier) compId[i]; | |
comps.remove(vid); | |
if (!compsInact.contains(vid)) | |
compsInact.add(vid); | |
} | |
for (int i = 0; pluginId != null && i < pluginId.length; i++) { | |
vid = (VersionedIdentifier) pluginId[i]; | |
plugins.remove(vid); | |
if (!pluginsInact.contains(vid)) | |
pluginsInact.add(vid); | |
} | |
for (int i = 0; fragId != null && i < fragId.length; i++) { | |
vid = (VersionedIdentifier) fragId[i]; | |
fragments.remove(vid); | |
if (!fragmentsInact.contains(vid)) | |
fragmentsInact.add(vid); | |
} | |
} | |
private void setNewHistory() { | |
if (newHistory) | |
return; | |
nextId = Long.toString((new java.util.Date()).getTime()); | |
newHistory = true; | |
} | |
private void setNewId() { | |
id = Long.toString((new java.util.Date()).getTime()); | |
} | |
public void setPlugin(VersionedIdentifier plugin) { | |
set(plugin, plugins, pluginsInact, pluginsUnmgd); | |
} | |
public void setRuntime(VersionedIdentifier runtime) { | |
String platform = runtime == null ? null : runtime.toString(); | |
if (this.platform != null && !this.platform.equals(platform)) { | |
setNewHistory(); | |
if (platform != null && !platform.equals("")) | |
this.platform = platform; | |
else | |
this.platform = DEFAULT_PLATFORM; | |
} | |
} | |
static void shutdown() { | |
if (profile == null) | |
return; | |
try { | |
profile.store(); | |
} catch (IOException e) { | |
// was not able to save updated install profile | |
} finally { | |
if (profile.uos != null) { | |
try { | |
profile.uos.close(); | |
} catch (IOException e) { | |
} | |
} | |
if (profile.uf != null) | |
profile.uf.delete(); | |
} | |
} | |
static void startup(URL base) { | |
if (profile == null) { | |
// restore profile | |
LaunchInfo newProfile = restoreProfile(base); | |
newProfile.resetInfoURL(); | |
// make sure info url is always == active profile (however restored) | |
profile = newProfile; | |
// check if update will be enabled | |
profile.checkUpdateEnabled(); | |
if (DEBUG) | |
debug("Update mode " + (profile.isUpdateEnabled() ? "enabled" : "disabled")); | |
// clean up any pending deletes | |
if (profile.isUpdateEnabled()) | |
profile.uninstallPendingDelete(); | |
// detect changes from last startup | |
String path; | |
File dir; | |
String[] list; | |
String[] bakList; | |
FilenameFilter dirfilter = new FilenameFilter() { | |
public boolean accept(File dir, String name) { | |
return (new File(dir, name)).isDirectory(); | |
} | |
}; | |
FilenameFilter infofilter = new FilenameFilter() { | |
public boolean accept(File dir, String name) { | |
return (new File(dir, name)).isFile() && name.endsWith(INFO_EXTENSION); | |
} | |
}; | |
// look for changes in install/info/ | |
path = (base.getFile() + INSTALLINFODIR).replace('/', File.separatorChar); | |
dir = new File(path); | |
list = dir.list(infofilter); | |
if (DEBUG) | |
debug("Detecting install-info changes"); | |
profile.processInfoChanges(dir, list); | |
// look for configurations | |
path = | |
(base.getFile() + INSTALLDIR + CONFIGSDIR).replace('/', File.separatorChar); | |
dir = new File(path); | |
list = dir.list(dirfilter); | |
if (DEBUG) | |
debug("Detecting configuration changes"); | |
profile.synchConfigurations(list); | |
VersionedIdentifier[] configDelta; | |
if (list == null) | |
configDelta = new VersionedIdentifier[0]; | |
else { | |
configDelta = | |
profile.computeDelta( | |
list, | |
profile.configs, | |
profile.configsInact, | |
profile.configsPendingDelete); | |
} | |
// look for components | |
path = | |
(base.getFile() + INSTALLDIR + COMPSDIR).replace('/', File.separatorChar); | |
dir = new File(path); | |
list = dir.list(dirfilter); | |
if (DEBUG) | |
debug("Detecting component changes"); | |
profile.synchComponents(list); | |
VersionedIdentifier[] compDelta; | |
if (list == null) | |
compDelta = new VersionedIdentifier[0]; | |
else { | |
compDelta = | |
profile.computeDelta( | |
list, | |
profile.comps, | |
profile.compsInact, | |
profile.compsPendingDelete); | |
} | |
// complete "installation" of new configurations and components | |
if (configDelta.length > 0 || compDelta.length > 0) | |
profile.addStatus(BootUpdateManager.install(configDelta, compDelta)); | |
// look for plugins | |
path = (base.getFile() + PLUGINSDIR).replace('/', File.separatorChar); | |
dir = new File(path); | |
list = dir.list(dirfilter); | |
if (DEBUG) | |
debug("Detecting plugin changes"); | |
profile.synchPlugins(list); | |
VersionedIdentifier[] pluginDelta; | |
if (list == null) | |
pluginDelta = new VersionedIdentifier[0]; | |
else { | |
pluginDelta = | |
profile.computeDelta( | |
list, | |
profile.plugins, | |
profile.pluginsInact, | |
profile.pluginsPendingDelete); | |
} | |
for (int i = 0; i < pluginDelta.length; i++) | |
profile.pluginsUnmgd.add(pluginDelta[i]); | |
// look for fragments | |
path = (base.getFile() + FRAGMENTSDIR).replace('/', File.separatorChar); | |
dir = new File(path); | |
list = dir.list(dirfilter); | |
if (DEBUG) | |
debug("Detecting fragment changes"); | |
profile.synchFragments(list); | |
VersionedIdentifier[] fragmentDelta; | |
if (list == null) | |
fragmentDelta = new VersionedIdentifier[0]; | |
else { | |
fragmentDelta = | |
profile.computeDelta( | |
list, | |
profile.fragments, | |
profile.fragmentsInact, | |
profile.fragmentsPendingDelete); | |
} | |
for (int i = 0; i < fragmentDelta.length; i++) | |
profile.fragmentsUnmgd.add(fragmentDelta[i]); | |
// check to see if runtime is set | |
if (profile.getRuntime().equals(DEFAULT_PLATFORM)) { | |
profile.setDefaultRuntime(); | |
} | |
// try to see if we need to do cleanup pass | |
if (profile.isUpdateEnabled()) { | |
if (profile.newHistory) | |
profile.checkpoint(); | |
profile.uninstall(); | |
} | |
// check if runtime path has changed | |
profile.checkRuntimePath(); | |
if (DEBUG && profile.hasStatus()) { | |
Status[] status = profile.getStatus(); | |
for (int i = 0; i < status.length; i++) { | |
debug(status[i].getMessage()); | |
} | |
} | |
} | |
} | |
synchronized private void store() throws IOException { | |
if (!isUpdateEnabled()) | |
return; | |
File active = new File(infoUrl.getFile().replace('/', File.separatorChar)); | |
File dir = active.getParentFile(); | |
if (dir == null) | |
return; // cannot save | |
dir.mkdirs(); | |
File chkpt = new File(dir, LAUNCH_PROFILE_CHKPT); | |
File bak = new File(dir, LAUNCH_PROFILE_BAK); | |
// write out temp state | |
if (!checkpoint()) | |
return; | |
// check to see if we need to clone history | |
if (newHistory) { | |
if (active.exists()) { | |
// active may not exists if we recovered from temp or history | |
String suffix = this.id; // id for history | |
File history = | |
new File(dir, LAUNCH_PROFILE_NAME + "_" + suffix + "." + LAUNCH_PROFILE_EXT); | |
if (history.exists()) { | |
if (!history.delete()) | |
return; | |
} | |
if (!active.renameTo(history)) | |
return; | |
} | |
} else { | |
if (bak.exists()) { | |
if (!bak.delete()) | |
return; | |
} | |
if (active.exists()) { | |
// active may not exists if we recovered from temp or history | |
if (!active.renameTo(bak)) | |
return; | |
} | |
} | |
// activate new state | |
if (!chkpt.renameTo(active)) | |
return; | |
bak.delete(); | |
// write out launcher summary | |
File summary = new File(dir, LAUNCH_SUMMARY); | |
PrintWriter sum = null; | |
try { | |
// write summary | |
sum = new PrintWriter(new FileOutputStream(summary)); | |
writeSummary(getIdentifier(), sum); | |
} finally { | |
if (sum != null) | |
sum.close(); | |
} | |
} | |
private synchronized void synch(List dirlist, List infoList) { | |
// remove state entries that do not exist in file system | |
List list = new ArrayList(infoList); // clone list | |
for (int i = 0; i < list.size(); i++) { | |
VersionedIdentifier vid = (VersionedIdentifier) list.get(i); | |
if (!dirlist.contains(vid.toString())) { | |
infoList.remove(vid); | |
if (DEBUG) | |
debug(" missing " + vid.toString()); | |
} | |
} | |
} | |
private void synchComponents(String[] dirlist) { | |
List list = Arrays.asList(dirlist != null ? dirlist : new String[0]); | |
synch(list, comps); | |
synch(list, compsInact); | |
synch(list, compsDang); | |
synch(list, compsPendingDelete); | |
} | |
private void synchConfigurations(String[] dirlist) { | |
List list = Arrays.asList(dirlist != null ? dirlist : new String[0]); | |
synch(list, configs); | |
synch(list, configsInact); | |
synch(list, configsPendingDelete); | |
} | |
private void synchFragments(String[] dirlist) { | |
List list = Arrays.asList(dirlist != null ? dirlist : new String[0]); | |
synch(list, fragments); | |
synch(list, fragmentsInact); | |
synch(list, fragmentsUnmgd); | |
synch(list, fragmentsPendingDelete); | |
} | |
private void synchPlugins(String[] dirlist) { | |
List list = Arrays.asList(dirlist != null ? dirlist : new String[0]); | |
synch(list, plugins); | |
synch(list, pluginsInact); | |
synch(list, pluginsUnmgd); | |
synch(list, pluginsPendingDelete); | |
} | |
synchronized public void uninstall() { | |
// do history-based deletion sweep | |
if (!isUpdateEnabled()) | |
return; | |
History[] history = getLaunchInfoHistory(); | |
if (history.length <= (historyCount + 1)) | |
return; | |
// poof up launch info objects | |
LaunchInfo[] historyInfo = new LaunchInfo[history.length]; | |
for (int i = 0; i < history.length; i++) { | |
if (history[i].isCurrent()) | |
historyInfo[i] = LaunchInfo.getCurrent(); | |
else { | |
try { | |
historyInfo[i] = new LaunchInfo(history[i]); | |
} catch (IOException e) { | |
historyInfo[i] = new LaunchInfo(); | |
} | |
} | |
} | |
// determine list of deletion candidates | |
List candidateConfigs = new ArrayList(); | |
List candidateComps = new ArrayList(); | |
List candidatePlugins = new ArrayList(); | |
List candidateFragments = new ArrayList(); | |
for (int i = 0; i < (history.length - (historyCount + 1)); i++) { | |
uninstallGetCandidates( | |
candidateConfigs, | |
historyInfo[i].configs, | |
historyInfo[i].configsInact); | |
uninstallGetCandidates( | |
candidateComps, | |
historyInfo[i].comps, | |
historyInfo[i].compsInact); | |
uninstallGetCandidates( | |
candidatePlugins, | |
historyInfo[i].plugins, | |
historyInfo[i].pluginsInact); | |
uninstallGetCandidates( | |
candidateFragments, | |
historyInfo[i].fragments, | |
historyInfo[i].fragmentsInact); | |
} | |
// determine which candidates are not active in recent histories | |
List deleteConfigs = | |
uninstallMarkForDeletion(candidateConfigs, historyInfo, new ListSelector() { | |
public List get(LaunchInfo i) { | |
return i.configs; | |
} | |
}); | |
List deleteComps = | |
uninstallMarkForDeletion(candidateComps, historyInfo, new ListSelector() { | |
public List get(LaunchInfo i) { | |
return i.comps; | |
} | |
}); | |
List deletePlugins = | |
uninstallMarkForDeletion(candidatePlugins, historyInfo, new ListSelector() { | |
public List get(LaunchInfo i) { | |
return i.plugins; | |
} | |
}); | |
List deleteFragments = | |
uninstallMarkForDeletion(candidateFragments, historyInfo, new ListSelector() { | |
public List get(LaunchInfo i) { | |
return i.fragments; | |
} | |
}); | |
if (deleteConfigs.size() <= 0 | |
&& deleteComps.size() <= 0 | |
&& deletePlugins.size() <= 0 | |
&& deleteFragments.size() <= 0) | |
return; | |
// update state prior to deletion and harden it | |
uninstallPendingDelete(deleteConfigs, configsInact, configsPendingDelete); | |
uninstallPendingDelete(deleteComps, compsInact, compsPendingDelete); | |
uninstallPendingDelete(deletePlugins, pluginsInact, pluginsPendingDelete); | |
uninstallPendingDelete(deleteFragments, fragmentsInact, fragmentsPendingDelete); | |
uninstallPendingDelete(history); | |
if (!checkpoint()) | |
return; | |
// delete files | |
uninstall(history, 0, history.length - (historyCount + 1)); | |
uninstall(deleteConfigs, deleteComps, deletePlugins, deleteFragments); | |
checkpoint(); | |
} | |
private synchronized void uninstall(History[] history, int from, int to) { | |
for (int i = from; i < to; i++) { | |
if (history[i].isCurrent()) | |
continue; // just in case | |
if (DEBUG) | |
debug( | |
"Removing history " | |
+ history[i].getIdentifier() | |
+ " [" | |
+ history[i].getLaunchInfoDate().toString() | |
+ "]"); | |
File info = | |
new File( | |
history[i].getLaunchInfoURL().getFile().replace('/', File.separatorChar)); | |
if (!info.exists() || uninstall(info)) | |
historyPendingDelete.remove(history[i].getIdentifier()); | |
} | |
} | |
private boolean uninstall(File f) { | |
if (f.isDirectory()) { | |
File[] list = f.listFiles(); | |
if (list != null) { | |
for (int i = 0; i < list.length; i++) | |
uninstall(list[i]); | |
} | |
} | |
boolean ok = f.delete(); | |
if (DEBUG) | |
debug((ok ? "Unistalled " : "Unable to uninstall ") + f.toString()); | |
return ok; | |
} | |
synchronized private void uninstall( | |
List configId, | |
List compId, | |
List pluginId, | |
List fragId) { | |
if (!isUpdateEnabled()) | |
return; | |
String root = baseUrl.getFile().replace('/', File.separatorChar); | |
File dir; | |
// uninstall configurations | |
for (int i = 0; i < configId.size(); i++) { | |
if (DEBUG) | |
debug("Removing configuration " + configId.get(i).toString()); | |
dir = | |
new File( | |
root + INSTALLDIR + CONFIGSDIR + configId.get(i).toString() + File.separator); | |
if (!dir.exists() || uninstall(dir)) | |
configsPendingDelete.remove(configId.get(i)); | |
} | |
// unistall components | |
for (int i = 0; i < compId.size(); i++) { | |
if (DEBUG) | |
debug("Removing component " + compId.get(i).toString()); | |
dir = | |
new File( | |
root + INSTALLDIR + COMPSDIR + compId.get(i).toString() + File.separator); | |
if (!dir.exists() || uninstall(dir)) | |
compsPendingDelete.remove(compId.get(i)); | |
} | |
// uninstall plugins | |
for (int i = 0; i < pluginId.size(); i++) { | |
if (DEBUG) | |
debug("Removing plugin " + pluginId.get(i).toString()); | |
dir = new File(root + PLUGINSDIR + pluginId.get(i).toString() + File.separator); | |
if (!dir.exists() || uninstall(dir)) | |
pluginsPendingDelete.remove(pluginId.get(i)); | |
} | |
// uninstall fragments | |
for (int i = 0; i < fragId.size(); i++) { | |
if (DEBUG) | |
debug("Removing fragment " + fragId.get(i).toString()); | |
dir = new File(root + FRAGMENTSDIR + fragId.get(i).toString() + File.separator); | |
if (!dir.exists() || uninstall(dir)) | |
fragmentsPendingDelete.remove(fragId.get(i)); | |
} | |
} | |
private void uninstallGetCandidates( | |
List candidates, | |
List active, | |
List inactive) { | |
for (int i = 0; i < active.size(); i++) { | |
if (!candidates.contains(active.get(i))) | |
candidates.add(active.get(i)); | |
} | |
for (int i = 0; i < inactive.size(); i++) { | |
if (!candidates.contains(inactive.get(i))) | |
candidates.add(inactive.get(i)); | |
} | |
} | |
private List uninstallMarkForDeletion( | |
List candidates, | |
LaunchInfo[] history, | |
ListSelector active) { | |
List delete = new ArrayList(); | |
Iterator list = candidates.iterator(); | |
while (list.hasNext()) { | |
boolean found = false; | |
VersionedIdentifier vid = (VersionedIdentifier) list.next(); | |
for (int i = history.length - (historyCount + 1); i < history.length; i++) { | |
if (active.get(history[i]).contains(vid)) { | |
found = true; | |
break; | |
} | |
} | |
if (!found) | |
delete.add(vid); | |
} | |
return delete; | |
} | |
private synchronized void uninstallPendingDelete() { | |
if (baseUrl != null && historyPendingDelete.size() > 0) { | |
ArrayList history = new ArrayList(); | |
for (int i = 0; i < historyPendingDelete.size(); i++) { | |
String id = (String) historyPendingDelete.get(i); | |
URL prof; | |
try { | |
prof = | |
new URL( | |
baseUrl, | |
INSTALLDIR + LAUNCH_PROFILE_NAME + "_" + id + "." + LAUNCH_PROFILE_EXT); | |
history.add(new History(prof, id)); | |
} catch (MalformedURLException e) { | |
} | |
} | |
History[] historyArray = new History[history.size()]; | |
history.toArray(historyArray); | |
uninstall(historyArray, 0, historyArray.length); | |
} | |
if (configsPendingDelete.size() > 0 | |
|| compsPendingDelete.size() > 0 | |
|| pluginsPendingDelete.size() > 0 | |
|| fragmentsPendingDelete.size() > 0) { | |
ArrayList configsToDelete = new ArrayList(configsPendingDelete); | |
// need to clone list | |
ArrayList compsToDelete = new ArrayList(compsPendingDelete); | |
ArrayList pluginsToDelete = new ArrayList(pluginsPendingDelete); | |
ArrayList fragsToDelete = new ArrayList(fragmentsPendingDelete); | |
uninstall(configsToDelete, compsToDelete, pluginsToDelete, fragsToDelete); | |
} | |
} | |
private void uninstallPendingDelete(History[] history) { | |
for (int i = 0; i < (history.length - (historyCount + 1)); i++) { | |
String id = history[i].getIdentifier(); | |
if (!historyPendingDelete.contains(id)) | |
historyPendingDelete.add(id); | |
} | |
} | |
private void uninstallPendingDelete(List delete, List inactive, List pending) { | |
for (int i = 0; i < delete.size(); i++) { | |
Object o = delete.get(i); | |
inactive.remove(o); | |
if (!pending.contains(o)) | |
pending.add(o); | |
} | |
} | |
synchronized private void write(String id, PrintWriter w) throws IOException { | |
w.println(ID + "=" + id); | |
w.println(PLATFORM + "=" + platform); | |
w.println(APP + "=" + app); | |
w.println(APP_CONFIG + "=" + appconfig); | |
w.println(HISTORY_COUNT + "=" + Integer.toString(historyCount)); | |
writeList(w, historyPendingDelete, HISTORY_PENDDEL); | |
writeList(w, configs, CONFIG_ACT); | |
writeList(w, configsInact, CONFIG_INACT); | |
writeList(w, configsPendingDelete, CONFIG_PENDDEL); | |
writeList(w, comps, COMP_ACT); | |
writeList(w, compsInact, COMP_INACT); | |
writeList(w, compsDang, COMP_DANG); | |
writeList(w, compsPendingDelete, COMP_PENDDEL); | |
writeList(w, plugins, PLUGIN_ACT); | |
writeList(w, pluginsInact, PLUGIN_INACT); | |
writeList(w, pluginsUnmgd, PLUGIN_UNMGD); | |
writeList(w, pluginsPendingDelete, PLUGIN_PENDDEL); | |
writeList(w, fragments, FRAG_ACT); | |
writeList(w, fragmentsInact, FRAG_INACT); | |
writeList(w, fragmentsUnmgd, FRAG_UNMGD); | |
writeList(w, fragmentsPendingDelete, FRAG_PENDDEL); | |
writeMap(w, infoMap, INFO); | |
w.println(EOF_MARKER); | |
} | |
private void writeList(PrintWriter w, List list, String id) | |
throws IOException { | |
if (list == null || list.size() <= 0) | |
return; | |
for (int i = 0; LIST_SIZE * i < list.size(); i++) { | |
String prop = ""; | |
for (int j = 0; j < 10 && LIST_SIZE * i + j < list.size(); j++) { | |
if (j != 0) | |
prop += ","; | |
prop += list.get(LIST_SIZE * i + j).toString(); | |
} | |
w.println(id + "." + i + "=" + prop); | |
} | |
} | |
private void writeMap(PrintWriter w, Map map, String id) throws IOException { | |
if (map == null) | |
return; | |
Iterator keys = map.keySet().iterator(); | |
while (keys.hasNext()) { | |
String key = (String) keys.next(); | |
List list = (List) map.get(key); | |
if (list != null && list.size() > 0) { | |
String prop = ""; | |
for (int i = 0; i < list.size(); i++) { | |
if (i != 0) | |
prop += ","; | |
prop += list.get(i).toString(); | |
} | |
w.println(id + "." + key + "=" + prop); | |
} | |
} | |
} | |
synchronized private void writeSummary(String id, PrintWriter w) | |
throws IOException { | |
w.println(ID + "=" + id); | |
w.println(PLATFORM + "=" + platform); | |
w.println(APP + "=" + app); | |
w.println(APP_CONFIG + "=" + appconfig); | |
} | |
} |