Author: jwross
Date: Fri Sep  7 18:34:15 2012
New Revision: 1382116

URL: http://svn.apache.org/viewvc?rev=1382116&view=rev
Log:
ARIES-922: If a bundle is uninstalled from the root region while the bundle 
event hook is not registered but remains in persistent memory, root 
initialization will no longer fail with an NPE. The missing bundle is simply 
removed from memory.

A failure will still occur if the same thing happens for a non-root subsystem. 
I left it that way for now for fail-fast purposes since it would be very bad 
for someone to muck around with the deployed content of a non-root subsystem 
outside of the subsystems API. It might be reasonable in the future to make 
this more intelligent by checking to see if the missing resource is optional or 
trying to re-deploy it from a repository.

Also added a new test.

Modified:
    
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeployedContentHeader.java
    
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/AriesSubsystem.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/RootSubsystemTest.java

Modified: 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeployedContentHeader.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeployedContentHeader.java?rev=1382116&r1=1382115&r2=1382116&view=diff
==============================================================================
--- 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeployedContentHeader.java
 (original)
+++ 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeployedContentHeader.java
 Fri Sep  7 18:34:15 2012
@@ -85,6 +85,27 @@ public class DeployedContentHeader imple
                }
                
                @Override
+               public boolean equals(Object o) {
+                       if (o == this)
+                               return true;
+                       if (!(o instanceof Clause))
+                               return false;
+                       Clause that = (Clause)o;
+                       return getSymbolicName().equals(that.getSymbolicName())
+                                       && 
getDeployedVersion().equals(that.getDeployedVersion())
+                                       && getType().equals(that.getType());
+               }
+               
+               @Override
+               public int hashCode() {
+                       int result = 17;
+                       result = 31 * result + getSymbolicName().hashCode();
+                       result = 31 * result + getDeployedVersion().hashCode();
+                       result = 31 * result + getType().hashCode();
+                       return result;
+               }
+               
+               @Override
                public Attribute getAttribute(String name) {
                        Parameter result = parameters.get(name);
                        if (result instanceof Attribute)

Modified: 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/AriesSubsystem.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/AriesSubsystem.java?rev=1382116&r1=1382115&r2=1382116&view=diff
==============================================================================
--- 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/AriesSubsystem.java
 (original)
+++ 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/AriesSubsystem.java
 Fri Sep  7 18:34:15 2012
@@ -358,6 +358,20 @@ public class AriesSubsystem implements R
                        catch (Exception e) {
                                throw new SubsystemException(e);
                        }
+                       Collection<DeployedContentHeader.Clause> 
missingResources = resource.getMissingResources();
+                       if (!missingResources.isEmpty()) {
+                               if (isRoot())
+                                       // We don't care if the root subsystem 
has missing resources
+                                       // because they are either (1) 
extraneous bundles outside of
+                                       // the subsystems API or (2) 
provisioned dependencies of
+                                       // other subsystems. Those that fall in 
the latter category
+                                       // will be detected by the dependent 
subsystems.
+                                       removedContent(missingResources);
+                               else
+                                       // If a non-root subsystem has missing 
dependencies, let's
+                                       // fail fast for now.
+                                       throw new SubsystemException("Missing 
resources: " + missingResources);
+                       }
                }
                return resource;
        }
@@ -427,9 +441,17 @@ public class AriesSubsystem implements R
                DeployedContentHeader.Clause clause = 
header.getClause(resource);
                if (clause == null)
                        return;
+               removedContent(Collections.singleton(clause));
+       }
+       
+       void removedContent(Collection<DeployedContentHeader.Clause> content) {
+               DeploymentManifest manifest = getDeploymentManifest();
+               DeployedContentHeader header = 
manifest.getDeployedContentHeader();
+               if (header == null)
+                       return;
                Collection<DeployedContentHeader.Clause> clauses = new 
ArrayList<DeployedContentHeader.Clause>(header.getClauses());
                for (Iterator<DeployedContentHeader.Clause> i = 
clauses.iterator(); i.hasNext();)
-                       if (clause.equals(i.next())) {
+                       if (content.contains(i.next())) {
                                i.remove();
                                break;
                        }

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=1382116&r1=1382115&r2=1382116&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
 Fri Sep  7 18:34:15 2012
@@ -46,6 +46,7 @@ import org.eclipse.equinox.region.Region
 import org.eclipse.equinox.region.RegionDigraph;
 import org.eclipse.equinox.region.RegionFilter;
 import org.eclipse.equinox.region.RegionFilterBuilder;
+import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleException;
 import org.osgi.framework.InvalidSyntaxException;
@@ -76,6 +77,7 @@ public class SubsystemResource implement
        private final Collection<Resource> installableContent = new 
HashSet<Resource>();
        private final Collection<Resource> installableDependencies = new 
HashSet<Resource>();
        private final Collection<Resource> mandatoryResources = new 
HashSet<Resource>();
+       private final Collection<DeployedContentHeader.Clause> missingResources 
= new HashSet<DeployedContentHeader.Clause>();
        private final Collection<Resource> optionalResources = new 
HashSet<Resource>();
        private final AriesSubsystem parent;
        private final Repository preferredProviderRepository;
@@ -159,6 +161,10 @@ public class SubsystemResource implement
                return resource.getLocation().getValue();
        }
        
+       public Collection<DeployedContentHeader.Clause> getMissingResources() {
+               return missingResources;
+       }
+       
        public Collection<AriesSubsystem> getParents() {
                if (parent == null) {
                        Header<?> header = 
getDeploymentManifest().getHeaders().get(DeploymentManifest.ARIESSUBSYSTEM_PARENTS);
@@ -290,6 +296,10 @@ public class SubsystemResource implement
                        sharedDependencies.add(resource);
        }
        
+       private void addMissingResource(DeployedContentHeader.Clause resource) {
+               missingResources.add(resource);
+       }
+       
        private void addValidCapabilities(Collection<Capability> from, 
Collection<Capability> to) throws BundleException, IOException, 
InvalidSyntaxException, URISyntaxException {
                for (Capability c : from)
                        if (isValid(c))
@@ -332,8 +342,9 @@ public class SubsystemResource implement
                        for (DeployedContentHeader.Clause clause : 
header.getClauses()) {
                                Resource resource = findContent(clause);
                                if (resource == null)
-                                       throw new SubsystemException("Resource 
does not exist: " + clause);
-                               addContentResource(resource);
+                                       addMissingResource(clause);
+                               else
+                                       addContentResource(resource);
                        }
                }
        }
@@ -531,7 +542,10 @@ public class SubsystemResource implement
                if (resourceId != -1) {
                        String type = clause.getType();
                        if (IdentityNamespace.TYPE_BUNDLE.equals(type) || 
IdentityNamespace.TYPE_FRAGMENT.equals(type)) {
-                               return 
Activator.getInstance().getBundleContext().getBundle(0).getBundleContext().getBundle(resourceId).adapt(BundleRevision.class);
+                               Bundle resource = 
Activator.getInstance().getBundleContext().getBundle(0).getBundleContext().getBundle(resourceId);
+                               if (resource == null)
+                                       return null;
+                               return resource.adapt(BundleRevision.class);
                        }
                        else
                                return 
Activator.getInstance().getSubsystems().getSubsystemById(resourceId);

Modified: 
aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/RootSubsystemTest.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/RootSubsystemTest.java?rev=1382116&r1=1382115&r2=1382116&view=diff
==============================================================================
--- 
aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/RootSubsystemTest.java
 (original)
+++ 
aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/RootSubsystemTest.java
 Fri Sep  7 18:34:15 2012
@@ -14,6 +14,7 @@
 package org.apache.aries.subsystem.itests;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 
 import java.io.File;
 import java.io.IOException;
@@ -103,6 +104,34 @@ public class RootSubsystemTest extends S
        }
        
        @Test
+       public void testUninstallRootRegionBundleWithNoBundleEventHook() throws 
Exception {
+               // Install an extraneous bundle into the root region. The 
bundle will
+               // be recorded in the root subsystem's persistent memory.
+               Bundle bundleA = bundleContext.installBundle(new 
File(BUNDLE_A).toURI().toURL().toString());
+               try {
+                       Bundle core = getSubsystemCoreBundle();
+                       // Stop the subsystems bundle in order to unregister 
the bundle
+                       // event hook.
+                       core.stop();
+                       // Uninstall the bundle so it won't be there on restart.
+                       bundleA.uninstall();
+                       try {
+                               // Start the subsystems bundle and ensure the 
root subsystem
+                               // recovers from the uninstalled bundle being 
in persistent
+                               // memory.
+                               core.start();
+                       }
+                       catch (BundleException e) {
+                               fail("Could not start subsystems bundle after 
uninstalling a root region bundle with no bundle event hook registered");
+                       }
+               }
+               finally {
+                       if (Bundle.UNINSTALLED != bundleA.getState())
+                               bundleA.uninstall();
+               }
+       }
+       
+       @Test
        public void testVersion() {
                assertEquals("Wrong root version", 
getRootSubsystem().getVersion(), Version.parseVersion("1.0.0"));
        }


Reply via email to