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/08 14:54:44 UTC

svn commit: r1241900 [3/8] - in /aries/trunk/subsystem: ./ subsystem-api/ subsystem-api/src/main/java/org/osgi/service/repository/ subsystem-api/src/main/java/org/osgi/service/resolver/ subsystem-api/src/main/java/org/osgi/service/subsystem/ subsystem-...

Modified: aries/trunk/subsystem/subsystem-core/pom.xml
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/pom.xml?rev=1241900&r1=1241899&r2=1241900&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/pom.xml (original)
+++ aries/trunk/subsystem/subsystem-core/pom.xml Wed Feb  8 13:54:41 2012
@@ -44,12 +44,17 @@
             org.apache.aries.subsystem.core.internal.Activator
         </aries.osgi.activator>
         <aries.osgi.private.pkg>
+        	org.apache.aries.subsystem.core,
+        	org.apache.aries.subsystem.core.archive,
             org.apache.aries.subsystem.core.internal,
             org.apache.aries.subsystem.core.obr,
-            org.apache.felix.utils.manifest
+            org.apache.aries.subsystem.core.obr.felix,
+            org.apache.aries.subsystem.core.resource,
+            org.apache.felix.resolver,
+            org.apache.felix.resolver.impl
         </aries.osgi.private.pkg>
          <aries.osgi.import>
-             *
+         	*
          </aries.osgi.import>
     </properties>
 
@@ -68,7 +73,7 @@
         <dependency>
             <groupId>org.apache.aries</groupId>
             <artifactId>org.apache.aries.util</artifactId>
-            <version>0.4-SNAPSHOT</version>
+            <version>0.5-SNAPSHOT</version>
             <exclusions>
             	<exclusion>
             		<groupId>org.osgi</groupId>
@@ -87,32 +92,32 @@
             	</exclusion>
             </exclusions>
         </dependency>
+        <dependency>
+            <groupId>org.apache.aries.application</groupId>
+            <artifactId>org.apache.aries.application.utils</artifactId>
+            <version>0.3.1-SNAPSHOT</version>
+            <exclusions>
+            	<exclusion>
+            		<groupId>org.osgi</groupId>
+            		<artifactId>org.osgi.core</artifactId>
+            	</exclusion>
+            </exclusions>
+        </dependency>
     	<dependency>
             <groupId>org.eclipse</groupId>
-            <artifactId>osgi</artifactId>
+            <artifactId>org.eclipse.osgi</artifactId>
             <version>3.8.0-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
     	<dependency>
     		<groupId>org.eclipse.equinox</groupId>
-    		<artifactId>coordinator</artifactId>
-    		<version>1.0.0.v20110314</version>
+    		<artifactId>org.eclipse.equinox.coordinator</artifactId>
+    		<version>3.8.0-SNAPSHOT</version>
     	</dependency>
     	<dependency>
             <groupId>org.eclipse.equinox</groupId>
-            <artifactId>region</artifactId>
-            <version>1.0.0.v20110506</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.felix</groupId>
-            <artifactId>org.apache.felix.utils</artifactId>
-            <version>1.1.0</version>
-            <exclusions>
-            	<exclusion>
-            		<groupId>org.osgi</groupId>
-            		<artifactId>org.osgi.core</artifactId>
-            	</exclusion>
-            </exclusions>
+            <artifactId>org.eclipse.equinox.region</artifactId>
+            <version>3.8.0-SNAPSHOT</version>
         </dependency>
         <dependency>
             <groupId>org.apache.felix</groupId>
@@ -128,7 +133,7 @@
         <dependency>
             <groupId>org.apache.aries.testsupport</groupId>
             <artifactId>org.apache.aries.testsupport.unit</artifactId>
-            <version>0.4-SNAPSHOT</version>
+            <version>0.5-SNAPSHOT</version>
             <scope>test</scope>
             <exclusions>
             	<exclusion>

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/ResourceHelper.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/ResourceHelper.java?rev=1241900&r1=1241899&r2=1241900&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/ResourceHelper.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/ResourceHelper.java Wed Feb  8 13:54:41 2012
@@ -13,17 +13,39 @@
  */
 package org.apache.aries.subsystem.core;
 
+import static org.apache.aries.application.utils.AppConstants.LOG_ENTRY;
+import static org.apache.aries.application.utils.AppConstants.LOG_EXIT;
+
 import java.util.Collection;
 import java.util.List;
 
+import org.apache.aries.subsystem.core.archive.TypeAttribute;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.Version;
 import org.osgi.framework.resource.Capability;
 import org.osgi.framework.resource.Requirement;
 import org.osgi.framework.resource.Resource;
 import org.osgi.framework.resource.ResourceConstants;
 import org.osgi.service.repository.Repository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class ResourceHelper {
+	private static final Logger logger = LoggerFactory.getLogger(ResourceHelper.class);
+	
+	public static boolean areEqual(Resource resource1, Resource resource2) {
+		if (getTypeAttribute(resource1).equals(getTypeAttribute(resource2))) {
+			if (getSymbolicNameAttribute(resource1).equals(getSymbolicNameAttribute(resource2))) {
+				if (getVersionAttribute(resource1).equals(getVersionAttribute(resource2))) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+	
 	public static String getContentAttribute(Resource resource) {
 		// TODO Add to constants.
 		return (String)getContentAttribute(resource, "osgi.content");
@@ -52,10 +74,46 @@ public class ResourceHelper {
 	}
 	
 	public static String getTypeAttribute(Resource resource) {
-		return (String)getIdentityAttribute(resource, ResourceConstants.IDENTITY_TYPE_ATTRIBUTE);
+		String result = (String)getIdentityAttribute(resource, ResourceConstants.IDENTITY_TYPE_ATTRIBUTE);
+		if (result == null)
+			result = TypeAttribute.DEFAULT_VALUE;
+		return result;
 	}
 	
 	public static Version getVersionAttribute(Resource resource) {
-		return (Version)getIdentityAttribute(resource, ResourceConstants.IDENTITY_VERSION_ATTRIBUTE);
+		Version result = (Version)getIdentityAttribute(resource, ResourceConstants.IDENTITY_VERSION_ATTRIBUTE);
+		if (result == null)
+			result = Version.emptyVersion;
+		return result;
+	}
+	
+	public static boolean matches(Requirement requirement, Capability capability) {
+		if (logger.isDebugEnabled())
+			logger.debug(LOG_ENTRY, "matches", new Object[]{requirement, capability});
+		boolean result = false;
+		if (requirement == null && capability == null)
+			result = true;
+		else if (requirement == null || capability == null) 
+			result = false;
+		else if (!capability.getNamespace().equals(requirement.getNamespace())) 
+			result = false;
+		else {
+			String filterStr = requirement.getDirectives().get(Constants.FILTER_DIRECTIVE);
+			if (filterStr == null)
+				result = true;
+			else {
+				try {
+					if (FrameworkUtil.createFilter(filterStr).matches(capability.getAttributes()))
+						result = true;
+				}
+				catch (InvalidSyntaxException e) {
+					logger.debug("Requirement had invalid filter string: " + requirement, e);
+					result = false;
+				}
+			}
+		}
+		// TODO Check directives.
+		logger.debug(LOG_EXIT, "matches", result);
+		return result;
 	}
 }

Added: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/BundleManifestVersionHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/BundleManifestVersionHeader.java?rev=1241900&view=auto
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/BundleManifestVersionHeader.java (added)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/BundleManifestVersionHeader.java Wed Feb  8 13:54:41 2012
@@ -0,0 +1,16 @@
+package org.apache.aries.subsystem.core.archive;
+
+import org.osgi.framework.Constants;
+
+public class BundleManifestVersionHeader extends VersionHeader {
+	public static final String DEFAULT_VALUE = "2.0";
+	public static final String NAME = Constants.BUNDLE_MANIFESTVERSION;
+	
+	public BundleManifestVersionHeader() {
+		this(DEFAULT_VALUE);
+	}
+
+	public BundleManifestVersionHeader(String value) {
+		super(NAME, value);
+	}
+}

Added: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/BundleSymbolicNameHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/BundleSymbolicNameHeader.java?rev=1241900&view=auto
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/BundleSymbolicNameHeader.java (added)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/BundleSymbolicNameHeader.java Wed Feb  8 13:54:41 2012
@@ -0,0 +1,11 @@
+package org.apache.aries.subsystem.core.archive;
+
+import org.osgi.framework.Constants;
+
+public class BundleSymbolicNameHeader extends SymbolicNameHeader {
+	public static final String NAME = Constants.BUNDLE_SYMBOLICNAME;
+	
+	public BundleSymbolicNameHeader(String value) {
+		super(NAME, value);
+	}
+}

Added: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/BundleVersionHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/BundleVersionHeader.java?rev=1241900&view=auto
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/BundleVersionHeader.java (added)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/BundleVersionHeader.java Wed Feb  8 13:54:41 2012
@@ -0,0 +1,17 @@
+package org.apache.aries.subsystem.core.archive;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+public class BundleVersionHeader extends VersionHeader {
+	public static final String DEFAULT_VALUE = Version.emptyVersion.toString();
+	public static final String NAME = Constants.BUNDLE_VERSION;
+	
+	public BundleVersionHeader() {
+		this(DEFAULT_VALUE);
+	}
+
+	public BundleVersionHeader(String value) {
+		super(NAME, value);
+	}
+}

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=1241900&r1=1241899&r2=1241900&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  8 13:54:41 2012
@@ -13,51 +13,73 @@
  */
 package org.apache.aries.subsystem.core.archive;
 
+import static org.apache.aries.application.utils.AppConstants.LOG_ENTRY;
+import static org.apache.aries.application.utils.AppConstants.LOG_EXIT;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 
 import org.apache.aries.subsystem.core.internal.Activator;
+import org.apache.aries.subsystem.core.internal.OsgiIdentityCapability;
 import org.apache.aries.subsystem.core.internal.OsgiIdentityRequirement;
 import org.apache.aries.subsystem.core.obr.SubsystemEnvironment;
+import org.osgi.framework.Version;
+import org.osgi.framework.resource.Capability;
+import org.osgi.framework.resource.Requirement;
 import org.osgi.framework.resource.Resource;
+import org.osgi.framework.resource.ResourceConstants;
 import org.osgi.framework.resource.Wire;
+import org.osgi.service.resolver.Resolver;
 import org.osgi.service.subsystem.SubsystemException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-public class DeploymentManifest extends Manifest {
-	public static DeploymentManifest newInstance(Archive archive, SubsystemEnvironment environment) {
-		SubsystemManifest manifest = archive.getSubsystemManifest();
+public class DeploymentManifest extends Manifest implements Resource {
+	public static final String IDENTITY_TYPE = "org.apache.aries.subsystem.manifest.deployment";
+	
+	private static final Logger logger = LoggerFactory.getLogger(DeploymentManifest.class);
+	
+	public static DeploymentManifest newInstance(SubsystemManifest manifest, SubsystemEnvironment environment) {
+		if (logger.isDebugEnabled())
+			logger.debug(LOG_ENTRY, "newInstance", new Object[]{manifest, environment});
 		DeploymentManifest result = new DeploymentManifest();
 		result.headers.put(ManifestVersionHeader.NAME, manifest.getManifestVersion());
-		Collection<Resource> resources = new ArrayList<Resource>();
-		for (SubsystemContentHeader.Content content : manifest.getSubsystemContent().getContents()) {
-			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: " + resource);
-			resources.add(resource);
-		}
-		// TODO This does not validate that all content bundles were found.
-		Map<Resource, List<Wire>> resolution = Activator.getResolver().resolve(environment, resources, Collections.EMPTY_LIST);
-		// TODO Once we have a resolver that actually returns lists of wires, we can use them to compute other manifest headers such as Import-Package.
-		Collection<Resource> deployedContent = new ArrayList<Resource>();
-		Collection<Resource> provisionResource = new ArrayList<Resource>();
-		for (Resource resource : resolution.keySet()) {
-			if (environment.isContentResource(resource))
-				deployedContent.add(resource);
-			else
-				provisionResource.add(resource);
+		Collection<Resource> resources = new HashSet<Resource>();
+		SubsystemContentHeader contentHeader = manifest.getSubsystemContent();
+		if (contentHeader != null) {
+			for (SubsystemContentHeader.Content content : contentHeader.getContents()) {
+				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);
+				resources.add(resource);
+			}
+			// TODO This does not validate that all content bundles were found.
+			Map<Resource, List<Wire>> resolution = Activator.getInstance().getServiceProvider().getService(Resolver.class).resolve(environment, resources, Collections.EMPTY_LIST);
+			// TODO Once we have a resolver that actually returns lists of wires, we can use them to compute other manifest headers such as Import-Package.
+			Collection<Resource> deployedContent = new HashSet<Resource>();
+			Collection<Resource> provisionResource = new HashSet<Resource>();
+			for (Resource resource : resolution.keySet()) {
+				if (contentHeader.contains(resource))
+					deployedContent.add(resource);
+				else
+					provisionResource.add(resource);
+			}
+			// Make sure any already resolved content resources are added back in.
+			deployedContent.addAll(resources);
+			result.headers.put(DeployedContentHeader.NAME, DeployedContentHeader.newInstance(deployedContent));
+			if (!provisionResource.isEmpty())
+				result.headers.put(ProvisionResourceHeader.NAME, ProvisionResourceHeader.newInstance(provisionResource));
 		}
-		result.headers.put(DeployedContentHeader.NAME, DeployedContentHeader.newInstance(deployedContent));
-		if (!provisionResource.isEmpty())
-			result.headers.put(ProvisionResourceHeader.NAME, ProvisionResourceHeader.newInstance(provisionResource));
 		result.headers.put(SubsystemSymbolicNameHeader.NAME, manifest.getSubsystemSymbolicName());
 		result.headers.put(SubsystemVersionHeader.NAME, manifest.getSubsystemVersion());
 		SubsystemTypeHeader typeHeader = manifest.getSubsystemType();
@@ -71,6 +93,7 @@ public class DeploymentManifest extends 
 			// TODO Compute additional headers for a composite. 
 		}
 		// Features require no additional headers.
+		logger.debug(LOG_EXIT, "newInstance", result);
 		return result;
 	}
 	
@@ -91,6 +114,26 @@ public class DeploymentManifest extends 
 			headers.put(ProvisionResourceHeader.NAME, ProvisionResourceHeader.newInstance(provisionResource));
 	}
 	
+	@Override
+	public List<Capability> getCapabilities(String namespace) {
+		List<Capability> result = new ArrayList<Capability>(1);
+		if (namespace == null || namespace.equals(ResourceConstants.IDENTITY_NAMESPACE)) {
+			OsgiIdentityCapability capability = new OsgiIdentityCapability(
+					this,
+					// TODO Reusing IDENTITY_TYPE for the symbolic name here.
+					// Since there's only one subsystem manifest per subsystem,
+					// this shouldn't cause any technical issues. However, it
+					// might be best to use the subsystem's symbolic name here.
+					// But there are issues with that as well since type is not
+					// part of the unique identity.
+					IDENTITY_TYPE,
+					Version.emptyVersion,
+					IDENTITY_TYPE);
+			result.add(capability);
+		}
+		return result;
+	}
+	
 	public DeployedContentHeader getDeployedContent() {
 		return (DeployedContentHeader)getHeader(DeployedContentHeader.NAME);
 	}
@@ -98,4 +141,20 @@ public class DeploymentManifest extends 
 	public ProvisionResourceHeader getProvisionResource() {
 		return (ProvisionResourceHeader)getHeader(ProvisionResourceHeader.NAME);
 	}
+	
+	@Override
+	public List<Requirement> getRequirements(String namespace) {
+		return Collections.emptyList();
+	}
+	
+	public SubsystemSymbolicNameHeader getSubsystemSymbolicName() {
+		return (SubsystemSymbolicNameHeader)getHeader(SubsystemSymbolicNameHeader.NAME);
+	}
+	
+	public SubsystemVersionHeader getSubsystemVersion() {
+		SubsystemVersionHeader result = (SubsystemVersionHeader)getHeader(SubsystemVersionHeader.NAME);
+		if (result == null)
+			return SubsystemVersionHeader.DEFAULT;
+		return result;
+	}
 }

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ExportPackageHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ExportPackageHeader.java?rev=1241900&r1=1241899&r2=1241900&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ExportPackageHeader.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ExportPackageHeader.java Wed Feb  8 13:54:41 2012
@@ -19,6 +19,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.aries.subsystem.core.resource.AbstractCapability;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Version;
 import org.osgi.framework.resource.Capability;
@@ -37,7 +38,7 @@ public class ExportPackageHeader extends
 		for (final Clause clause : clauses) {
 			String[] exportedPackages = clause.getPath().split(";");
 			for (final String exportedPackage : exportedPackages) {
-				capabilities.add(new Capability() {
+				capabilities.add(new AbstractCapability() {
 					@Override
 					public String getNamespace() {
 						return ResourceConstants.WIRING_PACKAGE_NAMESPACE;

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/GenericClause.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/GenericClause.java?rev=1241900&r1=1241899&r2=1241900&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/GenericClause.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/GenericClause.java Wed Feb  8 13:54:41 2012
@@ -22,7 +22,10 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 public class GenericClause implements Clause {
-	private static final String REGEX = '(' + Grammar.PATH + "(?:;" + Grammar.PATH + ")*)(?:;(" + Grammar.PARAMETER + "))*";
+	// TODO The introduction of the whitespace (\\s*) before a parameter was added to get around a grammatically
+	// incorrect clause in Equinox: org.eclipse.osgi; singleton:=true;deployed-version=3.7.0.v20110221;type=osgi.bundle.
+	// Note the space before the singleton directive.
+	private static final String REGEX = '(' + Grammar.PATH + "(?:;" + Grammar.PATH + ")*)(?:;\\s*(" + Grammar.PARAMETER + "))*";
 	
 	private static final Pattern PATTERN = Pattern.compile(REGEX);
 	private static final Pattern PATTERN_PARAMETER = Pattern.compile(Grammar.PARAMETER);

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/Grammar.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/Grammar.java?rev=1241900&r1=1241899&r2=1241900&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/Grammar.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/Grammar.java Wed Feb  8 13:54:41 2012
@@ -89,7 +89,10 @@ public interface Grammar {
 	public static final String PATH_UNQUOTED = PATH_SEP + '|' + PATH_SEP + '?' + PATH_ELEMENT + "(?:" + PATH_SEP + PATH_ELEMENT + ")*";
 	public static final String PATH_UNQUOTED_NT = PATH_SEP + '|' + PATH_SEP + '?' + PATH_ELEMENT_NT + "(?:" + PATH_SEP + PATH_ELEMENT_NT + ")*";
 	public static final String PATH = "(?:" + PATH_UNQUOTED_NT + ")|\"(?:" + PATH_UNQUOTED + ")\"";
-	public static final String CLAUSE = "(?:" + PATH + ")(?:;" + PATH + ")*(?:;(?:" + PARAMETER + "))*";
+	// TODO The introduction of the whitespace (\\s*) before a parameter was added to get around a grammatically
+	// incorrect clause in Equinox: org.eclipse.osgi; singleton:=true;deployed-version=3.7.0.v20110221;type=osgi.bundle.
+	// Note the space before the singleton directive.
+	public static final String CLAUSE = "(?:" + PATH + ")(?:;" + PATH + ")*(?:;\\s*(?:" + PARAMETER + "))*";
 	public static final String HEADERCHAR = ALPHANUM + "|_|-";
 	public static final String NAME = ALPHANUM + "(?:" + HEADERCHAR + ")*";
 	public static final String HEADER = NAME + ": " + CLAUSE + "(?:," + CLAUSE + ")*";

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=1241900&r1=1241899&r2=1241900&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  8 13:54:41 2012
@@ -90,6 +90,10 @@ public class HeaderFactory {
 			return new ExportPackageHeader(value);
 		if (ImportPackageHeader.NAME.equals(name))
 			return new ImportPackageHeader(value);
+		if (DeployedContentHeader.NAME.equals(name))
+			return new DeployedContentHeader(value);
+		if (ProvisionResourceHeader.NAME.equals(name))
+			return new ProvisionResourceHeader(value);
 		return new GenericHeader(name, value);
 			
 	}

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/Manifest.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/Manifest.java?rev=1241900&r1=1241899&r2=1241900&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/Manifest.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/Manifest.java Wed Feb  8 13:54:41 2012
@@ -27,7 +27,7 @@ import java.util.jar.Attributes;
 import org.apache.aries.util.manifest.ManifestProcessor;
 
 public abstract class Manifest {
-	protected final Map<String, Header> headers = new HashMap<String, Header>();
+	protected final Map<String, Header> headers = Collections.synchronizedMap(new HashMap<String, Header>());
 	protected final java.util.jar.Manifest manifest;
 	
 	public Manifest(InputStream in) throws IOException {
@@ -64,6 +64,19 @@ public abstract class Manifest {
 		return getHeader(Attributes.Name.MANIFEST_VERSION.toString());
 	}
 	
+	@Override
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		sb.append('[').append(getClass().getName()).append(": ");
+		if (!headers.values().isEmpty()) {
+			for (Header header : headers.values())
+				sb.append(header.getName()).append('=').append(header.getValue()).append(", ");
+			sb.delete(sb.length() - 2, sb.length());
+		}
+		sb.append(']');
+		return sb.toString();
+	}
+	
 	public void write(OutputStream out) throws IOException {
 		java.util.jar.Manifest m = new java.util.jar.Manifest();
 		Attributes attributes = m.getMainAttributes();

Added: 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=1241900&view=auto
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemArchive.java (added)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemArchive.java Wed Feb  8 13:54:41 2012
@@ -0,0 +1,426 @@
+package org.apache.aries.subsystem.core.archive;
+
+import static org.apache.aries.application.utils.AppConstants.LOG_ENTRY;
+import static org.apache.aries.application.utils.AppConstants.LOG_EXIT;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.aries.subsystem.core.ResourceHelper;
+import org.apache.aries.subsystem.core.internal.DataFile;
+import org.apache.aries.subsystem.core.internal.OsgiIdentityCapability;
+import org.apache.aries.subsystem.core.internal.StaticDataFile;
+import org.apache.aries.subsystem.core.resource.AbstractRequirement;
+import org.apache.aries.subsystem.core.resource.BundleResource;
+import org.apache.aries.subsystem.core.resource.SubsystemFileResource;
+import org.osgi.framework.Constants;
+import org.osgi.framework.resource.Capability;
+import org.osgi.framework.resource.Requirement;
+import org.osgi.framework.resource.Resource;
+import org.osgi.framework.resource.ResourceConstants;
+import org.osgi.service.repository.Repository;
+import org.osgi.service.repository.RepositoryContent;
+import org.osgi.service.subsystem.SubsystemConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SubsystemArchive implements Repository, RepositoryContent, Resource {
+	private static final Logger logger = LoggerFactory.getLogger(SubsystemArchive.class);
+	
+	private final File directory;
+	private final Map<Resource, URL> resources = new HashMap<Resource, URL>();
+	
+	public SubsystemArchive(File content) throws Exception {
+		logger.debug(LOG_ENTRY, "init", content);
+		if (!content.exists())
+			throw new IllegalArgumentException(content.getName());
+		if (content.isDirectory()) {
+			directory = content;
+			processDirectory(content);
+		}
+		else { // It's a file.
+			directory = content.getParentFile();
+			processFile(content);
+		}
+		logger.debug(LOG_EXIT, "init");
+	}
+	
+	@Override
+	public synchronized Collection<Capability> findProviders(Requirement requirement) {
+		logger.debug(LOG_ENTRY, "findProviders", requirement);
+		Collection<Capability> capabilities = new ArrayList<Capability>(1);
+		for (Resource resource : resources.keySet()) {
+			logger.debug("Evaluating resource: " + resource);
+			for (Capability capability : resource.getCapabilities(requirement.getNamespace())) {
+				logger.debug("Evaluating capability: " + capability);
+				if (ResourceHelper.matches(requirement, capability)) {
+					logger.debug("Adding capability: " + capability);
+					capabilities.add(capability);
+				}
+			}
+		}
+		logger.debug(LOG_EXIT, "findProviders", capabilities);
+		return capabilities;
+	}
+	
+	@Override
+	public Map<Requirement, Collection<Capability>> findProviders(Collection<? extends Requirement> requirements) {
+		Map<Requirement, Collection<Capability>> result = new HashMap<Requirement, Collection<Capability>>(requirements.size());
+		for (Requirement requirement : requirements)
+			result.put(requirement, findProviders(requirement));
+		return result;
+	}
+	
+	@Override
+	public List<Capability> getCapabilities(String namespace) {
+		if (namespace == null || namespace.equals(ResourceConstants.IDENTITY_NAMESPACE)) {
+			List<Capability> result = new ArrayList<Capability>(1);
+			OsgiIdentityCapability capability = new OsgiIdentityCapability(
+					this,
+					getSubsystemManifest().getSubsystemSymbolicName().getSymbolicName(),
+					getSubsystemManifest().getSubsystemVersion().getVersion(),
+					SubsystemConstants.IDENTITY_TYPE_SUBSYSTEM);
+			result.add(capability);
+			return result;
+		}
+		return Collections.emptyList();
+	}
+	
+	@Override
+	public synchronized InputStream getContent() throws IOException {
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+		ZipOutputStream zos = new ZipOutputStream(baos);
+		for (Resource resource : getResources()) {
+			ZipEntry ze = new ZipEntry(
+					new StringBuilder(ResourceHelper.getSymbolicNameAttribute(resource))
+					.append(';')
+					.append(ResourceHelper.getVersionAttribute(resource))
+					.append(';')
+					.append(ResourceHelper.getTypeAttribute(resource))
+					.toString());
+			try {
+				zos.putNextEntry(ze);
+				InputStream is = ((RepositoryContent) resource).getContent();
+				byte[] bytes = new byte[2048];
+				int read;
+				while ((read = is.read(bytes)) != -1)
+					zos.write(bytes, 0, read);
+				zos.closeEntry();
+			} 
+			finally {
+				try {
+					zos.close();
+				} 
+				catch (IOException e) {
+					logger.debug("Unable to close the stream: ", e);
+				}
+			}
+		}
+		return new ByteArrayInputStream(baos.toByteArray());
+	}
+	
+	public synchronized DataFile getDataFile() {
+		Collection<Capability> capabilities = findProviders(new AbstractRequirement() {
+			@Override
+			public Map<String, Object> getAttributes() {
+				return Collections.emptyMap();
+			}
+
+			@Override
+			public Map<String, String> getDirectives() {
+				Map<String, String> directives = new HashMap<String, String>(1);
+				directives.put(
+						Constants.FILTER_DIRECTIVE, 
+						new StringBuilder()
+								.append('(')
+								.append(ResourceConstants.IDENTITY_TYPE_ATTRIBUTE)
+								.append('=')
+								.append(DataFile.IDENTITY_TYPE)
+								.append(')')
+								.toString());
+				return Collections.unmodifiableMap(directives);
+			}
+
+			@Override
+			public String getNamespace() {
+				return ResourceConstants.IDENTITY_NAMESPACE;
+			}
+
+			@Override
+			public Resource getResource() {
+				return null;
+			}
+		});
+		if (capabilities.isEmpty())
+			return null;
+		return (DataFile)capabilities.iterator().next().getResource();
+	}
+	
+	public synchronized DeploymentManifest getDeploymentManifest() {
+		Collection<Capability> capabilities = findProviders(new AbstractRequirement() {
+			@Override
+			public Map<String, Object> getAttributes() {
+				return Collections.emptyMap();
+			}
+
+			@Override
+			public Map<String, String> getDirectives() {
+				Map<String, String> directives = new HashMap<String, String>(1);
+				directives.put(
+						Constants.FILTER_DIRECTIVE, 
+						new StringBuilder()
+								.append('(')
+								.append(ResourceConstants.IDENTITY_TYPE_ATTRIBUTE)
+								.append('=')
+								.append(DeploymentManifest.IDENTITY_TYPE)
+								.append(')')
+								.toString());
+				return Collections.unmodifiableMap(directives);
+			}
+
+			@Override
+			public String getNamespace() {
+				return ResourceConstants.IDENTITY_NAMESPACE;
+			}
+
+			@Override
+			public Resource getResource() {
+				return null;
+			}
+		});
+		if (capabilities.isEmpty())
+			return null;
+		return (DeploymentManifest)capabilities.iterator().next().getResource();
+	}
+	
+	@Override
+	public List<Requirement> getRequirements(String namespace) {
+		return Collections.emptyList();
+	}
+	
+	public synchronized Collection<Resource> getResources() {
+		Collection<Capability> capabilities = findProviders(new AbstractRequirement() {
+			@Override
+			public Map<String, Object> getAttributes() {
+				return Collections.emptyMap();
+			}
+
+			@Override
+			public Map<String, String> getDirectives() {
+				Map<String, String> directives = new HashMap<String, String>(1);
+				directives.put(
+						Constants.FILTER_DIRECTIVE, 
+						new StringBuilder("(")
+								.append(ResourceConstants.IDENTITY_TYPE_ATTRIBUTE)
+								.append("=osgi.*)")
+								.toString());
+				return Collections.unmodifiableMap(directives);
+			}
+
+			@Override
+			public String getNamespace() {
+				return ResourceConstants.IDENTITY_NAMESPACE;
+			}
+
+			@Override
+			public Resource getResource() {
+				return null;
+			}
+		});
+		if (capabilities.isEmpty())
+			return Collections.emptyList();
+		Collection<Resource> resources = new HashSet<Resource>(capabilities.size());
+		for (Capability capability : capabilities) {
+			resources.add(capability.getResource());
+		}
+		return resources;
+	}
+	
+	public synchronized StaticDataFile getStaticDataFile() {
+		Collection<Capability> capabilities = findProviders(new AbstractRequirement() {
+			@Override
+			public Map<String, Object> getAttributes() {
+				return Collections.emptyMap();
+			}
+
+			@Override
+			public Map<String, String> getDirectives() {
+				Map<String, String> directives = new HashMap<String, String>(1);
+				directives.put(
+						Constants.FILTER_DIRECTIVE, 
+						new StringBuilder()
+								.append('(')
+								.append(ResourceConstants.IDENTITY_TYPE_ATTRIBUTE)
+								.append('=')
+								.append(StaticDataFile.IDENTITY_TYPE)
+								.append(')')
+								.toString());
+				return Collections.unmodifiableMap(directives);
+			}
+
+			@Override
+			public String getNamespace() {
+				return ResourceConstants.IDENTITY_NAMESPACE;
+			}
+
+			@Override
+			public Resource getResource() {
+				return null;
+			}
+		});
+		if (capabilities.isEmpty())
+			return null;
+		return (StaticDataFile)capabilities.iterator().next().getResource();
+	}
+	
+	public synchronized SubsystemManifest getSubsystemManifest() {
+		logger.debug(LOG_ENTRY, "getSubsystemManifest");
+		Collection<Capability> capabilities = findProviders(new AbstractRequirement() {
+			@Override
+			public Map<String, Object> getAttributes() {
+				return Collections.emptyMap();
+			}
+
+			@Override
+			public Map<String, String> getDirectives() {
+				Map<String, String> directives = new HashMap<String, String>(1);
+				directives.put(
+						Constants.FILTER_DIRECTIVE, 
+						new StringBuilder()
+								.append('(')
+								.append(ResourceConstants.IDENTITY_TYPE_ATTRIBUTE)
+								.append('=')
+								.append(SubsystemManifest.IDENTITY_TYPE)
+								.append(')')
+								.toString());
+				return Collections.unmodifiableMap(directives);
+			}
+
+			@Override
+			public String getNamespace() {
+				return ResourceConstants.IDENTITY_NAMESPACE;
+			}
+
+			@Override
+			public Resource getResource() {
+				return null;
+			}
+		});
+		SubsystemManifest result = null;
+		if (!capabilities.isEmpty())
+			result = (SubsystemManifest)capabilities.iterator().next().getResource();
+		logger.debug(LOG_EXIT, "getSubsystemManifest", result);
+		return result;
+	}
+	
+	public synchronized void setDataFile(DataFile dataFile) throws IOException {
+		DataFile old = getDataFile();
+		// TODO Add to constants.
+		File file = new File(directory, DataFile.IDENTITY_TYPE);
+		OutputStream out = new FileOutputStream(file);
+		try {
+			dataFile.write(out);
+			resources.remove(old);
+			resources.put(dataFile, file.toURI().toURL());
+		} finally {
+			out.close();
+		}
+	}
+	
+	public synchronized void setDeploymentManifest(DeploymentManifest manifest) throws IOException {
+		logger.debug(LOG_ENTRY, "setDeploymentManifest", manifest);
+		DeploymentManifest old = getDeploymentManifest();
+		// TODO Add to constants.
+		File file = new File(directory, "OSGI-INF");
+		if (!file.exists() && !file.mkdir())
+			throw new IOException("Unable to make directory for "
+					+ file.getCanonicalPath());
+		file = new File(file, "DEPLOYMENT.MF");
+		OutputStream out = new FileOutputStream(file);
+		try {
+			manifest.write(out);
+			resources.remove(old);
+			resources.put(manifest, file.toURI().toURL());
+		} finally {
+			out.close();
+		}
+		logger.debug(LOG_EXIT, "setDeploymentManifest");
+	}
+	
+	public synchronized void setSubsystemManifest(SubsystemManifest manifest) throws IOException {
+		logger.debug(LOG_ENTRY, "setSubsystemManifest", manifest);
+		SubsystemManifest old = getSubsystemManifest();
+		// TODO Add to constants.
+		File file = new File(directory, "OSGI-INF");
+		if (!file.exists() && !file.mkdir())
+			throw new IOException("Unable to make directory for " + file.getCanonicalPath());
+		file = new File(file, "SUBSYSTEM.MF");
+		OutputStream out = new FileOutputStream(file);
+		try {
+			manifest.write(out);
+			resources.remove(old);
+			resources.put(manifest, file.toURI().toURL());
+		}
+		finally {
+			out.close();
+		}
+		logger.debug(LOG_EXIT, "setSubsystemManifest");
+	}
+	
+	private Resource createResource(File file) throws Exception {
+		if (file.isDirectory() && file.getName().startsWith("subsystem"))
+			return new SubsystemArchive(file);
+		if (file.getName().endsWith(".jar"))
+			return BundleResource.newInstance(file.toURI().toURL());
+		else if (!file.getName().startsWith("subsystem") && file.getName().endsWith(".ssa"))
+			return new SubsystemFileResource(file);
+		else if (file.getName().endsWith("SUBSYSTEM.MF"))
+			return new SubsystemManifest(file);
+		else if (file.getName().endsWith("DEPLOYMENT.MF"))
+			return new DeploymentManifest(file);
+		else if (file.getName().endsWith(DataFile.IDENTITY_TYPE))
+			return new DataFile(file);
+		else if (file.getName().endsWith(StaticDataFile.IDENTITY_TYPE))
+			return new StaticDataFile(file);
+		logger.warn("Ignoring unsupported resource type: " + file.getCanonicalPath());
+		return null;
+	}
+	
+	private void processDirectory(File content) throws Exception {
+		logger.debug(LOG_ENTRY, "processDirectory", content);
+		for (File file : content.listFiles()) {
+			logger.debug("Processing file {}", file);
+			processFile(file);
+		}
+		logger.debug(LOG_EXIT, "processDirectory");
+	}
+	
+	private void processFile(File content) throws Exception {
+		logger.debug(LOG_ENTRY, "processFile", content);
+		if (content.isDirectory() && "OSGI-INF".equals(content.getName())) {
+			processDirectory(content);
+		}
+		else {
+			Resource resource = createResource(content);
+			if (resource != null) {
+				resources.put(resource, content.toURI().toURL());
+			}
+		}
+		logger.debug(LOG_EXIT, "processFile");
+	}
+}

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemContentHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemContentHeader.java?rev=1241900&r1=1241899&r2=1241900&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemContentHeader.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemContentHeader.java Wed Feb  8 13:54:41 2012
@@ -19,9 +19,11 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
+import org.apache.aries.subsystem.core.ResourceHelper;
 import org.apache.aries.util.VersionRange;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Version;
+import org.osgi.framework.resource.Resource;
 
 public class SubsystemContentHeader extends AbstractHeader {
 	public static class Content {
@@ -118,6 +120,19 @@ public class SubsystemContentHeader exte
 			}
 		});
 	}
+	
+	public boolean contains(Resource resource) {
+		String symbolicName = ResourceHelper.getSymbolicNameAttribute(resource);
+		Version version = ResourceHelper.getVersionAttribute(resource);
+		String type = ResourceHelper.getTypeAttribute(resource);
+		for (Content content : contents) {
+			if (symbolicName.equals(content.getName())
+					&& content.getVersionRange().matches(version)
+					&& type.equals(content.getType()))
+				return true;
+		}
+		return false;
+	}
 
 	public Collection<Content> getContents() {
 		return Collections.unmodifiableCollection(contents);

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemManifest.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemManifest.java?rev=1241900&r1=1241899&r2=1241900&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemManifest.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemManifest.java Wed Feb  8 13:54:41 2012
@@ -13,21 +13,34 @@
  */
 package org.apache.aries.subsystem.core.archive;
 
+import static org.apache.aries.application.utils.AppConstants.LOG_ENTRY;
+import static org.apache.aries.application.utils.AppConstants.LOG_EXIT;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 
+import org.apache.aries.subsystem.core.internal.OsgiIdentityCapability;
 import org.osgi.framework.Version;
 import org.osgi.framework.resource.Capability;
+import org.osgi.framework.resource.Requirement;
 import org.osgi.framework.resource.Resource;
 import org.osgi.framework.resource.ResourceConstants;
-import org.osgi.service.subsystem.SubsystemException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-public class SubsystemManifest extends Manifest {
+public class SubsystemManifest extends Manifest implements Resource {
+	public static final String IDENTITY_TYPE = "org.apache.aries.subsystem.manifest";
+	
+	private static final Logger logger = LoggerFactory.getLogger(SubsystemManifest.class);
+	
 	public static SubsystemManifest newInstance(String symbolicName, Version version, Collection<Resource> resources) {
-		if (resources.isEmpty())
-			throw new SubsystemException("A subsystem must have content");
+		if (logger.isDebugEnabled())
+			logger.debug(LOG_ENTRY, "newInstance", new Object[]{symbolicName, version, resources});
 		SubsystemManifest manifest = new SubsystemManifest();
 		manifest.headers.put(SubsystemTypeHeader.NAME, new SubsystemTypeHeader());
 		manifest.headers.put(ManifestVersionHeader.NAME, new ManifestVersionHeader());
@@ -49,9 +62,12 @@ public class SubsystemManifest extends M
 				// TODO Add to constants.
 				.append("type").append('=').append(type).append(',');
 		}
-		// Remove the trailing comma.
-		content.deleteCharAt(content.length() - 1);
-		manifest.headers.put(SubsystemContentHeader.NAME, new SubsystemContentHeader(content.toString()));
+		if (content.length() != 0) {
+			// Remove the trailing comma.
+			content.deleteCharAt(content.length() - 1);
+			manifest.headers.put(SubsystemContentHeader.NAME, new SubsystemContentHeader(content.toString()));
+		}
+		logger.debug(LOG_EXIT, "newInstance", manifest);
 		return manifest;
 	}
 
@@ -65,6 +81,31 @@ public class SubsystemManifest extends M
 
 	private SubsystemManifest() {}
 	
+	@Override
+	public List<Capability> getCapabilities(String namespace) {
+		List<Capability> result = new ArrayList<Capability>(1);
+		if (namespace == null || namespace.equals(ResourceConstants.IDENTITY_NAMESPACE)) {
+			OsgiIdentityCapability capability = new OsgiIdentityCapability(
+					this,
+					// TODO Reusing IDENTITY_TYPE for the symbolic name here.
+					// Since there's only one subsystem manifest per subsystem,
+					// this shouldn't cause any technical issues. However, it
+					// might be best to use the subsystem's symbolic name here.
+					// But there are issues with that as well since type is not
+					// part of the unique identity.
+					IDENTITY_TYPE,
+					Version.emptyVersion,
+					IDENTITY_TYPE);
+			result.add(capability);
+		}
+		return result;
+	}
+	
+	@Override
+	public List<Requirement> getRequirements(String namespace) {
+		return Collections.emptyList();
+	}
+	
 	public SubsystemContentHeader getSubsystemContent() {
 		return (SubsystemContentHeader)getHeader(SubsystemContentHeader.NAME);
 	}

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=1241900&r1=1241899&r2=1241900&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  8 13:54:41 2012
@@ -13,10 +13,11 @@
  */
 package org.apache.aries.subsystem.core.archive;
 
+import org.osgi.service.subsystem.SubsystemConstants;
+
 public class SubsystemManifestVersionHeader extends VersionHeader {
 	public static final String DEFAULT_VALUE = "1.0";
-	// TODO Add to constants.
-	public static final String NAME = "Subsystem-ManifestVersion";
+	public static final String NAME = SubsystemConstants.SUBSYSTEM_MANIFESTVERSION;
 	
 	public SubsystemManifestVersionHeader() {
 		this(DEFAULT_VALUE);

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemSymbolicNameHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemSymbolicNameHeader.java?rev=1241900&r1=1241899&r2=1241900&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemSymbolicNameHeader.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemSymbolicNameHeader.java Wed Feb  8 13:54:41 2012
@@ -13,21 +13,11 @@
  */
 package org.apache.aries.subsystem.core.archive;
 
-import java.util.regex.Pattern;
-
-public class SubsystemSymbolicNameHeader extends AbstractHeader {
+public class SubsystemSymbolicNameHeader extends SymbolicNameHeader {
 	// TODO Add to constants.
 	public static final String NAME = "Subsystem-SymbolicName";
 	
 	public SubsystemSymbolicNameHeader(String value) {
 		super(NAME, value);
-		if (getClauses().size() != 1)
-			throw new IllegalArgumentException(/* TODO Message */);
-		if (!Pattern.matches(Grammar.SYMBOLICNAME, getClauses().get(0).getPath()))
-			throw new IllegalArgumentException(/* TODO Message */);
-	}
-	
-	public String getSymbolicName() {
-		return getClauses().get(0).getPath();
 	}
 }

Added: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SymbolicNameHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SymbolicNameHeader.java?rev=1241900&view=auto
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SymbolicNameHeader.java (added)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SymbolicNameHeader.java Wed Feb  8 13:54:41 2012
@@ -0,0 +1,17 @@
+package org.apache.aries.subsystem.core.archive;
+
+import java.util.regex.Pattern;
+
+public abstract class SymbolicNameHeader extends AbstractHeader {
+	public SymbolicNameHeader(String name, String value) {
+		super(name, value);
+		if (getClauses().size() != 1)
+			throw new IllegalArgumentException("Symbolic name headers must have one, and only one, clause: " + getClauses().size());
+		if (!Pattern.matches(Grammar.SYMBOLICNAME, getClauses().get(0).getPath()))
+			throw new IllegalArgumentException("Invalid symbolic name: " + getClauses().get(0).getPath());
+	}
+	
+	public String getSymbolicName() {
+		return getClauses().get(0).getPath();
+	}
+}

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/Activator.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/Activator.java?rev=1241900&r1=1241899&r2=1241900&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/Activator.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/Activator.java Wed Feb  8 13:54:41 2012
@@ -13,30 +13,20 @@
  */
 package org.apache.aries.subsystem.core.internal;
 
+import static org.apache.aries.application.utils.AppConstants.LOG_ENTRY;
+import static org.apache.aries.application.utils.AppConstants.LOG_EXIT;
+
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Dictionary;
-import java.util.Hashtable;
 import java.util.List;
-import java.util.concurrent.Executor;
 
-import org.apache.felix.bundlerepository.RepositoryAdmin;
-import org.eclipse.equinox.region.RegionDigraph;
+import org.apache.felix.resolver.impl.ResolverImpl;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
+import org.osgi.framework.BundleListener;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.framework.hooks.resolver.ResolverHookFactory;
-import org.osgi.framework.wiring.FrameworkWiring;
-import org.osgi.service.coordinator.Coordinator;
-import org.osgi.service.event.EventAdmin;
-import org.osgi.service.event.EventConstants;
-import org.osgi.service.event.EventHandler;
-import org.osgi.service.repository.Repository;
 import org.osgi.service.resolver.Resolver;
 import org.osgi.service.subsystem.Subsystem;
-import org.osgi.util.tracker.ServiceTracker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -45,125 +35,79 @@ import org.slf4j.LoggerFactory;
  * activator will create and register the SubsystemAdmin service.
  */
 public class Activator implements BundleActivator {
-	private static final Logger LOGGER = LoggerFactory.getLogger(Activator.class);
-
-	private static BundleContext context;
-	
-	public static BundleContext getBundleContext() {
-		return context;
-	}
-	
-	public static Coordinator getCoordinator() {
-		return context.getService(context.getServiceReference(Coordinator.class));
-	}
-	
-	public static EventAdmin getEventAdmin() {
-		ServiceReference<EventAdmin> ref = context.getServiceReference(EventAdmin.class);
-		if (ref == null) return null;
-		return context.getService(ref);
-	}
-	
-	public static Executor getExecutor() {
-		return context.getService(context.getServiceReference(Executor.class));
-	}
+	private static final Logger logger = LoggerFactory.getLogger(Activator.class);
 	
-	public static FrameworkWiring getFrameworkWiring() {
-		return context.getBundle(0).adapt(FrameworkWiring.class);
-	}
+	private static Activator instance;
 	
-	public static RegionDigraph getRegionDigraph() {
-		return context.getService(context.getServiceReference(RegionDigraph.class));
+	public static synchronized Activator getInstance() {
+		logger.debug(LOG_ENTRY, "getInstance");
+		checkInstance();
+		logger.debug(LOG_EXIT, "getInstance", instance);
+		return instance;
 	}
 	
-	public static Collection<Repository> getRepositories() {
-		Collection<ServiceReference<Repository>> references;
-		try {
-			references = context.getServiceReferences(Repository.class, null);
-		}
-		catch (InvalidSyntaxException e) {
-			// This should never happen, but I don't want to cover it up. Nor do I want to force clients to handle it.
-			throw new RuntimeException(e);
-		}
-		ArrayList<Repository> repositories = new ArrayList<Repository>(references.size());
-		for (ServiceReference<Repository> reference : references) {
-			Repository repository = context.getService(reference);
-			if (repository != null)
-				repositories.add(repository);
-		}
-		repositories.trimToSize();
-		return repositories;
+	private static synchronized void checkInstance() {
+		logger.debug(LOG_ENTRY, "checkInstance");
+		if (instance == null)
+			throw new IllegalStateException("The activator has not been initialized or has been shutdown");
+		logger.debug(LOG_EXIT, "checkInstance");
 	}
 	
-	public static RepositoryAdmin getRepositoryAdmin() {
-		ServiceTracker st = new ServiceTracker(context, RepositoryAdmin.class.getName(), null);
-		try {
-			st.open();
-			return (RepositoryAdmin)st.waitForService(5000);
-		}
-		catch (InterruptedException e) {
-			Thread.currentThread().interrupt();
-			return null;
-		}
-		finally {
-			st.close();
-		}
-	}
-	
-	/*
-	 * TODO
-	 * A naive implementation serving as a placeholder until a real Resolver comes along.
-	 */
-	public static Resolver getResolver() {
-//		return new SubsystemResolver();
-		ServiceTracker st = new ServiceTracker(context, Resolver.class.getName(), null);
-		try {
-			st.open();
-			return (Resolver)st.waitForService(5000);
-		}
-		catch (InterruptedException e) {
-			Thread.currentThread().interrupt();
-			return null;
-		}
-		finally {
-			st.close();
-		}
-	}
-	
-//	private final BundleListener bundleListener = new SubsystemSynchronousBundleListener();
+	private final BundleListener bundleListener = new SubsystemSynchronousBundleListener();
 	private final List<ServiceRegistration<?>> registrations = new ArrayList<ServiceRegistration<?>>();
 	
-	public void start(final BundleContext context) throws Exception {
-		if (LOGGER.isDebugEnabled()) {
-			LOGGER.debug("subsystem activator starting");
-		}
-		Activator.context = context;
-		register(Subsystem.class.getName(), new SubsystemServiceFactory(), null);
-		register(ResolverHookFactory.class, new SubsystemResolverHookFactory(), null);
-//		context.getBundle(0).getBundleContext().addBundleListener(bundleListener);
-		Dictionary<String, Object> properties = new Hashtable<String, Object>();
-		properties.put(EventConstants.EVENT_TOPIC, new String[]{"org/osgi/framework/BundleEvent/*"});
-		register(EventHandler.class, new BundleEventHandler(), properties);
-	}
-
-	public void stop(BundleContext context) throws Exception {
-		if (LOGGER.isDebugEnabled()) {
-			LOGGER.debug("subsystem activator stopping");
-		}
-//		context.getBundle(0).getBundleContext().removeBundleListener(bundleListener);
-		for (ServiceRegistration<?> r : registrations) {
+	private BundleContext bundleContext;
+	private ServiceProviderImpl serviceProvider;
+	
+	public synchronized BundleContext getBundleContext() {
+		logger.debug(LOG_ENTRY, "getBundleContext");
+		BundleContext result = bundleContext;
+		logger.debug(LOG_EXIT, "getBundleContext", result);
+		return result;
+	}
+	
+	public synchronized ServiceProvider getServiceProvider() {
+		logger.debug(LOG_ENTRY, "getServiceProvider");
+		ServiceProvider result = serviceProvider;
+		logger.debug(LOG_EXIT, "getServiceProvider", result);
+		return result;
+	}
+
+	@Override
+	public synchronized void start(final BundleContext context) throws Exception {
+		logger.debug(LOG_ENTRY, "start", context);
+		synchronized (Activator.class) {
+			instance = this;
+		}
+		bundleContext = context;
+		serviceProvider = new ServiceProviderImpl(bundleContext);
+		context.getBundle(0).getBundleContext().addBundleListener(bundleListener);
+		registrations.add(bundleContext.registerService(ResolverHookFactory.class, new SubsystemResolverHookFactory(), null));
+		// TODO The registration of the Resolver service should be temporary, unless Felix 
+		// does not have an official release at the time.
+		registrations.add(bundleContext.registerService(Resolver.class, new ResolverImpl(null), null));
+		AriesSubsystem root = new AriesSubsystem();
+		registrations.add(bundleContext.registerService(Subsystem.class.getName(), root, null));
+		root.install();
+		logger.debug(LOG_EXIT, "start");
+	}
+
+	@Override
+	public synchronized void stop(BundleContext context) /*throws Exception*/ {
+		logger.debug(LOG_ENTRY, "stop", context);
+		for (int i = registrations.size() - 1; i >= 0; i--) {
 			try {
-				r.unregister();
-			} catch (Exception e) {
-				LOGGER.warn("Subsystem Activator shut down", e);
+				registrations.get(i).unregister();
+			}
+			catch (IllegalStateException e) {
+				logger.debug("Service had already been unregistered", e);
 			}
 		}
-	}
-
-	private <T> void register(Class<T> clazz, T service, Dictionary<String, ?> props) {
-		registrations.add(context.registerService(clazz.getName(), service, props));
-	}
-	
-	private <T> void register(String clazz, T service, Dictionary<String, ?> props) {
-		registrations.add(context.registerService(clazz, service, props));
+		context.getBundle(0).getBundleContext().removeBundleListener(bundleListener);
+		serviceProvider.shutdown();
+		synchronized (Activator.class) {
+			instance = null;
+		}
+		logger.debug(LOG_EXIT, "stop");
 	}
 }