Extreme Java When vanilla Java is not enough

22May/090

Using Spring with Mojarra InjectionProvider

Are you using SpringBeanFacesELResolver to handle dependency injection on managed beans? It works nice, but your faces-config grows fast with this approach.

Two months ago, I posted about using Spring injection outside application context. I forgot to mention that the same trick can be used to replace ELResolver solution if you are using Mojarra as your JSF provider.

You only need to add a servlet context parameter:

<context-param>
  <param-name>
    com.sun.faces.injectionProvider
  </param-name>
  <param-value>
    mypkg.SpringJSFInjectionProvider
  </param-value>
</context-param>

And the implementation of SpringJSFInjectionProvider is as simple as:

public class SpringJSFInjectionProvider
       implements InjectionProvider {
  private WebContainerInjectionProvider wcip =
      new WebContainerInjectionProvider();
 
  @Override
  public void inject(Object bean)
         throws InjectionProviderException {
    FacesContextUtils
      .getWebApplicationContext(
         FacesContext.getCurrentInstance())
      .getAutowireCapableBeanFactory()
      .autowireBeanProperties(bean,
         AutowireCapableBeanFactory.AUTOWIRE_NO,
         false);
    wcip.inject(bean);
  }
  @Override
  public void invokePreDestroy(Object bean)
         throws InjectionProviderException {
    wcip.invokePreDestroy(bean);
  }
  @Override
  public void invokePostConstruct(Object bean)
         throws InjectionProviderException {
    wcip.invokePostConstruct(bean);
  }
}

Adding this, you can shrink your faces-context, removing all "managed-property" tags. Your managed beans can look more JavaEE 5 after you change this:

private BO bo;
 
public void setBo(BO bo) {
  this.bo = bo;
}

To this:

@Resource(name="bean-on-appctx")
private BO bo;

No setter, no ELResolver. This means less code to maintain! If you plan to migrate from Spring to EJB, this means you will only need to promote the BO to an stateless bean and change @Resource to @EJB!

17Mar/090

Spring injection outside application context

Are you using ApplicationContext (and friends) to create a single "spring context" for all your web application? If so, you are on the right way. And it is possible that you feel the need to inject properties where Spring isn't accessible (like a Servlet Filter). You can use this little trick:

public static void inject(ServletContext context,
    Object bean) {
  WebApplicationContext wCtx =
      WebApplicationContextUtils
          .getWebApplicationContext(context);
  if (wCtx != null) {
    wCtx.getAutowireCapableBeanFactory()
        .autowireBeanProperties(bean,
            AutowireCapableBeanFactory.AUTOWIRE_NO,
            false);
  }
}
16Mar/090

Spring classes and their long names

Spring team likes big names. One of the biggest is "SimpleRemoteStatelessSessionProxyFactoryBean". This is not a problem, since I'm not a Fortran programmer. Unfortunatelly, I found two classes with similar names that aren't type-safe (i.e.: I don't receive a ClassCast or similar if I swap them): MethodInvokingFactoryBean e MethodInvokingJobDetailFactoryBean. I use the latter a lot, but, after using NB's autocomplete feature, I selected the former instead:

<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
  <property name="targetObject" ref="cache"/>
  <property name="targetMethod" value="reload"/>
</bean>

Results? NullPointerException:

org.quartz.SchedulerException: Registration of jobs and triggers failed: null [See nested exception: java.lang.NullPointerException]
  at org.springframework.scheduling.quartz.SchedulerFactoryBean.registerJobsAndTriggers(SchedulerFactoryBean.java:861)
  at org.springframework.scheduling.quartz.SchedulerFactoryBean.afterPropertiesSet(SchedulerFactoryBean.java:649)
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1368)
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1334)
  ... 13 more
Caused by: java.lang.NullPointerException
  at org.quartz.core.QuartzScheduler.addJob(QuartzScheduler.java:808)
  at org.quartz.impl.StdScheduler.addJob(StdScheduler.java:288)
  at org.springframework.scheduling.quartz.SchedulerFactoryBean.addJobToScheduler(SchedulerFactoryBean.java:883)
  at org.springframework.scheduling.quartz.SchedulerFactoryBean.addTriggerToScheduler(SchedulerFactoryBean.java:906)
  at org.springframework.scheduling.quartz.SchedulerFactoryBean.registerJobsAndTriggers(SchedulerFactoryBean.java:842)
  ... 16 more

This wasn’t easy to solve, since the NPE wasn’t obvious. Both MIFB and MIJDFB are “factory” instances (they create real instances as needed), and, in this case, no type check was done.