aries-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jwr...@apache.org
Subject svn commit: r1241900 [4/8] - in /aries/trunk/subsystem: ./ subsystem-api/ subsystem-api/src/main/java/org/osgi/service/repository/ subsystem-api/src/main/java/org/osgi/service/resolver/ subsystem-api/src/main/java/org/osgi/service/subsystem/ subsystem-...
Date Wed, 08 Feb 2012 13:54:44 GMT
Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/AriesSubsystem.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/AriesSubsystem.java?rev=1241900&r1=1241899&r2=1241900&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/AriesSubsystem.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/AriesSubsystem.java Wed Feb  8 13:54:41 2012
@@ -13,10 +13,17 @@
  */
 package org.apache.aries.subsystem.core.internal;
 
+import static org.apache.aries.application.utils.AppConstants.LOG_ENTRY;
+import static org.apache.aries.application.utils.AppConstants.LOG_EXIT;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.URISyntaxException;
+import java.io.OutputStream;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -27,26 +34,30 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
-import java.util.MissingResourceException;
-import java.util.ResourceBundle;
 import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
 
 import org.apache.aries.subsystem.core.ResourceHelper;
-import org.apache.aries.subsystem.core.archive.Archive;
 import org.apache.aries.subsystem.core.archive.DeployedContentHeader;
 import org.apache.aries.subsystem.core.archive.DeployedContentHeader.DeployedContent;
 import org.apache.aries.subsystem.core.archive.DeploymentManifest;
 import org.apache.aries.subsystem.core.archive.Header;
 import org.apache.aries.subsystem.core.archive.ProvisionResourceHeader;
 import org.apache.aries.subsystem.core.archive.ProvisionResourceHeader.ProvisionedResource;
+import org.apache.aries.subsystem.core.archive.SubsystemArchive;
 import org.apache.aries.subsystem.core.archive.SubsystemManifest;
 import org.apache.aries.subsystem.core.archive.SubsystemSymbolicNameHeader;
 import org.apache.aries.subsystem.core.archive.VersionHeader;
 import org.apache.aries.subsystem.core.obr.SubsystemEnvironment;
+import org.apache.aries.subsystem.core.resource.SubsystemFileResource;
 import org.eclipse.equinox.region.Region;
+import org.eclipse.equinox.region.RegionDigraph;
 import org.eclipse.equinox.region.RegionFilter;
 import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleEvent;
 import org.osgi.framework.BundleException;
 import org.osgi.framework.Version;
@@ -55,45 +66,128 @@ import org.osgi.framework.resource.Requi
 import org.osgi.framework.resource.Resource;
 import org.osgi.framework.resource.ResourceConstants;
 import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.FrameworkWiring;
 import org.osgi.service.coordinator.Coordination;
 import org.osgi.service.coordinator.CoordinationException;
+import org.osgi.service.coordinator.Coordinator;
 import org.osgi.service.coordinator.Participant;
-import org.osgi.service.event.Event;
-import org.osgi.service.event.EventAdmin;
-import org.osgi.service.event.EventConstants;
+import org.osgi.service.repository.RepositoryContent;
 import org.osgi.service.subsystem.Subsystem;
 import org.osgi.service.subsystem.SubsystemConstants;
 import org.osgi.service.subsystem.SubsystemException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class AriesSubsystem implements Subsystem, Resource {
+public class AriesSubsystem implements Subsystem, Resource, RepositoryContent {
+	public static final String ROOT_SYMBOLIC_NAME = "org.osgi.service.subsystem.root";
+	public static final Version ROOT_VERSION = Version.parseVersion("1.0.0");
+	public static final String ROOT_LOCATION = "subsystem://?"
+			+ SubsystemConstants.SUBSYSTEM_SYMBOLICNAME + '='
+			+ ROOT_SYMBOLIC_NAME + '&' + SubsystemConstants.SUBSYSTEM_VERSION
+			+ '=' + ROOT_VERSION;
+	
 	private static final Logger LOGGER = LoggerFactory.getLogger(AriesSubsystem.class);
-	private static final String ROOT_LOCATION = "root";
-	private static final String ROOT_SYMBOLIC_NAME = "org.apache.aries.subsystem.root";
-	private static final Version ROOT_VERSION = Version.parseVersion("1.0.0");
 	
-	private static final Map<String, AriesSubsystem> locationToSubsystem = new HashMap<String, AriesSubsystem>();
-	private static final Map<Resource, Set<Subsystem>> resourceToSubsystems = new HashMap<Resource, Set<Subsystem>>();
+	private static final Map<String, AriesSubsystem> locationToSubsystem = Collections.synchronizedMap(new HashMap<String, AriesSubsystem>());
+	private static final Map<Resource, Set<AriesSubsystem>> resourceToSubsystems = Collections.synchronizedMap(new HashMap<Resource, Set<AriesSubsystem>>());
+	
+	private static long lastId;
 	
 	static synchronized Collection<AriesSubsystem> getSubsystems(Resource resource) {
-		ArrayList<AriesSubsystem> result = new ArrayList<AriesSubsystem>(locationToSubsystem.size());
-		for (AriesSubsystem subsystem : locationToSubsystem.values()) {
-			if (subsystem.contains(resource)) {
-				result.add(subsystem);
+		// TODO Does this need to be a copy? Unmodifiable?
+		Collection<AriesSubsystem> result = resourceToSubsystems.get(resource);
+		if (result == null)
+			return Collections.emptyList();
+		return result;
+	}
+	
+	private static void copyContent(InputStream content, File destination) throws IOException {
+		copyContent(
+				new BufferedInputStream(content),
+				new BufferedOutputStream(new FileOutputStream(destination)));
+	}
+	
+	private static void copyContent(InputStream content, OutputStream destination) throws IOException {
+		// TODO What's the optimal byte array size? Put this in a constant?
+		byte[] bytes = new byte[2048];
+		int read;
+		try {
+			while ((read = content.read(bytes)) != -1)
+				destination.write(bytes, 0, read);
+		}
+		finally {
+			try {
+				destination.close();
 			}
+			catch (IOException e) {}
+			try {
+				content.close();
+			}
+			catch (IOException e) {}
 		}
-		result.trimToSize();
-		return result;
 	}
 	
-	private static long lastId;
+	private static void unzipContent(File content, File destination) throws IOException {
+		unzipContent(
+				new BufferedInputStream(new FileInputStream(content)),
+				destination);
+	}
 	
+	private static void unzipContent(InputStream content, File destination) throws IOException {
+		ZipInputStream zis = new ZipInputStream(content);
+		try {
+			ZipEntry entry;
+			while ((entry = zis.getNextEntry()) != null) {
+				try {
+					File file = new File(destination, entry.getName());
+					if (entry.isDirectory()) {
+						if (!file.exists() && !file.mkdirs())
+							throw new SubsystemException("Failed to create resource directory: " + file);
+					}
+					// TODO Let's just overwrite any existing resources for now.
+//					else if (file.exists())
+//						throw new SubsystemException("Resource already exists: " + file);
+					else {
+						OutputStream fos = new FileOutputStream(file);
+						try {
+							byte[] bytes = new byte[2048];
+							int read;
+							while ((read = zis.read(bytes)) != -1)
+								fos.write(bytes, 0, read);
+						}
+						finally {
+							try {
+								fos.close();
+							}
+							catch (IOException e) {}
+						}
+					}
+				}
+				finally {
+					try {
+						zis.closeEntry();
+					}
+					catch (IOException e) {}
+				}
+			}
+		}
+		finally {
+			try {
+				zis.close();
+			}
+			catch (IOException e) {}
+		}
+	}
+
 	private static void deleteFile(File file) {
+		LOGGER.debug(LOG_ENTRY, "deleteFile", file);
 		if (file.isDirectory()) {
 			deleteFiles(file.listFiles());
 		}
-		file.delete();
+		LOGGER.debug("Deleting file {}", file);
+		if (!file.delete())
+			LOGGER.warn("Unable to delete file {}", file);
+		LOGGER.debug(LOG_EXIT, "deleteFile");
 	}
 	
 	private static void deleteFiles(File[] files) {
@@ -108,77 +202,189 @@ public class AriesSubsystem implements S
 		return ++lastId;
 	}
 	
-	private static void postEvent(Subsystem subsystem, SubsystemConstants.EVENT_TYPE type) {
-		postEvent(subsystem, type, null);
-	}
-	
-	private static void postEvent(Subsystem subsystem, SubsystemConstants.EVENT_TYPE type, Throwable t) {
-		EventAdmin eventAdmin = Activator.getEventAdmin();
-		if (eventAdmin != null) {
-			Map<String, Object> map = new HashMap<String, Object>();
-			map.put(SubsystemConstants.EVENT_SUBSYSTEM_ID, subsystem.getSubsystemId());
-			map.put(SubsystemConstants.EVENT_SUBSYSTEM_LOCATION, subsystem.getLocation());
-			try {
-				map.put(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, subsystem.getSymbolicName());
-			}
-			catch (IllegalStateException e) {}
-			try {
-				map.put(SubsystemConstants.SUBSYSTEM_VERSION, String.valueOf(subsystem.getVersion()));
-			}
-			catch (IllegalStateException e) {}
-			// TODO This needs to be defined as a constant.
-			map.put("subsystem.state", String.valueOf(subsystem.getState()));
-			map.put(EventConstants.TIMESTAMP, System.currentTimeMillis());
-			if (t != null) {
-				map.put(EventConstants.EXCEPTION, t);
-				map.put(EventConstants.EXCEPTION_CLASS, t.getClass().getName());
-				map.put(EventConstants.EXCEPTION_MESSAGE, t.getMessage());
-			}
-			// TODO This needs to be defined as a constant.
-			Event event = new Event("org/osgi/service/Subsystem/" + type, map);
-			eventAdmin.postEvent(event);
-		}
-	}
-	
-	private final Set<Resource> constituents = Collections.synchronizedSet(new HashSet<Resource>());
+	private final SubsystemArchive archive;
 	private final Set<AriesSubsystem> children = Collections.synchronizedSet(new HashSet<AriesSubsystem>());
-	private volatile SubsystemEnvironment environment;
+	private final Set<Resource> constituents = Collections.synchronizedSet(new HashSet<Resource>());
+	private final File directory;
+	private final SubsystemEnvironment environment;
 	private final long id;
 	private final String location;
-	private final AriesSubsystem parent;
+	private final ArrayList<AriesSubsystem> parents = new ArrayList<AriesSubsystem>();
+	private final Region region;
 	
-	private Archive archive;
-	private Region region;
 	private Subsystem.State state;
-	
+	 
 	public AriesSubsystem() throws Exception {
-		archive = null;
+		// Create the root subsystem.
+		LOGGER.debug(LOG_ENTRY, "init");
 		id = 0;
 		location = ROOT_LOCATION;
-		parent = null;
-		region = createRegion();
-		state = State.ACTIVE;
-		// TODO Haven't thought this through yet. Issues?
-		environment = null;
+		region = createRegion(null);
+		setState(State.ACTIVE);
+		environment = new SubsystemEnvironment(this);
+		// TODO The directory field is kept separate from the archive so that it can be referenced
+		// by any embedded child subsystems during archive initialization. See the constructors.
+		directory = Activator.getInstance().getBundleContext().getDataFile("");
+		archive = new SubsystemArchive(directory);
+		// TODO The creation of the subsystem manifest is in two places. See other constructor.
+		SubsystemManifest manifest = archive.getSubsystemManifest();
+		if (manifest == null) {
+			// This is the first time the root subsystem has been initialized in this framework or
+			// a framework clean start was requested.
+			SubsystemUri uri = new SubsystemUri(ROOT_LOCATION);
+			manifest = SubsystemManifest.newInstance(uri.getSymbolicName(), uri.getVersion(), archive.getResources());
+			archive.setSubsystemManifest(manifest);
+		}
+		else
+			// Need to generate a new subsystem manifest in order to generated a new deployment manifest based
+			// on any persisted resources.
+			manifest = SubsystemManifest.newInstance(getSymbolicName(), getVersion(), archive.getResources());
+		archive.setDeploymentManifest(DeploymentManifest.newInstance(manifest, environment));
+		StaticDataFile sdf = archive.getStaticDataFile();
+		LOGGER.debug("Data file: {}", sdf);
+		if (sdf != null) {
+			lastId = sdf.getLastSubsystemId();
+		}
+		LOGGER.debug("Last ID will start at {}", lastId);
+		// TODO Begin proof of concept.
+		// This is a proof of concept for initializing the relationships between the root subsystem and bundles
+		// that already existed in its region. Not sure this will be the final resting place. Plus, there are issues
+		// since this does not take into account the possibility of already existing bundles going away or new bundles
+		// being installed out of band while this initialization is taking place. Need a bundle event hook for that.
+		BundleContext context = Activator.getInstance().getBundleContext();
+		for (long id : region.getBundleIds()) {
+			BundleRevision br = context.getBundle(id).adapt(BundleRevision.class);
+			Set<AriesSubsystem> s = resourceToSubsystems.get(br);
+			if (s == null) {
+				s = new HashSet<AriesSubsystem>();
+				resourceToSubsystems.put(br, s);
+			}
+			// TODO Escaping 'this' reference.
+			s.add(this);
+			// TODO What the heck is going on here? Don't we need to add the bundle revision
+			// as a constituent as well?
+		}
+		// TODO End proof of concept.
+		LOGGER.debug(LOG_EXIT, "init");
 	}
 	
-	private AriesSubsystem(String location, /*InputStream content,*/ AriesSubsystem parent) /*throws Exception*/ {
+	public AriesSubsystem(String location, InputStream content, AriesSubsystem parent) throws Exception {
+		// Create a non-root subsystem.
+		SubsystemUri uri = null;
+		if (location.startsWith("subsystem://"))
+			uri = new SubsystemUri(location);
+		if (content == null) {
+			if (uri != null)
+				content = uri.getURL().openStream();
+			else
+				content = new URL(location).openStream();
+			
+		}
 		this.location = location;
-		this.parent = parent;
+		this.parents.add(parent);
 		id = getNextId();
+		String directoryName = "subsystem" + id;
+		String fileName = directoryName + ".ssa";
+		File zipFile = new File(parent.directory, fileName);
+		directory = new File(parent.directory, directoryName);
+		if (!directory.mkdir())
+			throw new IOException("Unable to make directory for " + directory.getCanonicalPath());
+		try {
+			copyContent(content, zipFile);
+			unzipContent(zipFile, directory);
+			archive = new SubsystemArchive(directory);
+		}
+		catch (Exception e) {
+			deleteFile(directory);
+			deleteFile(zipFile);
+			throw new SubsystemException(e);
+		}
+		environment = new SubsystemEnvironment(this);
+		if (archive.getSubsystemManifest() == null) {
+			// TODO Since it's optional to use the subsystem URI, it might be
+			// better to create the URI in a try/catch block and throw an
+			// exception with a message indicating we received a subsystem
+			// with no manifest and no subsystem URI.
+			archive.setSubsystemManifest(SubsystemManifest.newInstance(
+					uri.getSymbolicName(), 
+					uri.getVersion(), 
+					archive.getResources()));
+			// TODO If the subsystem manifest is not null, need to provide default headers if subsystem URI was used.
+		}
+		region = createRegion(getSymbolicName() + ';' + getVersion());
+	}
+	
+	public AriesSubsystem(SubsystemArchive archive, AriesSubsystem parent) throws Exception {
+		this.archive = archive;
+		DataFile data = archive.getDataFile();
+		if (data == null)
+			throw new IllegalArgumentException("Missing data file");
+		this.location = data.getLocation();
+		this.parents.add(parent);
+		id = data.getSubsystemId();
+		String directoryName = "subsystem" + id;
+//		String fileName = directoryName + ".ssa";
+		directory = new File(parent.directory, directoryName);
+//		if (!directory.mkdir())
+//			throw new IOException("Unable to make directory for "
+//					+ directory.getCanonicalPath());
+//		File zipFile = new File(directory, fileName);
+//		try {
+//			copyContent(content, zipFile);
+//			unzipContent(zipFile);
+//			archive = new SubsystemArchive(directory);
+//		} catch (Exception e) {
+//			deleteFile(directory);
+//			deleteFile(zipFile);
+//			throw new SubsystemException(e);
+//		}
+		environment = new SubsystemEnvironment(this);
+//		if (archive.getSubsystemManifest() == null) {
+//			// TODO Since it's optional to use the subsystem URI, it might be
+//			// better to create the URI in a try/catch block and throw an
+//			// exception with a message indicating we received a subsystem
+//			// with no manifest and no subsystem URI.
+//			archive.setSubsystemManifest(SubsystemManifest.newInstance(
+//					uri.getSymbolicName(), uri.getVersion(),
+//					archive.getResources()));
+//			// TODO If the subsystem manifest is not null, need to provide
+//			// default headers if subsystem URI was used.
+//		}
+		region = createRegion(data.getRegionName());
+	}
+	
+//	public AriesSubsystem(File content, AriesSubsystem parent) throws Exception {
+//		// Create a non-root, persisted subsystem.
+//		if (!content.isDirectory())
+//			throw new IllegalArgumentException(content.getCanonicalPath());
+//		this.parents.add(parent);
+//		directory = content;
+//		// TODO The following call leads to a potentially dangerous escaping 'this' reference to this subsystem
+//		// (as the parent) if there is an embedded subsystem archive within this subsystem's archive.
+//		// Need to investigate.
+//		archive = new SubsystemArchive(directory);
+//		environment = new SubsystemEnvironment(this);
+//		DataFile data = archive.getDataFile();
+//		id = data.getSubsystemId();
+//		location = data.getLocation();
+//		ServiceProvider sp = Activator.getInstance().getServiceProvider();
+//		RegionDigraph digraph = sp.getService(RegionDigraph.class);
+//		region = digraph.getRegion(data.getRegionName());
+//	}
+	
+	public SubsystemArchive getArchive() {
+		return archive;
 	}
 	
 	@Override
-	public void cancel() throws SubsystemException {
+	public BundleContext getBundleContext() {
 		// TODO Auto-generated method stub
-	}
-	
-	public synchronized Archive getArchive() {
-		return archive;
+		return null;
 	}
 	
 	@Override
 	public List<Capability> getCapabilities(String namespace) {
+		// TODO Need to filter by namespace.
 		Capability capability = new OsgiIdentityCapability(this, getSymbolicName(), getVersion(), "osgi.subsystem");
 		return Arrays.asList(new Capability[]{capability});
 	}
@@ -205,58 +411,19 @@ public class AriesSubsystem implements S
 			// TODO This does not take into account the possibility that resource types other than bundle became part of the root subsystem.
 			Set<Long> bundleIds = region.getBundleIds();
 			Collection<Resource> resources = new ArrayList<Resource>(bundleIds.size());
+			BundleContext context = Activator.getInstance().getBundleContext();
 			for (Long bundleId : bundleIds)
-				resources.add(Activator.getBundleContext().getBundle(bundleId).adapt(BundleRevision.class));
+				resources.add(context.getBundle(bundleId).adapt(BundleRevision.class));
 			return resources;
 		}
 		return Collections.unmodifiableCollection(constituents);
 	}
 	
-	public DeploymentManifest getDeploymentManifest() throws IOException {
-		if (archive.getDeploymentManifest() == null) {
-			archive.setDeploymentManifest(DeploymentManifest.newInstance(archive, environment));
-		}
-		return archive.getDeploymentManifest();
-	}
-	
-	public synchronized SubsystemEnvironment getEnvironment() {
-		return environment;
-	}
-
 	@Override
-	public Map<String, String> getHeaders() {
-		return getHeaders(null);
-	}
-
-	@Override
-	public Map<String, String> getHeaders(String locale) {
-		// Archive will be null for the root subsystem.
-		if (archive == null)
-			return Collections.emptyMap();
-		ResourceBundle rb = null;
-		if (locale != null && locale.length() != 0) {
-			try {
-				rb = ResourceBundle.getBundle(locale);
-			}
-			catch (MissingResourceException e) {}
-		}
-		Collection<Header> headers = archive.getSubsystemManifest().getHeaders();
-		Map<String, String> result = new HashMap<String, String>(headers.size());
-		for (Header header : headers) {
-			String value = header.getValue();
-			if (rb != null && value.startsWith("%")) {
-				value = value.substring(1);
-				String translation = null;
-				try {
-					translation = rb.getString(value);
-				}
-				catch (MissingResourceException e) {}
-				if (translation != null)
-					value = translation;
-			}
-			result.put(header.getName(), value);
-		}
-		return result;
+	public InputStream getContent() throws IOException {
+		// TODO Assumes original archive location remains valid. Might want to
+		// copy zipped archive to subsystem directory as well as extracted contents.
+		return new URL(location).openStream();
 	}
 
 	@Override
@@ -265,8 +432,8 @@ public class AriesSubsystem implements S
 	}
 
 	@Override
-	public AriesSubsystem getParent() {
-		return parent;
+	public Collection<Subsystem> getParents() {
+		return Collections.unmodifiableCollection(new ArrayList<Subsystem>(parents));
 	}
 
 	@Override
@@ -275,9 +442,20 @@ public class AriesSubsystem implements S
 	}
 
 	@Override
-	public Subsystem.State getState() {
+	public synchronized Subsystem.State getState() {
 		return state;
 	}
+	
+	@Override
+	public Map<String, String> getSubsystemHeaders(Locale locale) {
+		Collection<Header> headers = archive.getSubsystemManifest().getHeaders();
+		Map<String, String> result = new HashMap<String, String>(headers.size());
+		for (Header header : headers) {
+			String value = header.getValue();
+			result.put(header.getName(), value);
+		}
+		return result;
+	}
 
 	@Override
 	public long getSubsystemId() {
@@ -286,16 +464,15 @@ public class AriesSubsystem implements S
 
 	@Override
 	public String getSymbolicName() {
-		if (archive == null) {
-			// If the archive is null, this is either the root subsystem or an installing subsystem not yet initialized.
-			if (State.INSTALLING.equals(getState()))
-				// The root subsystem's state will never be INSTALLING, so this is an uninitialized subsystem.
-				throw new IllegalStateException();
-			// This is the root subsystem.
+		if (isRoot())
 			return ROOT_SYMBOLIC_NAME;
-		}
 		return ((SubsystemSymbolicNameHeader)archive.getSubsystemManifest().getSubsystemSymbolicName()).getSymbolicName();
 	}
+	
+	@Override
+	public String getType() {
+		return getSubsystemHeaders(null).get(SubsystemConstants.SUBSYSTEM_TYPE);
+	}
 
 	@Override
 	public Version getVersion() {
@@ -316,52 +493,52 @@ public class AriesSubsystem implements S
 	}
 	
 	@Override
-	public synchronized Subsystem install(String location, final InputStream content) throws SubsystemException {
-		final AriesSubsystem subsystem;
-		if (locationToSubsystem.containsKey(location))
-			return locationToSubsystem.get(location);
-		subsystem = new AriesSubsystem(location, this);
-		locationToSubsystem.put(location, subsystem);
-		subsystem.setState(Subsystem.State.INSTALLING);
-		postEvent(subsystem, SubsystemConstants.EVENT_TYPE.INSTALLING);
-		children.add(subsystem);
-		constituents.add(subsystem);
-		Activator.getExecutor().execute(new Runnable() {
-			public void run() {
+	public Subsystem install(String location, InputStream content) throws SubsystemException {
+		try {
+			AriesSubsystem subsystem = locationToSubsystem.get(location);
+			if (subsystem != null)
+				return subsystem;
+			subsystem = new AriesSubsystem(location, content, this);
+			Coordination coordination = Activator.getInstance().getServiceProvider().getService(Coordinator.class).create(getSymbolicName() + '-' + getSubsystemId(), 0);
+			try {
+				installSubsystemResource(subsystem, coordination, false);
+				return subsystem;
+			}
+			catch (Exception e) {
+				coordination.fail(e);
+				throw e;
+			}
+			finally {
+				coordination.end();
+			}
+		}
+		catch (Exception e) {
+			LOGGER.error("Subsystem failed to install: " + location, e);
+			throw new SubsystemException(e);
+		}
+		finally {
+			if (content != null) {
 				try {
-					subsystem.initialize(content);
-					subsystem.install();
-					subsystem.setState(Subsystem.State.INSTALLED);
-					postEvent(subsystem, SubsystemConstants.EVENT_TYPE.INSTALLED);
-				}
-				catch (Exception e) {
-					LOGGER.error("Failed to install subsystem: " + subsystem, e);
-					postEvent(subsystem, SubsystemConstants.EVENT_TYPE.FAILED, e);
-					subsystem.setState(Subsystem.State.UNINSTALLED);
-					postEvent(subsystem, SubsystemConstants.EVENT_TYPE.UNINSTALLED);
+					content.close();
 				}
+				catch (IOException e) {}
 			}
-		});
-		return subsystem;
+		}
 	}
 	
 	public boolean isApplication() {
 		// TODO Add to constants.
-		return "osgi.application".equals(archive.getSubsystemManifest().getSubsystemType().getValue());
+		return !isRoot() && "osgi.application".equals(archive.getSubsystemManifest().getSubsystemType().getValue());
 	}
 	
 	public boolean isComposite() {
 		// TODO Add to constants.
-		return "osgi.composite".equals(archive.getSubsystemManifest().getSubsystemType().getValue());
+		return !isRoot() && "osgi.composite".equals(archive.getSubsystemManifest().getSubsystemType().getValue());
 	}
 	
 	public boolean isFeature() {
 		// TODO Add to constants.
-		return "osgi.feature".equals(archive.getSubsystemManifest().getSubsystemType().getValue());
-	}
-	
-	public void removeConstituent(Resource resource) {
-		constituents.remove(resource);
+		return !isRoot() && "osgi.feature".equals(archive.getSubsystemManifest().getSubsystemType().getValue());
 	}
 	
 	/* INSTALLING	Wait, Start
@@ -382,72 +559,47 @@ public class AriesSubsystem implements S
 		if (state == State.UNINSTALLING || state == State.UNINSTALLED) {
 			throw new SubsystemException("Cannot stop from state " + state);
 		}
-		if (state == State.INSTALLING || state == State.RESOLVING || state == State.STOPPING || state == State.UPDATING) {
+		if (state == State.INSTALLING || state == State.RESOLVING || state == State.STOPPING) {
 			waitForStateChange();
 			start();
 			return;
 		}
+		// TODO Should we wait on STARTING to see if the outcome is ACTIVE?
 		if (state == State.STARTING || state == State.ACTIVE) {
 			return;
 		}
+		// Resolve the subsystem, if necessary.
 		if (state == State.INSTALLED) {
-			setState(State.RESOLVING);
-			postEvent(this, SubsystemConstants.EVENT_TYPE.RESOLVING);
+			resolve();
 		}
-		else {
-			setState(State.STARTING);
-			postEvent(this, SubsystemConstants.EVENT_TYPE.STARTING);
-		}
-		Activator.getExecutor().execute(new Runnable() {
-			public void run() {
-				// TODO Need to hold a lock here to guarantee that another start operation can't occur when the state goes to RESOLVED.
-				// Resolve the subsystem, if necessary.
-				if (getState() == State.RESOLVING) { // Otherwise, the state will be STARTING.
-					try {
-						setImportIsolationPolicy();
-						// TODO I think this is insufficient. Do we need both pre-install and post-install environments for the Resolver?
-						if (!Activator.getFrameworkWiring().resolveBundles(getBundles())) {
-							throw new Exception("Framework could not resolve the bundles");
-						}
-						setExportIsolationPolicy();
-						// TODO Could avoid calling setState (and notifyAll) here and avoid the need for a lock.
-						setState(State.RESOLVED);
-						postEvent(AriesSubsystem.this, SubsystemConstants.EVENT_TYPE.RESOLVED);
-						setState(State.STARTING);
-						postEvent(AriesSubsystem.this, SubsystemConstants.EVENT_TYPE.STARTING);
-					}
-					catch (Exception e) {
-						setState(State.INSTALLED);
-						postEvent(AriesSubsystem.this, SubsystemConstants.EVENT_TYPE.FAILED, e);
-						return;
-					}
-				}
-				// Start the subsystem.
-				Coordination coordination = Activator.getCoordinator().create(getSymbolicName() + '-' + getSubsystemId(), 0);
-				try {
-					// TODO Need to make sure the consitutents are ordered by start level.
-					for (Resource resource : constituents) {
-						startResource(resource, coordination);
-					}
-					setState(State.ACTIVE);
-					postEvent(AriesSubsystem.this, SubsystemConstants.EVENT_TYPE.STARTED);
-				}
-				catch (Exception e) {
-					coordination.fail(e);
-					// TODO Need to reinstate complete isolation by disconnecting the region and transition to INSTALLED.
-				}
-				finally {
-					try {
-						coordination.end();
-					}
-					catch (CoordinationException e) {
-						LOGGER.error("An error occurred while starting in a resource in subsystem " + this, e);
-						setState(State.RESOLVED);
-						postEvent(AriesSubsystem.this, SubsystemConstants.EVENT_TYPE.FAILED, e);
-					}
-				}
+		setState(State.STARTING);
+		// TODO Need to hold a lock here to guarantee that another start
+		// operation can't occur when the state goes to RESOLVED.
+		// Start the subsystem.
+		Coordination coordination = Activator.getInstance()
+				.getServiceProvider().getService(Coordinator.class)
+				.create(getSymbolicName() + '-' + getSubsystemId(), 0);
+		try {
+			// TODO Need to make sure the constituents are ordered by start level.
+			for (Resource resource : constituents) {
+				startResource(resource, coordination);
+			}
+			setState(State.ACTIVE);
+//			persist(State.ACTIVE);
+		} catch (Exception e) {
+			coordination.fail(e);
+			// TODO Need to reinstate complete isolation by disconnecting the
+			// region and transition to INSTALLED.
+		} finally {
+			try {
+				coordination.end();
+			} catch (CoordinationException e) {
+				LOGGER.error(
+						"An error occurred while starting in a resource in subsystem "
+								+ this, e);
+				setState(State.RESOLVED);
 			}
-		});
+		}
 	}
 	
 	/* INSTALLING	Noop
@@ -465,7 +617,7 @@ public class AriesSubsystem implements S
 	public synchronized void stop() throws SubsystemException {
 		checkRoot();
 		if (getState() == State.UNINSTALLING || getState() == State.UNINSTALLED) {
-			throw new SubsystemException("Cannot stop from state " + state);
+			throw new SubsystemException("Cannot stop from state " + getState());
 		}
 		else if (getState() == State.STARTING) {
 			waitForStateChange();
@@ -475,26 +627,23 @@ public class AriesSubsystem implements S
 			return;
 		}
 		setState(State.STOPPING);
-		postEvent(this, SubsystemConstants.EVENT_TYPE.STOPPING);
-		// TODO Need to store the task for cancellation.
-		Activator.getExecutor().execute(new Runnable() {
-			public void run() {
-				// TODO Persist stop state.
-				for (Resource resource : constituents) {
-					try {
-						stopResource(resource);
-					}
-					catch (Exception e) {
-						LOGGER.error("An error occurred while stopping resource " + resource + " of subsystem " + this, e);
-						// TODO Should FAILED go out for each failure?
-						postEvent(AriesSubsystem.this, SubsystemConstants.EVENT_TYPE.FAILED, e);
-					}
-				}
-				// TODO Can we automatically assume it actually is resolved?
-				setState(State.RESOLVED);
-				postEvent(AriesSubsystem.this, SubsystemConstants.EVENT_TYPE.STOPPED);
+		for (Resource resource : constituents) {
+			try {
+				stopResource(resource);
+			} catch (Exception e) {
+				LOGGER.error("An error occurred while stopping resource "
+						+ resource + " of subsystem " + this, e);
+				// TODO Should FAILED go out for each failure?
 			}
-		});
+		}
+		// TODO Can we automatically assume it actually is resolved?
+		setState(State.RESOLVED);
+//		try {
+//			persist(State.RESOLVED);
+//		}
+//		catch (IOException e) {
+//			throw new SubsystemException(e);
+//		}
 	}
 	
 	/* INSTALLING	Wait, Uninstall
@@ -515,7 +664,7 @@ public class AriesSubsystem implements S
 		if (state == State.UNINSTALLING || state == State.UNINSTALLED) {
 			return;
 		}
-		else if (state == State.INSTALLING || state == State.RESOLVING || state == State.STARTING || state == State.STOPPING || state == State.UPDATING) {
+		else if (state == State.INSTALLING || state == State.RESOLVING || state == State.STARTING || state == State.STOPPING) {
 			waitForStateChange();
 			uninstall();
 		}
@@ -524,28 +673,22 @@ public class AriesSubsystem implements S
 			uninstall();
 		}
 		setState(State.UNINSTALLING);
-		postEvent(this, SubsystemConstants.EVENT_TYPE.UNINSTALLING);
-		Activator.getExecutor().execute(new Runnable() {
-			public void run() {
-				for (Iterator<Resource> iterator = constituents.iterator(); iterator.hasNext();) {
-					Resource resource = iterator.next();
-					try {
-						uninstallResource(resource);
-					}
-					catch (Exception e) {
-						LOGGER.error("An error occurred while uninstalling resource " + resource + " of subsystem " + this, e);
-						// TODO Should FAILED go out for each failure?
-						postEvent(AriesSubsystem.this, SubsystemConstants.EVENT_TYPE.FAILED, e);
-					}
-					iterator.remove();
-				}
-				parent.children.remove(AriesSubsystem.this);
-				locationToSubsystem.remove(location);
-				deleteFile(Activator.getBundleContext().getDataFile("subsystem" + id + System.getProperty("file.separator")));
-				setState(State.UNINSTALLED);
-				postEvent(AriesSubsystem.this, SubsystemConstants.EVENT_TYPE.UNINSTALLED);
+		for (Iterator<Resource> iterator = constituents.iterator(); iterator.hasNext();) {
+			Resource resource = iterator.next();
+			try {
+				uninstallResource(resource);
 			}
-		});
+			catch (Exception e) {
+				LOGGER.error("An error occurred while uninstalling resource " + resource + " of subsystem " + this, e);
+				// TODO Should FAILED go out for each failure?
+			}
+			iterator.remove();
+		}
+		for (AriesSubsystem parent : parents)
+			parent.children.remove(AriesSubsystem.this);
+		locationToSubsystem.remove(location);
+		deleteFile(directory);
+		setState(State.UNINSTALLED);
 	}
 	
 	void bundleChanged(BundleEvent event) {
@@ -571,6 +714,61 @@ public class AriesSubsystem implements S
 		}
 	}
 	
+	Region getRegion() {
+		return region;
+	}
+	
+	void install() throws Exception {
+		List<Resource> contentResources = new ArrayList<Resource>();
+		List<Resource> transitiveDependencies = new ArrayList<Resource>();
+		DeploymentManifest manifest = getDeploymentManifest();
+		DeployedContentHeader contentHeader = manifest.getDeployedContent();
+		if (contentHeader != null) {
+			for (DeployedContent content : contentHeader.getDeployedContents()) {
+				Collection<Capability> capabilities = environment.findProviders(
+						new OsgiIdentityRequirement(content.getName(), content.getDeployedVersion(), content.getNamespace(), false));
+				if (capabilities.isEmpty())
+					throw new SubsystemException("Subsystem content resource does not exist: " + content.getName() + ";version=" + content.getDeployedVersion());
+				Resource resource = capabilities.iterator().next().getResource();
+				contentResources.add(resource);
+			}
+		}
+		ProvisionResourceHeader resourceHeader = manifest.getProvisionResource();
+		if (resourceHeader != null) {
+			for (ProvisionedResource content : resourceHeader.getProvisionedResources()) {
+				Collection<Capability> capabilities = environment.findProviders(
+						new OsgiIdentityRequirement(content.getName(), content.getDeployedVersion(), content.getNamespace(), true));
+				if (capabilities.isEmpty())
+					throw new SubsystemException("Subsystem provisioned resource does not exist: " + content.getName() + ";version=" + content.getDeployedVersion());
+				Resource resource = capabilities.iterator().next().getResource();
+				transitiveDependencies.add(resource);
+			}
+		}
+		// Install content resources and transitive dependencies.
+		if (!contentResources.isEmpty()) {
+			Coordination coordination = Activator.getInstance().getServiceProvider().getService(Coordinator.class).create(getSymbolicName() + '-' + getSubsystemId(), 0);
+			try {
+				// Install the content resources.
+				for (Resource resource : contentResources) {
+					installResource(resource, coordination, false);
+				}
+				// Discover and install transitive dependencies.
+				for (Resource resource : transitiveDependencies) {
+					installResource(resource, coordination, true);
+				}
+			}
+			catch (Exception e) {
+				// TODO Log this exception? If not, who's responsible for logging it?
+				LOGGER.error("Failed to install subsystem", e);
+				coordination.fail(e);
+				throw e;
+			}
+			finally {
+				coordination.end();
+			}
+		}
+	}
+	
 	protected boolean contains(Resource resource) {
 		return constituents.contains(resource);
 	}
@@ -586,7 +784,22 @@ public class AriesSubsystem implements S
 	}
 	
 	protected synchronized void setState(Subsystem.State state) {
+		// TODO Need to update service registration properties so that a
+		// ServiceEvent goes out with the updated state.
 		this.state = state;
+		// The archive will be null if this is the root subsystem.
+		if (archive != null) {
+			// If necessary, update this subsystem's data file to honor start and stop requests.
+			if (EnumSet.of(State.INSTALLED, State.RESOLVED, State.ACTIVE).contains(state)) {
+				DataFile data = new DataFile(location, region.getName(), state, id);
+				try {
+					archive.setDataFile(data);
+				}
+				catch (IOException e) {
+					throw new SubsystemException(e);
+				}
+			}
+		}
 		notifyAll();
 	}
 	
@@ -605,15 +818,13 @@ public class AriesSubsystem implements S
 		}
 	}
 	
-	private Region createRegion() throws BundleException {
-		if (isRoot())
-			// The root subsystem's region should be the same one the subsystem bundle was installed into.
-			return Activator.getRegionDigraph().getRegion(Activator.getBundleContext().getBundle());
-		if (isFeature())
-			// Feature subsystems do not have regions because they are not isolated.
-			return null;
-		// All other subsystems get a dedicated region for isolation.
-		return Activator.getRegionDigraph().createRegion(getSymbolicName() + ';' + getVersion());
+	private Region createRegion(String name) throws BundleException {
+		if (name == null)
+			return Activator.getInstance().getServiceProvider().getService(RegionDigraph.class).getRegion(Activator.getInstance().getBundleContext().getBundle());
+		Region region = Activator.getInstance().getServiceProvider().getService(RegionDigraph.class).getRegion(name);
+		if (region == null)
+			return Activator.getInstance().getServiceProvider().getService(RegionDigraph.class).createRegion(name);
+		return region;
 	}
 	
 	private AriesSubsystem getConstituentOf(Resource resource, AriesSubsystem provisionTo, boolean transitive) {
@@ -624,105 +835,30 @@ public class AriesSubsystem implements S
 		return this;
 	}
 	
+	private DeploymentManifest getDeploymentManifest() throws IOException {
+		if (archive.getDeploymentManifest() == null) {
+			archive.setDeploymentManifest(DeploymentManifest.newInstance(archive.getSubsystemManifest(), environment));
+		}
+		return archive.getDeploymentManifest();
+	}
+	
 	private AriesSubsystem getProvisionTo(Resource resource, boolean transitive) {
 		// Application and composite resources are provisioned into the application or composite.
 		AriesSubsystem provisionTo = this;
 		if (transitive) {
 			// Transitive dependencies should be provisioned into the highest possible level.
 			// TODO Assumes root is always the appropriate level.
-			while (provisionTo.getParent() != null)
-				provisionTo = provisionTo.getParent();
+			while (!provisionTo.parents.isEmpty())
+				provisionTo = provisionTo.parents.get(0);
 		}
 		else {
-			if (provisionTo.isFeature())
+			while (provisionTo.isFeature())
 				// Feature resources should be provisioned into the first parent that's not a feature.
-				while (provisionTo.region == null)
-					provisionTo = provisionTo.getParent();
+				provisionTo = provisionTo.parents.get(0);
 		}
 		return provisionTo;
 	}
-	
-	/*private*/ synchronized void initialize(InputStream content) throws BundleException, IOException, URISyntaxException {
-		// TODO Begin proof of concept.
-		// This is a proof of concept for initializing the relationships between the root subsystem and bundles
-		// that already existed in its region. Not sure this will be the final resting place. Plus, there are issues
-		// since this does not take into account the possibility of already existing bundles going away or new bundles
-		// being installed out of band while this initialization is taking place. Need a bundle event hook for that.
-		if (isRoot()) {
-			for (long id : region.getBundleIds()) {
-				BundleRevision br = Activator.getBundleContext().getBundle(id).adapt(BundleRevision.class);
-				synchronized (resourceToSubsystems) {
-					Set<Subsystem> s = resourceToSubsystems.get(br);
-					if (s == null) {
-						s = new HashSet<Subsystem>();
-						resourceToSubsystems.put(br, s);
-					}
-					s.add(this);
-				}
-			}
-			return;
-		}
-		// TODO End proof of concept.
-		if (content == null)
-			content = new URL(location).openStream();
-		File rootDirectory = Activator.getBundleContext().getDataFile("");
-		File subsystemDirectory = new File(rootDirectory, "subsystem" + id + System.getProperty("file.separator"));
-		archive = new Archive(location, subsystemDirectory, content);
-		region = createRegion();
-		environment = new SubsystemEnvironment(this);
-		if (archive.getSubsystemManifest() == null) {
-			SubsystemUri uri = new SubsystemUri(location);
-			archive.setSubsystemManifest(SubsystemManifest.newInstance(uri.getSymbolicName(), uri.getVersion(), archive.getResources()));
-		}
-	}
 
-	private synchronized void install() throws Exception {
-		List<Resource> contentResources = new ArrayList<Resource>();
-		List<Resource> transitiveDependencies = new ArrayList<Resource>();
-		DeploymentManifest manifest = getDeploymentManifest();
-		DeployedContentHeader contentHeader = manifest.getDeployedContent();
-		for (DeployedContent content : contentHeader.getDeployedContents()) {
-			Collection<Capability> capabilities = environment.findProviders(
-					new OsgiIdentityRequirement(content.getName(), content.getDeployedVersion(), content.getNamespace(), false));
-			if (capabilities.isEmpty())
-				throw new SubsystemException("Subsystem content resource does not exist: " + content.getName() + ";version=" + content.getDeployedVersion());
-			Resource resource = capabilities.iterator().next().getResource();
-			contentResources.add(resource);
-		}
-		ProvisionResourceHeader resourceHeader = manifest.getProvisionResource();
-		if (resourceHeader != null) {
-			for (ProvisionedResource content : resourceHeader.getProvisionedResources()) {
-				Collection<Capability> capabilities = environment.findProviders(
-						new OsgiIdentityRequirement(content.getName(), content.getDeployedVersion(), content.getNamespace(), true));
-				if (capabilities.isEmpty())
-					throw new SubsystemException("Subsystem content resource does not exist: " + content.getName() + ";version=" + content.getDeployedVersion());
-				Resource resource = capabilities.iterator().next().getResource();
-				transitiveDependencies.add(resource);
-			}
-		}
-		// Install content resources and transitive dependencies.
-		if (!contentResources.isEmpty()) {
-			Coordination coordination = Activator.getCoordinator().create(getSymbolicName() + '-' + getSubsystemId(), 0);
-			try {
-				// Install the content resources.
-				for (Resource resource : contentResources) {
-					installResource(resource, coordination, false);
-				}
-				// Discover and install transitive dependencies.
-				for (Resource resource : transitiveDependencies) {
-					installResource(resource, coordination, true);
-				}
-			}
-			catch (Exception e) {
-				// TODO Log this exception? If not, who's responsible for logging it?
-				coordination.fail(e);
-			}
-			finally {
-				coordination.end();
-			}
-		}
-	}
-	
 	private void installBundleResource(Resource resource, Coordination coordination, boolean transitive) throws BundleException, IOException {
 		AriesSubsystem provisionTo = null;
 		final BundleRevision revision;
@@ -733,16 +869,16 @@ public class AriesSubsystem implements S
 				revision = (BundleRevision)resource;
 			}
 			else {
-				URL content = environment.getContent(resource);
+				InputStream content = ((RepositoryContent)resource).getContent();
 				String location = provisionTo.getSubsystemId() + '@' + provisionTo.getSymbolicName() + '@' + content;
-				Bundle bundle = provisionTo.region.installBundle(location, content.openStream());
+				Bundle bundle = provisionTo.region.installBundle(location, content);
 				revision = bundle.adapt(BundleRevision.class);
 			}
 			// TODO The null check is necessary for when the bundle is in the root subsystem. Currently, the root subsystem is not initialized with
 			// these relationships. Need to decide if that would be better.
-			Set<Subsystem> subsystems = resourceToSubsystems.get(revision);
+			Set<AriesSubsystem> subsystems = resourceToSubsystems.get(revision);
 			if (subsystems == null) {
-				subsystems = new HashSet<Subsystem>();
+				subsystems = new HashSet<AriesSubsystem>();
 				resourceToSubsystems.put(revision, subsystems);
 			}
 			subsystems.add(this);
@@ -755,7 +891,7 @@ public class AriesSubsystem implements S
 	
 			public void failed(Coordination coordination) throws Exception {
 				synchronized (resourceToSubsystems) {
-					Set<Subsystem> subsystems = resourceToSubsystems.get(revision);
+					Set<AriesSubsystem> subsystems = resourceToSubsystems.get(revision);
 					subsystems.remove(AriesSubsystem.this);
 					if (subsystems.isEmpty()) {
 						resourceToSubsystems.remove(revision);
@@ -766,10 +902,9 @@ public class AriesSubsystem implements S
 		});
 	}
 
-	private void installResource(Resource resource, Coordination coordination, boolean transitive) throws IOException, BundleException {
+	private void installResource(Resource resource, Coordination coordination, boolean transitive) throws Exception {
 		String type = ResourceHelper.getTypeAttribute(resource);
-		// TODO Add to constants.
-		if ("osgi.subsystem".equals(type))
+		if (SubsystemConstants.IDENTITY_TYPE_SUBSYSTEM.equals(type))
 			installSubsystemResource(resource, coordination, transitive);
 		else if (ResourceConstants.IDENTITY_TYPE_BUNDLE.equals(type))
 			installBundleResource(resource, coordination, transitive);
@@ -777,34 +912,44 @@ public class AriesSubsystem implements S
 			throw new SubsystemException("Unsupported resource type: " + type);
 	}
 
-	private void installSubsystemResource(Resource resource, Coordination coordination, boolean transitive) throws IOException {
+	private void installSubsystemResource(Resource resource, Coordination coordination, boolean transitive) throws Exception {
 		final AriesSubsystem subsystem;
-		synchronized (resourceToSubsystems) {
-			if (resource instanceof Subsystem) {
-				resourceToSubsystems.get(resource).add(this);
-				return;
-			}
-			URL content = environment.getContent(resource);
-			String location = id + '@' + getSymbolicName() + '@' + content;
-			subsystem = (AriesSubsystem)install(location, content.openStream());
-			Set<Subsystem> subsystems = new HashSet<Subsystem>();
-			subsystems.add(this);
-			resourceToSubsystems.put(subsystem, subsystems);
+		if (resource instanceof AriesSubsystem) {
+			subsystem = (AriesSubsystem)resource;
+			locationToSubsystem.put(subsystem.getLocation(), subsystem);
+		}
+		else if (resource instanceof SubsystemFileResource) {
+			SubsystemFileResource sfr = (SubsystemFileResource)resource;
+			subsystem = (AriesSubsystem)install(sfr.getLocation(), sfr.getContent());
+			return;
 		}
+		else {
+			SubsystemArchive archive = (SubsystemArchive)resource;
+			subsystem = new AriesSubsystem(archive, this);
+			locationToSubsystem.put(subsystem.getLocation(), subsystem);
+		}
+		subsystem.setState(State.INSTALLING);
+		Set<AriesSubsystem> subsystems = new HashSet<AriesSubsystem>();
+		subsystems.add(this);
+		resourceToSubsystems.put(subsystem, subsystems);
+		children.add(subsystem);
+		constituents.add(subsystem);
+		subsystem.install();
 		coordination.addParticipant(new Participant() {
 			public void ended(Coordination coordination) throws Exception {
-				// noop
+				subsystem.setState(State.INSTALLED);
 			}
 	
 			public void failed(Coordination coordination) throws Exception {
 				subsystem.uninstall();
-				synchronized (resourceToSubsystems) {
-					Set<Subsystem> subsystems = resourceToSubsystems.get(subsystem);
-					subsystems.remove(AriesSubsystem.this);
-					if (subsystems.isEmpty()) {
-						resourceToSubsystems.remove(subsystem);
-					}
-				}
+				constituents.remove(subsystem);
+				children.remove(subsystem);
+				Set<AriesSubsystem> subsystems = resourceToSubsystems.get(subsystem);
+				subsystems.remove(AriesSubsystem.this);
+				if (subsystems.isEmpty())
+					resourceToSubsystems.remove(subsystem);
+				locationToSubsystem.remove(location);
+				subsystem.setState(State.INSTALL_FAILED);
 			}
 		});
 	}
@@ -820,6 +965,32 @@ public class AriesSubsystem implements S
 		// TODO Implement export isolation policy for composites.
 	}
 	
+	private void resolve() {
+		setState(State.RESOLVING);
+		try {
+			setImportIsolationPolicy();
+			// TODO I think this is insufficient. Do we need both
+			// pre-install and post-install environments for the Resolver?
+			Collection<Bundle> bundles = getBundles();
+			if (!Activator.getInstance().getBundleContext().getBundle(0)
+					.adapt(FrameworkWiring.class).resolveBundles(bundles)) {
+				LOGGER.error(
+						"Unable to resolve bundles for subsystem/version/id {}/{}/{}: {}",
+						new Object[] { getSymbolicName(), getVersion(),
+								getSubsystemId(), bundles });
+				// TODO SubsystemException?
+				throw new SubsystemException("Framework could not resolve the bundles");
+			}
+			setExportIsolationPolicy();
+			// TODO Could avoid calling setState (and notifyAll) here and
+			// avoid the need for a lock.
+			setState(State.RESOLVED);
+		} catch (Exception e) {
+			setState(State.INSTALLED);
+			throw new SubsystemException(e);
+		}
+	}
+	
 	private void setImportIsolationPolicy() throws BundleException {
 		// Archive is null for root subsystem.
 		if (archive == null)
@@ -833,7 +1004,7 @@ public class AriesSubsystem implements S
 			// Applications have an implicit import policy equating to "import everything that I require", which is not the same as features.
 			// This must be computed from the application requirements and will be done using the Wires returned by the Resolver, when one is available.
 			region.connectRegion(
-					parent.region, 
+					parents.get(0).region, 
 					region.getRegionDigraph().createRegionFilterBuilder().allowAll(RegionFilter.VISIBLE_ALL_NAMESPACE).build());
 		}
 		else if (isComposite()) {
@@ -858,7 +1029,7 @@ public class AriesSubsystem implements S
 		});
 	}
 
-	private void startResource(Resource resource, Coordination coordination) throws BundleException {
+	private void startResource(Resource resource, Coordination coordination) throws BundleException, IOException {
 		String type = ResourceHelper.getTypeAttribute(resource);
 		// TODO Add to constants.
 		if ("osgi.subsystem".equals(type))
@@ -869,8 +1040,8 @@ public class AriesSubsystem implements S
 			throw new SubsystemException("Unsupported resource type: " + type);
 	}
 
-	private void startSubsystemResource(Resource resource, Coordination coordination) {
-		final Subsystem subsystem = (Subsystem)resource;
+	private void startSubsystemResource(Resource resource, Coordination coordination) throws IOException {
+		final AriesSubsystem subsystem = (AriesSubsystem)resource;
 		subsystem.start();
 		if (coordination == null)
 			return;
@@ -889,7 +1060,7 @@ public class AriesSubsystem implements S
 		((BundleRevision)resource).getBundle().stop();
 	}
 
-	private void stopResource(Resource resource) throws BundleException {
+	private void stopResource(Resource resource) throws BundleException, IOException {
 		String type = ResourceHelper.getTypeAttribute(resource);
 		// TODO Add to constants.
 		if ("osgi.subsystem".equals(type))
@@ -900,23 +1071,40 @@ public class AriesSubsystem implements S
 			throw new SubsystemException("Unsupported resource type: " + type);
 	}
 
-	private void stopSubsystemResource(Resource resource) {
-		((Subsystem)resource).stop();
+	private void stopSubsystemResource(Resource resource) throws IOException {
+		((AriesSubsystem)resource).stop();
 	}
 
 	private void uninstallBundleResource(Resource resource) throws BundleException {
+		LOGGER.debug(LOG_ENTRY, "uninstallBundleResource", resource);
+		Bundle bundle = null;
 		synchronized (resourceToSubsystems) {
-			Set<Subsystem> subsystems = resourceToSubsystems.get(resource);
+			Set<AriesSubsystem> subsystems = resourceToSubsystems.get(resource);
+			if (LOGGER.isDebugEnabled())
+				LOGGER.debug("Subsystems that currently have {} as a constituent: {}", new Object[]{resource, subsystems});
 			subsystems.remove(this);
-			if (!subsystems.isEmpty()) {
-				return;
+			if (subsystems.isEmpty()) {
+				resourceToSubsystems.remove(resource);
+				bundle = ((BundleRevision)resource).getBundle();
 			}
-			resourceToSubsystems.remove(resource);
 		}
-		((BundleRevision)resource).getBundle().uninstall();
+		if (bundle != null) {
+			LOGGER.debug("Uninstalling bundle {}", bundle);
+			bundle.uninstall();
+		}
+		LOGGER.debug(LOG_EXIT, "uninstallBundleResource");
 	}
 
 	private void uninstallResource(Resource resource) throws BundleException {
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug(LOG_ENTRY, "uninstallResource", resource);
+			LOGGER.debug("Subsystem {} is uninstalling resource {};{};{}", new Object[]{
+					getSymbolicName(),
+					ResourceHelper.getSymbolicNameAttribute(resource),
+					ResourceHelper.getVersionAttribute(resource),
+					ResourceHelper.getTypeAttribute(resource)
+			});
+		}
 		String type = ResourceHelper.getTypeAttribute(resource);
 		// TODO Add to constants.
 		if ("osgi.subsystem".equals(type))
@@ -925,17 +1113,18 @@ public class AriesSubsystem implements S
 			uninstallBundleResource(resource);
 		else
 			throw new SubsystemException("Unsupported resource type: " + type);
+		LOGGER.debug(LOG_EXIT, "uninstallResource");
 	}
 
 	private void uninstallSubsystemResource(Resource resource) {
 		synchronized (resourceToSubsystems) {
-			Set<Subsystem> subsystems = resourceToSubsystems.get(resource);
+			Set<AriesSubsystem> subsystems = resourceToSubsystems.get(resource);
 			subsystems.remove(this);
 			if (!subsystems.isEmpty()) {
 				return;
 			}
 			subsystems.remove(resource);
 		}
-		((Subsystem)resource).uninstall();
+		((AriesSubsystem)resource).uninstall();
 	}
 }

Added: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/DataFile.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/DataFile.java?rev=1241900&view=auto
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/DataFile.java (added)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/DataFile.java Wed Feb  8 13:54:41 2012
@@ -0,0 +1,126 @@
+package org.apache.aries.subsystem.core.internal;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.osgi.framework.Version;
+import org.osgi.framework.resource.Capability;
+import org.osgi.framework.resource.Requirement;
+import org.osgi.framework.resource.Resource;
+import org.osgi.framework.resource.ResourceConstants;
+import org.osgi.service.subsystem.Subsystem;
+
+public class DataFile implements Resource {
+	public static final String IDENTITY_TYPE = "org.apache.aries.subsystem.data";
+	
+	private final String location;
+	private final String regionName;
+	private final Subsystem.State state;
+	private final long subsystemId;
+	
+	public DataFile(String location, String regionName, Subsystem.State state, long subsystemId) {
+		this.location = location;
+		this.regionName = regionName;
+		this.state = state;
+		this.subsystemId = subsystemId;
+	}
+	
+	public DataFile(File file) throws IOException {
+		if (!file.isFile())
+			throw new IllegalArgumentException(file.getCanonicalPath());
+		DataInputStream dis = new DataInputStream(new FileInputStream(file));
+		try {
+			location = dis.readUTF();
+			regionName = dis.readUTF();
+			state = Subsystem.State.valueOf(dis.readUTF());
+			subsystemId = dis.readLong();
+		}
+		finally {
+			try {
+				dis.close();
+			}
+			catch (IOException e) {}
+		}
+	}
+	
+	@Override
+	public boolean equals(Object o) {
+		if (o == this)
+			return true;
+		if (!(o instanceof DataFile))
+			return false;
+		DataFile that = (DataFile)o;
+		return that.location.equals(location)
+				&& that.regionName.equals(regionName)
+				&& that.subsystemId == subsystemId
+				&& that.state.equals(state);
+	}
+	
+	@Override
+	public int hashCode() {
+		int result = 17;
+		result = 31 * result + location.hashCode();
+		result = 31 * result + regionName.hashCode();
+		result = 31 * result + (int)(subsystemId^(subsystemId>>>32));
+		result = 31 * result + state.hashCode();
+		return result;
+	}
+	
+	@Override
+	public List<Capability> getCapabilities(String namespace) {
+		List<Capability> result = new ArrayList<Capability>(1);
+		if (namespace == null || namespace.equals(ResourceConstants.IDENTITY_NAMESPACE)) {
+			OsgiIdentityCapability capability = new OsgiIdentityCapability(
+					this,
+					IDENTITY_TYPE,
+					Version.emptyVersion,
+					IDENTITY_TYPE);
+			result.add(capability);
+		}
+		return result;
+	}
+	
+	public String getLocation() {
+		return location;
+	}
+	
+	public String getRegionName() {
+		return regionName;
+	}
+	
+	@Override
+	public List<Requirement> getRequirements(String namespace) {
+		return Collections.emptyList();
+	}
+	
+	public /*synchronized*/ Subsystem.State getState() {
+		return state;
+	}
+	
+	public long getSubsystemId() {
+		return subsystemId;
+	}
+	
+	public void write(OutputStream out) throws IOException {
+		DataOutputStream dos = new DataOutputStream(out);
+		try {
+			dos.writeUTF(location);
+			dos.writeUTF(regionName);
+			dos.writeUTF(state.toString());
+			dos.writeLong(subsystemId);
+		}
+		finally {
+			try {
+				dos.close();
+			}
+			catch (IOException e) {}
+		}
+	}
+}

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/OsgiContentCapability.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/OsgiContentCapability.java?rev=1241900&r1=1241899&r2=1241900&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/OsgiContentCapability.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/OsgiContentCapability.java Wed Feb  8 13:54:41 2012
@@ -18,10 +18,10 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.osgi.framework.resource.Capability;
+import org.apache.aries.subsystem.core.resource.AbstractCapability;
 import org.osgi.framework.resource.Resource;
 
-public class OsgiContentCapability implements Capability {
+public class OsgiContentCapability extends AbstractCapability {
 	private final Map<String, Object> attributes = new HashMap<String, Object>();
 	private final Resource resource;
 	

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/OsgiIdentityCapability.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/OsgiIdentityCapability.java?rev=1241900&r1=1241899&r2=1241900&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/OsgiIdentityCapability.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/OsgiIdentityCapability.java Wed Feb  8 13:54:41 2012
@@ -19,13 +19,13 @@ import java.util.Map;
 
 import org.apache.aries.subsystem.core.archive.BundleManifest;
 import org.apache.aries.subsystem.core.archive.SubsystemManifest;
+import org.apache.aries.subsystem.core.resource.AbstractCapability;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Version;
-import org.osgi.framework.resource.Capability;
 import org.osgi.framework.resource.Resource;
 import org.osgi.framework.resource.ResourceConstants;
 
-public class OsgiIdentityCapability implements Capability {
+public class OsgiIdentityCapability extends AbstractCapability {
 	private final Map<String, Object> attributes = new HashMap<String, Object>();
 	private final Resource resource;
 	

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/OsgiIdentityRequirement.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/OsgiIdentityRequirement.java?rev=1241900&r1=1241899&r2=1241900&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/OsgiIdentityRequirement.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/OsgiIdentityRequirement.java Wed Feb  8 13:54:41 2012
@@ -18,19 +18,18 @@ import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.aries.subsystem.core.archive.VersionRangeAttribute;
+import org.apache.aries.subsystem.core.resource.AbstractRequirement;
 import org.apache.aries.util.VersionRange;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Filter;
 import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.Version;
-import org.osgi.framework.resource.Capability;
-import org.osgi.framework.resource.Requirement;
 import org.osgi.framework.resource.Resource;
 import org.osgi.framework.resource.ResourceConstants;
 import org.osgi.service.subsystem.SubsystemException;
 
-public class OsgiIdentityRequirement implements Requirement {
+public class OsgiIdentityRequirement extends AbstractRequirement {
 	private static Filter createFilter(String symbolicName, Version version, String type) {
 		return createFilter(
 				symbolicName,
@@ -83,7 +82,6 @@ public class OsgiIdentityRequirement imp
 	}
 	
 	private final Map<String, String> directives = new HashMap<String, String>();
-	private final Filter filter;
 	private final Resource resource;
 	private final boolean transitive;
 	
@@ -100,12 +98,13 @@ public class OsgiIdentityRequirement imp
 	}
 	
 	private OsgiIdentityRequirement(Filter filter, Resource resource, boolean transitive) {
-		this.filter = filter;
 		this.resource = resource;
 		this.transitive = transitive;
 		directives.put(Constants.FILTER_DIRECTIVE, filter.toString());
-		directives.put(ResourceConstants.IDENTITY_SINGLETON_DIRECTIVE, Boolean.FALSE.toString());
-		directives.put(Constants.EFFECTIVE_DIRECTIVE, Constants.EFFECTIVE_RESOLVE);
+		// TODO Let's not add these directives until we know what we're doing and that
+		// we really need them.
+//		directives.put(ResourceConstants.IDENTITY_SINGLETON_DIRECTIVE, Boolean.FALSE.toString());
+//		directives.put(Constants.EFFECTIVE_DIRECTIVE, Constants.EFFECTIVE_RESOLVE);
 	}
 
 	@Override
@@ -131,13 +130,4 @@ public class OsgiIdentityRequirement imp
 	public boolean isTransitiveDependency() {
 		return transitive;
 	}
-
-	@Override
-	public boolean matches(Capability capability) {
-		if (capability == null) return false;
-		if (!capability.getNamespace().equals(getNamespace())) return false;
-		if (!filter.matches(capability.getAttributes())) return false;
-		// TODO Check directives.
-		return true;
-	}
 }

Added: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/RootSubsystem.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/RootSubsystem.java?rev=1241900&view=auto
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/RootSubsystem.java (added)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/RootSubsystem.java Wed Feb  8 13:54:41 2012
@@ -0,0 +1,114 @@
+package org.apache.aries.subsystem.core.internal;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.Map;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Version;
+import org.osgi.framework.resource.Resource;
+import org.osgi.service.subsystem.Subsystem;
+import org.osgi.service.subsystem.SubsystemException;
+
+public class RootSubsystem implements Subsystem {
+
+	@Override
+	public BundleContext getBundleContext() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public Collection<Subsystem> getChildren() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public Map<String, String> getSubsystemHeaders(Locale locale) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public String getLocation() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public Collection<Subsystem> getParents() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public Collection<Resource> getConstituents() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public State getState() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public long getSubsystemId() {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	@Override
+	public String getSymbolicName() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public String getType() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public Version getVersion() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public Subsystem install(String location) throws SubsystemException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public Subsystem install(String location, InputStream content)
+			throws SubsystemException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public void start() throws SubsystemException {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void stop() throws SubsystemException {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void uninstall() throws SubsystemException {
+		// TODO Auto-generated method stub
+
+	}
+
+}

Added: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ServiceProvider.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ServiceProvider.java?rev=1241900&view=auto
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ServiceProvider.java (added)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ServiceProvider.java Wed Feb  8 13:54:41 2012
@@ -0,0 +1,22 @@
+/*
+ * Licensed 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.core.internal;
+
+import java.util.Collection;
+
+public interface ServiceProvider {
+	<C> C getService(Class<C> clazz);
+	
+	<C> Collection<C> getServices(Class<C> clazz);
+}

Added: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ServiceProviderImpl.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ServiceProviderImpl.java?rev=1241900&view=auto
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ServiceProviderImpl.java (added)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ServiceProviderImpl.java Wed Feb  8 13:54:41 2012
@@ -0,0 +1,104 @@
+/*
+ * Licensed 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.core.internal;
+
+import static org.apache.aries.application.utils.AppConstants.LOG_ENTRY;
+import static org.apache.aries.application.utils.AppConstants.LOG_EXIT;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ServiceProviderImpl implements ServiceProvider {
+	private static final Logger logger = LoggerFactory.getLogger(ServiceProviderImpl.class);
+	
+	private final BundleContext bundleContext;
+	private final Map<Class<?>, ServiceTracker> serviceTrackers = new HashMap<Class<?>, ServiceTracker>();
+	
+	private boolean shutdown;
+	
+	public ServiceProviderImpl(BundleContext bundleContext) {
+		if (bundleContext == null)
+			throw new NullPointerException();
+		this.bundleContext = bundleContext;
+	}
+
+	@Override
+	public <C> C getService(Class<C> clazz) {
+		logger.debug(LOG_ENTRY, "getService", clazz);
+		Object result = null;
+		ServiceTracker serviceTracker = getServiceTracker(clazz);
+		try {
+			result = serviceTracker.waitForService(5000);
+		}
+		catch (InterruptedException e) {
+			Thread.currentThread().interrupt();
+		}
+		logger.debug(LOG_EXIT, "getService", result);
+		return (C) result;
+	}
+
+	@Override
+	public <C> Collection<C> getServices(Class<C> clazz) {
+		logger.debug(LOG_ENTRY, "getServices", clazz);
+		ServiceTracker serviceTracker = getServiceTracker(clazz);
+		Collection<C> result = Collections.emptyList();
+		Object[] o = serviceTracker.getServices();
+		if (o != null)
+			result = (Collection<C>)Arrays.asList(o);
+		logger.debug(LOG_EXIT, "getSevices", result);
+		return (Collection<C>) result;
+	}
+	
+	public void shutdown() {
+		logger.debug(LOG_ENTRY, "shutdown");
+		synchronized (this) {
+			shutdown = true;
+		}
+		for (Class<?> clazz : serviceTrackers.keySet()) {
+			ServiceTracker serviceTracker = serviceTrackers.get(clazz);
+			logger.debug("Closing service tracker {}", clazz);
+			serviceTracker.close();
+		}
+		logger.debug(LOG_EXIT, "shutdown");
+	}
+	
+	private synchronized void checkShutdown() {
+		logger.debug(LOG_ENTRY, "checkShutdown");
+		if (shutdown == true)
+			throw new IllegalStateException("This service provider has been shutdown");
+		logger.debug(LOG_EXIT, "checkShutdown");
+	}
+	
+	private synchronized ServiceTracker getServiceTracker(Class<?> clazz) {
+		logger.debug(LOG_ENTRY, "getServiceTracker", clazz);
+		checkShutdown();
+		ServiceTracker serviceTracker = serviceTrackers.get(clazz);
+		if (serviceTracker == null) {
+			serviceTracker = new ServiceTracker(bundleContext, clazz.getName(), null);
+			logger.debug("Opening new service tracker {}", clazz);
+			serviceTracker.open();
+			serviceTrackers.put(clazz, serviceTracker);
+		}
+		logger.debug(LOG_EXIT, "getServiceTracker", serviceTracker);
+		return serviceTracker;
+	}
+}

Added: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/StaticDataFile.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/StaticDataFile.java?rev=1241900&view=auto
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/StaticDataFile.java (added)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/StaticDataFile.java Wed Feb  8 13:54:41 2012
@@ -0,0 +1,81 @@
+package org.apache.aries.subsystem.core.internal;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.osgi.framework.Version;
+import org.osgi.framework.resource.Capability;
+import org.osgi.framework.resource.Requirement;
+import org.osgi.framework.resource.Resource;
+import org.osgi.framework.resource.ResourceConstants;
+
+public class StaticDataFile implements Resource {
+	public static final String IDENTITY_TYPE = "org.apache.aries.subsystem.data.static";
+	
+	private final long lastSubsystemId;
+	
+	public StaticDataFile(InputStream content) throws IOException {
+		DataInputStream dis = new DataInputStream(content);
+		try {
+			lastSubsystemId = dis.readLong();
+		}
+		finally {
+			try {
+				dis.close();
+			}
+			catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+	}
+	
+	public StaticDataFile(File file) throws IOException {
+		this(new FileInputStream(file));
+	}
+	
+	@Override
+	public List<Capability> getCapabilities(String namespace) {
+		List<Capability> result = new ArrayList<Capability>(1);
+		if (namespace == null || namespace.equals(ResourceConstants.IDENTITY_NAMESPACE)) {
+			OsgiIdentityCapability capability = new OsgiIdentityCapability(
+					this,
+					IDENTITY_TYPE,
+					Version.emptyVersion,
+					IDENTITY_TYPE);
+			result.add(capability);
+		}
+		return result;
+	}
+	
+	public long getLastSubsystemId() {
+		return lastSubsystemId;
+	}
+	
+	@Override
+	public List<Requirement> getRequirements(String namespace) {
+		return Collections.emptyList();
+	}
+	
+	public void write(File file) throws IOException {
+		DataOutputStream dos = new DataOutputStream(new FileOutputStream(file, false));
+		try {
+			dos.writeLong(lastSubsystemId);
+		}
+		finally {
+			try {
+				dos.close();
+			}
+			catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+	}
+}

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemSynchronousBundleListener.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemSynchronousBundleListener.java?rev=1241900&r1=1241899&r2=1241900&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemSynchronousBundleListener.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemSynchronousBundleListener.java Wed Feb  8 13:54:41 2012
@@ -20,18 +20,63 @@ import org.osgi.framework.BundleEvent;
 import org.osgi.framework.SynchronousBundleListener;
 import org.osgi.framework.resource.Resource;
 import org.osgi.framework.wiring.BundleRevision;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class SubsystemSynchronousBundleListener implements SynchronousBundleListener {
+	private static final Logger logger = LoggerFactory.getLogger(SubsystemSynchronousBundleListener.class);
+	
 	public void bundleChanged(BundleEvent event) {
-		Bundle bundle = event.getBundle();
-		if (bundle.getBundleId() == 0) {
-			// TODO If this event is associated with the system bundle, then all subsystems are affected.
-			return;
-		}
-		Resource resource = bundle.adapt(BundleRevision.class);
-		Collection<AriesSubsystem> subsystems = AriesSubsystem.getSubsystems(resource);
-		for (AriesSubsystem subsystem : subsystems) {
-			subsystem.bundleChanged(event);
+		String type;
+		switch (event.getType()) {
+			case BundleEvent.INSTALLED:
+				type = "INSTALLED";
+				break;
+			case BundleEvent.LAZY_ACTIVATION:
+				type = "LAZY_ACTIVATION";
+				break;
+			case BundleEvent.RESOLVED:
+				type = "RESOLVED";
+				break;
+			case BundleEvent.STARTED:
+				type = "STARTED";
+				break;
+			case BundleEvent.STARTING:
+				type = "STARTING";
+				break;
+			case BundleEvent.STOPPED:
+				type = "STOPPED";
+				break;
+			case BundleEvent.STOPPING:
+				type = "STOPPING";
+				break;
+			case BundleEvent.UNINSTALLED:
+				type = "UNINSTALLED";
+				break;
+			case BundleEvent.UNRESOLVED:
+				type = "UNRESOLVED";
+				break;
+			case BundleEvent.UPDATED:
+				type = "UPDATED";
+				break;
+			default:
+				type = "Unknown (" + event.getType() + ")";
 		}
+		logger.debug("Received {} event for bundle {};{};{}", new Object[]{
+				type,
+				event.getBundle().getSymbolicName(),
+				event.getBundle().getVersion(),
+				event.getBundle().getBundleId()
+		});
+//		Bundle bundle = event.getBundle();
+//		if (bundle.getBundleId() == 0) {
+//			// TODO If this event is associated with the system bundle, then all subsystems are affected.
+//			return;
+//		}
+//		Resource resource = bundle.adapt(BundleRevision.class);
+//		Collection<AriesSubsystem> subsystems = AriesSubsystem.getSubsystems(resource);
+//		for (AriesSubsystem subsystem : subsystems) {
+//			subsystem.bundleChanged(event);
+//		}
 	}
 }

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemUri.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemUri.java?rev=1241900&r1=1241899&r2=1241900&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemUri.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemUri.java Wed Feb  8 13:54:41 2012
@@ -17,47 +17,51 @@ import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
-import org.apache.aries.subsystem.core.archive.Grammar;
+import org.apache.aries.subsystem.core.archive.SubsystemSymbolicNameHeader;
+import org.apache.aries.subsystem.core.archive.SubsystemVersionHeader;
 import org.osgi.framework.Version;
 
 public class SubsystemUri {
+	private static final String REGEXP = "([^=]*)=([^&]*)&?";
+	private static final Pattern PATTERN = Pattern.compile(REGEXP);
+	
 	private final String symbolicName;
-	private final String type;
 	private final URL url;
 	private final Version version;
 	
 	public SubsystemUri(String location) throws URISyntaxException, MalformedURLException {
-		URI uri = new URI(location);
-		String scheme = uri.getScheme();
-		// TODO Add to constants.
-		if (!scheme.matches("subsystem(?:.(?:eba|cba|fba))?"))
-			throw new IllegalArgumentException(location);
-		int i = scheme.indexOf('.');
-		if (i != -1)
-			type = scheme.substring(i);
-		else
-			// TODO Add to constants.
-			type = "eba";
-		symbolicName = uri.getQuery();
-		if (!symbolicName.matches(Grammar.SYMBOLICNAME))
+		if (!location.startsWith("subsystem://"))
 			throw new IllegalArgumentException(location);
-		url = new URL(uri.getAuthority());
-		String fragment = uri.getFragment();
-		if (fragment != null)
-			version = Version.parseVersion(uri.getFragment());
+		URI uri = new URI(location);
+		if (uri.getAuthority() != null)
+			url = new URL(uri.getAuthority());
 		else
-			version = null;
+			url = null;
+		Matcher matcher = PATTERN.matcher(uri.getQuery());
+		String symbolicName = null;
+		Version version = Version.emptyVersion;
+		while (matcher.find()) {
+			String name = matcher.group(1);
+			if (SubsystemSymbolicNameHeader.NAME.equals(name))
+				symbolicName = new SubsystemSymbolicNameHeader(matcher.group(2)).getValue();
+			else if (SubsystemVersionHeader.NAME.equals(name))
+				version = Version.parseVersion(matcher.group(2));
+			else
+				throw new IllegalArgumentException("Unsupported subsystem URI parameter: " + name);
+		}
+		if (symbolicName == null)
+			throw new IllegalArgumentException("Missing required subsystem URI parameter: " + SubsystemSymbolicNameHeader.NAME);
+		this.symbolicName = symbolicName;
+		this.version = version;
 	}
 	
 	public String getSymbolicName() {
 		return symbolicName;
 	}
 	
-	public String getType() {
-		return type;
-	}
-	
 	public URL getURL() {
 		return url;
 	}



Mime
View raw message