Removed auto-property processing out of the framework and into the default
launcher. (FELIX-393)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@604310 13f79535-47bb-0310-9956-ffa450edef68
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 8b3c641..254b5e6 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -113,8 +113,8 @@
 
     /**
      * <p>
-     * This method starts the framework instance; instances of the framework
-     * are dormant until this method is called. The caller may also provide
+     * 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.
@@ -148,18 +148,6 @@
      *       OSGi Log Service (i.e., 1 = error, 2 = warning, 3 = information,
      *       and 4 = debug). The default value is 1.
      *   </li>
-     *   <li><tt>felix.auto.install.&lt;n&gt;</tt> - Space-delimited list of
-     *       bundles to automatically install into start level <tt>n</tt> when
-     *       the framework is started. Append a specific start level to this
-     *       property name to assign the bundles' start level
-     *       (e.g., <tt>felix.auto.install.2</tt>).
-     *   </li>
-     *   <li><tt>felix.auto.start.&lt;n&gt;</tt> - Space-delimited list of
-     *       bundles to automatically install and start into start level
-     *       <tt>n</tt> when the framework is started. Append a
-     *       specific start level to this property name to assign the
-     *       bundles' start level(e.g., <tt>felix.auto.start.2</tt>).
-     *   </li>
      *   <li><tt>felix.startlevel.framework</tt> - The initial start level
      *       of the framework once it starts execution; the default
      *       value is 1.
@@ -195,12 +183,6 @@
      * API documentation.
      * </p>
      * <p>
-     * Framework properties are somewhat misnamed, since they are not used by
-     * the framework, but by bundles via <tt>BundleContext.getProperty()</tt>.
-     * Please refer to bundle documentation of your specific bundle for any
-     * available properties.
-     * </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
@@ -210,7 +192,7 @@
      * class documentation for more information.
      * </p>
      *
-     * @param configMutableMap An map for obtaining configuration properties,
+     * @param configMutableMap A map for obtaining configuration properties,
      *        may be <tt>null</tt>.
      * @param activatorList A list of System Bundle activators.
     **/
@@ -219,7 +201,7 @@
         // Initialize member variables.
         m_configMutableMap = (configMutableMap == null)
             ? new StringMap(false) : configMutableMap;
-        m_configMap = createUnmodifiableMap(m_configMutableMap); 
+        m_configMap = createUnmodifiableMap(m_configMutableMap);
         m_activatorList = activatorList;
 
         // Create logger with appropriate log level. Even though the
@@ -271,15 +253,15 @@
             m_factory.createModule("0", m_extensionManager));
     }
 
-    private Map createUnmodifiableMap(Map mutableMap) 
+    private Map createUnmodifiableMap(Map mutableMap)
     {
         Map result = Collections.unmodifiableMap(mutableMap);
 
-        // Work around a bug in certain version of J9 where a call to 
-        // Collections.unmodifiableMap().keySet().iterator() throws 
-        // a NoClassDefFoundError. We try to detect this and return 
+        // Work around a bug in certain version of J9 where a call to
+        // Collections.unmodifiableMap().keySet().iterator() throws
+        // a NoClassDefFoundError. We try to detect this and return
         // the given mutableMap instead.
-        try 
+        try
         {
             result.keySet().iterator();
         }
@@ -684,60 +666,10 @@
         // Initialize event dispatcher.
         m_dispatcher = EventDispatcher.start(m_logger);
 
-        // Before we reload any cached bundles, let's create a system
-        // bundle that is responsible for providing specific container
-        // related services.
-        try
-        {
-            IContentLoader cl = m_extensionManager;
-            cl.setSearchPolicy(
-                new R4SearchPolicy(
-                    m_policyCore, m_systemBundleInfo.getCurrentModule()));
-            m_factory.setContentLoader(
-                m_systemBundleInfo.getCurrentModule(),
-                cl);
-            m_factory.setSecurityContext(
-                m_systemBundleInfo.getCurrentModule(),
-                getClass().getProtectionDomain());
-
-            m_installedBundleMap.put(
-                m_systemBundleInfo.getLocation(), this);
-
-            // Manually resolve the System Bundle, which will cause its
-            // state to be set to RESOLVED.
-            try
-            {
-                m_policyCore.resolve(m_systemBundleInfo.getCurrentModule());
-            }
-            catch (ResolveException ex)
-            {
-                // This should never happen.
-                throw new BundleException(
-                        "Unresolved package in System Bundle:"
-                        + ex.getRequirement());
-            }
-
-            // 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));
-            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.");
-        }
-
-        // 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());
-
-        // Now reload the cached bundles.
+        // 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.
@@ -826,8 +758,58 @@
             }
         }
 
-        // Load bundles from auto-install and auto-start properties;
-        processAutoProperties();
+        // Now that the cached bundles are reloaded, create the system
+        // bundle that is responsible for providing specific container
+        // related services and activating all custom framework activators.
+        try
+        {
+            IContentLoader cl = m_extensionManager;
+            cl.setSearchPolicy(
+                new R4SearchPolicy(
+                    m_policyCore, m_systemBundleInfo.getCurrentModule()));
+            m_factory.setContentLoader(
+                m_systemBundleInfo.getCurrentModule(),
+                cl);
+            m_factory.setSecurityContext(
+                m_systemBundleInfo.getCurrentModule(),
+                getClass().getProtectionDomain());
+
+            m_installedBundleMap.put(
+                m_systemBundleInfo.getLocation(), this);
+
+            // Manually resolve the System Bundle, which will cause its
+            // state to be set to RESOLVED.
+            try
+            {
+                m_policyCore.resolve(m_systemBundleInfo.getCurrentModule());
+            }
+            catch (ResolveException ex)
+            {
+                // This should never happen.
+                throw new BundleException(
+                    "Unresolved package in System Bundle:"
+                    + ex.getRequirement());
+            }
+
+            // 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));
+            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.");
+        }
+
+        // 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
@@ -3615,214 +3597,6 @@
         return sb.toString();
     }
 
-    private void processAutoProperties()
-    {
-        // The auto-install property specifies a space-delimited list of
-        // bundle URLs to be automatically installed into each new profile;
-        // the start level to which the bundles are assigned is specified by
-        // appending a ".n" to the auto-install property name, where "n" is
-        // the desired start level for the list of bundles.
-        for (Iterator i = m_configMap.keySet().iterator(); i.hasNext(); )
-        {
-            String key = (String) i.next();
-
-            // Ignore all keys that are not the auto-install property.
-            if (!key.startsWith(FelixConstants.AUTO_INSTALL_PROP))
-            {
-                continue;
-            }
-
-            // If the auto-install property does not have a start level,
-            // then assume it is the default bundle start level, otherwise
-            // parse the specified start level.
-            int startLevel = getInitialBundleStartLevel();
-            if (!key.equals(FelixConstants.AUTO_INSTALL_PROP))
-            {
-                try
-                {
-                    startLevel = Integer.parseInt(key.substring(key.lastIndexOf('.') + 1));
-                }
-                catch (NumberFormatException ex)
-                {
-                    m_logger.log(Logger.LOG_ERROR, "Invalid property: " + key);
-                }
-            }
-
-            StringTokenizer st = new StringTokenizer((String) m_configMap.get(key), "\" ",true);
-            if (st.countTokens() > 0)
-            {
-                String location = null;
-                do
-                {
-                    location = nextLocation(st);
-                    if (location != null)
-                    {
-                        try
-                        {
-                            FelixBundle b = (FelixBundle) installBundle(location, null);
-                            b.getInfo().setStartLevel(startLevel);
-                        }
-                        catch (Exception ex)
-                        {
-                            m_logger.log(
-                                Logger.LOG_ERROR, "Auto-properties install.", ex);
-                        }
-                    }
-                }
-                while (location != null);
-            }
-        }
-
-        // The auto-start property specifies a space-delimited list of
-        // bundle URLs to be automatically installed and started into each
-        // new profile; the start level to which the bundles are assigned
-        // is specified by appending a ".n" to the auto-start property name,
-        // where "n" is the desired start level for the list of bundles.
-        // The following code starts bundles in two passes, first it installs
-        // them, then it starts them.
-        for (Iterator i = m_configMap.keySet().iterator(); i.hasNext(); )
-        {
-            String key = (String) i.next();
-
-            // Ignore all keys that are not the auto-start property.
-            if (!key.startsWith(FelixConstants.AUTO_START_PROP))
-            {
-                continue;
-            }
-
-            // If the auto-start property does not have a start level,
-            // then assume it is the default bundle start level, otherwise
-            // parse the specified start level.
-            int startLevel = getInitialBundleStartLevel();
-            if (!key.equals(FelixConstants.AUTO_START_PROP))
-            {
-                try
-                {
-                    startLevel = Integer.parseInt(key.substring(key.lastIndexOf('.') + 1));
-                }
-                catch (NumberFormatException ex)
-                {
-                    m_logger.log(Logger.LOG_ERROR, "Invalid property: " + key);
-                }
-            }
-
-            StringTokenizer st = new StringTokenizer((String) m_configMap.get(key), "\" ",true);
-            if (st.countTokens() > 0)
-            {
-                String location = null;
-                do
-                {
-                    location = nextLocation(st);
-                    if (location != null)
-                    {
-                        try
-                        {
-                            FelixBundle b = (FelixBundle) installBundle(location, null);
-                            b.getInfo().setStartLevel(startLevel);
-                        }
-                        catch (Exception ex)
-                        {
-                            m_logger.log(Logger.LOG_ERROR, "Auto-properties install.", ex);
-                        }
-                    }
-                }
-                while (location != null);
-            }
-        }
-
-        // Now loop through and start the installed bundles.
-        for (Iterator i = m_configMap.keySet().iterator(); i.hasNext(); )
-        {
-            String key = (String) i.next();
-            if (key.startsWith(FelixConstants.AUTO_START_PROP))
-            {
-                StringTokenizer st = new StringTokenizer((String) m_configMap.get(key), "\" ",true);
-                if (st.countTokens() > 0)
-                {
-                    String location = null;
-                    do
-                    {
-                        location = nextLocation(st);
-                        if (location != null)
-                        {
-                            // Installing twice just returns the same bundle.
-                            try
-                            {
-                                FelixBundle bundle = (FelixBundle) installBundle(location, null);
-                                if (bundle != null)
-                                {
-                                    startBundle(bundle, true);
-                                }
-                            }
-                            catch (Exception ex)
-                            {
-                                m_logger.log(
-                                    Logger.LOG_ERROR, "Auto-properties start.", ex);
-                            }
-                        }
-                    }
-                    while (location != null);
-                }
-            }
-        }
-    }
-
-    private String nextLocation(StringTokenizer st)
-    {
-        String retVal = null;
-
-        if (st.countTokens() > 0)
-        {
-            String tokenList = "\" ";
-            StringBuffer tokBuf = new StringBuffer(10);
-            String tok = null;
-            boolean inQuote = false;
-            boolean tokStarted = false;
-            boolean exit = false;
-            while ((st.hasMoreTokens()) && (!exit))
-            {
-                tok = st.nextToken(tokenList);
-                if (tok.equals("\""))
-                {
-                    inQuote = ! inQuote;
-                    if (inQuote)
-                    {
-                        tokenList = "\"";
-                    }
-                    else
-                    {
-                        tokenList = "\" ";
-                    }
-
-                }
-                else if (tok.equals(" "))
-                {
-                    if (tokStarted)
-                    {
-                        retVal = tokBuf.toString();
-                        tokStarted=false;
-                        tokBuf = new StringBuffer(10);
-                        exit = true;
-                    }
-                }
-                else
-                {
-                    tokStarted = true;
-                    tokBuf.append(tok.trim());
-                }
-            }
-
-            // Handle case where end of token stream and
-            // still got data
-            if ((!exit) && (tokStarted))
-            {
-                retVal = tokBuf.toString();
-            }
-        }
-
-        return retVal;
-    }
-
     //
     // Private utility methods.
     //
@@ -3940,11 +3714,11 @@
             }
 
             // Add the bundle activator for the package admin service.
-            m_activatorList.add(new PackageAdminActivator(Felix.this));
+            m_activatorList.add(0, new PackageAdminActivator(Felix.this));
             // Add the bundle activator for the start level service.
-            m_activatorList.add(new StartLevelActivator(m_logger, Felix.this));
+            m_activatorList.add(0, new StartLevelActivator(m_logger, Felix.this));
             // Add the bundle activator for the url handler service.
-            m_activatorList.add(new URLHandlersActivator(m_configMap, Felix.this));
+            m_activatorList.add(0, new URLHandlersActivator(m_configMap, Felix.this));
 
             // Start all activators.
             for (int i = 0; i < m_activatorList.size(); i++)
@@ -4574,4 +4348,4 @@
             m_bundleLock.notifyAll();
         }
     }
-}
+}
\ No newline at end of file
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 bb5ab86..3c9861a 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,6 @@
 
     // Miscellaneous framework configuration property names.
     public static final String LOG_LEVEL_PROP = "felix.log.level";
-    public static final String AUTO_INSTALL_PROP = "felix.auto.install";
-    public static final String AUTO_START_PROP = "felix.auto.start";
     public static final String EMBEDDED_EXECUTION_PROP = "felix.embedded.execution";
     public static final String STRICT_OSGI_PROP = "felix.strict.osgi";
     public static final String FRAMEWORK_STARTLEVEL_PROP
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 7d4444c..0fea622 100644
--- a/main/src/main/java/org/apache/felix/main/Main.java
+++ b/main/src/main/java/org/apache/felix/main/Main.java
@@ -25,7 +25,12 @@
 
 import org.apache.felix.framework.Felix;
 import org.apache.felix.framework.cache.BundleCache;
+import org.apache.felix.framework.util.FelixConstants;
 import org.apache.felix.framework.util.StringMap;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.startlevel.StartLevel;
 
 /**
  * <p>
@@ -33,15 +38,13 @@
  * intended to be the only way to instantiate and execute the framework; rather, it is
  * one example of how to do so. When embedding the framework in a host application,
  * this class can serve as a simple guide of how to do so. It may even be
- * worthwhile to reuse some of its property handling capabilities. This class
- * is completely static and is only intended to start a single instance of
- * the framework.
+ * worthwhile to reuse some of its property handling capabilities.
  * </p>
 **/
-public class Main
+public class Main implements BundleActivator
 {
     /**
-     * The system property name used to specify an URL to the system
+     * The property name used to specify an URL to the system
      * property file.
     **/
     public static final String SYSTEM_PROPERTIES_PROP = "felix.system.properties";
@@ -50,7 +53,7 @@
     **/
     public static final String SYSTEM_PROPERTIES_FILE_VALUE = "system.properties";
     /**
-     * The system property name used to specify an URL to the configuration
+     * The property name used to specify an URL to the configuration
      * property file to be used for the created the framework instance.
     **/
     public static final String CONFIG_PROPERTIES_PROP = "felix.config.properties";
@@ -62,10 +65,39 @@
      * The default name used for the default configuration properties file.
     **/
     public static final String DEFAULT_PROPERTIES_FILE_VALUE = "default.properties";
+    /**
+     * The property name prefix for the launcher's auto-install property.
+    **/
+    public static final String AUTO_INSTALL_PROP = "felix.auto.install";
+    /**
+     * The property name prefix for the launcher's auto-start property.
+    **/
+    public static final String AUTO_START_PROP = "felix.auto.start";
 
+    private static Properties m_configProps = null;
     private static Felix m_felix = null;
 
     /**
+     * Used to instigate auto-install and auto-start configuration
+     * property processing via a custom framework activator during
+     * framework startup.
+     * @param context The system bundle context.
+    **/
+    public void start(BundleContext context)
+    {
+        Main.processAutoProperties(context);
+    }
+
+    /**
+     * Currently does nothing as part of framework shutdown.
+     * @param context The system bundle context.
+    **/
+    public void stop(BundleContext context)
+    {
+        // Do nothing.
+    }
+
+    /**
      * <p>
      * This method performs the main task of constructing an framework instance
      * and starting its execution. The following functions are performed
@@ -80,7 +112,8 @@
      *       The only properties defined in this file that will impact the framework's
      *       behavior are the those concerning setting HTTP proxies, such as
      *       <tt>http.proxyHost</tt>, <tt>http.proxyPort</tt>, and
-     *       <tt>http.proxyAuth</tt>.
+     *       <tt>http.proxyAuth</tt>. Generally speaking, the framework does
+     *       not use system properties at all.
      *   </li>
      *   <li><i><b>Perform system property variable substitution on system
      *       properties.</b></i> Any system properties in the system property
@@ -122,7 +155,7 @@
      *       <a href="cache/DefaultBundleCache.html"><tt>DefaultBundleCache</tt></a>
      *       documentation for more details its configuration options.
      *   </li>
-     *   <li><i><b>Creates and starts a framework instance.</b></i> A 
+     *   <li><i><b>Creates and starts a framework instance.</b></i> A
      *       case insensitive
      *       <a href="util/StringMap.html"><tt>StringMap</tt></a>
      *       is created for the configuration property file and is passed
@@ -138,30 +171,50 @@
      * interact with the framework are installed or if the configuration property
      * file cannot be found, the framework will appear to be hung or deadlocked.
      * This is not the case, it is executing correctly, there is just no way to
-     * interact with it. Refer to the
-     * <a href="Felix.html#Felix(java.util.Map, java.util.List)">
-     * <tt>Felix</tt></a> constructor documentation for more information on
-     * framework configuration options.
+     * interact with it. The default launcher provides two configuration properties
+     * to help you automatically install and/or start bundles, which are:
+     * </p>
+     * <ul>
+     *   <li><tt>felix.auto.install.&lt;n&gt;</tt> - Space-delimited list of
+     *       bundle URLs to automatically install into start level <tt>n</tt> when
+     *       the framework is started. Append a specific start level to this
+     *       property name to assign the bundles' start level
+     *       (e.g., <tt>felix.auto.install.2</tt>); otherwise, bundles are
+     *       installed into the default bundle start level.
+     *   </li>
+     *   <li><tt>felix.auto.start.&lt;n&gt;</tt> - Space-delimited list of
+     *       bundle URLs to automatically install and start into start level
+     *       <tt>n</tt> when the framework is started. Append a
+     *       specific start level to this property name to assign the
+     *       bundles' start level(e.g., <tt>felix.auto.start.2</tt>); otherwise,
+     *       bundles are installed into the default bundle start level.
+     *   </li>
+     * </ul>
+     * <p>
+     * These properties should be specified in the <tt>config.properties</tt>
+     * so that they can be processed by the launcher during the framework
+     * startup process.
      * </p>
      * @param argv An array of arguments, all of which are ignored.
      * @throws Exception If an error occurs.
     **/
+
     public static void main(String[] argv) throws Exception
     {
         // Load system properties.
         Main.loadSystemProperties();
 
         // Read configuration properties.
-        Properties configProps = Main.loadConfigProperties();
+        m_configProps = Main.loadConfigProperties();
 
         // Copy framework properties from the system properties.
-        Main.copySystemProperties(configProps);
+        Main.copySystemProperties(m_configProps);
 
         // See if the profile name property was specified.
-        String profileName = configProps.getProperty(BundleCache.CACHE_PROFILE_PROP);
+        String profileName = m_configProps.getProperty(BundleCache.CACHE_PROFILE_PROP);
 
         // See if the profile directory property was specified.
-        String profileDirName = configProps.getProperty(BundleCache.CACHE_PROFILE_DIR_PROP);
+        String profileDirName = m_configProps.getProperty(BundleCache.CACHE_PROFILE_DIR_PROP);
 
         // Print welcome banner.
         System.out.println("\nWelcome to Felix.");
@@ -185,7 +238,7 @@
             System.out.println("");
             if (profileName.length() != 0)
             {
-                configProps.setProperty(BundleCache.CACHE_PROFILE_PROP, profileName);
+                m_configProps.setProperty(BundleCache.CACHE_PROFILE_PROP, profileName);
             }
         }
 
@@ -198,8 +251,13 @@
 
         try
         {
+            // Create a list for custom framework activators and
+            // add an instance of Main to process auto-properties.
+            List list = new ArrayList();
+            list.add(new Main());
             // Now create an instance of the framework.
-            m_felix = new Felix(new StringMap(configProps, false), null);
+            Map configMap = new StringMap(m_configProps, false);
+            m_felix = new Felix(configMap, list);
             m_felix.start();
         }
         catch (Exception ex)
@@ -212,6 +270,222 @@
 
     /**
      * <p>
+     * Processes the auto-install and auto-start properties from the
+     * specified configuration properties.
+     */
+    private static void processAutoProperties(BundleContext context)
+    {
+        // Retrieve the Start Level service, since it will be needed
+        // to set the start level of the installed bundles.
+        StartLevel sl = (StartLevel) context.getService(
+            context.getServiceReference(org.osgi.service.startlevel.StartLevel.class.getName()));
+
+        // The auto-install property specifies a space-delimited list of
+        // bundle URLs to be automatically installed into each new profile;
+        // the start level to which the bundles are assigned is specified by
+        // appending a ".n" to the auto-install property name, where "n" is
+        // the desired start level for the list of bundles.
+        for (Iterator i = m_configProps.keySet().iterator(); i.hasNext(); )
+        {
+            String key = (String) i.next();
+
+            // Ignore all keys that are not the auto-install property.
+            if (!key.startsWith(AUTO_INSTALL_PROP))
+            {
+                continue;
+            }
+
+            // If the auto-install property does not have a start level,
+            // then assume it is the default bundle start level, otherwise
+            // parse the specified start level.
+            int startLevel = sl.getInitialBundleStartLevel();
+            if (!key.equals(AUTO_INSTALL_PROP))
+            {
+                try
+                {
+                    startLevel = Integer.parseInt(key.substring(key.lastIndexOf('.') + 1));
+                }
+                catch (NumberFormatException ex)
+                {
+                    System.err.println("Invalid property: " + key);
+                }
+            }
+
+            StringTokenizer st = new StringTokenizer(m_configProps.getProperty(key), "\" ",true);
+            if (st.countTokens() > 0)
+            {
+                String location = null;
+                do
+                {
+                    location = nextLocation(st);
+                    if (location != null)
+                    {
+                        try
+                        {
+                            Bundle b = context.installBundle(location, null);
+                            sl.setBundleStartLevel(b, startLevel);
+                        }
+                        catch (Exception ex)
+                        {
+                            System.err.println("Auto-properties install: " + ex);
+                        }
+                    }
+                }
+                while (location != null);
+            }
+        }
+
+        // The auto-start property specifies a space-delimited list of
+        // bundle URLs to be automatically installed and started into each
+        // new profile; the start level to which the bundles are assigned
+        // is specified by appending a ".n" to the auto-start property name,
+        // where "n" is the desired start level for the list of bundles.
+        // The following code starts bundles in two passes, first it installs
+        // them, then it starts them.
+        for (Iterator i = m_configProps.keySet().iterator(); i.hasNext(); )
+        {
+            String key = (String) i.next();
+
+            // Ignore all keys that are not the auto-start property.
+            if (!key.startsWith(AUTO_START_PROP))
+            {
+                continue;
+            }
+
+            // If the auto-start property does not have a start level,
+            // then assume it is the default bundle start level, otherwise
+            // parse the specified start level.
+            int startLevel = sl.getInitialBundleStartLevel();
+            if (!key.equals(AUTO_START_PROP))
+            {
+                try
+                {
+                    startLevel = Integer.parseInt(key.substring(key.lastIndexOf('.') + 1));
+                }
+                catch (NumberFormatException ex)
+                {
+                    System.err.println("Invalid property: " + key);
+                }
+            }
+
+            StringTokenizer st = new StringTokenizer(m_configProps.getProperty(key), "\" ",true);
+            if (st.countTokens() > 0)
+            {
+                String location = null;
+                do
+                {
+                    location = nextLocation(st);
+                    if (location != null)
+                    {
+                        try
+                        {
+                            Bundle b = context.installBundle(location, null);
+                            sl.setBundleStartLevel(b, startLevel);
+                        }
+                        catch (Exception ex)
+                        {
+                            System.err.println("Auto-properties install:" + ex);
+                        }
+                    }
+                }
+                while (location != null);
+            }
+        }
+
+        // Now loop through and start the installed bundles.
+        for (Iterator i = m_configProps.keySet().iterator(); i.hasNext(); )
+        {
+            String key = (String) i.next();
+            if (key.startsWith(AUTO_START_PROP))
+            {
+                StringTokenizer st = new StringTokenizer(m_configProps.getProperty(key), "\" ",true);
+                if (st.countTokens() > 0)
+                {
+                    String location = null;
+                    do
+                    {
+                        location = nextLocation(st);
+                        if (location != null)
+                        {
+                            // Installing twice just returns the same bundle.
+                            try
+                            {
+                                Bundle b = context.installBundle(location, null);
+                                if (b != null)
+                                {
+                                    b.start();
+                                }
+                            }
+                            catch (Exception ex)
+                            {
+                                System.err.println("Auto-properties start: " + ex);
+                            }
+                        }
+                    }
+                    while (location != null);
+                }
+            }
+        }
+    }
+
+    private static String nextLocation(StringTokenizer st)
+    {
+        String retVal = null;
+
+        if (st.countTokens() > 0)
+        {
+            String tokenList = "\" ";
+            StringBuffer tokBuf = new StringBuffer(10);
+            String tok = null;
+            boolean inQuote = false;
+            boolean tokStarted = false;
+            boolean exit = false;
+            while ((st.hasMoreTokens()) && (!exit))
+            {
+                tok = st.nextToken(tokenList);
+                if (tok.equals("\""))
+                {
+                    inQuote = ! inQuote;
+                    if (inQuote)
+                    {
+                        tokenList = "\"";
+                    }
+                    else
+                    {
+                        tokenList = "\" ";
+                    }
+
+                }
+                else if (tok.equals(" "))
+                {
+                    if (tokStarted)
+                    {
+                        retVal = tokBuf.toString();
+                        tokStarted=false;
+                        tokBuf = new StringBuffer(10);
+                        exit = true;
+                    }
+                }
+                else
+                {
+                    tokStarted = true;
+                    tokBuf.append(tok.trim());
+                }
+            }
+
+            // Handle case where end of token stream and
+            // still got data
+            if ((!exit) && (tokStarted))
+            {
+                retVal = tokBuf.toString();
+            }
+        }
+
+        return retVal;
+    }
+
+    /**
+     * <p>
      * Loads the properties in the system property file associated with the
      * framework installation into <tt>System.setProperty()</tt>. These properties
      * are not directly used by the framework in anyway. By default, the system
@@ -455,7 +729,7 @@
         for (Enumeration e = System.getProperties().propertyNames();
              e.hasMoreElements(); )
         {
-            String key = (String)e.nextElement();
+            String key = (String) e.nextElement();
             if (key.startsWith("felix.") ||
                 key.equals("org.osgi.framework.system.packages") ||
                 key.equals("org.osgi.framework.bootdelegation"))
@@ -588,4 +862,4 @@
         // Return the value.
         return val;
     }
-}
+}
\ No newline at end of file