void setActiveStartLevel()

in framework/src/main/java/org/apache/felix/framework/Felix.java [1410:1635]


    void setActiveStartLevel(int requestedLevel, FrameworkListener[] listeners)
    {
        Bundle[] bundles;

        // Record the target start level immediately and use this for
        // comparisons for starting/stopping bundles to avoid race
        // conditions to restart stopped bundles.
        m_targetStartLevel = requestedLevel;

        // Do nothing if the requested start level is the same as the
        // active start level.
        if (m_targetStartLevel != m_activeStartLevel)
        {
            // Synchronization for changing the start level is rather loose.
            // The framework's active start level is volatile, so no lock is
            // needed to access it. No locks are held while processing the
            // currently installed bundles for starting/stopping based on the new
            // active start level. The only locking that occurs is for individual
            // bundles when startBundle()/stopBundle() is called, but this locking
            // is done in the respective method.
            //
            // This approach does mean that it is possible for a for individual
            // bundle states to change during this operation. If a bundle's start
            // level changes, then it is possible for it to be processed out of
            // order. Uninstalled bundles are just logged and ignored.
            //
            // Calls to this method are only made by the start level thread, which
            // serializes framework start level changes. Thus, it is not possible
            // for two requests to change the framework's start level to interfere
            // with each other.

            // Acquire global lock.
            boolean locked = acquireGlobalLock();
            if (!locked)
            {
                // If the calling thread holds bundle locks, then we might not
                // be able to get the global lock.
                throw new IllegalStateException(
                    "Unable to acquire global lock to create bundle snapshot.");
            }

            boolean bundlesRemaining;
            try
            {
                synchronized (m_startLevelBundles)
                {
                    // Get a sorted snapshot of all installed bundles
                    // to be processed during the start level change.
                    // We also snapshot the start level here, since it
                    // may change and we don't want to consider any
                    // changes since they will be queued for the start
                    // level thread.
                    bundles = getBundles();
                    for (Bundle b : bundles)
                    {
                        m_startLevelBundles.add(
                            new StartLevelTuple(
                                (BundleImpl) b,
                                ((BundleImpl) b).getStartLevel(
                                    getInitialBundleStartLevel())));
                    }
                    bundlesRemaining = !m_startLevelBundles.isEmpty();
                }
            }
            finally
            {
                releaseGlobalLock();
            }

            // Determine if we are lowering or raising the
            // active start level.
            boolean isLowering = (m_targetStartLevel < m_activeStartLevel);
            // Determine the range of start levels to process.
            int low = (isLowering) ? m_targetStartLevel + 1 : m_activeStartLevel + 1;
            int high = (isLowering) ? m_activeStartLevel : m_targetStartLevel;
            m_activeStartLevel = (isLowering) ? high : low;

            // Process bundles and stop or start them accordingly.
            while (bundlesRemaining)
            {
                StartLevelTuple tuple;

                // Remove our tuple to be processed while holding the queue lock
                // and update the active start level accordingly, which allows
                // us to determine in startBundle() if concurrent requests to
                // start a bundle should be handled synchronously or just added
                // to the queue and handled asynchronously.
                synchronized (m_startLevelBundles)
                {
                    if (isLowering)
                    {
                        tuple = m_startLevelBundles.last();
                    }
                    else
                    {
                        tuple = m_startLevelBundles.first();
                    }

                    if ((tuple.m_level >= low) && (tuple.m_level <= high))
                    {
                        m_activeStartLevel = tuple.m_level;
                    }
                }

                // Ignore the system bundle, since its start() and
                // stop() methods get called explicitly in Felix.start()
                // and Felix.stop(), respectively.
                if (tuple.m_bundle.getBundleId() != 0)
                {
                    // Lock the current bundle.
                    try
                    {
                        acquireBundleLock(tuple.m_bundle,
                            Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE
                            | Bundle.STARTING | Bundle.STOPPING);
                    }
                    catch (IllegalStateException ex)
                    {
                        // Ignore if the bundle has been uninstalled.
                        if (tuple.m_bundle.getState() != Bundle.UNINSTALLED)
                        {
                            fireFrameworkEvent(FrameworkEvent.ERROR, tuple.m_bundle, ex);
                            m_logger.log(tuple.m_bundle,
                                Logger.LOG_ERROR,
                                "Error locking " + tuple.m_bundle._getLocation(), ex);
                        }
                        else
                        {
                            synchronized (m_startLevelBundles)
                            {
                                m_startLevelBundles.remove(tuple);
                                bundlesRemaining = !m_startLevelBundles.isEmpty();
                            }
                        }
                        continue;
                    }

                    try
                    {
                        // Start the bundle if necessary.
                        // Note that we only attempt to start the bundle if
                        // its start level is equal to the active start level,
                        // which means we assume lower bundles are in the state
                        // they should be in (i.e., we won't attempt to restart
                        // them if they previously failed to start).
                        if (!isLowering
                            && (((tuple.m_bundle.getPersistentState() == Bundle.ACTIVE)
                                || (tuple.m_bundle.getPersistentState() == Bundle.STARTING))
                                && (tuple.m_level == m_activeStartLevel)))
                        {
                            try
                            {
// TODO: LAZY - Not sure if this is the best way...
                                int options = Bundle.START_TRANSIENT;
                                options = (tuple.m_bundle.getPersistentState() == Bundle.STARTING)
                                    ? options | Bundle.START_ACTIVATION_POLICY
                                    : options;
                                startBundle(tuple.m_bundle, options);
                            }
                            catch (Throwable th)
                            {
                                fireFrameworkEvent(FrameworkEvent.ERROR, tuple.m_bundle, th);
                                m_logger.log(tuple.m_bundle,
                                    Logger.LOG_ERROR,
                                    "Error starting " + tuple.m_bundle._getLocation(), th);
                            }
                        }
                        // Stop the bundle if necessary.
                        else if (isLowering
                            && (((tuple.m_bundle.getState() == Bundle.ACTIVE)
                                || (tuple.m_bundle.getState() == Bundle.STARTING))
                                && (tuple.m_level == m_activeStartLevel)))
                        {
                            try
                            {
                                stopBundle(tuple.m_bundle, false);
                            }
                            catch (Throwable th)
                            {
                                fireFrameworkEvent(FrameworkEvent.ERROR, tuple.m_bundle, th);
                                m_logger.log(tuple.m_bundle,
                                    Logger.LOG_ERROR,
                                    "Error stopping " + tuple.m_bundle._getLocation(), th);
                            }
                        }
                    }
                    finally
                    {
                        // Always release bundle lock.
                        releaseBundleLock(tuple.m_bundle);
                    }
                }

                synchronized (m_startLevelBundles)
                {
                    m_startLevelBundles.remove(tuple);
                    bundlesRemaining = !m_startLevelBundles.isEmpty();
                }
            }

            m_activeStartLevel = m_targetStartLevel;
        }

        if (getState() == Bundle.ACTIVE)
        {
            fireFrameworkEvent(FrameworkEvent.STARTLEVEL_CHANGED, this, null);

            if (listeners != null)
            {
                FrameworkEvent event = new FrameworkEvent(
                    FrameworkEvent.STARTLEVEL_CHANGED, this, null);
                for (FrameworkListener l : listeners)
                {
                    try
                    {
                        l.frameworkEvent(event);
                    }
                    catch (Throwable th)
                    {
                        m_logger.log(Logger.LOG_ERROR,
                            "Framework listener delivery error.", th);
                    }
                }
            }
        }
    }