blob: 1e414b2e1bbe4525d45883594219a53cb5dffc2a [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.configurator;
import java.io.*;
import java.lang.reflect.*;
import java.net.*;
import java.util.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.eclipse.core.internal.boot.*;
import org.eclipse.core.runtime.*;
import org.eclipse.update.configurator.*;
import org.w3c.dom.*;
/**
* This class is responsible for providing the features and plugins (bundles) to
* the runtime. Configuration data is stored in the .config/platform.xml file.
* When eclipse starts, it tries to load the config info from platform.xml.
* If the file does not exist, then it also tries to read it from a temp or backup file.
* If this does not succeed, a platform.xml is created by inspecting the eclipse
* installation directory (its features and plugin folders).
* If platform.xml already exists, a check is made to see when it was last modified
* and whether there are any file system changes that are newer (users may manually unzip
* features and plugins). In this case, the newly added features and plugins are picked up.
* A check for existence of features and plugins is also performed, to detect deletions.
*/
public class PlatformConfiguration implements IPlatformConfiguration, IConfigurationConstants {
private static PlatformConfiguration currentPlatformConfiguration = null;
private static final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
private static final TransformerFactory transformerFactory = TransformerFactory.newInstance();
private Configuration config;
private URL configLocation;
// private HashMap sites;
private HashMap externalLinkSites; // used to restore prior link site state
private HashMap bootPlugins;
private long changeStamp;
// private boolean changeStampIsValid = false;
// private long lastFeaturesChangeStamp;
private long featuresChangeStamp;
private boolean featuresChangeStampIsValid;
// private long lastPluginsChangeStamp;
private long pluginsChangeStamp;
private boolean pluginsChangeStampIsValid;
private File cfgLockFile;
private RandomAccessFile cfgLockFileRAF;
private boolean isNew; // true when created out of parsing files
private static final String ECLIPSE = "eclipse"; //$NON-NLS-1$
private static final String CONFIG_DIR = ".config"; //$NON-NLS-1$
private static final String CONFIG_NAME = "platform.xml"; //$NON-NLS-1$
private static final String CONFIG_FILE = CONFIG_DIR + "/" + CONFIG_NAME; //$NON-NLS-1$
private static final String CONFIG_FILE_INIT = "install.ini"; //$NON-NLS-1$
private static final String CONFIG_INI = "config.ini"; //NON-NLS-1$
private static final String CONFIG_FILE_LOCK_SUFFIX = ".lock"; //$NON-NLS-1$
private static final String CONFIG_FILE_TEMP_SUFFIX = ".tmp"; //$NON-NLS-1$
private static final String CONFIG_FILE_BAK_SUFFIX = ".bak"; //$NON-NLS-1$
private static final String CHANGES_MARKER = ".newupdates"; //$NON-NLS-1$
private static final String LINKS = "links"; //$NON-NLS-1$
// private static final String[] BOOTSTRAP_PLUGINS = { "org.eclipse.core.boot" }; //$NON-NLS-1$
private static final String[] BOOTSTRAP_PLUGINS = {}; //$NON-NLS-1$
private static final String INIT_DEFAULT_FEATURE_ID = "feature.default.id"; //$NON-NLS-1$
private static final String INIT_DEFAULT_PLUGIN_ID = "feature.default.plugin.id"; //$NON-NLS-1$
private static final String INIT_DEFAULT_FEATURE_APPLICATION = "feature.default.application"; //$NON-NLS-1$
private static final String DEFAULT_FEATURE_ID = "org.eclipse.platform"; //$NON-NLS-1$
private static final String DEFAULT_FEATURE_APPLICATION = "org.eclipse.ui.ide.workbench"; //$NON-NLS-1$
private static final String LINK_PATH = "path"; //$NON-NLS-1$
private static final String LINK_READ = "r"; //$NON-NLS-1$
private static final String LINK_READ_WRITE = "rw"; //$NON-NLS-1$
private static URL installURL;
private PlatformConfiguration(String configPath) throws CoreException, IOException {
// this.sites = new HashMap();
this.externalLinkSites = new HashMap();
this.bootPlugins = new HashMap();
this.config = null;
// initialize configuration
initializeCurrent(configPath);
// pick up any first-time default settings (relative to install location)
loadInitializationAttributes();
// Detect external links. These are "soft link" to additional sites. The link
// files are usually provided by external installation programs. They are located
// relative to this configuration URL.
configureExternalLinks();
// Validate sites in the configuration. Causes any sites that do not exist to
// be removed from the configuration
validateSites();
// compute differences between configuration and actual content of the sites
// (base sites and link sites)
// Note: when the config is transient (generated by PDE, etc.) we don't reconcile
changeStamp = computeChangeStamp();
if (changeStamp > config.getDate().getTime() && !isTransient())
reconcile();
// determine which plugins we will use to start the rest of the "kernel"
// (need to get core.runtime matching the executing core.boot and
// xerces matching the selected core.runtime)
// locateDefaultPlugins();
}
PlatformConfiguration(URL url) throws Exception {
// this.sites = new HashMap();
this.externalLinkSites = new HashMap();
this.bootPlugins = new HashMap();
initialize(url);
}
/*
* @see IPlatformConfiguration#createSiteEntry(URL, ISitePolicy)
*/
public ISiteEntry createSiteEntry(URL url, ISitePolicy policy) {
return new SiteEntry(url, policy);
}
/*
* @see IPlatformConfiguration#createSitePolicy(int, String[])
*/
public ISitePolicy createSitePolicy(int type, String[] list) {
return new SitePolicy(type, list);
}
/*
* @see IPlatformConfiguration#createFeatureEntry(String, String, String, boolean, String, URL)
*/
public IFeatureEntry createFeatureEntry(String id, String version, String pluginVersion, boolean primary, String application, URL[] root) {
return new FeatureEntry(id, version, pluginVersion, primary, application, root);
}
/*
* @see IPlatformConfiguration#createFeatureEntry(String, String, String,
* String, boolean, String, URL)
*/
public IFeatureEntry createFeatureEntry(String id, String version, String pluginIdentifier, String pluginVersion, boolean primary, String application, URL[] root) {
return new FeatureEntry(id, version, pluginIdentifier, pluginVersion, primary, application, root);
}
/*
* @see IPlatformConfiguration#configureSite(ISiteEntry)
*/
public void configureSite(ISiteEntry entry) {
configureSite(entry, false);
}
/*
* @see IPlatformConfiguration#configureSite(ISiteEntry, boolean)
*/
public synchronized void configureSite(ISiteEntry entry, boolean replace) {
if (entry == null)
return;
URL url = entry.getURL();
if (url == null)
return;
String key = url.toExternalForm();
// if (sites.containsKey(key) && !replace)
// return;
if (config.getSiteEntry(key) != null && !replace)
return;
//
// sites.put(key, entry);
if (entry instanceof SiteEntry)
config.addSiteEntry(key, (SiteEntry)entry);
}
/*
* @see IPlatformConfiguration#unconfigureSite(ISiteEntry)
*/
public synchronized void unconfigureSite(ISiteEntry entry) {
if (entry == null)
return;
URL url = entry.getURL();
if (url == null)
return;
String key = url.toExternalForm();
// sites.remove(key);
if (entry instanceof SiteEntry)
config.removeSiteEntry(key);
}
/*
* @see IPlatformConfiguration#getConfiguredSites()
*/
public ISiteEntry[] getConfiguredSites() {
// if (sites.size() == 0)
// return new ISiteEntry[0];
//
// return (ISiteEntry[]) sites.values().toArray(new ISiteEntry[0]);
if (config == null)
return new ISiteEntry[0];
SiteEntry[] sites = config.getSites();
ArrayList enabledSites = new ArrayList(sites.length);
for (int i=0; i<sites.length; i++) {
if (sites[i].isEnabled())
enabledSites.add(sites[i]);
}
return (ISiteEntry[])enabledSites.toArray(new ISiteEntry[enabledSites.size()]);
}
/*
* @see IPlatformConfiguration#findConfiguredSite(URL)
*/
public ISiteEntry findConfiguredSite(URL url) {
return findConfiguredSite(url, true);
}
/**
*
* @param url site url
* @param checkPlatformURL if true, check for url format that is platform:/...
* @return
*/
public SiteEntry findConfiguredSite(URL url, boolean checkPlatformURL) {
if (url == null)
return null;
String key = url.toExternalForm();
// ISiteEntry result = (ISiteEntry) sites.get(key);
SiteEntry result = config.getSiteEntry(key);
try {
key = URLDecoder.decode(key, "UTF-8");
} catch (UnsupportedEncodingException e) {
// ignore
}
if (result == null) // retry with decoded URL string
// result = (ISiteEntry) sites.get(key);
result = config.getSiteEntry(key);
if (result == null && checkPlatformURL) {
try {
result = findConfiguredSite(Utils.asPlatformURL(url), false);
} catch (Exception e) {
//ignore
}
}
return result;
}
/*
* @see IPlatformConfiguration#configureFeatureEntry(IFeatureEntry)
*/
public synchronized void configureFeatureEntry(IFeatureEntry entry) {
if (entry == null)
return;
String key = entry.getFeatureIdentifier();
if (key == null)
return;
// we should check each site and find where the feature is
// located and then configure it
if (config == null)
config = new Configuration();
SiteEntry defaultSite = config.getSiteEntry(PlatformURLHandler.PROTOCOL + PlatformURLHandler.PROTOCOL_SEPARATOR + "/" + "base" + "/");
if (defaultSite != null) {
defaultSite.addFeatureEntry(entry);
}
// else, do nothing (we need a site)
}
/*
* @see IPlatformConfiguration#unconfigureFeatureEntry(IFeatureEntry)
*/
public synchronized void unconfigureFeatureEntry(IFeatureEntry entry) {
if (entry == null)
return;
String key = entry.getFeatureIdentifier();
if (key == null)
return;
config.unconfigureFeatureEntry(entry);
}
/*
* @see IPlatformConfiguration#getConfiguredFeatureEntries()
*/
public IFeatureEntry[] getConfiguredFeatureEntries() {
ArrayList configFeatures = new ArrayList();
SiteEntry[] sites = config.getSites();
for (int i=0; i<sites.length; i++) {
FeatureEntry[] features = sites[i].getFeatureEntries();
for (int j=0; j<features.length; j++)
configFeatures.add(features[j]);
}
return (IFeatureEntry[])configFeatures.toArray(new IFeatureEntry[configFeatures.size()]);
}
/*
* @see IPlatformConfiguration#findConfiguredFeatureEntry(String)
*/
public IFeatureEntry findConfiguredFeatureEntry(String id) {
if (id == null)
return null;
SiteEntry[] sites = config.getSites();
for (int i=0; i<sites.length; i++) {
FeatureEntry f = sites[i].getFeatureEntry(id);
if (f != null)
return f;
}
return null;
}
/*
* @see IPlatformConfiguration#getConfigurationLocation()
*/
public URL getConfigurationLocation() {
return configLocation;
}
/*
* @see IPlatformConfiguration#getChangeStamp()
*/
public long getChangeStamp() {
// if (!changeStampIsValid)
// computeChangeStamp();
// return changeStamp;
return 0;
}
/*
* @see IPlatformConfiguration#getFeaturesChangeStamp()
*/
public long getFeaturesChangeStamp() {
// if (!featuresChangeStampIsValid)
// computeFeaturesChangeStamp();
// return featuresChangeStamp;
return 0;
}
/*
* @see IPlatformConfiguration#getPluginsChangeStamp()
*/
public long getPluginsChangeStamp() {
// if (!pluginsChangeStampIsValid)
// computePluginsChangeStamp();
// return pluginsChangeStamp;
return 0;
}
/*
* @see IPlatformConfiguration#getApplicationIdentifier()
*/
public String getApplicationIdentifier() {
// if (cmdInitialize) {
// // we are running post-install initialization. Force
// // running of the reconciler
// return RECONCILER_APP;
// }
//
// if (featuresChangeStamp != lastFeaturesChangeStamp) {
// // we have detected feature changes ... see if we need to reconcile
// boolean update = !cmdNoUpdate || cmdUpdate;
// if (update)
// return RECONCILER_APP;
// }
// "normal" startup ... run specified application
return getApplicationIdentifierInternal();
}
private String getApplicationIdentifierInternal() {
String feature = config.getDefaultFeature();
// lookup application for feature (specified or defaulted)
if (feature != null) {
IFeatureEntry fe = findConfiguredFeatureEntry(feature);
if (fe != null) {
if (fe.getFeatureApplication() != null)
return fe.getFeatureApplication();
}
}
// return hardcoded default if we failed
return DEFAULT_FEATURE_APPLICATION;
}
/*
* @see IPlatformConfiguration#getPrimaryFeatureIdentifier()
*/
public String getPrimaryFeatureIdentifier() {
String primaryFeatureId = null;
if (config.getDefaultFeature() != null)
primaryFeatureId = config.getDefaultFeature(); // return customized default if set
else
primaryFeatureId = DEFAULT_FEATURE_ID; // return hardcoded default
// check if feature exists
if (findConfiguredFeatureEntry(primaryFeatureId) == null)
return null;
else
return primaryFeatureId;
}
/*
* @see IPlatformConfiguration#getPluginPath()
*/
public URL[] getPluginPath() {
ArrayList path = new ArrayList();
Utils.debug("computed plug-in path:"); //$NON-NLS-1$
ISiteEntry[] sites = getConfiguredSites();
URL pathURL;
for (int i = 0; i < sites.length; i++) {
String[] plugins = sites[i].getPlugins();
for (int j = 0; j < plugins.length; j++) {
try {
pathURL = new URL(((SiteEntry) sites[i]).getResolvedURL(), plugins[j]);
path.add(pathURL);
Utils.debug(" " + pathURL.toString()); //$NON-NLS-1$
} catch (MalformedURLException e) {
// skip entry ...
Utils.debug(" bad URL: " + e); //$NON-NLS-1$
}
}
}
return (URL[]) path.toArray(new URL[0]);
}
/*
* @see IPlatformConfiguration#getBootstrapPluginIdentifiers()
*/
public String[] getBootstrapPluginIdentifiers() {
return BOOTSTRAP_PLUGINS;
}
/*
* @see IPlatformConfiguration#setBootstrapPluginLocation(String, URL)
*/
public void setBootstrapPluginLocation(String id, URL location) {
// String[] ids = getBootstrapPluginIdentifiers();
// for (int i = 0; i < ids.length; i++) {
// if (ids[i].equals(id)) {
// bootPlugins.put(id, location.toExternalForm());
// break;
// }
// }
}
/*
* @see IPlatformConfiguration#isUpdateable()
*/
public boolean isUpdateable() {
return true;
}
/*
* @see IPlatformConfiguration#isTransient()
*/
public boolean isTransient() {
if (config != null)
return config.isTransient();
else
return false;
}
/*
* @see IPlatformConfiguration#isTransient(boolean)
*/
public void isTransient(boolean value) {
if (this != getCurrent() && config != null)
config.setTransient(value);
}
/*
* @see IPlatformConfiguration#refresh()
*/
public synchronized void refresh() {
// Reset computed values. Will be lazily refreshed
// on next access
ISiteEntry[] sites = getConfiguredSites();
for (int i = 0; i < sites.length; i++) {
// reset site entry
((SiteEntry) sites[i]).refresh();
}
// reset configuration entry.
// lastFeaturesChangeStamp = featuresChangeStamp;
// lastPluginsChangeStamp = pluginsChangeStamp;
// changeStampIsValid = false;
// featuresChangeStampIsValid = false;
// pluginsChangeStampIsValid = false;
}
/*
* @see IPlatformConfiguration#save()
*/
public void save() throws IOException {
if (isUpdateable())
save(configLocation);
}
/*
* @see IPlatformConfiguration#save(URL)
*/
public synchronized void save(URL url) throws IOException {
if (url == null)
throw new IOException(Messages.getString("cfig.unableToSave.noURL")); //$NON-NLS-1$
OutputStream os = null;
if (!url.getProtocol().equals("file")) { //$NON-NLS-1$
// not a file protocol - attempt to save to the URL
URLConnection uc = url.openConnection();
uc.setDoOutput(true);
os = uc.getOutputStream();
try {
saveAsXML(os);
config.setDirty(false);
} catch (CoreException e) {
throw new IOException(Messages.getString("cfig.unableToSave", url.toExternalForm())); //$NON-NLS-1$
} finally {
os.close();
}
} else {
// file protocol - do safe i/o
File cfigFile = new File(url.getFile().replace('/', File.separatorChar));
if (!cfigFile.getName().equals(CONFIG_NAME))
cfigFile = new File(cfigFile, CONFIG_NAME);
File cfigDir = cfigFile.getParentFile();
if (cfigDir != null)
cfigDir.mkdirs();
// Backup old file
File oldConfigFile = new File(cfigDir, CONFIG_NAME);
if (oldConfigFile.exists()){
File backupDir = new File(cfigDir, "history");
if (!backupDir.exists())
backupDir.mkdir();
File preservedFile = new File(backupDir, String.valueOf(oldConfigFile.lastModified())+".xml");
copy(oldConfigFile, preservedFile);
preservedFile.setLastModified(oldConfigFile.lastModified());
}
// If config.ini does not exist, generate it
writeConfigIni(cfigDir);
// first save the file as temp
File cfigTmp = new File(cfigFile.getAbsolutePath() + CONFIG_FILE_TEMP_SUFFIX);
os = new FileOutputStream(cfigTmp);
try {
saveAsXML(os);
// set file time stamp to match that of the config element
cfigTmp.setLastModified(config.getDate().getTime());
// make the change stamp to be the same as the config file
changeStamp = config.getDate().getTime();
// changeStampIsValid = true;
} catch (CoreException e) {
throw new IOException(Messages.getString("cfig.unableToSave", cfigTmp.getAbsolutePath())); //$NON-NLS-1$
} finally {
os.close();
}
// make the saved config the "active" one
File cfigBak = new File(cfigFile.getAbsolutePath() + CONFIG_FILE_BAK_SUFFIX);
cfigBak.delete(); // may have old .bak due to prior failure
if (cfigFile.exists())
cfigFile.renameTo(cfigBak);
// at this point we have old config (if existed) as "bak" and the
// new config as "tmp".
boolean ok = cfigTmp.renameTo(cfigFile);
if (ok) {
// at this point we have the new config "activated", and the old
// config (if it existed) as "bak"
cfigBak.delete(); // clean up
} else {
// this codepath represents a tiny failure window. The load processing
// on startup will detect missing config and will attempt to start
// with "tmp" (latest), then "bak" (the previous). We can also end up
// here if we failed to rename the current config to "bak". In that
// case we will restart with the previous state.
throw new IOException(Messages.getString("cfig.unableToSave", cfigTmp.getAbsolutePath())); //$NON-NLS-1$
}
}
}
private void writeConfigIni(File configDir) {
try {
File configIni = new File(configDir, CONFIG_INI);
if (!configIni.exists()) {
URL configIniURL = ConfigurationActivator.getBundleContext().getBundle().getEntry(CONFIG_INI);
copy(configIniURL, configIni);
}
} catch (Exception e) {
System.out.println(Messages.getString("cfg.unableToCreateConfig.ini"));
}
}
public static PlatformConfiguration getCurrent() {
return currentPlatformConfiguration;
}
/**
* Create and initialize the current platform configuration
* @param cmdArgs command line arguments (startup and boot arguments are
* already consumed)
* @param r10apps application identifies as passed on the BootLoader.run(...)
* method. Supported for R1.0 compatibility.
*/
public static synchronized void startup(URL installURL, String configPath) throws Exception {
PlatformConfiguration.installURL = installURL;
// create current configuration
if (currentPlatformConfiguration == null) {
currentPlatformConfiguration = new PlatformConfiguration(configPath);
if (currentPlatformConfiguration.isNew)
currentPlatformConfiguration.save();
}
}
public static synchronized void shutdown() throws IOException {
// save platform configuration
PlatformConfiguration config = getCurrent();
if (config != null) {
// only save if there are changes in the config
// TODO clean this up when merging with the rest of update code
long lastStamp = config.config.getDate().getTime();
long computedStamp = config.computeChangeStamp();
if (config.config.isDirty() || computedStamp > lastStamp) {
try {
config.save();
} catch (IOException e) {
Utils.debug("Unable to save configuration " + e.toString()); //$NON-NLS-1$
// will recover on next startup
}
}
config.clearConfigurationLock();
}
}
private synchronized void initializeCurrent(String configPath) throws IOException {
// FIXME: commented out for now. Remove if not needed.
//boolean concurrentUse = false;
// Configuration URL was is specified by the OSGi layer.
// Default behavior is to look
// for configuration in the specified meta area. If not found, look
// for pre-initialized configuration in the installation location.
// If it is found it is used as the initial configuration. Otherwise
// a new configuration is created. In either case the resulting
// configuration is written into the specified configuration area.
URL configDirURL = new URL("file", "", configPath);
URL configFileURL = new URL("file", "", configPath + "/" + CONFIG_NAME);
try {
// check concurrent use lock
// FIXME: might not need this method call.
getConfigurationLock(configFileURL);
// try loading the configuration
try {
config = loadConfig(configFileURL);
Utils.debug("Using configuration " + configFileURL.toString()); //$NON-NLS-1$
} catch (Exception e) {
// failed to load, see if we can find pre-initialized configuration.
// Don't attempt this initialization when self-hosting (is unpredictable)
try {
URL sharedConfigDirURL = new URL(getInstallURL(), CONFIG_DIR);
URL sharedConfigFileURL = new URL(getInstallURL(), CONFIG_FILE);
config = loadConfig(sharedConfigFileURL);
// pre-initialized config loaded OK ... copy any remaining update metadata
// Only copy if the default config location is not the install location
if (!sharedConfigDirURL.equals(configDirURL)) {
if (true)
// need to link config info instead of using a copy
linkInitializedState(config, sharedConfigDirURL, configFileURL);
else
// copy config info
copyInitializedState(sharedConfigDirURL, configPath);
Utils.debug("Configuration initialized from " + sharedConfigDirURL.toString()); //$NON-NLS-1$
}
return;
} catch (Exception ioe) {
Utils.debug("Creating default configuration from " + configFileURL.toExternalForm());
createDefaultConfiguration(configFileURL);
}
}
} finally {
configLocation = configFileURL;
if (config.getURL() == null)
config.setURL(configFileURL);
verifyPath(configLocation);
Utils.debug("Creating configuration " + configFileURL.toString()); //$NON-NLS-1$
}
}
private synchronized void initialize(URL url) throws Exception {
if (url == null) {
config = new Configuration();
config.setURL(url);
Utils.debug("Creating empty configuration object"); //$NON-NLS-1$
return;
}
config = loadConfig(url);
configLocation = url;
Utils.debug("Using configuration " + configLocation.toString()); //$NON-NLS-1$
}
private void createDefaultConfiguration(URL url)throws IOException{
// we are creating new configuration
config = new Configuration();
config.setURL(url);
SiteEntry defaultSite = (SiteEntry)getRootSite();
configureSite(defaultSite);
try {
// parse the site directory to discover features
defaultSite.loadFromDisk();
isNew = true;
} catch (CoreException e1) {
Utils.log("Cannot load default site " + defaultSite.getResolvedURL());
return;
}
}
private ISiteEntry getRootSite() {
// create default site entry for the root
ISitePolicy defaultPolicy = createSitePolicy(DEFAULT_POLICY_TYPE, DEFAULT_POLICY_LIST);
URL siteURL = null;
try {
siteURL = new URL(PlatformURLHandler.PROTOCOL + PlatformURLHandler.PROTOCOL_SEPARATOR + "/" + "base" + "/"); //$NON-NLS-1$ //$NON-NLS-2$ // try using platform-relative URL
} catch (MalformedURLException e) {
siteURL = getInstallURL(); // ensure we come up ... use absolute file URL
}
ISiteEntry defaultSite = createSiteEntry(siteURL, defaultPolicy);
return defaultSite;
}
private void resetInitializationConfiguration(URL url) throws IOException {
// [20111]
if (!supportsDetection(url))
return; // can't do ...
URL resolved = resolvePlatformURL(url);
File initCfg = new File(resolved.getFile().replace('/', File.separatorChar));
File initDir = initCfg.getParentFile();
resetInitializationLocation(initDir);
}
private void resetInitializationLocation(File dir) {
// [20111]
if (dir == null || !dir.exists() || !dir.isDirectory())
return;
File[] list = dir.listFiles();
for (int i = 0; i < list.length; i++) {
if (list[i].isDirectory())
resetInitializationLocation(list[i]);
list[i].delete();
}
}
private boolean getConfigurationLock(URL url) {
// if (!url.getProtocol().equals("file")) //$NON-NLS-1$
// return false;
//
// verifyPath(url);
// String cfgName = url.getFile().replace('/', File.separatorChar);
// String lockName = cfgName + CONFIG_FILE_LOCK_SUFFIX;
// cfgLockFile = new File(lockName);
//
// //if the lock file already exists, try to delete,
// //assume failure means another eclipse has it open
// if (cfgLockFile.exists())
// cfgLockFile.delete();
// if (cfgLockFile.exists()) {
// throw new RuntimeException(Policy.bind("cfig.inUse", cfgName, lockName)); //$NON-NLS-1$
// }
//
// // OK so far ... open the lock file so other instances will fail
// try {
// cfgLockFileRAF = new RandomAccessFile(cfgLockFile, "rw"); //$NON-NLS-1$
// cfgLockFileRAF.writeByte(0);
// } catch (IOException e) {
// throw new RuntimeException(Policy.bind("cfig.failCreateLock", cfgName)); //$NON-NLS-1$
// }
return false;
}
private void clearConfigurationLock() {
try {
if (cfgLockFileRAF != null) {
cfgLockFileRAF.close();
cfgLockFileRAF = null;
}
} catch (IOException e) {
// ignore ...
}
if (cfgLockFile != null) {
cfgLockFile.delete();
cfgLockFile = null;
}
}
private long computeChangeStamp() {
featuresChangeStamp = computeFeaturesChangeStamp();
pluginsChangeStamp = computePluginsChangeStamp();
changeStamp = Math.max(featuresChangeStamp, pluginsChangeStamp);
// round off to seconds
changeStamp = (changeStamp/1000)*1000;
return changeStamp;
}
private long computeFeaturesChangeStamp() {
if (featuresChangeStampIsValid)
return featuresChangeStamp;
long result = 0;
ISiteEntry[] sites = config.getSites();
for (int i = 0; i < sites.length; i++) {
result = Math.max(result, sites[i].getFeaturesChangeStamp());
}
featuresChangeStamp = result;
featuresChangeStampIsValid = true;
return featuresChangeStamp;
}
private long computePluginsChangeStamp() {
if (pluginsChangeStampIsValid)
return pluginsChangeStamp;
long result = 0;
ISiteEntry[] sites = config.getSites();
for (int i = 0; i < sites.length; i++) {
result = Math.max(result, sites[i].getPluginsChangeStamp());
}
pluginsChangeStamp = result;
pluginsChangeStampIsValid = true;
return pluginsChangeStamp;
}
private void configureExternalLinks() {
URL linkURL = getInstallURL();
if (!supportsDetection(linkURL))
return;
try {
linkURL = new URL(linkURL, LINKS + "/"); //$NON-NLS-1$
} catch (MalformedURLException e) {
// skip bad links ...
Utils.debug("Unable to obtain link URL"); //$NON-NLS-1$
return;
}
File linkDir = new File(linkURL.getFile());
File[] links = linkDir.listFiles();
if (links == null || links.length == 0) {
Utils.debug("No links detected in " + linkURL.toExternalForm()); //$NON-NLS-1$
return;
}
for (int i = 0; i < links.length; i++) {
if (links[i].isDirectory())
continue;
Utils.debug("Link file " + links[i].getAbsolutePath()); //$NON-NLS-1$
Properties props = new Properties();
FileInputStream is = null;
try {
is = new FileInputStream(links[i]);
props.load(is);
configureExternalLinkSites(links[i], props);
} catch (IOException e) {
// skip bad links ...
Utils.debug(" unable to load link file " + e); //$NON-NLS-1$
continue;
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
// ignore ...
}
}
}
}
}
private void configureExternalLinkSites(File linkFile, Properties props) {
String path = props.getProperty(LINK_PATH);
if (path == null) {
Utils.debug(" no path definition"); //$NON-NLS-1$
return;
}
String link;
boolean updateable = true;
URL siteURL;
SiteEntry linkSite;
ISitePolicy linkSitePolicy = createSitePolicy(DEFAULT_POLICY_TYPE, DEFAULT_POLICY_LIST);
// parse out link information
if (path.startsWith(LINK_READ + " ")) { //$NON-NLS-1$
updateable = false;
link = path.substring(2).trim();
} else if (path.startsWith(LINK_READ_WRITE + " ")) { //$NON-NLS-1$
link = path.substring(3).trim();
} else {
link = path;
}
// make sure we have a valid link specification
try {
if (!link.endsWith(File.separator))
link += File.separator;
File target = new File(link + ECLIPSE);
link = "file:" + target.getAbsolutePath().replace(File.separatorChar, '/'); //$NON-NLS-1$
if (!link.endsWith("/")) //$NON-NLS-1$
link += "/"; // sites must be directories //$NON-NLS-1$
siteURL = new URL(link);
} catch (MalformedURLException e) {
// ignore bad links ...
Utils.debug(" bad URL " + e); //$NON-NLS-1$
return;
}
// process the link
linkSite = (SiteEntry) externalLinkSites.get(siteURL);
if (linkSite == null) {
// this is a link to a new target so create site for it
linkSite = (SiteEntry) createSiteEntry(siteURL, linkSitePolicy);
}
// update site entry if needed
linkSite.setUpdateable(updateable);
linkSite.setLinkFileName(linkFile.getAbsolutePath());
// configure the new site
// NOTE: duplicates are not replaced (first one in wins)
configureSite(linkSite);
Utils.debug(" " + (updateable ? "R/W -> " : "R/O -> ") + siteURL.toString()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
private void validateSites() {
// check to see if all sites are valid. Remove any sites that do not exist.
SiteEntry[] list = config.getSites();
for (int i = 0; i < list.length; i++) {
URL siteURL = list[i].getResolvedURL();
if (!supportsDetection(siteURL))
continue;
File siteRoot = new File(siteURL.getFile().replace('/', File.separatorChar));
if (!siteRoot.exists()) {
unconfigureSite(list[i]);
Utils.debug("Site " + siteURL + " does not exist ... removing from configuration"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
private void linkInitializedState(Configuration sharedConfig, URL sharedConfigURL, URL newConfigURL) {
try {
URL oldConfigIniURL = new URL(sharedConfigURL, CONFIG_INI);
URL newConfigIniURL = new URL(newConfigURL, CONFIG_INI);
if (!newConfigIniURL.getProtocol().equals("file")) //$NON-NLS-1$
return; // need to be able to do write
// modify config.ini and platform.xml to only link original files
File configIni = new File(newConfigIniURL.getFile());
Properties props = new Properties();
props.put(CFG_SHARED_URL, oldConfigIniURL.toExternalForm());
props.store(new FileOutputStream(configIni), "Linked configuration");
config = new Configuration(new Date());
config.setURL(newConfigURL);
config.setLinkedConfig(sharedConfig);
isNew = true;
} catch (IOException e) {
// this is an optimistic copy. If we fail, the state will be reconciled
// when the update manager is triggered.
System.out.println(e);
}
}
private void copyInitializedState(URL source, String target) {
try {
if (!source.getProtocol().equals("file")) //$NON-NLS-1$
return; // need to be able to do "dir"
copy(new File(source.getFile()), new File(target));
} catch (IOException e) {
// this is an optimistic copy. If we fail, the state will be reconciled
// when the update manager is triggered.
}
}
private void copy(File src, File tgt) throws IOException {
if (src.isDirectory()) {
// copy content of directories
tgt.mkdir();
FilenameFilter filter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return !name.equals(ConfigurationActivator.LAST_CONFIG_STAMP);
}
};
File[] list = src.listFiles(filter);
if (list == null)
return;
for (int i = 0; i < list.length; i++) {
copy(list[i], new File(tgt, list[i].getName()));
}
} else {
// copy individual files
FileInputStream is = null;
FileOutputStream os = null;
try {
is = new FileInputStream(src);
os = new FileOutputStream(tgt);
byte[] buff = new byte[1024];
int count = is.read(buff);
while (count != -1) {
os.write(buff, 0, count);
count = is.read(buff);
}
} catch (IOException e) {
// continue ... update reconciler will have to reconstruct state
} finally {
if (is != null)
try {
is.close();
} catch (IOException e) {
// ignore ...
}
if (os != null)
try {
os.close();
} catch (IOException e) {
// ignore ...
}
}
}
}
private void copy(URL src, File tgt) throws IOException {
InputStream is = null;
OutputStream os = null;
try {
is = src.openStream();
os = new FileOutputStream(tgt);
byte[] buff = new byte[1024];
int count = is.read(buff);
while (count != -1) {
os.write(buff, 0, count);
count = is.read(buff);
}
} finally {
if (is != null)
try {
is.close();
} catch (IOException e) {
// ignore ...
}
if (os != null)
try {
os.close();
} catch (IOException e) {
// ignore ...
}
}
}
private Configuration loadConfig(URL url) throws Exception {
if (url == null)
throw new IOException(Messages.getString("cfig.unableToLoad.noURL")); //$NON-NLS-1$
// try to load saved configuration file (watch for failed prior save())
ConfigurationParser parser = null;
try {
parser = new ConfigurationParser();
} catch (InvocationTargetException e) {
throw (Exception)e.getTargetException();
}
config = null;
Exception originalException = null;
try {
config = parser.parse(url);
} catch (Exception e1) {
// check for save failures, so open temp and backup configurations
originalException = e1;
try {
URL tempURL = new URL(url.toExternalForm()+CONFIG_FILE_TEMP_SUFFIX);
config = parser.parse(tempURL);
} catch (Exception e2) {
try {
URL backupUrl = new URL(url.toExternalForm()+CONFIG_FILE_BAK_SUFFIX);
config = parser.parse(backupUrl);
} catch (IOException e3) {
throw originalException; // we tried, but no config here ...
}
}
}
if (config != null) {
SiteEntry[] sites = config.getSites();
for (int i=0; i<sites.length; i++) {
configureSite(sites[i]);
IFeatureEntry[] features = sites[i].getFeatureEntries();
for (int j=0; j<features.length; j++)
configureFeatureEntry(features[j]);
}
}
return config;
}
private String loadAttribute(Properties props, String name, String dflt) {
String prop = props.getProperty(name);
if (prop == null)
return dflt;
else
return prop.trim();
}
private void loadInitializationAttributes() {
// look for the product initialization file relative to the install location
URL url = getInstallURL();
// load any initialization attributes. These are the default settings for
// key attributes (eg. default primary feature) supplied by the packaging team.
// They are always reloaded on startup to pick up any changes due to
// "native" updates.
Properties initProps = new Properties();
InputStream is = null;
try {
URL initURL = new URL(url, CONFIG_FILE_INIT);
is = initURL.openStream();
initProps.load(is);
Utils.debug("Defaults from " + initURL.toExternalForm()); //$NON-NLS-1$
} catch (IOException e) {
return; // could not load default settings
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
// ignore ...
}
}
}
// use default settings if supplied
String initId = loadAttribute(initProps, INIT_DEFAULT_FEATURE_ID, null);
if (initId != null) {
String application = loadAttribute(initProps, INIT_DEFAULT_FEATURE_APPLICATION, null);
String initPluginId = loadAttribute(initProps, INIT_DEFAULT_PLUGIN_ID, null);
if (initPluginId == null)
initPluginId = initId;
IFeatureEntry fe = findConfiguredFeatureEntry(initId);
if (fe == null) {
// bug 26896 : setup optimistic reconciliation if the primary feature has changed or is new
// create entry if not exists
fe = createFeatureEntry(initId, null, initPluginId, null, true, application, null);
} else
// update existing entry with new info
fe = createFeatureEntry(initId, fe.getFeatureVersion(), fe.getFeaturePluginIdentifier(), fe.getFeaturePluginVersion(), fe.canBePrimary(), application, fe.getFeatureRootURLs());
configureFeatureEntry(fe);
if (config != null)
config.setDefaultFeature(initId);
if (ConfigurationActivator.DEBUG) {
Utils.debug(" Default primary feature: " + initId); //$NON-NLS-1$
if (application != null)
Utils.debug(" Default application : " + application); //$NON-NLS-1$
}
}
}
//
// private static String[] checkForNewUpdates(IPlatformConfiguration cfg, String[] args) {
// try {
// URL markerURL = new URL(cfg.getConfigurationLocation(), CHANGES_MARKER);
// File marker = new File(markerURL.getFile());
// if (!marker.exists())
// return args;
//
// // indicate -newUpdates
// marker.delete();
// String[] newArgs = new String[args.length + 1];
// newArgs[0] = CMD_NEW_UPDATES;
// System.arraycopy(args, 0, newArgs, 1, args.length);
// return newArgs;
// } catch (MalformedURLException e) {
// return args;
// }
// }
public static boolean supportsDetection(URL url) {
String protocol = url.getProtocol();
if (protocol.equals("file")) //$NON-NLS-1$
return true;
else if (protocol.equals(PlatformURLHandler.PROTOCOL)) {
URL resolved = null;
try {
resolved = resolvePlatformURL(url); // 19536
} catch (IOException e) {
return false; // we tried but failed to resolve the platform URL
}
return resolved.getProtocol().equals("file"); //$NON-NLS-1$
} else
return false;
}
private static void verifyPath(URL url) {
String protocol = url.getProtocol();
String path = null;
if (protocol.equals("file")) //$NON-NLS-1$
path = url.getFile();
else if (protocol.equals(PlatformURLHandler.PROTOCOL)) {
URL resolved = null;
try {
resolved = resolvePlatformURL(url); // 19536
if (resolved.getProtocol().equals("file")) //$NON-NLS-1$
path = resolved.getFile();
} catch (IOException e) {
// continue ...
}
}
if (path != null) {
File dir = new File(path).getParentFile();
if (dir != null)
dir.mkdirs();
}
}
public static URL resolvePlatformURL(URL url) throws IOException {
// 19536
if (url.getProtocol().equals(PlatformURLHandler.PROTOCOL)) {
URLConnection connection = url.openConnection();
if (connection instanceof PlatformURLConnection) {
url = ((PlatformURLConnection) connection).getResolvedURL();
} else {
// connection = new PlatformURLBaseConnection(url);
// url = ((PlatformURLConnection)connection).getResolvedURL();
url = getInstallURL();
}
}
return url;
}
// private void resetUpdateManagerState(URL url) throws IOException {
// // [20111]
// if (!supportsDetection(url))
// return; // can't do ...
//
// // find directory where the platform configuration file is
// URL resolved = resolvePlatformURL(url);
// File initCfg = new File(resolved.getFile().replace('/', File.separatorChar));
// File initDir = initCfg.getParentFile();
//
// // Find the Update Manager State directory
// if (initDir == null || !initDir.exists() || !initDir.isDirectory())
// return;
// String temp = initCfg.getName() + ".metadata"; //$NON-NLS-1$
// File UMDir = new File(initDir, temp + '/');
//
// // Attempt to rename it
// if (UMDir == null || !UMDir.exists() || !UMDir.isDirectory())
// return;
// Date now = new Date();
// boolean renamed = UMDir.renameTo(new File(initDir, temp + now.getTime() + '/'));
//
// if (!renamed)
// resetInitializationLocation(UMDir);
// }
public static URL getInstallURL() {
return installURL;
}
private void saveAsXML(OutputStream stream) throws CoreException {
try {
DocumentBuilder docBuilder = documentBuilderFactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
if (config == null)
throw Utils.newCoreException("Configuration cannot be saved because it does not exist",null);
config.setDate(new Date());
Element configElement = config.toXML(doc);
doc.appendChild(configElement);
// Write out to a file
Transformer transformer=transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(stream);
transformer.transform(source,result);
stream.close();
} catch (Exception e) {
throw Utils.newCoreException("", e);
}
}
private void reconcile() throws CoreException {
long lastChange = config.getDate().getTime();
SiteEntry[] sites = config.getSites();
for (int s=0; s<sites.length; s++) {
long siteTimestamp = sites[s].getChangeStamp();
if (siteTimestamp > lastChange)
sites[s].loadFromDisk();
}
}
public Configuration getConfiguration() {
return config;
}
}