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/02/15 21:22:11 UTC

svn commit: r1244685 - in /aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core: archive/ internal/ obr/ resource/

Author: jwross
Date: Wed Feb 15 20:22:10 2012
New Revision: 1244685

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

(1) sort deployed content so that bundles are favored and, consequently, installed before any child subsystems; this allows the child subsystem resolution to detect already installed resources that fulfill requirements where appropriate
(2) getChildren must return an unmodifiable snapshot
(3) getConstituents must return an unmodifiable snapshot
(4) don't uninstall the region context bundle while uninstalling other constituents
(5) don't fail subsystem install if optional resource is not found
(6) support for subsystem resources that implement RepositoryContent
(7) subsystem uri now supports missing symbolic name parameter
(8) SubsystemFileResource now uses subsystem URI as the location
(9) subsystem install now fails if Subsystem-ManifestVersion != 1.0
(10) Created new TargetRegion class as a, possibly temporary, utility for calculating certain install failures.
(11) Now fails install if subsystem with same location already exists but is not in target region.
(12) Now fails install if subsystem with same location already exists but does not have same symbolic name, version, and type.
(13) Removed obsolete DataFile and StaticDataFile classes.
(14) Added subsystem-type attribute to osgi.identity capability for internal reasons.
(15) Now fails install if subsystem already exists in target region but has different type.

Added:
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/TargetRegion.java
Removed:
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/DataFile.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/StaticDataFile.java
Modified:
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeploymentManifest.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/HeaderFactory.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemArchive.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemManifestVersionHeader.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/VersionHeader.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/OsgiIdentityCapability.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemUri.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/obr/SubsystemEnvironment.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/SubsystemDirectoryResource.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/SubsystemFileResource.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/SubsystemStreamResource.java

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeploymentManifest.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeploymentManifest.java?rev=1244685&r1=1244684&r2=1244685&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeploymentManifest.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeploymentManifest.java Wed Feb 15 20:22:10 2012
@@ -90,9 +90,11 @@ public class DeploymentManifest {
 					OsgiIdentityRequirement requirement = new OsgiIdentityRequirement(content.getName(), content.getVersionRange(), content.getType(), false);
 					Resource resource = environment.findResource(requirement);
 					// If the resource is null, can't continue.
-					// TODO Actually, can continue if resource is optional.
-					if (resource == null)
-						throw new SubsystemException("Resource does not exist: " + requirement);
+					if (resource == null) {
+						if (content.isMandatory())
+							throw new SubsystemException("Resource does not exist: " + requirement);
+						continue;
+					}
 					resources.add(resource);
 				}
 				// TODO This does not validate that all content bundles were found.

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/HeaderFactory.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/HeaderFactory.java?rev=1244685&r1=1244684&r2=1244685&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/HeaderFactory.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/HeaderFactory.java Wed Feb 15 20:22:10 2012
@@ -94,6 +94,8 @@ public class HeaderFactory {
 			return new DeployedContentHeader(value);
 		if (ProvisionResourceHeader.NAME.equals(name))
 			return new ProvisionResourceHeader(value);
+		if (SubsystemManifestVersionHeader.NAME.equals(name))
+			return new SubsystemManifestVersionHeader(value);
 		return new GenericHeader(name, value);
 			
 	}

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemArchive.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemArchive.java?rev=1244685&r1=1244684&r2=1244685&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemArchive.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemArchive.java Wed Feb 15 20:22:10 2012
@@ -14,8 +14,6 @@ import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.aries.subsystem.core.ResourceHelper;
-import org.apache.aries.subsystem.core.internal.DataFile;
-import org.apache.aries.subsystem.core.internal.StaticDataFile;
 import org.apache.aries.subsystem.core.resource.BundleResource;
 import org.apache.aries.subsystem.core.resource.SubsystemDirectoryResource;
 import org.apache.aries.subsystem.core.resource.SubsystemFileResource;
@@ -140,9 +138,5 @@ public class SubsystemArchive implements
 		}
 		else if (name.endsWith(".ssa") && !name.startsWith("subsystem"))
 			resources.put(new SubsystemFileResource(file), file.toURI().toURL());
-		else if (name.endsWith(DataFile.IDENTITY_TYPE))
-			resources.put(new DataFile(file), file.toURI().toURL());
-		else if (name.endsWith(StaticDataFile.IDENTITY_TYPE))
-			resources.put(new StaticDataFile(file), file.toURI().toURL());
 	}
 }

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemManifestVersionHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemManifestVersionHeader.java?rev=1244685&r1=1244684&r2=1244685&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemManifestVersionHeader.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemManifestVersionHeader.java Wed Feb 15 20:22:10 2012
@@ -13,17 +13,26 @@
  */
 package org.apache.aries.subsystem.core.archive;
 
+import org.osgi.framework.Version;
 import org.osgi.service.subsystem.SubsystemConstants;
 
 public class SubsystemManifestVersionHeader extends VersionHeader {
-	public static final String DEFAULT_VALUE = "1.0";
+	public static final Version DEFAULT_VALUE = Version.parseVersion("1.0");
 	public static final String NAME = SubsystemConstants.SUBSYSTEM_MANIFESTVERSION;
 	
+	public static final SubsystemManifestVersionHeader DEFAULT = new SubsystemManifestVersionHeader();
+	
 	public SubsystemManifestVersionHeader() {
 		this(DEFAULT_VALUE);
 	}
 
 	public SubsystemManifestVersionHeader(String value) {
-		super(NAME, value);
+		this(Version.parseVersion(value));
+	}
+	
+	public SubsystemManifestVersionHeader(Version version) {
+		super(NAME, version);
+		if (!version.equals(DEFAULT_VALUE))
+			throw new IllegalArgumentException(NAME + " must be " + DEFAULT_VALUE);
 	}
 }

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/VersionHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/VersionHeader.java?rev=1244685&r1=1244684&r2=1244685&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/VersionHeader.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/VersionHeader.java Wed Feb 15 20:22:10 2012
@@ -16,7 +16,7 @@ package org.apache.aries.subsystem.core.
 import org.osgi.framework.Version;
 
 public abstract class VersionHeader extends AbstractHeader {
-	private final Version version;
+	protected final Version version;
 	
 	public VersionHeader(String name, String value) {
 		this(name, Version.parseVersion(value));

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=1244685&r1=1244684&r2=1244685&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 Wed Feb 15 20:22:10 2012
@@ -29,15 +29,16 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.TreeSet;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 
@@ -53,6 +54,8 @@ import org.apache.aries.subsystem.core.a
 import org.apache.aries.subsystem.core.obr.SubsystemEnvironment;
 import org.apache.aries.subsystem.core.resource.SubsystemDirectoryResource;
 import org.apache.aries.subsystem.core.resource.SubsystemFileResource;
+import org.apache.aries.subsystem.core.resource.SubsystemStreamResource;
+import org.apache.aries.util.io.IOUtils;
 import org.eclipse.equinox.region.Region;
 import org.eclipse.equinox.region.RegionDigraph;
 import org.eclipse.equinox.region.RegionFilter;
@@ -209,7 +212,7 @@ public class AriesSubsystem implements S
 	private final SubsystemEnvironment environment;
 	private final long id;
 	private final String location;
-	private final ArrayList<AriesSubsystem> parents = new ArrayList<AriesSubsystem>();
+	private final Set<AriesSubsystem> parents = Collections.synchronizedSet(new HashSet<AriesSubsystem>());
 	private final Region region;
 	
 	private boolean autostart;
@@ -304,29 +307,28 @@ public class AriesSubsystem implements S
 			copyContent(content, zipFile);
 			unzipContent(zipFile, directory);
 			archive = new SubsystemArchive(directory);
+			environment = new SubsystemEnvironment(this);
+			// Make sure the relevant headers are derived, if absent.
+			archive.setSubsystemManifest(new SubsystemManifest(
+					archive.getSubsystemManifest(),
+					uri == null ? null : uri.getSymbolicName(), 
+					uri == null ? null : uri.getVersion(), 
+					archive.getResources()));
+			// Unscoped subsystems don't get their own region. They share the region with their scoped parent.
+			if (isFeature())
+				region = parent.region;
+			else
+				region = createRegion(getSymbolicName() + ';' + getVersion() + ';' + getType() + ';' + getSubsystemId());
 		}
 		catch (Exception e) {
 			deleteFile(directory);
 			deleteFile(zipFile);
 			throw new SubsystemException(e);
 		}
-		environment = new SubsystemEnvironment(this);
-		// Make sure the relevant headers are derived, if absent.
-		archive.setSubsystemManifest(new SubsystemManifest(
-				archive.getSubsystemManifest(),
-				uri == null ? null : uri.getSymbolicName(), 
-				uri == null ? null : uri.getVersion(), 
-				archive.getResources()));
-		// Unscoped subsystems don't get their own region. They share the region with their scoped parent.
-		if (isFeature())
-			region = parents.get(0).region;
-		else
-			region = createRegion(getSymbolicName() + ';' + getVersion() + ';' + getType() + ';' + getSubsystemId());
 	}
 	
 	public AriesSubsystem(SubsystemArchive archive, AriesSubsystem parent) throws Exception {
 		this.archive = archive;
-		
 		DeploymentManifest manifest = archive.getDeploymentManifest();
 		if (manifest == null)
 			throw new IllegalStateException("Missing deployment manifest");
@@ -339,7 +341,7 @@ public class AriesSubsystem implements S
 		parents.add(parent);
 		// Unscoped subsystems don't get their own region. They share the region with their scoped parent.
 		if (isFeature())
-			region = parents.get(0).region;
+			region = parent.region;
 		else
 			region = createRegion(getSymbolicName() + ';' + getVersion() + ';' + getType() + ';' + getSubsystemId());
 	}
@@ -352,28 +354,38 @@ public class AriesSubsystem implements S
 	public BundleContext getBundleContext() {
 		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())
-			return parents.get(0).getBundleContext();
-		return region.getBundle(RegionContextBundleHelper.SYMBOLICNAME_PREFIX + id, RegionContextBundleHelper.VERSION).getBundleContext();
+		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();
 	}
 	
 	@Override
 	public List<Capability> getCapabilities(String namespace) {
-		// TODO Need to filter by namespace.
-		Capability capability = new OsgiIdentityCapability(this, getSymbolicName(), getVersion(), "osgi.subsystem");
-		return Arrays.asList(new Capability[]{capability});
+		if (namespace == null || namespace.equals(ResourceConstants.IDENTITY_NAMESPACE)) {
+			Capability capability = new OsgiIdentityCapability(this, getSymbolicName(), getVersion(), SubsystemConstants.IDENTITY_TYPE_SUBSYSTEM, getType());
+			return Arrays.asList(new Capability[]{capability});
+		}
+		return Collections.emptyList();
 	}
 
 	@SuppressWarnings({ "unchecked", "rawtypes" })
 	public Collection<Subsystem> getChildren() {
-		return (Collection<Subsystem>)(Collection)Collections.unmodifiableSet(children);
+		return (Collection<Subsystem>)(Collection)Collections.unmodifiableCollection(new ArrayList<Subsystem>(children));
 	}
 
 	@Override
 	public synchronized Collection<Resource> getConstituents() {
-		return Collections.unmodifiableCollection(constituents);
+		return Collections.unmodifiableCollection(new ArrayList<Resource>(constituents));
 	}
 
 	@Override
@@ -414,23 +426,17 @@ public class AriesSubsystem implements S
 
 	@Override
 	public String getSymbolicName() {
-		return archive.getSubsystemManifest().getHeaders().get(SubsystemManifest.SUBSYSTEM_SYMBOLICNAME).getValue();
+		return archive.getSubsystemManifest().getSubsystemSymbolicNameHeader().getSymbolicName();
 	}
 	
 	@Override
 	public String getType() {
-		String result = getSubsystemHeaders(null).get(SubsystemConstants.SUBSYSTEM_TYPE);
-		if (result == null)
-			result = SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION;
-		return result;
+		return archive.getSubsystemManifest().getSubsystemTypeHeader().getValue();
 	}
 
 	@Override
 	public Version getVersion() {
-		String version = getSubsystemHeaders(null).get(SubsystemConstants.SUBSYSTEM_VERSION);
-		if (version == null)
-			return Version.emptyVersion;
-		return Version.parseVersion(version);
+		return archive.getSubsystemManifest().getSubsystemVersionHeader().getVersion();
 	}
 
 	@Override
@@ -440,11 +446,31 @@ public class AriesSubsystem implements S
 	
 	@Override
 	public Subsystem install(String location, InputStream content) throws SubsystemException {
+		SubsystemStreamResource ssr = null;
 		try {
+			TargetRegion region = new TargetRegion(this);
+			ssr = new SubsystemStreamResource(location, content);
 			AriesSubsystem subsystem = locationToSubsystem.get(location);
-			if (subsystem != null)
+			if (subsystem != null) {
+				if (!region.contains(subsystem))
+					throw new SubsystemException("Location already exists but existing subsystem is not part of target region: " + location);
+				if (!(subsystem.getSymbolicName().equals(ssr.getSubsystemSymbolicName())
+						&& subsystem.getVersion().equals(ssr.getSubsystemVersion())
+						&& subsystem.getType().equals(ssr.getSubsystemType())))
+					throw new SubsystemException("Location already exists but symbolic name, version, and type are not the same: " + location);
+				children.add(subsystem);
+				constituents.add(subsystem);
+				return subsystem;
+			}
+			subsystem = (AriesSubsystem)region.find(ssr.getSubsystemSymbolicName(), ssr.getSubsystemVersion());
+			if (subsystem != null) {
+				if (!subsystem.getType().equals(ssr.getSubsystemType()))
+					throw new SubsystemException("Subsystem already exists in target region but has a different type: " + location);
+				children.add(subsystem);
+				constituents.add(subsystem);
 				return subsystem;
-			subsystem = new AriesSubsystem(location, content, this);
+			}
+			subsystem = new AriesSubsystem(location, ssr.getContent(), this);
 			Coordination coordination = Activator.getInstance().getServiceProvider().getService(Coordinator.class).create(getSymbolicName() + '-' + getSubsystemId(), 0);
 			try {
 				installSubsystemResource(subsystem, coordination, false);
@@ -463,12 +489,9 @@ public class AriesSubsystem implements S
 			throw new SubsystemException(e);
 		}
 		finally {
-			if (content != null) {
-				try {
-					content.close();
-				}
-				catch (IOException e) {}
-			}
+			if (ssr != null)
+				ssr.close();
+			IOUtils.close(content);
 		}
 	}
 	
@@ -601,8 +624,21 @@ public class AriesSubsystem implements S
 			uninstall();
 		}
 		setState(State.UNINSTALLING);
-		for (Iterator<Resource> iterator = constituents.iterator(); iterator.hasNext();) {
-			Resource resource = iterator.next();
+		// Uninstall child subsystems first.
+		for (Subsystem subsystem : getChildren()) {
+			try {
+				uninstallSubsystemResource((AriesSubsystem)subsystem);
+			}
+			catch (Exception e) {
+				LOGGER.error("An error occurred while uninstalling resource " + subsystem + " of subsystem " + this, e);
+				// TODO Should FAILED go out for each failure?
+			}
+		}
+		// Uninstall any remaining constituents.
+		for (Resource resource : getConstituents()) {
+			// Don't uninstall the region context bundle here.
+			if (ResourceHelper.getSymbolicNameAttribute(resource).startsWith(RegionContextBundleHelper.SYMBOLICNAME_PREFIX))
+				continue;
 			try {
 				uninstallResource(resource);
 			}
@@ -610,10 +646,11 @@ public class AriesSubsystem implements S
 				LOGGER.error("An error occurred while uninstalling resource " + resource + " of subsystem " + this, e);
 				// TODO Should FAILED go out for each failure?
 			}
-			iterator.remove();
 		}
-		for (AriesSubsystem parent : parents)
-			parent.children.remove(AriesSubsystem.this);
+		for (AriesSubsystem parent : parents) {
+			parent.children.remove(this);
+			parent.constituents.remove(this);
+		}
 		locationToSubsystem.remove(location);
 		deleteFile(directory);
 		setState(State.UNINSTALLED);
@@ -653,7 +690,29 @@ public class AriesSubsystem implements S
 		if (!isFeature())
 			constituents.add(RegionContextBundleHelper.installRegionContextBundle(this));
 		Activator.getInstance().getSubsystemServiceRegistrar().register(this);
-		List<Resource> contentResources = new ArrayList<Resource>();
+		Set<Resource> contentResources = new TreeSet<Resource>(
+				new Comparator<Resource>() {
+					@Override
+					public int compare(Resource o1, Resource o2) {
+						if (o1.equals(o2))
+							// Consistent with equals.
+							return 0;
+						String t1 = ResourceHelper.getTypeAttribute(o1);
+						String t2 = ResourceHelper.getTypeAttribute(o2);
+						boolean b1 = ResourceConstants.IDENTITY_TYPE_BUNDLE.equals(t1)
+								|| ResourceConstants.IDENTITY_TYPE_FRAGMENT.equals(t1);
+						boolean b2 = ResourceConstants.IDENTITY_TYPE_BUNDLE.equals(t2)
+								|| ResourceConstants.IDENTITY_TYPE_FRAGMENT.equals(t2);
+						if (b1 && !b2)
+							// o1 is a bundle or fragment but o2 is not.
+							return -1;
+						if (!b1 && b2)
+							// o1 is not a bundle or fragment but o2 is.
+							return 1;
+						// Either both or neither are bundles or fragments. In this case we don't care about the order.
+						return -1;
+					}
+				});
 		List<Resource> transitiveDependencies = new ArrayList<Resource>();
 		DeploymentManifest manifest = getDeploymentManifest();
 		DeployedContentHeader contentHeader = manifest.getDeployedContentHeader();
@@ -721,7 +780,7 @@ public class AriesSubsystem implements S
 		// Stop child subsystems first.
 		for (AriesSubsystem subsystem : children) {
 			try {
-				stopResource(subsystem);
+				stopSubsystemResource(subsystem);
 			}
 			catch (Exception e) {
 				LOGGER.error("An error occurred while stopping resource "
@@ -731,10 +790,10 @@ public class AriesSubsystem implements S
 		// For non-root subsystems, stop any remaining constituents.
 		if (!isRoot()){
 			for (Resource resource : constituents) {
+				// Don't stop the region context bundle.
+				if (ResourceHelper.getSymbolicNameAttribute(resource).startsWith(RegionContextBundleHelper.SYMBOLICNAME_PREFIX))
+					continue;
 				try {
-					// Don't stop the region context bundle.
-					if (ResourceHelper.getSymbolicNameAttribute(resource).startsWith(RegionContextBundleHelper.SYMBOLICNAME_PREFIX))
-						continue;
 					stopResource(resource);
 				} catch (Exception e) {
 					LOGGER.error("An error occurred while stopping resource "
@@ -838,7 +897,7 @@ public class AriesSubsystem implements S
 			// Transitive dependencies should be provisioned into the highest possible level.
 			// TODO Assumes root is always the appropriate level.
 			while (!provisionTo.parents.isEmpty())
-				provisionTo = provisionTo.parents.get(0);
+				provisionTo = provisionTo.parents.iterator().next();
 		}
 		return provisionTo;
 	}
@@ -912,11 +971,19 @@ public class AriesSubsystem implements S
 			subsystem = (AriesSubsystem)install(sfr.getLocation(), sfr.getContent());
 			return;
 		}
-		else {
+		else if (resource instanceof SubsystemDirectoryResource) {
 			SubsystemDirectoryResource sdr = (SubsystemDirectoryResource)resource;
 			subsystem = new AriesSubsystem(sdr.getArchive(), this);
 			locationToSubsystem.put(subsystem.getLocation(), subsystem);
 		}
+		else if (resource instanceof RepositoryContent) {
+			String location = getSubsystemId() + "@" + getSymbolicName() + "@" + ResourceHelper.getSymbolicNameAttribute(resource);
+			subsystem = (AriesSubsystem)install(location, ((RepositoryContent)resource).getContent());
+			return;
+		}
+		else {
+			throw new IllegalArgumentException("Unrecognized subsystem resource: " + resource);
+		}
 		Set<AriesSubsystem> subsystems = new HashSet<AriesSubsystem>();
 		subsystems.add(this);
 		resourceToSubsystems.put(subsystem, subsystems);
@@ -992,7 +1059,7 @@ public class AriesSubsystem implements S
 			// Applications have an implicit import policy equating to "import everything that I require", which is not the same as features.
 			// This must be computed from the application requirements and will be done using the Wires returned by the Resolver, when one is available.
 			region.connectRegion(
-					parents.get(0).region, 
+					parents.iterator().next().region, 
 					region.getRegionDigraph().createRegionFilterBuilder().allowAll(RegionFilter.VISIBLE_ALL_NAMESPACE).build());
 		}
 		else if (isComposite()) {
@@ -1099,8 +1166,7 @@ public class AriesSubsystem implements S
 			});
 		}
 		String type = ResourceHelper.getTypeAttribute(resource);
-		// TODO Add to constants.
-		if ("osgi.subsystem".equals(type))
+		if (SubsystemConstants.IDENTITY_TYPE_SUBSYSTEM.equals(type))
 			uninstallSubsystemResource(resource);
 		else if (ResourceConstants.IDENTITY_TYPE_BUNDLE.equals(type))
 			uninstallBundleResource(resource);

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/OsgiIdentityCapability.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/OsgiIdentityCapability.java?rev=1244685&r1=1244684&r2=1244685&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/OsgiIdentityCapability.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/OsgiIdentityCapability.java Wed Feb 15 20:22:10 2012
@@ -24,6 +24,7 @@ import org.osgi.framework.Constants;
 import org.osgi.framework.Version;
 import org.osgi.framework.resource.Resource;
 import org.osgi.framework.resource.ResourceConstants;
+import org.osgi.service.subsystem.SubsystemConstants;
 
 public class OsgiIdentityCapability extends AbstractCapability {
 	private final Map<String, Object> attributes = new HashMap<String, Object>();
@@ -38,6 +39,10 @@ public class OsgiIdentityCapability exte
 	}
 	
 	public OsgiIdentityCapability(Resource resource, String symbolicName, Version version, String type) {
+		this(resource, symbolicName, version, type, null);
+	}
+	
+	public OsgiIdentityCapability(Resource resource, String symbolicName, Version version, String identityType, String subsystemType) {
 		this.resource = resource;
 		attributes.put(
 				ResourceConstants.IDENTITY_NAMESPACE, 
@@ -47,7 +52,10 @@ public class OsgiIdentityCapability exte
 				version);
 		attributes.put(
 				ResourceConstants.IDENTITY_TYPE_ATTRIBUTE, 
-				type);
+				identityType);
+		if (subsystemType != null)
+			// TODO Add to constants.
+			attributes.put("subsystem-type", subsystemType);
 		// TODO Add directives, particularly "effective" and "singleton".
 	}
 	
@@ -56,8 +64,8 @@ public class OsgiIdentityCapability exte
 				resource,
 				manifest.getSubsystemSymbolicNameHeader().getSymbolicName(),
 				manifest.getSubsystemVersionHeader().getVersion(),
-				// TODO Add to constants.
-				"osgi.subsystem");
+				SubsystemConstants.IDENTITY_TYPE_SUBSYSTEM,
+				manifest.getSubsystemTypeHeader().getValue());
 	}
 	
 	public OsgiIdentityCapability(Resource resource, BundleManifest manifest) {

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemUri.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemUri.java?rev=1244685&r1=1244684&r2=1244685&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemUri.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemUri.java Wed Feb 15 20:22:10 2012
@@ -52,8 +52,6 @@ public class SubsystemUri {
 			else
 				throw new IllegalArgumentException("Unsupported subsystem URI parameter: " + name);
 		}
-		if (symbolicName == null)
-			throw new IllegalArgumentException("Missing required subsystem URI parameter: " + SubsystemSymbolicNameHeader.NAME);
 		this.symbolicName = symbolicName;
 		this.version = version;
 	}

Added: 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=1244685&view=auto
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/TargetRegion.java (added)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/TargetRegion.java Wed Feb 15 20:22:10 2012
@@ -0,0 +1,46 @@
+package org.apache.aries.subsystem.core.internal;
+
+import java.util.Collection;
+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 boolean contains(Subsystem subsystem) {
+		for (Subsystem s : region) {
+			if (s.getSymbolicName().equals(subsystem.getSymbolicName())
+					&& s.getVersion().equals(subsystem.getVersion()))
+				return true;
+		}
+		return false;
+	}
+	
+	public Subsystem find(String symbolicName, Version version) {
+		for (Subsystem s : region) {
+			if (s.getSymbolicName().equals(symbolicName)
+					&& s.getVersion().equals(version))
+				return s;
+		}
+		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());
+			}
+			region.add(child);
+		}
+	}
+}

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/obr/SubsystemEnvironment.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/obr/SubsystemEnvironment.java?rev=1244685&r1=1244684&r2=1244685&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/obr/SubsystemEnvironment.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/obr/SubsystemEnvironment.java Wed Feb 15 20:22:10 2012
@@ -74,7 +74,7 @@ public class SubsystemEnvironment implem
 						boolean br2 = capability2.getResource() instanceof BundleRevision;
 						if (br1 && !br2)
 							result = -1;
-						if (!br1 && br2)
+						else if (!br1 && br2)
 							result = 1;
 						logger.debug(LOG_EXIT, "compare", result);
 						return result;

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/SubsystemDirectoryResource.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/SubsystemDirectoryResource.java?rev=1244685&r1=1244684&r2=1244685&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/SubsystemDirectoryResource.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/SubsystemDirectoryResource.java Wed Feb 15 20:22:10 2012
@@ -26,7 +26,8 @@ public class SubsystemDirectoryResource 
 				this, 
 				manifest.getSubsystemSymbolicNameHeader().getSymbolicName(), 
 				manifest.getSubsystemVersionHeader().getVersion(), 
-				SubsystemConstants.IDENTITY_TYPE_SUBSYSTEM));
+				SubsystemConstants.IDENTITY_TYPE_SUBSYSTEM,
+				manifest.getSubsystemTypeHeader().getValue()));
 		this.capabilities = Collections.unmodifiableList(capabilities);
 	}
 	

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/SubsystemFileResource.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/SubsystemFileResource.java?rev=1244685&r1=1244684&r2=1244685&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/SubsystemFileResource.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/SubsystemFileResource.java Wed Feb 15 20:22:10 2012
@@ -25,12 +25,13 @@ import org.osgi.service.repository.Repos
 import org.osgi.service.subsystem.SubsystemConstants;
 
 public class SubsystemFileResource implements Resource, RepositoryContent {
-	private static final String REGEX = "([^@])(?:@(.*))?.ssa";
+	private static final String REGEX = "([^@]+)(?:@(.+))?.ssa";
 	private static final Pattern PATTERN = Pattern.compile(REGEX);
 	
 	private final List<Capability> capabilities;
 	private final IDirectory directory;
 	private final File file;
+	private final String location;
 	
 	public SubsystemFileResource(File content) throws IOException {
 		file = content;
@@ -40,6 +41,7 @@ public class SubsystemFileResource imple
 			manifest = ManifestProcessor.obtainManifestFromAppDir(directory, "OSGI-INF/SUBSYSTEM.MF");
 		String symbolicName = null;
 		Version version = Version.emptyVersion;
+		String type = SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION;
 		if (manifest != null) {
 			String value = manifest.getMainAttributes().getValue(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME);
 			if (value != null)
@@ -47,8 +49,11 @@ public class SubsystemFileResource imple
 			value = manifest.getMainAttributes().getValue(SubsystemConstants.SUBSYSTEM_VERSION);
 			if (value != null)
 				version = Version.parseVersion(value);
+			value = manifest.getMainAttributes().getValue(SubsystemConstants.SUBSYSTEM_TYPE);
+			if (value != null)
+				type = value;
 		}
-		Matcher matcher = PATTERN.matcher(content.getName());;
+		Matcher matcher = PATTERN.matcher(content.getName());
 		if (symbolicName == null) {
 			if (!matcher.matches())
 				throw new IllegalArgumentException("No symbolic name");
@@ -60,8 +65,9 @@ public class SubsystemFileResource imple
 				version = Version.parseVersion(group);
 		}
 		List<Capability> capabilities = new ArrayList<Capability>(1);
-		capabilities.add(new OsgiIdentityCapability(this, symbolicName, version, SubsystemConstants.IDENTITY_TYPE_SUBSYSTEM));
+		capabilities.add(new OsgiIdentityCapability(this, symbolicName, version, SubsystemConstants.IDENTITY_TYPE_SUBSYSTEM, type));
 		this.capabilities = Collections.unmodifiableList(capabilities);
+		location = "subsystem://?" + SubsystemConstants.SUBSYSTEM_SYMBOLICNAME + '=' + symbolicName + '&' + SubsystemConstants.SUBSYSTEM_VERSION + '=' + version;
 	}
 	
 	@Override
@@ -77,7 +83,7 @@ public class SubsystemFileResource imple
 	}
 	
 	public String getLocation() {
-		return file.getAbsolutePath();
+		return location;
 	}
 
 	@Override

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/SubsystemStreamResource.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/SubsystemStreamResource.java?rev=1244685&r1=1244684&r2=1244685&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/SubsystemStreamResource.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/SubsystemStreamResource.java Wed Feb 15 20:22:10 2012
@@ -1,5 +1,7 @@
 package org.apache.aries.subsystem.core.resource;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URISyntaxException;
@@ -10,6 +12,7 @@ import java.util.List;
 import java.util.jar.Manifest;
 
 import org.apache.aries.subsystem.core.archive.SubsystemSymbolicNameHeader;
+import org.apache.aries.subsystem.core.archive.SubsystemTypeHeader;
 import org.apache.aries.subsystem.core.internal.OsgiIdentityCapability;
 import org.apache.aries.subsystem.core.internal.SubsystemUri;
 import org.apache.aries.util.filesystem.FileSystem;
@@ -25,6 +28,7 @@ import org.osgi.service.repository.Repos
 import org.osgi.service.subsystem.SubsystemConstants;
 
 public class SubsystemStreamResource implements Resource, RepositoryContent {
+	private final byte[] content;
 	private final List<Capability> capabilities;
 	private final ICloseableDirectory directory;
 	
@@ -39,18 +43,24 @@ public class SubsystemStreamResource imp
 				else
 					content = new URL(location).openStream();
 			}
-			directory = FileSystem.getFSRoot(content);
+			ByteArrayOutputStream baos = new ByteArrayOutputStream();
+			byte[] bytes = new byte[2048];
+			int read;
+			while ((read = content.read(bytes)) != -1) {
+				baos.write(bytes, 0, read);
+			}
+			this.content = baos.toByteArray();
+			directory = FileSystem.getFSRoot(new ByteArrayInputStream(baos.toByteArray()));
 			if (directory == null)
 				throw new IOException("Unable to parse content of " + location);
 		}
 		finally {
 			IOUtils.close(content);
 		}
-		Manifest manifest = ManifestProcessor.obtainManifestFromAppDir(directory, "OSGI-INF/DEPLOYMENT.MF");
-		if (manifest == null)
-			manifest = ManifestProcessor.obtainManifestFromAppDir(directory, "OSGI-INF/SUBSYSTEM.MF");
+		Manifest manifest = ManifestProcessor.obtainManifestFromAppDir(directory, "OSGI-INF/SUBSYSTEM.MF");
 		String symbolicName = null;
 		Version version = Version.emptyVersion;
+		String type = SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION;
 		if (manifest != null) {
 			String value = manifest.getMainAttributes().getValue(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME);
 			if (value != null)
@@ -58,6 +68,9 @@ public class SubsystemStreamResource imp
 			value = manifest.getMainAttributes().getValue(SubsystemConstants.SUBSYSTEM_VERSION);
 			if (value != null)
 				version = Version.parseVersion(value);
+			value = manifest.getMainAttributes().getValue(SubsystemConstants.SUBSYSTEM_TYPE);
+			if (value != null)
+				type = new SubsystemTypeHeader(value).getValue();
 		}
 		if (symbolicName == null) {
 			if (uri == null)
@@ -67,7 +80,7 @@ public class SubsystemStreamResource imp
 		if (version == Version.emptyVersion && uri != null)
 			version = uri.getVersion();
 		List<Capability> capabilities = new ArrayList<Capability>(1);
-		capabilities.add(new OsgiIdentityCapability(this, symbolicName, version, SubsystemConstants.IDENTITY_TYPE_SUBSYSTEM));
+		capabilities.add(new OsgiIdentityCapability(this, symbolicName, version, SubsystemConstants.IDENTITY_TYPE_SUBSYSTEM, type));
 		this.capabilities = Collections.unmodifiableList(capabilities);
 	}
 	
@@ -84,11 +97,27 @@ public class SubsystemStreamResource imp
 
 	@Override
 	public InputStream getContent() throws IOException {
-		return directory.open();
+		return new ByteArrayInputStream(content);
 	}
 
 	@Override
 	public List<Requirement> getRequirements(String namespace) {
 		return Collections.emptyList();
 	}
+	
+	public String getSubsystemSymbolicName() {
+		Capability identity = capabilities.get(0);
+		return (String)identity.getAttributes().get(ResourceConstants.IDENTITY_NAMESPACE);
+	}
+	
+	public String getSubsystemType() {
+		Capability identity = capabilities.get(0);
+		// TODO Add to constants.
+		return (String)identity.getAttributes().get("subsystem-type");
+	}
+	
+	public Version getSubsystemVersion() {
+		Capability identity = capabilities.get(0);
+		return (Version)identity.getAttributes().get(ResourceConstants.IDENTITY_VERSION_ATTRIBUTE);
+	}
 }