Author: jwross
Date: Tue Feb 9 19:39:26 2016
New Revision: 1729443
URL: http://svn.apache.org/viewvc?rev=1729443&view=rev
Log:
[ARIES-1445] Bundles that are not direct dependencies of a subsystem can be
removed while still in use
The resolve context will now return an empty map for getWirings(). This means
that when computing dependencies, a "total" resolution will occur ensuring that
all involved resources are
part of the resolution map. The wirings continue to be computed as before and
used for purposes internal to the resolve context.
In pseudocode, the findProviders algorithm is:
If the requirement resource is already resolved, and the requirement is
effective:=resolve, then:
If the requirement resource is a fragment, and the requirement is payload,
then:
Get the requirement's resource.
Get the resource's wiring.
Get the required wires from the wiring for the osgi.wiring.host
namespace.
Process the provider of each wire as a bundle.
Otherwise, process as a bundle.
Get the requirement's namespace.
Get the requirement's resource.
Get the resource's wiring.
Get the required wires from the wiring for the namespace.
Return all matching capabilities.
If no capabilities are found,
and the namespace is osgi.wiring.package,
and the requirement is mandatory,
assume the requirer is the same as the provider (i.e.
substitutable export):
Get the requirement's namespace.
Get the requirement's resource.
Get the resource's wiring.
Get the resource capabilities from the wiring for the namespace.
Return all matching capabilities.
Otherwise, process the requirement as before.
The effective:=active requirements are processed as before because it doesn't
matter which capability is returned to the resolver since the framework ignores
these requirements and no
runtime wires exist.
The implementation includes support for handling fragments, but it would be
nice at some point to have some dedicated tests.
(1) Ensure a payload requirement of an already resolved fragment is handled
properly. The resolve context should return the matching wired capability from
the required wires of each
host to which the fragment is attached.
(2) Ensure a non-payload requirement of an already resolved fragment is handled
properly. The resolve context should return the matching wired capability from
the required wires of the
fragment.
As a more general test, ensure that a multiple cardinality requirement of an
already resolved resource is handled properly. The resolve context should
return all matching wired
capabilities.
Added:
aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1445Test.java
Modified:
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ResolveContext.java
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemResource.java
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/UninstallAction.java
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/Utils.java
Modified:
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ResolveContext.java
URL:
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ResolveContext.java?rev=1729443&r1=1729442&r2=1729443&view=diff
==============================================================================
---
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ResolveContext.java
(original)
+++
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ResolveContext.java
Tue Feb 9 19:39:26 2016
@@ -34,18 +34,21 @@ import org.eclipse.equinox.region.Region
import org.osgi.framework.BundleException;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.namespace.ExecutionEnvironmentNamespace;
+import org.osgi.framework.namespace.HostNamespace;
import org.osgi.framework.namespace.IdentityNamespace;
import org.osgi.framework.namespace.NativeNamespace;
+import org.osgi.framework.namespace.PackageNamespace;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.namespace.service.ServiceNamespace;
import org.osgi.resource.Capability;
import org.osgi.resource.Namespace;
import org.osgi.resource.Requirement;
import org.osgi.resource.Resource;
+import org.osgi.resource.Wire;
import org.osgi.resource.Wiring;
import org.osgi.service.resolver.HostedCapability;
import org.osgi.service.subsystem.Subsystem.State;
-import org.osgi.service.subsystem.SubsystemException;
public class ResolveContext extends org.osgi.service.resolver.ResolveContext {
private final Repository contentRepository;
@@ -94,44 +97,147 @@ public class ResolveContext extends org.
AccessController.doPrivileged(new
StartAction(subsystem, subsystem, subsystem, Restriction.INSTALL_ONLY));
}
}
-
- @Override
- public List<Capability> findProviders(Requirement requirement) {
- installDependenciesOfRequirerIfNecessary(requirement);
- ArrayList<Capability> result = new ArrayList<Capability>();
+
+ private boolean isResolved(Resource resource) {
+ return wirings.containsKey(resource);
+ }
+
+ private boolean isProcessableAsFragment(Requirement requirement) {
+ Resource resource = requirement.getResource();
+ String namespace = requirement.getNamespace();
+ return Utils.isFragment(resource)
+ &&
!(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE.equals(namespace)
+ ||
HostNamespace.HOST_NAMESPACE.equals(namespace));
+ }
+
+ private void processAsFragment(Requirement requirement,
List<Capability> capabilities) {
+ String namespace = requirement.getNamespace();
+ Resource fragment = requirement.getResource();
+ Wiring fragmentWiring = wirings.get(fragment);
+ List<Wire> fragmentWires =
fragmentWiring.getRequiredResourceWires(HostNamespace.HOST_NAMESPACE);
+ for (Wire fragmentWire : fragmentWires) {
+ Resource host = fragmentWire.getProvider();
+ Wiring hostWiring = wirings.get(host);
+ List<Wire> hostWires =
hostWiring.getRequiredResourceWires(namespace);
+ processWires(hostWires, requirement, capabilities);
+ }
+ }
+
+ private void processWires(Collection<Wire> wires, Requirement
requirement, List<Capability> capabilities) {
+ if (wires.isEmpty()) {
+ handleNoWires(requirement, capabilities);
+ return;
+ }
+ for (Wire wire : wires) {
+ processWire(wire, requirement, capabilities);
+ }
+ }
+
+ private void processWire(Wire wire, Requirement requirement,
List<Capability> capabilities) {
+ Capability capability = wire.getCapability();
+ processCapability(capability, requirement, capabilities);
+ }
+
+ private void processCapability(Capability capability, Requirement
requirement, List<Capability> capabilities) {
+ if (ResourceHelper.matches(requirement, capability)) {
+ capabilities.add(capability);
+ }
+ }
+
+ private void processResourceCapabilities(Collection<Capability>
resourceCapabilities, Requirement requirement, List<Capability> capabilities) {
+ for (Capability resourceCapability : resourceCapabilities) {
+ processCapability(resourceCapability, requirement,
capabilities);
+ }
+ }
+
+ private void processAsBundle(Requirement requirement, List<Capability>
capabilities) {
+ String namespace = requirement.getNamespace();
+ Resource bundle = requirement.getResource();
+ Wiring wiring = wirings.get(bundle);
+ List<Wire> wires = wiring.getRequiredResourceWires(namespace);
+ processWires(wires, requirement, capabilities);
+ }
+
+ private void handleNoWires(Requirement requirement, List<Capability>
capabilities) {
+ String namespace = requirement.getNamespace();
+ if (!ServiceNamespace.SERVICE_NAMESPACE.equals(namespace)) {
+ return;
+ }
+ try {
+ addDependenciesFromSystemRepository(requirement,
capabilities);
+ }
+ catch (Exception e) {
+ Utils.handleTrowable(e);
+ }
+ }
+
+ private void processAsSubstitutableExport(Requirement requirement,
List<Capability> capabilities) {
+ String namespace = requirement.getNamespace();
+ if (!PackageNamespace.PACKAGE_NAMESPACE.equals(namespace)) {
+ return;
+ }
+ Resource resource = requirement.getResource();
+ Wiring wiring = wirings.get(resource);
+ List<Capability> resourceCapabilities =
wiring.getResourceCapabilities(namespace);
+ processResourceCapabilities(resourceCapabilities, requirement,
capabilities);
+ }
+
+ private void processAlreadyResolvedResource(Resource resource,
Requirement requirement, List<Capability> capabilities) {
+ if (isProcessableAsFragment(requirement)) {
+ processAsFragment(requirement, capabilities);
+ }
+ else {
+ processAsBundle(requirement, capabilities);
+ }
+ if (capabilities.isEmpty() && Utils.isMandatory(requirement)) {
+ processAsSubstitutableExport(requirement, capabilities);
+ }
+ }
+
+ private void processNewlyResolvedResource(Resource resource,
Requirement requirement, List<Capability> capabilities) {
try {
// Only check the system repository for osgi.ee and
osgi.native
if
(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE.equals(requirement.getNamespace())
||
NativeNamespace.NATIVE_NAMESPACE.equals(requirement.getNamespace())) {
-
addDependenciesFromSystemRepository(requirement, result);
+
addDependenciesFromSystemRepository(requirement, capabilities);
} else {
-
addDependenciesFromContentRepository(requirement, result);
-
addDependenciesFromPreferredProviderRepository(requirement, result);
-
addDependenciesFromSystemRepository(requirement, result);
- addDependenciesFromLocalRepository(requirement,
result);
- if (result.isEmpty()) {
-
addDependenciesFromRepositoryServiceRepositories(requirement, result);
+
addDependenciesFromContentRepository(requirement, capabilities);
+
addDependenciesFromPreferredProviderRepository(requirement, capabilities);
+
addDependenciesFromSystemRepository(requirement, capabilities);
+ addDependenciesFromLocalRepository(requirement,
capabilities);
+ if (capabilities.isEmpty()) {
+
addDependenciesFromRepositoryServiceRepositories(requirement, capabilities);
}
}
- if (result.isEmpty()) {
+ if (capabilities.isEmpty()) {
// Is the requirement optional?
String resolution =
requirement.getDirectives().get(Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE);
if
(Namespace.RESOLUTION_OPTIONAL.equals(resolution)) {
// Yes, it's optional. Add a missing
capability to ensure
// it gets added to the sharing policy
per the specification.
- result.add(new
MissingCapability(requirement));
+ capabilities.add(new
MissingCapability(requirement));
}
}
}
catch (Throwable t) {
- if (t instanceof SubsystemException)
- throw (SubsystemException)t;
- if (t instanceof SecurityException)
- throw (SecurityException)t;
- throw new SubsystemException(t);
+ Utils.handleTrowable(t);
+ }
+ }
+
+ @Override
+ public List<Capability> findProviders(Requirement requirement) {
+ ArrayList<Capability> capabilities = new
ArrayList<Capability>();
+ Resource resource = requirement.getResource();
+ if (isResolved(resource)
+ && Utils.isEffectiveResolve(requirement)) {
+ processAlreadyResolvedResource(resource, requirement,
capabilities);
+ }
+ else {
+ installDependenciesOfRequirerIfNecessary(requirement);
+ processNewlyResolvedResource(resource, requirement,
capabilities);
}
- result.trimToSize();
- return result;
+ capabilities.trimToSize();
+ return capabilities;
}
@Override
@@ -163,7 +269,7 @@ public class ResolveContext extends org.
@Override
public Map<Resource, Wiring> getWirings() {
- return wirings;
+ return Collections.emptyMap();
}
private boolean addDependencies(Repository repository, Requirement
requirement, List<Capability> capabilities, boolean validate) throws
BundleException, IOException, InvalidSyntaxException, URISyntaxException {
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=1729443&r1=1729442&r2=1729443&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
Tue Feb 9 19:39:26 2016
@@ -294,15 +294,6 @@ public class SubsystemResource implement
sharedContent.add(resource);
}
- private void addDependency(Resource resource) {
- if (resource == null)
- return;
- if (isInstallable(resource))
- installableDependencies.add(resource);
- else
- sharedDependencies.add(resource);
- }
-
private void addMissingResource(DeployedContentHeader.Clause resource) {
missingResources.add(resource);
}
@@ -383,9 +374,17 @@ public class SubsystemResource implement
}
}
}
+
+ private void addDependency(Resource resource) {
+ if (resource == null)
+ return;
+ if (isInstallable(resource))
+ installableDependencies.add(resource);
+ else
+ sharedDependencies.add(resource);
+ }
private void computeDependencies(SubsystemManifest manifest,
Coordination coordination) {
- SubsystemContentHeader contentHeader =
manifest.getSubsystemContentHeader();
try {
// The following line is necessary in order to ensure
that the
// export sharing policies of composites are in place
for capability
@@ -393,36 +392,29 @@ public class SubsystemResource implement
StartAction.setExportPolicyOfAllInstallingSubsystemsWithProvisionDependenciesResolve(coordination);
Map<Resource, List<Wire>> resolution =
Activator.getInstance().getResolver().resolve(createResolveContext());
setImportIsolationPolicy(resolution);
- for (Map.Entry<Resource, List<Wire>> entry :
resolution.entrySet()) {
- Resource key = entry.getKey();
- String type =
ResourceHelper.getTypeAttribute(key);
- // Do not include synthetic resources in the
dependencies.
- if
(!Constants.ResourceTypeSynthesized.equals(type)
- &&
!contentHeader.contains(key)) {
- addDependency(key);
- }
- for (Wire wire : entry.getValue()) {
- Resource provider = wire.getProvider();
- type =
ResourceHelper.getTypeAttribute(provider);
- // Do not include synthetic resources
in the dependencies.
- if
(!Constants.ResourceTypeSynthesized.equals(type)
- &&
!contentHeader.contains(provider)) {
- addDependency(provider);
- }
- }
- }
- }
- catch (ResolutionException e) {
- throw new SubsystemException(e);
+ addDependencies(resolution);
}
catch (Exception e) {
- if (e instanceof SubsystemException) {
- throw (SubsystemException)e;
- }
- if (e instanceof SecurityException) {
- throw (SecurityException)e;
- }
- throw new SubsystemException(e);
+ Utils.handleTrowable(e);
+ }
+ }
+
+ private void addDependencies(Map<Resource, List<Wire>> resolution) {
+ for (Map.Entry<Resource, List<Wire>> entry :
resolution.entrySet()) {
+ addDependencies(entry, resolution);
+ }
+ }
+
+ private void addDependencies(Map.Entry<Resource, List<Wire>> entry,
Map<Resource, List<Wire>> resolution) {
+ addDependencies(entry.getKey(), entry, resolution);
+ }
+
+ private void addDependencies(Resource resource, Map.Entry<Resource,
List<Wire>> entry, Map<Resource, List<Wire>> resolution) {
+ String type = ResourceHelper.getTypeAttribute(resource);
+ SubsystemContentHeader contentHeader =
getSubsystemManifest().getSubsystemContentHeader();
+ if (!Constants.ResourceTypeSynthesized.equals(type) // Do not
include synthetic resources as dependencies.
+ && !contentHeader.contains(resource)) { // Do
not include content as dependencies.
+ addDependency(resource);
}
}
Modified:
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/UninstallAction.java
URL:
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/UninstallAction.java?rev=1729443&r1=1729442&r2=1729443&view=diff
==============================================================================
---
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/UninstallAction.java
(original)
+++
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/UninstallAction.java
Tue Feb 9 19:39:26 2016
@@ -29,8 +29,9 @@ public class UninstallAction extends Abs
return null;
}
try {
- // Acquire the global write lock to prevent all other
operations until
- // the installation is complete. There is no need to
hold any other locks.
+ // Acquire the global write lock to prevent all other
operations
+ // until the uninstall is complete. There is no need to
hold any
+ // other locks.
LockingStrategy.writeLock();
try {
checkRoot();
Modified:
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/Utils.java
URL:
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/Utils.java?rev=1729443&r1=1729442&r2=1729443&view=diff
==============================================================================
---
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/Utils.java
(original)
+++
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/Utils.java
Tue Feb 9 19:39:26 2016
@@ -14,6 +14,7 @@
package org.apache.aries.subsystem.core.internal;
import java.util.Collection;
+import java.util.Map;
import org.apache.aries.subsystem.core.archive.DeploymentManifest;
import org.apache.aries.subsystem.core.archive.ProvisionResourceHeader;
@@ -21,11 +22,14 @@ import org.apache.aries.subsystem.core.a
import org.apache.aries.subsystem.core.archive.SubsystemManifest;
import org.osgi.framework.namespace.IdentityNamespace;
import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.resource.Namespace;
+import org.osgi.resource.Requirement;
import org.osgi.resource.Resource;
import org.osgi.service.coordinator.Coordination;
import org.osgi.service.coordinator.CoordinationException;
import org.osgi.service.subsystem.Subsystem;
import org.osgi.service.subsystem.SubsystemConstants;
+import org.osgi.service.subsystem.SubsystemException;
import org.osgi.service.subsystem.Subsystem.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -98,6 +102,16 @@ public class Utils {
return -1;
}
+ public static void handleTrowable(Throwable t) {
+ if (t instanceof SubsystemException) {
+ throw (SubsystemException)t;
+ }
+ if (t instanceof SecurityException) {
+ throw (SecurityException)t;
+ }
+ throw new SubsystemException(t);
+ }
+
public static void installResource(Resource resource, BasicSubsystem
subsystem) {
Coordination coordination = Utils.createCoordination(subsystem);
try {
@@ -126,6 +140,23 @@ public class Utils {
IdentityNamespace.TYPE_FRAGMENT.equals(type);
}
+ public static boolean isFragment(Resource resource) {
+ String type = ResourceHelper.getTypeAttribute(resource);
+ return IdentityNamespace.TYPE_FRAGMENT.equals(type);
+ }
+
+ public static boolean isEffectiveResolve(Requirement requirement) {
+ Map<String, String> directives = requirement.getDirectives();
+ String value =
directives.get(Namespace.REQUIREMENT_EFFECTIVE_DIRECTIVE);
+ return value == null ||
Namespace.EFFECTIVE_RESOLVE.equals(value);
+ }
+
+ public static boolean isMandatory(Requirement requirement) {
+ Map<String, String> directives = requirement.getDirectives();
+ String value =
directives.get(Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE);
+ return value == null ||
Namespace.RESOLUTION_MANDATORY.equals(value);
+ }
+
/*
* The Deployed-Content header in the deployment manifest is used to
store
* information about explicitly installed resources and provisioned
Added:
aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1445Test.java
URL:
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1445Test.java?rev=1729443&view=auto
==============================================================================
---
aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1445Test.java
(added)
+++
aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1445Test.java
Tue Feb 9 19:39:26 2016
@@ -0,0 +1,218 @@
+/*
+ * 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.fail;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.aries.subsystem.itests.SubsystemTest;
+import org.apache.aries.subsystem.itests.util.BundleArchiveBuilder;
+import org.apache.aries.subsystem.itests.util.SubsystemArchiveBuilder;
+import org.apache.aries.subsystem.itests.util.TestCapability;
+import org.apache.aries.subsystem.itests.util.TestRepository;
+import org.apache.aries.subsystem.itests.util.TestRepositoryContent;
+import org.apache.aries.subsystem.itests.util.TestRequirement;
+import org.junit.Test;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.Version;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.namespace.service.ServiceNamespace;
+import org.osgi.service.repository.Repository;
+import org.osgi.service.subsystem.Subsystem;
+import org.osgi.service.subsystem.SubsystemConstants;
+import org.osgi.service.subsystem.SubsystemException;
+
+public class Aries1445Test extends SubsystemTest {
+ @Test
+ public void testFeatureFeature() throws Exception {
+ test(SubsystemConstants.SUBSYSTEM_TYPE_FEATURE,
SubsystemConstants.SUBSYSTEM_TYPE_FEATURE);
+ }
+
+ @Test
+ public void testApplicationApplication() throws Exception {
+ test(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION,
SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION);
+ }
+
+ @Test
+ public void testCompositeComposite() throws Exception {
+ test(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE,
SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE);
+ }
+
+ @Test
+ public void testFeatureApplication() throws Exception {
+ test(SubsystemConstants.SUBSYSTEM_TYPE_FEATURE,
SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION);
+ }
+
+ @Test
+ public void testCompositeFeature() throws Exception {
+ test(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE,
SubsystemConstants.SUBSYSTEM_TYPE_FEATURE);
+ }
+
+ private void test(String type1, String type2) throws Exception {
+ serviceRegistrations.add(bundleContext.registerService(
+ Repository.class,
+ new TestRepository.Builder()
+ .resource(new
TestRepositoryContent.Builder()
+ .capability(new
TestCapability.Builder()
+
.namespace(IdentityNamespace.IDENTITY_NAMESPACE)
+ .attribute(
+
IdentityNamespace.IDENTITY_NAMESPACE,
+ "b")
+ .attribute(
+
IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE,
+
Version.emptyVersion)
+ .attribute(
+
IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE,
+
IdentityNamespace.TYPE_BUNDLE))
+ .capability(new TestCapability.Builder()
+
.namespace(PackageNamespace.PACKAGE_NAMESPACE)
+ .attribute(
+
PackageNamespace.PACKAGE_NAMESPACE,
+
"b.package")
+ .attribute(
+
IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE,
+
Version.emptyVersion))
+ .requirement(new
TestRequirement.Builder()
+
.namespace(PackageNamespace.PACKAGE_NAMESPACE)
+ .directive(
+
PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE,
+
"(osgi.wiring.package=c.package)"))
+ .requirement(new
TestRequirement.Builder()
+
.namespace(ServiceNamespace.SERVICE_NAMESPACE)
+ .directive(
+
ServiceNamespace.REQUIREMENT_FILTER_DIRECTIVE,
+
"(objectClass=foo.Bar)")
+ .directive(
+
ServiceNamespace.REQUIREMENT_EFFECTIVE_DIRECTIVE,
+
ServiceNamespace.EFFECTIVE_ACTIVE))
+ .content(new BundleArchiveBuilder()
+ .symbolicName("b")
+
.exportPackage("b.package")
+
.importPackage("c.package")
+
.requireCapability("osgi.service;filter:=\"(objectClass=foo.Bar)\";effective:=active")
+ .buildAsBytes())
+ .build())
+ .resource(new TestRepositoryContent.Builder()
+ .capability(new
TestCapability.Builder()
+
.namespace(IdentityNamespace.IDENTITY_NAMESPACE)
+ .attribute(
+
IdentityNamespace.IDENTITY_NAMESPACE,
+ "c")
+ .attribute(
+
IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE,
+
Version.emptyVersion)
+ .attribute(
+
IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE,
+
IdentityNamespace.TYPE_BUNDLE))
+ .capability(new TestCapability.Builder()
+
.namespace(PackageNamespace.PACKAGE_NAMESPACE)
+ .attribute(
+
PackageNamespace.PACKAGE_NAMESPACE,
+
"c.package")
+ .attribute(
+
IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE,
+
Version.emptyVersion))
+ .content(new BundleArchiveBuilder()
+ .symbolicName("c")
+
.exportPackage("c.package")
+ .buildAsBytes())
+ .build())
+ .resource(new TestRepositoryContent.Builder()
+ .capability(new
TestCapability.Builder()
+
.namespace(IdentityNamespace.IDENTITY_NAMESPACE)
+ .attribute(
+
IdentityNamespace.IDENTITY_NAMESPACE,
+ "d")
+ .attribute(
+
IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE,
+
Version.emptyVersion)
+ .attribute(
+
IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE,
+
IdentityNamespace.TYPE_BUNDLE))
+ .capability(new TestCapability.Builder()
+
.namespace(ServiceNamespace.SERVICE_NAMESPACE)
+ .attribute(
+
Constants.OBJECTCLASS,
+
"foo.Bar")
+ .attribute(
+
IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE,
+
Version.emptyVersion)
+ .directive(
+
ServiceNamespace.CAPABILITY_EFFECTIVE_DIRECTIVE,
+
ServiceNamespace.EFFECTIVE_ACTIVE))
+ .content(new BundleArchiveBuilder()
+ .symbolicName("d")
+
.provideCapability("osgi.service;objectClass=foo.Bar;effective:=active")
+ .buildAsBytes())
+ .build())
+ .build(),
+ null));
+ Subsystem root = getRootSubsystem();
+ Subsystem s1 = installSubsystem(
+ root,
+ "s1",
+ buildSubsystem(root, "s1", type1));
+ uninstallableSubsystems.add(s1);
+ startSubsystem(s1);
+ stoppableSubsystems.add(s1);
+ Subsystem s2 = installSubsystem(
+ root,
+ "s2",
+ buildSubsystem(root, "s2", type2));
+ uninstallableSubsystems.add(s2);
+ stopSubsystem(s1);
+ stoppableSubsystems.remove(s1);
+ uninstallSubsystem(s1);
+ uninstallableSubsystems.remove(s1);
+ getSystemBundleAsFrameworkWiring().refreshBundles(null,
(FrameworkListener)null);
+ try {
+ s2.start();
+ stoppableSubsystems.add(s2);
+ }
+ catch (SubsystemException e) {
+ e.printStackTrace();
+ fail("Subsystem should have started");
+ }
+ // Test the effective:=active service capability and
requirement. Bundle
+ // D should have had a reference count of 2 and not uninstalled
as part
+ // of S1. Because effective:=active does not effect runtime
resolution,
+ // we must ensure it is still a constituent of root.
+ assertConstituent(root, "d");
+ }
+
+ private InputStream buildSubsystem(Subsystem parent, String
symbolicName, String type) throws IOException {
+ SubsystemArchiveBuilder builder = new SubsystemArchiveBuilder();
+ builder.symbolicName(symbolicName);
+ builder.type(type);
+ if (SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE.equals(type)) {
+ builder.importPackage("b.package");
+ }
+ builder.bundle(
+ "a",
+ new BundleArchiveBuilder()
+ .symbolicName("a")
+ .importPackage("b.package")
+ .build());
+ return builder.build();
+ }
+}