blob: edcf0e35285738e33145fea9a8964420c3dab445 [file] [log] [blame]
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.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.net.UnknownServiceException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Properties;
import java.util.StringTokenizer;
import org.eclipse.core.boot.BootLoader;
import org.eclipse.core.boot.IPlatformConfiguration;
import org.eclipse.core.boot.IPlatformConfiguration.IFeatureEntry;
import org.eclipse.core.boot.IPlatformConfiguration.ISiteEntry;
import org.eclipse.core.boot.IPlatformConfiguration.ISitePolicy;
public class PlatformConfiguration implements IPlatformConfiguration {
private static PlatformConfiguration currentPlatformConfiguration = null;
private URL configLocation;
private HashMap sites;
private HashMap externalLinkSites; // used to restore prior link site state
private HashMap cfgdFeatures;
private HashMap bootPlugins;
private String defaultFeature;
private long lastChangeStamp;
private long changeStamp;
private boolean changeStampIsValid = false;
private long lastFeaturesChangeStamp;
private long featuresChangeStamp;
private boolean featuresChangeStampIsValid = false;
private long lastPluginsChangeStamp;
private long pluginsChangeStamp;
private boolean pluginsChangeStampIsValid = false;
private boolean featureChangesConfigured = false;
private boolean transientConfig = false;
private static String cmdConfiguration;
private static String cmdFeature;
private static String cmdApplication;
private static URL cmdPlugins;
private static boolean cmdUpdate;
private static boolean cmdNoUpdate;
private static boolean cmdDev;
static boolean DEBUG = false;
private static final String ECLIPSEDIR = "eclipse";
private static final String PLUGINS = "plugins";
private static final String INSTALL = "install";
private static final String CONFIG_FILE = "platform.cfg";
private static final String CONFIG_FILE_INIT = "install.ini";
private static final String FEATURES = INSTALL + "/features";
private static final String LINKS = "links";
private static final String PLUGIN_XML = "plugin.xml";
private static final String FRAGMENT_XML = "fragment.xml";
private static final String FEATURE_XML = "feature.xml";
private static final String[] BOOTSTRAP_PLUGINS = {"org.eclipse.core.boot", "org.eclipse.core.runtime"};
private static final String CFG_BOOT_PLUGIN = "bootstrap";
private static final String CFG_SITE = "site";
private static final String CFG_URL = "url";
private static final String CFG_POLICY = "policy";
private static final String[] CFG_POLICY_TYPE = {"USER-INCLUDE", "USER-EXCLUDE"};
private static final String CFG_POLICY_TYPE_UNKNOWN = "UNKNOWN";
private static final String CFG_LIST = "list";
private static final String CFG_STAMP = "stamp";
private static final String CFG_FEATURE_STAMP = "stamp.features";
private static final String CFG_PLUGIN_STAMP = "stamp.plugins";
private static final String CFG_UPDATEABLE = "updateable";
private static final String CFG_LINK_FILE = "linkfile";
private static final String CFG_FEATURE_ENTRY = "feature";
private static final String CFG_FEATURE_ENTRY_DEFAULT = "feature.default.id";
private static final String CFG_FEATURE_ENTRY_ID = "id";
private static final String CFG_FEATURE_ENTRY_VERSION = "version";
private static final String CFG_FEATURE_ENTRY_APPLICATION = "application";
private static final String CFG_FEATURE_ENTRY_ROOT = "root";
private static final String INIT_DEFAULT_FEATURE_ID = "feature.default.id";
private static final String INIT_DEFAULT_FEATURE_APPLICATION = "feature.default.application";
private static final String DEFAULT_FEATURE_ID = "org.eclipse.sdk";
private static final String DEFAULT_FEATURE_APPLICATION = "org.eclipse.ui.workbench";
private static final String CFG_VERSION = "version";
private static final String CFG_TRANSIENT = "transient";
private static final String VERSION = "1.0";
private static final String EOF = "eof";
private static final int CFG_LIST_LENGTH = 10;
private static final int DEFAULT_POLICY_TYPE = ISitePolicy.USER_EXCLUDE;
private static final String[] DEFAULT_POLICY_LIST = new String[0];
private static final String ARG_USER_DIR = "user.dir";
private static final String ARG_USER_HOME = "user.home";
private static final String ARG_COMMON = "common";
private static final String LINK_PATH = "path";
private static final String LINK_READ = "r";
private static final String LINK_READ_WRITE = "rw";
private static final String CMD_CONFIGURATION = "-configuration";
private static final String CMD_FEATURE = "-feature";
private static final String CMD_APPLICATION = "-application";
private static final String CMD_PLUGINS = "-plugins";
private static final String CMD_UPDATE = "-update";
private static final String CMD_NO_UPDATE = "-noupdate";
private static final String CMD_DEV = "-dev"; // triggers -noupdate
private static final String RECONCILER_APP = "org.eclipse.update.core.reconciler";
private static final char[] HEX = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
public class SiteEntry implements IPlatformConfiguration.ISiteEntry {
private URL url; // this is the external URL for the site
private URL resolvedURL; // this is the resolved URL used internally
private ISitePolicy policy;
private boolean updateable = true;
private ArrayList features;
private ArrayList plugins;
private PlatformConfiguration parent;
private long lastChangeStamp;
private long changeStamp;
private boolean changeStampIsValid = false;
private long lastFeaturesChangeStamp;
private long featuresChangeStamp;
private boolean featuresChangeStampIsValid = false;
private long lastPluginsChangeStamp;
private long pluginsChangeStamp;
private boolean pluginsChangeStampIsValid = false;
private String linkFileName = null;
private SiteEntry() {
}
private SiteEntry(URL url, ISitePolicy policy, PlatformConfiguration parent) {
if (url==null)
throw new IllegalArgumentException();
if (policy==null)
throw new IllegalArgumentException();
if (parent==null)
throw new IllegalArgumentException();
this.url = url;
this.policy = policy;
this.parent = parent;
this.features = null;
this.plugins = null;
this.resolvedURL = this.url;
if (url.getProtocol().equals(PlatformURLHandler.PROTOCOL)) {
try {
resolvedURL = ((PlatformURLConnection)url.openConnection()).getResolvedURL();
} catch(IOException e) {
}
}
}
/*
* @see ISiteEntry#getURL()
*/
public URL getURL() {
return url;
}
/*
* @see ISiteEntry#getSitePolicy()
*/
public ISitePolicy getSitePolicy() {
return policy;
}
/*
* @see ISiteEntry#setSitePolicy(ISitePolicy)
*/
public synchronized void setSitePolicy(ISitePolicy policy) {
if (policy==null)
throw new IllegalArgumentException();
this.policy = policy;
}
/*
* @see ISiteEntry#getFeatures()
*/
public String[] getFeatures() {
return getDetectedFeatures();
}
/*
* @see ISiteEntry#getPlugins()
*/
public String[] getPlugins() {
ISitePolicy policy = getSitePolicy();
if (policy.getType()==ISitePolicy.USER_INCLUDE)
return policy.getList();
if (policy.getType()==ISitePolicy.USER_EXCLUDE) {
ArrayList detectedPlugins = new ArrayList(Arrays.asList(getDetectedPlugins()));
String[] excludedPlugins = policy.getList();
for (int i=0; i<excludedPlugins.length; i++) {
if (detectedPlugins.contains(excludedPlugins[i]))
detectedPlugins.remove(excludedPlugins[i]);
}
return (String[])detectedPlugins.toArray(new String[0]);
}
// bad policy type
return new String[0];
}
/*
* @see ISiteEntry#getChangeStamp()
*/
public long getChangeStamp() {
if (!changeStampIsValid)
computeChangeStamp();
return changeStamp;
}
/*
* @see ISiteEntry#getFeaturesChangeStamp()
*/
public long getFeaturesChangeStamp() {
if (!featuresChangeStampIsValid)
computeFeaturesChangeStamp();
return featuresChangeStamp;
}
/*
* @see ISiteEntry#getPluginsChangeStamp()
*/
public long getPluginsChangeStamp() {
if (!pluginsChangeStampIsValid)
computePluginsChangeStamp();
return pluginsChangeStamp;
}
/*
* @see ISiteEntry#isUpdateable()
*/
public boolean isUpdateable() {
//FIXME: should add actual read-write check
return updateable;
}
private String[] detectFeatures() {
// invalidate stamps ... we are doing discovery
changeStampIsValid = false;
featuresChangeStampIsValid = false;
parent.changeStampIsValid = false;
parent.featuresChangeStampIsValid = false;
features = new ArrayList();
if (!supportsDetection(resolvedURL))
return new String[0];
// locate feature entries on site
long start = 0;
if (DEBUG)
start = (new Date()).getTime();
File root =
new File(resolvedURL.getFile().replace('/', File.separatorChar) + FEATURES);
String[] list = root.list();
String path;
File plugin;
for (int i = 0; list != null && i < list.length; i++) {
path = list[i] + File.separator + FEATURE_XML;
plugin = new File(root, path);
if (!plugin.exists()) {
continue;
}
features.add(FEATURES + "/" + path.replace(File.separatorChar, '/'));
}
if (DEBUG) {
long end = (new Date()).getTime();
debug(resolvedURL.toString()+" located "+features.size()+" feature(s) in "+(end-start)+"ms");
}
return (String[])features.toArray(new String[0]);
}
private String[] detectPlugins() {
// invalidate stamps ... we are doing discovery
changeStampIsValid = false;
pluginsChangeStampIsValid = false;
parent.changeStampIsValid = false;
parent.pluginsChangeStampIsValid = false;
plugins = new ArrayList();
if (!supportsDetection(resolvedURL))
return new String[0];
// locate plugin entries on site
long start = 0;
if (DEBUG)
start = (new Date()).getTime();
File root =
new File(resolvedURL.getFile().replace('/', File.separatorChar) + PLUGINS);
String[] list = root.list();
String path;
File plugin;
for (int i = 0; list != null && i < list.length; i++) {
path = list[i] + File.separator + PLUGIN_XML;
plugin = new File(root, path);
if (!plugin.exists()) {
path = list[i] + File.separator + FRAGMENT_XML;
plugin = new File(root, path);
if (!plugin.exists())
continue;
}
plugins.add(PLUGINS + "/" + path.replace(File.separatorChar, '/'));
}
if (DEBUG) {
long end = (new Date()).getTime();
debug(resolvedURL.toString()+" located "+plugins.size()+" plugin(s) in "+(end-start)+"ms");
}
return (String[])plugins.toArray(new String[0]);
}
private synchronized String[] getDetectedFeatures() {
if (features == null)
return detectFeatures();
else
return (String[])features.toArray(new String[0]);
}
private synchronized String[] getDetectedPlugins() {
if (plugins == null)
return detectPlugins();
else
return (String[])plugins.toArray(new String[0]);
}
private URL getResolvedURL() {
return resolvedURL;
}
private void computeChangeStamp() {
computeFeaturesChangeStamp();
computePluginsChangeStamp();
changeStamp = resolvedURL.hashCode() ^ featuresChangeStamp ^ pluginsChangeStamp;
changeStampIsValid = true;
}
private synchronized void computeFeaturesChangeStamp() {
if (featuresChangeStampIsValid)
return;
String[] features = getFeatures();
featuresChangeStamp = computeStamp(features);
featuresChangeStampIsValid = true;
if (DEBUG)
debug(resolvedURL.toString()+" feature stamp: "+featuresChangeStamp+((featuresChangeStamp==lastFeaturesChangeStamp)?" [no changes]":" [was "+lastFeaturesChangeStamp+"]"));
}
private synchronized void computePluginsChangeStamp() {
if (pluginsChangeStampIsValid)
return;
String[] plugins = getPlugins();
pluginsChangeStamp = computeStamp(plugins);
pluginsChangeStampIsValid = true;
if (DEBUG)
debug(resolvedURL.toString()+" plugin stamp: "+pluginsChangeStamp+((pluginsChangeStamp==lastPluginsChangeStamp)?" [no changes]":" [was "+lastPluginsChangeStamp+"]"));
}
private long computeStamp(String[] targets) {
long result = 0;
if (!supportsDetection(resolvedURL)) {
// FIXME: this path should not be executed until we support running
// from an arbitrary URL (in particular from http server). For
// now just compute stamp across the list of names. Eventually
// when general URLs are supported we need to do better (factor
// in at least the existence of the target). However, given this
// code executes early on the startup sequence we need to be
// extremely mindful of performance issues.
for (int i=0; i<targets.length; i++)
result ^= targets[i].hashCode();
if (DEBUG)
debug("*WARNING* computing stamp using URL hashcodes only");
} else {
// compute stamp across local targets
String rootPath = resolvedURL.getFile().replace('/',File.separatorChar);
if (!rootPath.endsWith(File.separator))
rootPath += File.separator;
File rootFile = new File(rootPath);
if (rootFile.exists()) {
File f = null;
for (int i=0; i<targets.length; i++) {
f = new File(rootFile,targets[i]);
if (f.exists())
result ^= f.getAbsolutePath().hashCode() ^ f.lastModified() ^ f.length();
}
}
}
return result;
}
private boolean isExternallyLinkedSite() {
return (linkFileName!=null && !linkFileName.trim().equals(""));
}
private synchronized void refresh() {
// reset computed values. Will be updated on next access.
lastChangeStamp = changeStamp;
lastFeaturesChangeStamp = featuresChangeStamp;
lastPluginsChangeStamp = pluginsChangeStamp;
changeStampIsValid = false;
featuresChangeStampIsValid = false;
pluginsChangeStampIsValid = false;
features = null;
plugins = null;
}
}
public class SitePolicy implements IPlatformConfiguration.ISitePolicy {
private int type;
private String[] list;
private SitePolicy() {
}
private SitePolicy(int type, String[] list) {
if (type != ISitePolicy.USER_INCLUDE
&& type != ISitePolicy.USER_EXCLUDE)
throw new IllegalArgumentException();
this.type = type;
if (list == null)
this.list = new String[0];
else
this.list = list;
}
/*
* @see ISitePolicy#getType()
*/
public int getType() {
return type;
}
/*
* @see ISitePolicy#getList()
*/
public String[] getList() {
return list;
}
/*
* @see ISitePolicy#setList(String[])
*/
public synchronized void setList(String[] list) {
if (list == null)
this.list = new String[0];
else
this.list = list;
}
}
private class FeatureEntry implements IPlatformConfiguration.IFeatureEntry {
private String id;
private String version;
private String application;
private URL root;
private FeatureEntry(String id, String version, String application, URL root) {
if (id == null)
throw new IllegalArgumentException();
this.id = id;
this.version = version;
this.application = application;
this.root = root;
}
/*
* @see IFeatureEntry#getFeatureIdentifier()
*/
public String getFeatureIdentifier() {
return id;
}
/*
* @see IFeatureEntry#getFeatureVersion()
*/
public String getFeatureVersion() {
return version;
}
/*
* @see IFeatureEntry#getFeatureApplication()
*/
public String getFeatureApplication() {
return application;
}
/*
* @see IFeatureEntry#getFeatureRootURL()
*/
public URL getFeatureRootURL() {
return root;
}
}
private class VersionedIdentifier {
private String identifier = "";
private int major = 0;
private int minor = 0;
private int service = 0;
private String qualifier = "";
private static final String VER_SEPARATOR = ".";
private static final String ID_SEPARATOR = "_";
public VersionedIdentifier(String s) {
if (s==null || (s=s.trim()).equals(""))
return;
int loc = s.lastIndexOf(ID_SEPARATOR);
if (loc != -1) {
this.identifier = s.substring(0, loc);
String version = s.substring(loc+1);
parseVersion(version);
} else
this.identifier = s;
}
public boolean identifierEquals(String id) {
if (id == null)
return identifier == null;
else
return id.equals(identifier);
}
public int compareVersion(VersionedIdentifier id) {
if (id == null) {
if (major==0 && minor==0 && service==0) return -1;
else return 1;
}
if (major > id.major) return 1;
if (major < id.major) return -1;
if (minor > id.minor) return 1;
if (minor < id.minor) return -1;
if (service > id.service) return 1;
if (service < id.service) return -1;
return compareQualifiers(qualifier, id.qualifier);
}
private int compareQualifiers(String q1, String q2) {
int result = q1.compareTo(q2);
if (result<0)
return -1;
else if (result>0)
return 1;
else
return 0;
}
private void parseVersion(String v) {
if( v == null || (v=v.trim()).equals(""))
return;
try{
StringTokenizer st = new StringTokenizer(v, VER_SEPARATOR);
ArrayList elements = new ArrayList(4);
while(st.hasMoreTokens()) {
elements.add(st.nextToken());
}
if (elements.size()>=1) this.major = (new Integer((String)elements.get(0))).intValue();
if (elements.size()>=2) this.minor = (new Integer((String)elements.get(1))).intValue();
if (elements.size()>=3) this.service = (new Integer((String)elements.get(2))).intValue();
if (elements.size()>=4) this.qualifier = removeWhiteSpace((String)elements.get(3));
} catch (Exception e) { // use what we got so far
}
}
private String removeWhiteSpace(String s) {
char[] chars = s.trim().toCharArray();
boolean whitespace = false;
for(int i=0; i<chars.length; i++) {
if (Character.isWhitespace(chars[i])) {
chars[i] = '_';
whitespace = true;
}
}
return whitespace ? new String(chars) : s;
}
}
private PlatformConfiguration(String configArg) throws IOException {
this.sites = new HashMap();
this.externalLinkSites = new HashMap();
this.cfgdFeatures = new HashMap();
this.bootPlugins = new HashMap();
// Determine configuration URL to use (based on command line argument)
URL configURL = getConfigurationURL(configArg);
// initialize configuration
initializeCurrent(configURL);
// pick up any first-time default settings relative to selected config location
loadInitializationAttributes(configLocation);
// FIXME: support for "safe mode"
// 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();
// compute differences between configuration and actual content of the sites
// (base sites and link sites)
computeChangeStamp();
}
PlatformConfiguration(URL url) throws IOException {
this.sites = new HashMap();
this.externalLinkSites = new HashMap();
this.cfgdFeatures = new HashMap();
this.bootPlugins = new HashMap();
initialize(url);
}
/*
* @see IPlatformConfiguration#createSiteEntry(URL, ISitePolicy)
*/
public ISiteEntry createSiteEntry(URL url, ISitePolicy policy) {
return new PlatformConfiguration.SiteEntry(url, policy, this);
}
/*
* @see IPlatformConfiguration#createSitePolicy(int, String[])
*/
public ISitePolicy createSitePolicy(int type, String[] list) {
return new PlatformConfiguration.SitePolicy(type, list);
}
/*
* @see IPlatformConfiguration#createFeatureEntry(String, String, String, URL)
*/
public IFeatureEntry createFeatureEntry(
String id,
String version,
String application,
URL root) {
return new PlatformConfiguration.FeatureEntry(id, version, 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;
sites.put(key, 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);
}
/*
* @see IPlatformConfiguration#getConfiguredSites()
*/
public ISiteEntry[] getConfiguredSites() {
if (sites.size() == 0)
return new ISiteEntry[0];
return (ISiteEntry[]) sites.values().toArray(new ISiteEntry[0]);
}
/*
* @see IPlatformConfiguration#findConfiguredSite(URL)
*/
public ISiteEntry findConfiguredSite(URL url) {
if (url == null)
return null;
String key = url.toExternalForm();
ISiteEntry result = (ISiteEntry) sites.get(key);
if (result == null) // retry with decoded URL string
result = (ISiteEntry) sites.get(URLDecoder.decode(key));
return result;
}
/*
* @see IPlatformConfiguration#configureFeatureEntry(IFeatureEntry)
*/
public synchronized void configureFeatureEntry(IFeatureEntry entry) {
if (entry == null)
return;
String key = entry.getFeatureIdentifier();
if (key == null)
return;
cfgdFeatures.put(key, entry);
}
/*
* @see IPlatformConfiguration#unconfigureFeatureEntry(IFeatureEntry)
*/
public synchronized void unconfigureFeatureEntry(IFeatureEntry entry) {
if (entry == null)
return;
String key = entry.getFeatureIdentifier();
if (key == null)
return;
cfgdFeatures.remove(key);
}
/*
* @see IPlatformConfiguration#getConfiguredFeatureEntries()
*/
public IFeatureEntry[] getConfiguredFeatureEntries() {
if (cfgdFeatures.size() == 0)
return new IFeatureEntry[0];
return (IFeatureEntry[]) cfgdFeatures.values().toArray(new IFeatureEntry[0]);
}
/*
* @see IPlatformConfiguration#findConfiguredFeatureEntry(String)
*/
public IFeatureEntry findConfiguredFeatureEntry(String id) {
if (id == null)
return null;
return (IFeatureEntry) cfgdFeatures.get(id);
}
/*
* @see IPlatformConfiguration#getConfigurationLocation()
*/
public URL getConfigurationLocation() {
return configLocation;
}
/*
* @see IPlatformConfiguration#getChangeStamp()
*/
public long getChangeStamp() {
if (!changeStampIsValid)
computeChangeStamp();
return changeStamp;
}
/*
* @see IPlatformConfiguration#getFeaturesChangeStamp()
*/
public long getFeaturesChangeStamp() {
if (!featuresChangeStampIsValid)
computeFeaturesChangeStamp();
return featuresChangeStamp;
}
/*
* @see IPlatformConfiguration#getPluginsChangeStamp()
*/
public long getPluginsChangeStamp() {
if (!pluginsChangeStampIsValid)
computePluginsChangeStamp();
return pluginsChangeStamp;
}
/*
* @see IPlatformConfiguration#getApplicationIdentifier()
*/
public String getApplicationIdentifier() {
if (featuresChangeStamp != lastFeaturesChangeStamp) {
// we have detected feature changes ... see if we need to reconcile
boolean update = !(cmdNoUpdate || (cmdDev && !cmdUpdate));
if (update)
return RECONCILER_APP;
}
// "normal" startup ... run specified application
return getApplicationIdentifierInternal();
}
private String getApplicationIdentifierInternal() {
if (cmdApplication != null) // application was specified
return cmdApplication;
else {
// if -feature was not specified use the default feature
String feature = cmdFeature;
if (feature == null)
feature = defaultFeature;
// 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() {
if (cmdFeature != null) // -feature was specified on command line
return cmdFeature;
// feature was not specified on command line
if (defaultFeature != null)
return defaultFeature; // return customized default if set
else
return DEFAULT_FEATURE_ID; // return hardcoded default
}
/*
* @see IPlatformConfiguration#getPluginPath()
*/
public URL[] getPluginPath() {
ArrayList path = new ArrayList();
if (DEBUG)
debug("computed plug-in path:");
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);
if (DEBUG)
debug(" "+pathURL.toString());
} catch(MalformedURLException e) {
if (DEBUG)
debug(" bad URL: "+e);
}
}
}
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() {
// FIXME: support r/o configuration
return true;
}
/*
* @see IPlatformConfiguration#isTransient()
*/
public boolean isTransient() {
return transientConfig;
}
/*
* @see IPlatformConfiguration#isTransient(boolean)
*/
public void isTransient(boolean value) {
if (this != BootLoader.getCurrentPlatformConfiguration())
transientConfig = 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.
lastChangeStamp = changeStamp;
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(Policy.bind("cfig.unableToSave.noURL"));
URLConnection uc = url.openConnection();
uc.setDoOutput(true);
OutputStream os = null;
try {
os = uc.getOutputStream();
} catch (UnknownServiceException e) {
// retry with direct file i/o
if (!url.getProtocol().equals("file"))
throw e;
File cfigFile = new File(url.getFile().replace('/',File.separatorChar));
File cfigDir = cfigFile.getParentFile();
if (cfigDir!=null) {
cfigDir.mkdirs();
}
os = new FileOutputStream(cfigFile);
}
PrintWriter w = new PrintWriter(os);
try {
write(w);
} finally {
w.close();
}
}
URL getPluginPath(String pluginId) {
return getPluginPath(pluginId, null);
}
// This method is currently public because it is called by InternalPlatform.
// However, it is NOT intended as API
// FIXME: restructure the code so that InternalBootLoader passes the
// required information to InternalPlatform
public URL getPluginPath(String pluginId, String versionId) {
// return the plugin path element for the specified plugin. This method
// is used during boot processing to obtain "kernel" plugins whose
// class loaders must be created prior to the plugin registry being
// available (ie. loaders needed to create the plugin registry)
// must be created
ISiteEntry[] sites = getConfiguredSites();
if (sites == null || sites.length == 0)
return null;
// for now look for the "latest" version of the requested plugin
// using naming convention of the installer and the policy set for
// the site
// FIXME: the current code in this method implements the R1.0 "best guess"
// algorithm
VersionedIdentifier savedVid = new VersionedIdentifier(null);
String savedEntry = null;
URL savedURL = null;
for (int j=0; j<sites.length; j++) {
String[] plugins = sites[j].getPlugins();
for (int i=0; plugins!=null && i<plugins.length; i++) {
// look for best match.
// The entries are in the form <path>/<pluginDir>/plugin.xml
// look for -------------------------^
int ix = findEntrySeparator(plugins[i],2); // second from end
if (ix == -1)
continue; // bad entry ... skip
String pluginDir = plugins[i].substring(ix+1);
ix = pluginDir.indexOf("/");
if (ix != -1)
pluginDir = pluginDir.substring(0,ix);
if (pluginDir.equals(""))
continue; // bad entry ... skip
VersionedIdentifier vid = new VersionedIdentifier(pluginDir);
if (vid.identifierEquals(pluginId)) {
if (vid.compareVersion(savedVid) >= 0) {
savedVid = vid;
savedEntry = plugins[i];
savedURL = ((SiteEntry)sites[j]).getResolvedURL();
}
}
}
}
if (savedEntry == null)
return null;
// callers are expecting a directory URL
if (!savedEntry.endsWith("/")) {
int ix = savedEntry.lastIndexOf("/");
if (ix == -1)
return null; // bad entry
savedEntry = savedEntry.substring(0,ix+1); // include trailing separator
}
try {
return new URL(savedURL,savedEntry);
} catch(MalformedURLException e) {
return null;
}
}
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 r10plugins plugin-path URL as passed on the BootLoader.run(...)
* or BootLoader.startup(...) method. Supported for R1.0 compatibility
* @param r10apps application identifies as passed on the BootLoader.run(...)
* method. Supported for R1.0 compatibility.
*/
static synchronized String[] startup(String[] cmdArgs, URL r10plugins, String r10app) throws Exception {
// initialize command line settings
cmdConfiguration = null;
cmdFeature = null;
cmdApplication = null;
cmdPlugins = null;
cmdPlugins = r10plugins; // R1.0 compatibility
cmdApplication = r10app; // R1.0 compatibility
String[] passthruArgs = processCommandLine(cmdArgs);
// determine launch mode
if (cmdPlugins != null) {
// R1.0 compatibility mode ... explicit plugin-path was specified.
// Convert the plugins path into a configuration
try {
cmdConfiguration = createConfigurationFromPlugins(cmdPlugins, cmdConfiguration);
} catch (Exception e) {
if (DEBUG)
debug("Unable to use specified plugin-path: "+e);
}
}
// create current configuration
if (currentPlatformConfiguration == null)
currentPlatformConfiguration = new PlatformConfiguration(cmdConfiguration);
// check if we will be forcing reconciliation
passthruArgs = checkForFeatureChanges(passthruArgs, currentPlatformConfiguration);
return passthruArgs;
}
static synchronized void shutdown() throws IOException {
// save platform configuration
PlatformConfiguration config = getCurrent();
if (config != null) {
try {
config.save();
} catch(IOException e) {
if (DEBUG)
debug("Unable to save configuration "+e.toString());
// will recover on next startup
}
}
}
private void initializeCurrent(URL url) throws IOException {
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// NOTE:
// Due to Eclipse class loader structure and class
// visibility, the configuration file search is
// implemented in 2 different classes:
// org.eclipse.core.internal.boot.PlatformConfiguration
// getConfigurationURL(String)
// initializeCurrent(URL)
// org.eclipse.core.launcher.Main
// getConfigurationURL(String)
// loadConfiguration(URL)
// If you are making changes to this method make sure
// the change is applied in both places
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// URL of configuration file was specified ... just use it, or create one in specified
// location if it does not exist
if (url != null) {
try {
load(url);
if (DEBUG)
debug("Using configuration " + url.toString());
} catch(IOException e) {
ISitePolicy defaultPolicy = createSitePolicy(DEFAULT_POLICY_TYPE, DEFAULT_POLICY_LIST);
ISiteEntry defaultSite = createSiteEntry(BootLoader.getInstallURL(), defaultPolicy);
configureSite(defaultSite);
if (DEBUG)
debug("Creating configuration " + url.toString());
}
configLocation = url;
verifyPath(configLocation);
return;
}
// URL was not specified. Default behavior is to look for configuration file in
// USER.DIR then USER.HOME then COMMON
URL userdirURL = null;
try {
String tmp = System.getProperty("user.dir");
if (!tmp.endsWith(File.separator))
tmp += File.separator;
userdirURL = new URL("file:" + tmp.replace(File.separatorChar,'/') + ECLIPSEDIR + "/" + INSTALL + "/" + CONFIG_FILE);
load(userdirURL);
configLocation = userdirURL;
if (DEBUG)
debug("Using configuration " + configLocation.toString());
return;
} catch (IOException e) {
if (DEBUG)
debug("Unable to load configuration from USER.DIR " + e);
}
URL userhomeURL = null;
try {
String tmp = System.getProperty("user.home");
if (!tmp.endsWith(File.separator))
tmp += File.separator;
userhomeURL = new URL("file:" + tmp.replace(File.separatorChar,'/') + ECLIPSEDIR + "/" + INSTALL + "/" + CONFIG_FILE);
load(userhomeURL);
configLocation = userhomeURL;
if (DEBUG)
debug("Using configuration " + configLocation.toString());
return;
} catch (IOException e) {
if (DEBUG)
debug("Unable to load configuration from USER.HOME " + e);
}
URL commonURL = null;
try {
commonURL = new URL(BootLoader.getInstallURL(), INSTALL + "/" + CONFIG_FILE);
load(commonURL);
configLocation = commonURL;
if (DEBUG)
debug("Using configuration " + configLocation.toString());
return;
} catch (IOException e) {
if (DEBUG)
debug("Unable to load configuration from COMMON " + e);
}
// No configuration files found. Assume COMMON (note: this may not be a r/w
// location so we may not be able to save the configuration on shutdown. This is OK
// for the default case)
ISitePolicy defaultPolicy = createSitePolicy(DEFAULT_POLICY_TYPE, DEFAULT_POLICY_LIST);
URL siteURL = null;
try {
siteURL = new URL(PlatformURLBaseConnection.PLATFORM_URL_STRING); // try using platform-relative URL
} catch (MalformedURLException e) {
siteURL = BootLoader.getInstallURL(); // ensure we come up ... use absolute file URL
}
ISiteEntry defaultSite = createSiteEntry(siteURL, defaultPolicy);
configureSite(defaultSite);
configLocation = commonURL;
verifyPath(configLocation);
if (DEBUG)
debug("Creating configuration " + configLocation.toString());
}
private void initialize(URL url) throws IOException {
if (url == null) {
if (DEBUG)
debug("Creating empty configuration");
return;
}
load(url);
configLocation = url;
if (DEBUG)
debug("Using configuration " + configLocation.toString());
}
private void computeChangeStamp() {
computeFeaturesChangeStamp();
computePluginsChangeStamp();
changeStamp = featuresChangeStamp ^ pluginsChangeStamp;
changeStampIsValid = true;
}
private void computeFeaturesChangeStamp() {
if (featuresChangeStampIsValid)
return;
long result = 0;
ISiteEntry[] sites = getConfiguredSites();
for (int i=0; i<sites.length; i++) {
result ^= sites[i].getFeaturesChangeStamp();
}
featuresChangeStamp = result;
featuresChangeStampIsValid = true;
}
private void computePluginsChangeStamp() {
if (pluginsChangeStampIsValid)
return;
long result = 0;
ISiteEntry[] sites = getConfiguredSites();
for (int i=0; i<sites.length; i++) {
result ^= sites[i].getPluginsChangeStamp();
}
pluginsChangeStamp = result;
pluginsChangeStampIsValid = true;
}
private void configureExternalLinks() {
if (!supportsDetection(configLocation))
return;
File cfigFile = new File(configLocation.getFile());
File linkDir = new File(cfigFile.getParentFile(),LINKS+File.separator);
File[] links = linkDir.listFiles();
if (links==null || links.length==0) {
if (DEBUG)
debug("No links detected in "+linkDir.getAbsolutePath());
return;
}
for (int i=0; i<links.length; i++) {
if (links[i].isDirectory())
continue;
if (DEBUG)
debug("Link file "+links[i].getAbsolutePath());
Properties props = new Properties();
FileInputStream is = null;
try {
is = new FileInputStream(links[i]);
props.load(is);
configureExternalLinkSites(links[i],props);
} catch(IOException e) {
if (DEBUG)
debug(" unable to load link file "+e);
continue;
} finally {
if (is != null) {
try {
is.close();
} catch(IOException e) {
}
}
}
}
}
private void configureExternalLinkSites(File linkFile, Properties props) {
String path = props.getProperty(LINK_PATH);
if (path==null) {
if (DEBUG)
debug(" no path definition");
return;
}
String[] links = stringListToArray(path,",");
String link;
boolean updateable;
URL siteURL;
SiteEntry linkSite;
ISitePolicy linkSitePolicy = createSitePolicy(DEFAULT_POLICY_TYPE, DEFAULT_POLICY_LIST);
for (int i=0; i<links.length; i++) {
updateable = false;
if (links[i].startsWith(LINK_READ+" ")) {
link = links[i].substring(2).trim();
} else if (links[i].startsWith(LINK_READ_WRITE+" ")) {
updateable = true;
link = links[i].substring(3).trim();
} else {
link = links[i];
}
try {
link = "file:"+link;
siteURL = new URL(link);
} catch(MalformedURLException e) {
if (DEBUG)
debug(" bad URL "+e);
continue;
}
linkSite = (SiteEntry) createSiteEntry(siteURL, linkSitePolicy);
linkSite.updateable = updateable;
linkSite.linkFileName = linkFile.getAbsolutePath();
SiteEntry lastLinkSite = (SiteEntry) externalLinkSites.get(siteURL);
if (lastLinkSite != null) {
// restore previous change stamps
linkSite.lastChangeStamp = lastLinkSite.lastChangeStamp;
linkSite.lastFeaturesChangeStamp = lastLinkSite.lastFeaturesChangeStamp;
linkSite.lastPluginsChangeStamp = lastLinkSite.lastPluginsChangeStamp;
}
configureSite(linkSite);
if (DEBUG)
debug(" "+(updateable?"R/W -> ":"R/O -> ")+siteURL.toString());
}
}
private void load(URL url) throws IOException {
if (url == null)
throw new IOException(Policy.bind("cfig.unableToLoad.noURL"));
// try to load saved configuration file
Properties props = new Properties();
InputStream is = null;
try {
is = url.openStream();
props.load(is);
// check to see if we have complete config file
if (!EOF.equals(props.getProperty(EOF))) {
throw new IOException(Policy.bind("cfig.unableToLoad.incomplete",url.toString()));
}
} finally {
if (is!=null) {
try {
is.close();
} catch(IOException e) {
}
}
}
// check version
String v = props.getProperty(CFG_VERSION);
if (!VERSION.equals(v)) {
throw new IOException(Policy.bind("cfig.badVersion",v));
}
// load simple properties
defaultFeature = loadAttribute(props, CFG_FEATURE_ENTRY_DEFAULT, null);
String flag = loadAttribute(props, CFG_TRANSIENT, null);
if (flag != null) {
if (flag.equals("true"))
transientConfig = true;
else
transientConfig = false;
}
String stamp = loadAttribute(props, CFG_STAMP, null);
if (stamp != null) {
try {
lastChangeStamp = Long.parseLong(stamp);
} catch(NumberFormatException e) {
}
}
stamp = loadAttribute(props, CFG_FEATURE_STAMP, null);
if (stamp != null) {
try {
lastFeaturesChangeStamp = Long.parseLong(stamp);
} catch(NumberFormatException e) {
}
}
stamp = loadAttribute(props, CFG_PLUGIN_STAMP, null);
if (stamp != null) {
try {
lastPluginsChangeStamp = Long.parseLong(stamp);
} catch(NumberFormatException e) {
}
}
// load bootstrap entries
String[] ids = getBootstrapPluginIdentifiers();
for (int i=0; i<ids.length; i++) {
bootPlugins.put(ids[i], loadAttribute(props, CFG_BOOT_PLUGIN + "." + ids[i], null));
}
// load feature entries
IFeatureEntry fe = loadFeatureEntry(props, CFG_FEATURE_ENTRY+".0", null);
for (int i=1; fe != null; i++) {
configureFeatureEntry(fe);
fe = loadFeatureEntry(props, CFG_FEATURE_ENTRY+"."+i, null);
}
// load site properties
SiteEntry se = (SiteEntry) loadSite(props, CFG_SITE+".0", null);
for (int i=1; se != null; i++) {
if (!se.isExternallyLinkedSite())
configureSite(se);
else
// remember external link site state, but do not configure
externalLinkSites.put(se.getURL(),se);
se = (SiteEntry) loadSite(props, CFG_SITE+"."+i, null);
}
}
private ISiteEntry loadSite(Properties props, String name, ISiteEntry dflt) {
String urlString = loadAttribute(props, name+"."+CFG_URL, null);
if (urlString == null)
return dflt;
URL url = null;
try {
url = new URL(urlString);
} catch(MalformedURLException e) {
return dflt;
}
int policyType;
String[] policyList;
String typeString = loadAttribute(props, name+"."+CFG_POLICY, null);
if (typeString == null) {
policyType = DEFAULT_POLICY_TYPE;
policyList = DEFAULT_POLICY_LIST;
} else {
int i;
for (i=0; i<CFG_POLICY_TYPE.length; i++) {
if (typeString.equals(CFG_POLICY_TYPE[i])) {
break;
}
}
if (i>=CFG_POLICY_TYPE.length) {
policyType = DEFAULT_POLICY_TYPE;
policyList = DEFAULT_POLICY_LIST;
} else {
policyType = i;
policyList = loadListAttribute(props, name+"."+CFG_LIST, new String[0]);
}
}
ISitePolicy sp = createSitePolicy(policyType, policyList);
SiteEntry site = (SiteEntry) createSiteEntry(url,sp);
String stamp = loadAttribute(props, name+"."+CFG_STAMP, null);
if (stamp != null) {
try {
site.lastChangeStamp = Long.parseLong(stamp);
} catch(NumberFormatException e) {
}
}
stamp = loadAttribute(props, name+"."+CFG_FEATURE_STAMP, null);
if (stamp != null) {
try {
site.lastFeaturesChangeStamp = Long.parseLong(stamp);
} catch(NumberFormatException e) {
}
}
stamp = loadAttribute(props, name+"."+CFG_PLUGIN_STAMP, null);
if (stamp != null) {
try {
site.lastPluginsChangeStamp = Long.parseLong(stamp);
} catch(NumberFormatException e) {
}
}
String flag = loadAttribute(props, name+"."+CFG_UPDATEABLE, null);
if (flag != null) {
if (flag.equals("true"))
site.updateable = true;
else
site.updateable = false;
}
String linkname = loadAttribute(props, name+"."+CFG_LINK_FILE, null);
if (linkname != null && !linkname.equals("")) {
site.linkFileName = linkname.replace('/',File.separatorChar);
}
return site;
}
private IFeatureEntry loadFeatureEntry(Properties props, String name, IFeatureEntry dflt) {
String id = loadAttribute(props, name+"."+CFG_FEATURE_ENTRY_ID, null);
if (id == null)
return dflt;
String version = loadAttribute(props, name+"."+CFG_FEATURE_ENTRY_VERSION, null);
String application = loadAttribute(props, name+"."+CFG_FEATURE_ENTRY_APPLICATION, null);
String rootString = loadAttribute(props, name+"."+CFG_FEATURE_ENTRY_ROOT, null);
URL root = null;
if (rootString != null)
try {
root = new URL(rootString);
} catch (MalformedURLException e) {
}
return createFeatureEntry(id, version, application, root);
}
private String[] loadListAttribute(Properties props, String name, String[] dflt) {
ArrayList list = new ArrayList();
String value = loadAttribute(props, name+".0",null);
if (value == null)
return dflt;
for (int i=1; value != null; i++) {
loadListAttributeSegment(list, value);
value = loadAttribute(props, name+"."+i, null);
}
return (String[])list.toArray(new String[0]);
}
private void loadListAttributeSegment(ArrayList list,String value) {
if (value==null) return;
StringTokenizer tokens = new StringTokenizer(value, ",");
String token;
while (tokens.hasMoreTokens()) {
token = tokens.nextToken().trim();
if (!token.equals(""))
list.add(token);
}
return;
}
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(URL url) {
if (url == null)
return;
if (defaultFeature != null)
return; // already set
// load any initialization attributes. These become the initial default settings
// for critical attributes (eg. default primary feature) supplied by the packaging team.
// Once these are reflected in the configuration they cannot be changed via the
// initialization mechanism
Properties initProps = new Properties();
InputStream is = null;
try {
URL initURL = new URL(url, CONFIG_FILE_INIT);
is = initURL.openStream();
initProps.load(is);
} catch(IOException e) {
return; // could not load "first-time" settings
} finally {
if (is!=null) {
try {
is.close();
} catch(IOException e) {
}
}
}
// use "first-time" settings if not already set
defaultFeature = loadAttribute(initProps, INIT_DEFAULT_FEATURE_ID, null);
if (defaultFeature != null) {
String application = loadAttribute(initProps, INIT_DEFAULT_FEATURE_APPLICATION, null);
IFeatureEntry fe = createFeatureEntry(defaultFeature, null, application, null);
configureFeatureEntry(fe);
}
}
private boolean isReadWriteLocation(URL url) {
if (!url.getProtocol().equals("file"))
return false;
File f = new File(url.getFile());
return f.canWrite();
}
private void write(PrintWriter w) {
// write header
w.println("# "+(new Date()).toString());
writeAttribute(w, CFG_VERSION, VERSION);
if (transientConfig)
writeAttribute(w,CFG_TRANSIENT,"true");
w.println("");
// write global attributes
writeAttribute(w,CFG_STAMP,Long.toString(getChangeStamp()));
writeAttribute(w,CFG_FEATURE_STAMP,Long.toString(getFeaturesChangeStamp()));
writeAttribute(w,CFG_PLUGIN_STAMP,Long.toString(getPluginsChangeStamp()));
// write out bootstrap entries
String[] ids = getBootstrapPluginIdentifiers();
for (int i=0; i<ids.length; i++) {
String location = (String) bootPlugins.get(ids[i]);
if (location != null)
writeAttribute(w, CFG_BOOT_PLUGIN + "." + ids[i], location);
}
// write out feature entries
w.println("");
writeAttribute(w,CFG_FEATURE_ENTRY_DEFAULT,defaultFeature);
IFeatureEntry[] feats = getConfiguredFeatureEntries();
for (int i=0; i<feats.length; i++) {
writeFeatureEntry(w, CFG_FEATURE_ENTRY + "." + Integer.toString(i), feats[i]);
}
// write out site entries
SiteEntry[] list = (SiteEntry[]) sites.values().toArray(new SiteEntry[0]);
for (int i = 0; i < list.length; i++) {
writeSite(w, CFG_SITE + "." + Integer.toString(i), list[i]);
}
// write end-of-file marker
writeAttribute(w, EOF, EOF);
}
private void writeSite(PrintWriter w, String id, SiteEntry entry) {
// write site separator
w.println("");
// write out site settings
writeAttribute(w, id + "." + CFG_URL, entry.getURL().toString());
writeAttribute(w, id + "." + CFG_STAMP,Long.toString(entry.getChangeStamp()));
writeAttribute(w, id + "." + CFG_FEATURE_STAMP,Long.toString(entry.getFeaturesChangeStamp()));
writeAttribute(w, id + "." + CFG_PLUGIN_STAMP,Long.toString(entry.getPluginsChangeStamp()));
writeAttribute(w, id + "." + CFG_UPDATEABLE, entry.updateable?"true":"false");
if (entry.linkFileName != null && !entry.linkFileName.trim().equals(""))
writeAttribute(w, id + "." + CFG_LINK_FILE, entry.linkFileName.trim().replace(File.separatorChar,'/'));
// write out site policy
int type = entry.getSitePolicy().getType();
String typeString = CFG_POLICY_TYPE_UNKNOWN;
try {
typeString = CFG_POLICY_TYPE[type];
} catch (IndexOutOfBoundsException e) {
}
writeAttribute(w, id + "." + CFG_POLICY, typeString);
writeListAttribute(w, id + "." + CFG_LIST, entry.getSitePolicy().getList());
}
private void writeFeatureEntry(PrintWriter w, String id, IFeatureEntry entry) {
// write feature entry separator
w.println("");
// write out feature entry settings
writeAttribute(w, id + "." + CFG_FEATURE_ENTRY_ID, entry.getFeatureIdentifier());
writeAttribute(w, id + "." + CFG_FEATURE_ENTRY_VERSION, entry.getFeatureVersion());
writeAttribute(w, id + "." + CFG_FEATURE_ENTRY_APPLICATION, entry.getFeatureApplication());
writeAttribute(w, id + "." + CFG_FEATURE_ENTRY_ROOT, entry.getFeatureRootURL()==null ? null : entry.getFeatureRootURL().toExternalForm());
}
private void writeListAttribute(PrintWriter w, String id, String[] list) {
if (list == null || list.length == 0)
return;
String value = "";
int listLen = 0;
int listIndex = 0;
for (int i = 0; i < list.length; i++) {
if (listLen != 0)
value += ",";
else
value = "";
value += list[i];
if (++listLen >= CFG_LIST_LENGTH) {
writeAttribute(w, id + "." + Integer.toString(listIndex++), value);
listLen = 0;
}
}
if (listLen != 0)
writeAttribute(w, id + "." + Integer.toString(listIndex), value);
}
private void writeAttribute(PrintWriter w, String id, String value) {
if (value==null || value.trim().equals(""))
return;
w.println(id + "=" + escapedValue(value));
}
private String escapedValue(String value) {
// if required, escape property values as \\uXXXX
StringBuffer buf = new StringBuffer(value.length()*2); // assume expansion by less than factor of 2
for (int i=0; i<value.length(); i++) {
char character = value.charAt(i);
if (character == '\\'
|| character == '\t'
|| character == '\r'
|| character == '\n'
|| character == '\f') {
// handle characters requiring leading \
buf.append('\\');
buf.append(character);
} else if ((character < 0x0020) || (character > 0x007e)) {
// handle characters outside base range (encoded)
buf.append('\\');
buf.append('u');
buf.append(HEX[(character >> 12) & 0xF]); // first nibble
buf.append(HEX[(character >> 8) & 0xF]); // second nibble
buf.append(HEX[(character >> 4) & 0xF]); // third nibble
buf.append(HEX[character & 0xF]); // fourth nibble
} else {
// handle base characters
buf.append(character);
}
}
return buf.toString();
}
private static String[] checkForFeatureChanges(String[] args, PlatformConfiguration cfg) {
String original = cfg.getApplicationIdentifierInternal();
String actual = cfg.getApplicationIdentifier();
if (original.equals(actual))
// base startup of specified application
return args;
else {
// Will run reconciler.
// Re-insert -application argument with original app
String[] newArgs = new String[args.length+2];
newArgs[0] = CMD_APPLICATION;
newArgs[1] = original;
System.arraycopy(args,0,newArgs,2,args.length);
return newArgs;
}
}
private static String[] processCommandLine(String[] args) throws Exception {
int[] configArgs = new int[100];
configArgs[0] = -1; // need to initialize the first element to something that could not be an index.
int configArgIndex = 0;
for (int i = 0; i < args.length; i++) {
boolean found = false;
// check for args without parameters (i.e., a flag arg)
// look for the update flag
if (args[i].equalsIgnoreCase(CMD_UPDATE)) {
cmdUpdate = true;
found = true;
}
// look for the no-update flag
if (args[i].equalsIgnoreCase(CMD_NO_UPDATE)) {
cmdNoUpdate = true;
found = true;
}
// look for the development mode flag ... triggers no-update
if (args[i].equalsIgnoreCase(CMD_DEV)) {
cmdDev = true;
continue; // do not remove from command line
}
if (found) {
configArgs[configArgIndex++] = i;
continue;
}
// check for args with parameters. If we are at the last argument or if the next one
// has a '-' as the first character, then we can't have an arg with a parm so continue.
if (i == args.length - 1 || args[i + 1].startsWith("-")) {
continue;
}
String arg = args[++i];
// look for the platform configuration to use.
if (args[i - 1].equalsIgnoreCase(CMD_CONFIGURATION)) {
found = true;
cmdConfiguration = arg;
}
// look for the feature to use for customization.
if (args[i - 1].equalsIgnoreCase(CMD_FEATURE)) {
found = true;
cmdFeature = arg;
}
// look for the application to run. Only use the value from the
// command line if the application identifier was not explicitly
// passed on BootLoader.run(...) invocation.
if (args[i - 1].equalsIgnoreCase(CMD_APPLICATION)) {
found = true;
if (cmdApplication == null)
cmdApplication = arg;
}
// R1.0 compatibility
// look for the plugins location to use. Only use the value from the
// command line if the plugins location was not explicitly passed on
// BootLoader.run(...) or BootLoader.startup(...) invocation.
if (args[i - 1].equalsIgnoreCase(CMD_PLUGINS)) {
found = true;
// if the arg can be made into a URL use it. Otherwise assume that
// it is a file path so make a file URL.
try {
if (cmdPlugins == null)
cmdPlugins = new URL(arg);
} catch (MalformedURLException e) {
try {
cmdPlugins = new URL("file:" + arg);
} catch (MalformedURLException e2) {
}
}
}
// done checking for args. Remember where an arg was found
if (found) {
configArgs[configArgIndex++] = i - 1;
configArgs[configArgIndex++] = i;
}
}
// remove all the arguments consumed by this argument parsing
if (configArgIndex == 0)
return args;
String[] passThruArgs = new String[args.length - configArgIndex];
configArgIndex = 0;
int j = 0;
for (int i = 0; i < args.length; i++) {
if (i == configArgs[configArgIndex])
configArgIndex++;
else
passThruArgs[j++] = args[i];
}
return passThruArgs;
}
private static URL getConfigurationURL(String configArg) throws MalformedURLException {
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// NOTE:
// Due to Eclipse class loader structure and class
// visibility, the configuration file search is
// implemented in 2 different classes:
// org.eclipse.core.internal.boot.PlatformConfiguration
// getConfigurationURL(String)
// initializeCurrent(URL)
// org.eclipse.core.launcher.Main
// getConfigurationURL(String)
// loadConfiguration(URL)
// If you are making changes to this method make sure
// the change is applied in both places
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Determine configuration URL to use (based on command line argument)
// flag: -configuration COMMON | USER.HOME | USER.DIR | <url>
// COMMON in <eclipse>/install/<cfig>
// USER.HOME in <user.home>/eclipse/install/<cfig>
// USER.DIR in <user.dir>/eclipse/install/<cfig>
// <url> URL as specififed
String tmp;
URL result = null;
if (configArg != null && !configArg.trim().equals("")) {
if (configArg.equalsIgnoreCase(ARG_USER_DIR)) {
tmp = System.getProperty("user.dir");
if (!tmp.endsWith(File.separator))
tmp += File.separator;
result = new URL("file:" + tmp.replace(File.separatorChar,'/') + ECLIPSEDIR + "/" + INSTALL + "/" + CONFIG_FILE);
} else if (configArg.equalsIgnoreCase(ARG_USER_HOME)) {
tmp = System.getProperty("user.home");
if (!tmp.endsWith(File.separator))
tmp += File.separator;
result = new URL("file:" + tmp.replace(File.separatorChar,'/') + ECLIPSEDIR + "/" + INSTALL + "/" + CONFIG_FILE);
} else if (configArg.equalsIgnoreCase(ARG_COMMON)) {
result = new URL(BootLoader.getInstallURL(), INSTALL + "/" + CONFIG_FILE);
} else {
try {
result = new URL(configArg);
} catch(MalformedURLException e) {
throw new IllegalArgumentException(Policy.bind("cfig.badUrlArg",configArg));
}
}
}
return result;
}
/*
* R1.0 compatibility mode ... -plugins was specified (possibly with -configuration)
*/
private static String createConfigurationFromPlugins(URL file, String cfigCmd) throws Exception {
// get the actual plugin path
URL[] pluginPath = BootLoader.getPluginPath(file);
if (pluginPath == null || pluginPath.length == 0)
return null;
// create a temp configuration and populate it based on plugin path
PlatformConfiguration tempConfig = new PlatformConfiguration((URL)null);
for (int i=0; i<pluginPath.length; i++) {
String entry = pluginPath[i].toExternalForm();
String sitePortion;
String pluginPortion;
int ix;
if (entry.endsWith("/")) {
// assume directory path in the form <site>/plugins/
// look for -------------------------------^
ix = findEntrySeparator(entry,2); // second from end
sitePortion = entry.substring(0,ix+1);
pluginPortion = entry.substring(ix+1);
if (!pluginPortion.equals("plugins/"))
continue; // unsupported entry ... skip it ("fragments/" are handled)
pluginPortion = null;
} else {
// assume full path in the form <site>/<pluginsDir>/<some.plugin>/plugin.xml
// look for --------------------------^
ix = findEntrySeparator(entry, 3); // third from end
sitePortion = entry.substring(0,ix+1);
pluginPortion = entry.substring(ix+1);
}
if (ix == -1)
continue; // bad entry ... skip it
URL siteURL = null;
try {
siteURL = new URL(sitePortion);
} catch (MalformedURLException e) {
continue; // bad entry ... skip it
}
// configure existing site or create a new one for the entry
ISiteEntry site = tempConfig.findConfiguredSite(siteURL);
ISitePolicy policy;
if (site == null) {
// new site
if (pluginPortion == null)
policy = tempConfig.createSitePolicy(ISitePolicy.USER_EXCLUDE, null);
else
policy = tempConfig.createSitePolicy(ISitePolicy.USER_INCLUDE, new String[] { pluginPortion });
site = tempConfig.createSiteEntry(siteURL,policy);
tempConfig.configureSite(site);
} else {
// existing site
policy = site.getSitePolicy();
if (policy.getType() == ISitePolicy.USER_EXCLUDE)
continue; // redundant entry ... skip it
if (pluginPortion == null) {
// directory entry ... change policy to exclusion (with empty list)
policy = tempConfig.createSitePolicy(ISitePolicy.USER_EXCLUDE, null);
} else {
// explicit entry ... add it to the inclusion list
ArrayList list = new ArrayList(Arrays.asList(policy.getList()));
list.add(pluginPortion);
policy = tempConfig.createSitePolicy(ISitePolicy.USER_INCLUDE,(String[])list.toArray(new String[0]));
}
site.setSitePolicy(policy);
}
}
// check to see if configuration was specified. If specified, will be used to
// persist the new configuration. Otherwise a transient configuration will be
// created in temp space.
URL tmpURL = null;
if (cfigCmd != null && !cfigCmd.trim().equals("")) {
try {
tmpURL = getConfigurationURL(cfigCmd);
try {
// attemp to load the specified configuration. If found, merge
// it with the newly computed one. The merge algorithm includes
// sites from the old configuration that are not part of the new
// configuration. Note, that this does not provide for a complete
// merge, but the assumption is that if -plugins was specified,
// the sites included in the specification are explicitly
// controlled.
PlatformConfiguration oldConfig = new PlatformConfiguration(tmpURL);
ISiteEntry[] oldSites = oldConfig.getConfiguredSites();
for (int i=0; i<oldSites.length; i++) {
tempConfig.configureSite(oldSites[i], false /*do not replace*/);
}
} catch(IOException e) {
}
} catch(MalformedURLException e) {
}
}
if (tmpURL == null) {
// save the configuration in temp location
String tmpDirName = System.getProperty("java.io.tmpdir");
if (!tmpDirName.endsWith(File.separator))
tmpDirName += File.separator;
tmpDirName += Long.toString((new Date()).getTime()) + File.separator;
File tmpDir = new File(tmpDirName);
tmpDir.mkdirs();
tmpDir.deleteOnExit();
File tmpCfg = File.createTempFile("platform",".cfg",tmpDir);
tmpCfg.deleteOnExit();
tmpURL = new URL("file:" + tmpCfg.getAbsolutePath().replace(File.separatorChar, '/'));
tempConfig.transientConfig = true;
}
// force writing null stamps
ISiteEntry[] se = tempConfig.getConfiguredSites();
for (int i=0; i<se.length; i++) {
((SiteEntry)se[i]).changeStampIsValid = true;
((SiteEntry)se[i]).pluginsChangeStampIsValid = true;
((SiteEntry)se[i]).featuresChangeStampIsValid = true;
}
tempConfig.changeStampIsValid = true;
tempConfig.pluginsChangeStampIsValid = true;
tempConfig.featuresChangeStampIsValid = true;
// write out configuration
tempConfig.save(tmpURL); // write the temporary configuration we just created
// return reference to new configuration
return tmpURL.toExternalForm();
}
private static int findEntrySeparator(String pathEntry, int cnt) {
for (int i=pathEntry.length()-1; i>=0; i--) {
if (pathEntry.charAt(i) == '/') {
if (--cnt == 0)
return i;
}
}
return -1;
}
private static String[] stringListToArray(String prop, String separator) {
if (prop == null || prop.trim().equals(""))
return new String[0];
ArrayList list = new ArrayList();
StringTokenizer tokens = new StringTokenizer(prop, separator);
while (tokens.hasMoreTokens()) {
String token = tokens.nextToken().trim();
if (!token.equals(""))
list.add(token);
}
return list.isEmpty() ? new String[0] : (String[]) list.toArray(new String[0]);
}
private static boolean supportsDetection(URL url) {
String protocol = url.getProtocol();
if (protocol.equals("file"))
return true;
else if (protocol.equals(PlatformURLHandler.PROTOCOL)) {
URL resolved = null;
try {
resolved = ((PlatformURLConnection)url.openConnection()).getResolvedURL();
} catch(IOException e) {
return false; // we tried but failed to resolve the platform URL
}
return resolved.getProtocol().equals("file");
} else
return false;
}
private static void verifyPath(URL url) {
String protocol = url.getProtocol();
String path = null;
if (protocol.equals("file"))
path = url.getFile();
else if (protocol.equals(PlatformURLHandler.PROTOCOL)) {
URL resolved = null;
try {
resolved = ((PlatformURLConnection)url.openConnection()).getResolvedURL();
if (resolved.getProtocol().equals("file"))
path = resolved.getFile();
} catch(IOException e) {
}
}
if (path != null) {
File dir = new File(path).getParentFile();
if (dir != null)
dir.mkdirs();
}
}
private static void debug(String s) {
System.out.println("PlatformConfig: " + s);
}
}