Bug 531345 - Publish product osgi.ee requirement according to <vm>

Use the EE defined in the <vm> node of product definition to define some
osgi.ee capability requirements in product metadata.
Add tests to make sure JRE is still included while unit isn't directly
referenced by product requirements.

Change-Id: Ic2bd5c30215e9936201c70f7268e5c6068979405
Signed-off-by: Mickael Istria <mistria@redhat.com>
diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/ProductAction.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/ProductAction.java
index bca948e..452db53 100644
--- a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/ProductAction.java
+++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/ProductAction.java
@@ -16,12 +16,14 @@
 import org.eclipse.core.runtime.*;
 import org.eclipse.equinox.internal.p2.publisher.eclipse.*;
 import org.eclipse.equinox.p2.metadata.*;
+import org.eclipse.equinox.p2.metadata.expression.IMatchExpression;
 import org.eclipse.equinox.p2.publisher.*;
 import org.eclipse.equinox.p2.publisher.actions.*;
 import org.eclipse.osgi.util.NLS;
 import org.eclipse.pde.internal.publishing.Activator;
 
 public class ProductAction extends AbstractPublisherAction {
+	protected static final String DEFAULT_EE_CAPABILITY_NAME = "JavaSE"; //$NON-NLS-1$
 	protected String source;
 	protected String id;
 	protected Version version;
@@ -61,9 +63,9 @@
 			actions.add(createApplicationExecutableAction(info.getConfigurations()));
 		// add the actions that just configure things.
 		actions.add(createConfigCUsAction());
-		actions.add(createJREAction());
 		actions.add(createDefaultCUsAction());
 		actions.add(createRootIUAction());
+		actions.add(createJREAction());
 		return actions.toArray(new IPublisherAction[actions.size()]);
 	}
 
@@ -76,9 +78,50 @@
 	}
 
 	protected IPublisherAction createRootIUAction() {
-		return new RootIUAction(id, version, name);
+		return new RootIUAction(id, version, name) {
+			@Override
+			protected Collection<IRequirement> createIURequirements(Collection<? extends IVersionedId> children) {
+				Collection<IRequirement> res = new ArrayList<>(super.createIURequirements(children));
+				Set<String> processedOs = new HashSet<>();
+				Set<String> osWithUndefinedVM = new HashSet<>();
+				for (String configSpec : info.getConfigurations()) {
+					String os = parseConfigSpec(configSpec)[1];
+					if (processedOs.contains(os)) {
+						continue;
+					}
+					processedOs.add(os);
+					String vm = product.getVM(os);
+					if (vm != null) {
+						IMatchExpression<IInstallableUnit> filter = createFilterSpec(createConfigSpec(CONFIG_ANY, os, CONFIG_ANY));
+						String[] segments = vm.split("/"); //$NON-NLS-1$
+						String ee = segments[segments.length - 1];
+						String[] eeSegments = ee.split("-"); //$NON-NLS-1$
+						String eeName = eeSegments[0].replace('%', '/');
+						String eeVersion = eeSegments[1];
+						res.add(MetadataFactory.createRequirement(JREAction.NAMESPACE_OSGI_EE, eeName, VersionRange.create('[' + eeVersion + ',' + eeVersion + ']'), filter, false, false));
+					} else {
+						osWithUndefinedVM.add(os);
+					}
+				}
+				if (osWithUndefinedVM.equals(processedOs)) {
+					res.add(MetadataFactory.createRequirement(JREAction.NAMESPACE_OSGI_EE, DEFAULT_EE_CAPABILITY_NAME, VersionRange.create("0.0.0"), null, false, false)); //$NON-NLS-1$
+				} else {
+					osWithUndefinedVM.forEach(os -> {
+						IMatchExpression<IInstallableUnit> filter = createFilterSpec(createConfigSpec(CONFIG_ANY, os, CONFIG_ANY));
+						res.add(MetadataFactory.createRequirement(JREAction.NAMESPACE_OSGI_EE, DEFAULT_EE_CAPABILITY_NAME, VersionRange.create("0.0.0"), filter, false, false)); //$NON-NLS-1$
+					});
+				}
+				return res;
+			}
+		};
 	}
 
+	/*protected Collection<IRequirement> createJRERequirements() {
+		VersionRange jreRange = VersionRange.create(versionRange);
+		MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, "a.jre.javase", jreRange)));
+		MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, "config.a.jre.javase", jreRange)));
+	}*/
+
 	protected IPublisherAction createConfigCUsAction() {
 		return new ConfigCUsAction(info, flavor, id, version);
 	}
diff --git a/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/actions/JREAction.java b/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/actions/JREAction.java
index bab154b..46f60f9 100644
--- a/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/actions/JREAction.java
+++ b/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/actions/JREAction.java
@@ -56,8 +56,7 @@
 		this.environment = environment;
 	}
 
-	@Override
-	public IStatus perform(IPublisherInfo publisherInfo, IPublisherResult results, IProgressMonitor monitor) {
+	@Override public IStatus perform(IPublisherInfo publisherInfo, IPublisherResult results, IProgressMonitor monitor) {
 		String problemMessage = NLS.bind(Messages.message_problemsWhilePublishingEE, jreLocation != null ? jreLocation : environment);
 		resultStatus = new MultiStatus(Activator.ID, 0, problemMessage, null);
 
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTest.java
index f7cd53d..b44036e 100644
--- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTest.java
+++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTest.java
@@ -12,20 +12,38 @@
 package org.eclipse.equinox.p2.tests.publisher.actions;
 
 import static org.easymock.EasyMock.createNiceMock;
-import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.*;
-import static org.hamcrest.CoreMatchers.*;
+import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.errorStatus;
+import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.okStatus;
+import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.statusWithMessageWhich;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
 import static org.junit.Assert.assertThat;
 
 import java.io.File;
 import java.io.IOException;
 import java.net.URI;
-import java.util.*;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.NullProgressMonitor;
-import org.eclipse.equinox.internal.p2.metadata.*;
+import org.eclipse.equinox.internal.p2.metadata.IRequiredCapability;
+import org.eclipse.equinox.internal.p2.metadata.InstallableUnit;
+import org.eclipse.equinox.internal.p2.metadata.RequiredCapability;
 import org.eclipse.equinox.internal.p2.publisher.eclipse.ProductFile;
-import org.eclipse.equinox.p2.metadata.*;
-import org.eclipse.equinox.p2.publisher.*;
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.metadata.IInstallableUnitFragment;
+import org.eclipse.equinox.p2.metadata.IRequirement;
+import org.eclipse.equinox.p2.metadata.ITouchpointData;
+import org.eclipse.equinox.p2.metadata.IUpdateDescriptor;
+import org.eclipse.equinox.p2.metadata.Version;
+import org.eclipse.equinox.p2.publisher.AbstractPublisherAction;
+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.actions.JREAction;
 import org.eclipse.equinox.p2.publisher.actions.QueryableFilterAdvice;
 import org.eclipse.equinox.p2.publisher.eclipse.IConfigAdvice;
 import org.eclipse.equinox.p2.publisher.eclipse.ProductAction;
@@ -49,20 +67,17 @@
 	String source = "";
 	protected TestArtifactRepository artifactRepository = new TestArtifactRepository(getAgent());
 
-	@Override
-	protected IPublisherInfo createPublisherInfoMock() {
+	@Override protected IPublisherInfo createPublisherInfoMock() {
 		//override to create a nice mock, because we don't care about other method calls.
 		return createNiceMock(IPublisherInfo.class);
 	}
 
-	@Override
-	public void setUp() throws Exception {
+	@Override public void setUp() throws Exception {
 		setupPublisherInfo();
 		setupPublisherResult();
 	}
 
-	@Override
-	public void setupPublisherInfo() {
+	@Override public void setupPublisherInfo() {
 		PublisherInfo publisherInfoImpl = new PublisherInfo();
 		publisherInfoImpl.setArtifactRepository(artifactRepository);
 		publisherInfoImpl.setArtifactOptions(IPublisherInfo.A_PUBLISH);
@@ -339,6 +354,44 @@
 		// TODO the message should have a code identifying it
 	}
 
+	public void testJREIncluded() throws Exception {
+		ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "brandedProduct/branded.product").toString());
+		addContextIU("org.eclipse.platform.feature.group", "1.2.3");
+
+		performProductAction(productFile);
+		Collection<IInstallableUnit> ius = publisherResult.getIUs("branded.product", IPublisherResult.NON_ROOT);
+		assertEquals(1, ius.size());
+		assertEquals("Missing a.jre.javase", 1, publisherResult.getIUs("a.jre.javase", IPublisherResult.ROOT).size());
+		assertEquals("Missing config.a.jre.javase", 1, publisherResult.getIUs("config.a.jre.javase", IPublisherResult.ROOT).size());
+	}
+
+	public void testRequiredEEAsSpecified() throws Exception {
+		ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "productFileActionTest.product").toString());
+		addContextIU("org.eclipse.core.commands", "5.0.0");
+
+		performProductAction(productFile);
+		Collection<IInstallableUnit> ius = publisherResult.getIUs("SampleProduct", IPublisherResult.NON_ROOT);
+		assertEquals(1, ius.size());
+		IInstallableUnit productIU = ius.iterator().next();
+		IInstallableUnit aJre = publisherResult.getIUs("a.jre.javase", IPublisherResult.ROOT).iterator().next();
+		boolean found = false;
+		for (IRequirement req : productIU.getRequirements()) {
+			if (req instanceof RequiredCapability) {
+				RequiredCapability required = (RequiredCapability) req;
+				if (JREAction.NAMESPACE_OSGI_EE.equals(required.getNamespace())) {
+					found = true;
+					assertEquals("OSGi/Minimum", required.getName());
+					assertEquals("1.0.0", required.getRange().getMinimum().toString());
+					assertEquals("1.0.0", required.getRange().getMaximum().toString());
+					assertTrue(req.isMatch(aJre));
+				} else if (required.getName().contains("a.jre")) {
+					fail("instead of unit requirement, should use a osgi.ee requirement");
+				}
+			}
+		}
+		assertTrue(found);
+	}
+
 	private void performProductAction(ProductFile productFile) {
 		IStatus status = performProductActionAndReturnStatus(productFile);
 		assertThat(status, is(okStatus()));
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductContentTypeTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductContentTypeTest.java
index d85619d..1826370 100644
--- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductContentTypeTest.java
+++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductContentTypeTest.java
@@ -40,12 +40,12 @@
 	private static final String TEST_DATA_FOLDER = "ProductContentTypeTest";
 	private static final String flavor = "tooling";
 
-	private List<IInstallableUnit> cusList;
+	private List<IInstallableUnit> requiredUnits;
+	private List<IInstallableUnit> additionalPublishedUnits;
 	private IInstallableUnit featureIU = createIU("TestFeature.feature.group");
 	private IInstallableUnit bundleIU = createIU("TestBundle");
 
-	@Override
-	public void setUp() throws Exception {
+	@Override public void setUp() throws Exception {
 		setupPublisherResult();
 		initCUsList();
 	}
@@ -53,28 +53,28 @@
 	/**
 	 * Publish product with attribute <code>type="bundles"</code>.
 	 * Check that the generated product IU
-	 * requires the default CU list + CU for the product + bundle IU.
+	 * requires the default CU list + CU for the product + bundle IU  + EE requirement.
 	 */
 	public void test01PublishWithBundle() throws Exception {
-		testTemplate("ProductWithBundle.product", "1", cusList.size() + 2, bundleIU);
+		testTemplate("ProductWithBundle.product", "1", requiredUnits.size() + 3, bundleIU);
 	}
 
 	/**
 	 * Publish product with attribute <code>type="features"</code>.
 	 * Check that the generated product IU
-	 * requires the default CU list + CU for the product + feature IU.
+	 * requires the default CU list + CU for the product + feature IU  + EE requirement.
 	 */
 	public void test02PublishWithFeature() throws Exception {
-		testTemplate("ProductWithFeature.product", "1", cusList.size() + 2, featureIU);
+		testTemplate("ProductWithFeature.product", "1", requiredUnits.size() + 3, featureIU);
 	}
 
 	/**
 	 * Publish product with attribute <code>type="mixed"</code>.
 	 * Check that the generated product IU
-	 * requires the default CU list + CU for the product + bundle IU + feature IU.
+	 * requires the default CU list + CU for the product + bundle IU + feature IU  + EE requirement.
 	 */
 	public void test03PublishWithMixedContent() throws Exception {
-		testTemplate("MixedContentProduct.product", "1", cusList.size() + 3, bundleIU, featureIU);
+		testTemplate("MixedContentProduct.product", "1", requiredUnits.size() + 4, bundleIU, featureIU);
 	}
 
 	/**
@@ -106,33 +106,34 @@
 	/**
 	 * Publish product with attributes <code>type="bundles"</code> and <code>useFeatures="true"</code>.
 	 * Check that the generated product IU
-	 * requires the default CU list + CU for the product + bundle IU.
+	 * requires the default CU list + CU for the product + bundle IU + EE requirement.
 	 */
 	public void test06OverrideUseFeaturesAttr() throws Exception {
-		testTemplate("OverrideUseFeaturesAttr.product", "1", cusList.size() + 2, bundleIU);
+		testTemplate("OverrideUseFeaturesAttr.product", "1", requiredUnits.size() + 3, bundleIU);
 	}
 
 	/**
 	 * Publish product with attributes <code>type="mixed"</code> and <code>useFeatures="true"</code>.
 	 * Check that the generated product IU
-	 * requires the default CU list + CU for the product + bundle IU + feature IU.
+	 * requires the default CU list + CU for the product + bundle IU + feature IU + EE requirement.
 	 */
 	public void test07OverrideUseFeaturesAttr2() throws Exception {
-		testTemplate("OverrideUseFeaturesAttr2.product", "1", cusList.size() + 3, bundleIU, featureIU);
+		testTemplate("OverrideUseFeaturesAttr2.product", "1", requiredUnits.size() + 4, bundleIU, featureIU);
 	}
 
 	private void initCUsList() {
-		cusList = new ArrayList<>();
-		cusList.add(createIU(flavor + ".source.default"));
-		cusList.add(createIU(flavor + ".osgi.bundle.default"));
-		cusList.add(createIU(flavor + ".org.eclipse.update.feature.default"));
-		cusList.add(createIU("a.jre.javase", Version.create("9.0")));
-		cusList.add(createIU("config.a.jre.javase", Version.create("9.0")));
+		requiredUnits = new ArrayList<>(3);
+		requiredUnits.add(createIU(flavor + ".source.default"));
+		requiredUnits.add(createIU(flavor + ".osgi.bundle.default"));
+		requiredUnits.add(createIU(flavor + ".org.eclipse.update.feature.default"));
+		additionalPublishedUnits = new ArrayList<>(2);
+		additionalPublishedUnits.add(createIU("a.jre.javase", Version.create("9.0")));
+		additionalPublishedUnits.add(createIU("config.a.jre.javase", Version.create("9.0")));
 	}
 
 	private void testTemplate(String productFileName, String productVersion, int expectedRequirementsSize, IInstallableUnit... requiredInstallableUnits) throws Exception {
-		for (int i = 0; i < requiredInstallableUnits.length; i++) {
-			publisherResult.addIU(requiredInstallableUnits[i], IPublisherResult.NON_ROOT);
+		for (IInstallableUnit requiredUnit : requiredInstallableUnits) {
+			publisherResult.addIU(requiredUnit, IPublisherResult.NON_ROOT);
 		}
 
 		File productFileLocation = TestData.getFile(TEST_DATA_FOLDER, productFileName);
@@ -156,8 +157,7 @@
 	}
 
 	private void verifyRequirementsForConfigurationUnits(Collection<IRequirement> requirements, String productName, String productVersion) {
-
-		List<IInstallableUnit> cusListCopy = new ArrayList<>(cusList);
+		List<IInstallableUnit> cusListCopy = new ArrayList<>(requiredUnits);
 		cusListCopy.add(createIU(flavor + productName + ".configuration", Version.create(productVersion)));
 		for (Iterator<IInstallableUnit> cusIterator = cusListCopy.iterator(); cusIterator.hasNext();) {
 			IInstallableUnit cu = cusIterator.next();
@@ -165,19 +165,15 @@
 				cusIterator.remove();
 			}
 		}
-
 		assertTrue("Some of the default configuration units are not included in the product - " + cusListCopy, cusListCopy.isEmpty());
 	}
 
 	private boolean verifyRequirement(Collection<IRequirement> requirements, IInstallableUnit iu) {
-
-		for (Iterator<IRequirement> iterator = requirements.iterator(); iterator.hasNext();) {
-			IRequirement requirement = iterator.next();
+		for (IRequirement requirement : requirements) {
 			if (requirement.isMatch(iu)) {
 				return true;
 			}
 		}
-
 		return false;
 	}
 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/ProductActionTest/productFileActionTest.product b/bundles/org.eclipse.equinox.p2.tests/testData/ProductActionTest/productFileActionTest.product
index 5890a0c..e7ffcb3 100644
--- a/bundles/org.eclipse.equinox.p2.tests/testData/ProductActionTest/productFileActionTest.product
+++ b/bundles/org.eclipse.equinox.p2.tests/testData/ProductActionTest/productFileActionTest.product
@@ -1,20 +1,21 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?pde version="3.5"?>
 
-<product name="SampleProduct" version="1.0.0" useFeatures="false" includeLaunchers="true">
+<product name="SampleProduct" uid="SampleProduct" version="1.0.0" useFeatures="false" includeLaunchers="true">
 
    <configIni use="">
    </configIni>
 
    <launcherArgs>
-      <programArgs>-product &quot;com,ma&quot;</programArgs>
-      <vmArgs>-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8272</vmArgs>
+      <programArgs>-product &quot;com,ma&quot;
+      </programArgs>
+      <vmArgs>-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8272
+      </vmArgs>
    </launcherArgs>
 
    <windowImages/>
 
    <launcher>
-      <solaris/>
       <win useIco="false">
          <bmp
             winSmallLow="icon.bmp"/>
@@ -22,6 +23,8 @@
    </launcher>
 
    <vm>
+      <linux include="false">org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-9</linux>
+      <windows include="false">org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/OSGi%Minimum-1.0</windows>
    </vm>
 
    <plugins>