Search code examples
javajsfjsf-2managed-bean

How to extend com.sun.jsf.mgbean.ManagedBeanBuilder


I would like to extend the com.sun.jsf.mgbean.ManagedBeanBuilder. Where do I have to register such a class?

The reason is, I need to set a value (which is based on the bean which is being created) within the bean-to-create before the PostConstruct. So I would have to change the com.sun.faces.mgbean.BeanBuilder.build method:

public Object build(InjectionProvider injectionProvider, FacesContext context) {
  Object bean = newBeanInstance();
  injectResources(bean, injectionProvider);
  buildBean(bean, context);
  if (bean instanceof SomeInterface == false) {
    invokePostConstruct(bean, injectionProvider);
  }
  return bean;
}

.. afterwards in MyManagedBeanELResolver's revoleBean (registered in faces-config.xml as el-resolver) method:

result = manager.create(beanName, builder, facesContext);
if (result instanceof SomeInterface) {
  ((SomeInterface) result).setValue(...)
  builder.invoktePostConstruct(result);
}

Is there a better solution to it?

UPDATE why I can't use the PhaseListener proposed by kolossus
I cannot use the viewId to fetch the ManagedBean because I would like to have n types of Bean currently active, each accessed by their bean name.

I have a 1:n relation between viewId and ManagedBean, e.g. I have a test.jsf which has the backing-bean TestBean. It's also possible that a TestBean0 exists which is the, lets say, a copy of TestBean but with a different value (which I need to inject, e.g. a "userId"). My test.jsf (together with a ControllerBean is able to determine which ManagedBean should be accessed, either the TestBean or TestBean0, which shows either the content from the TestBean (e.g. userId=27) or *TestBean_0* (e.g. userId=33).
This allows me to have the 1 view (test.jsf) with n models (TestBean) displayed at in the same page at the same time, e.g my test.html looks like this.

....
<h:outputText value="#{controllerBean.getBean('testBean', component).name}" />
<f:subview id="someId">
  <ui:include src="/WEB-INF/templates/test.xhtml" />
</f:subview>
....

So therefore I can have a recursion (depth of the recursion is 1 in this case), where the outputText from the level 0 shows the name "Your userId is 27" and the included test.xhtml's outputText shows "Your userId is 33".


Solution

  • My solution is as follows (not exactly what I wanted, but until now I did not find a better way):

    As I wasn't able to extend the ManagedBeanBuilder i decided to create my own BeanManager (MyBeanManager). It behaves equally as the com.sun.faces.mgbean.BeanManager's getBeanFromScope and create method, but a bit differently on getBuilder:

    if (getRegisteredBeans() != null) {
      BeanBuilder builder = getRegisteredBeans().get(name);
      if (builder instanceof ManagedBeanBuilder) {
        builder = new MyManagedBeanBuilder(builder.getManagedBeanInfo());
      }
      return builder;
    }
    return null;
    

    So i was able to inject MyManagedBeanBuilder and use my construct proposed above:

    public Object build(InjectionProvider injectionProvider,
        FacesContext context) {
      Object bean = newBeanInstance();
      injectResources(bean, injectionProvider);
      buildBean(bean, context);
      if (bean instanceof SomeInterface == false) {
        invokePostConstruct(bean, injectionProvider);
      }
      return bean;
    }
    

    .. and in order to invoke @PostConstruct after i set the desired value into the bean, i had to override the invokePostConstruct to make it public instead of protected

    public void invokePostConstruct(Object bean, InjectionProvider injectionProvider) {
      try {
        injectionProvider.invokePostConstruct(bean);
      } catch (InjectionProviderException ipe) {
        String message = MessageUtils.getExceptionMessageString(
        MessageUtils.MANAGED_BEAN_INJECTION_ERROR_ID,
        beanInfo.getName());
        throw new ManagedBeanCreationException(message, ipe);
      }
    }
    

    .. finally in MyManagedBeanELResolver I was able to set my desired value into the bean without having @PostConstruct called before. in MyManagedBeanELResolver:

    if (result instanceof SomeInterface) {
      ((SomeInterface) result).setValue(value);
      ((MyManagedBeanBuilder) builder).invokePostConstruct(result, getInjectionProvider());
    }