[ 
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)

Reply via email to