| /******************************************************************************* |
| * Copyright (c) 2004, 2015 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Rapicorp, Inc - Support for Mac Layout (bug 431116) |
| *******************************************************************************/ |
| package org.eclipse.osgi.internal.location; |
| |
| import java.io.*; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.util.Properties; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import org.eclipse.osgi.internal.framework.EquinoxConfiguration; |
| import org.eclipse.osgi.internal.framework.EquinoxConfiguration.ConfigValues; |
| import org.eclipse.osgi.internal.framework.EquinoxContainer; |
| import org.eclipse.osgi.service.datalocation.Location; |
| import org.eclipse.osgi.storage.StorageUtil; |
| import org.osgi.framework.Constants; |
| |
| /** |
| * This class is used to manage the various Locations for Eclipse. |
| */ |
| public class EquinoxLocations { |
| public static final String READ_ONLY_AREA_SUFFIX = ".readOnly"; //$NON-NLS-1$ |
| public static final String PROP_INSTALL_AREA = "osgi.install.area"; //$NON-NLS-1$ |
| public static final String PROP_CONFIG_AREA = "osgi.configuration.area"; //$NON-NLS-1$ |
| public static final String PROP_CONFIG_AREA_DEFAULT = "osgi.configuration.area.default"; //$NON-NLS-1$ |
| public static final String PROP_SHARED_CONFIG_AREA = "osgi.sharedConfiguration.area"; //$NON-NLS-1$ |
| public static final String PROP_INSTANCE_AREA = "osgi.instance.area"; //$NON-NLS-1$ |
| public static final String PROP_INSTANCE_AREA_DEFAULT = "osgi.instance.area.default"; //$NON-NLS-1$ |
| public static final String PROP_USER_AREA = "osgi.user.area"; //$NON-NLS-1$ |
| public static final String PROP_USER_AREA_DEFAULT = "osgi.user.area.default"; //$NON-NLS-1$ |
| public static final String PROP_USER_HOME = "user.home"; //$NON-NLS-1$ |
| public static final String PROP_USER_DIR = "user.dir"; //$NON-NLS-1$ |
| public static final String PROP_HOME_LOCATION_AREA = "eclipse.home.location"; //$NON-NLS-1$ |
| public static final String PROP_LAUNCHER = "eclipse.launcher"; //$NON-NLS-1$ |
| |
| // Constants for configuration location discovery |
| private static final String ECLIPSE = "eclipse"; //$NON-NLS-1$ |
| private static final String PRODUCT_SITE_MARKER = ".eclipseproduct"; //$NON-NLS-1$ |
| private static final String PRODUCT_SITE_ID = "id"; //$NON-NLS-1$ |
| private static final String PRODUCT_SITE_VERSION = "version"; //$NON-NLS-1$ |
| |
| private static final String CONFIG_DIR = "configuration"; //$NON-NLS-1$ |
| |
| // Data mode constants for user, configuration and data locations. |
| private static final String NONE = "@none"; //$NON-NLS-1$ |
| private static final String NO_DEFAULT = "@noDefault"; //$NON-NLS-1$ |
| private static final String USER_HOME = "@user.home"; //$NON-NLS-1$ |
| private static final String USER_DIR = "@user.dir"; //$NON-NLS-1$ |
| // Placeholder for hashcode of installation directory |
| private static final String INSTALL_HASH_PLACEHOLDER = "@install.hash"; //$NON-NLS-1$ |
| |
| private static final String INSTANCE_DATA_AREA_PREFIX = ".metadata/.plugins/"; //$NON-NLS-1$ |
| |
| private final ConfigValues equinoxConfig; |
| private final EquinoxContainer container; |
| private final AtomicBoolean debugLocations; |
| |
| private final Location installLocation; |
| private final Location configurationLocation; |
| private final Location userLocation; |
| private final Location instanceLocation; |
| private final Location eclipseHomeLocation; |
| |
| public EquinoxLocations(ConfigValues equinoxConfig, EquinoxContainer container, AtomicBoolean debugLocations) { |
| this.equinoxConfig = equinoxConfig; |
| this.container = container; |
| this.debugLocations = debugLocations; |
| |
| // Initializes the Location objects for the LocationManager. |
| // set the osgi storage area if it exists |
| String osgiStorage = equinoxConfig.getConfiguration(Constants.FRAMEWORK_STORAGE); |
| if (osgiStorage != null) |
| equinoxConfig.setConfiguration(PROP_CONFIG_AREA, osgiStorage); |
| // do install location initialization first since others may depend on it |
| // assumes that the property is already set |
| installLocation = buildLocation(PROP_INSTALL_AREA, null, "", true, false, null); //$NON-NLS-1$ |
| |
| // TODO not sure what the data area prefix should be here for the user area |
| Location temp = buildLocation(PROP_USER_AREA_DEFAULT, null, "", false, false, null); //$NON-NLS-1$ |
| URL defaultLocation = temp == null ? null : temp.getURL(); |
| if (defaultLocation == null) |
| defaultLocation = buildURL(new File(System.getProperty(PROP_USER_HOME), "user").getAbsolutePath(), true); //$NON-NLS-1$ |
| userLocation = buildLocation(PROP_USER_AREA, defaultLocation, "", false, false, null); //$NON-NLS-1$ |
| |
| temp = buildLocation(PROP_INSTANCE_AREA_DEFAULT, null, "", false, false, INSTANCE_DATA_AREA_PREFIX); //$NON-NLS-1$ |
| defaultLocation = temp == null ? null : temp.getURL(); |
| if (defaultLocation == null) |
| defaultLocation = buildURL(new File(System.getProperty(PROP_USER_DIR), "workspace").getAbsolutePath(), true); //$NON-NLS-1$ |
| instanceLocation = buildLocation(PROP_INSTANCE_AREA, defaultLocation, "", false, false, INSTANCE_DATA_AREA_PREFIX); //$NON-NLS-1$ |
| |
| mungeConfigurationLocation(); |
| // compute a default but it is very unlikely to be used since main will have computed everything |
| temp = buildLocation(PROP_CONFIG_AREA_DEFAULT, null, "", false, false, null); //$NON-NLS-1$ |
| defaultLocation = temp == null ? null : temp.getURL(); |
| if (defaultLocation == null && equinoxConfig.getConfiguration(PROP_CONFIG_AREA) == null) |
| // only compute the default if the configuration area property is not set |
| defaultLocation = buildURL(computeDefaultConfigurationLocation(), true); |
| configurationLocation = buildLocation(PROP_CONFIG_AREA, defaultLocation, "", false, false, null); //$NON-NLS-1$ |
| // get the parent location based on the system property. This will have been set on the |
| // way in either by the caller/user or by main. There will be no parent location if we are not |
| // cascaded. |
| URL parentLocation = computeSharedConfigurationLocation(); |
| if (parentLocation != null && !parentLocation.equals(configurationLocation.getURL())) { |
| Location parent = new BasicLocation(null, parentLocation, true, null, equinoxConfig, container, debugLocations); |
| ((BasicLocation) configurationLocation).setParent(parent); |
| } |
| |
| if (equinoxConfig.getConfiguration(PROP_HOME_LOCATION_AREA) == null) { |
| String eclipseLauncher = equinoxConfig.getConfiguration(PROP_LAUNCHER); |
| String eclipseHomeLocationPath = getEclipseHomeLocation(eclipseLauncher, equinoxConfig); |
| if (eclipseHomeLocationPath != null) |
| equinoxConfig.setConfiguration(PROP_HOME_LOCATION_AREA, eclipseHomeLocationPath); |
| } |
| // if eclipse.home.location is not set then default to osgi.install.area |
| if (equinoxConfig.getConfiguration(PROP_HOME_LOCATION_AREA) == null && equinoxConfig.getConfiguration(PROP_INSTALL_AREA) != null) |
| equinoxConfig.setConfiguration(PROP_HOME_LOCATION_AREA, equinoxConfig.getConfiguration(PROP_INSTALL_AREA)); |
| eclipseHomeLocation = buildLocation(PROP_HOME_LOCATION_AREA, null, "", true, true, null); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Builds a URL with the given specification |
| * @param spec the URL specification |
| * @param trailingSlash flag to indicate a trailing slash on the spec |
| * @return a URL |
| */ |
| public static URL buildURL(String spec, boolean trailingSlash) { |
| return LocationHelper.buildURL(spec, trailingSlash); |
| } |
| |
| private void mungeConfigurationLocation() { |
| // if the config property was set, munge it for backwards compatibility. |
| String location = equinoxConfig.getConfiguration(PROP_CONFIG_AREA); |
| if (location != null) { |
| if (location.endsWith(".cfg")) { //$NON-NLS-1$ |
| int index = location.lastIndexOf('/'); |
| if (index < 0) |
| index = location.lastIndexOf('\\'); |
| location = location.substring(0, index + 1); |
| equinoxConfig.setConfiguration(PROP_CONFIG_AREA, location); |
| } |
| } |
| } |
| |
| private static String getEclipseHomeLocation(String launcher, ConfigValues configValues) { |
| if (launcher == null) |
| return null; |
| File launcherFile = new File(launcher); |
| if (launcherFile.getParent() == null) |
| return null; |
| File launcherDir = new File(launcherFile.getParent()); |
| // check for mac os; the os check is copied from EclipseEnvironmentInfo. |
| String macosx = org.eclipse.osgi.service.environment.Constants.OS_MACOSX; |
| if (macosx.equals(configValues.getConfiguration(EquinoxConfiguration.PROP_OSGI_OS))) |
| launcherDir = getMacOSEclipseHomeLocation(launcherDir); |
| return (launcherDir.exists() && launcherDir.isDirectory()) ? launcherDir.getAbsolutePath() : null; |
| } |
| |
| private static File getMacOSEclipseHomeLocation(File launcherDir) { |
| if (!launcherDir.getName().equalsIgnoreCase("macos")) //$NON-NLS-1$ |
| return launcherDir; // don't do the up three stuff if not in macos directory |
| return new File(launcherDir.getParent(), "Eclipse"); //$NON-NLS-1$ |
| } |
| |
| @SuppressWarnings("deprecation") |
| private Location buildLocation(String property, URL defaultLocation, String userDefaultAppendage, boolean readOnlyDefault, boolean computeReadOnly, String dataAreaPrefix) { |
| String location = equinoxConfig.clearConfiguration(property); |
| // the user/product may specify a non-default readOnly setting |
| String userReadOnlySetting = equinoxConfig.getConfiguration(property + READ_ONLY_AREA_SUFFIX); |
| boolean readOnly = (userReadOnlySetting == null ? readOnlyDefault : Boolean.valueOf(userReadOnlySetting).booleanValue()); |
| // if the instance location is not set, predict where the workspace will be and |
| // put the instance area inside the workspace meta area. |
| if (location == null) |
| return new BasicLocation(property, defaultLocation, userReadOnlySetting != null || !computeReadOnly ? readOnly : !canWrite(defaultLocation), dataAreaPrefix, equinoxConfig, container, debugLocations); |
| String trimmedLocation = location.trim(); |
| if (trimmedLocation.equalsIgnoreCase(NONE)) |
| return null; |
| if (trimmedLocation.equalsIgnoreCase(NO_DEFAULT)) |
| return new BasicLocation(property, null, readOnly, dataAreaPrefix, equinoxConfig, container, debugLocations); |
| if (trimmedLocation.startsWith(USER_HOME)) { |
| String base = substituteVar(location, USER_HOME, PROP_USER_HOME); |
| location = new File(base, userDefaultAppendage).getAbsolutePath(); |
| } else if (trimmedLocation.startsWith(USER_DIR)) { |
| String base = substituteVar(location, USER_DIR, PROP_USER_DIR); |
| location = new File(base, userDefaultAppendage).getAbsolutePath(); |
| } |
| int idx = location.indexOf(INSTALL_HASH_PLACEHOLDER); |
| if (idx == 0) { |
| throw new RuntimeException("The location cannot start with '" + INSTALL_HASH_PLACEHOLDER + "': " + location); //$NON-NLS-1$ //$NON-NLS-2$ |
| } else if (idx > 0) { |
| location = location.substring(0, idx) + getInstallDirHash() + location.substring(idx + INSTALL_HASH_PLACEHOLDER.length()); |
| } |
| URL url = buildURL(location, true); |
| BasicLocation result = null; |
| if (url != null) { |
| result = new BasicLocation(property, null, userReadOnlySetting != null || !computeReadOnly ? readOnly : !canWrite(url), dataAreaPrefix, equinoxConfig, container, debugLocations); |
| result.setURL(url, false); |
| } |
| return result; |
| } |
| |
| private String substituteVar(String source, String var, String prop) { |
| String value = equinoxConfig.getConfiguration(prop, ""); //$NON-NLS-1$ |
| return value + source.substring(var.length()); |
| } |
| |
| private URL computeInstallConfigurationLocation() { |
| String property = equinoxConfig.getConfiguration(PROP_INSTALL_AREA); |
| if (property != null) |
| return LocationHelper.buildURL(property, true); |
| return null; |
| } |
| |
| private URL computeSharedConfigurationLocation() { |
| String property = equinoxConfig.getConfiguration(PROP_SHARED_CONFIG_AREA); |
| if (property == null) |
| return null; |
| try { |
| URL sharedConfigurationURL = LocationHelper.buildURL(property, true); |
| if (sharedConfigurationURL == null) |
| return null; |
| if (sharedConfigurationURL.getPath().startsWith("/")) //$NON-NLS-1$ |
| // absolute |
| return sharedConfigurationURL; |
| URL installURL = installLocation.getURL(); |
| if (!sharedConfigurationURL.getProtocol().equals(installURL.getProtocol())) |
| // different protocol |
| return sharedConfigurationURL; |
| sharedConfigurationURL = new URL(installURL, sharedConfigurationURL.getPath()); |
| equinoxConfig.setConfiguration(PROP_SHARED_CONFIG_AREA, sharedConfigurationURL.toExternalForm()); |
| } catch (MalformedURLException e) { |
| // do nothing here since it is basically impossible to get a bogus url |
| } |
| return null; |
| } |
| |
| private String computeDefaultConfigurationLocation() { |
| // 1) We store the config state relative to the 'eclipse' directory if possible |
| // 2) If this directory is read-only |
| // we store the state in <user.home>/.eclipse/<application-id>_<version> where <user.home> |
| // is unique for each local user, and <application-id> is the one |
| // defined in .eclipseproduct marker file. If .eclipseproduct does not |
| // exist, use "eclipse" as the application-id. |
| |
| URL installURL = computeInstallConfigurationLocation(); |
| if (installURL != null && "file".equals(installURL.getProtocol())) { //$NON-NLS-1$ |
| File installDir = new File(installURL.getFile()); |
| File defaultConfigDir = new File(installDir, CONFIG_DIR); |
| if (!defaultConfigDir.exists()) |
| defaultConfigDir.mkdirs(); |
| if (defaultConfigDir.exists() && StorageUtil.canWrite(defaultConfigDir)) |
| return defaultConfigDir.getAbsolutePath(); |
| } |
| // We can't write in the eclipse install dir so try for some place in the user's home dir |
| return computeDefaultUserAreaLocation(CONFIG_DIR); |
| } |
| |
| private static boolean canWrite(URL location) { |
| if (location != null && "file".equals(location.getProtocol())) { //$NON-NLS-1$ |
| File locationDir = new File(location.getFile()); |
| if (!locationDir.exists()) |
| locationDir.mkdirs(); |
| if (locationDir.exists() && StorageUtil.canWrite(locationDir)) |
| return true; |
| } |
| return false; |
| } |
| |
| private String computeDefaultUserAreaLocation(String pathAppendage) { |
| // we store the state in <user.home>/.eclipse/<application-id>_<version> where <user.home> |
| // is unique for each local user, and <application-id> is the one |
| // defined in .eclipseproduct marker file. If .eclipseproduct does not |
| // exist, use "eclipse" as the application-id. |
| String installProperty = equinoxConfig.getConfiguration(PROP_INSTALL_AREA); |
| URL installURL = buildURL(installProperty, true); |
| if (installURL == null) |
| return null; |
| File installDir = new File(installURL.getFile()); |
| String installDirHash = getInstallDirHash(); |
| |
| String appName = "." + ECLIPSE; //$NON-NLS-1$ |
| File eclipseProduct = new File(installDir, PRODUCT_SITE_MARKER); |
| if (eclipseProduct.exists()) { |
| Properties props = new Properties(); |
| try { |
| props.load(new FileInputStream(eclipseProduct)); |
| String appId = props.getProperty(PRODUCT_SITE_ID); |
| if (appId == null || appId.trim().length() == 0) |
| appId = ECLIPSE; |
| String appVersion = props.getProperty(PRODUCT_SITE_VERSION); |
| if (appVersion == null || appVersion.trim().length() == 0) |
| appVersion = ""; //$NON-NLS-1$ |
| appName += File.separator + appId + "_" + appVersion + "_" + installDirHash; //$NON-NLS-1$ //$NON-NLS-2$ |
| } catch (IOException e) { |
| // Do nothing if we get an exception. We will default to a standard location |
| // in the user's home dir. |
| // add the hash to help prevent collisions |
| appName += File.separator + installDirHash; |
| } |
| } else { |
| // add the hash to help prevent collisions |
| appName += File.separator + installDirHash; |
| } |
| String userHome = System.getProperty(PROP_USER_HOME); |
| return new File(userHome, appName + "/" + pathAppendage).getAbsolutePath(); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Return hash code identifying an absolute installation path |
| * @return hash code as String |
| */ |
| private String getInstallDirHash() { |
| // compute an install dir hash to prevent configuration area collisions with other eclipse installs |
| String installProperty = equinoxConfig.getConfiguration(PROP_INSTALL_AREA); |
| URL installURL = buildURL(installProperty, true); |
| if (installURL == null) |
| return ""; //$NON-NLS-1$ |
| File installDir = new File(installURL.getFile()); |
| int hashCode; |
| try { |
| hashCode = installDir.getCanonicalPath().hashCode(); |
| } catch (IOException ioe) { |
| // fall back to absolute path |
| hashCode = installDir.getAbsolutePath().hashCode(); |
| } |
| if (hashCode < 0) |
| hashCode = -(hashCode); |
| String installDirHash = String.valueOf(hashCode); |
| return installDirHash; |
| } |
| |
| /** |
| * Returns the user Location object |
| * @return the user Location object |
| */ |
| public Location getUserLocation() { |
| return userLocation; |
| } |
| |
| /** |
| * Returns the configuration Location object |
| * @return the configuration Location object |
| */ |
| public Location getConfigurationLocation() { |
| return configurationLocation; |
| } |
| |
| /** |
| * Returns the install Location object |
| * @return the install Location object |
| */ |
| public Location getInstallLocation() { |
| return installLocation; |
| } |
| |
| /** |
| * Returns the instance Location object |
| * @return the instance Location object |
| */ |
| public Location getInstanceLocation() { |
| return instanceLocation; |
| } |
| |
| public Location getEclipseHomeLocation() { |
| return eclipseHomeLocation; |
| } |
| } |