/*******************************************************************************
* Copyright (c) 2006-2013, Cloudsmith Inc.
* The code, documentation and other materials contained herein have been
* licensed under the Eclipse Public License - v 1.0 by the copyright holder
* listed above, as the Initial Contributor under such license. The text of
* such license is available at www.eclipse.org.
******************************************************************************/
package org.eclipse.buckminster.pde.tasks;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.buckminster.core.TargetPlatform;
import org.eclipse.buckminster.core.actor.IActionContext;
import org.eclipse.buckminster.core.version.VersionHelper;
import org.eclipse.buckminster.pde.Messages;
import org.eclipse.buckminster.runtime.Buckminster;
import org.eclipse.buckminster.runtime.Logger;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.equinox.frameworkadmin.BundleInfo;
import org.eclipse.equinox.internal.p2.publisher.eclipse.IProductDescriptor;
import org.eclipse.equinox.internal.provisional.frameworkadmin.ConfigData;
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
import org.eclipse.equinox.p2.metadata.ITouchpointData;
import org.eclipse.equinox.p2.metadata.ITouchpointInstruction;
import org.eclipse.equinox.p2.metadata.IVersionedId;
import org.eclipse.equinox.p2.metadata.MetadataFactory;
import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription;
import org.eclipse.equinox.p2.metadata.Version;
import org.eclipse.equinox.p2.metadata.VersionRange;
import org.eclipse.equinox.p2.publisher.AbstractAdvice;
import org.eclipse.equinox.p2.publisher.AbstractPublisherAction;
import org.eclipse.equinox.p2.publisher.IPublisherAction;
import org.eclipse.equinox.p2.publisher.IPublisherAdvice;
import org.eclipse.equinox.p2.publisher.IPublisherInfo;
import org.eclipse.equinox.p2.publisher.IPublisherResult;
import org.eclipse.equinox.p2.publisher.PublisherInfo;
import org.eclipse.equinox.p2.publisher.PublisherResult;
import org.eclipse.equinox.p2.publisher.actions.IAdditionalInstallableUnitAdvice;
import org.eclipse.equinox.p2.publisher.actions.ITouchpointAdvice;
import org.eclipse.equinox.p2.publisher.eclipse.ConfigAdvice;
import org.eclipse.equinox.p2.publisher.eclipse.EquinoxLauncherCUAction;
import org.eclipse.equinox.p2.publisher.eclipse.IConfigAdvice;
import org.eclipse.equinox.p2.query.IQuery;
import org.eclipse.equinox.p2.query.IQueryable;
import org.eclipse.equinox.p2.query.QueryUtil;
import org.eclipse.osgi.service.environment.Constants;
import org.eclipse.osgi.util.NLS;
import org.eclipse.pde.internal.build.IPDEBuildConstants;
/**
* Action that generates version adjusted products
*/
@SuppressWarnings("restriction")
public class ProductAction extends org.eclipse.equinox.p2.publisher.eclipse.ProductAction implements IPDEBuildConstants {
/**
* Special advice that sets the osgi.instance.area.default in case it hasn't
* bee set by other means.
*/
static class InstanceAreaAdvice extends AbstractAdvice implements IConfigAdvice {
private final String configSpec;
private final String instanceArea;
private static final BundleInfo[] noBundles = new BundleInfo[0];
InstanceAreaAdvice(String configSpec, String instanceArea) {
this.configSpec = configSpec;
this.instanceArea = instanceArea;
}
@Override
public BundleInfo[] getBundles() {
return noBundles;
}
@Override
public String getConfigSpec() {
return configSpec;
}
@Override
public Map<String, String> getProperties() {
return Collections.singletonMap(PROP_OSGI_INSTANCE_AREA_DEFAULT, instanceArea);
}
}
private static final String PROP_OSGI_INSTANCE_AREA_DEFAULT = "osgi.instance.area.default"; //$NON-NLS-1$
public ProductAction(IActionContext actionContext, String src, IProductDescriptor productDesc, String flvor, File exeFeatureLocation) {
super(src, new ProductVersionPatcher(productDesc, actionContext), flvor, exeFeatureLocation);
}
@Override
public IStatus perform(IPublisherInfo publisherInfo, IPublisherResult results, IProgressMonitor monitor) {
((ProductVersionPatcher) product).setQueryable(results);
IPublisherResult innerResult = new PublisherResult();
PublisherInfo innerInfo = new PublisherInfo();
String[] configs = publisherInfo.getConfigurations();
if (executablesFeatureLocation == null) {
// We can only create one single configuration and that's the one of
// the running platform
//
Logger log = Buckminster.getLogger();
String availableConfig = Platform.getWS() + '.' + Platform.getOS() + '.' + Platform.getOSArch();
boolean warningsPrinted = false;
boolean availConfigFound = false;
for (String config : configs) {
if (config.equals(availableConfig)) {
availConfigFound = true;
continue;
}
log.warning(NLS.bind(Messages.Missing_exe_launcher_for_config_0, config));
warningsPrinted = true;
}
if (warningsPrinted)
log.warning(Messages.Suggest_install_launchers_feature);
if (!availConfigFound)
return Status.OK_STATUS;
innerInfo.setConfigurations(new String[] { availableConfig });
if (Platform.inDevelopmentMode()) {
String ilProp = System.getProperty("eclipse.host.location"); //$NON-NLS-1$
if (ilProp == null) {
log.warning(Messages.Please_use_selfhost_vmargs);
} else
source = ilProp;
} else
source = TargetPlatform.getPlatformInstallLocation().getAbsolutePath();
} else
innerInfo.setConfigurations(configs);
innerInfo.setArtifactOptions(publisherInfo.getArtifactOptions());
innerInfo.setArtifactRepository(publisherInfo.getArtifactRepository());
innerInfo.setMetadataRepository(publisherInfo.getMetadataRepository());
innerInfo.setContextArtifactRepository(publisherInfo.getContextArtifactRepository());
innerInfo.setContextMetadataRepository(innerInfo.getContextMetadataRepository());
// The inner result must see the launchers since the
// EquinoxLauncherCUAction created by the
// ApplicationLauncherAction must find them in order to generate the
// correct CU's
//
IQueryable<IInstallableUnit> bundleScope = getTransitiveBundleScope(results);
ConfigData dfltStartInfos = new ConfigData(null, null, null, null);
nextIU: for (IInstallableUnit iu : results.getIUs(null, IPublisherResult.ROOT)) {
String symbolicId = iu.getId();
if (symbolicId.startsWith(EquinoxLauncherCUAction.ORG_ECLIPSE_EQUINOX_LAUNCHER)) {
innerResult.addIU(iu, IPublisherResult.ROOT);
continue;
}
if (product.useFeatures() && symbolicId.endsWith(".feature.group")) { //$NON-NLS-1$
// Strip '.feature.group'
String legacyId = symbolicId.substring(0, symbolicId.length() - 14);
for (IVersionedId feature : product.getFeatures())
if (legacyId.equals(feature.getId())) {
innerResult.addIU(iu, IPublisherResult.ROOT);
continue nextIU;
}
}
for (BundleInfo configBi : product.getBundleInfos()) {
if (!symbolicId.equals(configBi.getSymbolicName()))
continue;
Version configVer = null;
if (configBi.getVersion() != null) {
configVer = Version.parseVersion(configBi.getVersion());
if (configVer.equals(Version.emptyVersion))
configVer = null;
}
if (configVer != null && !configVer.equals(iu.getVersion()))
continue;
innerResult.addIU(iu, IPublisherResult.ROOT);
continue nextIU;
}
for (BundleInfo bi : getDefaultStartInfo()) {
if (!symbolicId.equals(bi.getSymbolicName()))
continue;
// Verify that this bundle is in the transitive scope
// of the product. We don't want to add it if it's not
// present
if (bundleScope.query(QueryUtil.createIUQuery(iu), monitor).isEmpty())
continue;
innerResult.addIU(iu, IPublisherResult.ROOT);
dfltStartInfos.addBundle(bi);
continue nextIU;
}
}
int dfltSICount = dfltStartInfos.getBundles().length;
for (String configSpec : innerInfo.getConfigurations()) {
if (dfltSICount > 0)
innerInfo.addAdvice(new ConfigAdvice(dfltStartInfos, configSpec));
String os = AbstractPublisherAction.parseConfigSpec(configSpec)[1];
if (Constants.OS_MACOSX.equals(os)) {
innerInfo.addAdvice(new InstanceAreaAdvice(configSpec, "@user.home/Documents/workspace")); //$NON-NLS-1$
} else {
innerInfo.addAdvice(new InstanceAreaAdvice(configSpec, "@user.home/workspace")); //$NON-NLS-1$
}
}
IStatus status = super.perform(innerInfo, innerResult, monitor);
if (status.getSeverity() != IStatus.ERROR)
results.merge(innerResult, IPublisherResult.MERGE_MATCHING);
return status;
}
@Override
protected IPublisherAction[] createActions(IPublisherResult results) {
IPublisherAction[] actions = super.createActions(results);
if (hasOtherInstanceAreaAdvice()) {
List<IPublisherAdvice> adviceList = ((PublisherInfo) info).getAdvice();
int idx = adviceList.size();
while (--idx >= 0) {
IPublisherAdvice advice = adviceList.get(idx);
if (advice instanceof InstanceAreaAdvice)
adviceList.remove(idx);
}
}
if (getExecutablesLocation() == null) {
IPublisherAction[] newActions = new IPublisherAction[actions.length + 1];
System.arraycopy(actions, 0, newActions, 1, actions.length);
actions = newActions;
actions[0] = createApplicationExecutableAction(info.getConfigurations());
}
return actions;
}
@Override
protected IPublisherAction createApplicationExecutableAction(String[] configSpecs) {
return new ApplicationLauncherAction(id, version, flavor, executableName, getExecutablesLocation(), configSpecs);
}
protected boolean hasOtherInstanceAreaAdvice() {
List<IPublisherAdvice> adviceList = ((PublisherInfo) info).getAdvice();
int idx = adviceList.size();
while (--idx >= 0) {
IPublisherAdvice advice = adviceList.get(idx);
if (advice instanceof IConfigAdvice && !(advice instanceof InstanceAreaAdvice)) {
IConfigAdvice pfAdvice = (IConfigAdvice) advice;
if (pfAdvice.getProperties().containsKey(PROP_OSGI_INSTANCE_AREA_DEFAULT))
return true;
}
if (advice instanceof ITouchpointAdvice) {
ITouchpointData tpData = MetadataFactory.createTouchpointData(Collections.<String, Object> emptyMap());
ITouchpointAdvice tpAdvice = (ITouchpointAdvice) advice;
tpData = tpAdvice.getTouchpointData(tpData);
Object configure = tpData.getInstruction("configure"); //$NON-NLS-1$
if (configure instanceof ITouchpointInstruction) {
String configString = ((ITouchpointInstruction) configure).getBody();
if (configString.startsWith("setProgramProperty") && configString.contains(PROP_OSGI_INSTANCE_AREA_DEFAULT)) //$NON-NLS-1$
return true;
}
}
if (advice instanceof IAdditionalInstallableUnitAdvice) {
IAdditionalInstallableUnitAdvice iuAdvice = (IAdditionalInstallableUnitAdvice) advice;
InstallableUnitDescription[] descs = iuAdvice.getAdditionalInstallableUnitDescriptions(null);
if (descs != null) {
for (InstallableUnitDescription desc : descs) {
for (ITouchpointData tpData : desc.getTouchpointData()) {
Object configure = tpData.getInstruction("configure"); //$NON-NLS-1$
if (configure instanceof ITouchpointInstruction) {
String configString = ((ITouchpointInstruction) configure).getBody();
if (configString.startsWith("setProgramProperty") && configString.contains(PROP_OSGI_INSTANCE_AREA_DEFAULT)) //$NON-NLS-1$
return true;
}
}
}
}
}
}
return false;
}
IQueryable<IInstallableUnit> getTransitiveBundleScope(IPublisherResult results) {
IProgressMonitor monitor = new NullProgressMonitor();
List<IInstallableUnit> roots = new ArrayList<IInstallableUnit>();
if (!product.useFeatures()) {
for (IVersionedId bundle : product.getBundles(false)) {
VersionRange vr = VersionHelper.unqualifiedRange(bundle.getVersion());
IQuery<IInstallableUnit> iuQuery = QueryUtil.createIUQuery(bundle.getId(), vr);
Iterator<IInstallableUnit> itor = results.query(iuQuery, monitor).iterator();
while (itor.hasNext())
roots.add(itor.next());
}
} else {
for (IVersionedId feature : product.getFeatures()) {
VersionRange vr = VersionHelper.unqualifiedRange(feature.getVersion());
IQuery<IInstallableUnit> iuQuery = QueryUtil.createIUQuery(feature.getId() + ".feature.group", vr); //$NON-NLS-1$
Iterator<IInstallableUnit> itor = results.query(iuQuery, monitor).iterator();
while (itor.hasNext())
roots.add(itor.next());
}
}
IQuery<IInstallableUnit> traversal = QueryUtil.createQuery( //
"$0.traverse(set(), _, { rqCache, parent | " + // //$NON-NLS-1$
"parent.requirements.unique(rqCache)" + // //$NON-NLS-1$
".collect(rc | everything.select(iu | iu ~= rc)).flatten()})", roots); //$NON-NLS-1$
return results.query(traversal, monitor);
}
private BundleInfo[] getDefaultStartInfo() {
BundleInfo[] defaults = new BundleInfo[6];
defaults[0] = new BundleInfo(BUNDLE_SIMPLE_CONFIGURATOR, null, null, 1, true);
defaults[1] = new BundleInfo(BUNDLE_EQUINOX_COMMON, null, null, 2, true);
defaults[2] = new BundleInfo(BUNDLE_OSGI, null, null, -1, true);
defaults[3] = new BundleInfo(BUNDLE_UPDATE_CONFIGURATOR, null, null, 4, true);
defaults[4] = new BundleInfo(BUNDLE_CORE_RUNTIME, null, null, 4, true);
defaults[5] = new BundleInfo(BUNDLE_DS, null, null, 2, true);
return defaults;
}
}