blob: f435ef2429000e54da1f3b0124626d2018df1943 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2020 Ericsson AB and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Ericsson AB - initial API and implementation
* Ericsson AB (Hamdan Msheik) - bug- 432167
*******************************************************************************/
package org.eclipse.equinox.internal.p2.ui.sdk.scheduler;
import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.Platform;
import org.eclipse.equinox.internal.p2.touchpoint.eclipse.AgentFromInstall;
import org.eclipse.equinox.p2.core.IProvisioningAgent;
public class PreviousConfigurationFinder {
private static final Pattern path = Pattern.compile("(.+?)_{1}?([0-9\\.]+)_{1}?(\\d+)(_*?([^_].*)|$)"); //$NON-NLS-1$
public static class Identifier implements Comparable<Identifier> {
private static final String DELIM = ". _-"; //$NON-NLS-1$
private int major, minor, service;
public Identifier(int major, int minor, int service) {
super();
this.major = major;
this.minor = minor;
this.service = service;
}
/**
* @throws NumberFormatException if cannot parse the major and minor version
* components
*/
Identifier(String versionString) {
super();
StringTokenizer tokenizer = new StringTokenizer(versionString, DELIM);
// major
if (tokenizer.hasMoreTokens())
major = Integer.parseInt(tokenizer.nextToken());
// minor
if (tokenizer.hasMoreTokens())
minor = Integer.parseInt(tokenizer.nextToken());
try {
// service
if (tokenizer.hasMoreTokens())
service = Integer.parseInt(tokenizer.nextToken());
} catch (NumberFormatException nfe) {
// ignore the service qualifier in that case and default to 0
// this will allow us to tolerate other non-conventional version numbers
}
}
/**
* Returns true if this id is considered to be greater than or equal to the
* given baseline. e.g. 1.2.9 >= 1.3.1 -> false 1.3.0 >= 1.3.1 -> false 1.3.1 >=
* 1.3.1 -> true 1.3.2 >= 1.3.1 -> true 2.0.0 >= 1.3.1 -> true
*/
boolean isGreaterEqualTo(Identifier minimum) {
if (major < minimum.major)
return false;
if (major > minimum.major)
return true;
// major numbers are equivalent so check minor
if (minor < minimum.minor)
return false;
if (minor > minimum.minor)
return true;
// minor numbers are equivalent so check service
return service >= minimum.service;
}
@Override
public boolean equals(Object other) {
if (!(other instanceof Identifier))
return false;
Identifier o = (Identifier) other;
if (major == o.major && minor == o.minor && service == o.service)
return true;
return false;
}
@Override
public String toString() {
return "" + major + '.' + minor + '.' + service; //$NON-NLS-1$
}
@Override
public int compareTo(Identifier o) {
if (o != null) {
if (major < o.major) {
return -1;
} else if (major > o.major) {
return 1;
} else if (minor < o.minor) {
return -1;
} else if (minor > o.minor) {
return 1;
} else if (service < o.service) {
return -1;
} else if (service > o.service) {
return 1;
} else {
return 0;
}
}
return 1;
}
}
public static class ConfigurationDescriptor {
String productId;
Identifier version;
String installPathHashcode;
File configFolder;
String os_ws_arch;
public ConfigurationDescriptor(String productId, Identifier version, String installPathHashcode,
String platformConfig, File configFolder) {
this.productId = productId;
this.version = version;
this.installPathHashcode = installPathHashcode;
this.configFolder = configFolder;
this.os_ws_arch = platformConfig;
}
public String getProductId() {
return productId;
}
public Identifier getVersion() {
return version;
}
public String getInstallPathHashcode() {
return installPathHashcode;
}
public File getConfig() {
return configFolder;
}
public String getPlatformConfig() {
return os_ws_arch;
}
}
public static class ConfigurationDescriptorComparator implements Comparator<ConfigurationDescriptor> {
// compare ConfigurationDescriptor according to their versions and when equals
// according to their lastModified field
@Override
public int compare(ConfigurationDescriptor o1, ConfigurationDescriptor o2) {
int result = -1;
if (o1 != null && o2 != null) {
if (o1.getVersion().compareTo(o2.getVersion()) != 0) {
result = o1.getVersion().compareTo(o2.getVersion());
} else {
if (o1.getConfig().lastModified() > o2.getConfig().lastModified()) {
result = 1;
} else if (o1.getConfig().lastModified() < o2.getConfig().lastModified()) {
result = -1;
} else
result = 0;
}
} else if (o1 == null) {
result = -1;
} else if (o2 == null) {
result = 1;
}
return result;
}
}
private File currentConfig;
public PreviousConfigurationFinder(File currentConfiguration) {
currentConfig = currentConfiguration;
}
public ConfigurationDescriptor extractConfigurationData(File candidate) {
Matcher m = path.matcher(candidate.getName());
if (!m.matches())
return null;
return new ConfigurationDescriptor(m.group(1), new Identifier(m.group(2)), m.group(3), m.group(5),
candidate.getAbsoluteFile());
}
public IProvisioningAgent findPreviousInstalls(File searchRoot, File installFolder) {
List<ConfigurationDescriptor> potentialConfigurations = readPreviousConfigurations(searchRoot);
ConfigurationDescriptor runningConfiguration = getConfigdataFromProductFile(installFolder);
if (runningConfiguration == null)
return null;
ConfigurationDescriptor match = findMostRelevantConfigurationFromInstallHashDir(potentialConfigurations,
runningConfiguration);
if (match == null)
match = findMostRelevantConfigurationFromProductId(potentialConfigurations, runningConfiguration);
if (match == null)
match = findSpecifiedConfiguration(searchRoot);
if (match == null)
return null;
return AgentFromInstall.createAgentFrom(AutomaticUpdatePlugin.getDefault().getAgentProvider(), null,
new File(match.getConfig(), "configuration"), null); //$NON-NLS-1$
}
public ConfigurationDescriptor findSpecifiedConfiguration(File searchRoot) {
final String prefixesAsString = System.getProperty("p2.forcedMigrationLocation"); //$NON-NLS-1$
if (prefixesAsString == null)
return null;
String[] prefixes = prefixesAsString.split(","); //$NON-NLS-1$
for (String prefix : prefixes) {
final String p = prefix;
File[] match = searchRoot.listFiles((FileFilter) candidate -> {
if (!candidate.isDirectory())
return false;
if (currentConfig.equals(candidate))
return false;
return candidate.getName().contains(p);
});
if (match.length != 0)
return new ConfigurationDescriptor("unknown", new Identifier("0.0.0"), "unknown", "unknown", match[0]); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$
}
return null;
}
private ConfigurationDescriptor getConfigdataFromProductFile(File installFolder) {
Object[] productFileInfo = loadEclipseProductFile(installFolder);
// Contrarily to the runtime, when the .eclipseproduct can't be found, we don't
// fallback to org.eclipse.platform.
if (productFileInfo.length == 0)
return null;
return new ConfigurationDescriptor((String) productFileInfo[0], (Identifier) productFileInfo[1],
getInstallDirHash(installFolder),
Platform.getOS() + '_' + Platform.getWS() + '_' + Platform.getOSArch(), null);
}
public ConfigurationDescriptor findMostRelevantConfigurationFromInstallHashDir(
List<ConfigurationDescriptor> configurations, ConfigurationDescriptor configToMatch) {
ConfigurationDescriptor bestMatch = null;
int numberOfcriteriaMet = 0;
for (ConfigurationDescriptor candidate : configurations) {
int criteriaMet = 0;
if (!candidate.getInstallPathHashcode().equals(configToMatch.getInstallPathHashcode())) {
continue;
}
criteriaMet++;
if (configToMatch.getProductId().equals(candidate.getProductId()) && //
configToMatch.getPlatformConfig().equals(candidate.getPlatformConfig()) && //
(!candidate.getVersion().isGreaterEqualTo(configToMatch.getVersion()))) {
// We have a match
criteriaMet++;
}
if (criteriaMet == 0)
continue;
if (criteriaMet > numberOfcriteriaMet) {
bestMatch = candidate;
numberOfcriteriaMet = criteriaMet;
} else if (criteriaMet == numberOfcriteriaMet) {
if (bestMatch.getVersion().equals(candidate.getVersion())) {
if (bestMatch.getConfig().lastModified() < candidate.getConfig().lastModified()) {
bestMatch = candidate;
}
} else {
if (candidate.getVersion().isGreaterEqualTo(bestMatch.getVersion()))
bestMatch = candidate;
}
}
}
return bestMatch;
}
// Out of a set of configuration, find the one with the most similar product
// info.
public ConfigurationDescriptor findMostRelevantConfigurationFromProductId(
List<ConfigurationDescriptor> configurations, ConfigurationDescriptor configToMatch) {
ConfigurationDescriptor bestMatch = null;
List<ConfigurationDescriptor> candidates = new ArrayList<>();
List<ConfigurationDescriptor> candidatesWithUnkonwArchitecture = new ArrayList<>();
for (ConfigurationDescriptor candidate : configurations) {
if (configToMatch.getProductId().equals(candidate.getProductId())
&& configToMatch.getVersion().isGreaterEqualTo(candidate.getVersion())) {
if (configToMatch.getPlatformConfig().equals(candidate.getPlatformConfig())) {
candidates.add(candidate);
} else { // candidate.getPlatformConfig() returns null in legacy installation prior to
// 4.x.x releases
candidatesWithUnkonwArchitecture.add(candidate);
}
}
}
if (!candidates.isEmpty()) {
candidates.sort(new ConfigurationDescriptorComparator());
bestMatch = candidates.get(candidates.size() - 1);
}
if (bestMatch == null) {
if (!candidatesWithUnkonwArchitecture.isEmpty()) {
candidatesWithUnkonwArchitecture.sort(new ConfigurationDescriptorComparator());
bestMatch = candidatesWithUnkonwArchitecture.get(candidatesWithUnkonwArchitecture.size() - 1);
}
}
return bestMatch;
}
// Load the .eclipseproduct file in the base of the installation. This logic is
// very similar to the one found in the launcher
private Object[] loadEclipseProductFile(File installDir) {
final String ECLIPSE = "eclipse"; //$NON-NLS-1$
final String PRODUCT_SITE_MARKER = ".eclipseproduct"; //$NON-NLS-1$
final String PRODUCT_SITE_ID = "id"; //$NON-NLS-1$
final String PRODUCT_SITE_VERSION = "version"; //$NON-NLS-1$
File eclipseProduct = new File(installDir, PRODUCT_SITE_MARKER);
String appId = null;
Identifier appVersion = null;
if (eclipseProduct.exists()) {
Properties props = new Properties();
try (FileInputStream is = new FileInputStream(eclipseProduct)) {
props.load(is);
appId = props.getProperty(PRODUCT_SITE_ID);
if (appId == null || appId.trim().length() == 0)
appId = ECLIPSE;
String version = props.getProperty(PRODUCT_SITE_VERSION);
if (version == null || version.trim().length() == 0)
appVersion = new Identifier(0, 0, 0);
else
appVersion = new Identifier(version);
} catch (IOException e) {
return new String[0];
}
} else {
return new String[0];
}
return new Object[] { appId, appVersion };
}
// Iterate through a folder to look for potential configuration folders and
// reify them.
public List<ConfigurationDescriptor> readPreviousConfigurations(File configurationFolder) {
File[] candidates = configurationFolder.listFiles();
List<ConfigurationDescriptor> configurations = new ArrayList<>(candidates.length);
for (File candidate : candidates) {
if (!candidate.isDirectory())
continue;
if (candidate.equals(currentConfig))
continue;
ConfigurationDescriptor tmp = extractConfigurationData(candidate);
if (tmp != null)
configurations.add(tmp);
}
return configurations;
}
// This code computes the hashCode of the install location.
// This is a simplified version of the code that the launcher executes.
private String getInstallDirHash(File installFolder) {
try {
return Integer.toString(installFolder.getCanonicalPath().hashCode());
} catch (IOException e) {
return ""; //$NON-NLS-1$
}
}
}