| /******************************************************************************* |
| * Copyright (c) 2008 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.File; |
| import java.io.IOException; |
| import java.net.URL; |
| import java.util.*; |
| import java.util.Map.Entry; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.equinox.internal.p2.publisher.eclipse.GeneratorBundleInfo; |
| import org.eclipse.equinox.internal.provisional.frameworkadmin.BundleInfo; |
| 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.query.InstallableUnitQuery; |
| import org.eclipse.equinox.internal.provisional.p2.query.Collector; |
| import org.eclipse.equinox.internal.provisional.p2.query.Query; |
| import org.eclipse.equinox.p2.publisher.*; |
| import org.eclipse.equinox.spi.p2.publisher.PublisherHelper; |
| import org.eclipse.osgi.util.ManifestElement; |
| import org.osgi.framework.*; |
| |
| /** |
| * Publish CUs for all the configuration data in the current result. |
| * This adds config-specific CUs to capture start levels etc found in the config.ini |
| * etc for is os, ws, arch combination seen so far. |
| */ |
| public class ConfigCUsAction extends AbstractPublisherAction { |
| |
| protected static final String ORG_ECLIPSE_UPDATE_CONFIGURATOR = "org.eclipse.update.configurator"; //$NON-NLS-1$ |
| private static Collection PROPERTIES_TO_SKIP; |
| private static HashSet PROGRAM_ARGS_TO_SKIP; |
| protected Version version; |
| protected String id; |
| protected String flavor; |
| |
| // TODO consider moving this filtering to the LaunchingAdvice and ConfigAdvice so |
| // it is not hardcoded in the action. |
| static { |
| PROPERTIES_TO_SKIP = new HashSet(); |
| PROPERTIES_TO_SKIP.add("osgi.frameworkClassPath"); //$NON-NLS-1$ |
| PROPERTIES_TO_SKIP.add("osgi.framework"); //$NON-NLS-1$ |
| PROPERTIES_TO_SKIP.add("osgi.bundles"); //$NON-NLS-1$ |
| PROPERTIES_TO_SKIP.add("eof"); //$NON-NLS-1$ |
| PROPERTIES_TO_SKIP.add("eclipse.p2.profile"); //$NON-NLS-1$ |
| PROPERTIES_TO_SKIP.add("eclipse.p2.data.area"); //$NON-NLS-1$ |
| PROPERTIES_TO_SKIP.add("osgi.launcherPath"); //$NON-NLS-1$ |
| PROPERTIES_TO_SKIP.add("org.eclipse.update.reconcile"); //$NON-NLS-1$ |
| PROPERTIES_TO_SKIP.add("org.eclipse.equinox.simpleconfigurator.configUrl"); //$NON-NLS-1$ |
| |
| PROGRAM_ARGS_TO_SKIP = new HashSet(); |
| PROGRAM_ARGS_TO_SKIP.add("--launcher.library"); //$NON-NLS-1$ |
| PROGRAM_ARGS_TO_SKIP.add("-startup"); //$NON-NLS-1$ |
| PROGRAM_ARGS_TO_SKIP.add("-configuration"); //$NON-NLS-1$ |
| } |
| |
| public static String getCUId(String id, String type, String flavor, String configSpec) { |
| return flavor + id + "." + type + "." + AbstractPublisherAction.createIdString(configSpec); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| public static String getAbstractCUCapabilityNamespace(String id, String type, String flavor, String configSpec) { |
| return flavor + id; |
| } |
| |
| public static String getAbstractCUCapabilityId(String id, String type, String flavor, String configSpec) { |
| return id + "." + type; |
| } |
| |
| /** |
| * Returns the id of the top level IU published by this action for the given id and flavor. |
| * @param id the id of the application being published |
| * @param flavor the flavor being published |
| * @return the if for ius published by this action |
| */ |
| public static String computeIUId(String id, String flavor) { |
| return flavor + id + ".configuration"; //$NON-NLS-1$ |
| } |
| |
| public ConfigCUsAction(IPublisherInfo info, String flavor, String id, Version version) { |
| this.flavor = flavor; |
| this.id = id; |
| this.version = version; |
| } |
| |
| public IStatus perform(IPublisherInfo info, IPublisherResult results, IProgressMonitor monitor) { |
| IPublisherResult innerResult = new PublisherResult(); |
| // we have N platforms, generate a CU for each |
| // TODO try and find common properties across platforms |
| String[] configSpecs = info.getConfigurations(); |
| for (int i = 0; i < configSpecs.length; i++) { |
| if (monitor.isCanceled()) |
| return Status.CANCEL_STATUS; |
| String configSpec = configSpecs[i]; |
| Collection configAdvice = info.getAdvice(configSpec, false, null, null, IConfigAdvice.class); |
| BundleInfo[] bundles = fillInBundles(configAdvice, results); |
| publishBundleCUs(info, bundles, configSpec, innerResult); |
| publishConfigIUs(configAdvice, innerResult, configSpec); |
| Collection launchingAdvice = info.getAdvice(configSpec, false, null, null, ILaunchingAdvice.class); |
| publishIniIUs(launchingAdvice, innerResult, configSpec); |
| } |
| // merge the IUs into the final result as non-roots and create a parent IU that captures them all |
| results.merge(innerResult, IPublisherResult.MERGE_ALL_NON_ROOT); |
| publishTopLevelConfigurationIU(innerResult.getIUs(null, IPublisherResult.ROOT), results); |
| return Status.OK_STATUS; |
| } |
| |
| private void publishTopLevelConfigurationIU(Collection children, IPublisherResult result) { |
| InstallableUnitDescription descriptor = createParentIU(children, computeIUId(id, flavor), version); |
| descriptor.setSingleton(true); |
| IInstallableUnit rootIU = MetadataFactory.createInstallableUnit(descriptor); |
| if (rootIU == null) |
| return; |
| result.addIU(rootIU, IPublisherResult.ROOT); |
| } |
| |
| // there seem to be cases where the bundle infos are not filled in with symbolic name and version. |
| // fill in the missing data. |
| private BundleInfo[] fillInBundles(Collection configAdvice, IPublisherResult results) { |
| ArrayList result = new ArrayList(); |
| for (Iterator j = configAdvice.iterator(); j.hasNext();) { |
| IConfigAdvice advice = (IConfigAdvice) j.next(); |
| BundleInfo[] bundles = advice.getBundles(); |
| for (int i = 0; i < bundles.length; i++) { |
| BundleInfo bundleInfo = bundles[i]; |
| // prime the result with the current info. This will be replaced if there is more info... |
| if ((bundleInfo.getSymbolicName() != null && bundleInfo.getVersion() != null) || bundleInfo.getLocation() == null) |
| result.add(bundles[i]); |
| else { |
| try { |
| File location = new File(new URL(bundleInfo.getLocation()).getPath()); |
| Dictionary manifest = BundlesAction.loadManifest(location); |
| if (manifest == null) |
| continue; |
| GeneratorBundleInfo newInfo = new GeneratorBundleInfo(bundleInfo); |
| ManifestElement[] element = ManifestElement.parseHeader("dummy-bsn", (String) manifest.get(Constants.BUNDLE_SYMBOLICNAME)); //$NON-NLS-1$ |
| newInfo.setSymbolicName(element[0].getValue()); |
| newInfo.setVersion((String) manifest.get(Constants.BUNDLE_VERSION)); |
| result.add(newInfo); |
| } catch (BundleException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } catch (IOException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| } |
| } |
| } |
| return (BundleInfo[]) result.toArray(new BundleInfo[result.size()]); |
| } |
| |
| /** |
| * Publish the IUs that capture the eclipse.ini information such as vmargs and program args, etc |
| */ |
| private void publishIniIUs(Collection launchingAdvice, IPublisherResult results, String configSpec) { |
| if (launchingAdvice.isEmpty()) |
| return; |
| |
| String configureData = ""; //$NON-NLS-1$ |
| String unconfigureData = ""; //$NON-NLS-1$ |
| if (!launchingAdvice.isEmpty()) { |
| String[] dataStrings = getLauncherConfigStrings(launchingAdvice); |
| configureData += dataStrings[0]; |
| unconfigureData += dataStrings[1]; |
| } |
| // if there is nothing to configure or unconfigure, then don't even bother generating this IU |
| if (configureData.length() == 0 && unconfigureData.length() == 0) |
| return; |
| |
| Map touchpointData = new HashMap(); |
| touchpointData.put("configure", configureData); //$NON-NLS-1$ |
| touchpointData.put("unconfigure", unconfigureData); //$NON-NLS-1$ |
| IInstallableUnit cu = createCU(id, version, "ini", flavor, configSpec, touchpointData); //$NON-NLS-1$ |
| results.addIU(cu, IPublisherResult.ROOT); |
| } |
| |
| /** |
| * Publish the IUs that capture the config.ini information such as properties etc |
| */ |
| private void publishConfigIUs(Collection configAdvice, IPublisherResult results, String configSpec) { |
| if (configAdvice.isEmpty()) |
| return; |
| |
| String configureData = ""; //$NON-NLS-1$ |
| String unconfigureData = ""; //$NON-NLS-1$ |
| if (!configAdvice.isEmpty()) { |
| String[] dataStrings = getConfigurationStrings(configAdvice); |
| configureData += dataStrings[0]; |
| unconfigureData += dataStrings[1]; |
| } |
| // if there is nothing to configure or unconfigure, then don't even bother generating this IU |
| if (configureData.length() == 0 && unconfigureData.length() == 0) |
| return; |
| |
| Map touchpointData = new HashMap(); |
| touchpointData.put("configure", configureData); //$NON-NLS-1$ |
| touchpointData.put("unconfigure", unconfigureData); //$NON-NLS-1$ |
| IInstallableUnit cu = createCU(id, version, "config", flavor, configSpec, touchpointData); //$NON-NLS-1$ |
| results.addIU(cu, IPublisherResult.ROOT); |
| } |
| |
| /** |
| * Create a CU whose id is flavor+id.type.configspec with the given version. |
| * The resultant IU has the self capability and an abstract capabilty in the flavor+id namespace |
| * with the name id.type and the given version. This allows others to create an abstract |
| * dependency on having one of these things around but not having to list out the configs. |
| */ |
| private IInstallableUnit createCU(String id, Version version, String type, String flavor, String configSpec, Map touchpointData) { |
| InstallableUnitDescription cu = new InstallableUnitDescription(); |
| String resultId = getCUId(id, type, flavor, configSpec); |
| cu.setId(resultId); |
| cu.setVersion(version); |
| cu.setFilter(AbstractPublisherAction.createFilterSpec(configSpec)); |
| cu.setProperty(IInstallableUnit.PROP_TYPE_FRAGMENT, Boolean.TRUE.toString()); |
| ProvidedCapability selfCapability = PublisherHelper.createSelfCapability(resultId, version); |
| String namespace = getAbstractCUCapabilityNamespace(id, type, flavor, configSpec); |
| String abstractId = getAbstractCUCapabilityId(id, type, flavor, configSpec); |
| ProvidedCapability abstractCapability = MetadataFactory.createProvidedCapability(namespace, abstractId, version); |
| cu.setCapabilities(new ProvidedCapability[] {selfCapability, abstractCapability}); |
| cu.addTouchpointData(MetadataFactory.createTouchpointData(touchpointData)); |
| cu.setTouchpointType(PublisherHelper.TOUCHPOINT_OSGI); |
| return MetadataFactory.createInstallableUnit(cu); |
| } |
| |
| protected String[] getConfigurationStrings(Collection configAdvice) { |
| String configurationData = ""; //$NON-NLS-1$ |
| String unconfigurationData = ""; //$NON-NLS-1$ |
| for (Iterator i = configAdvice.iterator(); i.hasNext();) { |
| IConfigAdvice advice = (IConfigAdvice) i.next(); |
| for (Iterator iterator = advice.getProperties().entrySet().iterator(); iterator.hasNext();) { |
| Entry aProperty = (Entry) iterator.next(); |
| String key = ((String) aProperty.getKey()); |
| if (shouldPublishProperty(key)) { |
| configurationData += "setProgramProperty(propName:" + key + ", propValue:" + ((String) aProperty.getValue()) + ");"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| unconfigurationData += "setProgramProperty(propName:" + key + ", propValue:);"; //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| } |
| return new String[] {configurationData, unconfigurationData}; |
| } |
| |
| private boolean shouldPublishProperty(String key) { |
| return !PROPERTIES_TO_SKIP.contains(key); |
| } |
| |
| private boolean shouldPublishJvmArg(String key) { |
| return true; |
| } |
| |
| private boolean shouldPublishProgramArg(String key) { |
| return !PROGRAM_ARGS_TO_SKIP.contains(key); |
| } |
| |
| protected String[] getLauncherConfigStrings(Collection launchingAdvice) { |
| String configurationData = ""; //$NON-NLS-1$ |
| String unconfigurationData = ""; //$NON-NLS-1$ |
| |
| for (Iterator j = launchingAdvice.iterator(); j.hasNext();) { |
| ILaunchingAdvice advice = (ILaunchingAdvice) j.next(); |
| String[] jvmArgs = advice.getVMArguments(); |
| for (int i = 0; i < jvmArgs.length; i++) |
| if (shouldPublishJvmArg(jvmArgs[i])) { |
| configurationData += "addJvmArg(jvmArg:" + jvmArgs[i] + ");"; //$NON-NLS-1$ //$NON-NLS-2$ |
| unconfigurationData += "removeJvmArg(jvmArg:" + jvmArgs[i] + ");"; //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| String[] programArgs = advice.getProgramArguments(); |
| for (int i = 0; i < programArgs.length; i++) |
| if (shouldPublishProgramArg(programArgs[i])) { |
| configurationData += "addProgramArg(programArg:" + programArgs[i] + ");"; //$NON-NLS-1$ //$NON-NLS-2$ |
| unconfigurationData += "removeProgramArg(programArg:" + programArgs[i] + ");"; //$NON-NLS-1$ //$NON-NLS-2$ |
| } else |
| // if we are not publishing then skip over the following arg as it is assumed to be a parameter |
| // to this command line arg. |
| i++; |
| } |
| return new String[] {configurationData, unconfigurationData}; |
| } |
| |
| /** |
| * Publish the CUs related to the given set of bundles. This generally covers the start-level and |
| * and whether or not the bundle is to be started. |
| */ |
| protected void publishBundleCUs(IPublisherInfo info, BundleInfo[] bundles, String configSpec, IPublisherResult result) { |
| if (bundles == null) |
| return; |
| |
| String cuIdPrefix = ""; //$NON-NLS-1$ |
| String filter = null; |
| if (configSpec != null) { |
| cuIdPrefix = createIdString(configSpec); |
| filter = createFilterSpec(configSpec); |
| } |
| |
| for (int i = 0; i < bundles.length; i++) { |
| GeneratorBundleInfo bundle = createGeneratorBundleInfo(info, bundles[i], result); |
| if (bundle == null) |
| continue; |
| |
| // TODO need to factor this out into its own action |
| if (bundle.getSymbolicName().equals(ORG_ECLIPSE_UPDATE_CONFIGURATOR)) { |
| bundle.setStartLevel(BundleInfo.NO_LEVEL); |
| bundle.setMarkedAsStarted(false); |
| bundle.setSpecialConfigCommands("setProgramProperty(propName:org.eclipse.update.reconcile, propValue:false);"); //$NON-NLS-1$ |
| bundle.setSpecialUnconfigCommands("setProgramProperty(propName:org.eclipse.update.reconcile, propValue:);"); //$NON-NLS-1$ |
| } else if (bundle.getStartLevel() == BundleInfo.NO_LEVEL && !bundle.isMarkedAsStarted()) { |
| // this bundle does not require any particular configuration, the plug-in default IU will handle installing it |
| continue; |
| } |
| |
| IInstallableUnit cu = BundlesAction.createBundleConfigurationUnit(bundle.getSymbolicName(), new Version(bundle.getVersion()), false, bundle, flavor + cuIdPrefix, filter); |
| if (cu != null) { |
| // Product Query will run against the repo, make sure these CUs are in before then |
| // TODO review the aggressive addition to the metadata repo. perhaps the query can query the result as well. |
| // IMetadataRepository metadataRepository = info.getMetadataRepository(); |
| // if (metadataRepository != null) { |
| // metadataRepository.addInstallableUnits(new IInstallableUnit[] {cu}); |
| // } |
| result.addIU(cu, IPublisherResult.ROOT); |
| } |
| } |
| } |
| |
| protected GeneratorBundleInfo createGeneratorBundleInfo(IPublisherInfo info, BundleInfo bundleInfo, IPublisherResult result) { |
| if (bundleInfo.getLocation() != null) |
| return new GeneratorBundleInfo(bundleInfo); |
| |
| String name = bundleInfo.getSymbolicName(); |
| |
| //easy case: do we have a matching IU? |
| IInstallableUnit iu = result.getIU(name, null); |
| if (iu != null) { |
| bundleInfo.setVersion(iu.getVersion().toString()); |
| return new GeneratorBundleInfo(bundleInfo); |
| } |
| |
| //harder: try id_version |
| int i = name.indexOf('_'); |
| while (i > -1) { |
| Version version = null; |
| try { |
| version = new Version(name.substring(i)); |
| bundleInfo.setSymbolicName(name.substring(0, i)); |
| bundleInfo.setVersion(version.toString()); |
| return new GeneratorBundleInfo(bundleInfo); |
| } catch (IllegalArgumentException e) { |
| // the '_' found was probably part of the symbolic id |
| i = name.indexOf('_', i); |
| } |
| } |
| |
| //Query the repo |
| Query query = new InstallableUnitQuery(name); |
| Collector collector = new Collector(); |
| Iterator matches = info.getMetadataRepository().query(query, collector, null).iterator(); |
| //pick the newest match |
| IInstallableUnit newest = null; |
| while (matches.hasNext()) { |
| IInstallableUnit candidate = (IInstallableUnit) matches.next(); |
| if (newest == null || (newest.getVersion().compareTo(candidate.getVersion()) < 0)) |
| newest = candidate; |
| } |
| if (newest != null) { |
| bundleInfo.setVersion(newest.getVersion().toString()); |
| return new GeneratorBundleInfo(bundleInfo); |
| } |
| |
| return null; |
| } |
| |
| } |