0
votes

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
2
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
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

2 Answers

1
votes

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.

1
votes

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.