/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.aries.subsystem.itests.defect;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.jar.Attributes;
import java.util.jar.Manifest;

import org.apache.aries.subsystem.AriesSubsystem;
import org.apache.aries.subsystem.core.archive.AriesProvisionDependenciesDirective;
import org.apache.aries.subsystem.core.archive.SubsystemTypeHeader;
import org.apache.aries.subsystem.itests.SubsystemTest;
import org.apache.aries.subsystem.itests.util.BundleArchiveBuilder;
import org.apache.aries.subsystem.itests.util.SubsystemArchiveBuilder;
import org.apache.aries.subsystem.itests.util.TestCapability;
import org.apache.aries.subsystem.itests.util.TestRepository;
import org.apache.aries.subsystem.itests.util.TestRepositoryContent;
import org.apache.aries.subsystem.itests.util.TestRequirement;
import org.easymock.internal.matchers.Null;
import org.junit.Test;
import org.ops4j.pax.tinybundles.core.InnerClassStrategy;
import org.ops4j.pax.tinybundles.core.TinyBundles;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.Version;
import org.osgi.framework.hooks.resolver.ResolverHook;
import org.osgi.framework.hooks.resolver.ResolverHookFactory;
import org.osgi.framework.namespace.BundleNamespace;
import org.osgi.framework.namespace.IdentityNamespace;
import org.osgi.framework.namespace.PackageNamespace;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRequirement;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.service.repository.Repository;
import org.osgi.service.subsystem.Subsystem;
import org.osgi.service.subsystem.Subsystem.State;
import org.osgi.service.subsystem.SubsystemConstants;
import org.osgi.service.subsystem.SubsystemException;

/*
 * https://issues.apache.org/jira/browse/ARIES-1383
 * 
 * Provide option to disable the provisioning of dependencies at install time.
 * 
 * For tests containing a numerical value in the name, see the description of
 * ARIES-1383 for an explanation.
 */
public class Aries1383Test extends SubsystemTest {
	private static final String SYMBOLICNAME_PREFIX = Aries1383Test.class.getSimpleName() + '.';
	
	private static final String APPLICATION_A = SYMBOLICNAME_PREFIX + "application.a";
	private static final String APPLICATION_B = SYMBOLICNAME_PREFIX + "application.b";
	private static final String APPLICATION_DEPENDENCY_IN_ARCHIVE = SYMBOLICNAME_PREFIX + "application.dependency.in.archive";
	private static final String APPLICATION_EMPTY = SYMBOLICNAME_PREFIX + "application.empty";
	private static final String APPLICATION_INSTALL_FAILED = SYMBOLICNAME_PREFIX + "application.install.failed";
	private static final String APPLICATION_INVALID_PROVISION_DEPENDENCIES = SYMBOLICNAME_PREFIX + "application.invalid.provision.dependency";
	private static final String APPLICATION_MISSING_DEPENDENCY = SYMBOLICNAME_PREFIX + "application.missing.dependency";
	private static final String APPLICATION_PROVISION_DEPENDENCIES_INSTALL = SYMBOLICNAME_PREFIX + "application.provision.dependencies.install";
	private static final String APPLICATION_START_FAILURE = SYMBOLICNAME_PREFIX + "application.start.failure";
	private static final String BUNDLE_A = SYMBOLICNAME_PREFIX + "bundle.a";
	private static final String BUNDLE_B = SYMBOLICNAME_PREFIX + "bundle.b";
	private static final String BUNDLE_C = SYMBOLICNAME_PREFIX + "bundle.c";
	private static final String BUNDLE_D = SYMBOLICNAME_PREFIX + "bundle.d";
	private static final String BUNDLE_INVALID_MANIFEST = SYMBOLICNAME_PREFIX + "bundle.invalid.manifest";
	private static final String BUNDLE_START_FAILURE = SYMBOLICNAME_PREFIX + "bundle.start.failure";
	private static final String ESA_EXTENSION = ".esa";
	private static final String FEATURE_PROVISION_DEPENDENCIES_INSTALL = "feature.provision.dependencies.install";
	private static final String FEATURE_PROVISION_DEPENDENCIES_RESOLVE = "feature.provision.dependencies.resolve";
	private static final String JAR_EXTENSION = ".jar";
	private static final String MANIFEST_VERSION = "1.0";
	private static final String PACKAGE_A = SYMBOLICNAME_PREFIX + "a";
	private static final String PACKAGE_B = SYMBOLICNAME_PREFIX + "b";
	private static final String PACKAGE_C = SYMBOLICNAME_PREFIX + "c";
	private static final String PACKAGE_D = SYMBOLICNAME_PREFIX + "d";
	private static final String SUBSYSTEM_MANIFEST_FILE = "OSGI-INF/SUBSYSTEM.MF";
	
	/*
	 * (1) A set of subsystems with interleaving content dependencies are able 
	 * to be independently, simultaneously, and successfully installed and 
	 * started.
	 */
	@Test
	public void test1() throws Exception {
		Subsystem root = getRootSubsystem();
		final Subsystem c1 = installSubsystem(
				root,
				"c1", 
				new SubsystemArchiveBuilder()
						.symbolicName("c1")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE + ';' 
										+ AriesProvisionDependenciesDirective.RESOLVE.toString())
						.build(),
				false
		);
		uninstallableSubsystems.add(c1);
		c1.start();
		stoppableSubsystems.add(c1);
		@SuppressWarnings("unchecked")
		Callable<Subsystem>[] installCallables = new Callable[] {
				new Callable<Subsystem>() {
					@Override
					public Subsystem call() throws Exception {
						Subsystem result = installSubsystem(
								c1,
								"a1", 
								new SubsystemArchiveBuilder()
										.symbolicName("a1")
										.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION + ';' 
														+ AriesProvisionDependenciesDirective.RESOLVE.toString())
										.content("b1")
										.bundle(
												"b1", 
												new BundleArchiveBuilder()
														.symbolicName("b1")
														.importPackage("b2")
														.build())
										.build(),
								false);
						uninstallableSubsystems.add(result);
						return result;
					}
					
				},
				new Callable<Subsystem>() {
					@Override
					public Subsystem call() throws Exception {
						Subsystem result = installSubsystem(
								c1,
								"f1", 
								new SubsystemArchiveBuilder()
										.symbolicName("f1")
										.type(SubsystemConstants.SUBSYSTEM_TYPE_FEATURE + ';' 
														+ AriesProvisionDependenciesDirective.RESOLVE.toString())
										.content("b2")
										.bundle(
												"b2", 
												new BundleArchiveBuilder()
														.symbolicName("b2")
														.exportPackage("b2")
														.importPackage("b3")
														.build())
										.build(),
								false);
						uninstallableSubsystems.add(result);
						return result;
					}
					
				},
				new Callable<Subsystem>() {
					@Override
					public Subsystem call() throws Exception {
						Subsystem result = installSubsystem(
								c1,
								"f2", 
								new SubsystemArchiveBuilder()
										.symbolicName("f2")
										.type(SubsystemConstants.SUBSYSTEM_TYPE_FEATURE + ';' 
														+ AriesProvisionDependenciesDirective.RESOLVE.toString())
										.content("b4")
										.bundle(
												"b4", 
												new BundleArchiveBuilder()
														.symbolicName("b4")
														.exportPackage("b4")
														.importPackage("b2")
														.importPackage("b3")
														.build())
										.build(),
								false);
						uninstallableSubsystems.add(result);
						return result;
					}
					
				},
				new Callable<Subsystem>() {
					@Override
					public Subsystem call() throws Exception {
						Subsystem result = installSubsystem(
								c1,
								"c2", 
								new SubsystemArchiveBuilder()
										.symbolicName("c2")
										.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE + ';' 
														+ AriesProvisionDependenciesDirective.RESOLVE.toString())
										.content("b3;version=\"[0,0]\"")
										.exportPackage("b3")
										.importPackage("b4")
										.bundle(
												"b3", 
												new BundleArchiveBuilder()
														.symbolicName("b3")
														.exportPackage("b3")
														.importPackage("b4")
														.build())
										.build(),
								false);
						uninstallableSubsystems.add(result);
						return result;
					}
					
				}
		};
		ExecutorService executor = Executors.newFixedThreadPool(4);
		List<Future<Subsystem>> installFutures = executor.invokeAll(Arrays.asList(installCallables));
		final Subsystem a1 = installFutures.get(0).get();
		assertConstituent(a1, "b1");
		final Subsystem f1 = installFutures.get(1).get();
		assertConstituent(f1, "b2");
		final Subsystem f2 = installFutures.get(2).get();
		assertConstituent(f2, "b4");
		final Subsystem c2 = installFutures.get(3).get();
		assertConstituent(c2, "b3");
		@SuppressWarnings("unchecked")
		Callable<Null>[] startCallables = new Callable[] {
			new Callable<Null>() {
				@Override
				public Null call() throws Exception {
					a1.start();
					assertEvent(a1, State.INSTALLED, subsystemEvents.poll(a1.getSubsystemId(), 5000));
					assertEvent(a1, State.RESOLVING, subsystemEvents.poll(a1.getSubsystemId(), 5000));
					assertEvent(a1, State.RESOLVED, subsystemEvents.poll(a1.getSubsystemId(), 5000));
					stoppableSubsystems.add(a1);
					return null;
				}
			},
			new Callable<Null>() {
				@Override
				public Null call() throws Exception {
					f1.start();
					assertEvent(f1, State.INSTALLED, subsystemEvents.poll(f1.getSubsystemId(), 5000));
					assertEvent(f1, State.RESOLVING, subsystemEvents.poll(f1.getSubsystemId(), 5000));
					assertEvent(f1, State.RESOLVED, subsystemEvents.poll(f1.getSubsystemId(), 5000));
					stoppableSubsystems.add(f1);
					return null;
				}
			},
			new Callable<Null>() {
				@Override
				public Null call() throws Exception {
					f2.start();
					assertEvent(f2, State.INSTALLED, subsystemEvents.poll(f2.getSubsystemId(), 5000));
					assertEvent(f2, State.RESOLVING, subsystemEvents.poll(f2.getSubsystemId(), 5000));
					assertEvent(f2, State.RESOLVED, subsystemEvents.poll(f2.getSubsystemId(), 5000));
					stoppableSubsystems.add(f2);
					return null;
				}
			},
			new Callable<Null>() {
				@Override
				public Null call() throws Exception {
					c2.start();
					assertEvent(c2, State.INSTALLED, subsystemEvents.poll(c2.getSubsystemId(), 5000));
					assertEvent(c2, State.RESOLVING, subsystemEvents.poll(c2.getSubsystemId(), 5000));
					assertEvent(c2, State.RESOLVED, subsystemEvents.poll(c2.getSubsystemId(), 5000));
					stoppableSubsystems.add(c2);
					return null;
				}
			}
		};
		List<Future<Null>> startFutures = executor.invokeAll(Arrays.asList(startCallables));
		startFutures.get(0).get();
		startFutures.get(1).get();
		startFutures.get(2).get();
		startFutures.get(3).get();
	}
	
	/*
	 * (2) Subsystem with apache-aries-provision-dependencies:=resolve is in the 
	 * INSTALLING state after a successful installation.
	 */
	@Test
	public void test2() throws Exception {
		Subsystem root = getRootSubsystem();
		Subsystem subsystem = installSubsystem(root, APPLICATION_EMPTY, applicationEmpty(), false);
		try {
			assertState(State.INSTALLING, subsystem);
		}
		finally {
			uninstallSubsystemSilently(subsystem);
		}
	}
	
	/*
	 * (3) Subsystem with apache-aries-provision-dependencies:=resolve is available 
	 * as a service after a successful installation.
	 */
	@Test
	public void test3() throws Exception {
		Subsystem root = getRootSubsystem();
		Subsystem subsystem = installSubsystem(root, APPLICATION_EMPTY, applicationEmpty(), false);
		try {
			assertReferences(
					Subsystem.class, 
					"(&(subsystem.symbolicName=" 
							+ APPLICATION_EMPTY 
							+ ")(subsystem.version=0.0.0)(subsystem.type=" 
							+ SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION 
							+ ")(subsystem.state=" 
							+ State.INSTALLING 
							+ "))",
					1);
			assertReferences(
					AriesSubsystem.class, 
					"(&(subsystem.symbolicName=" 
							+ APPLICATION_EMPTY 
							+ ")(subsystem.version=0.0.0)(subsystem.type=" 
							+ SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION 
							+ ")(subsystem.state=" 
							+ State.INSTALLING 
							+ "))",
					1);
		}
		finally {
			uninstallSubsystemSilently(subsystem);
		}
	}
	
	/*
	 * (4) Subsystem with apache-aries-provision-dependencies:=resolve does not 
	 * have its dependencies installed after a successful installation.
	 */
	@Test
	public void test4() throws Exception {
		Subsystem root = getRootSubsystem();
		Subsystem subsystem = installSubsystem(root, APPLICATION_DEPENDENCY_IN_ARCHIVE, applicationDependencyInArchive(), false);
		try {
			assertConstituent(subsystem, BUNDLE_A);
			assertNotConstituent(subsystem, BUNDLE_B);
			assertNotConstituent(root, BUNDLE_B);
		}
		finally {
			uninstallSubsystemSilently(subsystem);
		}
	}
	
	/*
	 * (5) Subsystem with apache-aries-provision-dependencies:=resolve undergoes 
	 * the following state transitions when starting: INSTALLING -> INSTALLED 
	 * -> RESOLVING -> RESOLVED -> STARTING -> ACTIVE.
	 */
	@Test
	public void test5() throws Exception {
		Subsystem root = getRootSubsystem();
		subsystemEvents.clear();
		Subsystem subsystem = root.install(
				"application", 
				new SubsystemArchiveBuilder()
						.header(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, "application")
						.header(SubsystemConstants.SUBSYSTEM_TYPE, 
								SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION + ';' 
										+ AriesProvisionDependenciesDirective.RESOLVE.toString())
						.build()
		);
		try {
			long id = subsystem.getSubsystemId();
			assertEvent(subsystem, State.INSTALLING, subsystemEvents.poll(id, 5000));
			assertNull(subsystemEvents.poll(id, 1));
			subsystem.start();
			try {
				assertEvent(subsystem, State.INSTALLED, subsystemEvents.poll(id, 5000));
				assertEvent(subsystem, State.RESOLVING, subsystemEvents.poll(id, 5000));
				assertEvent(subsystem, State.RESOLVED, subsystemEvents.poll(id, 5000));
				assertEvent(subsystem, State.STARTING, subsystemEvents.poll(id, 5000));
				assertEvent(subsystem, State.ACTIVE, subsystemEvents.poll(id, 5000));
				assertNull(subsystemEvents.poll(id, 1));
			}
			finally {
				stopSubsystemSilently(subsystem);
			}
		}
		finally {
			uninstallSubsystemSilently(subsystem);
		}
	}
	
	/*
	 * (6) Subsystem with apache-aries-provision-dependencies:=resolve has its 
	 * dependencies installed after a successful start.
	 */
	@Test
	public void test6() throws Exception {
		Subsystem root = getRootSubsystem();
		Subsystem subsystem = root.install(
				"application", 
				new SubsystemArchiveBuilder()
						.symbolicName("application")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION + ';' 
										+ AriesProvisionDependenciesDirective.RESOLVE.toString())
						.content("bundle1")
						.bundle(
								"bundle1", 
								new BundleArchiveBuilder()
										.symbolicName("bundle1")
										.exportPackage("a")
										.importPackage("b")
										.build())
						.bundle(
								"bundle2", 
								new BundleArchiveBuilder()
										.symbolicName("bundle2")
										.exportPackage("b")
										.build())
						.build()
		);
		try {
			assertNotConstituent(root, "bundle2");
			startSubsystem(subsystem, false);
			try {
				assertConstituent(root, "bundle2");
			}
			finally {
				stopSubsystemSilently(subsystem);
			}
		}
		finally {
			uninstallSubsystemSilently(subsystem);
		}
	}
	
	/*
	 * (7) Subsystem with apache-aries-provision-dependencies:=resolve is in the 
	 * INSTALL_FAILED state after an unsuccessful installation.
	 */
	@Test
	public void test7() throws Exception {
		Subsystem root = getRootSubsystem();
		subsystemEvents.clear();
		try {
			Subsystem subsystem = root.install(APPLICATION_INSTALL_FAILED, applicationInstallFailed());
			uninstallSubsystemSilently(subsystem);
			fail("Subsystem should not have installed");
		}
		catch (SubsystemException e) {
			e.printStackTrace();
			long id = lastSubsystemId();
			assertEvent(id, APPLICATION_INSTALL_FAILED, Version.emptyVersion,
					SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
					State.INSTALLING, subsystemEvents.poll(id, 5000),
					ServiceEvent.REGISTERED);
			assertEvent(id, APPLICATION_INSTALL_FAILED, Version.emptyVersion,
					SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
					State.INSTALL_FAILED, subsystemEvents.poll(id, 5000),
					ServiceEvent.MODIFIED);
			assertEvent(id, APPLICATION_INSTALL_FAILED, Version.emptyVersion,
					SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
					State.UNINSTALLING, subsystemEvents.poll(id, 5000),
					ServiceEvent.MODIFIED);
			assertEvent(id, APPLICATION_INSTALL_FAILED, Version.emptyVersion,
					SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
					State.UNINSTALLED, subsystemEvents.poll(id, 5000),
					ServiceEvent.MODIFIED);
		}
	}
	
	/*
	 * (8) Subsystem with apache-aries-provision-dependencies:=resolve is not 
	 * available as a service after an unsuccessful installation.
	 */
	@Test
	public void test8() throws Exception {
		Subsystem root = getRootSubsystem();
		try {
			Subsystem subsystem = installSubsystem(root, APPLICATION_INSTALL_FAILED, applicationInstallFailed(), false);
			uninstallSubsystemSilently(subsystem);
			fail("Subsystem should not have installed");
		}
		catch (SubsystemException e) {
			e.printStackTrace();
			assertEquals("Subsystem service should not exist", 0,
					bundleContext.getServiceReferences(
							Subsystem.class, 
							"(" + SubsystemConstants.SUBSYSTEM_ID_PROPERTY + "=" + lastSubsystemId() + ")"
					).size());
		}
	}
	
	/*
	 * (9) Subsystem with apache-aries-provision-dependencies:=resolve is in the 
	 * INSTALLING state when dependencies cannot be provisioned after invoking 
	 * the start method.
	 */
	@Test
	public void test9() throws Exception {
		Subsystem root = getRootSubsystem();
		Subsystem subsystem = installSubsystem(root, APPLICATION_MISSING_DEPENDENCY, applicationMissingDependency(), false);
		try {
			startSubsystem(subsystem, false);
			stopSubsystemSilently(subsystem);
			fail("Subsystem should not have started");
		}
		catch (SubsystemException e) {
			e.printStackTrace();
			assertState(State.INSTALLING, subsystem);
		}
		finally {
			uninstallSubsystemSilently(subsystem);
		}
	}
	
	/*
	 * (10) Subsystem fails installation if the apache-aries-provision-dependencies 
	 * directive has a value other than "install" or "resolve".
	 */
	@Test
	public void test10() throws Exception {
		Subsystem root = getRootSubsystem();
		try {
			Subsystem subsystem = installSubsystem(
					root, 
					APPLICATION_INVALID_PROVISION_DEPENDENCIES, 
					applicationInvalidProvisionDependencies(), 
					false);
			uninstallSubsystemSilently(subsystem);
			fail("Subsystem should not have installed");
		}
		catch (SubsystemException e) {
			e.printStackTrace();
		}
	}
	
	/*
	 * (11) Subsystem with apache-aries-provision-dependencies:=resolve undergoes 
	 * the following state transitions when starting fails due to a runtime 
	 * resolution failure: INSTALLING -> INSTALLED -> RESOLVING -> INSTALLED.
	 */
	@Test
	public void test11() throws Exception {
		Subsystem root = getRootSubsystem();
		subsystemEvents.clear();
		Subsystem subsystem = root.install(APPLICATION_DEPENDENCY_IN_ARCHIVE, applicationDependencyInArchive());
		ServiceRegistration<ResolverHookFactory> registration = bundleContext.registerService(
				ResolverHookFactory.class, 
				new ResolverHookFactory() {
					@Override
					public ResolverHook begin(Collection<BundleRevision> triggers) {
						return new ResolverHook() {
							@Override
							public void filterResolvable(Collection<BundleRevision> candidates) {
								for (Iterator<BundleRevision> i = candidates.iterator(); i.hasNext();) {
									BundleRevision revision = i.next();
									if (revision.getSymbolicName().equals(BUNDLE_B)) {
										i.remove();
									}
								}
							}

							@Override
							public void filterSingletonCollisions(
									BundleCapability singleton,
									Collection<BundleCapability> collisionCandidates) {
								// Nothing.
							}

							@Override
							public void filterMatches(
									BundleRequirement requirement,
									Collection<BundleCapability> candidates) {
								// Nothing.
							}

							@Override
							public void end() {
								// Nothing.
							}
						};
					}
				}, 
				null
		);
		try {
			subsystem.start();
			stopSubsystemSilently(subsystem);
			fail("Subsystem should not have started");
		}
		catch (SubsystemException e) {
			e.printStackTrace();
			long id = lastSubsystemId();
			assertEvent(id, APPLICATION_DEPENDENCY_IN_ARCHIVE, Version.emptyVersion,
					SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
					State.INSTALLING, subsystemEvents.poll(id, 5000),
					ServiceEvent.REGISTERED);
			assertEvent(id, APPLICATION_DEPENDENCY_IN_ARCHIVE, Version.emptyVersion,
					SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
					State.INSTALLED, subsystemEvents.poll(id, 5000),
					ServiceEvent.MODIFIED);
			assertEvent(id, APPLICATION_DEPENDENCY_IN_ARCHIVE, Version.emptyVersion,
					SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
					State.RESOLVING, subsystemEvents.poll(id, 5000),
					ServiceEvent.MODIFIED);
			assertEvent(id, APPLICATION_DEPENDENCY_IN_ARCHIVE, Version.emptyVersion,
					SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
					State.INSTALLED, subsystemEvents.poll(id, 5000),
					ServiceEvent.MODIFIED);
		}
		finally {
			registration.unregister();
			uninstallSubsystemSilently(subsystem);
		}
	}
	
	/*
	 * (12) Subsystem with apache-aries-provision-dependencies:=resolve undergoes 
	 * the following state transitions when starting fails due to a start 
	 * failure: INSTALLING -> INSTALLED -> RESOLVING -> RESOLVED -> STARTING -> 
	 * RESOLVED.
	 */
	@Test
	public void test12() throws Exception {
		Subsystem root = getRootSubsystem();
		subsystemEvents.clear();
		Subsystem subsystem = root.install(APPLICATION_START_FAILURE, applicationStartFailure());
		try {
			subsystem.start();
			stopSubsystemSilently(subsystem);
			fail("Subsystem should not have started");
		}
		catch (SubsystemException e) {
			e.printStackTrace();
			long id = lastSubsystemId();
			assertEvent(id, APPLICATION_START_FAILURE, Version.emptyVersion,
					SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
					State.INSTALLING, subsystemEvents.poll(id, 5000),
					ServiceEvent.REGISTERED);
			assertEvent(id, APPLICATION_START_FAILURE, Version.emptyVersion,
					SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
					State.INSTALLED, subsystemEvents.poll(id, 5000),
					ServiceEvent.MODIFIED);
			assertEvent(id, APPLICATION_START_FAILURE, Version.emptyVersion,
					SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
					State.RESOLVING, subsystemEvents.poll(id, 5000),
					ServiceEvent.MODIFIED);
			assertEvent(id, APPLICATION_START_FAILURE, Version.emptyVersion,
					SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
					State.RESOLVED, subsystemEvents.poll(id, 5000),
					ServiceEvent.MODIFIED);
			assertEvent(id, APPLICATION_START_FAILURE, Version.emptyVersion,
					SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
					State.STARTING, subsystemEvents.poll(id, 5000),
					ServiceEvent.MODIFIED);
			assertEvent(id, APPLICATION_START_FAILURE, Version.emptyVersion,
					SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
					State.RESOLVED, subsystemEvents.poll(id, 5000),
					ServiceEvent.MODIFIED);
		}
		finally {
			uninstallSubsystemSilently(subsystem);
		}
	}
	
	/*
	 * (13) The root subsystem has apache-aries-provision-dependencies:=install.
	 */
	@Test
	public void test13() throws Exception {
		Subsystem root = getRootSubsystem();
		Map<String, String> headers = root.getSubsystemHeaders(null);
		String headerStr = headers.get(SubsystemConstants.SUBSYSTEM_TYPE);
		SubsystemTypeHeader header = new SubsystemTypeHeader(headerStr);
		AriesProvisionDependenciesDirective directive = header.getAriesProvisionDependenciesDirective();
		assertEquals(
				"Wrong directive", 
				AriesProvisionDependenciesDirective.INSTALL,
				directive);
	}
	
	/*
	 * (14) Subsystem with explicit apache-aries-provision-dependencies:=install 
	 * works as before.
	 */
	@Test
	public void test14() throws Exception {
		Subsystem root = getRootSubsystem();
		Subsystem subsystem = installSubsystem(root, APPLICATION_PROVISION_DEPENDENCIES_INSTALL,
				applicationProvisionDependenciesInstall(), true);
		try {
			assertConstituent(subsystem, BUNDLE_A);
			assertConstituent(root, BUNDLE_B);
			startSubsystem(subsystem, true);
			stopSubsystem(subsystem);
		}
		finally {
			uninstallSubsystem(subsystem);
		}
	}
	
	/*
	 * (15) Unscoped subsystem with a value of apache-aries-provision-dependencies 
	 * that is different than the scoped parent fails installation.
	 */
	@Test
	public void test15a() throws Exception {
		Subsystem root = getRootSubsystem();
		try {
			Subsystem subsystem = installSubsystem(root, FEATURE_PROVISION_DEPENDENCIES_RESOLVE,
					featureProvisionDependenciesResolve(), false);
			uninstallSubsystemSilently(subsystem);
			fail("Subsystem should not have installed");
		}
		catch (SubsystemException e) {
			e.printStackTrace();
		}
	}
	
	/*
	 * (15) Unscoped subsystem with a value of apache-aries-provision-dependencies 
	 * that is different than the scoped parent fails installation.
	 */
	@Test
	public void test15b() throws Exception {
		Subsystem root = getRootSubsystem();
		Subsystem application = installSubsystem(root, APPLICATION_PROVISION_DEPENDENCIES_INSTALL,
				applicationProvisionDependenciesInstall(), true);
		try {
			Subsystem feature = installSubsystem(application, FEATURE_PROVISION_DEPENDENCIES_RESOLVE,
					featureProvisionDependenciesResolve(), false);
			uninstallSubsystemSilently(feature);
			fail("Subsystem should not have installed");
		}
		catch (SubsystemException e) {
			e.printStackTrace();
		}
		finally {
			uninstallSubsystemSilently(application);
		}
	}
	
	/*
	 * (16) Unscoped subsystem with a value of apache-aries-provision-dependencies 
	 * that is the same as the scoped parent installs successfully.
	 */
	@Test
	public void test16a() throws Exception {
		Subsystem root = getRootSubsystem();
		try {
			Subsystem subsystem = installSubsystem(
					root, 
					FEATURE_PROVISION_DEPENDENCIES_INSTALL,
					new SubsystemArchiveBuilder()
							.symbolicName("application")
							.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION + ';' 
									+ AriesProvisionDependenciesDirective.INSTALL.toString())
							.build(), 
					true);
			uninstallSubsystemSilently(subsystem);
		}
		catch (SubsystemException e) {
			e.printStackTrace();
			fail("Subsystem should have installed");
		}
	}
	
	/*
	 * (16) Unscoped subsystem with a value of apache-aries-provision-dependencies 
	 * that is the same as the scoped parent installs successfully.
	 */
	@Test
	public void test16b() throws Exception {
		Subsystem root = getRootSubsystem();
		Subsystem application = installSubsystem(
				root, 
				"application",
				new SubsystemArchiveBuilder()
						.symbolicName("application")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION + ';' 
								+ AriesProvisionDependenciesDirective.INSTALL.toString())
						.build(), 
				true);
		try {
			Subsystem feature = installSubsystem(
					application, 
					"feature",
					new SubsystemArchiveBuilder()
							.symbolicName("feature")
							.type(SubsystemConstants.SUBSYSTEM_TYPE_FEATURE + ';' 
									+ AriesProvisionDependenciesDirective.INSTALL.toString())
							.build(),  
					true);
			uninstallSubsystemSilently(feature);
		}
		catch (SubsystemException e) {
			e.printStackTrace();
			fail("Subsystem should have installed");
		}
		finally {
			uninstallSubsystemSilently(application);
		}
	}
	
	/*
	 * (16) Unscoped subsystem with a value of apache-aries-provision-dependencies 
	 * that is the same as the scoped parent installs successfully.
	 */
	@Test
	public void test16c() throws Exception {
		Subsystem root = getRootSubsystem();
		try {
			Subsystem subsystem = installSubsystem(
					root, 
					"application",
					new SubsystemArchiveBuilder()
							.symbolicName("application")
							.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION + ';' 
									+ AriesProvisionDependenciesDirective.RESOLVE.toString())
							.subsystem(
									"feature", 
									new SubsystemArchiveBuilder()
											.symbolicName("feature")
											.type(SubsystemConstants.SUBSYSTEM_TYPE_FEATURE + ';' 
													+ AriesProvisionDependenciesDirective.RESOLVE.toString())
											.build())
							.build(), 
					false);
			uninstallSubsystemSilently(subsystem);
		}
		catch (SubsystemException e) {
			e.printStackTrace();
			fail("Subsystem should have installed");
		}
	}
	
	/*
	 * (17) Scoped subsystem with a value of apache-aries-provision-dependencies 
	 * that is the same as the scoped parent behaves accordingly.
	 */
	@Test
	public void test17() throws Exception {
		Subsystem root = getRootSubsystem();
		subsystemEvents.clear();
		Subsystem parent = root.install(APPLICATION_B, applicationB());
		try {
			long id = parent.getSubsystemId();
			assertEvent(id, APPLICATION_B, Version.emptyVersion,
					SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
					State.INSTALLING, subsystemEvents.poll(id, 5000),
					ServiceEvent.REGISTERED);
			assertNull("Unexpected event", subsystemEvents.poll(id, 1));
			assertNotConstituent(root, BUNDLE_B);
			assertConstituent(parent, BUNDLE_A);
			Subsystem child = getChild(parent, APPLICATION_A);
			assertNotNull("Missing child", child);
			id = child.getSubsystemId();
			assertEvent(id, APPLICATION_A, Version.emptyVersion,
					SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
					State.INSTALLING, subsystemEvents.poll(id, 5000),
					ServiceEvent.REGISTERED);
			assertNull("Unexpected event", subsystemEvents.poll(id, 1));
			assertNotConstituent(root, BUNDLE_D);
			assertConstituent(child, BUNDLE_A);
			assertConstituent(child, BUNDLE_C);
			parent.start();
			try {
				id = parent.getSubsystemId();
				assertEvent(id, APPLICATION_B, Version.emptyVersion,
						SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
						State.INSTALLED, subsystemEvents.poll(id, 5000),
						ServiceEvent.MODIFIED);
				assertEvent(id, APPLICATION_B, Version.emptyVersion,
						SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
						State.RESOLVING, subsystemEvents.poll(id, 5000),
						ServiceEvent.MODIFIED);
				assertEvent(id, APPLICATION_B, Version.emptyVersion,
						SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
						State.RESOLVED, subsystemEvents.poll(id, 5000),
						ServiceEvent.MODIFIED);
				assertEvent(id, APPLICATION_B, Version.emptyVersion,
						SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
						State.STARTING, subsystemEvents.poll(id, 5000),
						ServiceEvent.MODIFIED);
				assertEvent(id, APPLICATION_B, Version.emptyVersion,
						SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
						State.ACTIVE, subsystemEvents.poll(id, 5000),
						ServiceEvent.MODIFIED);
				assertNull("Unexpected event", subsystemEvents.poll(id, 1));
				assertConstituent(root, BUNDLE_B);
				id = child.getSubsystemId();
				assertEvent(id, APPLICATION_A, Version.emptyVersion,
						SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
						State.INSTALLED, subsystemEvents.poll(id, 5000),
						ServiceEvent.MODIFIED);
				assertEvent(id, APPLICATION_A, Version.emptyVersion,
						SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
						State.RESOLVING, subsystemEvents.poll(id, 5000),
						ServiceEvent.MODIFIED);
				assertEvent(id, APPLICATION_A, Version.emptyVersion,
						SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
						State.RESOLVED, subsystemEvents.poll(id, 5000),
						ServiceEvent.MODIFIED);
				assertEvent(id, APPLICATION_A, Version.emptyVersion,
						SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
						State.STARTING, subsystemEvents.poll(id, 5000),
						ServiceEvent.MODIFIED);
				assertEvent(id, APPLICATION_A, Version.emptyVersion,
						SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
						State.ACTIVE, subsystemEvents.poll(id, 5000),
						ServiceEvent.MODIFIED);
				assertNull("Unexpected event", subsystemEvents.poll(id, 1));
				assertConstituent(root, BUNDLE_D);
			}
			finally {
				stopSubsystemSilently(parent);
			}
		}
		finally {
			uninstallSubsystemSilently(parent);
		}
	}
	
	/*
	 * (18) Scoped subsystem with a value of apache-aries-provision-dependencies 
	 * that overrides the scoped parent behaves accordingly.
	 */
	@Test
	public void test18() throws Exception {
		Subsystem root = getRootSubsystem();
		subsystemEvents.clear();
		Subsystem parent = root.install(
				"parent", 
				new SubsystemArchiveBuilder()
						.symbolicName("parent")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION 
								+ ';' 
								+ AriesProvisionDependenciesDirective.INSTALL.toString()
								+ ';'
								+ SubsystemConstants.PROVISION_POLICY_DIRECTIVE
								+ ":="
								+ SubsystemConstants.PROVISION_POLICY_ACCEPT_DEPENDENCIES)
						.content("bundle1")
						.bundle(
								"bundle1", 
								new BundleArchiveBuilder()
										.symbolicName("bundle1")
										.importPackage("a")
										.build())
						.bundle(
								"bundle2", 
								new BundleArchiveBuilder()
										.symbolicName("bundle2")
										.exportPackage("a")
										.build())
				.build()
		);
		try {
			long id = parent.getSubsystemId();
			assertEvent(id, "parent", Version.emptyVersion,
					SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
					State.INSTALLING, subsystemEvents.poll(id, 5000),
					ServiceEvent.REGISTERED);
			assertEvent(id, "parent", Version.emptyVersion,
					SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
					State.INSTALLED, subsystemEvents.poll(id, 5000),
					ServiceEvent.MODIFIED);
			assertNull("Unexpected event", subsystemEvents.poll(id, 1));
			assertConstituent(parent, "bundle2");
			assertConstituent(parent, "bundle1");
			parent.start();
			Subsystem child = parent.install(
					"child", 
					new SubsystemArchiveBuilder()
							.symbolicName("child")
							.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION + ';' 
									+ AriesProvisionDependenciesDirective.RESOLVE.toString())
							.content("bundle3")
							.bundle(
									"bundle3", 
									new BundleArchiveBuilder()
											.symbolicName("bundle3")
											.importPackage("b")
											.build())
							.bundle(
									"bundle4", 
									new BundleArchiveBuilder()
											.symbolicName("bundle4")
											.exportPackage("b")
											.build())
							.build()
			);
			id = child.getSubsystemId();
			assertEvent(id, "child", Version.emptyVersion,
					SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
					State.INSTALLING, subsystemEvents.poll(id, 5000),
					ServiceEvent.REGISTERED);
			assertNull("Unexpected event", subsystemEvents.poll(id, 1));
			assertNotConstituent(parent, "bundle4");
			assertConstituent(child, "bundle3");
			child.start();
			try {
				id = parent.getSubsystemId();
				assertEvent(id, "parent", Version.emptyVersion,
						SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
						State.RESOLVING, subsystemEvents.poll(id, 5000),
						ServiceEvent.MODIFIED);
				assertEvent(id, "parent", Version.emptyVersion,
						SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
						State.RESOLVED, subsystemEvents.poll(id, 5000),
						ServiceEvent.MODIFIED);
				assertEvent(id, "parent", Version.emptyVersion,
						SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
						State.STARTING, subsystemEvents.poll(id, 5000),
						ServiceEvent.MODIFIED);
				assertEvent(id, "parent", Version.emptyVersion,
						SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
						State.ACTIVE, subsystemEvents.poll(id, 5000),
						ServiceEvent.MODIFIED);
				assertNull("Unexpected event", subsystemEvents.poll(id, 1));
				id = child.getSubsystemId();
				assertEvent(id, "child", Version.emptyVersion,
						SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
						State.INSTALLED, subsystemEvents.poll(id, 5000),
						ServiceEvent.MODIFIED);
				assertEvent(id, "child", Version.emptyVersion,
						SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
						State.RESOLVING, subsystemEvents.poll(id, 5000),
						ServiceEvent.MODIFIED);
				assertEvent(id, "child", Version.emptyVersion,
						SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
						State.RESOLVED, subsystemEvents.poll(id, 5000),
						ServiceEvent.MODIFIED);
				assertEvent(id, "child", Version.emptyVersion,
						SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
						State.STARTING, subsystemEvents.poll(id, 5000),
						ServiceEvent.MODIFIED);
				assertEvent(id, "child", Version.emptyVersion,
						SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, 
						State.ACTIVE, subsystemEvents.poll(id, 5000),
						ServiceEvent.MODIFIED);
				assertNull("Unexpected event", subsystemEvents.poll(id, 1));
				assertConstituent(parent, "bundle4");
			}
			finally {
				stopSubsystemSilently(parent);
			}
		}
		finally {
			uninstallSubsystemSilently(parent);
		}
	}
	
	/*
	 * (19) Scoped subsystem with only features as parents is able to override 
	 * the value of apache-aries-provision-dependencies.
	 */
	@Test
	public void test19() throws Exception {
		Subsystem root = getRootSubsystem();
		subsystemEvents.clear();
		Subsystem feature1 = installSubsystem(
				root,
				"feature1", 
				new SubsystemArchiveBuilder()
						.symbolicName("feature1")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_FEATURE)
						.build()
		);
		try {
			Subsystem feature2 = installSubsystem(
					root,
					"feature2", 
					new SubsystemArchiveBuilder()
							.symbolicName("feature2")
							.type(SubsystemConstants.SUBSYSTEM_TYPE_FEATURE
									+ ';'
									+ AriesProvisionDependenciesDirective.INSTALL.toString())
							.build()
			);
			try {
				SubsystemArchiveBuilder applicationArchive = new SubsystemArchiveBuilder()
						.symbolicName("application")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION
								+ ';'
								+ AriesProvisionDependenciesDirective.RESOLVE.toString()
								+ ';'
								+ SubsystemConstants.PROVISION_POLICY_DIRECTIVE
								+ ":="
								+ SubsystemConstants.PROVISION_POLICY_ACCEPT_DEPENDENCIES)
						.content("bundle1")
						.bundle(
								"bundle1", 
								new BundleArchiveBuilder()
										.symbolicName("bundle1")
										.importPackage("a")
										.build())
						.bundle(
								"bundle2",
								new BundleArchiveBuilder()
										.symbolicName("bundle2")
										.exportPackage("a")
										.build());
				Subsystem application1 = feature1.install("application", applicationArchive.build());
				Subsystem application2 = feature2.install("application", applicationArchive.build());
				assertSame("Wrong subsystem", application1, application2);
				assertEquals("Wrong subsystem", application1, application2);
				assertChild(feature1, "application");
				assertChild(feature2, "application");
				long id = application1.getSubsystemId();
				assertEvent(id, "application", Version.emptyVersion,
						SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, State.INSTALLING, 
						subsystemEvents.poll(id, 5000), ServiceEvent.REGISTERED);
				assertNull("Unexpected event", subsystemEvents.poll(id, 1));
				assertConstituent(application1, "bundle1");
				assertNotConstituent(application1, "bundle2");
				application1.start();
				try {
					assertEvent(id, "application", Version.emptyVersion,
							SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, State.INSTALLED, 
							subsystemEvents.poll(id, 5000), ServiceEvent.MODIFIED);
					assertEvent(id, "application", Version.emptyVersion,
							SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, State.RESOLVING, 
							subsystemEvents.poll(id, 5000), ServiceEvent.MODIFIED);
					assertEvent(id, "application", Version.emptyVersion,
							SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, State.RESOLVED, 
							subsystemEvents.poll(id, 5000), ServiceEvent.MODIFIED);
					assertEvent(id, "application", Version.emptyVersion,
							SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, State.STARTING, 
							subsystemEvents.poll(id, 5000), ServiceEvent.MODIFIED);
					assertEvent(id, "application", Version.emptyVersion,
							SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION, State.ACTIVE, 
							subsystemEvents.poll(id, 5000), ServiceEvent.MODIFIED);
					assertNull("Unexpected event", subsystemEvents.poll(id, 1));
					assertConstituent(application1, "bundle2");
				}
				finally {
					stopSubsystemSilently(application1);
				}
			}
			finally {
				uninstallSubsystemSilently(feature2);
			}
		}
		finally {
			uninstallSubsystemSilently(feature1);
		}
	}
	
	/*
	 * (20) Install a scoped subsystem, S1, with 
	 * apache-aries-provision-dependencies:=resolve. Install two features, F1 and 
	 * F2, independently as children of S1. F1 has bundle B1 as content. F2 has 
	 * bundle B2 as content. B2 has B1 as a dependency. B1 should be a 
	 * constituent of F1 but not of the root subsystem.
	 */
	@Test
	public void test20() throws Exception {
		serviceRegistrations.add(bundleContext.registerService(
				Repository.class,
				new TestRepository.Builder()
		        		.resource(new TestRepositoryContent.Builder()
		                		.capability(new TestCapability.Builder()
		                        		.namespace(IdentityNamespace.IDENTITY_NAMESPACE)
		                        		.attribute(
		                        				IdentityNamespace.IDENTITY_NAMESPACE, 
		                        				"b1")
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, 
		                        				Version.emptyVersion)
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE,
		                        				IdentityNamespace.TYPE_BUNDLE))
		                        .content(new BundleArchiveBuilder()
		                        		.symbolicName("b1")
		                        		.exportPackage("a")
		                        		.buildAsBytes())
		                        .build())
		                .resource(new TestRepositoryContent.Builder()
		                		.capability(new TestCapability.Builder()
		                        		.namespace(IdentityNamespace.IDENTITY_NAMESPACE)
		                        		.attribute(
		                        				IdentityNamespace.IDENTITY_NAMESPACE, 
		                        				"b2")
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, 
		                        				Version.emptyVersion)
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE,
		                        				IdentityNamespace.TYPE_BUNDLE))
		                        .content(new BundleArchiveBuilder()
		                        		.symbolicName("b2")
		                        		.importPackage("a")
		                        		.buildAsBytes())
		                        .build())
		        		.build(),
                null));
		Subsystem root = getRootSubsystem();
		Subsystem s1 = installSubsystem(
				root,
				"s1",
				new SubsystemArchiveBuilder()
						.symbolicName("s1")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION
								+ ';'
								+ AriesProvisionDependenciesDirective.RESOLVE.toString())
						.build(),
				false
		);
		uninstallableSubsystems.add(s1);
		startSubsystem(s1, false);
		stoppableSubsystems.add(s1);
		Subsystem f2 = installSubsystem(
				s1,
				"f2",
				new SubsystemArchiveBuilder()
						.symbolicName("f2")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_FEATURE)
						.content("b2")
						.build(),
				false
		);
		uninstallableSubsystems.add(f2);
		assertChild(s1, "f2", null, SubsystemConstants.SUBSYSTEM_TYPE_FEATURE);
		assertConstituent(f2, "b2");
		Subsystem f1 = installSubsystem(
				s1,
				"f1",
				new SubsystemArchiveBuilder()
						.symbolicName("f1")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_FEATURE)
						.content("b1")
						.build(),
				false
		);
		uninstallableSubsystems.add(f1);
		assertChild(s1, "f1", null, SubsystemConstants.SUBSYSTEM_TYPE_FEATURE);
		assertConstituent(f1, "b1");
		assertNotConstituent(root, "b1");
	}
	
	/*
	 * (21) Install a scoped subsystem, S1, with 
	 * apache-aries-provision-dependencies:=resolve. Install two features, F1 and 
	 * F2, independently as children of S1. F1 has bundle B1 and B2 as content. 
	 * F2 has bundle B2 and B3 as content. B2 is shared content. B1 has a 
	 * dependency on bundle B4, B2 has a dependency on bundle B5. B3 has a 
	 * dependency on bundle B6. Start F1. Dependency bundles B4 and B5 should be 
	 * provisioned but not B6.
	 */
	@Test
	public void test21() throws Exception {
		serviceRegistrations.add(bundleContext.registerService(
				Repository.class,
				new TestRepository.Builder()
		        		.resource(new TestRepositoryContent.Builder()
		                		.capability(new TestCapability.Builder()
		                        		.namespace(IdentityNamespace.IDENTITY_NAMESPACE)
		                        		.attribute(
		                        				IdentityNamespace.IDENTITY_NAMESPACE, 
		                        				"b1")
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, 
		                        				Version.emptyVersion)
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE,
		                        				IdentityNamespace.TYPE_BUNDLE))
		                        .requirement(new TestRequirement.Builder()
		                        		.namespace(PackageNamespace.PACKAGE_NAMESPACE)
		                        		.directive(
		                        				PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE,
		                        				"(osgi.wiring.package=b4)"))
		                        .content(new BundleArchiveBuilder()
		                        		.symbolicName("b1")
		                        		.importPackage("b4")
		                        		.buildAsBytes())
		                        .build())
		                .resource(new TestRepositoryContent.Builder()
		                		.capability(new TestCapability.Builder()
		                        		.namespace(IdentityNamespace.IDENTITY_NAMESPACE)
		                        		.attribute(
		                        				IdentityNamespace.IDENTITY_NAMESPACE, 
		                        				"b2")
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, 
		                        				Version.emptyVersion)
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE,
		                        				IdentityNamespace.TYPE_BUNDLE))
		                        .requirement(new TestRequirement.Builder()
		                        		.namespace(PackageNamespace.PACKAGE_NAMESPACE)
		                        		.directive(
		                        				PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE,
		                        				"(osgi.wiring.package=b5)"))
		                        .content(new BundleArchiveBuilder()
		                        		.symbolicName("b2")
		                        		.importPackage("b5")
		                        		.buildAsBytes())
		                        .build())
		                .resource(new TestRepositoryContent.Builder()
		                		.capability(new TestCapability.Builder()
		                        		.namespace(IdentityNamespace.IDENTITY_NAMESPACE)
		                        		.attribute(
		                        				IdentityNamespace.IDENTITY_NAMESPACE, 
		                        				"b3")
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, 
		                        				Version.emptyVersion)
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE,
		                        				IdentityNamespace.TYPE_BUNDLE))
		                        .requirement(new TestRequirement.Builder()
		                        		.namespace(PackageNamespace.PACKAGE_NAMESPACE)
		                        		.directive(
		                        				PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE,
		                        				"(osgi.wiring.package=b6)"))
		                        .content(new BundleArchiveBuilder()
		                        		.symbolicName("b3")
		                        		.importPackage("b6")
		                        		.buildAsBytes())
		                        .build())
		                .resource(new TestRepositoryContent.Builder()
		                		.capability(new TestCapability.Builder()
		                        		.namespace(IdentityNamespace.IDENTITY_NAMESPACE)
		                        		.attribute(
		                        				IdentityNamespace.IDENTITY_NAMESPACE, 
		                        				"b4")
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, 
		                        				Version.emptyVersion)
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE,
		                        				IdentityNamespace.TYPE_BUNDLE))
		                        .capability(new TestCapability.Builder()
		                        		.namespace(PackageNamespace.PACKAGE_NAMESPACE)
		                        		.attribute(
		                        				PackageNamespace.PACKAGE_NAMESPACE,
		                        				"b4"))
		                        .content(new BundleArchiveBuilder()
		                        		.symbolicName("b4")
		                        		.exportPackage("b4")
		                        		.buildAsBytes())
		                        .build())
		                .resource(new TestRepositoryContent.Builder()
		                		.capability(new TestCapability.Builder()
		                        		.namespace(IdentityNamespace.IDENTITY_NAMESPACE)
		                        		.attribute(
		                        				IdentityNamespace.IDENTITY_NAMESPACE, 
		                        				"b5")
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, 
		                        				Version.emptyVersion)
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE,
		                        				IdentityNamespace.TYPE_BUNDLE))
		                        .capability(new TestCapability.Builder()
		                        		.namespace(PackageNamespace.PACKAGE_NAMESPACE)
		                        		.attribute(
		                        				PackageNamespace.PACKAGE_NAMESPACE,
		                        				"b5"))
		                        .content(new BundleArchiveBuilder()
		                        		.symbolicName("b5")
		                        		.exportPackage("b5")
		                        		.buildAsBytes())
		                        .build())
		                .resource(new TestRepositoryContent.Builder()
		                		.capability(new TestCapability.Builder()
		                        		.namespace(IdentityNamespace.IDENTITY_NAMESPACE)
		                        		.attribute(
		                        				IdentityNamespace.IDENTITY_NAMESPACE, 
		                        				"b6")
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, 
		                        				Version.emptyVersion)
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE,
		                        				IdentityNamespace.TYPE_BUNDLE))
		                        .capability(new TestCapability.Builder()
		                        		.namespace(PackageNamespace.PACKAGE_NAMESPACE)
		                        		.attribute(
		                        				PackageNamespace.PACKAGE_NAMESPACE,
		                        				"b6"))
		                        .content(new BundleArchiveBuilder()
		                        		.symbolicName("b6")
		                        		.exportPackage("b6")
		                        		.buildAsBytes())
		                        .build())
		        		.build(),
                null));
		Subsystem root = getRootSubsystem();
		Subsystem s1 = installSubsystem(
				root,
				"s1",
				new SubsystemArchiveBuilder()
						.symbolicName("s1")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION
								+ ';'
								+ AriesProvisionDependenciesDirective.RESOLVE.toString()
								+ ';'
								+ SubsystemConstants.PROVISION_POLICY_DIRECTIVE
								+ ":="
								+ SubsystemConstants.PROVISION_POLICY_ACCEPT_DEPENDENCIES)
						.build(),
				false
		);
		uninstallableSubsystems.add(s1);
		startSubsystem(s1, false);
		stoppableSubsystems.add(s1);
		Subsystem f2 = installSubsystem(
				s1,
				"f2",
				new SubsystemArchiveBuilder()
				.symbolicName("f2")
				.type(SubsystemConstants.SUBSYSTEM_TYPE_FEATURE)
				.content("b2,b3")
				.build(),
				false
				);
		uninstallableSubsystems.add(f2);
		assertChild(s1, "f2", null, SubsystemConstants.SUBSYSTEM_TYPE_FEATURE);
		assertConstituent(s1, "f2", null, SubsystemConstants.SUBSYSTEM_TYPE_FEATURE);
		assertConstituent(f2, "b2");
		assertConstituent(f2, "b3");
		Subsystem f1 = installSubsystem(
				s1,
				"f1",
				new SubsystemArchiveBuilder()
				.symbolicName("f1")
				.type(SubsystemConstants.SUBSYSTEM_TYPE_FEATURE)
				.content("b1,b2")
				.build(),
				false
				);
		uninstallableSubsystems.add(f1);
		assertChild(s1, "f1", null, SubsystemConstants.SUBSYSTEM_TYPE_FEATURE);
		assertConstituent(s1, "f1", null, SubsystemConstants.SUBSYSTEM_TYPE_FEATURE);
		assertConstituent(f1, "b1");
		assertConstituent(f1, "b2");
		startSubsystem(f1, false);
		stoppableSubsystems.add(f1);
		assertState(EnumSet.of(State.RESOLVED, State.ACTIVE), f2);
		assertConstituent(s1, "b4");
		assertConstituent(s1, "b5");
		assertConstituent(s1, "b6");
	}
	
	@Test
	public void testFullLifeCycle() throws Exception {
		Subsystem root = getRootSubsystem();
		Subsystem subsystem = installSubsystem(root, APPLICATION_EMPTY, applicationEmpty(), false);
		startSubsystem(subsystem, false);
		stopSubsystem(subsystem);
		uninstallSubsystem(subsystem);
	}
	
	@Test
	public void testImplicitlyInstalledChildOverridesProvisionDependencies() throws Exception {
		Subsystem root = getRootSubsystem();
		subsystemEvents.clear();
		try {
			Subsystem subsystem = root.install(
					"parent", 
					new SubsystemArchiveBuilder()
							.symbolicName("parent")
							.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION + ';' +
									AriesProvisionDependenciesDirective.INSTALL.toString())
							.subsystem(
									"child", 
									new SubsystemArchiveBuilder()
											.symbolicName("child")
											.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION + ';' +
													AriesProvisionDependenciesDirective.RESOLVE.toString())
											.build())
							.build());
			uninstallSubsystemSilently(subsystem);
			fail("Subsystem should not have installed");
		}
		catch (SubsystemException e) {
			e.printStackTrace();
		}
	}
	
	@Test
	public void testInstall() throws Exception {
		Subsystem root = getRootSubsystem();
		try {
			Subsystem subsystem = installSubsystem(root, APPLICATION_EMPTY, applicationEmpty(), false);
			uninstallSubsystemSilently(subsystem);
		}
		catch (Exception e) {
			e.printStackTrace();
			fail("Subsystem should have installed");
		}
	}
	
	@Test
	public void testInstallChildIntoInstallingParent() throws Exception {
		Subsystem root = getRootSubsystem();
		Subsystem subsystem = installSubsystem(root, APPLICATION_DEPENDENCY_IN_ARCHIVE, applicationDependencyInArchive(), false);
		try {
			assertState(State.INSTALLING, subsystem);
			installSubsystem(subsystem, APPLICATION_A, applicationA(), false);
			fail("Subsystem should not have installed");
		}
		catch (SubsystemException e) {
			e.printStackTrace();
		}
		finally {
			uninstallSubsystemSilently(subsystem);
		}
	}
	
	@Test
	public void testStart() throws Exception {
		Subsystem root = getRootSubsystem();
		Subsystem subsystem = installSubsystem(root, APPLICATION_EMPTY, applicationEmpty(), false);
		try {
			startSubsystem(subsystem, false);
			stopSubsystemSilently(subsystem);
		}
		catch (Exception e) {
			e.printStackTrace();
			fail("Subsystem should have started");
		}
		finally {
			uninstallSubsystemSilently(subsystem);
		}
	}
	
	@Test
	public void testStop() throws Exception {
		Subsystem root = getRootSubsystem();
		Subsystem subsystem = installSubsystem(root, APPLICATION_EMPTY, applicationEmpty(), false);
		try {
			startSubsystem(subsystem, false);
			try {
				stopSubsystem(subsystem);
			}
			catch (Exception e) {
				e.printStackTrace();
				fail("Subsystem should have stopped");
			}
		}
		finally {
			uninstallSubsystemSilently(subsystem);
		}
	}
	
	@Test
	public void testUninstall() throws Exception {
		Subsystem root = getRootSubsystem();
		Subsystem subsystem = installSubsystem(root, APPLICATION_EMPTY, applicationEmpty(), false);
		try {
			uninstallSubsystem(subsystem);
		}
		catch (Exception e) {
			e.printStackTrace();
			fail("Subsystem should have uninstalled");
		}
	}
	
	private InputStream applicationA() throws Exception {
		Manifest manifest = new Manifest();
		Attributes attributes = manifest.getMainAttributes();
		attributes.putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0");
		attributes.putValue(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, APPLICATION_A);
		attributes.putValue(SubsystemConstants.SUBSYSTEM_TYPE, 
				SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION + ';' +
				AriesProvisionDependenciesDirective.RESOLVE.toString());
		attributes.putValue(SubsystemConstants.SUBSYSTEM_CONTENT, BUNDLE_A + ',' + BUNDLE_C);
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		manifest.write(baos);
		baos.close();
		return TinyBundles
				.bundle()
				.add(SUBSYSTEM_MANIFEST_FILE, new ByteArrayInputStream(baos.toByteArray()))
				.add(BUNDLE_A + JAR_EXTENSION, bundleA())
				.add(BUNDLE_B + JAR_EXTENSION, bundleB())
				.add(BUNDLE_C + JAR_EXTENSION, bundleC())
				.add(BUNDLE_D + JAR_EXTENSION, bundleD())
				.build();
	}
	
	private InputStream applicationB() throws Exception {
		Manifest manifest = new Manifest();
		Attributes attributes = manifest.getMainAttributes();
		attributes.putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0");
		attributes.putValue(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, APPLICATION_B);
		attributes.putValue(SubsystemConstants.SUBSYSTEM_TYPE, 
				SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION + ';' +
				AriesProvisionDependenciesDirective.RESOLVE.toString());
		attributes.putValue(SubsystemConstants.SUBSYSTEM_CONTENT, BUNDLE_A + ',' + APPLICATION_A + ";type=osgi.subsystem.application");
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		manifest.write(baos);
		baos.close();
		return TinyBundles
				.bundle()
				.add(SUBSYSTEM_MANIFEST_FILE, new ByteArrayInputStream(baos.toByteArray()))
				.add(BUNDLE_A + JAR_EXTENSION, bundleA())
				.add(BUNDLE_B + JAR_EXTENSION, bundleB())
				.add(APPLICATION_A + ESA_EXTENSION, applicationA())
				.build();
	}
	
	private InputStream applicationDependencyInArchive() throws Exception {
		Manifest manifest = new Manifest();
		Attributes attributes = manifest.getMainAttributes();
		attributes.putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0");
		attributes.putValue(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, APPLICATION_DEPENDENCY_IN_ARCHIVE);
		attributes.putValue(SubsystemConstants.SUBSYSTEM_TYPE, 
				SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION + ';' +
				AriesProvisionDependenciesDirective.RESOLVE.toString());
		attributes.putValue(SubsystemConstants.SUBSYSTEM_CONTENT, BUNDLE_A);
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		manifest.write(baos);
		baos.close();
		return TinyBundles
				.bundle()
				.add(SUBSYSTEM_MANIFEST_FILE, new ByteArrayInputStream(baos.toByteArray()))
				.add(BUNDLE_A + JAR_EXTENSION, bundleA())
				.add(BUNDLE_B + JAR_EXTENSION, bundleB())
				.build();
	}
	
	private InputStream applicationEmpty() throws Exception {
		Manifest manifest = new Manifest();
		Attributes attributes = manifest.getMainAttributes();
		attributes.putValue(Attributes.Name.MANIFEST_VERSION.toString(), MANIFEST_VERSION);
		attributes.putValue(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, APPLICATION_EMPTY);
		attributes.putValue(SubsystemConstants.SUBSYSTEM_TYPE, 
				SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION + ';' +
				AriesProvisionDependenciesDirective.RESOLVE.toString());
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		manifest.write(baos);
		baos.close();
		return TinyBundles
				.bundle()
				.add(SUBSYSTEM_MANIFEST_FILE, new ByteArrayInputStream(baos.toByteArray()))
				.build();
	}
	
	private InputStream applicationInstallFailed() throws Exception {
		Manifest manifest = new Manifest();
		Attributes attributes = manifest.getMainAttributes();
		attributes.putValue(Attributes.Name.MANIFEST_VERSION.toString(), MANIFEST_VERSION);
		attributes.putValue(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, APPLICATION_INSTALL_FAILED);
		attributes.putValue(SubsystemConstants.SUBSYSTEM_TYPE, 
				SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION + ';' +
				AriesProvisionDependenciesDirective.RESOLVE.toString());
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		manifest.write(baos);
		baos.close();
		return TinyBundles
				.bundle()
				.add(SUBSYSTEM_MANIFEST_FILE, new ByteArrayInputStream(baos.toByteArray()))
				.add(BUNDLE_INVALID_MANIFEST + JAR_EXTENSION, bundleInvalidManifest())
				.build();
	}
	
	private InputStream applicationInvalidProvisionDependencies() throws Exception {
		Manifest manifest = new Manifest();
		Attributes attributes = manifest.getMainAttributes();
		attributes.putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0");
		attributes.putValue(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, APPLICATION_INVALID_PROVISION_DEPENDENCIES);
		attributes.putValue(SubsystemConstants.SUBSYSTEM_TYPE, 
				SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION + ';' +
				AriesProvisionDependenciesDirective.NAME + ":=foo");
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		manifest.write(baos);
		baos.close();
		return TinyBundles
				.bundle()
				.add(SUBSYSTEM_MANIFEST_FILE, new ByteArrayInputStream(baos.toByteArray()))
				.build();
	}
	
	private InputStream applicationMissingDependency() throws Exception {
		Manifest manifest = new Manifest();
		Attributes attributes = manifest.getMainAttributes();
		attributes.putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0");
		attributes.putValue(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, APPLICATION_MISSING_DEPENDENCY);
		attributes.putValue(SubsystemConstants.SUBSYSTEM_TYPE, 
				SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION + ';' +
				AriesProvisionDependenciesDirective.RESOLVE.toString());
		attributes.putValue(SubsystemConstants.SUBSYSTEM_CONTENT, BUNDLE_A);
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		manifest.write(baos);
		baos.close();
		return TinyBundles
				.bundle()
				.add(SUBSYSTEM_MANIFEST_FILE, new ByteArrayInputStream(baos.toByteArray()))
				.add(BUNDLE_A + JAR_EXTENSION, bundleA())
				.build();
	}
	
	private InputStream applicationProvisionDependenciesInstall() throws Exception {
		Manifest manifest = new Manifest();
		Attributes attributes = manifest.getMainAttributes();
		attributes.putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0");
		attributes.putValue(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, APPLICATION_PROVISION_DEPENDENCIES_INSTALL);
		attributes.putValue(SubsystemConstants.SUBSYSTEM_TYPE, 
				SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION + ';' +
				AriesProvisionDependenciesDirective.INSTALL.toString());
		attributes.putValue(SubsystemConstants.SUBSYSTEM_CONTENT, BUNDLE_A);
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		manifest.write(baos);
		baos.close();
		return TinyBundles
				.bundle()
				.add(SUBSYSTEM_MANIFEST_FILE, new ByteArrayInputStream(baos.toByteArray()))
				.add(BUNDLE_A + JAR_EXTENSION, bundleA())
				.add(BUNDLE_B + JAR_EXTENSION, bundleB())
				.build();
	}
	
	private InputStream applicationStartFailure() throws Exception {
		Manifest manifest = new Manifest();
		Attributes attributes = manifest.getMainAttributes();
		attributes.putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0");
		attributes.putValue(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, APPLICATION_START_FAILURE);
		attributes.putValue(SubsystemConstants.SUBSYSTEM_TYPE, 
				SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION + ';' +
				AriesProvisionDependenciesDirective.RESOLVE.toString());
		attributes.putValue(SubsystemConstants.SUBSYSTEM_CONTENT, BUNDLE_START_FAILURE);
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		manifest.write(baos);
		baos.close();
		return TinyBundles
				.bundle()
				.add(SUBSYSTEM_MANIFEST_FILE, new ByteArrayInputStream(baos.toByteArray()))
				.add(BUNDLE_START_FAILURE + JAR_EXTENSION, bundleStartFailure())
				.build();
	}
	
	private void assertReferences(Class<?> clazz, String filter, int expected) throws Exception {
		ServiceReference<?>[] references = bundleContext.getAllServiceReferences(clazz.getName(), filter);
		if (expected < 1) {
			assertNull("References exist", references);
		}
		else {
			assertNotNull("No references", references);
			assertEquals("No references or more than one", expected, references.length);
		}
	}

	private InputStream bundleA() {
		return TinyBundles
				.bundle()
				.set(Constants.BUNDLE_SYMBOLICNAME, BUNDLE_A)
				.set(Constants.EXPORT_PACKAGE, PACKAGE_A)
				.set(Constants.IMPORT_PACKAGE, PACKAGE_B)
				.build();
	}
	
	private InputStream bundleB() {
		return TinyBundles
				.bundle()
				.set(Constants.BUNDLE_SYMBOLICNAME, BUNDLE_B)
				.set(Constants.EXPORT_PACKAGE, PACKAGE_B)
				.build();
	}
	
	private InputStream bundleC() {
		return TinyBundles
				.bundle()
				.set(Constants.BUNDLE_SYMBOLICNAME, BUNDLE_C)
				.set(Constants.EXPORT_PACKAGE, PACKAGE_C)
				.set(Constants.IMPORT_PACKAGE, PACKAGE_B)
				.set(Constants.IMPORT_PACKAGE, PACKAGE_D)
				.build();
	}
	
	private InputStream bundleD() {
		return TinyBundles
				.bundle()
				.set(Constants.BUNDLE_SYMBOLICNAME, BUNDLE_D)
				.set(Constants.EXPORT_PACKAGE, PACKAGE_D)
				.build();
	}
	
	private InputStream bundleInvalidManifest() {
		return TinyBundles
				.bundle()
				.set(Constants.BUNDLE_SYMBOLICNAME, BUNDLE_INVALID_MANIFEST)
				.set(Constants.PROVIDE_CAPABILITY, "osgi.ee;osgi.ee=J2SE-1.4")
				.build();
	}
	
	private InputStream bundleStartFailure() {
		return TinyBundles
				.bundle()
				.set(Constants.BUNDLE_SYMBOLICNAME, BUNDLE_START_FAILURE)
				.set(Constants.BUNDLE_ACTIVATOR, BundleStartFailureActivator.class.getName())
				.add(BundleStartFailureActivator.class, InnerClassStrategy.NONE)
				.build();
	}
	
	private InputStream featureProvisionDependenciesResolve() throws Exception {
		Manifest manifest = new Manifest();
		Attributes attributes = manifest.getMainAttributes();
		attributes.putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0");
		attributes.putValue(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, FEATURE_PROVISION_DEPENDENCIES_RESOLVE);
		attributes.putValue(SubsystemConstants.SUBSYSTEM_TYPE, 
				SubsystemConstants.SUBSYSTEM_TYPE_FEATURE + ';' +
				AriesProvisionDependenciesDirective.RESOLVE.toString());
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		manifest.write(baos);
		baos.close();
		return TinyBundles
				.bundle()
				.add(SUBSYSTEM_MANIFEST_FILE, new ByteArrayInputStream(baos.toByteArray()))
				.build();
	}
	
	public static interface TestService {}
	
	public static class TestServiceImpl implements TestService {}
	
	public static class TestServiceClientActivator implements BundleActivator {
		@Override
		public void start(BundleContext context) throws Exception {
			ServiceReference<TestService> ref = null;
			for (int i = 0; i < 80; i++) { // 20 seconds with 250ms sleep.
				ref = context.getServiceReference(TestService.class);
				if (ref == null) {
					Thread.sleep(250);
					continue;
				}
				break;
			}
			try {
				TestService service = context.getService(ref);
				service.getClass();
			}
			finally {
				context.ungetService(ref);
			}
		}

		@Override
		public void stop(BundleContext context) throws Exception {
		}
	}
	
	public static class TestServiceImplActivator implements BundleActivator {
		private ServiceRegistration<TestService> reg;
		
		@Override
		public void start(BundleContext context) throws Exception {
			reg = context.registerService(
					TestService.class, 
					new TestServiceImpl(), 
					null);
		}

		@Override
		public void stop(BundleContext context) throws Exception {
			reg.unregister();
		}
	}
	
	private static class BundleStartFailureActivator implements BundleActivator {
		@Override
		public void start(BundleContext context) throws Exception {
			throw new IllegalStateException();
		}

		@Override
		public void stop(BundleContext context) throws Exception {
			// Nothing.
		}
	}
	
	@Test
	public void testInterleavingContentDependencies() throws Exception {
		Subsystem root = getRootSubsystem();
		Subsystem c1 = installSubsystem(
				root,
				"c1",
				new SubsystemArchiveBuilder()
						.symbolicName("c1")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE
								+ ';'
								+ AriesProvisionDependenciesDirective.RESOLVE.toString())
						.content("c1b1;version=\"[0,0]\"")
						.exportPackage("c1b1")
						.importPackage("c2b1")
						.bundle(
								"c1b1", 
								new BundleArchiveBuilder()
										.symbolicName("c1b1")
										.exportPackage("c1b1")
										.importPackage("c2b1")
										.build())
						.build(),
				false
		);
		uninstallableSubsystems.add(c1);
		Subsystem c2 = installSubsystem(
				root,
				"c2",
				new SubsystemArchiveBuilder()
						.symbolicName("c2")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE
								+ ';'
								+ AriesProvisionDependenciesDirective.RESOLVE.toString())
						.content("c2b1;version=\"[0,0]\"")
						.exportPackage("c2b1")
						.importPackage("c1b1")
						.bundle(
								"c2b1", 
								new BundleArchiveBuilder()
										.symbolicName("c2b1")
										.exportPackage("c2b1")
										.importPackage("c1b1")
										.build())
						.build(),
				false
		);
		uninstallableSubsystems.add(c2);
		startSubsystem(c1, false);
		stoppableSubsystems.add(c1);
		assertState(EnumSet.of(State.RESOLVED, State.ACTIVE), c2);
		stoppableSubsystems.add(c2);
	}
	
	@Test
	public void testRestart2() throws Exception {
		Subsystem root = getRootSubsystem();
		Subsystem c1 = installSubsystem(
				root,
				"c1",
				new SubsystemArchiveBuilder()
						.symbolicName("c1")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE
								+ ';'
								+ AriesProvisionDependenciesDirective.RESOLVE.toString())
						.content("c1b1;version=\"[0,0]\"")
						.exportPackage("c1b1")
						.importPackage("c2b1")
						.bundle(
								"c1b1", 
								new BundleArchiveBuilder()
										.symbolicName("c1b1")
										.exportPackage("c1b1")
										.importPackage("c2b1")
										.build())
						.build(),
				false
		);
		uninstallableSubsystems.add(c1);
		Subsystem c2 = installSubsystem(
				root,
				"c2",
				new SubsystemArchiveBuilder()
						.symbolicName("c2")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE
								+ ';'
								+ AriesProvisionDependenciesDirective.RESOLVE.toString())
						.content("c2b1;version=\"[0,0]\"")
						.exportPackage("c2b1")
						.importPackage("c1b1")
						.bundle(
								"c2b1", 
								new BundleArchiveBuilder()
										.symbolicName("c2b1")
										.exportPackage("c2b1")
										.importPackage("c1b1")
										.build())
						.build(),
				false
		);
		uninstallableSubsystems.add(c2);
		assertChild(root, "c1", null, SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE);
		assertChild(root, "c2", null, SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE);
		restartSubsystemsImplBundle();
		root = getRootSubsystem();
		c1 = getChild(root, "c1", null, SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE);
		assertNotNull("Missing child", c1);
		uninstallableSubsystems.add(c1);
		c2 = getChild(root, "c2", null, SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE);
		assertNotNull("Missing child", c2);
		uninstallableSubsystems.add(c2);
		startSubsystem(c1, false);
		stoppableSubsystems.add(c1);
		try {
			assertState(EnumSet.of(State.RESOLVED, State.ACTIVE), c2);
		}
		catch (AssertionError e) {
			System.out.println(c2.getState());
		}
	}
	
	@Test
	public void testRestart() throws Exception {
		Subsystem root = getRootSubsystem();
		Subsystem a1 = installSubsystem(
				root,
				"a1", 
				new SubsystemArchiveBuilder()
						.symbolicName("a1")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION + ';' 
								+ AriesProvisionDependenciesDirective.RESOLVE.toString())
						.content("b1,c1;type=osgi.subsystem.composite")
						.bundle(
								"b1", 
								new BundleArchiveBuilder()
										.symbolicName("b1")
										.importPackage("b2")
										.build())
						.bundle(
								"b2", 
								new BundleArchiveBuilder()
										.symbolicName("b2")
										.exportPackage("b2")
										.build())
						.subsystem(
								"c1", 
								new SubsystemArchiveBuilder()
										.symbolicName("c1")
										.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE
												+ ';'
												+ AriesProvisionDependenciesDirective.RESOLVE.toString())
										.content("b1;version=\"[0,0]\"")
										.importPackage("b2")
										.bundle(
												"b1", 
												new BundleArchiveBuilder()
														.symbolicName("b1")
														.importPackage("b2")
														.build())
								.build())
						.build(),
				false);
		uninstallableSubsystems.add(a1);
		assertChild(root, "a1");
		assertState(State.INSTALLING, a1);
		Subsystem c1 = getChild(a1, "c1", null, SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE);
		assertNotNull("Missing child", c1);
		assertState(State.INSTALLING, c1);

		restartSubsystemsImplBundle();
		root = getRootSubsystem();
		
		a1 = getChild(root, "a1");
		assertNotNull("Missing child", a1);
		uninstallableSubsystems.add(a1);
		assertState(State.INSTALLING, a1);
		assertConstituent(a1, "b1");
		assertNotConstituent(root, "b2");
		
		c1 = getChild(a1, "c1", null, SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE);
		assertNotNull("Missing child", c1);
		uninstallableSubsystems.add(c1);
		assertConstituent(c1, "b1");
		
		startSubsystem(c1, false);
		stoppableSubsystems.add(c1);
		
		assertState(State.INSTALLED, a1);
		stoppableSubsystems.add(a1);
		
		assertConstituent(root, "b2");
	}
	
	@Test
	public void test4e3bCompliance() throws Exception {
		serviceRegistrations.add(bundleContext.registerService(
				Repository.class,
				new TestRepository.Builder()
		        		.resource(new TestRepositoryContent.Builder()
		                		.capability(new TestCapability.Builder()
		                        		.namespace(IdentityNamespace.IDENTITY_NAMESPACE)
		                        		.attribute(
		                        				IdentityNamespace.IDENTITY_NAMESPACE, 
		                        				"a")
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, 
		                        				Version.emptyVersion)
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE,
		                        				IdentityNamespace.TYPE_BUNDLE))
		                        .capability(new TestCapability.Builder()
		                        		.namespace(PackageNamespace.PACKAGE_NAMESPACE)
		                        		.attribute(
		                        				PackageNamespace.PACKAGE_NAMESPACE, 
		                        				"x")
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, 
		                        				Version.emptyVersion))
		                        .capability(new TestCapability.Builder()
		                        		.namespace(BundleNamespace.BUNDLE_NAMESPACE)
		                        		.attribute(
		                        				BundleNamespace.BUNDLE_NAMESPACE, 
		                        				"a")
		                        		.attribute(
		                        				BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE, 
		                        				Version.emptyVersion))
		                        .content(new BundleArchiveBuilder()
		                        		.symbolicName("a")
		                        		.exportPackage("x")
		                        		.buildAsBytes())
		                        .build())
		                .resource(new TestRepositoryContent.Builder()
		                		.capability(new TestCapability.Builder()
		                        		.namespace(IdentityNamespace.IDENTITY_NAMESPACE)
		                        		.attribute(
		                        				IdentityNamespace.IDENTITY_NAMESPACE, 
		                        				"b")
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, 
		                        				Version.emptyVersion)
		                        		.attribute(
		                        				IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE,
		                        				IdentityNamespace.TYPE_BUNDLE))
		                        .capability(new TestCapability.Builder()
		                        		.namespace("y"))
		                        .content(new BundleArchiveBuilder()
		                        		.symbolicName("b")
		                        		.header("Provide-Capability", "y")
		                        		.buildAsBytes())
		                        .build())
		        		.build(),
                null));
		Subsystem root = getRootSubsystem();
		try {
			Subsystem s1 = installSubsystem(
					root,
					"s1",
					new SubsystemArchiveBuilder()
							.symbolicName("s1")
							.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE)
							.subsystem(
									"s3",
									new SubsystemArchiveBuilder()
											.symbolicName("s3")
											.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE)
											.subsystem(
													"s2", 
													new SubsystemArchiveBuilder()
															.symbolicName("s2")
															.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION)
															.bundle(
																	"c", 
																	new BundleArchiveBuilder()
																			.symbolicName("c")
																			.importPackage("x")
																			.build())
															.bundle(
																	"d", 
																	new BundleArchiveBuilder()
																			.symbolicName("d")
																			.header("Require-Bundle", "a")
																			.build())
															.bundle(
																	"e", 
																	new BundleArchiveBuilder()
																			.symbolicName("e")
																			.header("Require-Capability", "y")
																			.build())
															.build())
											.build())
							.build(),
					true);
			uninstallableSubsystems.add(s1);
			fail("Subsystem should not have installed");
		}
		catch (SubsystemException e) {
			e.printStackTrace();
		}
	}
	
	@Test
	public void testMatchingCapabilityInDisconnectedRegion() throws Exception {
		Subsystem root = getRootSubsystem();
		BundleArchiveBuilder b1Builder = new BundleArchiveBuilder()
				.symbolicName("b1")
				.exportPackage("b1");
		Bundle b1 = root.getBundleContext().installBundle("b1", b1Builder.build());
		try {
			Subsystem a1 = installSubsystem(
					root,
					"a1",
					new SubsystemArchiveBuilder()
							.symbolicName("a1")
							.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION
									+ ';'
									+ AriesProvisionDependenciesDirective.RESOLVE.toString()
									+ ';'
									+ SubsystemConstants.PROVISION_POLICY_DIRECTIVE
									+ ":="
									+ SubsystemConstants.PROVISION_POLICY_ACCEPT_DEPENDENCIES)
							.build(),
					false
			);
			uninstallableSubsystems.add(a1);
			startSubsystem(a1, false);
			stoppableSubsystems.add(a1);
			removeConnectionWithParent(a1);
			Subsystem a2 = installSubsystem(
					a1,
					"a2",
					new SubsystemArchiveBuilder()
							.symbolicName("a2")
							.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION)
							.content("b2")
							.bundle(
									"b2", 
									new BundleArchiveBuilder()
											.symbolicName("b2")
											.importPackage("b1")
											.build())
							.bundle(
									"b1",
									b1Builder.build())
							.build(),
					false
			);
			uninstallableSubsystems.add(a2);
			assertState(State.INSTALLING, a2);
			assertNotConstituent(a1, "b1");
			try {
				startSubsystem(a2, false);
				stoppableSubsystems.add(a2);
				assertConstituent(a1, "b1");
			}
			catch (SubsystemException e) {
				e.printStackTrace();
				fail("Subsystem should have started");
			}
		}
		finally {
			uninstallSilently(b1);
		}
	}
	
	@Test
	public void testProvideCapabilityNamespaceOnly() throws Exception {
		Subsystem root = getRootSubsystem();
		Subsystem c1 = installSubsystem(
				root,
				"c1", 
				new SubsystemArchiveBuilder()
						.symbolicName("c1")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE)
						.provideCapability("y")
						.build());
		uninstallableSubsystems.add(c1);
		try {
			startSubsystem(c1);
			stoppableSubsystems.add(c1);
		}
		catch (SubsystemException e) {
			e.printStackTrace();
			fail("Subsystem should have started");
		}
	}
	
	@Test
	public void testComApiComImplAppClient() throws Exception {
		Subsystem root = getRootSubsystem();
		final Subsystem shared = installSubsystem(
				root,
				"shared", 
				new SubsystemArchiveBuilder()
						.symbolicName("shared")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE 
								+ ';' 
								+ AriesProvisionDependenciesDirective.RESOLVE.toString()
								+ ';'
								+ SubsystemConstants.PROVISION_POLICY_DIRECTIVE
								+ ":="
								+ SubsystemConstants.PROVISION_POLICY_ACCEPT_DEPENDENCIES)
						.importPackage("org.osgi.framework")
						.build(),
				false
		);
		uninstallableSubsystems.add(shared);
		shared.start();
		stoppableSubsystems.add(shared);
		@SuppressWarnings("unchecked")
		Callable<Subsystem>[] installCallables = new Callable[] {
				new Callable<Subsystem>() {
					@Override
					public Subsystem call() throws Exception {
						Subsystem result = installSubsystem(
								shared,
								"client", 
								new SubsystemArchiveBuilder()
										.symbolicName("client")
										.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION)
										.bundle(
												"client", 
												new BundleArchiveBuilder()
														.symbolicName("client")
														.importPackage("org.apache.aries.subsystem.itests.defect")
														.requireCapability("osgi.service;filter:=\"(objectClass="
																+ TestService.class.getName()
																+ ")\";effective:=active")
														.build())
										.build(),
								false);
						uninstallableSubsystems.add(result);
						return result;
					}
					
				},
				new Callable<Subsystem>() {
					@Override
					public Subsystem call() throws Exception {
						Subsystem result = installSubsystem(
								shared,
								"impl", 
								new SubsystemArchiveBuilder()
										.symbolicName("impl")
										.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE)
										.content("impl;version=\"[0,0]\"")
										.provideCapability("osgi.service;objectClass:List<String>=\"" 
												+ TestService.class.getName() 
												+ "\"")
										.importPackage("org.osgi.framework")
										.requireBundle("api")
										.bundle(
												"impl", 
												new BundleArchiveBuilder()
														.symbolicName("impl")
														.provideCapability("osgi.service;objectClass:List<String>=\"" 
																+ TestService.class.getName() 
																+ "\"")
														.importPackage("org.osgi.framework")
														.requireBundle("api")
														.clazz(TestServiceImpl.class)
														.activator(TestServiceImplActivator.class)
														.build())
										.build(),
								false);
						uninstallableSubsystems.add(result);
						return result;
					}
					
				},
				new Callable<Subsystem>() {
					@Override
					public Subsystem call() throws Exception {
						Subsystem result = installSubsystem(
								shared,
								"api", 
								new SubsystemArchiveBuilder()
										.symbolicName("api")
										.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE)
										.content("api;version=\"[0,0]\"")
										.exportPackage("org.apache.aries.subsystem.itests.defect")
										.provideCapability("osgi.wiring.bundle;osgi.wiring.bundle=api;bundle-version=0")
										.bundle(
												"api", 
												new BundleArchiveBuilder()
														.symbolicName("api")
														.exportPackage("org.apache.aries.subsystem.itests.defect")
														.clazz(TestService.class)
														.build())
										.build(),
								false);
						uninstallableSubsystems.add(result);
						return result;
					}
					
				}
		};
		ExecutorService executor = Executors.newFixedThreadPool(3);
		List<Future<Subsystem>> installFutures = executor.invokeAll(Arrays.asList(installCallables));
		final Subsystem a1 = installFutures.get(0).get();
		final Subsystem c1 = installFutures.get(1).get();
		final Subsystem c2 = installFutures.get(2).get();
		@SuppressWarnings("unchecked")
		Callable<Void>[] startCallables = new Callable[] {
			new Callable<Void>() {
				@Override
				public Void call() throws Exception {
					a1.start();
					stoppableSubsystems.add(a1);
					assertEvent(a1, State.INSTALLED, subsystemEvents.poll(a1.getSubsystemId(), 5000));
					assertEvent(a1, State.RESOLVING, subsystemEvents.poll(a1.getSubsystemId(), 5000));
					assertEvent(a1, State.RESOLVED, subsystemEvents.poll(a1.getSubsystemId(), 5000));
					assertEvent(a1, State.STARTING, subsystemEvents.poll(a1.getSubsystemId(), 5000));
					assertEvent(a1, State.ACTIVE, subsystemEvents.poll(a1.getSubsystemId(), 5000));
					return null;
				}
			},
			new Callable<Void>() {
				@Override
				public Void call() throws Exception {
					c1.start();
					stoppableSubsystems.add(c1);
					assertEvent(c1, State.INSTALLED, subsystemEvents.poll(c1.getSubsystemId(), 5000));
					assertEvent(c1, State.RESOLVING, subsystemEvents.poll(c1.getSubsystemId(), 5000));
					assertEvent(c1, State.RESOLVED, subsystemEvents.poll(c1.getSubsystemId(), 5000));
					assertEvent(c1, State.STARTING, subsystemEvents.poll(c1.getSubsystemId(), 5000));
					assertEvent(c1, State.ACTIVE, subsystemEvents.poll(c1.getSubsystemId(), 5000));
					return null;
				}
			},
			new Callable<Void>() {
				@Override
				public Void call() throws Exception {
					c2.start();
					stoppableSubsystems.add(c2);
					assertEvent(c2, State.INSTALLED, subsystemEvents.poll(c2.getSubsystemId(), 5000));
					assertEvent(c2, State.RESOLVING, subsystemEvents.poll(c2.getSubsystemId(), 5000));
					assertEvent(c2, State.RESOLVED, subsystemEvents.poll(c2.getSubsystemId(), 5000));
					assertEvent(c2, State.STARTING, subsystemEvents.poll(c2.getSubsystemId(), 5000));
					assertEvent(c2, State.ACTIVE, subsystemEvents.poll(c2.getSubsystemId(), 5000));
					return null;
				}
			}
		};
		List<Future<Void>> startFutures = executor.invokeAll(Arrays.asList(startCallables));
		startFutures.get(0).get();
		startFutures.get(1).get();
		startFutures.get(2).get();
	}
	
	@Test
	public void testComApiComImplComClient() throws Exception {
		Subsystem root = getRootSubsystem();
		final Subsystem shared = installSubsystem(
				root,
				"shared", 
				new SubsystemArchiveBuilder()
						.symbolicName("shared")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE 
								+ ';' 
								+ AriesProvisionDependenciesDirective.RESOLVE.toString()
								+ ';'
								+ SubsystemConstants.PROVISION_POLICY_DIRECTIVE
								+ ":="
								+ SubsystemConstants.PROVISION_POLICY_ACCEPT_DEPENDENCIES)
						.importPackage("org.osgi.framework")
						.build(),
				false
		);
		uninstallableSubsystems.add(shared);
		shared.start();
		stoppableSubsystems.add(shared);
		@SuppressWarnings("unchecked")
		Callable<Subsystem>[] installCallables = new Callable[] {
				new Callable<Subsystem>() {
					@Override
					public Subsystem call() throws Exception {
						Subsystem result = installSubsystem(
								shared,
								"client", 
								new SubsystemArchiveBuilder()
										.symbolicName("client")
										.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE)
										.content("client;version=\"[0,0]\"")
										.importPackage("org.osgi.framework")
										.requireCapability("osgi.service;filter:=\"(objectClass="
												+ TestService.class.getName()
												+ ")\";effective:=active")
										.importService(TestService.class.getName())
										.requireBundle("api,impl")
										.bundle(
												"client", 
												new BundleArchiveBuilder()
														.symbolicName("client")
														.importPackage("org.osgi.framework")
														.requireCapability("osgi.service;filter:=\"(objectClass="
																+ TestService.class.getName()
																+ ")\";effective:=active")
														.requireBundle("api,impl")
														.activator(TestServiceClientActivator.class)
														.build())
										.build(),
								false);
						return result;
					}
					
				},
				new Callable<Subsystem>() {
					@Override
					public Subsystem call() throws Exception {
						Subsystem result = installSubsystem(
								shared,
								"impl", 
								new SubsystemArchiveBuilder()
										.symbolicName("impl")
										.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE)
										.content("impl;version=\"[0,0]\"")
										.provideCapability("osgi.service;objectClass:List<String>=\"" 
												+ TestService.class.getName() 
												+ "\"")
										.exportService(TestService.class.getName())
										.importPackage("org.osgi.framework")
										.requireBundle("api")
										.provideCapability("osgi.wiring.bundle;osgi.wiring.bundle=impl;bundle-version=0")
										.bundle(
												"impl", 
												new BundleArchiveBuilder()
														.symbolicName("impl")
														.provideCapability("osgi.service;objectClass:List<String>=\"" 
																+ TestService.class.getName() 
																+ "\"")
														.importPackage("org.osgi.framework")
														.requireBundle("api")
														.clazz(TestServiceImpl.class)
														.activator(TestServiceImplActivator.class)
														.build())
										.build(),
								false);
						return result;
					}
					
				},
				new Callable<Subsystem>() {
					@Override
					public Subsystem call() throws Exception {
						Subsystem result = installSubsystem(
								shared,
								"api", 
								new SubsystemArchiveBuilder()
										.symbolicName("api")
										.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE)
										.content("api;version=\"[0,0]\"")
										.exportPackage("org.apache.aries.subsystem.itests.defect")
										.provideCapability("osgi.wiring.bundle;osgi.wiring.bundle=api;bundle-version=0")
										.bundle(
												"api", 
												new BundleArchiveBuilder()
														.symbolicName("api")
														.exportPackage("org.apache.aries.subsystem.itests.defect")
														.clazz(TestService.class)
														.build())
										.build(),
								false);
						return result;
					}
					
				}
		};
		ExecutorService executor = Executors.newFixedThreadPool(3);
		List<Future<Subsystem>> installFutures = executor.invokeAll(Arrays.asList(installCallables));
		final Subsystem client = installFutures.get(0).get();
		final Subsystem impl = installFutures.get(1).get();
		final Subsystem api = installFutures.get(2).get();
		@SuppressWarnings("unchecked")
		Callable<Void>[] startCallables = new Callable[] {
			new Callable<Void>() {
				@Override
				public Void call() throws Exception {
					client.start();
					assertEvent(client, State.INSTALLED, subsystemEvents.poll(client.getSubsystemId(), 5000));
					assertEvent(client, State.RESOLVING, subsystemEvents.poll(client.getSubsystemId(), 5000));
					assertEvent(client, State.RESOLVED, subsystemEvents.poll(client.getSubsystemId(), 5000));
					assertEvent(client, State.STARTING, subsystemEvents.poll(client.getSubsystemId(), 5000));
					assertEvent(client, State.ACTIVE, subsystemEvents.poll(client.getSubsystemId(), 5000));
					return null;
				}
			},
			new Callable<Void>() {
				@Override
				public Void call() throws Exception {
					impl.start();
					assertEvent(impl, State.INSTALLED, subsystemEvents.poll(impl.getSubsystemId(), 5000));
					assertEvent(impl, State.RESOLVING, subsystemEvents.poll(impl.getSubsystemId(), 5000));
					assertEvent(impl, State.RESOLVED, subsystemEvents.poll(impl.getSubsystemId(), 5000));
					assertEvent(impl, State.STARTING, subsystemEvents.poll(impl.getSubsystemId(), 5000));
					assertEvent(impl, State.ACTIVE, subsystemEvents.poll(impl.getSubsystemId(), 5000));
					return null;
				}
			},
			new Callable<Void>() {
				@Override
				public Void call() throws Exception {
					api.start();
					assertEvent(api, State.INSTALLED, subsystemEvents.poll(api.getSubsystemId(), 5000));
					assertEvent(api, State.RESOLVING, subsystemEvents.poll(api.getSubsystemId(), 5000));
					assertEvent(api, State.RESOLVED, subsystemEvents.poll(api.getSubsystemId(), 5000));
					assertEvent(api, State.STARTING, subsystemEvents.poll(api.getSubsystemId(), 5000));
					assertEvent(api, State.ACTIVE, subsystemEvents.poll(api.getSubsystemId(), 5000));
					return null;
				}
			}
		};
		List<Future<Void>> startFutures = executor.invokeAll(Arrays.asList(startCallables));
		startFutures.get(0).get();
		startFutures.get(1).get();
		startFutures.get(2).get();
	}
	
	@Test
	public void testAutoInstallDependenciesComposite() throws Exception {
		Subsystem root = getRootSubsystem();
		Subsystem b = installSubsystem(
				root,
				"b", 
				new SubsystemArchiveBuilder()
						.symbolicName("b")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE 
								+ ';' 
								+ AriesProvisionDependenciesDirective.RESOLVE.toString())
						.content("a;version=\"[0,0]\"")
						.exportPackage("a")
						.importPackage("b")
						.bundle(
								"a", 
								new BundleArchiveBuilder()
								.symbolicName("a")
								.importPackage("b")
								.exportPackage("a")
								.build())
						.bundle(
								"b", 
								new BundleArchiveBuilder()
								.symbolicName("b")
								.exportPackage("b")
								.build())
						.build(),
				false
		);
		uninstallableSubsystems.add(b);
		try {
			Subsystem a = installSubsystem(
					root,
					"a", 
					new SubsystemArchiveBuilder()
							.symbolicName("a")
							.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION)
							.bundle(
									"a", 
									new BundleArchiveBuilder()
									.symbolicName("a")
									.importPackage("a")
									.build())
							.build(),
					true
			);
			uninstallableSubsystems.add(a);
			assertState(EnumSet.of(State.INSTALLED, State.RESOLVED), b);
		}
		catch (Exception e) {
			e.printStackTrace();
			fail("Subsystem should have installed");
		}
	}
	
	@Test
	public void testAutoInstallDependenciesFeature() throws Exception {
		Subsystem root = getRootSubsystem();
		Subsystem shared = installSubsystem(
				root,
				"shared", 
				new SubsystemArchiveBuilder()
						.symbolicName("shared")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE 
								+ ';' 
								+ AriesProvisionDependenciesDirective.RESOLVE.toString()
								+ ';'
								+ SubsystemConstants.PROVISION_POLICY_DIRECTIVE
								+ ":="
								+ SubsystemConstants.PROVISION_POLICY_ACCEPT_DEPENDENCIES)
						.build(),
				false
		);
		uninstallableSubsystems.add(shared);
		startSubsystem(shared, false);
		Subsystem b = installSubsystem(
				shared,
				"b", 
				new SubsystemArchiveBuilder()
						.symbolicName("b")
						.type(SubsystemConstants.SUBSYSTEM_TYPE_FEATURE)
						.content("a")
						.bundle(
								"a", 
								new BundleArchiveBuilder()
								.symbolicName("a")
								.importPackage("b")
								.exportPackage("a")
								.build())
						.bundle(
								"b", 
								new BundleArchiveBuilder()
								.symbolicName("b")
								.exportPackage("b")
								.build())
						.build(),
				false
		);
		try {
			installSubsystem(
					shared,
					"a", 
					new SubsystemArchiveBuilder()
							.symbolicName("a")
							.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION
									+ ';' 
									+ AriesProvisionDependenciesDirective.INSTALL.toString())
							.bundle(
									"a", 
									new BundleArchiveBuilder()
									.symbolicName("a")
									.importPackage("a")
									.build())
							.build(),
					true
			);
			assertState(EnumSet.of(State.INSTALLED, State.RESOLVED), b);
		}
		catch (Exception e) {
			e.printStackTrace();
			fail("Subsystem should have installed");
		}
	}
}
