CHAPTER 7

image

Integrating Session Beans, Entities, Message-Driven Beans, and Web Services

Introduction

Previous chapters in this book covered the individual components of EJB and related technologies. These included session beans, message-driven beans (MDBs), stateless session beans as Web services, and JPA entities. In this chapter, we will show you how to integrate these components into a complete Java EE 7 application using a fictitious wine store application as an example.

Application Overview

The sample application that we are going to develop in this chapter is the Wines Online application, which provides customers (either individuals or distributors) with a variety of search criteria with which they can browse, select wines, add them to a shopping cart, and process their order. Customers need to register with the Wines Online application before they can submit orders. Once an order is submitted, the customer’s credit card is validated, and this triggers an order processing message and an e-mail notification to the customer on the status of the order.

Screens used by the customer to search, navigate, and submit orders are part of a simple JavaServer Faces (JSF) Web application that interacts with back-end services and components developed using EJBs. In this chapter, our focus will be on developing the back-end part of the application, which can be tested with both a servlet and a simple Java client. In Chapter 12, we will develop a JSF client for these back-end services.

As shown in Figure 7-1, the Wines Online application consists of several back-end components that are used by the client application, and these back-end components in turn make use of different types of services to perform CRUD (create, retrieve, update, delete) operations, send e-mail notifications to the customer about order status, process the incoming orders, and verify the credit card status of the customer. We will discuss the behavior and functionality of these components in the next section.

9781430246923_Fig07-01.jpg

Figure 7-1.  The Wines Online application architecture

Application Components and Services

The back-end wine store application consists of different types of EJBs that are created as components and services. The key components and services are described in the following sections.

The Shopping Cart Component

The shopping cart component manages the cart items for a registered customer who has logged into the application. This component is a stateful session bean, and it keeps track of cart items (wines and quantity) that are added to and removed from the shopping cart. Finally, the shopping cart component transfers the order information to the order-processing component.

The Search Façade Component

The search façade component allows the customer to retrieve all wines or search for wines using criteria such as year, country, and varietal. This component is a stateless session bean, and it returns a list of wines based on executed search criteria.

The Customer Façade Component

The customer façade component allows customers to register themselves as members of the wine application and also retrieve customer information based on their e-mail addresses. This component is a stateless session bean.

The Order Processing Façade Component

The order processing façade component acts as a coordinator between the credit service and the order processing service. This component processes the cart items and creates a purchase order (PO) that can be consumed by the order processing service. This component is a stateless session bean.

Persistence Services

Persistence services comprise a packaged persistence unit that consists of a set of entities that are mapped to the wine store database schema. All other components and services in the back-end part of the application use this common persistence unit to perform CRUD operations.

The E-mail Service

The e-mail service is an MDB that sends out an e-mail to the customer about the status of the order submitted. This service is essentially the same MDB that we built in Chapter 5, and it requires the same JMS and Java Mail Session resources.

The Credit Service

The credit service is a Web service that is consumed by the application. This service takes credit card information as an input message and returns the status of the credit for a particular customer. We will use the credit service developed in Chapter 6.

The Order Processing Service

The order processing service is an MDB that does the bulk of the order processing after a PO has been received.

The Wines Online Application Business Process

Figure 7-2 illustrates the process flow for the wine application. Once the shopping cart service submits the customer and cart item information to the order processing façade, it verifies that the customer credit card information is accurate and the card is valid before proceeding to process the order. After approval is received from the credit service, it creates a purchase order that contains the customer and order information and submits it to the order processing service. If a negative response is received from the credit service, the order is cancelled, and the e-mail service sends a notification to the customer. Once a purchase order is received by the order processing service, it proceeds to fulfill the order, which entails updating the inventory and sending an e-mail notification on the status of the order to the customer using the e-mail service.

9781430246923_Fig07-02.jpg

Figure 7-2.  The Wines Online application business process

Figure 7-3 illustrates the interactions that occur among the components and services of the wine application. A step-by-step explanation of these interactions follows the diagram.

9781430246923_Fig07-03.jpg

Figure 7-3.  The Wines Online application component and service interactions

In-Depth Component/Service Walkthrough

In the following sections, we will walk through the code for each individual component to explain its interaction with the other components and services. Each of the listings contains code that is included in the Chapter07-IntegratedServices sample application. At the end of the chapter, you will find step-by-step instructions on how to configure, build, deploy, and execute this application.

Persistence Services

Persistence services present a domain model to the application in the form of a JPA persistence unit. This persistence unit is comprised of JPA entities along with an EntityManager for performing CRUD operations on the entities and for managing the persistent state of those entities in a transactional context.

Figure 7-4 illustrates the entities, the inheritance models, and the relationships between them. The Customer entity is inherited by the Individual and Distributor entities. InventoryItem, CartItem, and OrderItem entities inherit the WineItem entity. The BusinessContact entity is inherited by the Supplier entity. The wine store persistence unit also contains different types of relationships between these entities (including one-to-one, one-to-many, and many-to-many), which will be accessed from the application code. The mappings used in these entities were covered in Chapter 3 and Chapter 4. We will focus on the code that is used in other components of this application to integrate this persistence unit.

9781430246923_Fig07-04.jpg

Figure 7-4.  The Wines Online domain model

The Customer Façade Component

CustomerFacadeBean is a stateless session bean. It provides business methods to allow the client application either to query the customer based on its e-mail address or perform CRUD operations on the Customer entity and its subclasses. This façade is injected with an EntityManager via the @PersistenceContext annotation that it uses to perform CRUD operations on Customer entities. Listing 7-1 shows the getCustomerFindByEmail method in CustomerFacadeBean. This method calls the named query Customer.findByEmail defined in the Customer entity of the wine store persistence unit using the createNamedQuery() method in the EntityManager.

image Note   createNamedQuery() is a method in the javax.persistence.EntityManager interface. This method creates an instance of javax.persistence.Query for executing a named query specified in JPQL (Java Persistence Query Language) or native SQL.

Listing 7-1.  The getCustomerFindByEmail Method

public Customer getCustomerFindByEmail(String email) {
  return em.createNamedQuery("Customer.findByEmail", Customer.class).setParameter("email", email).  getSingleResult();
}

Listing 7-2 shows the complete code for CustomerFacadeBean, which has the business methods to perform CRUD operations using the EntityManager. This bean class does not implement Local or Remote interfaces, and so the EJB container calls the bean class directly in Local mode.

image Note   In Chapter 2, we provided the details on the differences between remote and client view architectures for session beans with a summary of advantages and disadvantages along with a discussion of the no-interface mode introduced in EJB 3.1. Chapter 3 provided the details on the methods that are available on the EntityManager to perform the CRUD operations.

Listing 7-2.  CustomerFacadeBean.java

@Stateless(name = "CustomerFacade", mappedName = "Chapter07-IntegratedSamples-Chapter07-ServiceIntegration-ejb-CustomerFacade")
public class CustomerFacadeBean {
  @PersistenceContext(unitName = "Chapter07-WineAppUnit-JTA")
  private EntityManager em;

  public <T> T persistEntity(T entity) {
    em.persist(entity);
    return entity;
  }

  public <T> T mergeEntity(T entity) {
    return em.merge(entity);
  }

  public void removeCustomer(Customer customer) {
    customer = em.find(Customer.class, customer.getId());
    em.remove(customer);
  }

  /**
   * <code>select o from Customer o</code>
   */
  public List<Customer> getCustomerFindAll() {
    return em.createNamedQuery("Customer.findAll", Customer.class).getResultList();
  }

  public Customer getCustomerFindById(Integer id) {
    return em.find(Customer.class, id);
  }

  /**
   * <code>select o from Customer o where o.email = :email</code>
   */
  public Customer getCustomerFindByEmail(String email) {
    return em.createNamedQuery("Customer.findByEmail", Customer.class).setParameter("email", email).    getSingleResult();
  }

  public Customer registerCustomer(Customer customer) {
    return persistEntity(customer);
  }
}

The Search Façade Component

SearchFacadeBean is a stateless session bean. This bean provides business methods that allow the client application to query the Wine entity in the persistence unit based on the year, country, and varietal of the wine. The persistence unit in SearchFacadeBean is injected via the @PersistenceContext annotation. Listing 7-3 shows the complete code for SearchFacadeBean, which has the business methods to perform search operations using the EntityManager. This bean class has a business interface, SearchFacadeLocal, which supports local client access. The methods getWineFindAll(), getWineFindByYear(), getWineFindByCountry(), and getWineFindByVarietal() call the respectively named queries defined in the Wine entity of the wine store persistence unit using the createNamedQuery() method in the EntityManager, and return zero or more Wine objects in java.util.List.

Listing 7-3.  SearchFacadeBean.java|

@Stateless(name = "SearchFacade", mappedName = "Chapter07-IntegratedSamples-Chapter07-ServiceIntegration-ejb-SearchFacade")
public class SearchFacadeBean implements SearchFacadeLocal {
  @PersistenceContext(unitName = "Chapter07-WineAppUnit-JTA")
  private EntityManager em;

  public Object queryByRange(String jpqlStmt, int firstResult, int maxResults) {
    Query query = em.createQuery(jpqlStmt);
    if (firstResult > 0) {
      query = query.setFirstResult(firstResult);
    }
    if (maxResults > 0) {
      query = query.setMaxResults(maxResults);
    }
    return query.getResultList();
  }

  public <T> T persistEntity(T entity) {
    em.persist(entity);
    return entity;
  }

  public <T> T mergeEntity(T entity) {
    return em.merge(entity);
  }

  public void removeWine(Wine wine) {
    wine = em.find(Wine.class, wine.getId());
    em.remove(wine);
  }

  /**
   * <code>select object(o) from Wine o</code>
   */
  public List<Wine> getWineFindAll() {
    return em.createNamedQuery("Wine.findAll", Wine.class).getResultList();
  }

  /**
   * <code>select object(wine) from Wine wine where wine.year = :year</code>
   */
  public List<Wine> getWineFindByYear(Integer year) {
    return em.createNamedQuery("Wine.findByYear", Wine.class).setParameter("year", year).    getResultList();
  }

  /**
   * <code>select object(wine) from Wine wine where wine.country = :country</code>
   */
  public List<Wine> getWineFindByCountry(String country) {
    return em.createNamedQuery("Wine.findByCountry", Wine.class).setParameter("country", country).    getResultList();
  }

  /**
   * <code>select object(wine) from Wine wine where wine.varietal = :varietal</code>
   */
  public List<Wine> getWineFindByVarietal(String varietal) {
    return em.createNamedQuery("Wine.findByVarietal", Wine.class).setParameter("varietal",     varietal).getResultList();
  }
}

The Shopping Cart Component

ShoppingCartBean is a stateful session bean, and uses an extended persistence context. This bean preserves the state of a customer who is logged into the system and is currently either adding or removing wine items from the shopping cart. Once the customer submits an order, ShoppingCartBean sends the customer information to the order processing façade, which takes care of processing the order. We have decided to use a stateful session bean for this use case for the following reasons:

  • There is more than one type of client application that accesses the back-end application (Web, Swing, and command-line client).
  • We want to show the usage of EJB stateful session beans in a typical application.

image Note   In general, there is a common belief that stateful session beans are a bit heavyweight to store the state (as compared to storing the state on the client using an HTTP session for Web clients). There is evidence that there isn’t a drastic cost in terms of either performance or transactions associated with stateful session beans. (See J2EE Performance Testing with BEA WebLogic Server, by Peter Zadrozny [Apress, 2003], for thorough coverage of this topic.)

ShoppingCartBean has the business methods described in the following subsections.

Finding Customers

Listing 7-4 shows the findCustomer() method in ShoppingCartBean. This method uses the injected CustomerFacadeBean and calls the findCustomerByEmail() method to get an instance, which in our case happens to be an Individual sub-entity. Once a Customer is retrieved, it is assigned to a class-level attribute so that it can be referenced subsequently through other methods on this stateful session bean. Because we are using an extended persistence context, this instance will remain managed throughout the life of the bean, through transaction boundaries, unless it is actively removed from the persistence context.

Listing 7-4.  The findCustomer Business Method

public Customer findCustomer(String email) {
  customer = customerFacade.getCustomerFindByEmail(email);
  return customer;
}

Adding Wine Items

Listing 7-5 shows the addWineItem() method in ShoppingCartBean. This business method is called by the client application to add a particular wine item to the shopping cart along with the quantity. The code creates a new instance of the CartItem entity and sets the quantity, wine, and time of creation using the setter methods in CartItem.

image Note   A quick refresher on the three states of an entity instance: managed, detached, and new. A managed instance is one that is actively referenced by a persistence context, and all changes made to that instance are tracked by the ­persistence context. When the persistence context is synchronized with the database, all pending changes found in ­managed instances are flushed to the database through SQL statements. A detached entity instance is one that is no longer referenced by a persistence context, and so its changes are not being tracked. If a client modifies a detached instance, the client needs to ensure that those changes are merged into a managed instance (for instance, through an EntityManager.merge() call), or else the changes will never be synchronized with the database. A new entity instance is like a detached instance, only it has not yet been persisted in the database.

The addWineItem() method creates a new CartItem with the specified properties and adds it to the Customer instance. Because we queried the Customer instance and cached it in a class-level variable on our stateful session bean, we can count on it being in a managed state. ShoppingCartBean uses container-managed transactions, and the addWineItem() method defaults to using TransactionAttributeType.REQUIRED, so a transaction will be started and committed during the course of this method. When the commit is performed, the persistence context will be synchronized with the database, and any changes pending in our managed customer instance will be pushed out to the database. Because of the cascade rule on Customer’s cartItemList property, any referenced CartItem instances will also be persisted. The result of this method call, then, will be a new row in CartItem’s table with a foreign key reference to the Customer table (or Individual table, depending on how the entity inheritance hierarchy is mapped).

Listing 7-5.  The addWineItem Business Method

public void addWineItem(Wine wine, int quantity) {
  CartItem cartItem = new CartItem(quantity, wine);
  customer.addCartItem(cartItem);
}

Removing Wine Items

Listing 7-6 shows the removeWineItem() method in ShoppingCartBean. This method lets the client applications remove the items from the shopping cart when requested by the end user (via the user interface). Again, because the customer attribute is a managed instance, any changes applied to it will be persisted at the conclusion of this method when the CMT transaction created at the outset of this method is committed.

Listing 7-6.  The removeWineItem Business Method

public void removeWineItem(CartItem cartItem) {
  customer.removeCartItem(cartItem);
}

Submitting Orders to the Order Processing Façade

Listing 7-7 shows the sendOrderToOPC() method in ShoppingCartBean. This method is called by the client applications to submit the order when the end user finally decides to buy one or more wines. This method uses the injected OrderProcessFacadeBean and calls the processOrder() method to submit the order by passing the customer object.

image Note   The rationale behind the naming of the sendOrderToOPC() method was that OrderProcess image FacadeBean, which receives the purchase orders, in effect acts as an “order processing center.” With this in mind, we decided to ­abbreviate “order processing center” to OPC so as to avoid an extremely long method name.

Listing 7-7.  The sendOrderToOPC Business Method

public String sendOrderToOPC() {
  String result = null;
  try {
    orderProcessFacade.processOrder(customer);
    result = "Your Order has been submitted - you will be notified about the status via email";
  } catch (Exception ex) {
    ex.printStackTrace();
    result = "An error occurred while processing your order. Please contact Customer Service.";
  }

  return result;
}

Retrieving the Customer’s Cart Items

Listing 7-8 shows the getCartItems() method in ShoppingCartBean. This method retrieves the current list of cart items from the customer field (which may be either an Individual or a Distributor entity type). This method will be useful when we build a JSF application in which we can show all of the cart items in the user interface before the customer submits the order.

Listing 7-8.  The getCartItems Business Method

public List<CartItem> getCartItems() {
  return customer.getCartItemList();
}

The complete code for ShoppingCartBean is shown in Listing 7-9.

Listing 7-9.  ShoppingCartBean.java

@Stateful(name = "ShoppingCart", mappedName = "Chapter07-IntegratedSamples-Chapter07-ServiceIntegration-ejb-ShoppingCart")
public class ShoppingCartBean implements ShoppingCartLocal {
  @PersistenceContext(unitName = "Chapter07-WineAppUnit-JTA", type = PersistenceContextType.  EXTENDED)
  private EntityManager em;
  private Customer customer;
  @EJB
  private CustomerFacadeBean customerFacade;
  @EJB
  private OrderProcessFacadeBean orderProcessFacade;

  public Customer getCustomer() {
    return customer;
  }

  public void addWineItem(Wine wine, int quantity) {
    CartItem cartItem = new CartItem(quantity, wine);
    customer.addCartItem(cartItem);
  }

  public void addWineItem(Wine wine) {
    CartItem cartItem = new CartItem();
    cartItem.setQuantity(20);
    wine = em.find(Wine.class, wine.getId());
    cartItem.setWine(wine);
    cartItem.setCreatedDate(new Timestamp(System.currentTimeMillis()));
    customer.addCartItem(cartItem);
  }

  public void removeWineItem(CartItem cartItem) {
    customer.removeCartItem(cartItem);
  }

  public void addCartItemsTemporarily() {
    List<Wine> wines = em.createNamedQuery("findAllWine").getResultList();
    for (Wine wine : wines) {
      final CartItem cartItem = new CartItem();
      cartItem.setCreatedDate(new Timestamp(System.currentTimeMillis()));
      cartItem.setQuantity(20);
      cartItem.setWine(wine);
      customer.addCartItem(cartItem);
    }
  }

  public Customer findCustomer(String email) {
    customer = customerFacade.getCustomerFindByEmail(email);
    return customer;
  }

  public String sendOrderToOPC() {
    String result = null;
    try {
      orderProcessFacade.processOrder(customer);
      result = "Your Order has been submitted - you will be notified about the status via email";
    } catch (Exception ex) {
      ex.printStackTrace();
      result = "An error occurred while processing your order. Please contact Customer Service.";
    }

    return result;
  }

  public <T> T persistEntity(T entity) {
    em.persist(entity);
    return entity;
  }

  public <T> T mergeEntity(T entity) {
    return em.merge(entity);
  }

  public void removeCartItem(CartItem cartItem) {
    cartItem = em.find(CartItem.class, cartItem.getId());
    em.remove(cartItem);
  }

  public List<CartItem> getCartItems() {
    return customer.getCartItemList();
  }

  public void removeWine(Wine wine) {
    wine = em.find(Wine.class, wine.getId());
    em.remove(wine);
  }

  /**
   * <code>select object(o) from Wine o</code>
   */
  public List<Wine> getWineFindAll() {
    return em.createNamedQuery("Wine.findAll", Wine.class).getResultList();
  }

  /**
   * <code>select object(wine) from Wine wine where wine.year = :year</code>
   */
  public List<Wine> getWineFindByYear(Integer year) {
    return em.createNamedQuery("Wine.findByYear", Wine.class).setParameter("year", year).    getResultList();
  }

  /**
   * <code>select object(wine) from Wine wine where wine.country = :country</code>
   */
  public List<Wine> getWineFindByCountry(String country) {
    return em.createNamedQuery("Wine.findByCountry", Wine.class).setParameter("country", country).    getResultList();
  }

  /**
   * <code>select object(wine) from Wine wine where wine.varietal = :varietal</code>
   */
  public List<Wine> getWineFindByVarietal(String varietal) {
    return em.createNamedQuery("Wine.findByVarietal", Wine.class).setParameter("varietal",     varietal).getResultList();
  }
}

The Order Processing Façade Component

OrderProcessFacadeBean is a stateless session bean. This bean provides business methods that are invoked by ShoppingCartBean to submit an order and other methods that interact with the credit and order processing services. OrderProcessFacadeBean has the business methods described in the following subsections.

Credit Check

Listing 7-10 shows the performCreditCheck() method in OrderProcessFacadeBean. This method uses a CreditService that was injected using the @WebServiceRef annotation to get the port of the Web service. Once the port is available, the code can call the creditCheck Web service operation, which takes a credit card number and returns a message on the validity of the card in true or false terms. We are using the credit service that we developed in Chapter 6.

Listing 7-10.  The performCreditCheck Business Method

private boolean performCreditCheck(Individual individual) {
  String ccnum = individual.getCcNum().toString();
  CreditCheckEndpointBean creditService = service.getCreditCheckEndpointBeanPort();
  return creditService.creditCheck(ccnum);
}

Creating a Purchase Order

Listing 7-11 shows the processOrder() method in OrderProcessFacadeBean. ShoppingCartBean calls this business method when an order is submitted by the client application. This method has one parameter, which is of Customer entity type. The customer’s shopping cart is contained in its cartItems list, each member of which references a wine and a quantity added to the cart by the customer while shopping.

To start with, the method checks whether the received entity is managed. If not, a call to the merge() method of the EntityManager is made to retrieve a managed Customer object.

image Note   It is worth noting that when we run this application (following the steps at the end of this chapter), the ­customer parameter will be found in the persistence context, and so the merge step will not be performed. This is because processOrder() is called from an existing transactional context—the transaction that was created when its calling method, ShoppingCartBean.sendOrderToOPC() was invoked. A single persistence context is always bound to a given transaction, and in this case the extended persistence context from ShoppingCartBean is the one associated with this transaction. When sendOrderToOPC() is invoked, an existing context transaction is found and used. When em.contains(customer) is called inside processOrder, the EntityManager for OrderProcessFacadeBean observes that a persistence context is already associated with this transaction, and it uses this persistence context, which is the same extended persistence context associated with ShoppingCartBean. This is why the customer instance is already determined to be managed.

Once the managed customer entity is obtained, it checks whether the customer is an Individual or a Distributor. For individuals, a call to the performCreditCheck() method is made to verify the credit card. If the credit card is found to be invalid, the message “Invalid Credit Card” is sent back to the client application via ShoppingCartBean. For distributors, the memberStatus property is checked, and any value other than “APPROVED” is rejected.

If the customer is approved, then processing continues, and a new order of type CustomerOrder is created. This new order is populated with the collection of CartItem objects associated with the Customer and found in its cartItems property. For each cart item, an OrderItem object is created to capture the quantity of each wine and the total price for the wine in that cart item. The price for each order item is calculated using the available retail price information for each Wine entity.

Once all the cart items have been processed and corresponding order items have been created, the new CustomerOrder containing the order items is persisted, in part to acquire a generated value for its id field, which is used when generating an e-mail from within the call sendPOtoMDB(). The removeCartItem() method is then called on each cart item to remove it from the Customer.

The non-entity utility class PurchaseOrder, which associates a Customer object with a specific CustomerOrder, is created. This Purchase Order instance is passed as an argument to the sendPOtoMDB() method, sending the purchase order to the processing service. Once the call is made, the process becomes asynchronous, and a message is sent back to the client application that the order has been sent for processing.

Since the entire method is executed in a transactional context, changes to the customer object, and any other managed objects, will be persisted when the transaction concludes.

Listing 7-11.  The processOrder Business Method

public String processOrder(Customer customer) {
  String processStatus = null;
  if (!em.contains(customer)) {
    customer = em.merge(customer);
  }

  if (customer instanceof Individual) {
    if (!performCreditCheck((Individual) customer)) {
      processStatus = "Invalid Credit Card number or credit check failed";
    }
  } else if (customer instanceof Distributor) {
    if (!"APPROVED".equals(((Distributor) customer).getMemberStatus())) {
      processStatus = "Distributor credit check rejected";
    }
  }

  if (processStatus == null) {
    CustomerOrder order = new CustomerOrder();
    order.setCreationDate(new Timestamp(System.currentTimeMillis()));
    em.persist(order);

    List<CartItem> cartItems = customer.getCartItemList();
    if (cartItems != null) {
      List<CartItem> tempCartItems = new ArrayList<CartItem>();
      for (CartItem cItem : cartItems) {
        OrderItem oItem = new OrderItem();
        int qty = cItem.getQuantity();
        oItem.setQuantity(qty);
        oItem.setOrderDate(new Timestamp(System.currentTimeMillis()));
        oItem.setWine(cItem.getWine());
        Wine tempWine = cItem.getWine();
        Float d = tempWine.getRetailPrice();
        Float price = d * cItem.getQuantity();
        oItem.setPrice(price);
        order.addOrderItem(oItem);
        tempCartItems.add(cItem);
      }
      for (CartItem cartItem : tempCartItems) {
        customer.removeCartItem(cartItem);
        em.remove(cartItem);
      }
    }
    customer.addCustomerOrder(order);

    PurchaseOrder po = new PurchaseOrder();
    po.setCustomer(customer);
    po.setCustomerOrder(order);
      
    sendPOtoMDB(po);
    processStatus = "Purchase Order sent for processing to the process queue";
  }
  return processStatus;
}

Sending a Purchase Order to the Order Processing Service

Listing 7-12 shows the sendPOtoMDB() method in OrderProcessFacadeBean. This business method makes use of injected Java Messaging Service (JMS) resources for a topic connection factory and a topic. A connection to a topic connection factory is created and the connection is started. Once a connection is available, a session is created and a MessageProducer is created with a topic. An ObjectMessage is created to take the PurchaseOrder object, and the MessageProducer is used to send the PurchaseOrder to the topic.

Listing 7-12.  The sendPOtoMDB Business Method

private void sendPOtoMDB(PurchaseOrder po) {
  //send PO to MDB now
  Connection connection = null;
  Session session = null;
  try {
    connection = poTopicCF.createConnection();
    connection.start();
    session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
    MessageProducer producer = session.createProducer(poTopic);
    ObjectMessage objMessage = session.createObjectMessage();
    objMessage.setObject(po);
    producer.send(objMessage);
  } catch (JMSException e) {
    e.printStackTrace();
  } finally {
    if (session != null) {
      try {
        session.close();
      } catch (JMSException ex) {
        Logger.getLogger(OrderProcessFacadeBean.class.getName()).log(Level.SEVERE, null, ex);
      }
    }
    if (connection != null) {
      try {
        connection.close();
      } catch (JMSException ex) {
        Logger.getLogger(OrderProcessFacadeBean.class.getName()).log(Level.SEVERE, null, ex);
      }
    }
  }
}

The complete code for OrderProcessFacadeBean.java is shown in Listing 7-13.

Listing 7-13.  OrderProcessFacadeBean.java

@Stateless(name = "OrderProcessFacade", mappedName = "OrderProcessFacade")
public class OrderProcessFacadeBean {
  @PersistenceContext(unitName = "Chapter07-WineAppUnit-JTA")
  private EntityManager em;
  @Resource(mappedName = "poTopicConnectionFactory")
  private TopicConnectionFactory poTopicCF;
  @Resource(mappedName = "PurchaseOrderTopic")
  private Topic poTopic;
  @WebServiceRef(type = CreditService.class)
  CreditService service;

  public Object mergeEntity(Object entity) {
    return em.merge(entity);
  }

  public Object persistEntity(Object entity) {
    em.persist(entity);
    return entity;
  }

  public void createNewOrder(CustomerOrder newOrder) {
    persistEntity(newOrder);
  }

  private boolean performCreditCheck(Individual individual) {
    String ccnum = individual.getCcNum().toString();
    CreditCheckEndpointBean creditService = service.getCreditCheckEndpointBeanPort();
    return creditService.creditCheck(ccnum);
  }

  public String processOrder(Customer customer) {
    String processStatus = null;
    if (!em.contains(customer)) {
      customer = em.merge(customer);
    }

    if (customer instanceof Individual) {
      if (!performCreditCheck((Individual) customer)) {
        processStatus = "Invalid Credit Card number or credit check failed";
      }
    } else if (customer instanceof Distributor) {
      if (!"APPROVED".equals(((Distributor) customer).getMemberStatus())) {
        processStatus = "Distributor credit check rejected";
      }
    }

    if (processStatus == null) {
      CustomerOrder order = new CustomerOrder();
      order.setCreationDate(new Timestamp(System.currentTimeMillis()));
      em.persist(order);

      List<CartItem> cartItems = customer.getCartItemList();
      if (cartItems != null) {
        List<CartItem> tempCartItems = new ArrayList<CartItem>();
        for (CartItem cItem : cartItems) {
          OrderItem oItem = new OrderItem();
          int qty = cItem.getQuantity();
          oItem.setQuantity(qty);
          oItem.setOrderDate(new Timestamp(System.currentTimeMillis()));
          oItem.setWine(cItem.getWine());
          Wine tempWine = cItem.getWine();
          Float d = tempWine.getRetailPrice();
          Float price = d * cItem.getQuantity();
          oItem.setPrice(price);
          order.addOrderItem(oItem);
          tempCartItems.add(cItem);
        }
        for (CartItem cartItem : tempCartItems) {
          customer.removeCartItem(cartItem);
          em.remove(cartItem);
        }
      }
      customer.addCustomerOrder(order);

      PurchaseOrder po = new PurchaseOrder();
      po.setCustomer(customer);
      po.setCustomerOrder(order);
      
      sendPOtoMDB(po);
      processStatus = "Purchase Order sent for processing to the process queue";
    }
    return processStatus;
  }

  private void sendPOtoMDB(PurchaseOrder po) {
    //send PO to MDB now
    Connection connection = null;
    Session session = null;
    try {
      connection = poTopicCF.createConnection();
      connection.start();
      session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
      MessageProducer producer = session.createProducer(poTopic);
      ObjectMessage objMessage = session.createObjectMessage();
      objMessage.setObject(po);
      producer.send(objMessage);
    } catch (JMSException e) {
      e.printStackTrace();
    } finally {
      if (session != null) {
        try {
          session.close();
        } catch (JMSException ex) {
          Logger.getLogger(OrderProcessFacadeBean.class.getName()).log(Level.SEVERE, null, ex);
        }
      }
      if (connection != null) {
        try {
          connection.close();
        } catch (JMSException ex) {
          Logger.getLogger(OrderProcessFacadeBean.class.getName()).log(Level.SEVERE, null, ex);
        }
      }
    }
  }
}

The Order Processing Service

The order processing service is an MDB. The idea behind having MDBs in the wine store application is to show how some of the processing in an enterprise application can be done in an asynchronous fashion, and how you can work with the EntityManager, session beans, and other MDBs from an MDB.

image Note   Chapter 5, which covers MDBs, describes the asynchronous architecture in detail and gives examples of some possible implementations.

OrderProcessingMDBBean is a plain old Java object (POJO) that is annotated with a class-level @MessageDriven annotation to indicate that it is an MDB. This POJO implements the mandatory onMessage() method to process the incoming messages with the help of a utility method, processOrder(). We will walk through the methods in the MDB from here.

Listing 7-14 shows the code for the onMessage() method. This method checks whether the received message is of ObjectMessage instance type, and the retrieved object is then typecast to the PurchaseOrder utility class. After that, a call to the processOrder() utility method is made to process the received purchase order.

Listing 7-14.  The onMessage Method in OrderProcessingMDBBean

public void onMessage(Message message) {
  try {
    if (message instanceof ObjectMessage) {
      ObjectMessage objMessage = (ObjectMessage) message;
      Object obj = objMessage.getObject();
      if (obj instanceof PurchaseOrder) {
        po = (PurchaseOrder) obj;
        processOrder(po);
      }
    }
  } catch (JMSException e) {
    e.printStackTrace();
  }
}

Listing 7-15 shows the code for the processOrder() method, which begins by opening up the PurchaseOrder to obtain the Customer and CustomerOrder objects. For each order item in the customer order, a corresponding amount of wine is deducted from the inventory tables. To perform this work, the deductInventory() utility method is called.

After the inventory is deducted, it is time to send a status update to the customer. This is done using the e-mail service. The customer’s e-mail information and order ID are retrieved from the PO object, the e-mail content is constructed, and a call is made to the sendStatus() utility method.

Listing 7-15.  The processOrder Business Method

private void processOrder(PurchaseOrder po) {
  Customer customer = po.getCustomer();
  CustomerOrder order = po.getCustomerOrder();

  for (OrderItem oItem : order.getOrderItemList()) {
    Wine wine = oItem.getWine();
    int qty = oItem.getQuantity();
    deductInventory(wine, qty);
  }

  String from = PopulateDemoData.FROM_EMAIL_ADDRESS;
  String to = customer.getEmail();
  String content =
      "Your order has been processed. "
      + "If you have questions call Beginning EJB Wine Store "
      + "Application with order id # "
      + po.getCustomerOrder().getId().toString();    sendStatus(from, to, content);
}

Listing 7-16 shows the code for the deductInventory() method. This method makes use of the injected EntityManager to call the query named Inventory.findItemByWine defined in the InventoryItem entity. Once the inventory for the specific wine is retrieved, the quantity is updated using the setQuantity() setter method. The inventory item is managed, so its modifications are synchronized with the database when this CMT method ends, or whenever its context transaction commits.

Listing 7-16.  The deductInventory Business Method

private void deductInventory(Wine tempWine, int deductQty) {
  InventoryItem iItem =
          em.createNamedQuery("InventoryItem.findItemByWine",
          InventoryItem.class).setParameter("wine", tempWine).getSingleResult();

  int newQty = iItem.getQuantity() - deductQty;
  iItem.setQuantity(newQty);
}

Listing 7-17 shows the code for the sendStatus() method. This utility method makes use of an injected JMS resource for a topic connection factory and a topic. A connection to a topic connection factory is created and the connection is started. Once a connection is available, a session is created, and a MessageProducer containing the topic is created. A Message object is created, and the JMSType is set to MailMessage. After that, a series of calls to the setStringProperty() method on the Message object is made to create the to, from, subject, and content sections of the e-mail. Once all of the properties are set, the message is sent out to the message topic that will be processed by the e-mail service.

Listing 7-17.  The sendStatus() Business Method

private void sendStatus(String from, String to, String content) {
  try {
    System.out.println("Before status TopicCF connection");
    Connection connection = statusMessageTopicCF.createConnection();
    System.out.println("Created connection");
    connection.start();
    System.out.println("Started connection");
    System.out.println("Starting Topic Session");
    Session topicSession =
            connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

    MessageProducer publisher = topicSession.createProducer(statusTopic);
    System.out.println("created producer");
    MapMessage message = topicSession.createMapMessage();
    message.setStringProperty("from", from);
    message.setStringProperty("to", to);
    message.setStringProperty("subject", "Status of your wine order");
    message.setStringProperty("content", content);
    System.out.println("before send");
    publisher.send(message);
    System.out.println("after send");
  } catch (JMSException e) {
    e.printStackTrace();
  }
}

The complete code for OrderProcessingMDBBean is shown in Listing 7-18.

Listing 7-18.  OrderProcessingMDBBean.java

@MessageDriven(activationConfig = {
  @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic")},
    mappedName = "PurchaseOrderTopic")
public class OrderProcessingMDBBean implements MessageListener {
  private PurchaseOrder po;
  @PersistenceContext(unitName = "Chapter07-WineAppUnit-JTA")
  private EntityManager em;
  @Resource(mappedName = "StatusMessageTopicConnectionFactory")
  private TopicConnectionFactory statusMessageTopicCF;
  @Resource(mappedName = "StatusMessageTopic")
  private Topic statusTopic;

  public void onMessage(Message message) {
    try {
      if (message instanceof ObjectMessage) {
        ObjectMessage objMessage = (ObjectMessage) message;
        Object obj = objMessage.getObject();
        if (obj instanceof PurchaseOrder) {
          po = (PurchaseOrder) obj;
          processOrder(po);
        }
      }
    } catch (JMSException e) {
      e.printStackTrace();
    }
  }

  private void processOrder(PurchaseOrder po) {
    Customer customer = po.getCustomer();
    CustomerOrder order = po.getCustomerOrder();

    for (OrderItem oItem : order.getOrderItemList()) {
      Wine wine = oItem.getWine();
      int qty = oItem.getQuantity();
      deductInventory(wine, qty);
    }

    String from = PopulateDemoData.FROM_EMAIL_ADDRESS;
    String to = customer.getEmail();
    String content =
        "Your order has been processed. "
        + "If you have questions call Beginning EJB Wine Store "
        + "Application with order id # "
        + po.getCustomerOrder().getId().toString();
    sendStatus(from, to, content);
  }

  private void deductInventory(Wine tempWine, int deductQty) {
    InventoryItem iItem =
        em.createNamedQuery("InventoryItem.findItemByWine",
        InventoryItem.class).setParameter("wine", tempWine).getSingleResult();
    int newQty = iItem.getQuantity() - deductQty;
    iItem.setQuantity(newQty);
  }

  private void sendStatus(String from, String to, String content) {
    try {
      System.out.println("Before status TopicCF connection");
      Connection connection = statusMessageTopicCF.createConnection();
      System.out.println("Created connection");
      connection.start();
      System.out.println("Started connection");
      System.out.println("Starting Topic Session");
      Session topicSession =
          connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

      MessageProducer publisher = topicSession.createProducer(statusTopic);
      System.out.println("created producer");
      MapMessage message = topicSession.createMapMessage();
      message.setStringProperty("from", from);
      message.setStringProperty("to", to);
      message.setStringProperty("subject", "Status of your wine order");
      message.setStringProperty("content", content);
      System.out.println("before send");
      publisher.send(message);
      System.out.println("after send");
    } catch (JMSException e) {
      e.printStackTrace();
    }
  }
}

The E-mail Service

The e-mail service is an MDB. This MDB processes the incoming messages by sending out e-mails using an e-mail resource that is injected as a resource reference.

Listing 7-19 shows the code for the onMessage() method in the StatusMailerBean MDB. We will start by retrieving all of the properties from the message, and then we will create an object of javax.mail.Message using the injected mail resource reference mailSession. Then the message is decorated with relevant e-mail information, and the send() method is called to send the e-mail message.

Listing 7-19.  The onMessage Method in the StatusMailer MDB

public void onMessage(Message message) {
  try {
    if (message instanceof MapMessage) {
      MapMessage orderMessage = (MapMessage) message;
      String from = orderMessage.getStringProperty("from");
      String to = orderMessage.getStringProperty("to");
      String subject = orderMessage.getStringProperty("subject");
      String content = orderMessage.getStringProperty("content");
      javax.mail.Message msg = new MimeMessage(mailSession);
      msg.setFrom(new InternetAddress(from));
      InternetAddress[] address = {new InternetAddress(to)};
      msg.setRecipients(RecipientType.TO, address);
      msg.setSubject(subject);
      msg.setSentDate(new java.util.Date());
      msg.setContent(content, "text/html");
      System.out.println("MDB: Sending Message from " + from + " to " + to + "...");
      Transport.send(msg);
      System.out.println("MDB: Message Sent");
    } else {
      System.out.println("Invalid message ");
    }

  } catch (Exception ex) {
    ex.printStackTrace();
  }
}

The complete code for StatusMailerBean is shown in Listing 7-20.

Listing 7-20.  StatusMailerBean.java

@MessageDriven(activationConfig = {
  @ActivationConfigProperty(propertyName = "destinationName",
                            propertyValue = "StatusMessageTopic"),
  @ActivationConfigProperty(propertyName = "destinationType",
                            propertyValue = "javax.jms.Topic")
}, mappedName = "StatusMessageTopic")
public class StatusMailerBean implements MessageListener {
  @Resource(name = "mail/wineappMail")
  private Session mailSession;

  public void onMessage(Message message) {
    try {
      if (message instanceof MapMessage) {
        MapMessage orderMessage = (MapMessage) message;
        String from = orderMessage.getStringProperty("from");
        String to = orderMessage.getStringProperty("to");
        String subject = orderMessage.getStringProperty("subject");
        String content = orderMessage.getStringProperty("content");
        javax.mail.Message msg = new MimeMessage(mailSession);
        msg.setFrom(new InternetAddress(from));
        InternetAddress[] address = {new InternetAddress(to)};
        msg.setRecipients(RecipientType.TO, address);
        msg.setSubject(subject);
        msg.setSentDate(new java.util.Date());
        msg.setContent(content, "text/html");
        System.out.println("MDB: Sending Message from " + from + " to " + to + "...");
        Transport.send(msg);
        System.out.println("MDB: Message Sent");
      } else {
        System.out.println("Invalid message ");
      }
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
}

The Credit Service

The credit service is a stateless session bean that is published as a Web service using JSR 181 (Web Services Metadata for the Java Platform) annotations. The idea behind creating this Web service is twofold:

  • It shows how you can expose a stateless session bean as a Web service.
  • It shows how you can consume a Web service from an EJB application.

This Web service is consumed by OrderProcessFacadeBean to check the customer’s credit card. We are going to use the credit service that we developed in Chapter 6.

The Database Schema

The Wines Online application uses a single database schema to store all of the information related to customers, orders, inventory, and so on. Figure 7-5 shows a database diagram with all of the tables and relationships. The schema is designed to accommodate or showcase most of the O/R (object/relational) mappings in the JPA specification that are covered in this book, including the different types of inheritance strategies between entities. Not shown are the tables dedicated to ID generation for each root entity in an inheritance hierarchy (for example, BUSINESS_CONTACT_ID_GEN) or entity not involved in inheritance (for instance, ADDRESS_ID_GEN).

9781430246923_Fig07-05.jpg

Figure 7-5.  Database diagram for the Wines Online application

Building, Deploying, and Testing the Application

EJBs need to be packaged into EJB JAR (Java Archive) files before they are assembled into EAR (Enterprise Archive) files that hold all of the required modules and libraries for the application. Most application servers provide deployment utilities or Ant tasks to facilitate deployment of EJBs to their containers. Java integrated development environments (IDEs) like NetBeans, JDeveloper, and Eclipse provide graphical deployment tools that allow developers to package, assemble, and deploy applications to application servers. There is no requirement that EJBs have to be packaged into EJB JAR files and assembled into EAR files. You can also deploy the EJB JAR files themselves. In this chapter, we will assemble them into EAR files so that we can make the persistence unit a shared module (in its own JAR file), and also to set the stage for Chapter 12, in which we will be building client applications for the wine store back-end that we have just developed. We will also package the EJB/WebService module from Chapter 6 into this EAR file, along with a Web module that contains a simple test servlet that drives the test scenario.

Packaging, assembly, and deployment aspects are covered in detail in Chapter 11. In this chapter, we have developed the wine store application back-end using session beans, MDBs, JPA entities, and Web Services. Using NetBeans and deploying to GlassFish, we will perform the following steps to package, assemble, deploy, and test the services defined in this chapter.

Prerequisites

Before performing any of the steps detailed in the next sections, complete the “Getting Started” section of Chapter 1, which will walk you through the installation and environment setup required for the samples in this chapter.

Creating the Database Connection

The samples in this chapter require a database connection, and for these tests we will use the Derby database that is bundled with NetBeans and Glassfish. If you have not already created the WineApp database, also used for the Chapter 3 and Chapter 4 examples, click on the Services tab, expand the Databases icon, and invoke “Create Database...” on the Java DB node. Create a database named “WineApp” with username and password wineapp/wineapp, as shown in Figure 7-6:

9781430246923_Fig07-06.jpg

Figure 7-6.  Creating the WineApp database and connection

This last step created a database connection, which is referenced from the persistence units in the persistence.xml files found in both the JPA and EJB projects. While it is possible to pre-create the database objects (tables, sequences, key constraints, and so on), we will let JPA create these database objects automatically the first time they are needed by each persistence unit.

image Note   The samples for this chapter, like those for Chapter 3, include a JDBC data source that references these ­connection details. If you choose to use different credentials, make sure that you update the persistence.xml files, as well as the data source defined in the glassfish-resources.xml file in the Chapter07-IntegratedServices-ejb project.

Creating the JMS and JavaMail Resources

The MDBs in this integrated application require JMS resources. In Chapter 5, we created a JMS topic connection factory and a JMS topic, and we will create an additional one of each that will be consumed by the MDBs. Initially, OrderProcessFacadeBean sends a message to the queue that is consumed by OrderProcessingMDBBean. In turn, OrderProcessingMDBBean uses a JMS topic connection factory to send a message to the queue that will be used by StatusMailerBean to send out e-mail messages to the customer. Each of our topic connection factories also requires a JMS topic to be defined.

If you have not completed the samples in Chapter 5 (EJB Message-Driven Beans), follow the steps in that chapter in the section “Creating the JMS and JavaMail Resources.” In addition to the resources created for that chapter’s samples, you will need to create an additional TopicConnectionFactory and Topic. Chapter 5 will guide you in creating these additional resources using the GlassFish admin console.

The new resources you will need to create are a TopicConnectionFactory named poTopicConnectionFactory and a Topic named PurchaseOrderTopic.

Opening the Sample Application

Before performing the next step, you may wish to close any existing projects in NetBeans to avoid clutter. Open Chapter07-ServiceIntegration using the File image Open Project menu, as shown in Figure 7-7. Make sure that the ‘Open Required Projects’ checkbox is checked.

9781430246923_Fig07-07.jpg

Figure 7-7.  Opening the Chapter07-ServiceIntegration project

Note that this will open the Chapter06-WebServiceSamples-ejb project as well, since it is used directly by the Chapter 7 samples. Some of the source files will initially be flagged with warnings because they are missing dependencies until the EJB Web Service has been configured in the next step.

Configuring the EJB Web Service

We have built our wine store back-end application on top of the work we did in Chapter 6, in which we implemented the credit service.

  1. The application in this chapter will not compile until you follow the steps in Chapter 6 to deploy the Web Service and create the Web Service client. These steps are outlined here, but you may wish to refer to Chapter 6 for more comprehensive instructions for performing these operations. Right-click on the Chapter06-WebServiceSamples-ejb project, and choose ‘Clean and Build’ followed by ‘Deploy.’
  2. In the Chapter06-WebServiceSamples project, open the ‘Web Services’ section, right-click on CreditCheckEndpointBean and choose ‘Test Web Service.’ When the Web page comes up, click on ‘WSDL File’ link, and then copy the browsers URL for the .wsdl file (typically, http://localhost:8080/CreditService/CreditCheckEndpointBean?Tester).
  3. Now right-click on the Chapter06-WebServiceSamples-ejb project again, and choose ‘New | Web Service Client.’ In the wizard, select the ‘WSDL URL’ radio button and paste the .wsdl URL you copied in step 2. Set the package to ‘com.apress.ejb.chapter06.services.client’ and choose Finish. This will complete the Web Service configuration required to run the Chapter 7 samples.

A quick look at the modules included in this example reveals that, in addition to the EJB/WebServices module from Chapter 6, we have another EJB module containing Session bean facades and MDBs, a number of JPA entities that comprise a JPA persistence unit, and a Web module containing a test servlet.

The [email protected] Account and the user.properties File

We have set up a Yahoo! mail account, [email protected], that you may use for testing purposes. The password is: wine_app. All samples in this book that involve a JavaMail Session default to this account. You are free to use it, or you may prefer to send and receive e-mail through your own mail server (for example, if you are behind a firewall) using your own e-mail account(s).

In the Chapter07-ServiceIntegration-jpa project, you will find a user.properties file in the root source directory. This file allows you to specify the From: and To: e-mail addresses for the e-mail generated by this sample. It is shown in Listing 7-21:

Listing 7-21.  The user.properties file found in the source root of the Chapter07-ServiceIntegration-jpa project

# Specify the From: and To: email accounts that will be shown
# in the mail header that is sent from the JavaMail Session
# named 'mail/wineappMail' and configured in Chapter 5. Note
# that although the email header may appear to be from the
# From account below, it will originate from the account
# configured in the 'mail/wineappMail' JavaMail Session.
from_email_address =[email protected]
to_email_address =[email protected]

To change the To: and/or From: e-mail addresses, feel free to edit this plaintext file. Also, refer to the Readme.txt file for guidance with proxy servers and other issues resulting from non-default installation environments.

Building, Deploying, and Executing the Sample Application

With all of the configuration steps completed, you are now ready to build, deploy, and run this chapter’s WineApp application through its simple servlet client. To ensure that any previous artifacts are removed, begin by selecting all five NetBeans projects and invoking the ‘Clean’ operation, shown in Figure 7-8:

9781430246923_Fig07-08.jpg

Figure 7-8.  Cleaning the Chapter07-ServiceIntegration project

image Note   If GlassFish is running already, and you get errors while building because .jar files cannot be deleted, try shutting down GlassFish and re-invoking the ‘Clean’ operation.

The next steps are to build, package, and deploy the EJB, JPA, and Web modules and run the servlet client. All of these steps are performed automatically when invoking the ‘Run’ operation shown in Figure 7-9. NetBeans is able to track the state of the projects and perform any required work prior to executing the ShoppingCartClient servlet.

9781430246923_Fig07-09.jpg

Figure 7-9.  Running the Chapter07-ServiceIntegration project, which executes the servlet ShoppingCartClient found in the Chapter07-ServiceIntegration-web module

Client applications and different application architectures will be discussed in detail in Chapter 12. In order to test the deployed wine store application, we have provided a simple servlet client. GlassFish injects the ShoppingCart and SearchFacade session beans into the servlet to search the wines, add items to the shopping cart, and finally submit the order.

Listing 7-22 shows the code for the servlet client. To begin, the client looks up the SearchFacadeBean session bean and calls the findWineByYear() method, which returns a list of wines. In order to add wines to the shopping cart, the client program looks up the ShoppingCart session bean and calls the findCustomer() method. Once the customer has been found, the client goes into a while loop and adds the list of wines retrieved from SearchFacadeBean using the addWineItem() method in the ShoppingCart bean. To keep the client simple, the quantity of each wine is set to 20. Once the client finishes adding all of the wines to the shopping cart, the sendOrderToOPC() method in the ShoppingCart bean is called to submit the order.

The ShoppingCartClient servlet uses EJB injection to obtain the EJBs SearchFacade and ShoppingCart through their Local interfaces. This servlet performs the following steps, logging its progress by sending HTML back to the browser window:

  • Initializes the sample data through a Java service façade— PopulateDemoData—that uses the JPA entities to populate the database
  • Uses the stateful ShoppingCart to find an existing customer by e-mail in the sample data set and cache it in the session bean
  • Uses the SearchFacade to look up the wines from 2004 using the "Wine.findByYear" JPQL named query, displays information about wines from that year, and adds 20 bottles of each to the customer’s shopping cart using the addWineItem() method in the ShoppingCart bean
  • Once the client finishes adding all of the wines to the shopping cart, the sendOrderToOPC() method in the ShoppingCart bean is called to submit the order

Listing 7-22 shows the servlet source:

Listing 7-22.  The ShoppingCartClient.java servlet

@WebServlet(name = "ShoppingCartClient", urlPatterns = {"/ShoppingCartClient"})
public class ShoppingCartClient extends HttpServlet {
  @EJB
  private SearchFacadeLocal searchFacade;
  @EJB
  private ShoppingCartLocal shoppingCart;

  /**
   * Processes requests for both HTTP
   * <code>GET</code> and
   * <code>POST</code> methods.
   *
   * @param request servlet request
   * @param response servlet response
   * @throws ServletException if a servlet-specific error occurs
   * @throws IOException if an I/O error occurs
   */
  protected void processRequest(HttpServletRequest request, HttpServletResponse response)
          throws ServletException, IOException {
    response.setContentType("text/html;charset=UTF-8");
    PrintWriter out = response.getWriter();
    try {
      /* TODO output your page here. You may use following sample code. */
      out.println("<html>");
      out.println("<head>");
      out.println("<title>Servlet ShoppingCartClient</title>");
      out.println("</head>");
      out.println("<body>");
      out.println("<h1>Servlet ShoppingCartClient at " + request.getContextPath() + "</h1>");
      out.println("</body>");
      out.println("</html>");

      out.print("<h2>Populating Demo Data... ");
      new PopulateDemoData("Chapter07-WineAppUnit-ResourceLocal").resetData();
      out.println("done</h2>");

      out.print("<h2>Calling the ShoppingCart to find and cache customer with email address " +
      PopulateDemoData.TO_EMAIL_ADDRESS + "... ");

      final Customer customer = shoppingCart.findCustomer(PopulateDemoData.TO_EMAIL_ADDRESS);
      out.println("found " + customer.getFirstName() + " " + customer.getLastName() + "</h2>");

      out.println("<h2>Calling the SearchFacade to find wines from 2004</h2>");
      List<Wine> yearWineList = searchFacade.getWineFindByYear(2004);
      if (yearWineList != null) {
        for (Wine wine : yearWineList) {
          shoppingCart.addWineItem(wine, 20);
          out.println("<h3>Added cart item for 20 bottles of " + wine.getName() + " " + wine.
          getYear() + "</h3>");

        }
      }

      out.print("<h2>Calling the ShoppingCart to send the order to the Order Processing Center... ");
      shoppingCart.sendOrderToOPC();
      out.println("done</h2>");
    } catch (Exception ex) {
      ex.printStackTrace();
    } finally {
      out.close();
    }
  }

  /* HttpServlet methods */
}

The Servlet Output

The output from the ShoppingCartClient servlet, logging the high-level operations performed by the client, is shown in Figure 7-10:

9781430246923_Fig07-10.jpg

Figure 7-10.  Output from the ShoppingCartClient servlet

The Resulting E-mail

The servlet output shown in Figure 7-10 tells only part of the story. The servlet’s work ends when the order is sent to the Order Processing Center, in the call to shoppingCart.sendOrderToOPC(). In this sample, the result of a successfully processed order is an e-mail sent from the account configured in the JavaMail session (created in Chapter 5) to the account specified in to_email_account property in the user.properties file. The email that is sent using the default account information is shown in Figure 7-11:

9781430246923_Fig07-11.jpg

Figure 7-11.  E-mail from [email protected] appearing in the wineapp account’s inbox

If no e-mail appears in the inbox of the account listed in the to_email_address property of the user.properties file, check the Spam folder for that account or look in the ‘GlassFish Server’ tab of the Output window in NetBeans for diagnostic information. The Readme.txt file in the Chapter07-ServiceIntegration project contains additional troubleshooting information should you need it.

Conclusion

In this chapter, we have covered some ways to integrate different types of EJBs, JPA entities, and Web services with resources like data sources, JMS topics, and JavaMail.

We looked at the conceptual design of our fictitious wine store application, and we layed out the design for the components and services that need to be built. We looked at individual components and services, and we demonstrated how to use different types of EJBs to solve specific application problems.

The wine store application illustrated the various parts of typical back-end applications, which included a comprehensive persistence unit that utilized a range of JPA mappings, session beans that interacted with entities in the persistence unit, MDBs, and Web services using dependency injection.

We also looked at how to inject various types of resources in session beans and MDBs and how to build asynchronicity into the application using the MDBs.

Additionally, we looked into dealing with managed and detached entities from the persistence context and integrating an asynchronous model into a typical back-end application.

Finally, we detailed steps on how you can package individual components and services into modules, assemble them into a Java EE application, and deploy them to the GlassFish server.

In the next chapter, we discuss the transactional services provided by the EJB container and explore the different types of transactional models that you can use in a variety of application architectures.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.180.223