Professional Documents
Culture Documents
Introduction
Enterprise JavaBeans 3.0 is a major enhancement to the EJB specification, introducing a new POJO-based programming model that greatly simplifies development. The EJB 3.0 specification was created under JSR 220, and is divided into three documents: y EJB 3.0 Simplified API: Defines the new simplified API used to code EJB components; specifically, session beans and message-driven beans. y Enterprise JavaBeans Core Contracts and Requirements: Defines the EJB contracts between the bean and the EJB container. y Persistence API: Defines the new Persistence Model for Java. This article introduces you to the IBM WebSphere Application Server V6.1 Feature Pack for EJB 3.0, and shows you how to build a Java Persistence API (JPA) and session bean POJO from scratch, plus how to run it inside WebSphere Application Sever. To highlight aspect oriented programming enhancements to the EJB 3.0 specification, this article will also walk you through building an additional session bean, demonstrating POJO injection, and help you build an EJB 3.0 interceptor. This article uses the WebSphere Application Server Toolkit V6.1 and the Dali plug-in available from Eclipse. IBM Rational Application Developer V7.5 was released as an open beta bundled with the Eclipse Dali functionality, so you might want to consider looking into that. You will notice future articles use Rational Application Developer V7.5 to build EJB 3.0 applications.
Preparation
In this article, you will build an EJB 3.0 application within WebSphere Application Server V6.1 using the new Feature Pack for EJB 3.0. For this example, the sample application is a simple customer order system, in which a customer creates an order, adds line items, and then submits the order. Figure 1 illustrates the use cases in this sample system, and the sequence in which the use cases are executed is shown in the activity diagram in Figure 2. Figure 1. Sample application use cases
This article assumes: y WebSphere Application Server V6.1 is installed. y WebSphere Application Server V6.1 Feature Pack for Enterprise JavaBeans 3.0 has been downloaded. y WebSphere Application Server Toolkit v6.1 is installed. y Database schema and test data has been configured. (The DDL for populating a Derby database is provided in the download materials for this article.)
7.
A summary panel will display next, listing the items to be installed (Figure 4). Click Next to install the feature pack.
8.
When the installation completes, the panel shown in Figure 10 will display. Un-check Launch the Profile management tool and click Finish. (Links with instructions and considerations for installing the feature pack to a Network Deployment topology are also available on this panel.) Figure 5. Launch Profile Manager
Dali is a Object/Relational mapping plug-in for Eclipse that provides a Java Persistence perspective that supports top-down, bottom-up, and meet-in-the-middle O/R mapping. Dali also provides wizards and edit boxes for managing various Java Persistence API (JPA) annotations. Dali can be added to either the WebSphere Application Server Toolkit or Rational Application Developer V7.0.x. (Keep in mind that Dali is an open source plug-in so support comes from Eclipse. You can also use the Rational Application Developer V7.5 Beta as an alternative, which comes with the Dali plug-in installed. SeeResources for more.) To add Dali to the Application Server Toolkit: 1. Download the latest 0.5 version of Dali. 2. Make sure that the Application Server Toolkit is stopped. 3. Extract the contents of the Dali download file to the main Application Server Toolkit installation directory. (This is done because the file contains the directory path starting with the eclipse subdirectory that resides in the Application Server Toolkit install directory.) 4. From a command prompt, go to the Application Server Toolkit install directory and use this command to start the toolkit:
ast.exe -clean
This command will make sure the Dali plug-in will be detected. Back to top
A simple example
You are now ready to build your first EJB 3.0 application for WebSphere Application Server. By following the steps in this section, you will develop a simple JPA POJO and a session bean using the example described earlier, and then test the scenario: A. Set up the development environment B. Create your first JPA POJO C. Create a simple sessions bean D. Import the client
b. c.
In the New EAR Application Project wizard under the Target Runtime section, click New. In the New Server Runtime wizard (Figure 12), select WebSphere Application Server v6.1 and click Next. Figure 7. WebSphere Application Server v6.1 runtime
d. e.
Enter the name of the WebSphere Application Server installation directory, if yours is different than the default. Click Finish. Name the project OrderSystem and ensure that the Target Runtime is WebSphere Application Server v6.1(Figure 8). (Do not select the stub option). Press Finish. Figure 8. EAR Application Project Wizard
f. g.
Your EAR file should display an error because there are no Java EE modules. EJB 3.0 is a plain POJO model, so you will create a simple Java project as a utility project that will contain your EJB 3.0 POJOs. Right-click the OrderSystem EAR and select New=>Other (Figure 9). Figure 9. Create new project
h.
Expand J2EE => Utility Project and click Next (Figure 10). A utility project is a plain Java project that is packaged within the EAR. You will mark this project as an EJB module later in the application.xml.
i.
Name the project OrderSystemEJB (Figure 11). Ensure that the EAR Project Name matches the already created EAR project. Figure 11. Utility Project Wizard
2.
You now need to update the EAR file so that it sees the Java project as an EJB Module: a. Expand the OrderSystem and double click the Deployment Descriptor Editor (Figure 12).
b.
Go to the source tab, shown in Figure 13. Add these XML fragments after the display name tag:
<module> <ejb>OrderSystemEJB.jar</ejb> </module>
c. d. e.
3.
f. Save the editor and close the Application Deployment Descriptor. Add the local WebSphere Application Server to the Application Server Toolkit workspace as a server: a. In the J2EE perspective, go to the Servers tab. Right-click within the servers list and select New=>Server(Figure 14). Figure 14. New Server
b. c.
Accept the defaults, then Next. Un-check Run server with resources within the workspace, then Finish. Figure 15. WebSphere Server Settings
4.
d. Right-click on the new server and select Start. Add a data source for the Derby database that you populated with the CUSTSCH schema provided in CUSTSCH-derby.ddl: a. Right-click on the server and select Run administrative console (Figure 16). Figure 16. Launch administrative console
b. c.
In the administrative console, navigate to Resources=>JDBC=>Data sources. Select a scope for the data source (Figure 17). What you choose will vary depending on your installation. Select a server scope for your local server, then click New. Figure 17. Create a DataSource
d.
On the Step 1 panel (Figure 18), enter Order DS for the Data source name and jdbc/orderds for the JNDI name, then Next.
e.
On the Step 2 panel (Figure 19), select Create new JDBC Provider, then Next. Figure 19. JDBC Provider
f.
On the Step 2.1 panel (Figure 20), select the following values: Database type: Derby Provider type: Derby Network Server Using Derby Client Implementation type: Connection pool data source Enter optional Description text, then click Next.
g.
On the Step 3 panel (Figure 21), enter JPATEST for the database name, then Next.
h.
On the Step 4 panel (Figure 22), review the data to make sure all values were entered correctly, then clickFinish. Figure 22. Summary
i. j.
Save your changes by clicking the Save link at the top of the page. Verify that the Derby network database is running, then select the Order DS data source and click Test Connection (Figure 23). Figure 23. Test Connection
k.
You should receive a message at the top of the panel similar to that shown in Figure 24 Figure 24. Connection Confirmation
b.
In the New Java Class panel (Figure 26), store the class in the Package calledcom.ibm.ejb3.order.entities, name the class Customer, then Finish.
c.
d. e.
In the Ourtline view, right-click on the Customer class, then select Source => Generate Getters and Setters(Figure 27).
f.
g.
2.
You are now ready to make your POJO a JPA entity. In most cases, all you need is a good Java editor. This example uses the editor in Eclipse that you use to create JPA applications: a. Over the class declaration, begin typing @Enti, then press Ctrl-Space to trigger the Eclipse content assist. Select @Entity. (Figure 30)
b.
An Entity class needs a primary key. Over the customerId property, add the @Id annotation (Figure 31). Figure 31. Id Annotation
3.
If your property names match that of the database, you are done and you have your first JPA POJO. However, the schema in this example is slightly different, and you will use the JPA Eclipse tools for help: a. Switch to the Java Persistence perspective. You can begin the Open Perspective wizard by rightclicking on the perspective icons in the upper right corner of the panel and selecting Other. (Figure 32)
b. c.
Select Java Persistence from the list that displays. Go to the Database Explorer view. To load your database, right-click on Connections, then select New => Connection... (Figure 33) Figure 33. New Connection
d.
Enter or select the following properties (Figure 34): Uncheck Use default naming convention Connection Name: OrderDB Database Manager: Derby => 10.1 Database Name: JPATEST Class Location: navigate to Derby lib directory (for example: \WebSphere\AppServer\derby\lib\derbyclient.jar) User Id and Password: Enetr appropriate values for your system. Figure 34. Connection Parameters
e.
Navigate to OrderDB => JPATEST => Schemas => CUSTSCH => Tables => CUSTOMER and examine the fields (Figure 35). Figure 35. CUSTOMER Table
4.
You need to add the persistence.xml file to the JPA project. This file is the JPA deployment descriptor for configuring JPA Properties. You can use the JPA development tools for this: a. In the Package Explorer view, right-click OrderSystemEJB and select Java Persistence => Add Java Persistence... (Figure 36)
b.
For Connection, select OrderDB, and for Schema, select CUSTSCH. (The connection could have timed out. If so, select the Reconnect... link and enter the same user ID and password). For Persistence unit name, enterOrderDB (Figure 37).
c. d.
Once you do this, your Customer POJO will have an error because the Eclipse JPA plug-in will detect a mismatch between the database model and the JPA object model. You will fix this in the next step. Open the META-INF folder of OrderSystemEJB, and open the newly generated persistence.xml (Figure 38).
e.
You need to add the data source to the JPA provider. Open persistence.xml and click on the Source tab. Add this xml tag within the persistence-unit tag, as shown in Figure 39:
<jta-data-source>jdbc/orderds</jta-data-source>
5.
Save and close persistence.xml. (Because you are running inside a Java EE container, the default provider will be used.) Because the database does not match exactly with your POJO, you need to fix your mappings a bit: a. In the Outline view, select the customerId property. b. In the lower right corner of the panel, ensure that Map As is set to Id. Under Column Name, select CUST_ID. Ensure the rest of the fields match the values shown in Figure 40. Figure 40. Persistence Properties
g.
c.
Select the PK Generation tab. Check the Primary Key Generation and select Identity for Strategy (Figure 41). JPA enables a few strategies for automatic primary key generation. In this case, JPA will delegate to DB2's identity feature. Figure 41. Identity Strategy
d.
In the Customer POJO, you need to override the default schema name because the database is stored in a different schema. Add the @Table annotation as shown in Figure 42. Add CUSTOMER for name and CUSTSCH for schema.
e.
b.
For Package name, enter or browse to com.ibm.ejb3.order.session. For class name, enter CustomerTask(Figure 44).
c.
d. e.
You should have an error, because there is no class called CustomerDoesNotExist. Use the Eclipse suggestion to create the class, as in Figure 45. Figure 45. Create Exception
f.
Make sure that Constructors from superclass is checked and that the superclass is java.lang.Exception(Figure 46). Figure 46. Class Wizard
g.
Next, add a @Local annotation above the class declaration, as shown in the Figure 47. This makes your session bean a local EJB. Figure 47. Local Annotation
h.
Right click in the editor and select Source => Organize Imports (Figure 48) Figure 48. Adding Imports
2.
Next, you will create your Session Bean POJO: a. Right-click the com.ibm.ejb3.order.session package and select New => Class (Figure 49).
b.
Name the class CustomerTaskImpl, and add the CustomerTask interface to the Interface list (Figure 50).
c.
To make CustomerTaskImpl a session bean, add an @Stateless annotation, as shown in Figure 51. This makes your POJO a stateless session bean. Figure 51. Stateless Annotation
3.
An EJB 3.0 container is also an injection server. To have persistence actions, you need an entity manager. This can be easily added to the session bean through injection. When using JPA within an EJB 3.0 container, the container automatically manages passing the correct persistence context for you. This is known as a container managed entity manager. Using JPA in this fashion, the EJB 3.0 container will properly manage the lifecycle of the persistence context, and propagate the correct context across EJB invocations. a. Add the EntityManager declaration shown below to the CustomerTaskImpl class.
@PersistenceContext(name="OrderDB") EntityManager em;
b. c.
You can use code assist to bring in the PersistenceContext Import (Figure 52). Figure 52. PersistenceContext Annotation
d.
e. f.
Organize imports as before (Figure 53), then save and close the file.
c.
For WAR file, Browse to the location where you saved the provided OrderClient.war file. Keep the default name for the Web project, and ensure that OrderSystem is the EAR Project Name (Figure 55).
2.
Next, you can examine the servlet code. a. Expand the Java Resources: src folder and open the EJBClientServlet.java file (Figure 56).
b.
Notice that the EJB session bean can be injected right into the servlet.
public class EJBClientServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet { /* (non-Java-doc) * @see javax.servlet.http.HttpServlet#HttpServlet() */ @EJB private CustomerTask customerTask = null;
c. d.
In the displayCustomerAndOrder method, notice that once you have the session bean instance, it is just simple Java.
private void displayCustomerAndOrder(int customreId, PrintWriter out) throws CustomerDoesNotExist { Customer customer = customerTask.findCustomer(customreId); out.println("<br>Customer ID => " + customer.getCustomerId()); out.println("<br>Customer Name => " + customer.getName());
3.
e. You will now deploy and test the application: a. From the J2EE perspective, go to the Servers view, right-click the server and press Start (Figure 57).
b.
Once the server is started, right-click the server again and select Add and Remove Projects... (Figure 58) Figure 58. Add the Project
c.
Add the OrderSystem application by selecting the application from the Available projects list on the left and press Add (Figure 59).
d.
Back in the Project Explorer view, expand the Deployment Descriptor => Servlets, then rightclickEJBClientServlet and select Run As => Run on Server (Figure 60).
e.
Select Choose an existing server and check the Set server as project default option, then Finish (Figure 61).
f.
The browser should display the Customer ID and Customer Name (Figure 62). Figure 62. Examine Browser
g.
Close the browser and remove the project from the server (Figures 63 and 64) Figure 63. Remove the Project
h.
b.
Select your Connection (your database connection may have closed by now; select the Reconnect link and enter your credentials again, if necessary), and select the CUSTSCH schema. Press Next (Figure 66).
c.
On the Generate Entities from Tables panel, only select LINEITEM, CUSTORDER, and PRODUCT. Make sure your Package name is com.ibm.ejb3.order.entities. Since the CUSTORDER table name is different than the Entity Name ("Order," which is the desired name of the entity), enter Order as the Entity name for CUSTORDER (Figure 67). Press Finish.
2.
Next, you will update the mappings: a. Open the Order entity (Figure 68). Figure 68. Examine Order Class
b.
Use the Java Persistence Properties Editor or add the annotations marked below in bold: @Table annotation is needed to override the schema name. @NamedQuery is adding a query to your JPA POJO. JPA has a very rich query language you can use, called EJB-QL. This query is asking for an open order from the customer. The ordered property needs to be annotated as an ID, as you did before. Also, you are adding a generation strategy of Identity. Finally, notice the Order has a relationship to LineItems. You will add a fetch rule of Eager. This means that when someone asks for an order, the JPA engine will fetch all the line items that are associated with the order as well.
@Entity @Table(name="CUSTORDER", schema="CUSTSCH") @NamedQuery(name="getCurrentOrder", query="select o from Order o where o.customerId = :customerId and o.status ='OPEN'") public class Order implements Serializable { @Id @Column(name="ORDER_ID") @GeneratedValue(strategy=GenerationType.IDENTITY) private int orderId; private String status; private double total;
3.
c. You need to update the Customer entity to have a relationship to Order. The requirements of the system are such that a Customer has only one open Order. You want to create a relationship to that open Order. You do not want to generate the relationship because a Customer has many orders, but only when he has one Open Order. a. Open the Customer entity. Add Order currentOrder, as shown in the code below. Generate the setters and getters using the Eclipse Java tools.
package com.ibm.ejb3.order.entities; import import import import import import import java.io.Serializable; javax.persistence.Entity; javax.persistence.Id; javax.persistence.GeneratedValue; javax.persistence.GenerationType; javax.persistence.Column; javax.persistence.Table;
@Entity @Table(name="CUSTOMER", schema="CUSTSCH") public class Customer implements Serializable { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="CUST_ID") private int customerId; private String name;
private Order currentOrder; public int getCustomerId() { return customerId; } public void setCustomerId(int customerId) { this.customerId = customerId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Order getCurrentOrder() { return currentOrder; } public void setCurrentOrder(Order currentOrder) { this.currentOrder = currentOrder; } }
b. c.
Add the following @OneToOne and @JoinColumn annotations, as shown below. Notice that the fetch rule is set to lazy, so you will not fetch the order when someone asks for the customer. You have set some cascading rules. If someone updates the open order, changes will cascade as defined. The relationship is optional, which means the customer will not always have an open order.
You have specified the Join column (the name corresponds to the foreign key on the Customer table, while the referencedColumn name corresponds to the primary key of the Custorder table.) If you do not specify the Join column, JPA will attempt to resolve the keys based on a naming scheme.
d. e.
Update the LineItem class to point to the correct schema, as you did for Customer and Order (Figure 69). Figure 69. LineItem Schema
f.
Similarly, override the schema for Product (Figure 70). Figure 70. Product Schema
g.
a.
In the session package, select New => Interface (Figure 71) and name the Interface OrderTask. Click Finish. Figure 71. New Interface
b.
Annotate the class as Local, and add the getCurrentOrder method, as shown below.
package com.ibm.ejb3.order.session; import com.ibm.ejb3.order.entities.Order; @Local public interface OrderTask { public Order getCurrentOrder(int customerId) throws CustomerDoesNotExist; }
2.
c. Create a new stateless session bean. a. Right-click the session's package and select New => Class (Figure 72). Figure 72. New Class
b.
Make sure the class implements the OrderTask Interface you just created (Figure 73). Figure 73. OrderTaskImpl
c.
Annotate the bean as stateless. Add the code in bold below. Notice that you can inject the CustomerTask into the OrderTask. This greatly simplifies things by avoiding JNDI lookup code. Also, you are also injecting an entity manager, as you did before.
package com.ibm.ejb3.order.session; import javax.ejb.EJB; import javax.ejb.Stateless; import com.ibm.ejb3.order.entities.Order; @Stateless public class OrderTaskImpl implements OrderTask {
@EJB(beanName = "CustomerTaskImpl") CustomerTask customerTask; @PersistenceContext(name="OrderDB") EntityManager em; public Order getCurrentOrder(int customerId) throws CustomerDoesNotExist { // TODO Auto-generated method stub return null; } }
d. e.
Finally, add the code in bold below to the getCurrentOrder method. Notice how easy it is to access queries. Also notice that the order method interacts with the Customer object. It is important to understand that because both classes are using @PersistenceContext, they will get proper propagation of the Entity Manager.
public Order getCurrentOrder(int customerId) throws CustomerDoesNotExist { Query query = em.createNamedQuery("getCurrentOrder"); query.setParameter(1, customerId); Order currentOrder = (Order)query.getSingleResult(); if(currentOrder != null) { Customer customer = customerTask.findCustomer(customerId); Order alreadySet = customer.getCurrentOrder(); if(alreadySet == null) { customer.setCurrentOrder(currentOrder); } } return currentOrder; }
f. g.
C. Add an interceptor
EJB 3.0 also adds the ability to use Aspect Oriented Programming techniques in your application. EJB 3.0 supports interceptors, which enable you to intercept code for cross cutting techniques. Next, you will create an audit interceptor that displays the customer's actions on the administrative console. 1. Create a simple Audit class: a. Create a class called AuditInterceptor (Figure 74).
b.
Add the code below in bold. @AroundInvoke tells the container what method to call when the interceptor intercepts. The code accesses the customerId and method name and displays them.
package com.ibm.ejb3.order.session; import java.lang.reflect.Method; import javax.interceptor.AroundInvoke; import javax.interceptor.InvocationContext; public class AuditInterceptor { @AroundInvoke public Object audit(InvocationContext invocationContext) throws Exception { Method operation = invocationContext.getMethod(); String name = operation.getName();
Object param[] = invocationContext.getParameters(); System.out.println("Customer Id " + param[0] + " executing operation => " + name); return invocationContext.proceed(); } }
2.
c. You can add the interceptor to the session beans by annotating the class with the @Interceptor annotation. You will do this for simplicity in this exercise, but it is often a best practice for a class not to be aware of who is intercepting it. EJB 3.0 supports partial XML deployment descriptors where you can define interceptor bindings that externalize matching an interceptor to a class. It is usually best to externalize interceptors. a. Open the OrderTaskImpl class and add the @Interceptors (AuditInterceptors.class), as shown below.
@Stateless @Interceptors(AuditInterceptor.class) public class OrderTaskImpl implements OrderTask {
b. c.
d.
2. 3.
Uncomment the code in bold below. Notice that you access the Order and Iterate functions through the LineItems. The LineItems will be populated because of the eager fetching rule you set.
private void displayCustomerAndOrder(int customerId, PrintWriter out) throws CustomerDoesNotExist { Customer customer = customerTask.findCustomer(customerId); out.println("<br>Customer ID => " + customer.getCustomerId()); out.println("<br>Customer Name => " + customer.getName()); Order order = orderTask.getCurrentOrder(customerId); if(order != null)
{ out.println("<br>order id => " + order.getOrderId()); out.println("<br>status => " + order.getStatus()); out.println("<br>Total => " + order.getTotal()); Collection<Lineitem> lineItems = order.getLineitemCollection(); for(Lineitem lineItem: lineItems) { out.println("<br>****"); out.println("<br>Line Item Id => " + lineItem.getLiId()); out.println("<br>Quantity => " + lineItem.getQuantity()); out.println("<br>Total => " + lineItem.getAmount()); } } else { out.println("<br>Customer has no open order..."); } }
4. 5.
Add the project to the server as you did before (Figure 75). Figure 75. Add Project Again
6.
7.
The browser should display the customer, the current order, and all the line items (Figure 77).
8.
Finally, examine the console to see the print statements for the interceptor (Figure 78). You will notice the findCustomer task is called twice, once from the client and once from the order task, and the getCurrentOrder task is called once. Figure 78. Examine Interceptor result
Back to top
Conclusion
EJB 3.0 is a major step forward in simplifying application development in the enterprise. This article showed you how to build EJB 3.0 applications for WebSphere Application Server V6.1 using the simplified programming model, the
new Java Persistence API, and interceptors. By using the EJB 3.0 Feature Pack for WebSphere Application Server, you can benefit from the simplified development experience, new persistence model, and new features such as interceptors, while still deploying to a robust WebSphere platform. Back to top
Acknowledgements
The authors wish to acknowledge Tom Alcott, Jim Knutson, Randy Schnier, Kevin Sutter, Tim Francis, and Keys Botzum for their contributions to the article. Back to top
Download
Description Code sample Name WAS_EJB3_LAB.zip Size 10 KB Download method HTTP