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/03/14 19:47:59 UTC

svn commit: r1300686 - in /aries/trunk/subsystem: subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ subsystem-core/src/main/java/org/apache/aries/subsystem/core/...

Author: jwross
Date: Wed Mar 14 18:47:59 2012
New Revision: 1300686

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

Added initial support for provision-policy directive on Subsystem-Type header.

Added:
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ProvisionPolicyDirective.java
    aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/ProvisionPolicyTest.java
Modified:
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/BundleManifest.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/BundleVersionHeader.java
    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/DirectiveFactory.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemContentHeader.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemManifest.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemTypeHeader.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/SubsystemManifestValidator.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/SubsystemStreamResource.java
    aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/ApplicationTest.java
    aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/IntegrationTest.java
    aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/SubsystemTest.java

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/BundleManifest.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/BundleManifest.java?rev=1300686&r1=1300685&r2=1300686&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/BundleManifest.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/BundleManifest.java Wed Mar 14 18:47:59 2012
@@ -13,8 +13,17 @@
  */
 package org.apache.aries.subsystem.core.archive;
 
+import org.osgi.framework.Constants;
+
 public class BundleManifest extends Manifest {
 	public BundleManifest(java.util.jar.Manifest manifest) {
 		super(manifest);
+		fillInDefaults();
+	}
+	
+	private void fillInDefaults() {
+		Header<?> header = headers.get(Constants.BUNDLE_VERSION);
+		if (header == null)
+			headers.put(Constants.BUNDLE_VERSION, BundleVersionHeader.DEFAULT);
 	}
 }

Modified: 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=1300686&r1=1300685&r2=1300686&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/BundleVersionHeader.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/BundleVersionHeader.java Wed Mar 14 18:47:59 2012
@@ -7,6 +7,8 @@ public class BundleVersionHeader extends
 	public static final String DEFAULT_VALUE = Version.emptyVersion.toString();
 	public static final String NAME = Constants.BUNDLE_VERSION;
 	
+	public static final BundleVersionHeader DEFAULT = new BundleVersionHeader();
+	
 	public BundleVersionHeader() {
 		this(DEFAULT_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=1300686&r1=1300685&r2=1300686&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 Mar 14 18:47:59 2012
@@ -112,7 +112,7 @@ public class DeploymentManifest {
 			headers.put(SUBSYSTEM_SYMBOLICNAME, subsystemManifest.getSubsystemSymbolicNameHeader());
 			headers.put(SUBSYSTEM_VERSION, subsystemManifest.getSubsystemVersionHeader());
 			SubsystemTypeHeader typeHeader = subsystemManifest.getSubsystemTypeHeader();
-			if (SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION.equals(typeHeader.getValue())) {
+			if (typeHeader.isApplication()) {
 				if (resolution != null) {
 					Header<?> header = computeImportPackageHeader(resolution, deployedContent, acceptDependencies);
 					if (header != null)
@@ -126,7 +126,7 @@ public class DeploymentManifest {
 				}
 				// TODO Compute additional headers for an application.
 			}
-			else if (SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE.equals(typeHeader.getValue())) {
+			else if (typeHeader.isComposite()) {
 				Header<?> header = subsystemManifest.getImportPackageHeader();
 				if (header != null)
 					headers.put(IMPORT_PACKAGE, header);

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DirectiveFactory.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DirectiveFactory.java?rev=1300686&r1=1300685&r2=1300686&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DirectiveFactory.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DirectiveFactory.java Wed Mar 14 18:47:59 2012
@@ -25,6 +25,8 @@ public class DirectiveFactory {
 			return EffectiveDirective.getInstance(value);
 		if (VisibilityDirective.NAME.equals(name))
 			return VisibilityDirective.getInstance(value);
+		if (ProvisionPolicyDirective.NAME.equals(name))
+			return ProvisionPolicyDirective.getInstance(value);
 		return new GenericDirective(name, value);
 	}
 }

Added: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ProvisionPolicyDirective.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ProvisionPolicyDirective.java?rev=1300686&view=auto
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ProvisionPolicyDirective.java (added)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ProvisionPolicyDirective.java Wed Mar 14 18:47:59 2012
@@ -0,0 +1,42 @@
+package org.apache.aries.subsystem.core.archive;
+
+import org.osgi.service.subsystem.SubsystemConstants;
+
+public class ProvisionPolicyDirective extends AbstractDirective {
+	public static final String NAME = SubsystemConstants.PROVISION_POLICY_DIRECTIVE;
+	public static final String VALUE_ACCEPT_DEPENDENCIES = SubsystemConstants.PROVISION_POLICY_ACCEPT_DEPENDENCIES;
+	public static final String VALUE_REJECT_DEPENDENCIES = SubsystemConstants.PROVISION_POLICY_REJECT_DEPENDENCIES;
+	
+	public static final ProvisionPolicyDirective ACCEPT_DEPENDENCIES = new ProvisionPolicyDirective(VALUE_ACCEPT_DEPENDENCIES);
+	public static final ProvisionPolicyDirective REJECT_DEPENDENCIES = new ProvisionPolicyDirective(VALUE_REJECT_DEPENDENCIES);
+	
+	public static final ProvisionPolicyDirective DEFAULT = REJECT_DEPENDENCIES;
+	
+	public static ProvisionPolicyDirective getInstance(String value) {
+		if (VALUE_ACCEPT_DEPENDENCIES.equals(value))
+			return ACCEPT_DEPENDENCIES;
+		if (VALUE_REJECT_DEPENDENCIES.equals(value))
+			return REJECT_DEPENDENCIES;
+		return new ProvisionPolicyDirective(value);
+	}
+	
+	public ProvisionPolicyDirective(String value) {
+		super(NAME, value);
+		if (!(VALUE_ACCEPT_DEPENDENCIES.equals(value)
+				|| VALUE_REJECT_DEPENDENCIES.equals(value))) {
+			throw new IllegalArgumentException("Invalid " + NAME + " directive value: " + value);
+		}
+	}
+	
+	public String getProvisionPolicy() {
+		return getValue();
+	}
+	
+	public boolean isAcceptDependencies() {
+		return this == ACCEPT_DEPENDENCIES || VALUE_ACCEPT_DEPENDENCIES.equals(getProvisionPolicy());
+	}
+	
+	public boolean isRejectDependencies() {
+		return this == REJECT_DEPENDENCIES || VALUE_REJECT_DEPENDENCIES.equals(getProvisionPolicy());
+	}
+}

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=1300686&r1=1300685&r2=1300686&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 Mar 14 18:47:59 2012
@@ -18,12 +18,16 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.aries.subsystem.core.ResourceHelper;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Version;
 import org.osgi.framework.VersionRange;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.resource.Capability;
 import org.osgi.resource.Resource;
+import org.osgi.service.subsystem.SubsystemConstants;
 
 public class SubsystemContentHeader extends AbstractHeader {
 	public static class Content {
@@ -83,8 +87,26 @@ public class SubsystemContentHeader exte
 		}
 	}
 	
-	// TODO Add to constants.
-	public static final String NAME = "Subsystem-Content";
+	public static final String NAME = SubsystemConstants.SUBSYSTEM_CONTENT;
+	
+	private static String processResources(Collection<Resource> resources) {
+		if (resources.isEmpty())
+			throw new IllegalArgumentException("At least one resource must be specified");
+		StringBuilder sb = new StringBuilder();
+		for (Resource resource : resources) {
+			Capability c = resource.getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE).get(0);
+			Map<String, Object> a = c.getAttributes();
+			String s = (String)a.get(IdentityNamespace.IDENTITY_NAMESPACE);
+			Version v = (Version)a.get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE);
+			String t = (String)a.get(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE);
+			sb.append(s).append(';')
+				.append(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE).append('=').append(v).append(';')
+				.append(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE).append('=').append(t).append(',');
+		}
+		// Remove the trailing comma.
+		sb.deleteCharAt(sb.length() - 1);
+		return sb.toString();
+	}
 	
 	private final List<Content> contents;
 	
@@ -121,6 +143,10 @@ public class SubsystemContentHeader exte
 		});
 	}
 	
+	public SubsystemContentHeader(Collection<Resource> resources) {
+		this(processResources(resources));
+	}
+	
 	public boolean contains(Resource resource) {
 		String symbolicName = ResourceHelper.getSymbolicNameAttribute(resource);
 		Version version = ResourceHelper.getVersionAttribute(resource);

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=1300686&r1=1300685&r2=1300686&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 Mar 14 18:47:59 2012
@@ -16,12 +16,60 @@ import java.util.jar.Manifest;
 import org.apache.aries.util.manifest.ManifestProcessor;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Version;
-import org.osgi.framework.namespace.IdentityNamespace;
-import org.osgi.resource.Capability;
 import org.osgi.resource.Resource;
 import org.osgi.service.subsystem.SubsystemConstants;
 
 public class SubsystemManifest {
+	public static class Builder {
+		private Map<String, Header<?>> headers = new HashMap<String, Header<?>>();
+		
+		public Builder(String symbolicName) {
+			headers.put(SUBSYSTEM_SYMBOLICNAME, new SubsystemSymbolicNameHeader(symbolicName));
+		}
+		
+		public SubsystemManifest build() {
+			return new SubsystemManifest(headers);
+		}
+		
+		public Builder content(String value) {
+			return value == null ? this : content(new SubsystemContentHeader(value));
+		}
+		
+		public Builder content(Collection<Resource> value) {
+			return value == null || value.isEmpty() ? this : content(new SubsystemContentHeader(value));
+		}
+		
+		public Builder content(SubsystemContentHeader value) {
+			return header(value);
+		}
+		
+		public Builder header(Header<?> value) {
+			if (value != null)
+				headers.put(value.getName(), value);
+			return this;
+		}
+		
+		public Builder type(String value) {
+			return value == null ? this : type(new SubsystemTypeHeader(value));
+		}
+		
+		public Builder type(SubsystemTypeHeader value) {
+			return header(value);
+		}
+		
+		public Builder version(String value) {
+			return value == null ? this : version(Version.parseVersion(value));
+		}
+		
+		public Builder version(Version value) {
+			return value == null ? this : version(new SubsystemVersionHeader(value));
+		}
+		
+		public Builder version(SubsystemVersionHeader value) {
+			return header(value);
+		}
+	}
+	
 	public static final String EXPORT_PACKAGE = Constants.EXPORT_PACKAGE;
 	public static final String IMPORT_PACKAGE = Constants.IMPORT_PACKAGE;
 	public static final String PREFERRED_PROVIDER = SubsystemConstants.PREFERRED_PROVIDER;
@@ -38,8 +86,24 @@ public class SubsystemManifest {
 	public static final String SUBSYSTEM_TYPE = SubsystemConstants.SUBSYSTEM_TYPE;
 	public static final String SUBSYSTEM_VERSION = SubsystemConstants.SUBSYSTEM_VERSION;
 	
+	private static void fillInDefaults(Map<String, Header<?>> headers) {
+		Header<?> header = headers.get(SUBSYSTEM_VERSION);
+		if (header == null) {
+			headers.put(SUBSYSTEM_VERSION, SubsystemVersionHeader.DEFAULT);
+		}
+		header = headers.get(SUBSYSTEM_TYPE);
+		if (header == null)
+			headers.put(SUBSYSTEM_TYPE, SubsystemTypeHeader.DEFAULT);
+	}
+	
 	private final Map<String, Header<?>> headers;
 	
+	private SubsystemManifest(Map<String, Header<?>> headers) {
+		Map<String, Header<?>> map = new HashMap<String, Header<?>>(headers);
+		fillInDefaults(map);
+		this.headers = Collections.unmodifiableMap(map);
+	}
+	
 	public SubsystemManifest(File file) throws FileNotFoundException, IOException {
 		Manifest manifest = ManifestProcessor.parseManifest(new FileInputStream(file));
 		Attributes attributes = manifest.getMainAttributes();
@@ -48,13 +112,7 @@ public class SubsystemManifest {
 			String key = String.valueOf(entry.getKey());
 			headers.put(key, HeaderFactory.createHeader(key, String.valueOf(entry.getValue())));
 		}
-		Header<?> header = headers.get(SUBSYSTEM_VERSION);
-		if (header == null) {
-			headers.put(SUBSYSTEM_VERSION, SubsystemVersionHeader.DEFAULT);
-		}
-		header = headers.get(SUBSYSTEM_TYPE);
-		if (header == null)
-			headers.put(SUBSYSTEM_TYPE, SubsystemTypeHeader.DEFAULT);
+		fillInDefaults(headers);
 		this.headers = Collections.unmodifiableMap(headers);
 	}
 	
@@ -74,35 +132,14 @@ public class SubsystemManifest {
 		if (header == null)
 			headers.put(SUBSYSTEM_SYMBOLICNAME, new SubsystemSymbolicNameHeader(symbolicName));
 		header = headers.get(SUBSYSTEM_VERSION);
-		if (header == null) {
-			if (version == null)
-				headers.put(SUBSYSTEM_VERSION, SubsystemVersionHeader.DEFAULT);
-			else
-				headers.put(SUBSYSTEM_VERSION, new SubsystemVersionHeader(version));
+		if (header == null && !(version == null)) {
+			headers.put(SUBSYSTEM_VERSION, new SubsystemVersionHeader(version));
 		}
 		header = headers.get(SUBSYSTEM_CONTENT);
 		if (header == null && content != null && !content.isEmpty()) {
-			// TODO Better way than using StringBuilder? Would require a more robust SubsystemContentHeader in order to fulfill the Header contract.
-			StringBuilder sb = new StringBuilder();
-			for (Resource resource : content) {
-				Capability c = resource.getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE).get(0);
-				Map<String, Object> a = c.getAttributes();
-				String s = (String)a.get(IdentityNamespace.IDENTITY_NAMESPACE);
-				Version v = (Version)a.get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE);
-				String t = (String)a.get(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE);
-				sb.append(s).append(';')
-					.append(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE).append('=').append(v).append(';')
-					.append(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE).append('=').append(t).append(',');
-			}
-			if (sb.length() != 0) {
-				// Remove the trailing comma.
-				sb.deleteCharAt(sb.length() - 1);
-				headers.put(SubsystemContentHeader.NAME, new SubsystemContentHeader(sb.toString()));
-			}
+			headers.put(SubsystemContentHeader.NAME, new SubsystemContentHeader(content));
 		}
-		header = headers.get(SUBSYSTEM_TYPE);
-		if (header == null)
-			headers.put(SUBSYSTEM_TYPE, SubsystemTypeHeader.DEFAULT);
+		fillInDefaults(headers);
 		this.headers = Collections.unmodifiableMap(headers);
 	}
 	

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemTypeHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemTypeHeader.java?rev=1300686&r1=1300685&r2=1300686&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemTypeHeader.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemTypeHeader.java Wed Mar 14 18:47:59 2012
@@ -13,18 +13,193 @@
  */
 package org.apache.aries.subsystem.core.archive;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 import org.osgi.service.subsystem.SubsystemConstants;
 
-public class SubsystemTypeHeader extends AbstractHeader {
-	public static final SubsystemTypeHeader DEFAULT = new SubsystemTypeHeader();
-	public static final String DEFAULT_VALUE = SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION;
+public class SubsystemTypeHeader implements Header<SubsystemTypeHeader.Clause> {
+	public static class Clause implements org.apache.aries.subsystem.core.archive.Clause {
+		private static final Pattern PATTERN_TYPE = Pattern.compile('(' + TYPE_APPLICATION + '|' + TYPE_COMPOSITE + '|' + TYPE_FEATURE + ")(?=;|\\z)");
+		private static final Pattern PATTERN_PARAMETER = Pattern.compile('(' + Grammar.PARAMETER + ")(?=;|\\z)");
+		private static final Pattern PATTERN_PROVISION_POLICY = Pattern.compile(PROVISION_POLICY_ACCEPT_DEPENDENCIES + '|' + PROVISION_POLICY_REJECT_DEPENDENCIES);
+		
+		private static void fillInDefaults(Map<String, Parameter> parameters) {
+			Parameter parameter = parameters.get(DIRECTIVE_PROVISION_POLICY);
+			if (parameter == null)
+				parameter = ProvisionPolicyDirective.REJECT_DEPENDENCIES;
+			String value = ((Directive)parameter).getValue();
+			if (!PATTERN_PROVISION_POLICY.matcher(value).matches())
+				throw new IllegalArgumentException("Invalid " + DIRECTIVE_PROVISION_POLICY + " directive: " + value);
+			parameters.put(DIRECTIVE_PROVISION_POLICY, parameter);
+		}
+		
+		private final String path;
+		private final Map<String, Parameter> parameters = new HashMap<String, Parameter>();
+		
+		public Clause(String clause) {
+			Matcher matcher = PATTERN_TYPE.matcher(clause);
+			if (!matcher.find())
+				throw new IllegalArgumentException("Invalid subsystem type: " + clause);
+			path = matcher.group();
+			matcher.usePattern(PATTERN_PARAMETER);
+			while (matcher.find()) {
+				Parameter parameter = ParameterFactory.create(matcher.group());
+				parameters.put(parameter.getName(), parameter);
+			}
+			fillInDefaults(parameters);
+		}
+		
+		@Override
+		public Attribute getAttribute(String name) {
+			Parameter result = parameters.get(name);
+			if (result instanceof Attribute) {
+				return (Attribute)result;
+			}
+			return null;
+		}
+
+		@Override
+		public Collection<Attribute> getAttributes() {
+			ArrayList<Attribute> attributes = new ArrayList<Attribute>(parameters.size());
+			for (Parameter parameter : parameters.values()) {
+				if (parameter instanceof Attribute) {
+					attributes.add((Attribute)parameter);
+				}
+			}
+			attributes.trimToSize();
+			return attributes;
+		}
+
+		@Override
+		public Directive getDirective(String name) {
+			Parameter result = parameters.get(name);
+			if (result instanceof Directive) {
+				return (Directive)result;
+			}
+			return null;
+		}
+
+		@Override
+		public Collection<Directive> getDirectives() {
+			ArrayList<Directive> directives = new ArrayList<Directive>(parameters.size());
+			for (Parameter parameter : parameters.values()) {
+				if (parameter instanceof Directive) {
+					directives.add((Directive)parameter);
+				}
+			}
+			directives.trimToSize();
+			return directives;
+		}
+
+		@Override
+		public Parameter getParameter(String name) {
+			return parameters.get(name);
+		}
+
+		@Override
+		public Collection<Parameter> getParameters() {
+			return Collections.unmodifiableCollection(parameters.values());
+		}
+
+		@Override
+		public String getPath() {
+			return path;
+		}
+		
+		public ProvisionPolicyDirective getProvisionPolicyDirective() {
+			return (ProvisionPolicyDirective)getDirective(DIRECTIVE_PROVISION_POLICY);
+		}
+		
+		public String getType() {
+			return path;
+		}
+		
+		@Override
+		public String toString() {
+			StringBuilder builder = new StringBuilder()
+					.append(getPath());
+			for (Parameter parameter : getParameters()) {
+				builder.append(';').append(parameter);
+			}
+			return builder.toString();
+		}
+	}
+	
+	public static final String DIRECTIVE_PROVISION_POLICY = SubsystemConstants.PROVISION_POLICY_DIRECTIVE;
 	public static final String NAME = SubsystemConstants.SUBSYSTEM_TYPE;
+	public static final String PROVISION_POLICY_ACCEPT_DEPENDENCIES = SubsystemConstants.PROVISION_POLICY_ACCEPT_DEPENDENCIES;
+	public static final String PROVISION_POLICY_REJECT_DEPENDENCIES = SubsystemConstants.PROVISION_POLICY_REJECT_DEPENDENCIES;
+	public static final String TYPE_APPLICATION = SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION;
+	public static final String TYPE_COMPOSITE = SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE;
+	public static final String TYPE_FEATURE = SubsystemConstants.SUBSYSTEM_TYPE_FEATURE;
 	
-	public SubsystemTypeHeader() {
-		this(DEFAULT_VALUE);
+	public static final SubsystemTypeHeader DEFAULT = new SubsystemTypeHeader(TYPE_APPLICATION);
+	
+	private final Clause clause;
+	
+	public SubsystemTypeHeader(Clause clause) {
+		if (clause == null)
+			throw new NullPointerException("Missing required parameter: clause");
+		this.clause = clause;
 	}
-
+	
 	public SubsystemTypeHeader(String value) {
-		super(NAME, value);
+		this(new Clause(value));
+	}
+	
+	public Clause getClause() {
+		return clause;
+	}
+	
+	@Override
+	public Collection<SubsystemTypeHeader.Clause> getClauses() {
+		return Collections.singleton(clause);
+	}
+
+	@Override
+	public String getName() {
+		return NAME;
+	}
+	
+	public ProvisionPolicyDirective getProvisionPolicyDirective() {
+		return clause.getProvisionPolicyDirective();
+	}
+	
+	public String getType() {
+		return clause.getType();
+	}
+
+	@Override
+	public String getValue() {
+		return toString();
+	}
+	
+	public boolean isApplication() {
+		return this == DEFAULT || TYPE_APPLICATION.equals(getType());
+	}
+	
+	public boolean isComposite() {
+		return TYPE_COMPOSITE.equals(getType());
+	}
+	
+	public boolean isFeature() {
+		return TYPE_FEATURE.equals(getType());
+	}
+	
+	@Override
+	public String toString() {
+		StringBuilder builder = new StringBuilder();
+		for (Clause clause : getClauses()) {
+			builder.append(clause).append(',');
+		}
+		// Remove the trailing comma. Note at least one clause is guaranteed to exist.
+		builder.deleteCharAt(builder.length() - 1);
+		return builder.toString();
 	}
 }

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=1300686&r1=1300685&r2=1300686&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 Mar 14 18:47:59 2012
@@ -65,6 +65,7 @@ import org.apache.aries.subsystem.core.a
 import org.apache.aries.subsystem.core.archive.SubsystemImportServiceHeader;
 import org.apache.aries.subsystem.core.archive.SubsystemImportServiceRequirement;
 import org.apache.aries.subsystem.core.archive.SubsystemManifest;
+import org.apache.aries.subsystem.core.archive.SubsystemTypeHeader;
 import org.apache.aries.subsystem.core.resource.SubsystemDirectoryResource;
 import org.apache.aries.subsystem.core.resource.SubsystemFileResource;
 import org.apache.aries.subsystem.core.resource.SubsystemStreamResource;
@@ -272,16 +273,28 @@ public class AriesSubsystem implements S
 		// TODO The creation of the subsystem manifest is in two places. See other constructor.
 		SubsystemManifest subsystemManifest = archive.getSubsystemManifest();
 		if (subsystemManifest == null) {
-			// This is the first time the root subsystem has been initialized in this framework or
-			// a framework clean start was requested.
+			// This is the first time the root subsystem has been initialized in
+			// this framework or a framework clean start was requested.
 			SubsystemUri uri = new SubsystemUri(ROOT_LOCATION);
-			subsystemManifest = new SubsystemManifest(uri.getSymbolicName(), uri.getVersion(), archive.getResources());
+			subsystemManifest = new SubsystemManifest.Builder(
+					uri.getSymbolicName())
+					.version(uri.getVersion())
+					.content(archive.getResources())
+					.type(SubsystemTypeHeader.TYPE_APPLICATION
+							+ ';'
+							+ SubsystemTypeHeader.DIRECTIVE_PROVISION_POLICY
+							+ ":="
+							+ SubsystemTypeHeader.PROVISION_POLICY_ACCEPT_DEPENDENCIES)
+					.build();
 			archive.setSubsystemManifest(subsystemManifest);
 		}
-		else
+		else {
 			// Need to generate a new subsystem manifest in order to generated a new deployment manifest based
 			// on any persisted resources.
-			subsystemManifest = new SubsystemManifest(getSymbolicName(), getVersion(), archive.getResources());
+			subsystemManifest = new SubsystemManifest.Builder(getSymbolicName())
+					.version(getVersion()).content(archive.getResources())
+					.build();
+		}
 		environment = new SubsystemEnvironment(this);
 		// The root subsystem establishes the subsystem graph;
 		subsystemGraph = new SubsystemGraph(this);
@@ -349,10 +362,12 @@ public class AriesSubsystem implements S
 			else
 				region = createRegion(getSymbolicName() + ';' + getVersion() + ';' + getType() + ';' + getSubsystemId());
 		}
-		catch (Exception e) {
+		catch (Throwable t) {
 			deleteFile(directory);
 			deleteFile(zipFile);
-			throw new SubsystemException(e);
+			if (t instanceof SubsystemException)
+				throw (SubsystemException)t;
+			throw new SubsystemException(t);
 		}
 		subsystemGraph = parent.subsystemGraph;
 	}
@@ -461,7 +476,7 @@ public class AriesSubsystem implements S
 	
 	@Override
 	public String getType() {
-		return archive.getSubsystemManifest().getSubsystemTypeHeader().getValue();
+		return archive.getSubsystemManifest().getSubsystemTypeHeader().getType();
 	}
 
 	@Override
@@ -481,36 +496,33 @@ public class AriesSubsystem implements S
 		try {
 			result = install(location, content, coordination);
 		}
-		catch (Exception e) {
-			coordination.fail(e);
+		catch (Throwable t) {
+			coordination.fail(t);
 		}
 		finally {
 			try {
 				coordination.end();
 			}
 			catch (CoordinationException e) {
-				throw new SubsystemException(e);
+				Throwable t = e.getCause();
+				if (t instanceof SubsystemException)
+					throw (SubsystemException)t;
+				throw new SubsystemException(t);
 			}
 		}
 		return result;
 	}
 	
 	public boolean isApplication() {
-		return SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION.equals(archive
-				.getSubsystemManifest().getHeaders()
-				.get(SubsystemManifest.SUBSYSTEM_TYPE).getValue());
+		return archive.getSubsystemManifest().getSubsystemTypeHeader().isApplication();
 	}
 
 	public boolean isComposite() {
-		return SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE.equals(archive
-				.getSubsystemManifest().getHeaders()
-				.get(SubsystemManifest.SUBSYSTEM_TYPE).getValue());
+		return archive.getSubsystemManifest().getSubsystemTypeHeader().isComposite();
 	}
 
 	public boolean isFeature() {
-		return SubsystemConstants.SUBSYSTEM_TYPE_FEATURE.equals(archive
-				.getSubsystemManifest().getHeaders()
-				.get(SubsystemManifest.SUBSYSTEM_TYPE).getValue());
+		return archive.getSubsystemManifest().getSubsystemTypeHeader().isFeature();
 	}
 	
 	/* INSTALLING	Wait, Start
@@ -778,12 +790,13 @@ public class AriesSubsystem implements S
 	}
 	
 	private AriesSubsystem getProvisionTo(Resource resource, boolean transitive) {
-		// Application and composite resources are provisioned into the application or composite.
+		// Content resources are provisioned into the subsystem that declares
+		// them.
 		AriesSubsystem provisionTo = this;
 		if (transitive) {
-			// Transitive dependencies should be provisioned into the highest possible level.
-			// TODO Assumes root is always the appropriate level.
-			while (!provisionTo.getParents().isEmpty())
+			// Transitive dependencies should be provisioned into the first
+			// subsystem that accepts dependencies.
+			while (provisionTo.archive.getSubsystemManifest().getSubsystemTypeHeader().getProvisionPolicyDirective().isRejectDependencies())
 				provisionTo = (AriesSubsystem)provisionTo.getParents().iterator().next();
 		}
 		return provisionTo;
@@ -883,6 +896,9 @@ public class AriesSubsystem implements S
 			installSubsystemResource(subsystem, coordination, false);
 			return subsystem;
 		}
+		catch (SubsystemException e) {
+			throw e;
+		}
 		catch (Exception e) {
 			throw new SubsystemException(e);
 		}
@@ -897,10 +913,10 @@ public class AriesSubsystem implements S
 		final BundleRevision revision;
 		AriesSubsystem provisionTo = getProvisionTo(resource, transitive);
 		if (resource instanceof BundleRevision) {
-			// This means the resource is a bundle that's already been installed, but we still need to establish the resource->subsystem relationship.
+			// This means the resource is an already installed bundle.
 			revision = (BundleRevision)resource;
 			// Need to simulate the install process since an install does not
-			// actually occur here and the event hook is not called.
+			// actually occur here, and the event hook is not called.
 			provisionTo.bundleInstalled(revision);
 		}
 		else {

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=1300686&r1=1300685&r2=1300686&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 Mar 14 18:47:59 2012
@@ -56,7 +56,7 @@ public class OsgiIdentityCapability exte
 				resource,
 				manifest.getSubsystemSymbolicNameHeader().getSymbolicName(),
 				manifest.getSubsystemVersionHeader().getVersion(),
-				manifest.getSubsystemTypeHeader().getValue());
+				manifest.getSubsystemTypeHeader().getType());
 	}
 	
 	public OsgiIdentityCapability(Resource resource, BundleManifest manifest) {

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemManifestValidator.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemManifestValidator.java?rev=1300686&r1=1300685&r2=1300686&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemManifestValidator.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemManifestValidator.java Wed Mar 14 18:47:59 2012
@@ -17,6 +17,11 @@ public class SubsystemManifestValidator 
 					throw new SubsystemException("Composite subsystem using version range for content: " + content);
 			}
 		}
+		else if (subsystem.isFeature()) {
+			if (manifest.getSubsystemTypeHeader().getProvisionPolicyDirective().isAcceptDependencies()) {
+				throw new SubsystemException("Feature subsystems may not declare a provision-policy of acceptDependencies");
+			}
+		}
 	}
 	
 	private static boolean isExactVersion(VersionRange range) {

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=1300686&r1=1300685&r2=1300686&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 Mar 14 18:47:59 2012
@@ -25,7 +25,7 @@ public class SubsystemDirectoryResource 
 				this, 
 				manifest.getSubsystemSymbolicNameHeader().getSymbolicName(), 
 				manifest.getSubsystemVersionHeader().getVersion(), 
-				manifest.getSubsystemTypeHeader().getValue()));
+				manifest.getSubsystemTypeHeader().getType()));
 		this.capabilities = Collections.unmodifiableList(capabilities);
 	}
 	

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=1300686&r1=1300685&r2=1300686&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 Mar 14 18:47:59 2012
@@ -70,7 +70,7 @@ public class SubsystemStreamResource imp
 				version = Version.parseVersion(value);
 			value = manifest.getMainAttributes().getValue(SubsystemConstants.SUBSYSTEM_TYPE);
 			if (value != null)
-				type = new SubsystemTypeHeader(value).getValue();
+				type = new SubsystemTypeHeader(value).getType();
 		}
 		if (symbolicName == null) {
 			if (uri == null)

Modified: aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/ApplicationTest.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/ApplicationTest.java?rev=1300686&r1=1300685&r2=1300686&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/ApplicationTest.java (original)
+++ aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/ApplicationTest.java Wed Mar 14 18:47:59 2012
@@ -167,7 +167,7 @@ public class ApplicationTest extends Sub
 		createdApplications = true;
 	}
 	
-	public void setUp() {
+	public void setUp() throws Exception {
 		super.setUp();
 		try {
 			serviceRegistrations.add(
@@ -266,7 +266,7 @@ public class ApplicationTest extends Sub
     			.namespace(PackageNamespace.PACKAGE_NAMESPACE)
     			.attribute(PackageNamespace.PACKAGE_NAMESPACE, "org.apache.aries.subsystem.itests.tb3")
     			.attribute(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE, "0.0.0"))
-    			.content(createTestBundle3Content())
+    	.content(createTestBundle3Content())
     	.build();
     }
     

Modified: aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/IntegrationTest.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/IntegrationTest.java?rev=1300686&r1=1300685&r2=1300686&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/IntegrationTest.java (original)
+++ aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/IntegrationTest.java Wed Mar 14 18:47:59 2012
@@ -53,7 +53,7 @@ public abstract class IntegrationTest {
     private Map<String, ServiceTracker> srs;
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         srs = new HashMap<String, ServiceTracker>();
     }
     

Added: aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/ProvisionPolicyTest.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/ProvisionPolicyTest.java?rev=1300686&view=auto
==============================================================================
--- aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/ProvisionPolicyTest.java (added)
+++ aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/ProvisionPolicyTest.java Wed Mar 14 18:47:59 2012
@@ -0,0 +1,241 @@
+package org.apache.aries.subsystem.itests;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.framework.Constants;
+import org.osgi.service.subsystem.Subsystem;
+import org.osgi.service.subsystem.SubsystemConstants;
+import org.osgi.service.subsystem.SubsystemException;
+
+@RunWith(JUnit4TestRunner.class)
+public class ProvisionPolicyTest extends SubsystemTest {
+	/*
+	 * Subsystem-SymbolicName: application.a.esa
+	 * Subsystem-Type: osgi.subsystem.application;provision-policy:=acceptDependencies
+	 */
+	private static final String APPLICATION_A = "application.a.esa";
+	/*
+	 * Bundle-SymbolicName: bundle.a.jar
+	 * Import-Package: x
+	 */
+	private static final String BUNDLE_A = "bundle.a.jar";
+	/*
+	 * Bundle-SymbolicName: bundle.b.jar
+	 * Export-Package: x
+	 */
+	private static final String BUNDLE_B = "bundle.b.jar";
+	/*
+	 * Subsystem-SymbolicName: composite.a.esa
+	 * Subsystem-Type: osgi.subsystem.composite
+	 * Import-Package: x
+	 */
+	private static final String COMPOSITE_A = "composite.a.esa";
+	/*
+	 * Subsystem-SymbolicName: feature.a.esa
+	 * Subsystem-Type: osgi.subsystem.feature;provision-policy:=acceptDependencies
+	 */
+	private static final String FEATURE_A = "feature.a.esa";
+	/*
+	 * Subsystem-SymbolicName: feature.b.esa
+	 * Subsystem-Type: osgi.subsystem.feature
+	 * Subsystem-Content: bundle.a.jar
+	 */
+	private static final String FEATURE_B = "feature.b.esa";
+	
+	private static void createApplicationA() throws IOException {
+		createApplicationAManifest();
+		createSubsystem(APPLICATION_A, BUNDLE_A);
+	}
+	
+	private static void createApplicationAManifest() throws IOException {
+		Map<String, String> attributes = new HashMap<String, String>();
+		attributes.put(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, APPLICATION_A);
+		attributes
+		.put(SubsystemConstants.SUBSYSTEM_TYPE,
+				SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION
+						+ ';'
+						+ SubsystemConstants.PROVISION_POLICY_DIRECTIVE
+						+ ":="
+						+ SubsystemConstants.PROVISION_POLICY_ACCEPT_DEPENDENCIES);
+		createManifest(APPLICATION_A + ".mf", attributes);
+	}
+	
+	private static void createBundleA() throws IOException {
+		Map<String, String> headers = new HashMap<String, String>();
+		headers.put(Constants.IMPORT_PACKAGE, "x");
+		createBundle(BUNDLE_A, headers);
+	}
+	
+	private static void createBundleB() throws IOException {
+		Map<String, String> headers = new HashMap<String, String>();
+		headers.put(Constants.EXPORT_PACKAGE, "x");
+		createBundle(BUNDLE_B, headers);
+	}
+	
+	private static void createCompositeA() throws IOException {
+		createCompositeAManifest();
+		createSubsystem(COMPOSITE_A);
+	}
+	
+	private static void createCompositeAManifest() throws IOException {
+		Map<String, String> attributes = new HashMap<String, String>();
+		attributes.put(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, COMPOSITE_A);
+		attributes.put(SubsystemConstants.SUBSYSTEM_TYPE, SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE);
+		attributes.put(Constants.IMPORT_PACKAGE, "x");
+		createManifest(COMPOSITE_A + ".mf", attributes);
+	}
+	
+	private static void createFeatureA() throws IOException {
+		createFeatureAManifest();
+		createSubsystem(FEATURE_A);
+	}
+	
+	private static void createFeatureAManifest() throws IOException {
+		Map<String, String> attributes = new HashMap<String, String>();
+		attributes.put(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, FEATURE_A);
+		attributes
+				.put(SubsystemConstants.SUBSYSTEM_TYPE,
+						SubsystemConstants.SUBSYSTEM_TYPE_FEATURE
+								+ ';'
+								+ SubsystemConstants.PROVISION_POLICY_DIRECTIVE
+								+ ":="
+								+ SubsystemConstants.PROVISION_POLICY_ACCEPT_DEPENDENCIES);
+		createManifest(FEATURE_A + ".mf", attributes);
+	}
+	
+	private static void createFeatureB() throws IOException {
+		createFeatureBManifest();
+		createSubsystem(FEATURE_B, BUNDLE_A);
+	}
+	
+	private static void createFeatureBManifest() throws IOException {
+		Map<String, String> attributes = new HashMap<String, String>();
+		attributes.put(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, FEATURE_B);
+		attributes.put(SubsystemConstants.SUBSYSTEM_TYPE, SubsystemConstants.SUBSYSTEM_TYPE_FEATURE);
+		attributes.put(SubsystemConstants.SUBSYSTEM_CONTENT, BUNDLE_A);
+		createManifest(FEATURE_B + ".mf", attributes);
+	}
+	
+	private static boolean createdTestFiles;
+	@Before
+	public static void createTestFiles() throws Exception {
+		if (createdTestFiles)
+			return;
+		createBundleA();
+		createBundleB();
+		createApplicationA();
+		createCompositeA();
+		createFeatureA();
+		createFeatureB();
+		createdTestFiles = true;
+	}
+	
+	public void setUp() throws Exception {
+		super.setUp();
+		Subsystem root = getRootSubsystem();
+		assertProvisionPolicy(root, true);
+		registerRepositoryService(BUNDLE_B);
+	}
+	
+	@Test
+	public void testFailInstallFeatureAcceptDependencies() throws Exception {
+		Subsystem subsystem = null;
+		try {
+			subsystem = installSubsystemFromFile(FEATURE_A);
+			fail("Feature with provision-policy:=acceptDependencies did not fail installation");
+		}
+		catch (SubsystemException e) {
+			// TODO Brittle...
+			assertTrue(e.getMessage().contains("Feature subsystems may not declare a provision-policy of acceptDependencies"));
+		}
+		finally {
+			uninstallSubsystemSilently(subsystem);
+		}
+	}
+	
+	@Test
+	public void testProvisionToNonRootAncestor() throws Exception {
+		Subsystem root = getRootSubsystem();
+		Subsystem application = installSubsystemFromFile(root, APPLICATION_A);
+		try {
+			assertProvisionPolicy(application, true);
+			Subsystem composite = installSubsystemFromFile(application, COMPOSITE_A);
+			try {
+				assertProvisionPolicy(composite, false);
+				Subsystem feature = installSubsystemFromFile(composite, FEATURE_B);
+				try {
+					assertProvisionPolicy(feature, false);
+					assertConstituent(feature, BUNDLE_A);
+					assertNotConstituent(feature, BUNDLE_B);
+					assertNotConstituent(composite, BUNDLE_A);
+					assertNotConstituent(composite, BUNDLE_B);
+					assertConstituent(application, BUNDLE_A);
+					assertConstituent(application, BUNDLE_B);
+					assertNotConstituent(root, BUNDLE_A);
+					assertNotConstituent(root, BUNDLE_B);
+				}
+				finally {
+					uninstallSubsystemSilently(feature);
+				}
+			}
+			finally {
+				uninstallSubsystemSilently(composite);
+			}
+		}
+		finally {
+			uninstallSubsystemSilently(application);
+		}
+	}
+	
+	@Test
+	public void testProvisionToRoot() throws Exception {
+		Subsystem root = getRootSubsystem();
+		Subsystem composite = installSubsystemFromFile(root, COMPOSITE_A);
+		try {
+			assertProvisionPolicy(composite, false);
+			Subsystem feature = installSubsystemFromFile(composite, FEATURE_B);
+			try {
+				assertProvisionPolicy(feature, false);
+				assertConstituent(feature, BUNDLE_A);
+				assertNotConstituent(feature, BUNDLE_B);
+				assertNotConstituent(composite, BUNDLE_A);
+				assertNotConstituent(composite, BUNDLE_B);
+				assertNotConstituent(root, BUNDLE_A);
+				assertConstituent(root, BUNDLE_B);
+			}
+			finally {
+				uninstallSubsystemSilently(feature);
+			}
+		}
+		finally {
+			uninstallSubsystemSilently(composite);
+		}
+	}
+	
+	@Test
+	public void testProvisionToSelf() throws Exception {
+		Subsystem root = getRootSubsystem();
+		assertProvisionPolicy(root, true);
+		registerRepositoryService(BUNDLE_B);
+		Subsystem subsystem = installSubsystemFromFile(root, APPLICATION_A);
+		try {
+			assertProvisionPolicy(subsystem, true);
+			assertConstituent(subsystem, BUNDLE_A);
+			assertConstituent(subsystem, BUNDLE_B);
+			assertNotConstituent(root, BUNDLE_A);
+			assertNotConstituent(root, BUNDLE_B);
+		}
+		finally {
+			uninstallSubsystemSilently(subsystem);
+		}
+	}
+}

Modified: aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/SubsystemTest.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/SubsystemTest.java?rev=1300686&r1=1300685&r2=1300686&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/SubsystemTest.java (original)
+++ aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/SubsystemTest.java Wed Mar 14 18:47:59 2012
@@ -16,6 +16,7 @@ package org.apache.aries.subsystem.itest
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.ops4j.pax.exam.CoreOptions.equinox;
@@ -38,12 +39,14 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
-import junit.framework.Assert;
-
 import org.apache.aries.subsystem.core.ResourceHelper;
+import org.apache.aries.subsystem.core.archive.ProvisionPolicyDirective;
+import org.apache.aries.subsystem.core.archive.SubsystemTypeHeader;
 import org.apache.aries.subsystem.core.internal.SubsystemIdentifier;
 import org.apache.aries.subsystem.core.obr.felix.RepositoryAdminRepository;
+import org.apache.aries.subsystem.core.resource.BundleResource;
 import org.apache.aries.subsystem.itests.util.RepositoryGenerator;
+import org.apache.aries.subsystem.itests.util.TestRepository;
 import org.apache.aries.subsystem.itests.util.Utils;
 import org.apache.aries.unittest.fixture.ArchiveFixture;
 import org.apache.aries.unittest.fixture.ArchiveFixture.JarFixture;
@@ -64,6 +67,7 @@ import org.osgi.framework.Version;
 import org.osgi.framework.namespace.IdentityNamespace;
 import org.osgi.resource.Resource;
 import org.osgi.service.repository.Repository;
+import org.osgi.service.repository.RepositoryContent;
 import org.osgi.service.subsystem.Subsystem;
 import org.osgi.service.subsystem.Subsystem.State;
 import org.osgi.service.subsystem.SubsystemConstants;
@@ -203,7 +207,7 @@ public abstract class SubsystemTest exte
 	
 	protected Collection<ServiceRegistration<?>> serviceRegistrations = new ArrayList<ServiceRegistration<?>>();
 	
-	public void setUp() {
+	public void setUp() throws Exception {
 		super.setUp();
 		new RepositoryGenerator(bundleContext).generateOBR();
 		serviceRegistrations.add(bundleContext.registerService(Repository.class, new RepositoryAdminRepository(getOsgiService(RepositoryAdmin.class)), null));
@@ -250,17 +254,21 @@ public abstract class SubsystemTest exte
 		assertTrue("Parent did not contain all children", parent.getChildren().containsAll(children));
 	}
 	
+	protected void assertConstituent(Subsystem subsystem, String symbolicName) {
+		assertConstituent(subsystem, symbolicName, Version.emptyVersion);
+	}
+	
+	protected void assertConstituent(Subsystem subsystem, String symbolicName, Version version) {
+		assertConstituent(subsystem, symbolicName, version, IdentityNamespace.TYPE_BUNDLE);
+	}
+	
+	protected void assertContituent(Subsystem subsystem, String symbolicName, String type) {
+		assertConstituent(subsystem, symbolicName, Version.emptyVersion, type);
+	}
+	
 	protected void assertConstituent(Subsystem subsystem, String symbolicName, Version version, String type) {
-		for (Resource resource : subsystem.getConstituents()) {
-			if (symbolicName.equals(ResourceHelper.getSymbolicNameAttribute(resource))) {
-				if (version != null)
-					assertVersion(version, ResourceHelper.getVersionAttribute(resource));
-				if (type != null)
-					assertEquals("Wrong type", type, ResourceHelper.getTypeAttribute(resource));
-				return;
-			}
-		}
-		Assert.fail("Constituent not found: " + symbolicName);
+		Resource constituent = getConstituent(subsystem, symbolicName, version, type);
+		assertNotNull("Constituent not found: " + symbolicName + ';' + version + ';' + type, constituent);
 	}
 	
 	protected void assertConstituents(int size, Subsystem subsystem) {
@@ -336,6 +344,15 @@ public abstract class SubsystemTest exte
 		assertFalse("Parent contained child", parent.getChildren().contains(child));
 	}
 	
+	protected void assertNotConstituent(Subsystem subsystem, String symbolicName) {
+		assertNotConstituent(subsystem, symbolicName, Version.emptyVersion, IdentityNamespace.TYPE_BUNDLE);
+	}
+	
+	protected void assertNotConstituent(Subsystem subsystem, String symbolicName, Version version, String type) {
+		Resource constituent = getConstituent(subsystem, symbolicName, version, type);
+		assertNull("Constituent found: " + symbolicName + ';' + version + ';' + type, constituent);
+	}
+	
 	protected void assertParent(Subsystem expected, Subsystem subsystem) {
 		for (Subsystem parent : subsystem.getParents()) {
 			if (parent.equals(expected))
@@ -345,6 +362,17 @@ public abstract class SubsystemTest exte
 		fail("Parent did not exist: " + expected.getSymbolicName());
 	}
 	
+	protected void assertProvisionPolicy(Subsystem subsystem, boolean acceptsDependencies) {
+		String headerStr = subsystem.getSubsystemHeaders(null).get(SubsystemConstants.SUBSYSTEM_TYPE);
+		assertNotNull("Missing subsystem type header", headerStr);
+		SubsystemTypeHeader header = new SubsystemTypeHeader(headerStr);
+		ProvisionPolicyDirective directive = header.getProvisionPolicyDirective();
+		if (acceptsDependencies)
+			assertTrue("Subsystem does not accept dependencies", directive.isAcceptDependencies());
+		else
+			assertTrue("Subsystem accepts dependencies", directive.isRejectDependencies());
+	}
+	
 	protected void assertRegionContextBundle(Subsystem s) {
 		Bundle b = getRegionContextBundle(s);
 		assertEquals("Not active", Bundle.ACTIVE, b.getState());
@@ -470,6 +498,14 @@ public abstract class SubsystemTest exte
 		write(symbolicName, bundle);
 	}
 	
+	protected RepositoryContent createBundleRepositoryContent(String file) throws Exception {
+		return createBundleRepositoryContent(new File(file));
+	}
+	
+	protected RepositoryContent createBundleRepositoryContent(File file) throws Exception {
+		return BundleResource.newInstance(file.toURI().toURL());
+	}
+	
 	protected static void createManifest(String name, Map<String, String> headers) throws IOException {
 		ManifestFixture manifest = ArchiveFixture.newJar().manifest();
 		for (Entry<String, String> header : headers.entrySet()) {
@@ -492,6 +528,23 @@ public abstract class SubsystemTest exte
 		write(name, fixture);
 	}
 	
+	protected Resource getConstituent(Subsystem subsystem, String symbolicName, Version version, String type) {
+		for (Resource resource : subsystem.getConstituents()) {
+			if (symbolicName.equals(ResourceHelper.getSymbolicNameAttribute(resource))) {
+				if (version == null)
+					version = Version.emptyVersion;
+				if (version.equals(ResourceHelper.getVersionAttribute(resource))) {
+					if (type == null)
+						type = IdentityNamespace.TYPE_BUNDLE;
+					if (type.equals(ResourceHelper.getTypeAttribute(resource))) {
+						return resource;
+					}
+				}
+			}
+		}
+		return null;
+	}
+	
 	protected Bundle getRegionContextBundle(Subsystem subsystem) {
 		BundleContext bc = subsystem.getBundleContext();
 		assertNotNull("No region context bundle", bc);
@@ -558,7 +611,7 @@ public abstract class SubsystemTest exte
 	
 	protected Subsystem installSubsystem(Subsystem parent, String location, InputStream content) throws Exception {
 		subsystemEvents.clear();
-		Subsystem subsystem = getRootSubsystem().install(location, content);
+		Subsystem subsystem = parent.install(location, content);
 		assertSubsystemNotNull(subsystem);
 		assertEvent(subsystem, State.INSTALLING, 5000);
 		assertEvent(subsystem, State.INSTALLED, 5000);
@@ -568,10 +621,33 @@ public abstract class SubsystemTest exte
 		assertState(State.INSTALLED, subsystem);
 		assertLocation(location, subsystem);
 		assertId(subsystem);
-		assertDirectory(subsystem);
+		// TODO This does not take into account nested directories.
+//		assertDirectory(subsystem);
 		return subsystem;
 	}
 	
+	protected void registerRepositoryService(Repository repository) {
+		serviceRegistrations.add(bundleContext.registerService(
+				Repository.class, repository, null));
+	}
+	
+	protected void registerRepositoryService(Resource...resources) {
+		TestRepository.Builder builder = new TestRepository.Builder();
+		for (Resource resource : resources) {
+			builder.resource(resource);
+		}
+		registerRepositoryService(builder.build());
+	}
+	
+	protected void registerRepositoryService(String...files) throws Exception {
+		Resource[] resources = new Resource[files.length];
+		int i = 0;
+		for (String file : files) {
+			resources[i++] = (Resource)createBundleRepositoryContent(file);
+		}
+		registerRepositoryService(resources);
+	}
+	
 	protected void startBundle(Bundle bundle) throws BundleException {
 		startBundle(bundle, getRootSubsystem());
 	}
@@ -592,6 +668,11 @@ public abstract class SubsystemTest exte
 		assertState(State.ACTIVE, subsystem);
 	}
 	
+	protected void stopAndUninstallSubsystemSilently(Subsystem subsystem) {
+		stopSubsystemSilently(subsystem);
+		uninstallSubsystemSilently(subsystem);
+	}
+	
 	protected void stopSubsystem(Subsystem subsystem) throws Exception {
 		assertState(State.ACTIVE, subsystem);
 		subsystemEvents.clear();
@@ -630,6 +711,8 @@ public abstract class SubsystemTest exte
 	}
 	
 	protected void uninstallSubsystemSilently(Subsystem subsystem) {
+		if (subsystem == null)
+			return;
 		try {
 			uninstallSubsystem(subsystem);
 		}