Return-Path: X-Original-To: apmail-aries-commits-archive@www.apache.org Delivered-To: apmail-aries-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id D6DBF17DF8 for ; Thu, 8 Oct 2015 02:14:03 +0000 (UTC) Received: (qmail 6706 invoked by uid 500); 8 Oct 2015 02:14:03 -0000 Delivered-To: apmail-aries-commits-archive@aries.apache.org Received: (qmail 6637 invoked by uid 500); 8 Oct 2015 02:14:03 -0000 Mailing-List: contact commits-help@aries.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@aries.apache.org Delivered-To: mailing list commits@aries.apache.org Received: (qmail 6626 invoked by uid 99); 8 Oct 2015 02:14:03 -0000 Received: from Unknown (HELO spamd1-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 08 Oct 2015 02:14:03 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd1-us-west.apache.org (ASF Mail Server at spamd1-us-west.apache.org) with ESMTP id 30FD5C4512 for ; Thu, 8 Oct 2015 02:14:03 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd1-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: 1.79 X-Spam-Level: * X-Spam-Status: No, score=1.79 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, T_RP_MATCHES_RCVD=-0.01] autolearn=disabled Received: from mx1-us-east.apache.org ([10.40.0.8]) by localhost (spamd1-us-west.apache.org [10.40.0.7]) (amavisd-new, port 10024) with ESMTP id Jll5GzK7345R for ; Thu, 8 Oct 2015 02:14:00 +0000 (UTC) Received: from mailrelay1-us-west.apache.org (mailrelay1-us-west.apache.org [209.188.14.139]) by mx1-us-east.apache.org (ASF Mail Server at mx1-us-east.apache.org) with ESMTP id 1BB4A439CB for ; Thu, 8 Oct 2015 02:14:00 +0000 (UTC) Received: from svn01-us-west.apache.org (svn.apache.org [10.41.0.6]) by mailrelay1-us-west.apache.org (ASF Mail Server at mailrelay1-us-west.apache.org) with ESMTP id 57F3AE0339 for ; Thu, 8 Oct 2015 02:13:59 +0000 (UTC) Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id BC8033A02E5 for ; Thu, 8 Oct 2015 02:13:58 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1707428 - in /aries/trunk/subsystem: subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ subsystem-core/src/test/java/org/apache/aries/subsystem/core/... Date: Thu, 08 Oct 2015 02:13:58 -0000 To: commits@aries.apache.org From: jwross@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20151008021358.BC8033A02E5@svn01-us-west.apache.org> Author: jwross Date: Thu Oct 8 02:13:58 2015 New Revision: 1707428 URL: http://svn.apache.org/viewvc?rev=1707428&view=rev Log: ARIES-1425 Support both osgi.bundle and osgi.fragment resource types when given a Subsystem-Content header clause with an unspecified type attribute. Add one unit test specifically for the Subsystem-Content header behavior. Add one integration test for bundle versus fragment selection as well as the generated Deployed-Content header. Compute default parameters only once. Check to see if the type attribute was specified and set a flag accordingly. Then add the default value in order to maintain the equals/hashCode integrity. That is, "foo" equals "foo;type=osgi.bundle". Maintain a copy of the original value so that the generated subsystem manifest and header as retrieved from getSubsystemHeaders will reflect whether or not the type was originally specified. If the type was unspecified, ensure the generated osgi.identity requirement will match both bundles and fragments. Discovering content works the same as before in that first the local repository is searched followed by repository services. Bundles are preferred over fragments within the same repository. If anything is found in the local repository, the search stops as before and does not proceed to the repository services. This means that a fragment in the local repository will be preferred over a bundle in a repository service. A few unreferenced methods were removed from SubsystemContentHeader because it made it easier to enforce invariants, etc. A modification was made to the abstract SubsystemTest class to allow bundle file names to be different from the symbolic name. A few typos were corrected. Added: aries/trunk/subsystem/subsystem-core/src/test/java/org/apache/aries/subsystem/core/archive/Aries1425Test.java aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1425Test.java Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemContentHeader.java aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemContentRequirement.java aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemResource.java aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/SubsystemTest.java Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemContentHeader.java URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemContentHeader.java?rev=1707428&r1=1707427&r2=1707428&view=diff ============================================================================== --- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemContentHeader.java (original) +++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemContentHeader.java Thu Oct 8 02:13:58 2015 @@ -20,6 +20,8 @@ import java.util.List; import org.apache.aries.subsystem.core.internal.ResourceHelper; import org.osgi.framework.Version; import org.osgi.framework.VersionRange; +import org.osgi.framework.namespace.IdentityNamespace; +import org.osgi.resource.Capability; import org.osgi.resource.Requirement; import org.osgi.resource.Resource; import org.osgi.service.subsystem.SubsystemConstants; @@ -31,30 +33,37 @@ public class SubsystemContentHeader exte public static final String DIRECTIVE_RESOLUTION = ResolutionDirective.NAME; public static final String DIRECTIVE_STARTORDER = StartOrderDirective.NAME; + private static final Collection defaultParameters = generateDefaultParameters( + // A default value for the type attribute is not included here + // because we need to determine in the constructor whether or + // not it was specified as part of the original value. + // See ARIES-1425. + VersionRangeAttribute.DEFAULT_VERSION, + ResolutionDirective.MANDATORY, + // This is an implementation specific start-order directive + // value. The specification states there is no default value. + new StartOrderDirective("0")); + + // Was the type attribute specified as part of the original value? + private final boolean isTypeSpecified; + private final String originalValue; + public Clause(String clause) { super( parsePath(clause, Patterns.SYMBOLIC_NAME, false), parseParameters(clause, true), - generateDefaultParameters( - TypeAttribute.DEFAULT, - VersionRangeAttribute.DEFAULT_VERSION, - ResolutionDirective.MANDATORY, - // This is an implementation specific start-order directive - // value. The specification states there is no default value. - new StartOrderDirective("0"))); - } - - public Clause(Resource resource) { - this(appendResource(resource, new StringBuilder()).toString()); - } - - public boolean contains(Resource resource) { - return getSymbolicName().equals( - ResourceHelper.getSymbolicNameAttribute(resource)) - && getVersionRange().includes( - ResourceHelper.getVersionAttribute(resource)) - && getType().equals( - ResourceHelper.getTypeAttribute(resource)); + defaultParameters); + if (parameters.get(TypeAttribute.NAME) == null) { + // The resource type was not specified. + isTypeSpecified = false; + // Add the default type. + parameters.put(TypeAttribute.NAME, TypeAttribute.DEFAULT); + } + else { + // The resource type was specified. + isTypeSpecified = true; + } + originalValue = clause; } public String getSymbolicName() { @@ -77,9 +86,18 @@ public class SubsystemContentHeader exte return ((ResolutionDirective)getDirective(DIRECTIVE_RESOLUTION)).isMandatory(); } + public boolean isTypeSpecified() { + return isTypeSpecified; + } + public SubsystemContentRequirement toRequirement(Resource resource) { return new SubsystemContentRequirement(this, resource); } + + @Override + public String toString() { + return originalValue; + } } public static final String NAME = SubsystemConstants.SUBSYSTEM_CONTENT; @@ -112,9 +130,7 @@ public class SubsystemContentHeader exte return builder; } - public SubsystemContentHeader(Collection clauses) { - super(clauses); - } + private final String originalValue; public SubsystemContentHeader(String value) { super( @@ -125,6 +141,7 @@ public class SubsystemContentHeader exte return new Clause(clause); } }); + originalValue = value; } public boolean contains(Resource resource) { @@ -132,16 +149,14 @@ public class SubsystemContentHeader exte } public Clause getClause(Resource resource) { - String symbolicName = ResourceHelper.getSymbolicNameAttribute(resource); - Version version = ResourceHelper.getVersionAttribute(resource); - String type = ResourceHelper.getTypeAttribute(resource); + Capability capability = resource.getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE).get(0); for (Clause clause : clauses) { - if (symbolicName.equals(clause.getPath()) - && clause.getVersionRange().includes(version) - && type.equals(clause.getType())) + Requirement requirement = clause.toRequirement(resource); + if (ResourceHelper.matches(requirement, capability)) { return clause; + } } - return null; + return null; } public boolean isMandatory(Resource resource) { @@ -158,7 +173,7 @@ public class SubsystemContentHeader exte @Override public String getValue() { - return toString(); + return originalValue; } @Override @@ -168,4 +183,9 @@ public class SubsystemContentHeader exte requirements.add(clause.toRequirement(resource)); return requirements; } + + @Override + public String toString() { + return originalValue; + } } Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemContentRequirement.java URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemContentRequirement.java?rev=1707428&r1=1707427&r2=1707428&view=diff ============================================================================== --- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemContentRequirement.java (original) +++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemContentRequirement.java Thu Oct 8 02:13:58 2015 @@ -33,8 +33,21 @@ public class SubsystemContentRequirement StringBuilder builder = new StringBuilder("(&(") .append(NAMESPACE).append('=') .append(clause.getSymbolicName()).append(')'); - for (Attribute attribute : clause.getAttributes()) - attribute.appendToFilter(builder); + for (Attribute attribute : clause.getAttributes()) { + if (!clause.isTypeSpecified() + && TypeAttribute.NAME.equals(attribute.getName())) { + // If the type attribute was not specified as part of the + // original clause, match against both bundles and fragments. + // See ARIES-1425. + builder.append("(|(").append(TypeAttribute.NAME).append('=') + .append(IdentityNamespace.TYPE_BUNDLE).append(")(") + .append(TypeAttribute.NAME).append('=').append(IdentityNamespace.TYPE_FRAGMENT) + .append("))"); + } + else { + attribute.appendToFilter(builder); + } + } directives.put(DIRECTIVE_FILTER, builder.append(')').toString()); this.resource = resource; } Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemResource.java URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemResource.java?rev=1707428&r1=1707427&r2=1707428&view=diff ============================================================================== --- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemResource.java (original) +++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemResource.java Thu Oct 8 02:13:58 2015 @@ -427,7 +427,7 @@ public class SubsystemResource implement } private ProvisionResourceHeader computeProvisionResourceHeader() { - Collection dependencies = getDepedencies(); + Collection dependencies = getDependencies(); if (dependencies.isEmpty()) return null; return ProvisionResourceHeader.newInstance(dependencies); @@ -485,16 +485,27 @@ public class SubsystemResource implement } } } + // First search the local repository. map = resource.getLocalRepository().findProviders(Collections.singleton(requirement)); - if (map.containsKey(requirement)) { - Collection capabilities = map.get(requirement); - if (!capabilities.isEmpty()) - return capabilities.iterator().next().getResource(); - } - Collection capabilities = new RepositoryServiceRepository().findProviders(requirement); - if (!capabilities.isEmpty()) - return capabilities.iterator().next().getResource(); - return null; + Collection capabilities = map.get(requirement); + if (capabilities.isEmpty()) { + // Nothing found in the local repository so search the repository services. + capabilities = new RepositoryServiceRepository().findProviders(requirement); + } + if (capabilities.isEmpty()) { + // Nothing found period. + return null; + } + for (Capability capability : capabilities) { + if (!IdentityNamespace.TYPE_FRAGMENT.equals( + capability.getAttributes().get(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE))) { + // Favor the first resource that is not a fragment bundle. + // See ARIES-1425. + return capability.getResource(); + } + } + // Nothing here but fragment bundles. Return the first one. + return capabilities.iterator().next().getResource(); } private Resource findContent(DeployedContentHeader.Clause clause) throws BundleException, IOException, InvalidSyntaxException, URISyntaxException { @@ -540,7 +551,7 @@ public class SubsystemResource implement return result; } - private Collection getDepedencies() { + private Collection getDependencies() { Collection result = new ArrayList(installableDependencies.size() + sharedDependencies.size()); result.addAll(installableDependencies); result.addAll(sharedDependencies); Added: aries/trunk/subsystem/subsystem-core/src/test/java/org/apache/aries/subsystem/core/archive/Aries1425Test.java URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/test/java/org/apache/aries/subsystem/core/archive/Aries1425Test.java?rev=1707428&view=auto ============================================================================== --- aries/trunk/subsystem/subsystem-core/src/test/java/org/apache/aries/subsystem/core/archive/Aries1425Test.java (added) +++ aries/trunk/subsystem/subsystem-core/src/test/java/org/apache/aries/subsystem/core/archive/Aries1425Test.java Thu Oct 8 02:13:58 2015 @@ -0,0 +1,195 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.aries.subsystem.core.archive; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.aries.subsystem.core.internal.OsgiIdentityCapability; +import org.apache.aries.subsystem.core.internal.ResourceHelper; +import org.junit.Test; +import org.osgi.framework.Version; +import org.osgi.framework.namespace.IdentityNamespace; +import org.osgi.resource.Capability; +import org.osgi.resource.Requirement; +import org.osgi.resource.Resource; + +/* + * https://issues.apache.org/jira/browse/ARIES-1425 + * + * Support both osgi.bundle and osgi.fragment resource types when given a + * Subsystem-Content header clause with an unspecified type attribute. + */ +public class Aries1425Test { + private static final String BUNDLE_A = "bundle.a"; + private static final String BUNDLE_B = "bundle.b;type=osgi.bundle"; + private static final String BUNDLE_C = "bundle.c;type=osgi.fragment"; + private static final String BUNDLE_D = "bundle.a;type=osgi.bundle"; + private static final String BUNDLE_E = "bundle.b"; + + private static final String HEADER_1 = BUNDLE_A + ',' + BUNDLE_B + ',' + BUNDLE_C; + private static final String HEADER_2 = BUNDLE_C + ',' + BUNDLE_D + ',' + BUNDLE_E; + + @Test + public void testGetValue() { + SubsystemContentHeader header = new SubsystemContentHeader(HEADER_1); + assertEquals("Wrong value", HEADER_1, header.getValue()); + } + + @Test + public void testToString() { + SubsystemContentHeader header = new SubsystemContentHeader(HEADER_1); + assertEquals("Wrong value", HEADER_1, header.toString()); + } + + @Test + public void testClauseToString() { + Set clauseStrs = new HashSet(Arrays.asList(HEADER_1.split(","))); + SubsystemContentHeader header = new SubsystemContentHeader(HEADER_1); + Collection clauses = header.getClauses(); + assertEquals("Wrong size", 3, clauses.size()); + for (SubsystemContentHeader.Clause clause : clauses) { + String clauseStr = clause.toString(); + assertTrue("Wrong clause", clauseStrs.remove(clauseStr)); + } + } + + @Test + public void testGetType() { + SubsystemContentHeader header = new SubsystemContentHeader(HEADER_1); + Collection clauses = header.getClauses(); + assertEquals("Wrong size", 3, clauses.size()); + Map map = new HashMap(3); + for (SubsystemContentHeader.Clause clause : clauses) { + map.put(clause.toString(), clause); + } + SubsystemContentHeader.Clause clause = map.get(BUNDLE_A); + assertEquals("Wrong type", IdentityNamespace.TYPE_BUNDLE, clause.getType()); + clause = map.get(BUNDLE_B); + assertEquals("Wrong type", IdentityNamespace.TYPE_BUNDLE, clause.getType()); + clause = map.get(BUNDLE_C); + assertEquals("Wrong type", IdentityNamespace.TYPE_FRAGMENT, clause.getType()); + } + + @Test + public void testIsTypeSpecified() { + SubsystemContentHeader header = new SubsystemContentHeader(HEADER_1); + Collection clauses = header.getClauses(); + assertEquals("Wrong size", 3, clauses.size()); + Map map = new HashMap(3); + for (SubsystemContentHeader.Clause clause : clauses) { + map.put(clause.toString(), clause); + } + SubsystemContentHeader.Clause clause = map.get(BUNDLE_A); + assertEquals("Should not be specified", Boolean.FALSE, clause.isTypeSpecified()); + clause = map.get(BUNDLE_B); + assertEquals("Should be specified", Boolean.TRUE, clause.isTypeSpecified()); + clause = map.get(BUNDLE_C); + assertEquals("Should be specified", Boolean.TRUE, clause.isTypeSpecified()); + } + + @Test + public void testToRequirement() { + SubsystemContentHeader header = new SubsystemContentHeader(HEADER_1); + Collection clauses = header.getClauses(); + assertEquals("Wrong size", 3, clauses.size()); + Map map = new HashMap(3); + for (SubsystemContentHeader.Clause clause : clauses) { + map.put(clause.toString(), clause); + } + Resource resource = new Resource() { + @Override + public List getCapabilities(String namespace) { + return Collections.emptyList(); + } + + @Override + public List getRequirements(String namespace) { + return Collections.emptyList(); + } + }; + SubsystemContentHeader.Clause clause = map.get(BUNDLE_A); + Requirement requirement = clause.toRequirement(resource); + assertTrue("Wrong requirement", ResourceHelper.matches( + requirement, + new OsgiIdentityCapability( + resource, + BUNDLE_A, + Version.emptyVersion, + IdentityNamespace.TYPE_FRAGMENT))); + assertTrue("Wrong requirement", ResourceHelper.matches( + requirement, + new OsgiIdentityCapability( + resource, + BUNDLE_A, + Version.emptyVersion, + IdentityNamespace.TYPE_BUNDLE))); + clause = map.get(BUNDLE_B); + requirement = clause.toRequirement(resource); + assertFalse("Wrong requirement", ResourceHelper.matches( + requirement, + new OsgiIdentityCapability( + resource, + "bundle.b", + Version.emptyVersion, + IdentityNamespace.TYPE_FRAGMENT))); + assertTrue("Wrong requirement", ResourceHelper.matches( + requirement, + new OsgiIdentityCapability( + resource, + "bundle.b", + Version.emptyVersion, + IdentityNamespace.TYPE_BUNDLE))); + clause = map.get(BUNDLE_C); + requirement = clause.toRequirement(resource); + assertTrue("Wrong requirement", ResourceHelper.matches( + requirement, + new OsgiIdentityCapability( + resource, + "bundle.c", + Version.emptyVersion, + IdentityNamespace.TYPE_FRAGMENT))); + assertFalse("Wrong requirement", ResourceHelper.matches( + requirement, + new OsgiIdentityCapability( + resource, + "bundle.c", + Version.emptyVersion, + IdentityNamespace.TYPE_BUNDLE))); + } + + @Test + public void testEquals() { + SubsystemContentHeader header1 = new SubsystemContentHeader(HEADER_1); + SubsystemContentHeader header2 = new SubsystemContentHeader(HEADER_2); + assertEquals("Headers are equal", header1, header2); + } + + @Test + public void testHashcode() { + SubsystemContentHeader header1 = new SubsystemContentHeader(HEADER_1); + SubsystemContentHeader header2 = new SubsystemContentHeader(HEADER_2); + assertEquals("Headers are equal", header1.hashCode(), header2.hashCode()); + } +} Modified: aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/SubsystemTest.java URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/SubsystemTest.java?rev=1707428&r1=1707427&r2=1707428&view=diff ============================================================================== --- aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/SubsystemTest.java (original) +++ aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/SubsystemTest.java Thu Oct 8 02:13:58 2015 @@ -271,7 +271,7 @@ public abstract class SubsystemTest exte assertConstituent(subsystem, symbolicName, version, IdentityNamespace.TYPE_BUNDLE); } - protected void assertContituent(Subsystem subsystem, String symbolicName, String type) { + protected void assertConstituent(Subsystem subsystem, String symbolicName, String type) { assertConstituent(subsystem, symbolicName, Version.emptyVersion, type); } @@ -557,10 +557,13 @@ public abstract class SubsystemTest exte } createBundle(emptyFiles, headerMap); } + + protected static void createBundle(List emptyFiles, Map headers) throws IOException { + createBundle(headers.get(Constants.BUNDLE_SYMBOLICNAME), emptyFiles, headers); + } - private static void createBundle(List emptyFiles, Map headers) throws IOException + protected static void createBundle(String fileName, List emptyFiles, Map headers) throws IOException { - String symbolicName = headers.get(Constants.BUNDLE_SYMBOLICNAME); JarFixture bundle = ArchiveFixture.newJar(); ManifestFixture manifest = bundle.manifest(); for (Entry header : headers.entrySet()) { @@ -569,7 +572,7 @@ public abstract class SubsystemTest exte for (String path : emptyFiles) { bundle.file(path).end(); } - write(symbolicName, bundle); + write(fileName, bundle); } protected static void createBlueprintBundle(String symbolicName, String blueprintXml) Added: aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1425Test.java URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1425Test.java?rev=1707428&view=auto ============================================================================== --- aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1425Test.java (added) +++ aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1425Test.java Thu Oct 8 02:13:58 2015 @@ -0,0 +1,287 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.aries.subsystem.itests.defect; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.aries.subsystem.core.archive.DeployedContentHeader; +import org.apache.aries.subsystem.itests.SubsystemTest; +import org.junit.Before; +import org.junit.Test; +import org.osgi.framework.Bundle; +import org.osgi.framework.Constants; +import org.osgi.framework.namespace.IdentityNamespace; +import org.osgi.service.subsystem.Subsystem; +import org.osgi.service.subsystem.SubsystemConstants; +import org.osgi.service.subsystem.SubsystemException; + +/* + * https://issues.apache.org/jira/browse/ARIES-1425 + * + * Support both osgi.bundle and osgi.fragment resource types when given a + * Subsystem-Content header clause with an unspecified type attribute. + */ +public class Aries1425Test extends SubsystemTest { + /* + * Subsystem-SymbolicName: application.a.esa + * Subsystem-Content: bundle.a.jar, bundle.b.jar + * + * Included In Archive + * bundle.a.fragment.jar + * bundle.b.jar + */ + private static final String APPLICATION_A = "application.a.esa"; + /* + * Subsystem-SymbolicName: application.b.esa + * Subsystem-Content: bundle.a.jar + * + * Included In Archive + * bundle.a.fragment.jar + * bundle.a.bundle.jar + */ + private static final String APPLICATION_B = "application.b.esa"; + /* + * Subsystem-SymbolicName: application.c.esa + * Subsystem-Content: bundle.a.jar + */ + private static final String APPLICATION_C = "application.c.esa"; + /* + * Subsystem-SymbolicName: application.d.esa + * Subsystem-Content: bundle.a.jar, bundle.b.jar + * + * Included In Archive + * bundle.a.fragment.jar + */ + private static final String APPLICATION_D = "application.d.esa"; + + private static final String BUNDLE_A = "bundle.a.jar"; + /* + * Bundle-SymbolicName: bundle.a.jar + * Fragment-Host: bundle.b.jar + */ + private static final String BUNDLE_A_FRAGMENT = "bundle.a.fragment.jar"; + /* + * Bundle-SymbolicName: bundle.a.jar + */ + private static final String BUNDLE_A_BUNDLE = "bundle.a.bundle.jar"; + /* + * Bundle-SymbolicName: bundle.b.jar + */ + private static final String BUNDLE_B = "bundle.b.jar"; + + private static boolean createdTestFiles; + + @Before + public void createTestFiles() throws Exception { + if (createdTestFiles) + return; + createBundleABundle(); + createBundleAFragment(); + createBundleB(); + createApplicationA(); + createApplicationB(); + createApplicationC(); + createApplicationD(); + createdTestFiles = true; + } + + private void createBundleABundle() throws IOException { + Map headers = new HashMap(); + headers.put(Constants.BUNDLE_SYMBOLICNAME, BUNDLE_A); + createBundle(BUNDLE_A_BUNDLE, Collections.emptyList(), headers); + } + + private void createBundleAFragment() throws IOException { + Map headers = new HashMap(); + headers.put(Constants.BUNDLE_SYMBOLICNAME, BUNDLE_A); + headers.put(Constants.FRAGMENT_HOST, BUNDLE_B); + createBundle(BUNDLE_A_FRAGMENT, Collections.emptyList(), headers); + } + + private void createBundleB() throws IOException { + createBundle(name(BUNDLE_B)); + } + + private static void createApplicationA() throws IOException { + createApplicationAManifest(); + createSubsystem(APPLICATION_A, BUNDLE_A_FRAGMENT, BUNDLE_B); + } + + private static void createApplicationAManifest() throws IOException { + Map attributes = new HashMap(); + attributes.put(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, APPLICATION_A); + attributes.put(SubsystemConstants.SUBSYSTEM_CONTENT, BUNDLE_A + ',' + BUNDLE_B); + createManifest(APPLICATION_A + ".mf", attributes); + } + + private static void createApplicationB() throws IOException { + createApplicationBManifest(); + createSubsystem(APPLICATION_B, BUNDLE_A_FRAGMENT, BUNDLE_A_BUNDLE); + } + + private static void createApplicationBManifest() throws IOException { + Map attributes = new HashMap(); + attributes.put(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, APPLICATION_B); + attributes.put(SubsystemConstants.SUBSYSTEM_CONTENT, BUNDLE_A); + createManifest(APPLICATION_B + ".mf", attributes); + } + + private static void createApplicationC() throws IOException { + createApplicationCManifest(); + createSubsystem(APPLICATION_C); + } + + private static void createApplicationCManifest() throws IOException { + Map attributes = new HashMap(); + attributes.put(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, APPLICATION_C); + attributes.put(SubsystemConstants.SUBSYSTEM_CONTENT, BUNDLE_A); + createManifest(APPLICATION_C + ".mf", attributes); + } + + private static void createApplicationD() throws IOException { + createApplicationDManifest(); + createSubsystem(APPLICATION_D, BUNDLE_A_FRAGMENT); + } + + private static void createApplicationDManifest() throws IOException { + Map attributes = new HashMap(); + attributes.put(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, APPLICATION_D); + attributes.put(SubsystemConstants.SUBSYSTEM_CONTENT, BUNDLE_A); + createManifest(APPLICATION_D + ".mf", attributes); + } + + @Test + public void testFragmentSelected() throws Exception { + try { + Subsystem applicationA = installSubsystemFromFile(APPLICATION_A); + try { + assertConstituent(applicationA, BUNDLE_A, null, IdentityNamespace.TYPE_FRAGMENT); + } + finally { + uninstallSubsystemSilently(applicationA); + } + } + catch (SubsystemException e) { + e.printStackTrace(); + fail("Subsystem should have installed"); + } + } + + @Test + public void testFragmentResolved() throws Exception { + Subsystem applicationA = installSubsystemFromFile(APPLICATION_A); + try { + applicationA.start(); + try { + Bundle bundleA = getConstituentAsBundle(applicationA, BUNDLE_A, null, IdentityNamespace.TYPE_FRAGMENT); + assertBundleState(bundleA, Bundle.RESOLVED); + } + finally { + stopSubsystemSilently(applicationA); + } + } + finally { + uninstallSubsystemSilently(applicationA); + } + } + + @Test + public void testDeployedContentHeader() throws Exception { + Subsystem applicationA = installSubsystemFromFile(APPLICATION_A); + try { + Map headers = applicationA.getDeploymentHeaders(); + String header = headers.get(SubsystemConstants.DEPLOYED_CONTENT); + DeployedContentHeader dch = new DeployedContentHeader(header); + boolean foundClause = false; + for (DeployedContentHeader.Clause clause : dch.getClauses()) { + if (BUNDLE_A.equals(clause.getSymbolicName())) { + assertEquals("Wrong type", IdentityNamespace.TYPE_FRAGMENT, clause.getType()); + foundClause = true; + break; + } + } + assertTrue("Missing clause", foundClause); + } + finally { + uninstallSubsystemSilently(applicationA); + } + } + + @Test + public void testProvisionResourceHeader() throws Exception { + Subsystem applicationA = installSubsystemFromFile(APPLICATION_A); + try { + Map headers = applicationA.getDeploymentHeaders(); + String header = headers.get(SubsystemConstants.PROVISION_RESOURCE); + assertFalse("Fragment content treated as dependency", header != null && header.contains(BUNDLE_A)); + } + finally { + uninstallSubsystemSilently(applicationA); + } + } + + @Test + public void testBundleSelectedFromLocalRepository() throws Exception { + Subsystem applicationB = installSubsystemFromFile(APPLICATION_B); + try { + assertNotConstituent(applicationB, BUNDLE_A, null, IdentityNamespace.TYPE_FRAGMENT); + assertConstituent(applicationB, BUNDLE_A, null, IdentityNamespace.TYPE_BUNDLE); + } + finally { + uninstallSubsystemSilently(applicationB); + } + } + + @Test + public void testBundleSelectedFromRemoteRepository() throws Exception { + // Make sure the repository containing the fragment comes first. + registerRepositoryService(BUNDLE_A_FRAGMENT); + registerRepositoryService(BUNDLE_A_BUNDLE); + + Subsystem applicationC = installSubsystemFromFile(APPLICATION_C); + try { + assertNotConstituent(applicationC, BUNDLE_A, null, IdentityNamespace.TYPE_FRAGMENT); + assertConstituent(applicationC, BUNDLE_A, null, IdentityNamespace.TYPE_BUNDLE); + } + finally { + uninstallSubsystemSilently(applicationC); + } + } + + @Test + public void testFragmentFromLocalRepoSelectedBeforeBundleRemoteRepository() throws Exception { + registerRepositoryService(BUNDLE_A_BUNDLE, BUNDLE_B); + Subsystem applicationD = installSubsystemFromFile(APPLICATION_D); + try { + assertNotConstituent(applicationD, BUNDLE_A, null, IdentityNamespace.TYPE_BUNDLE); + assertConstituent(applicationD, BUNDLE_A, null, IdentityNamespace.TYPE_FRAGMENT); + } + finally { + uninstallSubsystemSilently(applicationD); + } + } +}