Author: jwross
Date: Thu Feb 18 20:52:44 2016
New Revision: 1731141

URL: http://svn.apache.org/viewvc?rev=1731141&view=rev
Log:
[ARIES-1443] After a restart the capabilities of a subsystem have changed (seem 
correct) before the restart they seem wrong

This issue affects only newly installing features. The Subsystem service is 
registered and added to the system repository before all of its capabilities 
have been computed. Because
features implicitly export everything, their capabilities must be derived from 
their content. This is currently done examining constituents and filtering them 
based on the
Subsystem-Content header. It must be done this way for persisted subsystems, 
which are unaffected by this issue.

Initially, I tried to fix the issue by looking for features in the INSTALLED 
state as part of the modifiedService method of the SystemRepository because by 
then the consituents have
been computed and all capabilities are available. Unfortunately, this will not 
work for newly installing features with 
apache-aries-provision-dependencies:=resolve and that remain
in the INSTALLING state.

Ultimately, the issue was fixed in BasicSubsystem by adding an additional path 
to both getCapabilities and getRequirements. Newly installing subsystems can be 
distinguished from
persisted subsystems by the presence of a SubsystemResource, which the former 
will have while the latter will not. If a SubsystemResource is present, the 
capabilities and
requirements can be computed from the content contained therein. This is 
available as the newly installing subsystem is being added to the system 
repository. Persisted subsystems
continue to function as before.

Note that this should also fix ARIES-1442.

Added:
    
aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1443Test.java
Modified:
    
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/BasicSubsystem.java

Modified: 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/BasicSubsystem.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/BasicSubsystem.java?rev=1731141&r1=1731140&r2=1731141&view=diff
==============================================================================
--- 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/BasicSubsystem.java
 (original)
+++ 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/BasicSubsystem.java
 Thu Feb 18 20:52:44 2016
@@ -157,40 +157,107 @@ public class BasicSubsystem implements R
        
        @Override
        public List<Capability> getCapabilities(String namespace) {
+               // First, add the capabilities from the manifest.
                SubsystemManifest manifest = getSubsystemManifest();
                List<Capability> result = manifest.toCapabilities(this);
-               if (namespace != null)
-                       for (Iterator<Capability> i = result.iterator(); 
i.hasNext();)
-                               if (!i.next().getNamespace().equals(namespace))
+               if (namespace != null) {
+                       for (Iterator<Capability> i = result.iterator(); 
i.hasNext();) {
+                               if (!i.next().getNamespace().equals(namespace)) 
{
                                        i.remove();
-               // TODO Somehow, exposing the capabilities of content resources 
of a
-               // feature is causing an infinite regression of feature2 
installations
-               // in FeatureTest.testSharedContent() under certain conditions.
-               if (isScoped() || 
IdentityNamespace.IDENTITY_NAMESPACE.equals(namespace))
+                               }
+                       }
+               }
+               if (isScoped() || 
IdentityNamespace.IDENTITY_NAMESPACE.equals(namespace)) {
+                       // Scoped subsystems have all capabilities explicitly 
declared in
+                       // their manifests. Also, we do not want to include the 
osgi.identity
+                       // capabilities of content since a resource must have 
zero or one
+                       // osgi.identity capabilities.
                        return result;
-               SubsystemContentHeader header = 
manifest.getSubsystemContentHeader();
-               for (Resource constituent : getConstituents())
-                       if (header.contains(constituent))
-                               for (Capability capability : 
constituent.getCapabilities(namespace))
-                                       result.add(new 
BasicCapability(capability, this));
+               }
+               // This is an unscoped subsystem that implicitly exports 
everything.
+               // Its capabilities must be derived from its content.
+               if (resource == null) {
+                       // This is a persisted subsystem. We no longer have 
access to the
+                       // original content. We must look at constituents that 
are also 
+                       // content.
+                       SubsystemContentHeader header = 
manifest.getSubsystemContentHeader();
+                       for (Resource constituent : getConstituents()) {
+                               if (header.contains(constituent)) {
+                                       for (Capability capability : 
constituent.getCapabilities(namespace)) {
+                                               result.add(new 
BasicCapability(capability, this));
+                                       }
+                               }
+                       }
+                       return result;
+               }
+               // This is a newly installing subsystem. We therefore have 
access to the
+               // original content via the SubsystemResource and can derive the
+               // capabilities from there.
+               Collection<Resource> installableContent = 
resource.getInstallableContent();
+               Collection<Resource> sharedContent = 
resource.getSharedContent();
+               Collection<Resource> contents = new 
ArrayList<Resource>(installableContent.size() + sharedContent.size());
+               contents.addAll(installableContent);
+               contents.addAll(sharedContent);
+               for (Resource content : contents) {
+                       for (Capability capability : 
content.getCapabilities(namespace)) {
+                               if (namespace == null && 
IdentityNamespace.IDENTITY_NAMESPACE.equals(capability.getNamespace())) {
+                                       // Don't want to include the 
osgi.identity capabilities of
+                                       // content. Need a second check here in 
case the namespace
+                                       // is null.
+                                       continue;
+                               }
+                               result.add(new BasicCapability(capability, 
this));
+                       }
+               }
                return result;
        }
 
        @Override
        public List<Requirement> getRequirements(String namespace) {
+               // First, add the requirements from the manifest.
                SubsystemManifest manifest = getSubsystemManifest();
                List<Requirement> result = manifest.toRequirements(this);
-               if (namespace != null)
-                       for (Iterator<Requirement> i = result.iterator(); 
i.hasNext();)
-                               if (!i.next().getNamespace().equals(namespace))
+               if (namespace != null) {
+                       for (Iterator<Requirement> i = result.iterator(); 
i.hasNext();) {
+                               if (!i.next().getNamespace().equals(namespace)) 
{
                                        i.remove();
-               if (isScoped())
+                               }
+                       }
+               }
+               if (isScoped()) {
+                       // Scoped subsystems have all requirements explicitly 
declared in
+                       // their manifests.
+                       return result;
+               }
+               // This is an unscoped subsystem that implicitly imports 
everything.
+               // Its requirements must be derived from its content.
+               if (resource == null) {
+                       // This is a persisted subsystem. We no longer have 
access to the
+                       // original content. We must look at constituents that 
are also 
+                       // content.
+                       SubsystemContentHeader header = 
manifest.getSubsystemContentHeader();
+                       for (Resource constituent : getConstituents()) {
+                               if (header.contains(constituent)) {
+                                       for (Requirement requirement : 
constituent.getRequirements(namespace)) {
+                                               result.add(new 
BasicRequirement(requirement, this));
+                                       }
+                               }
+                       }
                        return result;
-               SubsystemContentHeader header = 
manifest.getSubsystemContentHeader();
-               for (Resource constituent : getConstituents())
-                       if (header.contains(constituent))
-                               for (Requirement requirement : 
constituent.getRequirements(namespace))
-                                       result.add(new 
BasicRequirement(requirement, this));
+               }
+               // This is a newly installing subsystem. We therefore have 
access to the
+               // original content via the SubsystemResource and can derive the
+               // requirements from there.
+               Collection<Resource> installableContent = 
resource.getInstallableContent();
+               Collection<Resource> sharedContent = 
resource.getSharedContent();
+               Collection<Resource> contents = new 
ArrayList<Resource>(installableContent.size() + sharedContent.size());
+               contents.addAll(installableContent);
+               contents.addAll(sharedContent);
+               for (Resource content : contents) {
+                       for (Requirement requirement : 
content.getRequirements(namespace)) {
+                               result.add(new BasicRequirement(requirement, 
this));
+                       }
+               }
                return result;
        }
        

Added: 
aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1443Test.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1443Test.java?rev=1731141&view=auto
==============================================================================
--- 
aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1443Test.java
 (added)
+++ 
aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1443Test.java
 Thu Feb 18 20:52:44 2016
@@ -0,0 +1,99 @@
+/*
+ * 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 java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import 
org.apache.aries.subsystem.core.archive.AriesProvisionDependenciesDirective;
+import org.apache.aries.subsystem.core.internal.Activator;
+import org.apache.aries.subsystem.core.internal.SystemRepository;
+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.TestRequirement;
+import org.junit.Test;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.service.subsystem.Subsystem;
+import org.osgi.service.subsystem.SubsystemConstants;
+
+public class Aries1443Test extends SubsystemTest {
+       @Test
+    public void testProvisionDependenciesInstall() throws Exception {
+               test(AriesProvisionDependenciesDirective.VALUE_INSTALL);
+    }
+       
+       @Test
+    public void testProvisionDependenciesResolve() throws Exception {
+               test(AriesProvisionDependenciesDirective.VALUE_RESOLVE);
+    }
+       
+       private void test(String apacheAriesProvisionDependencies) throws 
Exception {
+               Subsystem root = getRootSubsystem();
+               Subsystem composite = installSubsystem(
+                               root,
+                               "composite", 
+                               new SubsystemArchiveBuilder()
+                                               .symbolicName("composite")
+                                               
.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE 
+                                                               + ';' 
+                                                               + 
AriesProvisionDependenciesDirective.NAME
+                                                               + ":="
+                                                               + 
apacheAriesProvisionDependencies)
+                                               
.importPackage("org.osgi.framework")
+                                               .build(),
+                               
AriesProvisionDependenciesDirective.VALUE_INSTALL.equals(apacheAriesProvisionDependencies)
+               );
+               uninstallableSubsystems.add(composite);
+               startSubsystem(composite, 
AriesProvisionDependenciesDirective.VALUE_INSTALL.equals(apacheAriesProvisionDependencies));
+               stoppableSubsystems.add(composite);
+               installSubsystem(
+                               composite,
+                               "feature", 
+                               new SubsystemArchiveBuilder()
+                                               .symbolicName("feature")
+                                               
.type(SubsystemConstants.SUBSYSTEM_TYPE_FEATURE
+                                                               + ';'
+                                                               + 
AriesProvisionDependenciesDirective.NAME
+                                                               + ":="
+                                                               + 
apacheAriesProvisionDependencies)
+                                               .bundle(
+                                                               "a", 
+                                                               new 
BundleArchiveBuilder()
+                                                                               
.symbolicName("a")
+                                                                               
.exportPackage("a")
+                                                               .build())
+                                               .build(),
+                               
AriesProvisionDependenciesDirective.VALUE_INSTALL.equals(apacheAriesProvisionDependencies)
+               );
+               SystemRepository repository = 
Activator.getInstance().getSystemRepository();
+               Requirement requirement = new TestRequirement.Builder()
+                               .namespace("osgi.wiring.package")
+                               .directive("filter", "(osgi.wiring.package=a)")
+                               .build();
+               Map<Requirement, Collection<Capability>> providers = 
repository.findProviders(
+                               Collections.singleton(requirement));
+               Collection<Capability> capabilities = 
providers.get(requirement);
+               assertEquals("Missing provider", 2, capabilities.size());
+       }
+}


Reply via email to