You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by jw...@apache.org on 2012/04/09 17:04:14 UTC

svn commit: r1311274 - in /aries/trunk/subsystem: subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/tmp/ subsystem-itests/src/test/java/org/apache/aries/subsyste...

Author: jwross
Date: Mon Apr  9 15:04:13 2012
New Revision: 1311274

URL: http://svn.apache.org/viewvc?rev=1311274&view=rev
Log:
ARIES-825: Update subsystems to latest Subsystem, Resolver, and Repository APIs.

(1) Fixed issue where default version specified in subsystem URI was not being used.
(2) Fixed issue where embedded URL within subsystem URI was not being used.
(3) Fixed issue where feature installations were not failed when Preferred-Provider header was declared.
(4) Fixed issue with equals and hashCode of SubsystemGraph where symbolic name, version, and type were being used. Either location or ID should be used instead.
(5) Fixed issue where target region was being computed incorrectly.
(6) Added support for subsystem service visibility rules by region.
(7) Fixed issue where getBundleContext() would throw an NPE for features that only had features as parents.
(8) Child subsystems will now be resolved before the parent's bundles. This puts the events in the expected order as well as insures the child's export policy is applied in order to satisfy any dependencies within the parent.
(9) Added INSTALLED as a transitional event when uninstalling a subsystem.
(10) Fixed issue where UNINSTALLING and UNINSTALLED events were not taking place from the INSTALL_FAILED state.
(11) Updated tests to expect transitional INSTALLED state when uninstalling a subsystem.
(12) Fixed issue with SubsystemResource.Location.open() where provided location string was not being used as a last resort.

Modified:
    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/Constants.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemGraph.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemManifestValidator.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemServiceRegistrar.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/TargetRegion.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/tmp/SubsystemResource.java
    aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/FeatureTest.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/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=1311274&r1=1311273&r2=1311274&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 Mon Apr  9 15:04:13 2012
@@ -433,21 +433,14 @@ public class AriesSubsystem implements S
 	
 	@Override
 	public BundleContext getBundleContext() {
-		if (EnumSet.of(State.INSTALL_FAILED, State.UNINSTALLED).contains(getState()))
+		if (EnumSet.of(State.INSTALL_FAILED, State.UNINSTALLED).contains(
+				getState()))
 			return null;
-		Region region = this.region;
-		Subsystem subsystem = this;
-		// Features, and unscoped subsystems in general, do not have their own region context
-		// bundle but rather share with the scoped subsystem in the same region.
-		if (isFeature()) {
-			for (Subsystem parent : getParents()) {
-				if (!((AriesSubsystem)parent).isFeature()) {
-					region = ((AriesSubsystem)parent).getRegion();
-					subsystem = parent;
-				}
-			}
-		}
-		return region.getBundle(RegionContextBundleHelper.SYMBOLICNAME_PREFIX + subsystem.getSubsystemId(), RegionContextBundleHelper.VERSION).getBundleContext();
+		AriesSubsystem subsystem = findScopedSubsystemInRegion();
+		return region.getBundle(
+				RegionContextBundleHelper.SYMBOLICNAME_PREFIX
+						+ subsystem.getSubsystemId(),
+				RegionContextBundleHelper.VERSION).getBundleContext();
 	}
 	
 	@Override
@@ -706,7 +699,7 @@ public class AriesSubsystem implements S
 				.getServiceProvider().getService(Coordinator.class)
 				.create(getSymbolicName() + "-" + getSubsystemId(), 0);
 		try {
-			install(coordination);
+			install(coordination, null);
 		} catch (Exception e) {
 			coordination.fail(e);
 		} finally {
@@ -800,6 +793,34 @@ public class AriesSubsystem implements S
 		}
 	}
 	
+	private void addSubsystemServiceImportToSharingPolicy(
+			RegionFilterBuilder builder) throws InvalidSyntaxException {
+		builder.allow(
+				RegionFilter.VISIBLE_SERVICE_NAMESPACE,
+				new StringBuilder("(&(")
+						.append(org.osgi.framework.Constants.OBJECTCLASS)
+						.append('=').append(Subsystem.class.getName())
+						.append(")(")
+						.append(Constants.SubsystemServicePropertyRegions)
+						.append('=').append(region.getName())
+						.append("))").toString());
+	}
+	
+	private void addSubsystemServiceImportToSharingPolicy(RegionFilterBuilder builder, Region to)
+			throws InvalidSyntaxException, BundleException {
+		// TODO This check seems brittle. There is apparently no constant for
+		// the root region's name in Digraph.
+		if (to.getName().equals("org.eclipse.equinox.region.kernel"))
+			addSubsystemServiceImportToSharingPolicy(builder);
+		else {
+			to = findRootRegion();
+			builder = to.getRegionDigraph().createRegionFilterBuilder();
+			addSubsystemServiceImportToSharingPolicy(builder);
+			RegionFilter regionFilter = builder.build();
+			region.connectRegion(to, regionFilter);
+		}
+	}
+	
 	private void checkRoot() {
 		if (isRoot()) {
 			throw new SubsystemException("This operation may not be performed on the root subsystem");
@@ -817,6 +838,24 @@ public class AriesSubsystem implements S
 		return region;
 	}
 	
+	private Region findRootRegion() {
+		return findRootSubsystem().region;
+	}
+	
+	private AriesSubsystem findRootSubsystem() {
+		AriesSubsystem root = this;
+		while (!root.isRoot())
+			root = ((AriesSubsystem)root.getParents().iterator().next());
+		return root;
+	}
+	
+	private AriesSubsystem findScopedSubsystemInRegion() {
+		AriesSubsystem result = this;
+		while (!result.isScoped())
+			result = (AriesSubsystem)result.getParents().iterator().next();
+		return result;
+	}
+	
 	private DeploymentManifest getDeploymentManifest() throws IOException, URISyntaxException {
 //		if (archive.getDeploymentManifest() == null) {
 			archive.setDeploymentManifest(new DeploymentManifest(
@@ -834,10 +873,10 @@ public class AriesSubsystem implements S
 		return archive.getDeploymentManifest();
 	}
 	
-	private synchronized void install(Coordination coordination) throws Exception {
+	private synchronized void install(Coordination coordination, AriesSubsystem parent) throws Exception {
 		if (!isFeature())
 			RegionContextBundleHelper.installRegionContextBundle(this);
-		Activator.getInstance().getSubsystemServiceRegistrar().register(this);
+		Activator.getInstance().getSubsystemServiceRegistrar().register(this, parent);
 		Set<Resource> contentResources = new TreeSet<Resource>(
 				new Comparator<Resource>() {
 					@Override
@@ -917,6 +956,7 @@ public class AriesSubsystem implements S
 						&& subsystem.getVersion().equals(ssr.getSubsystemManifest().getSubsystemVersionHeader().getVersion())
 						&& subsystem.getType().equals(ssr.getSubsystemManifest().getSubsystemTypeHeader().getType())))
 					throw new SubsystemException("Location already exists but symbolic name, version, and type are not the same: " + location);
+				subsystemInstalling(subsystem);
 				subsystemInstalled(subsystem);
 				return subsystem;
 			}
@@ -926,6 +966,7 @@ public class AriesSubsystem implements S
 			if (subsystem != null) {
 				if (!subsystem.getType().equals(ssr.getSubsystemManifest().getSubsystemTypeHeader().getType()))
 					throw new SubsystemException("Subsystem already exists in target region but has a different type: " + location);
+				subsystemInstalling(subsystem);
 				subsystemInstalled(subsystem);
 				return subsystem;
 			}
@@ -1050,7 +1091,7 @@ public class AriesSubsystem implements S
 		// before the child. This results in the child (i.e. this subsystem) being uninstalled as part
 		// of that process, but its state has not moved from INSTALLING to INSTALL_FAILED, which results
 		// in an eternal wait for a state change.
-		subsystemInstalled(subsystem);
+		subsystemInstalling(subsystem);
 		coordination.addParticipant(new Participant() {
 			public void ended(Coordination coordination) throws Exception {
 				// noop
@@ -1062,7 +1103,8 @@ public class AriesSubsystem implements S
 				subsystemUninstalled(subsystem);
 			}
 		});
-		subsystem.install(coordination);
+		subsystem.install(coordination, this);
+		subsystemInstalled(subsystem);
 		return subsystem;
 	}
 
@@ -1070,9 +1112,15 @@ public class AriesSubsystem implements S
 		return ROOT_LOCATION.equals(getLocation());
 	}
 	
+	private boolean isScoped() {
+		return isApplication() || isComposite();
+	}
+	
 	private void resolve() {
 		setState(State.RESOLVING);
 		try {
+			for (Subsystem child : subsystemGraph.getChildren(this))
+				((AriesSubsystem)child).resolve();
 			// TODO I think this is insufficient. Do we need both
 			// pre-install and post-install environments for the Resolver?
 			Collection<Bundle> bundles = getBundles();
@@ -1166,15 +1214,12 @@ public class AriesSubsystem implements S
 	}
 
 	private void setImportIsolationPolicy() throws BundleException, IOException, InvalidSyntaxException, URISyntaxException {
-		if (isRoot())
-			// Nothing to do if this is the root subsystem.
-			return;
-		if (isFeature())
-			// Features share the same isolation as that of their scoped parent.
+		if (isRoot() || isFeature())
 			return;
 		Region from = region;
-		Region to = ((AriesSubsystem)getParents().iterator().next()).region;
 		RegionFilterBuilder builder = from.getRegionDigraph().createRegionFilterBuilder();
+		Region to = ((AriesSubsystem)getParents().iterator().next()).region;
+		addSubsystemServiceImportToSharingPolicy(builder, to);
 		if (isApplication() || isComposite()) {
 			// Both applications and composites have Import-Package headers that require processing.
 			// In the case of applications, the header is generated.
@@ -1330,6 +1375,10 @@ public class AriesSubsystem implements S
 	}
 	
 	private synchronized void subsystemInstalled(AriesSubsystem subsystem) {
+		Activator.getInstance().getSubsystemServiceRegistrar().addRegion(subsystem, region);
+	}
+	
+	private synchronized void subsystemInstalling(AriesSubsystem subsystem) {
 		locationToSubsystem.put(subsystem.getLocation(), subsystem);
 		subsystemGraph.add(this, subsystem);
 		addResourceToSubsystem(subsystem, this);
@@ -1337,6 +1386,7 @@ public class AriesSubsystem implements S
 	}
 	
 	private synchronized void subsystemUninstalled(AriesSubsystem subsystem) {
+		Activator.getInstance().getSubsystemServiceRegistrar().removeRegion(subsystem, region);
 		constituents.remove(subsystem);
 		removeResourceToSubsystem(subsystem, this);
 		subsystemGraph.remove(subsystem);
@@ -1345,7 +1395,8 @@ public class AriesSubsystem implements S
 	
 	private void uninstall(boolean changeState) {
 		if (changeState)
-			setState(State.UNINSTALLING);
+			setState(State.INSTALLED);
+		setState(State.UNINSTALLING);
 		// Uninstall child subsystems first.
 		for (Subsystem subsystem : getChildren()) {
 			try {
@@ -1378,8 +1429,7 @@ public class AriesSubsystem implements S
 		subsystemGraph.remove(this);
 		locationToSubsystem.remove(location);
 		deleteFile(directory);
-		if (changeState)
-			setState(State.UNINSTALLED);
+		setState(State.UNINSTALLED);
 		Activator.getInstance().getSubsystemServiceRegistrar().unregister(this);
 		if (!isFeature())
 			RegionContextBundleHelper.uninstallRegionContextBundle(this);

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/Constants.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/Constants.java?rev=1311274&r1=1311273&r2=1311274&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/Constants.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/Constants.java Mon Apr  9 15:04:13 2012
@@ -4,6 +4,7 @@ public class Constants {
 	public static final String BundleSymbolicName = org.osgi.framework.Constants.BUNDLE_SYMBOLICNAME;
 	public static final String BundleVersion = org.osgi.framework.Constants.BUNDLE_VERSION;
 	public static final String RegionContextBundleSymbolicNamePrefix = "org.osgi.service.subsystem.region.context.";
+	public static final String SubsystemServicePropertyRegions = "org.apache.aries.subsystem.service.regions";
 	
 	private Constants() {}
 }

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemGraph.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemGraph.java?rev=1311274&r1=1311273&r2=1311274&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemGraph.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemGraph.java Mon Apr  9 15:04:13 2012
@@ -26,9 +26,7 @@ public class SubsystemGraph {
 			if (!(o instanceof SubsystemWrapper))
 				return false;
 			SubsystemWrapper that = (SubsystemWrapper)o;
-			return s.getSymbolicName().equals(that.s.getSymbolicName())
-					&& s.getVersion().equals(that.s.getVersion())
-					&& s.getType().equals(that.s.getType());
+			return s.getLocation().equals(that.s.getLocation());
 		}
 		
 		public Subsystem getSubsystem() {
@@ -37,28 +35,24 @@ public class SubsystemGraph {
 		
 		@Override
 		public int hashCode() {
-			int result = 17;
-			result = result + 31 * s.getSymbolicName().hashCode();
-			result = result + 31 * s.getVersion().hashCode();
-			result = result + 31 * s.getType().hashCode();
-			return result;
+			return s.getLocation().hashCode();
 		}
 		
 		@Override
 		public String toString() {
-			return new StringBuilder().append(s.getClass().getName())
-					.append(": symbolicName=").append(s.getSymbolicName())
+			return new StringBuilder("location=").append(s.getLocation())
+					.append(", symbolicName=").append(s.getSymbolicName())
 					.append(", version=").append(s.getVersion())
 					.append(", type=").append(s.getType()).toString();
 		}
 	}
 	private final Map<SubsystemWrapper, Collection<SubsystemWrapper>> adjacencyList = new HashMap<SubsystemWrapper, Collection<SubsystemWrapper>>();
 	
-	public SubsystemGraph(Subsystem root) {
+	public SubsystemGraph(AriesSubsystem root) {
 		adjacencyList.put(new SubsystemWrapper(root), new HashSet<SubsystemWrapper>());
 	}
 	
-	public synchronized void add(Subsystem parent, Subsystem child) {
+	public synchronized void add(AriesSubsystem parent, AriesSubsystem child) {
 		SubsystemWrapper parentWrap = new SubsystemWrapper(parent);
 		SubsystemWrapper childWrap = new SubsystemWrapper(child);
 		if (containsAncestor(childWrap, parentWrap))
@@ -76,7 +70,7 @@ public class SubsystemGraph {
 		subsystems.add(childWrap);
 	}
 	
-	public synchronized Collection<Subsystem> getChildren(Subsystem parent) {
+	public synchronized Collection<Subsystem> getChildren(AriesSubsystem parent) {
 		Collection<SubsystemWrapper> children = adjacencyList.get(new SubsystemWrapper(parent));
 		if (children == null || children.isEmpty())
 			return Collections.emptySet();
@@ -86,7 +80,7 @@ public class SubsystemGraph {
  		return Collections.unmodifiableCollection(result);
 	}
 	
-	public synchronized Collection<Subsystem> getParents(Subsystem child) {
+	public synchronized Collection<Subsystem> getParents(AriesSubsystem child) {
 		Collection<SubsystemWrapper> parents = getParents(new SubsystemWrapper(child));
 		Collection<Subsystem> result = new ArrayList<Subsystem>(parents.size());
 		for (SubsystemWrapper parent : parents) {
@@ -95,7 +89,7 @@ public class SubsystemGraph {
 		return Collections.unmodifiableCollection(result);
 	}
 	
-	public synchronized void remove(Subsystem subsystem) {
+	public synchronized void remove(AriesSubsystem subsystem) {
 		SubsystemWrapper subsystemWrap = new SubsystemWrapper(subsystem);
 		Collection<SubsystemWrapper> parents = getParents(subsystemWrap);
 		for (SubsystemWrapper parent : parents)

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemManifestValidator.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemManifestValidator.java?rev=1311274&r1=1311273&r2=1311274&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemManifestValidator.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemManifestValidator.java Mon Apr  9 15:04:13 2012
@@ -3,6 +3,7 @@ package org.apache.aries.subsystem.core.
 import org.apache.aries.subsystem.core.archive.SubsystemContentHeader;
 import org.apache.aries.subsystem.core.archive.SubsystemManifest;
 import org.osgi.framework.VersionRange;
+import org.osgi.service.subsystem.SubsystemConstants;
 import org.osgi.service.subsystem.SubsystemException;
 
 public class SubsystemManifestValidator {
@@ -18,9 +19,10 @@ public class SubsystemManifestValidator 
 			}
 		}
 		else if (subsystem.isFeature()) {
-			if (manifest.getSubsystemTypeHeader().getProvisionPolicyDirective().isAcceptDependencies()) {
+			if (manifest.getSubsystemTypeHeader().getProvisionPolicyDirective().isAcceptDependencies())
 				throw new SubsystemException("Feature subsystems may not declare a provision-policy of acceptDependencies");
-			}
+			if (manifest.getHeaders().get(SubsystemConstants.PREFERRED_PROVIDER) != null)
+				throw new SubsystemException("Feature subsystems may not declare a " + SubsystemConstants.PREFERRED_PROVIDER + " header");
 		}
 	}
 	

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemServiceRegistrar.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemServiceRegistrar.java?rev=1311274&r1=1311273&r2=1311274&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemServiceRegistrar.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemServiceRegistrar.java Mon Apr  9 15:04:13 2012
@@ -1,11 +1,15 @@
 package org.apache.aries.subsystem.core.internal;
 
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Dictionary;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.Map;
 
+import org.eclipse.equinox.region.Region;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.subsystem.Subsystem;
@@ -21,12 +25,43 @@ public class SubsystemServiceRegistrar {
 		this.context = context;
 	}
 	
-	public synchronized void register(Subsystem subsystem) {
-		if (map.containsKey(subsystem))
-			throw new IllegalStateException("Subsystem '" + subsystem + "' already has service registration '" + map.get(subsystem) + "'");
+	public synchronized void addRegion(AriesSubsystem subsystem, Region region) {
+		ServiceRegistration<Subsystem> registration = map.get(subsystem);
+		if (registration == null)
+			throw new IllegalStateException("Subsystem '" + subsystem + "' is not registered");
+		Collection<String> currentRegions = (Collection<String>)registration.getReference().getProperty(Constants.SubsystemServicePropertyRegions);
+		String regionName = region.getName();
+		if (currentRegions.contains(regionName))
+			return;
+		Collection<String> newRegions = new HashSet<String>(currentRegions.size() + 1);
+		newRegions.addAll(currentRegions);
+		newRegions.add(regionName);
 		Dictionary<String, Object> properties = properties(subsystem);
-		ServiceRegistration<Subsystem> registration = context.registerService(Subsystem.class, subsystem, properties);
-		map.put(subsystem, registration);
+		properties.put(Constants.SubsystemServicePropertyRegions, Collections.unmodifiableCollection(newRegions));
+		registration.setProperties(properties);
+	}
+	
+	public synchronized void register(AriesSubsystem child, AriesSubsystem parent) {
+		if (map.containsKey(child))
+			throw new IllegalStateException("Subsystem '" + child + "' already has service registration '" + map.get(child) + "'");
+		Dictionary<String, Object> properties = properties(child, parent);
+		ServiceRegistration<Subsystem> registration = context.registerService(Subsystem.class, child, properties);
+		map.put(child, registration);
+	}
+	
+	public synchronized void removeRegion(AriesSubsystem subsystem, Region region) {
+		ServiceRegistration<Subsystem> registration = map.get(subsystem);
+		if (registration == null)
+			return;
+		Collection<String> regions = (Collection<String>)registration.getReference().getProperty(Constants.SubsystemServicePropertyRegions);
+		String regionName = region.getName();
+		if (regions == null || !regions.contains(regionName))
+			return;
+		regions = new HashSet<String>(regions);
+		regions.remove(regionName);
+		Dictionary<String, Object> properties = properties(subsystem);
+		properties.put(Constants.SubsystemServicePropertyRegions, Collections.unmodifiableCollection(regions));
+		registration.setProperties(properties);
 	}
 	
 	public synchronized void unregister(Subsystem subsystem) {
@@ -44,21 +79,43 @@ public class SubsystemServiceRegistrar {
 		}
 	}
 	
-	public synchronized void update(Subsystem subsystem) {
+	public synchronized void update(AriesSubsystem subsystem) {
 		ServiceRegistration<Subsystem> registration = map.get(subsystem);
 		if (registration == null)
 			throw new IllegalStateException("Subsystem '" + subsystem + "' is not registered");
-		Dictionary<String, Object> properties = properties(subsystem);
+		Dictionary<String, Object> properties = properties(subsystem, registration);
 		registration.setProperties(properties);
 	}
 	
-	private Dictionary<String, Object> properties(Subsystem subsystem) {
-		Dictionary<String, Object> properties = new Hashtable<String, Object>();
-		properties.put(SubsystemConstants.SUBSYSTEM_ID_PROPERTY, subsystem.getSubsystemId());
-		properties.put(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME_PROPERTY, subsystem.getSymbolicName());
-		properties.put(SubsystemConstants.SUBSYSTEM_VERSION_PROPERTY, subsystem.getVersion());
-		properties.put(SubsystemConstants.SUBSYSTEM_TYPE_PROPERTY, subsystem.getType());
-		properties.put(SubsystemConstants.SUBSYSTEM_STATE_PROPERTY, subsystem.getState());
-		return properties;
+	private Dictionary<String, Object> properties(AriesSubsystem subsystem) {
+		Dictionary<String, Object> result = new Hashtable<String, Object>();
+		result.put(SubsystemConstants.SUBSYSTEM_ID_PROPERTY, subsystem.getSubsystemId());
+		result.put(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME_PROPERTY, subsystem.getSymbolicName());
+		result.put(SubsystemConstants.SUBSYSTEM_VERSION_PROPERTY, subsystem.getVersion());
+		result.put(SubsystemConstants.SUBSYSTEM_TYPE_PROPERTY, subsystem.getType());
+		result.put(SubsystemConstants.SUBSYSTEM_STATE_PROPERTY, subsystem.getState());
+		result.put(Constants.SubsystemServicePropertyRegions, Collections.singleton(subsystem.getRegion().getName()));
+		return result;
+	}
+	
+	private Dictionary<String, Object> properties(AriesSubsystem child, AriesSubsystem parent) {
+		Dictionary<String, Object> result = properties(child);
+		if (parent == null)
+			return result;
+		Collection<String> currentRegions = (Collection<String>)result.get(Constants.SubsystemServicePropertyRegions);
+		Collection<String> newRegions = new HashSet<String>(currentRegions.size() + 1);
+		newRegions.addAll(currentRegions);
+		newRegions.add(parent.getRegion().getName());
+		result.put(Constants.SubsystemServicePropertyRegions, Collections.unmodifiableCollection(newRegions));
+		return result;
+	}
+	
+	private Dictionary<String, Object> properties(AriesSubsystem subsystem, ServiceRegistration<Subsystem> registration) {
+		Dictionary<String, Object> result = properties(subsystem);
+		Collection<String> regions = (Collection<String>)registration.getReference().getProperty(Constants.SubsystemServicePropertyRegions);
+		if (regions == null)
+			return result;
+		result.put(Constants.SubsystemServicePropertyRegions, regions);
+		return result;
 	}
 }

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/TargetRegion.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/TargetRegion.java?rev=1311274&r1=1311273&r2=1311274&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/TargetRegion.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/TargetRegion.java Mon Apr  9 15:04:13 2012
@@ -5,14 +5,22 @@ import java.util.HashSet;
 
 import org.osgi.framework.Version;
 import org.osgi.service.subsystem.Subsystem;
-import org.osgi.service.subsystem.SubsystemConstants;
 
 public class TargetRegion {
 	Collection<Subsystem> region = new HashSet<Subsystem>();
 
-	public TargetRegion(AriesSubsystem target) {
-		region.add(target);
-		addToRegion(target.getChildren());
+	public TargetRegion(AriesSubsystem subsystem) {
+		// Find the scoped subsystem that controls the region.
+		AriesSubsystem controllingScopedSubsystem = subsystem;
+		while (controllingScopedSubsystem.isFeature())
+			controllingScopedSubsystem = (AriesSubsystem)subsystem.getParents().iterator().next();
+		// The scoped subsystem controlling the region is part of the region.
+		region.add(controllingScopedSubsystem);
+		// All children of the scoped subsystem are part of the region. If the
+		// child is a feature, then all descendants of the child that are
+		// features and part of an unbroken line of features are part of the
+		// region.
+		addChildrenToRegion(controllingScopedSubsystem);
 	}
 
 	public boolean contains(Subsystem subsystem) {
@@ -32,15 +40,25 @@ public class TargetRegion {
 		}
 		return null;
 	}
-
-	private void addToRegion(Collection<Subsystem> children) {
-		for (Subsystem child : children) {
-			if (SubsystemConstants.SUBSYSTEM_TYPE_FEATURE.equals(child
-					.getSubsystemHeaders(null).get(
-							SubsystemConstants.SUBSYSTEM_TYPE))) {
-				addToRegion(child.getChildren());
-			}
+	
+	private void addChildrenToRegion(AriesSubsystem controllingScopedSubsystem) {
+		for (Subsystem child : controllingScopedSubsystem.getChildren()) {
 			region.add(child);
+			// If the child is a feature, all of its children that are features
+			// must be added as well.
+			if (((AriesSubsystem)child).isFeature())
+				addFeatureDescendentsToRegion((AriesSubsystem)child);
 		}
 	}
+	
+	private void addFeatureDescendentsToRegion(AriesSubsystem parent) {
+		for (Subsystem child : parent.getChildren())
+			// If the descendant is not a feature, skip it.
+			if (((AriesSubsystem)child).isFeature()) {
+				region.add(child);
+				// All descendants that are features and part of an unbroken
+				// line of features must be added.
+				addFeatureDescendentsToRegion((AriesSubsystem)child);
+			}
+	}
 }

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/tmp/SubsystemResource.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/tmp/SubsystemResource.java?rev=1311274&r1=1311273&r2=1311274&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/tmp/SubsystemResource.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/tmp/SubsystemResource.java Mon Apr  9 15:04:13 2012
@@ -22,6 +22,7 @@ import org.apache.aries.subsystem.core.a
 import org.apache.aries.subsystem.core.archive.SubsystemManifest;
 import org.apache.aries.subsystem.core.archive.SubsystemSymbolicNameHeader;
 import org.apache.aries.subsystem.core.archive.SubsystemTypeHeader;
+import org.apache.aries.subsystem.core.archive.SubsystemVersionHeader;
 import org.apache.aries.subsystem.core.internal.Activator;
 import org.apache.aries.subsystem.core.internal.SubsystemUri;
 import org.apache.aries.subsystem.core.resource.BundleResource;
@@ -44,6 +45,7 @@ import org.osgi.service.subsystem.Subsys
 public abstract class SubsystemResource implements Resource {
 	protected static class Location {
 		private final String symbolicName;
+		private final URL url;
 		private final String value;
 		private final Version version;
 		
@@ -53,6 +55,7 @@ public abstract class SubsystemResource 
 			if (location.startsWith("subsystem://"))
 				uri = new SubsystemUri(location);
 			symbolicName = uri == null ? null : uri.getSymbolicName();
+			url = uri == null ? null : uri.getURL();
 			version = uri == null ? null : uri.getVersion();
 		}
 		
@@ -67,23 +70,28 @@ public abstract class SubsystemResource 
 		public Version getVersion() {
 			return version;
 		}
+		
+		public InputStream open() throws IOException {
+			return url == null ? new URL(value).openStream() : url.openStream();
+		}
 	}
 	
 	protected static final Pattern PATTERN = Pattern.compile("([^@]+)(?:@(.+))?.esa");
 	
 	public static SubsystemResource newInstance(String location, InputStream content) throws IOException, URISyntaxException {
+		Location loc = new Location(location);
 		if (content == null)
-			content = new URL(location).openStream();
+			content = loc.open();
 		IDirectory directory = FileSystem.getFSRoot(content);
 		SubsystemManifest manifest = computeSubsystemManifest(directory);
 		String type = manifest.getSubsystemTypeHeader().getType();
 		// TODO Make an enum out of the types?
 		if (SubsystemTypeHeader.TYPE_APPLICATION.equals(type))
-			return new ApplicationResource(new Location(location), directory, manifest);
+			return new ApplicationResource(loc, directory, manifest);
 		if (SubsystemTypeHeader.TYPE_COMPOSITE.equals(type))
-			return new CompositeResource(new Location(location), directory, manifest);
+			return new CompositeResource(loc, directory, manifest);
 		if (SubsystemTypeHeader.TYPE_FEATURE.equals(type))
-			return new FeatureResource(new Location(location), directory, manifest);
+			return new FeatureResource(loc, directory, manifest);
 		throw new SubsystemException("Unsupported subsystem type: " + type);
 	}
 	
@@ -209,6 +217,10 @@ public abstract class SubsystemResource 
 		addHeader(builder, computeSubsystemSymbolicNameHeader(manifest));
 	}
 	
+	protected void addSubsystemVersionHeader(SubsystemManifest.Builder builder, SubsystemManifest manifest) {
+		addHeader(builder, computeSubsystemVersionHeader(manifest));
+	}
+	
 	protected List<Capability> computeCapabilities() {
 		return subsystemManifest.toCapabilities(this);
 	}
@@ -322,14 +334,22 @@ public abstract class SubsystemResource 
 	protected SubsystemManifest computeSubsystemManifestBeforeRequirements(SubsystemManifest manifest) {
 		SubsystemManifest.Builder builder = new SubsystemManifest.Builder().manifest(manifest);
 		addSubsystemSymbolicNameHeader(builder, manifest);
+		addSubsystemVersionHeader(builder, manifest);
 		addSubsystemContentHeader(builder, manifest);
 		return builder.build();
 	}
 	
 	protected SubsystemSymbolicNameHeader computeSubsystemSymbolicNameHeader(SubsystemManifest manifest) {
-		Header<?> header = manifest.getSubsystemSymbolicNameHeader();
+		SubsystemSymbolicNameHeader header = manifest.getSubsystemSymbolicNameHeader();
 		if (header == null)
 			header = new SubsystemSymbolicNameHeader(location.getSymbolicName());
-		return (SubsystemSymbolicNameHeader)header;
+		return header;
+	}
+	
+	protected SubsystemVersionHeader computeSubsystemVersionHeader(SubsystemManifest manifest) {
+		SubsystemVersionHeader header = manifest.getSubsystemVersionHeader();
+		if (header.getVersion().equals(Version.emptyVersion) && location.getVersion() != null)
+			header = new SubsystemVersionHeader(location.getVersion());
+		return header;
 	}
 }

Modified: aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/FeatureTest.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/FeatureTest.java?rev=1311274&r1=1311273&r2=1311274&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/FeatureTest.java (original)
+++ aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/FeatureTest.java Mon Apr  9 15:04:13 2012
@@ -114,6 +114,7 @@ public class FeatureTest extends Subsyst
 			try {
 				uninstallSubsystem(feature1);
 				if (feature2 != null) {
+					assertEvent(feature2, Subsystem.State.INSTALLED, 5000);
 					assertEvent(feature2, Subsystem.State.UNINSTALLING, 5000);
 					assertEvent(feature2, Subsystem.State.UNINSTALLED, 5000);
 					assertNotChild(feature1, feature2);

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=1311274&r1=1311273&r2=1311274&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 Mon Apr  9 15:04:13 2012
@@ -707,6 +707,7 @@ public abstract class SubsystemTest exte
 				|| subsystem.getType().equals(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE))
 			b = getRegionContextBundle(subsystem);
 		subsystem.uninstall();
+		assertEvent(subsystem, State.INSTALLED, 5000);
 		assertEvent(subsystem, State.UNINSTALLING, 5000);
 		assertEvent(subsystem, State.UNINSTALLED, 5000);
 		assertState(State.UNINSTALLED, subsystem);