How to create and destroy managed CDI (Weld) Beans through BeanManager?

I am trying to instantiate managed CDI beans using BeanManager, not an .select () instance. get ().

This was suggested as a workaround to the problem that I encountered ApplicationScoped beans and their dependency garbage collection - see CDI applications and dependent areas can conspire to affect garbage collection? for the background and this is the proposed solution to the problem.

If you use the Instance programmatic search method on the ApplicationScoped bean, the Instance object and any beans that you get from it ultimately depend on the ApplicationScoped bean and therefore share its life cycle. However, if you create beans using BeanManager, you have a handle to the Bean instance itself, and apparently it can explicitly destroy it, and I understand that it will be GCed.

My current approach is to create a Bean in the BeanManagerUtil class and return the bean, instance, and CreationalContext compound object:

public class BeanManagerUtil { @Inject private BeanManager beanManager; @SuppressWarnings("unchecked") public <T> DestructibleBeanInstance<T> getDestructibleBeanInstance(final Class<T> type, final Annotation... qualifiers) { DestructibleBeanInstance<T> result = null; Bean<T> bean = (Bean<T>) beanManager.resolve(beanManager.getBeans(type, qualifiers)); if (bean != null) { CreationalContext<T> creationalContext = beanManager.createCreationalContext(bean); if (creationalContext != null) { T instance = bean.create(creationalContext); result = new DestructibleBeanInstance<T>(instance, bean, creationalContext); } } return result; } } public class DestructibleBeanInstance<T> { private T instance; private Bean<T> bean; private CreationalContext<T> context; public DestructibleBeanInstance(T instance, Bean<T> bean, CreationalContext<T> context) { this.instance = instance; this.bean = bean; this.context = context; } public T getInstance() { return instance; } public void destroy() { bean.destroy(instance, context); } } 

From this in the calling code, I can get the actual instance, put it on the card for later retrieval and use as usual:

 private Map<Worker, DestructibleBeanInstance<Worker>> beansByTheirWorkers = new HashMap<Worker, DestructibleBeanInstance<Worker>>(); ... DestructibleBeanInstance<Worker> destructible = beanUtils.getDestructibleBeanInstance(Worker.class, workerBindingQualifier); Worker worker = destructible.getInstance(); ... 

When I'm done with this, I can find the destructible shell and call destroy () on it, and Bean and its dependents should be cleaned:

 DestructibleBeanInstance<JamWorker> workerBean = beansByTheirWorkers.remove(worker); workerBean.destroy(); worker = null; 

However, after starting a few employees and leaving my JBoss (7.1.0.Alpha1-SNAPSHOT) for 20 minutes or so, I see that the GC is happening

 2011.002: [GC Desired survivor size 15794176 bytes, new threshold 1 (max 15) 1884205K->1568621K(3128704K), 0.0091281 secs] 

However, the JMAP histogram still shows the old workers and their dependent instances hanging around, unGCed. What am I missing?

As a result of debugging, I see that the context field of the created Bean has a context of the correct Worker type, no incomplete events, and no parentDependentInstances. He has a number of dependent conditions that are expected from the fields of the worker.

One of these fields in the Workplace is the Instance, and when I compare this field with that of the Worker obtained using the Instance program search, they have a slightly different CreationContext makeup. The “Instance” field at the Workplace, viewed through the “Instance”, has the employee under incomplete Instances, while the “Instance” field at the Workplace received from the BeanManager does not work. They both have identical parentDependentInstances and dependInstances.

This suggests that I did not correctly reflect the extraction of the instance. Could this contribute to a lack of destruction?

Finally, when debugging, I can see bean.destroy (), which is called in my DestructibleBeanInstance.destroy (), and this continues until ManagedBean.destroy, and I can see that the dependent objects are destroyed as part. release(). However, they still do not collect garbage!

Any help on this would be greatly appreciated! Thanks.

+7
source share
3 answers

In the code you inserted, I would change a couple of things.

  • Make this class a regular java class, without injection, and go into BeanManager. Something may be ruined in this way. This is unlikely, but possible.
  • Create a new CreationalContext using BeanManager.createCreationContext(null) , which will give you an essentially dependent area that you can free when you finish by calling CreationalContext.release() .

You may be able to get everything to work correctly the way you want by calling the release method in CreationalContext, which you already have in DestructibleBeanInstance , assuming there is no other Beans in this CreationalContext that would ruin your application.Try it first and see will it hurt?

+2
source

Passing to null should only be done when you enter a class other than a bean. In your case, you enter a bean. However, I would still expect the GC to work in this case, so that you can write JIRA to the Weld tracker with a test case and steps for playback?

+2
source

The best solution would be to use a dynamic proxy to handle the destruction of the bean. The code to get an instance of the bean class will look like this:

 public static <B> B getBeanClassInstance(BeanManager beanManager, Class<B> beanType, Annotation... qualifiers) { final B result; Set<Bean<?>> beans = beanManager.getBeans(beanType, qualifiers); if (beans.isEmpty()) result = null; else { final Bean<B> bean = (Bean<B>) beanManager.resolve(beans); if (bean == null) result = null; else { final CreationalContext<B> cc = beanManager.createCreationalContext(bean); final B reference = (B) beanManager.getReference(bean, beanType, cc); Class<? extends Annotation> scope = bean.getScope(); if (scope.equals(Dependent.class)) { if (beanType.isInterface()) { result = (B) Proxy.newProxyInstance(bean.getBeanClass().getClassLoader(), new Class<?>[] { beanType, Finalizable.class }, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("finalize")) { bean.destroy(reference, cc); } try { return method.invoke(reference, args); } catch (InvocationTargetException e) { throw e.getCause(); } } }); } else throw new IllegalArgumentException("If the resolved bean is dependent scoped then the received beanType should be an interface in order to manage the destruction of the created dependent bean class instance."); } else result = reference; } } return result; } interface Finalizable { void finalize() throws Throwable; } 

Thus, the user code is simpler. He should not care about destruction. A limitation of this approach is that the case where the resulting beanType is not an interface and the permitted bean class does not match @Dependent is not supported. But easy to work arround. Just use the interface. I tested this code (with JBoss 7.1.1) and it also works for a dependent session with beans state.

+1
source

All Articles