blob: 60fa77a44140e727e630792058be0e8182ae6f06 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2018 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.internal.p2.reconciler.dropins;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.Map.Entry;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.internal.p2.core.helpers.LogHelper;
import org.eclipse.equinox.internal.p2.extensionlocation.*;
import org.eclipse.equinox.internal.p2.update.*;
import org.eclipse.equinox.internal.provisional.p2.directorywatcher.DirectoryChangeListener;
import org.eclipse.equinox.p2.core.ProvisionException;
import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository;
import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository;
import org.eclipse.osgi.util.NLS;
/**
* This class watches a platform.xml file. Note that we don't really need to use the DirectoryChangeListener
* framework since we are doing a single poll on startup, but we will leave the code here in case we
* want to watch for changes during a session. Note that the code to actually synchronize the repositories
* is on the Activator so we will need to call out to that if this behaviour is changed.
*
* @since 1.0
*/
public class PlatformXmlListener extends DirectoryChangeListener {
private static final String PLATFORM_XML = "platform.xml"; //$NON-NLS-1$
private boolean changed = false;
private File root;
private long lastModified = -1l;
private Set<IMetadataRepository> configRepositories;
private String toString(Feature[] features, String[] list) {
StringBuilder buffer = new StringBuilder();
if (features != null) {
for (Feature feature : features) {
String featureURL = feature.getUrl();
if (featureURL != null) {
buffer.append(featureURL).append(',');
} else {
String id = feature.getId();
String version = feature.getVersion();
if (id != null && version != null)
buffer.append("features/" + id + "_" + version + "/,"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
}
if (list != null) {
for (String list1 : list) {
buffer.append(list1).append(',');
}
}
if (buffer.length() == 0)
return ""; //$NON-NLS-1$
return buffer.substring(0, buffer.length() - 1);
}
/*
* Construct a new listener based on the given platform.xml file.
*/
public PlatformXmlListener(File file) {
super();
if (!PLATFORM_XML.equals(file.getName()))
throw new IllegalArgumentException();
this.root = file;
}
@Override
public boolean added(File file) {
changed = changed || PLATFORM_XML.equals(file.getName());
return false;
}
@Override
public boolean changed(File file) {
changed = changed || PLATFORM_XML.equals(file.getName());
return false;
}
@Override
public Long getSeenFile(File file) {
return Long.valueOf(0);
}
@Override
public boolean isInterested(File file) {
return file.getName().equals(PLATFORM_XML) && lastModified != file.lastModified();
}
@Override
public boolean removed(File file) {
changed = changed || PLATFORM_XML.equals(file.getName());
return false;
}
@Override
public void startPoll() {
changed = false;
}
@Override
public void stopPoll() {
if (changed) {
lastModified = root.lastModified();
try {
Configuration configuration = Configuration.load(root, Activator.getOSGiInstallArea());
synchronizeConfiguration(configuration);
} catch (ProvisionException e) {
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, Messages.errorProcessingConfg, e));
}
}
changed = false;
}
public Collection<IMetadataRepository> getMetadataRepositories() {
if (configRepositories == null)
return Collections.emptySet();
return configRepositories;
}
/*
* Look through the given list of repositories and see if there is one
* currently associated with the given url string. Return null if one could not
* be found.
*/
private IMetadataRepository getMatchingRepo(Collection<IMetadataRepository> repositoryList, String urlString) {
if (repositoryList == null)
return null;
IPath urlPath = new Path(urlString).makeAbsolute();
for (IMetadataRepository repo : repositoryList) {
File file = URIUtil.toFile(repo.getLocation());
if (file == null)
continue;
Path repoPath = new Path(file.getAbsolutePath());
if (repoPath.makeAbsolute().equals(urlPath))
return repo;
// normalize the URLs to be the same
if (repo instanceof ExtensionLocationMetadataRepository) {
try {
File one = ExtensionLocationMetadataRepository.getBaseDirectory(repo.getLocation());
File two = ExtensionLocationMetadataRepository.getBaseDirectory(new URI(urlString));
if (one.equals(two))
return repo;
} catch (ProvisionException e) {
// Skip the repo if it's not found. Log all other errors.
if (e.getStatus().getCode() != ProvisionException.REPOSITORY_NOT_FOUND)
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, "Error occurred while comparing repository locations.", e)); //$NON-NLS-1$
} catch (URISyntaxException e) {
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, "Error occurred while comparing repository locations.", e)); //$NON-NLS-1$
}
}
}
return null;
}
/*
* Ensure that we have a repository for each site in the given configuration.
*/
protected void synchronizeConfiguration(Configuration config) {
List<Site> sites = config.getSites();
Set<IMetadataRepository> newRepos = new LinkedHashSet<>();
Set<Site> toBeRemoved = new HashSet<>();
for (Site site : sites) {
String siteURL = site.getUrl();
IMetadataRepository match = getMatchingRepo(Activator.getRepositories(), siteURL);
if (match == null) {
try {
String linkFile = site.getLinkFile();
if (linkFile != null && linkFile.length() > 0) {
File link = new File(linkFile);
if (!link.exists()) {
toBeRemoved.add(site);
continue;
}
}
if (!site.isEnabled()) {
toBeRemoved.add(site);
continue;
}
String eclipseExtensionURL = siteURL + Constants.EXTENSION_LOCATION;
// use the URI constructor here and not URIUtil#fromString because
// our string is already encoded
URI location = new URI(eclipseExtensionURL);
Map<String, String> properties = new HashMap<>();
properties.put(SiteListener.SITE_POLICY, site.getPolicy());
// In a "USER-INCLUDE" we add the site's features to the list
// this is done to support backwards compatibility where previously features were not really installed.
// One can always directly add the features to this list. This might be useful for excluding a particular feature
// in a "USER-EXCLUDE" site.
Feature[] listFeatures = site.getPolicy().equals(Site.POLICY_USER_INCLUDE) ? site.getFeatures() : null;
properties.put(SiteListener.SITE_LIST, toString(listFeatures, site.getList()));
// deal with the metadata repository
IMetadataRepository metadataRepository = null;
try {
metadataRepository = Activator.createExtensionLocationMetadataRepository(location, "extension location metadata repository: " + location.toString(), properties); //$NON-NLS-1$
} catch (ProvisionException ex) {
try {
metadataRepository = Activator.loadMetadataRepository(location, null);
} catch (ProvisionException inner) {
// handle the case where someone has removed the extension location from
// disk. Note: we use the siteURL not the eclipseextensionURL
// use the URI constructor here and not URIUtil#fromString because
// our string is already encoded
URI fileURI = new URI(siteURL);
File file = URIUtil.toFile(fileURI);
if (file != null && !file.exists()) {
toBeRemoved.add(site);
continue;
}
throw inner;
}
// set the repository properties here in case they have changed since the last time we loaded
for (Entry<String, String> entry : properties.entrySet()) {
metadataRepository.setProperty(entry.getKey(), entry.getValue());
}
}
newRepos.add(metadataRepository);
// now the artifact repository
try {
Activator.createExtensionLocationArtifactRepository(location, "extension location artifact repository: " + location, properties); //$NON-NLS-1$
} catch (ProvisionException ex) {
IArtifactRepository artifactRepository = Activator.loadArtifactRepository(location, null);
// set the repository properties here in case they have changed since the last time we loaded
for (String key : properties.keySet()) {
String value = properties.get(key);
artifactRepository.setProperty(key, value);
}
}
} catch (URISyntaxException e) {
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.errorLoadingRepository, siteURL), e));
} catch (ProvisionException e) {
// Skip the repo if it's not found. Log all other errors.
if (e.getStatus().getCode() != ProvisionException.REPOSITORY_NOT_FOUND)
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.errorLoadingRepository, siteURL), e));
}
} else {
newRepos.add(match);
}
}
if (!toBeRemoved.isEmpty()) {
for (Site site : toBeRemoved)
config.removeSite(site);
try {
config.save(root, Activator.getOSGiInstallArea());
} catch (ProvisionException e) {
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, "Error occurred while saving configuration at: " + root, e)); //$NON-NLS-1$
}
}
configRepositories = newRepos;
}
}