javaosgicdipax

PAX-CDI: CdiContainerFactory and BundleTracker


I use felix and pax-cdi 0.13 and weld. I have two bundles A in B. In bundle A I have BundleTracker and Customizer, in bundle B I have CDI beans. So I want in bundle tracker to get cdi beans from bundle B using bean manager. So I have method in customizer:

@Override
    public Object addingBundle(Bundle bundle, BundleEvent event) {
        String marker = (String) bundle.getHeaders().get("Some-Marker");
        if (marker != null) {
            try {
                //1 we get CdiContainerFactory
                BundleContext thisBundleContext=FrameworkUtil.getBundle(this.getClass()).getBundleContext();
                ServiceReference<?> sr = thisBundleContext.getServiceReference(CdiContainerFactory.class.getName());
                CdiContainerFactory cdiContainerFactory = (CdiContainerFactory) thisBundleContext.getService(sr);
                if (cdiContainerFactory==null){
                    System.out.println("Cdi container factory is null");
                }else{
                    System.out.println("Cdi container factory is not null");
                }
                //2 we get BeanManager from CdiContainerFactory
                Class<?> klass  = bundle.loadClass("com.temp.Temp");
                BeanManager beanManager = cdiContainerFactory.getContainer(bundle).getBeanManager();
                Bean<?> bean = (Bean<?>)beanManager.resolve(beanManager.getBeans(klass));
                Object temp= beanManager.getReference(bean, bean.getBeanClass(), beanManager.createCreationalContext(bean));
            } catch (ClassNotFoundException ex) {
                Logger.getLogger(ExtenderBundleTracker.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return bundle;
    }

To register bundle tracker I use the following code:

bundleTracker=new BundleTracker<>(context, Bundle.ACTIVE, new MyCustomizer());

The code above works. But not always. I have timing problem. I always get Cdi container factory is not null, but the following line

BeanManager beanManager = cdiContainerFactory.getContainer(bundle).getBeanManager();

sometimes throws NullPointerException. As I suppose when Bundle B becomes active CDI container is still not ready for it. How can such problem be solved?


Solution

  • The fact that the bean manager sometimes cannot be found is due to the fact that the CDI container is (asynchronously) created by a bundle listener from the PAX-CDI bundles as well. This means that you may run into a race condition because the container may or may not be created at the time your bundle listener is called.

    When using PAX-CDI, I would not recommend trying to access the bean manager, but in stead export the bean you need as an OSGi service (@OsgiServiceProvider annotation on the bean) and track that service via a ServiceTracker. If that is not possible because of the scope of the bean, you can always create a separate factory pattern bean in the container that does the bean creation work for you and export that one as a service.