Section 6.16. Iteration 2Move Business Logic Out of the Controller


6.16. Iteration 2Move Business Logic Out of the Controller

In this Iteration, we're going to move all business logic out of the Controller Servlet into the InventoryFacadeBean (which groups our synchronous activities together and wraps the DAO). We'll take the following steps:

  • Move the code from the Controller Servlet's actions into the InventoryFacadeBean.

  • Modify the Controller Servlet to call the new methods in the InventoryFacadeBean.

  • Modify the HibernateCarDAO to work with CMT.

  • Change the ServiceLocator's getHibernateSession( ) method to work with CMT.

6.16.1. Refactoring the Business Logic

If you'll recall from earlier in this chapter, we had modified the Controller Servlet so its viewCarList action used the InventoryFacadeBean to find all available cars. We will now refactor the Controller Servlet by moving all the business logic from its actions into the InventoryFacadeBean. Example 6-21 shows the modifications to the Controller Servlet.

Example 6-21. ControllerServlet.java
 package com.jbossatwork; ... import com.jbossatwork.ejb.*; ... import javax.ejb.*; ... public class ControllerServlet extends HttpServlet {     ...     protected void processRequest(HttpServletRequest request,       HttpServletResponse response) throws ServletException, IOException     {         ...         InventoryFacadeLocalHome inventoryHome;         inventoryHome = (InventoryFacadeLocalHome)         ServiceLocator.getEjbLocalHome(InventoryFacadeLocalHome.COMP_NAME);            InventoryFacadeLocal inventory = null;         try {             inventory = inventoryHome.create(  );         } catch (CreateException ce) {             throw new RuntimeException(ce.getMessage(  ));         }         // perform action         if (VIEW_CAR_LIST_ACTION.equals(actionName))         {             request.setAttribute("carList", inventory.listAvailableCars(  ));             destinationPage = "/carList.jsp";         }         else if (ADD_CAR_ACTION.equals(actionName))         {             request.setAttribute("car", new CarDTO(  ));             destinationPage = "/carForm.jsp";         }         else if (EDIT_CAR_ACTION.equals(actionName))         {             int id = Integer.parseInt(request.getParameter("id"));             request.setAttribute("car", inventory.findCar(id));             destinationPage = "/carForm.jsp";         }         else if (SAVE_CAR_ACTION.equals(actionName))         {             // build the car from the request parameters             CarDTO car = new CarDTO(  );             car.setId(Integer.parseInt(request.getParameter("id")));             car.setMake(request.getParameter("make"));             car.setModel(request.getParameter("model"));             car.setModelYear(request.getParameter("modelYear"));             // save the car             inventory.saveCar(car);             // prepare the list             request.setAttribute("carList", inventory.listAvailableCars(  ));             destinationPage = "/carList.jsp";         }         else if (DELETE_CAR_ACTION.equals(actionName))         {             // get list of ids to delete             String[  ] ids = request.getParameterValues("id");             // delete the list of ids             inventory.deleteCars(ids);             // prepare the list             request.setAttribute("carList", inventory.listAvailableCars(  ));             destinationPage = "/carList.jsp";         }         ...     } } 

So, rather than making DAO calls directly from the Controller Servlet, we're now calling the InventoryFacadeBean to execute the business logic. Let's show the changes in the InventoryFacadeBean(Example 6-22).

Example 6-22. InventoryFacadeBean.java
 package com.jbossatwork.ejb; import java.util.*; import javax.ejb.*; import com.jbossatwork.dao.*; import com.jbossatwork.dto.CarDTO; public class InventoryFacadeBean implements SessionBean {     ...     /**      * @ejb.interface-method      * @ejb.transaction      *  type="Required"      *      */     public List listAvailableCars(  ) throws EJBException {         CarDAO carDAO = new HibernateCarDAO(  );         return carDAO.filterByStatus(CarDTO.STATUS_AVAILABLE);     }     /**      * @ejb.interface-method      * @ejb.transaction      *  type="Required"      *      */     public CarDTO findCar(int id) throws EJBException {         CarDAO carDAO = new HibernateCarDAO(  );         return carDAO.findById(id);     }     /**      * @ejb.interface-method      * @ejb.transaction      *  type="Required"      *      */     public void deleteCars(String[  ] ids) throws EJBException {         CarDAO carDAO = new HibernateCarDAO(  );         if (ids != null) {             carDAO.delete(ids);         }     }     /**      * @ejb.interface-method      * @ejb.transaction      *  type="Required"      *      */     public void saveCar(CarDTO car) throws EJBException {         CarDAO carDAO = new HibernateCarDAO(  );         if (car.getId(  ) =  = -1) {             carDAO.create(car);         } else {             carDAO.update(car);         }     } } 

Again, there isn't anything too exciting going on here. We've taken all the DAO-related code from the Controller Servlet and created new methods in the InventoryFacadeBean:


findCar( )

Finds a car based on the caller-supplied Car ID.


deleteCars( )

Deletes one or more cars based on the array of caller-supplied Car IDs.


saveCar( )

Creates the Car if it doesn't exist in the database; updates the Car if it's already in the database.

Notice that even the read-only methods (listAvailableCars( ) and findCar( ) ) require a transaction. We need run inside a transaction because of how we're getting the Hibernate Sessionthis is a new wrinkle introduced with Hibernate 3. Each method-level @ejb.transaction tag sets the transaction attribute for an EJB's business method in ejb-jar.xml. We'll cover Hibernate 3 and CMT in the next couple of sections.

You may be wondering why we've taken the trouble to factor our business logic out of the Controller Servlet into a Session Bean. We refactored for a couple of reasons:

  • Running our code from within an EJB enables us to use Container-Managed Transactions (CMT), and you'll see in the next section that this greatly reduces the amount of code you have to write.

  • We may not always have a web client and we'd like to encapsulate our business logic so other clients can use our services. In the Web Services chapter, we'll expose one of the InventoryFacadeBean's methods as a Web Service.

We could've gone one step further and factored all the business logic in the InventoryFacadeBean into another POJO known as an Application Service, a Core J2EE Pattern. If set up correctly, an Application Service enables you to test your business logic outside of JBoss. But since this isn't a JUnit book, we leave this refactoring to the reader.

6.16.2. Hibernate 3 and CMT

Until now, we've managed Hibernate transactions programmatically. Now that we're inside an EJB and using CMT, we don't have to perform user-managed transactions in Hibernate anymore. Now that the container manages transactions for us, we can remove the Hibernate API calls that set up and tear down our transactions. Example 6-23 contains the original HibernateCarDAO's (first introduced in Chapter 5) read-only findById( ) method that closed its Hibernate Session and the update( ) method that managed its own transaction.

Example 6-23. HibernateCarDAO.java
 ... public class HibernateCarDAO implements CarDAO {     ...     public CarDTO findById(int id)     {         CarDTO car = null;         Session session = null;         try         {             session = ServiceLocator.getHibernateSession(                                   HIBERNATE_SESSION_FACTORY);             car = (CarDTO) session.get(CarDTO.class, new Integer(id));         }         catch (Exception e)         {             System.out.println(e);         }         finally         {             try             {                 if (session != null) {session.close(  );}             }             catch (Exception e)             {                 System.out.println(e);             }         }         return car;     }     ...     public void update(CarDTO car)     {         Session session = null;         Transaction tx = null;         try         {             session = ServiceLocator.getHibernateSession(                                   HIBERNATE_SESSION_FACTORY);             tx = session.beginTransaction(  );             session.update(car);             tx.commit(  );         }         catch (Exception e)         {             try{tx.rollback(  );}             catch(Exception e2){System.out.println(e2);}             System.out.println(e);         }         finally         {             try             {                 if (session != null) {session.close(  );}             }             catch (Exception e)             {                 System.out.println(e);             }         }     }     ... } 

Most of the code is concerned with transaction and session setup and tear down. Now let's look at the new HibernateCarDAO that uses Container-Managed Transactions in Example 6-24.

Example 6-24. HibernateCarDAO.java
 ... public class HibernateCarDAO implements CarDAO {     private List carList;     private static final String HIBERNATE_SESSION_FACTORY =                       "java:comp/env/hibernate/SessionFactory";     public HibernateCarDAO(  ){  }     public List findAll(  )     {         List carList = new ArrayList(  );         Session session = null;         try         {             session = ServiceLocator.getHibernateSession(                                   HIBERNATE_SESSION_FACTORY);             Criteria criteria = session.createCriteria(CarDTO.class);             carList = criteria.list(  );         }         catch (Exception e)         {             System.out.println(e);         }         return carList;     }     public List filterByStatus(String status)     {         List availableCarList = new ArrayList(  );         Session session = null;         try         {             session = ServiceLocator.getHibernateSession(                                   HIBERNATE_SESSION_FACTORY);             Criteria criteria = session.createCriteria(CarDTO.class)                      .add( Restrictions.eq("status", status));             availableCarList = criteria.list(  );         }         catch (Exception e)         {             System.out.println(e);         }         return availableCarList;     }     public CarDTO findById(int id)     {         CarDTO car = null;         Session session = null;         try         {             session = ServiceLocator.getHibernateSession(                                   HIBERNATE_SESSION_FACTORY);             car = (CarDTO) session.get(CarDTO.class, new Integer(id));         }         catch (Exception e)         {             System.out.println(e);         }         return car;     }     public void create(CarDTO car)     {         Session session = null;         try         {             session = ServiceLocator.getHibernateSession(                                   HIBERNATE_SESSION_FACTORY);             session.save(car);         }         catch (Exception e)         {             System.out.println(e);         }     }     public void update(CarDTO car)     {         Session session = null;         try         {             session = ServiceLocator.getHibernateSession(                                   HIBERNATE_SESSION_FACTORY);             session.update(car);         }         catch (Exception e)         {             System.out.println(e);         }     }     public void delete(String[  ] ids)     {         Session session = null;         try         {             session = ServiceLocator.getHibernateSession(                                   HIBERNATE_SESSION_FACTORY);             for (int i = 0; i < ids.length; i++)             {                 CarDTO car = (CarDTO) session.get(CarDTO.class,                                       new Integer(ids[i]));                 session.delete(car);             }         }         catch (Exception e)         {             System.out.println(e);         }     } } 

You should notice the following differences in the new HibernateCarDAO:

  • The read-only methods no longer need to close their Session, resulting in a 1/3 reduction in the lines of code.

  • The transactional methods no longer use the Hibernate transaction API calls, nor do they close their Session. These changes reduce the code by 50 percent. The transactional methods don't close the Session anymore because you lose your changes if you perform an update or delete operation and then close the Session.

  • Most importantly, the business purpose of the code is clearer.

We've retrofitted the HibernateCarDAO so it works with Container-Managed Transactions, but you're not done yet. We also have to modify how we're getting the Hibernate Session in the ServiceLocator's getHibernateSession( ) method. Previously, we called openSession( ), as in Example 6-25.

Example 6-25. ServiceLocator.java
    ... public class ServiceLocator {     ...     public static Session getHibernateSession(String jndiSessionFactoryName)     throws ServiceLocatorException {         Session session = null;         ...         session = getHibernateSessionFactory(jndiSessionFactoryName)         .openSession(  );         ...         return session;     }     ... } 

To work within a transaction, we had to modify the getHibernateSession( ) method, as in Example 6-26.

Example 6-26. ServiceLocator.java
 ... public class ServiceLocator {     ...     public static Session getHibernateSession(String jndiSessionFactoryName)     throws ServiceLocatorException {         Session session = null;         ...         session = getHibernateSessionFactory(jndiSessionFactoryName)         .getCurrentSession(  );         ...         return session;     }     ... } 

So, rather than using the Session Factory's openSession( ) method to open a new Session, we call the getCurrentSession( ) method to get the Session that's part of the current transactional context managed by the container. Your code must run within a transaction, or the Session Factory's getCurrentSession( ) method will throw a NullPointerExceptionthat's why each of the InventoryFacadeBean's methods had a transaction setting of Required.



JBoss at Work. A Practical Guide
JBoss at Work: A Practical Guide
ISBN: 0596007345
EAN: 2147483647
Year: 2004
Pages: 197

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net