0

I have one OSGi bundle (BundleA) for Sling that I deploy in Felix. This bundle loads initial Sling content and then through an activator executes some operations on this content (sets permissions and sorts nodes). That works well, I build it with the Felix maven-bundle-plugin and I've put a bundle event listener in the activator:

@Override
public void bundleChanged(BundleEvent event) {
  if (BundleEvent.STARTED == event.getType()) {
    logger.info("Bundle A has started");
    BundleContext context = event.getBundle().getBundleContext();
  }
}  

But I have another bundle (BundleB) that when deployed, somehow triggers BundleA's activator. Consequently, the code is executed again and I don't want that.

I didn't find any documentation about "linked" bundles. But I'm not familiar with Felix/OSGi so I don't really know what to look for. The BundleA has BundleB has a Maven dependency, scope provided, because it uses some Constants from it. Otherwise, I have no idea what could link the two of them.

Any hint about what can trigger another bundle's activator would be really helpful to understand how it works. I can post more code/info if needed. Thank you

EDIT: This is the cycle raising the issue, when using CI

  1. Deploy BundleB (sling-core): contains an enum class to store constants
  2. Deploy BundleA (sling-content): loads Sling initial content, and with an activator automatically sets permissions and sort nodes using the Jackrabbit API. This codes uses enums stored in BundleA.
  3. Re-deploy BundleB (sling-core) because some changes were pushed
  4. Issue: BundleA also restarts and then re-do permissions and nodes sorting, which I do not want
  • BundleA also restarts: Is it possible that BundleA is wired to BundleB (uses classes from Bundle B or any other Require-Capability is defined between them)? – Balazs Zsoldos Feb 24 '16 at 10:37
  • Yes, BundleB (core bundle) contains an enum class to store resource types, this enum is used by BundleA (content bundle) to filter JCR operations. About Require-Capability, that's all I see in the MANIFEST (generated by the Maven bundle plugin): Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.7))" – Guillaume Lucazeau Feb 24 '16 at 10:51

2 Answers2

1

With very short words:

A Bundle (A) is started if

  • it is set to be automatically started, and the start level of the bundle is lower than the start level of the framework
  • another bundle (B) is started that uses classes from the bundle A

I guess your bundle B uses classes from Bundle A, so Bundle A will be started before Bundle B is started.

You probably want to divide Bundle A to two bundles. You can create a new Bundle that contains only the Activator, so when Bundle B is started, this new Bundle will be not started automatically (as no classes are used from this new Bundle).

If you want to have a deeper understand, I suggest that you should read that you should read chapter 4.3 together with some other chapters linked from this one of OSGi Core specification.

Btw.: I have never had to use lazy starting policy, or non-started bundles in any of my projects. If you start using a component model like Declarative Services and make the component instantiation logic based on configuration via ConfigAdmin, you might not have issues like this anymore. You will not really have the necessity to implement Bundle Activators.

Edit

Based on the new information in the question:

As Bundle B is updated and the bundles are refreshed, Bundle A is re-wired to BundleB and then started again. You should think of dividing Bundle B into two separate bundles. One part is more constant (e.g.: B-API) and the other part is not that constant (B-IMPL). Bundle A should use B-API only and you will not have re-wiring when B-IMPL is updated.

First it seems to make you more work, but this is the real separation of logic. We have many bundles that contain only 1-2 class files, but they are stable and have no new version since a long time.

Balazs Zsoldos
  • 6,036
  • 2
  • 23
  • 31
  • Indeed I'm not sure an Activator is the best way to do what I want, but I got the idea from here: http://stackoverflow.com/questions/21380400/sling-initial-content-loading-how-to-set-access-permissions and didn't find any other solution. Basically what I want is to set up things after the content is loaded (i.e. onlye when BundleA is deployed), but not when another bundle is deployed. I am using Declarative Services, but I want these actions to be automatically executed to properly set up the environnement each time BundleA is deployed by our CI tool. – Guillaume Lucazeau Feb 24 '16 at 10:29
  • From your comments I now understand that my BundleB restarts my BundleA because this last one uses classes from BundleB. While I understand your explanations about separation of logice, I may just duplicate my enum class as my content bundle is a temporary bundle to load and set up data, that will no longer be used once it's done in production. I'll try and see if it fixes my issue – Guillaume Lucazeau Feb 24 '16 at 13:15
  • I have finally decided to flag my content as "initialized" (property set on a node in the JCR) instead of moving everything. Like I said, this bundle is just for content migration purpose so it's not a big deal. But thanks, I've understood now a little more how it works and why they're wired; Thank you. – Guillaume Lucazeau Feb 25 '16 at 13:11
1

I don't think you should do this at the bundle level. Instead you should use components and services. Use Declarative Services (DS) for this.

The basic plan would be:

  1. Component1 performs the setup of whatever resources you need in Sling. When this is all done, it publishes a service... the type of the service is not important, it could simply be a marker interface with no methods.

  2. Component2 has a mandatory reference (using the @Reference annotation) on that service.

Now, Component2 will not activate until the service becomes available. Therefore in the activate method of Component2, you know for sure that the resources have been properly setup already.

By the way, if the tasks you are doing in Component1 are time-consuming, you should not do them synchronously in the activate method of the component (or in the start method of a BundleActivator for that matter!). Instead you should spin off a thread and do them there. This avoids locking up the OSGi event system and hijacking a thread belonging to either the launcher or another bundle.

Neil Bartlett
  • 23,743
  • 4
  • 44
  • 77
  • I'm not sure if I understand. I want my code to be executed automatically, without calling anything, for CI convenience (setup everything everytime I redeploy my content bundle). That's why I used the Activator. When it's done, I don't need the services anymore, and I could actually remove the bundle from Felix. I don't have to worry about performances here, this operation is a one shot, I just redeploy the bundle many times during development phase. So basically I would like to have this code executed when I deploy THIS bundle, and not another one. – Guillaume Lucazeau Feb 24 '16 at 13:10
  • I think I got what you mean: I've create a BundleActivated service (Component2 in your example) with a Reference annotation to NodeSorter (Component1), the service that sort JCR nodes. In this service, I have put an Activate annotation on my method to sort the nodes. But it's never executed, even though all the services are set as "registered". I'm investigating... but does it sound like what you suggested? – Guillaume Lucazeau Feb 24 '16 at 14:33
  • I made this, it's cleaner, but I have the same problem (bundles wired). – Guillaume Lucazeau Feb 25 '16 at 13:09
  • This suggests a poorly designed arrangement of package exports. Ideally, don't export implementation classes, and put API packages and implementation packages in separate bundles. – Neil Bartlett Feb 26 '16 at 00:30
  • Thank you Neil, I got it now and will separate interfaces and implementations. However, as we're in development phase, both will be updated frequently and I still don't want to restart my content bundle once it's deployed and started. Actually, if there was an easy way to say "stop and undeploy yourself" I'd go for it. – Guillaume Lucazeau Feb 26 '16 at 10:00
  • Bundles should NEVER try to control their own lifecycle. Think about it... you're in a bundle and you stop yourself, which is a bit like a person shooting himself in the head except the code doing this is still running! Now it tries to do an update or something, and it is using an invalid `BundleContext` because OSGi thinks the bundle has stopped. Lots of weirdness ensues. Much better to have a separate bundle whose only job is to manage other bundles: this is known as a "management agent". You could try using Felix FileInstall, for example. – Neil Bartlett Feb 26 '16 at 10:53
  • I know I won't do that, It was just to explain this particular bundle that loads and initialize content using Sling loader, when it's done it becomes useless without code running so I actually remove it myself sometimes :) – Guillaume Lucazeau Feb 26 '16 at 11:10