
| Goals | After completing this chapter, the student will be able to
|
| Prerequisites | In order to complete this chapter successfully, the student must
|
| Objectives | The objective for this chapter is to combine all of the technologies learned thus far in the course into one application, demonstrating how each technology works with the other technologies to create powerful and fault-tolerant web applications. |
This chapter presents a web application to keep track of the employees in a company, MyCo. This is done through a simple user interface that supports adding, removing, and modifying employee information, which includes the employee's name, extension, department, and city where the employee is located.
The main page represents the user with two options. The user can either view the directory of employees, or add a new employee. From the view page, the user can choose to modify or delete an employee. All pages include links back to the main page.
The application is implemented with JSPs, the Struts framework, and Enterprise Java Beans.
Database connections are essential resources, but they are heavy to create. Many users can use a web application simultaneously, and each needs to have a database connection, but none of them need to use it for more than a few seconds at a time (if that). Each user could create a new connection every time they need it, but this would lead to poor performance. Each user could create a database connection once and keep it open until they quit, but this would lead to poor scalability. As a compromise, a pool of open connections to the database can be maintained by the J2EE server. When a connection to the database is needed, the user requests one from the pool. When the connection is no longer needed, it is surrendered back to the connection pool.
To use a connection pool in JBoss, open the jboss.jcml configuration file and find the JDBC configuration section. Actually, find the configuration information we added in the previous chapter, that told JBoss about OpenBase. Add the following information just below the OpenBase driver information in the JDBC section:
|
|
|
Instead of creating a normal SQL statement, a prepared statement may be used. A prepared statement looks like a normal SQL statement, except that it may contain ‘?’s. A ‘?’ in a prepared statement will be substituted for a value at runtime. This enables prepared statements to provide quicker results because not all the work is done at runtime. Prepared statements are not always necessary; they are an optimization technique that you can use to speed up the most heavily used areas of your application.
|
|
|
|
|
The MyCo database will be implemented in OpenBase, which we saw in the previous chapter. However, the schema is simple enough that it could be implemented in virtually any database. All you would need to do is change the JDBC driver, and the rest of your code would still work.
| EMPLOYEE | DEPARTMENT | CITY |
| EMP_ID (integer) primary key, not null |
DEPT_ID (integer) primary key, not null |
CITY_ID (integer) primary key, not null |
| FIRSTNAME (varchar) | NAME (varchar) | NAME (varchar) |
| LASTNAME (varchar) | PHONE (varchar) | |
| EXTENSION (varchar) | SECRETARY_ID (integer) | |
| CITY_ID (integer) | ||
| DEPT_ID (integer) |
We will not actually be creating this database from scratch; we have a dump of the data already, so we will be restoring that data (like we did in the previous chapter with the PHONE database).
(If you have not already done so, you can download this and other examples used in this course. Mac OS X or other UNIX users click here instead.)
Do this:
| databases/employee.bck |
|---|
|
The directory structure for this case study is shown below. This should be old hat by now. Copy the struts.jar to the lib folder and the matching .tld files into the WEB-INF folder. Any JSPs should be placed in the same folder as the WEB-INF/. The source files to the Action and ActionForm classes and the EJBs should go in the classes folder, with an appropriate hierarchy for the package name. The ApplicationResources.properties file should be placed in the top level of the classes folder. The struts.jar and accompanying files should be placed in the lib and WEB-INF/ folders, respectively.
| code/Chapter7/Employee |
|---|
Employee
|
+-- build.xml (*)
|
+-- EmployeeClient
| |
| +-- employeeadd.jsp (*)
| |
| +-- employeeaddfailure.jsp (*)
| |
| +-- employeeaddsuccess.jsp (*)
| |
| +-- employeedeletefailure.jsp (*)
| |
| +-- employeedeletesuccess.jsp (*)
| |
| +-- employeeviewfailure.jsp (*)
| |
| +-- index.jsp (*)
| |
| +-- build.xml (*)
| |
| +-- employeeviewsuccess.jsp (*)
| |
| +-- employeemodify.jsp (*)
| |
| +-- WEB-INF
| |
| +-- ApplicationResources.properties (*)
| |
| +-- web.xml (*)
| |
| +-- app.tld (*)
| |
| +-- struts-bean.tld (*)
| |
| +-- struts-form.tld (*)
| |
| +-- struts-html.tld (*)
| |
| +-- struts-logic.tld (*)
| |
| +-- struts-template.tld (*)
| |
| +-- struts.tld (*)
| |
| +-- struts-config.xml (*)
| |
| +-- classes
| | |
| | +-- com
| | |
| | +-- masslight
| | |
| | +-- Employee
| | |
| | +-- EmployeeDeleteForm.java (*)
| | |
| | +-- EmployeeAddForm.java (*)
| | |
| | +-- EmployeeDeleteAction.java (*)
| | |
| | +-- EmployeeAddAction.java (*)
| | |
| | +-- EmployeeModifyAction.java (*)
| | |
| | +-- EmployeeModifyForm.java (*)
| | |
| | +-- EmployeeModifySetupForm.java (*)
| | |
| | +-- EmployeeViewAction.java (*)
| | |
| | +-- EmployeeAddSetupAction.java (*)
| | |
| | +-- EmployeeModifySetupAction.java (*)
| |
| +-- lib
| |
| +-- struts.jar (*)
|
+-- EmployeeEJB
|
+-- build.xml (*)
|
+-- META-INF
| |
| +-- sun-j2ee-ri.xml (*)
| |
| +-- application.xml (*)
|
+-- com
|
+-- masslight
|
+-- Employee
| |
| +-- Employee.java (*)
| |
| +-- ejb-jar.xml (*)
| |
| +-- EmployeeHome.java (*)
| |
| +-- EmployeeEJB.java (*)
|
+-- Department
| |
| +-- Department.java (*)
| |
| +-- ejb-jar.xml (*)
| |
| +-- DepartmentHome.java (*)
| |
| +-- DepartmentEJB.java (*)
|
+-- City
|
+-- City.java (*)
|
+-- CityHome.java (*)
|
+-- ejb-jar.xml (*)
|
+-- CityEJB.java (*)
(*) denotes a file |
The Employee EJB is an Entity Enterprise Bean with Bean Managed Persistence. It provides accessor and mutator methods for each column in the Employee table. It also must provide accessor and mutator methods for the Department and City relationships that correspond to the keys provided by EMPLOYEE.CITY_ID and EMPLOYEE.DEPT_ID attributes.
Only one create method is defined. It requires an id for use with the employee being created, along with the first name, last name, and the extension for the employee. The SQL for this operation is
insert into EMPLOYEE (EMP_ID, FIRSTNAME, LASTNAME, EXTENSION, CITY_ID, DEPT_ID)
values (XXX, 'XXXXXXXXXXXXX', 'XXXXXXXXXX', 'XXXX', XXX, XXX)
The SQL for removing an employee with a given employee id is
delete from EMPLOYEE where EMP_ID = XXX
For retrieving a row with a given employee id, the SQL is
select FIRSTNAME, LASTNAME, EXTENSION, CITY_ID, DEPT_ID from EMPLOYEE where EMP_ID = XXX
The findAll method must return a Collection of primary keys for the records in the table. The Home interface will convert these primary keys into a Collection of Employee interfaces for us; it is only necessary to fetch the primary keys from the EMPLOYEE table. The SQL to do this is
select EMP_ID from EMPLOYEE
Similar SQL is used in the load and store methods, as is appropriate.
Getting the City and Department objects is as easy as getting a handle on the Home interface for the respective object. Secondly, the findByPrimaryKey method is invoked to obtain a reference to a Remote interface that corresponds with the record found in the database whose primary key was passed to findByPrimaryKey. The accessor methods can then be invoked on the Remote interface to get information about the associated object.
For example, to find the extension for the secretary of the department for an employee, first find the department to which the employee is a member. Next, find the employee whose id is the same as the id of the secretary listed in the department record. Invoking getExtension on that object reveals the extension. Following is a snippet of code that does these things.
// Create a string in which to store the extension for the secretary
String extensionForSecretary = null;
// Setup the properties for out context
Properties env = new Properties();
env.setProperty("...");
try {
// Get a context
InitialContext jndiContext = new InitialContext(env);
// Do a JNDI lookup for Department
Object ref = jndiContext.lookup("Department");
// Get the Home interface for Department
DepartmentHome home =
(DepartmentHome)PortableRemoteObject.narrow(ref, DepartmentHome.class);
// Use the getDepartmentid from the current Employee to get a Remote
// interface to the appropriate Department
Department department = home.findByPrimaryKey(getDepartmentid());
// Get the id for the secretary for the department
Integer secretaryId = department.getSecretaryId();
// Get a new context
InitialContext jndiContext2 = new InitialContext(env);
// Do a JNDI lookup for Employee
Object ref2 = jndiContext2.lookup("Employee");
// Get the Home interface for Employee
EmployeeHome home2 =
(EmployeeHome)PortableRemoteObject.narrow(ref2, EmployeeHome.class);
// Get a remote interface to the appropriate Employee
Employee secretary = home2.findByPrimaryKey(secretaryId);
// Access the extension for the secretary
extensionForSecretary = secretary.getExtension();
} catch (Exception e) {
System.out.println("Encountered an error while getting secretary's extenion.");
}
The makeConnection method creates a connection to the database by looking up the DataSource associated with the JNDI name java:OpenBasePool, which is the name defined for the connection pool. If the DataSource lookup is successful, a connection is obtained from it.
The appropriate hostname or IP address should be substituted for localhost where appropriate, if the EJBs will be running on a different machine than the JNDI service.
Here are the sources to the Remote and Home interfaces, and the Bean class.
| code/Chapter7/Employee/EmployeeEJB/com/masslight/Employee/Employee.java |
|---|
|
| code/Chapter7/Employee/EmployeeEJB/com/masslight/Employee/EmployeeHome.java |
|---|
|
| code/Chapter7/Employee/EmployeeEJB/com/masslight/Employee/EmployeeEJB.java |
|---|
|
The City EJB is an Entity Enterprise Bean with Bean Managed Persistence. It provides accessor and mutator methods for each column in the City table.
The City EJB works just as the Employee EJB does. If a question arises, ask the instructor.
Here are the sources to the City EJB.
| code/Chapter7/Employee/EmployeeEJB/com/masslight/City/City.java |
|---|
|
| code/Chapter7/Employee/EmployeeEJB/com/masslight/City/CityHome.java |
|---|
|
| code/Chapter7/Employee/EmployeeEJB/com/masslight/City/CityEJB.java |
|---|
|
The Department EJB is an Entity Enterprise Bean with Bean Managed Persistence. It provides accessor and mutator methods for each column in the Department table. Although a foreign key exists in the database to associate an Employee as the secretary for a Department, it is not implemented. Only one create method is defined. It requires an id for use with the department being created, along with the name of the department, and the phone number. As before, the SQL is not discussed here. Again, ask the instructor any questions.
Here are the sources for the Department EJB.
| code/Chapter7/Employee/EmployeeEJB/com/masslight/Department/Department.java |
|---|
|
| code/Chapter7/Employee/EmployeeEJB/com/masslight/Department/DepartmentHome.java |
|---|
|
| code/Chapter7/Employee/EmployeeEJB/com/masslight/Department/DepartmentEJB.java |
|---|
|
Shown here is the big picture for the user interface.
The JSPs for the application are shown below.
| code/Chapter7/Employee/EmployeeClient/index.jsp |
|---|
|
| code/Chapter7/Employee/EmployeeClient/employeeviewsuccess.jsp |
|---|
|
| code/Chapter7/Employee/EmployeeClient/employeeviewfailure.jsp |
|---|
|
| code/Chapter7/Employee/EmployeeClient/employeeadd.jsp |
|---|
|
| code/Chapter7/Employee/EmployeeClient/employeeaddsuccess.jsp |
|---|
|
| code/Chapter7/Employee/EmployeeClient/employeeaddfailure.jsp |
|---|
|
| code/Chapter7/Employee/EmployeeClient/employeedeletesuccess.jsp |
|---|
|
| code/Chapter7/Employee/EmployeeClient/employeedeletefailure.jsp |
|---|
|
| code/Chapter7/Employee/EmployeeClient/employeemodify.jsp |
|---|
|
Create the ApplicationResources.properties file under the classes directory.
| code/Chapter7/Employee/EmployeeClient/WEB-INF/ApplicationResources.properties |
|---|
|
The actions contain the business logic for the application. Here are the sources for the Action classes and any ActionForm classes that may be associated with an action.
| code/Chapter7/Employee/EmployeeClient/WEB-INF/classes/com/masslight/Employee/EmployeeAddAction.java |
|---|
|
| code/Chapter7/Employee/EmployeeClient/WEB-INF/classes/com/masslight/Employee/EmployeeAddForm.java |
|---|
|
| code/Chapter7/Employee/EmployeeClient/WEB-INF/classes/com/masslight/Employee/EmployeeAddSetupAction.java |
|---|
|
| code/Chapter7/Employee/EmployeeClient/WEB-INF/classes/com/masslight/Employee/EmployeeDeleteAction.java |
|---|
|
| code/Chapter7/Employee/EmployeeClient/WEB-INF/classes/com/masslight/Employee/EmployeeDeleteForm.java |
|---|
|
| code/Chapter7/Employee/EmployeeClient/WEB-INF/classes/com/masslight/Employee/EmployeeModifyAction.java |
|---|
|
| code/Chapter7/Employee/EmployeeClient/WEB-INF/classes/com/masslight/Employee/EmployeeModifyForm.java |
|---|
|
| code/Chapter7/Employee/EmployeeClient/WEB-INF/classes/com/masslight/Employee/EmployeeModifySetupAction.java |
|---|
|
| code/Chapter7/Employee/EmployeeClient/WEB-INF/classes/com/masslight/Employee/EmployeeModifySetupForm.java |
|---|
|
| code/Chapter7/Employee/EmployeeClient/WEB-INF/classes/com/masslight/Employee/EmployeeViewAction.java |
|---|
|
As with all applications that use the Struts framework, a struts-config.xml file is needed to provide the appropriate mappings between the actions and forms and for specifying global forwards.
| code/Chapter7/Employee/EmployeeClient/WEB-INF/struts-config.xml |
|---|
|
The web.xml configuration used for this web application is no more special than the ones used for other web applications, except that it provides support for the Struts framework.
The action servlet mapping specifies that any request ending in .do should be considered an Action servlet. When a request for a URL ending in .do is submitted, the class specified in the struts-config.xml action mappings will be instantiated and methods invoked on it to perform the logic. For example, consider the case when the URL http://.../employeeaddsetup.do is requested. The container knows from struts-config.xml that an instance of com.masslight.Employee.EmployeeAddSetupAction should be instantiated and invokes the appropriate methods.
Also notice that the welcome file is index.jsp.
| code/Chapter7/Employee/EmployeeClient/WEB-INF/web.xml |
|---|
|
To make creating deployment descriptors easier for developers, Sun provides an application called Deploy Tool with the J2EE SDK. It is located in the bin directory of the J2EE installation, and the file name is deploytool. Start the Deploy Tool and choose File->New->Application. Name the new application Chapter6ejb.
Make sure that Chapter6ejb is selected and choose File->New->Enterprise Bean. The New Enterprise Bean Wizard will appear. Click the Next button on the Introduction screen. Enter EJBsChapter6 for the JAR Display Name.
The Home and Remote interfaces, and the class for the first EJB need to be added. To do this, select the Add button. Add the City EJB first. In the top portion of the window that appears, navigate to where the Home and Remote interfaces and the EJB implementation from the City EJB are located.
Choose Add and click OK.
Move to the next section in the New Enterprise Bean Wizard. Since City is an entity bean, choose Entity for the Bean Type. Make the appropriate selections in the Enterprise Bean Class, Home Interface, and Remote Interface pop-up menus. Give the Enterprise Bean the name City.
Choose Bean-Managed Persistence and enter java.lang.Integer for the Primary Key Class. Then continue to the next section.
Skip through the next several sections in the wizard, and stop at the Transaction-Management dialog. Choose Container-Managed Transactions. For the accessor and mutator methods, select Supports for the Transaction Type. For the remove, create, and finder methods, choose Required for the Transaction Type.
In the next dialog, the EJB deployment descriptor will be shown. Choose Finish.
The deployment tool main window shows the City EJB has been added.
Repeat the appropriate steps above to add the Employee and Department Enterprise Beans.
Finally, choose File->Save to save the EJB .ear file. The .ear can now be deployed, making the Enteprise Beans available for use in other applications. Copy the .ear to the deploy directory under the JBoss installation hierarchy.
This application has three Ant build scripts, one for the JSPs and Actions, one for the EJBs, and one that ties both of them together. Change to the root directory of the Employee project and run ant all.
| code/Chapter7/Employee/EmployeeClient/build.xml |
|---|
|
| code/Chapter7/Employee/EmployeeEJB/build.xml |
|---|
|