Integrating Guice and JSF

I was wondering about how the integration of JSF and the community’s new hot IOC Framework Guice can be done and later figured out a way. The example I’ve created is simple, there’s a JSF page used for creating an Account entity, a service class AccountService that implements IAccountService, and a JSF managed bean serving as the backing bean of the JSF page. Simply the JSF bean has a dependency to IAccountService and the injection is done via Guice.

Account

  
    
    

package com.cc.guicejsf.domain;

import java.io.Serializable;

public class Account implements Serializable{

  private String name;
  private String password;
  
  public Account() {}
  
  public Account(String name, String password) {
    this.name = name;
    this.password = password;
  }
  
  //Getters/Setters of properties
}
      

IAccountService

 
    
    

package com.cc.guicejsf.service;

import com.cc.guicejsf.domain.Account;

public interface IAccountService {
  public void save(Account account);
}
      

AccountService

package com.cc.guicejsf.service;

import com.cc.guicejsf.domain.Account;

public class AccountService implements IAccountService{

  public void save(Account account) {
    //TODO: Persist account
  }
}
      

createAccount form

<%@ taglib uri=”http://java.sun.com/jsf/html&#8221; prefix=”h” %>
<%@ taglib uri=”http://java.sun.com/jsf/core&#8221; prefix=”f” %>
<html>
    <head>
        <title>Create an Account</title>
    </head>
    <body>
        <f:view>
            <h:form>
                <h:panelGrid columns=”2″>
                    <h:outputLabel value=”Name” for=”txt_name”></h:outputLabel>
                    <h:inputText id=”txt_name” value=”#{createAccount.account.name}” />
                   
                    <h:outputLabel value=”Password” for=”txt_password”></h:outputLabel>
                    <h:inputSecret id=”txt_password” value=”#{createAccount.account.password}” />
                </h:panelGrid>
                <h:commandButton action=”#{createAccount.save}” value=”Save” />
            </h:form>
        </f:view>
    </body>   
</html> 

CreateAccount

  
    
    

package com.cc.guicejsf.pages;

import com.cc.guicejsf.domain.Account;
import com.cc.guicejsf.service.IAccountService;
import com.google.inject.Inject;

public class CreateAccount extends BasePageBean{

  private Account account = new Account();
  private IAccountService accountService;

  public Account getAccount() {
    return account;
  }
  public void setAccount(Account account) {
    this.account = account;
  }
  
  public String save() {
    accountService.save(account);
    return null;
  }
  
  public IAccountService getAccountService() {
    return accountService;
  }
  @Inject
  public void setAccountService(IAccountService accountService) {
    this.accountService = accountService;
  }
}
      

BaseBackingBean

  
    
    

package com.cc.guicejsf.pages;

import java.io.Serializable;

import javax.annotation.PostConstruct;
import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;

import com.google.inject.Injector;

public abstract class BasePageBean implements Serializable{

  private Injector injector;
  
  public BasePageBean() {}
  
  public Injector getInjector() {
    if(injector == null) {
      ServletContext servletContext = (ServletContext)FacesContext.getCurrentInstance().
                                       getExternalContext
().getContext();
      injector = (Injector)servletContext.getAttribute(Injector.class.getName());  
    }
    return injector;
  }
  public void setInjector(Injector injector) {
    this.injector = injector;
  }
  
  @PostConstruct
  public void init() {
    getInjector().injectMembers(this);
  }
}
      

Listener (To be defined in web.xml)

  
    
    

 package com.cc.guicejsf.config;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import com.google.inject.Guice;
import com.google.inject.Injector;

public class ModuleListener implements ServletContextListener {

  public void contextDestroyed(ServletContextEvent servletContextEvent) {
    ServletContext servletContext = servletContextEvent.getServletContext();
    servletContext.removeAttribute(Injector.class.getName());
  }

  public void contextInitialized(ServletContextEvent servletContextEvent) {
    Injector injector = Guice.createInjector(new  GuiceJSFModule());
    ServletContext servletContext = servletContextEvent.getServletContext();
    servletContext.setAttribute(Injector.class.getName(), injector);
  }
}
      

GuiceJSFModule

  
    
    

 package com.cc.guicejsf.config;

import com.cc.guicejsf.service.IAccountService;
import com.cc.guicejsf.service.AccountService;
import com.google.inject.AbstractModule;
import com.google.inject.Scopes;

public class GuiceJSFModule extends AbstractModule{

  public void configure() {
    bind(IAccountService.class).to(AccountService.class).in(Scopes.SINGLETON);
  }
}
      

The idea here is, the backing JSF bean is created by JSF IOC but it’s dependencies like IAccountService is injected by Guice. At startup, a listener creates and sets a Guice Injector to application scope(I get the listener idea from this blog). This part can be refactored so that the module lists are not hardcoded and retrieved from a context param instead. Anyway, when a JSF bean is created, the method annotated with @PostConstruct gets the injector and calls the injection procedure. This @PostConstruct is called after the bean is created but before being put into it’s scope.

The example is a kickstart for an app that uses JSF and Guice, frankly I don’t think Guice fits the JSF model well, the @PostConstruct is a JSF 1.2 feature and it’ll be problematic to do a similar thing with JSF 1.1. I’m not a spring fan but using spring 2.0 with JSF is very easy and effective compared to Guice since it’s also possible to define the JSF beans in Spring Context with different scopes, apply AOP on them, add BeanPostProcessor to add custom lifecycle logics and etc.

Create a JSF Slider in 5 minutes

There might be cases where it’d be cool to make users enter the values of an JSF inputtext using a slider. One possible way to do this is to create a custom JSF component and render the javascript but this is hard to create, test and maintain. On the otherhand thanks to the best jsf viewhandler Facelets, it’s sooo easy. Actually I’ve created this example to demonstrate how it’s easy to do stuff like this.

Here’s the slider.xhtml built on top of the cool scriptaculous slider widget;

<ui:composition>
    <t:inputText id=”#{id}” style=”width:100px” value=”#{value}” forceId=”true”></t:inputText>

    <div id=”#{id}_track_id” style=”width:105px;background-color:#aaa;height:5px;”>
        <div id=”#{id}_handle_id” style=”width:5px;height:10px;background-color:#f00;cursor:move;”></div>
    </div>

    <script type=”text/javascript”>
//some scriptaculous script here
         var slider_#{id} = new Control.Slider(‘#{id}_handle_id’,’#{id}_track_id’, {range:$R(#{minimum},#{maximum})});              
        slider_#{id}.options.onSlide = function(value){                                 
          $(‘#{id}’).value = (value + ”).split(“.”)[0];
        };
    </script>
</ui:composition>

After declaring slider tag in your facelets-taglib file, it’s all set and ready to use as;

<barca:slider id=”ageSlider” value=”#{CustomerCreate.customer.age}” minimum=”18″ maximum=”50″></barca:slider> 

How it looks

Scriptaculous slider’s lookandfeel can be changed using the css attributes so it’s possible to make it look cooler like a swing slider. But that’s not the point, the thing is with facelets, I don’t think there is so much need to write custom JSF components with java because it’s obvious that with facelets composition infrastructure creating composition components or fancy rich components like this slider is almost a no-brainer.

Full Text Search with Hibernate Search

Doing Full Text Search in your domain model might be tricky in the past but now with Hibernate Search based on Lucene it’s not a problem. If you’re already using hibernate annotations, configuration is very easy and can be done in a few steps. Firstly annotating the domain model with a few simple annotations and then adding the hibernate event listeners to the hibernate configuration sets up the whole thing. In our project with my teammate Sylvain Vieujot(co-author of Hibernate Search along with Emmanuel Bernard) we’ve integrated full text search to our forum module. Here’s a simplified example;

Configuration
By default lucene indexes are can be saved either on file system or in memory. This can be configured by the “hibernate.search.default.directory_provider” parameter. Instead of these directory providers, it’s possible to hold the indexes in database using the Compass’s extension called JDBCDirectoryProvider. In addition a couple of event listeners need to be configured.

hibernate.search.default.directory_provider=org.hibernate.search.store.FSDirectoryProvider
hibernate.search.default.indexDir=C:\LuceneIndexes

 hibernate-configuration>
    …
    …
    …
    <event type=”post-update”
        <listener class=”org.hibernate.search.event.FullTextIndexEventListener”/>
    </event>
    <event type=”post-insert”
        <listener class=”org.hibernate.search.event.FullTextIndexEventListener”/>
    </event>
    <event type=”post-delete”
        <listener class=”org.hibernate.search.event.FullTextIndexEventListener”/>
    </event>
    …
    …
    …
</hibernate-configuration>

Domain Model
The annotations are the glue to the lucene. Field, Index, Store, Boost they all refer to the core lucene api. By use of these hibernate search takes care of the underlying lucene search engine configuration.

  
    
    

@Entity
@Indexed
public class ForumPost implements Serializable{

  private Long id;
  private String subject;
  private String message;
  
  @Id
  @DocumentId
  public Long getId() {
    return id;
  }
  public void setId(Long id) {
    this.id = id;
  }
  
  @Field(index=Index.TOKENIZED)
  public String getSubject() {
    return subject;
  }
  public void setSubject(String subject) {
    this.subject = subject;
  }
  
  @Field(index=Index.TOKENIZED, store=Store.YES)
  public String getMessage() {
    return message;
  }
  public void setMessage(String message) {
    this.message = message;
  }
}
      

Query
The coolest thing here is that, a lucene Query becomes a hibernate Query and you can use the regular stuff like list, scroll with it. In the example below Lucene Query Api is used, but it’s also possible to use Lucene’s Query Parser too, it does not matter since both can create a Lucene Query Object.

  
    
    

    public List searchByKeyword(String keyword) {
    BooleanQuery booleanQuery = new BooleanQuery();
    booleanQuery.addnew PrefixQuerynew Term"subject", keyword ) ), BooleanClause.Occur.SHOULD );
    booleanQuery.addnew PrefixQuerynew Term"message", keyword ) ), BooleanClause.Occur.SHOULD );
    org.hibernate.Query fullTextQuery = getFullTextSession().createFullTextQuery(booleanQuery, ForumPost.class);
    List result = fullTextQuery.list();
}

  public FullTextSession getFullTextSession() {
    Session session = sessionFactory.openSession();
    FullTextSession fullTextSession = Search.createFullTextSession(session);
    return fullTextSession;
}
      

When the session factory is built, empty lucene directories per entity is created under the main lucene index. Using FullTextSession.index(Object entity) method, it’s possible to rebuild an index of an entity. With the Luke tool, you can browse the lucene indexes kept in filesystem, this’ll really help during development.

Follow

Get every new post delivered to your Inbox.

Join 107 other followers