EJB Dependency Injection of Session Bean Facade in JSF 1.2 on WebLogic 10.3 (JSF with JPA) html

EJB Dependency Injection of Session Bean Facade in JSF 1.2 on WebLogic 10.3 (JSF with JPA)

 

As I was preparing for my Devoxx presentation next Monday, I decided to look at the somewhat harder way of integrating a JPA based business tier into a JavaServer Faces web tier. The easy way – when using the ADF 11g framework in JDeveloper 11g is creating a Data Control for the Session Facade Bean. The Web Application can create data bindings for entities, entity collections and operations exposed in the Session Facade Bean that completely hide the injection or lookup details from the web tier developer. It’s the ADF responsibility to find the session bean and manage references to it.

However, if you do not use ADF, you need to establish the link between the Web Application and the JPA Business Service yourself.

The JEE 5 way of achieving this consists of a few simple steps – on top of the JPA entity definitions:

1. Create a session bean facade that implements the Facade Interface (an annotated interface)

2. Publish the session bean face as Stateless (or Stateful) Session Bean using a few simple annotations (@Stateless, @Stateful, @Remote, @Local) and deployment to a JEE 5 compliant container; this session bean gets injected by the container with an EntityManager that is uses to perform operations on the Entities (annotated classes that map to database tables)

3. Have the container inject the reference to the (Stateless) Session Bean into the Web Application artefact; rely on the container to locate the session bean and through dependency injection set the reference on a private member in a class in the web application

The injection by the container into the Web Application can be achieved in a very simple way – in theory at least – using the @EJB annotation. The following code ties the application together across the tiers, all handled by the container:

The FacadeBean, published as Stateless Session Bean:

@Stateless(name = "HRMFacade", mappedName = "HumanResourceManager-SessionEJB")
@Remote
@Local
public class HRMFacadeBean implements HRMFacade, HRMFacadeLocal {
    @PersistenceContext(unitName="Model")
    private EntityManager em;

    public HRMFacadeBean() {
    }

    public List<Employee> queryEmployeeFindAll() {
        return em.createNamedQuery("Employee.findAll").getResultList();
    }

...

The HRMFacade interface:

@Remote
public interface HRMFacade {

    List<Employee> queryEmployeeFindAll();
....

And the Servlet that gets a reference to the Sessoin Bean injected:

public class EmpLister extends HttpServlet {
    private static final String CONTENT_TYPE = "text/html; charset=windows-1252";
    @EJB private HRMFacade hrmFacade;

    public void init(ServletConfig config) throws ServletException {
        super.init(config);
    }

    public void doGet(HttpServletRequest request,
                      HttpServletResponse response) throws ServletException,
                                                           IOException {
    ....

In the doGet() method, the hrmFacade member is available, initialized by the container and ready to provide data and operations defined in the Session Bean using the EntityManager that leverages container specified data sources to connect to a database. With little effort and almost no configuration, the data-bound web application is created.

Ideally, in JSF applications, the injection of the Session Bean would be directly into Managed Beans. Unfortunately, that does not seem to work in JDeveloper 11g with WebLogic 10.3 and JSF 1.2. I have seen some indications on the internet that this used to be a known issue – injection for the @EJB annotation only worked for resources configured in the web.xml file – such as ServletListeners, ServletFilters, Servlets.

One way of working then would be to have the container inject the Session Bean reference(s) into for example a ServletListener and have the ServletListener store those references in the servlet context. Each managed bean could then, upon instantiation, retrieve the references it needs from the servlet context and from that moment on work with them as if they had been injected directly into the managed bean. This fairly elegant approach leaves the responsibility for locating the Session Beans entirely with the container. (note: this approach was descibed by Jocelyn Demoy in his article http://softquipeut.blogspot.com/2007/12/injection-dun-ejb-dans-un-back-bean-jsf.html). The steps:

Create ServletContextListener:

package nl.amis.hrm.view;

import javax.ejb.EJB;

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

import nl.amis.hrm.model.HRMFacade;

public class InitializeEJBReferences implements ServletContextListener {
    private ServletContext context = null;
    @EJB private HRMFacade hrmFacade;

    public void contextInitialized(ServletContextEvent event) {
        context = event.getServletContext();
        context.setAttribute("HRMFacade", hrmFacade );
    }

    public void contextDestroyed(ServletContextEvent event) {
        context = event.getServletContext();
    }
}

Configure the listener in the web.xml


    <listener>
        <listener-class>nl.amis.hrm.view.InitializeEJBReferences</listener-class>
    </listener>

Leverage the listener in the JSF Managed Bean

public class HrmManagerBean {
  private HRMFacade hrmFacade; // without the @EJB annotation as the WL 10.3 container does not seem to be able to inject the Session Bean (Facase) reference into a JSF managed bean

  // have the HRMFacade reference established in the constructor
  public HrmManagerBean() {
     javax.faces.context.ExternalContext ex = javax.faces.context.FacesContext.getCurrentInstance().getExternalContext();
     ServletContext sc = (ServletContext)ex.getContext();
     hrmFacade = (HRMFacade)sc.getAttribute("HRMFacade");

Another approach is the slightly more old-fashioned way of getting the Session Bean reference from the Context inside the managed bean:

 

public class HrmManagerBean {
  private HRMFacade hrmFacade; // without the @EJB annotation as the WL 10.3 container does not seem to be able to inject the Session Bean (Facase) reference into a JSF managed bean

  // have the HRMFacade reference established in the constructor
  public HrmManagerBean() {
        final Context context;
        try {
            context = new InitialContext();
            hrmFacade = (HRMFacade)context.lookup("HumanResourceManager-SessionEJB#nl.amis.hrm.model.HRMFacade");
        } catch (NamingException e) {
        }
    }
  .. make happy use of the hrmFacade in the methods of the (managed) bean class

 

Resources

Work around for the issue of not being able to dependency inject Session Bean directly into JSF Managed Beans  softquipeut.blogspot.com/2007/12/injection-dun-ejb-dans-un-back-bean-jsf.html

Oracle WebLogic 10.3 Documentation download.oracle.com/docs/cd/E12839_01/wls/docs103/webapp

/configurejsfandjtsl.html#wp162660

Using an EJB Session Bean as a Model Facade – blueprints.dev.java.net/bpcatalog/ee5/persistence/ejbfacade.html

Discussion on BEA forum on EJB DI into JSF and a workaround (replacing the  jsf 1.2 jar files): forums.bea.com/thread.jspa (note: this entry seems no longer available, I had to get it from the Google Cache. Part of the content: "… – WL 10MP1 supports DI in JSF. The trick here is to use bundled JSF implementation. It looks like SUN RI 1.2, but contains an extra entity resolver which is WL10 specific. The easiest way to get DI working is to make a surgery on bundled war (I stress that it’s war not jar) of jsf 1.2 and cut jsf-api and jsf-imlp from there and put them into the libs of your app. Other solutions include putting them to lib of the server and linking via WL vendor specific descriptor. …"

One Response

  1. Laco Skokan September 6, 2011