Showing posts with label ejb tutorial. Show all posts
Showing posts with label ejb tutorial. Show all posts

Saturday, 24 December 2011

Running the SavingsAccountEJB Example

Setting Up the Database

The instructions that follow explain how to use the SavingsAccountEJB example with a Cloudscape database. The Cloudscape software is included with the J2EE SDK download bundle.
  1. From the command-line prompt, run the Cloudscape database server by typing cloudscape -start. (When you are ready to shut down the server, type cloudscape -stop.)
  2. Create the savingsaccount database table.
    1. Go to the j2eetutorial/examples directory
    2. Type ant create-savingsaccount-table.
You may also run this example with databases other than Cloudscape. (See the Release Notes of the J2EE SDK for a list of supported databases.) If you are using one of these other databases, you may run the j2eetutorial/examples/sql/savingsaccount.sql script to create the savingsaccount table.

Deploying the Application

  1. In deploytool, open the j2eetutorial/examples/ears/SavingsAccountApp.ear file (FileOpen).
  2. Deploy the SavingsAccountApp application (ToolsDeploy). In the Introduction dialog box, make sure that you select the Return Client JAR checkbox.

Running the Client

  1. In a terminal window, go to the j2eetutorial/examples/ears directory.
  2. Set the APPCPATH environment variable to SavingsAccountAppClient.jar.
  3. Type the following command on a single line:
    runclient -client SavingsAccountApp.ear -name
          SavingsAccountClient -textauth
     
    
  4. At the login prompts, enter guest for the user name and guest123 for the password.
  5. The client should display the following lines:
    balance = 68.25
       balance = 32.55
       456: 44.77
       730: 19.54
       268: 100.07
       836: 32.55
       456: 44.77
       4.00
       7.00

Remote Interface

The remote interface extends javax.ejb.EJBObject and defines the business methods that a remote client may invoke. Here is the SavingsAccount remote interface:
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
import java.math.BigDecimal;

public interface SavingsAccount extends EJBObject {
    
    public void debit(BigDecimal amount)
        throws InsufficientBalanceException, RemoteException;

    public void credit(BigDecimal amount)
        throws RemoteException;
 
    public String getFirstName()
        throws RemoteException;

    public String getLastName()
        throws RemoteException;
   
    public BigDecimal getBalance()
        throws RemoteException;
}
 
The requirements for the method definitions in a remote interface are the same for both session and entity beans:
  • Each method in the remote interface must match a method in the enterprise bean class.
  • The signatures of the methods in the remote interface must be identical to the signatures of the corresponding methods in the enterprise bean class.
  • The arguments and return values must be valid RMI types.
  • The throws clause must include java.rmi.RemoteException.
A local interface has the same requirements, with the following exceptions:
  • The arguments and return values are not required to be valid RMI types.
  • The throws clause does not include java.rmi.RemoteException.

Database Calls

Table 5-1 summarizes the database access calls in the SavingsAccountBean class. The business methods of the SavingsAccountBean class are absent from the preceding table because they do not access the database. Instead, these business methods update the instance variables, which are written to the database when the EJB container calls ejbStore. Another developer might have chosen to access the database in the business methods of the SavingsAccountBean class. This choice is one of those design decisions that depend on the specific needs of your application.
Before accessing a database, you must connect to it.
Table 5-1 SQL Statements in SavingsAccountBean 
MethodSQL Statement
ejbCreateINSERT
ejbFindByPrimaryKeySELECT
ejbFindByLastNameSELECT
ejbFindInRangeSELECT
ejbLoadSELECT
ejbRemoveDELETE
ejbStoreUPDATE

Home Methods

A home method contains the business logic that applies to all entity beans of a particular class. In contrast, the logic in a business method applies to a single entity bean, an instance with a unique identity. During a home method invocation, the instance has neither a unique identity nor a state that represents a business object. Consequently, a home method must not access the bean's persistence state (instance variables). (For container-managed persistence, a home method also must not access relationships.)
Typically, a home method locates a collection of bean instances and invokes business methods as it iterates through the collection. This approach is taken by theejbHomeChargeForLowBalance method of the SavingsAccountBean class. The ejbHomeChargeForLowBalance method applies a service charge to all savings accounts with balances less than a specified amount. The method locates these accounts by invoking the findInRange method. As it iterates through the collection of SavingsAccount instances, theejbHomeChargeForLowBalance method checks the balance and invokes the debit business method. Here is the source code of the ejbHomeChargeForLowBalance method:
public void ejbHomeChargeForLowBalance(
    BigDecimal minimumBalance, BigDecimal charge) 
    throws InsufficientBalanceException {

   try {
       SavingsAccountHome home =
       (SavingsAccountHome)context.getEJBHome();
       Collection c = home.findInRange(new BigDecimal("0.00"),
           minimumBalance.subtract(new BigDecimal("0.01")));

       Iterator i = c.iterator();

       while (i.hasNext()) {
          SavingsAccount account = (SavingsAccount)i.next();
          if (account.getBalance().compareTo(charge) == 1) {
             account.debit(charge);
          }
       }

   } catch (Exception ex) {
       throw new EJBException("ejbHomeChargeForLowBalance: " 
           + ex.getMessage());
   } 
} 
 
The home interface defines a corresponding method named chargeForLowBalance. Since the interface provides the client view, the SavingsAccountClientprogram invokes the home method as follows:
SavingsAccountHome home;
...
home.chargeForLowBalance(new BigDecimal("10.00"), 
   new BigDecimal("1.00"));
 
In the entity bean class, the implementation of a home method must adhere to these rules:
  • A home method name must start with the prefix ejbHome.
  • The access control modifier must be public.
  • The method modifier cannot be static.
The throws clause may include exceptions that are specific to your application; it must not throw the java.rmi.RemoteException.

ejbLoad and ejbStore Methods

If the EJB container needs to synchronize the instance variables of an entity bean with the corresponding values stored in a database, it invokes the ejbLoad and ejbStore methods. TheejbLoad method refreshes the instance variables from the database, and the ejbStore method writes the variables to the database. The client may not call ejbLoad and ejbStore.
If a business method is associated with a transaction, the container invokes ejbLoad before the business method executes. Immediately after the business method executes, the container callsejbStore. Because the container invokes ejbLoad and ejbStore, you do not have to refresh and store the instance variables in your business methods. The SavingsAccountBean class relies on the container to synchronize the instance variables with the database. Therefore, the business methods of SavingsAccountBean should be associated with transactions.
If the ejbLoad and ejbStore methods cannot locate an entity in the underlying database, they should throw the javax.ejb.NoSuchEntityException. This exception is a subclass ofEJBException. Because EJBException is a subclass of RuntimeException, you do not have to include it in the throws clause. When NoSuchEntityException is thrown, the EJB container wraps it in a RemoteException before returning it to the client.
In the SavingsAccountBean class, ejbLoad invokes the loadRow method, which issues a SQL SELECT statement and assigns the retrieved data to the instance variables. The ejbStoremethod calls the storeRow method, which stores the instance variables in the database with a SQL UPDATE statement. Here is the code for the ejbLoad and ejbStore methods:
public void ejbLoad() {

   try {
      loadRow();
   } catch (Exception ex) {
      throw new EJBException("ejbLoad: " + 
         ex.getMessage());
   }
}

public void ejbStore() {

   try {
      storeRow();
   } catch (Exception ex) {
      throw new EJBException("ejbStore: " + 
         ex.getMessage());
   }
}

ejbRemove Method

A client deletes an entity bean by invoking the remove method. This invocation causes the EJB container to call the ejbRemove method, which deletes the entity state from the database. In theSavingsAccountBean class, the ejbRemove method invokes a private method named deleteRow, which issues a SQL DELETE statement. The ejbRemove method is short:
public void ejbRemove() {
    try {
        deleteRow(id);
    catch (Exception ex) {
        throw new EJBException("ejbRemove: " +
        ex.getMessage());
    }
}
 
If the ejbRemove method encounters a system problem, it should throw the javax.ejb.EJBException. If it encounters an application error, it should throw a javax.ejb.RemoveException. 
An entity bean may also be removed directly by a database deletion. For example, if a SQL script deletes a row that contains an entity bean state, then that entity bean is removed.

ejbPostCreate Method

For each ejbCreate method, you must write an ejbPostCreate method in the entity bean class. The EJB container invokes ejbPostCreate immediately after it calls ejbCreate. Unlike theejbCreate method, the ejbPostCreate method can invoke the getPrimaryKey and getEJBObject methods of the EntityContext interface. For more information on the getEJBObjectmethod. Often, your ejbPostCreate methods will be empty.
The signature of an ejbPostCreate method must meet the following requirements:
  • The number and types of arguments must match a corresponding ejbCreate method.
  • The access control modifier must be public.
  • The method modifier cannot be final or static.
  • The return type must be void.
The throws clause may include the javax.ejb.CreateException and exceptions that are specific to your application.

Friday, 23 December 2011

What Is RMI?

In short, RMI is remote method invocation. RMI allows a program to invoke methods on an object when the object is not located on the same machine as the program. This is at the heart of distributed computing in the Java world, and is the backbone of EJB as well as many enterprise application implementations. Without getting into too much detail, RMI uses client stubs to describe the methods a remote object has available for invocation. The client acts upon these stubs (which are Java interfaces), and RMI handles the "magic" of translating requests to a stub into a network call. This call invokes the method on the machine with the actual object, and then streams the result back across the network. Finally, the stub returns this result to the client that made the original method call, and the client moves on. The main idea is that the client doesn't typically worry about the RMI and network details; it uses the stub as if it were the actual object with implemented methods. RMI (using JRMPFigure 11.1.1, Java's remote protocol) makes all this network communication happen behind the scenes, allowing the client to deal with a generic exception (java.rmi.RemoteException) and spend more time handling business rules and application logic. RMI can also use different protocols such as Internet Inter-ORB Protocol (IIOP), allowing communication between Java and CORBA objects, often in different languages such as C or C++.
RMI carries a cost, though. First, using RMI is resource-intensive. JRMP provides very poor performance, and writing a remote protocol to replace it is not a simple task. As clients issue RMI calls, sockets must be opened and maintained, and the number of sockets can affect system performance, particularly when the system is accessible via a network (which then requires more sockets to be opened for HTTP access). RMI also requires a server or provider to bind objects to. Until an object is bound to a name on one of these providers, the object is not accessible to other programs. This requires using an RMI registry, a Lightweight Directory Access Protocol (LDAP) directory server, or a variety of other Java Naming and Directory Interface (JNDI) services. Finally, RMI can involve a lot of coding, even with all the helpful RMI server classes you get with the JDK; a remote interface describing the methods available to be invoked must be coded (as well as quite a few other interfaces if you are using EJB). This also means that adding an additional method to the server class results in a change to the interface and recompilation of the client stubs, something that is often not desirable and sometimes not possible.
Learn What is RPC?
See XML-RPC

Sunday, 18 December 2011

Scripting in JSP Pages

JSP scripting elements allow you to use Java programming language statements in your JSP pages. Scripting elements are typically used to create and access objects, define methods, and manage the flow of control. Many tasks that require the use of scripts can be eliminated by using custom tag libraries, in particular the JSP Standard Tag Library. Because one of the goals of JSP technology is to separate static data from the code needed to dynamically generate content, very sparing use of JSP scripting is recommended. Nevertheless, there may be some circumstances that require its use.
There are three ways to create and use objects in scripting elements:
  • Instance and class variables of the JSP pages servlet class are created in declarations and accessed in scriptlets and expressions.
  • Local variables of the JSP page's servlet class are created and used in scriptlets and expressions.
  • Attributes of scope objects (see Using Scope Objects) are created and used in scriptlets and expressions.

Initializing and Finalizing a JSP Page

You can customize the initialization process to allow the JSP page to read persistent configuration data, initialize resources, and perform any other one-time activities by overriding the jspInitmethod of the JspPage interface. You release resources using the jspDestroy method.
The bookstore example page initdestroy.jsp defines the jspInit method to retrieve or create an enterprise bean database.BookDBEJB that accesses the bookstore database;initdestroy.jsp stores a reference to the bean in bookDBEJB.
private BookDBEJB bookDBEJB;
public void jspInit() {
   bookDBEJB =
      (BookDB)getServletContext().getAttribute("bookDBEJB");
   if (bookDBEJB == null) {
      try {

         InitialContext ic = new InitialContext();
         Object objRef = ic.lookup(
            "java:comp/env/ejb/BookDBEJB");
         BookDBEJBHome home =
            (BookDBEJBHome)PortableRemoteObject.narrow(objRef,
               database.BookDBEJBHome.class);
         bookDBEJB = home.create();
         getServletContext().setAttribute("bookDBEJB",
            bookDBEJB);

      } catch (RemoteException ex) {
         System.out.println(
            "Couldn't create database bean." + ex.getMessage());
      } catch (CreateException ex) {
         System.out.println(
            "Couldn't create database bean." + ex.getMessage());
      } catch (NamingException ex) {
         System.out.println("Unable to lookup home: " +
            "java:comp/env/ejb/BookDBEJB."+ ex.getMessage());
      }
   }
}
 
When the JSP page is removed from service, the jspDestroy method releases the BookDBEJB variable:
public void jspDestroy() {
   bookDBEJB = null;
}
 
Since the enterprise bean is shared between all the JSP pages, it should be initialized when the application is started, instead of in each JSP page. Java Servlet technology provides application life cycle events and listener classes for this purpose. As an exercise, you can move the code that manages the creation of the enterprise bean to a context listener class. See Handling Servlet Life-Cycle Events for the context listener that initializes the Java Servlet version of the bookstore application.

The Life Cycle of a JSP Page

A JSP page services requests as a servlet. Thus, the life cycle and many of the capabilities of JSP pages (in particular the dynamic aspects) are determined by Java Servlet technology.
When a request is mapped to a JSP page, it is handled by a special servlet that first checks whether the JSP page's servlet is older than the JSP page. If it is, it translates the JSP page into a servlet class and compiles the class. During development, one of the advantages of JSP pages over servlets is that the build process is performed automatically.

Translation and Compilation

During the translation phase, each type of data in a JSP page is treated differently. Template data is transformed into code that will emit the data into the stream that returns data to the client. JSP elements are treated as follows:
  • Directives are used to control how the Web container translates and executes the JSP page.
  • Scripting elements are inserted into the JSP page's servlet class.
  • Elements of the form <jsp:XXX ... /> are converted into method calls to JavaBeans components or invocations of the Java Servlet API.
For a JSP page named pageName, the source for a JSP page's servlet is kept in the file
J2EE_HOME/repository/host/web/
   context_root/_0002fpageName_jsp.java
 
For example, the source for the index page (named index.jsp) for the date localization example discussed at the beginning of the chapter would be named
J2EE_HOME/repository/host/web/date/_0002findex_jsp.java
 
Both the translation and compilation phases can yield errors that are only observed when the page is requested for the first time. If an error occurs while the page is being translated (for example, if the translator encounters a malformed JSP element), the server will return a ParseException, and the servlet class source file will be empty or incomplete. The last incomplete line will give a pointer to the incorrect JSP element.
If an error occurs while the JSP page is being compiled (for example, there is a syntax error in a scriptlet), the server will return a JasperException and a message that includes the name of the JSP page's servlet and the line where the error occurred.
Once the page has been translated and compiled, the JSP page's servlet for the most part follows the servlet life cycle described in the section Servlet Life Cycle:
  1. If an instance of the JSP page's servlet does not exist, the container:
    1. Loads the JSP page's servlet class
    2. Instantiates an instance of the servlet class
    3. Initializes the servlet instance by calling the jspInit method
  2. Invokes the _jspService method, passing a request and response object.
If the container needs to remove the JSP page's servlet, it calls the jspDestroy method.

Execution

You can control various JSP page execution parameters using by page directives. The directives that pertain to buffering output and handling errors are discussed here. Other directives are covered in the context of specific page authoring tasks throughout the chapter.

Buffering

When a JSP page is executed, output written to the response object is automatically buffered. You can set the size of the buffer with the following page directive:
<%@ page buffer="none|xxxkb" %>
 
A larger buffer allows more content to be written before anything is actually sent back to the client, thus providing the JSP page with more time to set appropriate status codes and headers or to forward to another Web resource. A smaller buffer decreases server memory load and allows the client to start receiving data more quickly.

Handling Errors

Any number of exceptions can arise when a JSP page is executed. To specify that the Web container should forward control to an error page if an exception occurs, include the following pagedirective at the beginning of your JSP page:
<%@ page errorPage="file_name" %>
 
The Duke's Bookstore application page initdestroy.jsp contains the directive
<%@ page errorPage="errorpage.jsp"%>
 
The beginning of errorpage.jsp indicates that it is serving as an error page with the following page directive:
<%@ page isErrorPage="true|false" %>
 
This directive makes the exception object (of type javax.servlet.jsp.JspException) available to the error page, so that you can retrieve, interpret, and possibly display information about the cause of the exception in the error page.

Note: You can also define error pages for the WAR that contains a JSP page. If error pages are defined for both the WAR and a JSP page, the JSP page's error page takes precedence.

The JSP Pages Examples

To illustrate JSP technology, this chapter rewrites each servlet in the Duke's Bookstore application introduced in The Example Servlets in as a JSP page. Table 11-1 lists the functions and their corresponding JSP pages.

Table 11-1 Duke's Bookstore Example JSP Pages 
FunctionJSP Pages
Enter the bookstorebookstore.jsp
Create the bookstore bannerbanner.jsp
Browse the books offered for salecatalog.jsp
Put a book in a shopping cartcatalog.jsp and bookdetails.jsp
Get detailed information on a specific bookbookdetails.jsp
Display the shopping cartshowcart.jsp
Remove one or more books from the shopping cartshowcart.jsp
Buy the books in the shopping cartcashier.jsp
Receive an acknowledgement for the purchasereceipt.jsp

The data for the bookstore application is still maintained in a database. However, two changes are made to the database helper object database.BookDB.
  • The database helper object is rewritten to conform to JavaBeans component design patterns as described in JavaBeans Component Design Conventions (page 270). This change is made so that JSP pages can access the helper object using JSP language elements specific to JavaBeans components.
  • Instead of accessing the bookstore database directly, the helper object goes through an enterprise bean. The advantage of using an enterprise bean is that the helper object is no longer responsible for connecting to the database; this job is taken over by the enterprise bean. Furthermore, because the EJB container maintains the pool of database connections, an enterprise bean can get a connection quicker than the helper object can. The relevant interfaces and classes for the enterprise bean are the database.BookDBEJBHome home interface,database.BookDBEJB remote interface, and the database.BookDBEJBImpl implementation class, which contains all the JDBC calls to the database.
The implementation of the database helper object follows. The bean has two instance variables: the current book and a reference to the database enterprise bean.
public class BookDB {
   private String bookId = "0";
   private BookDBEJB database = null;

   public BookDB () throws Exception {
   }
   public void setBookId(String bookId) {
      this.bookId = bookId;
   }
   public void setDatabase(BookDBEJB database) {
      this.database = database;
   }
   public BookDetails getBookDetails() 
      throws Exception {
      try {
         return (BookDetails)database.
            getBookDetails(bookId);
      } catch (BookNotFoundException ex) {
         throw ex;
      } 
   }
   ...
}
 
Finally, this version of the example contains an applet to generate a dynamic digital clock in the banner.
The source code for the application is located in the j2eetutorial/examples/src/web/bookstore2 directory created when you unzip the tutorial bundle. To build, deploy, and run the example:
  1. Go to j2eetutorial/examples and build the example by running ant bookstore2.
  2. Start the j2ee server.
  3. Start deploytool.
  4. Start the Cloudscape database by executing cloudscape -start.
  5. If you have not already created the bookstore database, run ant create-web-db.
  6. Create a J2EE application called Bookstore2App.
    1. Select FileNewApplication.
    2. In the file chooser, navigate to j2eetutorial/examples/src/web/bookstore2.
    3. In the File Name field, enter Bookstore2App.
    4. Click New Application.
    5. Click OK.
  7. Add the Bookstore2WAR WAR to the Bookstore2App application.
    1. Select FileAddWeb WAR.
    2. In the Add Web WAR dialog box, navigate to j2eetutorial/examples/build/web/bookstore2. Select bookstore2.war. Click Add Web WAR.
  8. Add the BookDBEJB enterprise bean to the application.
    1. Select FileNew Enterprise Bean.
    2. Select Bookstore2App from the Create New JAR File In Application combo box.
    3. Type BookDBJAR in the JAR Display Name field.
    4. Click Edit to add the content files.
    5. In the Edit Archive Contents dialog box, navigate to the j2eetutorial/examples/build/web/ejb directory and add the database and exception packages. Click Next.
    6. Choose Session and Stateless for the Bean Type.
    7. Select database.BookDBEJBImpl for Enterprise Bean Class.
    8. In the Remote Interfaces box, select database.BookDBEJBHome for Remote Home Interface and database.BookDBEJB for Remote Interface.
    9. Enter BookDBEJB for Enterprise Bean Name.
    10. Click Next and then click Finish.
  9. Add a resource reference for the Cloudscape database to the BookDBEJB bean.
    1. Select the BookDBEJB enterprise bean.
    2. Select the Resource Refs tab.
    3. Click Add.
    4. Select javax.sql.DataSource from the Type column.
    5. Enter jdbc/BookDB in the Coded Name field.
  10. Save BookDBJAR.
    1. Select BookDBJAR.
    2. Select FileSave As.
    3. Navigate to the directory examples/build/web/ejb.
    4. Enter bookDB.jar in the File Name field.
    5. Click Save EJB JAR As.
  11. Add a reference to the enterprise bean BookDBEJB.
    1. Select Bookstore2WAR.
    2. Select the EJB Refs tab.
    3. Click Add.
    4. Enter ejb/BookDBEJB in the Coded Name column.
    5. Select Session in the Type column.
    6. Select Remote in the Interfaces column.
    7. Enter database.BookDBEJBHome in the Home Interface column.
    8. Enter database.BookDBEJB in the Local/Remote Interface column.
  12. Specify the JNDI Names.
    1. Select Bookstore2App.
    2. In the Application table, locate the EJB component and enter BookDBEJB in the JNDI Name column.
    3. In the References table, locate the EJB Ref and enter BookDBEJB in the JNDI Name column.
    4. In the References table, locate the Resource component and enter jdbc/Cloudscape in the JNDI Name column.
  13. Enter the context root.
    1. Select the Web Context tab.
    2. Enter bookstore2.
  14. Deploy the application.
    1. Select ToolsDeploy.
    2. Click Finish.
  15. Open the bookstore URL http://<host>:8000/bookstore2/enter.