Modified the Felix API to aligned with the proposed standard OSGi framework
API. The main changes implemented here are the new constructor that takes
a single Map argument and the addition of the init() and waitForStop(),
the latter resplaces stopAndWait(). This is not complete, but it is a good
start. The framework instance can now be stopped and restarted. (FELIX-753)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@702992 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
index b60723c..08c9ebc 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
@@ -29,9 +29,9 @@
 {
     private final long m_id;
     private Felix m_felix = null;
-    private BundleInfo m_info = null;
+    private RegularBundleInfo m_info = null;
 
-    protected BundleImpl(Felix felix, BundleInfo info)
+    protected BundleImpl(Felix felix, RegularBundleInfo info)
     {
         m_felix = felix;
         m_info = info;
@@ -46,7 +46,7 @@
     /*
      * Only used when refreshing a bundle.
     **/
-    /* package private */ void setInfo(BundleInfo info)
+    /* package private */ void setInfo(RegularBundleInfo info)
     {
         m_info = info;
     }
diff --git a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
index 7449830..a229e53 100644
--- a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
+++ b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
@@ -19,7 +19,7 @@
 package org.apache.felix.framework;
 
 import java.io.IOException;
-import java.net.InetAddress; 
+import java.net.InetAddress;
 import java.io.InputStream;
 import java.net.URL;
 import java.net.URLConnection;
@@ -57,39 +57,39 @@
 import org.osgi.framework.Constants;
 
 /**
- * The ExtensionManager class is used in several ways. 
+ * The ExtensionManager class is used in several ways.
  * <p>
- * First, a private instance is added (as URL with the instance as 
- * URLStreamHandler) to the classloader that loaded the class. 
- * It is assumed that this is an instance of URLClassloader (if not extension 
- * bundles will not work). Subsequently, extension bundles can be managed by 
- * instances of this class (their will be one instance per framework instance). 
+ * First, a private instance is added (as URL with the instance as
+ * URLStreamHandler) to the classloader that loaded the class.
+ * It is assumed that this is an instance of URLClassloader (if not extension
+ * bundles will not work). Subsequently, extension bundles can be managed by
+ * instances of this class (their will be one instance per framework instance).
  * </p>
  * <p>
  * Second, it is used as module definition of the systembundle. Added extension
- * bundles with exported packages will contribute their exports to the 
+ * bundles with exported packages will contribute their exports to the
  * systembundle export.
  * </p>
  * <p>
- * Third, it is used as content loader of the systembundle. Added extension 
+ * Third, it is used as content loader of the systembundle. Added extension
  * bundles exports will be available via this loader.
  * </p>
  */
-// The general approach is to have one private static instance that we register 
+// The general approach is to have one private static instance that we register
 // with the parent classloader and one instance per framework instance that
 // keeps track of extension bundles and systembundle exports for that framework
 // instance.
 class ExtensionManager extends URLStreamHandler implements IModuleDefinition, IContentLoader, IContent
 {
     // The private instance that is added to Felix.class.getClassLoader() -
-    // will be null if extension bundles are not supported (i.e., we are not 
+    // will be null if extension bundles are not supported (i.e., we are not
     // loaded by an instance of URLClassLoader)
     static final ExtensionManager m_extensionManager;
-    
+
     static
     {
-        // We use the secure action of Felix to add a new instance to the parent 
-        // classloader. 
+        // We use the secure action of Felix to add a new instance to the parent
+        // classloader.
         ExtensionManager extensionManager = new ExtensionManager();
         try
         {
@@ -98,16 +98,16 @@
                 "felix://extensions/", extensionManager),
                 Felix.class.getClassLoader());
         }
-        catch (Exception ex) 
+        catch (Exception ex)
         {
-            // extension bundles will not be supported. 
+            // extension bundles will not be supported.
             extensionManager = null;
         }
         m_extensionManager = extensionManager;
     }
 
     private Logger m_logger = null;
-    private BundleInfo m_systemBundleInfo = null;
+    private BundleInfo m_sbi = null;
     private ICapability[] m_capabilities = null;
     private Set m_exportNames = null;
     private ISearchPolicy m_searchPolicy = null;
@@ -128,28 +128,28 @@
 
     /**
      * This constructor is used to create one instance per framework instance.
-     * The general approach is to have one private static instance that we register 
+     * The general approach is to have one private static instance that we register
      * with the parent classloader and one instance per framework instance that
      * keeps track of extension bundles and systembundle exports for that framework
      * instance.
-     * 
+     *
      * @param logger the logger to use.
      * @param config the configuration to read properties from.
      * @param systemBundleInfo the info to change if we need to add exports.
      */
-    ExtensionManager(Logger logger, Map configMap, BundleInfo systemBundleInfo)
+    ExtensionManager(Logger logger, Map configMap, BundleInfo sbi)
     {
         m_extensions = null;
         m_names = null;
         m_sourceToExtensions = null;
         m_logger = logger;
-        m_systemBundleInfo = systemBundleInfo;
+        m_sbi = sbi;
 
 // TODO: FRAMEWORK - Not all of this stuff really belongs here, probably only exports.
         // Populate system bundle header map.
         // Note: This is a reference to the actual header map,
         // so these changes are saved. Kind of hacky.
-        Map map = ((SystemBundleArchive) m_systemBundleInfo.getArchive()).getManifestHeader(0);
+        Map map = m_sbi.getCurrentHeader();
         // Initialize header map as a case insensitive map.
         map.put(FelixConstants.BUNDLE_VERSION,
             configMap.get(FelixConstants.FELIX_VERSION_PROPERTY));
@@ -249,19 +249,19 @@
 
     /**
      * Add an extension bundle. The bundle will be added to the parent classloader
-     * and it's exported packages will be added to the module definition 
-     * exports of this instance. Subsequently, they are available form the 
+     * and it's exported packages will be added to the module definition
+     * exports of this instance. Subsequently, they are available form the
      * instance in it's role as content loader.
-     * 
+     *
      * @param felix the framework instance the given extension bundle comes from.
      * @param bundle the extension bundle to add.
      * @throws BundleException if extension bundles are not supported or this is
      *          not a framework extension.
-     * @throws SecurityException if the caller does not have the needed 
+     * @throws SecurityException if the caller does not have the needed
      *          AdminPermission.EXTENSIONLIFECYCLE and security is enabled.
      * @throws Exception in case something goes wrong.
      */
-    void addExtensionBundle(Felix felix, FelixBundle bundle) 
+    void addExtensionBundle(Felix felix, FelixBundle bundle)
         throws SecurityException, BundleException, Exception
     {
         Object sm = System.getSecurityManager();
@@ -311,7 +311,7 @@
             }
 
             // Add the bundle as extension if we support extensions
-            if (m_extensionManager != null) 
+            if (m_extensionManager != null)
             {
                 // This needs to be the private instance.
                 m_extensionManager.addExtension(felix, bundle);
@@ -338,14 +338,14 @@
         {
             felix.releaseBundleLock(felix);
         }
-    
+
         bundle.getInfo().setState(Bundle.RESOLVED);
     }
 
     /**
      * This is a Felix specific extension mechanism that allows extension bundles
      * to have activators and be started via this method.
-     * 
+     *
      * @param felix the framework instance the extension bundle is installed in.
      * @param bundle the extension bundle to start if it has a Felix specific activator.
      */
@@ -354,7 +354,7 @@
         String activatorClass = (String)
         bundle.getInfo().getCurrentHeader().get(
             FelixConstants.FELIX_EXTENSION_ACTIVATOR);
-        
+
         if (activatorClass != null)
         {
             try
@@ -362,13 +362,14 @@
                 BundleActivator activator = (BundleActivator)
                     felix.getClass().getClassLoader().loadClass(
                         activatorClass.trim()).newInstance();
-                
+
+// TODO: KARL - This is kind of hacky, can we improve it?
                 felix.m_activatorList.add(activator);
-                
-                BundleContext context = m_systemBundleInfo.getBundleContext();
-                
+
+                BundleContext context = m_sbi.getBundleContext();
+
                 bundle.getInfo().setBundleContext(context);
-                
+
                 if (felix.getInfo().getState() == Bundle.ACTIVE)
                 {
                     Felix.m_secureAction.startActivator(activator, context);
@@ -382,15 +383,15 @@
             }
         }
     }
-    
+
     /**
      * Remove all extension registered by the given framework instance. Note, it
      * is not possible to unregister allready loaded classes form those extensions.
      * That is why the spec requires a JVM restart.
-     * 
-     * @param felix the framework instance whose extensions need to be unregistered. 
+     *
+     * @param felix the framework instance whose extensions need to be unregistered.
      */
-    void removeExtensions(Felix felix) 
+    void removeExtensions(Felix felix)
     {
         if (m_extensionManager != null)
         {
@@ -412,7 +413,7 @@
 
         // Note: This is a reference to the actual header map,
         // so these changes are saved. Kind of hacky.
-        Map map = ((SystemBundleArchive) m_systemBundleInfo.getArchive()).getManifestHeader(0);
+        Map map = m_sbi.getCurrentHeader();
         map.put(Constants.EXPORT_PACKAGE, convertCapabilitiesToHeaders(map));
     }
 
@@ -461,11 +462,11 @@
 
         return exportSB.toString();
     }
-    
+
     //
     // IContentLoader
     //
-    
+
     public void open()
     {
         // Nothing needed here.
@@ -560,7 +561,7 @@
         // No native libs associated with the system bundle.
         return null;
     }
-    
+
     //
     // Classpath Extension
     //
@@ -591,10 +592,10 @@
         throw new IOException("Resource not provided by any extension!");
     }
 
-    protected InetAddress getHostAddress(URL u) 
-    { 
-        // the extension URLs do not address real hosts 
-        return null; 
+    protected InetAddress getHostAddress(URL u)
+    {
+        // the extension URLs do not address real hosts
+        return null;
     }
 
     private synchronized void addExtension(Object source, Bundle extension)
@@ -642,16 +643,16 @@
         }
     }
 
-    public Enumeration getEntries() 
+    public Enumeration getEntries()
     {
         return new Enumeration()
         {
-            public boolean hasMoreElements() 
+            public boolean hasMoreElements()
             {
                 return false;
             }
 
-            public Object nextElement() throws NoSuchElementException 
+            public Object nextElement() throws NoSuchElementException
             {
                 throw new NoSuchElementException();
             }
@@ -662,12 +663,12 @@
         return false;
     }
 
-    public byte[] getEntryAsBytes(String name) 
+    public byte[] getEntryAsBytes(String name)
     {
         return null;
     }
 
-    public InputStream getEntryAsStream(String name) throws IOException 
+    public InputStream getEntryAsStream(String name) throws IOException
     {
         return null;
     }
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index a368c7d..c21c81b 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -33,7 +33,7 @@
 import org.osgi.service.packageadmin.ExportedPackage;
 import org.osgi.service.startlevel.StartLevel;
 
-public class Felix extends FelixBundle
+public class Felix extends FelixBundle implements SystemBundle
 {
     // The secure action used to do privileged calls
     static SecureAction m_secureAction = new SecureAction();
@@ -65,8 +65,8 @@
     private Object[] m_installRequestLock_Priority1 = new Object[0];
 
     // Maps a bundle location to a bundle.
-    private HashMap m_installedBundleMap = new HashMap();
-    private SortedMap m_installedBundleIndex = new TreeMap();
+    private HashMap m_installedBundleMap;
+    private SortedMap m_installedBundleIndex;
     // This lock must be acquired to modify m_installedBundleMap;
     // to help avoid deadlock this lock as priority 2 and should
     // be acquired before locks with lower priority.
@@ -87,7 +87,8 @@
     private BundleCache m_cache = null;
 
     // System bundle bundle info instance.
-    private BundleInfo m_systemBundleInfo = null;
+    private BundleInfo m_sbi;
+
     // System bundle activator list.
     List m_activatorList = null;
 
@@ -110,33 +111,16 @@
 
     // Shutdown thread.
     private Thread m_shutdownThread = null;
-
-    /**
-     * Creates a new Felix framework instance with a default logger.
-     *
-     * @param configMutableMap An map for obtaining configuration properties,
-     *        may be <tt>null</tt>.
-     * @param activatorList A list of System Bundle activators.
-     *
-     * @see #Felix(Logger, Map, List)
-     */
-    public Felix(Map configMutableMap, List activatorList)
-    {
-        this(null, configMutableMap, activatorList);
-    }
+    private ThreadGate m_shutdownGate = null;
 
     /**
      * <p>
-     * This method creates the framework instance; instances of the framework
-     * are not active until they are started. The constructor accepts a
-     * <tt>Map</tt> instance that will be used to obtain configuration or
-     * framework properties. Configuration properties are used internally by
-     * the framework and its extensions to alter its default behavior.
-     * Framework properties are used by bundles and are accessible from
-     * <tt>BundleContext.getProperty()</tt>. This map instance is used
-     * directly (i.e., it is not copied), which means that it is possible to
-     * change the property values externally at run time, but this is strongly
-     * discouraged for the framework's configuration properties.
+     * This constructor creates a framework instance with a specified <tt>Map</tt>
+     * of configuration properties. Configuration properties are used internally
+     * by the framework to alter its default behavior. The configuration properties
+     * should have a <tt>String</tt> key and an <tt>Object</tt> value. The passed
+     * in <tt>Map</tt> is copied by the framework and all keys are converted to
+     * <tt>String</tt>s.
      * </p>
      * <p>
      * Configuration properties are the sole means to configure the framework's
@@ -146,15 +130,19 @@
      * <tt>Map</tt> instance for any and all configuration properties. It is
      * possible to specify a <tt>null</tt> for the configuration property map,
      * in which case the framework will use its default behavior in all cases.
-     * However, if the
-     * <a href="cache/DefaultBundleCache.html"><tt>DefaulBundleCache</tt></a>
-     * is used, then at a minimum a profile name or profile directory must
-     * be specified.
      * </p>
      * <p>
      * The following configuration properties can be specified:
      * </p>
      * <ul>
+     *   <li><tt>felix.systembundle.activators</tt> - A <tt>List</tt> of
+     *       <tt>BundleActivator</tt> instances that are started/stopped when
+     *       the System Bundle is started/stopped; the specified instances will
+     *       receive the System Bundle's <tt>BundleContext</tt> when invoked.
+     *   </li>
+     *   <li><tt>felix.log.logger</tt> - An instance of <tt>Logger</tt> that the
+     *       framework uses as its default logger.
+     *   </li>
      *   <li><tt>felix.log.level</tt> - An integer value indicating the degree
      *       of logging reported by the framework; the higher the value the more
      *       logging is reported. If zero ('0') is specified, then logging is
@@ -175,28 +163,37 @@
      *       service will result in the <tt>URL.setURLStreamHandlerFactory()</tt>
      *       and <tt>URLConnection.setContentHandlerFactory()</tt> being called.
      *   </li>
-     *   <li><tt>felix.embedded.execution</tt> - Flag to indicate whether
-     *       the framework is embedded into a host application; the default value is
-     *       "<tt>false</tt>". If this flag is "<tt>true</tt>" then the framework
-     *       will not called <tt>System.exit()</tt> upon termination.
+     *   <li><tt>felix.cache.bufsize</tt> - Sets the buffer size to be used by
+     *       the cache; the default value is 4096. The integer
+     *       value of this string provides control over the size of the
+     *       internal buffer of the disk cache for performance reasons.
      *   </li>
-     *   <li><tt>felix.strict.osgi</tt> - Flag to indicate whether the framework is
-     *       running in strict OSGi mode; the default value is "<tt>true</tt>".
-     *       If this flag is "<tt>false</tt>" it enables a non-OSGi-compliant
-     *       feature by persisting <tt>BundleActivator</tt>s that implement
-     *       <tt>Serializable</tt>. This feature is not recommended since
-     *       it is non-compliant.
+     *   <li><tt>felix.cache.dir</tt> - Sets the directory to be used by the
+     *       cache as its cache directory. The cache directory is where all
+     *       profile directories are stored and a profile directory is where a
+     *       set of installed bundles are stored. By default, the cache
+     *       directory is <tt>.felix</tt> in the user's home directory. If
+     *       this property is specified, then its value will be used as the cache
+     *       directory instead of <tt>.felix</tt>. This directory will be created
+     *       if it does not exist.
+     *   </li>
+     *   <li><tt>felix.cache.profile</tt> - Sets the profile name that will be
+     *       used to create a profile directory inside of the cache directory.
+     *       The created directory will contained all installed bundles associated
+     *       with the profile.
+     *   </li>
+     *   <li><tt>felix.cache.profiledir</tt> - Sets the directory to use as the
+     *       profile directory for the bundle cache; by default the profile
+     *       name is used to create a directory in the <tt>.felix</tt> cache
+     *       directory. If this property is specified, then the cache directory
+     *       and profile name properties are ignored. The specified value of this
+     *       property is used directly as the directory to contain all cached
+     *       bundles. If this property is set, it is not necessary to set the
+     *       cache directory or profile name properties. This directory will be
+     *       created if it does not exist.
      *   </li>
      * </ul>
      * <p>
-     * Besides the above framework configuration properties, it is also
-     * possible to specify properties for the bundle cache. The available
-     * bundle cache properties depend on the cache implementation
-     * being used. For the properties of the default bundle cache, refer to the
-     * <a href="cache/DefaultBundleCache.html"><tt>DefaulBundleCache</tt></a>
-     * API documentation.
-     * </p>
-     * <p>
      * The <a href="Main.html"><tt>Main</tt></a> class implements some
      * functionality for default property file handling, which makes it
      * possible to specify configuration properties and framework properties
@@ -205,27 +202,36 @@
      * able to take advantage of the features it provides; refer to its
      * class documentation for more information.
      * </p>
+     * <p>
+     * The framework is not actually started until the <tt>start()</tt> method
+     * is called.
+     * </p>
      *
-     * @param logger The logger for use by the framework or <code>null</code>
-     *        use the default logger.
-     * @param configMutableMap A map for obtaining configuration properties,
+     * @param configMap A map for obtaining configuration properties,
      *        may be <tt>null</tt>.
-     * @param activatorList A list of System Bundle activators.
     **/
-    public Felix(Logger logger, Map configMutableMap, List activatorList)
+    public Felix(Map configMap)
     {
-        // Initialize member variables.
-        m_configMutableMap = (configMutableMap == null)
-            ? new StringMap(false) : configMutableMap;
+        // Copy the configuration properties; convert keys to strings.
+        m_configMutableMap = new StringMap(false);
+        if (configMap != null)
+        {
+            for (Iterator i = configMap.entrySet().iterator(); i.hasNext(); )
+            {
+                Map.Entry entry = (Map.Entry) i.next();
+                m_configMutableMap.put(entry.getKey().toString(), entry.getValue());
+            }
+        }
         m_configMap = createUnmodifiableMap(m_configMutableMap);
-        m_activatorList = (activatorList == null) ? new ArrayList() : activatorList;
 
         // Create logger with appropriate log level. Even though the
         // logger needs the system bundle's context for tracking log
         // services, it is created now because it is needed before
-        // the system bundle is created. The system bundle's context
-        // will be set below after the system bundle is created.\
-        m_logger = (logger == null) ? new Logger() : logger;
+        // the system bundle is activated. The system bundle's context
+        // will be set in the init() method after the system bundle
+        // is activated.
+        m_logger = (Logger) m_configMutableMap.get(FelixConstants.LOG_LOGGER_PROP);
+        m_logger = (m_logger == null) ? new Logger() : m_logger;
         try
         {
             m_logger.setLogLevel(
@@ -240,46 +246,98 @@
         // Initialize framework properties.
         initializeFrameworkProperties();
 
-        // Create the bundle cache since we need it for the system bundle
-        // archive, which in turn is needed by the system bundle info,
-        // which we need to keep track of the framework state.
+        // Create default bundle stream handler.
+        m_bundleStreamHandler = new URLHandlersBundleStreamHandler(this);
+
+        // Create search policy for module loader.
+        m_policyCore = new R4SearchPolicyCore(m_logger, m_configMap);
+
+        // Add a resolver listener to the search policy
+        // so that we will be notified when modules are resolved
+        // in order to update the bundle state.
+        m_policyCore.addResolverListener(new ResolveListener() {
+            public void moduleResolved(ModuleEvent event)
+            {
+                FelixBundle bundle = null;
+                try
+                {
+                    long id = Util.getBundleIdFromModuleId(
+                        event.getModule().getId());
+                    if (id > 0)
+                    {
+                        // Update the bundle's state to resolved when the
+                        // current module is resolved; just ignore resolve
+                        // events for older revisions since this only occurs
+                        // when an update is done on an unresolved bundle
+                        // and there was no refresh performed.
+                        bundle = (FelixBundle) getBundle(id);
+
+                        // Lock the bundle first.
+                        try
+                        {
+                            acquireBundleLock(bundle);
+                            if (bundle.getInfo().getCurrentModule() == event.getModule())
+                            {
+                                if (bundle.getInfo().getState() != Bundle.INSTALLED)
+                                {
+                                    m_logger.log(
+                                        Logger.LOG_WARNING,
+                                        "Received a resolve event for a bundle that has already been resolved.");
+                                }
+                                else
+                                {
+                                    bundle.getInfo().setState(Bundle.RESOLVED);
+                                    fireBundleEvent(BundleEvent.RESOLVED, bundle);
+                                }
+                            }
+                        }
+                        finally
+                        {
+                            releaseBundleLock(bundle);
+                        }
+                    }
+                }
+                catch (NumberFormatException ex)
+                {
+                    // Ignore.
+                }
+            }
+
+            public void moduleUnresolved(ModuleEvent event)
+            {
+                // We can ignore this, because the only time it
+                // should happen is when a refresh occurs. The
+                // refresh operation resets the bundle's state
+                // by calling BundleInfo.reset(), thus it is not
+                // necessary for us to reset the bundle's state
+                // here.
+            }
+        });
+
+        // Create the module factory and attach it to the search policy.
+        m_factory = new ModuleFactoryImpl(m_logger);
+        m_policyCore.setModuleFactory(m_factory);
+
+        // Create the system bundle info object, which will hold state info.
+        m_sbi = new SystemBundleInfo();
+        // Create the extension manager, which we will use as the module
+        // definition for creating the system bundle module.
+        m_extensionManager = new ExtensionManager(m_logger, m_configMap, m_sbi);
+        m_sbi.addModule(m_factory.createModule("0", m_extensionManager));
+        // Set the extension manager as the content loader for the system
+        // bundle module.
+        m_extensionManager.setSearchPolicy(
+            new R4SearchPolicy(m_policyCore, m_sbi.getCurrentModule()));
+        m_factory.setContentLoader(m_sbi.getCurrentModule(), m_extensionManager);
+        // Lastly, set the system bundle's protection domain.
         try
         {
-            m_cache = new BundleCache(m_logger, m_configMap);
+            addSecurity(this);
         }
         catch (Exception ex)
         {
-            System.err.println("Error creating bundle cache:");
-            ex.printStackTrace();
-
-            // Only shutdown the JVM if the framework is running stand-alone.
-            String embedded = (String) m_configMap.get(
-                FelixConstants.EMBEDDED_EXECUTION_PROP);
-            boolean isEmbedded = (embedded == null)
-                ? false : embedded.equals("true");
-            if (!isEmbedded)
-            {
-                m_secureAction.exit(-1);
-            }
-            else
-            {
-                throw new RuntimeException(ex.toString());
-            }
+            // This should not happen
         }
-
-        // Create the module factory and add the system bundle
-        // module to it, since we need the system bundle info
-        // object to keep track of the framework state.
-        m_factory = new ModuleFactoryImpl(m_logger);
-        m_systemBundleInfo = new BundleInfo(
-            m_logger, new SystemBundleArchive(m_cache), null);
-        m_extensionManager =
-            new ExtensionManager(m_logger, m_configMap, m_systemBundleInfo);
-// TODO: REFACTOR - Right now this module added event is getting lost
-//       by R4SearchPolicyCore, which uses it to populate the avail package
-//       map. Not super important, since it gets the resolved event.
-        m_systemBundleInfo.addModule(
-            m_factory.createModule("0", m_extensionManager));
     }
 
     private Map createUnmodifiableMap(Map mutableMap)
@@ -308,17 +366,13 @@
 
     /* package private */ BundleInfo getInfo()
     {
-        return m_systemBundleInfo;
+        return m_sbi;
     }
 
     public BundleContext getBundleContext()
     {
 // TODO: SECURITY - We need a security check here.
-        if (m_systemBundleInfo != null)
-        {
-            return m_systemBundleInfo.getBundleContext();
-        }
-        return null;
+        return m_sbi.getBundleContext();
     }
 
     public long getBundleId()
@@ -405,11 +459,7 @@
 
     public long getLastModified()
     {
-        if (m_systemBundleInfo != null)
-        {
-            return m_systemBundleInfo.getLastModified();
-        }
-        return -1;
+        return m_sbi.getLastModified();
     }
 
     public String getLocation()
@@ -561,7 +611,7 @@
 
     public int getState()
     {
-        return m_systemBundleInfo.getState();
+        return m_sbi.getState();
     }
 
     public String getSymbolicName()
@@ -599,247 +649,54 @@
         return loadBundleClass(this, name);
     }
 
-    public synchronized void start() throws BundleException
+    /**
+     * This method initializes the framework, which is comprised of resolving
+     * the system bundle, reloading any cached bundles, and activating the system
+     * bundle. The framework is left in the <tt>Bundle.STARTING</tt> state and
+     * reloaded bundles are in the <tt>Bundle.INSTALLED</tt> state. After
+     * successfully invoking this method, <tt>getBundleContext()</tt> will
+     * return a valid <tt>BundleContext</tt> for the system bundle. To finish
+     * starting the framework, invoke the <tt>start()</tt> method.
+     *
+     * @throws org.osgi.framework.BundleException if any error occurs.
+    **/
+    public synchronized void init() throws BundleException
     {
-        // The system bundle is only started once and it
-        // is started by the framework.
-        if (getState() == Bundle.ACTIVE)
+        // The system bundle can only be initialized if it currently isn't started.
+        final int state = m_sbi.getState();
+        if ((state == Bundle.INSTALLED) || (state == Bundle.RESOLVED))
         {
-            return;
-        }
-        else if (m_systemBundleInfo.getState() != Bundle.INSTALLED)
-        {
-            throw new IllegalStateException("Invalid framework state: " + m_systemBundleInfo.getState());
-        }
+            // Get any system bundle activators.
+            m_activatorList = (List) m_configMutableMap.get(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP);
+            m_activatorList = (m_activatorList == null) ? new ArrayList() : new ArrayList(m_activatorList);
 
-        // The framework is now in its startup sequence.
-        m_systemBundleInfo.setState(Bundle.STARTING);
+            // Initialize event dispatcher.
+            m_dispatcher = EventDispatcher.start(m_logger);
 
-        // Create default bundle stream handler.
-        m_bundleStreamHandler = new URLHandlersBundleStreamHandler(this);
-
-        // Create service registry.
-        m_registry = new ServiceRegistry(m_logger);
-        // Add a listener to the service registry; this is
-        // used to distribute service registry events to
-        // service listeners.
-        m_registry.addServiceListener(new ServiceListener() {
-            public void serviceChanged(ServiceEvent event)
-            {
-                fireServiceEvent(event);
-            }
-        });
-
-        // Create search policy for module loader.
-        m_policyCore = new R4SearchPolicyCore(m_logger, m_configMap);
-
-        // Add a resolver listener to the search policy
-        // so that we will be notified when modules are resolved
-        // in order to update the bundle state.
-        m_policyCore.addResolverListener(new ResolveListener() {
-            public void moduleResolved(ModuleEvent event)
-            {
-                FelixBundle bundle = null;
-                try
-                {
-                    long id = Util.getBundleIdFromModuleId(
-                        event.getModule().getId());
-                    if (id > 0)
-                    {
-                        // Update the bundle's state to resolved when the
-                        // current module is resolved; just ignore resolve
-                        // events for older revisions since this only occurs
-                        // when an update is done on an unresolved bundle
-                        // and there was no refresh performed.
-                        bundle = (FelixBundle) getBundle(id);
-
-                        // Lock the bundle first.
-                        try
-                        {
-                            acquireBundleLock(bundle);
-                            if (bundle.getInfo().getCurrentModule() == event.getModule())
-                            {
-                                if (bundle.getInfo().getState() != Bundle.INSTALLED)
-                                {
-                                    m_logger.log(
-                                        Logger.LOG_WARNING,
-                                        "Received a resolve event for a bundle that has already been resolved.");
-                                }
-                                else
-                                {
-                                    bundle.getInfo().setState(Bundle.RESOLVED);
-                                    fireBundleEvent(BundleEvent.RESOLVED, bundle);
-                                }
-                            }
-                        }
-                        finally
-                        {
-                            releaseBundleLock(bundle);
-                        }
-                    }
-                }
-                catch (NumberFormatException ex)
-                {
-                    // Ignore.
-                }
-            }
-
-            public void moduleUnresolved(ModuleEvent event)
-            {
-                // We can ignore this, because the only time it
-                // should happen is when a refresh occurs. The
-                // refresh operation resets the bundle's state
-                // by calling BundleInfo.reset(), thus it is not
-                // necessary for us to reset the bundle's state
-                // here.
-            }
-        });
-
-        m_policyCore.setModuleFactory(m_factory);
-
-        // Initialize event dispatcher.
-        m_dispatcher = EventDispatcher.start(m_logger);
-
-        // Reload the cached bundles bundles before creating and starting
-        // the system bundle, since we want all cached bundles to be reloaded
-        // when we activate the system bundle and any subsequent custom
-        // framework activators passed into the framework constructor.
-        BundleArchive[] archives = null;
-
-        // First get cached bundle identifiers.
-        try
-        {
-            archives = m_cache.getArchives();
-        }
-        catch (Exception ex)
-        {
-            m_logger.log(
-                Logger.LOG_ERROR,
-                "Unable to list saved bundles.",
-                ex);
-            archives = null;
-        }
-
-        // create the system bundle that is responsible for providing specific
-        // container related services.
-        IContentLoader cl = m_extensionManager;
-        cl.setSearchPolicy(
-            new R4SearchPolicy(
-                m_policyCore, m_systemBundleInfo.getCurrentModule()));
-        m_factory.setContentLoader(
-            m_systemBundleInfo.getCurrentModule(),
-            cl);
-
-        try
-        {
-            addSecurity(this);
-        }
-        catch (Exception e)
-        {
-            // This should not happen
-        }
-
-        // Note: we need this ordering to launch:
-        // 1) create all stuff of the system bundle (extension manager needs it)
-        // 2) install all bundles from cache (will start extension bundles)
-        // 3) start the system bundle (will start custom activators)
-        // 4) start the other bundles via the start level
-        // it is important to keep this order because bundles are installed
-        // from added activators in step 3 and extension bundles are started
-        // in 2 and need the stuff from 1.
-        m_installedBundleMap.put(
-            m_systemBundleInfo.getLocation(), this);
-        m_installedBundleIndex.put(new Long(0), this);
-
-        // Create system bundle activator.
-        m_systemBundleInfo.setActivator(new SystemBundleActivator());
-
-        // Create the bundle context for the system bundle and
-        // then activate it.
-        m_systemBundleInfo.setBundleContext(
-            new BundleContextImpl(m_logger, this, this));
-
-        FelixBundle bundle = null;
-
-        // Now install all cached bundles.
-        for (int i = 0; (archives != null) && (i < archives.length); i++)
-        {
+            // Create the bundle cache so that we can reload any installed bundles.
             try
             {
-                // Keep track of the max bundle ID currently in use since we
-                // will need to use this as our next bundle ID value if the
-                // persisted value cannot be read.
-                m_nextId = Math.max(m_nextId, archives[i].getId() + 1);
-
-                // It is possible that a bundle in the cache was previously
-                // uninstalled, but not completely deleted (perhaps because
-                // of a crash or a locked file), so if we see an archive
-                // with an UNINSTALLED persistent state, then try to remove
-                // it now.
-                if (archives[i].getPersistentState() == Bundle.UNINSTALLED)
-                {
-                    m_cache.remove(archives[i]);
-                }
-                // Otherwise re-install the cached bundle.
-                else
-                {
-                    // Install the cached bundle.
-                    bundle = (FelixBundle) installBundle(
-                        archives[i].getId(), archives[i].getLocation(), null);
-                }
+                m_cache = new BundleCache(m_logger, m_configMap);
             }
             catch (Exception ex)
             {
-ex.printStackTrace();
-                fireFrameworkEvent(FrameworkEvent.ERROR, this, ex);
-                try
-                {
-                    m_logger.log(
-                        Logger.LOG_ERROR,
-                        "Unable to re-install " + archives[i].getLocation(),
-                        ex);
-                }
-                catch (Exception ex2)
-                {
-                    m_logger.log(
-                        Logger.LOG_ERROR,
-                        "Unable to re-install cached bundle.",
-                        ex);
-                }
-                // TODO: FRAMEWORK - Perhaps we should remove the cached bundle?
+                m_logger.log(Logger.LOG_ERROR, "Error creating bundle cache.", ex);
+                throw new BundleException("Error creating bundle cache.", ex);
             }
-        }
 
-        // Now that we have loaded all cached bundles and have determined the
-        // max bundle ID of cached bundles, we need to try to load the next
-        // bundle ID from persistent storage. In case of failure, we should
-        // keep the max value.
-        m_nextId = Math.max(m_nextId, loadNextId());
+            // Initialize installed bundle data structures.
+            m_installedBundleMap = new HashMap();
+            m_installedBundleIndex = new TreeMap();
 
-        // Get the framework's default start level.
-        int startLevel = FelixConstants.FRAMEWORK_DEFAULT_STARTLEVEL;
-        String s = (String) m_configMap.get(FelixConstants.FRAMEWORK_STARTLEVEL_PROP);
-        if (s != null)
-        {
-            try
-            {
-                startLevel = Integer.parseInt(s);
-            }
-            catch (NumberFormatException ex)
-            {
-                startLevel = FelixConstants.FRAMEWORK_DEFAULT_STARTLEVEL;
-            }
-        }
+            // Add the system bundle to the set of installed bundles.
+            m_installedBundleMap.put(m_sbi.getLocation(), this);
+            m_installedBundleIndex.put(new Long(0), this);
 
-        // Now that the cached bundles are reloaded,
-        // activating all custom framework activators.
-        try
-        {
-            // Manually resolve the System Bundle, which will cause its
+            // Manually resolve the system bundle, which will cause its
             // state to be set to RESOLVED.
             try
             {
-                m_policyCore.resolve(m_systemBundleInfo.getCurrentModule());
+                m_policyCore.resolve(m_sbi.getCurrentModule());
             }
             catch (ResolveException ex)
             {
@@ -849,51 +706,185 @@
                     + ex.getRequirement());
             }
 
-            Felix.m_secureAction.startActivator(m_systemBundleInfo.getActivator(),
-                m_systemBundleInfo.getBundleContext());
-        }
-        catch (Throwable ex)
-        {
-            m_factory = null;
-            EventDispatcher.shutdown();
-            m_logger.log(Logger.LOG_ERROR, "Unable to start system bundle.", ex);
-            throw new RuntimeException("Unable to start system bundle.");
-        }
+            // Reload the cached bundles before creating and starting the
+            // system bundle, since we want all cached bundles to be reloaded
+            // when we activate the system bundle and any subsequent system
+            // bundle activators passed into the framework constructor.
+            BundleArchive[] archives = null;
 
-        // Now that the system bundle is successfully created we can give
-        // its bundle context to the logger so that it can track log services.
-        m_logger.setSystemBundleContext(m_systemBundleInfo.getBundleContext());
-
-        // Set the start level using the start level service;
-        // this ensures that all start level requests are
-        // serialized.
-        try
-        {
-            StartLevel sl = (StartLevel) getService(
-                getBundle(0),getServiceReferences((FelixBundle) getBundle(0),
-                StartLevel.class.getName(), null, true)[0]);
-            if (sl instanceof StartLevelImpl)
+            // First get cached bundle identifiers.
+            try
             {
-                ((StartLevelImpl) sl).setStartLevelAndWait(startLevel);
+                archives = m_cache.getArchives();
             }
-            else
+            catch (Exception ex)
             {
-                sl.setStartLevel(startLevel);
+                m_logger.log(Logger.LOG_ERROR, "Unable to list saved bundles.", ex);
+                archives = null;
             }
+
+            // Now load all cached bundles.
+            for (int i = 0; (archives != null) && (i < archives.length); i++)
+            {
+                try
+                {
+                    // Keep track of the max bundle ID currently in use since we
+                    // will need to use this as our next bundle ID value if the
+                    // persisted value cannot be read.
+                    m_nextId = Math.max(m_nextId, archives[i].getId() + 1);
+
+                    // It is possible that a bundle in the cache was previously
+                    // uninstalled, but not completely deleted (perhaps because
+                    // of a crash or a locked file), so if we see an archive
+                    // with an UNINSTALLED persistent state, then try to remove
+                    // it now.
+                    if (archives[i].getPersistentState() == Bundle.UNINSTALLED)
+                    {
+                        m_cache.remove(archives[i]);
+                    }
+                    // Otherwise re-install the cached bundle.
+                    else
+                    {
+                        // Install the cached bundle.
+                        installBundle(archives[i].getId(), archives[i].getLocation(), null);
+                    }
+                }
+                catch (Exception ex)
+                {
+ex.printStackTrace();
+                    fireFrameworkEvent(FrameworkEvent.ERROR, this, ex);
+                    try
+                    {
+                        m_logger.log(
+                            Logger.LOG_ERROR,
+                            "Unable to re-install " + archives[i].getLocation(),
+                            ex);
+                    }
+                    catch (Exception ex2)
+                    {
+                        m_logger.log(
+                            Logger.LOG_ERROR,
+                            "Unable to re-install cached bundle.",
+                            ex);
+                    }
+                    // TODO: FRAMEWORK - Perhaps we should remove the cached bundle?
+                }
+            }
+
+            // Now that we have loaded all cached bundles and have determined the
+            // max bundle ID of cached bundles, we need to try to load the next
+            // bundle ID from persistent storage. In case of failure, we should
+            // keep the max value.
+            m_nextId = Math.max(m_nextId, loadNextId());
+
+            // Create service registry.
+            m_registry = new ServiceRegistry(m_logger);
+            // Add a listener to the service registry; this is
+            // used to distribute service registry events to
+            // service listeners.
+            m_registry.addServiceListener(new ServiceListener() {
+                public void serviceChanged(ServiceEvent event)
+                {
+                    fireServiceEvent(event);
+                }
+            });
+
+            // The framework is now in its startup sequence.
+            m_sbi.setState(Bundle.STARTING);
+
+            // Now it is possible for threads to wait for the framework to stop,
+            // so create a gate for that purpose.
+            m_shutdownGate = new ThreadGate();
+
+            // Create system bundle activator and bundle context so we can activate it.
+            m_sbi.setActivator(new SystemBundleActivator());
+            m_sbi.setBundleContext(new BundleContextImpl(m_logger, this, this));
+            try
+            {
+                Felix.m_secureAction.startActivator(
+                    m_sbi.getActivator(), m_sbi.getBundleContext());
+            }
+            catch (Throwable ex)
+            {
+                m_factory = null;
+                EventDispatcher.shutdown();
+                m_logger.log(Logger.LOG_ERROR, "Unable to start system bundle.", ex);
+                throw new RuntimeException("Unable to start system bundle.");
+            }
+
+            // Now that the system bundle is successfully created we can give
+            // its bundle context to the logger so that it can track log services.
+            m_logger.setSystemBundleContext(m_sbi.getBundleContext());
         }
-        catch (InvalidSyntaxException ex)
+    }
+
+    /**
+     * This method starts the framework instance, which will transition the
+     * framework from start level 0 to its active start level as specified in
+     * its configuration properties (1 by default). If the <tt>init()</tt> was
+     * not explicitly invoked before calling this method, then it will be
+     * implicitly invoked before starting the framework.
+     *
+     * @throws org.osgi.framework.BundleException if any error occurs.
+    **/
+    public synchronized void start() throws BundleException
+    {
+        final int state = m_sbi.getState();
+        // Initialize if necessary.
+        if ((state == Bundle.INSTALLED) || (state == Bundle.RESOLVED))
         {
-            // Should never happen.
+            init();
         }
 
-        // The framework is now running.
-        m_systemBundleInfo.setState(Bundle.ACTIVE);
+        // If the current state is STARTING, then the system bundle can be started.
+        if (m_sbi.getState() == Bundle.STARTING)
+        {
+            // Get the framework's default start level.
+            int startLevel = FelixConstants.FRAMEWORK_DEFAULT_STARTLEVEL;
+            String s = (String) m_configMap.get(FelixConstants.FRAMEWORK_STARTLEVEL_PROP);
+            if (s != null)
+            {
+                try
+                {
+                    startLevel = Integer.parseInt(s);
+                }
+                catch (NumberFormatException ex)
+                {
+                    startLevel = FelixConstants.FRAMEWORK_DEFAULT_STARTLEVEL;
+                }
+            }
 
-        // Fire started event for system bundle.
-        fireBundleEvent(BundleEvent.STARTED, this);
+            // Set the start level using the start level service;
+            // this ensures that all start level requests are
+            // serialized.
+            try
+            {
+                StartLevel sl = (StartLevel) getService(
+                    getBundle(0),getServiceReferences((FelixBundle) getBundle(0),
+                    StartLevel.class.getName(), null, true)[0]);
+                if (sl instanceof StartLevelImpl)
+                {
+                    ((StartLevelImpl) sl).setStartLevelAndWait(startLevel);
+                }
+                else
+                {
+                    sl.setStartLevel(startLevel);
+                }
+            }
+            catch (InvalidSyntaxException ex)
+            {
+                // Should never happen.
+            }
 
-        // Send a framework event to indicate the framework has started.
-        fireFrameworkEvent(FrameworkEvent.STARTED, this, null);
+            // The framework is now running.
+            m_sbi.setState(Bundle.ACTIVE);
+
+            // Fire started event for system bundle.
+            fireBundleEvent(BundleEvent.STARTED, this);
+
+            // Send a framework event to indicate the framework has started.
+            fireFrameworkEvent(FrameworkEvent.STARTED, this, null);
+        }
     }
 
     public void start(int options) throws BundleException
@@ -904,7 +895,7 @@
     }
 
     /**
-     * This method cleanly shuts down the framework, it must be called at the
+     * This method asynchronously shuts down the framework, it must be called at the
      * end of a session in order to shutdown all active bundles.
     **/
     public void stop() throws BundleException
@@ -927,41 +918,24 @@
         stop();
     }
 
-    public void stopAndWait()
+    /**
+     * This method will cause the calling thread to block until the framework
+     * shuts down.
+     * @param timeout A timeout value.
+     * @throws java.lang.InterruptedException If the thread was interrupted.
+    **/
+    public void waitForStop(long timeout) throws InterruptedException
     {
-        // Shut the framework down by calling stop() on the system bundle.
-        // Since stop() on the system bundle will return immediately, we
-        // will synchronize on the framework instance so we can use it to
-        // be notified when the shutdown is complete.
+        // If there is a gate, wait on it; otherwise, return immediately.
+        ThreadGate gate;
         synchronized (this)
         {
-            if (m_systemBundleInfo.getState() == Bundle.ACTIVE)
-            {
-                try
-                {
-                    getBundle(0).stop();
-                }
-                catch (BundleException ex)
-                {
-                    fireFrameworkEvent(FrameworkEvent.ERROR, this, ex);
-                    m_logger.log(
-                        Logger.LOG_ERROR,
-                        "Error stopping system bundle.",
-                        ex);
-                }
-            }
+            gate = m_shutdownGate;
+        }
 
-            while (m_systemBundleInfo.getState() != Bundle.UNINSTALLED)
-            {
-                try
-                {
-                    wait();
-                }
-                catch (InterruptedException ex)
-                {
-                    // Keep waiting if necessary.
-                }
-            }
+        if (gate != null)
+        {
+            gate.await(timeout);
         }
     }
 
@@ -1176,7 +1150,7 @@
             bundles[i] = null;
         }
 
-        if (m_systemBundleInfo.getState() == Bundle.ACTIVE)
+        if (m_sbi.getState() == Bundle.ACTIVE)
         {
             fireFrameworkEvent(FrameworkEvent.STARTLEVEL_CHANGED, this, null);
         }
@@ -1845,7 +1819,7 @@
                     {
                         addSecurity(bundle);
                         m_extensionManager.addExtensionBundle(this, bundle);
-                        m_factory.refreshModule(m_systemBundleInfo.getCurrentModule());
+                        m_factory.refreshModule(m_sbi.getCurrentModule());
                         bundle.getInfo().setState(Bundle.RESOLVED);
                     }
                     else if (bundle.getInfo().isExtension())
@@ -2013,25 +1987,6 @@
             {
                 m_secureAction.stopActivator(info.getActivator(), info.getBundleContext());
             }
-
-            // Try to save the activator in the cache.
-            // NOTE: This is non-standard OSGi behavior and only
-            // occurs if strictness is disabled.
-            String strict = (String) m_configMap.get(FelixConstants.STRICT_OSGI_PROP);
-            boolean isStrict = (strict == null) ? true : strict.equals("true");
-            if (!isStrict)
-            {
-                try
-                {
-                    m_cache.getArchive(info.getBundleId())
-                        .setActivator(info.getActivator());
-                }
-                catch (Exception ex)
-                {
-                    // Problem saving activator, so ignore it.
-                    // TODO: FRAMEWORK - Perhaps we should handle this some other way?
-                }
-            }
         }
         catch (Throwable th)
         {
@@ -2326,7 +2281,7 @@
                 else
                 {
                     m_extensionManager.addExtensionBundle(this, bundle);
-                    m_factory.refreshModule(m_systemBundleInfo.getCurrentModule());
+                    m_factory.refreshModule(m_sbi.getCurrentModule());
                 }
 
             }
@@ -2847,11 +2802,10 @@
         {
             if (bundle == this)
             {
-                return m_systemBundleInfo.getArchive().getDataFile(s);
+                return m_cache.getSystemBundleDataFile(s);
             }
 
-            return m_cache.getArchive(
-                bundle.getBundleId()).getDataFile(s);
+            return m_cache.getArchive(bundle.getBundleId()).getDataFile(s);
         }
         catch (Exception ex)
         {
@@ -2891,11 +2845,11 @@
                 }
             }
         }
-        try 
+        try
         {
             return (getInfo().getCurrentModule().getClass(clazz.getName()) == clazz) ? this : null;
         }
-        catch(ClassNotFoundException ex) 
+        catch(ClassNotFoundException ex)
         {
             return null;
         }
@@ -3301,7 +3255,7 @@
     // Miscellaneous private methods.
     //
 
-    private BundleInfo createBundleInfo(BundleArchive archive, Map headerMap, boolean isExtension)
+    private RegularBundleInfo createBundleInfo(BundleArchive archive, Map headerMap, boolean isExtension)
         throws Exception
     {
         // Create the module for the bundle; although there should only
@@ -3312,7 +3266,7 @@
             isExtension);
 
         // Finally, create an return the bundle info.
-        BundleInfo info = new BundleInfo(m_logger, archive, module);
+        RegularBundleInfo info = new RegularBundleInfo(m_logger, archive, module);
         info.setExtension(isExtension);
 
         return info;
@@ -3458,45 +3412,24 @@
         // This method is called indirectly from startBundle() (via _startBundle()),
         // which has the exclusion lock, so there is no need to do any locking here.
 
+        // Get the activator class from the header map.
         BundleActivator activator = null;
-
-        String strict = (String) m_configMap.get(FelixConstants.STRICT_OSGI_PROP);
-        boolean isStrict = (strict == null) ? true : strict.equals("true");
-        if (!isStrict)
+        Map headerMap = info.getCurrentHeader();
+        String className = (String) headerMap.get(Constants.BUNDLE_ACTIVATOR);
+        // Try to instantiate activator class if present.
+        if (className != null)
         {
+            className = className.trim();
+            Class clazz;
             try
             {
-                activator = info.getArchive().getActivator(info.getCurrentModule());
+                clazz = info.getCurrentModule().getClass(className);
             }
-            catch (Exception ex)
-            {
-                activator = null;
+            catch (ClassNotFoundException ex) {
+                throw new BundleException("Not found: "
+                    + className, ex);
             }
-        }
-
-        // If there was no cached activator, then get the activator
-        // class from the bundle manifest.
-        if (activator == null)
-        {
-            // Get the activator class from the header map.
-            Map headerMap = info.getCurrentHeader();
-            String className = (String) headerMap.get(Constants.BUNDLE_ACTIVATOR);
-            // Try to instantiate activator class if present.
-            if (className != null)
-            {
-                className = className.trim();
-                Class clazz;
-                try 
-                {
-                    clazz = info.getCurrentModule().getClass(className);
-                }
-                catch (ClassNotFoundException ex) {
-                    throw new BundleException("Not found: "
-                        + className, ex);
-                }
-
-                activator = (BundleActivator) clazz.newInstance();
-            }
+            activator = (BundleActivator) clazz.newInstance();
         }
 
         return activator;
@@ -3799,7 +3732,7 @@
             {
                 // Change framework state from active to stopping.
                 // If framework is not active, then just return.
-                if (m_systemBundleInfo.getState() != Bundle.STOPPING)
+                if (m_sbi.getState() != Bundle.STOPPING)
                 {
                     return;
                 }
@@ -3832,7 +3765,9 @@
             for (int i = 0; i < bundles.length; i++)
             {
                 FelixBundle bundle = (FelixBundle) bundles[i];
-                if (bundle.getInfo().getArchive().getRevisionCount() > 1)
+                BundleInfo info = bundle.getInfo();
+                if ((info instanceof RegularBundleInfo) &&
+                    (((RegularBundleInfo) info).getArchive().getRevisionCount() > 1))
                 {
                     try
                     {
@@ -3911,19 +3846,13 @@
                 m_extensionManager.removeExtensions(Felix.this);
             }
 
-            // Notify any waiters that the framework is back in its initial state.
+            // Set the framework state to resolved.
             synchronized (Felix.this)
             {
-                m_systemBundleInfo.setState(Bundle.UNINSTALLED);
-                Felix.this.notifyAll();
-            }
-
-            // Finally shutdown the JVM if the framework is running stand-alone.
-            String embedded = (String) m_configMap.get(FelixConstants.EMBEDDED_EXECUTION_PROP);
-            boolean isEmbedded = (embedded == null) ? false : embedded.equals("true");
-            if (!isEmbedded)
-            {
-                m_secureAction.exit(0);
+                m_sbi.setState(Bundle.RESOLVED);
+                m_shutdownGate.open();
+                m_shutdownGate = null;
+                m_shutdownThread = null;
             }
         }
     }
@@ -3997,8 +3926,8 @@
             {
                 try
                 {
-                    BundleInfo info = m_bundle.getInfo();
-                    BundleInfo newInfo = createBundleInfo(
+                    RegularBundleInfo info = (RegularBundleInfo) m_bundle.getInfo();
+                    RegularBundleInfo newInfo = createBundleInfo(
                         info.getArchive(), info.getCurrentHeader(), info.isExtension());
                     newInfo.syncLock(info);
                     ((BundleImpl) m_bundle).setInfo(newInfo);
@@ -4300,7 +4229,9 @@
                         while (iter.hasNext())
                         {
                             FelixBundle bundle = (FelixBundle) iter.next();
-                            if (bundle.getInfo().getArchive().getRevisionCount() > 1)
+                            BundleInfo info = bundle.getInfo();
+                            if ((info instanceof RegularBundleInfo) &&
+                                (((RegularBundleInfo) info).getArchive().getRevisionCount() > 1))
                             {
                                 list.add(bundle);
                             }
diff --git a/framework/src/main/java/org/apache/felix/framework/FelixBundle.java b/framework/src/main/java/org/apache/felix/framework/FelixBundle.java
index 7810548..4e27160 100644
--- a/framework/src/main/java/org/apache/felix/framework/FelixBundle.java
+++ b/framework/src/main/java/org/apache/felix/framework/FelixBundle.java
@@ -23,4 +23,4 @@
 abstract class FelixBundle implements Bundle
 {
     /* package private */ abstract BundleInfo getInfo();
-}
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/RegularBundleInfo.java b/framework/src/main/java/org/apache/felix/framework/RegularBundleInfo.java
new file mode 100644
index 0000000..8a781fd
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/RegularBundleInfo.java
@@ -0,0 +1,580 @@
+/*
+ * 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.felix.framework;
+
+import java.io.IOException;
+import java.net.URL;
+import java.security.ProtectionDomain;
+import java.util.*;
+
+import org.apache.felix.framework.cache.BundleArchive;
+import org.apache.felix.framework.searchpolicy.ModuleDefinition;
+import org.apache.felix.framework.util.manifestparser.ManifestParser;
+import org.apache.felix.moduleloader.ICapability;
+import org.apache.felix.moduleloader.IContentLoader;
+import org.apache.felix.moduleloader.IModule;
+import org.osgi.framework.*;
+
+class RegularBundleInfo extends BundleInfo
+{
+    private Logger m_logger = null;
+    private BundleArchive m_archive = null;
+    private IModule[] m_modules = null;
+    private int m_state = 0;
+    private BundleActivator m_activator = null;
+    private BundleContext m_context = null;
+    private String m_cachedSymbolicName = null;
+    private long m_cachedSymbolicNameTimestamp;
+    private Map m_cachedHeaders = new HashMap();
+    private long m_cachedHeadersTimestamp;
+
+    // Indicates whether the bundle is stale, meaning that it has
+    // been refreshed and completely removed from the framework.
+    private boolean m_stale = false;
+
+    // Indicates whether the bundle is an extension, meaning that it is
+    // installed as an extension bundle to the framework (i.e., can not be
+    // removed or updated until a framework restart.
+    private boolean m_extension = false;
+
+    // Used for bundle locking.
+    private int m_lockCount = 0;
+    private Thread m_lockThread = null;
+
+    protected RegularBundleInfo(Logger logger, BundleArchive archive, IModule module)
+    {
+        m_logger = logger;
+        m_archive = archive;
+        m_modules = (module == null) ? new IModule[0] : new IModule[] { module };
+
+        m_state = Bundle.INSTALLED;
+        m_stale = false;
+        m_activator = null;
+        m_context = null;
+    }
+
+    /**
+     *  Returns the bundle archive associated with this bundle.
+     * @return the bundle archive associated with this bundle.
+    **/
+// TODO: KARL - Should this be on BundleInfo and just return in SystemBundleInfo or exception?
+    public BundleArchive getArchive()
+    {
+        return m_archive;
+    }
+
+    /**
+     * Returns an array of all modules associated with the bundle represented by
+     * this <tt>BundleInfo</tt> object. A module in the array corresponds to a
+     * revision of the bundle's JAR file and is ordered from oldest to newest.
+     * Multiple revisions of a bundle JAR file might exist if a bundle is
+     * updated, without refreshing the framework. In this case, exports from
+     * the prior revisions of the bundle JAR file are still offered; the
+     * current revision will be bound to packages from the prior revision,
+     * unless the packages were not offered by the prior revision. There is
+     * no limit on the potential number of bundle JAR file revisions.
+     * @return array of modules corresponding to the bundle JAR file revisions.
+    **/
+    public synchronized IModule[] getModules()
+    {
+        return m_modules;
+    }
+
+    /**
+     * Determines if the specified module is associated with this bundle.
+     * @param module the module to determine if it is associate with this bundle.
+     * @return <tt>true</tt> if the specified module is in the array of modules
+     *         associated with this bundle, <tt>false</tt> otherwise.
+    **/
+    public synchronized boolean hasModule(IModule module)
+    {
+        for (int i = 0; i < m_modules.length; i++)
+        {
+            if (m_modules[i] == module)
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the newest module, which corresponds to the last module
+     * in the module array.
+     * @return the newest module.
+    **/
+    public synchronized IModule getCurrentModule()
+    {
+        return m_modules[m_modules.length - 1];
+    }
+
+    /**
+     * Add a module that corresponds to a new bundle JAR file revision for
+     * the bundle associated with this <tt>BundleInfo</tt> object.
+     * @param module the module to add.
+    **/
+    public synchronized void addModule(IModule module)
+    {
+        IModule[] dest = new IModule[m_modules.length + 1];
+        System.arraycopy(m_modules, 0, dest, 0, m_modules.length);
+        dest[m_modules.length] = module;
+        m_modules = dest;
+    }
+
+    public synchronized String getSymbolicName()
+    {
+        // If the bundle has been updated, clear the cached symbolic name.
+        if (getLastModified() > m_cachedSymbolicNameTimestamp)
+        {
+            m_cachedSymbolicName = null;
+            m_cachedSymbolicNameTimestamp = getLastModified();
+            try
+            {
+                // TODO: FRAMEWORK - Rather than reparsing every time, I wonder if
+                //       we should be caching this value some place.
+                final ICapability moduleCap = ManifestParser.parseBundleSymbolicName(getCurrentHeader());
+                if (moduleCap != null)
+                {
+                    m_cachedSymbolicName = (String) moduleCap.getProperties().get(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
+                }
+            }
+            catch (BundleException ex)
+            {
+                // Return null.
+            }
+        }
+        return m_cachedSymbolicName;
+    }
+
+    public long getBundleId()
+    {
+        try
+        {
+            return m_archive.getId();
+        }
+        catch (Exception ex)
+        {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                "Error getting the identifier from bundle archive.",
+                ex);
+            return -1;
+        }
+    }
+
+    public String getLocation()
+    {
+        try
+        {
+            return m_archive.getLocation();
+        }
+        catch (Exception ex)
+        {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                "Error getting location from bundle archive.",
+                ex);
+            return null;
+        }
+    }
+
+    public int getStartLevel(int defaultLevel)
+    {
+        try
+        {
+            return m_archive.getStartLevel();
+        }
+        catch (Exception ex)
+        {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                "Error reading start level from bundle archive.",
+                ex);
+            return defaultLevel;
+        }
+    }
+
+    public void setStartLevel(int i)
+    {
+        try
+        {
+            m_archive.setStartLevel(i);
+        }
+        catch (Exception ex)
+        {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                "Error writing start level to bundle archive.",
+                ex);
+        }
+    }
+
+    public Map getCurrentHeader()
+    {
+        Map headerMap = null;
+        // Special case the system bundle
+        if (getBundleId() == 0)
+        {
+            // TODO: REFACTOR - This is sort of a hack, we should just expose
+            //       the bundle symbolic name from our API.
+            try
+            {
+                headerMap = m_archive.getRevision(0).getManifestHeader();
+            }
+            catch (Exception ex)
+            {
+                // This should never happen.
+            }
+        }
+        else
+        {
+            headerMap = ((ModuleDefinition) getCurrentModule().getDefinition()).getHeaders();
+        }
+
+        return headerMap;
+    }
+
+    public Map getCurrentLocalizedHeader(String locale)
+    {
+        synchronized (m_cachedHeaders)
+        {
+            // If the bundle has been updated, clear the cached headers
+            if (getLastModified() > m_cachedHeadersTimestamp)
+            {
+                m_cachedHeaders.clear();
+            }
+            else
+            {
+                // Check if headers for this locale have already been resolved
+                if (m_cachedHeaders.containsKey(locale))
+                {
+                    return (Map) m_cachedHeaders.get(locale);
+                }
+            }
+        }
+
+        Map rawHeaders = getCurrentHeader();
+        Map headers = new HashMap(rawHeaders.size());
+        headers.putAll(rawHeaders);
+
+        // Check to see if we actually need to localize anything
+        boolean needsLocalization = false;
+        for (Iterator it = headers.values().iterator(); it.hasNext(); )
+        {
+            if (((String) it.next()).startsWith("%"))
+            {
+                needsLocalization = true;
+                break;
+            }
+        }
+
+        if (!needsLocalization)
+        {
+            // If localization is not needed, just cache the headers and return them as-is
+            // Not sure if this is useful
+            updateHeaderCache(locale, headers);
+            return headers;
+        }
+
+        // Do localization here and return the localized headers
+        IContentLoader loader = this.getCurrentModule().getContentLoader();
+
+        String basename = (String) headers.get(Constants.BUNDLE_LOCALIZATION);
+        if (basename == null)
+        {
+            basename = Constants.BUNDLE_LOCALIZATION_DEFAULT_BASENAME;
+        }
+
+        // Create ordered list of files to load properties from
+        List resourceList = createResourceList(basename, locale);
+
+        // Create a merged props file with all available props for this locale
+        Properties mergedProperties = new Properties();
+        for (Iterator it = resourceList.iterator(); it.hasNext(); )
+        {
+            URL temp = loader.getResource(it.next() + ".properties");
+            if (temp == null)
+            {
+                continue;
+            }
+            try
+            {
+                mergedProperties.load(temp.openConnection().getInputStream());
+            }
+            catch (IOException ex)
+            {
+                // File doesn't exist, just continue loop
+            }
+        }
+
+        // Resolve all localized header entries
+        for (Iterator it = headers.entrySet().iterator(); it.hasNext(); )
+        {
+            Map.Entry entry = (Map.Entry) it.next();
+            String value = (String) entry.getValue();
+            if (value.startsWith("%"))
+            {
+                String newvalue;
+                String key = value.substring(value.indexOf("%") + 1);
+                newvalue = mergedProperties.getProperty(key);
+                if (newvalue==null)
+                {
+                    newvalue = key;
+                }
+                entry.setValue(newvalue);
+            }
+        }
+
+        updateHeaderCache(locale, headers);
+        return headers;
+    }
+
+    private void updateHeaderCache(String locale, Map localizedHeaders)
+    {
+        synchronized (m_cachedHeaders)
+        {
+            m_cachedHeaders.put(locale, localizedHeaders);
+            m_cachedHeadersTimestamp = System.currentTimeMillis();
+        }
+    }
+
+    private List createResourceList(String basename, String locale)
+    {
+        List result = new ArrayList(4);
+
+        StringTokenizer tokens;
+        StringBuffer tempLocale = new StringBuffer(basename);
+
+        result.add(tempLocale.toString());
+
+        if (locale.length() > 0)
+        {
+            tokens = new StringTokenizer(locale, "_");
+            while (tokens.hasMoreTokens())
+            {
+                tempLocale.append("_").append(tokens.nextToken());
+                result.add(tempLocale.toString());
+            }
+        }
+        return result;
+    }
+
+    public synchronized int getState()
+    {
+        return m_state;
+    }
+
+    public synchronized void setState(int i)
+    {
+        m_state = i;
+    }
+
+    public long getLastModified()
+    {
+        try
+        {
+            return m_archive.getLastModified();
+        }
+        catch (Exception ex)
+        {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                "Error reading last modification time from bundle archive.",
+                ex);
+            return 0;
+        }
+    }
+
+    public void setLastModified(long l)
+    {
+        try
+        {
+            m_archive.setLastModified(l);
+        }
+        catch (Exception ex)
+        {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                "Error writing last modification time to bundle archive.",
+                ex);
+        }
+    }
+
+    public int getPersistentState()
+    {
+        try
+        {
+            return m_archive.getPersistentState();
+        }
+        catch (Exception ex)
+        {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                "Error reading persistent state from bundle archive.",
+                ex);
+            return Bundle.INSTALLED;
+        }
+    }
+
+    public void setPersistentStateInactive()
+    {
+        try
+        {
+            m_archive.setPersistentState(Bundle.INSTALLED);
+        }
+        catch (Exception ex)
+        {
+            m_logger.log(Logger.LOG_ERROR,
+                "Error writing persistent state to bundle archive.",
+                ex);
+        }
+    }
+
+    public void setPersistentStateActive()
+    {
+        try
+        {
+            m_archive.setPersistentState(Bundle.ACTIVE);
+        }
+        catch (Exception ex)
+        {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                "Error writing persistent state to bundle archive.",
+                ex);
+        }
+    }
+
+    public void setPersistentStateUninstalled()
+    {
+        try
+        {
+            m_archive.setPersistentState(Bundle.UNINSTALLED);
+        }
+        catch (Exception ex)
+        {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                "Error writing persistent state to bundle archive.",
+                ex);
+        }
+    }
+
+    public synchronized BundleContext getBundleContext()
+    {
+        return m_context;
+    }
+
+    public synchronized void setBundleContext(BundleContext context)
+    {
+        m_context = context;
+    }
+
+    public synchronized BundleActivator getActivator()
+    {
+        return m_activator;
+    }
+
+    public synchronized void setActivator(BundleActivator activator)
+    {
+        m_activator = activator;
+    }
+
+    public synchronized boolean isStale()
+    {
+        return m_stale;
+    }
+
+    public synchronized void setStale()
+    {
+        m_stale = true;
+    }
+
+    public synchronized boolean isExtension()
+    {
+        return m_extension;
+    }
+
+    public synchronized void setExtension(boolean extension)
+    {
+        m_extension = extension;
+    }
+
+    //
+    // Locking related methods.
+    // NOTE: These methods are not synchronized because it is assumed they
+    // will only ever be called when the caller is in a synchronized block.
+    //
+
+    public synchronized boolean isLockable()
+    {
+        return (m_lockCount == 0) || (m_lockThread == Thread.currentThread());
+    }
+
+    public synchronized void lock()
+    {
+        if ((m_lockCount > 0) && (m_lockThread != Thread.currentThread()))
+        {
+            throw new IllegalStateException("Bundle is locked by another thread.");
+        }
+        m_lockCount++;
+        m_lockThread = Thread.currentThread();
+    }
+
+    public synchronized void unlock()
+    {
+        if (m_lockCount == 0)
+        {
+            throw new IllegalStateException("Bundle is not locked.");
+        }
+        if ((m_lockCount > 0) && (m_lockThread != Thread.currentThread()))
+        {
+            throw new IllegalStateException("Bundle is locked by another thread.");
+        }
+        m_lockCount--;
+        if (m_lockCount == 0)
+        {
+            m_lockThread = null;
+        }
+    }
+
+    public synchronized void syncLock(RegularBundleInfo info)
+    {
+        m_lockCount = info.m_lockCount;
+        m_lockThread = info.m_lockThread;
+    }
+
+    public synchronized void setProtectionDomain(ProtectionDomain pd)
+    {
+        getCurrentModule().getContentLoader().setSecurityContext(pd);
+    }
+
+    public synchronized ProtectionDomain getProtectionDomain()
+    {
+        ProtectionDomain pd = null;
+
+        for (int i = m_modules.length - 1; (i >= 0) && (pd == null); i--)
+        {
+            pd = (ProtectionDomain)
+                m_modules[i].getContentLoader().getSecurityContext();
+        }
+
+        return pd;
+    }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/SystemBundleArchive.java b/framework/src/main/java/org/apache/felix/framework/SystemBundleArchive.java
deleted file mode 100644
index da64e15..0000000
--- a/framework/src/main/java/org/apache/felix/framework/SystemBundleArchive.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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.felix.framework;
-
-import org.apache.felix.framework.cache.*;
-import java.io.File;
-import java.io.InputStream;
-import java.util.Map;
-
-import org.apache.felix.framework.util.FelixConstants;
-import org.apache.felix.framework.util.StringMap;
-import org.apache.felix.moduleloader.IContent;
-import org.apache.felix.moduleloader.IModule;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleActivator;
-
-/**
- * <p>
- * This class represents the bundle archive of the system bundle. It is a
- * special case that is mostly just an empty implementation, since the system
- * bundle is not a real archive.
- * </p>
-**/
-public class SystemBundleArchive extends BundleArchive
-{
-    private BundleCache m_cache;
-    private Map m_headerMap = new StringMap(false);
-    private BundleRevision m_revision;
-
-    public SystemBundleArchive(BundleCache cache)
-    {
-        m_cache = cache;
-
-        try
-        {
-            m_revision = new BundleRevision(null, null, null) {
-
-                public Map getManifestHeader() throws Exception
-                {
-                    return m_headerMap;
-                }
-
-                public IContent getContent() throws Exception
-                {
-                    return null;
-                }
-
-                public void dispose() throws Exception
-                {
-                }
-            };
-        }
-        catch (Exception ex)
-        {
-            // This should never happen.
-        }
-    }
-
-    public long getId()
-    {
-        return 0;
-    }
-
-    public String getLocation() throws Exception
-    {
-        return FelixConstants.SYSTEM_BUNDLE_LOCATION;
-    }
-
-    public String getCurrentLocation() throws Exception
-    {
-        return null;
-    }
-
-    public void setCurrentLocation(String location) throws Exception
-    {
-    }
-
-    public int getPersistentState() throws Exception
-    {
-        return Bundle.ACTIVE;
-    }
-
-    public void setPersistentState(int state) throws Exception
-    {
-    }
-
-    public int getStartLevel() throws Exception
-    {
-        return FelixConstants.SYSTEMBUNDLE_DEFAULT_STARTLEVEL;
-    }
-
-    public void setStartLevel(int level) throws Exception
-    {
-    }
-
-    public File getDataFile(String fileName) throws Exception
-    {
-        return m_cache.getSystemBundleDataFile(fileName);
-    }
-
-    public BundleActivator getActivator(IModule module)
-        throws Exception
-    {
-        return null;
-    }
-
-    public void setActivator(Object obj) throws Exception
-    {
-    }
-
-    public int getRevisionCount()
-    {
-        return 1;
-    }
-
-    public BundleRevision getRevision(int i)
-    {
-        return m_revision;
-    }
-
-    public void revise(String location, InputStream is)
-        throws Exception
-    {
-    }
-
-    public  void purge() throws Exception
-    {
-    }
-
-    public void dispose() throws Exception
-    {
-    }
-
-    public Map getManifestHeader(int revision)
-    {
-        return m_headerMap;
-    }
-}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/SystemBundleInfo.java b/framework/src/main/java/org/apache/felix/framework/SystemBundleInfo.java
new file mode 100644
index 0000000..e2177e6
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/SystemBundleInfo.java
@@ -0,0 +1,90 @@
+/*
+ * 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.felix.framework;
+
+import java.util.Map;
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.StringMap;
+import org.osgi.framework.Bundle;
+
+class SystemBundleInfo extends BundleInfo
+{
+    private Map m_headerMap = new StringMap(false);
+    private int m_startLevel = FelixConstants.SYSTEMBUNDLE_DEFAULT_STARTLEVEL;
+
+    public String getSymbolicName()
+    {
+        return FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME;
+    }
+
+    public long getBundleId()
+    {
+        return 0;
+    }
+
+    public String getLocation()
+    {
+        return FelixConstants.SYSTEM_BUNDLE_LOCATION;
+    }
+
+    public synchronized int getStartLevel(int defaultLevel)
+    {
+        return m_startLevel;
+    }
+
+    public synchronized void setStartLevel(int i)
+    {
+        m_startLevel = i;
+    }
+
+    public Map getCurrentHeader()
+    {
+        return m_headerMap;
+    }
+
+    public long getLastModified()
+    {
+        return 0;
+    }
+
+    public void setLastModified(long l)
+    {
+        // Ignore.
+    }
+
+    public int getPersistentState()
+    {
+        return Bundle.ACTIVE;
+    }
+
+    public void setPersistentStateInactive()
+    {
+        // Ignore.
+    }
+
+    public void setPersistentStateActive()
+    {
+        // Ignore.
+    }
+
+    public void setPersistentStateUninstalled()
+    {
+        // Ignore.
+    }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java b/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
index 43692ca..525dad3 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
@@ -23,6 +23,7 @@
 
 import org.apache.felix.framework.Logger;
 import org.apache.felix.framework.util.SecureAction;
+import org.osgi.framework.SystemBundle;
 
 /**
  * <p>
@@ -301,6 +302,9 @@
 
         // See if the profile directory is specified.
         String profileDirStr = (String) m_configMap.get(CACHE_PROFILE_DIR_PROP);
+        profileDirStr = (profileDirStr == null)
+            ? (String) m_configMap.get(SystemBundle.FRAMEWORK_STORAGE_PROP)
+            : profileDirStr;
         if (profileDirStr != null)
         {
             m_profileDir = new File(profileDirStr);
diff --git a/framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java b/framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java
index 5e86ac7..f3f9e20 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java
@@ -43,8 +43,9 @@
 
     // Miscellaneous framework configuration property names.
     public static final String LOG_LEVEL_PROP = "felix.log.level";
-    public static final String EMBEDDED_EXECUTION_PROP = "felix.embedded.execution";
-    public static final String STRICT_OSGI_PROP = "felix.strict.osgi";
+    public static final String LOG_LOGGER_PROP = "felix.log.logger";
+    public static final String SYSTEMBUNDLE_ACTIVATORS_PROP
+        = "felix.systembundle.activators";
     public static final String FRAMEWORK_STARTLEVEL_PROP
         = "felix.startlevel.framework";
     public static final String BUNDLE_STARTLEVEL_PROP
diff --git a/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java b/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
index 910c118..f497cb8 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
@@ -20,8 +20,6 @@
 
 import java.io.*;
 import java.lang.reflect.*;
-import java.net.JarURLConnection;
-import java.net.MalformedURLException;
 import java.net.*;
 import java.security.*;
 import java.util.Hashtable;
@@ -614,28 +612,6 @@
         }
     }
 
-    public void exit(int code)
-    {
-        if (System.getSecurityManager() != null)
-        {
-            try
-            {
-                Actions actions = (Actions) m_actions.get();
-                actions.set(Actions.SYSTEM_EXIT_ACTION, new Integer(code));
-                AccessController.doPrivileged(actions, m_acc);
-            }
-            catch (PrivilegedActionException ex)
-            {
-                // We don't need to rethrow anything since System.exit throws
-                // runtime exceptions only
-            }
-        }
-        else
-        {
-            System.exit(code);
-        }
-    }
-
     public Policy getPolicy()
     {
         if (System.getSecurityManager() != null)
@@ -657,22 +633,22 @@
         }
     }
 
-    public void addURLToURLClassLoader(URL extension, ClassLoader loader) throws Exception 
+    public void addURLToURLClassLoader(URL extension, ClassLoader loader) throws Exception
     {
-        if (System.getSecurityManager() != null) 
+        if (System.getSecurityManager() != null)
         {
             Actions actions = (Actions) m_actions.get();
             actions.set(Actions.ADD_EXTENSION_URL, extension, loader);
-            try 
+            try
             {
                 AccessController.doPrivileged(actions, m_acc);
-            } 
-            catch (PrivilegedActionException e) 
+            }
+            catch (PrivilegedActionException e)
             {
                 throw e.getException();
             }
         }
-        else 
+        else
         {
             Method addURL =
                 URLClassLoader.class.getDeclaredMethod("addURL",
@@ -681,7 +657,7 @@
             addURL.invoke(loader, new Object[]{extension});
         }
     }
-    
+
     public Constructor getConstructor(Class target, Class[] types) throws Exception
     {
         if (System.getSecurityManager() != null)
@@ -702,7 +678,7 @@
             return target.getConstructor(types);
         }
     }
-    
+
     public Method getMethod(Class target, String method, Class[] types) throws Exception
     {
         if (System.getSecurityManager() != null)
@@ -723,7 +699,7 @@
             return target.getMethod(method, types);
         }
     }
-    
+
     public Method getDeclaredMethod(Class target, String method, Class[] types) throws Exception
     {
         if (System.getSecurityManager() != null)
@@ -744,7 +720,7 @@
             return target.getDeclaredMethod(method, types);
         }
     }
-    
+
     public Object invoke(Method method, Object target, Object[] params) throws Exception
     {
         if (System.getSecurityManager() != null)
@@ -766,7 +742,7 @@
             return method.invoke(target, params);
         }
     }
-    
+
     public Object invoke(Constructor constructor, Object[] params) throws Exception
     {
         if (System.getSecurityManager() != null)
@@ -789,7 +765,7 @@
         }
     }
 
-    public Object getDeclaredField(Class targetClass, String name, Object target) 
+    public Object getDeclaredField(Class targetClass, String name, Object target)
         throws Exception
     {
         if (System.getSecurityManager() != null)
@@ -809,18 +785,18 @@
         {
             Field field = targetClass.getDeclaredField(name);
             field.setAccessible(true);
-            
+
             return field.get(target);
         }
     }
 
-    public Object swapStaticFieldIfNotClass(Class targetClazz, 
+    public Object swapStaticFieldIfNotClass(Class targetClazz,
         Class targetType, Class condition, String lockName) throws Exception
     {
         if (System.getSecurityManager() != null)
         {
             Actions actions = (Actions) m_actions.get();
-            actions.set(Actions.SWAP_FIELD_ACTION, targetClazz, targetType, 
+            actions.set(Actions.SWAP_FIELD_ACTION, targetClazz, targetType,
                 condition, lockName);
             try
             {
@@ -833,25 +809,25 @@
         }
         else
         {
-            return _swapStaticFieldIfNotClass(targetClazz, targetType, 
+            return _swapStaticFieldIfNotClass(targetClazz, targetType,
                 condition, lockName);
         }
     }
-    
-    private static Object _swapStaticFieldIfNotClass(Class targetClazz, 
+
+    private static Object _swapStaticFieldIfNotClass(Class targetClazz,
         Class targetType, Class condition, String lockName) throws Exception
     {
         Object lock = null;
         if (lockName != null)
         {
-            try 
+            try
             {
-                Field lockField = 
-                    targetClazz.getDeclaredField(lockName); 
+                Field lockField =
+                    targetClazz.getDeclaredField(lockName);
                 lockField.setAccessible(true);
                 lock = lockField.get(null);
-            } 
-            catch (NoSuchFieldException ex) 
+            }
+            catch (NoSuchFieldException ex)
             {
             }
         }
@@ -866,13 +842,13 @@
             Object result = null;
             for (int i = 0; (i < fields.length) && (result == null); i++)
             {
-                if (Modifier.isStatic(fields[i].getModifiers()) && 
+                if (Modifier.isStatic(fields[i].getModifiers()) &&
                     (fields[i].getType() == targetType))
                 {
                     fields[i].setAccessible(true);
-                    
+
                     result = fields[i].get(null);
-                    
+
                     if (result != null)
                     {
                         if ((condition == null) ||
@@ -890,7 +866,7 @@
                     // reset cache
                     for (int i = 0; i < fields.length; i++)
                     {
-                        if (Modifier.isStatic(fields[i].getModifiers()) && 
+                        if (Modifier.isStatic(fields[i].getModifiers()) &&
                             (fields[i].getType() == Hashtable.class))
                         {
                             fields[i].setAccessible(true);
@@ -907,7 +883,7 @@
         }
         return null;
     }
-    
+
     private static class Actions implements PrivilegedExceptionAction
     {
         public static final int GET_PROPERTY_ACTION = 0;
@@ -1114,7 +1090,7 @@
                 {
                     return ((JarURLConnection) m_arg1).getJarFile();
                 }
-                else if (m_action == ADD_EXTENSION_URL) 
+                else if (m_action == ADD_EXTENSION_URL)
                 {
                     Method addURL =
                         URLClassLoader.class.getDeclaredMethod("addURL",
@@ -1142,7 +1118,7 @@
                 }
                 else if (m_action == SWAP_FIELD_ACTION)
                 {
-                    return _swapStaticFieldIfNotClass((Class) m_arg1, 
+                    return _swapStaticFieldIfNotClass((Class) m_arg1,
                         (Class) m_arg2, (Class) m_arg3, (String) m_arg4);
                 }
                 else if (m_action == GET_FIELD_ACTION)
@@ -1155,7 +1131,7 @@
                 {
                     return ((Class) m_arg1).getDeclaredMethod((String) m_arg2, (Class[]) m_arg3);
                 }
-                
+
                 return null;
             }
             finally
@@ -1164,4 +1140,4 @@
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/util/ThreadGate.java b/framework/src/main/java/org/apache/felix/framework/util/ThreadGate.java
new file mode 100644
index 0000000..9546f7a
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/util/ThreadGate.java
@@ -0,0 +1,51 @@
+/*
+ * 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.felix.framework.util;
+
+/**
+ * This class implements a simple one-shot gate for threads. The gate
+ * starts closed and will block any threads that try to wait on it. Once
+ * opened, all waiting threads will be released. The gate cannot be reused.
+**/
+public class ThreadGate
+{
+    private boolean m_open = false;
+
+    /**
+     * Open the gate and release any waiting threads.
+    **/
+    public synchronized void open()
+    {
+        m_open = true;
+        notifyAll();
+    }
+
+    /**
+     * Wait for the gate to open.
+     * @throws java.lang.InterruptedException If the calling thread is interrupted;
+     *         the gate still remains closed until opened.
+    **/
+    public synchronized void await(long timeout) throws InterruptedException
+    {
+        while (!m_open)
+        {
+            wait(timeout);
+        }
+    }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/osgi/framework/SystemBundle.java b/framework/src/main/java/org/osgi/framework/SystemBundle.java
new file mode 100644
index 0000000..cc60222
--- /dev/null
+++ b/framework/src/main/java/org/osgi/framework/SystemBundle.java
@@ -0,0 +1,43 @@
+/*
+ * 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.osgi.framework;
+
+public interface SystemBundle extends Bundle
+{
+    public static final String FRAMEWORK_STORAGE_PROP = "org.osgi.framework.storage";
+
+    /**
+     * Initializes the System Bundle. After calling this method, the System
+     * Bundle must be in the STARTING state and must have a valid Bundle Context.
+     * The System Bundle will not actually be started until start() is called.
+     * If the System Bundle is not initialized called prior to calling start(),
+     * then the start method must initialize the System Bundle prior to starting. 
+    **/
+    public void init() throws BundleException;
+
+    /**
+     * Wait until this System Bundle has completely stopped. The stop and update
+     * methods on a System Bundle perform an asynchronous stop of the System
+     * Bundle. This method can be used to wait until the asynchronous stop of
+     * this System Bundle has completed. This method will only wait if called
+     * when this System Bundle is in the STARTING, ACTIVE, or STOPPING states.
+     * Otherwise it will return immediately.
+    **/
+    public void waitForStop(long timeout) throws InterruptedException;
+}
\ No newline at end of file
diff --git a/main/src/main/java/org/apache/felix/main/Main.java b/main/src/main/java/org/apache/felix/main/Main.java
index 09ab34c..9a68dc7 100644
--- a/main/src/main/java/org/apache/felix/main/Main.java
+++ b/main/src/main/java/org/apache/felix/main/Main.java
@@ -26,6 +26,7 @@
 import org.apache.felix.framework.Felix;
 import org.apache.felix.framework.cache.BundleCache;
 import org.apache.felix.framework.util.StringMap;
+import org.osgi.framework.SystemBundle;
 
 /**
  * <p>
@@ -181,6 +182,9 @@
 
         // See if the profile directory property was specified.
         String profileDirName = configProps.getProperty(BundleCache.CACHE_PROFILE_DIR_PROP);
+        profileDirName = (profileDirName == null)
+            ? configProps.getProperty(SystemBundle.FRAMEWORK_STORAGE_PROP)
+            : profileDirName;
 
         // Print welcome banner.
         System.out.println("\nWelcome to Felix.");
@@ -231,9 +235,12 @@
             list.add(new AutoActivator(configProps));
             // Create a case-insensitive property map.
             Map configMap = new StringMap(configProps, false);
+            configMap.put("felix.systembundle.activators", list);
             // Create an instance of the framework.
-            m_felix = new Felix(configMap, list);
+            m_felix = new Felix(configMap);
             m_felix.start();
+            m_felix.waitForStop(0);
+            System.exit(0);
         }
         catch (Exception ex)
         {