blob: 0eb2ec163dcdb52acaab9d4f422b9b6a78a37063 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008-2009 Code 9 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:
* Code 9 - initial API and implementation
* IBM - ongoing development
******************************************************************************/
package org.eclipse.equinox.p2.publisher.eclipse;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.internal.p2.core.helpers.FileUtils;
import org.eclipse.equinox.internal.p2.core.helpers.LogHelper;
import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
import org.eclipse.equinox.internal.p2.publisher.*;
import org.eclipse.equinox.internal.p2.publisher.eclipse.FeatureParser;
import org.eclipse.equinox.internal.provisional.p2.artifact.repository.ArtifactDescriptor;
import org.eclipse.equinox.internal.provisional.p2.artifact.repository.IArtifactDescriptor;
import org.eclipse.equinox.internal.provisional.p2.core.Version;
import org.eclipse.equinox.internal.provisional.p2.core.VersionRange;
import org.eclipse.equinox.internal.provisional.p2.metadata.*;
import org.eclipse.equinox.internal.provisional.p2.metadata.MetadataFactory.InstallableUnitDescription;
import org.eclipse.equinox.internal.provisional.p2.metadata.MetadataFactory.InstallableUnitPatchDescription;
import org.eclipse.equinox.internal.provisional.p2.metadata.repository.IMetadataRepository;
import org.eclipse.equinox.internal.provisional.p2.repository.IRepository;
import org.eclipse.equinox.p2.publisher.*;
import org.eclipse.equinox.spi.p2.publisher.PublisherHelper;
import org.eclipse.osgi.util.NLS;
/**
* Publish IUs for all of the features in the given set of locations. The locations can
* be actual locations of the features or folders of features.
*/
public class FeaturesAction extends AbstractPublisherAction {
public static final String INSTALL_FEATURES_FILTER = "(org.eclipse.update.install.features=true)"; //$NON-NLS-1$
private static final String UPDATE_FEATURE_APPLICATION_PROP = "org.eclipse.update.feature.application"; //$NON-NLS-1$
private static final String UPDATE_FEATURE_PLUGIN_PROP = "org.eclipse.update.feature.plugin"; //$NON-NLS-1$
private static final String UPDATE_FEATURE_EXCLUSIVE_PROP = "org.eclipse.update.feature.exclusive"; //$NON-NLS-1$
private static final String UPDATE_FEATURE_PRIMARY_PROP = "org.eclipse.update.feature.primary"; //$NON-NLS-1$
protected Feature[] features;
private File[] locations;
public static IArtifactKey createFeatureArtifactKey(String id, String version) {
return new ArtifactKey(PublisherHelper.ECLIPSE_FEATURE_CLASSIFIER, id, Version.parseVersion(version));
}
public static IInstallableUnit createFeatureJarIU(Feature feature, IPublisherInfo info) {
InstallableUnitDescription iu = new MetadataFactory.InstallableUnitDescription();
String id = getTransformedId(feature.getId(), /*isPlugin*/false, /*isGroup*/false);
iu.setId(id);
Version version = Version.parseVersion(feature.getVersion());
iu.setVersion(version);
// set properties for other feature attributes
iu.setProperty(IInstallableUnit.PROP_NAME, feature.getLabel());
if (feature.getDescription() != null)
iu.setProperty(IInstallableUnit.PROP_DESCRIPTION, feature.getDescription());
if (feature.getDescriptionURL() != null)
iu.setProperty(IInstallableUnit.PROP_DESCRIPTION_URL, feature.getDescriptionURL());
if (feature.getProviderName() != null)
iu.setProperty(IInstallableUnit.PROP_PROVIDER, feature.getProviderName());
if (feature.getLicense() != null)
iu.setLicense(MetadataFactory.createLicense(toURIOrNull(feature.getLicenseURL()), feature.getLicense()));
if (feature.getCopyright() != null)
iu.setCopyright(MetadataFactory.createCopyright(toURIOrNull(feature.getCopyrightURL()), feature.getCopyright()));
if (feature.getApplication() != null)
iu.setProperty(UPDATE_FEATURE_APPLICATION_PROP, feature.getApplication());
if (feature.getPlugin() != null)
iu.setProperty(UPDATE_FEATURE_PLUGIN_PROP, feature.getPlugin());
if (feature.isExclusive())
iu.setProperty(UPDATE_FEATURE_EXCLUSIVE_PROP, Boolean.TRUE.toString());
if (feature.isPrimary())
iu.setProperty(UPDATE_FEATURE_PRIMARY_PROP, Boolean.TRUE.toString());
// The required capabilities are not specified at this level because we don't want the feature jar to be attractive to install.
iu.setTouchpointType(PublisherHelper.TOUCHPOINT_OSGI);
iu.setFilter(INSTALL_FEATURES_FILTER);
iu.setSingleton(true);
if (feature.getInstallHandler() != null && feature.getInstallHandler().trim().length() > 0) {
String installHandlerProperty = "handler=" + feature.getInstallHandler(); //$NON-NLS-1$
if (feature.getInstallHandlerLibrary() != null)
installHandlerProperty += ", library=" + feature.getInstallHandlerLibrary(); //$NON-NLS-1$
if (feature.getInstallHandlerURL() != null)
installHandlerProperty += ", url=" + feature.getInstallHandlerURL(); //$NON-NLS-1$
iu.setProperty(PublisherHelper.ECLIPSE_INSTALL_HANDLER_PROP, installHandlerProperty);
}
ArrayList providedCapabilities = new ArrayList();
providedCapabilities.add(PublisherHelper.createSelfCapability(id, version));
providedCapabilities.add(PublisherHelper.FEATURE_CAPABILITY);
providedCapabilities.add(MetadataFactory.createProvidedCapability(PublisherHelper.CAPABILITY_NS_UPDATE_FEATURE, feature.getId(), version));
iu.setCapabilities(new IProvidedCapability[] {PublisherHelper.createSelfCapability(id, version), PublisherHelper.FEATURE_CAPABILITY, MetadataFactory.createProvidedCapability(PublisherHelper.CAPABILITY_NS_UPDATE_FEATURE, feature.getId(), version)});
iu.setArtifacts(new IArtifactKey[] {createFeatureArtifactKey(feature.getId(), version.toString())});
Map touchpointData = new HashMap();
touchpointData.put("zipped", "true"); //$NON-NLS-1$ //$NON-NLS-2$
iu.addTouchpointData(MetadataFactory.createTouchpointData(touchpointData));
Map localizations = feature.getLocalizations();
if (localizations != null) {
for (Iterator iter = localizations.keySet().iterator(); iter.hasNext();) {
Locale locale = (Locale) iter.next();
Properties translatedStrings = (Properties) localizations.get(locale);
Enumeration propertyKeys = translatedStrings.propertyNames();
while (propertyKeys.hasMoreElements()) {
String nextKey = (String) propertyKeys.nextElement();
iu.setProperty(locale.toString() + '.' + nextKey, translatedStrings.getProperty(nextKey));
}
providedCapabilities.add(PublisherHelper.makeTranslationCapability(id, locale));
}
}
processInstallableUnitPropertiesAdvice(iu, info);
return MetadataFactory.createInstallableUnit(iu);
}
private static String getTransformedId(String original, boolean isPlugin, boolean isGroup) {
return (isPlugin ? original : original + (isGroup ? ".feature.group" : ".feature.jar")); //$NON-NLS-1$//$NON-NLS-2$
}
/**
* Returns a URI corresponding to the given URL in string form, or null
* if a well formed URI could not be created.
*/
private static URI toURIOrNull(String url) {
if (url == null)
return null;
try {
return URIUtil.fromString(url);
} catch (URISyntaxException e) {
return null;
}
}
public FeaturesAction(Feature[] features) {
this.features = features;
}
public FeaturesAction(File[] locations) {
this.locations = locations;
}
// attach the described files from the given location to the given iu description. Return
// the list of files identified.
private File[] attachFiles(InstallableUnitDescription iu, FileSetDescriptor descriptor, File location) {
String fileList = descriptor.getFiles();
String[] fileSpecs = getArrayFromString(fileList, ","); //$NON-NLS-1$
File[] files = new File[fileSpecs.length];
if (fileSpecs.length > 0) {
for (int i = 0; i < fileSpecs.length; i++) {
String spec = fileSpecs[i];
if (spec.startsWith("file:")) //$NON-NLS-1$
spec = spec.substring(5);
files[i] = new File(location, spec);
}
}
// add touchpoint actions to unzip and cleanup as needed
// TODO need to support fancy root file location specs
Map touchpointData = new HashMap(2);
String configurationData = "unzip(source:@artifact, target:${installFolder});"; //$NON-NLS-1$
touchpointData.put("install", configurationData); //$NON-NLS-1$
String unConfigurationData = "cleanupzip(source:@artifact, target:${installFolder});"; //$NON-NLS-1$
touchpointData.put("uninstall", unConfigurationData); //$NON-NLS-1$
iu.addTouchpointData(MetadataFactory.createTouchpointData(touchpointData));
// prime the IU with an artifact key that will correspond to the zipped up root files.
IArtifactKey key = new ArtifactKey(PublisherHelper.BINARY_ARTIFACT_CLASSIFIER, iu.getId(), iu.getVersion());
iu.setArtifacts(new IArtifactKey[] {key});
return files;
}
/**
* Looks for advice in a p2.inf file inside the feature location.
*/
private void createAdviceFileAdvice(Feature feature, IPublisherInfo publisherInfo) {
//assume p2.inf is co-located with feature.xml
String location = feature.getLocation();
if (location != null) {
String groupId = getTransformedId(feature.getId(), /*isPlugin*/false, /*isGroup*/true);
AdviceFileAdvice advice = new AdviceFileAdvice(groupId, Version.parseVersion(feature.getVersion()), new Path(location), new Path("p2.inf")); //$NON-NLS-1$
if (advice.containsAdvice())
publisherInfo.addAdvice(advice);
}
}
/**
* Gather any advice we can from the given feature. In particular, it may have
* information about the shape of the bundles it includes. The discovered advice is
* added to the given result.
* @param feature the feature to process
* @param publisherInfo the publishing info to update
*/
private void createBundleShapeAdvice(Feature feature, IPublisherInfo publisherInfo) {
FeatureEntry entries[] = feature.getEntries();
for (int i = 0; i < entries.length; i++) {
FeatureEntry entry = entries[i];
if (entry.isUnpack() && entry.isPlugin() && !entry.isRequires())
publisherInfo.addAdvice(new BundleShapeAdvice(entry.getId(), Version.parseVersion(entry.getVersion()), IBundleShapeAdvice.DIR));
}
}
protected Object[] createFeatureRootFileIU(String featureId, String featureVersion, File location, FileSetDescriptor descriptor) {
InstallableUnitDescription iu = new MetadataFactory.InstallableUnitDescription();
iu.setSingleton(true);
String id = featureId + '_' + descriptor.getKey();
iu.setId(id);
Version version = Version.parseVersion(featureVersion);
iu.setVersion(version);
iu.setCapabilities(new IProvidedCapability[] {PublisherHelper.createSelfCapability(id, version)});
iu.setTouchpointType(PublisherHelper.TOUCHPOINT_NATIVE);
String configSpec = descriptor.getConfigSpec();
if (configSpec != null && configSpec.length() > 0)
iu.setFilter(createFilterSpec(configSpec));
File[] fileResult = attachFiles(iu, descriptor, location);
setupLinks(iu, descriptor);
setupPermissions(iu, descriptor);
IInstallableUnit iuResult = MetadataFactory.createInstallableUnit(iu);
// need to return both the iu and any files.
return new Object[] {iuResult, fileResult};
}
protected IInstallableUnit createGroupIU(Feature feature, List childIUs, IPublisherInfo publisherInfo) {
if (isPatch(feature))
return createPatchIU(feature, childIUs, publisherInfo);
InstallableUnitDescription iu = new MetadataFactory.InstallableUnitDescription();
String id = getGroupId(feature.getId());
iu.setId(id);
Version version = Version.fromOSGiVersion(new org.osgi.framework.Version(feature.getVersion()));
iu.setVersion(version);
iu.setProperty(IInstallableUnit.PROP_NAME, feature.getLabel());
if (feature.getDescription() != null)
iu.setProperty(IInstallableUnit.PROP_DESCRIPTION, feature.getDescription());
if (feature.getDescriptionURL() != null)
iu.setProperty(IInstallableUnit.PROP_DESCRIPTION_URL, feature.getDescriptionURL());
if (feature.getProviderName() != null)
iu.setProperty(IInstallableUnit.PROP_PROVIDER, feature.getProviderName());
if (feature.getLicense() != null)
iu.setLicense(MetadataFactory.createLicense(toURIOrNull(feature.getLicenseURL()), feature.getLicense()));
if (feature.getCopyright() != null)
iu.setCopyright(MetadataFactory.createCopyright(toURIOrNull(feature.getCopyrightURL()), feature.getCopyright()));
iu.setUpdateDescriptor(MetadataFactory.createUpdateDescriptor(id, BundlesAction.computeUpdateRange(new org.osgi.framework.Version(feature.getVersion())), IUpdateDescriptor.NORMAL, null));
FeatureEntry entries[] = feature.getEntries();
List required = new ArrayList(entries.length + (childIUs == null ? 0 : childIUs.size()));
for (int i = 0; i < entries.length; i++) {
VersionRange range = getVersionRange(entries[i]);
String requiredId = getTransformedId(entries[i].getId(), entries[i].isPlugin(), /*isGroup*/true);
required.add(MetadataFactory.createRequiredCapability(IInstallableUnit.NAMESPACE_IU_ID, requiredId, range, getFilter(entries[i]), entries[i].isOptional(), false));
}
// link in all the children (if any) as requirements.
// TODO consider if these should be linked as exact version numbers. Should be ok but may be brittle.
if (childIUs != null) {
for (int i = 0; i < childIUs.size(); i++) {
IInstallableUnit child = (IInstallableUnit) childIUs.get(i);
required.add(MetadataFactory.createRequiredCapability(PublisherHelper.IU_NAMESPACE, child.getId(), new VersionRange(child.getVersion(), true, child.getVersion(), true), child.getFilter(), false, false));
}
}
iu.setRequiredCapabilities((IRequiredCapability[]) required.toArray(new IRequiredCapability[required.size()]));
iu.setTouchpointType(ITouchpointType.NONE);
iu.setProperty(IInstallableUnit.PROP_TYPE_GROUP, Boolean.TRUE.toString());
processTouchpointAdvice(iu, null, publisherInfo);
processInstallableUnitPropertiesAdvice(iu, publisherInfo);
//Create a fake entry to reuse the logic to create the filters
FeatureEntry entry = new FeatureEntry("fake", "0.0.0", false); //$NON-NLS-1$ //$NON-NLS-2$
entry.setEnvironment(feature.getOS(), feature.getWS(), feature.getArch(), feature.getNL());
String filter = getFilter(entry);
if (filter != null)
iu.setFilter(filter);
// Create set of provided capabilities
ArrayList providedCapabilities = new ArrayList();
providedCapabilities.add(createSelfCapability(id, version));
Map localizations = feature.getLocalizations();
if (localizations != null) {
for (Iterator iter = localizations.keySet().iterator(); iter.hasNext();) {
Locale locale = (Locale) iter.next();
Properties translatedStrings = (Properties) localizations.get(locale);
Enumeration propertyKeys = translatedStrings.propertyNames();
while (propertyKeys.hasMoreElements()) {
String nextKey = (String) propertyKeys.nextElement();
iu.setProperty(locale.toString() + '.' + nextKey, translatedStrings.getProperty(nextKey));
}
providedCapabilities.add(PublisherHelper.makeTranslationCapability(id, locale));
}
}
iu.setCapabilities((IProvidedCapability[]) providedCapabilities.toArray(new IProvidedCapability[providedCapabilities.size()]));
processCapabilityAdvice(iu, publisherInfo);
return MetadataFactory.createInstallableUnit(iu);
}
protected String getGroupId(String featureId) {
return getTransformedId(featureId, /*isPlugin*/false, /*isGroup*/true);
}
private IInstallableUnit createPatchIU(Feature feature, List childIUs, IPublisherInfo publisherInfo) {
InstallableUnitPatchDescription iu = new MetadataFactory.InstallableUnitPatchDescription();
String id = getTransformedId(feature.getId(), /*isPlugin*/false, /*isGroup*/true);
iu.setId(id);
Version version = Version.parseVersion(feature.getVersion());
iu.setVersion(version);
iu.setProperty(IInstallableUnit.PROP_NAME, feature.getLabel());
if (feature.getDescription() != null)
iu.setProperty(IInstallableUnit.PROP_DESCRIPTION, feature.getDescription());
if (feature.getDescriptionURL() != null)
iu.setProperty(IInstallableUnit.PROP_DESCRIPTION_URL, feature.getDescriptionURL());
if (feature.getProviderName() != null)
iu.setProperty(IInstallableUnit.PROP_PROVIDER, feature.getProviderName());
if (feature.getLicense() != null)
iu.setLicense(MetadataFactory.createLicense(toURIOrNull(feature.getLicenseURL()), feature.getLicense()));
if (feature.getCopyright() != null)
iu.setCopyright(MetadataFactory.createCopyright(toURIOrNull(feature.getCopyrightURL()), feature.getCopyright()));
iu.setUpdateDescriptor(MetadataFactory.createUpdateDescriptor(id, BundlesAction.computeUpdateRange(new org.osgi.framework.Version(feature.getVersion())), IUpdateDescriptor.NORMAL, null));
FeatureEntry entries[] = feature.getEntries();
ArrayList applicabilityScope = new ArrayList();
ArrayList patchRequirements = new ArrayList();
ArrayList requirementChanges = new ArrayList();
for (int i = 0; i < entries.length; i++) {
VersionRange range = getVersionRange(entries[i]);
IRequiredCapability req = MetadataFactory.createRequiredCapability(IInstallableUnit.NAMESPACE_IU_ID, getTransformedId(entries[i].getId(), entries[i].isPlugin(), /*isGroup*/true), range, getFilter(entries[i]), entries[i].isOptional(), false);
if (entries[i].isRequires()) {
applicabilityScope.add(req);
continue;
}
if (entries[i].isPlugin()) {
IRequiredCapability from = MetadataFactory.createRequiredCapability(IInstallableUnit.NAMESPACE_IU_ID, getTransformedId(entries[i].getId(), entries[i].isPlugin(), /*isGroup*/true), VersionRange.emptyRange, getFilter(entries[i]), entries[i].isOptional(), false);
requirementChanges.add(MetadataFactory.createRequirementChange(from, req));
continue;
}
patchRequirements.add(req);
}
//Always add a requirement on the IU containing the feature jar
if (childIUs != null) {
for (int i = 0; i < childIUs.size(); i++) {
IInstallableUnit child = (IInstallableUnit) childIUs.get(i);
patchRequirements.add(MetadataFactory.createRequiredCapability(PublisherHelper.IU_NAMESPACE, child.getId(), new VersionRange(child.getVersion(), true, child.getVersion(), true), child.getFilter(), false, false));
}
}
iu.setRequiredCapabilities((IRequiredCapability[]) patchRequirements.toArray(new IRequiredCapability[patchRequirements.size()]));
iu.setApplicabilityScope(new IRequiredCapability[][] {(IRequiredCapability[]) applicabilityScope.toArray(new IRequiredCapability[applicabilityScope.size()])});
iu.setRequirementChanges((IRequirementChange[]) requirementChanges.toArray(new IRequirementChange[requirementChanges.size()]));
//Generate lifecycle
IRequiredCapability lifeCycle = null;
if (applicabilityScope.size() > 0) {
IRequiredCapability req = (IRequiredCapability) applicabilityScope.get(0);
lifeCycle = MetadataFactory.createRequiredCapability(req.getNamespace(), req.getName(), req.getRange(), null, false, false, false);
iu.setLifeCycle(lifeCycle);
}
iu.setTouchpointType(ITouchpointType.NONE);
processTouchpointAdvice(iu, null, publisherInfo);
processInstallableUnitPropertiesAdvice(iu, publisherInfo);
iu.setProperty(IInstallableUnit.PROP_TYPE_GROUP, Boolean.TRUE.toString());
iu.setProperty(IInstallableUnit.PROP_TYPE_PATCH, Boolean.TRUE.toString());
// TODO: shouldn't the filter for the group be constructed from os, ws, arch, nl
// of the feature?
// iu.setFilter(filter);
// Create set of provided capabilities
ArrayList providedCapabilities = new ArrayList();
providedCapabilities.add(createSelfCapability(id, version));
Map localizations = feature.getLocalizations();
if (localizations != null) {
for (Iterator iter = localizations.keySet().iterator(); iter.hasNext();) {
Locale locale = (Locale) iter.next();
Properties translatedStrings = (Properties) localizations.get(locale);
Enumeration propertyKeys = translatedStrings.propertyNames();
while (propertyKeys.hasMoreElements()) {
String nextKey = (String) propertyKeys.nextElement();
iu.setProperty(locale.toString() + '.' + nextKey, translatedStrings.getProperty(nextKey));
}
providedCapabilities.add(PublisherHelper.makeTranslationCapability(id, locale));
}
}
iu.setCapabilities((IProvidedCapability[]) providedCapabilities.toArray(new IProvidedCapability[providedCapabilities.size()]));
processCapabilityAdvice(iu, publisherInfo);
return MetadataFactory.createInstallableUnitPatch(iu);
}
private File[] expandLocations(File[] list) {
ArrayList result = new ArrayList();
expandLocations(list, result);
return (File[]) result.toArray(new File[result.size()]);
}
private void expandLocations(File[] list, ArrayList result) {
if (list == null)
return;
for (int i = 0; i < list.length; i++) {
File location = list[i];
if (location.isDirectory()) {
// if the location is itself a feature, just add it. Otherwise r down
if (new File(location, "feature.xml").exists()) //$NON-NLS-1$
result.add(location);
else
expandLocations(location.listFiles(), result);
} else {
result.add(location);
}
}
}
protected void generateFeatureIUs(Feature[] featureList, IPublisherResult result) {
// Build Feature IUs, and add them to any corresponding categories
for (int i = 0; i < featureList.length; i++) {
Feature feature = featureList[i];
//first gather any advice that might help us
createBundleShapeAdvice(feature, info);
createAdviceFileAdvice(feature, info);
ArrayList childIUs = new ArrayList();
IInstallableUnit featureJarIU = queryForIU(result, getTransformedId(feature.getId(), false, false), Version.parseVersion(feature.getVersion()));
if (featureJarIU == null)
featureJarIU = generateFeatureJarIU(feature, info);
if (featureJarIU != null) {
publishFeatureArtifacts(feature, featureJarIU, info);
result.addIU(featureJarIU, IPublisherResult.NON_ROOT);
childIUs.add(featureJarIU);
}
IInstallableUnit groupIU = queryForIU(result, getGroupId(feature.getId()), Version.parseVersion(feature.getVersion()));
if (groupIU == null) {
childIUs.addAll(generateRootFileIUs(feature, result, info));
groupIU = createGroupIU(feature, childIUs, info);
}
if (groupIU != null) {
result.addIU(groupIU, IPublisherResult.ROOT);
InstallableUnitDescription[] others = processAdditionalInstallableUnitsAdvice(groupIU, info);
for (int iuIndex = 0; others != null && iuIndex < others.length; iuIndex++) {
result.addIU(MetadataFactory.createInstallableUnit(others[iuIndex]), IPublisherResult.ROOT);
}
}
generateSiteReferences(feature, result, info);
}
}
protected IInstallableUnit generateFeatureJarIU(Feature feature, IPublisherInfo publisherInfo) {
return createFeatureJarIU(feature, publisherInfo);
}
private IInstallableUnit generateRootFileIU(String featureId, String featureVersion, File location, FileSetDescriptor rootFile, IPublisherResult result, IPublisherInfo publisherInfo) {
File tempLocation = null;
try {
if (location.isFile()) {
// We cannot copy from a jar file. It must be expanded into a temporary folder
try {
tempLocation = File.createTempFile("p2.generator", ""); //$NON-NLS-1$ //$NON-NLS-2$
tempLocation.delete();
tempLocation.mkdirs();
FileUtils.unzipFile(location, tempLocation);
} catch (IOException e) {
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, e.getMessage()));
return null;
}
location = tempLocation;
}
Object[] iuAndFiles = createFeatureRootFileIU(featureId, featureVersion, location, rootFile);
IInstallableUnit iuResult = (IInstallableUnit) iuAndFiles[0];
File[] fileResult = (File[]) iuAndFiles[1];
if (fileResult != null && fileResult.length > 0) {
IArtifactKey artifact = iuResult.getArtifacts()[0];
ArtifactDescriptor descriptor = new ArtifactDescriptor(artifact);
publishArtifact(descriptor, fileResult, null, publisherInfo, FileUtils.createDynamicPathComputer(1));
}
result.addIU(iuResult, IPublisherResult.NON_ROOT);
return iuResult;
} finally {
if (tempLocation != null)
FileUtils.deleteAll(tempLocation);
}
}
protected ArrayList generateRootFileIUs(Feature feature, IPublisherResult result, IPublisherInfo publisherInfo) {
File location = new File(feature.getLocation());
Properties props = loadProperties(location, "build.properties"); //$NON-NLS-1$
ArrayList ius = new ArrayList();
FileSetDescriptor[] rootFileDescriptors = getRootFileDescriptors(props);
for (int i = 0; i < rootFileDescriptors.length; i++) {
IInstallableUnit iu = generateRootFileIU(feature.getId(), feature.getVersion(), location, rootFileDescriptors[i], result, publisherInfo);
if (iu != null)
ius.add(iu);
}
return ius;
}
/**
* Generates and publishes a reference to an update site location
* @param location The update site location
* @param nickname The update site label
* @param featureId the identifier of the feature where the error occurred, or null
* @param metadataRepo The repo into which the references are added
*/
private void generateSiteReference(String location, String nickname, String featureId, IMetadataRepository metadataRepo) {
if (location == null) {
String message = featureId == null ? NLS.bind(Messages.exception_invalidSiteReference, location) : NLS.bind(Messages.exception_invalidSiteReferenceInFeature, location, featureId);
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, message));
return;
}
try {
URI associateLocation = new URI(location);
metadataRepo.addReference(associateLocation, nickname, IRepository.TYPE_METADATA, IRepository.NONE);
metadataRepo.addReference(associateLocation, nickname, IRepository.TYPE_ARTIFACT, IRepository.NONE);
} catch (URISyntaxException e) {
String message = featureId == null ? NLS.bind(Messages.exception_invalidSiteReference, location) : NLS.bind(Messages.exception_invalidSiteReferenceInFeature, location, featureId);
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, message));
}
}
protected void generateSiteReferences(Feature feature, IPublisherResult result, IPublisherInfo publisherInfo) {
//publish feature site references
URLEntry updateURL = feature.getUpdateSite();
//don't enable feature update sites by default since this results in too many
//extra sites being loaded and searched (Bug 234177)
if (updateURL != null)
generateSiteReference(updateURL.getURL(), updateURL.getAnnotation(), feature.getId(), publisherInfo.getMetadataRepository());
URLEntry[] discoverySites = feature.getDiscoverySites();
for (int i = 0; i < discoverySites.length; i++)
generateSiteReference(discoverySites[i].getURL(), discoverySites[i].getAnnotation(), feature.getId(), publisherInfo.getMetadataRepository());
}
protected Feature[] getFeatures(File[] featureLocations) {
ArrayList result = new ArrayList(featureLocations.length);
for (int i = 0; i < featureLocations.length; i++) {
Feature feature = new FeatureParser().parse(featureLocations[i]);
if (feature != null) {
feature.setLocation(featureLocations[i].getAbsolutePath());
result.add(feature);
}
}
return (Feature[]) result.toArray(new Feature[result.size()]);
}
private String getFilter(FeatureEntry entry) {
StringBuffer result = new StringBuffer();
result.append("(&"); //$NON-NLS-1$
if (entry.getFilter() != null)
result.append(entry.getFilter());
expandFilter(entry.getOS(), "osgi.os", result); //$NON-NLS-1$
expandFilter(entry.getWS(), "osgi.ws", result); //$NON-NLS-1$
expandFilter(entry.getArch(), "osgi.arch", result);//$NON-NLS-1$
expandFilter(entry.getNL(), "osgi.nl", result); //$NON-NLS-1$
if (result.length() == 2)
return null;
result.append(')');
return result.toString();
}
private void expandFilter(String filter, String osgiFilterValue, StringBuffer result) {
if (filter != null) {
StringTokenizer token = new StringTokenizer(filter, ","); //$NON-NLS-1$
if (token.countTokens() == 1)
result.append('(' + osgiFilterValue + '=' + filter + ')');
else {
result.append("(|"); //$NON-NLS-1$
while (token.hasMoreElements()) {
result.append('(' + osgiFilterValue + '=' + token.nextToken() + ')');
}
result.append(')');
}
}
}
protected FileSetDescriptor[] getRootFileDescriptors(Properties props) {
HashMap result = new HashMap();
for (Iterator i = props.keySet().iterator(); i.hasNext();) {
String property = (String) i.next();
// we only care about root properties
if (!(property.startsWith("root") && (property.length() == 4 || property.charAt(4) == '.'))) //$NON-NLS-1$
continue;
String[] spec = getArrayFromString(property, "."); //$NON-NLS-1$
String descriptorKey = spec[0];
String configSpec = null;
// if the spec is 4 or more then there must be a config involved to get it
if (spec.length > 3) {
configSpec = createConfigSpec(spec[2], spec[1], spec[3]);
descriptorKey += "." + createIdString(configSpec); //$NON-NLS-1$
}
FileSetDescriptor descriptor = (FileSetDescriptor) result.get(descriptorKey);
if (descriptor == null) {
descriptor = new FileSetDescriptor(descriptorKey, configSpec);
result.put(descriptorKey, descriptor);
}
// if the property was simply 'root'
if (spec.length == 1)
// it must be a straight file copy (without a config)
descriptor.setFiles(props.getProperty(property));
// if the last segment in the spec is "link"
else if (spec[spec.length - 1] == "link") //$NON-NLS-1$
descriptor.setLinks(props.getProperty(property));
else {
// if the second last segment is "permissions"
if (spec[spec.length - 2].equals("permissions")) //$NON-NLS-1$
descriptor.addPermissions(new String[] {spec[spec.length - 1], props.getProperty(property)});
else {
// so it is not a link or a permissions, it must be a straight file copy (with or without a config)
descriptor.setFiles(props.getProperty(property));
}
}
}
Collection values = result.values();
return (FileSetDescriptor[]) values.toArray(new FileSetDescriptor[values.size()]);
}
private VersionRange getVersionRange(FeatureEntry entry) {
String versionSpec = entry.getVersion();
if (versionSpec == null)
return VersionRange.emptyRange;
Version version = Version.parseVersion(versionSpec);
if (version.equals(Version.emptyVersion))
return VersionRange.emptyRange;
if (!entry.isRequires())
return new VersionRange(version, true, version, true);
String match = entry.getMatch();
if (match == null)
// TODO should really be returning VersionRange.emptyRange here...
return null;
if (match.equals("perfect")) //$NON-NLS-1$
return new VersionRange(version, true, version, true);
if (match.equals("equivalent")) { //$NON-NLS-1$
Version upper = Version.createOSGi(version.getMajor(), version.getMinor() + 1, 0);
return new VersionRange(version, true, upper, false);
}
if (match.equals("compatible")) { //$NON-NLS-1$
Version upper = Version.createOSGi(version.getMajor() + 1, 0, 0);
return new VersionRange(version, true, upper, false);
}
if (match.equals("greaterOrEqual")) //$NON-NLS-1$
return new VersionRange(version, true, new VersionRange(null).getMaximum(), true);
return null;
}
private boolean isPatch(Feature feature) {
FeatureEntry[] entries = feature.getEntries();
for (int i = 0; i < entries.length; i++) {
if (entries[i].isPatch())
return true;
}
return false;
}
private static Properties loadProperties(File location, String file) {
Properties props = new Properties();
try {
if (!location.isDirectory()) {
JarFile jar = null;
try {
jar = new JarFile(location);
JarEntry entry = jar.getJarEntry(file);
if (entry != null)
parseProperties(jar.getInputStream(entry), props, location.toString() + '#' + file);
} finally {
if (jar != null)
jar.close();
}
} else {
try {
InputStream in = null;
try {
File propsFile = new File(location, file);
in = new FileInputStream(propsFile);
parseProperties(in, props, propsFile.toString());
} finally {
if (in != null)
in.close();
}
} catch (FileNotFoundException e) {
// ignore if it is just a file not found.
}
}
} catch (IOException e) {
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, Messages.exception_errorPublishingArtifacts, e));
}
return props;
}
private static void parseProperties(InputStream in, Properties props, String file) {
try {
props.load(new BufferedInputStream(in));
} catch (IOException e) {
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.exception_errorLoadingProperties, file), e));
}
}
public IStatus perform(IPublisherInfo publisherInfo, IPublisherResult results, IProgressMonitor monitor) {
if (features == null && locations == null)
throw new IllegalStateException(Messages.exception_noFeaturesOrLocations);
this.info = publisherInfo;
if (features == null)
features = getFeatures(expandLocations(locations));
generateFeatureIUs(features, results);
return Status.OK_STATUS;
}
protected void publishFeatureArtifacts(Feature feature, IInstallableUnit featureIU, IPublisherInfo publisherInfo) {
// add all the artifacts associated with the feature
// TODO this is a little strange. If there are several artifacts, how do we know which files go with
// which artifacts when we publish them? For now it would be surprising to have more than one
// artifact per feature IU.
IArtifactKey[] artifacts = featureIU.getArtifacts();
for (int j = 0; j < artifacts.length; j++) {
File file = new File(feature.getLocation());
ArtifactDescriptor ad = (ArtifactDescriptor) PublisherHelper.createArtifactDescriptor(artifacts[j], file);
processArtifactPropertiesAdvice(featureIU, ad, publisherInfo);
ad.setProperty(IArtifactDescriptor.DOWNLOAD_CONTENTTYPE, IArtifactDescriptor.TYPE_ZIP);
// if the artifact is a dir then zip it up.
if (file.isDirectory())
publishArtifact(ad, new File[] {file}, null, publisherInfo, createRootPrefixComputer(file));
else
publishArtifact(ad, file, publisherInfo);
}
}
private void setupLinks(InstallableUnitDescription iu, FileSetDescriptor descriptor) {
String[] links = getArrayFromString(descriptor.getLinks(), ","); //$NON-NLS-1$
StringBuffer linkActions = new StringBuffer();
int i = 0;
while (i < links.length) {
//format is [target,name]*
String target = links[i++];
if (i < links.length) {
String name = links[i++];
linkActions.append("ln(linkTarget:" + target); //$NON-NLS-1$
linkActions.append(",targetDir:${installFolder},linkName:" + name); //$NON-NLS-1$
linkActions.append(");"); //$NON-NLS-1$
}
}
if (linkActions.length() > 0) {
Map touchpointData = new HashMap();
//we do ln during configure to avoid complicating branding which uses the install phase
touchpointData.put("configure", linkActions.toString()); //$NON-NLS-1$
iu.addTouchpointData(MetadataFactory.createTouchpointData(touchpointData));
}
}
private void setupPermissions(InstallableUnitDescription iu, FileSetDescriptor descriptor) {
Map touchpointData = new HashMap();
String[][] permsList = descriptor.getPermissions();
for (int i = 0; i < permsList.length; i++) {
String[] permSpec = permsList[i];
String configurationData = " chmod(targetDir:${installFolder}, targetFile:" + permSpec[1] + ", permissions:" + permSpec[0] + ");"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
touchpointData.put("install", configurationData); //$NON-NLS-1$
iu.addTouchpointData(MetadataFactory.createTouchpointData(touchpointData));
}
}
}