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/25 23:39:11 UTC

svn commit: r1293711 - 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/co...

Author: jwross
Date: Sat Feb 25 22:39:10 2012
New Revision: 1293711

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

(1) Added initial support for composite Import-Package header.
(2) Added new composite test.
(3) Updated references to coordinator and region in subsystem pom.
(4) Removed the installation of a temporary bundle in order to avoid location renaming as part of installing the region context bundle as it is no longer necessary with the latest region updates.
(5) Switched back to org.osgi.framework.VersionRange since there's a public build of equinox with it now.
(6) Fixed some more issues with the regex grammar not accounting for whitespace between terminals.

Added:
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/RegionContextBundleManager.java
    aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/CompositeTest.java
Modified:
    aries/trunk/subsystem/pom.xml
    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/Grammar.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ImportPackageHeader.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ImportPackageRequirement.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/VersionRangeAttribute.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/OsgiIdentityRequirement.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/RegionContextBundleHelper.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/AbstractRequirement.java
    aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/SubsystemTest.java

Modified: aries/trunk/subsystem/pom.xml
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/pom.xml?rev=1293711&r1=1293710&r2=1293711&view=diff
==============================================================================
--- aries/trunk/subsystem/pom.xml (original)
+++ aries/trunk/subsystem/pom.xml Sat Feb 25 22:39:10 2012
@@ -179,14 +179,14 @@
                                     <artifactId>org.eclipse.equinox.coordinator</artifactId>
                                     <version>3.8.0-SNAPSHOT</version>
                                     <packaging>jar</packaging>
-                                    <downloadUrl>http://www.eclipse.org/downloads/download.php?file=/equinox/drops/N20120222-2000/org.eclipse.equinox.coordinator_1.1.0.N20120222-2000.jar&amp;url=http://download.eclipse.org/equinox/drops/N20120222-2000/org.eclipse.equinox.coordinator_1.1.0.N20120222-2000.jar&amp;mirror_id=1</downloadUrl>
+                                    <downloadUrl>http://www.eclipse.org/downloads/download.php?file=/equinox/drops/N20120223-2000/org.eclipse.equinox.coordinator_1.1.0.N20120223-2000.jar&amp;url=http://download.eclipse.org/equinox/drops/N20120223-2000/org.eclipse.equinox.coordinator_1.1.0.N20120223-2000.jar&amp;mirror_id=1</downloadUrl>
                                 </artifactItem>
                                 <artifactItem>
                                     <groupId>org.eclipse.equinox</groupId>
                                     <artifactId>org.eclipse.equinox.region</artifactId>
                                     <version>3.8.0-SNAPSHOT</version>
                                     <packaging>jar</packaging>
-                                    <downloadUrl>http://www.eclipse.org/downloads/download.php?file=/equinox/drops/S-3.8M5-201201251800/org.eclipse.equinox.region_1.0.0.v20111107-1631.jar&amp;url=http://download.eclipse.org/equinox/drops/S-3.8M5-201201251800/org.eclipse.equinox.region_1.0.0.v20111107-1631.jar&amp;mirror_id=1</downloadUrl>
+                                    <downloadUrl>http://www.eclipse.org/downloads/download.php?file=/equinox/drops/N20120223-2000/org.eclipse.equinox.region_1.1.0.N20120223-2000.jar&amp;url=http://download.eclipse.org/equinox/drops/N20120223-2000/org.eclipse.equinox.region_1.1.0.N20120223-2000.jar&amp;mirror_id=1</downloadUrl>
                                 </artifactItem>
                                 <artifactItem>
                                     <groupId>org.eclipse.osgi</groupId>

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=1293711&r1=1293710&r2=1293711&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 Sat Feb 25 22:39:10 2012
@@ -110,12 +110,17 @@ public class DeploymentManifest {
 			headers.put(SUBSYSTEM_VERSION, subsystemManifest.getSubsystemVersionHeader());
 			SubsystemTypeHeader typeHeader = subsystemManifest.getSubsystemTypeHeader();
 			if (SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION.equals(typeHeader.getValue())) {
-				if (resolution != null)
-					headers.put(IMPORT_PACKAGE, computeImportPackageHeader(resolution, deployedContent, acceptDependencies));
+				if (resolution != null) {
+					ImportPackageHeader header = computeImportPackageHeader(resolution, deployedContent, acceptDependencies);
+					if (header != null)
+						headers.put(IMPORT_PACKAGE, header);
+				}
 				// TODO Compute additional headers for an application.
 			}
-			// TODO Add to constants.
 			else if (SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE.equals(typeHeader.getValue())) {
+				ImportPackageHeader importPackage = subsystemManifest.getImportPackageHeader();
+				if (importPackage != null)
+					headers.put(IMPORT_PACKAGE, importPackage);
 				// TODO Compute additional headers for a composite. 
 			}
 			// Features require no additional headers.
@@ -177,10 +182,12 @@ public class DeploymentManifest {
 				// For all other cases, we need an import.
 				Requirement requirement = wire.getRequirement();
 				if (PackageNamespace.PACKAGE_NAMESPACE.equals(requirement.getNamespace())) {
-					clauses.add(new ImportPackageRequirement(requirement).toClause());
+					clauses.add(new ImportPackageHeader.Clause(requirement));
 				}
 			}
 		}
+		if (clauses.isEmpty())
+			return null;
 		return new ImportPackageHeader(clauses);
 	}
 }

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=1293711&r1=1293710&r2=1293711&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 Sat Feb 25 22:39:10 2012
@@ -89,9 +89,6 @@ 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 + ")\"";
-	// 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 + ")*";
@@ -114,9 +111,9 @@ public interface Grammar {
 	public static final String UNIQUENAME = IDENTIFIER + "(?:\\." + IDENTIFIER + ")*";
 	public static final String SYMBOLICNAME = TOKEN + "(?:\\." + TOKEN + ")*";
 	public static final String PACKAGENAME = UNIQUENAME;
-	public static final String PACKAGENAMES = PACKAGENAME + "(?:\\;" + PACKAGENAME + ")*";
-	public static final String IMPORT = PACKAGENAMES + "(?:;(?:" + PARAMETER + "))*";
-	public static final String IMPORTPACKAGE = IMPORT + "(?:\\," + IMPORT + ")*";
+	public static final String PACKAGENAMES = PACKAGENAME + "(?:\\;\\s*" + PACKAGENAME + ")*";
+	public static final String IMPORT = PACKAGENAMES + "(?:;\\s*(?:" + PARAMETER + "))*";
+	public static final String IMPORTPACKAGE = IMPORT + "(?:\\,\\s*" + IMPORT + ")*";
 	
 	/*
 	 * number ::= digit+

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ImportPackageHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ImportPackageHeader.java?rev=1293711&r1=1293710&r2=1293711&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ImportPackageHeader.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ImportPackageHeader.java Sat Feb 25 22:39:10 2012
@@ -34,14 +34,44 @@ import org.osgi.resource.Resource;
 
 public class ImportPackageHeader implements Header<ImportPackageHeader.Clause> {
 	public static class Clause implements org.apache.aries.subsystem.core.archive.Clause {
+		private static final String REGEX = "\\((" + PackageNamespace.PACKAGE_NAMESPACE + ")(=)([^\\)]+)\\)";
 		private static final String REGEX1 = '(' + Grammar.PACKAGENAMES + ")(?=;|\\z)";
 		private static final String REGEX2 = '(' + Grammar.PARAMETER + ")(?=;|\\z)";
+		private static final Pattern PATTERN = Pattern.compile(REGEX);
 		private static final Pattern PATTERN1 = Pattern.compile(REGEX1);
 		private static final Pattern PATTERN2 = Pattern.compile(REGEX2);
+
+		private static void fillInDefaults(Map<String, Parameter> parameters) {
+			Parameter parameter = parameters.get(Constants.VERSION_ATTRIBUTE);
+			if (parameter == null)
+				parameters.put(Constants.VERSION_ATTRIBUTE, new VersionRangeAttribute());
+		}
 		
 		private final Map<String, Parameter> myParameters = new HashMap<String, Parameter>();
 		private final String myPath;
 		
+		public Clause(Requirement requirement) {
+			if (!PackageNamespace.PACKAGE_NAMESPACE.equals(requirement.getNamespace()))
+				throw new IllegalArgumentException("Requirement must be in the '" + PackageNamespace.PACKAGE_NAMESPACE + "' namespace");
+			String filter = requirement.getDirectives().get(PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE);
+			String packageName = null;
+			Matcher matcher = PATTERN.matcher(filter);
+			while (matcher.find()) {
+				String name = matcher.group(1);
+				String operator = matcher.group(2);
+				String value = matcher.group(3);
+				if (PackageNamespace.PACKAGE_NAMESPACE.equals(name)) {
+					packageName = value;
+				}
+				else if (PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE.equals(name)) {
+					// TODO Parse the version range from the filter.
+				}
+			}
+			if (packageName == null)
+				throw new IllegalArgumentException("Missing filter key: " + PackageNamespace.PACKAGE_NAMESPACE);
+			myPath = packageName;
+		}
+		
 		public Clause(String clause) {
 			Matcher matcher = PATTERN1.matcher(clause);
 			if (matcher.find())
@@ -53,22 +83,7 @@ public class ImportPackageHeader impleme
 				Parameter parameter = ParameterFactory.create(matcher.group());
 				myParameters.put(parameter.getName(), parameter);
 			}
-//			Attribute attribute = new GenericAttribute(BundleRevision.PACKAGE_NAMESPACE, getPath());
-//			myParameters.put(attribute.getName(), attribute);
-//			attribute = getAttribute(Constants.VERSION_ATTRIBUTE);
-//			if (attribute == null) {
-//				attribute = new VersionRangeAttribute();
-//				myParameters.put(attribute.getName(), attribute);
-//			}
-//			Directive directive = getDirective(Constants.FILTER_DIRECTIVE);
-//			if (directive == null) {
-//				StringBuilder builder = new StringBuilder("(&");
-//				for (Attribute a : getAttributes()) {
-//					a.appendToFilter(builder);
-//				}
-//				directive = new GenericDirective(Constants.FILTER_DIRECTIVE, builder.append(')').toString());
-//				myParameters.put(directive.getName(), directive);
-//			}
+			fillInDefaults(myParameters);
 		}
 		
 		public Attribute getAttribute(String name) {
@@ -192,32 +207,24 @@ public class ImportPackageHeader impleme
 	private static final String REGEX = Grammar.IMPORT + "(?=,|\\z)";
 	private static final Pattern PATTERN = Pattern.compile(REGEX);
 	
-//	private static String valueOf(Collection<Clause> clauses) {
-//		StringBuilder sb = new StringBuilder();
-//		for (Clause clause : clauses) {
-//			sb.append(clause).append(',');
-//		}
-//		if (sb.length() != 0)
-//			sb.deleteCharAt(sb.length() - 1);
-//		return sb.toString();
-//	}
+	private static Collection<Clause> processHeader(String header) {
+		Matcher matcher = PATTERN.matcher(header);
+		Set<Clause> clauses = new HashSet<Clause>();
+		while (matcher.find())
+			clauses.add(new Clause(matcher.group()));
+		return clauses;
+	}
 	
 	private final Set<Clause> clauses;
-//	private final String value;
 	
 	public ImportPackageHeader(Collection<Clause> clauses) {
+		if (clauses.isEmpty())
+			throw new IllegalArgumentException("An Import-Package header must have at least one clause");
 		this.clauses = new HashSet<Clause>(clauses);
 	}
 	
 	public ImportPackageHeader(String header) {
-		Matcher matcher = PATTERN.matcher(header);
-		Set<Clause> clauses = new HashSet<Clause>();
-		while (matcher.find())
-			clauses.add(new Clause(matcher.group()));
-		if (clauses.isEmpty())
-			throw new IllegalArgumentException("Invalid header syntax -> " + NAME + ": " + header);
-//		value = header;
-		this.clauses = clauses;
+		this(processHeader(header));
 	}
 	
 	public Collection<ImportPackageHeader.Clause> getClauses() {
@@ -242,11 +249,14 @@ public class ImportPackageHeader impleme
 		return toString();
 	}
 	
+	@Override
 	public String toString() {
 		StringBuilder builder = new StringBuilder();
 		for (Clause clause : getClauses()) {
-			builder.append(clause);
+			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/archive/ImportPackageRequirement.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ImportPackageRequirement.java?rev=1293711&r1=1293710&r2=1293711&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ImportPackageRequirement.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ImportPackageRequirement.java Sat Feb 25 22:39:10 2012
@@ -1,12 +1,15 @@
 package org.apache.aries.subsystem.core.archive;
 
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 
+import org.apache.aries.subsystem.core.resource.AbstractRequirement;
 import org.osgi.framework.namespace.PackageNamespace;
-import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
 
-public class ImportPackageRequirement {
+public class ImportPackageRequirement extends AbstractRequirement {
 	private static final String BUNDLE_SYMBOLICNAME = PackageNamespace.CAPABILITY_BUNDLE_SYMBOLICNAME_ATTRIBUTE;
 	private static final String BUNDLE_VERSION = PackageNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE;
 	private static final String EQUAL = "=";
@@ -21,32 +24,38 @@ public class ImportPackageRequirement {
 	private static final String RESOLUTION_OPTIONAL = PackageNamespace.RESOLUTION_OPTIONAL;
 	private static final String VERSION = PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE;
 	
-	private static final String REGEX = 
-			"\\((" + NAMESPACE + ")(=)([^\\)]+)\\)";
-	private static final Pattern PATTERN = Pattern.compile(REGEX);
+	private final Map<String, String> directives = new HashMap<String, String>(1);
 	
-	private final String packageName;
-	
-	public ImportPackageRequirement(Requirement requirement) {
-		if (!NAMESPACE.equals(requirement.getNamespace()))
-			throw new IllegalArgumentException("Requirement must be in the '" + NAMESPACE + "' namespace");
-		String filter = requirement.getDirectives().get(FILTER);
-		String packageName = null;
-		Matcher matcher = PATTERN.matcher(filter);
-		while (matcher.find()) {
-			String name = matcher.group(1);
-			String operator = matcher.group(2);
-			String value = matcher.group(3);
-			if (NAMESPACE.equals(name)) {
-				packageName = value;
-			}
+	public ImportPackageRequirement(ImportPackageHeader.Clause clause) {
+		Collection<String> packageNames = clause.getPackageNames();
+		if (packageNames.isEmpty() || packageNames.size() > 1)
+			throw new IllegalArgumentException("Only one package name per requirement allowed");
+		StringBuilder filter = new StringBuilder("(&(").append(NAMESPACE)
+				.append('=').append(packageNames.iterator().next()).append(')');
+		VersionRangeAttribute versionRange = clause.getVersionRangeAttribute();
+		if (versionRange != null) {
+			versionRange.appendToFilter(filter);
 		}
-		if (packageName == null)
-			throw new IllegalArgumentException("Missing filter key: " + NAMESPACE);
-		this.packageName = packageName;
+		directives.put(FILTER, filter.append(')').toString());
 	}
 	
-	public ImportPackageHeader.Clause toClause() {
-		return new ImportPackageHeader.Clause(packageName);
+	@Override
+	public Map<String, Object> getAttributes() {
+		return Collections.emptyMap();
+	}
+
+	@Override
+	public Map<String, String> getDirectives() {
+		return Collections.unmodifiableMap(directives);
+	}
+
+	@Override
+	public String getNamespace() {
+		return PackageNamespace.PACKAGE_NAMESPACE;
+	}
+
+	@Override
+	public Resource getResource() {
+		return null;
 	}
 }

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=1293711&r1=1293710&r2=1293711&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 Sat Feb 25 22:39:10 2012
@@ -20,9 +20,9 @@ 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.VersionRange;
 import org.osgi.resource.Resource;
 
 public class SubsystemContentHeader extends AbstractHeader {
@@ -127,7 +127,7 @@ public class SubsystemContentHeader exte
 		String type = ResourceHelper.getTypeAttribute(resource);
 		for (Content content : contents) {
 			if (symbolicName.equals(content.getName())
-					&& content.getVersionRange().matches(version)
+					&& content.getVersionRange().includes(version)
 					&& type.equals(content.getType()))
 				return true;
 		}

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=1293711&r1=1293710&r2=1293711&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 Sat Feb 25 22:39:10 2012
@@ -110,6 +110,10 @@ public class SubsystemManifest {
 		return headers;
 	}
 	
+	public ImportPackageHeader getImportPackageHeader() {
+		return (ImportPackageHeader)getHeaders().get(IMPORT_PACKAGE);
+	}
+	
 	public SubsystemContentHeader getSubsystemContentHeader() {
 		return (SubsystemContentHeader)getHeaders().get(SUBSYSTEM_CONTENT);
 	}

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/VersionRangeAttribute.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/VersionRangeAttribute.java?rev=1293711&r1=1293710&r2=1293711&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/VersionRangeAttribute.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/VersionRangeAttribute.java Sat Feb 25 22:39:10 2012
@@ -13,46 +13,49 @@
  */
 package org.apache.aries.subsystem.core.archive;
 
-import org.apache.aries.util.VersionRange;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Version;
+import org.osgi.framework.VersionRange;
 
 public class VersionRangeAttribute extends AbstractAttribute {
-	public static String toFilterString(VersionRange range) {
-		String version = Constants.VERSION_ATTRIBUTE;
-		Version min = range.getMinimumVersion();
-		Version max = range.getMaximumVersion();
-		StringBuilder sb = new StringBuilder();
-		if (max != null)
-			sb.append("(&");
-		if (range.isMinimumExclusive())
-			sb.append("(!(").append(version).append("<=").append(min).append("))");
-		else
-			sb.append('(').append(version).append(">=").append(min).append(')');
-		if (max != null) {
-			if (range.isMaximumExclusive())
-				sb.append("(!(").append(version).append(">=").append(range.getMaximumVersion()).append("))");
-			else
-				sb.append('(').append(version).append("<=").append(max).append(')');
-			sb.append(')');
-		}
-		return sb.toString();
-	}
+//	public static String toFilterString(VersionRange range) {
+//		String version = Constants.VERSION_ATTRIBUTE;
+//		Version min = range.getMinimumVersion();
+//		Version max = range.getMaximumVersion();
+//		StringBuilder sb = new StringBuilder();
+//		if (max != null)
+//			sb.append("(&");
+//		if (range.isMinimumExclusive())
+//			sb.append("(!(").append(version).append("<=").append(min).append("))");
+//		else
+//			sb.append('(').append(version).append(">=").append(min).append(')');
+//		if (max != null) {
+//			if (range.isMaximumExclusive())
+//				sb.append("(!(").append(version).append(">=").append(range.getMaximumVersion()).append("))");
+//			else
+//				sb.append('(').append(version).append("<=").append(max).append(')');
+//			sb.append(')');
+//		}
+//		return sb.toString();
+//	}
 	
 	private final VersionRange range;
 	
 	public VersionRangeAttribute() {
-		super(Constants.VERSION_ATTRIBUTE, Version.emptyVersion.toString());
-		range = new VersionRange(getValue());
+		this(Version.emptyVersion.toString());
 	}
 			
 	public VersionRangeAttribute(String value) {
-		super(Constants.VERSION_ATTRIBUTE, value);
-		range = new VersionRange(getValue());
+		this(new VersionRange(value));
+	}
+	
+	public VersionRangeAttribute(VersionRange range) {
+		super(Constants.VERSION_ATTRIBUTE, range.toString());
+		this.range = range;
 	}
 	
 	public StringBuilder appendToFilter(StringBuilder builder) {
-		return builder.append(toFilterString(range));
+		return builder.append(range.toFilterString(Constants.VERSION_ATTRIBUTE));
 	}
 
 	public VersionRange getVersionRange() {

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=1293711&r1=1293710&r2=1293711&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 Sat Feb 25 22:39:10 2012
@@ -48,6 +48,7 @@ import org.apache.aries.subsystem.core.a
 import org.apache.aries.subsystem.core.archive.DeploymentManifest;
 import org.apache.aries.subsystem.core.archive.Header;
 import org.apache.aries.subsystem.core.archive.ImportPackageHeader;
+import org.apache.aries.subsystem.core.archive.ImportPackageRequirement;
 import org.apache.aries.subsystem.core.archive.ProvisionResourceHeader;
 import org.apache.aries.subsystem.core.archive.ProvisionResourceHeader.ProvisionedResource;
 import org.apache.aries.subsystem.core.archive.SubsystemArchive;
@@ -60,6 +61,7 @@ import org.apache.aries.util.io.IOUtils;
 import org.eclipse.equinox.region.Region;
 import org.eclipse.equinox.region.RegionDigraph;
 import org.eclipse.equinox.region.RegionFilter;
+import org.eclipse.equinox.region.RegionFilterBuilder;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleEvent;
@@ -1036,35 +1038,40 @@ public class AriesSubsystem implements S
 	}
 
 	private void setImportIsolationPolicy() throws BundleException, IOException, InvalidSyntaxException {
-		// Nothing to do if this is the root subsystem.
 		if (isRoot())
+			// Nothing to do if this is the root subsystem.
 			return;
-		// Features share the same isolation as that of their scoped parent.
-		if (isFeature()) {
+		if (isFeature())
+			// Features share the same isolation as that of their scoped parent.
 			return;
-		}
-		if (isApplication()) {
-			// TODO Implement import isolation policy for applications.
-			// TODO Support for generic requirements such as osgi.ee.
+		if (isApplication() || isComposite()) {
+			// Both applications and composites have Import-Package headers that require processing.
+			// In the case of applications, the header is generated.
 			ImportPackageHeader importPackage = getDeploymentManifest().getImportPackageHeader();
 			if (importPackage != null) {
+				Region from = region;
+				Region to = ((AriesSubsystem)getParents().iterator().next()).region;
+				String policy = RegionFilter.VISIBLE_PACKAGE_NAMESPACE;
+				RegionFilterBuilder builder = from.getRegionDigraph().createRegionFilterBuilder();
 				for (ImportPackageHeader.Clause clause : importPackage.getClauses()) {
-					Region from = region;
-					Region to = ((AriesSubsystem)getParents().iterator().next()).region;
-					String policy = RegionFilter.VISIBLE_PACKAGE_NAMESPACE;
-					String filter = clause.getRequirement(this).getDirectives().get(PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE);
+					ImportPackageRequirement requirement = new ImportPackageRequirement(clause);
+					String filter = requirement.getDirectives().get(PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE);
 					if (LOGGER.isDebugEnabled())
-						LOGGER.debug("Establishing region connection: from="
-								+ from + ", to=" + to + ", policy=" + policy
-								+ ", filter=" + filter);
-					from.connectRegion(
-							to, 
-							from.getRegionDigraph().createRegionFilterBuilder().allow(
-									RegionFilter.VISIBLE_PACKAGE_NAMESPACE, 
-									filter).build());
+						LOGGER.debug("Allowing " + policy + " of " + filter);
+					builder.allow(policy, filter);
 				}
+				RegionFilter regionFilter = builder.build();
+				if (LOGGER.isDebugEnabled())
+					LOGGER.debug("Establishing region connection: from="
+							+ from + ", to=" + to + ", policy=" + policy
+							+ ", filter=" + regionFilter);
+				from.connectRegion(to, regionFilter);
 			}
 		}
+		if (isApplication()) {
+			// TODO Implement import isolation policy for applications.
+			// TODO Support for generic requirements such as osgi.ee.
+		}
 		else if (isComposite()) {
 			// TODO Implement import isolation policy for composites.
 			// Composites specify an explicit import policy in their subsystem and deployment manifests.

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/OsgiIdentityRequirement.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/OsgiIdentityRequirement.java?rev=1293711&r1=1293710&r2=1293711&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/OsgiIdentityRequirement.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/OsgiIdentityRequirement.java Sat Feb 25 22:39:10 2012
@@ -17,14 +17,13 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.aries.subsystem.core.archive.VersionRangeAttribute;
 import org.apache.aries.subsystem.core.resource.AbstractRequirement;
-import org.apache.aries.util.VersionRange;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Filter;
 import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.Version;
+import org.osgi.framework.VersionRange;
 import org.osgi.framework.namespace.IdentityNamespace;
 import org.osgi.resource.Resource;
 import org.osgi.service.subsystem.SubsystemException;
@@ -46,7 +45,7 @@ public class OsgiIdentityRequirement ext
 	private static Filter createFilter(String symbolicName, VersionRange versionRange, String type) {
 		return createFilter(
 				symbolicName,
-				VersionRangeAttribute.toFilterString(versionRange),
+				versionRange.toFilterString(Constants.VERSION_ATTRIBUTE),
 				type);
 	}
 	

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/RegionContextBundleHelper.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/RegionContextBundleHelper.java?rev=1293711&r1=1293710&r2=1293711&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/RegionContextBundleHelper.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/RegionContextBundleHelper.java Sat Feb 25 22:39:10 2012
@@ -24,17 +24,7 @@ public class RegionContextBundleHelper {
 		Bundle b = subsystem.getRegion().getBundle(symbolicName, VERSION);
 		if (b != null)
 			return b.adapt(BundleRevision.class);
-		Bundle t = subsystem.getRegion().installBundle(location + "/temp", createTempBundle(symbolicName));
-		try {
-			t.start();
-			b = t.getBundleContext().installBundle(location, createRegionContextBundle(symbolicName));
-		}
-		finally {
-			try {
-				t.uninstall();
-			}
-			catch (BundleException e) {}
-		}
+		b = subsystem.getRegion().installBundleAtLocation(location, createRegionContextBundle(symbolicName));
 		// The region context bundle must be started persistently.
 		b.start();
 		return b.adapt(BundleRevision.class);
@@ -54,14 +44,6 @@ public class RegionContextBundleHelper {
 		return bundle.adapt(BundleRevision.class);
 	}
 	
-	private static InputStream createRegionContextBundle(String symbolicName) throws IOException {
-		Manifest manifest = createManifest(symbolicName);
-		ByteArrayOutputStream baos = new ByteArrayOutputStream();
-		JarOutputStream jos = new JarOutputStream(baos, manifest);
-		jos.close();
-		return new ByteArrayInputStream(baos.toByteArray());
-	}
-	
 	private static Manifest createManifest(String symbolicName) {
 		Manifest manifest = new Manifest();
 		manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
@@ -70,10 +52,8 @@ public class RegionContextBundleHelper {
 		return manifest;
 	}
 	
-	private static InputStream createTempBundle(String symbolicName) throws IOException {
-		Manifest manifest = new Manifest();
-		manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
-		manifest.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, symbolicName + ".temp");
+	private static InputStream createRegionContextBundle(String symbolicName) throws IOException {
+		Manifest manifest = createManifest(symbolicName);
 		ByteArrayOutputStream baos = new ByteArrayOutputStream();
 		JarOutputStream jos = new JarOutputStream(baos, manifest);
 		jos.close();

Added: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/RegionContextBundleManager.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/RegionContextBundleManager.java?rev=1293711&view=auto
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/RegionContextBundleManager.java (added)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/RegionContextBundleManager.java Sat Feb 25 22:39:10 2012
@@ -0,0 +1,35 @@
+package org.apache.aries.subsystem.core.internal;
+
+import java.util.Map;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleEvent;
+
+public class RegionContextBundleManager {
+	private static class BundleListener implements org.osgi.framework.BundleListener {
+		private final Map<Bundle, AriesSubsystem> map;
+		
+		public BundleListener(Map<Bundle, AriesSubsystem> map) {
+			this.map = map;
+		}
+		
+		@Override
+		public void bundleChanged(BundleEvent event) {
+			// Figure out what action needs to be taken based on the event type.
+			switch (event.getType()) {
+				// We're interested in INSTALLED events because we may need to
+				// assign the bundle to a subsystem.
+				case BundleEvent.INSTALLED:
+					// See if the bundle originating the event is the region
+					// context bundle of some subsystem.
+					AriesSubsystem subsystem = map.get(event.getOrigin());
+					
+					break;
+				// We're interested in UNINSTALLED events because we may need to
+				// do some clean up.
+				case BundleEvent.UNINSTALLED:
+					break;
+			}
+		}
+	}
+}

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=1293711&r1=1293710&r2=1293711&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 Sat Feb 25 22:39:10 2012
@@ -2,6 +2,7 @@ package org.apache.aries.subsystem.core.
 
 import org.apache.aries.subsystem.core.archive.SubsystemContentHeader;
 import org.apache.aries.subsystem.core.archive.SubsystemManifest;
+import org.osgi.framework.VersionRange;
 import org.osgi.service.subsystem.SubsystemException;
 
 public class SubsystemManifestValidator {
@@ -11,9 +12,18 @@ public class SubsystemManifestValidator 
 			if (header == null)
 				return;
 			for (SubsystemContentHeader.Content content : header.getContents()) {
-				if (!content.getVersionRange().isExactVersion())
+				if (!isExactVersion(content.getVersionRange()))
 					throw new SubsystemException("Composite subsystem using version range for content: " + content);
 			}
 		}
 	}
+	
+	private static boolean isExactVersion(VersionRange range) {
+		if (range.getLeftType() == VersionRange.LEFT_CLOSED
+		          && range.getLeft().equals(range.getRight())
+		          && range.getRightType() == VersionRange.RIGHT_CLOSED) {
+		     return true;
+		}
+		return false;
+	}
 }

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/AbstractRequirement.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/AbstractRequirement.java?rev=1293711&r1=1293710&r2=1293711&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/AbstractRequirement.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/resource/AbstractRequirement.java Sat Feb 25 22:39:10 2012
@@ -30,11 +30,10 @@ public abstract class AbstractRequiremen
 	
 	@Override
 	public String toString() {
-		return new StringBuffer().append("[Requirement: ")
+		return new StringBuffer().append(getClass().getName()).append(": ")
 				.append("namespace=").append(getNamespace())
 				.append(", attributes=").append(getAttributes())
 				.append(", directives=").append(getDirectives())
-				.append(", resource=").append(getResource()).append(']')
-				.toString();
+				.append(", resource=").append(getResource()).toString();
 	}
 }

Added: aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/CompositeTest.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/CompositeTest.java?rev=1293711&view=auto
==============================================================================
--- aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/CompositeTest.java (added)
+++ aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/CompositeTest.java Sat Feb 25 22:39:10 2012
@@ -0,0 +1,96 @@
+package org.apache.aries.subsystem.itests;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+
+import org.apache.aries.unittest.fixture.ArchiveFixture;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.service.subsystem.Subsystem;
+import org.osgi.service.subsystem.SubsystemConstants;
+
+@RunWith(JUnit4TestRunner.class)
+public class CompositeTest extends SubsystemTest {
+	private static boolean createdTestFiles;
+	
+	@Before
+	public static void createTestFiles() throws Exception {
+		if (createdTestFiles)
+			return;
+		createBundleA();
+		createCompositeC();
+		createBundleC();
+		createdTestFiles = true;
+	}
+	
+	private static void createBundleA() throws IOException {
+		write("bundle.a.jar",
+				ArchiveFixture
+						.newJar()
+						.manifest()
+						.symbolicName(
+								"org.apache.aries.subsystem.itests.bundle.a")
+						.attribute(Constants.EXPORT_PACKAGE,
+								"org.apache.aries.subsystem.itests.bundle.a.x;version=\"1.0\"")
+						.end());
+	}
+	
+	private static void createBundleC() throws IOException {
+		write("bundle.c.jar",
+				ArchiveFixture
+						.newJar()
+						.manifest()
+						.symbolicName(
+								"org.apache.aries.subsystem.itests.bundle.c")
+						.attribute(Constants.IMPORT_PACKAGE,
+								"org.apache.aries.subsystem.itests.bundle.a.x;version=\"[1.0,2.0)\"")
+						.end());
+	}
+	
+	private static void createCompositeC() throws IOException {
+		createCompositeCManifest();
+		write("composite.c.esa",
+				ArchiveFixture.newZip().binary("OSGI-INF/SUBSYSTEM.MF",
+						new FileInputStream("COMPOSITE.C.MF")));
+	}
+	
+	private static void createCompositeCManifest() throws IOException {
+		write("COMPOSITE.C.MF",
+				ArchiveFixture
+						.newJar()
+						.manifest()
+						.attribute(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME,
+								"org.apache.aries.subsystem.itests.subsystem.composite.c")
+						.attribute(SubsystemConstants.SUBSYSTEM_TYPE,
+								SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE)
+						.attribute(Constants.IMPORT_PACKAGE,
+								"org.apache.aries.subsystem.itests.bundle.a.x, does.not.exist; a=b"));
+	}
+	
+	@Test
+	public void testImportPackage() throws Exception {
+		Bundle bundleA = installBundleFromFile("bundle.a.jar");
+		try {
+			Subsystem compositeC = installSubsystemFromFile("composite.c.esa");
+			try {
+				Bundle bundleC = installBundleFromFile("bundle.c.jar", compositeC);
+				try {
+					startBundle(bundleC, compositeC);
+				}
+				finally {
+					bundleC.uninstall();
+				}
+			}
+			finally {
+				uninstallScopedSubsystem(compositeC);
+			}
+		}
+		finally {
+			bundleA.uninstall();
+		}
+	}
+}

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=1293711&r1=1293710&r2=1293711&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 Sat Feb 25 22:39:10 2012
@@ -23,6 +23,10 @@ import static org.ops4j.pax.exam.CoreOpt
 import static org.ops4j.pax.exam.CoreOptions.systemProperty;
 
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.lang.reflect.Field;
 import java.net.URL;
@@ -39,11 +43,13 @@ import org.apache.aries.subsystem.core.R
 import org.apache.aries.subsystem.core.obr.felix.RepositoryAdminRepository;
 import org.apache.aries.subsystem.itests.util.RepositoryGenerator;
 import org.apache.aries.subsystem.itests.util.Utils;
+import org.apache.aries.unittest.fixture.ArchiveFixture;
 import org.apache.felix.bundlerepository.RepositoryAdmin;
 import org.ops4j.pax.exam.Option;
 import org.ops4j.pax.exam.container.def.PaxRunnerOptions;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
 import org.osgi.framework.Constants;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceEvent;
@@ -216,13 +222,13 @@ public abstract class SubsystemTest exte
 	protected void assertBundleState(int state, String symbolicName, Subsystem subsystem) {
     	boolean found = false;
     	for (Bundle bundle : subsystem.getBundleContext().getBundles()) {
-			if (symbolicName.equals(bundle.getSymbolicName())) {
-				assertTrue("Wrong state: " + symbolicName, (bundle.getState() & state) != 0);
+			if (symbolicName.equals(bundle.getSymbolicName())) { 
+				assertTrue("Wrong state: " + symbolicName + " [expected " + state + " but was " + bundle.getState() + "]", (bundle.getState() & state) != 0);
 				found = true;
 				break;
 			}
 		}
-    	assertTrue("Bundle '" + symbolicName + "' not found in region context bundle of '" + subsystem + "'", found);
+    	assertTrue("Bundle '" + symbolicName + "' not found in region of '" + subsystem + "'", found);
     }
 	
 	protected void assertChild(Subsystem parent, Subsystem child) {
@@ -442,6 +448,28 @@ public abstract class SubsystemTest exte
 		return findBundleBySymbolicName("org.apache.aries.subsystem.core");
 	}
 	
+	protected Bundle installBundleFromFile(String fileName) throws FileNotFoundException, BundleException {
+		return installBundleFromFile(new File(fileName));
+	}
+	
+	protected Bundle installBundleFromFile(File file) throws FileNotFoundException, BundleException {
+		return installBundleFromFile(file, getRootSubsystem());
+	}
+	
+	protected Bundle installBundleFromFile(String file, Subsystem subsystem) throws FileNotFoundException, BundleException {
+		return installBundleFromFile(new File(file), subsystem);
+	}
+	
+	protected Bundle installBundleFromFile(File file, Subsystem subsystem) throws FileNotFoundException, BundleException {
+		Bundle bundle = installBundleFromFile(file, subsystem.getBundleContext());
+		assertBundleState(Bundle.INSTALLED|Bundle.RESOLVED, bundle.getSymbolicName(), subsystem);
+		return bundle;
+	}
+	
+	protected Bundle installBundleFromFile(File file, BundleContext bundleContext) throws FileNotFoundException, BundleException {
+		return bundleContext.installBundle(file.toURI().toString(), new FileInputStream(file));
+	}
+	
 	protected Subsystem installSubsystemFromFile(Subsystem parent, String fileName) throws Exception {
 		return installSubsystemFromFile(parent, new File(fileName));
 	}
@@ -486,6 +514,15 @@ public abstract class SubsystemTest exte
 		return subsystem;
 	}
 	
+	protected void startBundle(Bundle bundle) throws BundleException {
+		startBundle(bundle, getRootSubsystem());
+	}
+	
+	protected void startBundle(Bundle bundle, Subsystem subsystem) throws BundleException {
+		bundle.start();
+		assertBundleState(Bundle.ACTIVE, bundle.getSymbolicName(), subsystem);
+	}
+	
 	protected void startSubsystem(Subsystem subsystem) throws Exception {
 		assertState(State.INSTALLED, subsystem);
 		subsystemEvents.clear();
@@ -528,4 +565,18 @@ public abstract class SubsystemTest exte
 	protected void uninstallUnscopedSubsystem(Subsystem subsystem) throws Exception {
 		uninstallSubsystem(subsystem);
 	}
+	
+	protected static void write(String file, ArchiveFixture.AbstractFixture fixture) throws IOException {
+		write(new File(file), fixture);
+	}
+	
+	protected static void write(File file, ArchiveFixture.AbstractFixture fixture) throws IOException {
+		FileOutputStream fos = new FileOutputStream(file);
+    	try {
+    		fixture.writeOut(fos);
+    	}
+    	finally {
+    		fos.close();
+    	}
+	}
 }