You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@felix.apache.org by "Peter Kriens (Jira)" <ji...@apache.org> on 2022/12/12 11:15:00 UTC

[jira] [Created] (FELIX-6586) Invalid startlevel ordering when startlevels are changed in Bundle Activator

Peter Kriens created FELIX-6586:
-----------------------------------

             Summary: Invalid startlevel ordering when startlevels are changed in Bundle Activator
                 Key: FELIX-6586
                 URL: https://issues.apache.org/jira/browse/FELIX-6586
             Project: Felix
          Issue Type: Bug
          Components: Framework
    Affects Versions: framework-6.0.2
            Reporter: Peter Kriens


When a bundle A's activator modifies the start level of a bundle B that has an adjacent startlevel to a higher level, then the FrameworkStartLevelImpl class will still try to start the bundle B and this will throw an error because B's startlevel is now higher than the current start level.

Example.
 # startlevel(A) == 10, startlevel(B)=11.
 # A.start sets startlevel(B)==30
 # Framework Error thrown that B cannot be started

{code:java}
ERROR: Bundle qivicon.felix.TestStartLevels.testFailingToSetStartLevelBecauseChanged-2 [2] Error starting {qivicon.felix.TestStartLevels.testFailingToSetStartLevelBecauseChanged-2={}}-0 (org.osgi.framework.BundleException: Cannot start bundle qivicon.felix.TestStartLevels.testFailingToSetStartLevelBecauseChanged-2 [2] because its start level is 30, which is greater than the framework's start level of 11.)
org.osgi.framework.BundleException: Cannot start bundle qivicon.felix.TestStartLevels.testFailingToSetStartLevelBecauseChanged-2 [2] because its start level is 30, which is greater than the framework's start level of 11.
at org.apache.felix.framework.Felix.startBundle(Felix.java:2175)
at org.apache.felix.framework.Felix.setActiveStartLevel(Felix.java:1539)
at org.apache.felix.framework.FrameworkStartLevelImpl.run(FrameworkStartLevelImpl.java:308)
at java.lang.Thread.run(Thread.java:750){code}
The framework will start bundle B in the end after some delay.

The error does not occur if the startlevel(B) is not adjacent. For example, if it is set to 12, then the error will no occur.

The following is the test code that I used to verify the bug:

 
{code:java}
package qivicon.felix;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.concurrent.Semaphore;
import org.junit.Test;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.launch.Framework;
import org.osgi.framework.startlevel.BundleStartLevel;
import org.osgi.framework.startlevel.FrameworkStartLevel;
import aQute.launchpad.Launchpad;
import aQute.launchpad.LaunchpadBuilder;
public class TestStartLevels {
    final static LaunchpadBuilder    builder    = new LaunchpadBuilder().runfw("org.apache.felix.framework")
            .set("felix.log.level ", "4");
    private BundleContext            context;
    public static class ChangeSL implements BundleActivator{
        @Override
        public void start(BundleContext context) throws Exception {
            System.out.println("In BundleActivator.start: Changing start level of bundles with sl=11 to sl=30");
            for ( Bundle b : context.getBundles()) {
                BundleStartLevel sl = b.adapt(BundleStartLevel.class);
                int startLevel = sl.getStartLevel();
                if ( startLevel == 11)
                    sl.setStartLevel(30);
            }
        }
        @Override
        public void stop(BundleContext context) throws Exception {
            // TODO Auto-generated method stub
            
        }
        
    }
    @Test
    public void testFailingToSetStartLevelBecauseChanged() throws Exception {
        try (Launchpad lp = builder.nostart().create()) {
            context = lp.getFramework().getBundleContext();
            context.addFrameworkListener(fe -> event(fe, "for errors"));
            
            Bundle changer = lp.bundle().bundleActivator(ChangeSL.class).start();
            Bundle victim = lp.bundle().start();            
            setStartLevel(changer, 10);
            setStartLevel(victim, 11);
            Framework framework = lp.getFramework();
            FrameworkStartLevel fsl = framework.adapt(FrameworkStartLevel.class);
            fsl.setStartLevel(1, fe -> event(fe, "setting FW startLevel=1"));
            System.out.println("starting framework up to level 1");
            lp.start();
            Semaphore s = new Semaphore(0);
            System.out.println("reached 1, setting fw startlevel to 1000");
            fsl.setStartLevel(1000, fe -> {
                s.release();
                event(fe, "reached startlevel 1000");
            });
            s.acquire();
            System.out.print("the bundle is actually not started immediately: " );
             bundle(victim);
            assertThat(victim.getState()).isNotEqualTo(Bundle.ACTIVE);
            Thread.sleep(100);
            System.out.print("but a bit later it is " );
             bundle(victim);
            assertThat(victim.getState()).isEqualTo(Bundle.ACTIVE);
        }
    }
    private void bundle(Bundle b) {
        BundleStartLevel sl = b.adapt(BundleStartLevel.class);
        System.out.format("%02d %-20s %s\n", sl.getStartLevel(), b, state(b));
    }
    private void setStartLevel(Bundle b, int startlevel) throws BundleException {
        BundleStartLevel slb = b.adapt(BundleStartLevel.class);
        slb.setStartLevel(startlevel);
        b.start();
    }
    String state(Bundle b) {
        switch (b.getState()) {
        case Bundle.UNINSTALLED:
            return "UNINSTALLED";
        case Bundle.INSTALLED:
            return "INSTALLED";
        case Bundle.RESOLVED:
            return "RESOLVED";
        case Bundle.STARTING:
            return "STARTING";
        case Bundle.ACTIVE:
            return "ACTIVE";
        case Bundle.STOPPING:
            return "STOPPING";
        default:
            return "?" + b.getState();
        }
    }
    private void event(FrameworkEvent frameworkevent, String msg) {
    }
    String event(FrameworkEvent e) {
        switch (e.getType()) {
        case FrameworkEvent.ERROR:
            return "ERROR";
        case FrameworkEvent.INFO:
            return "INFO";
        case FrameworkEvent.PACKAGES_REFRESHED:
            return "PACKAGES_REFRESHED";
        case FrameworkEvent.STARTED:
            return "STARTED";
        case FrameworkEvent.STARTLEVEL_CHANGED:
            return "STARTLEVEL_CHANGED";
        case FrameworkEvent.STOPPED:
            return "STOPPED";
        case FrameworkEvent.STOPPED_BOOTCLASSPATH_MODIFIED:
            return "STOPPED_BOOTCLASSPATH_MODIFIED";
        case FrameworkEvent.STOPPED_UPDATE:
            return "STOPPED_UPDATE";
        case FrameworkEvent.WAIT_TIMEDOUT:
            return "WAIT_TIMEDOUT";
        default:
            return "?" + e.getType();
        }
    }
}
{code}
And the output:
{code:java}
 
starting framework up to level 1
reached 1, setting fw startlevel to 1000
In BundleActivator.start: Changing start level of bundles with sl=11 to sl=30
ERROR: Bundle qivicon.felix.TestStartLevels.testFailingToSetStartLevelBecauseChanged-2 [2] Error starting {qivicon.felix.TestStartLevels.testFailingToSetStartLevelBecauseChanged-2={}}-0 (org.osgi.framework.BundleException: Cannot start bundle qivicon.felix.TestStartLevels.testFailingToSetStartLevelBecauseChanged-2 [2] because its start level is 30, which is greater than the framework's start level of 11.)
org.osgi.framework.BundleException: Cannot start bundle qivicon.felix.TestStartLevels.testFailingToSetStartLevelBecauseChanged-2 [2] because its start level is 30, which is greater than the framework's start level of 11.
    at org.apache.felix.framework.Felix.startBundle(Felix.java:2175)
    at org.apache.felix.framework.Felix.setActiveStartLevel(Felix.java:1539)
    at org.apache.felix.framework.FrameworkStartLevelImpl.run(FrameworkStartLevelImpl.java:308)
    at java.lang.Thread.run(Thread.java:750)
the bundle is actually not started immediately30 qivicon.felix.TestStartLevels.testFailingToSetStartLevelBecauseChanged-2 [2] INSTALLED
but a bit later it is 30 qivicon.felix.TestStartLevels.testFailingToSetStartLevelBecauseChanged-2 [2] ACTIVE
{code}
This was tested on 6.0.2 but it occurred in the one of the latest 7.x versions. This very likely affects all versions

 



--
This message was sent by Atlassian Jira
(v8.20.10#820010)