diff options
author | Mickael Istria | 2012-06-08 12:57:12 +0000 |
---|---|---|
committer | Pascal Rapicault | 2012-07-10 11:50:42 +0000 |
commit | ffeddcac921157a66d827325325ce5fbde418ff7 (patch) | |
tree | 4be030299612b0b995aa2cff9aaa5137ccf093f2 | |
parent | fd760bcb9b71263cce6970964de85a434a5a4d50 (diff) | |
download | rt.equinox.p2-ffeddcac921157a66d827325325ce5fbde418ff7.tar.gz rt.equinox.p2-ffeddcac921157a66d827325325ce5fbde418ff7.tar.xz rt.equinox.p2-ffeddcac921157a66d827325325ce5fbde418ff7.zip |
378338 : A "bundle" element in category.xml/site.xml
Signed-off-by: Pascal Rapicault <pascal@rapicault.net>
17 files changed, 1089 insertions, 75 deletions
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/updatesite/CategoryXMLActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/updatesite/CategoryXMLActionTest.java index 79b716857..59b6a0afd 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/updatesite/CategoryXMLActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/updatesite/CategoryXMLActionTest.java @@ -11,10 +11,13 @@ package org.eclipse.equinox.p2.tests.updatesite; import java.io.File; import java.net.URI; +import java.util.Set; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.equinox.internal.p2.updatesite.CategoryXMLAction; import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.equinox.p2.publisher.*; +import org.eclipse.equinox.p2.publisher.actions.MergeResultsAction; +import org.eclipse.equinox.p2.publisher.eclipse.BundlesAction; import org.eclipse.equinox.p2.publisher.eclipse.FeaturesAction; import org.eclipse.equinox.p2.query.*; import org.eclipse.equinox.p2.tests.*; @@ -92,4 +95,54 @@ public class CategoryXMLActionTest extends AbstractProvisioningTest { IQueryResult<IInstallableUnit> categoryMembers = actionResult.query(memberQuery, new NullProgressMonitor()); assertEquals("2.0", 2, categoryMembers.toUnmodifiableSet().size()); } + + public void testBundlesInCategory() throws Exception { + PublisherInfo info = new PublisherInfo(); + + info.setMetadataRepository(metadataRepository); + siteLocation = TestData.getFile("updatesite", "CategoryXMLActionTest/categoryWithBundle.xml").toURI(); + FeaturesAction featuresAction = new FeaturesAction(new File[] {TestData.getFile("updatesite", "CategoryXMLActionTest")}); + BundlesAction bundlesAction = new BundlesAction(new File[] {TestData.getFile("updatesite", "CategoryXMLActionTest")}); + MergeResultsAction publishAction = new MergeResultsAction(new IPublisherAction[] {featuresAction, bundlesAction}, IPublisherResult.MERGE_ALL_NON_ROOT); + publishAction.perform(info, actionResult, new NullProgressMonitor()); + + CategoryXMLAction action = new CategoryXMLAction(siteLocation, null); + action.perform(info, actionResult, getMonitor()); + + IQueryResult result = actionResult.query(QueryUtil.createIUCategoryQuery(), new NullProgressMonitor()); + assertEquals("1.0", 1, queryResultSize(result)); + IInstallableUnit iu = (IInstallableUnit) result.iterator().next(); + assertEquals("1.1", "Test Category Label", iu.getProperty(IInstallableUnit.PROP_NAME)); + + IQuery<IInstallableUnit> memberQuery = QueryUtil.createIUCategoryMemberQuery(iu); + IQueryResult<IInstallableUnit> categoryMembers = actionResult.query(memberQuery, new NullProgressMonitor()); + Set<IInstallableUnit> categoryMembersSet = categoryMembers.toUnmodifiableSet(); + assertEquals("2.0", 1, categoryMembersSet.size()); + assertEquals("2.1", "test.bundle", categoryMembersSet.iterator().next().getId()); + } + + public void testUncategorizedBundlesInCategory() throws Exception { + PublisherInfo info = new PublisherInfo(); + + info.setMetadataRepository(metadataRepository); + siteLocation = TestData.getFile("updatesite", "CategoryXMLActionTest/categoryUncategorizedBundle.xml").toURI(); + FeaturesAction featuresAction = new FeaturesAction(new File[] {TestData.getFile("updatesite", "CategoryXMLActionTest")}); + BundlesAction bundlesAction = new BundlesAction(new File[] {TestData.getFile("updatesite", "CategoryXMLActionTest")}); + MergeResultsAction publishAction = new MergeResultsAction(new IPublisherAction[] {featuresAction, bundlesAction}, IPublisherResult.MERGE_ALL_NON_ROOT); + publishAction.perform(info, actionResult, new NullProgressMonitor()); + + CategoryXMLAction action = new CategoryXMLAction(siteLocation, null); + action.perform(info, actionResult, getMonitor()); + + IQueryResult result = actionResult.query(QueryUtil.createIUCategoryQuery(), new NullProgressMonitor()); + assertEquals("1.0", 1, queryResultSize(result)); + IInstallableUnit iu = (IInstallableUnit) result.iterator().next(); + assertEquals("1.1", "Uncategorized", iu.getProperty(IInstallableUnit.PROP_NAME)); + + IQuery<IInstallableUnit> memberQuery = QueryUtil.createIUCategoryMemberQuery(iu); + IQueryResult<IInstallableUnit> categoryMembers = actionResult.query(memberQuery, new NullProgressMonitor()); + Set<IInstallableUnit> categoryMembersSet = categoryMembers.toUnmodifiableSet(); + assertEquals("2.0", 1, categoryMembersSet.size()); + assertEquals("2.1", "test.bundle", categoryMembersSet.iterator().next().getId()); + } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/updatesite/LocalUpdatesiteTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/updatesite/LocalUpdatesiteTest.java index 690319889..17cc26fe3 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/updatesite/LocalUpdatesiteTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/updatesite/LocalUpdatesiteTest.java @@ -11,13 +11,12 @@ package org.eclipse.equinox.p2.tests.updatesite; import java.io.File; import java.io.IOException; -import java.util.Iterator; +import java.util.*; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.equinox.internal.p2.updatesite.UpdateSitePublisherApplication; import org.eclipse.equinox.p2.core.ProvisionException; import org.eclipse.equinox.p2.metadata.IInstallableUnit; -import org.eclipse.equinox.p2.query.IQueryResult; -import org.eclipse.equinox.p2.query.QueryUtil; +import org.eclipse.equinox.p2.query.*; import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository; import org.eclipse.equinox.p2.tests.AbstractProvisioningTest; import org.eclipse.equinox.p2.tests.TestData; @@ -57,4 +56,28 @@ public class LocalUpdatesiteTest extends AbstractProvisioningTest { assertEquals("3.0", "Test Category Label", unit.getProperty(IInstallableUnit.PROP_NAME)); } } + + public void testBundleInCategory() throws Exception { + File siteSource = TestData.getFile("updatesite", "SiteXMLActionTest"); + UpdateSitePublisherApplication application = new UpdateSitePublisherApplication(); + try { + application.run(new String[] {"-metadataRepository", repoLocation.toURI().toString(), "-source", siteSource.toString(), "-categoryQualifier", "fooQualifier"}); + } catch (Exception e) { + fail("0.99"); + } + IMetadataRepository repository = getMetadataRepositoryManager().loadRepository(repoLocation.toURI(), new NullProgressMonitor()); + + IQueryResult results = repository.query(QueryUtil.createIUCategoryQuery(), new NullProgressMonitor()); + Iterator iter = results.iterator(); + + IInstallableUnit unit = (IInstallableUnit) iter.next(); + IQuery<IInstallableUnit> memberQuery = QueryUtil.createIUCategoryMemberQuery(unit); + IQueryResult<IInstallableUnit> categoryMembers = repository.query(memberQuery, new NullProgressMonitor()); + Set<String> membersId = new HashSet<String>(); + for (IInstallableUnit iu : categoryMembers.toUnmodifiableSet()) { + membersId.add(iu.getId()); + } + assertEquals("1.0", 2, membersId.size()); + assertTrue("2.0", membersId.contains("test.bundle")); + } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/updatesite/SiteXMLActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/updatesite/SiteXMLActionTest.java index 566b44fbd..f196d0d76 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/updatesite/SiteXMLActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/updatesite/SiteXMLActionTest.java @@ -13,17 +13,17 @@ package org.eclipse.equinox.p2.tests.updatesite; import java.io.File; import java.io.PrintStream; import java.net.URI; -import java.util.Collection; -import java.util.Iterator; +import java.util.*; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.URIUtil; import org.eclipse.equinox.internal.p2.updatesite.SiteXMLAction; import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.equinox.p2.metadata.IProvidedCapability; import org.eclipse.equinox.p2.publisher.*; +import org.eclipse.equinox.p2.publisher.actions.MergeResultsAction; +import org.eclipse.equinox.p2.publisher.eclipse.BundlesAction; import org.eclipse.equinox.p2.publisher.eclipse.FeaturesAction; -import org.eclipse.equinox.p2.query.IQueryResult; -import org.eclipse.equinox.p2.query.QueryUtil; +import org.eclipse.equinox.p2.query.*; import org.eclipse.equinox.p2.repository.IRepository; import org.eclipse.equinox.p2.repository.IRepositoryReference; import org.eclipse.equinox.p2.tests.*; @@ -43,9 +43,13 @@ public class SiteXMLActionTest extends AbstractProvisioningTest { PublisherInfo info = new PublisherInfo(); metadataRepository = new TestMetadataRepository(getAgent(), new IInstallableUnit[0]); info.setMetadataRepository(metadataRepository); - siteLocation = TestData.getFile("updatesite", "SiteXMLActionTest/site.xml").toURI(); + File siteLocationFile = TestData.getFile("updatesite", "SiteXMLActionTest/site.xml"); + siteLocation = siteLocationFile.toURI(); + FeaturesAction featuresAction = new FeaturesAction(new File[] {TestData.getFile("updatesite", "SiteXMLActionTest")}); - featuresAction.perform(info, actionResult, new NullProgressMonitor()); + BundlesAction bundlesAction = new BundlesAction(new File[] {TestData.getFile("updatesite", "SiteXMLActionTest")}); + IPublisherAction publishAction = new MergeResultsAction(new IPublisherAction[] {bundlesAction, featuresAction}, IPublisherResult.MERGE_ALL_NON_ROOT); + publishAction.perform(info, actionResult, new NullProgressMonitor()); SiteXMLAction action = new SiteXMLAction(siteLocation, null); PrintStream out = System.out; @@ -99,4 +103,20 @@ public class SiteXMLActionTest extends AbstractProvisioningTest { String mirrorsURL = metadataRepository.getProperties().get(IRepository.PROP_MIRRORS_URL); assertEquals("1.0", "http://www.eclipse.org/downloads/download.php?file=/eclipse/updates/3.4&format=xml", mirrorsURL); } + + public void testBundleInCategory() { + IQueryResult results = actionResult.query(QueryUtil.createIUCategoryQuery(), new NullProgressMonitor()); + Iterator iter = results.iterator(); + + IInstallableUnit unit = (IInstallableUnit) iter.next(); + IQuery<IInstallableUnit> memberQuery = QueryUtil.createIUCategoryMemberQuery(unit); + IQueryResult<IInstallableUnit> categoryMembers = actionResult.query(memberQuery, new NullProgressMonitor()); + Set<String> membersId = new HashSet<String>(); + for (IInstallableUnit iu : categoryMembers.toUnmodifiableSet()) { + membersId.add(iu.getId()); + } + assertEquals("1.0", 2, membersId.size()); + assertTrue("2.0", membersId.contains("test.bundle")); + + } } diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/categoryUncategorizedBundle.xml b/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/categoryUncategorizedBundle.xml new file mode 100644 index 000000000..39db801ba --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/categoryUncategorizedBundle.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<site associateSitesURL="associateSites.xml" mirrorsURL="http://www.eclipse.org/downloads/download.php?file=/eclipse/updates/3.4&format=xml"> + <bundle url="plugins/test.bundle_1.0.0.jar" id="test.bundle" version="1.0.0"> + </bundle> +</site> diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/categoryWithBundle.xml b/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/categoryWithBundle.xml new file mode 100644 index 000000000..efec57d86 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/categoryWithBundle.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<site associateSitesURL="associateSites.xml" mirrorsURL="http://www.eclipse.org/downloads/download.php?file=/eclipse/updates/3.4&format=xml"> + <bundle url="plugins/test.bundle_1.0.0.jar" id="test.bundle" version="1.0.0"> + <category name="Test Category"/> + </bundle> + <category-def label="Test Category Label" name="Test Category"> + <description>Test Category Description</description> + </category-def> +</site> diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/plugins/test.bundle_1.0.0.jar b/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/plugins/test.bundle_1.0.0.jar Binary files differnew file mode 100644 index 000000000..00dd829d8 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/plugins/test.bundle_1.0.0.jar diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/plugins/test.fragment_1.0.0.jar b/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/plugins/test.fragment_1.0.0.jar Binary files differnew file mode 100644 index 000000000..d033e2eda --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/plugins/test.fragment_1.0.0.jar diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/SiteXMLActionTest/site.xml b/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/SiteXMLActionTest/site.xml index dc1286381..3e0b2f175 100644 --- a/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/SiteXMLActionTest/site.xml +++ b/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/SiteXMLActionTest/site.xml @@ -3,6 +3,9 @@ <feature url="features/test.feature_1.0.0.jar" id="test.feature" version="1.0.0"> <category name="Test Category"/> </feature> + <bundle url="plugins/test.bundle_1.0.0.jar" id="test.bundle" version="1.0.0"> + <category name="Test Category"/> + </bundle> <category-def label="Test Category Label" name="Test Category"> <description>Test Category Description</description> </category-def> diff --git a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/CategoryParser.java b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/CategoryParser.java index 9bd569a7e..77fb88c32 100644 --- a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/CategoryParser.java +++ b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/CategoryParser.java @@ -36,6 +36,7 @@ public class CategoryParser extends DefaultHandler { private static final String CATEGORY_DEF = "category-def"; //$NON-NLS-1$ private static final String DESCRIPTION = "description"; //$NON-NLS-1$ private static final String FEATURE = "feature"; //$NON-NLS-1$ + private static final String BUNDLE = "bundle"; //$NON-NLS-1$ private static final String SITE = "site"; //$NON-NLS-1$ private static final String IU = "iu"; //$NON-NLS-1$ private static final String QUERY = "query"; //$NON-NLS-1$ @@ -48,6 +49,7 @@ public class CategoryParser extends DefaultHandler { private static final int STATE_DESCRIPTION_CATEGORY_DEF = 7; private static final int STATE_DESCRIPTION_SITE = 6; private static final int STATE_FEATURE = 2; + private static final int STATE_BUNDLE = 12; private static final int STATE_IGNORED_ELEMENT = -1; private static final int STATE_INITIAL = 0; private static final int STATE_IU = 8; @@ -187,6 +189,11 @@ public class CategoryParser extends DefaultHandler { objectStack.pop(); break; + case STATE_BUNDLE : + stateStack.pop(); + objectStack.pop(); + break; + case STATE_IU : stateStack.pop(); SiteIU completeIU = (SiteIU) objectStack.pop(); @@ -350,6 +357,9 @@ public class CategoryParser extends DefaultHandler { case STATE_FEATURE : return "Feature"; //$NON-NLS-1$ + case STATE_BUNDLE : + return "Bundle"; //$NON-NLS-1$ + case STATE_IU : return "IU"; //$NON-NLS-1$ @@ -403,6 +413,14 @@ public class CategoryParser extends DefaultHandler { internalErrorUnknownTag(NLS.bind(Messages.DefaultSiteParser_UnknownElement, (new String[] {elementName, getState(currentState())}))); } + private void handleBundleState(String elementName, Attributes attributes) { + if (elementName.equals(CATEGORY)) { + stateStack.push(new Integer(STATE_CATEGORY)); + processCategory(attributes); + } else + internalErrorUnknownTag(NLS.bind(Messages.DefaultSiteParser_UnknownElement, (new String[] {elementName, getState(currentState())}))); + } + private void handleInitialState(String elementName, Attributes attributes) throws SAXException { if (elementName.equals(SITE)) { stateStack.push(new Integer(STATE_SITE)); @@ -422,6 +440,9 @@ public class CategoryParser extends DefaultHandler { } else if (elementName.equals(FEATURE)) { stateStack.push(new Integer(STATE_FEATURE)); processFeature(attributes); + } else if (elementName.equals(BUNDLE)) { + stateStack.push(new Integer(STATE_BUNDLE)); + processBundle(attributes); } else if (elementName.equals(IU)) { stateStack.push(new Integer(STATE_IU)); processIU(attributes); @@ -551,10 +572,13 @@ public class CategoryParser extends DefaultHandler { String category = attributes.getValue("name"); //$NON-NLS-1$ Object obj = objectStack.peek(); // TODO could create common class/interface for adding categories - if (obj instanceof SiteFeature) + if (obj instanceof SiteFeature) { ((SiteFeature) obj).addCategoryName(category); - else if (obj instanceof SiteIU) + } else if (obj instanceof SiteBundle) { + ((SiteBundle) obj).addCategoryName(category); + } else if (obj instanceof SiteIU) { ((SiteIU) obj).addCategoryName(category); + } if (Tracing.DEBUG_GENERATOR_PARSING) debug("End processing Category: name:" + category); //$NON-NLS-1$ @@ -609,6 +633,35 @@ public class CategoryParser extends DefaultHandler { } /* + * process bundle info + */ + private void processBundle(Attributes attributes) { + SiteBundle bundle = new SiteBundle(); + + // identifier and version + String id = attributes.getValue("id"); //$NON-NLS-1$ + String ver = attributes.getValue("version"); //$NON-NLS-1$ + + boolean noId = (id == null || id.trim().equals("")); //$NON-NLS-1$ + + // We need to have id and version, or the url, or both. + if (noId) + internalError(NLS.bind(Messages.DefaultSiteParser_Missing, (new String[] {"url", getState(currentState())}))); //$NON-NLS-1$ + + bundle.setBundleIdentifier(id); + bundle.setBundleVersion(ver); + + SiteModel site = (SiteModel) objectStack.peek(); + site.addBundle(bundle); + bundle.setSiteModel(site); + + objectStack.push(bundle); + + if (Tracing.DEBUG_GENERATOR_PARSING) + debug("End Processing Bundle Tag: id:" + id + " version:" + ver); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /* * process IU info */ private void processIU(Attributes attributes) { @@ -714,6 +767,10 @@ public class CategoryParser extends DefaultHandler { handleFeatureState(localName, attributes); break; + case STATE_BUNDLE : + handleBundleState(localName, attributes); + break; + case STATE_IU : handleIUState(localName, attributes); break; diff --git a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/DefaultSiteParser.java b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/DefaultSiteParser.java index 0338fdcda..e5c14ef48 100644 --- a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/DefaultSiteParser.java +++ b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/DefaultSiteParser.java @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Red Hat Inc. - 378338: Support for "bundle" element *******************************************************************************/ package org.eclipse.equinox.internal.p2.updatesite; @@ -38,7 +39,9 @@ public class DefaultSiteParser extends DefaultHandler { private static final String DEFAULT_INFO_URL = "index.html"; //$NON-NLS-1$ private static final String DESCRIPTION = "description"; //$NON-NLS-1$ private static final String FEATURE = "feature"; //$NON-NLS-1$ + private static final String BUNDLE = "bundle"; //$NON-NLS-1$ private static final String FEATURES = "features/"; //$NON-NLS-1$ + private static final String PLUGINS = "plugins/"; //$NON-NLS-1$ private final static SAXParserFactory parserFactory = SAXParserFactory.newInstance(); private static final String PLUGIN_ID = Activator.ID; private static final String SITE = "site"; //$NON-NLS-1$ @@ -49,6 +52,7 @@ public class DefaultSiteParser extends DefaultHandler { private static final int STATE_DESCRIPTION_CATEGORY_DEF = 7; private static final int STATE_DESCRIPTION_SITE = 6; private static final int STATE_FEATURE = 2; + private static final int STATE_BUNDLE = 8; private static final int STATE_IGNORED_ELEMENT = -1; private static final int STATE_INITIAL = 0; private static final int STATE_SITE = 1; @@ -198,6 +202,11 @@ public class DefaultSiteParser extends DefaultHandler { objectStack.pop(); break; + case STATE_BUNDLE : + stateStack.pop(); + objectStack.pop(); + break; + case STATE_CATEGORY_DEF : stateStack.pop(); if (objectStack.peek() instanceof String) { @@ -331,6 +340,9 @@ public class DefaultSiteParser extends DefaultHandler { case STATE_FEATURE : return "Feature"; //$NON-NLS-1$ + case STATE_BUNDLE : + return "Bundle"; //$NON-NLS-1$ + case STATE_ARCHIVE : return "Archive"; //$NON-NLS-1$ @@ -365,6 +377,9 @@ public class DefaultSiteParser extends DefaultHandler { if (elementName.equals(FEATURE)) { stateStack.push(new Integer(STATE_FEATURE)); processFeature(attributes); + } else if (elementName.equals(BUNDLE)) { + stateStack.push(new Integer(STATE_BUNDLE)); + processBundle(attributes); } else if (elementName.equals(ARCHIVE)) { stateStack.push(new Integer(STATE_ARCHIVE)); processArchive(attributes); @@ -385,6 +400,9 @@ public class DefaultSiteParser extends DefaultHandler { } else if (elementName.equals(FEATURE)) { stateStack.push(new Integer(STATE_FEATURE)); processFeature(attributes); + } else if (elementName.equals(BUNDLE)) { + stateStack.push(new Integer(STATE_BUNDLE)); + processBundle(attributes); } else if (elementName.equals(ARCHIVE)) { stateStack.push(new Integer(STATE_ARCHIVE)); processArchive(attributes); @@ -418,6 +436,23 @@ public class DefaultSiteParser extends DefaultHandler { internalErrorUnknownTag(NLS.bind(Messages.DefaultSiteParser_UnknownElement, (new String[] {elementName, getState(currentState)}))); } + private void handleBundleState(String elementName, Attributes attributes) { + if (elementName.equals(DESCRIPTION)) { + stateStack.push(new Integer(STATE_DESCRIPTION_SITE)); + processInfo(attributes); + } else if (elementName.equals(ARCHIVE)) { + stateStack.push(new Integer(STATE_ARCHIVE)); + processArchive(attributes); + } else if (elementName.equals(CATEGORY_DEF)) { + stateStack.push(new Integer(STATE_CATEGORY_DEF)); + processCategoryDef(attributes); + } else if (elementName.equals(CATEGORY)) { + stateStack.push(new Integer(STATE_CATEGORY)); + processCategory(attributes); + } else + internalErrorUnknownTag(NLS.bind(Messages.DefaultSiteParser_UnknownElement, (new String[] {elementName, getState(currentState)}))); + } + private void handleInitialState(String elementName, Attributes attributes) throws SAXException { if (elementName.equals(SITE)) { stateStack.push(new Integer(STATE_SITE)); @@ -437,6 +472,9 @@ public class DefaultSiteParser extends DefaultHandler { } else if (elementName.equals(FEATURE)) { stateStack.push(new Integer(STATE_FEATURE)); processFeature(attributes); + } else if (elementName.equals(BUNDLE)) { + stateStack.push(new Integer(STATE_BUNDLE)); + processBundle(attributes); } else if (elementName.equals(ARCHIVE)) { stateStack.push(new Integer(STATE_ARCHIVE)); processArchive(attributes); @@ -547,8 +585,14 @@ public class DefaultSiteParser extends DefaultHandler { */ private void processCategory(Attributes attributes) { String category = attributes.getValue("name"); //$NON-NLS-1$ - SiteFeature feature = (SiteFeature) objectStack.peek(); - feature.addCategoryName(category); + Object item = objectStack.peek(); + if (item instanceof SiteFeature) { + SiteFeature feature = (SiteFeature) item; + feature.addCategoryName(category); + } else if (item instanceof SiteBundle) { + SiteBundle bundle = (SiteBundle) item; + bundle.addCategoryName(category); + } if (Tracing.DEBUG_GENERATOR_PARSING) debug("End processing Category: name:" + category); //$NON-NLS-1$ @@ -653,6 +697,85 @@ public class DefaultSiteParser extends DefaultHandler { } /* + * process feature info + */ + private void processBundle(Attributes attributes) { + SiteBundle bundle = new SiteBundle(); + + // feature location on the site + String urlInfo = attributes.getValue("url"); //$NON-NLS-1$ + // identifier and version + String id = attributes.getValue("id"); //$NON-NLS-1$ + String ver = attributes.getValue("version"); //$NON-NLS-1$ + + boolean noURL = (urlInfo == null || urlInfo.trim().equals("")); //$NON-NLS-1$ + boolean noId = (id == null || id.trim().equals("")); //$NON-NLS-1$ + boolean noVersion = (ver == null || ver.trim().equals("")); //$NON-NLS-1$ + + // We need to have id and version, or the url, or both. + if (noURL) { + if (noId || noVersion) + internalError(NLS.bind(Messages.DefaultSiteParser_Missing, (new String[] {"url", getState(currentState)}))); //$NON-NLS-1$ + else + // default url + urlInfo = PLUGINS + id + '_' + ver; // + } + + bundle.setURLString(urlInfo); + + String type = attributes.getValue("type"); //$NON-NLS-1$ + bundle.setType(type); + + // if one is null, and not the other + if (noId ^ noVersion) { + String[] values = new String[] {id, ver, getState(currentState)}; + log(NLS.bind(Messages.DefaultFeatureParser_IdOrVersionInvalid, values)); + } else { + bundle.setBundleIdentifier(id); + bundle.setBundleVersion(ver); + } + + // get label if it exists + String label = attributes.getValue("label"); //$NON-NLS-1$ + if (label != null) { + if ("".equals(label.trim())) //$NON-NLS-1$ + label = null; + checkTranslated(label); + } + bundle.setLabel(label); + + // OS + String os = attributes.getValue("os"); //$NON-NLS-1$ + bundle.setOS(os); + + // WS + String ws = attributes.getValue("ws"); //$NON-NLS-1$ + bundle.setWS(ws); + + // NL + String nl = attributes.getValue("nl"); //$NON-NLS-1$ + bundle.setNL(nl); + + // arch + String arch = attributes.getValue("arch"); //$NON-NLS-1$ + bundle.setArch(arch); + + //patch + String patch = attributes.getValue("patch"); //$NON-NLS-1$ + bundle.setPatch(patch); + + SiteModel site = (SiteModel) objectStack.peek(); + site.addBundle(bundle); + bundle.setSiteModel(site); + + objectStack.push(bundle); + + if (Tracing.DEBUG_GENERATOR_PARSING) + debug("End Processing DefaultFeature Tag: url:" + urlInfo + " type:" + type); //$NON-NLS-1$ //$NON-NLS-2$ + + } + + /* * process URL info with element text */ private void processInfo(Attributes attributes) { @@ -771,6 +894,10 @@ public class DefaultSiteParser extends DefaultHandler { handleFeatureState(localName, attributes); break; + case STATE_BUNDLE : + handleBundleState(localName, attributes); + break; + case STATE_ARCHIVE : handleSiteState(localName, attributes); break; diff --git a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/Messages.java b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/Messages.java index fc2de79ff..fb99a11b8 100644 --- a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/Messages.java +++ b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/Messages.java @@ -21,6 +21,7 @@ public class Messages extends NLS { public static String ErrorReadingDigest; public static String ErrorReadingFeature; + public static String ErrorReadingBundle; public static String ErrorReadingSite; public static String Error_generating_category; public static String Error_generating_siteXML; diff --git a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/SiteBundle.java b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/SiteBundle.java new file mode 100644 index 000000000..880fd5a30 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/SiteBundle.java @@ -0,0 +1,445 @@ +/******************************************************************************* + * Copyright (c) 2012, Red Hat Inc. 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: + * Mickael Istria (Red Hat) - Initial API & implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.updatesite; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.*; + +public class SiteBundle { + + private String arch; + // performance + private URL base; + private List<String> categoryNames; + private String bundleId; + private String bundleVersion; + private String label; + private String nl; + + private String os; + private String patch; + private final boolean resolved = false; + private SiteModel site; + private String type; + private URL url; + private String urlString; + private String ws; + + /* + * Compares two URL for equality + */ + public static boolean sameURL(URL url1, URL url2) { + if (url1 == url2) + return true; + if (url1 == null ^ url2 == null) + return false; + + // check if URL are file: URL as we may + // have 2 URL pointing to the same bundleReference + // but with different representation + // (i.e. file:/C;/ and file:C:/) + final boolean isFile1 = "file".equalsIgnoreCase(url1.getProtocol());//$NON-NLS-1$ + final boolean isFile2 = "file".equalsIgnoreCase(url2.getProtocol());//$NON-NLS-1$ + if (isFile1 && isFile2) { + File file1 = new File(url1.getFile()); + File file2 = new File(url2.getFile()); + return file1.equals(file2); + } + // URL1 xor URL2 is a file, return false. (They either both need to be files, or neither) + if (isFile1 ^ isFile2) + return false; + return getExternalForm(url1).equals(getExternalForm(url2)); + } + + /** + * Gets the external form of this URL. In particular, it trims any white space, + * removes a trailing slash and creates a lower case string. + */ + private static String getExternalForm(URL url) { + String externalForm = url.toExternalForm(); + if (externalForm == null) + return ""; //$NON-NLS-1$ + externalForm = externalForm.trim(); + if (externalForm.endsWith("/")) { //$NON-NLS-1$ + // Remove the trailing slash + externalForm = externalForm.substring(0, externalForm.length() - 1); + } + return externalForm.toLowerCase(); + + } + + /** + * Creates an uninitialized bundle reference model object. + */ + public SiteBundle() { + super(); + } + + /** + * Adds the name of a category this bundle belongs to. + * Throws a runtime exception if this object is marked read-only. + * + * @param categoryName category name + */ + public void addCategoryName(String categoryName) { + if (this.categoryNames == null) + this.categoryNames = new ArrayList<String>(); + if (!this.categoryNames.contains(categoryName)) + this.categoryNames.add(categoryName); + } + + private void delayedResolve() { + + // PERF: delay resolution + if (resolved) + return; + + // resolve local elements + try { + url = new URL(base, urlString); + } catch (MalformedURLException e) { + // UpdateCore.warn("", e); //$NON-NLS-1$ + } + } + + /** + * Compares 2 bundle reference models for equality + * + * @param object bundle reference model to compare with + * @return <code>true</code> if the two models are equal, + * <code>false</code> otherwise + */ + public boolean equals(Object object) { + if (object == null) + return false; + if (!(object instanceof SiteBundle)) + return false; + SiteBundle that = (SiteBundle) object; + if (this.bundleId == null) { + if (that.bundleId != null) + return false; + } else if (!this.bundleId.equals(that.bundleId)) + return false; + if (this.bundleVersion == null) { + if (that.bundleVersion != null) + return false; + } else if (!this.bundleVersion.equals(that.bundleVersion)) + return false; + if (this.label == null) { + if (that.label != null) + return false; + } else if (!this.label.equals(that.label)) + return false; + return sameURL(this.getURL(), that.getURL()); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (bundleId == null ? 0 : bundleId.hashCode()); + result = prime * result + (bundleVersion == null ? 0 : bundleVersion.hashCode()); + if (this.getURL() == null) + return result; + + if ("file".equalsIgnoreCase(getURL().getProtocol())) {//$NON-NLS-1$ + // If the URL is a file, then create the HashCode from the file + File f = new File(getURL().getFile()); + if (f != null) + result = prime * result + f.hashCode(); + } else + // Otherwise create it from the External form of the URL (in lower case) + result = prime * result + getExternalForm(this.getURL()).hashCode(); + return result; + } + + /** + * Returns the names of categories the referenced bundle belongs to. + * + * @return an array of names, or an empty array. + */ + public String[] getCategoryNames() { + if (categoryNames == null) + return new String[0]; + + return categoryNames.toArray(new String[0]); + } + + /** + * Returns the bundle identifier as a string + * + * @return bundle identifier + */ + public String getBundleIdentifier() { + return bundleId; + } + + /** + * Returns the bundle version as a string + * + * @return bundle version + */ + public String getBundleVersion() { + return bundleVersion; + } + + /** + * Retrieve the displayable label for the bundle reference. If the model + * object has been resolved, the label is localized. + * + * @return displayable label, or <code>null</code>. + */ + public String getLabel() { + return label; + } + + /** + * Retrieve the non-localized displayable label for the bundle reference. + * + * @return non-localized displayable label, or <code>null</code>. + */ + public String getLabelNonLocalized() { + return label; + } + + /** + * Get optional locale specification as a comma-separated string. + * + * @return the locale specification string, or <code>null</code>. + */ + public String getNL() { + return nl; + } + + /** + * Get optional operating system specification as a comma-separated string. + * + * @return the operating system specification string, or <code>null</code>. + */ + public String getOS() { + return os; + } + + /** + * Get optional system architecture specification as a comma-separated string. + * + * @return the system architecture specification string, or <code>null</code>. + */ + public String getOSArch() { + return arch; + } + + /** + * Returns the patch mode. + */ + public String getPatch() { + return patch; + } + + /** + * Returns the site model for the reference. + * + * @return site model + * @since 2.0 + */ + public SiteModel getSiteModel() { + return site; + } + + /** + * Returns the referenced bundle type. + * + * @return bundle type, or <code>null</code> representing the default + * bundle type for the site + */ + public String getType() { + return type; + } + + /** + * Returns the resolved URL for the bundle reference. + * + * @return url string + */ + public URL getURL() { + delayedResolve(); + return url; + } + + /** + * Returns the unresolved URL string for the reference. + * + * @return url string + */ + public String getURLString() { + return urlString; + } + + /** + * Get optional windowing system specification as a comma-separated string. + * + * @return the windowing system specification string, or <code>null</code>. + */ + public String getWS() { + return ws; + } + + /** + * Resolve the model object. + * Any URL strings in the model are resolved relative to the + * base URL argument. Any translatable strings in the model that are + * specified as translation keys are localized using the supplied + * resource bundle. + * + * @param resolveBase URL + * @param bundleURL resource bundle URL + * @exception MalformedURLException + */ + public void resolve(URL resolveBase, URL bundleURL) throws MalformedURLException { + this.base = resolveBase; + } + + /** + * Sets the system architecture specification. + * Throws a runtime exception if this object is marked read-only. + * + * @param arch system architecture specification as a comma-separated list + */ + public void setArch(String arch) { + this.arch = arch; + } + + /** + * Sets the names of categories this bundle belongs to. + * Throws a runtime exception if this object is marked read-only. + * + * @param categoryNames an array of category names + */ + public void setCategoryNames(String[] categoryNames) { + if (categoryNames == null) + this.categoryNames = null; + else + this.categoryNames = new ArrayList<String>(Arrays.asList(categoryNames)); + } + + /** + * Sets the bundle identifier. + * Throws a runtime exception if this object is marked read-only. + * + * @param bundleId bundle identifier + */ + public void setBundleIdentifier(String bundleId) { + this.bundleId = bundleId; + } + + /** + * Sets the bundle version. + * Throws a runtime exception if this object is marked read-only. + * + * @param bundleVersion bundle version + */ + public void setBundleVersion(String bundleVersion) { + this.bundleVersion = bundleVersion; + } + + /** + * Sets the label. + * @param label The label to set + */ + public void setLabel(String label) { + this.label = label; + } + + /** + * Sets the locale specification. + * Throws a runtime exception if this object is marked read-only. + * + * @param nl locale specification as a comma-separated list + */ + public void setNL(String nl) { + this.nl = nl; + } + + /** + * Sets the operating system specification. + * Throws a runtime exception if this object is marked read-only. + * + * @param os operating system specification as a comma-separated list + */ + public void setOS(String os) { + this.os = os; + } + + /** + * Sets the patch mode. + */ + public void setPatch(String patch) { + this.patch = patch; + } + + /** + * Sets the site for the referenced. + * Throws a runtime exception if this object is marked read-only. + * + * @param site site for the reference + */ + public void setSiteModel(SiteModel site) { + this.site = site; + } + + /** + * Sets the referenced bundle type. + * Throws a runtime exception if this object is marked read-only. + * + * @param type referenced bundle type + */ + public void setType(String type) { + this.type = type; + } + + /** + * Sets the unresolved URL for the bundle reference. + * Throws a runtime exception if this object is marked read-only. + * + * @param urlString unresolved URL string + */ + public void setURLString(String urlString) { + this.urlString = urlString; + this.url = null; + } + + /** + * Sets the windowing system specification. + * Throws a runtime exception if this object is marked read-only. + * + * @param ws windowing system specification as a comma-separated list + */ + public void setWS(String ws) { + this.ws = ws; + } + + /** + * @see Object#toString() + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append(getClass().toString() + " :"); //$NON-NLS-1$ + buffer.append(" at "); //$NON-NLS-1$ + if (url != null) + buffer.append(url.toExternalForm()); + return buffer.toString(); + } + +} diff --git a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/SiteModel.java b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/SiteModel.java index e45f08f9d..02237f440 100644 --- a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/SiteModel.java +++ b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/SiteModel.java @@ -32,6 +32,7 @@ public class SiteModel { * Map of String (feature id) -> SiteFeature */ private List<SiteFeature> features; + private List<SiteBundle> bundles; private List<SiteIU> ius; private URI locationURI; private String locationURIString; @@ -93,6 +94,17 @@ public class SiteModel { } /** + * Adds a bundle reference model to site. + * + * @param bundleReference bundle reference model + */ + public void addBundle(SiteBundle bundleReference) { + if (this.bundles == null) + this.bundles = new ArrayList<SiteBundle>(); + this.bundles.add(bundleReference); + } + + /** * Adds a iu model to site. * * @param iu iu model @@ -163,6 +175,17 @@ public class SiteModel { } /** + * Returns an array of bundle reference models on this site. + * + * @return an array of bundle reference models, or an empty array. + */ + public SiteBundle[] getBundles() { + if (bundles == null || bundles.size() == 0) + return new SiteBundle[0]; + return bundles.toArray(new SiteBundle[0]); + } + + /** * Returns an array of IU models on this site. * * @return an array of IU models, or an empty array. @@ -331,4 +354,5 @@ public class SiteModel { public String getDigestURIString() { return digestURIString; } + } diff --git a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/SiteXMLAction.java b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/SiteXMLAction.java index c11ac079f..9dbfd8ad0 100644 --- a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/SiteXMLAction.java +++ b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/SiteXMLAction.java @@ -98,6 +98,7 @@ public class SiteXMLAction extends AbstractPublisherAction { } } initialize(); + initializeRepoFromSite(publisherInfo); return generateCategories(publisherInfo, results, monitor); } @@ -115,14 +116,36 @@ public class SiteXMLAction extends AbstractPublisherAction { if (categories == null || categories.isEmpty()) categories = defaultCategorySet; for (SiteCategory category : categories) { - Set<IInstallableUnit> featureIUs = categoriesToIUs.get(category); - if (featureIUs == null) { - featureIUs = new HashSet<IInstallableUnit>(); - categoriesToIUs.put(category, featureIUs); + Set<IInstallableUnit> iusInCategory = categoriesToIUs.get(category); + if (iusInCategory == null) { + iusInCategory = new HashSet<IInstallableUnit>(); + categoriesToIUs.put(category, iusInCategory); } - featureIUs.addAll(ius); + iusInCategory.addAll(ius); } } + // Bundles -- bug 378338 + Map<SiteBundle, Set<SiteCategory>> bundlesToCategories = getBundleToCategoryMappings(publisherInfo); + for (SiteBundle bundle : bundlesToCategories.keySet()) { + if (monitor.isCanceled()) + return Status.CANCEL_STATUS; + Collection<IInstallableUnit> ius = getBundleIU(bundle, publisherInfo, results); + if (ius == null) + continue; + Set<SiteCategory> categories = bundlesToCategories.get(bundle); + // if there are no categories for this feature then add it to the default category. + if (categories == null || categories.isEmpty()) + categories = defaultCategorySet; + for (SiteCategory category : categories) { + Set<IInstallableUnit> iusInCategory = categoriesToIUs.get(category); + if (iusInCategory == null) { + iusInCategory = new HashSet<IInstallableUnit>(); + categoriesToIUs.put(category, iusInCategory); + } + iusInCategory.addAll(ius); + } + } + addSiteIUsToCategories(categoriesToIUs, publisherInfo, results); generateCategoryIUs(categoriesToIUs, results); return Status.OK_STATUS; @@ -219,6 +242,40 @@ public class SiteXMLAction extends AbstractPublisherAction { return null; } + private Collection<IInstallableUnit> getBundleIU(SiteBundle bundle, IPublisherInfo publisherInfo, IPublisherResult results) { + String id = bundle.getBundleIdentifier(); + String versionString = bundle.getBundleVersion(); + Version version = versionString != null && versionString.length() > 0 ? Version.create(versionString) : Version.emptyVersion; + IQuery<IInstallableUnit> query = null; + if (version.equals(Version.emptyVersion)) { + query = QueryUtil.createIUQuery(id); + } else { + String qualifier; + try { + qualifier = PublisherHelper.toOSGiVersion(version).getQualifier(); + } catch (UnsupportedOperationException e) { + qualifier = null; + } + if (qualifier != null && qualifier.endsWith(QUALIFIER)) { + VersionRange range = createVersionRange(version.toString()); + IQuery<IInstallableUnit> qualifierQuery = QueryUtil.createMatchQuery(qualifierMatchExpr, id, range); + query = qualifierQuery; + } else { + query = QueryUtil.createLimitQuery(QueryUtil.createIUQuery(id, version), 1); + } + } + + IQueryResult<IInstallableUnit> queryResult = results.query(query, null); + if (queryResult.isEmpty()) + queryResult = publisherInfo.getMetadataRepository().query(query, null); + if (queryResult.isEmpty() && publisherInfo.getContextMetadataRepository() != null) + queryResult = publisherInfo.getContextMetadataRepository().query(query, null); + + if (!queryResult.isEmpty()) + return queryResult.toUnmodifiableSet(); + return null; + } + protected VersionRange createVersionRange(String versionId) { VersionRange range = null; if (versionId == null || "0.0.0".equals(versionId)) //$NON-NLS-1$ @@ -259,6 +316,55 @@ public class SiteXMLAction extends AbstractPublisherAction { if (site == null) return mappings; + SiteFeature[] features = site.getFeatures(); + for (int i = 0; i < features.length; i++) { + //add a mapping for each category this feature belongs to + String[] categoryNames = features[i].getCategoryNames(); + Set<SiteCategory> categories = new HashSet<SiteCategory>(); + mappings.put(features[i], categories); + for (int j = 0; j < categoryNames.length; j++) { + SiteCategory category = site.getCategory(categoryNames[j]); + if (category != null) + categories.add(category); + } + } + return mappings; + } + + /** + * Computes the mapping of bundles to categories as defined in the site.xml, + * if available. Returns an empty map if there is not site.xml, or no categories. + * @return A map of SiteBundle -> Set<SiteCategory>. + */ + protected Map<SiteBundle, Set<SiteCategory>> getBundleToCategoryMappings(IPublisherInfo publisherInfo) { + HashMap<SiteBundle, Set<SiteCategory>> mappings = new HashMap<SiteBundle, Set<SiteCategory>>(); + if (updateSite == null) + return mappings; + SiteModel site = updateSite.getSite(); + if (site == null) + return mappings; + + SiteBundle[] bundles = site.getBundles(); + for (int i = 0; i < bundles.length; i++) { + //add a mapping for each category this feature belongs to + String[] categoryNames = bundles[i].getCategoryNames(); + Set<SiteCategory> categories = new HashSet<SiteCategory>(); + mappings.put(bundles[i], categories); + for (int j = 0; j < categoryNames.length; j++) { + SiteCategory category = site.getCategory(categoryNames[j]); + if (category != null) + categories.add(category); + } + } + return mappings; + } + + /** + * Initializes new p2 repository attributes such as mirror info, associate sites, localization... + * @param publisherInfo configuration for output repository + */ + private void initializeRepoFromSite(IPublisherInfo publisherInfo) { + SiteModel site = updateSite.getSite(); //copy mirror information from update site to p2 repositories String mirrors = site.getMirrorsURI(); if (mirrors != null) { @@ -304,20 +410,6 @@ public class SiteXMLAction extends AbstractPublisherAction { site.setLocalizations(LocalizationHelper.getJarPropertyLocalizations(siteParent, "site", null, keyStrings)); //$NON-NLS-1$ } } - - SiteFeature[] features = site.getFeatures(); - for (int i = 0; i < features.length; i++) { - //add a mapping for each category this feature belongs to - String[] categoryNames = features[i].getCategoryNames(); - Set<SiteCategory> categories = new HashSet<SiteCategory>(); - mappings.put(features[i], categories); - for (int j = 0; j < categoryNames.length; j++) { - SiteCategory category = site.getCategory(categoryNames[j]); - if (category != null) - categories.add(category); - } - } - return mappings; } /** diff --git a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/UpdateSite.java b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/UpdateSite.java index 00a37b507..57533cb58 100644 --- a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/UpdateSite.java +++ b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/UpdateSite.java @@ -23,6 +23,7 @@ import org.eclipse.equinox.internal.p2.publisher.eclipse.FeatureParser; import org.eclipse.equinox.internal.p2.repository.Transport; import org.eclipse.equinox.p2.core.ProvisionException; import org.eclipse.equinox.p2.publisher.eclipse.*; +import org.eclipse.osgi.service.resolver.BundleDescription; import org.eclipse.osgi.util.NLS; import org.xml.sax.SAXException; @@ -55,6 +56,8 @@ public class UpdateSite { private static Map<String, SoftReference<UpdateSite>> categoryCache = new HashMap<String, SoftReference<UpdateSite>>(); // map of String (featureID_featureVersion) to Feature private Map<String, Feature> featureCache = new HashMap<String, Feature>(); + // map of String (bundleID_featureVersion) to BundleDescriptr + private Map<String, BundleDescription> bundleCache = new HashMap<String, BundleDescription>(); private Transport transport; /* @@ -348,6 +351,22 @@ public class UpdateSite { } /* + * Return a URI which represents the location of the given bundle. + */ + public URI getSiteBundleURI(SiteBundle siteBundle) { + URL url = siteBundle.getURL(); + try { + if (url != null) + return URIUtil.toURI(url); + } catch (URISyntaxException e) { + //fall through and resolve the URI ourselves + } + URI base = getBaseURI(); + String bundleURIString = siteBundle.getURLString(); + return internalGetURI(base, bundleURIString); + } + + /* * Return a URI which represents the location of the given feature. */ public URI getFeatureURI(String id, String version) { @@ -366,6 +385,24 @@ public class UpdateSite { } /* + * Return a URI which represents the location of the given bundle. + */ + public URI getBundleURI(String id, String version) { + SiteBundle[] entries = site.getBundles(); + for (int i = 0; i < entries.length; i++) { + if (id.equals(entries[i].getBundleIdentifier()) && version.equals(entries[i].getBundleVersion())) { + return getSiteBundleURI(entries[i]); + } + } + + URI base = getBaseURI(); + URI url = getArchiveURI(base, FEATURE_DIR + id + VERSION_SEPARATOR + version + JAR_EXTENSION); + if (url != null) + return url; + return URIUtil.append(base, FEATURE_DIR + id + VERSION_SEPARATOR + version + JAR_EXTENSION); + } + + /* * Return the location of this site. */ public URI getLocation() { @@ -438,6 +475,16 @@ public class UpdateSite { } /* + * Load and return the bundle references in this update site. + */ + public synchronized BundleDescription[] loadBundles(IProgressMonitor monitor) throws ProvisionException { + if (!bundleCache.isEmpty()) + return bundleCache.values().toArray(new BundleDescription[bundleCache.size()]); + BundleDescription[] result = null; // TODO loadBundlesFromDigest(monitor); + return result == null ? loadBundlesFromSite(monitor) : result; + } + + /* * Try and load the feature information from the update site's * digest file, if it exists. */ @@ -570,4 +617,87 @@ public class UpdateSite { } } } + + /* + * Load and return the bundles that are referenced by this update site. Note this + * requires downloading and parsing the feature manifest locally. + */ + private BundleDescription[] loadBundlesFromSite(IProgressMonitor monitor) throws ProvisionException { + SiteBundle[] siteBundles = site.getBundles(); + Map<String, BundleDescription> tmpBundleCache = new HashMap<String, BundleDescription>(siteBundles.length); + + for (int i = 0; i < siteBundles.length; i++) { + if (monitor.isCanceled()) { + throw new OperationCanceledException(); + } + SiteBundle siteBundle = siteBundles[i]; + String key = null; + if (siteBundle.getBundleIdentifier() != null && siteBundle.getBundleVersion() != null) { + key = siteBundle.getBundleIdentifier() + VERSION_SEPARATOR + siteBundle.getBundleVersion(); + if (tmpBundleCache.containsKey(key)) + continue; + } + URI bundleURI = getSiteBundleURI(siteBundle); + BundleDescription bundle = parseBundleDescription(bundleURI, monitor); + if (bundle == null) { + LogHelper.log(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.ErrorReadingBundle, bundleURI))); + } else { + if (key == null) { + siteBundle.setBundleIdentifier(bundle.getSymbolicName()); + siteBundle.setBundleVersion(bundle.getVersion().toString()); + key = siteBundle.getBundleIdentifier() + VERSION_SEPARATOR + siteBundle.getBundleVersion().toString(); + } + tmpBundleCache.put(key, bundle); + } + } + bundleCache = tmpBundleCache; + return bundleCache.values().toArray(new BundleDescription[bundleCache.size()]); + } + + /* + * Reads a bundle and extract its BundleDescription + * In case of failure, the failure is logged and null is returned + */ + private BundleDescription parseBundleDescription(URI bundleURI, IProgressMonitor monitor) { + File bundleFile = null; + if (PROTOCOL_FILE.equals(bundleURI.getScheme())) { + bundleFile = URIUtil.toFile(bundleURI); + } + try { + bundleFile = File.createTempFile("bundle", JAR_EXTENSION); //$NON-NLS-1$ + IStatus transferResult = null; + //try the download twice in case of transient network problems + for (int i = 0; i < RETRY_COUNT; i++) { + if (monitor.isCanceled()) + throw new OperationCanceledException(); + OutputStream destination = new BufferedOutputStream(new FileOutputStream(bundleFile)); + try { + transferResult = transport.download(bundleURI, destination, monitor); + } finally { + try { + destination.close(); + } catch (IOException e) { + LogHelper.log(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.ErrorReadingFeature, bundleURI), e)); + return null; + } + } + if (transferResult.isOK()) + break; + } + if (monitor.isCanceled()) + throw new OperationCanceledException(); + if (!transferResult.isOK()) { + LogHelper.log(new ProvisionException(transferResult)); + return null; + } + return BundlesAction.createBundleDescription(bundleFile); + } catch (IOException e) { + LogHelper.log(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.ErrorReadingBundle, bundleURI), e)); + } finally { + if (bundleFile != null) + bundleFile.delete(); + } + return null; + } + } diff --git a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/artifact/UpdateSiteArtifactRepositoryFactory.java b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/artifact/UpdateSiteArtifactRepositoryFactory.java index ea9164574..100046f35 100644 --- a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/artifact/UpdateSiteArtifactRepositoryFactory.java +++ b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/artifact/UpdateSiteArtifactRepositoryFactory.java @@ -28,6 +28,7 @@ import org.eclipse.equinox.p2.repository.IRepositoryManager; import org.eclipse.equinox.p2.repository.artifact.*; import org.eclipse.equinox.p2.repository.artifact.spi.ArtifactRepositoryFactory; import org.eclipse.equinox.p2.repository.artifact.spi.ProcessingStepDescriptor; +import org.eclipse.osgi.service.resolver.BundleDescription; import org.eclipse.osgi.util.NLS; public class UpdateSiteArtifactRepositoryFactory extends ArtifactRepositoryFactory { @@ -114,53 +115,76 @@ public class UpdateSiteArtifactRepositoryFactory extends ArtifactRepositoryFacto private void generateArtifactDescriptors(UpdateSite updateSite, IArtifactRepository repository, IProgressMonitor monitor) throws ProvisionException { final String PACK_EXT = ".pack.gz"; //$NON-NLS-1$ - Feature[] features = updateSite.loadFeatures(monitor); Set<IArtifactDescriptor> allSiteArtifacts = new HashSet<IArtifactDescriptor>(); boolean packSupported = updateSite.getSite().isPack200Supported(); - for (int i = 0; i < features.length; i++) { - Feature feature = features[i]; - IArtifactKey featureKey = FeaturesAction.createFeatureArtifactKey(feature.getId(), feature.getVersion()); - SimpleArtifactDescriptor featureArtifactDescriptor = new SimpleArtifactDescriptor(featureKey); - URI featureURL = updateSite.getFeatureURI(feature.getId(), feature.getVersion()); - featureArtifactDescriptor.setRepositoryProperty(PROP_ARTIFACT_REFERENCE, featureURL.toString()); - allSiteArtifacts.add(featureArtifactDescriptor); - - if (packSupported) { - // Update site supports pack200, create a packed descriptor - featureArtifactDescriptor = new SimpleArtifactDescriptor(featureKey); - featureURL = updateSite.getFeatureURI(feature.getId(), feature.getVersion()); - featureArtifactDescriptor.setRepositoryProperty(PROP_ARTIFACT_REFERENCE, featureURL.toString() + PACK_EXT); - IProcessingStepDescriptor[] steps = new IProcessingStepDescriptor[] {new ProcessingStepDescriptor("org.eclipse.equinox.p2.processing.Pack200Unpacker", null, true)}; //$NON-NLS-1$ - featureArtifactDescriptor.setProcessingSteps(steps); - featureArtifactDescriptor.setProperty(IArtifactDescriptor.FORMAT, IArtifactDescriptor.FORMAT_PACKED); + { + Feature[] features = updateSite.loadFeatures(monitor); + for (int i = 0; i < features.length; i++) { + Feature feature = features[i]; + IArtifactKey featureKey = FeaturesAction.createFeatureArtifactKey(feature.getId(), feature.getVersion()); + SimpleArtifactDescriptor featureArtifactDescriptor = new SimpleArtifactDescriptor(featureKey); + URI featureURL = updateSite.getFeatureURI(feature.getId(), feature.getVersion()); + featureArtifactDescriptor.setRepositoryProperty(PROP_ARTIFACT_REFERENCE, featureURL.toString()); allSiteArtifacts.add(featureArtifactDescriptor); - } - FeatureEntry[] featureEntries = feature.getEntries(); - for (int j = 0; j < featureEntries.length; j++) { - FeatureEntry entry = featureEntries[j]; - if (entry.isPlugin() && !entry.isRequires()) { - IArtifactKey key = BundlesAction.createBundleArtifactKey(entry.getId(), entry.getVersion()); - SimpleArtifactDescriptor artifactDescriptor = new SimpleArtifactDescriptor(key); - URI pluginURL = updateSite.getPluginURI(entry); - artifactDescriptor.setRepositoryProperty(PROP_ARTIFACT_REFERENCE, pluginURL.toString()); - allSiteArtifacts.add(artifactDescriptor); - - if (packSupported) { - // Update site supports pack200, create a packed descriptor - key = BundlesAction.createBundleArtifactKey(entry.getId(), entry.getVersion()); - artifactDescriptor = new SimpleArtifactDescriptor(key); - pluginURL = updateSite.getPluginURI(entry); - artifactDescriptor.setRepositoryProperty(PROP_ARTIFACT_REFERENCE, pluginURL.toString() + PACK_EXT); - IProcessingStepDescriptor[] steps = new IProcessingStepDescriptor[] {new ProcessingStepDescriptor("org.eclipse.equinox.p2.processing.Pack200Unpacker", null, true)}; //$NON-NLS-1$ - artifactDescriptor.setProcessingSteps(steps); - artifactDescriptor.setProperty(IArtifactDescriptor.FORMAT, IArtifactDescriptor.FORMAT_PACKED); + if (packSupported) { + // Update site supports pack200, create a packed descriptor + featureArtifactDescriptor = new SimpleArtifactDescriptor(featureKey); + featureURL = updateSite.getFeatureURI(feature.getId(), feature.getVersion()); + featureArtifactDescriptor.setRepositoryProperty(PROP_ARTIFACT_REFERENCE, featureURL.toString() + PACK_EXT); + IProcessingStepDescriptor[] steps = new IProcessingStepDescriptor[] {new ProcessingStepDescriptor("org.eclipse.equinox.p2.processing.Pack200Unpacker", null, true)}; //$NON-NLS-1$ + featureArtifactDescriptor.setProcessingSteps(steps); + featureArtifactDescriptor.setProperty(IArtifactDescriptor.FORMAT, IArtifactDescriptor.FORMAT_PACKED); + allSiteArtifacts.add(featureArtifactDescriptor); + } + + FeatureEntry[] featureEntries = feature.getEntries(); + for (int j = 0; j < featureEntries.length; j++) { + FeatureEntry entry = featureEntries[j]; + if (entry.isPlugin() && !entry.isRequires()) { + IArtifactKey key = BundlesAction.createBundleArtifactKey(entry.getId(), entry.getVersion()); + SimpleArtifactDescriptor artifactDescriptor = new SimpleArtifactDescriptor(key); + URI pluginURL = updateSite.getPluginURI(entry); + artifactDescriptor.setRepositoryProperty(PROP_ARTIFACT_REFERENCE, pluginURL.toString()); allSiteArtifacts.add(artifactDescriptor); + + if (packSupported) { + // Update site supports pack200, create a packed descriptor + key = BundlesAction.createBundleArtifactKey(entry.getId(), entry.getVersion()); + artifactDescriptor = new SimpleArtifactDescriptor(key); + pluginURL = updateSite.getPluginURI(entry); + artifactDescriptor.setRepositoryProperty(PROP_ARTIFACT_REFERENCE, pluginURL.toString() + PACK_EXT); + IProcessingStepDescriptor[] steps = new IProcessingStepDescriptor[] {new ProcessingStepDescriptor("org.eclipse.equinox.p2.processing.Pack200Unpacker", null, true)}; //$NON-NLS-1$ + artifactDescriptor.setProcessingSteps(steps); + artifactDescriptor.setProperty(IArtifactDescriptor.FORMAT, IArtifactDescriptor.FORMAT_PACKED); + allSiteArtifacts.add(artifactDescriptor); + } } } } } - + { + BundleDescription[] bundles = updateSite.loadBundles(monitor); + for (int i = 0; i < bundles.length; i++) { + BundleDescription bundle = bundles[i]; + IArtifactKey bundleKey = BundlesAction.createBundleArtifactKey(bundle.getSymbolicName(), bundle.getVersion().toString()); + SimpleArtifactDescriptor bundleArtifactDescriptor = new SimpleArtifactDescriptor(bundleKey); + URI bundleURI = updateSite.getBundleURI(bundle.getSymbolicName(), bundle.getVersion().toString()); + bundleArtifactDescriptor.setRepositoryProperty(PROP_ARTIFACT_REFERENCE, bundleURI.toString()); + allSiteArtifacts.add(bundleArtifactDescriptor); + + if (packSupported) { + // Update site supports pack200, create a packed descriptor + bundleArtifactDescriptor = new SimpleArtifactDescriptor(bundleKey); + bundleURI = updateSite.getBundleURI(bundle.getSymbolicName(), bundle.getVersion().toString()); + bundleArtifactDescriptor.setRepositoryProperty(PROP_ARTIFACT_REFERENCE, bundleURI.toString() + PACK_EXT); + IProcessingStepDescriptor[] steps = new IProcessingStepDescriptor[] {new ProcessingStepDescriptor("org.eclipse.equinox.p2.processing.Pack200Unpacker", null, true)}; //$NON-NLS-1$ + bundleArtifactDescriptor.setProcessingSteps(steps); + bundleArtifactDescriptor.setProperty(IArtifactDescriptor.FORMAT, IArtifactDescriptor.FORMAT_PACKED); + allSiteArtifacts.add(bundleArtifactDescriptor); + } + } + } IArtifactDescriptor[] descriptors = allSiteArtifacts.toArray(new IArtifactDescriptor[allSiteArtifacts.size()]); repository.addDescriptors(descriptors); } diff --git a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/messages.properties b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/messages.properties index 7845a97a4..a5c25f150 100644 --- a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/messages.properties +++ b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/messages.properties @@ -10,21 +10,22 @@ ############################################################################### ErrorReadingDigest=Error reading site digest {0}. ErrorReadingFeature=Error reading feature {0}. +ErrorReadingBundle=Error reading bundle {0}. ErrorReadingSite=Error reading update site {0}. Error_generating_category=Error generating category xml action. Error_generating_siteXML=Error generating site xml action. Error_Generation=Problems generating update site metadata {0}. -DefaultFeatureParser_IdOrVersionInvalid= Error parsing feature stream. The unique identifier or the version is null or empty for the State: \"{2}\": unique identifier=\"{0}\" version=\"{1}\". +DefaultFeatureParser_IdOrVersionInvalid= Error parsing feature stream. The unique identifier or the version is null or empty for the State\: "{2}"\: unique identifier\="{0}" version\="{1}". DefaultSiteParser_NoSiteTag= Error parsing site stream. Unable to find root element \"site\" in the stream. -DefaultSiteParser_WrongParsingStack= Internal Error parsing site stream. Unexpected Parsing Stack: \"{0}\" +DefaultSiteParser_WrongParsingStack= Internal Error parsing site stream. Unexpected Parsing Stack\: "{0}" DefaultSiteParser_UnknownElement= Error parsing site stream. Unknown element \"{0}\" in parsing state \"{1}\". Check the validity of the XML file. DefaultSiteParser_UnknownStartState= Internal Error parsing site stream. Unknown start state \"{0}\". DefaultSiteParser_Missing= Error parsing site stream. The \"{0}\" tag of the element \"{1}\" is null or empty. Value is required. DefaultSiteParser_ParsingStackBackToInitialState= Internal Error parsing site stream. Parsing stack back to Initial State. -DefaultSiteParser_ElementAlreadySet= Error parsing site stream. Element: \"{0}\" already set for the Site. +DefaultSiteParser_ElementAlreadySet= Error parsing site stream. Element\: "{0}" already set for the Site. DefaultSiteParser_UnknownEndState= Internal Error parsing site stream. Unknown end state \"{0}\". -DefaultSiteParser_ErrorParsing= Error Parsing site stream. Error: \"{0}\" -DefaultSiteParser_ErrorlineColumnMessage= Error Parsing site stream. Element \"{0}\" line: \"{1}\" column:\"{2}\". Error: \"{3}\". +DefaultSiteParser_ErrorParsing= Error Parsing site stream. Error\: "{0}" +DefaultSiteParser_ErrorlineColumnMessage= Error Parsing site stream. Element "{0}" line\: "{1}" column\:"{2}". Error\: "{3}". DefaultSiteParser_ErrorParsingSite= Error Parsing site stream. DefaultSiteParser_UnknownState= Unknown State \"{0}\". DefaultSiteParser_InvalidXMLStream= The XML stream is not a valid default \"site.xml\" file. The root tag is not site. |