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

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

    [ https://issues.apache.org/jira/browse/FELIX-6586?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17648694#comment-17648694 ] 

Jochen Hiller commented on FELIX-6586:
--------------------------------------

Meanwhile I added a test case and a proposal for a fix to this PR:

[https://github.com/apache/felix-dev/pull/190]

From my point of view there are two problems happening:
 # When a bundle start level will be changed from a bundle activator which is started by FrameworkStartLevel thread, the way to process that is by default asynchronous. Which does not work when bundle startup is already done by FrameworkStartLevel thread, so the processing has to be done in current thread synchronous by calling {{m_felix-setBundleStartLevel()}} on Felix framework

 # When this method {{m_felix-setBundleStartLevel()}} will be called, there is already a computed TreeMap of {{m_startLevelBundles}} in Felix. This list contains the bundle with old start level. So new start level requested needs to be updated by removing old tuple and adding new tuple to get it in right sort-order in TreeMap.

I found that by adding a huge number of debug messages. I will leave them in for your testing. If you would agree on change, I would remove all debug output again, and do some re-formatting of code to Felix code style.

My tests are running fine, also the whole Felix framework tests did also work well.
Do you consider to re-run OSGi TCK test suite for a complete test? I can offer to support on that as well if helpful.

> 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
>            Priority: Major
>
> 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)