<commit_before>/*
 * #%L
 * JBossOSGi Framework
 * %%
 * Copyright (C) 2010 - 2012 JBoss by Red Hat
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 2.1 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 *
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
 * #L%
 */
package org.jboss.osgi.framework.internal;

import static org.jboss.osgi.framework.FrameworkLogger.LOGGER;
import static org.jboss.osgi.framework.FrameworkMessages.MESSAGES;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import org.jboss.logging.Logger.Level;
import org.jboss.modules.ModuleIdentifier;
import org.jboss.osgi.deployment.deployer.Deployment;
import org.jboss.osgi.deployment.interceptor.LifecycleInterceptorException;
import org.jboss.osgi.framework.Constants;
import org.jboss.osgi.framework.spi.BundleLifecycle;
import org.jboss.osgi.framework.spi.BundleStorage;
import org.jboss.osgi.framework.spi.DeploymentProvider;
import org.jboss.osgi.framework.spi.FrameworkEvents;
import org.jboss.osgi.framework.spi.ServiceState;
import org.jboss.osgi.framework.spi.StartLevelSupport;
import org.jboss.osgi.framework.spi.ModuleManager;
import org.jboss.osgi.framework.spi.StorageState;
import org.jboss.osgi.metadata.ActivationPolicyMetaData;
import org.jboss.osgi.metadata.OSGiMetaData;
import org.jboss.osgi.resolver.XBundle;
import org.jboss.osgi.resolver.XBundleRevision;
import org.jboss.osgi.resolver.XWiringSupport;
import org.jboss.osgi.resolver.spi.AbstractBundleWiring;
import org.jboss.osgi.resolver.spi.ResolverHookException;
import org.jboss.osgi.vfs.AbstractVFS;
import org.jboss.osgi.vfs.VirtualFile;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleException;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.Version;
import org.osgi.framework.hooks.bundle.CollisionHook;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.BundleRevisions;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.resource.Wire;

/**
 * This is the internal implementation of a Bundle based on a user {@link Deployment}.
 *
 * @author <EMAIL>
 * @since 12-Aug-2010
 */
class UserBundleState extends AbstractBundleState<UserBundleRevision> {

    private final AtomicBoolean alreadyStarting = new AtomicBoolean();
    private final AtomicBoolean awaitLazyActivation = new AtomicBoolean();
    private final AtomicInteger revisionIndex = new AtomicInteger();
    private final List<UserBundleRevision> revisions = new ArrayList<UserBundleRevision>();

    private BundleActivator bundleActivator;

    UserBundleState(FrameworkState frameworkState, UserBundleRevision brev) {
        super(frameworkState, brev, brev.getStorageState().getBundleId());
    }

    /**
     * Assert that the given bundle is an instance of {@link UserBundleState}
     */
    static UserBundleState assertBundleState(Bundle bundle) {
        bundle = AbstractBundleState.assertBundleState(bundle);
        assert bundle instanceof UserBundleState : "Not an UserBundleState: " + bundle;
        return (UserBundleState) bundle;
    }

    @Override
    public String getLocation() {
        return getBundleRevision().getLocation();
    }

    Deployment getDeployment() {
        return getBundleRevision().getDeployment();
    }

    @Override
    boolean isSingleton() {
        return getOSGiMetaData().isSingleton();
    }

    void initLazyActivation() {
        if (!isFragment()) {
            awaitLazyActivation.set(isActivationLazy());
        }
    }

    boolean isActivationLazy() {
        ActivationPolicyMetaData activationPolicy = getActivationPolicy();
        String policyType = (activationPolicy != null ? activationPolicy.getType() : null);
        return Constants.ACTIVATION_LAZY.equals(policyType);
    }

    ActivationPolicyMetaData getActivationPolicy() {
        return getOSGiMetaData().getBundleActivationPolicy();
    }

    boolean awaitLazyActivation() {
        return awaitLazyActivation.get();
    }

    void activateLazily() throws BundleException {
        if (awaitLazyActivation.getAndSet(false)) {
            if (startLevelValidForStart() == true) {
                int options = START_TRANSIENT;
                if (isBundleActivationPolicyUsed()) {
                    options |= START_ACTIVATION_POLICY;
                }
                LOGGER.debugf("Lazy activation of: %s", this);
                getBundleManager().startBundle(this, options);
            }
        }
    }

    void setBundleActivationPolicyUsed(boolean usePolicy) {
        StorageState storageState = getStorageState();
        storageState.setBundleActivationPolicyUsed(usePolicy);
    }

    boolean isAlreadyStarting() {
        return alreadyStarting.get();
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T adapt(Class<T> type) {
        T result = super.adapt(type);
        if (result == null) {
            if (type.isAssignableFrom(Deployment.class)) {
                result = (T) getDeployment();
            }
        }
        return result;
    }

    @Override
    public Dictionary<String, String> getHeaders(String locale) {
        // This method must continue to return Manifest header information while this bundle is in the UNINSTALLED state,
        // however the header values must only be available in the raw and default locale values
        Dictionary<String, String> headersOnUninstall = getBundleRevision().getHeadersOnUninstall();
        if (headersOnUninstall != null)
            return headersOnUninstall;

        return super.getHeaders(locale);
    }

    @Override
    BundleRevisions getBundleRevisions() {
        synchronized (revisions) {
            final Bundle bundle = this;
            return new BundleRevisions() {

                @Override
                public Bundle getBundle() {
                    return bundle;
                }

                @Override
                public List<BundleRevision> getRevisions() {
                    List<BundleRevision> bundleRevisions = new ArrayList<BundleRevision>(revisions);
                    return Collections.unmodifiableList(bundleRevisions);
                }

                @Override
                public String toString() {
                    return bundle + ": " + revisions;
                }
            };
        }
    }

    @Override
    void addBundleRevision(UserBundleRevision brev) {
        synchronized (revisions) {
            super.addBundleRevision(brev);
            if (!isFragment()) {
                ModuleManager moduleManager = getFrameworkState().getModuleManager();
                ModuleIdentifier moduleIdentifier = moduleManager.getModuleIdentifier(brev);
                brev.addAttachment(InternalConstants.MODULE_IDENTIFIER_KEY, moduleIdentifier);
            }
            revisionIndex.incrementAndGet();
            revisions.add(0, brev);
        }
    }

    int getRevisionIndex() {
        return revisionIndex.get();
    }

    List<XBundleRevision> getAllBundleRevisions() {
        synchronized (revisions) {
            List<XBundleRevision> result = new ArrayList<XBundleRevision>(revisions);
            return Collections.unmodifiableList(result);
        }
    }

    void clearOldRevisions() {
        synchronized (revisions) {
            UserBundleRevision rev = getBundleRevision();
            revisions.clear();
            revisions.add(rev);
        }
    }

    @Override
    UserBundleRevision getBundleRevisionById(int revisionId) {
        synchronized (revisions) {
            for (UserBundleRevision rev : revisions) {
                if (rev.getRevisionId() == revisionId) {
                    return rev;
                }
            }
            return null;
        }
    }

    @Override
    void updateInternal(InputStream input) throws BundleException {

        LOGGER.debugf("Updating bundle: %s", this);
        BundleLifecycle bundleLifecycle = getFrameworkState().getCoreServices().getBundleLifecycle();

        boolean restart = false;
        if (isFragment() == false) {
            int state = getState();
            if (state == Bundle.ACTIVE || state == Bundle.STARTING || state == Bundle.STOPPING) {
                // If this bundle's state is ACTIVE, STARTING or STOPPING, this bundle is stopped
                // If Bundle.stop throws an exception, the exception is rethrown terminating the update.
                bundleLifecycle.stop(this, Bundle.STOP_TRANSIENT);
                if (state != Bundle.STOPPING)
                    restart = true;
            }
        }

        changeState(Bundle.INSTALLED, BundleEvent.UNRESOLVED);

        // If the Framework is unable to install the updated version of this bundle, the original
        // version of this bundle must be restored and a BundleException must be thrown after
        // completion of the remaining steps.
        UserBundleRevision currentRev = getBundleRevision();
        try {
            // Create the update revision
            createUpdateRevision(input);

            // Make the {@link BundleWiring} for the old {@link BundleRevision} uneffective
            currentRev.getWiringSupport().makeUneffective();
        } catch (BundleException ex) {
            if (restart)
                bundleLifecycle.start(this, Bundle.START_TRANSIENT);
            throw ex;
        } catch (Exception ex) {
            BundleException be = MESSAGES.cannotUpdateBundle(ex, this);
            if (restart)
                bundleLifecycle.start(this, Bundle.START_TRANSIENT);
            throw be;
        }

        FrameworkEvents eventsPlugin = getFrameworkState().getFrameworkEvents();
        eventsPlugin.fireBundleEvent(this, BundleEvent.UPDATED);

        if (restart) {
            // If this bundle's state was originally ACTIVE or STARTING, the updated bundle is started
            // If Bundle.start throws an exception, a Framework event of type FrameworkEvent.ERROR is fired
            try {
                bundleLifecycle.start(this, Bundle.START_TRANSIENT);
            } catch (BundleException e) {
                eventsPlugin.fireFrameworkEvent(this, FrameworkEvent.ERROR, e);
            }
        }

        LOGGER.infoBundleUpdated(this);
        updateLastModified();
    }

    /**
     * Creates a new Bundle Revision when the bundle is updated. Multiple Bundle Revisions can co-exist at the same time.
     *
     * @param input The stream to create the bundle revision from or <tt>null</tt> if the new revision needs to be created from
     *              the same location as where the bundle was initially installed.
     * @throws Exception If the bundle cannot be read, or if the update attempt to change the BSN.
     */
    private UserBundleRevision createUpdateRevision(InputStream input) throws IOException, BundleException {

        String updateLocation = getOSGiMetaData().getHeader(Constants.BUNDLE_UPDATELOCATION);

        // If the specified InputStream is null, the Framework must create the InputStream from
        // which to read the updated bundle by interpreting, in an implementation dependent manner,
        // this bundle's Bundle-UpdateLocation Manifest header, if present, or this bundle's
        // original location.
        VirtualFile rootFile = null;
        if (input == null) {
            if (updateLocation != null) {
                URL updateURL = new URL(updateLocation);
                rootFile = AbstractVFS.toVirtualFile(updateURL);
            } else {
                rootFile = getDeployment().getRoot();
            }
        }

        if (rootFile == null && input != null)
            rootFile = AbstractVFS.toVirtualFile(input);

        BundleStorage storagePlugin = getFrameworkState().getBundleStorage();
        StorageState storageState = createStorageState(storagePlugin, getLocation(), rootFile);
        DeploymentProvider deploymentPlugin = getFrameworkState().getDeploymentProvider();
        Deployment dep = deploymentPlugin.createDeployment(storageState);
        OSGiMetaData metadata = deploymentPlugin.createOSGiMetaData(dep);
        dep.addAttachment(OSGiMetaData.class, metadata);
        dep.addAttachment(Bundle.class, this);
        dep.setBundleUpdate(true);
        dep.setAutoStart(false);

        // Check for symbolic name, version uniqueness
        String symbolicName = metadata.getBundleSymbolicName();
        Version bundleVersion = metadata.getBundleVersion();
        BundleManagerPlugin bundleManager = getBundleManager();
        bundleManager.checkUniqunessPolicy(this, symbolicName, bundleVersion, CollisionHook.UPDATING);

        // Attach a the update location
        if (updateLocation != null)
            dep.addAttachment(Constants.BUNDLE_UPDATELOCATION, updateLocation, String.class);

        BundleContext syscontext = getFrameworkState().getSystemBundle().getBundleContext();
        BundleLifecycle bundleLifecycle = getFrameworkState().getCoreServices().getBundleLifecycle();
        XBundleRevision brev = bundleLifecycle.createBundleRevision(syscontext, dep).getValue();
        return UserBundleRevision.assertBundleRevision(brev);
    }

    private StorageState createStorageState(BundleStorage storagePlugin, String location, VirtualFile rootFile) throws BundleException {
        StorageState storageState;
        try {
            int startlevel = getFrameworkState().getStartLevelSupport().getInitialBundleStartLevel();
            storageState = storagePlugin.createStorageState(getBundleId(), location, startlevel, rootFile);
        } catch (IOException ex) {
            throw MESSAGES.cannotSetupStorage(ex, rootFile);
        }
        return storageState;
    }

    /**
     * This method gets called by when the bundle needs to be refreshed,
     * this means that all the old revisions are thrown out.
     */
    void refresh() throws BundleException {
        assertNotUninstalled();
        if (isResolved() == false)
            throw MESSAGES.illegalStateRefreshUnresolvedBundle(this);

        // Remove the revisions from the environment
        CoreServices coreServices = getFrameworkState().getCoreServices();
        ModuleManager moduleManager = getFrameworkState().getModuleManager();
        BundleLifecycle bundleLifecycle = coreServices.getBundleLifecycle();
        UserBundleRevision currentRev = getBundleRevision();
        for (XBundleRevision brev : getAllBundleRevisions()) {

            if (currentRev != brev) {
                bundleLifecycle.removeBundleRevision(brev);
            }

            if (brev instanceof HostBundleRevision) {

                // Cleanup stale fragment revisions
                HostBundleRevision hostRev = (HostBundleRevision) brev;
                for (FragmentBundleRevision fragRev : hostRev.getAttachedFragments()) {
                    if (fragRev != fragRev.getBundle().getBundleRevision()) {
                        bundleLifecycle.removeBundleRevision(fragRev);
                    }
                }

                // Remove the {@link Module} and the associated ClassLoader
                ModuleIdentifier identifier = brev.getModuleIdentifier();
                moduleManager.removeModule(brev, identifier);
            }
        }

        clearOldRevisions();
        awaitLazyActivation.set(false);
        revisionIndex.set(1);

        FrameworkEvents eventsPlugin = getFrameworkState().getFrameworkEvents();
        eventsPlugin.fireBundleEvent(this, BundleEvent.UNRESOLVED);

        // Update the the current revision
        currentRev.refreshRevision();

        changeState(Bundle.INSTALLED);
    }

    @Override
    void uninstallInternal(int options) {

        int state = getState();
        if (state == Bundle.UNINSTALLED)
            return;

        getBundleRevision().setHeadersOnUninstall(getHeaders(null));

        // #2 If the bundle's state is ACTIVE, STARTING or STOPPING, the bundle is stopped
        if (isFragment() == false) {
            if (state == Bundle.ACTIVE || state == Bundle.STARTING || state == Bundle.STOPPING) {
                try {
                    stopInternal(options);
                } catch (Exception ex) {
                    // If Bundle.stop throws an exception, a Framework event of type FrameworkEvent.ERROR is fired
                    getBundleManager().fireFrameworkError(this, "stopping bundle: " + this, ex);
                }
            }
        }

        // Check if the bundle has still active wires
        boolean activeWires = hasActiveWiresWhileUninstalling();
        if (activeWires == false) {
            getBundleManager().unresolveBundle(this);
        }

        // Make the current {@link BundleWiring} uneffective and fire the UNRESOLVED event
        getBundleRevision().getWiringSupport().makeUneffective();
        FrameworkEvents eventsPlugin = getFrameworkState().getFrameworkEvents();
        eventsPlugin.fireBundleEvent(this, BundleEvent.UNRESOLVED);

        // #3 This bundle's state is set to UNINSTALLED
        changeState(Bundle.UNINSTALLED, 0);

        // #5 This bundle and any persistent storage area provided for this bundle by the Framework are removed
        if (activeWires == false) {
            getBundleManager().removeBundle(this, options);
        }

        // Remove other uninstalled bundles that now also are not in use any more
        Set<XBundle> uninstalled = getBundleManager().getBundles(Bundle.UNINSTALLED);
        for (XBundle auxState : uninstalled) {
            if (auxState != this) {
                boolean bundleInUse = false;
                XBundleRevision brev = auxState.getBundleRevision();
                XWiringSupport wiringSupport = brev.getWiringSupport();
                if (!wiringSupport.isEffective()) {
                    BundleWiring bwiring = (BundleWiring) wiringSupport.getWiring(false);
                    if (bwiring != null && bwiring.isInUse()) {
                        bundleInUse = true;
                        break;
                    }
                }
                if (bundleInUse == false) {
                    UserBundleState auxUser = UserBundleState.assertBundleState(auxState);
                    getBundleManager().unresolveBundle(auxUser);
                    getBundleManager().removeBundle(auxUser, options);<commit_msg>Always unresolve bundle through BundleManager<commit_after>/*
 * #%L
 * JBossOSGi Framework
 * %%
 * Copyright (C) 2010 - 2012 JBoss by Red Hat
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 2.1 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 *
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
 * #L%
 */
package org.jboss.osgi.framework.internal;

import static org.jboss.osgi.framework.FrameworkLogger.LOGGER;
import static org.jboss.osgi.framework.FrameworkMessages.MESSAGES;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import org.jboss.logging.Logger.Level;
import org.jboss.modules.ModuleIdentifier;
import org.jboss.osgi.deployment.deployer.Deployment;
import org.jboss.osgi.deployment.interceptor.LifecycleInterceptorException;
import org.jboss.osgi.framework.Constants;
import org.jboss.osgi.framework.spi.BundleLifecycle;
import org.jboss.osgi.framework.spi.BundleStorage;
import org.jboss.osgi.framework.spi.DeploymentProvider;
import org.jboss.osgi.framework.spi.FrameworkEvents;
import org.jboss.osgi.framework.spi.ServiceState;
import org.jboss.osgi.framework.spi.StartLevelSupport;
import org.jboss.osgi.framework.spi.ModuleManager;
import org.jboss.osgi.framework.spi.StorageState;
import org.jboss.osgi.metadata.ActivationPolicyMetaData;
import org.jboss.osgi.metadata.OSGiMetaData;
import org.jboss.osgi.resolver.XBundle;
import org.jboss.osgi.resolver.XBundleRevision;
import org.jboss.osgi.resolver.XWiringSupport;
import org.jboss.osgi.resolver.spi.AbstractBundleWiring;
import org.jboss.osgi.resolver.spi.ResolverHookException;
import org.jboss.osgi.vfs.AbstractVFS;
import org.jboss.osgi.vfs.VirtualFile;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleException;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.Version;
import org.osgi.framework.hooks.bundle.CollisionHook;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.BundleRevisions;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.resource.Wire;

/**
 * This is the internal implementation of a Bundle based on a user {@link Deployment}.
 *
 * @author <EMAIL>
 * @since 12-Aug-2010
 */
class UserBundleState extends AbstractBundleState<UserBundleRevision> {

    private final AtomicBoolean alreadyStarting = new AtomicBoolean();
    private final AtomicBoolean awaitLazyActivation = new AtomicBoolean();
    private final AtomicInteger revisionIndex = new AtomicInteger();
    private final List<UserBundleRevision> revisions = new ArrayList<UserBundleRevision>();

    private BundleActivator bundleActivator;

    UserBundleState(FrameworkState frameworkState, UserBundleRevision brev) {
        super(frameworkState, brev, brev.getStorageState().getBundleId());
    }

    /**
     * Assert that the given bundle is an instance of {@link UserBundleState}
     */
    static UserBundleState assertBundleState(Bundle bundle) {
        bundle = AbstractBundleState.assertBundleState(bundle);
        assert bundle instanceof UserBundleState : "Not an UserBundleState: " + bundle;
        return (UserBundleState) bundle;
    }

    @Override
    public String getLocation() {
        return getBundleRevision().getLocation();
    }

    Deployment getDeployment() {
        return getBundleRevision().getDeployment();
    }

    @Override
    boolean isSingleton() {
        return getOSGiMetaData().isSingleton();
    }

    void initLazyActivation() {
        if (!isFragment()) {
            awaitLazyActivation.set(isActivationLazy());
        }
    }

    boolean isActivationLazy() {
        ActivationPolicyMetaData activationPolicy = getActivationPolicy();
        String policyType = (activationPolicy != null ? activationPolicy.getType() : null);
        return Constants.ACTIVATION_LAZY.equals(policyType);
    }

    ActivationPolicyMetaData getActivationPolicy() {
        return getOSGiMetaData().getBundleActivationPolicy();
    }

    boolean awaitLazyActivation() {
        return awaitLazyActivation.get();
    }

    void activateLazily() throws BundleException {
        if (awaitLazyActivation.getAndSet(false)) {
            if (startLevelValidForStart() == true) {
                int options = START_TRANSIENT;
                if (isBundleActivationPolicyUsed()) {
                    options |= START_ACTIVATION_POLICY;
                }
                LOGGER.debugf("Lazy activation of: %s", this);
                getBundleManager().startBundle(this, options);
            }
        }
    }

    void setBundleActivationPolicyUsed(boolean usePolicy) {
        StorageState storageState = getStorageState();
        storageState.setBundleActivationPolicyUsed(usePolicy);
    }

    boolean isAlreadyStarting() {
        return alreadyStarting.get();
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T adapt(Class<T> type) {
        T result = super.adapt(type);
        if (result == null) {
            if (type.isAssignableFrom(Deployment.class)) {
                result = (T) getDeployment();
            }
        }
        return result;
    }

    @Override
    public Dictionary<String, String> getHeaders(String locale) {
        // This method must continue to return Manifest header information while this bundle is in the UNINSTALLED state,
        // however the header values must only be available in the raw and default locale values
        Dictionary<String, String> headersOnUninstall = getBundleRevision().getHeadersOnUninstall();
        if (headersOnUninstall != null)
            return headersOnUninstall;

        return super.getHeaders(locale);
    }

    @Override
    BundleRevisions getBundleRevisions() {
        synchronized (revisions) {
            final Bundle bundle = this;
            return new BundleRevisions() {

                @Override
                public Bundle getBundle() {
                    return bundle;
                }

                @Override
                public List<BundleRevision> getRevisions() {
                    List<BundleRevision> bundleRevisions = new ArrayList<BundleRevision>(revisions);
                    return Collections.unmodifiableList(bundleRevisions);
                }

                @Override
                public String toString() {
                    return bundle + ": " + revisions;
                }
            };
        }
    }

    @Override
    void addBundleRevision(UserBundleRevision brev) {
        synchronized (revisions) {
            super.addBundleRevision(brev);
            if (!isFragment()) {
                ModuleManager moduleManager = getFrameworkState().getModuleManager();
                ModuleIdentifier moduleIdentifier = moduleManager.getModuleIdentifier(brev);
                brev.addAttachment(InternalConstants.MODULE_IDENTIFIER_KEY, moduleIdentifier);
            }
            revisionIndex.incrementAndGet();
            revisions.add(0, brev);
        }
    }

    int getRevisionIndex() {
        return revisionIndex.get();
    }

    List<XBundleRevision> getAllBundleRevisions() {
        synchronized (revisions) {
            List<XBundleRevision> result = new ArrayList<XBundleRevision>(revisions);
            return Collections.unmodifiableList(result);
        }
    }

    void clearOldRevisions() {
        synchronized (revisions) {
            UserBundleRevision rev = getBundleRevision();
            revisions.clear();
            revisions.add(rev);
        }
    }

    @Override
    UserBundleRevision getBundleRevisionById(int revisionId) {
        synchronized (revisions) {
            for (UserBundleRevision rev : revisions) {
                if (rev.getRevisionId() == revisionId) {
                    return rev;
                }
            }
            return null;
        }
    }

    @Override
    void updateInternal(InputStream input) throws BundleException {

        LOGGER.debugf("Updating bundle: %s", this);
        BundleLifecycle bundleLifecycle = getFrameworkState().getCoreServices().getBundleLifecycle();

        boolean restart = false;
        if (isFragment() == false) {
            int state = getState();
            if (state == Bundle.ACTIVE || state == Bundle.STARTING || state == Bundle.STOPPING) {
                // If this bundle's state is ACTIVE, STARTING or STOPPING, this bundle is stopped
                // If Bundle.stop throws an exception, the exception is rethrown terminating the update.
                bundleLifecycle.stop(this, Bundle.STOP_TRANSIENT);
                if (state != Bundle.STOPPING)
                    restart = true;
            }
        }

        changeState(Bundle.INSTALLED, BundleEvent.UNRESOLVED);

        // If the Framework is unable to install the updated version of this bundle, the original
        // version of this bundle must be restored and a BundleException must be thrown after
        // completion of the remaining steps.
        UserBundleRevision currentRev = getBundleRevision();
        try {
            // Create the update revision
            createUpdateRevision(input);

            // Make the {@link BundleWiring} for the old {@link BundleRevision} uneffective
            currentRev.getWiringSupport().makeUneffective();
        } catch (BundleException ex) {
            if (restart)
                bundleLifecycle.start(this, Bundle.START_TRANSIENT);
            throw ex;
        } catch (Exception ex) {
            BundleException be = MESSAGES.cannotUpdateBundle(ex, this);
            if (restart)
                bundleLifecycle.start(this, Bundle.START_TRANSIENT);
            throw be;
        }

        FrameworkEvents eventsPlugin = getFrameworkState().getFrameworkEvents();
        eventsPlugin.fireBundleEvent(this, BundleEvent.UPDATED);

        if (restart) {
            // If this bundle's state was originally ACTIVE or STARTING, the updated bundle is started
            // If Bundle.start throws an exception, a Framework event of type FrameworkEvent.ERROR is fired
            try {
                bundleLifecycle.start(this, Bundle.START_TRANSIENT);
            } catch (BundleException e) {
                eventsPlugin.fireFrameworkEvent(this, FrameworkEvent.ERROR, e);
            }
        }

        LOGGER.infoBundleUpdated(this);
        updateLastModified();
    }

    /**
     * Creates a new Bundle Revision when the bundle is updated. Multiple Bundle Revisions can co-exist at the same time.
     *
     * @param input The stream to create the bundle revision from or <tt>null</tt> if the new revision needs to be created from
     *              the same location as where the bundle was initially installed.
     * @throws Exception If the bundle cannot be read, or if the update attempt to change the BSN.
     */
    private UserBundleRevision createUpdateRevision(InputStream input) throws IOException, BundleException {

        String updateLocation = getOSGiMetaData().getHeader(Constants.BUNDLE_UPDATELOCATION);

        // If the specified InputStream is null, the Framework must create the InputStream from
        // which to read the updated bundle by interpreting, in an implementation dependent manner,
        // this bundle's Bundle-UpdateLocation Manifest header, if present, or this bundle's
        // original location.
        VirtualFile rootFile = null;
        if (input == null) {
            if (updateLocation != null) {
                URL updateURL = new URL(updateLocation);
                rootFile = AbstractVFS.toVirtualFile(updateURL);
            } else {
                rootFile = getDeployment().getRoot();
            }
        }

        if (rootFile == null && input != null)
            rootFile = AbstractVFS.toVirtualFile(input);

        BundleStorage storagePlugin = getFrameworkState().getBundleStorage();
        StorageState storageState = createStorageState(storagePlugin, getLocation(), rootFile);
        DeploymentProvider deploymentPlugin = getFrameworkState().getDeploymentProvider();
        Deployment dep = deploymentPlugin.createDeployment(storageState);
        OSGiMetaData metadata = deploymentPlugin.createOSGiMetaData(dep);
        dep.addAttachment(OSGiMetaData.class, metadata);
        dep.addAttachment(Bundle.class, this);
        dep.setBundleUpdate(true);
        dep.setAutoStart(false);

        // Check for symbolic name, version uniqueness
        String symbolicName = metadata.getBundleSymbolicName();
        Version bundleVersion = metadata.getBundleVersion();
        BundleManagerPlugin bundleManager = getBundleManager();
        bundleManager.checkUniqunessPolicy(this, symbolicName, bundleVersion, CollisionHook.UPDATING);

        // Attach a the update location
        if (updateLocation != null)
            dep.addAttachment(Constants.BUNDLE_UPDATELOCATION, updateLocation, String.class);

        BundleContext syscontext = getFrameworkState().getSystemBundle().getBundleContext();
        BundleLifecycle bundleLifecycle = getFrameworkState().getCoreServices().getBundleLifecycle();
        XBundleRevision brev = bundleLifecycle.createBundleRevision(syscontext, dep).getValue();
        return UserBundleRevision.assertBundleRevision(brev);
    }

    private StorageState createStorageState(BundleStorage storagePlugin, String location, VirtualFile rootFile) throws BundleException {
        StorageState storageState;
        try {
            int startlevel = getFrameworkState().getStartLevelSupport().getInitialBundleStartLevel();
            storageState = storagePlugin.createStorageState(getBundleId(), location, startlevel, rootFile);
        } catch (IOException ex) {
            throw MESSAGES.cannotSetupStorage(ex, rootFile);
        }
        return storageState;
    }

    /**
     * This method gets called by when the bundle needs to be refreshed,
     * this means that all the old revisions are thrown out.
     */
    void refresh() throws BundleException {
        assertNotUninstalled();
        if (isResolved() == false)
            throw MESSAGES.illegalStateRefreshUnresolvedBundle(this);

        // Remove the {@link Module} and the associated ClassLoader
        getBundleManager().unresolveBundle(this);

        // Remove the revisions from the environment
        CoreServices coreServices = getFrameworkState().getCoreServices();
        BundleLifecycle bundleLifecycle = coreServices.getBundleLifecycle();
        UserBundleRevision currentRev = getBundleRevision();
        for (XBundleRevision brev : getAllBundleRevisions()) {

            if (currentRev != brev) {
                bundleLifecycle.removeBundleRevision(brev);
            }

            // Cleanup stale fragment revisions
            if (brev instanceof HostBundleRevision) {
                HostBundleRevision hostRev = (HostBundleRevision) brev;
                for (FragmentBundleRevision fragRev : hostRev.getAttachedFragments()) {
                    if (fragRev != fragRev.getBundle().getBundleRevision()) {
                        bundleLifecycle.removeBundleRevision(fragRev);
                    }
                }
            }
        }

        clearOldRevisions();
        awaitLazyActivation.set(false);
        revisionIndex.set(1);

        FrameworkEvents eventsPlugin = getFrameworkState().getFrameworkEvents();
        eventsPlugin.fireBundleEvent(this, BundleEvent.UNRESOLVED);

        // Update the the current revision
        currentRev.refreshRevision();

        changeState(Bundle.INSTALLED);
    }

    @Override
    void uninstallInternal(int options) {

        int state = getState();
        if (state == Bundle.UNINSTALLED)
            return;

        BundleManagerPlugin bundleManager = getBundleManager();
        getBundleRevision().setHeadersOnUninstall(getHeaders(null));

        // #2 If the bundle's state is ACTIVE, STARTING or STOPPING, the bundle is stopped
        if (isFragment() == false) {
            if (state == Bundle.ACTIVE || state == Bundle.STARTING || state == Bundle.STOPPING) {
                try {
                    stopInternal(options);
                } catch (Exception ex) {
                    // If Bundle.stop throws an exception, a Framework event of type FrameworkEvent.ERROR is fired
                    bundleManager.fireFrameworkError(this, "stopping bundle: " + this, ex);
                }
            }
        }

        // Check if the bundle has still active wires
        boolean activeWires = hasActiveWiresWhileUninstalling();
        if (activeWires == false) {
            bundleManager.unresolveBundle(this);
        }

        // Make the current {@link BundleWiring} uneffective and fire the UNRESOLVED event
        getBundleRevision().getWiringSupport().makeUneffective();
        FrameworkEvents eventsPlugin = getFrameworkState().getFrameworkEvents();
        eventsPlugin.fireBundleEvent(this, BundleEvent.UNRESOLVED);

        // #3 This bundle's state is set to UNINSTALLED
        changeState(Bundle.UNINSTALLED, 0);

        // #5 This bundle and any persistent storage area provided for this bundle by the Framework are removed
        if (activeWires == false) {
            bundleManager.removeBundle(this, options);
        }

        // Remove other uninstalled bundles that now also are not in use any more
        Set<XBundle> uninstalled = bundleManager.getBundles(Bundle.UNINSTALLED);
        for (XBundle auxState : uninstalled) {
            if (auxState != this) {
                boolean bundleInUse = false;
                XBundleRevision brev = auxState.getBundleRevision();
                XWiringSupport wiringSupport = brev.getWiringSupport();
                if (!wiringSupport.isEffective()) {
                    BundleWiring bwiring = (BundleWiring) wiringSupport.getWiring(false);
                    if (bwiring != null && bwiring.isInUse()) {
                        bundleInUse = true;
                        break;
                    }
                }
                if (bundleInUse == false) {
                    UserBundleState auxUser = UserBundleState.assertBundleState(auxState);
                    bundleManager.unresolveBundle(auxUser);
                    bundleManager.removeBundle(auxUser, options);
{'fraction_non_alphanumeric': 0.0845074090471106, 'fraction_numerical': 0.0012500325529310658, 'mean_word_length': 2.593150556751193, 'pattern_counts': {'":': 2, '<': 39, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6962835', 'n_tokens_mistral': 10096, 'n_tokens_neox': 9454, 'n_words': 2479}