void startBundle()

in framework/src/main/java/org/apache/felix/framework/Felix.java [2123:2371]


    void startBundle(BundleImpl bundle, int options) throws BundleException
    {
        // CONCURRENCY NOTE:
        // We will first acquire the bundle lock for the specific bundle
        // as long as the bundle is INSTALLED, RESOLVED, or ACTIVE. If this
        // bundle is not yet resolved, then it will be resolved too. In
        // that case, the global lock will be acquired to make sure no
        // bundles can be installed or uninstalled during the resolve.

        int eventType;
        boolean isTransient = (options & Bundle.START_TRANSIENT) != 0;

        // Acquire bundle lock.
        try
        {
            acquireBundleLock(bundle,
                Bundle.INSTALLED | Bundle.RESOLVED | Bundle.STARTING | Bundle.ACTIVE | Bundle.STOPPING);
        }
        catch (IllegalStateException ex)
        {
            if (bundle.getState() == Bundle.UNINSTALLED)
            {
                throw new IllegalStateException("Cannot start an uninstalled bundle.");
            }
            else
            {
                throw new BundleException(
                    "Bundle " + bundle
                    + " cannot be started: " + ex.getMessage());
            }
        }

        // Record whether the bundle is using its declared activation policy.
        boolean wasDeferred = bundle.isDeclaredActivationPolicyUsed()
            && (((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
                .getDeclaredActivationPolicy() == BundleRevisionImpl.LAZY_ACTIVATION);
        bundle.setDeclaredActivationPolicyUsed(
            (options & Bundle.START_ACTIVATION_POLICY) != 0);

        BundleException rethrow = null;
        try
        {
            // The spec doesn't say whether it is possible to start an extension
            // We just do nothing
            if (bundle.isExtension())
            {
                return;
            }

            // As per the OSGi spec, fragment bundles can not be started and must
            // throw a BundleException when there is an attempt to start one.
            if (Util.isFragment(bundle.adapt(BundleRevision.class)))
            {
                throw new BundleException("Fragment bundles can not be started.");
            }

            // Set and save the bundle's persistent state to active
            // if we are supposed to record state change.
            if (!isTransient)
            {
                if ((options & Bundle.START_ACTIVATION_POLICY) != 0)
                {
                    bundle.setPersistentStateStarting();
                }
                else
                {
                    bundle.setPersistentStateActive();
                }
            }

            // Check to see if the bundle's start level is greater than the
            // the framework's target start level and if so just return. For
            // transient starts we compare their start level to the current
            // active start level, since we never process transient starts
            // asynchronously (i.e., they are either satisfied and process
            // synchronously here or they throw an exception).
            int bundleLevel = bundle.getStartLevel(getInitialBundleStartLevel());
            if (isTransient && (bundleLevel > m_activeStartLevel))
            {
                // Throw an exception for transient starts.
                throw new BundleException(
                    "Cannot start bundle " + bundle + " because its start level is "
                    + bundleLevel
                    + ", which is greater than the framework's start level of "
                    + m_activeStartLevel + ".", BundleException.START_TRANSIENT_ERROR);
            }
            else if (bundleLevel > m_targetStartLevel)
            {
                // Ignore persistent starts that are not satisfied.
                return;
            }

            // Check to see if there is a start level change in progress and if
            // so queue this bundle to the start level bundle queue for the start
            // level thread and return, except for transient starts which are
            // queued but processed synchronously.
            // Note: Don't queue starts from the start level thread, otherwise
            // we'd never get anything started.
            if (!Thread.currentThread().getName().equals(FrameworkStartLevelImpl.THREAD_NAME))
            {
                synchronized (m_startLevelBundles)
                {
                    // Since we have the start level queue lock, we know the
                    // active start level cannot change. For transient starts
                    // we now need to double check and make sure we can still
                    // start them since technically the active start level could
                    // have changed.
                    if (isTransient && (bundleLevel > m_activeStartLevel))
                    {
                        throw new BundleException(
                            "Cannot start bundle " + bundle + " because its start level is "
                            + bundleLevel
                            + ", which is greater than the framework's start level of "
                            + m_activeStartLevel + ".", BundleException.START_TRANSIENT_ERROR);
                    }

                    // If the start level bundle queue is not empty, then we know
                    // there is a start level operation ongoing. We know that the
                    // bundle being started is satisfied by the target start level
                    // otherwise we wouldn't be here. In most cases we simply want
                    // to queue the bundle; however, if the bundle level is lower
                    // than the current active start level, then we want to handle
                    // it synchronously. This is because the start level thread
                    // ignores bundles that it should have already processed.
                    // So queue the bundle if its bundle start level is greater
                    // or equal to the active start level and then simply return
                    // since it will be processed asynchronously.
                    if (!m_startLevelBundles.isEmpty()
                        && (bundleLevel >= m_activeStartLevel))
                    {
                        // Only add the bundle to the start level bundles
                        // being process if it is not already there.
                        boolean found = false;
                        for (StartLevelTuple tuple : m_startLevelBundles)
                        {
                            if (tuple.m_bundle == bundle)
                            {
                                found = true;
                            }
                        }

                        if (!found)
                        {
                            m_startLevelBundles.add(new StartLevelTuple(bundle, bundleLevel));
                        }

                        // Note that although we queued the transiently started
                        // bundle, we don't return here because we handle all
                        // transient bundles synchronously. The reason why we
                        // queue it anyway is for the case where the start level
                        // is lowering, since the transiently started bundle may
                        // have already been processed by the start level thread
                        // and we will start it briefly here synchronously, but
                        // we want the start level thread to process it again
                        // so it gets another chance to stop it. This is not an
                        // issue when the start level is raising because the
                        // start level thread ignores non-persistently started
                        // bundles or if it is also persistently started it will
                        // be a no-op.
                        if (!isTransient)
                        {
                            return;
                        }
                    }
                }
            }

            switch (bundle.getState())
            {
                case Bundle.UNINSTALLED:
                    throw new IllegalStateException("Cannot start an uninstalled bundle.");
                case Bundle.STARTING:
                    if (!wasDeferred)
                    {
                        throw new BundleException(
                            "Bundle " + bundle
                            + " cannot be started, since it is starting.");
                    }
                    break;
                case Bundle.STOPPING:
                    throw new BundleException(
                        "Bundle " + bundle
                        + " cannot be started, since it is stopping.");
                case Bundle.ACTIVE:
                    return;
                case Bundle.INSTALLED:
                    resolveBundleRevision(bundle.adapt(BundleRevision.class));
                    // No break.
                case Bundle.RESOLVED:
                    // Set the bundle's context.
                    bundle.setBundleContext(new BundleContextImpl(m_logger, this, bundle));
                    // At this point, no matter if the bundle's activation policy is
                    // eager or deferred, we need to set the bundle's state to STARTING.
                    // We don't fire a BundleEvent here for this state change, since
                    // STARTING events are only fired if we are invoking the activator,
                    // which we may not do if activation is deferred.
                    setBundleStateAndNotify(bundle, Bundle.STARTING);
                    break;
            }

            // If the bundle's activation policy is eager or activation has already
            // been triggered, then activate the bundle immediately.
            if (!bundle.isDeclaredActivationPolicyUsed()
                || (((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
                    .getDeclaredActivationPolicy() != BundleRevisionImpl.LAZY_ACTIVATION)
                || ((BundleClassLoader) bundle.adapt(BundleWiring.class).getClassLoader())
                    .isActivationTriggered())
            {
                // Record the event type for the final event and activate.
                eventType = BundleEvent.STARTED;
                // Note that the STARTING event is thrown in the activateBundle() method.
                try
                {
                    activateBundle(bundle, false);
                }
                catch (BundleException ex)
                {
                    rethrow = ex;
                }
            }
            // Otherwise, defer bundle activation.
            else
            {
                // Record the event type for the final event.
                eventType = BundleEvent.LAZY_ACTIVATION;
            }

            // We still need to fire the STARTED event, but we will do
            // it later so we can release the bundle lock.
        }
        finally
        {
            // Release bundle lock.
            releaseBundleLock(bundle);
        }

        // If there was no exception, then we should fire the STARTED
        // or LAZY_ACTIVATION event here without holding the lock. Otherwise,
        // fire STOPPED and rethrow exception.
        if (rethrow == null)
        {
            fireBundleEvent(eventType, bundle);
        }
        else
        {
            fireBundleEvent(BundleEvent.STOPPED, bundle);
            throw rethrow;
        }
    }