Integrating EJB 3

Design patterns are generally accepted solutions that typically work for common problems. The application is generally represented by three tiers, presentation, application (business logic) and persistence. The common design patterns for each of these are

Presentation

The presentation tier is often referred to as the web tier, this is the tier that gives the user access to your well designed application, most of the time your users will access your software via the web but on occasions you could use a standalone application. The industry-accepted design pattern to perform this is the Model-View-Controller (MVC) pattern, it involves three components, each designed to isolate code into a specific area of responsibility.

I have already discussed MVC in my Tomcat section, I also have a simple MVC tutorialusing JSP and simple POJOs.

You can implement your own MVC pattern by building your own web framework, but there are many other frameworks out there that can ask easy and less costly for you, I have listed some of the more popular ones (there are over 50 of them)

Most of the frameworks will also use servlets and JSPs, as the controller and view mechanisms, to access the application via the web tier the servlets will invoke your application EJBs.

Persistence

In the early days the use of JDBC from business logic led to several maintenance problems and the Data Access Object (DAO) design pattern was invented to decouple data access code from business logic. DAO worked with JDBC, ORM frameworks and CMP entity beans. The Entity Access Object (EAO) pattern is a new incarnation of the DAO pattern that you can use with JPA.

The EAO pattern decouples the entity access logic from the business logic and improves code maintainability, it allows you to easily change your underlying entity access code without affecting the business logic. Usually you have one EAO object for every domain object that performs the CRUD operations for the domain objects (entities).

Application

Since EJB3 session beans are POJO (Plain Old Java Objects), you can use EAOs in your session beans by injection, the session beans code is lean. Using this approach with a Java EE has a side affect in that you automatically receive declarative transactions and a container-managed Entity-Manager.

One of the primary reasons the Session Facade pattern was invented was to reduce the number of calls for previous EJB incarnations. In EJB2 entity beans were remotely accessible but at a price of reduced performance and tight coupling of clients with there domain data, with a session bean facade you can use entity beans locally and expose the session beans to the clients. This cuts a vast amount of RMI calls and encourages loose coupling between client and entity beans, thus improving code maintenance. All you do is create a coarse-grained session bean and make it available to the clients, one advantage of using a coarse-grained session bean is that it makes managing the transactional and security aspects of your business logic a lot less work than implementing the same functionality for every operation using the fine-grain approach.

You have the option of using a stateful or stateless session facade, most of the time a session facade will be stateless, but if you need to maintain conversational state then you need to use a stateful session facade, it also has the benefit of an extended persistence context that helps you keep the entities in a managed state that may span across a transaction (for example a user filling in a registration form) and the persistence context is automatically closed when the bean instance is removed.

Session Beans

Session beans are used to build the business logic, you access these beans via the presentation or web tier, they can be either accessed using either dependency injection or JNDI lookup. Dependency injection is supported only with managed classes (classes such as servlets), JNDI can support both managed and unmanaged classes, below is a table detailing what is and what is not supported

ClassType
Injection
Servlets, filters, event listeners
Yes
JSP tag handlers, library event listeners
Yes
JSF managed beans
Yes
Helper classes, JSPs
No

Dependency injection is the easiest way to access an EJB 3 session bean, you can obtain a reference to an EJB in your managed class by using injection in two ways

The examples below show that using the annotation is clean and straightforward, but again it's a personal choice.

@EJB example public class ActionBazaarBidControllerServlet extends HttpServlet {
  @EJB private PlaceBid placebid;

  ...
  placeBid.add(bidderId, itemId, bidPrice);
  ...
}
XML example <ejb-ref>
  <ejb-ref-name>PlaceBid</ejb-ref-name>
  <ejb-ref-type>Session</ejb-ref-type>
  <remote>actionbazaar.buslogic.PlaceBid</remote>
  <injection-target>
    <injection-target-name>placeBid</injection-target-name>
    <injection-target-class>actionbazaar.web.ActionBazaarBidControllerServlet</injection-target-class>
  </injection-target>
<ejb-ref>

You must try and avoid injecting stateful session beans into classes that are multi-threaded in nature, such as servlets and JSF managed beans. Take the below example, the single BidderAccountCreator instance will be shared by all users of the servlet, this is not the desired behavior since loginfo, biographicalInfo and billingInfo are designed to be specific to an individual client. It becomes even trickier when you have a @Remove annotation because when a particular user invokes cancelAccountCreation method, the EJB instance will be destroyed. The next time a user tries to access the BidderAccountCreator EJB, the container will generate a javax.ejb.NoSuchEJBException. If you want to use stateful session beans in your application then you need to perform a JNDI lookup and store the returned EJB instance in the HttpSession object. This way, the same user reaches the same bean instance for future activity, it also scopes the EJB instance to the users session.

Avoid injecting stateful session beans @Stateful(name="BidderAccountCreator")
public class BidderAccountCreatorBean {

  private LoginInfo loginInfo;
  private BiographicalInfo biographicalInfo;
  private BillingInfo billingInfo;

  @Remove
  public void cancelAccountCreation() {
    ...
  }
}

Session beans and Helper classes

There are occasions when you need to access a session bean from a class that is not managed, remember that dependency injection is not supported in non-managed classes, thus you must use JNDI lookup. For looking up an EJB from a helper class you have to do the following:

  1. Establish the reference to EJB by using the @EJB annotation at the class level or the ejb-ref (ejb-local-ref) descriptor element
  2. Lookup the session bean

You can work around the above by specifying references using then @EJB annotation or the web.xml descriptor element by using the global JNDI name. Containers may implement different ways in doing this, two examples are below

Global JNDI lookup PlaceBid placeBid = (PlaceBid) context.lookup("PlaceBid");

PlaceBid placeBid = (PlaceBid) context.lookup("actionbazaar.buslogic.PlaceBid");

When using stateful session beans you have to establish a reference or dependency using the @EJB annotation at the class level and then look up the bean from the environment naming context using the reference name specified

working with Stateful session beans @EJB(name="UserRegistrationBean", beanInterface = UserRegistration.class)
public class ActionBazaarRegistrationControllerServlet extends HttpServlet {
   ...
}

try {
  InitialContext ctx = new InitialContext();
  userReg = (UserRegistration) ctx.lookup("java:comp/env/UserRegistrationBean");
  
  session.setAttribute("user_reg", userReg);

} catch (Exception e) {
  handleException(e);
}

Applications can be all sorts from Swing or SWT applications or web container applications like Tomcat all of which work outside the container, thus they do not have support for a container-managed EntityManager or JTA transaction, also testing your entities with a framework like JUnit will be outside the container. To use JPA without a EJB 3 container you need to create a persistence unit and create an EntityManager from the EntityManagerFactory. A persistence unit requires a DataSource configured in the application, this is used to connect to the database, the persistence unit is configured in the persistence.xml file, below is an example of a persistence unit ( I have created two data sources, basically two database connections), I am using Oracle as the database.

Datasource <datasources>
 <local-tx-datasource>
  <jndi-name>OracleDS</jndi-name>
  <connection-url>jdbc:oracle:thin:@localhost:1521:d01</connection-url>
  <driver-class>oracle.jdbc.driver.OracleDriver</driver-class>
  <user-name>eclipse</user-name>
  <password>eclipse</password>
  <exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter</exception-sorter-class-name>
  <check-valid-connection-sql>select * from dual</check-valid-connection-sql>
  <metadata>
    <type-mapping>Oracle9i</type-mapping>
  </metadata>
 </local-tx-datasource>

 <local-tx-datasource>
  <jndi-name>TitanDS</jndi-name>
  <connection-url>jdbc:oracle:thin:@localhost:1521:d01</connection-url>
  <driver-class>oracle.jdbc.driver.OracleDriver</driver-class>
  <user-name>titan</user-name>
  <password>titan</password>
  <exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter</exception-sorter-class-name>
  <check-valid-connection-sql>select * from dual</check-valid-connection-sql>
  <metadata>
    <type-mapping>Oracle9i</type-mapping>
  </metadata>
 </local-tx-datasource>

</datasources>

Persistent Unit <persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
             version="1.0" >

   <persistence-unit name="titanJPA">
   <provider>org.hibernate.ejb.HibernatePersistence</provider>
   <jta-data-source>java:TitanDS</jta-data-source>
   <properties>
      <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle9iDialect"/>
      <property name="hibernate.hdm2ddl.auto" value="update"/>
      <property name="hibernate.show_sql" value="true"/>
      <property name="hibernate.format_sql" value="true"/>
      <property name="hibernate.use_sql_comments" value="true"/>
   </properties>
   </persistence-unit>
</persistence>