Sunday, August 25, 2013

EJB 3.x Kata

This Code Kata will guide you through how to create an EJB 3 project in Eclipse.  It involves creating and using JPA Entity Beans along with both Stateless and Session Beans.  The project is supposed to represent a virtual fruit stand where customers can buy fruit and see how much they owe. (For more information on Katas I'd recommend reading The Clean Coder by Robert C. Martin.)

Open Eclipse
Select File > New > EJB Project


Name the project: Fruit-Stand



Here's what the newly created project looks like:



Since we'll be selling fruits, and a fruit is a thing, let's create an Entity Bean to represent it.  But first, we'll need to add the JPA Facet to our project.  Right click on the project and select Properties



Select “Project Facets” from the column on the left and then put a check mark next to JPA


Click OK.
Right click on the Project and select New > JPA Entity



Enter the following when the “New JPA Entity” wizard appears:
  • Java Package: jpa.entity
  • Class Name: Fruit



Click Next.
Add the following Entity fields
Notice that I made the Fruit's Name attribute the primary key, because I didn't want to have two fruits with the same name.

Click Finish.
Eclipse generates the following code for you:

package jpa.entity;

import java.io.Serializable;
import java.lang.String;
import javax.persistence.*;

/** Entity implementation class for Entity: Fruit */
@Entity
public class Fruit implements Serializable {

  @Id
  private String Name;
  private double Price;
  private static final long serialVersionUID = 1L;

  public Fruit() {
    super();
  }

  public String getName() {
    return this.Name;
  }

  public void setName(String Name) {
    this.Name = Name;
  }

  public double getPrice() {
    return this.Price;
  }

  public void setPrice(double Price) {
    this.Price = Price;
  }
}

Recall that JPA is not required to create our tables for us.  Therefore create a table named Fruit. Give it a Name column and a Price column.  I also inserted a new data row for a Fruit named "Apple" which I'm going to say will cost $0.25  (The price is just an arbitrary number.)

Before we go any further, let's test our JPA Entity Bean.

Add the following jars to your build path:
  • hibernate-entitymanager
  • junit
  • mysql-connect-j
  • gf-client
 Right click on the project and select New > Source Folder.


Name the New Source Folder test.


Right click on the new source folder and select New > Class.
Name the class FruitTest and put it in the jpa.entity package.
Update FruitTest so that it looks like the following

package jpa.entity;

import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertThat;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;

import org.hibernate.ejb.HibernatePersistence;
import org.junit.Test;

public class FruitTest {

  @Test
  public void testFruit() {
    EntityManagerFactory emf = new HibernatePersistence().createEntityManagerFactory("FruitStand", null);
    EntityManager em = emf.createEntityManager();

    Fruit fruit = em.find(Fruit.class, "Apple");
    assertThat(fruit.getName(), equalTo("Apple"));
    assertThat(fruit.getPrice(), equalTo(0.25));
  }
}

We also need to edit the persistence.xml file.  This file is located under the META-INF subfolder of  ejbModule.



Update the persistence.xml file to look like the following.
    <?xml version="1.0" encoding="UTF-8"?>
      <persistence
        version = "2.0"
        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_2_0.xsd">
      
        <persistence-unit name = "VideoGame" >     
          <class>jpa.entity.VideoGame</class>
          <properties>
      
            <property
              name="hibernate.connection.url" 
              value = "jdbc:mysql://localhost:3306/<DATABASE>" />
      
            <property
              name="hibernate.connection.username"
              value = "<USERNAME>" />
      
            <property
              name="hibernate.connection.password"
              value = "<PASSWORD>" />
      
            <property
              name="hibernate.connection.driver_class"
              value = "com.mysql.jdbc.Driver" />
      
            <property
              name="hibernate.dialect"
              value = "org.hibernate.dialect.MySQLDialect" />
      
            <property
              name="hibernate.show_sql"
              value = "true" />
      
          </properties>
        </persistence-unit>
      </persistence>      

Of course you'll want to replace the placeholders <DATABASE>, <USERNAME>, and <PASSWORD> with whatever database, user, and password that you are using.

You should be able to run FruitTest through JUnit and see that it passes.

Right click on the Project.
Select New > Session Bean (EJB 3.x)


When the "Create EJB 3x Session Bean" wizard appears, update it to the following:
  • Java Package: ejb3.session.stateless
  • Class Name: OhioSalesTax
  • In the State Type drop down box, make sure Stateless is selected
  • Select Local and make sure its text field says: ejb3.session.stateless.SalesTax
  • Deselect No-Interface View
 
Accept Container as the Transaction Type and click Finish

Eclipse generates the following code for you:
package ejb3.session.stateless;

import javax.ejb.Local;

@Local
public interface SalesTax {
}

/** Session Bean implementation class OhioSalesTax */
@Stateless
public class OhioSalesTax implements SalesTax {

  /** Default constructor. */
  public OhioSalesTax() {
    // TODO Auto-generated constructor stub
  }
}

Add the following method to SalesTax.java
  public double calculate(double subTotal);

SalesTax.java should now look like this:
package ejb3.session.stateless;

import javax.ejb.Local;

@Local
public interface SalesTax {
  public double calculate(double subTotal);
}

Open OhioSalesTax.java.
Right click anywhere on the file and select Source > Override/Implement Methods...

Check marks should be on SalesTax's calculate method.



Click Ok
Eclipse generates the following code for you.
// ...
public class OhioSalesTax implements SalesTax {

  // ...
  @Override
  public double calculate(double subTotal) {
    // TODO Auto-generated method stub
    return 0;
  }
}
Make the following changes to OhioSalesTax
// ...
public class OhioSalesTax implements SalesTax {

  // ...
  @Override
  public double calculate(double subTotal) {
    return 0.05 * subTotal;
  }
}
Note: I honestly don't know if there's a sales tax on fruit or if 0.05 is Ohio's sales tax rate.

Right click on the Project.
Select New > Session Bean (EJB 3.x)

When the "Create EJB 3x Session Bean" wizard appears, update it to the following:
  • Java Package: ejb3.session.stateful
  • Class Name: ShoppingCartBean
  • In the State Type drop down box, select Stateful
  • Select Local and make sure its text field says: ejb3.session.stateful.ShoppingCartLocal
  • Deselect No-Interface View

 Click Next.
Accept Container as the Transaction Type and click Finish

Eclipse generates the following code for you:
// ShoppingCartLocal.java
package ejb3.session.stateful;

import javax.ejb.Local;

@Local
public interface ShoppingCartLocal {
}

// ShoppingCartBean.java
package ejb3.session.stateful;

import javax.ejb.Stateful;

/** Session Bean implementation class ShoppingCartBean */
@Stateful
public class ShoppingCartBean implements ShoppingCartLocal {

  /** Default constructor. */
  public ShoppingCartBean() {
    // TODO Auto-generated constructor stub
  }
}
Add the following method to ShoppingCartLocal:
  public void addFruit(Fruit fruit);
  public double subTotal();
Next, override these methods in ShoppingCartBean. ShoppingCartBean should now look like the following:
import java.util.ArrayList;
import java.util.List;

import javax.ejb.Stateful;

import jpa.entity.Fruit;

/** Session Bean implementation class ShoppingCartBean */
@Stateful
public class ShoppingCartBean implements ShoppingCartLocal {

  /** Default constructor. */
  public ShoppingCartBean() {
    // TODO Auto-generated constructor stub
  }

  private List<fruit> fruits = new ArrayList<fruit>();

  @Override
  public void addFruit(Fruit fruit) {
    fruits.add(fruit);
  }

  @Override
  public double subTotal() {
    double subTotal = 0;
    for(Fruit fruit : fruits) {
      subTotal = fruit.getPrice();
    }
    return subTotal;
  }
}
Right click on the Project and select New > Class.
When the "New Java Class" wizard appears, enter the following in the appropriate textfields:
  • Java Package: Client
  • Class Name: Client


Click Finish.

Eclipse generates the following code for you:
package client;

public class Client {

}
Update it so that it looks like the following:
package client;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;

import jpa.entity.Fruit;

import org.hibernate.ejb.HibernatePersistence;

import ejb3.session.stateful.ShoppingCartLocal;
import ejb3.session.stateless.SalesTax;

public class Client {
  public static void main(String[] args) throws NamingException {
    EntityManagerFactory emf = new HibernatePersistence().createEntityManagerFactory("FruitStand", null);
    EntityManager em = emf.createEntityManager();

    Fruit apple = em.find(Fruit.class, "Apple");

    Context ctx = new InitialContext(); // throws NamingException

    ShoppingCartLocal shoppingCart = (ShoppingCartLocal) ctx.lookup("ejb3.session.stateful.ShoppingCartLocal");  // throws NamingException

    // As each item is added to the cart, display it's name and price (ie. call it's toString method).
    shoppingCart.addFruit(apple);

    // Display subTotal
    double subTotal = shoppingCart.subTotal();
    System.out.println("subTotal : " + subTotal);

    // Display salesTax
    SalesTax ohioSalesTax = (SalesTax) ctx.lookup("ejb3.session.stateless.SalesTax");  // throws NamingException
    double salesTax = ohioSalesTax.calculate(subTotal);
    System.out.println("sales tax : " + salesTax);

    // Display final bill (subTotal + salesTax)
    System.out.println("total : " + (subTotal + salesTax));
  }
}
Start the Server.  (I'm using glassfish)
Add the Fruit-Stand project to the server.


You should be able to run the Client as a Java Application.
Among other things, you should see the following output in the console:

subtotal: 0.25
sales tax: 0.05
total: 0.3

Suppose you want people from Texas to be able to buy your produce.

Right click on the Project.  Select New > Session Bean (EJB 3.x).

Enter the following when the "EJB 3.x Session Bean" wizard appears:
  • Java Package: ejb3.session.stateless
  • Class Name: TexasSalesTax
  • In the State Type drop down box, make sure Stateless is selected
  • Deselect No-Interface View
Click Next.
Add the ejb3.session.stateless.SalesTax interface

package ejb3.session.stateless;

import javax.ejb.Local;
import javax.ejb.Stateless;

/** Session Bean implementation class TexasSalesTax */
@Stateless
@Local(SalesTax.class)
public class TexasSalesTax implements SalesTax {

    /** Default constructor. */
    public TexasSalesTax() {
        // TODO Auto-generated constructor stub
    }

    /** @see SalesTax#calculate(double) */
    public double calculate(double subTotal) {
        // TODO Auto-generated method stub
        return 0;
    }

}
Update it so that it looks like the following:

package ejb3.session.stateless;

import javax.ejb.Stateless;

/** Session Bean implementation class OhioSalesTax */
@Stateless(mappedName = "TexasSalesTax")
public class TexasSalesTax implements SalesTax {

  /** Default constructor. */
  public TexasSalesTax() {
    // TODO Auto-generated constructor stub
  }

  @Override
  public double calculate(double subTotal) {
    return 0.10 * subTotal;
  }
}
Note: I don't know what Texas' sales tax is. It's just some random number I put in to make it easier for me to verify things are working properly.
Next, we need to update OhioSalesTax
/** Session Bean implementation class OhioSalesTax */
@Stateless(mappedName = "OhioSalesTax")
public class OhioSalesTax implements SalesTax {
  // ...
Here's how to use the TexasSalesTax stateless session bean in the Client.
package client;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;

import jpa.entity.Fruit;

import org.hibernate.ejb.HibernatePersistence;

import ejb3.session.stateful.ShoppingCartLocal;
import ejb3.session.stateless.SalesTax;

public class Client {
  public static void main(String[] args) throws NamingException {
    EntityManagerFactory emf = new HibernatePersistence().createEntityManagerFactory("FruitStand", null);
    EntityManager em = emf.createEntityManager();

    Context ctx = new InitialContext(); // throws NamingException
    ShoppingCartLocal shoppingCart = (ShoppingCartLocal) ctx.lookup("java:global/Fruit-Stand/ShoppingCartBean");

    Fruit apple = em.find(Fruit.class, "Apple");
    shoppingCart.addFruit(apple);

    // Display subTotal
    double subTotal = shoppingCart.subTotal();
    System.out.println("subTotal : " + subTotal);

    // Display salesTax
    SalesTax texasSalesTax = (SalesTax) ctx.lookup("java:global/Fruit-Stand/TexasSalesTax");

    double salesTax = texasSalesTax.calculate(subTotal);
    System.out.println("sales tax : " + salesTax);

    // Display final bill (subTotal + salesTax)
    System.out.println("total : " + (subTotal + salesTax));
  }
}
You should be able to run the Client as a Java Application.
Among other things, you should see the following output in the console:

subTotal: 0.25
sales tax: 0.025
total: 0.275

No comments:

Post a Comment