void updateBundle()

in framework/src/main/java/org/apache/felix/framework/Felix.java [2502:2733]


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

        // We must release the lock and close the input stream, so do both
        // in a finally block.
        try
        {
            // Check if the bundle is not currently STARTING or STOPPING because if it is
            // we are in a loop where the bundle being started or stopped triggered an update
            // of itself (either directly or indirectly) which we can not handle.
            if ((bundle.getState() == Bundle.STARTING && (!bundle.isDeclaredActivationPolicyUsed()
                    || ((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
                    .getDeclaredActivationPolicy() != BundleRevisionImpl.LAZY_ACTIVATION )) ||
                    bundle.getState() == Bundle.STOPPING)
            {
                throw new IllegalStateException("Bundle " + bundle
                    + " cannot be update, since it is either STARTING or STOPPING.");
            }
            
            // Variable to indicate whether bundle is active or not.
            Throwable rethrow = null;

            final int oldState = bundle.getState();

            // First get the update-URL from our header.
            String updateLocation = (String)
                ((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
                    .getHeaders().get(Constants.BUNDLE_UPDATELOCATION);

            // If no update location specified, use original location.
            if (updateLocation == null)
            {
                updateLocation = bundle._getLocation();
            }

            // Stop the bundle if it is active, but do not change its
            // persistent state.
            if (oldState == Bundle.ACTIVE)
            {
                stopBundle(bundle, false);
            }

            try
            {
                // Revising the bundle creates a new revision, which modifies
                // the global state, so we need to acquire the global lock
                // before revising.
                boolean locked = acquireGlobalLock();
                if (!locked)
                {
                    throw new BundleException(
                        "Cannot acquire global lock to update the bundle.");
                }
                try
                {
                    // Try to revise.
                    boolean wasExtension = bundle.isExtension();
                    bundle.revise(updateLocation, is);

                    // Verify bundle revision.
                    try
                    {
                        Object sm = System.getSecurityManager();

                        if (sm != null)
                        {
                            ((SecurityManager) sm).checkPermission(
                                new AdminPermission(bundle, AdminPermission.LIFECYCLE));
                        }

                        // If this is an update from a normal to an extension bundle
                        // then attach the extension
                        if (!wasExtension && bundle.isExtension())
                        {
                            m_extensionManager.addExtensionBundle(bundle);
                        }
                        else if (wasExtension)
                        {
                            setBundleStateAndNotify(bundle, Bundle.INSTALLED);
                        }
                    }
                    catch (Throwable ex)
                    {
                        try
                        {
                            bundle.rollbackRevise();
                        }
                        catch (Exception busted)
                        {
                            m_logger.log(
                                bundle, Logger.LOG_ERROR, "Unable to rollback.", busted);
                        }

                        throw ex;
                    }
                }
                finally
                {
                    // Always release the global lock.
                    releaseGlobalLock();
                }
            }
            catch (Throwable ex)
            {
                m_logger.log(
                    bundle, Logger.LOG_ERROR, "Unable to update the bundle.", ex);
                rethrow = ex;
            }

            // Set new state, mark as needing a refresh, and fire updated event
            // if successful.
            if (rethrow == null)
            {
                bundle.setLastModified(System.currentTimeMillis());

                if (!bundle.isExtension())
                {
                    setBundleStateAndNotify(bundle, Bundle.INSTALLED);
                }

                for (Bundle extension : m_extensionManager.resolveExtensionBundles(this))
                {
                    m_extensionManager.startExtensionBundle(this, (BundleImpl) extension);
                }

                fireBundleEvent(BundleEvent.UNRESOLVED, bundle);

                fireBundleEvent(BundleEvent.UPDATED, bundle);

                // Acquire global lock to check if we should auto-refresh.
                boolean locked = acquireGlobalLock();
                // If we did not get the global lock, then do not try to
                // auto-refresh.
                if (locked)
                {
                    try
                    {
                        if (!m_dependencies.hasDependents(bundle)
                            && !bundle.isExtension())
                        {
                            try
                            {
                                List<Bundle> list = new ArrayList<>(1);
                                list.add(bundle);
                                refreshPackages(list, null);
                            }
                            catch (Exception ex)
                            {
                                m_logger.log(bundle,
                                    Logger.LOG_ERROR,
                                    "Unable to immediately purge the bundle revisions.", ex);
                            }
                        }
                    }
                    finally
                    {
                        // Always release the global lock.
                        releaseGlobalLock();
                    }
                }
            }

            // If the old state was active, but the new revision is a fragment,
            // then mark the persistent state to inactive.
            if ((oldState == Bundle.ACTIVE)
                && Util.isFragment(bundle.adapt(BundleRevision.class)))
            {
                bundle.setPersistentStateInactive();
                m_logger.log(bundle, Logger.LOG_WARNING,
                    "Previously active bundle was updated to a fragment, resetting state to inactive: "
                    + bundle);
            }
            // Otherwise, restart the bundle if it was previously active,
            // but do not change its persistent state.
            else if (oldState == Bundle.ACTIVE)
            {
                startBundle(bundle, Bundle.START_TRANSIENT);
            }

            // If update failed, rethrow exception.
            if (rethrow != null)
            {
                if (rethrow instanceof AccessControlException)
                {
                    throw (AccessControlException) rethrow;
                }
                else if (rethrow instanceof BundleException)
                {
                    throw (BundleException) rethrow;
                }
                else
                {
                    throw new BundleException(
                        "Update of bundle " + bundle + " failed.", rethrow);
                }
            }
        }
        finally
        {
            // Close the input stream.
            try
            {
                if (is != null) is.close();
            }
            catch (Exception ex)
            {
                m_logger.log(bundle, Logger.LOG_ERROR, "Unable to close input stream.", ex);
            }

            // Release bundle lock.
            releaseBundleLock(bundle);
        }
    }